Started implementing Castle Wars (not ready to enable yet)
This commit is contained in:
@@ -0,0 +1,81 @@
|
||||
package rs09.game.content.activity.castlewars
|
||||
|
||||
import core.game.world.map.zone.ZoneBorders
|
||||
import org.rs09.consts.Items
|
||||
import org.rs09.consts.NPCs
|
||||
import org.rs09.consts.Scenery
|
||||
|
||||
object CastleWars {
|
||||
// Scenery IDs
|
||||
const val joinSaradominTeamPortal: Int = Scenery.SARADOMIN_PORTAL_4387
|
||||
const val joinZamorakTeamPortal: Int = Scenery.ZAMORAK_PORTAL_4388
|
||||
const val saradominLeaveLobbyPortal: Int = Scenery.PORTAL_4389
|
||||
const val zamorakLeaveLobbyPortal: Int = Scenery.PORTAL_4390
|
||||
const val joinGuthixTeamPortal: Int = Scenery.GUTHIX_PORTAL_4408
|
||||
const val cwCastleClimbingRope: Int = Scenery.CLIMBING_ROPE_36312
|
||||
const val castleWaterTap: Int = Scenery.TAP_4482
|
||||
const val cwSteppingStones: Int = Scenery.STEPPING_STONE_4411
|
||||
|
||||
// Map of the item table scenery id to the item id they give
|
||||
val cwTableItemRewardMap = mapOf(
|
||||
Scenery.TABLE_36573 to Items.TOOLKIT_4051, // Saradomin Toolkit Table
|
||||
Scenery.TABLE_36580 to Items.TOOLKIT_4051, // Zamorak Toolkit Table
|
||||
Scenery.TABLE_36574 to Items.ROCK_4043, // Saradomin Rock Table
|
||||
Scenery.TABLE_36581 to Items.ROCK_4043, // Zamorak Rock Table
|
||||
Scenery.TABLE_36575 to Items.BARRICADE_4053, // Saradomin Barricade Table
|
||||
Scenery.TABLE_36582 to Items.BARRICADE_4053, // Zamorak Barricade Table
|
||||
Scenery.TABLE_36576 to Items.CLIMBING_ROPE_4047, // Saradomin Climbing Rope Table
|
||||
Scenery.TABLE_36583 to Items.CLIMBING_ROPE_4047, // Zamorak Climbing Rope Table
|
||||
Scenery.TABLE_36577 to Items.EXPLOSIVE_POTION_4045, // Saradomin Explosive Potion Table
|
||||
Scenery.TABLE_36584 to Items.EXPLOSIVE_POTION_4045, // Zamorak Explosive Potion Table
|
||||
Scenery.TABLE_36578 to Items.BRONZE_PICKAXE_1265, // Saradomin Pickaxe Table
|
||||
Scenery.TABLE_36585 to Items.BRONZE_PICKAXE_1265, // Zamorak Pickaxe Table
|
||||
Scenery.TABLE_36579 to Items.BANDAGES_4049, // Saradomin Bandages Table
|
||||
Scenery.TABLE_36586 to Items.BANDAGES_4049 // Zamorak Bandages Table
|
||||
)
|
||||
|
||||
val cwCastleBattlementsMap = mapOf(
|
||||
Scenery.BATTLEMENTS_4446 to Scenery.BATTLEMENTS_36313,
|
||||
Scenery.BATTLEMENTS_4447 to Scenery.BATTLEMENTS_36314
|
||||
)
|
||||
|
||||
// Item IDs
|
||||
const val saradominTeamHoodedCloak: Int = Items.HOODED_CLOAK_4041
|
||||
const val zamorakTeamHoodedCloak: Int = Items.HOODED_CLOAK_4042
|
||||
const val saradominFlag: Int = Items.SARADOMIN_BANNER_4037 // Might be 4038
|
||||
const val zamorakFlag: Int = Items.ZAMORAK_BANNER_4039 // Might be 4040
|
||||
const val cwPickaxe: Int = Items.BRONZE_PICKAXE_1265
|
||||
const val cwRock: Int = Items.ROCK_4043
|
||||
const val cwExplosivePotion: Int = Items.EXPLOSIVE_POTION_4045
|
||||
const val cwClimbingRope: Int = Items.CLIMBING_ROPE_4047
|
||||
const val cwBandages: Int = Items.BANDAGES_4049
|
||||
const val cwToolkit: Int = Items.TOOLKIT_4051
|
||||
const val cwBarricade: Int = Items.BARRICADE_4053
|
||||
const val cwManualBook: Int = Items.CASTLEWARS_MANUAL_4055
|
||||
const val cwTicketRewardCurrency: Int = Items.CASTLE_WARS_TICKET_4067
|
||||
|
||||
// NPC IDs
|
||||
const val sheep = NPCs.SHEEP_1529
|
||||
const val imp = NPCs.IMP_1531
|
||||
const val rabbit = NPCs.RABBIT_1530
|
||||
const val unknownCwarsBarricade1 = NPCs.BARRICADE_1532
|
||||
const val unknownCwarsBarricadeOnFire = NPCs.BARRICADE_1533
|
||||
const val unknownCwarsBarricade2 = NPCs.BARRICADE_1534
|
||||
const val unknownCwarsBarricade3 = NPCs.BARRICADE_1535
|
||||
|
||||
// Locations
|
||||
val lobbyBankArea: ZoneBorders = ZoneBorders(2440, 3092, 2444, 3086, 0)
|
||||
|
||||
// Strings
|
||||
const val saradominName = "Saradomin"
|
||||
const val zamorakName = "Zamorak"
|
||||
const val guthixName = "Guthix"
|
||||
const val portalAttribute = "castlewars_portal"
|
||||
const val invFullMessage = "Your inventory is too full to hold any more "
|
||||
|
||||
// Ints
|
||||
const val gameTimeMinutes = 20
|
||||
const val gameCooldownMinutes = 5
|
||||
const val ropeAliveTicks = -1 // TODO: Find amount of time until wall reverts to non rope state
|
||||
|
||||
}
|
||||
@@ -0,0 +1,190 @@
|
||||
package rs09.game.content.activity.castlewars
|
||||
|
||||
import core.api.*
|
||||
import content.global.skill.agility.AgilityHandler
|
||||
import content.global.skill.summoning.familiar.BurdenBeast
|
||||
import core.cache.def.impl.ItemDefinition
|
||||
import core.game.container.Container
|
||||
import core.game.interaction.IntType
|
||||
import core.game.interaction.InteractionListener
|
||||
import core.game.node.entity.player.Player
|
||||
import core.game.node.entity.player.link.TeleportManager
|
||||
import core.game.node.entity.player.link.audio.Audio
|
||||
import core.game.node.item.Item
|
||||
import core.game.node.scenery.Scenery
|
||||
import core.game.node.scenery.SceneryBuilder
|
||||
import core.game.world.GameWorld
|
||||
import core.game.world.map.Location
|
||||
import core.game.world.update.flag.context.Animation
|
||||
import core.tools.Log
|
||||
import org.rs09.consts.Items
|
||||
import rs09.game.content.activity.castlewars.areas.CastleWarsWaitingArea
|
||||
import java.util.*
|
||||
|
||||
@Suppress("unused")
|
||||
class CastleWarsListeners : InteractionListener {
|
||||
|
||||
override fun defineListeners() {
|
||||
|
||||
// Scenery Interactions - PORTALS
|
||||
on(CastleWars.joinSaradominTeamPortal, IntType.SCENERY, "Enter") { player, _ ->
|
||||
if (GameWorld.settings?.enable_castle_wars != true) return@on false
|
||||
if (joinError(player)) return@on true
|
||||
// MapArea handles joining properties
|
||||
player.properties.teleportLocation = CastleWarsWaitingArea.saradominWaitingRoom.randomWalkableLoc
|
||||
player.setAttribute(CastleWars.portalAttribute, CastleWars.saradominName)
|
||||
return@on true
|
||||
}
|
||||
on(CastleWars.joinZamorakTeamPortal, IntType.SCENERY, "Enter") { player, _ ->
|
||||
if (GameWorld.settings?.enable_castle_wars != true) return@on false
|
||||
if (joinError(player)) return@on true
|
||||
// MapArea handles joining properties
|
||||
player.properties.teleportLocation = CastleWarsWaitingArea.zamorakWaitingRoom.randomWalkableLoc
|
||||
player.setAttribute(CastleWars.portalAttribute, CastleWars.zamorakName)
|
||||
return@on true
|
||||
}
|
||||
on(CastleWars.saradominLeaveLobbyPortal, IntType.SCENERY, "Exit") { player, _ ->
|
||||
player.properties.teleportLocation = CastleWars.lobbyBankArea.randomWalkableLoc
|
||||
// MapArea handles leaving properties
|
||||
return@on true
|
||||
}
|
||||
on(CastleWars.zamorakLeaveLobbyPortal, IntType.SCENERY, "Exit") { player, _ ->
|
||||
player.properties.teleportLocation = CastleWars.lobbyBankArea.randomWalkableLoc
|
||||
// MapArea handles leaving properties
|
||||
return@on true
|
||||
}
|
||||
on(CastleWars.joinGuthixTeamPortal, IntType.SCENERY, "Enter") { player, _ ->
|
||||
if (GameWorld.settings?.enable_castle_wars != true) return@on false
|
||||
if (joinError(player)) return@on true
|
||||
|
||||
// Join the team with fewer players, if they're equal, join randomly
|
||||
if (CastleWarsWaitingArea.waitingSaradominPlayers.size < CastleWarsWaitingArea.waitingZamorakPlayers.size) {
|
||||
player.properties.teleportLocation = CastleWarsWaitingArea.saradominWaitingRoom.randomWalkableLoc
|
||||
} else if (CastleWarsWaitingArea.waitingSaradominPlayers.size > CastleWarsWaitingArea.waitingZamorakPlayers.size) {
|
||||
player.properties.teleportLocation = CastleWarsWaitingArea.zamorakWaitingRoom.randomWalkableLoc
|
||||
} else {
|
||||
if (Math.random() < 0.5) {
|
||||
player.properties.teleportLocation = CastleWarsWaitingArea.saradominWaitingRoom.randomWalkableLoc
|
||||
} else {
|
||||
player.properties.teleportLocation = CastleWarsWaitingArea.zamorakWaitingRoom.randomWalkableLoc
|
||||
}
|
||||
}
|
||||
player.setAttribute(CastleWars.portalAttribute, CastleWars.guthixName)
|
||||
return@on true
|
||||
}
|
||||
|
||||
// Scenery Interactions - Item Tables
|
||||
on(CastleWars.cwTableItemRewardMap.keys.toIntArray(), SCENERY, "take-from") { player, node ->
|
||||
// Retrieve the item id from the map (null safe)
|
||||
val rewardItem: Int = CastleWars.cwTableItemRewardMap.getValue(node.id)
|
||||
|
||||
// If item is added to inventory, play pickup sound
|
||||
if (addItem(player, rewardItem)) {
|
||||
playAudio(player, Audio(2582))
|
||||
|
||||
// Warn player inventory full using custom dialogue box
|
||||
} else {
|
||||
val formattedItemName = (ItemDefinition.forId(rewardItem).name.lowercase() + "s.") // Get the formatted item name string
|
||||
.replace("pes.", "pe.") // Replacement for ropes. -> rope.
|
||||
.replace("bronze ", "") // Replacement for bronze pickaxes. -> pickaxes.
|
||||
|
||||
// Toolkit gets a more grammatically correct sentence
|
||||
if (rewardItem == Items.TOOLKIT_4051) {
|
||||
sendDialogue(player, "Your inventory is too full to hold a toolkit.")
|
||||
} else {
|
||||
sendDialogue(player, (CastleWars.invFullMessage + formattedItemName))
|
||||
}
|
||||
}
|
||||
return@on true
|
||||
}
|
||||
|
||||
// Item/Scenery Interaction - Outside Battlement (Wall) Rope Climb Setup
|
||||
onUseWith(SCENERY, CastleWars.cwClimbingRope, *CastleWars.cwCastleBattlementsMap.keys.toIntArray()) { player, rope, wall ->
|
||||
// Remove rope item from inventory
|
||||
removeItem(player, rope)
|
||||
// First, replace the scenery with the rope on top of the wall
|
||||
replaceScenery(wall.asScenery(), CastleWars.cwCastleBattlementsMap.getValue(wall.id), CastleWars.ropeAliveTicks)
|
||||
// Second, create a new scenery with the rope falling down
|
||||
val toAdd = Scenery(CastleWars.cwCastleClimbingRope, wall.location, 4, wall.direction.toInteger())
|
||||
SceneryBuilder.add(toAdd, CastleWars.ropeAliveTicks)
|
||||
toAdd.isActive = true
|
||||
return@onUseWith true
|
||||
}
|
||||
|
||||
// Scenery Interactions - Outside Battlement (Wall) Player Climbs Rope
|
||||
on(CastleWars.cwCastleClimbingRope, SCENERY, "climb") { player, rope ->
|
||||
// Get the direction of the scenery
|
||||
val dir = rope.asScenery().direction.opposite
|
||||
// Move player on top of the wall from that specific rope location
|
||||
teleport(player, Location(dir.stepY + player.location.x, -dir.stepX + player.location.y), TeleportManager.TeleportType.INSTANT)
|
||||
return@on true
|
||||
}
|
||||
|
||||
// Scenery Interactions - Water Tap - No pulse
|
||||
onUseWith(SCENERY, Items.BUCKET_1925, CastleWars.castleWaterTap) { player, used, _ ->
|
||||
// Lock player
|
||||
lock(player, 1)
|
||||
// Bucket fill animation
|
||||
animate(player, 832)
|
||||
// Bucket fill sound
|
||||
playAudio(player, Audio(2609, 1))
|
||||
// Replace empty -> full water bucket
|
||||
replaceSlot(player, used.asItem().slot, Item(Items.BUCKET_OF_WATER_1929))
|
||||
return@onUseWith true
|
||||
}
|
||||
|
||||
// Scenery Interactions - Stepping stones
|
||||
on(CastleWars.cwSteppingStones, SCENERY, "jump-to") { player, stone ->
|
||||
// Lock the player
|
||||
lock(player, 3)
|
||||
// Make the player "Jump" to the next stepping stone
|
||||
AgilityHandler.forceWalk(player, -1, player.location, stone.location, Animation(741), 10, 0.0, null, 1)
|
||||
// Delay sound to line up with "jump" movement
|
||||
runTask(player, 1) {
|
||||
// Play jumping sound
|
||||
playAudio(player, Audio(2461))
|
||||
}
|
||||
return@on true
|
||||
}
|
||||
}
|
||||
|
||||
private fun hasNonCombatItems(container: Container): Boolean {
|
||||
for (item in container.toArray()) {
|
||||
if (item?.id != null && (item.id == Items.COINS_995 || item.definition?.noteId == item.id))
|
||||
return true
|
||||
}
|
||||
return false
|
||||
}
|
||||
|
||||
private fun capeOrHelmetError(player: Player): String? {
|
||||
val wornCape = getItemFromEquipment(player, EquipmentSlot.CAPE)?.id ?: -1
|
||||
val wornHelmet = getItemFromEquipment(player, EquipmentSlot.HAT)?.id ?: -1
|
||||
|
||||
if (wornCape != -1 || wornHelmet != -1) return "You can't wear hats, capes, or helms in the arena."
|
||||
return null
|
||||
}
|
||||
|
||||
private fun nonCombatItemsCheck(player: Player): String? {
|
||||
// Coins & Noted items, possibly others?
|
||||
if (hasNonCombatItems(player.inventory)) return "You can't take non-combat items into the arena."
|
||||
|
||||
return null
|
||||
}
|
||||
|
||||
private fun familiarCheck(player: Player): String? {
|
||||
// https://forum.tip.it/topic/250066-castle-wars-familiar-msg/
|
||||
// Your familiar can't hold any non-combat items
|
||||
|
||||
// Check the player's familiar container for Coins or Noted items
|
||||
val familiar: BurdenBeast = player.familiarManager.familiar as? BurdenBeast ?: return null
|
||||
|
||||
if (hasNonCombatItems(familiar.container)) return "Your familiar can't take non-combat items into the arena."
|
||||
|
||||
return null
|
||||
}
|
||||
|
||||
private fun joinError(player: Player): Boolean {
|
||||
val errorMessage = capeOrHelmetError(player) ?: nonCombatItemsCheck(player) ?: familiarCheck(player) ?: return false
|
||||
player.sendMessage(errorMessage).also { return true }
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,22 @@
|
||||
import core.api.setVarbit
|
||||
import core.game.node.entity.player.Player
|
||||
|
||||
/**
|
||||
* Handles updating the Castle Wars overlay interface
|
||||
* @author dginovker
|
||||
*/
|
||||
object CastleWarsOverlay {
|
||||
@JvmStatic
|
||||
fun sendLobbyUpdate(player: Player, bothTeamsHavePlayers: Boolean, gameStartMinutes: Int) {
|
||||
player.varpManager.get(380).setVarbit(0, if (bothTeamsHavePlayers) gameStartMinutes else 0).send(player)
|
||||
}
|
||||
|
||||
@JvmStatic
|
||||
fun sendGameUpdate(player: Player) {
|
||||
// Todo - Figure out underground mine/etc
|
||||
setVarbit(player, 143, 0); // Flag status - safe = 0, taken = 1, dropped = 2
|
||||
setVarbit(player, 145, 5); // Saradomin's score
|
||||
setVarbit(player, 153, 0); // Flag status - safe = 0, taken = 1, dropped = 2
|
||||
setVarbit(player, 155, 7); // Zamorak's score
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,131 @@
|
||||
package rs09.game.content.activity.castlewars
|
||||
|
||||
import core.api.TickListener
|
||||
import core.api.openDialogue
|
||||
import core.api.sendMessage
|
||||
import core.game.dialogue.DialogueFile
|
||||
import core.game.dialogue.FacialExpression
|
||||
import core.game.interaction.IntType
|
||||
import core.game.interaction.InteractionListener
|
||||
import core.game.node.entity.npc.NPC
|
||||
import core.game.node.item.Item
|
||||
import core.tools.END_DIALOGUE
|
||||
import org.rs09.consts.Items
|
||||
import org.rs09.consts.NPCs
|
||||
|
||||
class Lanthus: InteractionListener, TickListener {
|
||||
override fun defineListeners() {
|
||||
on(NPCs.LANTHUS_1526, IntType.NPC, "talk-to") { player, npcNode ->
|
||||
val lanthusNpc = npcNode.asNpc()
|
||||
openDialogue(player, object : DialogueFile(){
|
||||
override fun handle(componentID: Int, buttonID: Int) {
|
||||
when(stage) {
|
||||
0 -> npcl(FacialExpression.FRIENDLY, "Good day, how may I help you?").also { stage = 2 }
|
||||
2 -> options(
|
||||
"What is this place?",
|
||||
"What do you have for trade?",
|
||||
"Do you have a manual? I'd like to learn how to play!"
|
||||
).also { stage++ }
|
||||
3 -> when (buttonID) {
|
||||
1 -> player("What is this place?").also { stage = 4 }
|
||||
2 -> lanthusNpc.openShop(player).also { stage = END_DIALOGUE }
|
||||
3 -> player("Do you have a manual? I'd like to learn how to play!").also { stage = 50 }
|
||||
}
|
||||
4 -> npcl(
|
||||
FacialExpression.FRIENDLY,
|
||||
"This is the great Castle Wars arena! Here you can fight for the glory of Saradomin or Zamorak."
|
||||
).also { stage++ }
|
||||
5 -> options(
|
||||
"Really, how do I do that?",
|
||||
"Are there any rules?",
|
||||
"What can I win?"
|
||||
).also { stage++ }
|
||||
6 -> when (buttonID) {
|
||||
1 -> player("Really, how do I do that?").also { stage = 7 }
|
||||
2 -> player("Are there any rules?").also { stage = 80 }
|
||||
3 -> player("What can I win?").also { stage = 90 }
|
||||
}
|
||||
7 -> npcl(
|
||||
FacialExpression.FRIENDLY,
|
||||
"Easy, you just step through one of the three portals. To join Zamorak, pass through the red portal. To join Saradomin, pass through the blue portal. If you don't mind then pass through the green portal.",
|
||||
).also { stage ++ }
|
||||
8 -> options(
|
||||
"Are there any rules?",
|
||||
"What can I win?",
|
||||
"What do you have for trade?",
|
||||
"Do you have a manual? I'd like to learn how to play!"
|
||||
).also { stage++ }
|
||||
9 -> when (buttonID) {
|
||||
1 -> player("Are there any rules?").also { stage = 80 }
|
||||
2 -> player("What can I win?").also { stage = 90 }
|
||||
3 -> lanthusNpc.openShop(player).also { stage = END_DIALOGUE }
|
||||
4 -> player("Do you have a manual? I'd like to learn how to play!").also { stage = 50 }
|
||||
}
|
||||
|
||||
80 -> npcl(
|
||||
FacialExpression.FRIENDLY, "Of course, there are always rules. Firstly you can't wear a cape as you enter the portal, you'll be given your team colours to wear while in the arena. You're also prohibited from taking non-combat related items in")
|
||||
.also { stage ++ }
|
||||
|
||||
81 -> npcl(
|
||||
FacialExpression.FRIENDLY, " with you. So you should only have equipment, potions, and runes on you. Secondly, attacking your own team or your team's defences isn't allowed. You don't want to be angering"
|
||||
).also { stage ++ }
|
||||
|
||||
82 -> npcl(
|
||||
FacialExpression.FRIENDLY, "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)) }
|
||||
|
||||
83 -> player("Great! Oh, how do I win the game?").also { stage ++ }
|
||||
84 -> npcl(
|
||||
FacialExpression.FRIENDLY,
|
||||
"The aim is to get into your opponents' castle and take their team standard. Then bring that back and capture it on your team's standard."
|
||||
).also { stage ++ }
|
||||
85 -> options(
|
||||
"What can I win?",
|
||||
"What do you have to trade?",
|
||||
"Do you have a manual? I'd like to learn how to play!"
|
||||
).also { stage ++ }
|
||||
86 -> when (buttonID) {
|
||||
1 -> player("What can I win?").also { stage = 90 }
|
||||
2 -> lanthusNpc.openShop(player).also { stage = END_DIALOGUE }
|
||||
3 -> player("Do you have a manual? I'd like to learn how to play!").also { stage = 50 }
|
||||
}
|
||||
|
||||
90 -> npcl(
|
||||
FacialExpression.FRIENDLY,
|
||||
"Players on the winning team will receive 2 Castle Wars Tickets which you can trade back to me for other items. In the event of a draw every player will get 1 ticket."
|
||||
).also { stage ++ }
|
||||
91 -> options(
|
||||
"Are there any rules?",
|
||||
"What do you have to trade?",
|
||||
"Do you have a manual? I'd like to learn how to play!"
|
||||
).also { stage ++ }
|
||||
92 -> when (buttonID) {
|
||||
1 -> player("Are there any rules?").also { stage = 80 }
|
||||
2 -> lanthusNpc.openShop(player).also { stage = END_DIALOGUE }
|
||||
3 -> player("Do you have a manual? I'd like to learn how to play!").also { stage = 50 }
|
||||
}
|
||||
// Default
|
||||
else -> sendMessage(player, "Error - unknown stage $stage").also { stage = END_DIALOGUE }
|
||||
}
|
||||
}
|
||||
}, lanthusNpc)
|
||||
|
||||
return@on true
|
||||
}
|
||||
on(NPCs.LANTHUS_1526, IntType.NPC, "trade-with") { player, npcNode ->
|
||||
npcNode.asNpc().openShop(player)
|
||||
return@on true
|
||||
}
|
||||
}
|
||||
|
||||
override fun tick() {
|
||||
"The next game will start in 4 minutes"
|
||||
"The next game will start in 1 minute"
|
||||
// He also talks to Postie Pete sometimes?
|
||||
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,81 @@
|
||||
package rs09.game.content.activity.castlewars.areas
|
||||
|
||||
import content.global.skill.summoning.familiar.BurdenBeast
|
||||
import core.api.LogoutListener
|
||||
import core.api.MapArea
|
||||
import core.api.log
|
||||
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.entity.state.EntityState
|
||||
import core.tools.Log
|
||||
import rs09.game.content.activity.castlewars.CastleWars
|
||||
|
||||
abstract class CastleWarsArea : MapArea, LogoutListener, InteractionListener {
|
||||
|
||||
override fun areaLeave(entity: Entity, logout: Boolean) {
|
||||
super.areaLeave(entity, logout)
|
||||
exitArea(entity as? Player ?: return)
|
||||
}
|
||||
|
||||
override fun logout(player: Player) {
|
||||
if (!defineAreaBorders().any { it.insideBorder(player.location) }) {
|
||||
return // We're not in this area
|
||||
}
|
||||
|
||||
// Move the player to the lobby bank area
|
||||
// Set location directly here, because we want it to take effect immediately
|
||||
player.location = CastleWars.lobbyBankArea.randomWalkableLoc
|
||||
exitArea(player)
|
||||
}
|
||||
|
||||
open fun exitArea(player: Player) {
|
||||
// Remove any transformation
|
||||
player.appearance.transformNPC(-1)
|
||||
|
||||
// Give the player their tabs back
|
||||
player.interfaceManager.restoreTabs()
|
||||
|
||||
// Let the player run
|
||||
player.walkingQueue.isRunDisabled = false
|
||||
|
||||
// If we're not entering another Castle Wars area, remove the Castle Wars items
|
||||
if ((CastleWarsGameArea.areaBorders + CastleWarsWaitingArea.areaBorders).none { it.insideBorder(player.location) }) {
|
||||
exitCastleWars(player)
|
||||
}
|
||||
}
|
||||
|
||||
private fun exitCastleWars(player: Player) {
|
||||
// Close the overlay interface
|
||||
player.interfaceManager.closeOverlay()
|
||||
|
||||
// Remove teleblock
|
||||
player.stateManager.remove(EntityState.TELEBLOCK)
|
||||
|
||||
// Remove any Castle Wars items
|
||||
// Todo Remove any tinderboxes or other castle wars items - See Jan 2018 update: https://oldschool.runescape.wiki/w/Castle_Wars
|
||||
val cwarsItems = intArrayOf(CastleWars.saradominTeamHoodedCloak,
|
||||
CastleWars.zamorakTeamHoodedCloak,
|
||||
CastleWars.saradominFlag,
|
||||
CastleWars.zamorakFlag)
|
||||
|
||||
player.equipment.removeAll(cwarsItems)
|
||||
player.inventory.removeAll(cwarsItems)
|
||||
(player.familiarManager.familiar as? BurdenBeast)?.container?.removeAll(cwarsItems)
|
||||
}
|
||||
|
||||
override fun defineListeners() {
|
||||
onUnequip(intArrayOf(CastleWars.saradominTeamHoodedCloak, CastleWars.zamorakTeamHoodedCloak)) { player, _ ->
|
||||
defineAreaBorders().forEach { border ->
|
||||
if (border.insideBorder(player)) {
|
||||
sendMessage(player, "You can't remove your team's colours")
|
||||
// TODO: Equipping a cape or helmet causes issues
|
||||
return@onUnequip false
|
||||
}
|
||||
}
|
||||
return@onUnequip true
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,101 @@
|
||||
package rs09.game.content.activity.castlewars.areas
|
||||
|
||||
import core.api.TickListener
|
||||
import core.api.log
|
||||
import core.game.component.Component
|
||||
import core.game.node.entity.Entity
|
||||
import core.game.node.entity.player.Player
|
||||
import core.game.node.entity.state.EntityState
|
||||
import core.game.node.item.Item
|
||||
import core.game.world.map.Location
|
||||
import core.game.world.map.zone.ZoneBorders
|
||||
import core.tools.Log
|
||||
import core.tools.ticksPerMinute
|
||||
import org.rs09.consts.Components
|
||||
import rs09.game.content.activity.castlewars.CastleWars
|
||||
|
||||
/**
|
||||
* Handles the Castle Wars game map
|
||||
*/
|
||||
class CastleWarsGameArea : CastleWarsArea(), TickListener {
|
||||
|
||||
companion object {
|
||||
private val saradominStandardFloor: ZoneBorders = ZoneBorders(Location.create(2426, 3073, 3), Location.create(2430, 3077, 3))
|
||||
private val zamorakStandardFloor: ZoneBorders = ZoneBorders(Location.create(2373, 3134, 3), Location.create(2369, 3130, 3))
|
||||
private val saradominUpperFloor: ZoneBorders = ZoneBorders(Location.create(2431, 3080, 2), Location.create(2423, 3072, 2))
|
||||
private val zamorakUpperFloor: ZoneBorders = ZoneBorders(Location.create(2368, 3127, 2), Location.create(2376, 3135, 2))
|
||||
private val saradominFloor: ZoneBorders = ZoneBorders(Location.create(2420, 3072, 1), Location.create(2431, 3083, 1))
|
||||
private val zamorakFloor: ZoneBorders = ZoneBorders(Location.create(2379, 3135, 1), Location.create(2368, 3124, 1))
|
||||
private val battleField: ZoneBorders = ZoneBorders(Location.create(2368, 3135, 0), Location.create(2431, 3072, 0))
|
||||
private val saradominTunnels: ZoneBorders = ZoneBorders(Location.create(2430, 9481, 0), Location.create(2400, 9504, 0))
|
||||
private val zamorakTunnels: ZoneBorders = ZoneBorders(Location.create(2401, 9503, 0), Location.create(2366, 9529, 0))
|
||||
|
||||
val areaBorders = arrayOf(saradominStandardFloor, zamorakStandardFloor, saradominUpperFloor, zamorakUpperFloor, saradominFloor, zamorakFloor, battleField, saradominTunnels, zamorakTunnels)
|
||||
|
||||
val saradominPlayers = mutableSetOf<Player>()
|
||||
val zamorakPlayers = mutableSetOf<Player>()
|
||||
|
||||
var ticksLeftInGame = 0
|
||||
|
||||
fun startGame() {
|
||||
saradominPlayers.addAll(CastleWarsWaitingArea.waitingSaradominPlayers)
|
||||
zamorakPlayers.addAll(CastleWarsWaitingArea.waitingZamorakPlayers)
|
||||
CastleWarsWaitingArea.waitingSaradominPlayers.clear()
|
||||
CastleWarsWaitingArea.waitingZamorakPlayers.clear()
|
||||
ticksLeftInGame = CastleWars.gameTimeMinutes * ticksPerMinute
|
||||
|
||||
// Put all the players in their respawn area
|
||||
saradominPlayers.forEach { player ->
|
||||
player.properties.teleportLocation = CastleWarsRespawnArea.saradominRespawnRoom.randomWalkableLoc
|
||||
}
|
||||
zamorakPlayers.forEach { player ->
|
||||
player.properties.teleportLocation = CastleWarsRespawnArea.zamorakRespawnRoom.randomWalkableLoc
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private fun endGame() {
|
||||
saradominPlayers.forEach { player ->
|
||||
player.properties.teleportLocation = CastleWars.lobbyBankArea.randomWalkableLoc
|
||||
}
|
||||
zamorakPlayers.forEach { player ->
|
||||
player.properties.teleportLocation = CastleWars.lobbyBankArea.randomWalkableLoc
|
||||
}
|
||||
saradominPlayers.clear()
|
||||
zamorakPlayers.clear()
|
||||
}
|
||||
|
||||
override fun defineAreaBorders(): Array<ZoneBorders> {
|
||||
return areaBorders
|
||||
}
|
||||
|
||||
override fun areaEnter(entity: Entity) {
|
||||
val player = entity as? Player ?: return
|
||||
super.areaEnter(player)
|
||||
// Block player teleport (for the remaining duration of the game)
|
||||
player.stateManager.set(EntityState.TELEBLOCK, (CastleWars.gameTimeMinutes)*60*2)
|
||||
|
||||
if (saradominPlayers.contains(player)) {
|
||||
player.interfaceManager.openOverlay(Component(Components.CASTLEWARS_STATUS_OVERLAY_SARADOMIN_58))
|
||||
player.equipment.replace(Item(CastleWars.saradominTeamHoodedCloak), 1)
|
||||
} else if (zamorakPlayers.contains(player)) {
|
||||
player.interfaceManager.openOverlay(Component(Components.CASTLEWARS_STATUS_OVERLAY_ZAMORAK_59))
|
||||
player.equipment.replace(Item(CastleWars.zamorakTeamHoodedCloak), 1)
|
||||
}
|
||||
}
|
||||
|
||||
override fun exitArea(player: Player) {
|
||||
super.exitArea(player)
|
||||
// Remove player from the players set (whichever one that is)
|
||||
saradominPlayers.remove(player)
|
||||
zamorakPlayers.remove(player)
|
||||
}
|
||||
|
||||
override fun tick() {
|
||||
ticksLeftInGame--
|
||||
if (ticksLeftInGame == 0) {
|
||||
endGame()
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,40 @@
|
||||
package rs09.game.content.activity.castlewars.areas
|
||||
|
||||
import core.api.TickListener
|
||||
import core.game.node.entity.Entity
|
||||
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.ZoneBorders
|
||||
import rs09.game.content.activity.castlewars.CastleWars
|
||||
|
||||
/**
|
||||
* Handles the Castle Wars respawn rooms
|
||||
*/
|
||||
class CastleWarsRespawnArea : CastleWarsArea(), TickListener {
|
||||
|
||||
companion object {
|
||||
val zamorakRespawnRoom: ZoneBorders = ZoneBorders(Location.create(2376, 3127, 1), Location.create(2368, 3135, 1))
|
||||
val saradominRespawnRoom: ZoneBorders = ZoneBorders(Location.create(2423, 3080, 1), Location.create(2431, 3072, 1))
|
||||
|
||||
val zamorakPlayersInRespawnRoom = mutableSetOf<Player>()
|
||||
val saradominPlayersInRespawnRoom = mutableSetOf<Player>()
|
||||
}
|
||||
|
||||
override fun defineAreaBorders(): Array<ZoneBorders> {
|
||||
return arrayOf(zamorakRespawnRoom, saradominRespawnRoom)
|
||||
}
|
||||
|
||||
override fun areaEnter(entity: Entity) {
|
||||
val player = entity as? Player ?: return
|
||||
if (saradominRespawnRoom.insideBorder(player.location)) {
|
||||
player.equipment.replace(Item(CastleWars.saradominTeamHoodedCloak), 1)
|
||||
} else if (zamorakRespawnRoom.insideBorder(player.location)) {
|
||||
player.equipment.replace(Item(CastleWars.zamorakTeamHoodedCloak), 1)
|
||||
}
|
||||
}
|
||||
|
||||
override fun tick() {
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,133 @@
|
||||
package rs09.game.content.activity.castlewars.areas
|
||||
|
||||
import CastleWarsOverlay
|
||||
import core.api.God
|
||||
import core.api.TickListener
|
||||
import core.api.hasGodItem
|
||||
import core.api.sendDialogue
|
||||
import core.game.component.Component
|
||||
import core.game.node.entity.Entity
|
||||
import core.game.node.entity.player.Player
|
||||
import core.game.node.entity.state.EntityState
|
||||
import core.game.node.item.Item
|
||||
import core.game.world.map.zone.ZoneBorders
|
||||
import core.tools.ticksPerMinute
|
||||
import org.rs09.consts.Components
|
||||
import rs09.game.content.activity.castlewars.CastleWars
|
||||
|
||||
/**
|
||||
* Handles the Castle Wars "waiting room"
|
||||
*/
|
||||
class CastleWarsWaitingArea : CastleWarsArea(), TickListener {
|
||||
|
||||
companion object {
|
||||
val zamorakWaitingRoom: ZoneBorders = ZoneBorders(2432, 9510, 2407, 9534, 0)
|
||||
val saradominWaitingRoom: ZoneBorders = ZoneBorders(2394, 9497, 2369, 9481, 0)
|
||||
|
||||
val areaBorders = arrayOf(zamorakWaitingRoom, saradominWaitingRoom)
|
||||
|
||||
val waitingSaradominPlayers = mutableSetOf<Player>()
|
||||
val waitingZamorakPlayers = mutableSetOf<Player>()
|
||||
}
|
||||
|
||||
override fun defineAreaBorders(): Array<ZoneBorders> {
|
||||
return areaBorders
|
||||
}
|
||||
|
||||
override fun areaEnter(entity: Entity) {
|
||||
val player = entity as? Player ?: return
|
||||
super.areaEnter(player)
|
||||
// Block player teleport (for at least the max wait time)
|
||||
player.stateManager.set(EntityState.TELEBLOCK, (CastleWars.gameCooldownMinutes + CastleWars.gameTimeMinutes)*60*2)
|
||||
|
||||
// Set team attribute and equip the hooded cloak on the entity based on which waiting room they're in
|
||||
if (zamorakWaitingRoom.insideBorder(player.location)) {
|
||||
player.equipment.replace(Item(CastleWars.zamorakTeamHoodedCloak), 1)
|
||||
waitingZamorakPlayers.add(player)
|
||||
} else if (saradominWaitingRoom.insideBorder(player.location)) {
|
||||
player.equipment.replace(Item(CastleWars.saradominTeamHoodedCloak), 1)
|
||||
waitingSaradominPlayers.add(player)
|
||||
}
|
||||
|
||||
var transformed = false
|
||||
if (player.attributes[CastleWars.portalAttribute] == CastleWars.guthixName
|
||||
&& (hasGodItem(player, God.ZAMORAK) || hasGodItem(player, God.SARADOMIN))) {
|
||||
// https://www.youtube.com/watch?v=Vg3Om-jZNIY
|
||||
// Turn the player into a sheep
|
||||
player.appearance.transformNPC(CastleWars.sheep)
|
||||
// Send the player a chat dialog
|
||||
sendDialogue(player, "I pity your faith in my brothers. I shall bless you with some time in the most holy of forms; maybe its wisdom will rub off on you and you'll see the error of your ways.")
|
||||
transformed = true
|
||||
}
|
||||
if (player.attributes[CastleWars.portalAttribute] == CastleWars.zamorakName
|
||||
&& hasGodItem(player, God.SARADOMIN) || hasGodItem(player, God.GUTHIX))
|
||||
{
|
||||
// https://youtu.be/hmF-CU9OEY4?t=63
|
||||
// Turn the player into an imp!
|
||||
player.appearance.transformNPC(CastleWars.imp)
|
||||
// Send the player a chat dialog
|
||||
sendDialogue(player, "You're wearing objects of my ignorant brothers and you come to me? Such treachery must be rewarded! Enjoy some time in the most mischievous of forms.")
|
||||
transformed = true
|
||||
}
|
||||
if (player.attributes[CastleWars.portalAttribute] == CastleWars.saradominName
|
||||
&& hasGodItem(player, God.ZAMORAK) || hasGodItem(player, God.GUTHIX))
|
||||
{
|
||||
// https://www.youtube.com/watch?v=Vg3Om-jZNIY
|
||||
// Turn the player into a rabbit!
|
||||
player.appearance.transformNPC(CastleWars.rabbit)
|
||||
// Send the player a chat dialog
|
||||
sendDialogue(player, "You wear objects of my foolish brothers? Perhaps some time spent as the lowliest of forms will help you appreciate the gifts that I can bestow upon my followers.")
|
||||
transformed = true
|
||||
}
|
||||
|
||||
if (transformed) {
|
||||
// Remove the tabs
|
||||
player.interfaceManager.removeTabs(0, 1, 2, 3, 4, 5, 6, 11, 12)
|
||||
// Disable player run
|
||||
player.walkingQueue.isRunDisabled = true
|
||||
}
|
||||
|
||||
// Remove the join portal attribute
|
||||
player.removeAttribute(CastleWars.portalAttribute)
|
||||
|
||||
// Open the white text overlay
|
||||
player.interfaceManager.openOverlay(Component(Components.CASTLEWARS_STATUS_OVERLAY_57))
|
||||
}
|
||||
|
||||
override fun exitArea(player: Player) {
|
||||
super.exitArea(player)
|
||||
// Remove player from waiting room set (whichever one that is)
|
||||
waitingSaradominPlayers.remove(player)
|
||||
waitingZamorakPlayers.remove(player)
|
||||
}
|
||||
|
||||
override fun tick() {
|
||||
var nextStart = Int.MAX_VALUE
|
||||
// The game will start when there's enough players and CastleWarsGameArea.ticksLeftInGame = -5 minutes
|
||||
if (CastleWarsGameArea.ticksLeftInGame >= 0) {
|
||||
// A game is going on, display 5 minutes + remaining game time
|
||||
nextStart = CastleWarsGameArea.ticksLeftInGame + CastleWars.gameCooldownMinutes * ticksPerMinute
|
||||
}
|
||||
else if (waitingSaradominPlayers.isEmpty() || waitingZamorakPlayers.isEmpty()) {
|
||||
// A game is not going and there aren't enough players - Keep the game over time to the current value
|
||||
CastleWarsGameArea.ticksLeftInGame = -1
|
||||
}
|
||||
else {
|
||||
// There are enough players - Set nextStart to gameCooldownMinutes + ticksLeftInGame
|
||||
nextStart = CastleWars.gameCooldownMinutes * ticksPerMinute + CastleWarsGameArea.ticksLeftInGame
|
||||
}
|
||||
|
||||
for (player in waitingSaradominPlayers + waitingZamorakPlayers) {
|
||||
CastleWarsOverlay.sendLobbyUpdate(
|
||||
player,
|
||||
waitingSaradominPlayers.isNotEmpty() && waitingZamorakPlayers.isNotEmpty() || CastleWarsGameArea.ticksLeftInGame >= 0,
|
||||
(nextStart - 1) / ticksPerMinute + 1 // Displays max 5 minutes, min 1 minute, seems authentic
|
||||
)
|
||||
}
|
||||
|
||||
if (nextStart <= 0) {
|
||||
CastleWarsGameArea.startGame()
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
Reference in New Issue
Block a user