mirror of
https://gitlab.com/2009scape/2009scape.git
synced 2026-08-25 04:15:14 -06:00
Big fix batch, return to non-MobyDick2
This commit is contained in:
parent
5b4545ea4b
commit
4ba22ba1a7
32 changed files with 504 additions and 388 deletions
|
|
@ -34820,6 +34820,7 @@
|
|||
"defence_anim": "420",
|
||||
"durability": null,
|
||||
"equipment_slot": "3",
|
||||
"unequip_without_inventory_space": "true",
|
||||
"render_anim": "131",
|
||||
"run_anim": "1427",
|
||||
"stand_anim": "1421",
|
||||
|
|
@ -34850,6 +34851,7 @@
|
|||
"defence_anim": "420",
|
||||
"durability": null,
|
||||
"equipment_slot": "3",
|
||||
"unequip_without_inventory_space": "true",
|
||||
"render_anim": "131",
|
||||
"run_anim": "1427",
|
||||
"stand_anim": "1421",
|
||||
|
|
|
|||
|
|
@ -2178,6 +2178,15 @@
|
|||
"title": "Castle Wars Ticket Exchange",
|
||||
"stock": "{4068,1,100}-{4069,1,100}-{4070,1,100}-{4071,1,100}-{4072,1,100}-{4503,1,100}-{4504,1,100}-{4505,1,100}-{4506,1,100}-{4507,1,100}-{4508,1,100}-{4509,1,100}-{4510,1,100}-{4511,1,100}-{4512,1,100}-{4513,1,100}-{4514,1,100}-{4515,1,100}-{4516,1,100}"
|
||||
},
|
||||
{
|
||||
"npcs": "5111",
|
||||
"high_alch": "0",
|
||||
"currency": "995",
|
||||
"general_store": "false",
|
||||
"id": "256",
|
||||
"title": "Leon's Prototype Crossbow",
|
||||
"stock": "{10156,2,100}"
|
||||
},
|
||||
{
|
||||
"npcs": "2039",
|
||||
"high_alch": "0",
|
||||
|
|
@ -2187,4 +2196,4 @@
|
|||
"title": "Uglug's Stuffsies",
|
||||
"stock": "{4844,100,10}-{10927,0,100}-{2862,100,100}-{1777,10,200}-{2876,0,500}-{2878,10,100}-{4850,0,100}-{946,5,100}-{4773,0,1000}-{4778,0,1000}-{4783,0,1000}-{4788,0,1500}-{4793,0,2000}-{4798,0,3000}-{4803,0,4000}-{4827,0,2000}"
|
||||
}
|
||||
]
|
||||
]
|
||||
|
|
|
|||
|
|
@ -11,7 +11,7 @@ import org.rs09.consts.NPCs
|
|||
* Custom NPC class for Castle Wars barricades.
|
||||
*
|
||||
* - Block all entity movement through them (like Pest Control Brawlers)
|
||||
* - Never retaliate when attacked (this was scary man they squared up)
|
||||
* - Never retaliate when attacked
|
||||
* - Never turn to face attackers or any direction
|
||||
* - Cannot attack or move
|
||||
*/
|
||||
|
|
@ -46,9 +46,7 @@ class CastleWarsBarricadeNPC : AbstractNPC {
|
|||
return false
|
||||
}
|
||||
|
||||
override fun attack(node: Node?) {
|
||||
return
|
||||
}
|
||||
override fun attack(node: Node?) {}
|
||||
|
||||
override fun isPoisonImmune(): Boolean {
|
||||
return true
|
||||
|
|
|
|||
|
|
@ -39,7 +39,7 @@ class CastleWarsCatapultInterface : InterfaceListener {
|
|||
}
|
||||
|
||||
private fun handleButtonClick(player: Player, buttonId: Int): Boolean {
|
||||
val team = getPlayerTeam(player) ?: return closeAndReturn(player, false)
|
||||
val team = CastleWarsGameState.getActiveGameTeam(player) ?: return closeAndReturn(player, true)
|
||||
val currentCoord = getCurrentCoordinate(player)
|
||||
|
||||
return when (val button = CatapultButton.fromId(buttonId)) {
|
||||
|
|
@ -89,6 +89,10 @@ class CastleWarsCatapultInterface : InterfaceListener {
|
|||
catapultTeam: CastleWarsTeam,
|
||||
catapultLocation: Location
|
||||
): Boolean {
|
||||
if (!CastleWarsGameState.isGameActive || CastleWarsGameState.getActiveGameTeam(player) != playerTeam) {
|
||||
sendMessage(player, "The Castle Wars game is no longer active.")
|
||||
return false
|
||||
}
|
||||
if (playerTeam != catapultTeam) {
|
||||
sendMessage(player, "You can't use the enemy's catapult!")
|
||||
return false
|
||||
|
|
@ -99,6 +103,10 @@ class CastleWarsCatapultInterface : InterfaceListener {
|
|||
sendMessage(player, "The catapult is no longer there.")
|
||||
return false
|
||||
}
|
||||
if (player.location.z != catapultLocation.z || player.location.getDistance(catapultLocation) > 2.0) {
|
||||
sendMessage(player, "You have moved too far away from the catapult.")
|
||||
return false
|
||||
}
|
||||
if (currentCatapult.id in CastleWars.damagedCatapultIds || !CastleWarsGameState.isCatapultOperational(
|
||||
catapultTeam
|
||||
)
|
||||
|
|
@ -143,10 +151,6 @@ class CastleWarsCatapultInterface : InterfaceListener {
|
|||
renderer.renderCoordinate(player, getCurrentCoordinate(player))
|
||||
}
|
||||
|
||||
private fun getPlayerTeam(player: Player): CastleWarsTeam? {
|
||||
return CastleWarsGameState.getPlayerTeam(player)
|
||||
}
|
||||
|
||||
private fun getCatapultLocation(player: Player): Location? {
|
||||
return getAttribute(player, ATTR_LOCATION, null)
|
||||
}
|
||||
|
|
|
|||
|
|
@ -25,7 +25,7 @@ class CastleWarsDoorAttackPulse(
|
|||
private var ticksUntilNextHit = 0
|
||||
|
||||
override fun pulse(): Boolean {
|
||||
if (!player.isActive || CastleWarsGameState.isDoorDestroyed(doorTeam) || CastleWarsGameState.getPlayerTeam(
|
||||
if (!player.isActive || CastleWarsGameState.isDoorDestroyed(doorTeam) || CastleWarsGameState.getActiveGameTeam(
|
||||
player
|
||||
) == null
|
||||
) {
|
||||
|
|
|
|||
|
|
@ -16,8 +16,15 @@ object CastleWarsEquipmentRestrictions {
|
|||
CastleWars.saradominTeamHoodedCloak, CastleWars.zamorakTeamHoodedCloak
|
||||
)
|
||||
|
||||
private val castleWarsBorders by lazy {
|
||||
CastleWarsGameArea.areaBorders.toList() + CastleWarsWaitingArea.areaBorders.toList() + listOf(
|
||||
CastleWarsRespawnArea.saradominRespawnRoom,
|
||||
CastleWarsRespawnArea.zamorakRespawnRoom
|
||||
)
|
||||
}
|
||||
|
||||
fun blocksEquip(player: Player, equipmentSlot: Int): Boolean {
|
||||
if (equipmentSlot !in restrictedSlots || !isInCastleWarsArea(player)) {
|
||||
if (equipmentSlot !in restrictedSlots) {
|
||||
return false
|
||||
}
|
||||
|
||||
|
|
@ -34,6 +41,10 @@ object CastleWarsEquipmentRestrictions {
|
|||
}
|
||||
}
|
||||
|
||||
fun isInCastleWarsArea(player: Player): Boolean {
|
||||
return castleWarsBorders.any { it.insideBorder(player.location) }
|
||||
}
|
||||
|
||||
private fun removeEquipment(player: Player, slot: Int) {
|
||||
val item = player.equipment[slot] ?: return
|
||||
player.equipment.remove(item)
|
||||
|
|
@ -41,13 +52,4 @@ object CastleWarsEquipmentRestrictions {
|
|||
GroundItemManager.create(item.copy(), player.location, player)
|
||||
}
|
||||
}
|
||||
|
||||
private fun isInCastleWarsArea(player: Player): Boolean {
|
||||
val allCastleWarsBorders =
|
||||
CastleWarsGameArea.areaBorders.toList() + CastleWarsWaitingArea.areaBorders.toList() + listOf(
|
||||
CastleWarsRespawnArea.saradominRespawnRoom,
|
||||
CastleWarsRespawnArea.zamorakRespawnRoom
|
||||
)
|
||||
return allCastleWarsBorders.any { it.insideBorder(player.location) }
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -43,7 +43,7 @@ class CastleWarsFlagHandler : InteractionListener {
|
|||
sendMessage(player, "There is no game in progress.")
|
||||
return@on true
|
||||
}
|
||||
val playerTeam = CastleWarsGameState.getPlayerTeam(player)
|
||||
val playerTeam = CastleWarsGameState.getActiveGameTeam(player)
|
||||
if (playerTeam == null) {
|
||||
sendMessage(player, "You are not in a Castle Wars game.")
|
||||
return@on true
|
||||
|
|
@ -129,7 +129,7 @@ class CastleWarsFlagHandler : InteractionListener {
|
|||
sendMessage(player, "There is no game in progress.")
|
||||
return@on true
|
||||
}
|
||||
val playerTeam = CastleWarsGameState.getPlayerTeam(player)
|
||||
val playerTeam = CastleWarsGameState.getActiveGameTeam(player)
|
||||
if (playerTeam == null) {
|
||||
sendMessage(player, "You are not in a Castle Wars game.")
|
||||
return@on true
|
||||
|
|
@ -149,11 +149,9 @@ class CastleWarsFlagHandler : InteractionListener {
|
|||
return@on true
|
||||
}
|
||||
if (flagTeam == playerTeam) {
|
||||
if (CastleWarsGameState.pickUpOwnFlag(player, flagTeam)) { // Message already sent by pickUpOwnFlag
|
||||
}
|
||||
CastleWarsGameState.pickUpOwnFlag(player, flagTeam)
|
||||
} else {
|
||||
if (CastleWarsGameState.pickUpFlag(player, flagTeam)) { // Message already sent by pickUpFlag
|
||||
}
|
||||
CastleWarsGameState.pickUpFlag(player, flagTeam)
|
||||
}
|
||||
|
||||
return@on true
|
||||
|
|
|
|||
|
|
@ -19,7 +19,6 @@ import core.game.node.scenery.SceneryBuilder
|
|||
import core.game.world.map.Location
|
||||
import core.game.world.map.RegionManager
|
||||
import core.game.world.map.zone.ZoneBorders
|
||||
import core.game.world.update.flag.chunk.ObjectUpdateFlag
|
||||
import org.rs09.consts.Items
|
||||
|
||||
/**
|
||||
|
|
@ -64,7 +63,9 @@ enum class BoulderState {
|
|||
}
|
||||
|
||||
data class ActiveClimbingRope(
|
||||
val rope: Scenery, val originalWall: Scenery
|
||||
val rope: Scenery,
|
||||
val originalWall: Scenery,
|
||||
val ropeWall: Scenery
|
||||
)
|
||||
|
||||
/**
|
||||
|
|
@ -275,12 +276,12 @@ object CastleWarsGameState {
|
|||
resetBattlefield()
|
||||
}
|
||||
|
||||
fun registerClimbingRope(rope: Scenery, originalWall: Scenery) {
|
||||
fun registerClimbingRope(rope: Scenery, originalWall: Scenery, ropeWall: Scenery) {
|
||||
val key = Location.create(rope.location)
|
||||
|
||||
rope.isActive = true
|
||||
rope.isRenderable = true
|
||||
activeClimbingRopes[key] = ActiveClimbingRope(rope, Scenery(originalWall))
|
||||
activeClimbingRopes[key] = ActiveClimbingRope(rope, Scenery(originalWall), Scenery(ropeWall))
|
||||
SceneryBuilder.add(rope)
|
||||
}
|
||||
|
||||
|
|
@ -288,18 +289,21 @@ object CastleWarsGameState {
|
|||
return activeClimbingRopes[Location.create(location)]?.rope?.takeIf { it.isActive }
|
||||
}
|
||||
|
||||
fun removeClimbingRope(location: Location, sendUpdate: Boolean = true): Boolean {
|
||||
val activeRope = activeClimbingRopes.remove(Location.create(location)) ?: return false
|
||||
fun removeClimbingRope(rope: Scenery): Boolean {
|
||||
val key = Location.create(rope.location)
|
||||
val activeRope = activeClimbingRopes[key] ?: return false
|
||||
if (activeRope.rope !== rope) return false
|
||||
activeClimbingRopes.remove(key)
|
||||
|
||||
activeRope.rope.isActive = false
|
||||
SceneryBuilder.replace(activeRope.rope, activeRope.originalWall)
|
||||
SceneryBuilder.remove(activeRope.rope)
|
||||
SceneryBuilder.replace(activeRope.ropeWall, activeRope.originalWall, false, false)
|
||||
return true
|
||||
}
|
||||
|
||||
fun clearClimbingRopes(sendUpdate: Boolean = true) {
|
||||
val ropeLocations = activeClimbingRopes.keys.map { Location.create(it) }
|
||||
for (location in ropeLocations) {
|
||||
removeClimbingRope(location, sendUpdate)
|
||||
fun clearClimbingRopes() {
|
||||
val ropes = activeClimbingRopes.values.map { it.rope }
|
||||
for (rope in ropes) {
|
||||
removeClimbingRope(rope)
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -385,33 +389,9 @@ object CastleWarsGameState {
|
|||
CastleWarsRespawnArea.zamorakRespawnRoom.insideBorder(location)
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the team a player is on.
|
||||
* Includes a self-healing mechanism if the player is in the area but not in the sets.
|
||||
*/
|
||||
fun getPlayerTeam(player: Player): CastleWarsTeam? {
|
||||
fun getActiveGameTeam(player: Player): CastleWarsTeam? {
|
||||
if (CastleWarsGameArea.saradominPlayers.contains(player)) return CastleWarsTeam.SARADOMIN
|
||||
if (CastleWarsGameArea.zamorakPlayers.contains(player)) return CastleWarsTeam.ZAMORAK
|
||||
if (CastleWarsWaitingArea.waitingSaradominPlayers.contains(player)) return CastleWarsTeam.SARADOMIN
|
||||
if (CastleWarsWaitingArea.waitingZamorakPlayers.contains(player)) return CastleWarsTeam.ZAMORAK
|
||||
if (isGameActive) {
|
||||
val inCWArea =
|
||||
CastleWarsGameArea.areaBorders.any { it.insideBorder(player.location) } || CastleWarsRespawnArea.saradominRespawnRoom.insideBorder(
|
||||
player.location
|
||||
) || CastleWarsRespawnArea.zamorakRespawnRoom.insideBorder(
|
||||
player.location
|
||||
)
|
||||
if (inCWArea) {
|
||||
val capeId = player.equipment.get(EquipmentContainer.SLOT_CAPE)?.id ?: -1
|
||||
if (capeId == CastleWars.saradominTeamHoodedCloak) {
|
||||
CastleWarsGameArea.saradominPlayers.add(player)
|
||||
return CastleWarsTeam.SARADOMIN
|
||||
} else if (capeId == CastleWars.zamorakTeamHoodedCloak) {
|
||||
CastleWarsGameArea.zamorakPlayers.add(player)
|
||||
return CastleWarsTeam.ZAMORAK
|
||||
}
|
||||
}
|
||||
}
|
||||
return null
|
||||
}
|
||||
|
||||
|
|
@ -422,7 +402,7 @@ object CastleWarsGameState {
|
|||
*/
|
||||
fun checkFlagCarriers() {
|
||||
saradominFlagCarrier?.let { player ->
|
||||
if (!player.isActive || getPlayerTeam(player) == null) {
|
||||
if (!player.isActive || getActiveGameTeam(player) == null) {
|
||||
dropFlag(player)
|
||||
} else {
|
||||
val weapon = player.equipment.get(EquipmentContainer.SLOT_WEAPON)
|
||||
|
|
@ -432,7 +412,7 @@ object CastleWarsGameState {
|
|||
}
|
||||
}
|
||||
zamorakFlagCarrier?.let { player ->
|
||||
if (!player.isActive || getPlayerTeam(player) == null) {
|
||||
if (!player.isActive || getActiveGameTeam(player) == null) {
|
||||
dropFlag(player)
|
||||
} else {
|
||||
val weapon = player.equipment.get(EquipmentContainer.SLOT_WEAPON)
|
||||
|
|
@ -442,7 +422,7 @@ object CastleWarsGameState {
|
|||
}
|
||||
}
|
||||
saradominOwnFlagCarrier?.let { player ->
|
||||
if (!player.isActive || getPlayerTeam(player) == null) {
|
||||
if (!player.isActive || getActiveGameTeam(player) == null) {
|
||||
dropOwnFlag(player)
|
||||
} else {
|
||||
val weapon = player.equipment.get(EquipmentContainer.SLOT_WEAPON)
|
||||
|
|
@ -454,7 +434,7 @@ object CastleWarsGameState {
|
|||
}
|
||||
}
|
||||
zamorakOwnFlagCarrier?.let { player ->
|
||||
if (!player.isActive || getPlayerTeam(player) == null) {
|
||||
if (!player.isActive || getActiveGameTeam(player) == null) {
|
||||
dropOwnFlag(player)
|
||||
} else {
|
||||
val weapon = player.equipment.get(EquipmentContainer.SLOT_WEAPON)
|
||||
|
|
@ -465,7 +445,6 @@ object CastleWarsGameState {
|
|||
}
|
||||
}
|
||||
}
|
||||
updateAllHintIcons()
|
||||
}
|
||||
|
||||
/**
|
||||
|
|
@ -539,7 +518,7 @@ object CastleWarsGameState {
|
|||
* @return true if the flag was successfully picked up
|
||||
*/
|
||||
fun pickUpFlag(player: Player, flagTeam: CastleWarsTeam): Boolean {
|
||||
val playerTeam = getPlayerTeam(player) ?: return false
|
||||
val playerTeam = getActiveGameTeam(player) ?: return false
|
||||
if (playerTeam == flagTeam) {
|
||||
sendMessage(player, "You can't pick up your own team's flag!")
|
||||
return false
|
||||
|
|
@ -572,7 +551,7 @@ object CastleWarsGameState {
|
|||
if (currentWeapon != null) {
|
||||
if (!player.inventory.add(currentWeapon)) {
|
||||
GroundItemManager.create(currentWeapon, player.location, player)
|
||||
sendMessage(player, "Your weapon was dropped because your inventory is full.") // TODO: nah?
|
||||
sendMessage(player, "Your weapon was dropped because your inventory is full.")
|
||||
} else {
|
||||
sendMessage(player, "Your weapon has been unequipped.")
|
||||
}
|
||||
|
|
@ -582,7 +561,7 @@ object CastleWarsGameState {
|
|||
player.equipment.remove(shield)
|
||||
if (!player.inventory.add(shield)) {
|
||||
GroundItemManager.create(shield, player.location, player)
|
||||
sendMessage(player, "Your shield was dropped because your inventory is full.") // TODO: nah?
|
||||
sendMessage(player, "Your shield was dropped because your inventory is full.")
|
||||
} else {
|
||||
sendMessage(player, "Your shield has been unequipped.")
|
||||
}
|
||||
|
|
@ -776,7 +755,7 @@ object CastleWarsGameState {
|
|||
* @return true if the flag was successfully picked up
|
||||
*/
|
||||
fun pickUpOwnFlag(player: Player, flagTeam: CastleWarsTeam): Boolean {
|
||||
val playerTeam = getPlayerTeam(player) ?: return false // Must be your own team's flag
|
||||
val playerTeam = getActiveGameTeam(player) ?: return false // Must be your own team's flag
|
||||
if (playerTeam != flagTeam) {
|
||||
sendMessage(player, "That's not your team's flag!")
|
||||
return false
|
||||
|
|
@ -842,7 +821,7 @@ object CastleWarsGameState {
|
|||
* A grace period prevents instant return if the flag is picked up while already inside the castle.
|
||||
*/
|
||||
private fun checkOwnFlagReturn(player: Player) {
|
||||
val playerTeam = getPlayerTeam(player) ?: return
|
||||
val playerTeam = getActiveGameTeam(player) ?: return
|
||||
val carriedFlag = getOwnCarriedFlag(player) ?: return
|
||||
if (carriedFlag != playerTeam) return
|
||||
val pickupTick = when (playerTeam) {
|
||||
|
|
@ -924,7 +903,7 @@ object CastleWarsGameState {
|
|||
* @return true if the flag was successfully captured
|
||||
*/
|
||||
fun captureFlag(player: Player): Boolean {
|
||||
val playerTeam = getPlayerTeam(player) ?: return false
|
||||
val playerTeam = getActiveGameTeam(player) ?: return false
|
||||
val carriedFlag = getCarriedFlag(player) ?: return false
|
||||
if (carriedFlag == playerTeam) {
|
||||
return false
|
||||
|
|
@ -957,6 +936,7 @@ object CastleWarsGameState {
|
|||
announceToAllPlayers("${playerTeam.teamName} has captured the ${carriedFlag.teamName} flag!")
|
||||
announceToAllPlayers("Score: Saradomin $saradominScore - $zamorakScore Zamorak")
|
||||
updateAllOverlays()
|
||||
updateAllHintIcons()
|
||||
return true
|
||||
}
|
||||
|
||||
|
|
@ -1034,7 +1014,7 @@ object CastleWarsGameState {
|
|||
* Update the hint icon for a single player
|
||||
*/
|
||||
private fun updateHintIconForPlayer(player: Player) {
|
||||
val playerTeam = getPlayerTeam(player) ?: return
|
||||
val playerTeam = getActiveGameTeam(player) ?: return
|
||||
val enemyTeam = playerTeam.opposite()
|
||||
playerHintIconSlots[player]?.let { slot ->
|
||||
HintIconManager.removeHintIcon(player, slot)
|
||||
|
|
@ -1046,8 +1026,7 @@ object CastleWarsGameState {
|
|||
val carrier = if (enemyTeam == CastleWarsTeam.SARADOMIN) saradominFlagCarrier ?: saradominOwnFlagCarrier
|
||||
else zamorakFlagCarrier ?: zamorakOwnFlagCarrier
|
||||
carrier?.let {
|
||||
if (it == player) {
|
||||
} else {
|
||||
if (it != player) {
|
||||
val slot = HintIconManager.registerHintIcon(player, it)
|
||||
if (slot >= 0) {
|
||||
playerHintIconSlots[player] = slot
|
||||
|
|
@ -1123,7 +1102,7 @@ object CastleWarsGameState {
|
|||
fun endGame(extraRewardRecipients: Map<Player, CastleWarsTeam> = emptyMap()) {
|
||||
val rewardRecipients = linkedMapOf<Player, CastleWarsTeam>()
|
||||
for (player in getAllGamePlayers()) {
|
||||
val team = getPlayerTeam(player) ?: continue
|
||||
val team = getActiveGameTeam(player) ?: continue
|
||||
rewardRecipients[player] = team
|
||||
}
|
||||
for ((player, team) in extraRewardRecipients) {
|
||||
|
|
@ -1190,6 +1169,7 @@ object CastleWarsGameState {
|
|||
* @param damage Amount of damage (0-100)
|
||||
*/
|
||||
fun damageDoor(team: CastleWarsTeam, damage: Int) {
|
||||
require(damage >= 0) { "Door damage cannot be negative" }
|
||||
val previousHealth = getDoorHealth(team)
|
||||
when (team) {
|
||||
CastleWarsTeam.SARADOMIN -> {
|
||||
|
|
@ -1252,6 +1232,7 @@ object CastleWarsGameState {
|
|||
* @param repair Amount to repair (0-100)
|
||||
*/
|
||||
fun repairDoor(team: CastleWarsTeam, repair: Int) {
|
||||
require(repair >= 0) { "Door repair amount cannot be negative" }
|
||||
when (team) {
|
||||
CastleWarsTeam.SARADOMIN -> {
|
||||
saradominDoorHealth = (saradominDoorHealth + repair).coerceAtMost(100)
|
||||
|
|
@ -1314,7 +1295,7 @@ object CastleWarsGameState {
|
|||
"saradomin2" -> saradominBoulder2State
|
||||
"zamorak1" -> zamorakBoulder1State
|
||||
"zamorak2" -> zamorakBoulder2State
|
||||
else -> BoulderState.COLLAPSED
|
||||
else -> throw IllegalArgumentException("Unknown Castle Wars boulder key: $boulderKey")
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -1327,6 +1308,7 @@ object CastleWarsGameState {
|
|||
"saradomin2" -> saradominBoulder2State = state
|
||||
"zamorak1" -> zamorakBoulder1State = state
|
||||
"zamorak2" -> zamorakBoulder2State = state
|
||||
else -> throw IllegalArgumentException("Unknown Castle Wars boulder key: $boulderKey")
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -1561,7 +1543,7 @@ object CastleWarsGameState {
|
|||
player, CASTLE_WARS_TIMER_VARP, remainingMinutes
|
||||
)
|
||||
// Boulder varbits: 0 = collapsed/blocked (green/secure), 1 = cleared (red/open)
|
||||
val team = getPlayerTeam(player) ?: return
|
||||
val team = getActiveGameTeam(player) ?: return
|
||||
when (team) {
|
||||
CastleWarsTeam.SARADOMIN -> {
|
||||
setVarbit(player, SARA_DOOR_HEALTH_VARBIT, saradominDoorHealth)
|
||||
|
|
@ -1581,48 +1563,11 @@ object CastleWarsGameState {
|
|||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Get all players currently in the Castle Wars game area zones.
|
||||
* Uses RegionManager to find all players in the zone borders,
|
||||
* ensuring we reach players even if they weren't properly added to the tracked sets.
|
||||
* @return Set of all players in the game area who have a valid team
|
||||
*/
|
||||
/** Get all connected players tracked as active participants. */
|
||||
private fun getAllGamePlayers(): Set<Player> {
|
||||
val players = mutableSetOf<Player>()
|
||||
players.addAll((CastleWarsGameArea.saradominPlayers + CastleWarsGameArea.zamorakPlayers).filter {
|
||||
return (CastleWarsGameArea.saradominPlayers + CastleWarsGameArea.zamorakPlayers).filter {
|
||||
it.isActive && it.session != null
|
||||
})
|
||||
for (border in CastleWarsGameArea.areaBorders) {
|
||||
val centerX = (border.southWestX + border.northEastX) / 2
|
||||
val centerY = (border.southWestY + border.northEastY) / 2
|
||||
val plane = border.plane
|
||||
val radius = maxOf(
|
||||
kotlin.math.abs(border.northEastX - border.southWestX),
|
||||
kotlin.math.abs(border.northEastY - border.southWestY)
|
||||
) / 2 + 10 // Add buffer
|
||||
val centerLocation = Location.create(centerX, centerY, plane)
|
||||
for (player in RegionManager.getLocalPlayers(
|
||||
centerLocation, radius
|
||||
)) {
|
||||
if (border.insideBorder(player.location) && getPlayerTeam(player) != null) {
|
||||
players.add(player)
|
||||
}
|
||||
}
|
||||
}
|
||||
val saraRespawn = CastleWarsRespawnArea.saradominRespawnRoom
|
||||
val zamRespawn = CastleWarsRespawnArea.zamorakRespawnRoom
|
||||
for (respawnBorder in arrayOf(saraRespawn, zamRespawn)) {
|
||||
val centerX = (respawnBorder.southWestX + respawnBorder.northEastX) / 2
|
||||
val centerY = (respawnBorder.southWestY + respawnBorder.northEastY) / 2
|
||||
val plane = respawnBorder.plane
|
||||
val centerLocation = Location.create(centerX, centerY, plane)
|
||||
for (player in RegionManager.getLocalPlayers(centerLocation, 20)) {
|
||||
if (respawnBorder.insideBorder(player.location) && getPlayerTeam(player) != null) {
|
||||
players.add(player)
|
||||
}
|
||||
}
|
||||
}
|
||||
return players
|
||||
}.toSet()
|
||||
}
|
||||
|
||||
/**
|
||||
|
|
@ -1631,7 +1576,7 @@ object CastleWarsGameState {
|
|||
* @return Set of all players on the specified team
|
||||
*/
|
||||
private fun getTeamPlayers(team: CastleWarsTeam): Set<Player> {
|
||||
return getAllGamePlayers().filter { getPlayerTeam(it) == team }.toSet()
|
||||
return getAllGamePlayers().filter { getActiveGameTeam(it) == team }.toSet()
|
||||
}
|
||||
|
||||
/**
|
||||
|
|
@ -1743,8 +1688,8 @@ object CastleWarsGameState {
|
|||
sendMessage(player, "You can't steal the flag from yourself.")
|
||||
return false
|
||||
}
|
||||
val playerTeam = getPlayerTeam(player)
|
||||
val carrierTeam = getPlayerTeam(carrier)
|
||||
val playerTeam = getActiveGameTeam(player)
|
||||
val carrierTeam = getActiveGameTeam(carrier)
|
||||
if (playerTeam == null || carrierTeam == null) {
|
||||
sendMessage(player, "You can only steal flags in Castle Wars.")
|
||||
return false
|
||||
|
|
|
|||
|
|
@ -8,50 +8,24 @@ import core.game.node.entity.impl.PulseManager
|
|||
import core.game.node.entity.player.Player
|
||||
import core.game.system.timer.impl.Poison
|
||||
import core.game.world.map.Location
|
||||
import core.game.world.map.RegionManager.getLocalNPCs
|
||||
import core.game.world.map.path.Pathfinder
|
||||
import core.game.world.map.zone.*
|
||||
|
||||
/**
|
||||
* MapZone for Castle Wars game area that handles death and respawn.
|
||||
* This is registered in addition to the MapArea to provide death handling,
|
||||
* since MapArea doesn't support overriding the death method.
|
||||
* This is registered in addition to the MapArea to provide death handling.
|
||||
*/
|
||||
class CastleWarsGameZone : MapZone("CastleWarsGame", true, ZoneRestriction.RANDOM_EVENTS), StartupListener {
|
||||
|
||||
companion object {
|
||||
|
||||
private val saradominStandardFloor = ZoneBorders(Location.create(2426, 3073, 3), Location.create(2430, 3077, 3))
|
||||
private val zamorakStandardFloor = ZoneBorders(Location.create(2373, 3134, 3), Location.create(2369, 3130, 3))
|
||||
private val saradominUpperFloor = ZoneBorders(Location.create(2431, 3080, 2), Location.create(2423, 3072, 2))
|
||||
private val zamorakUpperFloor = ZoneBorders(Location.create(2368, 3127, 2), Location.create(2376, 3135, 2))
|
||||
private val saradominFloor = ZoneBorders(Location.create(2420, 3072, 1), Location.create(2431, 3083, 1))
|
||||
private val zamorakFloor = ZoneBorders(Location.create(2379, 3135, 1), Location.create(2368, 3124, 1))
|
||||
private val battleField = ZoneBorders(Location.create(2368, 3135, 0), Location.create(2431, 3072, 0))
|
||||
private val saradominTunnels = ZoneBorders(Location.create(2430, 9481, 0), Location.create(2385, 9504, 0))
|
||||
private val zamorakTunnels = ZoneBorders(Location.create(2401, 9503, 0), Location.create(2366, 9529, 0))
|
||||
private val centerTunnels = ZoneBorders(Location.create(2396, 9493, 0), Location.create(2405, 9515, 0))
|
||||
val allBorders = arrayOf(
|
||||
saradominStandardFloor,
|
||||
zamorakStandardFloor,
|
||||
saradominUpperFloor,
|
||||
zamorakUpperFloor,
|
||||
saradominFloor,
|
||||
zamorakFloor,
|
||||
battleField,
|
||||
saradominTunnels,
|
||||
zamorakTunnels,
|
||||
centerTunnels
|
||||
)
|
||||
|
||||
/**
|
||||
* Handle Castle Wars death with poison preservation.
|
||||
* Shared between CastleWarsGameZone.death() and CastleWarsGameArea.handleDeath().
|
||||
* In Castle Wars, poison persists through death unlike normal safe deaths.
|
||||
*/
|
||||
fun handleCastleWarsDeath(player: Player): Boolean {
|
||||
if (CastleWarsGameArea.ticksLeftInGame <= 0) return false
|
||||
val team = CastleWarsGameState.getPlayerTeam(player) ?: return false
|
||||
val team = CastleWarsGameState.getActiveGameTeam(player) ?: return false
|
||||
|
||||
if (CastleWarsGameState.isCarryingFlag(player)) {
|
||||
CastleWarsGameState.dropFlag(player)
|
||||
|
|
@ -83,7 +57,7 @@ class CastleWarsGameZone : MapZone("CastleWarsGame", true, ZoneRestriction.RANDO
|
|||
|
||||
override fun startup() {
|
||||
ZoneBuilder.configure(this)
|
||||
for (border in allBorders) {
|
||||
for (border in CastleWarsGameArea.areaBorders) {
|
||||
register(border)
|
||||
}
|
||||
}
|
||||
|
|
@ -103,7 +77,7 @@ class CastleWarsGameZone : MapZone("CastleWarsGame", true, ZoneRestriction.RANDO
|
|||
val isMovingUp = to.z > from.z
|
||||
val isChangingZLevel = to.z != from.z
|
||||
|
||||
for (n in getLocalNPCs(e.location, 5)) {
|
||||
for (n in findLocalNPCs(e.location, 5)) {
|
||||
if (n.isInvisible || n === e) {
|
||||
continue
|
||||
}
|
||||
|
|
|
|||
|
|
@ -8,7 +8,6 @@ import content.minigame.castlewars.CastleWarsConstants.BURNING_BARRICADE_MAX_TIC
|
|||
import content.minigame.castlewars.CastleWarsConstants.BURNING_BARRICADE_PULSE_INTERVAL
|
||||
import content.minigame.castlewars.CastleWarsConstants.EXPLOSIVE_POTION_SELF_DAMAGE
|
||||
import content.minigame.castlewars.CastleWarsConstants.MAX_BARRICADES_PER_TEAM
|
||||
import content.minigame.castlewars.areas.CastleWarsGameArea
|
||||
import core.api.*
|
||||
import core.game.interaction.Clocks
|
||||
import core.game.interaction.IntType
|
||||
|
|
@ -63,11 +62,10 @@ class CastleWarsItemHandler : InteractionListener {
|
|||
}
|
||||
|
||||
fun dealExplosiveAoeDamage(attacker: Player, location: Location, radius: Int = 1) {
|
||||
val attackerTeam = CastleWarsGameState.getPlayerTeam(attacker) ?: return
|
||||
val attackerTeam = CastleWarsGameState.getActiveGameTeam(attacker) ?: return
|
||||
RegionManager.getLocalPlayers(location, radius).filter { target ->
|
||||
target != attacker && target.isActive && CastleWarsGameState.getPlayerTeam(target) != null && CastleWarsGameState.getPlayerTeam(
|
||||
target
|
||||
) != attackerTeam
|
||||
val targetTeam = CastleWarsGameState.getActiveGameTeam(target)
|
||||
target != attacker && target.isActive && targetTeam != null && targetTeam != attackerTeam
|
||||
}.forEach { target ->
|
||||
val damage = CastleWarsConstants.EXPLOSIVE_POTION_SELF_DAMAGE.coerceAtMost(target.skills.lifepoints)
|
||||
target.impactHandler.manualHit(
|
||||
|
|
@ -100,7 +98,7 @@ class CastleWarsItemHandler : InteractionListener {
|
|||
return@on false
|
||||
}
|
||||
|
||||
val team = CastleWarsGameState.getPlayerTeam(player)
|
||||
val team = CastleWarsGameState.getActiveGameTeam(player)
|
||||
if (team == null) {
|
||||
sendMessage(player, "You're not in a Castle Wars game.")
|
||||
return@on false
|
||||
|
|
@ -143,7 +141,7 @@ class CastleWarsItemHandler : InteractionListener {
|
|||
) { player, used, with ->
|
||||
val barricade = with as? NPC ?: return@onUseWith false
|
||||
|
||||
if (!CastleWarsGameState.isGameActive) {
|
||||
if (!isActiveGamePlayer(player)) {
|
||||
return@onUseWith false
|
||||
}
|
||||
|
||||
|
|
@ -170,7 +168,7 @@ class CastleWarsItemHandler : InteractionListener {
|
|||
}
|
||||
|
||||
on(CastleWars.cwExplosivePotion, IntType.ITEM, "drop") { player, node ->
|
||||
if (!CastleWarsGameState.isGameActive) {
|
||||
if (!isActiveGamePlayer(player)) {
|
||||
return@on false
|
||||
}
|
||||
|
||||
|
|
@ -205,7 +203,7 @@ class CastleWarsItemHandler : InteractionListener {
|
|||
) { player, _, with ->
|
||||
val barricade = with as? NPC ?: return@onUseWith false
|
||||
|
||||
if (!CastleWarsGameState.isGameActive) {
|
||||
if (!isActiveGamePlayer(player)) {
|
||||
return@onUseWith false
|
||||
}
|
||||
|
||||
|
|
@ -225,7 +223,9 @@ class CastleWarsItemHandler : InteractionListener {
|
|||
val firePulse = object : core.game.system.task.Pulse(BURNING_BARRICADE_PULSE_INTERVAL) {
|
||||
var tickCount = 0
|
||||
override fun pulse(): Boolean {
|
||||
if (!barricade.isActive || barricade.id != NPCs.BARRICADE_1533 || tickCount >= BURNING_BARRICADE_MAX_TICKS) {
|
||||
if (!CastleWarsGameState.isGameActive || !barricade.isActive ||
|
||||
barricade.id != NPCs.BARRICADE_1533 || tickCount >= BURNING_BARRICADE_MAX_TICKS
|
||||
) {
|
||||
burningBarricadePulses.remove(barricade)
|
||||
if (barricade.isActive && barricade.id == NPCs.BARRICADE_1533) {
|
||||
barricade.skills.lifepoints = 0
|
||||
|
|
@ -249,7 +249,7 @@ class CastleWarsItemHandler : InteractionListener {
|
|||
onUseWith(IntType.NPC, Items.BUCKET_OF_WATER_1929, NPCs.BARRICADE_1533) { player, used, with ->
|
||||
val barricade = with as? NPC ?: return@onUseWith false
|
||||
|
||||
if (!CastleWarsGameState.isGameActive) {
|
||||
if (!isActiveGamePlayer(player)) {
|
||||
return@onUseWith false
|
||||
}
|
||||
|
||||
|
|
@ -276,8 +276,8 @@ class CastleWarsItemHandler : InteractionListener {
|
|||
sendMessage(user, "You can only use bandages during a Castle Wars game.")
|
||||
return false
|
||||
}
|
||||
val userTeam = getActiveGameTeam(user)
|
||||
val targetTeam = getActiveGameTeam(target)
|
||||
val userTeam = CastleWarsGameState.getActiveGameTeam(user)
|
||||
val targetTeam = CastleWarsGameState.getActiveGameTeam(target)
|
||||
if (userTeam == null || targetTeam == null) {
|
||||
sendMessage(user, "You can only use bandages during a Castle Wars game.")
|
||||
return false
|
||||
|
|
@ -328,12 +328,8 @@ class CastleWarsItemHandler : InteractionListener {
|
|||
return true
|
||||
}
|
||||
|
||||
private fun getActiveGameTeam(player: Player): CastleWarsTeam? {
|
||||
return when {
|
||||
CastleWarsGameArea.saradominPlayers.contains(player) -> CastleWarsTeam.SARADOMIN
|
||||
CastleWarsGameArea.zamorakPlayers.contains(player) -> CastleWarsTeam.ZAMORAK
|
||||
else -> null
|
||||
}
|
||||
private fun isActiveGamePlayer(player: Player): Boolean {
|
||||
return CastleWarsGameState.isGameActive && CastleWarsGameState.getActiveGameTeam(player) != null
|
||||
}
|
||||
|
||||
private fun getLocalBarricades(location: Location, radius: Int): List<NPC> {
|
||||
|
|
@ -341,6 +337,6 @@ class CastleWarsItemHandler : InteractionListener {
|
|||
NPCs.BARRICADE_1532, NPCs.BARRICADE_1533, NPCs.BARRICADE_1534, NPCs.BARRICADE_1535
|
||||
)
|
||||
|
||||
return RegionManager.getLocalNPCs(location, radius).filter { it.id in barricadeIds && it.isActive }
|
||||
return findLocalNPCs(location, radius).filter { it.id in barricadeIds && it.isActive }
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -25,7 +25,6 @@ import core.game.world.GameWorld
|
|||
import core.game.world.map.Direction
|
||||
import core.game.world.map.Location
|
||||
import core.game.world.map.RegionManager
|
||||
import core.game.world.update.flag.chunk.ObjectUpdateFlag
|
||||
import core.game.world.update.flag.context.Animation
|
||||
import core.tools.Log
|
||||
import core.tools.RandomFunction
|
||||
|
|
@ -246,20 +245,20 @@ class CastleWarsListeners : InteractionListener {
|
|||
onUseWith(
|
||||
SCENERY, CastleWars.cwClimbingRope, *CastleWars.cwCastleBattlementsMap.keys.toIntArray()
|
||||
) { player, rope, wall ->
|
||||
if (!requireActiveGamePlayer(player)) return@onUseWith false
|
||||
if (CastleWarsGameState.getClimbingRope(wall.location) != null) {
|
||||
sendDialogue(player, "There is already a rope tied to the battlements.")
|
||||
return@onUseWith true
|
||||
}
|
||||
removeItem(player, rope)
|
||||
val wallScenery = wall.asScenery()
|
||||
val ropeWall = wallScenery.transform(CastleWars.cwCastleBattlementsMap.getValue(wall.id))
|
||||
SceneryBuilder.replace(wallScenery, ropeWall, CastleWars.ropeAliveTicks, false)
|
||||
SceneryBuilder.replace(wallScenery, ropeWall, false, false)
|
||||
val hangingRope = Scenery(CastleWars.cwCastleClimbingRope, wall.location, 4, wall.direction.toInteger())
|
||||
CastleWarsGameState.registerClimbingRope(hangingRope, wallScenery)
|
||||
RegionManager.getRegionChunk(hangingRope.location).flag(ObjectUpdateFlag(hangingRope, false))
|
||||
CastleWarsGameState.registerClimbingRope(hangingRope, wallScenery, ropeWall)
|
||||
removeItem(player, rope)
|
||||
GameWorld.Pulser.submit(object : Pulse(CastleWars.ropeAliveTicks) {
|
||||
override fun pulse(): Boolean {
|
||||
CastleWarsGameState.removeClimbingRope(hangingRope.location)
|
||||
CastleWarsGameState.removeClimbingRope(hangingRope)
|
||||
return true
|
||||
}
|
||||
})
|
||||
|
|
@ -301,7 +300,7 @@ class CastleWarsListeners : InteractionListener {
|
|||
}
|
||||
|
||||
on(CastleWars.saradominEnergyBarrier, IntType.SCENERY, "Pass") { player, barrier ->
|
||||
val playerTeam = CastleWarsGameState.getPlayerTeam(player)
|
||||
val playerTeam = CastleWarsGameState.getActiveGameTeam(player)
|
||||
if (playerTeam != CastleWarsTeam.SARADOMIN) {
|
||||
sendMessage(player, "Only the Saradomin team may pass through this barrier.")
|
||||
return@on true
|
||||
|
|
@ -330,7 +329,7 @@ class CastleWarsListeners : InteractionListener {
|
|||
return@on true
|
||||
}
|
||||
on(CastleWars.zamorakEnergyBarrier, IntType.SCENERY, "Pass") { player, barrier ->
|
||||
val playerTeam = CastleWarsGameState.getPlayerTeam(player)
|
||||
val playerTeam = CastleWarsGameState.getActiveGameTeam(player)
|
||||
if (playerTeam != CastleWarsTeam.ZAMORAK) {
|
||||
sendMessage(player, "Only the Zamorak team may pass through this barrier.")
|
||||
return@on true
|
||||
|
|
@ -370,8 +369,8 @@ class CastleWarsListeners : InteractionListener {
|
|||
|
||||
on(CastleWars.largeDoorIds, IntType.SCENERY, "Open") { player, node ->
|
||||
val door = node.asScenery()
|
||||
val doorTeam = getDoorTeam(door.location)
|
||||
val playerTeam = CastleWarsGameState.getPlayerTeam(player)
|
||||
val doorTeam = getCastleTeam(door.location)
|
||||
val playerTeam = CastleWarsGameState.getActiveGameTeam(player)
|
||||
|
||||
if (playerTeam == null) {
|
||||
// Not in Castle Wars game - shouldn't happen in game area
|
||||
|
|
@ -404,8 +403,8 @@ class CastleWarsListeners : InteractionListener {
|
|||
|
||||
on(CastleWars.largeDoorOpenIds, IntType.SCENERY, "Close") { player, node ->
|
||||
val door = node.asScenery()
|
||||
val doorTeam = getDoorTeam(door.location)
|
||||
val playerTeam = CastleWarsGameState.getPlayerTeam(player)
|
||||
val doorTeam = getCastleTeam(door.location)
|
||||
val playerTeam = CastleWarsGameState.getActiveGameTeam(player)
|
||||
|
||||
if (playerTeam == null) {
|
||||
return@on false
|
||||
|
|
@ -429,8 +428,8 @@ class CastleWarsListeners : InteractionListener {
|
|||
|
||||
on(CastleWars.largeDoorIds, IntType.SCENERY, "Attack") { player, node ->
|
||||
val door = node.asScenery()
|
||||
val doorTeam = getDoorTeam(door.location)
|
||||
val playerTeam = CastleWarsGameState.getPlayerTeam(player)
|
||||
val doorTeam = getCastleTeam(door.location)
|
||||
val playerTeam = CastleWarsGameState.getActiveGameTeam(player)
|
||||
|
||||
if (playerTeam == null) {
|
||||
return@on false
|
||||
|
|
@ -454,8 +453,8 @@ class CastleWarsListeners : InteractionListener {
|
|||
SCENERY, CastleWars.cwExplosivePotion, *(CastleWars.largeDoorIds + CastleWars.largeDoorOpenIds)
|
||||
) { player, used, with ->
|
||||
val door = with.asScenery()
|
||||
val doorTeam = getDoorTeam(door.location)
|
||||
val playerTeam = CastleWarsGameState.getPlayerTeam(player)
|
||||
val doorTeam = getCastleTeam(door.location)
|
||||
val playerTeam = CastleWarsGameState.getActiveGameTeam(player)
|
||||
|
||||
if (playerTeam == null) {
|
||||
sendMessage(player, "You can only use this in Castle Wars.")
|
||||
|
|
@ -488,10 +487,10 @@ class CastleWarsListeners : InteractionListener {
|
|||
|
||||
onUseWith(
|
||||
SCENERY, CastleWars.cwToolkit, *(CastleWars.largeDoorIds + CastleWars.largeDoorOpenIds)
|
||||
) { player, used, with ->
|
||||
) { player, _, with ->
|
||||
val door = with.asScenery()
|
||||
val doorTeam = getDoorTeam(door.location)
|
||||
val playerTeam = CastleWarsGameState.getPlayerTeam(player)
|
||||
val doorTeam = getCastleTeam(door.location)
|
||||
val playerTeam = CastleWarsGameState.getActiveGameTeam(player)
|
||||
|
||||
if (playerTeam == null) {
|
||||
sendMessage(player, "You can only use this in Castle Wars.")
|
||||
|
|
@ -517,7 +516,7 @@ class CastleWarsListeners : InteractionListener {
|
|||
val door = node.asScenery()
|
||||
val doorTeam =
|
||||
if (door.id == CastleWars.saradominBackdoor) CastleWarsTeam.SARADOMIN else CastleWarsTeam.ZAMORAK
|
||||
val playerTeam = CastleWarsGameState.getPlayerTeam(player) ?: return@on false
|
||||
val playerTeam = CastleWarsGameState.getActiveGameTeam(player) ?: return@on false
|
||||
if (playerTeam == doorTeam) {
|
||||
openBackdoor(player, door)
|
||||
} else {
|
||||
|
|
@ -535,7 +534,7 @@ class CastleWarsListeners : InteractionListener {
|
|||
val door = node.asScenery()
|
||||
val doorTeam =
|
||||
if (door.id == CastleWars.saradominBackdoor) CastleWarsTeam.SARADOMIN else CastleWarsTeam.ZAMORAK
|
||||
val playerTeam = CastleWarsGameState.getPlayerTeam(player) ?: return@on false
|
||||
val playerTeam = CastleWarsGameState.getActiveGameTeam(player) ?: return@on false
|
||||
if (playerTeam == doorTeam) {
|
||||
sendMessage(player, "You don't need to pick the lock on your own door.")
|
||||
return@on true
|
||||
|
|
@ -568,7 +567,7 @@ class CastleWarsListeners : InteractionListener {
|
|||
val door = node.asScenery()
|
||||
val doorTeam = if (door.id == CastleWars.saradominBackdoorOpen) CastleWarsTeam.SARADOMIN
|
||||
else CastleWarsTeam.ZAMORAK
|
||||
val playerTeam = CastleWarsGameState.getPlayerTeam(player) ?: return@on false
|
||||
val playerTeam = CastleWarsGameState.getActiveGameTeam(player) ?: return@on false
|
||||
if (playerTeam != doorTeam) {
|
||||
sendMessage(player, "You can't close the enemy's door!")
|
||||
return@on true
|
||||
|
|
@ -579,6 +578,7 @@ class CastleWarsListeners : InteractionListener {
|
|||
}
|
||||
|
||||
on(CastleWars.tunnelRocks, IntType.SCENERY, "Mine") { player, node ->
|
||||
if (!requireActiveGamePlayer(player)) return@on true
|
||||
val rocks = node.asScenery()
|
||||
val boulderKey = CastleWarsGameState.getBoulderKey(rocks.location)
|
||||
if (boulderKey == null) {
|
||||
|
|
@ -590,6 +590,7 @@ class CastleWarsListeners : InteractionListener {
|
|||
}
|
||||
|
||||
on(CastleWars.tunnelRocksWeakened, IntType.SCENERY, "Mine") { player, node ->
|
||||
if (!requireActiveGamePlayer(player)) return@on true
|
||||
val rocks = node.asScenery()
|
||||
val boulderKey = CastleWarsGameState.getBoulderKey(rocks.location)
|
||||
if (boulderKey == null) {
|
||||
|
|
@ -601,6 +602,7 @@ class CastleWarsListeners : InteractionListener {
|
|||
}
|
||||
|
||||
onUseWith(SCENERY, CastleWars.cwExplosivePotion, CastleWars.tunnelRocks) { player, used, with ->
|
||||
if (!requireActiveGamePlayer(player)) return@onUseWith false
|
||||
val rocks = with.asScenery()
|
||||
val boulderKey = CastleWarsGameState.getBoulderKey(rocks.location)
|
||||
if (boulderKey == null) {
|
||||
|
|
@ -616,6 +618,7 @@ class CastleWarsListeners : InteractionListener {
|
|||
}
|
||||
|
||||
onUseWith(SCENERY, CastleWars.cwExplosivePotion, CastleWars.tunnelRocksWeakened) { player, used, with ->
|
||||
if (!requireActiveGamePlayer(player)) return@onUseWith false
|
||||
val rocks = with.asScenery()
|
||||
val boulderKey = CastleWarsGameState.getBoulderKey(rocks.location)
|
||||
if (boulderKey == null) {
|
||||
|
|
@ -631,6 +634,7 @@ class CastleWarsListeners : InteractionListener {
|
|||
}
|
||||
|
||||
onUseWith(SCENERY, CastleWars.cwExplosivePotion, CastleWars.caveWall) { player, used, with ->
|
||||
if (!requireActiveGamePlayer(player)) return@onUseWith false
|
||||
val wall = with.asScenery()
|
||||
val boulderKey = CastleWarsGameState.getNearestBoulderKey(wall.location)
|
||||
if (boulderKey == null) {
|
||||
|
|
@ -649,7 +653,8 @@ class CastleWarsListeners : InteractionListener {
|
|||
return@onUseWith true
|
||||
}
|
||||
|
||||
onUseWith(SCENERY, pickaxeIds, CastleWars.caveWall) { player, used, with ->
|
||||
onUseWith(SCENERY, pickaxeIds, CastleWars.caveWall) { player, _, with ->
|
||||
if (!requireActiveGamePlayer(player)) return@onUseWith false
|
||||
val wall = with.asScenery()
|
||||
val boulderKey = CastleWarsGameState.getNearestBoulderKey(wall.location)
|
||||
if (boulderKey == null) {
|
||||
|
|
@ -665,6 +670,7 @@ class CastleWarsListeners : InteractionListener {
|
|||
}
|
||||
|
||||
on(CastleWars.caveWall, IntType.SCENERY, "Collapse") { player, node ->
|
||||
if (!requireActiveGamePlayer(player)) return@on true
|
||||
val wall = node.asScenery()
|
||||
val boulderKey = CastleWarsGameState.getNearestBoulderKey(wall.location)
|
||||
if (boulderKey == null) {
|
||||
|
|
@ -681,8 +687,8 @@ class CastleWarsListeners : InteractionListener {
|
|||
|
||||
on(CastleWars.allCatapultIds, IntType.SCENERY, "Operate") { player, node ->
|
||||
val catapult = node.asScenery()
|
||||
val catapultTeam = getCatapultTeam(catapult.location)
|
||||
val playerTeam = CastleWarsGameState.getPlayerTeam(player)
|
||||
val catapultTeam = getCastleTeam(catapult.location)
|
||||
val playerTeam = CastleWarsGameState.getActiveGameTeam(player)
|
||||
if (playerTeam == null) {
|
||||
sendMessage(player, "You need to be in a Castle Wars game to use this!")
|
||||
return@on true
|
||||
|
|
@ -713,15 +719,15 @@ class CastleWarsListeners : InteractionListener {
|
|||
}
|
||||
setVarbit(player, CastleWarsGameState.VARBIT_CATAPULT_X, CatapultConfig.DEFAULT_X)
|
||||
setVarbit(player, CastleWarsGameState.VARBIT_CATAPULT_Y, CatapultConfig.DEFAULT_Y)
|
||||
player.setAttribute("catapult:location", catapult.location)
|
||||
player.setAttribute(CatapultConfig.ATTR_LOCATION, catapult.location)
|
||||
player.interfaceManager.open(Component(Components.CASTLEWARS_CATAPULT_54))
|
||||
return@on true
|
||||
}
|
||||
|
||||
onUseWith(SCENERY, org.rs09.consts.Items.TINDERBOX_590, *CastleWars.allCatapultIds) { player, _, with ->
|
||||
val catapult = with.asScenery()
|
||||
val catapultTeam = getCatapultTeam(catapult.location)
|
||||
val playerTeam = CastleWarsGameState.getPlayerTeam(player) ?: return@onUseWith false
|
||||
val catapultTeam = getCastleTeam(catapult.location)
|
||||
val playerTeam = CastleWarsGameState.getActiveGameTeam(player) ?: return@onUseWith false
|
||||
if (playerTeam == catapultTeam) {
|
||||
sendMessage(player, "You can't destroy your own catapult!")
|
||||
return@onUseWith false
|
||||
|
|
@ -753,8 +759,8 @@ class CastleWarsListeners : InteractionListener {
|
|||
|
||||
onUseWith(SCENERY, CastleWars.cwExplosivePotion, *CastleWars.allCatapultIds) { player, used, with ->
|
||||
val catapult = with.asScenery()
|
||||
val catapultTeam = getCatapultTeam(catapult.location)
|
||||
val playerTeam = CastleWarsGameState.getPlayerTeam(player) ?: return@onUseWith false
|
||||
val catapultTeam = getCastleTeam(catapult.location)
|
||||
val playerTeam = CastleWarsGameState.getActiveGameTeam(player) ?: return@onUseWith false
|
||||
if (playerTeam == catapultTeam) {
|
||||
sendMessage(player, "You can't destroy your own catapult!")
|
||||
return@onUseWith false
|
||||
|
|
@ -772,8 +778,8 @@ class CastleWarsListeners : InteractionListener {
|
|||
|
||||
onUseWith(SCENERY, Items.BUCKET_OF_WATER_1929, *CastleWars.allCatapultIds) { player, used, with ->
|
||||
val catapult = with.asScenery()
|
||||
val catapultTeam = getCatapultTeam(catapult.location)
|
||||
val playerTeam = CastleWarsGameState.getPlayerTeam(player) ?: return@onUseWith false
|
||||
val catapultTeam = getCastleTeam(catapult.location)
|
||||
val playerTeam = CastleWarsGameState.getActiveGameTeam(player) ?: return@onUseWith false
|
||||
if (playerTeam != catapultTeam) {
|
||||
sendMessage(player, "You can't help the enemy's catapult!")
|
||||
return@onUseWith false
|
||||
|
|
@ -789,10 +795,10 @@ class CastleWarsListeners : InteractionListener {
|
|||
return@onUseWith true
|
||||
}
|
||||
|
||||
onUseWith(SCENERY, CastleWars.cwToolkit, *CastleWars.allCatapultIds) { player, used, with ->
|
||||
onUseWith(SCENERY, CastleWars.cwToolkit, *CastleWars.allCatapultIds) { player, _, with ->
|
||||
val catapult = with.asScenery()
|
||||
val catapultTeam = getCatapultTeam(catapult.location)
|
||||
val playerTeam = CastleWarsGameState.getPlayerTeam(player) ?: return@onUseWith false
|
||||
val catapultTeam = getCastleTeam(catapult.location)
|
||||
val playerTeam = CastleWarsGameState.getActiveGameTeam(player) ?: return@onUseWith false
|
||||
if (playerTeam != catapultTeam) {
|
||||
sendMessage(player, "You can't repair the enemy's catapult!")
|
||||
return@onUseWith false
|
||||
|
|
@ -806,8 +812,8 @@ class CastleWarsListeners : InteractionListener {
|
|||
return@onUseWith true
|
||||
}
|
||||
|
||||
on(CastleWars.saradominTrapdoor, IntType.SCENERY, "Go-down") { player, node ->
|
||||
val playerTeam = CastleWarsGameState.getPlayerTeam(player)
|
||||
on(CastleWars.saradominTrapdoor, IntType.SCENERY, "Go-down") { player, _ ->
|
||||
val playerTeam = CastleWarsGameState.getActiveGameTeam(player)
|
||||
if (playerTeam != CastleWarsTeam.SARADOMIN) {
|
||||
sendMessage(player, "Only the Saradomin team may use this trapdoor.")
|
||||
return@on true
|
||||
|
|
@ -819,8 +825,8 @@ class CastleWarsListeners : InteractionListener {
|
|||
teleport(player, saradominTrapdoorDestination, TeleportManager.TeleportType.INSTANT)
|
||||
return@on true
|
||||
}
|
||||
on(CastleWars.zamorakTrapdoor, IntType.SCENERY, "Go-down") { player, node ->
|
||||
val playerTeam = CastleWarsGameState.getPlayerTeam(player)
|
||||
on(CastleWars.zamorakTrapdoor, IntType.SCENERY, "Go-down") { player, _ ->
|
||||
val playerTeam = CastleWarsGameState.getActiveGameTeam(player)
|
||||
if (playerTeam != CastleWarsTeam.ZAMORAK) {
|
||||
sendMessage(player, "Only the Zamorak team may use this trapdoor.")
|
||||
return@on true
|
||||
|
|
@ -840,7 +846,7 @@ class CastleWarsListeners : InteractionListener {
|
|||
player.properties.teleportLocation = destination
|
||||
return@on true
|
||||
}
|
||||
val playerTeam = CastleWarsGameState.getPlayerTeam(player)
|
||||
val playerTeam = CastleWarsGameState.getActiveGameTeam(player)
|
||||
if (playerTeam == null) {
|
||||
player.properties.teleportLocation = destination
|
||||
return@on true
|
||||
|
|
@ -917,12 +923,16 @@ class CastleWarsListeners : InteractionListener {
|
|||
AgilityHandler.forceWalk(player, -1, startTile, endTile, Animation.create(819), 12, 0.0, null)
|
||||
}
|
||||
|
||||
private fun getDoorTeam(location: Location): CastleWarsTeam {
|
||||
private fun getCastleTeam(location: Location): CastleWarsTeam {
|
||||
return if (location.y < 3100) CastleWarsTeam.SARADOMIN else CastleWarsTeam.ZAMORAK
|
||||
}
|
||||
|
||||
private fun getCatapultTeam(location: Location): CastleWarsTeam {
|
||||
return if (location.y < 3100) CastleWarsTeam.SARADOMIN else CastleWarsTeam.ZAMORAK
|
||||
private fun requireActiveGamePlayer(player: Player): Boolean {
|
||||
if (CastleWarsGameState.isGameActive && CastleWarsGameState.getActiveGameTeam(player) != null) {
|
||||
return true
|
||||
}
|
||||
sendMessage(player, "You can only do that during a Castle Wars game.")
|
||||
return false
|
||||
}
|
||||
|
||||
private fun getOpenDoorVariant(closedId: Int): Int? = when (closedId) {
|
||||
|
|
@ -933,14 +943,6 @@ class CastleWarsListeners : InteractionListener {
|
|||
else -> null
|
||||
}
|
||||
|
||||
private fun getClosedDoorVariant(openId: Int): Int? = when (openId) {
|
||||
4425 -> 4423
|
||||
4426 -> 4424
|
||||
4429 -> 4427
|
||||
4430 -> 4428
|
||||
else -> null
|
||||
}
|
||||
|
||||
private fun isPlayerOutsideFrontDoor(player: Player, door: Scenery, doorTeam: CastleWarsTeam): Boolean {
|
||||
val playerY = player.location.y
|
||||
val doorY = door.location.y
|
||||
|
|
@ -994,7 +996,7 @@ class CastleWarsListeners : InteractionListener {
|
|||
}
|
||||
|
||||
private fun leaveGameThroughPortal(player: Player, portalTeam: CastleWarsTeam) {
|
||||
if (CastleWarsGameState.getPlayerTeam(player) != portalTeam) {
|
||||
if (CastleWarsGameState.getActiveGameTeam(player) != portalTeam) {
|
||||
sendMessage(player, "Only the ${portalTeam.teamName} team may use this portal.")
|
||||
return
|
||||
}
|
||||
|
|
|
|||
|
|
@ -4,6 +4,7 @@ import content.minigame.castlewars.areas.CastleWarsGameArea
|
|||
import content.minigame.castlewars.areas.CastleWarsRespawnArea
|
||||
import content.minigame.castlewars.areas.CastleWarsWaitingArea
|
||||
import core.api.ShutdownListener
|
||||
import core.api.removeAttributes
|
||||
import core.game.node.entity.player.Player
|
||||
import core.game.world.map.Location
|
||||
import core.game.world.map.RegionManager
|
||||
|
|
@ -21,6 +22,9 @@ class CastleWarsShutdownHandler : ShutdownListener {
|
|||
allTrackedPlayers += CastleWarsGameArea.zamorakPlayers
|
||||
allTrackedPlayers += CastleWarsWaitingArea.waitingSaradominPlayers
|
||||
allTrackedPlayers += CastleWarsWaitingArea.waitingZamorakPlayers
|
||||
allTrackedPlayers += CastleWarsRespawnArea.saradominPlayersInRespawnRoom.keys
|
||||
allTrackedPlayers += CastleWarsRespawnArea.zamorakPlayersInRespawnRoom.keys
|
||||
allTrackedPlayers += CastleWarsGameArea.loggedOutPlayers.values.map { it.player }
|
||||
|
||||
val respawnBorders = arrayOf(
|
||||
CastleWarsRespawnArea.saradominRespawnRoom, CastleWarsRespawnArea.zamorakRespawnRoom
|
||||
|
|
@ -54,24 +58,38 @@ class CastleWarsShutdownHandler : ShutdownListener {
|
|||
|
||||
for (player in allTrackedPlayers) {
|
||||
if (CastleWarsGameState.isCarryingFlag(player)) {
|
||||
CastleWarsGameState.dropFlag(player)
|
||||
CastleWarsGameState.dropFlag(player, broadcast = false)
|
||||
} else if (CastleWarsGameState.isCarryingOwnFlag(player)) {
|
||||
CastleWarsGameState.dropOwnFlag(player)
|
||||
CastleWarsGameState.dropOwnFlag(player, broadcast = false)
|
||||
}
|
||||
CastleWarsGameState.restoreAndCleanupPlayer(player)
|
||||
player.location = CastleWars.lobbyBankArea.randomWalkableLoc
|
||||
val lobbyLocation = CastleWars.lobbyBankArea.randomWalkableLoc
|
||||
player.location = lobbyLocation
|
||||
player.properties.teleportLocation = lobbyLocation
|
||||
removeAttributes(
|
||||
player,
|
||||
CastleWarsConstants.ATTR_LOGOUT_TICK,
|
||||
CastleWarsConstants.ATTR_LOGOUT_TIME_MILLIS,
|
||||
CastleWarsConstants.ATTR_LOGOUT_TEAM,
|
||||
CastleWarsConstants.ATTR_LOGOUT_LOCATION,
|
||||
CastleWarsConstants.ATTR_JOIN_TICK
|
||||
)
|
||||
}
|
||||
|
||||
CastleWarsItemHandler.clearAllBarricades()
|
||||
|
||||
CastleWarsGameState.clearCastleWarsGroundItems()
|
||||
CastleWarsGameState.resetGame()
|
||||
CastleWarsGameArea.clearSubstitutionState()
|
||||
|
||||
CastleWarsGameArea.saradominPlayers.clear()
|
||||
CastleWarsGameArea.zamorakPlayers.clear()
|
||||
CastleWarsGameArea.loggedOutPlayers.clear()
|
||||
CastleWarsWaitingArea.waitingSaradominPlayers.clear()
|
||||
CastleWarsWaitingArea.waitingZamorakPlayers.clear()
|
||||
CastleWarsRespawnArea.saradominPlayersInRespawnRoom.clear()
|
||||
CastleWarsRespawnArea.zamorakPlayersInRespawnRoom.clear()
|
||||
|
||||
CastleWarsGameArea.ticksLeftInGame = -1
|
||||
CastleWarsWaitingArea.gameStartCountdown = -1
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -10,6 +10,7 @@ import core.tools.START_DIALOGUE
|
|||
class CastleWarsSubstitutionDialogue(private val team: CastleWarsTeam) : DialogueFile() {
|
||||
|
||||
override fun handle(componentID: Int, buttonID: Int) {
|
||||
val currentPlayer = player ?: return
|
||||
when (stage) {
|
||||
START_DIALOGUE -> showTopics(
|
||||
Topic("Yes.", ACCEPT, true),
|
||||
|
|
@ -18,14 +19,14 @@ class CastleWarsSubstitutionDialogue(private val team: CastleWarsTeam) : Dialogu
|
|||
)
|
||||
|
||||
ACCEPT -> {
|
||||
if (!CastleWarsGameArea.claimVacancy(player!!, team)) {
|
||||
sendMessage(player!!, "That Castle Wars vacancy is no longer available.")
|
||||
if (!CastleWarsGameArea.claimVacancy(currentPlayer, team)) {
|
||||
sendMessage(currentPlayer, "That Castle Wars vacancy is no longer available.")
|
||||
}
|
||||
stage = END_DIALOGUE
|
||||
}
|
||||
|
||||
DECLINE -> {
|
||||
CastleWarsGameArea.declineVacancyOffer(player!!, team)
|
||||
CastleWarsGameArea.declineVacancyOffer(currentPlayer, team)
|
||||
stage = END_DIALOGUE
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -29,8 +29,6 @@ class CastleWarsTunnelMiningPulse(
|
|||
|
||||
private const val REQUIRED_LEVEL = 1
|
||||
private const val RATE = 0.40
|
||||
private const val MINING_ANIMATION = 625
|
||||
|
||||
private const val EXPERIENCE = 0.0
|
||||
}
|
||||
|
||||
|
|
@ -65,15 +63,18 @@ class CastleWarsTunnelMiningPulse(
|
|||
}
|
||||
|
||||
private fun checkRequirements(): Boolean {
|
||||
if (!canContinueCastleWarsMining(player, rocks)) {
|
||||
return false
|
||||
}
|
||||
val expectedState = if (isWeakened) BoulderState.WEAKENED else BoulderState.COLLAPSED
|
||||
if (CastleWarsGameState.getBoulderState(boulderKey) != expectedState) {
|
||||
return false
|
||||
}
|
||||
val pickaxe = SkillingTool.getPickaxe(player)
|
||||
if (pickaxe == null) {
|
||||
sendMessage(player, "You need a pickaxe to mine these rocks.")
|
||||
return false
|
||||
}
|
||||
if (!rocks.isActive) {
|
||||
return false
|
||||
}
|
||||
|
||||
return true
|
||||
}
|
||||
|
||||
|
|
@ -159,6 +160,12 @@ class CastleWarsCaveWallMiningPulse(
|
|||
}
|
||||
|
||||
private fun checkRequirements(): Boolean {
|
||||
if (!canContinueCastleWarsMining(player, caveWall)) {
|
||||
return false
|
||||
}
|
||||
if (!CastleWarsGameState.isBoulderCleared(boulderKey)) {
|
||||
return false
|
||||
}
|
||||
val pickaxe = SkillingTool.getPickaxe(player)
|
||||
if (pickaxe == null) {
|
||||
sendMessage(player, "You need a pickaxe to mine the cave wall.")
|
||||
|
|
@ -198,3 +205,9 @@ class CastleWarsCaveWallMiningPulse(
|
|||
sendMessage(player, "You collapse the tunnel!")
|
||||
}
|
||||
}
|
||||
|
||||
private fun canContinueCastleWarsMining(player: Player, scenery: Scenery): Boolean {
|
||||
return player.isActive && CastleWarsGameState.isGameActive &&
|
||||
CastleWarsGameState.getActiveGameTeam(player) != null && scenery.isActive &&
|
||||
player.location.z == scenery.location.z && player.location.getDistance(scenery.location) <= 2.0
|
||||
}
|
||||
|
|
|
|||
|
|
@ -69,7 +69,7 @@ class CatapultFiringHandler {
|
|||
val speed = PROJECTILE_BASE_SPEED + (distance * PROJECTILE_SPEED_PER_TILE)
|
||||
val impactDelayTicks = (speed / 30).coerceAtLeast(1)
|
||||
|
||||
sendProjectile(catapultLocation, finalLocation)
|
||||
sendProjectile(catapultLocation, finalLocation, distance, speed)
|
||||
playFireEffects(player, finalLocation, impactDelayTicks)
|
||||
scheduleImpact(player, finalLocation, impactDelayTicks)
|
||||
|
||||
|
|
@ -88,10 +88,7 @@ class CatapultFiringHandler {
|
|||
return Location.create(finalX, finalY, 0)
|
||||
}
|
||||
|
||||
private fun sendProjectile(from: Location, to: Location) {
|
||||
val distance = from.getDistance(to).toInt()
|
||||
val speed = PROJECTILE_BASE_SPEED + (distance * PROJECTILE_SPEED_PER_TILE)
|
||||
|
||||
private fun sendProjectile(from: Location, to: Location, distance: Int, speed: Int) {
|
||||
Projectile.create(
|
||||
from,
|
||||
to,
|
||||
|
|
@ -139,6 +136,7 @@ class CatapultFiringHandler {
|
|||
}
|
||||
|
||||
private fun isValidTarget(target: Player): Boolean {
|
||||
return CastleWarsGameState.getPlayerTeam(target) != null
|
||||
return CastleWarsGameState.isGameActive && target.isActive &&
|
||||
CastleWarsGameState.getActiveGameTeam(target) != null
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,7 +1,5 @@
|
|||
package content.minigame.castlewars
|
||||
|
||||
import content.minigame.castlewars.CatapultConfig.DEFAULT_X
|
||||
import content.minigame.castlewars.CatapultConfig.DEFAULT_Y
|
||||
import content.minigame.castlewars.CatapultConfig.DIGIT_CHILD_IDS
|
||||
import content.minigame.castlewars.CatapultConfig.DIGIT_ITEM_IDS
|
||||
import content.minigame.castlewars.CatapultConfig.INTERFACE_ID
|
||||
|
|
@ -17,6 +15,7 @@ import core.game.node.entity.player.Player
|
|||
import core.net.packet.PacketRepository
|
||||
import core.net.packet.context.ChildPositionContext
|
||||
import core.net.packet.out.RepositionChild
|
||||
import kotlin.math.roundToInt
|
||||
|
||||
/**
|
||||
* Handles rendering the catapult interface including marker position and digit displays.
|
||||
|
|
@ -61,24 +60,13 @@ class CatapultInterfaceRenderer {
|
|||
sendDigitModel(player, DIGIT_CHILD_IDS[3], yOnes)
|
||||
}
|
||||
|
||||
/**
|
||||
* Resets the interface to default coordinates.
|
||||
* @param player The player whose interface to reset
|
||||
*/
|
||||
fun resetToDefaults(player: Player) {
|
||||
val defaultCoord = Coordinate(DEFAULT_X, DEFAULT_Y)
|
||||
renderCoordinate(player, defaultCoord)
|
||||
internal fun calculateScreenX(coordX: Int): Int {
|
||||
return MAP_BASE_X + (coordX.toDouble() / MAX_COORDINATE * MAP_WIDTH).roundToInt()
|
||||
}
|
||||
|
||||
private fun calculateScreenX(coordX: Int): Int {
|
||||
val pixelsPerUnit = MAP_WIDTH / MAX_COORDINATE
|
||||
return MAP_BASE_X + (coordX * pixelsPerUnit)
|
||||
}
|
||||
|
||||
private fun calculateScreenY(coordY: Int): Int {
|
||||
val pixelsPerUnit = MAP_HEIGHT / MAX_COORDINATE
|
||||
internal fun calculateScreenY(coordY: Int): Int {
|
||||
// Y is inverted - 0 is at the bottom, MAX is at the top
|
||||
return MAP_BASE_Y + ((MAX_COORDINATE - coordY) * pixelsPerUnit)
|
||||
return MAP_BASE_Y + ((MAX_COORDINATE - coordY).toDouble() / MAX_COORDINATE * MAP_HEIGHT).roundToInt()
|
||||
}
|
||||
|
||||
private fun sendDigitModel(player: Player, childId: Int, digit: Int) {
|
||||
|
|
|
|||
|
|
@ -81,10 +81,13 @@ class Lanthus : InteractionListener, TickListener {
|
|||
"your patron god, do you? Other than that, just have fun and enjoy it!"
|
||||
).also { stage++ }
|
||||
|
||||
50 -> npcl(
|
||||
FacialExpression.FRIENDLY, "Sure, here you go."
|
||||
).also { stage = END_DIALOGUE }
|
||||
.also { player.inventory.add(Item(Items.CASTLEWARS_MANUAL_4055)) }
|
||||
50 -> {
|
||||
if (addItem(player, Items.CASTLEWARS_MANUAL_4055)) {
|
||||
npcl(FacialExpression.FRIENDLY, "Sure, here you go.")
|
||||
} else {
|
||||
npcl(FacialExpression.FRIENDLY, "You don't have enough inventory space.")
|
||||
}.also { stage = END_DIALOGUE }
|
||||
}
|
||||
|
||||
83 -> player("Great! Oh, how do I win the game?").also { stage++ }
|
||||
84 -> npcl(
|
||||
|
|
|
|||
|
|
@ -10,9 +10,14 @@ import core.api.sendMessage
|
|||
import core.game.interaction.InteractionListener
|
||||
import core.game.node.entity.Entity
|
||||
import core.game.node.entity.player.Player
|
||||
import core.game.node.item.Item
|
||||
|
||||
abstract class CastleWarsArea : MapArea, LogoutListener, InteractionListener {
|
||||
|
||||
override fun canEquipItem(player: Player, item: Item, equipmentSlot: Int): Boolean? {
|
||||
return !CastleWarsEquipmentRestrictions.blocksEquip(player, equipmentSlot)
|
||||
}
|
||||
|
||||
override fun areaEnter(entity: Entity) {
|
||||
val player = entity as? Player ?: return
|
||||
CastleWarsEquipmentRestrictions.removeInvalidEquipment(player)
|
||||
|
|
@ -64,18 +69,11 @@ abstract class CastleWarsArea : MapArea, LogoutListener, InteractionListener {
|
|||
|
||||
override fun defineListeners() {
|
||||
onUnequip(intArrayOf(CastleWars.saradominTeamHoodedCloak, CastleWars.zamorakTeamHoodedCloak)) { player, _ ->
|
||||
val allCastleWarsBorders =
|
||||
CastleWarsGameArea.areaBorders.toList() + CastleWarsWaitingArea.areaBorders.toList() + listOf(
|
||||
CastleWarsRespawnArea.saradominRespawnRoom,
|
||||
CastleWarsRespawnArea.zamorakRespawnRoom
|
||||
)
|
||||
for (border in allCastleWarsBorders) {
|
||||
if (border.insideBorder(player)) {
|
||||
sendMessage(player, "You can't remove your team's colours")
|
||||
return@onUnequip false
|
||||
}
|
||||
if (CastleWarsEquipmentRestrictions.isInCastleWarsArea(player)) {
|
||||
sendMessage(player, "You can't remove your team's colours")
|
||||
return@onUnequip false
|
||||
}
|
||||
return@onUnequip true
|
||||
true
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -4,6 +4,7 @@ import content.minigame.castlewars.*
|
|||
import content.minigame.castlewars.CastleWarsConstants.PLAYER_CLEANUP_INTERVAL_TICKS
|
||||
import core.api.*
|
||||
import core.game.component.Component
|
||||
import core.game.container.impl.EquipmentContainer
|
||||
import core.game.event.EventHook
|
||||
import core.game.event.InterfaceCloseEvent
|
||||
import core.game.interaction.Option
|
||||
|
|
@ -18,6 +19,7 @@ import core.game.world.map.zone.ZoneBorders
|
|||
import core.game.world.map.zone.ZoneType
|
||||
import core.tools.Log
|
||||
import org.rs09.consts.Components
|
||||
import java.util.Locale
|
||||
|
||||
/**
|
||||
* Handles the Castle Wars game map
|
||||
|
|
@ -111,14 +113,6 @@ class CastleWarsGameArea : CastleWarsArea(), TickListener, LoginListener {
|
|||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Handle player death in Castle Wars - delegates to CastleWarsGameZone.
|
||||
* This ensures consistent death behavior including poison preservation.
|
||||
*/
|
||||
fun handleDeath(player: Player, killer: Entity?): Boolean {
|
||||
return CastleWarsGameZone.handleCastleWarsDeath(player)
|
||||
}
|
||||
|
||||
fun clearSubstitutionState() {
|
||||
activeVacancyOffers.clear()
|
||||
activeVacancyOfferTicks.clear()
|
||||
|
|
@ -263,9 +257,6 @@ class CastleWarsGameArea : CastleWarsArea(), TickListener, LoginListener {
|
|||
clearLogoutState(player)
|
||||
saradominPlayers.remove(player)
|
||||
zamorakPlayers.remove(player)
|
||||
CastleWarsGameState.clearHintIconForPlayer(player)
|
||||
player.interaction.remove(Option._P_ATTACK)
|
||||
player.interaction.remove(Option._P_STEAL_FROM)
|
||||
if (offerReplacement) {
|
||||
openVacancy(leavingTeam)
|
||||
fillVacancy(leavingTeam)
|
||||
|
|
@ -278,7 +269,7 @@ class CastleWarsGameArea : CastleWarsArea(), TickListener, LoginListener {
|
|||
}
|
||||
|
||||
fun playerKey(player: Player): String {
|
||||
return player.username.lowercase()
|
||||
return player.username.lowercase(Locale.ROOT)
|
||||
}
|
||||
|
||||
private fun setTeam(player: Player, team: CastleWarsTeam) {
|
||||
|
|
@ -334,8 +325,8 @@ class CastleWarsGameArea : CastleWarsArea(), TickListener, LoginListener {
|
|||
CastleWarsGameState.clearHintIconForPlayer(player)
|
||||
CastleWarsGameState.restoreAndCleanupPlayer(player)
|
||||
player.interaction.remove(Option._P_ATTACK)
|
||||
player.location = waitingRoomFor(team).randomWalkableLoc
|
||||
player.equipment.replace(Item(team.cloakId), 1)
|
||||
relocateImmediately(player, waitingRoomFor(team).randomWalkableLoc)
|
||||
player.equipment.replace(Item(team.cloakId), EquipmentContainer.SLOT_CAPE)
|
||||
waitingSetFor(team).add(player)
|
||||
clearLogoutAttributes(player)
|
||||
}
|
||||
|
|
@ -347,11 +338,17 @@ class CastleWarsGameArea : CastleWarsArea(), TickListener, LoginListener {
|
|||
player.interaction.remove(Option._P_ATTACK)
|
||||
player.interaction.remove(Option._P_STEAL_FROM)
|
||||
val lobbyLocation = CastleWars.lobbyBankArea.randomWalkableLoc
|
||||
player.location = lobbyLocation
|
||||
player.properties.teleportLocation = lobbyLocation
|
||||
relocateImmediately(player, lobbyLocation)
|
||||
clearLogoutState(player)
|
||||
}
|
||||
|
||||
@Suppress("DEPRECATION")
|
||||
private fun relocateImmediately(player: Player, destination: Location) {
|
||||
// Logout restoration may run before the player can process a normal teleport.
|
||||
player.location = destination
|
||||
player.properties.teleportLocation = destination
|
||||
}
|
||||
|
||||
fun restoreActiveLogoutOnLogin(player: Player) {
|
||||
val savedTeamName = getAttribute(player, CastleWarsConstants.ATTR_LOGOUT_TEAM, "")
|
||||
val savedLogoutTime = getAttribute(player, CastleWarsConstants.ATTR_LOGOUT_TIME_MILLIS, 0L)
|
||||
|
|
@ -372,7 +369,7 @@ class CastleWarsGameArea : CastleWarsArea(), TickListener, LoginListener {
|
|||
return
|
||||
}
|
||||
if (ticksLeftInGame > 0 && elapsedMillis < CastleWarsConstants.LOGOUT_GRACE_PERIOD_MILLIS) {
|
||||
player.location = Location.create(logoutLocation)
|
||||
relocateImmediately(player, Location.create(logoutLocation))
|
||||
setTeam(player, team)
|
||||
clearLogoutAttributes(player)
|
||||
return
|
||||
|
|
@ -384,7 +381,7 @@ class CastleWarsGameArea : CastleWarsArea(), TickListener, LoginListener {
|
|||
}
|
||||
}
|
||||
|
||||
object SaradominInterfaceCloseHook : EventHook<InterfaceCloseEvent> { // TODO: this might be useless.
|
||||
object SaradominInterfaceCloseHook : EventHook<InterfaceCloseEvent> {
|
||||
override fun process(entity: Entity, event: InterfaceCloseEvent) {
|
||||
val player = entity as? Player ?: return
|
||||
// Only restore if player is still in the game
|
||||
|
|
@ -398,7 +395,7 @@ class CastleWarsGameArea : CastleWarsArea(), TickListener, LoginListener {
|
|||
}
|
||||
}
|
||||
|
||||
object ZamorakInterfaceCloseHook : EventHook<InterfaceCloseEvent> { // TODO: this might be useless.
|
||||
object ZamorakInterfaceCloseHook : EventHook<InterfaceCloseEvent> {
|
||||
override fun process(entity: Entity, event: InterfaceCloseEvent) {
|
||||
val player = entity as? Player ?: return
|
||||
if (!zamorakPlayers.contains(player)) return
|
||||
|
|
@ -453,29 +450,36 @@ class CastleWarsGameArea : CastleWarsArea(), TickListener, LoginListener {
|
|||
override fun areaEnter(entity: Entity) {
|
||||
val player = entity as? Player ?: return
|
||||
|
||||
restoreActiveLogoutOnLogin(player)
|
||||
if (areaBorders.none { it.insideBorder(player.location) }) {
|
||||
return
|
||||
}
|
||||
val team = CastleWarsGameState.getActiveGameTeam(player)
|
||||
if (team == null || ticksLeftInGame <= 0) {
|
||||
log(this::class.java, Log.WARN, "${player.name} entered Castle Wars without an active match membership")
|
||||
player.properties.teleportLocation = CastleWars.lobbyBankArea.randomWalkableLoc
|
||||
return
|
||||
}
|
||||
|
||||
super.areaEnter(player)
|
||||
zone.zoneType = ZoneType.CASTLE_WARS.id
|
||||
registerTimer(
|
||||
player,
|
||||
spawnTimer("teleblock", CastleWars.gameTimeMinutes * CastleWarsConstants.TICKS_PER_MINUTE, false)
|
||||
)
|
||||
val team = CastleWarsGameState.getPlayerTeam(player)
|
||||
when (team) {
|
||||
CastleWarsTeam.SARADOMIN -> {
|
||||
player.interfaceManager.openOverlay(Component(Components.CASTLEWARS_STATUS_OVERLAY_SARADOMIN_58))
|
||||
player.equipment.replace(Item(CastleWars.saradominTeamHoodedCloak), 1)
|
||||
player.equipment.replace(Item(CastleWars.saradominTeamHoodedCloak), EquipmentContainer.SLOT_CAPE)
|
||||
player.hook(Event.InterfaceClosed, SaradominInterfaceCloseHook)
|
||||
}
|
||||
|
||||
CastleWarsTeam.ZAMORAK -> {
|
||||
player.interfaceManager.openOverlay(Component(Components.CASTLEWARS_STATUS_OVERLAY_ZAMORAK_59))
|
||||
player.equipment.replace(Item(CastleWars.zamorakTeamHoodedCloak), 1)
|
||||
player.equipment.replace(Item(CastleWars.zamorakTeamHoodedCloak), EquipmentContainer.SLOT_CAPE)
|
||||
player.hook(Event.InterfaceClosed, ZamorakInterfaceCloseHook)
|
||||
}
|
||||
|
||||
null -> {
|
||||
log(this::class.java, Log.WARN, "${player.name} entered Castle Wars but is NOT on a team?!")
|
||||
}
|
||||
}
|
||||
CastleWarsGameState.setActiveGamePlayerOptions(player)
|
||||
}
|
||||
|
|
@ -546,22 +550,8 @@ class CastleWarsGameArea : CastleWarsArea(), TickListener, LoginListener {
|
|||
}
|
||||
CastleWarsGameState.checkFlagCarriers()
|
||||
CastleWarsGameState.checkDroppedFlagTimeouts()
|
||||
for (player in saradominPlayers) {
|
||||
val currentOverlayId = player.interfaceManager.overlay?.id
|
||||
if (currentOverlayId == null || currentOverlayId != 58) {
|
||||
player.interfaceManager.openOverlay(Component(Components.CASTLEWARS_STATUS_OVERLAY_SARADOMIN_58))
|
||||
}
|
||||
// TODO: this... should not be done lmfao CRITICAL TODO: PROPER CW MATCH UI UPDATING THATS NOT SHIT
|
||||
CastleWarsGameState.updateOverlay(player)
|
||||
}
|
||||
for (player in zamorakPlayers) {
|
||||
val currentOverlayId = player.interfaceManager.overlay?.id
|
||||
if (currentOverlayId == null || currentOverlayId != 59) {
|
||||
player.interfaceManager.openOverlay(Component(Components.CASTLEWARS_STATUS_OVERLAY_ZAMORAK_59))
|
||||
}
|
||||
// TODO: this... should not be done lmfao CRITICAL TODO: PROPER CW MATCH UI UPDATING THATS NOT SHIT
|
||||
CastleWarsGameState.updateOverlay(player)
|
||||
}
|
||||
saradominPlayers.forEach { ensureGameOverlay(it, CastleWarsTeam.SARADOMIN) }
|
||||
zamorakPlayers.forEach { ensureGameOverlay(it, CastleWarsTeam.ZAMORAK) }
|
||||
if (ticksLeftInGame % PLAYER_CLEANUP_INTERVAL_TICKS == 0) {
|
||||
cleanupInactivePlayers()
|
||||
}
|
||||
|
|
@ -609,12 +599,15 @@ class CastleWarsGameArea : CastleWarsArea(), TickListener, LoginListener {
|
|||
CastleWarsGameState.cleanupInactiveHintIcons()
|
||||
}
|
||||
|
||||
/**
|
||||
* Handle death in Castle Wars - respawn at team base instead of normal death
|
||||
*/
|
||||
fun death(entity: Entity, killer: Entity?): Boolean {
|
||||
if (entity !is Player) return false
|
||||
return handleDeath(entity, killer)
|
||||
private fun ensureGameOverlay(player: Player, team: CastleWarsTeam) {
|
||||
val overlayId = when (team) {
|
||||
CastleWarsTeam.SARADOMIN -> Components.CASTLEWARS_STATUS_OVERLAY_SARADOMIN_58
|
||||
CastleWarsTeam.ZAMORAK -> Components.CASTLEWARS_STATUS_OVERLAY_ZAMORAK_59
|
||||
}
|
||||
if (player.interfaceManager.overlay?.id != overlayId) {
|
||||
player.interfaceManager.openOverlay(Component(overlayId))
|
||||
CastleWarsGameState.updateOverlay(player)
|
||||
}
|
||||
}
|
||||
|
||||
override fun isPvpAllowed(entity: Entity, target: Node?, style: CombatStyle?, message: Boolean): Boolean? {
|
||||
|
|
@ -623,8 +616,8 @@ class CastleWarsGameArea : CastleWarsArea(), TickListener, LoginListener {
|
|||
if (ticksLeftInGame <= 0) {
|
||||
return false
|
||||
}
|
||||
val attackerTeam = CastleWarsGameState.getPlayerTeam(attacker) ?: return false
|
||||
val victimTeam = CastleWarsGameState.getPlayerTeam(victim) ?: return false
|
||||
val attackerTeam = CastleWarsGameState.getActiveGameTeam(attacker) ?: return false
|
||||
val victimTeam = CastleWarsGameState.getActiveGameTeam(victim) ?: return false
|
||||
if (attackerTeam == victimTeam) {
|
||||
if (message) {
|
||||
sendMessage(attacker, "You can only attack players on the opposite team!")
|
||||
|
|
@ -634,4 +627,15 @@ class CastleWarsGameArea : CastleWarsArea(), TickListener, LoginListener {
|
|||
return true
|
||||
}
|
||||
|
||||
override fun modifyOutgoingHitInArea(attacker: Entity, target: Entity, hit: Int): Int? {
|
||||
val attackingPlayer = attacker as? Player ?: return null
|
||||
val victim = target as? Player ?: return null
|
||||
if (!CastleWarsGameState.isGameActive || CastleWarsGameState.getActiveGameTeam(attackingPlayer) == null ||
|
||||
CastleWarsGameState.getActiveGameTeam(victim) == null
|
||||
) {
|
||||
return null
|
||||
}
|
||||
return CastleWarsGameState.applyBraceletDamageBoost(attackingPlayer, victim, hit)
|
||||
}
|
||||
|
||||
}
|
||||
|
|
|
|||
|
|
@ -3,11 +3,13 @@ package content.minigame.castlewars.areas
|
|||
import content.minigame.castlewars.CastleWars
|
||||
import content.minigame.castlewars.CastleWarsConstants
|
||||
import content.minigame.castlewars.CastleWarsGameState
|
||||
import content.minigame.castlewars.CastleWarsTeam
|
||||
import core.api.TickListener
|
||||
import core.api.getWorldTicks
|
||||
import core.api.sendMessage
|
||||
import core.game.node.entity.Entity
|
||||
import core.game.node.entity.player.Player
|
||||
import core.game.container.impl.EquipmentContainer
|
||||
import core.game.node.item.Item
|
||||
import core.game.world.map.Location
|
||||
import core.game.world.map.zone.ZoneBorders
|
||||
|
|
@ -33,6 +35,11 @@ class CastleWarsRespawnArea : CastleWarsArea(), TickListener {
|
|||
|
||||
override fun areaEnter(entity: Entity) {
|
||||
val player = entity as? Player ?: return
|
||||
val team = CastleWarsGameState.getActiveGameTeam(player)
|
||||
if (team == null || CastleWarsGameArea.ticksLeftInGame <= 0) {
|
||||
player.properties.teleportLocation = CastleWars.lobbyBankArea.randomWalkableLoc
|
||||
return
|
||||
}
|
||||
super.areaEnter(player)
|
||||
if (CastleWarsGameState.isCarryingAnyFlag(player)) {
|
||||
if (CastleWarsGameState.isCarryingFlag(player)) {
|
||||
|
|
@ -42,12 +49,24 @@ class CastleWarsRespawnArea : CastleWarsArea(), TickListener {
|
|||
}
|
||||
sendMessage(player, "You cannot take the flag into your respawn room!")
|
||||
}
|
||||
if (saradominRespawnRoom.insideBorder(player.location)) {
|
||||
player.equipment.replace(Item(CastleWars.saradominTeamHoodedCloak), 1)
|
||||
saradominPlayersInRespawnRoom[player] = getWorldTicks()
|
||||
} else if (zamorakRespawnRoom.insideBorder(player.location)) {
|
||||
player.equipment.replace(Item(CastleWars.zamorakTeamHoodedCloak), 1)
|
||||
zamorakPlayersInRespawnRoom[player] = getWorldTicks()
|
||||
when (team) {
|
||||
CastleWarsTeam.SARADOMIN -> {
|
||||
if (!saradominRespawnRoom.insideBorder(player.location)) {
|
||||
player.properties.teleportLocation = saradominRespawnRoom.randomWalkableLoc
|
||||
return
|
||||
}
|
||||
player.equipment.replace(Item(team.cloakId), EquipmentContainer.SLOT_CAPE)
|
||||
saradominPlayersInRespawnRoom[player] = getWorldTicks()
|
||||
}
|
||||
|
||||
CastleWarsTeam.ZAMORAK -> {
|
||||
if (!zamorakRespawnRoom.insideBorder(player.location)) {
|
||||
player.properties.teleportLocation = zamorakRespawnRoom.randomWalkableLoc
|
||||
return
|
||||
}
|
||||
player.equipment.replace(Item(team.cloakId), EquipmentContainer.SLOT_CAPE)
|
||||
zamorakPlayersInRespawnRoom[player] = getWorldTicks()
|
||||
}
|
||||
}
|
||||
CastleWarsGameState.setActiveGamePlayerOptions(player)
|
||||
}
|
||||
|
|
@ -82,7 +101,7 @@ class CastleWarsRespawnArea : CastleWarsArea(), TickListener {
|
|||
}
|
||||
saradominPlayersInRespawnRoom.remove(player)
|
||||
zamorakPlayersInRespawnRoom.remove(player)
|
||||
if (CastleWarsGameArea.ticksLeftInGame > 0 && CastleWarsGameState.getPlayerTeam(player) != null) {
|
||||
if (CastleWarsGameArea.ticksLeftInGame > 0 && CastleWarsGameState.getActiveGameTeam(player) != null) {
|
||||
CastleWarsGameArea().logout(player)
|
||||
return
|
||||
}
|
||||
|
|
|
|||
|
|
@ -8,6 +8,7 @@ import core.api.*
|
|||
import core.game.component.Component
|
||||
import core.game.node.entity.Entity
|
||||
import core.game.node.entity.player.Player
|
||||
import core.game.container.impl.EquipmentContainer
|
||||
import core.game.node.item.Item
|
||||
import core.game.world.map.zone.ZoneBorders
|
||||
import org.rs09.consts.Components
|
||||
|
|
@ -50,11 +51,11 @@ class CastleWarsWaitingArea : CastleWarsArea(), TickListener {
|
|||
|
||||
// Set team attribute and equip the hooded cloak on the entity based on which waiting room they're in
|
||||
val joinedTeam = if (zamorakWaitingRoom.insideBorder(player.location)) {
|
||||
player.equipment.replace(Item(CastleWars.zamorakTeamHoodedCloak), 1)
|
||||
player.equipment.replace(Item(CastleWars.zamorakTeamHoodedCloak), EquipmentContainer.SLOT_CAPE)
|
||||
waitingZamorakPlayers.add(player)
|
||||
CastleWarsTeam.ZAMORAK
|
||||
} else if (saradominWaitingRoom.insideBorder(player.location)) {
|
||||
player.equipment.replace(Item(CastleWars.saradominTeamHoodedCloak), 1)
|
||||
player.equipment.replace(Item(CastleWars.saradominTeamHoodedCloak), EquipmentContainer.SLOT_CAPE)
|
||||
waitingSaradominPlayers.add(player)
|
||||
CastleWarsTeam.SARADOMIN
|
||||
} else {
|
||||
|
|
|
|||
|
|
@ -3,6 +3,8 @@ package core.api
|
|||
import core.game.node.Node
|
||||
import core.game.node.entity.Entity
|
||||
import core.game.node.entity.combat.CombatStyle
|
||||
import core.game.node.entity.player.Player
|
||||
import core.game.node.item.Item
|
||||
import core.game.world.map.Location
|
||||
import core.game.world.map.zone.MapZone
|
||||
import core.game.world.map.zone.ZoneBorders
|
||||
|
|
@ -32,12 +34,17 @@ interface MapArea : ContentInterface {
|
|||
* Return null if you want the default check in MapZone. (default)
|
||||
**/
|
||||
fun isPvpAllowed(entity: Entity, target: Node?, style: CombatStyle?, message: Boolean): Boolean? { return null }
|
||||
|
||||
/**
|
||||
* Extends MapZone to cater to pvp, team games and all other unique targeting systems.
|
||||
* Return true/false if you want to allow the deaths, especially false for "safe" areas.
|
||||
* Return null if you want the default check in MapZone. (default)
|
||||
* Allows an area to reject equipment changes without coupling core equipment code
|
||||
* to a particular piece of content.
|
||||
**/
|
||||
fun canStartDeath(entity: Entity, killer: Entity?): Boolean? { return null }
|
||||
fun canEquipItem(player: Player, item: Item, equipmentSlot: Int): Boolean? { return null }
|
||||
|
||||
/**
|
||||
* Allows an area to modify damage dealt by an entity inside it.
|
||||
*/
|
||||
fun modifyOutgoingHitInArea(attacker: Entity, target: Entity, hit: Int): Int? { return null }
|
||||
|
||||
companion object {
|
||||
val zoneMaps = HashMap<String, MapZone>()
|
||||
|
|
|
|||
|
|
@ -2,7 +2,6 @@ package core.game.container.impl;
|
|||
|
||||
import content.global.skill.skillcapeperks.SkillcapeEquipmentPlugin;
|
||||
import content.global.skill.skillcapeperks.SkillcapePerks;
|
||||
import content.minigame.castlewars.CastleWars;
|
||||
import core.game.container.Container;
|
||||
import core.game.container.ContainerEvent;
|
||||
import core.game.container.ContainerListener;
|
||||
|
|
@ -151,7 +150,7 @@ public final class EquipmentContainer extends Container {
|
|||
return false;
|
||||
}
|
||||
ArrayList<Item> itemsToMoveToInventory = new ArrayList<>(current);
|
||||
itemsToMoveToInventory.removeIf(item -> isCastleWarsFlag(item) && !containsItem(item));
|
||||
itemsToMoveToInventory.removeIf(item -> canUnequipWithoutInventorySpace(item) && !containsItem(item));
|
||||
if(itemsToMoveToInventory.isEmpty()) {
|
||||
return true;
|
||||
}
|
||||
|
|
@ -191,7 +190,7 @@ public final class EquipmentContainer extends Container {
|
|||
int neededSlots = 0;
|
||||
|
||||
for(Item item : current) {
|
||||
if(isCastleWarsFlag(item)) {
|
||||
if(canUnequipWithoutInventorySpace(item)) {
|
||||
continue;
|
||||
}
|
||||
if(!item.getDefinition().isStackable()) {
|
||||
|
|
@ -205,8 +204,8 @@ public final class EquipmentContainer extends Container {
|
|||
return neededSlots;
|
||||
}
|
||||
|
||||
private boolean isCastleWarsFlag(Item item) {
|
||||
return item.getId() == CastleWars.saradominFlag || item.getId() == CastleWars.zamorakFlag;
|
||||
private boolean canUnequipWithoutInventorySpace(Item item) {
|
||||
return item.getDefinition().getConfiguration(ItemConfigParser.UNEQUIP_WITHOUT_INVENTORY_SPACE, false);
|
||||
}
|
||||
|
||||
@Nullable
|
||||
|
|
@ -230,7 +229,7 @@ public final class EquipmentContainer extends Container {
|
|||
if (plugin != null) {
|
||||
Object object = plugin.fireEvent("unequip", player, currentItem);
|
||||
if (object != null && !((Boolean) object)) {
|
||||
if (isCastleWarsFlag(currentItem) && !containsItem(currentItem)) {
|
||||
if (canUnequipWithoutInventorySpace(currentItem) && !containsItem(currentItem)) {
|
||||
continue;
|
||||
}
|
||||
canContinue = false;
|
||||
|
|
@ -241,7 +240,7 @@ public final class EquipmentContainer extends Container {
|
|||
canContinue = InteractionListeners.run(currentItem.getId(), player, currentItem, false);
|
||||
|
||||
if(!canContinue) {
|
||||
if (isCastleWarsFlag(currentItem) && !containsItem(currentItem)) {
|
||||
if (canUnequipWithoutInventorySpace(currentItem) && !containsItem(currentItem)) {
|
||||
canContinue = true;
|
||||
continue;
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,7 +1,5 @@
|
|||
package core.game.global.action
|
||||
|
||||
import content.minigame.castlewars.CastleWars
|
||||
import content.minigame.castlewars.CastleWarsEquipmentRestrictions
|
||||
import core.game.event.ItemEquipEvent
|
||||
import core.game.event.ItemUnequipEvent
|
||||
import core.game.container.impl.EquipmentContainer
|
||||
|
|
@ -12,12 +10,14 @@ import core.game.node.Node
|
|||
import core.game.node.entity.combat.equipment.WeaponInterface
|
||||
import core.game.node.entity.player.Player
|
||||
import core.game.node.entity.player.link.audio.Audio
|
||||
import core.game.node.item.Item
|
||||
import core.plugin.Plugin
|
||||
import core.game.interaction.InteractionListener
|
||||
import core.game.interaction.IntType
|
||||
import core.game.interaction.InteractionListeners
|
||||
import core.game.system.config.ItemConfigParser
|
||||
import org.rs09.consts.Sounds
|
||||
import java.util.Locale
|
||||
|
||||
/**
|
||||
* Represents the equipment equipping handler plugin.
|
||||
|
|
@ -37,20 +37,20 @@ class EquipHandler : InteractionListener {
|
|||
val item = node.asItem()
|
||||
val itemEquipmentSlot = item.definition.getConfiguration<Int>(ItemConfigParser.EQUIP_SLOT, -1)
|
||||
|
||||
if (CastleWarsEquipmentRestrictions.blocksEquip(player, itemEquipmentSlot)) {
|
||||
if (!player.zoneMonitor.canEquip(player, item, itemEquipmentSlot)) {
|
||||
return
|
||||
}
|
||||
|
||||
val currentEquippedItem = player.equipment[itemEquipmentSlot]
|
||||
if (item == null || currentEquippedItem == item || item.name.toLowerCase().contains("goblin mail")) {
|
||||
if (currentEquippedItem == item || item.name.lowercase(Locale.ROOT).contains("goblin mail")) {
|
||||
return
|
||||
}
|
||||
|
||||
if(currentEquippedItem != null){
|
||||
if(!InteractionListeners.run(currentEquippedItem.id, player, currentEquippedItem, false)){
|
||||
val droppedCastleWarsFlag =
|
||||
isCastleWarsFlag(currentEquippedItem.id) && player.equipment[itemEquipmentSlot] == null
|
||||
if (!droppedCastleWarsFlag) {
|
||||
val listenerRemovedItem =
|
||||
canUnequipWithoutInventorySpace(currentEquippedItem) && player.equipment[itemEquipmentSlot] == null
|
||||
if (!listenerRemovedItem) {
|
||||
return
|
||||
}
|
||||
}
|
||||
|
|
@ -128,7 +128,7 @@ class EquipHandler : InteractionListener {
|
|||
if (slot == EquipmentContainer.SLOT_WEAPON) {
|
||||
player.packetDispatch.sendString("", 92, 0)
|
||||
}
|
||||
if (!isCastleWarsFlag(item.id)) {
|
||||
if (!canUnequipWithoutInventorySpace(item)) {
|
||||
val maximumAdd = player.inventory.getMaximumAdd(item)
|
||||
if (maximumAdd < item.amount) {
|
||||
player.packetDispatch.sendMessage("Not enough free space in your inventory.")
|
||||
|
|
@ -138,10 +138,16 @@ class EquipHandler : InteractionListener {
|
|||
val plugin = item.definition.getConfiguration<Plugin<Any>>("equipment", null)
|
||||
if (plugin != null) {
|
||||
if (!(plugin.fireEvent("unequip", player, item) as Boolean)) {
|
||||
if (canUnequipWithoutInventorySpace(item) && player.equipment[slot] == null) {
|
||||
return
|
||||
}
|
||||
return
|
||||
}
|
||||
}
|
||||
if (!InteractionListeners.run(itemId, player, item, false)) {
|
||||
if (canUnequipWithoutInventorySpace(item) && player.equipment[slot] == null) {
|
||||
return
|
||||
}
|
||||
return
|
||||
}
|
||||
val maximumAdd = player.inventory.getMaximumAdd(item)
|
||||
|
|
@ -163,8 +169,8 @@ class EquipHandler : InteractionListener {
|
|||
}
|
||||
}
|
||||
|
||||
private fun isCastleWarsFlag(itemId: Int): Boolean {
|
||||
return itemId == CastleWars.saradominFlag || itemId == CastleWars.zamorakFlag
|
||||
private fun canUnequipWithoutInventorySpace(item: Item): Boolean {
|
||||
return item.definition.getConfiguration(ItemConfigParser.UNEQUIP_WITHOUT_INVENTORY_SPACE, false)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -23,7 +23,6 @@ import core.game.world.update.flag.context.Animation
|
|||
import core.tools.RandomFunction
|
||||
import core.game.system.config.ItemConfigParser
|
||||
import core.tools.Log
|
||||
import content.minigame.castlewars.CastleWarsGameState
|
||||
import org.rs09.consts.Sounds
|
||||
import java.util.*
|
||||
import kotlin.math.floor
|
||||
|
|
@ -587,7 +586,7 @@ abstract class CombatSwingHandler(var type: CombatStyle?) {
|
|||
hit += (hit.toDouble() * 0.15).toInt()
|
||||
}
|
||||
if (victim is Player) {
|
||||
hit = CastleWarsGameState.applyBraceletDamageBoost(player, victim.asPlayer(), hit)
|
||||
hit = player.zoneMonitor.modifyOutgoingHit(player, victim, hit)
|
||||
}
|
||||
}
|
||||
if (attacker is Familiar && victim is Player) {
|
||||
|
|
|
|||
|
|
@ -1,7 +1,7 @@
|
|||
package core.game.node.entity.combat
|
||||
|
||||
import core.game.node.entity.Entity
|
||||
import core.game.world.map.Location
|
||||
import core.game.world.map.zone.MapZone
|
||||
|
||||
/**
|
||||
* Stores the state at the moment death commenced.
|
||||
|
|
@ -16,18 +16,11 @@ import core.game.world.map.Location
|
|||
* @param deathLocation The location where death commenced
|
||||
* @param zoneType The zone type ID at death time (from ZoneMonitor.getType())
|
||||
* @param isSafeZone Whether the player was in a safe zone at death time
|
||||
* @param wasInWilderness Whether the player was in wilderness at death time
|
||||
* @param wasSkulled Whether the player was skulled at death time
|
||||
* @param killer The entity that killed the player (may be null for environmental deaths)
|
||||
* @param deathTick The game tick when death commenced
|
||||
* @param deathZones Snapshot of the zone handlers active when death commenced
|
||||
*/
|
||||
data class DeathContext(
|
||||
val deathLocation: Location,
|
||||
val zoneType: Int,
|
||||
val isSafeZone: Boolean,
|
||||
val wasInWilderness: Boolean,
|
||||
val wasSkulled: Boolean,
|
||||
val killer: Entity?,
|
||||
val deathTick: Int
|
||||
val deathZones: List<MapZone>
|
||||
)
|
||||
|
||||
|
|
|
|||
|
|
@ -49,6 +49,8 @@ import core.game.world.map.build.DynamicRegion;
|
|||
import core.game.world.map.path.Pathfinder;
|
||||
import core.game.world.map.zone.ZoneRestriction;
|
||||
import core.game.world.map.zone.ZoneType;
|
||||
import core.game.world.map.zone.MapZone;
|
||||
import core.game.world.map.zone.RegionZone;
|
||||
import core.game.node.entity.combat.DeathContext;
|
||||
import core.game.world.update.flag.PlayerFlags;
|
||||
import core.game.world.update.flag.*;
|
||||
|
|
@ -87,6 +89,7 @@ import core.worker.ManagementEvents;
|
|||
import core.game.world.update.flag.context.*;
|
||||
|
||||
import java.util.*;
|
||||
import java.util.stream.Collectors;
|
||||
import java.util.concurrent.TimeUnit;
|
||||
|
||||
import static core.api.ContentAPIKt.*;
|
||||
|
|
@ -612,13 +615,13 @@ public class Player extends Entity {
|
|||
public void commenceDeath(Entity killer) {
|
||||
if (!isPlaying()) return;
|
||||
DeathContext context = new DeathContext(
|
||||
getLocation(),
|
||||
Location.create(getLocation()),
|
||||
getZoneMonitor().getType(),
|
||||
getProperties().isSafeZone(),
|
||||
getSkullManager().isWilderness(),
|
||||
getSkullManager().isSkulled(),
|
||||
killer,
|
||||
GameWorld.getTicks()
|
||||
getZoneMonitor().getZones().stream()
|
||||
.map(RegionZone::getZone)
|
||||
.distinct()
|
||||
.collect(Collectors.toList())
|
||||
);
|
||||
setAttribute("death-context", context);
|
||||
super.commenceDeath(killer);
|
||||
|
|
@ -655,7 +658,9 @@ public class Player extends Entity {
|
|||
incrementAttribute("/save:"+STATS_BASE+":"+STATS_DEATHS);
|
||||
|
||||
packetDispatch.sendTempMusic(90);
|
||||
boolean zoneHandledDeath = getZoneMonitor().handleDeath(killer);
|
||||
boolean zoneHandledDeath = deathContext != null
|
||||
? deathContext.getDeathZones().stream().anyMatch(zone -> zone.death(this, killer))
|
||||
: getZoneMonitor().handleDeath(killer);
|
||||
if (!zoneHandledDeath && (!wasSafeZone && deathZoneType != ZoneType.SAFE.getId()) && getDetails().getRights() != Rights.ADMINISTRATOR) {
|
||||
//If player was a Hardcore Ironman, announce that they died
|
||||
if (this.getIronmanManager().getMode().equals(IronmanMode.HARDCORE)) { //if this was checkRestriction, ultimate irons would be moved to HARDCORE_DEAD as well
|
||||
|
|
|
|||
|
|
@ -227,6 +227,12 @@ class ItemConfigParser {
|
|||
*/
|
||||
const val CASTLE_WARS_TICKET_PRICE = "castle_wars_ticket_price"
|
||||
|
||||
/**
|
||||
* Whether an unequip listener may remove the item without first requiring
|
||||
* enough inventory space to hold it.
|
||||
*/
|
||||
const val UNEQUIP_WITHOUT_INVENTORY_SPACE = "unequip_without_inventory_space"
|
||||
|
||||
}
|
||||
|
||||
val parser = JSONParser()
|
||||
|
|
@ -264,6 +270,7 @@ class ItemConfigParser {
|
|||
"fun_weapon",
|
||||
"rare_item",
|
||||
"bankable",
|
||||
UNEQUIP_WITHOUT_INVENTORY_SPACE,
|
||||
"two_handed",
|
||||
"has_special",
|
||||
"remove_sleeves",
|
||||
|
|
@ -311,4 +318,4 @@ class ItemConfigParser {
|
|||
}
|
||||
log(this::class.java, Log.FINE, "Parsed $count item configs.")
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -168,6 +168,22 @@ public abstract class MapZone implements Zone {
|
|||
return true;
|
||||
}
|
||||
|
||||
/**
|
||||
* Checks whether an item may be equipped in this zone.
|
||||
*/
|
||||
public boolean canEquip(Player player, Item item, int equipmentSlot) {
|
||||
return true;
|
||||
}
|
||||
|
||||
/**
|
||||
* Applies zone-specific outgoing damage modifiers.
|
||||
*
|
||||
* @return the modified hit, or {@code null} when this zone has no modifier.
|
||||
*/
|
||||
public Integer modifyOutgoingHit(Entity attacker, Entity target, int hit) {
|
||||
return null;
|
||||
}
|
||||
|
||||
/**
|
||||
* If multi-zone boundaries should be ignored.
|
||||
* @param attacker The attacker.
|
||||
|
|
@ -514,4 +530,4 @@ public abstract class MapZone implements Zone {
|
|||
public int hashCode() {
|
||||
return Objects.hash(uid, name, overlappable, fireRandomEvents, restriction, zoneType);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -53,7 +53,7 @@ public final class ZoneMonitor {
|
|||
Items.RING_OF_WEALTH2_14642,
|
||||
Items.RING_OF_WEALTH3_14644,
|
||||
Items.RING_OF_LIFE_2570
|
||||
);
|
||||
);
|
||||
|
||||
/**
|
||||
* The entity.
|
||||
|
|
@ -187,6 +187,32 @@ public final class ZoneMonitor {
|
|||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
/**
|
||||
* Checks whether all current zones permit an equipment change.
|
||||
*/
|
||||
public boolean canEquip(Player player, Item item, int equipmentSlot) {
|
||||
for (RegionZone z : zones) {
|
||||
if (!z.getZone().canEquip(player, item, equipmentSlot)) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
/**
|
||||
* Applies outgoing damage modifiers from the entity's current zones.
|
||||
*/
|
||||
public int modifyOutgoingHit(Entity attacker, Entity target, int hit) {
|
||||
int modifiedHit = hit;
|
||||
for (RegionZone z : zones) {
|
||||
Integer zoneHit = z.getZone().modifyOutgoingHit(attacker, target, modifiedHit);
|
||||
if (zoneHit != null) {
|
||||
modifiedHit = zoneHit;
|
||||
}
|
||||
}
|
||||
return modifiedHit;
|
||||
}
|
||||
|
||||
/**
|
||||
* Checks if the entity can interact with the target.
|
||||
|
|
|
|||
|
|
@ -11,11 +11,13 @@ import core.game.node.Node
|
|||
import core.game.node.entity.Entity
|
||||
import core.game.node.entity.combat.CombatStyle
|
||||
import core.game.node.entity.npc.NPCBehavior
|
||||
import core.game.node.entity.player.Player
|
||||
import core.game.node.entity.player.info.login.LoginConfiguration
|
||||
import core.game.node.entity.player.info.login.PlayerSaveParser
|
||||
import core.game.node.entity.player.info.login.PlayerSaver
|
||||
import core.game.node.entity.player.link.quest.Quest
|
||||
import core.game.node.entity.player.link.quest.QuestRepository
|
||||
import core.game.node.item.Item
|
||||
import core.game.system.timer.RSTimer
|
||||
import core.game.system.timer.TimerRegistry
|
||||
import core.game.world.GameWorld
|
||||
|
|
@ -141,17 +143,13 @@ object ClassScanner {
|
|||
}
|
||||
}
|
||||
|
||||
// Additional hook for startDeath for PVP area controls
|
||||
override fun startDeath(e: Entity?, killer: Entity?): Boolean {
|
||||
if (e == null) {
|
||||
return super.startDeath(null, killer)
|
||||
}
|
||||
val clazzAttack = clazz.canStartDeath(e, killer)
|
||||
if (clazzAttack == null) { // If clazz returns null, continue the usual check.
|
||||
return super.startDeath(e, killer)
|
||||
} else {
|
||||
return clazzAttack // Otherwise return whatever function.
|
||||
}
|
||||
override fun canEquip(player: Player, item: Item, equipmentSlot: Int): Boolean {
|
||||
return clazz.canEquipItem(player, item, equipmentSlot)
|
||||
?: super.canEquip(player, item, equipmentSlot)
|
||||
}
|
||||
|
||||
override fun modifyOutgoingHit(attacker: Entity, target: Entity, hit: Int): Int? {
|
||||
return clazz.modifyOutgoingHitInArea(attacker, target, hit)
|
||||
}
|
||||
}
|
||||
for(border in clazz.defineAreaBorders()) zone.register(border)
|
||||
|
|
|
|||
|
|
@ -21,6 +21,9 @@ import core.game.system.timer.impl.Teleblock
|
|||
import core.game.world.GameWorld
|
||||
import core.game.world.map.Location
|
||||
import core.game.world.map.RegionManager
|
||||
import core.game.world.map.zone.MapZone
|
||||
import core.game.world.map.zone.RegionZone
|
||||
import core.game.world.map.zone.ZoneBorders
|
||||
import org.junit.jupiter.api.AfterEach
|
||||
import org.junit.jupiter.api.Assertions.*
|
||||
import org.junit.jupiter.api.Test
|
||||
|
|
@ -28,6 +31,16 @@ import org.rs09.consts.Items
|
|||
|
||||
class CastleWarsTests {
|
||||
|
||||
private fun addCastleWarsEquipmentZone(player: Player) {
|
||||
val area = CastleWarsGameArea()
|
||||
val zone = object : MapZone("Castle Wars equipment test", true) {
|
||||
override fun canEquip(player: Player, item: Item, equipmentSlot: Int): Boolean {
|
||||
return area.canEquipItem(player, item, equipmentSlot) ?: true
|
||||
}
|
||||
}
|
||||
player.zoneMonitor.zones.add(RegionZone(zone, ZoneBorders(player.location, player.location)))
|
||||
}
|
||||
|
||||
companion object {
|
||||
init {
|
||||
TestUtils.preTestSetup()
|
||||
|
|
@ -82,6 +95,26 @@ class CastleWarsTests {
|
|||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
fun waitingRoomMembershipDoesNotGrantActiveGamePrivileges() {
|
||||
TestUtils.getMockPlayer("cwwaitingonly").use { player ->
|
||||
CastleWarsGameArea.ticksLeftInGame = 100
|
||||
CastleWarsWaitingArea.waitingSaradominPlayers.add(player)
|
||||
|
||||
assertTrue(CastleWarsWaitingArea.waitingSaradominPlayers.contains(player))
|
||||
assertNull(CastleWarsGameState.getActiveGameTeam(player))
|
||||
|
||||
player.inventory.add(Item(CastleWars.cwBarricade))
|
||||
val setUp = requireNotNull(
|
||||
InteractionListeners.get(CastleWars.cwBarricade, IntType.ITEM.ordinal, "set-up")
|
||||
)
|
||||
setUp.invoke(player, player.inventory[0])
|
||||
|
||||
assertEquals(0, CastleWarsItemHandler.getBarricadeCount(CastleWarsTeam.SARADOMIN))
|
||||
assertTrue(player.inventory.contains(CastleWars.cwBarricade, 1))
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
fun spawnRoomPortalOffersVacancyAndFirstAcceptanceWins() {
|
||||
TestUtils.getMockPlayer("cwleaver").use { leaver ->
|
||||
|
|
@ -802,6 +835,7 @@ class CastleWarsTests {
|
|||
fun castleWarsPlayersCannotEquipHelmsAfterEntry() {
|
||||
TestUtils.getMockPlayer("cwhelm").use { player ->
|
||||
player.location = Location.create(2380, 9490, 0)
|
||||
addCastleWarsEquipmentZone(player)
|
||||
player.inventory.add(Item(Items.BRONZE_FULL_HELM_1155))
|
||||
|
||||
InteractionListeners.run(
|
||||
|
|
@ -817,6 +851,7 @@ class CastleWarsTests {
|
|||
fun castleWarsPlayersCannotManuallyEquipTeamCloaksAfterEntry() {
|
||||
TestUtils.getMockPlayer("cwcloak").use { player ->
|
||||
player.location = Location.create(2380, 9490, 0)
|
||||
addCastleWarsEquipmentZone(player)
|
||||
player.inventory.add(Item(Items.HOODED_CLOAK_4041))
|
||||
|
||||
InteractionListeners.run(
|
||||
|
|
@ -980,6 +1015,16 @@ class CastleWarsTests {
|
|||
assertEquals(-2, zamorakNorth.y - zamorakBase.y)
|
||||
}
|
||||
|
||||
@Test
|
||||
fun catapultMarkerUsesEntireInterfaceMap() {
|
||||
val renderer = CatapultInterfaceRenderer()
|
||||
|
||||
assertEquals(CatapultConfig.MAP_BASE_X, renderer.calculateScreenX(0))
|
||||
assertEquals(CatapultConfig.MAP_BASE_X + CatapultConfig.MAP_WIDTH, renderer.calculateScreenX(30))
|
||||
assertEquals(CatapultConfig.MAP_BASE_Y + CatapultConfig.MAP_HEIGHT, renderer.calculateScreenY(0))
|
||||
assertEquals(CatapultConfig.MAP_BASE_Y, renderer.calculateScreenY(30))
|
||||
}
|
||||
|
||||
@Test
|
||||
fun catapultShotInFlightBlocksOnlyShooterUntilLanding() {
|
||||
TestUtils.getMockPlayer("cwcatlock1").use { shooter ->
|
||||
|
|
@ -996,6 +1041,9 @@ class CastleWarsTests {
|
|||
)!!
|
||||
|
||||
CastleWarsGameArea.saradominPlayers.addAll(listOf(shooter, other))
|
||||
CastleWarsGameArea.ticksLeftInGame = 100
|
||||
shooter.location = catapult.location
|
||||
other.location = catapult.location
|
||||
shooter.inventory.add(Item(CastleWars.cwRock, 2))
|
||||
other.inventory.add(Item(CastleWars.cwRock))
|
||||
|
||||
|
|
@ -1048,6 +1096,8 @@ class CastleWarsTests {
|
|||
catapult.id, IntType.SCENERY.ordinal, "operate"
|
||||
)!!
|
||||
CastleWarsGameArea.saradominPlayers.add(shooter)
|
||||
CastleWarsGameArea.ticksLeftInGame = 100
|
||||
shooter.location = catapult.location
|
||||
shooter.inventory.add(Item(CastleWars.cwRock))
|
||||
|
||||
operateCatapult(shooter, catapult)
|
||||
|
|
@ -1064,10 +1114,39 @@ class CastleWarsTests {
|
|||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
fun staleCatapultInterfaceCannotFireAfterPlayerMovesAway() {
|
||||
TestUtils.getMockPlayer("cwcatmoved").use { shooter ->
|
||||
val catapult = Scenery(
|
||||
CastleWars.getOperationalCatapultId(CastleWarsTeam.SARADOMIN),
|
||||
CastleWarsGameState.saradominCatapultLocation,
|
||||
10,
|
||||
0
|
||||
)
|
||||
ensureCatapultObject(catapult)
|
||||
CastleWarsGameArea.saradominPlayers.add(shooter)
|
||||
CastleWarsGameArea.ticksLeftInGame = 100
|
||||
shooter.location = catapult.location
|
||||
shooter.inventory.add(Item(CastleWars.cwRock))
|
||||
shooter.setAttribute(CatapultConfig.ATTR_LOCATION, catapult.location)
|
||||
shooter.interfaceManager.open(Component(CatapultConfig.INTERFACE_ID))
|
||||
shooter.location = Location.create(catapult.location.x + 10, catapult.location.y, catapult.location.z)
|
||||
|
||||
InterfaceListeners.run(
|
||||
shooter, Component(CatapultConfig.INTERFACE_ID), 0, CatapultConfig.BUTTON_FIRE, 0, 0
|
||||
)
|
||||
|
||||
assertEquals(1, shooter.inventory.getAmount(CastleWars.cwRock))
|
||||
assertFalse(CatapultFiringHandler.hasShotInFlight(shooter))
|
||||
assertFalse(shooter.interfaceManager.hasMainComponent(CatapultConfig.INTERFACE_ID))
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
fun staleCatapultInterfaceCannotFireWhenCatapultObjectIsMissing() {
|
||||
TestUtils.getMockPlayer("cwcatmissing").use { shooter ->
|
||||
CastleWarsGameArea.saradominPlayers.add(shooter)
|
||||
CastleWarsGameArea.ticksLeftInGame = 100
|
||||
shooter.inventory.add(Item(CastleWars.cwRock))
|
||||
shooter.setAttribute(CatapultConfig.ATTR_LOCATION, Location.create(2400, 3090, 0))
|
||||
shooter.interfaceManager.open(Component(CatapultConfig.INTERFACE_ID))
|
||||
|
|
@ -1120,6 +1199,13 @@ class CastleWarsTests {
|
|||
assertTrue(canClimbUpClimbingRope(rope.location, rope))
|
||||
}
|
||||
|
||||
@Test
|
||||
fun invalidBoulderKeysFailFast() {
|
||||
assertThrows(IllegalArgumentException::class.java) {
|
||||
CastleWarsGameState.getBoulderState("not-a-boulder")
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
fun respawnEnergyBarrierPassesAcrossOneWallTile() {
|
||||
val barrierTile = Location.create(2426, 3080, 1)
|
||||
|
|
@ -1171,6 +1257,7 @@ class CastleWarsTests {
|
|||
TestUtils.getMockPlayer("cwbarrfire").use { player ->
|
||||
val barricade = CastleWarsBarricadeNPC.spawn(Location.create(2400, 3100, 0), CastleWarsTeam.SARADOMIN)
|
||||
CastleWarsGameArea.ticksLeftInGame = 100
|
||||
CastleWarsGameArea.saradominPlayers.add(player)
|
||||
CastleWarsItemHandler.saradominBarricades.add(barricade)
|
||||
player.inventory.add(Item(Items.BUCKET_OF_WATER_1929))
|
||||
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue