mirror of
https://gitlab.com/2009scape/2009scape.git
synced 2026-08-25 04:15:14 -06:00
Unforeseen consequences
Messageless teleblock, hard ass helmet/cape slot blocking in Castle Wars, many CW fixes
This commit is contained in:
parent
029870a1bd
commit
9716d6caf5
16 changed files with 1043 additions and 142 deletions
|
|
@ -9,6 +9,7 @@ import core.game.interaction.InterfaceListener
|
|||
import core.game.node.entity.player.Player
|
||||
import core.game.system.task.Pulse
|
||||
import core.game.world.map.Location
|
||||
import core.game.world.map.RegionManager
|
||||
|
||||
class CastleWarsCatapultInterface : InterfaceListener {
|
||||
|
||||
|
|
@ -67,18 +68,58 @@ class CastleWarsCatapultInterface : InterfaceListener {
|
|||
sendMessage(player, "Error: Catapult location not found.")
|
||||
return true
|
||||
}
|
||||
val catapultTeam = getCatapultTeam(catapultLocation)
|
||||
if (!canFireCatapult(player, team, catapultTeam, catapultLocation)) {
|
||||
return closeAndReturn(player, true)
|
||||
}
|
||||
|
||||
if (!removeItem(player, CastleWars.cwRock)) {
|
||||
sendMessage(player, "You don't have any rocks to fire!")
|
||||
return true
|
||||
}
|
||||
|
||||
val catapultTeam = CatapultTeam.fromCastleWarsTeam(team)
|
||||
firingHandler.fire(player, coord, catapultTeam, catapultLocation)
|
||||
firingHandler.fire(player, coord, CatapultTeam.fromCastleWarsTeam(catapultTeam), catapultLocation)
|
||||
closeInterface(player)
|
||||
return true
|
||||
}
|
||||
|
||||
private fun canFireCatapult(
|
||||
player: Player,
|
||||
playerTeam: CastleWarsTeam,
|
||||
catapultTeam: CastleWarsTeam,
|
||||
catapultLocation: Location
|
||||
): Boolean {
|
||||
if (playerTeam != catapultTeam) {
|
||||
sendMessage(player, "You can't use the enemy's catapult!")
|
||||
return false
|
||||
}
|
||||
|
||||
val currentCatapult = RegionManager.getObject(catapultLocation)
|
||||
if (currentCatapult == null || !currentCatapult.isActive || currentCatapult.id !in CastleWars.allCatapultIds) {
|
||||
sendMessage(player, "The catapult is no longer there.")
|
||||
return false
|
||||
}
|
||||
if (currentCatapult.id in CastleWars.damagedCatapultIds || !CastleWarsGameState.isCatapultOperational(
|
||||
catapultTeam
|
||||
)
|
||||
) {
|
||||
sendMessage(player, "The catapult is destroyed! Use a toolkit to repair it.")
|
||||
return false
|
||||
}
|
||||
if (currentCatapult.id in CastleWars.burningCatapultIds || CastleWarsGameState.isCatapultOnFire(
|
||||
catapultTeam
|
||||
)
|
||||
) {
|
||||
sendMessage(player, "The catapult is on fire! Use a bucket of water to extinguish it.")
|
||||
return false
|
||||
}
|
||||
return true
|
||||
}
|
||||
|
||||
private fun getCatapultTeam(location: Location): CastleWarsTeam {
|
||||
return if (location.y < 3100) CastleWarsTeam.SARADOMIN else CastleWarsTeam.ZAMORAK
|
||||
}
|
||||
|
||||
private fun handleCoordinateChange(
|
||||
player: Player, currentCoord: Coordinate, button: CatapultButton
|
||||
): Boolean {
|
||||
|
|
|
|||
|
|
@ -1,12 +1,9 @@
|
|||
package content.minigame.castlewars
|
||||
|
||||
import content.minigame.castlewars.areas.CastleWarsGameArea
|
||||
import content.minigame.castlewars.areas.CastleWarsGameArea.Companion.saradominPlayers
|
||||
import content.minigame.castlewars.areas.CastleWarsGameArea.Companion.zamorakPlayers
|
||||
import content.minigame.castlewars.areas.CastleWarsWaitingArea
|
||||
import core.api.Commands
|
||||
import core.api.sendMessage
|
||||
import core.game.interaction.Option
|
||||
import core.game.system.command.Privilege
|
||||
|
||||
/**
|
||||
|
|
@ -48,23 +45,7 @@ class CastleWarsCommands : Commands {
|
|||
sendMessage(player, "No Castle Wars game is currently running.")
|
||||
return@define
|
||||
}
|
||||
// Snapshot the sets before endGame() modifies them via teleport callbacks
|
||||
val saraSnapshot = saradominPlayers.toSet()
|
||||
val zamSnapshot = zamorakPlayers.toSet()
|
||||
CastleWarsGameState.endGame()
|
||||
CastleWarsItemHandler.clearAllBarricades()
|
||||
CastleWarsGameArea.ticksLeftInGame = -1
|
||||
CastleWarsWaitingArea.gameStartCountdown = -1
|
||||
for (p in saraSnapshot) {
|
||||
p.interaction.remove(Option._P_ATTACK)
|
||||
p.properties.teleportLocation = CastleWars.lobbyBankArea.randomWalkableLoc
|
||||
}
|
||||
for (p in zamSnapshot) {
|
||||
p.interaction.remove(Option._P_ATTACK)
|
||||
p.properties.teleportLocation = CastleWars.lobbyBankArea.randomWalkableLoc
|
||||
}
|
||||
saradominPlayers.clear()
|
||||
zamorakPlayers.clear()
|
||||
CastleWarsGameArea.endCurrentGame()
|
||||
sendMessage(player, "Castle Wars game ended by admin command.")
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -120,6 +120,9 @@ object CastleWarsConstants {
|
|||
/** Waiting-room cycle while no game is active: 2 minutes = 200 ticks */
|
||||
const val WAITING_ROOM_COUNTDOWN_TICKS = 2 * TICKS_PER_MINUTE
|
||||
|
||||
/** How long a substitution offer can block later vacancy offers */
|
||||
const val VACANCY_OFFER_TIMEOUT_TICKS = TICKS_PER_MINUTE
|
||||
|
||||
// ==================== Player Attribute Keys ====================
|
||||
/** Attribute key: world tick when player joined the current game */
|
||||
const val ATTR_JOIN_TICK = "cw:join_tick"
|
||||
|
|
|
|||
|
|
@ -3,11 +3,14 @@ package content.minigame.castlewars
|
|||
import core.api.animate
|
||||
import core.api.playAudio
|
||||
import core.game.container.impl.EquipmentContainer
|
||||
import core.game.node.entity.combat.equipment.WeaponInterface
|
||||
import core.game.node.entity.player.Player
|
||||
import core.game.node.entity.skill.Skills
|
||||
import core.game.node.scenery.Scenery
|
||||
import core.game.system.task.Pulse
|
||||
import core.tools.RandomFunction
|
||||
import org.rs09.consts.Sounds
|
||||
import kotlin.math.floor
|
||||
|
||||
/**
|
||||
* Handles attacking the Castle Wars front doors.
|
||||
|
|
@ -54,18 +57,24 @@ class CastleWarsDoorAttackPulse(
|
|||
}
|
||||
|
||||
private fun calculateDamage(): Int {
|
||||
// Doors are weak to crush > slash > stab
|
||||
val strengthLevel = player.skills.getLevel(Skills.STRENGTH)
|
||||
var effectiveStrength = player.skills.getLevel(Skills.STRENGTH).toDouble()
|
||||
when (player.properties.attackStyle.style) {
|
||||
WeaponInterface.STYLE_AGGRESSIVE -> effectiveStrength += 3.0
|
||||
WeaponInterface.STYLE_CONTROLLED -> effectiveStrength += 1.0
|
||||
}
|
||||
effectiveStrength += 8.0
|
||||
|
||||
val strengthBonus = player.properties.bonuses[11] + 64
|
||||
val maxHit = floor(0.5 + (effectiveStrength * strengthBonus / 640.0)).toInt().coerceAtLeast(1)
|
||||
val bonusType = player.properties.attackStyle.bonusType
|
||||
val styleBonus = when (bonusType) {
|
||||
2 -> 10 // Crush
|
||||
1 -> 4 // Slash
|
||||
0 -> 1 // Stab
|
||||
else -> 3 // Default
|
||||
} // Calculate damage: base from strength + style bonus
|
||||
val damage =
|
||||
(strengthLevel / 12) + styleBonus // Clamp to 1-25 range (max 25% of door health per hit) TODO: 100% inauthentic
|
||||
return damage.coerceIn(1, 25)
|
||||
val styleMultiplier = when (bonusType) {
|
||||
WeaponInterface.BONUS_CRUSH -> 1.25
|
||||
WeaponInterface.BONUS_SLASH -> 1.0
|
||||
WeaponInterface.BONUS_STAB -> 0.85
|
||||
else -> 0.75
|
||||
}
|
||||
val styleAdjustedMax = floor(maxHit * styleMultiplier).toInt().coerceAtLeast(1)
|
||||
return RandomFunction.random(styleAdjustedMax + 1).coerceIn(0, 25)
|
||||
}
|
||||
|
||||
private fun getAttackAnimation(): Int {
|
||||
|
|
|
|||
|
|
@ -117,10 +117,12 @@ class CastleWarsFlagHandler : InteractionListener {
|
|||
onUnequip(intArrayOf(CastleWars.saradominFlag, CastleWars.zamorakFlag)) { player, _ ->
|
||||
if (CastleWarsGameState.isCarryingFlag(player)) {
|
||||
CastleWarsGameState.dropFlag(player)
|
||||
return@onUnequip false
|
||||
} else if (CastleWarsGameState.isCarryingOwnFlag(player)) {
|
||||
CastleWarsGameState.dropOwnFlag(player)
|
||||
return@onUnequip false
|
||||
}
|
||||
return@onUnequip false
|
||||
return@onUnequip true
|
||||
}
|
||||
on(CastleWars.droppedFlagSceneryIds, IntType.SCENERY, "take") { player, node ->
|
||||
if (!CastleWarsGameState.isGameActive) {
|
||||
|
|
@ -158,4 +160,3 @@ class CastleWarsFlagHandler : InteractionListener {
|
|||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -13,6 +13,7 @@ import core.game.node.entity.player.Player
|
|||
import core.game.node.entity.player.link.HintIconManager
|
||||
import core.game.node.item.GroundItemManager
|
||||
import core.game.node.item.Item
|
||||
import core.game.node.scenery.Constructed
|
||||
import core.game.node.scenery.Scenery
|
||||
import core.game.node.scenery.SceneryBuilder
|
||||
import core.game.world.map.Location
|
||||
|
|
@ -165,6 +166,8 @@ object CastleWarsGameState {
|
|||
private set
|
||||
var zamorakCatapultOnFire: Boolean = false
|
||||
private set
|
||||
private var saradominCatapultFireGeneration: Int = 0
|
||||
private var zamorakCatapultFireGeneration: Int = 0
|
||||
|
||||
// Boulder locations for lookup
|
||||
val boulderLocations = mapOf(
|
||||
|
|
@ -262,6 +265,8 @@ object CastleWarsGameState {
|
|||
zamorakCatapultOperational = true
|
||||
saradominCatapultOnFire = false
|
||||
zamorakCatapultOnFire = false
|
||||
invalidateCatapultFire(CastleWarsTeam.SARADOMIN)
|
||||
invalidateCatapultFire(CastleWarsTeam.ZAMORAK)
|
||||
for (key in boulderLocations.keys) {
|
||||
updateBoulderScenery(key, BoulderState.COLLAPSED)
|
||||
}
|
||||
|
|
@ -331,28 +336,62 @@ object CastleWarsGameState {
|
|||
2418..2432 to 3070..3085, // Saradomin castle area
|
||||
2368..2382 to 3125..3140 // Zamorak castle area
|
||||
)
|
||||
val resetDoorOpenIds = CastleWars.largeDoorOpenIds + CastleWars.backdoorOpenIds
|
||||
for ((xRange, yRange) in doorResetAreas) {
|
||||
for (x in xRange) {
|
||||
for (y in yRange) {
|
||||
val loc = Location.create(x, y, 0)
|
||||
val obj = RegionManager.getObject(loc)
|
||||
if (obj != null && obj.id in CastleWars.largeDoorOpenIds) {
|
||||
val closedId = when (obj.id) {
|
||||
4425 -> 4423
|
||||
4426 -> 4424
|
||||
4429 -> 4427
|
||||
4430 -> 4428
|
||||
else -> null
|
||||
}
|
||||
if (closedId != null) {
|
||||
SceneryBuilder.replace(obj, Scenery(closedId, loc, obj.type, obj.rotation))
|
||||
}
|
||||
if (obj != null && obj.id in resetDoorOpenIds) {
|
||||
closeResetDoor(obj)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private fun closeResetDoor(obj: Scenery) {
|
||||
if (obj is Constructed) {
|
||||
val replaced = obj.replaced
|
||||
if (replaced != null) {
|
||||
SceneryBuilder.replace(obj, replaced)
|
||||
return
|
||||
}
|
||||
}
|
||||
val closedId = getClosedDoorId(obj.id) ?: getClosedBackdoorId(obj.id) ?: return
|
||||
SceneryBuilder.replace(obj, Scenery(closedId, obj.location, obj.type, obj.rotation))
|
||||
}
|
||||
|
||||
private fun getClosedDoorId(openDoorId: Int): Int? = when (openDoorId) {
|
||||
4425 -> 4423
|
||||
4426 -> 4424
|
||||
4429 -> 4427
|
||||
4430 -> 4428
|
||||
else -> null
|
||||
}
|
||||
|
||||
private fun getClosedBackdoorId(openDoorId: Int): Int? = when (openDoorId) {
|
||||
CastleWars.saradominBackdoorOpen -> CastleWars.saradominBackdoor
|
||||
CastleWars.zamorakBackdoorOpen -> CastleWars.zamorakBackdoor
|
||||
else -> null
|
||||
}
|
||||
|
||||
fun clearCastleWarsGroundItems() {
|
||||
GroundItemManager.getItems().toList()
|
||||
.filter { item ->
|
||||
val location = item.location ?: return@filter false
|
||||
item.id in CastleWars.CW_ITEMS_TO_REMOVE && isCastleWarsGroundCleanupLocation(location)
|
||||
}
|
||||
.forEach { GroundItemManager.destroy(it) }
|
||||
}
|
||||
|
||||
private fun isCastleWarsGroundCleanupLocation(location: Location): Boolean {
|
||||
return CastleWarsGameArea.areaBorders.any { it.insideBorder(location) } ||
|
||||
CastleWarsWaitingArea.areaBorders.any { it.insideBorder(location) } ||
|
||||
CastleWarsRespawnArea.saradominRespawnRoom.insideBorder(location) ||
|
||||
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.
|
||||
|
|
@ -566,7 +605,7 @@ object CastleWarsGameState {
|
|||
* Drop a flag (when a player dies or leaves) - creates scenery object
|
||||
* @param player The player dropping the flag
|
||||
*/
|
||||
fun dropFlag(player: Player) {
|
||||
fun dropFlag(player: Player, broadcast: Boolean = true) {
|
||||
val carriedFlag = getCarriedFlag(player) ?: return
|
||||
val dropLocation = Location.create(player.location)
|
||||
val shouldReturnToStand = CastleWars.isSteppingStoneTile(dropLocation)
|
||||
|
|
@ -593,8 +632,8 @@ object CastleWarsGameState {
|
|||
}
|
||||
removeAttribute(player, CastleWarsConstants.ATTR_FLAG_PICKUP_TICK)
|
||||
if (shouldReturnToStand) {
|
||||
returnFlagToStand(carriedFlag)
|
||||
} else {
|
||||
returnFlagToStand(carriedFlag, broadcast)
|
||||
} else if (broadcast) {
|
||||
announceToAllPlayers("${carriedFlag.teamName} flag has been dropped!")
|
||||
updateAllOverlays()
|
||||
updateAllHintIcons()
|
||||
|
|
@ -605,7 +644,7 @@ object CastleWarsGameState {
|
|||
* Drop own flag (when a player carrying their own team's flag dies or leaves)
|
||||
* @param player The player dropping the flag
|
||||
*/
|
||||
fun dropOwnFlag(player: Player) {
|
||||
fun dropOwnFlag(player: Player, broadcast: Boolean = true) {
|
||||
val carriedFlag = getOwnCarriedFlag(player) ?: return
|
||||
val dropLocation = Location.create(player.location)
|
||||
val shouldReturnToStand = CastleWars.isSteppingStoneTile(dropLocation)
|
||||
|
|
@ -631,8 +670,8 @@ object CastleWarsGameState {
|
|||
player.inventory.remove(Item(carriedFlag.flagItemId))
|
||||
}
|
||||
if (shouldReturnToStand) {
|
||||
returnFlagToStand(carriedFlag)
|
||||
} else {
|
||||
returnFlagToStand(carriedFlag, broadcast)
|
||||
} else if (broadcast) {
|
||||
announceToAllPlayers("${carriedFlag.teamName} flag has been dropped!")
|
||||
updateAllOverlays()
|
||||
updateAllHintIcons()
|
||||
|
|
@ -859,7 +898,7 @@ object CastleWarsGameState {
|
|||
* Return a dropped flag directly to its stand (instant, no player involved).
|
||||
* Used for auto-return (timeout, stepping stone) scenarios.
|
||||
*/
|
||||
fun returnFlagToStand(flagTeam: CastleWarsTeam) {
|
||||
fun returnFlagToStand(flagTeam: CastleWarsTeam, broadcast: Boolean = true) {
|
||||
when (flagTeam) {
|
||||
CastleWarsTeam.SARADOMIN -> {
|
||||
saradominDroppedFlagScenery?.let { SceneryBuilder.remove(it) }
|
||||
|
|
@ -879,9 +918,11 @@ object CastleWarsGameState {
|
|||
updateFlagStandObject(CastleWarsTeam.ZAMORAK, FlagState.SAFE)
|
||||
}
|
||||
}
|
||||
announceToAllPlayers("The ${flagTeam.teamName} flag has been returned to its stand!")
|
||||
updateAllOverlays()
|
||||
updateAllHintIcons()
|
||||
if (broadcast) {
|
||||
announceToAllPlayers("The ${flagTeam.teamName} flag has been returned to its stand!")
|
||||
updateAllOverlays()
|
||||
updateAllHintIcons()
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
|
|
@ -1077,6 +1118,7 @@ object CastleWarsGameState {
|
|||
player.inventory.removeAll(CastleWars.CW_ITEMS_TO_REMOVE)
|
||||
player.equipment.removeAll(CastleWars.CW_ITEMS_TO_REMOVE)
|
||||
(player.familiarManager.familiar as? BurdenBeast)?.container?.removeAll(CastleWars.CW_ITEMS_TO_REMOVE)
|
||||
removeTimer(player, "teleblock")
|
||||
clearActiveCastleWarsBracelet(player)
|
||||
player.interaction.remove(Option._P_ATTACK)
|
||||
player.interaction.remove(Option._P_STEAL_FROM)
|
||||
|
|
@ -1085,27 +1127,34 @@ object CastleWarsGameState {
|
|||
/**
|
||||
* End the game and distribute rewards
|
||||
*/
|
||||
fun endGame() {
|
||||
val allPlayers = getAllGamePlayers()
|
||||
for (player in allPlayers) {
|
||||
fun endGame(extraRewardRecipients: Map<Player, CastleWarsTeam> = emptyMap()) {
|
||||
val rewardRecipients = linkedMapOf<Player, CastleWarsTeam>()
|
||||
for (player in getAllGamePlayers()) {
|
||||
val team = getPlayerTeam(player) ?: continue
|
||||
rewardRecipients[player] = team
|
||||
}
|
||||
for ((player, team) in extraRewardRecipients) {
|
||||
if (!rewardRecipients.containsKey(player)) {
|
||||
rewardRecipients[player] = team
|
||||
}
|
||||
}
|
||||
for ((player, team) in rewardRecipients) {
|
||||
val tickets = when {
|
||||
saradominScore > zamorakScore && team == CastleWarsTeam.SARADOMIN -> 2
|
||||
zamorakScore > saradominScore && team == CastleWarsTeam.ZAMORAK -> 2
|
||||
saradominScore == zamorakScore -> 1
|
||||
else -> 0
|
||||
}
|
||||
if (tickets > 0) {
|
||||
addItem(player, Items.CASTLE_WARS_TICKET_4067, tickets)
|
||||
sendMessage(player, "You have received $tickets Castle Wars ticket${if (tickets > 1) "s" else ""}!")
|
||||
}
|
||||
if (isCarryingFlag(player)) {
|
||||
dropFlag(player)
|
||||
dropFlag(player, broadcast = false)
|
||||
} else if (isCarryingOwnFlag(player)) {
|
||||
dropOwnFlag(player)
|
||||
dropOwnFlag(player, broadcast = false)
|
||||
}
|
||||
player.depleteCastleWarsBracelet()
|
||||
restoreAndCleanupPlayer(player)
|
||||
if (tickets > 0) {
|
||||
awardCastleWarsTickets(player, tickets)
|
||||
}
|
||||
}
|
||||
val resultMessage = when {
|
||||
saradominScore > zamorakScore -> "Saradomin wins the game!"
|
||||
|
|
@ -1116,6 +1165,17 @@ object CastleWarsGameState {
|
|||
resetGame()
|
||||
}
|
||||
|
||||
private fun awardCastleWarsTickets(player: Player, tickets: Int) {
|
||||
val reward = Item(Items.CASTLE_WARS_TICKET_4067, tickets)
|
||||
val ticketText = "Castle Wars ticket${if (tickets > 1) "s" else ""}"
|
||||
if (player.inventory.add(reward)) {
|
||||
sendMessage(player, "You have received $tickets $ticketText!")
|
||||
return
|
||||
}
|
||||
GroundItemManager.create(reward, CastleWars.lobbyBankArea.randomWalkableLoc, player)
|
||||
sendMessage(player, "Your $ticketText dropped to the ground because your inventory is full.")
|
||||
}
|
||||
|
||||
/**
|
||||
* Handle a player leaving the game
|
||||
*/
|
||||
|
|
@ -1389,11 +1449,13 @@ object CastleWarsGameState {
|
|||
CastleWarsTeam.SARADOMIN -> {
|
||||
saradominCatapultOperational = false
|
||||
saradominCatapultOnFire = false
|
||||
invalidateCatapultFire(team)
|
||||
}
|
||||
|
||||
CastleWarsTeam.ZAMORAK -> {
|
||||
zamorakCatapultOperational = false
|
||||
zamorakCatapultOnFire = false
|
||||
invalidateCatapultFire(team)
|
||||
}
|
||||
}
|
||||
updateCatapultScenery(team)
|
||||
|
|
@ -1404,13 +1466,21 @@ object CastleWarsGameState {
|
|||
* Set a team's catapult on fire (degrades over time, not instantly destroyed).
|
||||
* Tinderbox sets fire, catapult degrades until extinguished or destroyed.
|
||||
*/
|
||||
fun setCatapultOnFire(team: CastleWarsTeam) {
|
||||
fun setCatapultOnFire(team: CastleWarsTeam): Int {
|
||||
when (team) {
|
||||
CastleWarsTeam.SARADOMIN -> saradominCatapultOnFire = true
|
||||
CastleWarsTeam.ZAMORAK -> zamorakCatapultOnFire = true
|
||||
CastleWarsTeam.SARADOMIN -> {
|
||||
saradominCatapultOnFire = true
|
||||
invalidateCatapultFire(team)
|
||||
}
|
||||
|
||||
CastleWarsTeam.ZAMORAK -> {
|
||||
zamorakCatapultOnFire = true
|
||||
invalidateCatapultFire(team)
|
||||
}
|
||||
}
|
||||
updateCatapultScenery(team)
|
||||
updateAllOverlays()
|
||||
return getCatapultFireGeneration(team)
|
||||
}
|
||||
|
||||
/**
|
||||
|
|
@ -1418,13 +1488,34 @@ object CastleWarsGameState {
|
|||
*/
|
||||
fun extinguishCatapult(team: CastleWarsTeam) {
|
||||
when (team) {
|
||||
CastleWarsTeam.SARADOMIN -> saradominCatapultOnFire = false
|
||||
CastleWarsTeam.ZAMORAK -> zamorakCatapultOnFire = false
|
||||
CastleWarsTeam.SARADOMIN -> {
|
||||
saradominCatapultOnFire = false
|
||||
invalidateCatapultFire(team)
|
||||
}
|
||||
|
||||
CastleWarsTeam.ZAMORAK -> {
|
||||
zamorakCatapultOnFire = false
|
||||
invalidateCatapultFire(team)
|
||||
}
|
||||
}
|
||||
updateCatapultScenery(team)
|
||||
updateAllOverlays()
|
||||
}
|
||||
|
||||
fun getCatapultFireGeneration(team: CastleWarsTeam): Int {
|
||||
return when (team) {
|
||||
CastleWarsTeam.SARADOMIN -> saradominCatapultFireGeneration
|
||||
CastleWarsTeam.ZAMORAK -> zamorakCatapultFireGeneration
|
||||
}
|
||||
}
|
||||
|
||||
private fun invalidateCatapultFire(team: CastleWarsTeam) {
|
||||
when (team) {
|
||||
CastleWarsTeam.SARADOMIN -> saradominCatapultFireGeneration++
|
||||
CastleWarsTeam.ZAMORAK -> zamorakCatapultFireGeneration++
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Check if a team's catapult is on fire
|
||||
*/
|
||||
|
|
|
|||
|
|
@ -8,6 +8,7 @@ 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
|
||||
|
|
@ -87,20 +88,6 @@ class CastleWarsItemHandler : InteractionListener {
|
|||
|
||||
onUseWithPlayer(CastleWars.cwBandages) { player, used, with ->
|
||||
val target = with as? Player ?: return@onUseWithPlayer false
|
||||
|
||||
val playerTeam = CastleWarsGameState.getPlayerTeam(player)
|
||||
val targetTeam = CastleWarsGameState.getPlayerTeam(target)
|
||||
|
||||
if (playerTeam == null || targetTeam == null) {
|
||||
sendMessage(player, "You can only use bandages in Castle Wars.")
|
||||
return@onUseWithPlayer false
|
||||
}
|
||||
|
||||
if (playerTeam != targetTeam) {
|
||||
sendMessage(player, "You can only heal your teammates!")
|
||||
return@onUseWithPlayer false
|
||||
}
|
||||
|
||||
if (useBandage(player, target)) {
|
||||
removeItem(player, used.asItem())
|
||||
}
|
||||
|
|
@ -231,6 +218,7 @@ class CastleWarsItemHandler : InteractionListener {
|
|||
|
||||
playAudio(player, 2599)
|
||||
|
||||
burningBarricadePulses.remove(barricade)?.stop()
|
||||
barricade.transform(burningId)
|
||||
sendMessage(player, "You set the barricade on fire!")
|
||||
|
||||
|
|
@ -272,6 +260,7 @@ class CastleWarsItemHandler : InteractionListener {
|
|||
}
|
||||
|
||||
barricade.transform(originalId)
|
||||
burningBarricadePulses.remove(barricade)?.stop()
|
||||
|
||||
replaceSlot(player, used.asItem().slot, Item(Items.BUCKET_1925))
|
||||
|
||||
|
|
@ -283,6 +272,21 @@ class CastleWarsItemHandler : InteractionListener {
|
|||
}
|
||||
|
||||
private fun useBandage(user: Player, target: Player): Boolean {
|
||||
if (!CastleWarsGameState.isGameActive) {
|
||||
sendMessage(user, "You can only use bandages during a Castle Wars game.")
|
||||
return false
|
||||
}
|
||||
val userTeam = getActiveGameTeam(user)
|
||||
val targetTeam = getActiveGameTeam(target)
|
||||
if (userTeam == null || targetTeam == null) {
|
||||
sendMessage(user, "You can only use bandages during a Castle Wars game.")
|
||||
return false
|
||||
}
|
||||
if (userTeam != targetTeam) {
|
||||
sendMessage(user, "You can only heal your teammates!")
|
||||
return false
|
||||
}
|
||||
|
||||
if (CastleWarsGameState.isCarryingOwnFlag(target)) {
|
||||
if (user == target) {
|
||||
sendMessage(user, "You can't heal while carrying your team's flag!")
|
||||
|
|
@ -324,6 +328,14 @@ 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 getLocalBarricades(location: Location, radius: Int): List<NPC> {
|
||||
val barricadeIds = intArrayOf(
|
||||
NPCs.BARRICADE_1532, NPCs.BARRICADE_1533, NPCs.BARRICADE_1534, NPCs.BARRICADE_1535
|
||||
|
|
|
|||
|
|
@ -473,7 +473,7 @@ class CastleWarsListeners : InteractionListener {
|
|||
}
|
||||
|
||||
val previousHealth = CastleWarsGameState.getDoorHealth(doorTeam)
|
||||
CastleWarsGameState.damageDoor(doorTeam, 25)
|
||||
CastleWarsGameState.damageDoor(doorTeam, CastleWarsConstants.EXPLOSIVE_POTION_DOOR_DAMAGE)
|
||||
removeItem(player, used.asItem())
|
||||
playAudio(player, 2599)
|
||||
animate(player, 827)
|
||||
|
|
@ -508,7 +508,7 @@ class CastleWarsListeners : InteractionListener {
|
|||
sendMessage(player, "The door is already fully repaired.")
|
||||
return@onUseWith false
|
||||
}
|
||||
CastleWarsGameState.repairDoor(doorTeam, 10)
|
||||
CastleWarsGameState.repairDoor(doorTeam, CastleWarsConstants.TOOLKIT_DOOR_REPAIR)
|
||||
animate(player, 898)
|
||||
sendMessage(player, "You repair some of the door's damage.")
|
||||
return@onUseWith true
|
||||
|
|
@ -734,16 +734,15 @@ class CastleWarsListeners : InteractionListener {
|
|||
sendMessage(player, "The catapult is already on fire!")
|
||||
return@onUseWith false
|
||||
}
|
||||
CastleWarsGameState.setCatapultOnFire(catapultTeam)
|
||||
val fireGeneration = CastleWarsGameState.setCatapultOnFire(catapultTeam)
|
||||
playAudio(player, 2599)
|
||||
sendMessage(player, "You set the catapult on fire!")
|
||||
GameWorld.Pulser.submit(object : Pulse(50) {
|
||||
override fun pulse(): Boolean {
|
||||
if (CastleWarsGameState.isCatapultOnFire(catapultTeam) && CastleWarsGameState.isCatapultOperational(
|
||||
if (CastleWarsGameState.getCatapultFireGeneration(catapultTeam) == fireGeneration && CastleWarsGameState.isCatapultOnFire(catapultTeam) && CastleWarsGameState.isCatapultOperational(
|
||||
catapultTeam
|
||||
)
|
||||
) {
|
||||
CastleWarsGameState.extinguishCatapult(catapultTeam)
|
||||
CastleWarsGameState.destroyCatapult(catapultTeam)
|
||||
}
|
||||
return true
|
||||
|
|
|
|||
|
|
@ -11,6 +11,7 @@ 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.entity.player.info.login.PlayerParser
|
||||
import core.game.node.item.Item
|
||||
import core.game.world.map.Location
|
||||
import core.game.world.map.zone.ZoneBorders
|
||||
|
|
@ -73,7 +74,9 @@ class CastleWarsGameArea : CastleWarsArea(), TickListener, LoginListener {
|
|||
var ticksLeftInGame = -1
|
||||
|
||||
private val activeVacancyOffers = mutableMapOf<CastleWarsTeam, MutableSet<Player>>()
|
||||
private val activeVacancyOfferTicks = mutableMapOf<CastleWarsTeam, Int>()
|
||||
private val declinedSubstitutionOffers = mutableMapOf<CastleWarsTeam, MutableSet<Player>>()
|
||||
private val openVacancies = mutableMapOf<CastleWarsTeam, Int>()
|
||||
|
||||
fun startGame() {
|
||||
if (!CastleWarsWaitingArea.hasMinimumPlayers()) {
|
||||
|
|
@ -118,7 +121,9 @@ class CastleWarsGameArea : CastleWarsArea(), TickListener, LoginListener {
|
|||
|
||||
fun clearSubstitutionState() {
|
||||
activeVacancyOffers.clear()
|
||||
activeVacancyOfferTicks.clear()
|
||||
declinedSubstitutionOffers.clear()
|
||||
openVacancies.clear()
|
||||
}
|
||||
|
||||
fun selectGuthixTeam(): CastleWarsTeam {
|
||||
|
|
@ -139,15 +144,18 @@ class CastleWarsGameArea : CastleWarsArea(), TickListener, LoginListener {
|
|||
*/
|
||||
fun fillVacancy(team: CastleWarsTeam) {
|
||||
if (ticksLeftInGame <= 0) return
|
||||
if (activeVacancyOffers[team]?.isNotEmpty() == true) return
|
||||
if ((openVacancies[team] ?: 0) <= 0) return
|
||||
val waitingSet = when (team) {
|
||||
CastleWarsTeam.SARADOMIN -> CastleWarsWaitingArea.waitingSaradominPlayers
|
||||
CastleWarsTeam.ZAMORAK -> CastleWarsWaitingArea.waitingZamorakPlayers
|
||||
}
|
||||
pruneVacancyOffer(team, waitingSet)
|
||||
if (activeVacancyOffers[team]?.isNotEmpty() == true) return
|
||||
val declined = declinedSubstitutionOffers[team].orEmpty()
|
||||
val candidates = waitingSet.filter { it !in declined }
|
||||
if (candidates.isEmpty()) return
|
||||
activeVacancyOffers[team] = candidates.toMutableSet()
|
||||
activeVacancyOfferTicks[team] = getWorldTicks()
|
||||
for (candidate in candidates) {
|
||||
openDialogue(candidate, CastleWarsSubstitutionDialogue(team))
|
||||
}
|
||||
|
|
@ -160,14 +168,30 @@ class CastleWarsGameArea : CastleWarsArea(), TickListener, LoginListener {
|
|||
CastleWarsTeam.SARADOMIN -> CastleWarsWaitingArea.waitingSaradominPlayers
|
||||
CastleWarsTeam.ZAMORAK -> CastleWarsWaitingArea.waitingZamorakPlayers
|
||||
}
|
||||
if (!waitingSet.remove(player)) return false
|
||||
if (!waitingSet.remove(player)) {
|
||||
offer.remove(player)
|
||||
if (offer.isEmpty()) {
|
||||
activeVacancyOffers.remove(team)
|
||||
activeVacancyOfferTicks.remove(team)
|
||||
}
|
||||
return false
|
||||
}
|
||||
activeVacancyOffers.remove(team)
|
||||
activeVacancyOfferTicks.remove(team)
|
||||
declinedSubstitutionOffers[team]?.remove(player)
|
||||
val remainingVacancies = ((openVacancies[team] ?: 0) - 1).coerceAtLeast(0)
|
||||
if (remainingVacancies > 0) {
|
||||
openVacancies[team] = remainingVacancies
|
||||
} else {
|
||||
openVacancies.remove(team)
|
||||
}
|
||||
val gameSet = when (team) {
|
||||
CastleWarsTeam.SARADOMIN -> saradominPlayers
|
||||
CastleWarsTeam.ZAMORAK -> zamorakPlayers
|
||||
}
|
||||
gameSet.add(player)
|
||||
CastleWarsGameState.activateBraceletIfEquipped(player, team)
|
||||
CastleWarsGameState.setActiveGamePlayerOptions(player)
|
||||
setAttribute(player, CastleWarsConstants.ATTR_JOIN_TICK, getWorldTicks())
|
||||
val respawnLoc = when (team) {
|
||||
CastleWarsTeam.SARADOMIN -> CastleWarsRespawnArea.saradominRespawnRoom.randomWalkableLoc
|
||||
|
|
@ -175,6 +199,9 @@ class CastleWarsGameArea : CastleWarsArea(), TickListener, LoginListener {
|
|||
}
|
||||
player.properties.teleportLocation = respawnLoc
|
||||
sendMessage(player, "A spot has opened up! You have been moved into the Castle Wars game.")
|
||||
if ((openVacancies[team] ?: 0) > 0) {
|
||||
fillVacancy(team)
|
||||
}
|
||||
return true
|
||||
}
|
||||
|
||||
|
|
@ -183,6 +210,42 @@ class CastleWarsGameArea : CastleWarsArea(), TickListener, LoginListener {
|
|||
declinedSubstitutionOffers.getOrPut(team) { mutableSetOf() }.add(player)
|
||||
if (activeVacancyOffers[team]?.isEmpty() == true) {
|
||||
activeVacancyOffers.remove(team)
|
||||
activeVacancyOfferTicks.remove(team)
|
||||
fillVacancy(team)
|
||||
}
|
||||
}
|
||||
|
||||
fun removeVacancyOffer(player: Player) {
|
||||
val teamsToRefill = mutableSetOf<CastleWarsTeam>()
|
||||
for (team in CastleWarsTeam.values()) {
|
||||
activeVacancyOffers[team]?.remove(player)
|
||||
declinedSubstitutionOffers[team]?.remove(player)
|
||||
if (activeVacancyOffers[team]?.isEmpty() == true) {
|
||||
activeVacancyOffers.remove(team)
|
||||
activeVacancyOfferTicks.remove(team)
|
||||
teamsToRefill.add(team)
|
||||
}
|
||||
if (declinedSubstitutionOffers[team]?.isEmpty() == true) {
|
||||
declinedSubstitutionOffers.remove(team)
|
||||
}
|
||||
}
|
||||
for (team in teamsToRefill) {
|
||||
fillVacancy(team)
|
||||
}
|
||||
}
|
||||
|
||||
private fun pruneVacancyOffer(team: CastleWarsTeam, waitingSet: Set<Player>) {
|
||||
val offer = activeVacancyOffers[team] ?: return
|
||||
val createdTick = activeVacancyOfferTicks[team] ?: getWorldTicks()
|
||||
if (getWorldTicks() - createdTick >= CastleWarsConstants.VACANCY_OFFER_TIMEOUT_TICKS) {
|
||||
activeVacancyOffers.remove(team)
|
||||
activeVacancyOfferTicks.remove(team)
|
||||
return
|
||||
}
|
||||
offer.removeAll { it !in waitingSet || !it.isActive }
|
||||
if (offer.isEmpty()) {
|
||||
activeVacancyOffers.remove(team)
|
||||
activeVacancyOfferTicks.remove(team)
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -204,11 +267,16 @@ class CastleWarsGameArea : CastleWarsArea(), TickListener, LoginListener {
|
|||
player.interaction.remove(Option._P_ATTACK)
|
||||
player.interaction.remove(Option._P_STEAL_FROM)
|
||||
if (offerReplacement) {
|
||||
openVacancy(leavingTeam)
|
||||
fillVacancy(leavingTeam)
|
||||
}
|
||||
return leavingTeam
|
||||
}
|
||||
|
||||
private fun openVacancy(team: CastleWarsTeam) {
|
||||
openVacancies[team] = (openVacancies[team] ?: 0) + 1
|
||||
}
|
||||
|
||||
fun playerKey(player: Player): String {
|
||||
return player.username.lowercase()
|
||||
}
|
||||
|
|
@ -272,6 +340,18 @@ class CastleWarsGameArea : CastleWarsArea(), TickListener, LoginListener {
|
|||
clearLogoutAttributes(player)
|
||||
}
|
||||
|
||||
private fun moveLoggedOutPlayerToLobby(player: Player) {
|
||||
removeFromTeam(player)
|
||||
CastleWarsGameState.clearHintIconForPlayer(player)
|
||||
CastleWarsGameState.restoreAndCleanupPlayer(player)
|
||||
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
|
||||
clearLogoutState(player)
|
||||
}
|
||||
|
||||
fun restoreActiveLogoutOnLogin(player: Player) {
|
||||
val savedTeamName = getAttribute(player, CastleWarsConstants.ATTR_LOGOUT_TEAM, "")
|
||||
val savedLogoutTime = getAttribute(player, CastleWarsConstants.ATTR_LOGOUT_TIME_MILLIS, 0L)
|
||||
|
|
@ -287,6 +367,10 @@ class CastleWarsGameArea : CastleWarsArea(), TickListener, LoginListener {
|
|||
val logoutLocation = getAttribute(
|
||||
player, CastleWarsConstants.ATTR_LOGOUT_LOCATION, record?.logoutLocation ?: player.location
|
||||
)
|
||||
if (ticksLeftInGame <= 0) {
|
||||
moveLoggedOutPlayerToLobby(player)
|
||||
return
|
||||
}
|
||||
if (ticksLeftInGame > 0 && elapsedMillis < CastleWarsConstants.LOGOUT_GRACE_PERIOD_MILLIS) {
|
||||
player.location = Location.create(logoutLocation)
|
||||
setTeam(player, team)
|
||||
|
|
@ -295,6 +379,7 @@ class CastleWarsGameArea : CastleWarsArea(), TickListener, LoginListener {
|
|||
}
|
||||
moveLoggedOutPlayerToWaitingRoom(player, team)
|
||||
if (ticksLeftInGame > 0) {
|
||||
openVacancy(team)
|
||||
fillVacancy(team)
|
||||
}
|
||||
}
|
||||
|
|
@ -324,24 +409,41 @@ class CastleWarsGameArea : CastleWarsArea(), TickListener, LoginListener {
|
|||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private fun endGame() { // TODO: ropes, rocks in tunnels, ground items, castle doors, catapults
|
||||
CastleWarsGameState.endGame()
|
||||
CastleWarsItemHandler.clearAllBarricades()
|
||||
clearSubstitutionState()
|
||||
ticksLeftInGame = -1
|
||||
saradominPlayers.forEach { player ->
|
||||
player.unhook(SaradominInterfaceCloseHook)
|
||||
player.properties.teleportLocation = CastleWars.lobbyBankArea.randomWalkableLoc
|
||||
fun endCurrentGame(startCooldown: Boolean = true) {
|
||||
val saraSnapshot = saradominPlayers.toSet()
|
||||
val zamSnapshot = zamorakPlayers.toSet()
|
||||
val loggedOutSnapshot = loggedOutPlayers.values.toList()
|
||||
val loggedOutRewardRecipients = loggedOutSnapshot.associate { it.player to it.team }
|
||||
CastleWarsGameState.endGame(loggedOutRewardRecipients)
|
||||
for (record in loggedOutSnapshot) {
|
||||
moveLoggedOutPlayerToLobby(record.player)
|
||||
if (!record.player.isActive || record.player.session == null) {
|
||||
PlayerParser.saveImmediately(record.player)
|
||||
}
|
||||
}
|
||||
CastleWarsItemHandler.clearAllBarricades()
|
||||
CastleWarsGameState.clearCastleWarsGroundItems()
|
||||
clearSubstitutionState()
|
||||
ticksLeftInGame = -1
|
||||
CastleWarsWaitingArea.gameStartCountdown = if (startCooldown) {
|
||||
CastleWars.gameCooldownMinutes * CastleWarsConstants.TICKS_PER_MINUTE
|
||||
} else {
|
||||
-1
|
||||
}
|
||||
saraSnapshot.forEach { player ->
|
||||
player.unhook(SaradominInterfaceCloseHook)
|
||||
player.properties.teleportLocation = CastleWars.lobbyBankArea.randomWalkableLoc
|
||||
}
|
||||
zamSnapshot.forEach { player ->
|
||||
player.unhook(ZamorakInterfaceCloseHook)
|
||||
player.properties.teleportLocation = CastleWars.lobbyBankArea.randomWalkableLoc
|
||||
}
|
||||
saradominPlayers.clear()
|
||||
zamorakPlayers.clear()
|
||||
CastleWarsRespawnArea.saradominPlayersInRespawnRoom.clear()
|
||||
CastleWarsRespawnArea.zamorakPlayersInRespawnRoom.clear()
|
||||
}
|
||||
zamorakPlayers.forEach { player ->
|
||||
player.unhook(ZamorakInterfaceCloseHook)
|
||||
player.properties.teleportLocation = CastleWars.lobbyBankArea.randomWalkableLoc
|
||||
}
|
||||
saradominPlayers.clear()
|
||||
zamorakPlayers.clear()
|
||||
loggedOutPlayers.clear()
|
||||
}
|
||||
|
||||
override fun defineAreaBorders(): Array<ZoneBorders> {
|
||||
|
|
@ -353,7 +455,10 @@ class CastleWarsGameArea : CastleWarsArea(), TickListener, LoginListener {
|
|||
|
||||
super.areaEnter(player)
|
||||
zone.zoneType = ZoneType.CASTLE_WARS.id
|
||||
registerTimer(player, spawnTimer("teleblock", (CastleWars.gameTimeMinutes) * 60 * 2))
|
||||
registerTimer(
|
||||
player,
|
||||
spawnTimer("teleblock", CastleWars.gameTimeMinutes * CastleWarsConstants.TICKS_PER_MINUTE, false)
|
||||
)
|
||||
val team = CastleWarsGameState.getPlayerTeam(player)
|
||||
when (team) {
|
||||
CastleWarsTeam.SARADOMIN -> {
|
||||
|
|
@ -425,7 +530,7 @@ class CastleWarsGameArea : CastleWarsArea(), TickListener, LoginListener {
|
|||
return
|
||||
}
|
||||
if (ticksLeftInGame == 0) {
|
||||
endGame()
|
||||
endCurrentGame()
|
||||
return
|
||||
}
|
||||
val previousMinutes =
|
||||
|
|
@ -434,7 +539,7 @@ class CastleWarsGameArea : CastleWarsArea(), TickListener, LoginListener {
|
|||
val currentMinutes =
|
||||
(ticksLeftInGame + CastleWarsConstants.TICKS_PER_MINUTE - 1) / CastleWarsConstants.TICKS_PER_MINUTE
|
||||
if (ticksLeftInGame == 0) {
|
||||
endGame()
|
||||
endCurrentGame()
|
||||
return
|
||||
} else if (currentMinutes != previousMinutes) {
|
||||
CastleWarsGameState.updateAllOverlays()
|
||||
|
|
@ -474,9 +579,11 @@ class CastleWarsGameArea : CastleWarsArea(), TickListener, LoginListener {
|
|||
for ((key, record) in expiredLogouts) {
|
||||
val player = record.player
|
||||
loggedOutPlayers.remove(key)
|
||||
clearLogoutAttributes(player)
|
||||
removeFromTeam(player)
|
||||
CastleWarsGameState.clearHintIconForPlayer(player)
|
||||
CastleWarsGameState.restoreAndCleanupPlayer(player)
|
||||
openVacancy(record.team)
|
||||
fillVacancy(record.team)
|
||||
}
|
||||
// Remove truly inactive players (not in grace period)
|
||||
|
|
@ -489,11 +596,13 @@ class CastleWarsGameArea : CastleWarsArea(), TickListener, LoginListener {
|
|||
for (player in inactiveSara) {
|
||||
saradominPlayers.remove(player)
|
||||
CastleWarsGameState.clearHintIconForPlayer(player)
|
||||
openVacancy(CastleWarsTeam.SARADOMIN)
|
||||
fillVacancy(CastleWarsTeam.SARADOMIN)
|
||||
}
|
||||
for (player in inactiveZam) {
|
||||
zamorakPlayers.remove(player)
|
||||
CastleWarsGameState.clearHintIconForPlayer(player)
|
||||
openVacancy(CastleWarsTeam.ZAMORAK)
|
||||
fillVacancy(CastleWarsTeam.ZAMORAK)
|
||||
}
|
||||
// Also cleanup hint icons for any other inactive players
|
||||
|
|
|
|||
|
|
@ -63,6 +63,7 @@ class CastleWarsRespawnArea : CastleWarsArea(), TickListener {
|
|||
}
|
||||
|
||||
private fun removePlayersOverSpawnLimit(players: MutableMap<Player, Int>) {
|
||||
players.entries.removeIf { (player, _) -> !player.isActive || player.session == null }
|
||||
val currentTick = getWorldTicks()
|
||||
val expiredPlayers = players.filter { (player, entryTick) ->
|
||||
player.isActive && player.session != null && currentTick - entryTick >= CastleWarsConstants.LOGOUT_GRACE_PERIOD_TICKS
|
||||
|
|
@ -79,6 +80,8 @@ class CastleWarsRespawnArea : CastleWarsArea(), TickListener {
|
|||
if (!defineAreaBorders().any { it.insideBorder(player.location) }) {
|
||||
return
|
||||
}
|
||||
saradominPlayersInRespawnRoom.remove(player)
|
||||
zamorakPlayersInRespawnRoom.remove(player)
|
||||
if (CastleWarsGameArea.ticksLeftInGame > 0 && CastleWarsGameState.getPlayerTeam(player) != null) {
|
||||
CastleWarsGameArea().logout(player)
|
||||
return
|
||||
|
|
|
|||
|
|
@ -3,6 +3,7 @@ package content.minigame.castlewars.areas
|
|||
import content.minigame.castlewars.CastleWars
|
||||
import content.minigame.castlewars.CastleWarsConstants
|
||||
import content.minigame.castlewars.CastleWarsOverlay
|
||||
import content.minigame.castlewars.CastleWarsTeam
|
||||
import core.api.*
|
||||
import core.game.component.Component
|
||||
import core.game.node.entity.Entity
|
||||
|
|
@ -39,16 +40,28 @@ class CastleWarsWaitingArea : CastleWarsArea(), TickListener {
|
|||
val player = entity as? Player ?: return
|
||||
super.areaEnter(player)
|
||||
registerTimer(
|
||||
player, spawnTimer("teleblock", (CastleWars.gameCooldownMinutes + CastleWars.gameTimeMinutes) * 60 * 2)
|
||||
player,
|
||||
spawnTimer(
|
||||
"teleblock",
|
||||
(CastleWars.gameCooldownMinutes + CastleWars.gameTimeMinutes) * CastleWarsConstants.TICKS_PER_MINUTE,
|
||||
false
|
||||
)
|
||||
)
|
||||
|
||||
// Set team attribute and equip the hooded cloak on the entity based on which waiting room they're in
|
||||
if (zamorakWaitingRoom.insideBorder(player.location)) {
|
||||
val joinedTeam = if (zamorakWaitingRoom.insideBorder(player.location)) {
|
||||
player.equipment.replace(Item(CastleWars.zamorakTeamHoodedCloak), 1)
|
||||
waitingZamorakPlayers.add(player)
|
||||
CastleWarsTeam.ZAMORAK
|
||||
} else if (saradominWaitingRoom.insideBorder(player.location)) {
|
||||
player.equipment.replace(Item(CastleWars.saradominTeamHoodedCloak), 1)
|
||||
waitingSaradominPlayers.add(player)
|
||||
CastleWarsTeam.SARADOMIN
|
||||
} else {
|
||||
null
|
||||
}
|
||||
if (joinedTeam != null && CastleWarsGameArea.ticksLeftInGame > 0) {
|
||||
CastleWarsGameArea.fillVacancy(joinedTeam)
|
||||
}
|
||||
|
||||
var transformed = false
|
||||
|
|
@ -118,6 +131,7 @@ class CastleWarsWaitingArea : CastleWarsArea(), TickListener {
|
|||
// Remove player from waiting room set (whichever one that is)
|
||||
waitingSaradominPlayers.remove(player)
|
||||
waitingZamorakPlayers.remove(player)
|
||||
CastleWarsGameArea.removeVacancyOffer(player)
|
||||
}
|
||||
|
||||
override fun tick() {
|
||||
|
|
|
|||
|
|
@ -2,6 +2,7 @@ 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;
|
||||
|
|
@ -146,9 +147,17 @@ public final class EquipmentContainer extends Container {
|
|||
}
|
||||
|
||||
boolean listenersSayWeCanUnequip = runUnequipHooks(current, newItem);
|
||||
if(!listenersSayWeCanUnequip) {
|
||||
return false;
|
||||
}
|
||||
ArrayList<Item> itemsToMoveToInventory = new ArrayList<>(current);
|
||||
itemsToMoveToInventory.removeIf(item -> isCastleWarsFlag(item) && !containsItem(item));
|
||||
if(itemsToMoveToInventory.isEmpty()) {
|
||||
return true;
|
||||
}
|
||||
|
||||
boolean allRemoved = true;
|
||||
for(Item item : current) {
|
||||
for(Item item : itemsToMoveToInventory) {
|
||||
if(!remove(item)) {
|
||||
allRemoved = false;
|
||||
break;
|
||||
|
|
@ -157,7 +166,7 @@ public final class EquipmentContainer extends Container {
|
|||
|
||||
boolean allAdded = allRemoved;
|
||||
if(allRemoved) {
|
||||
for (Item item : current) {
|
||||
for (Item item : itemsToMoveToInventory) {
|
||||
if (!player.getInventory().add(item, true, preferredSlot)) {
|
||||
allAdded = false;
|
||||
break;
|
||||
|
|
@ -168,7 +177,7 @@ public final class EquipmentContainer extends Container {
|
|||
if (listenersSayWeCanUnequip && allRemoved && allAdded) return true;
|
||||
else {
|
||||
//put things back if we couldn't remove everything
|
||||
for(Item item : current) {
|
||||
for(Item item : itemsToMoveToInventory) {
|
||||
if(!containsItem(item)) {
|
||||
add(item);
|
||||
}
|
||||
|
|
@ -182,6 +191,9 @@ public final class EquipmentContainer extends Container {
|
|||
int neededSlots = 0;
|
||||
|
||||
for(Item item : current) {
|
||||
if(isCastleWarsFlag(item)) {
|
||||
continue;
|
||||
}
|
||||
if(!item.getDefinition().isStackable()) {
|
||||
neededSlots++;
|
||||
} else {
|
||||
|
|
@ -193,6 +205,10 @@ public final class EquipmentContainer extends Container {
|
|||
return neededSlots;
|
||||
}
|
||||
|
||||
private boolean isCastleWarsFlag(Item item) {
|
||||
return item.getId() == CastleWars.saradominFlag || item.getId() == CastleWars.zamorakFlag;
|
||||
}
|
||||
|
||||
@Nullable
|
||||
private Item getSecondaryEquipIfApplicable(Item newItem, int equipmentSlot) {
|
||||
Item secondaryEquipItem = null;
|
||||
|
|
@ -211,19 +227,28 @@ public final class EquipmentContainer extends Container {
|
|||
|
||||
for(Item currentItem : currentItems) {
|
||||
Plugin<Object> plugin = currentItem.getDefinition().getConfiguration("equipment", null);
|
||||
if (plugin != null) {
|
||||
Object object = plugin.fireEvent("unequip", player, currentItem);
|
||||
if (object != null && !((Boolean) object)) {
|
||||
canContinue = false;
|
||||
if (plugin != null) {
|
||||
Object object = plugin.fireEvent("unequip", player, currentItem);
|
||||
if (object != null && !((Boolean) object)) {
|
||||
if (isCastleWarsFlag(currentItem) && !containsItem(currentItem)) {
|
||||
continue;
|
||||
}
|
||||
canContinue = false;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
canContinue = InteractionListeners.run(currentItem.getId(), player, currentItem, false);
|
||||
|
||||
if(!canContinue) {
|
||||
if (isCastleWarsFlag(currentItem) && !containsItem(currentItem)) {
|
||||
canContinue = true;
|
||||
continue;
|
||||
}
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
canContinue = InteractionListeners.run(currentItem.getId(), player, currentItem, false);
|
||||
|
||||
if(!canContinue) break;
|
||||
}
|
||||
|
||||
return canContinue;
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -1,5 +1,6 @@
|
|||
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
|
||||
|
|
@ -47,7 +48,11 @@ class EquipHandler : InteractionListener {
|
|||
|
||||
if(currentEquippedItem != null){
|
||||
if(!InteractionListeners.run(currentEquippedItem.id, player, currentEquippedItem, false)){
|
||||
return
|
||||
val droppedCastleWarsFlag =
|
||||
isCastleWarsFlag(currentEquippedItem.id) && player.equipment[itemEquipmentSlot] == null
|
||||
if (!droppedCastleWarsFlag) {
|
||||
return
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -123,10 +128,12 @@ class EquipHandler : InteractionListener {
|
|||
if (slot == EquipmentContainer.SLOT_WEAPON) {
|
||||
player.packetDispatch.sendString("", 92, 0)
|
||||
}
|
||||
val maximumAdd = player.inventory.getMaximumAdd(item)
|
||||
if (maximumAdd < item.amount) {
|
||||
player.packetDispatch.sendMessage("Not enough free space in your inventory.")
|
||||
return
|
||||
if (!isCastleWarsFlag(item.id)) {
|
||||
val maximumAdd = player.inventory.getMaximumAdd(item)
|
||||
if (maximumAdd < item.amount) {
|
||||
player.packetDispatch.sendMessage("Not enough free space in your inventory.")
|
||||
return
|
||||
}
|
||||
}
|
||||
val plugin = item.definition.getConfiguration<Plugin<Any>>("equipment", null)
|
||||
if (plugin != null) {
|
||||
|
|
@ -137,6 +144,11 @@ class EquipHandler : InteractionListener {
|
|||
if (!InteractionListeners.run(itemId, player, item, false)) {
|
||||
return
|
||||
}
|
||||
val maximumAdd = player.inventory.getMaximumAdd(item)
|
||||
if (maximumAdd < item.amount) {
|
||||
player.packetDispatch.sendMessage("Not enough free space in your inventory.")
|
||||
return
|
||||
}
|
||||
if (player.equipment.remove(item)) {
|
||||
/* TODO: Send different unequip sound based on what is being unequipped.*/
|
||||
playAudio(player, Sounds.EQUIP_FUN_2238)
|
||||
|
|
@ -150,5 +162,9 @@ class EquipHandler : InteractionListener {
|
|||
SlayerEquipmentFlags.updateFlags(player)
|
||||
}
|
||||
}
|
||||
|
||||
private fun isCastleWarsFlag(itemId: Int): Boolean {
|
||||
return itemId == CastleWars.saradominFlag || itemId == CastleWars.zamorakFlag
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -7,18 +7,20 @@ import core.game.system.timer.PersistTimer
|
|||
import core.game.system.timer.RSTimer
|
||||
import core.game.system.timer.TimerFlag
|
||||
|
||||
class Teleblock : PersistTimer (1, "teleblock", flags = arrayOf(TimerFlag.ClearOnDeath)) {
|
||||
class Teleblock(private val sendChatboxMessage: Boolean = true) :
|
||||
PersistTimer(1, "teleblock", flags = arrayOf(TimerFlag.ClearOnDeath)) {
|
||||
override fun run (entity: Entity) : Boolean {
|
||||
return false
|
||||
}
|
||||
|
||||
override fun onRegister (entity: Entity) {
|
||||
if (entity !is Player) return
|
||||
if (!sendChatboxMessage) return
|
||||
sendMessage (entity, "You have been teleblocked.")
|
||||
}
|
||||
|
||||
override fun getTimer (vararg args: Any) : RSTimer {
|
||||
val t = Teleblock()
|
||||
val t = Teleblock(args.getOrNull(1) as? Boolean ?: true)
|
||||
t.runInterval = args.getOrNull(0) as? Int ?: 100
|
||||
return t
|
||||
}
|
||||
|
|
|
|||
|
|
@ -17,7 +17,12 @@ import core.game.node.entity.player.info.Rights
|
|||
|
||||
class EquipTests {
|
||||
companion object {
|
||||
init {TestUtils.preTestSetup(); EquipHandler().defineListeners()}
|
||||
init {
|
||||
TestUtils.preTestSetup()
|
||||
if (InteractionListeners.get("equip", IntType.ITEM.ordinal) == null) {
|
||||
EquipHandler().defineListeners()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@Test fun equipShouldFireEquipListeners() {
|
||||
|
|
@ -52,6 +57,26 @@ class EquipTests {
|
|||
Assertions.assertEquals(true, didRun)
|
||||
}
|
||||
|
||||
@Test fun fullInventoryUnequipShouldNotFireUnequipListeners() {
|
||||
var didRun = false
|
||||
val listener = object : InteractionListener {
|
||||
override fun defineListeners() {
|
||||
onUnequip(Items.ABYSSAL_WHIP_4151) { _, _ -> didRun = true; return@onUnequip true }
|
||||
}
|
||||
}
|
||||
listener.defineListeners()
|
||||
|
||||
TestUtils.getMockPlayer("fullunequip").use { player ->
|
||||
player.equipment.replace(Item(Items.ABYSSAL_WHIP_4151), EquipmentSlot.WEAPON.ordinal)
|
||||
player.inventory.add(Item(Items.CUP_OF_TEA_1978, player.inventory.capacity()))
|
||||
|
||||
EquipHandler.unequip(player, EquipmentSlot.WEAPON.ordinal, Items.ABYSSAL_WHIP_4151)
|
||||
|
||||
Assertions.assertEquals(false, didRun)
|
||||
Assertions.assertEquals(Items.ABYSSAL_WHIP_4151, player.equipment[EquipmentSlot.WEAPON.ordinal].id)
|
||||
}
|
||||
}
|
||||
|
||||
@Test fun equippingItemThatReplacesAnotherItemShouldCallUnequipListenersForTheReplacedItem() {
|
||||
var didRun = false
|
||||
val listener = object : InteractionListener {
|
||||
|
|
|
|||
|
|
@ -1,6 +1,7 @@
|
|||
package content.minigame.castlewars
|
||||
|
||||
import TestUtils
|
||||
import MockSession
|
||||
import content.minigame.castlewars.areas.CastleWarsGameArea
|
||||
import content.minigame.castlewars.areas.CastleWarsRespawnArea
|
||||
import content.minigame.castlewars.areas.CastleWarsWaitingArea
|
||||
|
|
@ -8,12 +9,18 @@ import core.api.*
|
|||
import core.game.component.Component
|
||||
import core.game.global.action.EquipHandler
|
||||
import core.game.interaction.*
|
||||
import core.game.node.Node
|
||||
import core.game.node.entity.player.Player
|
||||
import core.game.node.entity.skill.Skills
|
||||
import core.game.node.item.GroundItemManager
|
||||
import core.game.node.item.Item
|
||||
import core.game.node.scenery.Scenery
|
||||
import core.game.node.scenery.SceneryBuilder
|
||||
import core.game.system.timer.TimerRegistry
|
||||
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 org.junit.jupiter.api.AfterEach
|
||||
import org.junit.jupiter.api.Assertions.*
|
||||
import org.junit.jupiter.api.Test
|
||||
|
|
@ -30,6 +37,7 @@ class CastleWarsTests {
|
|||
if (InteractionListeners.get(CastleWars.saradominBackdoorOpen, IntType.SCENERY.ordinal, "lock") == null) {
|
||||
CastleWarsListeners().defineDestinationOverrides()
|
||||
CastleWarsListeners().defineListeners()
|
||||
CastleWarsFlagHandler().defineListeners()
|
||||
}
|
||||
if (InteractionListeners.get(CastleWars.cwExplosivePotion, IntType.ITEM.ordinal, "drop") == null) {
|
||||
CastleWarsItemHandler().defineListeners()
|
||||
|
|
@ -37,6 +45,9 @@ class CastleWarsTests {
|
|||
if (InterfaceListeners.get(CatapultConfig.INTERFACE_ID) == null) {
|
||||
CastleWarsCatapultInterface().defineInterfaceListeners()
|
||||
}
|
||||
if (!TimerRegistry.timerMap.containsKey("teleblock")) {
|
||||
TimerRegistry.registerTimer(Teleblock())
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -52,6 +63,7 @@ class CastleWarsTests {
|
|||
CastleWarsGameArea.clearSubstitutionState()
|
||||
CastleWarsRespawnArea.saradominPlayersInRespawnRoom.clear()
|
||||
CastleWarsRespawnArea.zamorakPlayersInRespawnRoom.clear()
|
||||
CastleWarsItemHandler.clearAllBarricades()
|
||||
CastleWarsGameState.resetGame()
|
||||
}
|
||||
|
||||
|
|
@ -97,18 +109,125 @@ class CastleWarsTests {
|
|||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
fun enteringWaitingRoomAfterVacancyReceivesSubstitutionOffer() {
|
||||
TestUtils.getMockPlayer("cwlateleaver").use { leaver ->
|
||||
TestUtils.getMockPlayer("cwlatesub").use { waiting ->
|
||||
CastleWarsGameArea.ticksLeftInGame = 100
|
||||
CastleWarsGameArea.saradominPlayers.add(leaver)
|
||||
|
||||
CastleWarsGameArea.removePlayerFromGame(leaver)
|
||||
waiting.location = CastleWarsWaitingArea.saradominWaitingRoom.randomWalkableLoc
|
||||
CastleWarsWaitingArea().areaEnter(waiting)
|
||||
|
||||
assertSubstitutionDialogue(waiting)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
fun decliningSubstitutionKeepsPlayerInWaitingRoom() {
|
||||
TestUtils.getMockPlayer("cwdecline").use { waiting ->
|
||||
CastleWarsGameArea.ticksLeftInGame = 100
|
||||
CastleWarsWaitingArea.waitingZamorakPlayers.add(waiting)
|
||||
TestUtils.getMockPlayer("cwdeclineleave").use { leaver ->
|
||||
TestUtils.getMockPlayer("cwdecline").use { waiting ->
|
||||
CastleWarsGameArea.ticksLeftInGame = 100
|
||||
CastleWarsGameArea.zamorakPlayers.add(leaver)
|
||||
CastleWarsWaitingArea.waitingZamorakPlayers.add(waiting)
|
||||
|
||||
CastleWarsGameArea.fillVacancy(CastleWarsTeam.ZAMORAK)
|
||||
CastleWarsGameArea.declineVacancyOffer(waiting, CastleWarsTeam.ZAMORAK)
|
||||
CastleWarsGameArea.removePlayerFromGame(leaver)
|
||||
CastleWarsGameArea.declineVacancyOffer(waiting, CastleWarsTeam.ZAMORAK)
|
||||
|
||||
assertTrue(CastleWarsWaitingArea.waitingZamorakPlayers.contains(waiting))
|
||||
assertFalse(CastleWarsGameArea.zamorakPlayers.contains(waiting))
|
||||
assertFalse(CastleWarsGameArea.claimVacancy(waiting, CastleWarsTeam.ZAMORAK))
|
||||
assertTrue(CastleWarsWaitingArea.waitingZamorakPlayers.contains(waiting))
|
||||
assertFalse(CastleWarsGameArea.zamorakPlayers.contains(waiting))
|
||||
assertFalse(CastleWarsGameArea.claimVacancy(waiting, CastleWarsTeam.ZAMORAK))
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
fun decliningLastSubstitutionOfferReissuesToAvailableWaitingPlayer() {
|
||||
TestUtils.getMockPlayer("cwdeclinereissueleave").use { leaver ->
|
||||
TestUtils.getMockPlayer("cwdeclinereissueold").use { declined ->
|
||||
TestUtils.getMockPlayer("cwdeclinereissuenew").use { replacement ->
|
||||
CastleWarsGameArea.ticksLeftInGame = 100
|
||||
CastleWarsGameArea.zamorakPlayers.add(leaver)
|
||||
CastleWarsWaitingArea.waitingZamorakPlayers.add(declined)
|
||||
|
||||
CastleWarsGameArea.removePlayerFromGame(leaver)
|
||||
assertSubstitutionDialogue(declined)
|
||||
|
||||
CastleWarsWaitingArea.waitingZamorakPlayers.add(replacement)
|
||||
CastleWarsGameArea.declineVacancyOffer(declined, CastleWarsTeam.ZAMORAK)
|
||||
|
||||
assertSubstitutionDialogue(replacement)
|
||||
assertFalse(CastleWarsGameArea.claimVacancy(declined, CastleWarsTeam.ZAMORAK))
|
||||
assertTrue(CastleWarsGameArea.claimVacancy(replacement, CastleWarsTeam.ZAMORAK))
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
fun leavingWaitingRoomClearsStaleSubstitutionOffer() {
|
||||
TestUtils.getMockPlayer("cwstaleleaver").use { leaver ->
|
||||
TestUtils.getMockPlayer("cwstalesub").use { stale ->
|
||||
TestUtils.getMockPlayer("cwfreshsub").use { fresh ->
|
||||
CastleWarsGameArea.ticksLeftInGame = 100
|
||||
CastleWarsGameArea.saradominPlayers.add(leaver)
|
||||
stale.location = CastleWarsWaitingArea.saradominWaitingRoom.randomWalkableLoc
|
||||
CastleWarsWaitingArea.waitingSaradominPlayers.add(stale)
|
||||
|
||||
CastleWarsGameArea.removePlayerFromGame(leaver)
|
||||
CastleWarsWaitingArea().exitArea(stale)
|
||||
CastleWarsWaitingArea.waitingSaradominPlayers.add(fresh)
|
||||
CastleWarsGameArea.fillVacancy(CastleWarsTeam.SARADOMIN)
|
||||
|
||||
assertSubstitutionDialogue(fresh)
|
||||
assertTrue(CastleWarsGameArea.claimVacancy(fresh, CastleWarsTeam.SARADOMIN))
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
fun leavingLastSubstitutionOfferReissuesToAvailableWaitingPlayer() {
|
||||
TestUtils.getMockPlayer("cwleavereissueleaver").use { leaver ->
|
||||
TestUtils.getMockPlayer("cwleavereissueold").use { stale ->
|
||||
TestUtils.getMockPlayer("cwleavereissuenew").use { replacement ->
|
||||
CastleWarsGameArea.ticksLeftInGame = 100
|
||||
CastleWarsGameArea.saradominPlayers.add(leaver)
|
||||
stale.location = CastleWarsWaitingArea.saradominWaitingRoom.randomWalkableLoc
|
||||
CastleWarsWaitingArea.waitingSaradominPlayers.add(stale)
|
||||
|
||||
CastleWarsGameArea.removePlayerFromGame(leaver)
|
||||
assertSubstitutionDialogue(stale)
|
||||
|
||||
CastleWarsWaitingArea.waitingSaradominPlayers.add(replacement)
|
||||
CastleWarsWaitingArea().exitArea(stale)
|
||||
|
||||
assertSubstitutionDialogue(replacement)
|
||||
assertTrue(CastleWarsGameArea.claimVacancy(replacement, CastleWarsTeam.SARADOMIN))
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
fun expiredSubstitutionOfferDoesNotBlockLaterVacancy() {
|
||||
TestUtils.getMockPlayer("cwexpireleaver").use { leaver ->
|
||||
TestUtils.getMockPlayer("cwignoredsub").use { ignored ->
|
||||
TestUtils.getMockPlayer("cwlatersub").use { later ->
|
||||
CastleWarsGameArea.ticksLeftInGame = 200
|
||||
CastleWarsGameArea.zamorakPlayers.add(leaver)
|
||||
CastleWarsWaitingArea.waitingZamorakPlayers.add(ignored)
|
||||
|
||||
CastleWarsGameArea.removePlayerFromGame(leaver)
|
||||
TestUtils.advanceTicks(CastleWarsConstants.VACANCY_OFFER_TIMEOUT_TICKS, false)
|
||||
CastleWarsWaitingArea.waitingZamorakPlayers.add(later)
|
||||
CastleWarsGameArea.fillVacancy(CastleWarsTeam.ZAMORAK)
|
||||
|
||||
assertSubstitutionDialogue(later)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -201,13 +320,16 @@ class CastleWarsTests {
|
|||
}
|
||||
|
||||
@Test
|
||||
fun matchEndReturnsLifecycleToIdle() {
|
||||
fun matchEndStartsPostGameCooldown() {
|
||||
CastleWarsGameArea.ticksLeftInGame = 1
|
||||
|
||||
CastleWarsGameArea().tick()
|
||||
|
||||
assertEquals(-1, CastleWarsGameArea.ticksLeftInGame)
|
||||
assertEquals(-1, CastleWarsWaitingArea.gameStartCountdown)
|
||||
assertEquals(
|
||||
CastleWars.gameCooldownMinutes * CastleWarsConstants.TICKS_PER_MINUTE,
|
||||
CastleWarsWaitingArea.gameStartCountdown
|
||||
)
|
||||
}
|
||||
|
||||
@Test
|
||||
|
|
@ -256,6 +378,91 @@ class CastleWarsTests {
|
|||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
fun endedGameClearsLoggedOutPlayerState() {
|
||||
TestUtils.getMockPlayer("cwendedlogout").use { player ->
|
||||
player.location = Location.create(2401, 3101, 0)
|
||||
CastleWarsGameArea.ticksLeftInGame = 500
|
||||
CastleWarsGameArea.saradominPlayers.add(player)
|
||||
player.inventory.add(Item(CastleWars.cwBandages))
|
||||
registerTimer(player, spawnTimer("teleblock", 100, false))
|
||||
|
||||
CastleWarsGameArea().logout(player)
|
||||
assertEquals(CastleWarsTeam.SARADOMIN.name, getAttribute(player, CastleWarsConstants.ATTR_LOGOUT_TEAM, ""))
|
||||
|
||||
CastleWarsGameArea.endCurrentGame(startCooldown = false)
|
||||
|
||||
assertFalse(CastleWarsGameArea.loggedOutPlayers.containsKey(CastleWarsGameArea.playerKey(player)))
|
||||
assertEquals("", getAttribute(player, CastleWarsConstants.ATTR_LOGOUT_TEAM, ""))
|
||||
assertFalse(player.inventory.contains(CastleWars.cwBandages, 1))
|
||||
assertFalse(hasTimerActive(player, "teleblock"))
|
||||
assertTrue(CastleWars.lobbyBankArea.insideBorder(player.location))
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
fun expiredLogoutCleanupClearsSavedStateBeforePlayerRelogs() {
|
||||
TestUtils.getMockPlayer("cwexpiredlogout").use { player ->
|
||||
TestUtils.getMockPlayer("cwexpiredfirstsub").use { firstReplacement ->
|
||||
TestUtils.getMockPlayer("cwexpiredsecondsub").use { secondReplacement ->
|
||||
val logoutLocation = Location.create(2400, 3100, 0)
|
||||
player.location = logoutLocation
|
||||
CastleWarsGameArea.ticksLeftInGame = CastleWarsConstants.PLAYER_CLEANUP_INTERVAL_TICKS + 1
|
||||
CastleWarsGameArea.saradominPlayers.add(player)
|
||||
CastleWarsGameArea.loggedOutPlayers[CastleWarsGameArea.playerKey(player)] =
|
||||
CastleWarsGameArea.Companion.LogoutRecord(
|
||||
player,
|
||||
CastleWarsTeam.SARADOMIN,
|
||||
getWorldTicks() - CastleWarsConstants.LOGOUT_GRACE_PERIOD_TICKS,
|
||||
logoutLocation
|
||||
)
|
||||
setAttribute(player, CastleWarsConstants.ATTR_LOGOUT_TEAM, CastleWarsTeam.SARADOMIN.name)
|
||||
setAttribute(
|
||||
player,
|
||||
CastleWarsConstants.ATTR_LOGOUT_TIME_MILLIS,
|
||||
System.currentTimeMillis() - CastleWarsConstants.LOGOUT_GRACE_PERIOD_MILLIS - 1
|
||||
)
|
||||
setAttribute(player, CastleWarsConstants.ATTR_LOGOUT_LOCATION, Location.create(logoutLocation))
|
||||
CastleWarsWaitingArea.waitingSaradominPlayers.add(firstReplacement)
|
||||
|
||||
CastleWarsGameArea().tick()
|
||||
|
||||
assertEquals("", getAttribute(player, CastleWarsConstants.ATTR_LOGOUT_TEAM, ""))
|
||||
assertFalse(CastleWarsGameArea.loggedOutPlayers.containsKey(CastleWarsGameArea.playerKey(player)))
|
||||
assertSubstitutionDialogue(firstReplacement)
|
||||
assertTrue(CastleWarsGameArea.claimVacancy(firstReplacement, CastleWarsTeam.SARADOMIN))
|
||||
|
||||
CastleWarsWaitingArea.waitingSaradominPlayers.add(secondReplacement)
|
||||
CastleWarsGameArea().login(player)
|
||||
|
||||
assertFalse(secondReplacement.dialogueInterpreter.dialogue?.file is CastleWarsSubstitutionDialogue)
|
||||
assertFalse(CastleWarsGameArea.claimVacancy(secondReplacement, CastleWarsTeam.SARADOMIN))
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
fun staleLogoutAttributesAfterEndedGameRestoreToLobbyOnLogin() {
|
||||
TestUtils.getMockPlayer("cwstalelogin").use { player ->
|
||||
player.location = Location.create(2401, 3101, 0)
|
||||
CastleWarsGameArea.ticksLeftInGame = -1
|
||||
setAttribute(player, CastleWarsConstants.ATTR_LOGOUT_TEAM, CastleWarsTeam.SARADOMIN.name)
|
||||
setAttribute(player, CastleWarsConstants.ATTR_LOGOUT_TIME_MILLIS, System.currentTimeMillis())
|
||||
setAttribute(player, CastleWarsConstants.ATTR_LOGOUT_LOCATION, Location.create(player.location))
|
||||
player.inventory.add(Item(CastleWars.cwBandages))
|
||||
registerTimer(player, spawnTimer("teleblock", 100, false))
|
||||
|
||||
CastleWarsGameArea().login(player)
|
||||
|
||||
assertFalse(CastleWarsWaitingArea.waitingSaradominPlayers.contains(player))
|
||||
assertEquals("", getAttribute(player, CastleWarsConstants.ATTR_LOGOUT_TEAM, ""))
|
||||
assertFalse(player.inventory.contains(CastleWars.cwBandages, 1))
|
||||
assertFalse(hasTimerActive(player, "teleblock"))
|
||||
assertTrue(CastleWars.lobbyBankArea.insideBorder(player.location))
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
fun activeGameLogoutAfterTwoMinutesReturnsPlayerToTeamWaitingRoom() {
|
||||
TestUtils.getMockPlayer("cwslowrelog").use { player ->
|
||||
|
|
@ -295,6 +502,21 @@ class CastleWarsTests {
|
|||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
fun spawnRoomLogoutClearsRespawnRoomTracking() {
|
||||
TestUtils.getMockPlayer("cwspawntrack").use { player ->
|
||||
player.location = CastleWarsRespawnArea.saradominRespawnRoom.randomWalkableLoc
|
||||
CastleWarsGameArea.ticksLeftInGame = 500
|
||||
CastleWarsGameArea.saradominPlayers.add(player)
|
||||
CastleWarsRespawnArea().areaEnter(player)
|
||||
|
||||
CastleWarsRespawnArea().logout(player)
|
||||
|
||||
assertFalse(CastleWarsRespawnArea.saradominPlayersInRespawnRoom.containsKey(player))
|
||||
assertFalse(CastleWarsRespawnArea.zamorakPlayersInRespawnRoom.containsKey(player))
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
fun waitingRoomLogoutReturnsPlayerToLobbyAndLosesQueuePosition() {
|
||||
TestUtils.getMockPlayer("cwwaitlogout").use { player ->
|
||||
|
|
@ -410,6 +632,122 @@ class CastleWarsTests {
|
|||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
fun loggedOutGracePlayerReceivesEndGameTicket() {
|
||||
TestUtils.getMockPlayer("cwlogoutticket").use { player ->
|
||||
TestUtils.getMockPlayer("cwlogoutticketz").use { opponent ->
|
||||
CastleWarsGameArea.ticksLeftInGame = 100
|
||||
CastleWarsGameArea.saradominPlayers.add(player)
|
||||
CastleWarsGameArea.zamorakPlayers.add(opponent)
|
||||
CastleWarsGameArea.loggedOutPlayers[CastleWarsGameArea.playerKey(player)] =
|
||||
CastleWarsGameArea.Companion.LogoutRecord(
|
||||
player,
|
||||
CastleWarsTeam.SARADOMIN,
|
||||
getWorldTicks(),
|
||||
Location.create(2400, 3100, 0)
|
||||
)
|
||||
player.isActive = false
|
||||
try {
|
||||
CastleWarsGameArea.endCurrentGame(startCooldown = false)
|
||||
} finally {
|
||||
player.isActive = true
|
||||
}
|
||||
|
||||
assertEquals(1, player.inventory.getAmount(CastleWars.cwTicketRewardCurrency))
|
||||
assertTrue(CastleWars.lobbyBankArea.insideBorder(player.location))
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
fun gameEndCleansCastleWarsSuppliesBeforeAwardingTickets() {
|
||||
TestUtils.getMockPlayer("cwfullticket").use { player ->
|
||||
TestUtils.getMockPlayer("cwfullticketz").use { opponent ->
|
||||
player.inventory.add(Item(CastleWars.cwBandages, player.inventory.capacity()))
|
||||
CastleWarsGameArea.saradominPlayers.add(player)
|
||||
CastleWarsGameArea.zamorakPlayers.add(opponent)
|
||||
|
||||
CastleWarsGameState.endGame()
|
||||
|
||||
assertEquals(1, player.inventory.getAmount(CastleWars.cwTicketRewardCurrency))
|
||||
assertFalse(player.inventory.contains(CastleWars.cwBandages, 1))
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
fun matchEndClearsCastleWarsGroundItemsInArena() {
|
||||
TestUtils.getMockPlayer("cwgroundcleanup").use { player ->
|
||||
val arenaItem = GroundItemManager.create(
|
||||
Item(CastleWars.cwBandages),
|
||||
Location.create(2400, 3100, 0),
|
||||
player
|
||||
)
|
||||
|
||||
assertTrue(GroundItemManager.getItems().contains(arenaItem))
|
||||
|
||||
CastleWarsGameArea.endCurrentGame(startCooldown = false)
|
||||
|
||||
assertFalse(GroundItemManager.getItems().contains(arenaItem))
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
fun fullInventoryEndGameTicketsDropAtLobbyBank() {
|
||||
TestUtils.getMockPlayer("cwfullnormal").use { player ->
|
||||
TestUtils.getMockPlayer("cwfullnormalz").use { opponent ->
|
||||
player.location = Location.create(2400, 3100, 0)
|
||||
player.inventory.add(Item(Items.CUP_OF_TEA_1978, player.inventory.capacity()))
|
||||
CastleWarsGameArea.saradominPlayers.add(player)
|
||||
CastleWarsGameArea.zamorakPlayers.add(opponent)
|
||||
|
||||
CastleWarsGameState.endGame()
|
||||
|
||||
val droppedTickets = GroundItemManager.getItems().filter {
|
||||
it.id == CastleWars.cwTicketRewardCurrency && it.droppedBy(player)
|
||||
}
|
||||
assertTrue(droppedTickets.isNotEmpty())
|
||||
assertTrue(droppedTickets.all { CastleWars.lobbyBankArea.insideBorder(it.location) })
|
||||
droppedTickets.forEach { GroundItemManager.destroy(it) }
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
fun gameEndCleansCarriedFlagsWithoutDropOrReturnMessages() {
|
||||
TestUtils.getMockPlayer("cwquietflag").use { carrier ->
|
||||
TestUtils.getMockPlayer("cwquietflagz").use { opponent ->
|
||||
val carrierSession = carrier.session as MockSession
|
||||
val opponentSession = opponent.session as MockSession
|
||||
|
||||
CastleWarsGameArea.ticksLeftInGame = 100
|
||||
CastleWarsGameArea.saradominPlayers.add(carrier)
|
||||
CastleWarsGameArea.zamorakPlayers.add(opponent)
|
||||
assertTrue(CastleWarsGameState.pickUpFlag(carrier, CastleWarsTeam.ZAMORAK))
|
||||
carrierSession.clear()
|
||||
opponentSession.clear()
|
||||
|
||||
CastleWarsGameState.endGame()
|
||||
|
||||
val messages = gameMessagePayloads(carrierSession) + gameMessagePayloads(opponentSession)
|
||||
assertFalse(messages.any { it.contains("flag has been dropped") })
|
||||
assertFalse(messages.any { it.contains("flag has been returned") })
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
fun cleanupRemovesCastleWarsTeleblock() {
|
||||
TestUtils.getMockPlayer("cwcleanblock").use { player ->
|
||||
registerTimer(player, spawnTimer("teleblock", 100, false))
|
||||
assertTrue(hasTimerActive(player, "teleblock"))
|
||||
|
||||
CastleWarsGameState.restoreAndCleanupPlayer(player)
|
||||
|
||||
assertFalse(hasTimerActive(player, "teleblock"))
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
fun joinPortalRejectsFoodAndInventoryHelmets() {
|
||||
withCastleWarsEnabled {
|
||||
|
|
@ -548,6 +886,42 @@ class CastleWarsTests {
|
|||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
fun fullInventoryFlagCarrierCanUnequipToDropFlag() {
|
||||
TestUtils.getMockPlayer("cwfulldrop").use { carrier ->
|
||||
CastleWarsGameArea.ticksLeftInGame = 100
|
||||
CastleWarsGameArea.saradominPlayers.add(carrier)
|
||||
|
||||
assertTrue(CastleWarsGameState.pickUpFlag(carrier, CastleWarsTeam.ZAMORAK))
|
||||
carrier.inventory.add(Item(Items.CUP_OF_TEA_1978, carrier.inventory.capacity()))
|
||||
|
||||
EquipHandler.unequip(carrier, EquipmentSlot.WEAPON.ordinal, CastleWars.zamorakFlag)
|
||||
|
||||
assertFalse(CastleWarsGameState.isCarryingFlag(carrier))
|
||||
assertNull(carrier.equipment[EquipmentSlot.WEAPON.ordinal])
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
fun equippingWeaponWhileCarryingFlagDropsFlagAndEquipsWeapon() {
|
||||
TestUtils.getMockPlayer("cwflagswap").use { carrier ->
|
||||
CastleWarsGameArea.ticksLeftInGame = 100
|
||||
CastleWarsGameArea.saradominPlayers.add(carrier)
|
||||
carrier.inventory.add(Item(Items.BRONZE_SWORD_1277))
|
||||
|
||||
assertTrue(CastleWarsGameState.pickUpFlag(carrier, CastleWarsTeam.ZAMORAK))
|
||||
|
||||
InteractionListeners.run(
|
||||
Items.BRONZE_SWORD_1277, IntType.ITEM, "wield", carrier, carrier.inventory[0]
|
||||
)
|
||||
|
||||
assertFalse(CastleWarsGameState.isCarryingFlag(carrier))
|
||||
assertEquals(Items.BRONZE_SWORD_1277, carrier.equipment[EquipmentSlot.WEAPON.ordinal]?.id)
|
||||
assertFalse(carrier.inventory.contains(CastleWars.zamorakFlag, 1))
|
||||
assertEquals(FlagState.DROPPED, CastleWarsGameState.zamorakFlagState)
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
fun ownTeamBackdoorLockOptionIsHandled() {
|
||||
assertNotNull(
|
||||
|
|
@ -558,6 +932,27 @@ class CastleWarsTests {
|
|||
)
|
||||
}
|
||||
|
||||
@Test
|
||||
fun resetGameRestoresOpenedBackdoorsToClosedScenery() {
|
||||
val loc = (2418..2432).asSequence()
|
||||
.flatMap { x -> (3070..3085).asSequence().map { y -> Location.create(x, y, 0) } }
|
||||
.first { RegionManager.getObject(it) == null }
|
||||
val closed = Scenery(CastleWars.saradominBackdoor, loc, 10, 0)
|
||||
val open = Scenery(CastleWars.saradominBackdoorOpen, loc, 10, 0)
|
||||
try {
|
||||
SceneryBuilder.add(closed)
|
||||
SceneryBuilder.replace(requireNotNull(RegionManager.getObject(loc)), open, -1, true)
|
||||
|
||||
assertEquals(CastleWars.saradominBackdoorOpen, RegionManager.getObject(loc)?.id)
|
||||
|
||||
CastleWarsGameState.resetGame()
|
||||
|
||||
assertEquals(CastleWars.saradominBackdoor, RegionManager.getObject(loc)?.id)
|
||||
} finally {
|
||||
RegionManager.getObject(loc)?.let { SceneryBuilder.remove(it) }
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
fun backdoorPicklockUsesLinearThievingCurveFromLevelOneToNinetyNine() {
|
||||
assertEquals(6.3, getBackdoorPicklockSuccessChance(1), 0.0001)
|
||||
|
|
@ -595,6 +990,7 @@ class CastleWarsTests {
|
|||
10,
|
||||
0
|
||||
)
|
||||
ensureCatapultObject(catapult)
|
||||
val operateCatapult = InteractionListeners.get(
|
||||
catapult.id, IntType.SCENERY.ordinal, "operate"
|
||||
)!!
|
||||
|
|
@ -638,6 +1034,84 @@ class CastleWarsTests {
|
|||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
fun staleCatapultInterfaceCannotFireAfterCatapultIsDestroyed() {
|
||||
TestUtils.getMockPlayer("cwcatstale").use { shooter ->
|
||||
val catapult = Scenery(
|
||||
CastleWars.getOperationalCatapultId(CastleWarsTeam.SARADOMIN),
|
||||
CastleWarsGameState.saradominCatapultLocation,
|
||||
10,
|
||||
0
|
||||
)
|
||||
ensureCatapultObject(catapult)
|
||||
val operateCatapult = InteractionListeners.get(
|
||||
catapult.id, IntType.SCENERY.ordinal, "operate"
|
||||
)!!
|
||||
CastleWarsGameArea.saradominPlayers.add(shooter)
|
||||
shooter.inventory.add(Item(CastleWars.cwRock))
|
||||
|
||||
operateCatapult(shooter, catapult)
|
||||
assertTrue(shooter.interfaceManager.hasMainComponent(CatapultConfig.INTERFACE_ID))
|
||||
CastleWarsGameState.destroyCatapult(CastleWarsTeam.SARADOMIN)
|
||||
|
||||
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)
|
||||
shooter.inventory.add(Item(CastleWars.cwRock))
|
||||
shooter.setAttribute(CatapultConfig.ATTR_LOCATION, Location.create(2400, 3090, 0))
|
||||
shooter.interfaceManager.open(Component(CatapultConfig.INTERFACE_ID))
|
||||
|
||||
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 extinguishedCatapultFirePulseDoesNotDestroyRelitCatapult() {
|
||||
TestUtils.getMockPlayer("cwcatfirez").use { attacker ->
|
||||
TestUtils.getMockPlayer("cwcatfires").use { defender ->
|
||||
val catapult = Scenery(
|
||||
CastleWars.getOperationalCatapultId(CastleWarsTeam.SARADOMIN),
|
||||
CastleWarsGameState.saradominCatapultLocation,
|
||||
10,
|
||||
0
|
||||
)
|
||||
ensureCatapultObject(catapult)
|
||||
CastleWarsGameArea.zamorakPlayers.add(attacker)
|
||||
CastleWarsGameArea.saradominPlayers.add(defender)
|
||||
CastleWarsGameArea.ticksLeftInGame = 100
|
||||
defender.inventory.add(Item(Items.BUCKET_OF_WATER_1929))
|
||||
|
||||
assertTrue(runUseWith(Item(Items.TINDERBOX_590), catapult, IntType.SCENERY, attacker))
|
||||
TestUtils.advanceTicks(25, false)
|
||||
assertTrue(runUseWith(defender.inventory[0], catapult, IntType.SCENERY, defender))
|
||||
assertFalse(CastleWarsGameState.isCatapultOnFire(CastleWarsTeam.SARADOMIN))
|
||||
|
||||
assertTrue(runUseWith(Item(Items.TINDERBOX_590), catapult, IntType.SCENERY, attacker))
|
||||
TestUtils.advanceTicks(25, false)
|
||||
|
||||
assertTrue(CastleWarsGameState.isCatapultOperational(CastleWarsTeam.SARADOMIN))
|
||||
assertTrue(CastleWarsGameState.isCatapultOnFire(CastleWarsTeam.SARADOMIN))
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
fun climbingRopeOnPlayerTileCanBeClimbed() {
|
||||
val rope = Scenery(CastleWars.cwCastleClimbingRope, Location.create(2420, 3078, 1), 4, 0)
|
||||
|
|
@ -692,6 +1166,33 @@ class CastleWarsTests {
|
|||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
fun extinguishingBarricadeStopsPreviousBurnPulseBeforeRelight() {
|
||||
TestUtils.getMockPlayer("cwbarrfire").use { player ->
|
||||
val barricade = CastleWarsBarricadeNPC.spawn(Location.create(2400, 3100, 0), CastleWarsTeam.SARADOMIN)
|
||||
CastleWarsGameArea.ticksLeftInGame = 100
|
||||
CastleWarsItemHandler.saradominBarricades.add(barricade)
|
||||
player.inventory.add(Item(Items.BUCKET_OF_WATER_1929))
|
||||
|
||||
assertTrue(runUseWith(Item(Items.TINDERBOX_590), barricade, IntType.NPC, player))
|
||||
val firstPulse = CastleWarsItemHandler.burningBarricadePulses[barricade]
|
||||
assertNotNull(firstPulse)
|
||||
assertTrue(firstPulse!!.isRunning)
|
||||
|
||||
assertTrue(runUseWith(player.inventory[0], barricade, IntType.NPC, player))
|
||||
|
||||
assertFalse(firstPulse.isRunning)
|
||||
assertFalse(CastleWarsItemHandler.burningBarricadePulses.containsKey(barricade))
|
||||
|
||||
assertTrue(runUseWith(Item(Items.TINDERBOX_590), barricade, IntType.NPC, player))
|
||||
val secondPulse = CastleWarsItemHandler.burningBarricadePulses[barricade]
|
||||
assertNotNull(secondPulse)
|
||||
assertNotSame(firstPulse, secondPulse)
|
||||
assertFalse(firstPulse.isRunning)
|
||||
barricade.clear()
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
fun matchStartActivatesBraceletEffectsAndStealFromOption() {
|
||||
TestUtils.getMockPlayer("cwbrace1").use { braceletPlayer ->
|
||||
|
|
@ -716,6 +1217,24 @@ class CastleWarsTests {
|
|||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
fun substitutionActivatesBraceletEffects() {
|
||||
TestUtils.getMockPlayer("cwsubbraceleave").use { leaver ->
|
||||
TestUtils.getMockPlayer("cwsubbrace").use { substitute ->
|
||||
substitute.equipment.replace(Item(Items.CASTLEWAR_BRACE3_11079), EquipmentSlot.HANDS.ordinal)
|
||||
CastleWarsGameArea.ticksLeftInGame = 100
|
||||
CastleWarsGameArea.saradominPlayers.add(leaver)
|
||||
CastleWarsWaitingArea.waitingSaradominPlayers.add(substitute)
|
||||
|
||||
CastleWarsGameArea.removePlayerFromGame(leaver)
|
||||
|
||||
assertTrue(CastleWarsGameArea.claimVacancy(substitute, CastleWarsTeam.SARADOMIN))
|
||||
assertEquals(CastleWarsTeam.SARADOMIN, substitute.activeCastleWarsBraceletTeam())
|
||||
assertNotNull(substitute.interaction[Option._P_STEAL_FROM.index])
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
fun bandagesHealTenPercentRestoreRunEnergyAndCurePoison() {
|
||||
TestUtils.getMockPlayer("cwbandage").use { player ->
|
||||
|
|
@ -738,6 +1257,36 @@ class CastleWarsTests {
|
|||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
fun bandagesCannotBeUsedOutsideActiveCastleWarsGame() {
|
||||
TestUtils.getMockPlayer("cwbandageoutside").use { player ->
|
||||
player.skills.setStaticLevel(Skills.HITPOINTS, 99)
|
||||
player.skills.lifepoints = 50
|
||||
player.inventory.add(Item(CastleWars.cwBandages))
|
||||
|
||||
InteractionListeners.run(
|
||||
CastleWars.cwBandages, IntType.ITEM, "Heal", player, player.inventory[0]
|
||||
)
|
||||
|
||||
assertEquals(50, player.skills.lifepoints)
|
||||
assertTrue(player.inventory.contains(CastleWars.cwBandages, 1))
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
fun castleWarsTeleblockDoesNotSendChatboxMessage() {
|
||||
TestUtils.getMockPlayer("cwsilentteleblock").use { player ->
|
||||
val session = player.session as MockSession
|
||||
player.location = CastleWarsWaitingArea.saradominWaitingRoom.randomWalkableLoc
|
||||
session.clear()
|
||||
|
||||
CastleWarsWaitingArea().areaEnter(player)
|
||||
|
||||
assertTrue(hasTimerActive(player, "teleblock"))
|
||||
assertFalse(session.hasPacketReady(70))
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
fun activeBraceletBandagesHealTeammatesForFifteenPercent() {
|
||||
TestUtils.getMockPlayer("cwhealer").use { healer ->
|
||||
|
|
@ -815,6 +1364,27 @@ class CastleWarsTests {
|
|||
assertTrue(player.dialogueInterpreter.dialogue.file is CastleWarsSubstitutionDialogue)
|
||||
}
|
||||
|
||||
private fun ensureCatapultObject(catapult: Scenery) {
|
||||
val current = RegionManager.getObject(catapult.location)
|
||||
if (current != null && current.id in CastleWars.allCatapultIds) {
|
||||
if (current.id != catapult.id) {
|
||||
SceneryBuilder.replace(current, catapult)
|
||||
}
|
||||
return
|
||||
}
|
||||
SceneryBuilder.add(catapult)
|
||||
}
|
||||
|
||||
private fun runUseWith(used: Item, with: Node, type: IntType, player: Player): Boolean {
|
||||
val handler = InteractionListeners.get(used.id, with.id, type.ordinal)
|
||||
assertNotNull(handler)
|
||||
return handler!!.invoke(player, used, with)
|
||||
}
|
||||
|
||||
private fun gameMessagePayloads(session: MockSession): List<String> {
|
||||
return session.getPacketsWithOpcode(70).map { String(it, Charsets.ISO_8859_1) }
|
||||
}
|
||||
|
||||
private fun withCastleWarsEnabled(block: () -> Unit) {
|
||||
val settings = GameWorld.settings!!
|
||||
val original = settings.enable_castle_wars
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue