diff --git a/.gitlab-ci.yml b/.gitlab-ci.yml index 03a4e5b25..8e7707361 100644 --- a/.gitlab-ci.yml +++ b/.gitlab-ci.yml @@ -1,7 +1,7 @@ variables: # This will suppress any download for dependencies and plugins or upload messages which would clutter the console log. # `showDateTime` will show the passed time in milliseconds. You need to specify `--batch-mode` to make this work. - MAVEN_OPTS: "-Dhttps.protocols=TLSv1.2 -Dmaven.repo.local=$CI_PROJECT_DIR/.m2/repository -Dorg.slf4j.simpleLogger.log.org.apache.maven.cli.transfer.Slf4jMavenTransferListener=WARN -Dorg.slf4j.simpleLogger.showDateTime=true -Djava.awt.headless=true -Xmx3g" + MAVEN_OPTS: "-Dhttps.protocols=TLSv1.2 -Dmaven.repo.local=$CI_PROJECT_DIR/.m2/repository -Dorg.slf4j.simpleLogger.log.org.apache.maven.cli.transfer.Slf4jMavenTransferListener=WARN -Dorg.slf4j.simpleLogger.showDateTime=true -Djava.awt.headless=true -Xmx4g -Xms2g" # As of Maven 3.3.0 instead of this you may define these options in `.mvn/maven.config` so the same config is used # when running from the command line. MAVEN_CLI_OPTS: "--batch-mode --errors --fail-fast --show-version" diff --git a/Server/data/ObjectParser.xml b/Server/data/ObjectParser.xml deleted file mode 100644 index bfd1c5167..000000000 --- a/Server/data/ObjectParser.xml +++ /dev/null @@ -1,27 +0,0 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - - diff --git a/Server/data/configs/object_overrides.json b/Server/data/configs/object_overrides.json new file mode 100644 index 000000000..c00cbf672 --- /dev/null +++ b/Server/data/configs/object_overrides.json @@ -0,0 +1,165 @@ +[ + { + "mode": "add", + "id": "1814", + "location": "3090, 3475, 0", + "type": "4", + "direction": "nw" + }, + { + "mode": "add", + "id": "2646", + "location": "2445, 3413, 0", + "direction": "nw" + }, + { + "mode": "add", + "id": "2646", + "location": "2447, 3414, 0", + "direction": "e" + }, + { + "mode": "add", + "id": "2646", + "location": "2447, 3421, 0", + "direction": "nw" + }, + { + "comment": "Prison Pete Random", + "mode": "add", + "id": "36788", + "location": "2094, 4465, 0", + "type": "10", + "direction": "e" + }, + { + "comment": "Green Portal", + "mode": "remove", + "id": "11371", + "location": "2085, 4457, 0" + }, + { + "comment": "Lever", + "mode": "remove", + "id": "10817", + "location": "2083, 4460, 0" + }, + { + "comment": "Lever", + "mode": "add", + "id": "26191", + "location": "2083, 4460, 0", + "type": "10", + "direction": "e" + }, + { + "comment": "Window", + "mode": "add", + "id": "26184", + "location": "2084, 4459, 0", + "type": "0", + "direction": "n" + }, + { + "comment": "Door", + "mode": "add", + "id": "26186", + "location": "2085, 4459, 0", + "type": "0", + "direction": "n" + }, + { + "comment": "Door", + "mode": "add", + "id": "26188", + "location": "2086, 4459, 0", + "type": "0", + "direction": "n" + }, + { + "comment": "Window", + "mode": "add", + "id": "26184", + "location": "2087, 4459, 0", + "type": "0", + "direction": "n" + }, + { + "mode": "remove", + "id": "37670", + "location": "3292, 3299, 0" + }, + { + "mode": "add", + "id": "37304", + "location": "3292, 3299, 0" + }, + { + "mode": "add", + "id": "20987", + "location": "3821, 2996, 0" + }, + { + "mode": "remove", + "id": "356", + "location": "2804, 3428, 0" + }, + { + "mode": "add", + "id": "356", + "location": "2804, 3428, 0" + }, + { + "mode": "remove", + "id": "20228", + "location": "2537, 3573, 0" + }, + { + "mode": "add", + "id": "27663", + "location": "2537, 3573, 0", + "direction": "w" + }, + { + "mode": "add", + "id": "39237", + "location": "2957, 3266, 0", + "direction": "n" + }, + { + "mode": "add", + "id": "39245", + "location": "2956, 3270, 0", + "direction": "se" + }, + { + "mode": "add", + "id": "39242", + "location": "2955, 3273, 0", + "direction": "w" + }, + { + "mode": "add", + "id": "39241", + "location": "2955, 3274, 0" + }, + { + "mode": "add", + "id": "20987", + "location": "2821, 2996, 0", + "direction": "s" + }, + { + "mode": "add", + "id": "21301", + "location": "2662, 3162, 0", + "direction": "e" + }, + { + "mode": "add", + "id": "4176", + "location": "2629, 3661, 2", + "type": "4", + "direction": "e" + } +] diff --git a/Server/pom.xml b/Server/pom.xml index 5c93c44d2..ffc43d1c8 100644 --- a/Server/pom.xml +++ b/Server/pom.xml @@ -252,6 +252,7 @@ false false + -Xmx4g -Xms2g 2.22.0 diff --git a/Server/src/main/content/global/activity/shootingstar/ShootingStar.kt b/Server/src/main/content/global/activity/shootingstar/ShootingStar.kt index 074ab27a6..6a7b8802a 100644 --- a/Server/src/main/content/global/activity/shootingstar/ShootingStar.kt +++ b/Server/src/main/content/global/activity/shootingstar/ShootingStar.kt @@ -99,12 +99,18 @@ class ShootingStar(var level: ShootingStarType = ShootingStarType.values().rando return ShootingStarType.values()[level.ordinal - 1] } + /** + * Clears the current star object or sprite + */ + fun clear() { + SceneryBuilder.remove(starObject) + clearSprite() + } + /** * Fires the shooting star (spawns a new one). Only used when spawning new shooting stars, not for downgrading existing ones. */ fun fire() { - SceneryBuilder.remove(starObject) - clearSprite() SceneryBuilder.add(starObject) if(!isSpawned) { (0..2).forEach { diff --git a/Server/src/main/content/global/activity/shootingstar/ShootingStarPlugin.kt b/Server/src/main/content/global/activity/shootingstar/ShootingStarPlugin.kt index 7368b7689..57849d1db 100644 --- a/Server/src/main/content/global/activity/shootingstar/ShootingStarPlugin.kt +++ b/Server/src/main/content/global/activity/shootingstar/ShootingStarPlugin.kt @@ -17,6 +17,7 @@ import core.game.world.GameWorld import core.tools.Log import core.tools.secondsToTicks import content.data.Quests +import core.game.node.scenery.SceneryBuilder class ShootingStarPlugin : LoginListener, InteractionListener, TickListener, Commands, StartupListener { override fun login(player: Player) { @@ -46,6 +47,7 @@ class ShootingStarPlugin : LoginListener, InteractionListener, TickListener, Com // Check if it's time to fire a new one if (star.ticks >= tickDelay && !star.spriteSpawned) { + star.clear() star.rebuildVars() star.fire() } @@ -138,6 +140,7 @@ class ShootingStarPlugin : LoginListener, InteractionListener, TickListener, Com } define("submit", Privilege.ADMIN, description = "Rebuilds and fires the shooting star event immediately.") { _, _ -> + star.clear() star.rebuildVars() star.fire() } diff --git a/Server/src/main/content/global/ame/events/evilchicken/EvilChickenNPC.kt b/Server/src/main/content/global/ame/events/evilchicken/EvilChickenNPC.kt index 5b065f567..7743500ef 100644 --- a/Server/src/main/content/global/ame/events/evilchicken/EvilChickenNPC.kt +++ b/Server/src/main/content/global/ame/events/evilchicken/EvilChickenNPC.kt @@ -8,12 +8,14 @@ import core.game.node.item.Item import core.tools.RandomFunction import org.rs09.consts.Items import content.global.ame.RandomEventNPC +import core.api.sendMessage import core.api.utils.WeightBasedTable val ids = (2463..2468).toList() class EvilChickenNPC(override var loot: WeightBasedTable? = null) : RandomEventNPC(2463) { val phrases = arrayOf("Bwuk","Bwuk bwuk bwuk","Flee from me, @name!","Begone, @name!","Bwaaaauuuk bwuk bwuk","MUAHAHAHAHAAA!") + var sentMessage = false override fun talkTo(npc: NPC) {} override fun init() { @@ -31,13 +33,16 @@ class EvilChickenNPC(override var loot: WeightBasedTable? = null) : RandomEventN } override fun tick() { - if(!player.location.withinDistance(this.location,8)){ - this.terminate() - } - if(getWorldTicks() % 10 == 0){ - sendChat(phrases.random().replace("@name",player.username.capitalize())) + if (!sentMessage) { + if (!player.location.withinDistance(this.location, 8)) { + // https://www.youtube.com/watch?v=v9p_I_Tq3JE + sendMessage(player, "The Evil Chicken realises you're out of range and vanishes.") + sentMessage = true + terminate() + } else if (getWorldTicks() % 10 == 0) { + sendChat(phrases.random().replace("@name", player.username.capitalize())) + } } super.tick() - if(!player.viewport.currentPlane.npcs.contains(this)) this.clear() } } diff --git a/Server/src/main/content/global/ame/events/maze/MazeInterface.kt b/Server/src/main/content/global/ame/events/maze/MazeInterface.kt index c3c1ab7f4..1d94c4238 100644 --- a/Server/src/main/content/global/ame/events/maze/MazeInterface.kt +++ b/Server/src/main/content/global/ame/events/maze/MazeInterface.kt @@ -12,8 +12,6 @@ import core.game.interaction.InteractionListener import core.game.interaction.QueueStrength import core.game.node.entity.Entity import core.game.node.entity.player.Player -import core.game.system.task.Pulse -import core.game.world.GameWorld.Pulser import core.game.world.map.Location import core.game.world.map.zone.ZoneBorders import core.game.world.map.zone.ZoneRestriction @@ -109,73 +107,17 @@ class MazeInterface : InteractionListener, EventHook, MapArea { addItemOrDrop(player, REWARD_ITEM[randomNumber], itemQuantity.toInt()) } } - - /** - * Chest Location to rotation mapping. - * This is needed as it is impossible to obtain the underlying chest scenery for the rotation. - * 0: Facing North, 1: Facing East, 2: Facing South, 3: Facing West - */ - val chestLocationRotationMap = mapOf( - Location(2930, 4595, 0).toString() to 2, - Location(2924, 4572, 0).toString() to 2, - Location(2925, 4573, 0).toString() to 0, - Location(2900, 4578, 0).toString() to 2, - Location(2901, 4560, 0).toString() to 1, - Location(2890, 4599, 0).toString() to 2, - Location(2896, 4591, 0).toString() to 2, - Location(2895, 4592, 0).toString() to 1, - Location(2901, 4560, 0).toString() to 3, - Location(2918, 4590, 0).toString() to 1, - Location(2917, 4590, 0).toString() to 3, - ) - - /** - * Chest Interaction workaround - * - * The issue here is that the walls(3626) of the Maze are overlapping some(not all) chest sceneries. - * - * The types for the wallScenery: - * Type 0 - flat panel | - * Type 2 - right angle panel with rotation 0:r 1:7 2:> 3:L - * Type 3 - corner post . for angle edges of walls - */ - fun overrideScenery(wallScenery: core.game.node.scenery.Scenery, chestSceneryId: Int): core.game.node.scenery.Scenery { - if (wallScenery.id == chestSceneryId) { - replaceScenery(wallScenery, Scenery.CHEST_3636, 30) - wallScenery.isActive = true - return wallScenery // Return the chest scenery as the wallScenery isn't there. - } - - addScenery(Scenery.CHEST_3636, wallScenery.location, chestLocationRotationMap[wallScenery.location.toString()] ?: 0, 10) - addScenery(wallScenery) - // replaceScenery(newChestScenery, Scenery.CHEST_3636, 3) // didn't work for an underlying scenery - // I did a world pulse since everyone will get to see the chest open. - Pulser.submit(object : Pulse(30) { - override fun pulse(): Boolean { - addScenery(Scenery.CHEST_3635, wallScenery.location, chestLocationRotationMap[wallScenery.location.toString()] ?: 0, 10) - addScenery(wallScenery) - return true - } - }) - // Return the chest scenery to replace PacketProcessor so that MISMATCH will not happen. - return core.game.node.scenery.Scenery( - chestSceneryId, - wallScenery.location, - chestLocationRotationMap[wallScenery.location.toString()] ?: 0 - ) - } } override fun defineListeners() { - - // This somehow doesn't trigger as the scenery.id != objId (3626 != 3635) on(Scenery.CHEST_3635, IntType.SCENERY, "open") { player, node -> + lock(player, 3) + animate(player, 536) + replaceScenery(node.asScenery(), Scenery.CHEST_3636, 3) if (getAttribute(player, MAZE_ATTRIBUTE_TICKS_LEFT, 0) > 0 && getAttribute(player, MAZE_ATTRIBUTE_CHESTS_OPEN, 0) < 10) { - animate(player, 536) - // val actualScenery = RegionManager.getObject(node.location.z, node.location.x, node.location.y, 3626) val tableRoll = CHEST_REWARDS.roll() addItemOrBank(player, tableRoll[0].id, tableRoll[0].amount) - when (tableRoll[0].id){ + when (tableRoll[0].id) { Items.AIR_RUNE_556 -> sendItemDialogue(player, Items.AIR_RUNE_556, "You've found some air runes!") Items.WATER_RUNE_555 -> sendItemDialogue(player, Items.WATER_RUNE_555, "You've found some water runes!") Items.EARTH_RUNE_557 -> sendItemDialogue(player, Items.EARTH_RUNE_557, "You've found some earth runes!") @@ -187,7 +129,7 @@ class MazeInterface : InteractionListener, EventHook, MapArea { Items.STRENGTH_POTION2_117 -> sendItemDialogue(player, Items.STRENGTH_POTION2_117, "You've found a strength potion!") Items.DEFENCE_POTION2_135 -> sendItemDialogue(player, Items.DEFENCE_POTION2_135, "You've found a defence potion!") } - setAttribute(player, MAZE_ATTRIBUTE_CHESTS_OPEN, getAttribute(player, MAZE_ATTRIBUTE_CHESTS_OPEN, 0)) + setAttribute(player, MAZE_ATTRIBUTE_CHESTS_OPEN, getAttribute(player, MAZE_ATTRIBUTE_CHESTS_OPEN, 0) + 1) } else { sendMessage(player,"You find nothing of interest.") } diff --git a/Server/src/main/content/global/ame/events/rivertroll/RiverTrollRENPC.kt b/Server/src/main/content/global/ame/events/rivertroll/RiverTrollRENPC.kt index dff28d7e9..34b312148 100644 --- a/Server/src/main/content/global/ame/events/rivertroll/RiverTrollRENPC.kt +++ b/Server/src/main/content/global/ame/events/rivertroll/RiverTrollRENPC.kt @@ -4,6 +4,7 @@ import core.game.node.entity.Entity import core.game.node.entity.npc.NPC import content.global.ame.RandomEventNPC import core.api.utils.WeightBasedTable +import core.game.world.map.MapDistance val ids = (391..396).toList() @@ -21,10 +22,12 @@ class RiverTrollRENPC(override var loot: WeightBasedTable? = null) : RandomEvent super.finalizeDeath(killer) } override fun tick() { - if(!player.location.withinDistance(this.location,8)){ + if (!player.location.withinDistance(this.location, 8)) { this.terminate() } super.tick() - if(!player.viewport.currentPlane.npcs.contains(this)) this.clear() + if (!player.location.withinDistance(this.location, MapDistance.RENDERING.distance)) { + this.clear() + } } } \ No newline at end of file diff --git a/Server/src/main/content/global/ame/events/rockgolem/RockGolemRENPC.kt b/Server/src/main/content/global/ame/events/rockgolem/RockGolemRENPC.kt index eb3399657..593a9f917 100644 --- a/Server/src/main/content/global/ame/events/rockgolem/RockGolemRENPC.kt +++ b/Server/src/main/content/global/ame/events/rockgolem/RockGolemRENPC.kt @@ -4,6 +4,7 @@ import core.game.node.entity.Entity import core.game.node.entity.npc.NPC import content.global.ame.RandomEventNPC import core.api.utils.WeightBasedTable +import core.game.world.map.MapDistance val ids = (413..418).toList() @@ -21,10 +22,12 @@ class RockGolemRENPC(override var loot: WeightBasedTable? = null) : RandomEventN super.finalizeDeath(killer) } override fun tick() { - if(!player.location.withinDistance(this.location,8)){ + if (!player.location.withinDistance(this.location, 8)) { this.terminate() } super.tick() - if(!player.viewport.currentPlane.npcs.contains(this)) this.clear() + if (!player.location.withinDistance(this.location, MapDistance.RENDERING.distance)) { + this.clear() + } } } \ No newline at end of file diff --git a/Server/src/main/content/global/ame/events/shade/ShadeRENPC.kt b/Server/src/main/content/global/ame/events/shade/ShadeRENPC.kt index 60a0f57ae..5a5bff4d1 100644 --- a/Server/src/main/content/global/ame/events/shade/ShadeRENPC.kt +++ b/Server/src/main/content/global/ame/events/shade/ShadeRENPC.kt @@ -4,6 +4,7 @@ import core.game.node.entity.Entity import core.game.node.entity.npc.NPC import content.global.ame.RandomEventNPC import core.api.utils.WeightBasedTable +import core.game.world.map.MapDistance class ShadeRENPC(override var loot: WeightBasedTable? = null) : RandomEventNPC(425){ val ids = (425..430).toList() @@ -20,10 +21,12 @@ class ShadeRENPC(override var loot: WeightBasedTable? = null) : RandomEventNPC(4 super.finalizeDeath(killer) } override fun tick() { - if(!player.location.withinDistance(this.location,8)){ + if (!player.location.withinDistance(this.location, 8)) { this.terminate() } super.tick() - if(!player.viewport.currentPlane.npcs.contains(this)) this.clear() + if (!player.location.withinDistance(this.location, MapDistance.RENDERING.distance)) { + this.clear() + } } } diff --git a/Server/src/main/content/global/ame/events/treespirit/TreeSpiritRENPC.kt b/Server/src/main/content/global/ame/events/treespirit/TreeSpiritRENPC.kt index 3b0033972..a3a078398 100644 --- a/Server/src/main/content/global/ame/events/treespirit/TreeSpiritRENPC.kt +++ b/Server/src/main/content/global/ame/events/treespirit/TreeSpiritRENPC.kt @@ -4,6 +4,7 @@ import core.game.node.entity.Entity import core.game.node.entity.npc.NPC import content.global.ame.RandomEventNPC import core.api.utils.WeightBasedTable +import core.game.world.map.MapDistance val ids = (438..443).toList() @@ -21,10 +22,12 @@ class TreeSpiritRENPC(override var loot: WeightBasedTable? = null) : RandomEvent super.finalizeDeath(killer) } override fun tick() { - if(!player.location.withinDistance(this.location,8)){ + if (!player.location.withinDistance(this.location, 8)) { this.terminate() } super.tick() - if(!player.viewport.currentPlane.npcs.contains(this)) this.clear() + if (!player.location.withinDistance(this.location, MapDistance.RENDERING.distance)) { + this.clear() + } } } \ No newline at end of file diff --git a/Server/src/main/content/global/ame/events/zombie/ZombieRENPC.kt b/Server/src/main/content/global/ame/events/zombie/ZombieRENPC.kt index 6b43d54b4..48ebacd81 100644 --- a/Server/src/main/content/global/ame/events/zombie/ZombieRENPC.kt +++ b/Server/src/main/content/global/ame/events/zombie/ZombieRENPC.kt @@ -4,6 +4,7 @@ import core.game.node.entity.Entity import core.game.node.entity.npc.NPC import content.global.ame.RandomEventNPC import core.api.utils.WeightBasedTable +import core.game.world.map.MapDistance class ZombieRENPC(override var loot: WeightBasedTable? = null) : RandomEventNPC(419){ val ids = (419..424).toList() @@ -20,10 +21,12 @@ class ZombieRENPC(override var loot: WeightBasedTable? = null) : RandomEventNPC( super.finalizeDeath(killer) } override fun tick() { - if(!player.location.withinDistance(this.location,8)){ + if (!player.location.withinDistance(this.location, 8)) { this.terminate() } super.tick() - if(!player.viewport.currentPlane.npcs.contains(this)) this.clear() + if (!player.location.withinDistance(this.location, MapDistance.RENDERING.distance)) { + this.clear() + } } } diff --git a/Server/src/main/content/global/bots/Adventurer.kt b/Server/src/main/content/global/bots/Adventurer.kt index 916503945..86f157461 100644 --- a/Server/src/main/content/global/bots/Adventurer.kt +++ b/Server/src/main/content/global/bots/Adventurer.kt @@ -11,24 +11,20 @@ import core.game.world.GameWorld import core.game.world.map.Location import core.game.world.map.RegionManager import core.game.world.map.zone.ZoneBorders -import core.game.world.update.flag.* import org.json.simple.JSONArray import org.json.simple.JSONObject import core.ServerConstants -import core.api.log import core.game.bots.AIRepository import core.game.bots.CombatBotAssembler import core.game.bots.Script import core.game.interaction.IntType import core.game.interaction.InteractionListeners -import core.tools.Log import java.io.File import java.io.FileReader import java.time.LocalDateTime import java.time.format.DateTimeFormatter import kotlin.random.Random - /** * A bot script for Adventurers who explore the world! * @param counter used in the bots random idling function. @@ -114,7 +110,7 @@ class Adventurer(val style: CombatStyle): Script() { } private fun otherPlayersNearby(): Boolean { - val localPlayers = RegionManager.getLocalPlayers(bot) + val localPlayers = RegionManager.getLocalPlayers(bot.location) val otherPlayers = localPlayers.filter { it.name != bot.name } return otherPlayers.isNotEmpty() } @@ -503,7 +499,7 @@ class Adventurer(val style: CombatStyle): Script() { dateCode == 404 -> lineAlt = dialogue.getLines("easter").rand() } - var localPlayers = RegionManager.getLocalPlayers(bot) + val localPlayers = RegionManager.getLocalPlayers(bot.location) if (localPlayers.isNotEmpty()) { val localPlayer = localPlayers .filter { it.name != bot.name } diff --git a/Server/src/main/content/global/bots/CannonballSmelter.kt b/Server/src/main/content/global/bots/CannonballSmelter.kt index 8ac77f7e1..38887ad2c 100644 --- a/Server/src/main/content/global/bots/CannonballSmelter.kt +++ b/Server/src/main/content/global/bots/CannonballSmelter.kt @@ -58,7 +58,7 @@ class CannonballSmelter : Script() { } else if(!coalMine.insideBorder(bot)){ scriptAPI.walkTo(coalMine.randomLoc) } else { - val rock = scriptAPI.getNearestObjectByPredicate({node -> node?.name?.equals("rocks", true)!! && MiningNode.forId(node?.id!!).reward == Items.COAL_453 }) + val rock = scriptAPI.getNearestObjectByPredicate { node -> node?.name?.equals("rocks", true)!! && MiningNode.forId(node.id)?.reward == Items.COAL_453 } if(rock != null) { scriptAPI.interact(bot, rock, "mine") } else { @@ -75,7 +75,7 @@ class CannonballSmelter : Script() { var loc = ironMine.randomLoc scriptAPI.walkTo(loc) } else { - val rock = scriptAPI.getNearestObjectByPredicate({node -> node?.name?.equals("rocks", true)!! && MiningNode.forId(node?.id!!).reward == Items.IRON_ORE_440 }) + val rock = scriptAPI.getNearestObjectByPredicate { node -> node?.name?.equals("rocks", true)!! && MiningNode.forId(node.id)?.reward == Items.IRON_ORE_440 } //rock?.let { InteractionListeners.run(rock.id, IntType.SCENERY,"mine",bot,rock) } if(rock != null) { scriptAPI.interact(bot, rock, "mine") diff --git a/Server/src/main/content/global/bots/DoublingMoney.kt b/Server/src/main/content/global/bots/DoublingMoney.kt index 785fd8690..36b8ccf30 100644 --- a/Server/src/main/content/global/bots/DoublingMoney.kt +++ b/Server/src/main/content/global/bots/DoublingMoney.kt @@ -262,10 +262,12 @@ class DoublingMoney : Script() { minDist = min(minDist, loc.getDistance(bot.location)) } if (minDist > 1) return 2 - RegionManager.forId(bot.location.regionId).planes[bot.location.z].players.forEach { - if (AIRepository.PulseRepository[it?.username?.lowercase()]?.botScript is DoublingMoney - && it != bot - && it.location.getDistance(bot.location) <= 1) { + val region = RegionManager.forId(bot.location.regionId) + val players = region.assemblePlayerList(bot.location.z) + for (player in players) { + if (AIRepository.PulseRepository[player.username.lowercase()]?.botScript is DoublingMoney + && player != bot + && player.location.getDistance(bot.location) <= 1) { return 2 } } diff --git a/Server/src/main/content/global/handlers/item/equipment/special/ChainhitSpecialHandler.java b/Server/src/main/content/global/handlers/item/equipment/special/ChainhitSpecialHandler.java index b3e276dcd..08b180396 100644 --- a/Server/src/main/content/global/handlers/item/equipment/special/ChainhitSpecialHandler.java +++ b/Server/src/main/content/global/handlers/item/equipment/special/ChainhitSpecialHandler.java @@ -79,7 +79,7 @@ public final class ChainhitSpecialHandler extends RangeSwingHandler implements P if (!((Player) entity).getSettings().drainSpecial(SPECIAL_ENERGY)) { return -1; } - state.setStyle(CombatStyle.RANGE); + state.setStyle(CombatStyle.RANGE); if (victim instanceof NPC) { NPC npc = victim.asNpc(); if (npc.getId() == 2440) { @@ -186,9 +186,9 @@ public final class ChainhitSpecialHandler extends RangeSwingHandler implements P if (list == null) { int distance = 5; if (victim instanceof NPC) { - e.setAttribute("chain-hit_v", list = RegionManager.getLocalNpcs(e, distance)); + e.setAttribute("chain-hit_v", list = RegionManager.getLocalNPCs(e.getLocation(), distance)); } else { - e.setAttribute("chain-hit_v", list = RegionManager.getLocalPlayers(e, distance)); + e.setAttribute("chain-hit_v", list = RegionManager.getLocalPlayers(e.getLocation(), distance)); } list.remove(e); list.remove(victim); diff --git a/Server/src/main/content/global/handlers/item/equipment/special/ChinchompaSwingHandler.java b/Server/src/main/content/global/handlers/item/equipment/special/ChinchompaSwingHandler.java index a0801835e..fff663592 100644 --- a/Server/src/main/content/global/handlers/item/equipment/special/ChinchompaSwingHandler.java +++ b/Server/src/main/content/global/handlers/item/equipment/special/ChinchompaSwingHandler.java @@ -6,16 +6,15 @@ import core.game.node.entity.combat.equipment.Ammunition; import core.game.node.entity.combat.equipment.RangeWeapon; import core.game.node.entity.combat.equipment.Weapon; import core.game.node.entity.combat.equipment.WeaponInterface; -import core.game.node.entity.npc.NPC; import core.game.node.entity.player.Player; import core.game.node.entity.skill.Skills; -import core.game.world.map.RegionManager; import core.game.world.update.flag.context.Graphics; import core.tools.RandomFunction; -import java.util.ArrayList; import java.util.List; +import static core.game.node.entity.combat.MultihitTargetsKt.findMultihitTargetsForChinchompa; + /** * Handles a combat swing using red chinchompas. * @author Emperor @@ -56,22 +55,18 @@ public final class ChinchompaSwingHandler extends RangeSwingHandler { return -1; } - // ! Initial capacity of 14, but has dynamic size? i.e. we are not restricting to a max of 14 targets hit here now - List targetCandidates = new ArrayList<>(14); - targetCandidates.addAll(RegionManager.getSurroundingNPCs(victim, 14, entity)); - targetCandidates.addAll(RegionManager.getSurroundingPlayers(victim, 14, entity)); + boolean accurate = isAccurateImpact(entity, victim, CombatStyle.RANGE); + List targetCandidates = findMultihitTargetsForChinchompa(victim, entity); BattleState[] targets = new BattleState[targetCandidates.size()]; int count = 0; for (Entity e : targetCandidates) { - if (canSwing(entity, e) != InteractionType.NO_INTERACT && e.isAttackable(entity, CombatStyle.RANGE, false)) { - BattleState s = targets[count++] = new BattleState(entity, e); - s.setStyle(CombatStyle.RANGE); - int hit = 0; - if (isAccurateImpact(entity, e, CombatStyle.RANGE)) { - hit = RandomFunction.random(calculateHit(entity, e, 1.0) + 1); - } - s.setEstimatedHit(hit); + BattleState s = targets[count++] = new BattleState(entity, e); + s.setStyle(CombatStyle.RANGE); + int hit = 0; + if (accurate) { + hit = RandomFunction.random(calculateHit(entity, e, 1.0) + 1); } + s.setEstimatedHit(hit); } state.setTargets(targets); Companion.useAmmo(entity, state, null); diff --git a/Server/src/main/content/global/handlers/item/equipment/special/PowerstabSpecialHandler.java b/Server/src/main/content/global/handlers/item/equipment/special/PowerstabSpecialHandler.java index ba3ff8ed0..5498d6a24 100644 --- a/Server/src/main/content/global/handlers/item/equipment/special/PowerstabSpecialHandler.java +++ b/Server/src/main/content/global/handlers/item/equipment/special/PowerstabSpecialHandler.java @@ -5,12 +5,9 @@ import java.util.List; import core.game.node.entity.Entity; import core.game.node.entity.combat.BattleState; import core.game.node.entity.combat.CombatStyle; -import core.game.node.entity.combat.InteractionType; import core.game.node.entity.combat.MeleeSwingHandler; import core.game.node.entity.impl.Animator.Priority; -import core.game.node.entity.npc.NPC; import core.game.node.entity.player.Player; -import core.game.world.map.RegionManager; import core.game.world.update.flag.context.Animation; import core.game.world.update.flag.context.Graphics; import core.plugin.Plugin; @@ -19,6 +16,7 @@ import core.tools.RandomFunction; import org.rs09.consts.Sounds; import static core.api.ContentAPIKt.playGlobalAudio; +import static core.game.node.entity.combat.MultihitTargetsKt.findMultihitTargetsForD2h; /** * Handles the Powerstab special attack. @@ -75,21 +73,17 @@ public final class PowerstabSpecialHandler extends MeleeSwingHandler implements if (!multi) { return super.swing(entity, victim, state); } - @SuppressWarnings("rawtypes") - List list = victim instanceof NPC ? RegionManager.getSurroundingNPCs(entity, 9, entity) : RegionManager.getSurroundingPlayers(entity, 9, entity); + List list = findMultihitTargetsForD2h(entity, CombatStyle.MELEE); BattleState[] targets = new BattleState[list.size()]; int count = 0; - for (Object o : list) { - Entity e = (Entity) o; - if (CombatStyle.RANGE.getSwingHandler().canSwing(entity, e) != InteractionType.NO_INTERACT && e.isAttackable(entity, CombatStyle.RANGE, false)) { - BattleState s = targets[count++] = new BattleState(entity, e); - int hit = 0; - if (isAccurateImpact(entity, e)) { - hit = RandomFunction.random(calculateHit(entity, e, 1.0) + 1); - } - s.setStyle(CombatStyle.MELEE); - s.setEstimatedHit(hit); + for (Entity e : list) { + BattleState s = targets[count++] = new BattleState(entity, e); + int hit = 0; + if (isAccurateImpact(entity, e)) { + hit = RandomFunction.random(calculateHit(entity, e, 1.0) + 1); } + s.setStyle(CombatStyle.MELEE); + s.setEstimatedHit(hit); } state.setTargets(targets); return 1; diff --git a/Server/src/main/content/global/handlers/item/equipment/special/SpearWallSpecialHandler.java b/Server/src/main/content/global/handlers/item/equipment/special/SpearWallSpecialHandler.java index 2595b0479..727c51767 100644 --- a/Server/src/main/content/global/handlers/item/equipment/special/SpearWallSpecialHandler.java +++ b/Server/src/main/content/global/handlers/item/equipment/special/SpearWallSpecialHandler.java @@ -5,12 +5,9 @@ import java.util.List; import core.game.node.entity.Entity; import core.game.node.entity.combat.BattleState; import core.game.node.entity.combat.CombatStyle; -import core.game.node.entity.combat.InteractionType; import core.game.node.entity.combat.MeleeSwingHandler; import core.game.node.entity.impl.Animator.Priority; -import core.game.node.entity.npc.NPC; import core.game.node.entity.player.Player; -import core.game.world.map.RegionManager; import core.game.world.update.flag.context.Animation; import core.game.world.update.flag.context.Graphics; import core.plugin.Plugin; @@ -19,6 +16,7 @@ import core.tools.RandomFunction; import org.rs09.consts.Sounds; import static core.api.ContentAPIKt.playGlobalAudio; +import static core.game.node.entity.combat.MultihitTargetsKt.findMultihitTargets; /** * Handles Vesta's Spear special attack - Spear Wall. @@ -76,19 +74,14 @@ public final class SpearWallSpecialHandler extends MeleeSwingHandler implements if (!multi) { return super.swing(entity, victim, state); } - @SuppressWarnings("rawtypes") - List list = victim instanceof NPC ? RegionManager.getSurroundingNPCs(entity, 9, entity) : RegionManager.getSurroundingPlayers(entity, 9, entity); + List list = findMultihitTargets(victim, entity, CombatStyle.MELEE); BattleState[] targets = new BattleState[list.size()]; int count = 0; - for (Object o : list) { - Entity e = (Entity) o; - if (CombatStyle.RANGE.getSwingHandler().canSwing(entity, e) != InteractionType.NO_INTERACT && e.isAttackable(entity, CombatStyle.RANGE, false)) { - BattleState s = targets[count++] = new BattleState(entity, e); - int hit = 0; - hit = RandomFunction.random(calculateHit(entity, e, 1.0) + 1); - s.setStyle(CombatStyle.MELEE); - s.setEstimatedHit(hit); - } + for (Entity e : list) { + BattleState s = targets[count++] = new BattleState(entity, e); + int hit = RandomFunction.random(calculateHit(entity, e, 1.0) + 1); + s.setStyle(CombatStyle.MELEE); + s.setEstimatedHit(hit); } state.setTargets(targets); return 1; diff --git a/Server/src/main/content/global/handlers/item/equipment/special/SweepSpecialHandler.java b/Server/src/main/content/global/handlers/item/equipment/special/SweepSpecialHandler.java index 6c4b9cedc..40e7a79bc 100644 --- a/Server/src/main/content/global/handlers/item/equipment/special/SweepSpecialHandler.java +++ b/Server/src/main/content/global/handlers/item/equipment/special/SweepSpecialHandler.java @@ -4,17 +4,12 @@ import java.util.ArrayList; import java.util.List; import core.plugin.Initializable; -import content.global.skill.summoning.familiar.Familiar; import core.game.node.entity.Entity; import core.game.node.entity.combat.BattleState; import core.game.node.entity.combat.CombatStyle; import core.game.node.entity.combat.MeleeSwingHandler; import core.game.node.entity.impl.Animator.Priority; -import core.game.node.entity.npc.NPC; import core.game.node.entity.player.Player; -import core.game.world.map.Direction; -import core.game.world.map.Location; -import core.game.world.map.RegionManager; import core.game.world.update.flag.context.Animation; import core.game.world.update.flag.context.Graphics; import core.plugin.Plugin; @@ -22,6 +17,7 @@ import core.tools.RandomFunction; import org.rs09.consts.Sounds; import static core.api.ContentAPIKt.playGlobalAudio; +import static core.game.node.entity.combat.MultihitTargetsKt.findMultihitTargetsForDragonHalberd; /** * Handles the Dragon halberd special attack. @@ -92,25 +88,11 @@ public final class SweepSpecialHandler extends MeleeSwingHandler implements Plug if (!entity.getProperties().isMultiZone() || !victim.getProperties().isMultiZone()) { return new BattleState[]{state}; } - Location vl = victim.getLocation(); - int x = vl.getX(); - int y = vl.getY(); - Direction dir = Direction.getDirection(x - entity.getLocation().getX(), y - entity.getLocation().getY()); List l = new ArrayList<>(20); l.add(new BattleState(entity, victim)); - for (Entity n : victim instanceof NPC ? RegionManager.getSurroundingNPCs(victim, 9, entity, victim) : RegionManager.getSurroundingPlayers(victim, 9, entity, victim)) { - if (n instanceof Familiar) { - continue; - } - if (!n.isAttackable(entity, CombatStyle.MELEE, false)) { - continue; - } - if (n.getLocation().equals(vl.transform(dir.getStepY(), dir.getStepX(), 0)) || n.getLocation().equals(vl.transform(-dir.getStepY(), -dir.getStepX(), 0))) { - l.add(new BattleState(entity, n)); - if (l.size() >= 3) { - break; - } - } + List list = findMultihitTargetsForDragonHalberd(victim, entity); + for (Entity n : list) { + l.add(new BattleState(entity, n)); } return l.toArray(new BattleState[l.size()]); } diff --git a/Server/src/main/content/global/handlers/item/withitem/SoftclayPlugin.java b/Server/src/main/content/global/handlers/item/withitem/SoftclayPlugin.java index 37ff46584..73cd237fb 100644 --- a/Server/src/main/content/global/handlers/item/withitem/SoftclayPlugin.java +++ b/Server/src/main/content/global/handlers/item/withitem/SoftclayPlugin.java @@ -136,7 +136,7 @@ public final class SoftclayPlugin extends UseWithHandler { player.getPacketDispatch().sendMessage("You mix the clay and water. You now have some soft, workable clay."); player.getInventory().add(SOFT_CLAY); player.getInventory().add(returnItem); - if (!player.getAchievementDiaryManager().hasCompletedTask(DiaryType.LUMBRIDGE, 0, 6) && player.getViewport().getRegion().getId() == 12341) { + if (!player.getAchievementDiaryManager().hasCompletedTask(DiaryType.LUMBRIDGE, 0, 6) && player.getLocation().getRegionId() == 12341) { player.getAchievementDiaryManager().finishTask(player, DiaryType.LUMBRIDGE, 0, 6); } return true; diff --git a/Server/src/main/content/global/handlers/item/withobject/WaterSourceListener.kt b/Server/src/main/content/global/handlers/item/withobject/WaterSourceListener.kt index 75652b75d..dfa302014 100644 --- a/Server/src/main/content/global/handlers/item/withobject/WaterSourceListener.kt +++ b/Server/src/main/content/global/handlers/item/withobject/WaterSourceListener.kt @@ -15,30 +15,19 @@ import org.rs09.consts.Sounds * @author Ceikry */ class WaterSourceListener : InteractionListener { - // NOTE '9695' should be removed from the array below once the sink object can be used directly, this is a temporary - // solution. - // this is ugly! - private val waterSources = intArrayOf(21355, 16302, 6827, 11661, 24160, 34577, 15936, 15937, 15938, 23920, 35469, 24265, 153, 879, 880, 2864, 6232, 10436, 10437, 10827, 11007, 11759, 21764, 22973, 24161, 24214, 24265, 28662, 30223, 30820, 34579, 36781, 873, 874, 4063, 6151, 8699, 9143, 9684, 9695, 10175, 12279, 12974, 13563, 13564, 14868, 14917, 14918, 15678, 16704, 16705, 20358, 22715, 24112, 24314, 25729, 25929, 26966, 29105, 33458, 34082, 34411, 34496, 34547, 34566, 35762, 36971, 37154, 37155, 878, 884, 3264, 3305, 3359, 4004, 4005, 6097, 6249, 6549, 8747, 8927, 11793, 12201, 12897, 24166, 26945, 31359, 32023, 32024, 34576, 35671, 40063, 13561, 13563, 13559, 12089) + private val waterSources = intArrayOf(21355, 16302, 6827, 11661, 24160, 34577, 15936, 15937, 15938, 23920, 35469, 24265, 153, 879, 880, 2864, 6232, 10436, 10437, 10827, 11007, 11759, 21764, 22973, 24161, 24214, 24265, 28662, 30223, 30820, 34579, 36781, 873, 874, 4063, 6151, 8699, 9143, 9684, 10175, 12279, 12974, 13563, 13564, 14868, 14917, 14918, 15678, 16704, 16705, 20358, 22715, 24112, 24314, 25729, 25929, 26966, 29105, 33458, 34082, 34411, 34496, 34547, 34566, 35762, 36971, 37154, 37155, 878, 884, 3264, 3305, 3359, 4004, 4005, 6097, 6249, 6549, 8747, 8927, 11793, 12201, 12897, 24166, 26945, 31359, 32023, 32024, 34576, 35671, 40063, 13561, 13563, 13559, 12089) private val nonWellableMsg = "If I drop my @ down there, I don't think I'm likely to get it back." private val animation = Animation(832) - override fun defineListeners() - { + override fun defineListeners() { onUseWith(IntType.SCENERY, WaterVessel.getInputs(), *waterSources){ player, used, with -> val vessel = WaterVessel.forId(used.id) ?: return@onUseWith false - if(with.name.contains("well", ignoreCase = true) && !vessel.wellable) { sendMessage(player, formatMsgText(used.name, nonWellableMsg)) return@onUseWith true } - - if(with.id == 9695 && player.location.regionId != 11571) - { - return@onUseWith false - } - //ugly achievement code, achievement system sux if(vessel == WaterVessel.BUCKET && with.id == 11661) { @@ -67,15 +56,6 @@ class WaterSourceListener : InteractionListener { return@onUseWith true } - - // This is needed to make the crafting guild sink work, but technically it's wrong as it is the noticeboard and - // not the sink being used. This is a temporary solution until the sink object can be used directly. - setDest(IntType.SCENERY, intArrayOf(9695), "use") { player, node -> - if (player.location.regionId == 11571){ - return@setDest Location.create(2934, 3280, 0) - } - return@setDest node.location - } } private fun formatMsgText(name: String, template: String): String diff --git a/Server/src/main/content/global/handlers/scenery/TableListener.kt b/Server/src/main/content/global/handlers/scenery/TableListener.kt index c35ebd27b..c89c6262b 100644 --- a/Server/src/main/content/global/handlers/scenery/TableListener.kt +++ b/Server/src/main/content/global/handlers/scenery/TableListener.kt @@ -413,7 +413,7 @@ class TableListener : InteractionListener { } val droppedItem = used.dropItem if (droppedItem.id == Items.COINS_995) playAudio(player, DROP_COINS_SOUND) else playAudio(player, DROP_ITEM_SOUND) - GroundItemManager.create(droppedItem, Location(nearestX, nearestY), player) + GroundItemManager.create(droppedItem, Location(nearestX, nearestY, player.location.z), player) setAttribute(player, "droppedItem:${droppedItem.id}", getWorldTicks() + 2) PlayerParser.save(player) diff --git a/Server/src/main/content/global/skill/agility/brimhaven/BrimhavenArena.java b/Server/src/main/content/global/skill/agility/brimhaven/BrimhavenArena.java index 13a6a4ad6..0bae1f57c 100644 --- a/Server/src/main/content/global/skill/agility/brimhaven/BrimhavenArena.java +++ b/Server/src/main/content/global/skill/agility/brimhaven/BrimhavenArena.java @@ -229,7 +229,7 @@ public final class BrimhavenArena extends MapZone implements Plugin { DISPENSERS[index++] = Location.create(x, y, 3); } } - GameWorld.getPulser().submit(new Pulse(1) { + GameWorld.getPulser().submit(new Pulse(1) { @Override public boolean pulse() { Region r = RegionManager.forId(11157); @@ -237,9 +237,11 @@ public final class BrimhavenArena extends MapZone implements Plugin { return false; } if (GameWorld.getTicks() % 100 == 0) { - for (RegionPlane plane : r.getPlanes()) { - for (Player player : plane.getPlayers()) { - setDispenser(player); + for (int z = 0; z < 4; z++) { + for (Player player : r.assemblePlayerList(z)) { + if (player != null) { + setDispenser(player); + } } } handlePlankSwitching(); @@ -248,19 +250,19 @@ public final class BrimhavenArena extends MapZone implements Plugin { if (GameWorld.getTicks() % ticks == 0) { sawBladeActive = !sawBladeActive; if (sawBladeActive) { - Scenery object = RegionManager.getObject(3, 2788, 9579); + Scenery object = RegionManager.getObject(2788, 9579, 3); SceneryBuilder.replace(object, object.transform(3567, object.getRotation(), 10), ticks); - object = RegionManager.getObject(3, 2789, 9579); + object = RegionManager.getObject(2789, 9579, 3); SceneryBuilder.replace(object, object.transform(0), ticks); - object = RegionManager.getObject(3, 2783, 9551); + object = RegionManager.getObject(2783, 9551, 3); SceneryBuilder.replace(object, object.transform(3567, object.getRotation(), 10), ticks); - object = RegionManager.getObject(3, 2783, 9552); + object = RegionManager.getObject(2783, 9552, 3); SceneryBuilder.replace(object, object.transform(0), ticks); - object = RegionManager.getObject(3, 2761, 9584); + object = RegionManager.getObject(2761, 9584, 3); SceneryBuilder.replace(object, object.transform(3567, object.getRotation(), 10), ticks); - object = RegionManager.getObject(3, 2761, 9585); + object = RegionManager.getObject(2761, 9585, 3); SceneryBuilder.replace(object, object.transform(0), ticks); } } diff --git a/Server/src/main/content/global/skill/construction/BuildRoomDialogue.java b/Server/src/main/content/global/skill/construction/BuildRoomDialogue.java index b74e99cb3..9e9dde9df 100644 --- a/Server/src/main/content/global/skill/construction/BuildRoomDialogue.java +++ b/Server/src/main/content/global/skill/construction/BuildRoomDialogue.java @@ -241,7 +241,7 @@ public final class BuildRoomDialogue extends DialoguePlugin { } int rotation = directions[index].toInteger(); boundaries.clear(); - Location base = player.getViewport().getRegion().getBaseLocation().transform(roomX << 3, roomY << 3, player.getLocation().getZ()); + Location base = player.getLocation().getRegion().getBaseLocation().transform(roomX << 3, roomY << 3, player.getLocation().getZ()); for (int x = 0; x < 8; x++) { for (int y = 0; y < 8; y++) { Scenery[] objects = room.getChunk().getObjects(x, y); diff --git a/Server/src/main/content/global/skill/construction/BuildingUtils.java b/Server/src/main/content/global/skill/construction/BuildingUtils.java index 2ee636337..ad8b71bcd 100644 --- a/Server/src/main/content/global/skill/construction/BuildingUtils.java +++ b/Server/src/main/content/global/skill/construction/BuildingUtils.java @@ -8,10 +8,7 @@ import core.game.node.item.Item; import core.game.node.scenery.Scenery; import core.game.node.scenery.SceneryBuilder; import core.game.system.task.Pulse; -import core.game.world.map.BuildRegionChunk; -import core.game.world.map.Direction; -import core.game.world.map.Location; -import core.game.world.map.Region; +import core.game.world.map.*; import core.game.world.update.flag.context.Animation; import core.net.packet.PacketRepository; import core.net.packet.context.ContainerContext; @@ -250,7 +247,7 @@ public final class BuildingUtils { if (reg == null) { continue; } - BuildRegionChunk chunk = (BuildRegionChunk) reg.getPlanes()[plane % 3].getChunks()[l.getChunkX()][l.getChunkY()]; + RegionChunk chunk = reg.getChunks()[l.getChunkX()][l.getChunkY()][plane % 3]; Scenery[] objects = chunk.getObjects(h.getCurrentX(), h.getCurrentY()); for (Scenery o : objects) { if (o != null && o.getType() == object.getType()) { @@ -284,16 +281,19 @@ public final class BuildingUtils { hotspot.setDecorationIndex(decIndex); break; case RECURSIVE: - BuildRegionChunk chunk = (BuildRegionChunk) region.getPlanes()[l.getZ()].getChunks()[l.getChunkX()][l.getChunkY()]; + RegionChunk chunk = region.getChunks()[l.getChunkX()][l.getChunkY()][l.getZ()]; for (int x = 0; x < 8; x++) { for (int y = 0; y < 8; y++) { Hotspot h = room.getHotspot(hotspot.getHotspot(), x, y); if (h != null) { h.setDecorationIndex(decIndex); int objectId = hotspot.getHotspot().getObjectId(style); - Scenery o = chunk.get(x, y, chunk.getIndex(x, y, objectId)); - if (o != null && objectId == o.getId()) { - SceneryBuilder.replace(o, o.transform(hotspot.getHotspot().getDecorations()[decIndex].getObjectId(style))); + int index = chunk.getIndex(x, y, objectId, -1); + if (index != -1) { + Scenery o = chunk.getObjects()[x][y][index]; + if (objectId == o.getId()) { + SceneryBuilder.replace(o, o.transform(hotspot.getHotspot().getDecorations()[decIndex].getObjectId(style))); + } } } } @@ -301,7 +301,7 @@ public final class BuildingUtils { break; case LINKED: BuildHotspot[] linkedHotspots = BuildHotspot.getLinkedHotspots(hotspot.getHotspot()); - chunk = (BuildRegionChunk) region.getPlanes()[l.getZ()].getChunks()[l.getChunkX()][l.getChunkY()]; + chunk = region.getChunks()[l.getChunkX()][l.getChunkY()][l.getZ()]; for (int x = 0; x < 8; x++) { for (int y = 0; y < 8; y++) { for (BuildHotspot bh : linkedHotspots) { @@ -309,9 +309,12 @@ public final class BuildingUtils { if (h != null) { h.setDecorationIndex(decIndex); int objectId = bh.getObjectId(style); - Scenery o = chunk.get(x, y, chunk.getIndex(x, y, objectId)); - if (o != null && objectId == o.getId()) { - SceneryBuilder.replace(o, o.transform(bh.getDecorations()[decIndex].getObjectId(style))); + int index = chunk.getIndex(x, y, objectId, -1); + if (index != -1) { + Scenery o = chunk.getObjects()[x][y][index]; + if (objectId == o.getId()) { + SceneryBuilder.replace(o, o.transform(bh.getDecorations()[decIndex].getObjectId(style))); + } } } } @@ -385,7 +388,7 @@ public final class BuildingUtils { if (reg == null) { continue; } - BuildRegionChunk chunk = (BuildRegionChunk) reg.getPlanes()[plane % 3].getChunks()[l.getChunkX()][l.getChunkY()]; + RegionChunk chunk = reg.getChunks()[l.getChunkX()][l.getChunkY()][plane % 3]; Scenery[] objects = chunk.getObjects(h.getCurrentX(), h.getCurrentY()); for (Scenery o : objects) { if (o != null && o.getType() == object.getType()) { @@ -417,13 +420,14 @@ public final class BuildingUtils { hotspot.setDecorationIndex(-1); break; case RECURSIVE: - BuildRegionChunk chunk = (BuildRegionChunk) region.getPlanes()[l.getZ()].getChunks()[l.getChunkX()][l.getChunkY()]; + RegionChunk chunk = region.getChunks()[l.getChunkX()][l.getChunkY()][l.getZ()]; for (int x = 0; x < 8; x++) { for (int y = 0; y < 8; y++) { Hotspot h = room.getHotspot(hotspot.getHotspot(), x, y); if (h != null) { int objectId = hotspot.getHotspot().getDecorations()[h.getDecorationIndex()].getObjectId(style); - Scenery o = chunk.get(x, y, chunk.getIndex(x, y, objectId)); + int index = chunk.getIndex(x, y, objectId, -1); + Scenery o = chunk.getObjects()[x][y][index]; h.setDecorationIndex(-1); if (o != null && objectId == o.getId()) { SceneryBuilder.replace(o, o.transform(hotspot.getHotspot().getObjectId(style))); @@ -434,14 +438,15 @@ public final class BuildingUtils { break; case LINKED: BuildHotspot[] linkedHotspots = BuildHotspot.getLinkedHotspots(hotspot.getHotspot()); - chunk = (BuildRegionChunk) region.getPlanes()[l.getZ()].getChunks()[l.getChunkX()][l.getChunkY()]; + chunk = region.getChunks()[l.getChunkX()][l.getChunkY()][l.getZ()]; for (int x = 0; x < 8; x++) { for (int y = 0; y < 8; y++) { for (BuildHotspot bh : linkedHotspots) { Hotspot h = room.getHotspot(bh, x, y); if (h != null) { int objectId = bh.getDecorations()[h.getDecorationIndex()].getObjectId(style); - Scenery o = chunk.get(x, y, chunk.getIndex(x, y, objectId)); + int index = chunk.getIndex(x, y, objectId, -1); + Scenery o = chunk.getObjects()[x][y][index]; h.setDecorationIndex(-1); if (o != null && objectId == o.getId()) { SceneryBuilder.replace(o, o.transform(bh.getObjectId(style))); diff --git a/Server/src/main/content/global/skill/construction/HouseManager.java b/Server/src/main/content/global/skill/construction/HouseManager.java index 4db8e8191..773d252b5 100644 --- a/Server/src/main/content/global/skill/construction/HouseManager.java +++ b/Server/src/main/content/global/skill/construction/HouseManager.java @@ -20,19 +20,18 @@ import core.game.world.GameWorld; import org.rs09.consts.Sounds; import java.awt.*; +import java.util.ArrayList; import static core.api.ContentAPIKt.*; import static core.api.regionspec.RegionSpecificationKt.fillWith; import static core.api.regionspec.RegionSpecificationKt.using; - /** * Manages the player's house. * @author Emperor * */ public final class HouseManager { - /** * The current region. */ @@ -97,7 +96,6 @@ public final class HouseManager { */ } - public void parse(JSONObject data){ location = HouseLocation.values()[Integer.parseInt( data.get("location").toString())]; style = HousingStyle.values()[Integer.parseInt( data.get("style").toString())]; @@ -195,8 +193,8 @@ public final class HouseManager { } private void updateVarbits(Player player, boolean build) { - setVarp(player, 261, build ? 1 : 0); - setVarp(player, 262, getRoomAmount()); + setVarp(player, 261, build ? 1 : 0); + setVarp(player, 262, getRoomAmount()); } private void unlockMusicTrack(Player player) { @@ -241,13 +239,13 @@ public final class HouseManager { * Reloads the house. * @param player The player. * @param buildingMode If building mode should be enabled. - * NOTE: I think we should avoid this method, it might be causing some issues. It's actually really suspicious... + * NOTE: I think we should avoid this method, it might be causing some issues. It's actually really suspicious... */ public void reload(Player player, boolean buildingMode) { int diffX = player.getLocation().getLocalX(); int diffY = player.getLocation().getLocalY(); int diffZ = player.getLocation().getZ(); - boolean inDungeon = player.getViewport().getRegion() == dungeonRegion; + boolean inDungeon = player.getLocation().getRegion() == dungeonRegion; this.buildingMode = buildingMode; construct(); Location newLoc = (dungeonRegion == null ? houseRegion : (inDungeon ? dungeonRegion : houseRegion)).getBaseLocation().transform(diffX,diffY,diffZ); @@ -260,22 +258,17 @@ public final class HouseManager { */ public void expelGuests(Player player) { if (isLoaded()) { - for (RegionPlane plane : houseRegion.getPlanes()) { - for (Player p : plane.getPlayers()) { + for (int z = 0; z < Region.PLANES; z++) { + ArrayList players = new ArrayList<>(houseRegion.assemblePlayerList(z)); + if (dungeonRegion != null) { + players.addAll(dungeonRegion.assemblePlayerList(z)); + } + for (Player p : players) { if (p != player) { leave(p); } } } - if (dungeonRegion != null) { - for (RegionPlane plane : dungeonRegion.getPlanes()) { - for (Player p : plane.getPlayers()) { - if (p != player) { - leave(p); - } - } - } - } } } @@ -296,7 +289,7 @@ public final class HouseManager { if (h.getDecorationIndex() > -1) { Decoration d = h.getHotspot().getDecorations()[h.getDecorationIndex()]; if (d == Decoration.PORTAL) { - return houseRegion.getBaseLocation().transform(x * 8 + h.getChunkX(), y * 8 + h.getChunkY() + 2, 0); + return houseRegion.getBaseLocation().transform(x*RegionChunk.SIZE + h.getChunkX(), y*RegionChunk.SIZE + h.getChunkY() + 2, 0); } } } @@ -312,9 +305,9 @@ public final class HouseManager { */ public void redecorate(HousingStyle style) { this.style = style; - for (int z = 0; z < 4; z++) { - for (int x = 0; x < 8; x++) { - for (int y = 0; y < 8; y++) { + for (int z = 0; z < Region.PLANES; z++) { + for (int x = 0; x < RegionChunk.SIZE; x++) { + for (int y = 0; y < RegionChunk.SIZE; y++) { Room room = rooms[z][x][y]; if (room != null) { room.decorate(style); @@ -329,9 +322,9 @@ public final class HouseManager { */ @Deprecated public void clearRooms() { - for (int z = 0; z < 4; z++) { - for (int x = 0; x < 8; x++) { - for (int y = 0; y < 8; y++) { + for (int z = 0; z < Region.PLANES; z++) { + for (int x = 0; x < RegionChunk.SIZE; x++) { + for (int y = 0; y < RegionChunk.SIZE; y++) { rooms[z][x][y] = null; } } @@ -372,10 +365,9 @@ public final class HouseManager { } private DynamicRegion getPreparedRegion() { - ZoneBorders borders = DynamicRegion.reserveArea(8,8); + ZoneBorders borders = DynamicRegion.reserveArea(Region.CHUNKS_SIZE,Region.CHUNKS_SIZE); DynamicRegion region = new DynamicRegion(-1, borders.getSouthWestX() >> 6, borders.getSouthWestY() >> 6); region.setBorders(borders); - region.setUpdateAllPlanes(true); region.setBuild(true); RegionManager.addRegion(region.getId(), region); return region; @@ -393,12 +385,12 @@ public final class HouseManager { } @Override - public BuildRegionChunk getChunk(int x, int y, int plane, @NotNull DynamicRegion dyn) { - return rooms[plane][x][y].getChunk().copy(dyn.getPlanes()[plane]); + public @Nullable RegionChunk getChunk(int x, int y, int plane, @NotNull DynamicRegion dyn) { + return rooms[plane][x][y].getChunk().copy(); } @Override - public void afterSetting(@Nullable BuildRegionChunk chunk, int x, int y, int plane, @NotNull DynamicRegion dyn) { + public void afterSetting(@Nullable RegionChunk chunk, int x, int y, int plane, @NotNull DynamicRegion dyn) { rooms[plane][x][y].loadDecorations(dyn != manager.dungeonRegion ? plane : 3, chunk, manager); } } @@ -406,8 +398,8 @@ public final class HouseManager { private void prepareHouseChunks(HousingStyle style, DynamicRegion target, boolean buildingMode, Room[][][] rooms) { Region from = RegionManager.forId(style.getRegionId()); Region.load(from, true); - RegionChunk defaultChunk = from.getPlanes()[style.getPlane()].getRegionChunk(1, 0); - RegionChunk defaultSkyChunk = from.getPlanes()[1].getRegionChunk(0,0); + RegionChunk defaultChunk = from.getChunks()[1][0][style.getPlane()]; + RegionChunk defaultSkyChunk = from.getChunks()[0][0][1]; RoomLoadContract loadRooms = new RoomLoadContract(this, buildingMode, rooms); RegionSpecification spec = new RegionSpecification( @@ -430,7 +422,7 @@ public final class HouseManager { private void prepareDungeonChunks(HousingStyle style, DynamicRegion target, DynamicRegion house, boolean buildingMode, Room[][] rooms) { Region from = RegionManager.forId(style.getRegionId()); Region.load(from, true); - RegionChunk defaultChunk = from.getPlanes()[style.getPlane()].getRegionChunk(3, 0); + RegionChunk defaultChunk = from.getChunks()[3][0][style.getPlane()]; RoomLoadContract loadRooms = new RoomLoadContract(this, buildingMode, new Room[][][]{rooms}); RegionSpecification spec = new RegionSpecification( @@ -609,8 +601,8 @@ public final class HouseManager { int startY = 99; int endX = 0; int endY = 0; - for (int x = 0; x < 8; x++) { - for (int y = 0; y < 8; y++) { + for (int x = 0; x < Region.CHUNKS_SIZE; x++) { + for (int y = 0; y < Region.CHUNKS_SIZE; y++) { if (rooms[0][x][y] != null) { if (x < startX) startX = x; if (y < startY) startY = y; @@ -688,7 +680,7 @@ public final class HouseManager { * @return {@code True} if so. */ public boolean isInHouse(Player player) { - return isLoaded() && (player.getViewport().getRegion() == houseRegion || player.getViewport().getRegion() == dungeonRegion); + return isLoaded() && (player.getLocation().getRegion() == houseRegion || player.getLocation().getRegion() == dungeonRegion); } /** @@ -697,16 +689,13 @@ public final class HouseManager { * @return {@code True} if so. */ public static boolean isInDungeon(Player player) { - return player.getViewport().getRegion() == player.getHouseManager().dungeonRegion; + return player.getLocation().getRegion() == player.getHouseManager().dungeonRegion; } /** * Checks if the house region was constructed and active. * @return {@code True} if an active region for the house exists. */ - //public boolean isLoaded() { - // return (houseRegion != null) || (dungeonRegion != null); - //} public boolean isLoaded() { return (houseRegion != null && houseRegion.isActive()) || (dungeonRegion != null && dungeonRegion.isActive()); } diff --git a/Server/src/main/content/global/skill/construction/HousingStyle.java b/Server/src/main/content/global/skill/construction/HousingStyle.java index eace0e7bb..e2213e405 100644 --- a/Server/src/main/content/global/skill/construction/HousingStyle.java +++ b/Server/src/main/content/global/skill/construction/HousingStyle.java @@ -11,37 +11,35 @@ import java.util.Arrays; * */ public enum HousingStyle { - - // open door ids are doorId + 1 - + // open door ids are doorId + 1 BASIC_WOOD (1, 5000, 7503, 0, 13100, 13101, 13098, Decoration.BASIC_WOOD_WINDOW), - BASIC_STONE (10, 5000, 7503, 1, 13094, 13096, 1902, Decoration.BASIC_STONE_WINDOW), + BASIC_STONE (10, 5000, 7503, 1, 13094, 13096, 1902, 13090, Decoration.BASIC_STONE_WINDOW), WHITEWASHED_STONE (20, 7500, 7503, 2, 13006, 13007, 1415, Decoration.WHITEWASHED_STONE_WINDOW), FREMENNIK_STYLE_WOOD(30, 10000, 7503, 3, 13109, 13107, 13111, Decoration.FREMENNIK_WINDOW), TROPICAL_WOOD (40, 15000, 7759, 0, 13016, 13015, 13011, Decoration.TROPICAL_WOOD_WINDOW), FANCY_STONE (50, 25000, 7759, 1, 13119, 13118, 13116, Decoration.FANCY_STONE_WINDOW); - /** - * Array of all Dungeon Wall IDs. - * From Region 7503, Location.create(1898, 5084, 0) - */ - private static final int[] DUNGEON_WALL_IDS = { - 13019, 13020, 13021, 13022, 13023, 13024, 13025, 13026, 13027, 13028, - 13029, 13030, 13031, 13032, 13033, 13034, 13035, 13036, 13037, 13046, - 13048, 13049, 13050, 13051, 13055, 13056, 13058, 13059, 13060, 13061, - 13062, 13063, 13065, 13066, 13067, 13068, 13069, 13070, 13072, 13073, - 13074, 13075, 13076, 13077, 13079, 13080, 13081, 13082, 13083, 13084, - 13086, 13087, 13088, 13089 - }; + /** + * Array of all Dungeon Wall IDs. + * From Region 7503, Location.create(1898, 5084, 0) + */ + private static final int[] DUNGEON_WALL_IDS = { + 13019, 13020, 13021, 13022, 13023, 13024, 13025, 13026, 13027, 13028, + 13029, 13030, 13031, 13032, 13033, 13034, 13035, 13036, 13037, 13046, + 13048, 13049, 13050, 13051, 13055, 13056, 13058, 13059, 13060, 13061, + 13062, 13063, 13065, 13066, 13067, 13068, 13069, 13070, 13072, 13073, + 13074, 13075, 13076, 13077, 13079, 13080, 13081, 13082, 13083, 13084, + 13086, 13087, 13088, 13089 + }; - /** - * Checks if the provided ID is a dungeon wall. - * @param id The object ID. - * @return {@code true} if it's a dungeon wall. - */ - public static boolean isDungeonWall(int id) { - return Arrays.binarySearch(DUNGEON_WALL_IDS, id) >= 0; - } + /** + * Checks if the provided ID is a dungeon wall. + * @param id The object ID. + * @return {@code true} if it's a dungeon wall. + */ + public static boolean isDungeonWall(int id) { + return Arrays.binarySearch(DUNGEON_WALL_IDS, id) >= 0; + } /** * The level required. @@ -77,21 +75,26 @@ public enum HousingStyle { * The wall id. */ private final int wallId; + + /** + * A second wall id, used for walls behind fireplaces in some house styles. + * Only used for the basic stone house style, who knows why. Same as wallId for other house styles. + */ + private final int secondWallId; /** * The window style */ private final Decoration window; - /** - * Checks if the player has the level. - * - * @param player the player. - * @return {@code True} if so. - */ - public boolean hasLevel(Player player) { - return player.getSkills().getStaticLevel(Skills.CONSTRUCTION) >= levelRequirement; - } + /** + * Checks if the player has the level. + * @param player the player. + * @return {@code True} if so. + */ + public boolean hasLevel(Player player) { + return player.getSkills().getStaticLevel(Skills.CONSTRUCTION) >= levelRequirement; + } /** * Constructs a new {@code HousingStyle} {@code Object} @@ -100,8 +103,12 @@ public enum HousingStyle { * @param regionId The region id for this style. * @param plane The plane for this style. * @param doorId The door object id used in this style. + * @param secondDoorId The second-door object id used in this style. + * @param wallId The wall object id used in this style. + * @param secondWallId The second-wall object id used in this style. + * @param window The window decoration used in this style. */ - private HousingStyle(int level, int cost, int regionId, int plane, int doorId, int secondDoorId, int wallId, Decoration window) { + HousingStyle(int level, int cost, int regionId, int plane, int doorId, int secondDoorId, int wallId, int secondWallId, Decoration window) { this.levelRequirement = level; this.cost = cost; this.regionId = regionId; @@ -109,6 +116,30 @@ public enum HousingStyle { this.doorId = doorId; this.secondDoorId = secondDoorId; this.wallId = wallId; + this.secondWallId = secondWallId; + this.window = window; + } + + /** + * Constructs a new {@code HousingStyle} {@code Object} for housing styles that do not have a second wall id + * @param level The level required. + * @param cost The cost of the style. + * @param regionId The region id for this style. + * @param plane The plane for this style. + * @param doorId The door object id used in this style. + * @param secondDoorId The second-door object id used in this style. + * @param wallId The wall object id used in this style. + * @param window The window decoration used in this style. + */ + HousingStyle(int level, int cost, int regionId, int plane, int doorId, int secondDoorId, int wallId, Decoration window) { + this.levelRequirement = level; + this.cost = cost; + this.regionId = regionId; + this.plane = plane; + this.doorId = doorId; + this.secondDoorId = secondDoorId; + this.wallId = wallId; + this.secondWallId = wallId; this.window = window; } @@ -159,7 +190,15 @@ public enum HousingStyle { public int getWallId() { return wallId; } - + + /** + * Gets the second wall used in this style. + * @return The wall object id. + */ + public int getSecondWallId() { + return secondWallId; + } + /** * Gets the window id for this style of house * @return The windows object id @@ -175,4 +214,4 @@ public enum HousingStyle { public int getSecondDoorId() { return secondDoorId; } -} \ No newline at end of file +} diff --git a/Server/src/main/content/global/skill/construction/Room.java b/Server/src/main/content/global/skill/construction/Room.java index ac7b56cec..1dbbaecb7 100644 --- a/Server/src/main/content/global/skill/construction/Room.java +++ b/Server/src/main/content/global/skill/construction/Room.java @@ -1,6 +1,5 @@ package content.global.skill.construction; - import core.game.node.entity.player.Player; import core.game.node.scenery.Constructed; import core.game.node.scenery.Scenery; @@ -16,7 +15,6 @@ import static core.api.ContentAPIKt.log; * */ public final class Room { - /** * The default room type. */ @@ -95,7 +93,7 @@ public final class Room { public void decorate(HousingStyle style) { Region region = RegionManager.forId(style.getRegionId()); Region.load(region, true); - chunk = region.getPlanes()[style.getPlane()].getRegionChunk(properties.getChunkX(), properties.getChunkY()); + chunk = region.getChunks()[properties.getChunkX()][properties.getChunkY()][style.getPlane()]; } /** @@ -126,63 +124,59 @@ public final class Room { * Loads all the decorations. * @param housePlane The plane. * @param chunk The chunk used in the dynamic region. - */ - public void loadDecorations(int housePlane, BuildRegionChunk chunk, HouseManager house) { - for (int i = 0; i < hotspots.length; i++) { - Hotspot spot = hotspots[i]; + */ + public void loadDecorations(int housePlane, RegionChunk chunk, HouseManager house) { + for (Hotspot spot : hotspots) { int x = spot.getChunkX(); int y = spot.getChunkY(); - if (spot.getHotspot() == null) { - continue; - } - int index = chunk.getIndex(x, y, spot.getHotspot().getObjectId(house.getStyle())); - Scenery[][] objects = chunk.getObjects(index); - Scenery object = objects[x][y]; - if (object != null && object.getId() == spot.getHotspot().getObjectId(house.getStyle())) { - if (spot.getDecorationIndex() > -1 && spot.getDecorationIndex() < spot.getHotspot().getDecorations().length) { - int id = spot.getHotspot().getDecorations()[spot.getDecorationIndex()].getObjectId(house.getStyle()); - if (spot.getHotspot().getType() == BuildHotspotType.CREST) { - id += house.getCrest().ordinal(); + int id = spot.getHotspot().getObjectId(house.getStyle()); + int index = chunk.getIndex(x, y, id, -1); + if (index != -1) { + Scenery object = chunk.getObjects()[x][y][index]; + if (object.getId() == spot.getHotspot().getObjectId(house.getStyle())) { + if (spot.getDecorationIndex() > -1 && spot.getDecorationIndex() < spot.getHotspot().getDecorations().length) { + id = spot.getHotspot().getDecorations()[spot.getDecorationIndex()].getObjectId(house.getStyle()); + if (spot.getHotspot().getType() == BuildHotspotType.CREST) { + id += house.getCrest().ordinal(); + } + SceneryBuilder.replace(object, object.transform(id, object.getRotation(), chunk.getCurrentBase().transform(x, y, 0))); + } else if (object.getId() == BuildHotspot.WINDOW.getObjectId(house.getStyle()) || (!house.isBuildingMode() && object.getId() == BuildHotspot.CHAPEL_WINDOW.getObjectId(house.getStyle()))) { + SceneryBuilder.replace(object, object.transform(house.getStyle().getWindowStyle().getObjectId(house.getStyle()), object.getRotation(), object.getType())); } - SceneryBuilder.replace(object, object.transform(id, object.getRotation(), chunk.getCurrentBase().transform(x, y, 0))); + int[] pos = RegionChunk.getRotatedPosition(x, y, object.getSizeX(), object.getSizeY(), 0, rotation.toInteger()); + spot.setCurrentX(pos[0]); + spot.setCurrentY(pos[1]); } - else if (object.getId() == BuildHotspot.WINDOW.getObjectId(house.getStyle()) || (!house.isBuildingMode() && object.getId() == BuildHotspot.CHAPEL_WINDOW.getObjectId(house.getStyle()))) { - chunk.add(object.transform(house.getStyle().getWindowStyle().getObjectId(house.getStyle()), object.getRotation(), object.getType())); - } - int[] pos = RegionChunk.getRotatedPosition(x, y, object.getSizeX(), object.getSizeY(), 0, rotation.toInteger()); - spot.setCurrentX(pos[0]); - spot.setCurrentY(pos[1]); } } - if (rotation != Direction.NORTH && chunk.getRotation() == 0) { + if (rotation != Direction.NORTH && chunk.getRotation() == 0) { //TODO: it is absolutely insane that this is using Direction.NORTH and such rather than RegionChunk.NORTH_ROTATION and such. chunk.rotate(rotation); } if (!house.isBuildingMode()) { placeDoors(housePlane, house, chunk); - removeHotspots(housePlane, house, chunk); + removeHotspots(house, chunk); } } /** * Removes the building hotspots from the room. - * @param housePlane The room's plane in house. * @param house The house manager. * @param chunk The region chunk used. */ - private void removeHotspots(int housePlane, HouseManager house, BuildRegionChunk chunk) { + private void removeHotspots(HouseManager house, RegionChunk chunk) { if (properties.isRoof()) return; for (int x = 0; x < 8; x++) { for (int y = 0; y < 8; y++) { - for (int i = 0; i < BuildRegionChunk.ARRAY_SIZE; i++) { - Scenery object = chunk.get(x, y, i); + for (int i = 0; i < RegionChunk.ARRAY_SIZE; i++) { + Scenery object = chunk.getObjects()[x][y][i]; if (object != null) { - int id = object.getId(); - boolean isBuilt = object instanceof Constructed; - boolean isWall = HousingStyle.isDungeonWall(id) || id == house.getStyle().getWallId(); - boolean isDoor = id == house.getStyle().getDoorId() || id == house.getStyle().getSecondDoorId(); - if (!isBuilt && !isWall && !isDoor) { + int id = object.getId(); + boolean isBuilt = !object.getDefinition().hasAction("build"); + boolean isGround = object.getType() == 22 && !object.getDefinition().hasOptions(); + boolean isWall = HousingStyle.isDungeonWall(id) || id == house.getStyle().getWallId() || id == house.getStyle().getSecondWallId(); + boolean isDoor = id == house.getStyle().getDoorId() || id == house.getStyle().getSecondDoorId(); + if (!isBuilt && !isGround && !isWall && !isDoor) { SceneryBuilder.remove(object); - chunk.remove(object); } } } @@ -197,14 +191,14 @@ public final class Room { * @param house The house manager. * @param chunk The region chunk used. */ - private void placeDoors(int housePlane, HouseManager house, BuildRegionChunk chunk) { + private void placeDoors(int housePlane, HouseManager house, RegionChunk chunk) { Room[][][] rooms = house.getRooms(); int rx = chunk.getCurrentBase().getChunkX(); int ry = chunk.getCurrentBase().getChunkY(); - for (int i = 0; i < BuildRegionChunk.ARRAY_SIZE; i++) { + for (int i = 0; i < RegionChunk.ARRAY_SIZE; i++) { for (int x = 0; x < 8; x++) { for (int y = 0; y < 8; y++) { - Scenery object = chunk.get(x, y, i); + Scenery object = chunk.getObjects()[x][y][i]; if (object != null && BuildingUtils.isDoorHotspot(object)) { boolean edge = false; Room otherRoom = null; @@ -274,7 +268,7 @@ public final class Room { return object.getId() % 2 != 0 ? house.getStyle().getDoorId() : house.getStyle().getSecondDoorId(); } } - return room.getProperties().isDungeon() ? 13065 : house.getStyle().getWallId(); + return room.getProperties().isDungeon() ? 13065 : house.getStyle().getWallId(); //otherWallId is only used behind fireplaces } /** @@ -423,5 +417,4 @@ public final class Room { public Direction getRotation() { return rotation; } - } diff --git a/Server/src/main/content/global/skill/construction/RoomProperties.java b/Server/src/main/content/global/skill/construction/RoomProperties.java index 80bc6e6ed..f4199d46f 100644 --- a/Server/src/main/content/global/skill/construction/RoomProperties.java +++ b/Server/src/main/content/global/skill/construction/RoomProperties.java @@ -920,7 +920,7 @@ public enum RoomProperties { public boolean[] getExits() { Region region = RegionManager.forId(7503); Region.load(region, true); - RegionChunk chunk = region.getPlanes()[0].getRegionChunk(chunkX, chunkY); + RegionChunk chunk = region.getChunks()[chunkX][chunkY][0]; return new boolean[] { isExit(chunk, 7, 3), isExit(chunk, 3, 0), isExit(chunk, 0, 3), isExit(chunk, 3, 7) }; } diff --git a/Server/src/main/content/global/skill/construction/decoration/StaircasePlugin.java b/Server/src/main/content/global/skill/construction/decoration/StaircasePlugin.java index 8c22d84ab..b98ed4e01 100644 --- a/Server/src/main/content/global/skill/construction/decoration/StaircasePlugin.java +++ b/Server/src/main/content/global/skill/construction/decoration/StaircasePlugin.java @@ -29,18 +29,18 @@ public final class StaircasePlugin extends OptionHandler { public Plugin newInstance(Object arg) throws Throwable { ClassScanner.definePlugin(new BuildDialogue()); ClassScanner.definePlugin(new ClimbPohLadder()); - // 13497 - 13502 are normal stairs - // 13503 - 13506 are spiral stairs + // 13497 - 13502 are normal stairs + // 13503 - 13506 are spiral stairs for (int i = 13497; i <= 13506; i++) { SceneryDefinition.forId(i).getHandlers().put("option:climb", this); SceneryDefinition.forId(i).getHandlers().put("option:climb-up", this); SceneryDefinition.forId(i).getHandlers().put("option:climb-down", this); SceneryDefinition.forId(i).getHandlers().put("option:remove-room", this); } - // 13409 is dungeon entrance + // 13409 is dungeon entrance SceneryDefinition.forId(13409).getHandlers().put("option:enter", this); SceneryDefinition.forId(13409).getHandlers().put("option:remove-room", this); - // 13328 - 13330 are dungeon ladders + // 13328 - 13330 are dungeon ladders for (int id = 13328; id <= 13330; id++) { SceneryDefinition.forId(id).getHandlers().put("option:climb", this); SceneryDefinition.forId(id).getHandlers().put("option:remove-room", this); @@ -73,45 +73,43 @@ public final class StaircasePlugin extends OptionHandler { SceneryBuilder.replace(object, object.transform(object.getId() - 3)); return true; case "remove-room": + int currentZ = player.getLocation().getZ(); + int chunkX = player.getLocation().getChunkX(); + int chunkY = player.getLocation().getChunkY(); - int currentZ = player.getLocation().getZ(); - int chunkX = player.getLocation().getChunkX(); - int chunkY = player.getLocation().getChunkY(); + // must be in building mode + if(!player.getHouseManager().isBuildingMode()) { + player.getPacketDispatch().sendMessage("You can only do this in building mode."); + return true; + } - // must be in building mode - if(!player.getHouseManager().isBuildingMode()) { - player.getPacketDispatch().sendMessage("You can only do this in building mode."); - return true; - } + // if you're in the dungeon, try and remove the room at Z = 0 from the house region. + if (HouseManager.isInDungeon(player)) { + player.getDialogueInterpreter().open("con:remove", null, new int[]{chunkX, chunkY}, 0); - // if you're in the dungeon, try and remove the room at Z = 0 from the house region. - if (HouseManager.isInDungeon(player)) { - player.getDialogueInterpreter().open("con:remove", null, new int[]{chunkX, chunkY}, 0); + // From ground floor (z=0), stairs could lead to 1st floor (z=1) or the dungeon (z=3) + } else { + // garden dungeon entrance and oubliette ladders don't need a height selection + if (node.getId() == 13409 || node.getId() == 13675 || node.getId() == 13676 || node.getId() == 13677 || node.getId() == 13678 || node.getId() == 13679 || node.getId() == 13680) { + player.getDialogueInterpreter().open("con:remove", null, new int[]{chunkX, chunkY}, 3); + } else { + player.getDialogueInterpreter().sendOptions("Remove which room?", "Room Above", "Room Below", "Cancel"); + player.getDialogueInterpreter().addAction((interId, btn) -> { + if (btn == 2) { // Above + player.getDialogueInterpreter().open("con:remove", null, new int[]{chunkX, chunkY}, currentZ + 1); + } else if (btn == 3) { // Below + if (currentZ - 1 == -1) { + player.getDialogueInterpreter().open("con:remove", null, new int[]{chunkX, chunkY}, 3); + } else { + player.getDialogueInterpreter().open("con:remove", null, new int[]{chunkX, chunkY}, currentZ - 1); + } - // From ground floor (z=0), stairs could lead to 1st floor (z=1) or the dungeon (z=3) - } else { - // garden dungeon entrance and oubliette ladders don't need a height selection - if(node.getId() == 13409 || node.getId() == 13675 || node.getId() == 13676 || node.getId() == 13677 || node.getId() == 13678 || node.getId() == 13679 || node.getId() == 13680) { - player.getDialogueInterpreter().open("con:remove", null, new int[]{chunkX, chunkY}, 3); - } else { - player.getDialogueInterpreter().sendOptions("Remove which room?", "Room Above", "Room Below", "Cancel"); - player.getDialogueInterpreter().addAction((interId, btn) -> { - if (btn == 2) { // Above - player.getDialogueInterpreter().open("con:remove", null, new int[]{chunkX, chunkY}, currentZ + 1); - } else if (btn == 3) { // Below - if (currentZ - 1 == -1) { - player.getDialogueInterpreter().open("con:remove", null, new int[]{chunkX, chunkY}, 3); - } else { - player.getDialogueInterpreter().open("con:remove", null, new int[]{chunkX, chunkY}, currentZ - 1); - } - - } - }); - } - - } - return true; + } + }); + } + } + return true; case "climb": if (HouseManager.isInDungeon(player)) { climb(player, 1, house, object); @@ -151,7 +149,7 @@ public final class StaircasePlugin extends OptionHandler { if (plane < 0) { //Dungeon plane = 3; } - else if (player.getViewport().getRegion() == house.getDungeonRegion() && plane == 1) {//going up + else if (player.getLocation().getRegion() == house.getDungeonRegion() && plane == 1) {//going up plane = 0; } Room room = house.getRooms()[plane][roomX][roomY]; @@ -165,7 +163,7 @@ public final class StaircasePlugin extends OptionHandler { } } else { Location destination = l.transform(0, 0, z); - if (player.getViewport().getRegion() == house.getDungeonRegion()) { + if (player.getLocation().getRegion() == house.getDungeonRegion()) { destination = house.getHouseRegion().getBaseLocation().transform(l.getLocalX(), l.getLocalY(), 0); } else if (plane == 3) { @@ -417,7 +415,7 @@ public final class StaircasePlugin extends OptionHandler { for (Direction d : dirs) { if (d == room.getRotation()) { r.setRotation(d); - // todo this just puts a default set of stairs in the dungeon. The below code works for skill/quest hall, but not a dungeon entrance. + // todo this just puts a default set of stairs in the dungeon. The below code works for skill/quest hall, but not a dungeon entrance. //Hotspot stairs = room.getStairs(); //int index = stairs != null ? stairs.getDecorationIndex() : -1; BuildingUtils.buildRoom(player, r, plane, roomX, roomY, r.getExits(), true); @@ -475,4 +473,4 @@ public final class StaircasePlugin extends OptionHandler { } } -} \ No newline at end of file +} diff --git a/Server/src/main/content/global/skill/construction/decoration/chapel/BoneOfferPlugin.java b/Server/src/main/content/global/skill/construction/decoration/chapel/BoneOfferPlugin.java index e681c861d..0ca3bc038 100644 --- a/Server/src/main/content/global/skill/construction/decoration/chapel/BoneOfferPlugin.java +++ b/Server/src/main/content/global/skill/construction/decoration/chapel/BoneOfferPlugin.java @@ -8,7 +8,6 @@ import core.game.node.entity.skill.Skills; import core.game.interaction.NodeUsageEvent; import core.game.interaction.UseWithHandler; import core.game.node.entity.player.Player; -import core.game.node.entity.player.link.audio.Audio; import core.game.node.item.Item; import core.game.node.scenery.Scenery; import core.game.system.task.Pulse; @@ -60,11 +59,11 @@ public class BoneOfferPlugin extends UseWithHandler { Scenery left = null; Scenery right = null; if (event.getUsedWith().asScenery().getRotation() % 2 == 0) { - left = RegionManager.getObject(event.getUsedWith().getLocation().getZ(), event.getUsedWith().getLocation().getX() + 3, event.getUsedWith().getLocation().getY()); - right = RegionManager.getObject(event.getUsedWith().getLocation().getZ(), event.getUsedWith().getLocation().getX() - 2, event.getUsedWith().getLocation().getY()); + left = RegionManager.getObject(event.getUsedWith().getLocation().getX() + 3, event.getUsedWith().getLocation().getY(), event.getUsedWith().getLocation().getZ()); + right = RegionManager.getObject(event.getUsedWith().getLocation().getX() - 2, event.getUsedWith().getLocation().getY(), event.getUsedWith().getLocation().getZ()); } else { - left = RegionManager.getObject(event.getUsedWith().getLocation().getZ(), event.getUsedWith().getLocation().getX(), event.getUsedWith().getLocation().getY() + 3); - right = RegionManager.getObject(event.getUsedWith().getLocation().getZ(), event.getUsedWith().getLocation().getX(), event.getUsedWith().getLocation().getY() - 2); + left = RegionManager.getObject(event.getUsedWith().getLocation().getX(), event.getUsedWith().getLocation().getY() + 3, event.getUsedWith().getLocation().getZ()); + right = RegionManager.getObject(event.getUsedWith().getLocation().getX(), event.getUsedWith().getLocation().getY() - 2, event.getUsedWith().getLocation().getZ()); } Bones b = Bones.forId(event.getUsedItem().getId()); if (b != null) { diff --git a/Server/src/main/content/global/skill/construction/npc/HouseServantDialogue.java b/Server/src/main/content/global/skill/construction/npc/HouseServantDialogue.java index ddb967ebe..412f96da3 100644 --- a/Server/src/main/content/global/skill/construction/npc/HouseServantDialogue.java +++ b/Server/src/main/content/global/skill/construction/npc/HouseServantDialogue.java @@ -525,7 +525,7 @@ public class HouseServantDialogue extends DialoguePlugin { @Override public boolean pulse() { - if (player == null || player.getHouseManager().getHouseRegion() != player.getViewport().getRegion()) { //TODO: Check if in dungeon? + if (player == null || player.getHouseManager().getHouseRegion() != player.getLocation().getRegion()) { //TODO: Check if in dungeon? return true; } int amt = player.getBank().getAmount(item.getId()); diff --git a/Server/src/main/content/global/skill/crafting/spinning/SpinningPulse.java b/Server/src/main/content/global/skill/crafting/spinning/SpinningPulse.java index ae652f9a8..87e0ce531 100644 --- a/Server/src/main/content/global/skill/crafting/spinning/SpinningPulse.java +++ b/Server/src/main/content/global/skill/crafting/spinning/SpinningPulse.java @@ -76,13 +76,13 @@ public final class SpinningPulse extends SkillPulse { @Override public boolean reward() { - int tickThreshhold = 4; - if (player.getAchievementDiaryManager().getDiary(DiaryType.SEERS_VILLAGE).isComplete(2) - && player.getLocation().withinDistance(Location.create(2711,3471,1)) - && player.getEquipment().get(EquipmentContainer.SLOT_HAT) != null - && player.getEquipment().get(EquipmentContainer.SLOT_HAT).getId() == 14631) { - tickThreshhold = 2; - } + int tickThreshhold = 4; + if (player.getAchievementDiaryManager().getDiary(DiaryType.SEERS_VILLAGE).isComplete(2) + && player.getLocation().withinDistance(Location.create(2711,3471,1)) + && player.getEquipment().get(EquipmentContainer.SLOT_HAT) != null + && player.getEquipment().get(EquipmentContainer.SLOT_HAT).getId() == 14631) { + tickThreshhold = 2; + } if (++ticks % tickThreshhold != 0) { return false; } @@ -92,7 +92,7 @@ public final class SpinningPulse extends SkillPulse { player.getSkills().addExperience(Skills.CRAFTING, type.getExp(), true); // Seers achievement diary - if (player.getViewport().getRegion().getId() == 10806 + if (player.getLocation().getRegionId() == 10806 && !player.getAchievementDiaryManager().getDiary(DiaryType.SEERS_VILLAGE).isComplete(0, 4)) { if (player.getAttribute("diary:seers:bowstrings-spun", 0) >= 4) { player.setAttribute("/save:diary:seers:bowstrings-spun", 5); diff --git a/Server/src/main/content/global/skill/firemaking/FireMakingPulse.java b/Server/src/main/content/global/skill/firemaking/FireMakingPulse.java index 50b969efd..7fc0b070e 100644 --- a/Server/src/main/content/global/skill/firemaking/FireMakingPulse.java +++ b/Server/src/main/content/global/skill/firemaking/FireMakingPulse.java @@ -138,7 +138,7 @@ public final class FireMakingPulse extends SkillPulse { player.faceLocation(object.getFaceLocation(player.getLocation())); player.getSkills().addExperience(Skills.FIREMAKING,fire.getXp()); - int playerRegion = player.getViewport().getRegion().getId(); + int playerRegion = player.getLocation().getRegionId(); setLastFire(); player.dispatch(new LitFireEvent(fire.getLogId())); diff --git a/Server/src/main/content/global/skill/gather/woodcutting/WoodcuttingListener.kt b/Server/src/main/content/global/skill/gather/woodcutting/WoodcuttingListener.kt index 5a4a137b8..e9c3edcad 100644 --- a/Server/src/main/content/global/skill/gather/woodcutting/WoodcuttingListener.kt +++ b/Server/src/main/content/global/skill/gather/woodcutting/WoodcuttingListener.kt @@ -212,12 +212,11 @@ class WoodcuttingListener : InteractionListener { fun animateWoodcutting(player: Player) { if (!player.animator.isAnimating) { player.animate(SkillingTool.getHatchet(player).animation) - val playersAroundMe: List = RegionManager.getLocalPlayers(player, 2) + val playersAroundMe: List = RegionManager.getLocalPlayers(player.location, 2) .stream() - .filter { p: Player -> p.username != player.username } + .filter { p -> p != player } .toList() val soundIndex = RandomFunction.random(0, woodcuttingSounds.size) - for (p in playersAroundMe) { playAudio(p, woodcuttingSounds[soundIndex]) } @@ -260,7 +259,7 @@ class WoodcuttingListener : InteractionListener { } // Seers village medium reward - extra normal log while in seer's village - if (reward == 1511 && player.getAchievementDiaryManager().getDiary(DiaryType.SEERS_VILLAGE).isComplete(1) && player.getViewport().getRegion().getId() == 10806) { + if (reward == 1511 && player.achievementDiaryManager.getDiary(DiaryType.SEERS_VILLAGE).isComplete(1) && player.getLocation().regionId == 10806) { amount = 2 } return amount diff --git a/Server/src/main/content/global/skill/gather/woodcutting/WoodcuttingSkillPulse.java b/Server/src/main/content/global/skill/gather/woodcutting/WoodcuttingSkillPulse.java index d69c7fea1..859a26308 100644 --- a/Server/src/main/content/global/skill/gather/woodcutting/WoodcuttingSkillPulse.java +++ b/Server/src/main/content/global/skill/gather/woodcutting/WoodcuttingSkillPulse.java @@ -11,7 +11,6 @@ import core.game.dialogue.FacialExpression; import core.game.node.entity.impl.Animator; import core.game.node.entity.impl.Projectile; import core.game.node.entity.player.Player; -import core.game.node.entity.player.link.audio.Audio; import core.game.node.entity.player.link.diary.DiaryType; import core.game.node.entity.skill.Skills; import content.data.skill.SkillingTool; @@ -25,9 +24,6 @@ import core.game.world.update.flag.context.Animation; import core.tools.RandomFunction; import org.rs09.consts.Sounds; -import java.util.List; -import java.util.stream.Collectors; - import static core.api.ContentAPIKt.playAudio; import static core.game.system.command.sets.StatAttributeKeysKt.STATS_BASE; import static core.game.system.command.sets.StatAttributeKeysKt.STATS_LOGS; @@ -113,17 +109,13 @@ public class WoodcuttingSkillPulse extends Pulse { } public void animate() { - if(!player.getAnimator().isAnimating()) { + if (!player.getAnimator().isAnimating()) { player.animate(SkillingTool.getHatchet(player).getAnimation()); - - List playersAroundMe = RegionManager.getLocalPlayers(player, 2) - .stream() - .filter(p -> !p.getUsername().equals(player.getUsername())) - .collect(Collectors.toList()); - int soundIndex = RandomFunction.random(0, woodcuttingSounds.length); - - for (Player p : playersAroundMe) { + for (Player p : RegionManager.getLocalPlayers(player.getLocation(), 2)) { + if (p == player) { + continue; + } playAudio(p, woodcuttingSounds[soundIndex]); } } @@ -227,7 +219,7 @@ public class WoodcuttingSkillPulse extends Pulse { // Seers village medium reward - extra normal log while in seer's village if (reward == 1511 && player.getAchievementDiaryManager().getDiary(DiaryType.SEERS_VILLAGE).isComplete(1) - && player.getViewport().getRegion().getId() == 10806) { + && player.getLocation().getRegionId() == 10806) { amount = 2; } diff --git a/Server/src/main/content/global/skill/hunter/implings/ImplingData.kt b/Server/src/main/content/global/skill/hunter/implings/ImplingData.kt index affe2032c..e2b8df17b 100644 --- a/Server/src/main/content/global/skill/hunter/implings/ImplingData.kt +++ b/Server/src/main/content/global/skill/hunter/implings/ImplingData.kt @@ -173,6 +173,6 @@ enum class ImplingSpawnLocations (val type: ImplingSpawnTypes, vararg val locati object ImplingClipper : ClipMaskSupplier { override fun getClippingFlag (z: Int, x: Int, y: Int) : Int { var flag = RegionManager.getClippingFlag(z, x, y) - return flag and (RegionFlags.SOLID_TILE.inv()) and (RegionFlags.TILE_OBJECT.inv()) //Allow walking on water and flying over small objects, but keep all other tile flags the same. + return flag and (ChunkFlags.SOLID_TILE.inv()) and (ChunkFlags.TILE_OBJECT.inv()) //Allow walking on water and flying over small objects, but keep all other tile flags the same. } } diff --git a/Server/src/main/content/global/skill/magic/ancient/BloodSpells.java b/Server/src/main/content/global/skill/magic/ancient/BloodSpells.java index 2cd31111c..8bb2703fe 100644 --- a/Server/src/main/content/global/skill/magic/ancient/BloodSpells.java +++ b/Server/src/main/content/global/skill/magic/ancient/BloodSpells.java @@ -2,6 +2,8 @@ package content.global.skill.magic.ancient; import java.util.List; +import core.game.node.entity.combat.CombatStyle; +import core.game.node.entity.combat.MultihitTargetsKt; import core.game.node.entity.combat.spell.Runes; import core.game.node.Node; import core.game.node.entity.Entity; @@ -118,7 +120,8 @@ public final class BloodSpells extends CombatSpell { if (animation.getId() == 1978 || !entity.getProperties().isMultiZone() || !target.getProperties().isMultiZone()) { return super.getTargets(entity, target); } - List list = getMultihitTargets(entity, target, 9); + + List list = MultihitTargetsKt.findMultihitTargets(target, entity, CombatStyle.MAGIC); BattleState[] targets = new BattleState[list.size()]; int index = 0; for (Entity e : list) { diff --git a/Server/src/main/content/global/skill/magic/ancient/IceSpells.java b/Server/src/main/content/global/skill/magic/ancient/IceSpells.java index 4b0e29ff4..0cdd2216d 100644 --- a/Server/src/main/content/global/skill/magic/ancient/IceSpells.java +++ b/Server/src/main/content/global/skill/magic/ancient/IceSpells.java @@ -2,6 +2,8 @@ package content.global.skill.magic.ancient; import java.util.List; +import core.game.node.entity.combat.CombatStyle; +import core.game.node.entity.combat.MultihitTargetsKt; import core.game.node.entity.combat.spell.Runes; import core.game.node.Node; import core.game.node.entity.Entity; @@ -131,10 +133,10 @@ public final class IceSpells extends CombatSpell { return; } int ticks = (type.ordinal() - 4) * 8; - if (hasTimerActive(victim, "frozen") || hasTimerActive(victim, "frozen:immunity")) { - if (type == SpellType.BARRAGE) { state.setFrozen(true); } - return; - } + if (hasTimerActive(victim, "frozen") || hasTimerActive(victim, "frozen:immunity")) { + if (type == SpellType.BARRAGE) { state.setFrozen(true); } + return; + } registerTimer(victim, spawnTimer("frozen", ticks, true)); } @@ -143,7 +145,8 @@ public final class IceSpells extends CombatSpell { if (animation.getId() == 1978 || !entity.getProperties().isMultiZone() || !target.getProperties().isMultiZone()) { return super.getTargets(entity, target); } - List list = getMultihitTargets(entity, target, 9); + + List list = MultihitTargetsKt.findMultihitTargets(target, entity, CombatStyle.MAGIC); BattleState[] targets = new BattleState[list.size()]; int index = 0; for (Entity e : list) { diff --git a/Server/src/main/content/global/skill/magic/ancient/MiasmicSpells.java b/Server/src/main/content/global/skill/magic/ancient/MiasmicSpells.java index 5ead4131e..1353981c3 100644 --- a/Server/src/main/content/global/skill/magic/ancient/MiasmicSpells.java +++ b/Server/src/main/content/global/skill/magic/ancient/MiasmicSpells.java @@ -3,6 +3,8 @@ import java.util.List; import core.game.container.impl.EquipmentContainer; +import core.game.node.entity.combat.CombatStyle; +import core.game.node.entity.combat.MultihitTargetsKt; import core.plugin.Initializable; import core.game.node.entity.combat.spell.Runes; import core.game.node.Node; @@ -121,7 +123,7 @@ public final class MiasmicSpells extends CombatSpell { @Override public void fireEffect(Entity entity, Entity victim, BattleState state) { if (!hasTimerActive(victim, "miasmic:immunity")) { - registerTimer(victim, spawnTimer("miasmic", (getSpellId() - 15) * 20)); + registerTimer(victim, spawnTimer("miasmic", (getSpellId() - 15) * 20)); } } @@ -142,8 +144,8 @@ public final class MiasmicSpells extends CombatSpell { @Override public boolean cast(Entity entity, Node target) { if (!validStaffEquipped(entity) && !GameWorld.getSettings().isDevMode()) { - ((Player) entity).getPacketDispatch().sendMessage("You need to be wielding Zuriel's staff in order to cast this spell."); - return false; + ((Player) entity).getPacketDispatch().sendMessage("You need to be wielding Zuriel's staff in order to cast this spell."); + return false; } if (!meetsRequirements(entity, true, false)) { return false; @@ -157,7 +159,8 @@ public final class MiasmicSpells extends CombatSpell { || !entity.getProperties().isMultiZone() || !target.getProperties().isMultiZone()) { return super.getTargets(entity, target); } - List list = getMultihitTargets(entity, target, 9); + + List list = MultihitTargetsKt.findMultihitTargets(target, entity, CombatStyle.MAGIC); BattleState[] targets = new BattleState[list.size()]; int index = 0; for (Entity e : list) { diff --git a/Server/src/main/content/global/skill/magic/ancient/ShadowSpells.java b/Server/src/main/content/global/skill/magic/ancient/ShadowSpells.java index 22ca33fa3..2f55e002d 100644 --- a/Server/src/main/content/global/skill/magic/ancient/ShadowSpells.java +++ b/Server/src/main/content/global/skill/magic/ancient/ShadowSpells.java @@ -2,7 +2,8 @@ package content.global.skill.magic.ancient; import java.util.List; -import core.game.node.entity.player.Player; +import core.game.node.entity.combat.CombatStyle; +import core.game.node.entity.combat.MultihitTargetsKt; import core.game.node.entity.skill.Skills; import core.game.node.entity.combat.spell.Runes; import core.game.node.Node; @@ -115,7 +116,8 @@ public final class ShadowSpells extends CombatSpell { if (animation.getId() == 1978 || !entity.getProperties().isMultiZone() || !target.getProperties().isMultiZone()) { return super.getTargets(entity, target); } - List list = getMultihitTargets(entity, target, 9); + + List list = MultihitTargetsKt.findMultihitTargets(target, entity, CombatStyle.MAGIC); BattleState[] targets = new BattleState[list.size()]; int index = 0; for (Entity e : list) { diff --git a/Server/src/main/content/global/skill/magic/ancient/SmokeSpells.java b/Server/src/main/content/global/skill/magic/ancient/SmokeSpells.java index 23ddf938f..c7aab06ec 100644 --- a/Server/src/main/content/global/skill/magic/ancient/SmokeSpells.java +++ b/Server/src/main/content/global/skill/magic/ancient/SmokeSpells.java @@ -2,6 +2,8 @@ package content.global.skill.magic.ancient; import java.util.List; +import core.game.node.entity.combat.CombatStyle; +import core.game.node.entity.combat.MultihitTargetsKt; import core.game.node.entity.combat.spell.Runes; import core.game.node.Node; import core.game.node.entity.Entity; @@ -10,7 +12,6 @@ import core.game.node.entity.combat.spell.CombatSpell; import core.game.node.entity.combat.spell.SpellType; import core.game.node.entity.impl.Projectile; import core.game.node.entity.impl.Animator.Priority; -import core.game.node.entity.player.Player; import core.game.node.entity.player.link.SpellBookManager.SpellBook; import core.game.node.item.Item; import core.game.world.update.flag.context.Animation; @@ -114,7 +115,7 @@ public final class SmokeSpells extends CombatSpell { @Override public void fireEffect(Entity entity, Entity victim, BattleState state) { if (state.getEstimatedHit() > -1) { - applyPoison(victim, entity, type.ordinal() >= SpellType.BLITZ.ordinal() ? 4 : 2); + applyPoison(victim, entity, type.ordinal() >= SpellType.BLITZ.ordinal() ? 4 : 2); } } @@ -123,7 +124,8 @@ public final class SmokeSpells extends CombatSpell { if (animation.getId() == 1978 || !entity.getProperties().isMultiZone() || !target.getProperties().isMultiZone()) { return super.getTargets(entity, target); } - List list = getMultihitTargets(entity, target, 9); + + List list = MultihitTargetsKt.findMultihitTargets(target, entity, CombatStyle.MAGIC); BattleState[] targets = new BattleState[list.size()]; int index = 0; for (Entity e : list) { diff --git a/Server/src/main/content/global/skill/magic/lunar/HealSpell.java b/Server/src/main/content/global/skill/magic/lunar/HealSpell.java index f79c85728..228f92af2 100644 --- a/Server/src/main/content/global/skill/magic/lunar/HealSpell.java +++ b/Server/src/main/content/global/skill/magic/lunar/HealSpell.java @@ -1,5 +1,6 @@ package content.global.skill.magic.lunar; +import core.game.bots.AIPlayer; import core.game.node.entity.combat.spell.MagicSpell; import core.game.node.entity.combat.spell.Runes; import core.plugin.Initializable; @@ -92,7 +93,7 @@ public final class HealSpell extends MagicSpell { player.getPacketDispatch().sendMessage("This player is busy."); return false; } - if (!o.getSettings().isAcceptAid()) { + if (!o.getSettings().isAcceptAid() || (o instanceof AIPlayer)) { player.getPacketDispatch().sendMessage("The player is not accepting any aid."); return false; } @@ -111,11 +112,11 @@ public final class HealSpell extends MagicSpell { playGlobalAudio(o.getLocation(), Sounds.LUNAR_HEAL_OTHER_INDIVIDUAL_2892); o.graphics(GRAPHICS); } else { - List players = RegionManager.getLocalPlayers(player, 1); if (!super.meetsRequirements(player, true, true)) { return false; } int percentage = (int) Math.ceil(player.getSkills().getLifepoints() * 0.75); + List players = RegionManager.getLocalPlayers(player.getLocation(), 1); for (Iterator it = players.iterator(); it.hasNext();) { Player p = it.next(); if (p == player || !p.getSettings().isAcceptAid() || !p.isActive() || p.getSkills().getLifepoints() == p.getSkills().getMaximumLifepoints()) { diff --git a/Server/src/main/content/global/skill/magic/lunar/LunarListeners.kt b/Server/src/main/content/global/skill/magic/lunar/LunarListeners.kt index fd8dc5ccd..e05c4ebad 100644 --- a/Server/src/main/content/global/skill/magic/lunar/LunarListeners.kt +++ b/Server/src/main/content/global/skill/magic/lunar/LunarListeners.kt @@ -501,16 +501,16 @@ class LunarListeners : SpellListener("lunar"), Commands { removeRunes(player, true) visualizeSpell(player, Animations.LUNAR_SPELLBOOK_CURE_GROUP_4409, Graphics.LUNAR_SPELLBOOK_CURE_GROUP_744, 130, Sounds.LUNAR_CURE_GROUP_2882) curePoison(player) - for(acct in RegionManager.getLocalPlayers(player, 1)) { - if(!acct.isActive || acct.locks.isInteractionLocked) { + for (otherPlayer in RegionManager.getLocalPlayers(player.location, 1)) { + if (otherPlayer == player || !otherPlayer.isActive || otherPlayer.locks.isInteractionLocked) { continue } - if(!acct.settings.isAcceptAid) { + if (!otherPlayer.settings.isAcceptAid) { continue } - curePoison(acct) - sendMessage(acct, "You have been cured of poison.") - visualizeSpell(acct, -1, Graphics.LUNAR_SPELLBOOK_CURE_GROUP_744, 130, Sounds.LUNAR_CURE_OTHER_INDIVIDUAL_2889) + curePoison(otherPlayer) + sendMessage(otherPlayer, "You have been cured of poison.") + visualizeSpell(otherPlayer, -1, Graphics.LUNAR_SPELLBOOK_CURE_GROUP_744, 130, Sounds.LUNAR_CURE_OTHER_INDIVIDUAL_2889) } addXP(player, 74.0) } @@ -836,12 +836,12 @@ class LunarListeners : SpellListener("lunar"), Commands { } private fun sendGroupTeleport(player: Player, xp: Double, destName: String, loc: Location){ - RegionManager.getLocalPlayers(player, 1).forEach { - if(it == player) return@forEach - if(it.isTeleBlocked) return@forEach - if(!it.isActive) return@forEach - if(!it.settings.isAcceptAid) return@forEach - if(it.ironmanManager.isIronman) return@forEach + RegionManager.getLocalPlayers(player.location, 1).forEach { + if (it == player) return@forEach + if (it.isTeleBlocked) return@forEach + if (!it.isActive) return@forEach + if (!it.settings.isAcceptAid) return@forEach + if (it.ironmanManager.isIronman) return@forEach setAttribute(it, "t-o_location", loc) openInterface(it, Components.TELEPORT_OTHER_326) setInterfaceText(it, player.username, Components.TELEPORT_OTHER_326, 1) diff --git a/Server/src/main/content/global/skill/magic/lunar/StatBoostSpell.java b/Server/src/main/content/global/skill/magic/lunar/StatBoostSpell.java index 61aeee5f7..bdaa387ea 100644 --- a/Server/src/main/content/global/skill/magic/lunar/StatBoostSpell.java +++ b/Server/src/main/content/global/skill/magic/lunar/StatBoostSpell.java @@ -16,6 +16,7 @@ import core.game.world.update.flag.context.Animation; import core.game.world.update.flag.context.Graphics; import core.plugin.Initializable; import core.plugin.Plugin; +import org.rs09.consts.Items; import org.rs09.consts.Sounds; import java.util.List; @@ -40,35 +41,37 @@ public final class StatBoostSpell extends MagicSpell { @Override public boolean cast(Entity entity, Node target) { - final Player player = ((Player) entity); - Item item = ((Item) target); - final Potion potion = (Potion) Consumables.getConsumableById(item.getId()).getConsumable(); + final Player player = (Player) entity; + Item item = (Item) target; player.getInterfaceManager().setViewedTab(6); + + final Potion potion = (Potion) Consumables.getConsumableById(item.getId()).getConsumable(); if (potion == null) { player.getPacketDispatch().sendMessage("You can only cast this spell on a potion."); return false; } - if (!item.getDefinition().isTradeable() || item.getName().toLowerCase().contains("restore") || item.getName().toLowerCase().contains("zamorak") || item.getName().toLowerCase().contains("saradomin") || item.getName().toLowerCase().contains("combat")) { + if (!item.getDefinition().isTradeable() || item.getName().toLowerCase().contains("restore") || item.getName().toLowerCase().contains("zamorak") || item.getName().toLowerCase().contains("saradomin")) { player.getPacketDispatch().sendMessage("You can't cast this spell on that item."); return false; } - List pl = RegionManager.getLocalPlayers(player, 1); - int plSize = pl.size() - 1; + List pl = RegionManager.getLocalPlayers(player.getLocation(), 1); int doses = potion.getDose(item); - if (pl.size() == 0) { + if (pl.isEmpty()) { return false; } if (!super.meetsRequirements(player, true, false)) { return false; } int size = 0; - for (Player players : pl) { - Player o = (Player) players; - if (size >= doses) break; - if (!o.isActive() || o.getLocks().isInteractionLocked() || o == player) { + for (Player o : pl) { + if (size >= doses) break; + if (o == player) { continue; } - if (!o.getSettings().isAcceptAid() && !(o instanceof AIPlayer)) { + if (!o.isActive() || o.getLocks().isInteractionLocked()) { + continue; + } + if (!o.getSettings().isAcceptAid() || o instanceof AIPlayer) { continue; } o.graphics(GRAPHICS); @@ -81,17 +84,18 @@ public final class StatBoostSpell extends MagicSpell { return false; } super.meetsRequirements(player, true, true); - potion.getEffect().activate(player); - playGlobalAudio(player.getLocation(), Sounds.LUNAR_STRENGTH_SHARE_2901); - player.animate(ANIMATION); - player.graphics(GRAPHICS); - player.getInventory().remove(item); - int newIndex = (potion.getIds().length - doses) + size; - if (newIndex > potion.getIds().length - 1) { - player.getInventory().add(new Item(229)); - return true; - } - player.getInventory().add(new Item(potion.getIds()[newIndex])); + if (player.getInventory().remove(item)) { + potion.getEffect().activate(player); + playGlobalAudio(player.getLocation(), Sounds.LUNAR_STRENGTH_SHARE_2901); + player.animate(ANIMATION); + player.graphics(GRAPHICS); + int newIndex = (potion.getIds().length - doses) + size; + if (newIndex > potion.getIds().length - 1) { + player.getInventory().add(new Item(Items.VIAL_229)); + return true; + } + player.getInventory().add(new Item(potion.getIds()[newIndex])); + } return true; } } diff --git a/Server/src/main/content/global/skill/magic/lunar/StatRestoreSpell.java b/Server/src/main/content/global/skill/magic/lunar/StatRestoreSpell.java index 1f984d2da..bd05e85c9 100644 --- a/Server/src/main/content/global/skill/magic/lunar/StatRestoreSpell.java +++ b/Server/src/main/content/global/skill/magic/lunar/StatRestoreSpell.java @@ -16,6 +16,7 @@ import core.game.world.map.RegionManager; import core.game.world.update.flag.context.Animation; import core.game.world.update.flag.context.Graphics; import core.plugin.Plugin; +import org.rs09.consts.Items; import org.rs09.consts.Sounds; import java.util.List; @@ -27,7 +28,7 @@ public class StatRestoreSpell extends MagicSpell { private static final Animation ANIMATION = new Animation(4413); private static final Graphics GRAPHICS = new Graphics(733, 130); - private static final Consumables[] acceptedPotions = new Consumables[] { Consumables.RESTORE, Consumables.SUPER_RESTO, Consumables.PRAYER, Consumables.ENERGY, Consumables.SUPER_ENERGY }; + private static final Consumables[] acceptedPotions = new Consumables[] { Consumables.RESTORE, Consumables.SUPER_RESTO, Consumables.PRAYER, Consumables.ENERGY, Consumables.SUPER_ENERGY }; public StatRestoreSpell() { super(SpellBook.LUNAR, 81, 84, null, null, null, new Item[] { new Item(Runes.ASTRAL_RUNE.getId(), 2), new Item(Runes.EARTH_RUNE.getId(), 10), new Item(Runes.WATER_RUNE.getId(), 10) }); @@ -44,42 +45,38 @@ public class StatRestoreSpell extends MagicSpell { final Player player = ((Player) entity); Item item = ((Item) target); player.getInterfaceManager().setViewedTab(6); - - if (Consumables.getConsumableById(item.getId()) == null) { - player.getPacketDispatch().sendMessage("You can only cast this spell on a potion."); - return false; - } - + if (Consumables.getConsumableById(item.getId()) == null) { + player.getPacketDispatch().sendMessage("You can only cast this spell on a potion."); + return false; + } final Potion potion = (Potion) Consumables.getConsumableById(item.getId()).getConsumable(); - if (potion == null) { - player.getPacketDispatch().sendMessage("You can only cast this spell on a potion."); + player.getPacketDispatch().sendMessage("You can only cast this spell on a potion."); return false; } if (!item.getDefinition().isTradeable() || !isRestore(potion)) { player.getPacketDispatch().sendMessage("You can't cast this spell on that item."); return false; } - List pl = RegionManager.getLocalPlayers(player, 2); - int plSize = pl.size() - 1; + List pl = RegionManager.getLocalPlayers(player.getLocation(), 1); int doses = potion.getDose(item); - if (pl.size() == 0) { - return false; - } if (!super.meetsRequirements(player, true, false)) { return false; } int size = 0; - for (Player players : pl) { - if (size >= doses) break; - Player o = (Player) players; - if (!o.isActive() || o.getLocks().isInteractionLocked() || o == player) { + for (Player o : pl) { + if (size >= doses) break; + if (o == player) { + continue; + } + if (!o.isActive() || o.getLocks().isInteractionLocked()) { continue; } if (!o.getSettings().isAcceptAid() && !(o instanceof AIPlayer)) { continue; } o.graphics(GRAPHICS); + playGlobalAudio(o.getLocation(), Sounds.LUNAR_STAT_SHARE_INDIVIDUAL_2897); potion.getEffect().activate(o); size++; } @@ -92,21 +89,22 @@ public class StatRestoreSpell extends MagicSpell { playGlobalAudio(player.getLocation(), Sounds.LUNAR_STAT_SHARE_2899); player.animate(ANIMATION); player.graphics(GRAPHICS); - player.getInventory().remove(item); - int newIndex = (potion.getIds().length - doses) + size; - if (newIndex > potion.getIds().length - 1) { - player.getInventory().add(new Item(229)); - return true; - } - player.getInventory().add(new Item(potion.getIds()[newIndex])); + if (player.getInventory().remove(item)) { + int newIndex = (potion.getIds().length - doses) + size; + if (newIndex > potion.getIds().length - 1) { + player.getInventory().add(new Item(Items.VIAL_229)); + return true; + } + player.getInventory().add(new Item(potion.getIds()[newIndex])); + } return true; } private boolean isRestore(Potion p) { - for (int i = 0; i < acceptedPotions.length; i++) { - if (p == acceptedPotions[i].getConsumable()) - return true; - } + for (int i = 0; i < acceptedPotions.length; i++) { + if (p == acceptedPotions[i].getConsumable()) + return true; + } return false; } diff --git a/Server/src/main/content/global/skill/summoning/familiar/ForgeRegentNPC.java b/Server/src/main/content/global/skill/summoning/familiar/ForgeRegentNPC.java index 3c560fc41..3217290a8 100644 --- a/Server/src/main/content/global/skill/summoning/familiar/ForgeRegentNPC.java +++ b/Server/src/main/content/global/skill/summoning/familiar/ForgeRegentNPC.java @@ -152,9 +152,9 @@ public class ForgeRegentNPC extends Familiar { familiar.moveStep(); GroundItemManager.destroy(ground); player.getSkills().addExperience(Skills.FIREMAKING, log.getXp() + 10); - familiar.faceLocation(object.getFaceLocation(familiar.getLocation())); + familiar.faceLocation(object.getFaceLocation(familiar.getLocation())); SceneryBuilder.add(object, log.getLife(), FireMakingPulse.getAsh(player, log, object)); - if (player.getViewport().getRegion().getId() == 10806) { + if (player.getLocation().getRegionId() == 10806) { player.getAchievementDiaryManager().finishTask(player, DiaryType.SEERS_VILLAGE, 1, 9); } return true; diff --git a/Server/src/main/content/global/skill/summoning/familiar/GiantChinchompaNPC.java b/Server/src/main/content/global/skill/summoning/familiar/GiantChinchompaNPC.java index c0c57202a..ec8479e9a 100644 --- a/Server/src/main/content/global/skill/summoning/familiar/GiantChinchompaNPC.java +++ b/Server/src/main/content/global/skill/summoning/familiar/GiantChinchompaNPC.java @@ -56,7 +56,7 @@ public class GiantChinchompaNPC extends Familiar { if (!isOwnerAttackable()) { return false; } - final List entitys = RegionManager.getLocalEntitys(owner, 6); + final List entitys = RegionManager.getLocalEntities(owner.getLocation(), 6); entitys.remove(owner); entitys.remove(this); sendChat("Squeak!"); diff --git a/Server/src/main/content/global/skill/summoning/familiar/PyreLordNPC.java b/Server/src/main/content/global/skill/summoning/familiar/PyreLordNPC.java index c4672b643..fc867c0d3 100644 --- a/Server/src/main/content/global/skill/summoning/familiar/PyreLordNPC.java +++ b/Server/src/main/content/global/skill/summoning/familiar/PyreLordNPC.java @@ -133,7 +133,7 @@ public class PyreLordNPC extends Familiar { player.getSkills().addExperience(Skills.FIREMAKING, log.getXp() + 10); familiar.faceLocation(object.getFaceLocation(familiar.getLocation())); SceneryBuilder.add(object, log.getLife(), FireMakingPulse.getAsh(player, log, object)); - if (player.getViewport().getRegion().getId() == 10806) { + if (player.getLocation().getRegionId() == 10806) { player.getAchievementDiaryManager().finishTask(player, DiaryType.SEERS_VILLAGE, 1, 9); } return true; diff --git a/Server/src/main/content/global/skill/summoning/familiar/SmokeDevilNPC.java b/Server/src/main/content/global/skill/summoning/familiar/SmokeDevilNPC.java index 49a79fa6a..65f8fea75 100644 --- a/Server/src/main/content/global/skill/summoning/familiar/SmokeDevilNPC.java +++ b/Server/src/main/content/global/skill/summoning/familiar/SmokeDevilNPC.java @@ -70,7 +70,7 @@ public class SmokeDevilNPC extends Familiar { if (!isOwnerAttackable()) { return false; } - final List entitys = RegionManager.getLocalEntitys(this, 1); + final List entitys = RegionManager.getLocalEntities(location, 1); entitys.remove(this); entitys.remove(owner); visualize(Animation.create(7820), Graphics.create(1375)); diff --git a/Server/src/main/content/global/skill/summoning/familiar/SpiritTzKihNPC.java b/Server/src/main/content/global/skill/summoning/familiar/SpiritTzKihNPC.java index 19f97c6ba..e48196763 100644 --- a/Server/src/main/content/global/skill/summoning/familiar/SpiritTzKihNPC.java +++ b/Server/src/main/content/global/skill/summoning/familiar/SpiritTzKihNPC.java @@ -41,7 +41,7 @@ public class SpiritTzKihNPC extends Familiar { @Override protected boolean specialMove(FamiliarSpecial special) { - final List entitys = RegionManager.getLocalEntitys(owner, 8); + final List entitys = RegionManager.getLocalEntities(owner.getLocation(), 8); if (entitys.size() == 0) { return false; } diff --git a/Server/src/main/content/global/skill/summoning/pet/KittenInteractDialogue.java b/Server/src/main/content/global/skill/summoning/pet/KittenInteractDialogue.java index 24a387d18..9f5ce24ae 100644 --- a/Server/src/main/content/global/skill/summoning/pet/KittenInteractDialogue.java +++ b/Server/src/main/content/global/skill/summoning/pet/KittenInteractDialogue.java @@ -72,7 +72,7 @@ public final class KittenInteractDialogue extends DialoguePlugin { player.sendChat("Go on puss...kill that rat!"); boolean cant = true; NPC rat = null; - for (NPC n : RegionManager.getLocalNpcs(player.getLocation(), 10)) { + for (NPC n : RegionManager.getLocalNPCs(player.getLocation(), 10)) { if (!n.getName().contains("rat")) { cant = false; continue; diff --git a/Server/src/main/content/global/skill/thieving/StallThiefPulse.java b/Server/src/main/content/global/skill/thieving/StallThiefPulse.java index b08185335..b3a5ea755 100644 --- a/Server/src/main/content/global/skill/thieving/StallThiefPulse.java +++ b/Server/src/main/content/global/skill/thieving/StallThiefPulse.java @@ -115,7 +115,7 @@ public final class StallThiefPulse extends SkillPulse { SceneryBuilder.replace(node, node.transform(stall.getEmpty(node.getId())), stall.getDelay()); } final Item item = stall.getRandomLoot(); - player.getInventory().add(item); + player.getInventory().add(item); player.getSkills().addExperience(Skills.THIEVING, stall.getExperience(), true); if (item.getId() == 1987) { player.getPacketDispatch().sendMessage("You steal grapes from the grape stall."); @@ -135,9 +135,9 @@ public final class StallThiefPulse extends SkillPulse { if(stall == Stall.CANDLES) { return; } - if (type == 0) { - player.getPacketDispatch().sendMessage("You attempt to steal some " + stall.msgItem + " from the " + stall.name().toLowerCase().replace('_', ' ')); - } + if (type == 0) { + player.getPacketDispatch().sendMessage("You attempt to steal some " + stall.msgItem + " from the " + stall.name().toLowerCase().replace('_', ' ')); + } } /** @@ -156,7 +156,7 @@ public final class StallThiefPulse extends SkillPulse { player.sendMessage("A higher power smites you"); return false; } - for (NPC npc : RegionManager.getLocalNpcs(player.getLocation(), 8)) { + for (NPC npc : RegionManager.getLocalNPCs(player.getLocation(), 8)) { if (!npc.getProperties().getCombatPulse().isAttacking() && (npc.getId() == 32 || npc.getId() == 2236)) { npc.sendChat("Hey! Get your hands off there!"); npc.getProperties().getCombatPulse().attack(player); diff --git a/Server/src/main/content/minigame/barrows/BarrowsActivityPlugin.java b/Server/src/main/content/minigame/barrows/BarrowsActivityPlugin.java index 48b95f68f..eaf43c940 100644 --- a/Server/src/main/content/minigame/barrows/BarrowsActivityPlugin.java +++ b/Server/src/main/content/minigame/barrows/BarrowsActivityPlugin.java @@ -19,6 +19,7 @@ import content.global.skill.summoning.familiar.Familiar; import core.game.node.scenery.Scenery; import core.game.system.task.Pulse; import core.game.world.map.Location; +import core.game.world.map.Region; import core.game.world.map.RegionManager; import core.game.world.map.zone.ZoneBorders; import core.game.world.update.flag.context.Graphics; @@ -84,37 +85,41 @@ public final class BarrowsActivityPlugin extends ActivityPlugin { @Override public boolean pulse() { boolean end = true; - for (Player p : RegionManager.getRegionPlayers(14231)) { - end = false; - int index = p.getAttribute("barrow:drain-index", -1); - if (index > -1) { - p.removeAttribute("barrow:drain-index"); - p.getPacketDispatch().sendItemOnInterface(-1, 1, 24, index); - continue; - } - if (p.getLocation().getZ() == 0 && p.getAttribute("barrow:looted", false) && getWorldTicks() % 3 == 0) { - if (RandomFunction.random(15) == 0) { - p.getImpactHandler().manualHit(p, RandomFunction.random(5), HitsplatType.NORMAL); - Graphics.send(Graphics.create(405), p.getLocation()); + Region region = RegionManager.forId(14231); + Region.load(region); + for (int z = 0; z < 4; z++) { + for (Player p : region.assemblePlayerList(z)) { + end = false; + int index = p.getAttribute("barrow:drain-index", -1); + if (index > -1) { + p.removeAttribute("barrow:drain-index"); + p.getPacketDispatch().sendItemOnInterface(-1, 1, 24, index); + continue; } - } - int drain = 8; + if (p.getLocation().getZ() == 0 && p.getAttribute("barrow:looted", false) && getWorldTicks() % 3 == 0) { + if (RandomFunction.random(15) == 0) { + p.getImpactHandler().manualHit(p, RandomFunction.random(5), HitsplatType.NORMAL); + Graphics.send(Graphics.create(405), p.getLocation()); + } + } + int drain = 8; - //if (p.getLocks().isLocked("barrow:drain") || RandomFunction.random(100) % 2 == 0) { - // continue; - //} - for (boolean killed : p.getSavedData().getActivityData().getBarrowBrothers()) { - if (killed) { - drain += 1; + //if (p.getLocks().isLocked("barrow:drain") || RandomFunction.random(100) % 2 == 0) { + // continue; + //} + for (boolean killed : p.getSavedData().getActivityData().getBarrowBrothers()) { + if (killed) { + drain += 1; + } + } + if (getWorldTicks() % 30 == 0) { + p.getSkills().decrementPrayerPoints(drain); + p.getLocks().lock("barrow:drain", (3 + RandomFunction.random(15)) * 3); + index = 1 + RandomFunction.random(6); + p.setAttribute("barrow:drain-index", index); + p.getPacketDispatch().sendItemZoomOnInterface(4761 + RandomFunction.random(12), 100, 24, index); + p.getPacketDispatch().sendAnimationInterface(9810, 24, index); } - } - if(getWorldTicks() % 30 == 0){ - p.getSkills().decrementPrayerPoints(drain); - p.getLocks().lock("barrow:drain", (3 + RandomFunction.random(15)) * 3); - index = 1 + RandomFunction.random(6); - p.setAttribute("barrow:drain-index", index); - p.getPacketDispatch().sendItemZoomOnInterface(4761 + RandomFunction.random(12), 100, 24, index); - p.getPacketDispatch().sendAnimationInterface(9810, 24, index); } } return end; @@ -130,7 +135,7 @@ public final class BarrowsActivityPlugin extends ActivityPlugin { @Override public void locationUpdate(Entity e, Location last) { - if (e instanceof Player && e.getViewport().getRegion().getId() == 14231) { + if (e instanceof Player && e.getLocation().getRegionId() == 14231) { boolean tunnel = false; for (ZoneBorders border : MINI_TUNNELS) { if (border.insideBorder(e)) { @@ -140,7 +145,7 @@ public final class BarrowsActivityPlugin extends ActivityPlugin { } Player player = (Player) e; if ((getVarp(player, 1270) == 1) != tunnel) { - setVarp(player, 1270, tunnel ? 3 : 0, true); + setVarp(player, 1270, tunnel ? 3 : 0, true); } } } @@ -213,7 +218,7 @@ public final class BarrowsActivityPlugin extends ActivityPlugin { public static void shuffleCatacombs(Player player) { int value = TUNNEL_CONFIGS[RandomFunction.random(TUNNEL_CONFIGS.length)]; value |= 1 << (6 + RandomFunction.random(4)); - setVarp(player, 452, value); + setVarp(player, 452, value); } @Override @@ -293,10 +298,10 @@ public final class BarrowsActivityPlugin extends ActivityPlugin { return true; case 6774: player.lock(1); - int brother = player.getSavedData().getActivityData().getBarrowTunnelIndex(); - if (!player.getSavedData().getActivityData().getBarrowBrothers()[brother] && !player.getAttribute("brother:" + brother, false)) { - BarrowsCrypt.getCrypt(brother).spawnBrother(player, RegionManager.getTeleportLocation(target.getCenterLocation(), 4)); - } + int brother = player.getSavedData().getActivityData().getBarrowTunnelIndex(); + if (!player.getSavedData().getActivityData().getBarrowBrothers()[brother] && !player.getAttribute("brother:" + brother, false)) { + BarrowsCrypt.getCrypt(brother).spawnBrother(player, RegionManager.getTeleportLocation(target.getCenterLocation(), 4)); + } player.setAttribute("barrow:opened_chest", true); sendConfiguration(player); return true; @@ -335,7 +340,7 @@ public final class BarrowsActivityPlugin extends ActivityPlugin { if (player.getAttribute("barrow:opened_chest", false)) { config |= 1 << 16; } - setVarp(player, 453, config); + setVarp(player, 453, config); } @Override diff --git a/Server/src/main/content/minigame/mta/TelekineticGrabSpell.java b/Server/src/main/content/minigame/mta/TelekineticGrabSpell.java index 72728d1e6..00486fad4 100644 --- a/Server/src/main/content/minigame/mta/TelekineticGrabSpell.java +++ b/Server/src/main/content/minigame/mta/TelekineticGrabSpell.java @@ -100,8 +100,9 @@ public final class TelekineticGrabSpell extends MagicSpell { @Override public boolean cast(final Entity entity, final Node target) { - if (!(target instanceof GroundItem)) - return false; + if (!(target instanceof GroundItem)) { + return false; + } final GroundItem ground = (GroundItem) target; if (!canCast(entity, ground)) { return false; @@ -131,32 +132,34 @@ public final class TelekineticGrabSpell extends MagicSpell { Player player = entity instanceof Player ? (Player) entity : null; GroundItem g = GroundItemManager.get(ground.getId(), ground.getLocation(), player); if (g == null) { - player.getPacketDispatch().sendMessage("Too late!"); + if (player != null) { + player.getPacketDispatch().sendMessage("Too late!"); + } return true; } - if (g == SpecialGroundItems.AHAB_BEER.asGroundItem()){ - player.getDialogueInterpreter().open(2692, new NPC(2692), true); + if (g == SpecialGroundItems.AHAB_BEER.asGroundItem()) { + if (player != null) { + player.getDialogueInterpreter().open(2692, new NPC(2692), true); + } return true; } if (player == null) { return true; } boolean teleZone = player.getZoneMonitor().isInZone("Telekinetic Theatre") && g.getId() == 6888; - if (player != null) { - if (g == null || !g.isActive()) { - player.getPacketDispatch().sendMessage("Too late!"); - return true; - } - playAudio(player, Sounds.VULNERABILITY_IMPACT_3008); - if (!teleZone) { - player.getInventory().add(new Item(g.getId(), g.getAmount(), g.getCharge())); - } else { - TelekineticZone zone = TelekineticZone.getZone(player); - zone.moveStatue(); - player.lock(getDelay()); - } - player.getPacketDispatch().sendPositionedGraphics(END_GRAPHIC, ground.getLocation()); + if (!g.isActive()) { + player.getPacketDispatch().sendMessage("Too late!"); + return true; } + playAudio(player, Sounds.VULNERABILITY_IMPACT_3008); + if (!teleZone) { + player.getInventory().add(new Item(g.getId(), g.getAmount(), g.getCharge())); + } else { + TelekineticZone zone = TelekineticZone.getZone(player); + zone.moveStatue(); + player.lock(getDelay()); + } + player.getPacketDispatch().sendPositionedGraphics(END_GRAPHIC, ground.getLocation()); if (!teleZone) { GroundItemManager.destroy(g); } diff --git a/Server/src/main/content/minigame/pestcontrol/PCPortalNPC.java b/Server/src/main/content/minigame/pestcontrol/PCPortalNPC.java index 0bddb9a24..0cd9dff53 100644 --- a/Server/src/main/content/minigame/pestcontrol/PCPortalNPC.java +++ b/Server/src/main/content/minigame/pestcontrol/PCPortalNPC.java @@ -8,12 +8,9 @@ import core.game.node.entity.combat.BattleState; import core.game.node.entity.npc.AbstractNPC; import core.game.node.entity.npc.NPC; import core.game.node.entity.player.Player; -import core.game.world.map.Direction; -import core.game.world.map.Location; -import core.game.world.map.RegionPlane; +import core.game.world.map.*; import core.game.world.update.flag.context.Animation; import core.tools.RandomFunction; -import content.minigame.pestcontrol.monsters.PCRavagerNPC; import content.minigame.pestcontrol.monsters.PCSpinnerNPC; /** @@ -146,8 +143,9 @@ public final class PCPortalNPC extends AbstractNPC { public void tick() { super.tick(); if (session != null) { - RegionPlane plane = getViewport().getCurrentPlane(); - if (plane != null && session.getTicks() % 35 - plane.getPlayers().size() == 0 && plane.getNpcs().size() < 100) { + Region region = RegionManager.forId(location.getRegionId()); + if ((session.getTicks() % 35 - region.assemblePlayerList(0).size()) == 0 && + region.assembleNpcList(0).size() < 100) { spawnNPCs(); } if (updateLifepoints && session.getTicks() % 10 == 0) { @@ -187,9 +185,8 @@ public final class PCPortalNPC extends AbstractNPC { int index = getDifficultyIndex(); Random r = RandomFunction.RANDOM; int amount = index + 1; - if (getViewport().getCurrentPlane() != null) { - amount += getViewport().getCurrentPlane().getPlayers().size() / 10; - } + Region region = RegionManager.forId(location.getRegionId()); + amount += region.assemblePlayerList(0).size() / 10; if (getDifficultyIndex() == 0) { amount += ((getDifficultyIndex() + 1) * 3) / 2; } diff --git a/Server/src/main/content/minigame/pestcontrol/PestControlActivityPlugin.java b/Server/src/main/content/minigame/pestcontrol/PestControlActivityPlugin.java index f1fb680a0..2f7b97e4c 100644 --- a/Server/src/main/content/minigame/pestcontrol/PestControlActivityPlugin.java +++ b/Server/src/main/content/minigame/pestcontrol/PestControlActivityPlugin.java @@ -20,6 +20,7 @@ import core.game.node.item.Item; import core.game.system.task.Pulse; import core.game.world.GameWorld; import core.game.world.map.Location; +import core.game.world.map.RegionChunk; import core.game.world.map.build.DynamicRegion; import core.game.world.map.zone.RegionZone; import core.game.world.map.zone.ZoneBuilder; @@ -125,40 +126,48 @@ public final class PestControlActivityPlugin extends ActivityPlugin { if (!session.isActive()) { return; } - for (final Player p : session.getRegion().getPlanes()[0].getPlayers()) { - p.getProperties().setTeleportLocation(getLeaveLocation()); - if (!success) { - p.getDialogueInterpreter().open(3781, true, 0, true); - // default, - // type, - // default - } else if (success && p.getAttribute("pc_zeal", 0) >= 50) { - int amount = type.ordinal() + 2; - p.getSavedData().getActivityData().increasePestPoints(amount); - Item coins = new Item(995, p.getProperties().getCurrentCombatLevel() * 10); - if (!p.getInventory().add(coins)) { - GroundItemManager.create(coins, p); + RegionChunk[][][] chunks = session.getRegion().getChunks(); + for (int x = 0; x < 8; x++) { + for (int y = 0; y < 8; y++) { + for (int z = 0; z < 4; z++) { + RegionChunk chunk = chunks[x][y][z]; + for (Player p : chunk.getPlayers()) { + p.getProperties().setTeleportLocation(getLeaveLocation()); + if (!success) { + p.getDialogueInterpreter().open(3781, true, 0, true); + // default, + // type, + // default + } else if (p.getAttribute("pc_zeal", 0) >= 50) { + int amount = type.ordinal() + 2; + p.getSavedData().getActivityData().increasePestPoints(amount); + Item coins = new Item(995, p.getProperties().getCurrentCombatLevel() * 10); + if (!p.getInventory().add(coins)) { + GroundItemManager.create(coins, p); + } + // default, type, name + p.getDialogueInterpreter().open(3781, true, 1, type.ordinal() == 0 ? "two" : type.ordinal() == 1 ? "three" : "four"); + } else { + // default type, default + p.getDialogueInterpreter().open(3781, true, 2, true); + } + p.removeAttribute("pc_zeal"); + p.removeExtension(PestControlSession.class); + p.fullRestore(); + if (isPoisoned(p)) { + curePoison(p); + } + PulseManager.cancelDeathTask(p); + GameWorld.getPulser().submit(new Pulse(1, p) { + @Override + public boolean pulse() { + p.getSkills().restore(); + return true; + } + }); + } } - // default, type, name - p.getDialogueInterpreter().open(3781, true, 1, type.ordinal() == 0 ? "two" : type.ordinal() == 1 ? "three" : "four"); - } else { - // default type, default - p.getDialogueInterpreter().open(3781, true, 2, true); } - p.removeAttribute("pc_zeal"); - p.removeExtension(PestControlSession.class); - p.fullRestore(); - if (isPoisoned(p)) { - curePoison(p); - } - PulseManager.cancelDeathTask(p); - GameWorld.getPulser().submit(new Pulse(1, p) { - @Override - public boolean pulse() { - p.getSkills().restore(); - return true; - } - }); } session.getRegion().getRegionZones().clear(); session.setActive(false); @@ -293,7 +302,7 @@ public final class PestControlActivityPlugin extends ActivityPlugin { @Override public boolean death(Entity e, Entity killer) { - if (e instanceof Player && e.getViewport().getRegion().getRegionId() == 10536) { + if (e instanceof Player && e.getLocation().getRegion().getRegionId() == 10536) { PestControlSession session = e.getExtension(PestControlSession.class); if (session != null) { Location l = session.getRegion().getBaseLocation(); diff --git a/Server/src/main/content/minigame/pestcontrol/PestControlSession.java b/Server/src/main/content/minigame/pestcontrol/PestControlSession.java index 8396f97b8..73b84e202 100644 --- a/Server/src/main/content/minigame/pestcontrol/PestControlSession.java +++ b/Server/src/main/content/minigame/pestcontrol/PestControlSession.java @@ -9,7 +9,6 @@ import core.game.node.entity.player.Player; import core.game.node.scenery.Scenery; import core.game.world.map.Location; import core.game.world.map.Point; -import core.game.world.map.RegionPlane; import core.game.world.map.build.DynamicRegion; import core.tools.RandomFunction; @@ -21,6 +20,7 @@ import static core.api.ContentAPIKt.*; * @author Emperor */ public final class PestControlSession { + private static int STRANGE_PHANTOM_OBJECT_THAT_SHARES_A_TILE_WITH_A_BARRICADE = 25636; /** * The barricade object offsets. @@ -31,7 +31,7 @@ public final class PestControlSession { * The object ids of non-attackable barricades/gates. */ public static final int[] INVALID_OBJECT_IDS = {14230, 14231, 14232, // Barricades - 14245, 14246, 14247, 14248, // Gates + 14245, 14246, 14247, 14248 // Gates }; /** @@ -123,7 +123,7 @@ public final class PestControlSession { * @param message The message to send. */ public void sendMessage(String message) { - for (Player p : region.getPlanes()[0].getPlayers()) { + for (Player p : region.assemblePlayerList(0)) { if (p.isActive()) { p.getPacketDispatch().sendMessage(message); } @@ -136,7 +136,7 @@ public final class PestControlSession { * @param child The child id. */ public void sendString(String message, int child) { - for (Player p : region.getPlanes()[0].getPlayers()) { + for (Player p : region.assemblePlayerList(0)) { if (p.isActive()) { p.getPacketDispatch().sendString(message, 408, child); } @@ -164,9 +164,9 @@ public final class PestControlSession { * @param value The message value to send. */ public void sendConfig(int value) { - for (Player p : region.getPlanes()[0].getPlayers()) { + for (Player p : region.assemblePlayerList(0)) { if (p.isActive()) { - setVarp(p, 719, value); + setVarp(p, 719, value); } } } @@ -191,7 +191,7 @@ public final class PestControlSession { message = "The red, south-western portal shield has dropped!"; break; } - for (Player p : region.getPlanes()[0].getPlayers()) { + for (Player p : region.assemblePlayerList(0)) { if (p.isActive()) { p.getPacketDispatch().sendInterfaceConfig(408, 18 + (index << 1), true); p.getPacketDispatch().sendMessage(message); @@ -233,8 +233,7 @@ public final class PestControlSession { } } - for (Player p : remainingPlayers) - { + for (Player p : remainingPlayers) { int priority = p.getAttribute("pc_prior", 0) + 1; p.getPacketDispatch().sendMessage("You have been given priority level " + priority + " over other players in joining the next"); p.getPacketDispatch().sendMessage("game."); @@ -264,7 +263,7 @@ public final class PestControlSession { Random r = RandomFunction.RANDOM; Location l = region.getBaseLocation(); p.getProperties().setTeleportLocation(l.transform(32 + r.nextInt(4), 49 + r.nextInt(6), 0)); - setVarp(p, 1147, 0); + setVarp(p, 1147, 0); p.getDialogueInterpreter().sendDialogues(3781, FacialExpression.FURIOUS, "You must defend the Void Knight while the portals are", "unsummoned. The ritual takes twenty minutes though,", "so you can help out by destroying them yourselves!", "Now GO GO GO!"); } @@ -272,9 +271,15 @@ public final class PestControlSession { * Initializes the barricades list. */ private void initBarricadesList() { - RegionPlane p = region.getPlanes()[0]; - for (Point point : OBJECT_OFFSETS) { - barricades.add(p.getObjects()[point.getX()][point.getY()]); + List objectList = region.assembleObjectList(0); + for (Scenery object : objectList) { + for (Point point : OBJECT_OFFSETS) { + if (object.getLocation().getLocalX() == point.getX() && object.getLocation().getLocalY() == point.getY()) { + if (object.getId() != STRANGE_PHANTOM_OBJECT_THAT_SHARES_A_TILE_WITH_A_BARRICADE) { + barricades.add(object); + } + } + } } } @@ -385,5 +390,4 @@ public final class PestControlSession { public List getBarricades() { return barricades; } - } diff --git a/Server/src/main/content/minigame/pestcontrol/VoidSealPlugin.java b/Server/src/main/content/minigame/pestcontrol/VoidSealPlugin.java index 95244ea66..d6c475b5b 100644 --- a/Server/src/main/content/minigame/pestcontrol/VoidSealPlugin.java +++ b/Server/src/main/content/minigame/pestcontrol/VoidSealPlugin.java @@ -29,7 +29,7 @@ public final class VoidSealPlugin extends OptionHandler { @Override public boolean handle(Player player, Node node, String option) { - if (player.getViewport().getRegion().getRegionId() != 10536) { + if (player.getLocation().getRegionId() != 10536) { player.getPacketDispatch().sendMessage("You can only use the seal in Pest Control."); return true; } @@ -48,7 +48,7 @@ public final class VoidSealPlugin extends OptionHandler { player.getInventory().replace(replace, item.getSlot()); } player.graphics(Graphics.create(1177)); - for (NPC npc : RegionManager.getLocalNpcs(player, 2)) { + for (NPC npc : RegionManager.getLocalNPCs(player.getLocation(), 2)) { if (canTarget(npc)) { npc.getImpactHandler().manualHit(player, 7 + RandomFunction.randomize(5), HitsplatType.NORMAL, 1); npc.graphics(Graphics.create(1176)); diff --git a/Server/src/main/content/minigame/pestcontrol/bots/CombatState.kt b/Server/src/main/content/minigame/pestcontrol/bots/CombatState.kt index dbfb2ee4a..d03ef42a6 100644 --- a/Server/src/main/content/minigame/pestcontrol/bots/CombatState.kt +++ b/Server/src/main/content/minigame/pestcontrol/bots/CombatState.kt @@ -7,8 +7,6 @@ import core.game.world.map.Location import core.game.world.map.RegionManager import core.game.world.map.path.Pathfinder import core.tools.RandomFunction -import kotlinx.coroutines.GlobalScope -import kotlinx.coroutines.launch import content.minigame.pestcontrol.PestControlHelper.GATE_ENTRIES import content.minigame.pestcontrol.PestControlHelper.getMyPestControlSession1 import core.game.world.GameWorld @@ -63,7 +61,7 @@ class CombatState(val bot: PestControlTestBot) { if (portal != null) { if(bot.location.withinDistance(portal.location,10) && portal.isActive){ val spinners = ArrayList() - RegionManager.getLocalNpcs(bot).forEach { + RegionManager.getLocalNPCs(bot.location).forEach { if(it.name.toLowerCase().equals("spinner") && it.location.withinDistance(bot.location,10)) spinners.add(it) } if(spinners.isNotEmpty()){ diff --git a/Server/src/main/content/minigame/pestcontrol/bots/CombatStateIntermediate.kt b/Server/src/main/content/minigame/pestcontrol/bots/CombatStateIntermediate.kt index fbea681bd..669eca80b 100644 --- a/Server/src/main/content/minigame/pestcontrol/bots/CombatStateIntermediate.kt +++ b/Server/src/main/content/minigame/pestcontrol/bots/CombatStateIntermediate.kt @@ -7,8 +7,6 @@ import core.game.world.map.Location import core.game.world.map.RegionManager import core.game.world.map.path.Pathfinder import core.tools.RandomFunction -import kotlinx.coroutines.GlobalScope -import kotlinx.coroutines.launch import content.minigame.pestcontrol.PestControlHelper.GATE_ENTRIES import content.minigame.pestcontrol.PestControlHelper.getMyPestControlSession2 import core.game.world.GameWorld @@ -62,7 +60,7 @@ class CombatStateIntermediate(val bot: PestControlTestBot2) { if (portal != null) { if(bot.location.withinDistance(portal.location,10) && portal.isActive){ val spinners = ArrayList() - RegionManager.getLocalNpcs(bot).forEach { + RegionManager.getLocalNPCs(bot.location).forEach { if(it.name.toLowerCase().equals("spinner") && it.location.withinDistance(bot.location,10)) spinners.add(it) } if(spinners.isNotEmpty()){ diff --git a/Server/src/main/content/minigame/pestcontrol/monsters/PCRavagerNPC.java b/Server/src/main/content/minigame/pestcontrol/monsters/PCRavagerNPC.java index 4fba76be2..4d45fee9b 100644 --- a/Server/src/main/content/minigame/pestcontrol/monsters/PCRavagerNPC.java +++ b/Server/src/main/content/minigame/pestcontrol/monsters/PCRavagerNPC.java @@ -114,7 +114,7 @@ public class PCRavagerNPC extends AbstractNPC { GameWorld.getPulser().submit(new Pulse(1, this, o) { @Override public boolean pulse() { - if (getViewport().getRegion().isActive() && session.getBarricades().remove(o)) { + if (getLocation().getRegion().isActive() && session.getBarricades().remove(o)) { session.getBarricades().add(newTarget); SceneryBuilder.replace(o, newTarget); } @@ -173,10 +173,10 @@ public class PCRavagerNPC extends AbstractNPC { return true; } - @Override - public boolean shouldPreventStacking(Entity mover) { - return mover instanceof NPC; - } + @Override + public boolean shouldPreventStacking(Entity mover) { + return mover instanceof NPC; + } @Override public void onImpact(final Entity entity, BattleState state) { diff --git a/Server/src/main/content/minigame/pestcontrol/monsters/PCSpinnerNPC.java b/Server/src/main/content/minigame/pestcontrol/monsters/PCSpinnerNPC.java index 60cc22a37..9407a61d7 100644 --- a/Server/src/main/content/minigame/pestcontrol/monsters/PCSpinnerNPC.java +++ b/Server/src/main/content/minigame/pestcontrol/monsters/PCSpinnerNPC.java @@ -100,9 +100,9 @@ public final class PCSpinnerNPC extends AbstractNPC { */ public void explode() { animate(getProperties().getDeathAnimation()); - for (Player p : RegionManager.getLocalPlayers(this, 1)) { + for (Player p : RegionManager.getLocalPlayers(location, 1)) { p.getImpactHandler().manualHit(this, 5, HitsplatType.POISON); - applyPoison(p, this, 1); + applyPoison(p, this, 1); } GameWorld.getPulser().submit(new Pulse(1, this) { @Override diff --git a/Server/src/main/content/minigame/pestcontrol/monsters/PCSplatterNPC.java b/Server/src/main/content/minigame/pestcontrol/monsters/PCSplatterNPC.java index 5e5111211..cb8c83764 100644 --- a/Server/src/main/content/minigame/pestcontrol/monsters/PCSplatterNPC.java +++ b/Server/src/main/content/minigame/pestcontrol/monsters/PCSplatterNPC.java @@ -75,10 +75,10 @@ public final class PCSplatterNPC extends AbstractNPC { } } - @Override - public boolean shouldPreventStacking(Entity mover) { - return mover instanceof NPC; - } + @Override + public boolean shouldPreventStacking(Entity mover) { + return mover instanceof NPC; + } @Override public void onImpact(final Entity entity, BattleState state) { @@ -132,10 +132,10 @@ public final class PCSplatterNPC extends AbstractNPC { } } } - for (Player p : RegionManager.getLocalPlayers(this, 1)) { + for (Player p : RegionManager.getLocalPlayers(location, 1)) { p.getImpactHandler().manualHit(this, RandomFunction.random(minimum, maximum), null); } - for (NPC npc : RegionManager.getLocalNpcs(this, 1)) { + for (NPC npc : RegionManager.getLocalNPCs(location, 1)) { if (npc != this) { npc.getImpactHandler().manualHit(this, RandomFunction.random(minimum, maximum), null); } diff --git a/Server/src/main/content/minigame/puropuro/ImpDefenderBehavior.kt b/Server/src/main/content/minigame/puropuro/ImpDefenderBehavior.kt index 2c1bb509e..c01d48b28 100644 --- a/Server/src/main/content/minigame/puropuro/ImpDefenderBehavior.kt +++ b/Server/src/main/content/minigame/puropuro/ImpDefenderBehavior.kt @@ -27,7 +27,7 @@ class ImpDefenderBehavior : NPCBehavior(6074) { var nextCaptureTick = getAttribute(self, "next-capture-tick", 0) if (getWorldTicks() < nextCaptureTick) return true - var players = RegionManager.getLocalPlayers(self, 2) + var players = RegionManager.getLocalPlayers(self.location, 2) for (player in players) { var lowestTierImpling = BNetTypes.getImpling(player) if (lowestTierImpling == null) continue diff --git a/Server/src/main/content/minigame/sorceress/SorceressElementalNPC.java b/Server/src/main/content/minigame/sorceress/SorceressElementalNPC.java index af3dbc8e7..7c05215f2 100644 --- a/Server/src/main/content/minigame/sorceress/SorceressElementalNPC.java +++ b/Server/src/main/content/minigame/sorceress/SorceressElementalNPC.java @@ -1,7 +1,5 @@ package content.minigame.sorceress; -import java.util.List; - import core.game.component.Component; import core.game.node.entity.Entity; import core.game.node.entity.combat.CombatSwingHandler; @@ -12,6 +10,7 @@ import core.game.system.task.Pulse; import core.game.world.GameWorld; import core.game.world.map.Direction; import core.game.world.map.Location; +import core.game.world.map.RegionManager; import core.game.world.update.flag.context.Animation; import core.game.world.update.flag.context.Graphics; import core.net.packet.PacketRepository; @@ -194,20 +193,11 @@ public final class SorceressElementalNPC extends AbstractNPC { return respawn; } - /** - * Gets the tilesIndex. - * @return The tilesIndex. - */ - public int getTilesIndex() { - return tilesIndex; - } - @Override public void tick() { super.tick(); - List players = getViewport().getCurrentPlane().getPlayers(); - for (Player player : players) { - if (player == null || !player.isActive() || player.getLocks().isInteractionLocked() || DeathTask.isDead(player) || !canTeleport(player) || !CombatSwingHandler.isProjectileClipped(this, player, false)) { + for (Player player : RegionManager.getLocalPlayers(location)) { + if (!player.isActive() || player.getLocks().isInteractionLocked() || DeathTask.isDead(player) || !canTeleport(player) || !CombatSwingHandler.isProjectileClipped(this, player, false)) { continue; } animate(new Animation(5803)); diff --git a/Server/src/main/content/minigame/vinesweeper/Vinesweeper.kt b/Server/src/main/content/minigame/vinesweeper/Vinesweeper.kt index 04fe7686a..94ed9dceb 100644 --- a/Server/src/main/content/minigame/vinesweeper/Vinesweeper.kt +++ b/Server/src/main/content/minigame/vinesweeper/Vinesweeper.kt @@ -1,8 +1,6 @@ package content.minigame.vinesweeper -import BlinkinDialogue -import FarmerDialogue.Companion.FARMER_FLAG_LINES -import WinkinDialogue +import content.minigame.vinesweeper.FarmerDialogue.Companion.FARMER_FLAG_LINES import content.minigame.vinesweeper.Vinesweeper.Companion.FARMERS import content.minigame.vinesweeper.Vinesweeper.Companion.FARMER_CLEAR_RADIUS import content.minigame.vinesweeper.Vinesweeper.Companion.HOLES @@ -34,6 +32,8 @@ import core.game.system.task.Pulse import core.game.world.GameWorld import core.game.world.GameWorld.ticks import core.game.world.map.Location +import core.game.world.map.Region +import core.game.world.map.RegionChunk import core.game.world.map.RegionManager import core.game.world.map.zone.ZoneBorders import core.game.world.update.flag.context.Animation @@ -52,27 +52,26 @@ class Vinesweeper : InteractionListener, InterfaceListener, MapArea { } override fun areaEnter(entity: Entity) { - if(entity is Player) - { + if (entity is Player) { openOverlay(entity, Components.RABBIT_OVERLAY_689) sendUpdatedPoints(entity) } } override fun areaLeave(entity: Entity, logout: Boolean) { - if(entity is Player) { + if (entity is Player) { entity.interfaceManager.closeOverlay() - if(!logout) { + if (!logout) { sendMessage(entity, "Winkin's Farm thanks you for your visit.") sendMessage(entity, "Leftover ogleroots and flags have been returned to the establishment.") sendMessage(entity, "You have been reimbursed at a rate of 10gp per ogleroot and the flags have been collected.") val flags = entity.inventory.getAmount(Item(Items.FLAG_12625)) - if(flags > 0) { + if (flags > 0) { entity.setAttribute("/save:vinesweeper:stored-flags", flags) entity.inventory.remove(Item(Items.FLAG_12625, flags)) } val roots = entity.inventory.getAmount(Item(Items.OGLEROOT_12624)) - if(roots > 0) { + if (roots > 0) { entity.inventory.remove(Item(Items.OGLEROOT_12624, roots)) entity.inventory.add(Item(Items.COINS_995, roots * 10)) } @@ -98,7 +97,7 @@ class Vinesweeper : InteractionListener, InterfaceListener, MapArea { return@on true } on(HOLES, IntType.SCENERY, "dig") { player, node -> - if(!player.inventory.contains(Items.SPADE_952, 1)) { + if (!player.inventory.contains(Items.SPADE_952, 1)) { // TODO (crash): authenticity player.sendMessage("You need a spade to dig here.") } else { @@ -115,7 +114,7 @@ class Vinesweeper : InteractionListener, InterfaceListener, MapArea { on(HOLES, IntType.SCENERY, "flag") { player, node -> val hole = node as Scenery var count = 0 - if(player.location != node.location) { + if (player.location != node.location) { plantFlag(player,hole) return@on true } @@ -151,7 +150,7 @@ class Vinesweeper : InteractionListener, InterfaceListener, MapArea { 1 -> "You get some mud in your eye and it stings! You have no idea what is in the hole." 2 -> "The mud seems to be too thick to see what is there." 3 -> "A slimy worm wriggles out of the mud, making you jump and lose concentration. You're not sure if there is a seed here or not." - else -> if(isSeed(node.location)) { + else -> if (isSeed(node.location)) { "You notice a seed hidden in the dirt." } else { "You are certain there is no seed planted here." @@ -262,7 +261,7 @@ class Vinesweeper : InteractionListener, InterfaceListener, MapArea { data class SeedDestination(val player: Player, val loc: Location, val alive: Boolean) { override fun equals(other: Any?): Boolean { - return if(other is SeedDestination) { + return if (other is SeedDestination) { loc == other.loc } else { false @@ -274,8 +273,7 @@ class Vinesweeper : InteractionListener, InterfaceListener, MapArea { } } - companion object - { + companion object { val AVACH_NIMPORTO_LOC = Location.create(1637, 4709) val SIGNS = intArrayOf(Sceneries.INSTRUCTION_SIGN_29461, Sceneries.INSTRUCTION_SIGN_29462, Sceneries.INSTRUCTION_SIGN_29463, Sceneries.INSTRUCTION_SIGN_29464) val HOLES = intArrayOf(Sceneries.HOLE_29476, Sceneries.HOLE_29477, Sceneries.HOLE_29478) @@ -334,9 +332,9 @@ class Vinesweeper : InteractionListener, InterfaceListener, MapArea { val reward = REWARDS[buttonID] ?: return val cost = amount * reward.points val points = player.getAttribute("vinesweeper:points", 0) - if(cost in 1 until points) { + if (cost in 1 until points) { val item = Item(reward.itemID, amount) - if(!player.inventory.add(item)) { + if (!player.inventory.add(item)) { GroundItemManager.create(item, player) } player.incrementAttribute("/save:vinesweeper:points", -cost) @@ -347,7 +345,6 @@ class Vinesweeper : InteractionListener, InterfaceListener, MapArea { } } - val TUTORIAL = 685 val INSTRUCTION_SIGNS = hashMapOf( @@ -362,52 +359,87 @@ class Vinesweeper : InteractionListener, InterfaceListener, MapArea { val MAX_SEEDS = 300 val FARMER_CLEAR_RADIUS = 3 - val VINESWEEPER_BORDERS = ZoneBorders(1600,4672,1663,4735) + val VINESWEEPER_REGION = RegionManager.forId(6473) + var SEED_LOCS: HashSet = HashSet() fun sendUpdatedPoints(player: Player) { val points = player.getAttribute("vinesweeper:points", 0) setVarbit(player, 4449, points) } - var SEED_LOCS: HashSet = HashSet() - fun isSeed(loc: Location): Boolean { val scenery = getScenery(loc) return scenery != null && SEED_LOCS.contains(scenery.location) } - fun populateSeeds() { - val holes = countHoles() - val seeds = min(1.0 * MAX_SEEDS, holes * 0.13).toInt() - var tries = 0 // Prevent the while loop from crashing the server - while(SEED_LOCS.size < seeds && tries++ < 1000) { - val loc = VINESWEEPER_BORDERS.getRandomLoc() - val scenery = getScenery(loc) - if(scenery != null && HOLES.contains(scenery.id)) { - SEED_LOCS.add(loc) - } - } - } - - private fun countHoles(): Int { - val northEastX = VINESWEEPER_BORDERS.northEastX - val northEastY = VINESWEEPER_BORDERS.northEastY - val southWestX = VINESWEEPER_BORDERS.southWestX - val southWestY = VINESWEEPER_BORDERS.southWestY - var holeCount = 0 - for (x in southWestX .. northEastX){ - for (y in southWestY .. northEastY){ - val scenery = getScenery(x, y, 0) - if(scenery != null && HOLES.contains(scenery.id)) { - holeCount++ + private fun resetChunkIfBarren(chunk: RegionChunk) { + val wipedHoles = ArrayList() + for (x in 0 until 8) { + for (y in 0 until 8) { + for (i in 0 until 4) { + val obj = chunk.objects[x][y][i] ?: continue + if (obj.id in HOLES) { + return //this chunk is not barren and hence should not be reset + } + if (obj.id == NUMBERS[0]) { + wipedHoles.add(obj) + } } } } - return holeCount + for (scenery in wipedHoles) { + restoreOriginalHole(scenery) + } + } + + private fun countFieldState(chunk: RegionChunk, emptyHoleList: ArrayList, seedHoleList: ArrayList) { + for (x in 0 until RegionChunk.SIZE) { + for (y in 0 until RegionChunk.SIZE) { + for (i in 0 until RegionChunk.ARRAY_SIZE) { + val obj = chunk.objects[x][y][i] ?: continue + if (obj.id in HOLES) { + if (obj.location in SEED_LOCS) { + seedHoleList.add(obj) + } else { + emptyHoleList.add(obj) + } + } + } + } + } + } + + fun populateSeeds() { + // First, see if there are any chunks that have been completely wiped out. These need to be restored first (GL #2400). + Region.load(VINESWEEPER_REGION) + for (cx in 0 until RegionChunk.SIZE) { + for (cy in 0 until RegionChunk.SIZE) { + val chunk = VINESWEEPER_REGION.chunks[cx][cy][0] + resetChunkIfBarren(chunk) + } + } + // Next, get the state of the field + val emptyHoleList = ArrayList() + val seedHoleList = ArrayList() + for (cx in 0 until 8) { + for (cy in 0 until 8) { + val chunk = VINESWEEPER_REGION.chunks[cx][cy][0] + countFieldState(chunk, emptyHoleList, seedHoleList) + } + } + val target = min(1.0 * MAX_SEEDS, (seedHoleList.size + emptyHoleList.size) * 0.13).toInt() + val seedsToAdd = target - seedHoleList.size + if (seedsToAdd < 1) { + return + } + emptyHoleList.shuffle() + for (i in 0 until seedsToAdd) { + SEED_LOCS.add(emptyHoleList[i].location) + } } fun plantFlag(player: Player, hole: Scenery) { - if(player.inventory.remove(Item(Items.FLAG_12625, 1))) { + if (player.inventory.remove(Item(Items.FLAG_12625, 1))) { player.lock() player.visualize(Animation(Animations.HUMAN_PLANT_FLAG_8711),Graphics(Gfx.VINESWEEPER_PLANT_FLAG_1541)) player.sendMessage("You add a flag to the patch.") @@ -427,9 +459,9 @@ class Vinesweeper : InteractionListener, InterfaceListener, MapArea { fun feedRabbit(player: Player, rabbit: NPC) { val respawnDelay = 50 - if(rabbit.isInvisible || DeathTask.isDead(rabbit)) { return } - if(rabbit.getAttribute("dead", 0) > ticks) { return } - if(player.inventory.remove(Item(Items.OGLEROOT_12624, 1))) { + if (rabbit.isInvisible || DeathTask.isDead(rabbit)) { return } + if (rabbit.getAttribute("dead", 0) > ticks) { return } + if (player.inventory.remove(Item(Items.OGLEROOT_12624, 1))) { rabbit.setAttribute("dead",ticks + respawnDelay) player.skills.addExperience(Skills.HUNTER, 30.0) rabbit.sendChat("Squeak!") @@ -449,36 +481,36 @@ class Vinesweeper : InteractionListener, InterfaceListener, MapArea { } fun dig(player: Player, loc: Location) { - if(isSeed(loc)) { + if (isSeed(loc)) { val oldPoints = player.getAttribute("vinesweeper:points", 0) player.setAttribute("/save:vinesweeper:points", Math.max(oldPoints-10, 0)) sendUpdatedPoints(player) player.sendMessage("Oh dear! It looks like you dug up a potato seed by mistake.") scheduleNPCs(player, loc, false, false) val scenery = getScenery(loc) - if(scenery != null) { + if (scenery != null) { SceneryBuilder.replace(scenery, scenery.transform(Sceneries.DEAD_PLANT_29456)) } } else { player.incrementAttribute("/save:vinesweeper:points", 1) sendUpdatedPoints(player) var count = 0 - for(dx in -1..1) { - for(dy in -1..1) { - if(isSeed(loc.transform(dx, dy, 0))) { + for (dx in -1..1) { + for (dy in -1..1) { + if (isSeed(loc.transform(dx, dy, 0))) { count += 1 } } } val scenery = getScenery(loc) - if(scenery != null) { + if (scenery != null) { SceneryBuilder.replace(scenery, scenery.transform(NUMBERS[count])) } - if(count == 0) { - for(dx in -1..1) { - for(dy in -1..1) { + if (count == 0) { + for (dx in -1..1) { + for (dy in -1..1) { val newLoc = loc.transform(dx, dy, 0) - if(isHole(newLoc)) + if (isHole(newLoc)) dig(player, newLoc) } } @@ -492,10 +524,10 @@ class Vinesweeper : InteractionListener, InterfaceListener, MapArea { dest.player.incrementAttribute("/save:vinesweeper:points", points) dest.player.inventory.add(Item(Items.FLAG_12625, 1)) sendUpdatedPoints(dest.player) - for (neighbor in RegionManager.getLocalPlayers(npc)) { - if (neighbor != dest.player) { - neighbor.incrementAttribute("/save:vinesweeper:points", points / 2) - sendUpdatedPoints(neighbor) + for (player in RegionManager.getLocalPlayers(npc.location)) { + if (player.name != dest.player.name) { + player.incrementAttribute("/save:vinesweeper:points", points / 2) + sendUpdatedPoints(player) } } } @@ -507,9 +539,9 @@ class Vinesweeper : InteractionListener, InterfaceListener, MapArea { fun scheduleNPCs(player: Player, loc: Location, alive: Boolean, rabbit: Boolean) { val dest = SeedDestination(player, loc, alive) - val ids = if(rabbit) { RABBITS + FARMERS } else { FARMERS } - for(npc in findLocalNPCs(player, ids, 30)) { - if(npc is VinesweeperNPC) { + val ids = if (rabbit) { RABBITS + FARMERS } else { FARMERS } + for (npc in findLocalNPCs(player, ids, 30)) { + if (npc is VinesweeperNPC) { npc.seedDestinations.add(dest) npc.resetWalk() } @@ -572,21 +604,23 @@ class VinesweeperNPC : AbstractNPC { } override fun handleTickActions() { val dest = seedDestinations.find { sd -> sd.loc == location } - if (locks.isMovementLocked() || locks.isInteractionLocked()) + if (locks.isMovementLocked || locks.isInteractionLocked) return - if(dest != null) { - for(npc in RegionManager.getRegionPlane(location).npcs) { - if(npc is VinesweeperNPC) { - npc.seedDestinations.remove(dest) - npc.resetWalk() + if (dest != null) { + val npcs = location.region.assembleNpcList(0) + for (npc in npcs) { + if (npc !is VinesweeperNPC || npc.seedDestinations.isEmpty() /*npc is not currently walking towards a seed*/) { + continue } + npc.seedDestinations.remove(dest) + npc.resetWalk() } val scenery = getScenery(dest.loc) - if(scenery != null) { - if(id in RABBITS) { + if (scenery != null) { + if (id in RABBITS) { handleRabbitSeed(scenery, dest) } else { - if(dest.alive) { + if (dest.alive) { handleFarmerFlag(scenery, dest) } else { handleFarmerSeed(scenery, dest) @@ -599,7 +633,7 @@ class VinesweeperNPC : AbstractNPC { } override fun getMovementDestination(): Location? { - if(seedDestinations.size > 0) { + if (seedDestinations.size > 0) { seedDestinations.sortBy { a -> a.loc.getDistance(location).toInt() } return seedDestinations.first().loc } else { @@ -608,7 +642,7 @@ class VinesweeperNPC : AbstractNPC { } fun handleRabbitSeed(scenery: Scenery, dest: Vinesweeper.SeedDestination) { - if(SEED_LOCS.contains(dest.loc)) { + if (SEED_LOCS.contains(dest.loc)) { val replacement = Sceneries.DEAD_PLANT_29456 lock(4) animate(Animation(Animations.RABBIT_EAT_SEED_8718)) @@ -618,6 +652,7 @@ class VinesweeperNPC : AbstractNPC { scheduleNPCs(dest.player, dest.loc, alive = true, rabbit = false) } } + fun handleFarmerSeed(scenery: Scenery, dest: Vinesweeper.SeedDestination) { lock() var i = 0 @@ -631,7 +666,7 @@ class VinesweeperNPC : AbstractNPC { 1 -> { animate(Animation(Animations.GNOME_FARMER_SPADE_SMACK_8732)) sendChat(FARMER_FLAG_LINES.DEAD_PLANT.line) - SceneryBuilder.replace(scenery, scenery.transform(HOLES[0])) + restoreOriginalHole(scenery) } 2 -> { animate(Animation(Animations.GNOME_FARMER_CLEAR_HOLES_8724)) @@ -653,13 +688,13 @@ class VinesweeperNPC : AbstractNPC { animate(Animation(Animations.GNOME_FARMER_DIG_FLAG_8725)) GameWorld.Pulser.submit(object: Pulse(3) { override fun pulse(): Boolean { - when(i++) { + when (i++) { 0 -> { sendChat(FARMER_FLAG_LINES.FIND_FLAG.line) } 1 -> { - SceneryBuilder.replace(scenery, scenery.transform(HOLES[0])) - if(SEED_LOCS.contains(dest.loc)) { + restoreOriginalHole(scenery) + if (SEED_LOCS.contains(dest.loc)) { sendChat(FARMER_FLAG_LINES.FIND_SEED.line) animate(Animation(Animations.GNOME_FARMER_HOORAY_8731)) sendPoints(npc,dest) @@ -688,11 +723,11 @@ class VinesweeperNPC : AbstractNPC { } fun farmerClear(dest: Vinesweeper.SeedDestination) { - for(dx in -FARMER_CLEAR_RADIUS..FARMER_CLEAR_RADIUS) { - for(dy in -FARMER_CLEAR_RADIUS..FARMER_CLEAR_RADIUS) { + for (dx in -FARMER_CLEAR_RADIUS..FARMER_CLEAR_RADIUS) { + for (dy in -FARMER_CLEAR_RADIUS..FARMER_CLEAR_RADIUS) { val toClear = getScenery(dest.loc.transform(dx, dy, 0)) - if(toClear != null && intArrayOf(Sceneries.DEAD_PLANT_29456, *NUMBERS).contains(toClear.id)) { - SceneryBuilder.replace(toClear, toClear.transform(HOLES[0])) + if (toClear != null && intArrayOf(Sceneries.DEAD_PLANT_29456, *NUMBERS).contains(toClear.id)) { + restoreOriginalHole(toClear) } } } @@ -701,6 +736,16 @@ class VinesweeperNPC : AbstractNPC { } } +fun restoreOriginalHole(scenery: Scenery) { + var originalHoleId = HOLES[0] + for (obj in scenery.location.chunk.getStatObjects(scenery.location.chunkOffsetX, scenery.location.chunkOffsetY)) { + if (obj?.id != null) { + originalHoleId = obj.id + break + } + } + SceneryBuilder.replace(scenery, scenery.transform(originalHoleId)) +} /* diff --git a/Server/src/main/content/minigame/vinesweeper/VinesweeperDialogues.kt b/Server/src/main/content/minigame/vinesweeper/VinesweeperDialogues.kt index 23c012487..49f50f72e 100644 --- a/Server/src/main/content/minigame/vinesweeper/VinesweeperDialogues.kt +++ b/Server/src/main/content/minigame/vinesweeper/VinesweeperDialogues.kt @@ -1,3 +1,5 @@ +package content.minigame.vinesweeper + import core.game.component.Component import core.game.node.item.GroundItemManager import core.game.node.item.Item diff --git a/Server/src/main/content/region/asgarnia/burthorpe/handlers/BurthorpeTrainNPC.java b/Server/src/main/content/region/asgarnia/burthorpe/handlers/BurthorpeTrainNPC.java index 518c70715..36db1cfee 100644 --- a/Server/src/main/content/region/asgarnia/burthorpe/handlers/BurthorpeTrainNPC.java +++ b/Server/src/main/content/region/asgarnia/burthorpe/handlers/BurthorpeTrainNPC.java @@ -126,7 +126,7 @@ public class BurthorpeTrainNPC extends AbstractNPC { case 1061:// sergant sendChat(MESSAGES[RandomFunction.random(MESSAGES.length)]); delay = System.currentTimeMillis() + 9000 + RandomFunction.random(1000, 5000); - final List soldiers = RegionManager.getLocalNpcs(this); + final List soldiers = RegionManager.getLocalNPCs(location); NPC soldier = null; while (soldier == null) { soldier = soldiers.get(RandomFunction.random(soldiers.size())); @@ -164,7 +164,7 @@ public class BurthorpeTrainNPC extends AbstractNPC { GameWorld.getPulser().submit(new Pulse(2) { @Override public boolean pulse() { - final List soldiers = RegionManager.getLocalNpcs(BurthorpeTrainNPC.this, 12); + final List soldiers = RegionManager.getLocalNPCs(location, 12); for (NPC n : soldiers) { if (n.getId() != 1064) { continue; diff --git a/Server/src/main/content/region/asgarnia/burthorpe/handlers/wguild/cyclopes/CyclopesRoom.java b/Server/src/main/content/region/asgarnia/burthorpe/handlers/wguild/cyclopes/CyclopesRoom.java index 98111f7c1..829a95099 100644 --- a/Server/src/main/content/region/asgarnia/burthorpe/handlers/wguild/cyclopes/CyclopesRoom.java +++ b/Server/src/main/content/region/asgarnia/burthorpe/handlers/wguild/cyclopes/CyclopesRoom.java @@ -31,7 +31,6 @@ import core.plugin.Plugin; import core.plugin.ClassScanner; import core.tools.RandomFunction; import kotlin.Unit; -import kotlin.jvm.functions.Function1; import static core.api.ContentAPIKt.clearLogoutListener; import static core.api.ContentAPIKt.registerLogoutListener; @@ -73,7 +72,7 @@ public final class CyclopesRoom extends MapZone implements Plugin { Pathfinder.find(p.getLocation(), Location.create(2847, 3541, 2)).walk(p); p.lock(50); } else { - Scenery object = RegionManager.getObject(2, 2847, 3541); + Scenery object = RegionManager.getObject(2847, 3541, 2); if (object != null && p.getLocation().getX() == 2847 && p.getLocation().getY() == 3541) { DoorActionHandler.handleAutowalkDoor(p, object); leave(p); diff --git a/Server/src/main/content/region/asgarnia/burthorpe/handlers/wguild/dummy/DummyRoom.java b/Server/src/main/content/region/asgarnia/burthorpe/handlers/wguild/dummy/DummyRoom.java index bc1ad6f5a..6a9a2e1a1 100644 --- a/Server/src/main/content/region/asgarnia/burthorpe/handlers/wguild/dummy/DummyRoom.java +++ b/Server/src/main/content/region/asgarnia/burthorpe/handlers/wguild/dummy/DummyRoom.java @@ -111,12 +111,18 @@ public final class DummyRoom extends OptionHandler { setDelay(10); timeStamp = GameWorld.getTicks(); dummy = RandomFunction.getRandomElement(Dummy.values()); - SceneryBuilder.replace(RegionManager.getObject(dummy.getObject().getLocation()), dummy.getObject(), 11); + Scenery dummyObject = RegionManager.getObject(dummy.getObject().getLocation()); + if (dummyObject != null) { + SceneryBuilder.replace(dummyObject, dummy.getObject(), 11); + } activeDummy = true; if (dummy == Dummy.CONTROLLED && controlled == null) { Location l = Location.create(2860, 3551, 0); controlled = new Scenery(dummy.getObject().getId(), l, 10, 1); - SceneryBuilder.replace(RegionManager.getObject(l), controlled, 11); + Scenery controlledObject = RegionManager.getObject(l); + if (controlledObject != null) { + SceneryBuilder.replace(controlledObject, controlled, 11); + } } return false; } diff --git a/Server/src/main/content/region/asgarnia/falador/diary/FaladorAchievementDiary.kt b/Server/src/main/content/region/asgarnia/falador/diary/FaladorAchievementDiary.kt index aac922c9a..93a55d438 100644 --- a/Server/src/main/content/region/asgarnia/falador/diary/FaladorAchievementDiary.kt +++ b/Server/src/main/content/region/asgarnia/falador/diary/FaladorAchievementDiary.kt @@ -143,7 +143,7 @@ class FaladorAchievementDiary : DiaryEventHookBase(DiaryType.FALADOR) { ) override fun onInteracted(player: Player, event: InteractionEvent) { - when (player.viewport.region.id) { + when (player.location.regionId) { 12084 -> { if (event.option == "burst" && event.target.id in PARTY_BALLOONS) { finishTask( @@ -179,7 +179,7 @@ class FaladorAchievementDiary : DiaryEventHookBase(DiaryType.FALADOR) { } override fun onResourceProduced(player: Player, event: ResourceProducedEvent) { - when (player.viewport.region.id) { + when (player.location.regionId) { 11828 -> when (event.itemId) { Items.YEW_LOGS_1515, Items.MAGIC_LOGS_1513 -> { if (inBorders(player, PARK_TREE_PATCH_AREA)) { @@ -195,7 +195,7 @@ class FaladorAchievementDiary : DiaryEventHookBase(DiaryType.FALADOR) { } override fun onNpcKilled(player: Player, event: NPCKillEvent) { - when (player.viewport.region.id) { + when (player.location.regionId) { 11828 -> if (event.npc.id in PARK_DUCKS && inBorders(event.npc, PARK_POND_AREA)) { finishTask( player, diff --git a/Server/src/main/content/region/asgarnia/falador/quest/blackknightsfortress/FortressGuardDialogue.java b/Server/src/main/content/region/asgarnia/falador/quest/blackknightsfortress/FortressGuardDialogue.java index 51fa14058..0f33d69ce 100644 --- a/Server/src/main/content/region/asgarnia/falador/quest/blackknightsfortress/FortressGuardDialogue.java +++ b/Server/src/main/content/region/asgarnia/falador/quest/blackknightsfortress/FortressGuardDialogue.java @@ -200,8 +200,8 @@ public class FortressGuardDialogue extends DialoguePlugin { break; case 54: end(); - DoorActionHandler.handleAutowalkDoor(player, RegionManager.getObject(0, 3020, 3515)); - List npcs = RegionManager.getLocalNpcs(player); + DoorActionHandler.handleAutowalkDoor(player, RegionManager.getObject(3020, 3515, 0)); + List npcs = RegionManager.getLocalNPCs(player.getLocation()); for (NPC npc : npcs) { if (npc.getId() == 179) { npc.getProperties().getCombatPulse().attack(player); diff --git a/Server/src/main/content/region/asgarnia/goblinvillage/handlers/GoblinVillageNPC.java b/Server/src/main/content/region/asgarnia/goblinvillage/handlers/GoblinVillageNPC.java index 0c34e38d0..80865890c 100644 --- a/Server/src/main/content/region/asgarnia/goblinvillage/handlers/GoblinVillageNPC.java +++ b/Server/src/main/content/region/asgarnia/goblinvillage/handlers/GoblinVillageNPC.java @@ -103,8 +103,8 @@ public final class GoblinVillageNPC extends AbstractNPC { if (delay < System.currentTimeMillis() && !getProperties().getCombatPulse().isAttacking()) { final int rand = RandomFunction.random(1, 4); if (rand == 2) { - final List surronding = RegionManager.getLocalNpcs(this, 10); - for (NPC n : surronding) { + final List surrounding = RegionManager.getLocalNPCs(location, 10); + for (NPC n : surrounding) { if (n.getId() == getId()) { continue; } diff --git a/Server/src/main/content/region/asgarnia/goblinvillage/handlers/GoblinVillagePopulationListener.kt b/Server/src/main/content/region/asgarnia/goblinvillage/handlers/GoblinVillagePopulationListener.kt index 401d09d54..79102017a 100644 --- a/Server/src/main/content/region/asgarnia/goblinvillage/handlers/GoblinVillagePopulationListener.kt +++ b/Server/src/main/content/region/asgarnia/goblinvillage/handlers/GoblinVillagePopulationListener.kt @@ -3,14 +3,14 @@ package content.region.asgarnia.goblinvillage.handlers import core.game.interaction.IntType import core.game.interaction.InteractionListener import core.game.node.entity.combat.DeathTask -import core.game.world.map.RegionManager.getLocalNpcs +import core.game.world.map.RegionManager.getLocalNPCs import org.rs09.consts.Scenery class GoblinVillagePopulationListener : InteractionListener{ override fun defineListeners() { on(Scenery.SIGNPOST_31301, IntType.SCENERY, "read") { player, node -> var population = 3 // This covers General Wartface, General Bentnoze and Grubfoot - val npcs = getLocalNpcs(player, 50) // This radius covers more than the goblin village. + val npcs = getLocalNPCs(player.location, 50) // This radius covers more than the goblin village. // There should be 18 Goblins walking around. // 10 are within the village walls where the generals are. // 8 of them are fighting each other on the west of the path to the village. diff --git a/Server/src/main/content/region/asgarnia/handlers/WineOfZamorakInteraction.kt b/Server/src/main/content/region/asgarnia/handlers/WineOfZamorakInteraction.kt index d908028b4..f11eef2a7 100644 --- a/Server/src/main/content/region/asgarnia/handlers/WineOfZamorakInteraction.kt +++ b/Server/src/main/content/region/asgarnia/handlers/WineOfZamorakInteraction.kt @@ -15,7 +15,7 @@ class WineOfZamorakInteraction : InteractionListener { PickupHandler.take(player, wine as GroundItem) return@on true } - val npcs = RegionManager.getLocalNpcs(player) + val npcs = RegionManager.getLocalNPCs(player.location) for (n in npcs) { if (n.id == 188) { n.sendChat("Hands off zamorak's wine!") diff --git a/Server/src/main/content/region/asgarnia/taverley/dialogue/PetshopOwnerTaverly.java b/Server/src/main/content/region/asgarnia/taverley/dialogue/PetshopOwnerTaverly.java index 39f24867c..42a750302 100644 --- a/Server/src/main/content/region/asgarnia/taverley/dialogue/PetshopOwnerTaverly.java +++ b/Server/src/main/content/region/asgarnia/taverley/dialogue/PetshopOwnerTaverly.java @@ -53,7 +53,7 @@ public final class PetshopOwnerTaverly extends DialoguePlugin { @Override public boolean open(Object... args) { if (args.length > 1) { - List npcs = RegionManager.getLocalNpcs(player); + List npcs = RegionManager.getLocalNPCs(player.getLocation()); for (NPC n : npcs) { if (n.getId() == 6893) { npc = n; diff --git a/Server/src/main/content/region/asgarnia/taverley/quest/witchshouse/NoraTHaggNPC.java b/Server/src/main/content/region/asgarnia/taverley/quest/witchshouse/NoraTHaggNPC.java index 2192bf840..8a73079c3 100644 --- a/Server/src/main/content/region/asgarnia/taverley/quest/witchshouse/NoraTHaggNPC.java +++ b/Server/src/main/content/region/asgarnia/taverley/quest/witchshouse/NoraTHaggNPC.java @@ -9,6 +9,7 @@ import core.game.system.task.Pulse; import core.game.world.GameWorld; import core.game.world.map.Direction; import core.game.world.map.Location; +import core.game.world.map.RegionManager; import core.game.world.update.flag.context.Animation; import core.game.world.update.flag.context.Graphics; import core.net.packet.PacketRepository; @@ -123,7 +124,7 @@ public class NoraTHaggNPC extends AbstractNPC { @Override public void tick() { super.tick(); - List players = getViewport().getCurrentPlane().getPlayers(); + List players = RegionManager.getLocalPlayers(location); if(getLocation().getX() == 2930){ walkdir = false; } else if(getLocation().getX() == 2904){ diff --git a/Server/src/main/content/region/asgarnia/taverley/quest/witchshouse/WitchsExperimentNPC.java b/Server/src/main/content/region/asgarnia/taverley/quest/witchshouse/WitchsExperimentNPC.java index 5f1e46e06..08eb59dee 100644 --- a/Server/src/main/content/region/asgarnia/taverley/quest/witchshouse/WitchsExperimentNPC.java +++ b/Server/src/main/content/region/asgarnia/taverley/quest/witchshouse/WitchsExperimentNPC.java @@ -38,7 +38,7 @@ public class WitchsExperimentNPC extends AbstractNPC { @Override public void handleTickActions() { super.handleTickActions(); - if(!p.isActive() || !RegionManager.getLocalPlayers(this).contains(p)){ + if (!p.isActive() || !RegionManager.getLocalPlayers(location).contains(p)) { p.removeAttribute("witchs-experiment:npc_spawned"); clear(); } diff --git a/Server/src/main/content/region/asgarnia/trollheim/handlers/gwd/GWDGraardorSwingHandler.java b/Server/src/main/content/region/asgarnia/trollheim/handlers/gwd/GWDGraardorSwingHandler.java index 540d48f02..0e14342e9 100644 --- a/Server/src/main/content/region/asgarnia/trollheim/handlers/gwd/GWDGraardorSwingHandler.java +++ b/Server/src/main/content/region/asgarnia/trollheim/handlers/gwd/GWDGraardorSwingHandler.java @@ -12,6 +12,7 @@ import core.game.node.entity.combat.equipment.ArmourSet; import core.game.node.entity.impl.Projectile; import core.game.node.entity.impl.Animator.Priority; import core.game.node.entity.npc.NPC; +import core.game.node.entity.player.Player; import core.game.world.map.RegionManager; import core.game.world.map.zone.ZoneBorders; import core.game.world.update.flag.context.Animation; @@ -66,7 +67,7 @@ public final class GWDGraardorSwingHandler extends CombatSwingHandler { ticks += (int) Math.ceil(entity.getLocation().getDistance(victim.getLocation()) * 0.3); NPC npc = (NPC) entity; List list = new ArrayList<>(20); - for (Entity t : RegionManager.getLocalPlayers(npc, 28)) { + for (Player t : RegionManager.getLocalPlayers(npc.getLocation(), 28)) { if (!CHAMBER.insideBorder(t.getLocation())) { continue; } diff --git a/Server/src/main/content/region/asgarnia/trollheim/handlers/gwd/GWDKreeArraSwingHandler.java b/Server/src/main/content/region/asgarnia/trollheim/handlers/gwd/GWDKreeArraSwingHandler.java index 925f917fd..66f5e432c 100644 --- a/Server/src/main/content/region/asgarnia/trollheim/handlers/gwd/GWDKreeArraSwingHandler.java +++ b/Server/src/main/content/region/asgarnia/trollheim/handlers/gwd/GWDKreeArraSwingHandler.java @@ -12,6 +12,7 @@ import core.game.node.entity.combat.equipment.ArmourSet; import core.game.node.entity.impl.Projectile; import core.game.node.entity.impl.Animator.Priority; import core.game.node.entity.npc.NPC; +import core.game.node.entity.player.Player; import core.game.world.map.Direction; import core.game.world.map.Location; import core.game.world.map.RegionManager; @@ -83,7 +84,7 @@ public final class GWDKreeArraSwingHandler extends CombatSwingHandler { ticks += (int) Math.ceil(entity != null ? entity.getLocation().getDistance(victim.getLocation()) : 0.0 * 0.3); NPC npc = (NPC) entity; List list = new ArrayList<>(20); - for (Entity t : RegionManager.getLocalPlayers(npc, 28)) { + for (Player t : RegionManager.getLocalPlayers(npc.getLocation(), 28)) { if (!CHAMBER.insideBorder(t.getLocation())) { continue; } diff --git a/Server/src/main/content/region/asgarnia/trollheim/handlers/gwd/GWDZilyanaSwingHandler.java b/Server/src/main/content/region/asgarnia/trollheim/handlers/gwd/GWDZilyanaSwingHandler.java index a1f060d9a..bccf419f9 100644 --- a/Server/src/main/content/region/asgarnia/trollheim/handlers/gwd/GWDZilyanaSwingHandler.java +++ b/Server/src/main/content/region/asgarnia/trollheim/handlers/gwd/GWDZilyanaSwingHandler.java @@ -11,6 +11,7 @@ import core.game.node.entity.combat.InteractionType; import core.game.node.entity.combat.equipment.ArmourSet; import core.game.node.entity.impl.Animator.Priority; import core.game.node.entity.npc.NPC; +import core.game.node.entity.player.Player; import core.game.world.map.RegionManager; import core.game.world.update.flag.context.Animation; import core.game.world.update.flag.context.Graphics; @@ -59,7 +60,7 @@ public class GWDZilyanaSwingHandler extends CombatSwingHandler { } else { NPC npc = (NPC) entity; List list = new ArrayList<>(20); - for (Entity t : RegionManager.getLocalPlayers(npc.getCenterLocation(), (npc.size() >> 1) + 2)) { + for (Player t : RegionManager.getLocalPlayers(npc.getCenterLocation(), (npc.size() >> 1) + 2)) { if (t.getLocation().getX() < 2908 && t.isAttackable(npc, CombatStyle.MAGIC, false)) { list.add(new BattleState(entity, t)); } diff --git a/Server/src/main/content/region/asgarnia/trollheim/handlers/gwd/GodWarsNPC.java b/Server/src/main/content/region/asgarnia/trollheim/handlers/gwd/GodWarsNPC.java index 215de9f11..4dae4b212 100644 --- a/Server/src/main/content/region/asgarnia/trollheim/handlers/gwd/GodWarsNPC.java +++ b/Server/src/main/content/region/asgarnia/trollheim/handlers/gwd/GodWarsNPC.java @@ -61,7 +61,7 @@ public final class GodWarsNPC extends AbstractNPC { @Override public List getPossibleTargets(Entity entity, int radius) { List targets = new ArrayList<>(20); - for (Player player : RegionManager.getLocalPlayers(entity, radius)) { + for (Player player : RegionManager.getLocalPlayers(entity.getLocation(), radius)) { if (canSelectTarget(entity, player)) { targets.add(player); } @@ -69,7 +69,7 @@ public final class GodWarsNPC extends AbstractNPC { if (!targets.isEmpty()) { return targets; } - for (NPC npc : RegionManager.getLocalNpcs(entity, radius)) { + for (NPC npc : RegionManager.getLocalNPCs(entity.getLocation(), radius)) { if (canSelectTarget(entity, npc)) { targets.add(npc); } @@ -115,9 +115,9 @@ public final class GodWarsNPC extends AbstractNPC { @Override public boolean isAttackable(Entity entity, CombatStyle style, boolean message) { if (style == CombatStyle.MELEE && faction == GodWarsFaction.ARMADYL && entity instanceof Player) { - if(message) { - ((Player) entity).getPacketDispatch().sendMessage("The aviansie is flying too high for you to attack using melee."); - } + if(message) { + ((Player) entity).getPacketDispatch().sendMessage("The aviansie is flying too high for you to attack using melee."); + } return false; } return super.isAttackable(entity, style, message); diff --git a/Server/src/main/content/region/asgarnia/trollheim/handlers/gwd/GodwarsEntranceHandler.java b/Server/src/main/content/region/asgarnia/trollheim/handlers/gwd/GodwarsEntranceHandler.java index 8803d9d88..6ec32b53d 100644 --- a/Server/src/main/content/region/asgarnia/trollheim/handlers/gwd/GodwarsEntranceHandler.java +++ b/Server/src/main/content/region/asgarnia/trollheim/handlers/gwd/GodwarsEntranceHandler.java @@ -83,7 +83,7 @@ public final class GodwarsEntranceHandler extends OptionHandler { GameWorld.getPulser().submit(new Pulse(12, player) { @Override public boolean pulse() { - player.getPacketDispatch().sendSceneryAnimation(RegionManager.getObject(0, 2898, 3716), Animation.create(6981)); + player.getPacketDispatch().sendSceneryAnimation(RegionManager.getObject(2898, 3716, 0), Animation.create(6981)); return true; } }); diff --git a/Server/src/main/content/region/desert/alkharid/handlers/AlKharidWarriorNPC.kt b/Server/src/main/content/region/desert/alkharid/handlers/AlKharidWarriorNPC.kt index ba1b4662d..0f56be285 100644 --- a/Server/src/main/content/region/desert/alkharid/handlers/AlKharidWarriorNPC.kt +++ b/Server/src/main/content/region/desert/alkharid/handlers/AlKharidWarriorNPC.kt @@ -49,7 +49,7 @@ class AlKharidWarriorNPC : AbstractNPC { if (entity is Player) { if (target == null) { target = entity - RegionManager.getLocalNpcs(entity, supportRange).forEach { + RegionManager.getLocalNPCs(entity.location, supportRange).forEach { if (it.id == NPCs.AL_KHARID_WARRIOR_18 && !it.properties.combatPulse.isAttacking && it != this) { it.sendChat("Brother, I shall help thee with this infidel!") it.attack(entity) diff --git a/Server/src/main/content/region/desert/alkharid/quest/princealirescue/LadyKeliDialogue.java b/Server/src/main/content/region/desert/alkharid/quest/princealirescue/LadyKeliDialogue.java index 038567099..2c8985dec 100644 --- a/Server/src/main/content/region/desert/alkharid/quest/princealirescue/LadyKeliDialogue.java +++ b/Server/src/main/content/region/desert/alkharid/quest/princealirescue/LadyKeliDialogue.java @@ -64,7 +64,7 @@ public final class LadyKeliDialogue extends DialoguePlugin { case 60: case 100: npc.sendChat("You tricked me, and tied me up, Guards kill this stranger!"); - List npcc = RegionManager.getLocalNpcs(player); + List npcc = RegionManager.getLocalNPCs(player.getLocation()); for (NPC n : npcc) { if (n.getId() == 917) { n.sendChat("Yes M'lady"); diff --git a/Server/src/main/content/region/desert/bandits/handlers/BanditNPC.kt b/Server/src/main/content/region/desert/bandits/handlers/BanditNPC.kt index 29a7809d1..beadf788e 100644 --- a/Server/src/main/content/region/desert/bandits/handlers/BanditNPC.kt +++ b/Server/src/main/content/region/desert/bandits/handlers/BanditNPC.kt @@ -23,8 +23,7 @@ class BanditNPC(id: Int = NPCs.BANDIT_1926, location: Location? = null) : Abstra override fun tick() { if (!inCombat()) { - val players = RegionManager.getLocalPlayers(this, 5) - for (player in players) { + for (player in RegionManager.getLocalPlayers(location, 5)) { if (player.inCombat()) continue if (hasGodItem(player, God.SARADOMIN)) { sendChat("Time to die, Saradominist filth!") @@ -47,7 +46,7 @@ class BanditNPC(id: Int = NPCs.BANDIT_1926, location: Location? = null) : Abstra override fun onImpact(entity: Entity?, state: BattleState?) { if (entity is Player) { - RegionManager.getLocalNpcs(entity, supportRange).forEach { + RegionManager.getLocalNPCs(entity.location, supportRange).forEach { if (it.id == NPCs.BANDIT_1926 && !it.properties.combatPulse.isAttacking && it != this) { it.sendChat("You picked the wrong place to start trouble!") it.attack(entity) diff --git a/Server/src/main/content/region/desert/bedabin/handlers/BedabinPlugin.java b/Server/src/main/content/region/desert/bedabin/handlers/BedabinPlugin.java index a0a2870a9..99411ee68 100644 --- a/Server/src/main/content/region/desert/bedabin/handlers/BedabinPlugin.java +++ b/Server/src/main/content/region/desert/bedabin/handlers/BedabinPlugin.java @@ -47,7 +47,7 @@ public final class BedabinPlugin extends OptionHandler { player.getPacketDispatch().sendMessage("You walk back out the tent."); break; } - player.getDialogueInterpreter().open(834, RegionManager.getNpc(player, 834)); + player.getDialogueInterpreter().open(834, RegionManager.getNpc(player.getLocation(), 834, 16)); break; } break; diff --git a/Server/src/main/content/region/desert/handlers/KalphiteQueenNPC.java b/Server/src/main/content/region/desert/handlers/KalphiteQueenNPC.java index 39dbc9399..b9ec27143 100644 --- a/Server/src/main/content/region/desert/handlers/KalphiteQueenNPC.java +++ b/Server/src/main/content/region/desert/handlers/KalphiteQueenNPC.java @@ -211,7 +211,7 @@ public final class KalphiteQueenNPC extends AbstractNPC { */ private BattleState[] getRangeTargets(Entity e, Entity victim) { List list = new ArrayList<>(20); - for (Entity t : RegionManager.getLocalPlayers(victim, -1 + (int) e.getCenterLocation().getDistance(victim.getLocation()))) { + for (Player t : RegionManager.getLocalPlayers(victim.getLocation(), -1 + (int) e.getCenterLocation().getDistance(victim.getLocation()))) { if (t.isAttackable(e, CombatStyle.RANGE, false)) { list.add(new BattleState(e, t)); } @@ -315,12 +315,10 @@ public final class KalphiteQueenNPC extends AbstractNPC { return; } if (style == CombatStyle.MAGIC) { - BattleState[] states = new BattleState[entity.getViewport().getCurrentPlane().getPlayers().size() + 1]; + List localPlayers = RegionManager.getLocalPlayers(entity.getLocation()); + BattleState[] states = new BattleState[localPlayers.size() + 1]; int index = 1; - if (states.length == 0) { - return; - } - for (Player p : entity.getViewport().getCurrentPlane().getPlayers()) { + for (Player p : localPlayers) { if (p != victim) { states[index++] = new BattleState(entity, p); } diff --git a/Server/src/main/content/region/desert/handlers/KhardianInteractionPlugin.java b/Server/src/main/content/region/desert/handlers/KhardianInteractionPlugin.java index c2dbc4200..89d5dc1ab 100644 --- a/Server/src/main/content/region/desert/handlers/KhardianInteractionPlugin.java +++ b/Server/src/main/content/region/desert/handlers/KhardianInteractionPlugin.java @@ -80,7 +80,7 @@ public final class KhardianInteractionPlugin extends OptionHandler { if (!failed) { player.getSkills().addExperience(Skills.WOODCUTTING, 10, true); } - SceneryBuilder.replace(((Scenery) node), new Scenery(DRY_CACTUS, node.getLocation()), SPAWN_DELAY + RandomFunction.random(RegionManager.getLocalPlayers(player).size() / 2)); + SceneryBuilder.replace(((Scenery) node), new Scenery(DRY_CACTUS, node.getLocation()), SPAWN_DELAY + RandomFunction.random(RegionManager.getLocalPlayers(player.getLocation()).size() / 2)); break; } return true; diff --git a/Server/src/main/content/region/desert/quest/thetouristrap/AnaDialogue.java b/Server/src/main/content/region/desert/quest/thetouristrap/AnaDialogue.java index efb949796..ed8bb63a9 100644 --- a/Server/src/main/content/region/desert/quest/thetouristrap/AnaDialogue.java +++ b/Server/src/main/content/region/desert/quest/thetouristrap/AnaDialogue.java @@ -108,7 +108,7 @@ public final class AnaDialogue extends DialoguePlugin { case 3: player.getInventory().add(TouristTrap.WROUGHT_IRON_KEY, player); player.getPacketDispatch().sendMessage("Ana spots Irena and waves..."); - NPC irena = RegionManager.getNpc(player, 4986); + NPC irena = RegionManager.getNpc(player.getLocation(), 4986, 16); irena.sendChat("Hi Ana!"); quest.setStage(player, 98); end(); diff --git a/Server/src/main/content/region/desert/quest/thetouristrap/DesertGuardDialogue.java b/Server/src/main/content/region/desert/quest/thetouristrap/DesertGuardDialogue.java index 9c9a08367..80de1b845 100644 --- a/Server/src/main/content/region/desert/quest/thetouristrap/DesertGuardDialogue.java +++ b/Server/src/main/content/region/desert/quest/thetouristrap/DesertGuardDialogue.java @@ -307,8 +307,8 @@ public final class DesertGuardDialogue extends DialoguePlugin { * Method used to warn players whom are breaking rules. */ private void warn() { - final List players = RegionManager.getLocalPlayers(this); - for (final Player player : players) { + final List players = RegionManager.getLocalPlayers(location); + for (Player player : players) { if (player.getAttribute("guard-warning", 0) > GameWorld.getTicks() || !player.getZoneMonitor().isInZone("mining camp") || player.inCombat() || !player.getLocation().withinDistance(this.getLocation(), 8)) { continue; } diff --git a/Server/src/main/content/region/desert/quest/thetouristrap/TouristTrapPlugin.java b/Server/src/main/content/region/desert/quest/thetouristrap/TouristTrapPlugin.java index 463d61272..0f0f4a2af 100644 --- a/Server/src/main/content/region/desert/quest/thetouristrap/TouristTrapPlugin.java +++ b/Server/src/main/content/region/desert/quest/thetouristrap/TouristTrapPlugin.java @@ -201,7 +201,7 @@ public final class TouristTrapPlugin extends OptionHandler { GameWorld.getPulser().submit(new Pulse(2, player) { @Override public boolean pulse() { - player.getDialogueInterpreter().open(831, RegionManager.getNpc(player, 831)); + player.getDialogueInterpreter().open(831, RegionManager.getNpc(player.getLocation(), 831, 16)); return true; } }); @@ -1254,7 +1254,7 @@ public final class TouristTrapPlugin extends OptionHandler { @Override public boolean handle(NodeUsageEvent event) { final Player player = event.getPlayer(); - player.getDialogueInterpreter().open(831, RegionManager.getNpc(player, 831)); + player.getDialogueInterpreter().open(831, RegionManager.getNpc(player.getLocation(), 831, 16)); return true; } diff --git a/Server/src/main/content/region/fremennik/diary/FremennikAchievementDiary.kt b/Server/src/main/content/region/fremennik/diary/FremennikAchievementDiary.kt index 51d160974..3621ed8c6 100644 --- a/Server/src/main/content/region/fremennik/diary/FremennikAchievementDiary.kt +++ b/Server/src/main/content/region/fremennik/diary/FremennikAchievementDiary.kt @@ -167,7 +167,7 @@ class FremennikAchievementDiary : DiaryEventHookBase(DiaryType.FREMENNIK) { } override fun onResourceProduced(player: Player, event: ResourceProducedEvent) { - when (player.viewport.region.id) { + when (player.location.regionId) { 10553 -> if (event.source.id in FISHING_SPOTS) { finishTask( player, @@ -204,7 +204,7 @@ class FremennikAchievementDiary : DiaryEventHookBase(DiaryType.FREMENNIK) { } override fun onNpcKilled(player: Player, event: NPCKillEvent) { - when (player.viewport.region.id) { + when (player.location.regionId) { 10042, 10554 -> if (event.npc.id in ROCK_CRABS) { progressIncrementalTask( player, @@ -260,7 +260,7 @@ class FremennikAchievementDiary : DiaryEventHookBase(DiaryType.FREMENNIK) { } override fun onPickedUp(player: Player, event: PickUpEvent) { - when (player.viewport.region.id) { + when (player.location.regionId) { 10810 -> if (event.itemId == Items.SEAWEED_401) { progressIncrementalTask( player, @@ -284,7 +284,7 @@ class FremennikAchievementDiary : DiaryEventHookBase(DiaryType.FREMENNIK) { } override fun onSummoningPointsRecharged(player: Player, event: SummoningPointsRechargeEvent) { - when (player.viewport.region.id) { + when (player.location.regionId) { 10552 -> if (event.obelisk.id == Scenery.SMALL_OBELISK_29944) { finishTask( player, @@ -296,7 +296,7 @@ class FremennikAchievementDiary : DiaryEventHookBase(DiaryType.FREMENNIK) { } override fun onInteracted(player: Player, event: InteractionEvent) { - when (player.viewport.region.id) { + when (player.location.regionId) { 10811 -> if (event.target.id == Scenery.COLLAPSED_TRAP_19233 && inBorders(player, RELLEKKA_HUNTING_AREA) && event.option == "dismantle") { finishTask( player, diff --git a/Server/src/main/content/region/fremennik/rellekka/quest/thefremenniktrials/TFTInteractionListeners.kt b/Server/src/main/content/region/fremennik/rellekka/quest/thefremenniktrials/TFTInteractionListeners.kt index e7b48f9d3..cf5685f96 100644 --- a/Server/src/main/content/region/fremennik/rellekka/quest/thefremenniktrials/TFTInteractionListeners.kt +++ b/Server/src/main/content/region/fremennik/rellekka/quest/thefremenniktrials/TFTInteractionListeners.kt @@ -8,8 +8,6 @@ import core.game.node.entity.player.Player import core.game.node.entity.player.link.diary.DiaryType import core.game.node.entity.player.link.music.MusicEntry import core.game.node.entity.skill.Skills -import content.global.skill.gather.woodcutting.WoodcuttingSkillPulse -import core.game.node.scenery.Scenery import core.game.system.task.Pulse import core.game.world.map.Location import core.game.world.map.RegionManager @@ -23,6 +21,8 @@ import core.game.system.config.ItemConfigParser import core.game.world.GameWorld.Pulser import content.data.Quests import core.game.interaction.QueueStrength +import core.game.node.Node +import org.rs09.consts.Scenery class TFTInteractionListeners : InteractionListener { @@ -74,7 +74,7 @@ class TFTInteractionListeners : InteractionListener { removeItem(player,LOW_ALC_KEG) setAttribute(player,"/save:fremtrials:keg-mixed", true) sendMessage(player,"The cherry bomb in the pipe goes off.") - RegionManager.getLocalEntitys(player).stream().forEach { e -> e.sendChat("What was THAT??") } + RegionManager.getLocalNPCs(player.location).stream().forEach { e -> e.sendChat("What was THAT??") } sendMessage(player,"You mix the kegs together.") } else { player.dialogueInterpreter?.sendDialogue("I can't do this right now. I should create","a distraction.") @@ -204,7 +204,7 @@ class TFTInteractionListeners : InteractionListener { setAttribute(player,"/save:fremtrials:maze-complete",true) DestRoom(2662, 10034, 2668, 10039).getCenter() } - else -> getRandomLocation(player) + else -> getRandomLocation(player, portal) } return@on true } @@ -280,17 +280,15 @@ class TFTInteractionListeners : InteractionListener { return Location((swx + nex) / 2, (swy + ney) / 2).transform(1,0,0) } - fun getRandomLocation(player: Player?): Location{ - var obj: Scenery? = null - - while(obj?.id != 5138) { - val objects = player?.viewport?.chunks?.random()?.random()?.objects - obj = objects?.random()?.random() - if(obj == null || obj.location?.equals(Location(0,0,0))!!){ - continue + fun getRandomLocation(player: Player, portal: Node): Location { + val objects = player.location.region.assembleObjectList(player.location.z) + objects.shuffle() + for (o in objects) { + if (o.id == Scenery.PORTAL_5138) { + return o.location } } - return obj.location + return portal.location } fun hasEquippableItems(player: Player?): Boolean { diff --git a/Server/src/main/content/region/fremennik/waterbirth/handlers/DagannothKingNPC.java b/Server/src/main/content/region/fremennik/waterbirth/handlers/DagannothKingNPC.java index 65f5d4e43..7b5dc7809 100644 --- a/Server/src/main/content/region/fremennik/waterbirth/handlers/DagannothKingNPC.java +++ b/Server/src/main/content/region/fremennik/waterbirth/handlers/DagannothKingNPC.java @@ -70,7 +70,7 @@ public final class DagannothKingNPC extends AbstractNPC { state.setEstimatedHit(RandomFunction.random(type.getMaxHit() - 5, type.getMaxHit())); } if (type != DagType.REX && RandomFunction.random(5) <= 2) { - List players = RegionManager.getLocalPlayers(this, 9); + List players = RegionManager.getLocalPlayers(location, 9); if (players.size() <= 1) { return; } diff --git a/Server/src/main/content/region/fremennik/waterbirth/handlers/WaterBirthDungeonZone.java b/Server/src/main/content/region/fremennik/waterbirth/handlers/WaterBirthDungeonZone.java index 1a5a35235..6fa067f6a 100644 --- a/Server/src/main/content/region/fremennik/waterbirth/handlers/WaterBirthDungeonZone.java +++ b/Server/src/main/content/region/fremennik/waterbirth/handlers/WaterBirthDungeonZone.java @@ -17,6 +17,7 @@ import core.game.node.entity.combat.CombatStyle; import core.game.node.entity.npc.AbstractNPC; import core.game.node.entity.npc.NPC; import core.game.node.entity.player.Player; +import core.game.node.item.GroundItemManager; import core.game.node.scenery.SceneryBuilder; import core.game.system.task.Pulse; import core.game.world.GameWorld; @@ -71,7 +72,7 @@ public final class WaterBirthDungeonZone extends MapZone implements Plugin npcs = RegionManager.getLocalNpcs(location, 0); + List npcs = RegionManager.getLocalNPCs(location, 0); NPC npc = null; if (npcs.size() != 0) { npc = npcs.get(0); @@ -243,10 +244,10 @@ public final class WaterBirthDungeonZone extends MapZone implements Plugin 0) { + if (!RegionManager.getLocalPlayers(location, 0).isEmpty()) { return true; } - if (RegionManager.getRegionPlane(location).getItem(Items.PET_ROCK_3695,location,player) != null) { + if (GroundItemManager.get(Items.PET_ROCK_3695, location, player) != null) { return true; } return false; @@ -411,9 +412,9 @@ public final class WaterBirthDungeonZone extends MapZone implements Plugin System.currentTimeMillis()) { end(); - for (NPC npc : RegionManager.getLocalNpcs(player.getLocation(), 8)) { + for (NPC npc : RegionManager.getLocalNPCs(player.getLocation(), 8)) { if (!npc.getProperties().getCombatPulse().isAttacking() && npc.getId() == 32) { npc.sendChat("Hey! Get your hands off there!"); npc.getProperties().getCombatPulse().attack(player); diff --git a/Server/src/main/content/region/kandarin/ardougne/quest/hazeelcult/AlomoneDialogue.kt b/Server/src/main/content/region/kandarin/ardougne/quest/hazeelcult/AlomoneDialogue.kt index e6c54d22a..4f94a404e 100644 --- a/Server/src/main/content/region/kandarin/ardougne/quest/hazeelcult/AlomoneDialogue.kt +++ b/Server/src/main/content/region/kandarin/ardougne/quest/hazeelcult/AlomoneDialogue.kt @@ -8,6 +8,7 @@ import core.game.dialogue.* import core.game.node.entity.npc.NPC import core.game.node.entity.player.Player import core.game.world.map.Location +import core.game.world.map.RegionManager import core.plugin.Initializable import core.tools.END_DIALOGUE import org.rs09.consts.* @@ -100,7 +101,7 @@ class AlomoneDialogue(player: Player? = null) : DialoguePlugin(player) { 26 -> { end() removeItem(player, HAZEEL_SCROLL) - val hazeel = player.findNpcInViewport(NPCs.HAZEEL_892) + val hazeel = player.findNearbyNPC(NPCs.HAZEEL_892) hazeel?.clear() removeAttribute(player,"hazeelSpawned") finishQuest(player, Quests.HAZEEL_CULT) @@ -129,13 +130,10 @@ class AlomoneDialogue(player: Player? = null) : DialoguePlugin(player) { // this has the potential to remove other players' hazeels, but the player could still complete the quest by either continuing their // active dialogue or speaking to alomone, and just dealing with the fact that their hazeel is not on the map. relogging and restarting // the dialogue would get these players a new hazeel. - private fun Player.findNpcInViewport(npcId: Int): NPC? { - val viewport = viewport ?: return null - for (plane in viewport.viewingPlanes) { - for (npc in plane.npcs) { - if (npc.id == npcId) { - return npc - } + private fun Player.findNearbyNPC(npcId: Int): NPC? { + for (npc in RegionManager.getLocalNPCs(location)) { + if (npc.id == npcId) { + return npc } } return null diff --git a/Server/src/main/content/region/kandarin/ardougne/quest/hazeelcult/HazeelCultListeners.kt b/Server/src/main/content/region/kandarin/ardougne/quest/hazeelcult/HazeelCultListeners.kt index cd6ac6007..53ed92a3d 100644 --- a/Server/src/main/content/region/kandarin/ardougne/quest/hazeelcult/HazeelCultListeners.kt +++ b/Server/src/main/content/region/kandarin/ardougne/quest/hazeelcult/HazeelCultListeners.kt @@ -173,18 +173,16 @@ class HazeelCultListeners : InteractionListener { } // chest with hazeel scroll. unlocks with key, opens, and contains the scroll. - onUseWith(IntType.SCENERY, CHEST_KEY, Scenery.CHEST_2856) { player, _, _ -> + onUseWith(IntType.SCENERY, CHEST_KEY, Scenery.CHEST_2856) { player, _, node -> sendMessage(player, "You unlock the chest.") animate(player, Animations.HUMAN_OPEN_CHEST_536) - addScenery(Scenery.CHEST_2857,CHEST_LOCATION, 2, 10) - removeScenery(core.game.node.scenery.Scenery(Scenery.CHEST_2856, Location(2565, 3272, 2))) + replaceScenery(node.asScenery(), Scenery.CHEST_2857, 20) return@onUseWith true } // chest with hazeel scroll. unlocks with key, opens, and contains the scroll. - on(Scenery.CHEST_2857, IntType.SCENERY, "Close") { _, _ -> - addScenery(Scenery.CHEST_2856, CHEST_LOCATION, 2, 10) - removeScenery(core.game.node.scenery.Scenery(Scenery.CHEST_2857, CHEST_LOCATION)) + on(Scenery.CHEST_2857, IntType.SCENERY, "Close") { _, node -> + replaceScenery(node.asScenery(), Scenery.CHEST_2856, 20) return@on true } diff --git a/Server/src/main/content/region/kandarin/ardougne/quest/plaguecity/dialogue/mourners/MournerKidnapDialogue.kt b/Server/src/main/content/region/kandarin/ardougne/quest/plaguecity/dialogue/mourners/MournerKidnapDialogue.kt index 671b7d38b..a75ae7827 100644 --- a/Server/src/main/content/region/kandarin/ardougne/quest/plaguecity/dialogue/mourners/MournerKidnapDialogue.kt +++ b/Server/src/main/content/region/kandarin/ardougne/quest/plaguecity/dialogue/mourners/MournerKidnapDialogue.kt @@ -56,7 +56,7 @@ class MournerKidnapDialogueFile : DialogueFile(){ // Figure out who we are. It needs to be this since we can enter this dialogue from a door // Do this for the first time regardless of where we are if (stage == 0) { - RegionManager.getLocalNpcs(player!!, 2).forEach { + RegionManager.getLocalNPCs(player!!.location, 2).forEach { if (it.id == NPCs.MOURNER_3216) { closeMourner = it resetFace(closeMourner!!) @@ -78,7 +78,7 @@ class MournerKidnapDialogueFile : DialogueFile(){ 2 -> npcl(FacialExpression.HALF_WORRIED, " This is highly irregular. Please wait...").also { stage++ } 3 -> { // Look further for the other one - RegionManager.getLocalNpcs(player!!, 10).forEach { + RegionManager.getLocalNPCs(player!!.location, 10).forEach { if (it.id == NPCs.MOURNER_3216 && it != closeMourner){ farMourner = it resetFace(farMourner!!) diff --git a/Server/src/main/content/region/kandarin/feldip/quest/chompybird/BloatedToadNPC.kt b/Server/src/main/content/region/kandarin/feldip/quest/chompybird/BloatedToadNPC.kt index 49a074680..36d328445 100644 --- a/Server/src/main/content/region/kandarin/feldip/quest/chompybird/BloatedToadNPC.kt +++ b/Server/src/main/content/region/kandarin/feldip/quest/chompybird/BloatedToadNPC.kt @@ -43,7 +43,7 @@ class BloatedToadNPC : AbstractNPC { super.handleTickActions() if (ticksToLive-- <= 0) { - val toDamage = RegionManager.getLocalEntitys(this.location, 2) + val toDamage = RegionManager.getLocalEntities(this.location, 2) for (entity in toDamage) { if (entity == this) continue diff --git a/Server/src/main/content/region/kandarin/quest/dwarfcannon/dmc/DMCHandler.java b/Server/src/main/content/region/kandarin/quest/dwarfcannon/dmc/DMCHandler.java index 9b7d2363c..d66f0b062 100644 --- a/Server/src/main/content/region/kandarin/quest/dwarfcannon/dmc/DMCHandler.java +++ b/Server/src/main/content/region/kandarin/quest/dwarfcannon/dmc/DMCHandler.java @@ -6,7 +6,6 @@ import core.game.node.entity.combat.ImpactHandler.HitsplatType; import core.game.node.entity.impl.Projectile; import core.game.node.entity.npc.NPC; import core.game.node.entity.player.Player; -import core.game.node.entity.player.link.audio.Audio; import core.game.node.entity.skill.Skills; import core.game.node.item.Item; import core.game.node.scenery.Scenery; @@ -51,10 +50,10 @@ public final class DMCHandler implements LogoutListener { */ private DMCRevolution direction = DMCRevolution.NORTH; - private CannonTimer timer; + private CannonTimer timer; public DMCHandler() { - this.player = null; + this.player = null; } /** @@ -78,7 +77,7 @@ public final class DMCHandler implements LogoutListener { Location l = cannon.getLocation().transform(1, 1, 0); playGlobalAudio(l, Sounds.MCANNON_TURN_2877); direction = DMCRevolution.values()[(direction.ordinal() + 1) % DMCRevolution.values().length]; - for (NPC npc : RegionManager.getLocalNpcs(l, 10)) { + for (NPC npc : RegionManager.getLocalNPCs(l, 10)) { if (direction.isInSight(npc.getLocation().getX() - l.getX(), npc.getLocation().getY() - l.getY()) && npc.isAttackable(player, CombatStyle.RANGE, false) && CombatSwingHandler.isProjectileClipped(npc, l, false)) { int speed = (int) (25 + (l.getDistance(npc.getLocation()) * 10)); Projectile.create(l, npc.getLocation(), 53, 40, 36, 20, speed, 0, 128).send(); @@ -106,8 +105,8 @@ public final class DMCHandler implements LogoutListener { return; } if (timer.isFiring()) { - timer.setFiring(false); - return; + timer.setFiring(false); + return; } if (cannonballs < 1) { int amount = player.getInventory().getAmount(new Item(2)); @@ -127,7 +126,7 @@ public final class DMCHandler implements LogoutListener { player.sendMessage("Your cannon is already fully loaded."); } } - timer.setFiring(true); + timer.setFiring(true); } /** @@ -138,7 +137,7 @@ public final class DMCHandler implements LogoutListener { return; } player.sendMessage("Your cannon has " + (decay ? "decayed" : "been destroyed") + "!"); - for (Player p : RegionManager.getLocalPlayers(player)) { + for (Player p : RegionManager.getLocalPlayers(player.getLocation())) { p.getPacketDispatch().sendPositionedGraphic(189, 0, 1, cannon.getLocation()); } clear(false); @@ -194,15 +193,15 @@ public final class DMCHandler implements LogoutListener { player.getPacketDispatch().sendMessage("You add the furnace."); SceneryBuilder.remove(object); handler.configure(SceneryBuilder.add(object = object.transform(6))); - handler.timer = (CannonTimer) spawnTimer ("dmc:timer", handler); - registerTimer (player, handler.timer); + handler.timer = (CannonTimer) spawnTimer ("dmc:timer", handler); + registerTimer (player, handler.timer); return true; } playGlobalAudio(player.getLocation(), Sounds.MCANNON_SETUP_2876); - if(count != 0) { - SceneryBuilder.remove(object); - SceneryBuilder.add(object = object.transform(object.getId() + 1)); - } + if(count != 0) { + SceneryBuilder.remove(object); + SceneryBuilder.add(object = object.transform(object.getId() + 1)); + } return ++count == 4; } }); diff --git a/Server/src/main/content/region/kandarin/seers/diary/SeersVillageAchievementDiary.kt b/Server/src/main/content/region/kandarin/seers/diary/SeersVillageAchievementDiary.kt index bd9e90188..1581baeac 100644 --- a/Server/src/main/content/region/kandarin/seers/diary/SeersVillageAchievementDiary.kt +++ b/Server/src/main/content/region/kandarin/seers/diary/SeersVillageAchievementDiary.kt @@ -109,7 +109,7 @@ class SeersVillageAchievementDiary : DiaryEventHookBase(DiaryType.SEERS_VILLAGE) ) } - when (player.viewport.region.id) { + when (player.location.regionId) { 10805 -> if (event.itemId == Items.FLAX_1779) { progressIncrementalTask( player, @@ -192,7 +192,7 @@ class SeersVillageAchievementDiary : DiaryEventHookBase(DiaryType.SEERS_VILLAGE) } override fun onNpcKilled(player: Player, event: NPCKillEvent) { - when (player.viewport.region.id) { + when (player.location.regionId) { 10906 -> if (event.npc.id in WORKSHOP_ELEMENTALS) { progressFlaggedTask( player, @@ -316,7 +316,7 @@ class SeersVillageAchievementDiary : DiaryEventHookBase(DiaryType.SEERS_VILLAGE) } override fun onItemPurchasedFromShop(player: Player, event: ItemShopPurchaseEvent) { - if (event.itemId == Items.CANDLE_36 && player.viewport.region.id == 11061) { + if (event.itemId == Items.CANDLE_36 && player.location.regionId == 11061) { finishTask( player, DiaryLevel.EASY, @@ -344,7 +344,7 @@ class SeersVillageAchievementDiary : DiaryEventHookBase(DiaryType.SEERS_VILLAGE) } override fun onPrayerPointsRecharged(player: Player, event: PrayerPointsRechargeEvent) { - if (player.viewport.region.id == 10806) { + if (player.location.regionId == 10806) { if (event.altar.id == Scenery.ALTAR_409 || event.altar.id == Scenery.ALTAR_19145) { finishTask( player, diff --git a/Server/src/main/content/region/kandarin/seers/handlers/RangingGuildPlugin.java b/Server/src/main/content/region/kandarin/seers/handlers/RangingGuildPlugin.java index 5589a4d69..b11c6e164 100644 --- a/Server/src/main/content/region/kandarin/seers/handlers/RangingGuildPlugin.java +++ b/Server/src/main/content/region/kandarin/seers/handlers/RangingGuildPlugin.java @@ -698,7 +698,7 @@ public final class RangingGuildPlugin extends OptionHandler { case 17: ClimbActionHandler.climb(player, null, Location.create(2668, 3427, 2)); player.getInterfaceManager().close(); - List npcs = RegionManager.getLocalNpcs(Location.create(2668, 3427, 2)); + List npcs = RegionManager.getLocalNPCs(Location.create(2668, 3427, 2)); String dir = ""; for (NPC n : npcs) { if (n.getId() >= 684 && n.getId() <= 687) { diff --git a/Server/src/main/content/region/karamja/diary/KaramjaAchievementDiary.kt b/Server/src/main/content/region/karamja/diary/KaramjaAchievementDiary.kt index cda31648c..927198ce5 100644 --- a/Server/src/main/content/region/karamja/diary/KaramjaAchievementDiary.kt +++ b/Server/src/main/content/region/karamja/diary/KaramjaAchievementDiary.kt @@ -83,7 +83,7 @@ class KaramjaAchievementDiary : DiaryEventHookBase(DiaryType.KARAMJA) { } override fun onResourceProduced(player: Player, event: ResourceProducedEvent) { - when (player.viewport.region.id) { + when (player.location.regionId) { 10802 -> if (event.itemId == Items.GOLD_ORE_444) { finishTask( player, @@ -148,7 +148,7 @@ class KaramjaAchievementDiary : DiaryEventHookBase(DiaryType.KARAMJA) { } override fun onNpcKilled(player: Player, event: NPCKillEvent) { - when (player.viewport.region.id) { + when (player.location.regionId) { 10899, 10900 -> if (event.npc.id in METAL_DRAGONS) { finishTask( player, diff --git a/Server/src/main/content/region/karamja/tzhaar/handlers/TzhaarFightPitsPlugin.java b/Server/src/main/content/region/karamja/tzhaar/handlers/TzhaarFightPitsPlugin.java index 5070b76ef..893a2bb5e 100644 --- a/Server/src/main/content/region/karamja/tzhaar/handlers/TzhaarFightPitsPlugin.java +++ b/Server/src/main/content/region/karamja/tzhaar/handlers/TzhaarFightPitsPlugin.java @@ -8,6 +8,7 @@ import java.util.Random; import core.game.component.Component; import core.game.container.impl.EquipmentContainer; import core.game.activity.ActivityPlugin; +import core.game.world.map.RegionChunk; import core.plugin.Initializable; import core.game.dialogue.FacialExpression; import core.game.node.entity.skill.Skills; @@ -30,6 +31,7 @@ import core.game.world.map.RegionManager; import core.game.world.map.zone.ZoneBorders; import core.game.world.map.zone.ZoneRestriction; import core.tools.RandomFunction; +import org.rs09.consts.NPCs; import static core.api.ContentAPIKt.*; @@ -145,7 +147,15 @@ public final class TzhaarFightPitsPlugin extends ActivityPlugin { lastVictor.getPacketDispatch().sendString("Current Champion: " + getChampionName(), INTERFACE_ID, 0); resetDamagePulse(lastVictor); } - RegionManager.forId(9552).getPlanes()[0].getNpcs().get(0).setAttribute("fp_champn", getChampionName()); + RegionChunk[][][] chunks = RegionManager.forId(9552).getChunks(); + for (int x = 0; x < 8; x++) { + for (int y = 0; y < 8; y++) { + List npcs = chunks[x][y][0].getNpcs(); + for (NPC npc : npcs) { + npc.setAttribute("fp_champn", getChampionName()); + } + } + } } minutes = 0; } @@ -180,10 +190,15 @@ public final class TzhaarFightPitsPlugin extends ActivityPlugin { * Resets the last victor. */ private void resetLastVictor() { - List npcs = new ArrayList<>(RegionManager.forId(9552).getPlanes()[0].getNpcs()); - for (NPC n : npcs) { - if (n.getId() == 2734 || n.getId() == 2739) { - n.clear(); + RegionChunk[][][] chunks = RegionManager.forId(9552).getChunks(); + for (int x = 0; x < 8; x++) { + for (int y = 0; y < 8; y++) { + List npcs = chunks[x][y][0].getNpcs(); + for (NPC npc : npcs) { + if (npc.getId() == NPCs.TZ_KIH_2734 || npc.getId() == NPCs.TOK_XIL_2739) { + npc.clear(); + } + } } } if (lastVictor == null || !lastVictor.isActive()) { @@ -355,7 +370,7 @@ public final class TzhaarFightPitsPlugin extends ActivityPlugin { } int size = (LOBBY_PLAYERS.size() + WAR_PLAYERS.size()) - 1; if (!WAR_PLAYERS.isEmpty()) { - setVarp(WAR_PLAYERS.get(0), 560, size); + setVarp(WAR_PLAYERS.get(0), 560, size); } for (Iterator it = LOBBY_PLAYERS.iterator(); it.hasNext();) { Player p = it.next(); @@ -367,7 +382,7 @@ public final class TzhaarFightPitsPlugin extends ActivityPlugin { } p.getSkullManager().setSkullCheckDisabled(true); p.getSkullManager().setWilderness(true); - setVarp(p, 560, size); + setVarp(p, 560, size); p.getProperties().setTeleportLocation(getZoneDestination()); p.getInteraction().set(Option._P_ATTACK); tokkulAmount += p.getProperties().getCurrentCombatLevel(); @@ -449,7 +464,7 @@ public final class TzhaarFightPitsPlugin extends ActivityPlugin { */ public static void sendPlayersRemaining(int value) { for (Player p : WAR_PLAYERS) { - setVarp(p, 560, value); + setVarp(p, 560, value); } } diff --git a/Server/src/main/content/region/misthalin/draynor/dialogue/DraynorBankGuard.java b/Server/src/main/content/region/misthalin/draynor/dialogue/DraynorBankGuard.java index 13275ac76..611737fd1 100644 --- a/Server/src/main/content/region/misthalin/draynor/dialogue/DraynorBankGuard.java +++ b/Server/src/main/content/region/misthalin/draynor/dialogue/DraynorBankGuard.java @@ -1,6 +1,6 @@ package content.region.misthalin.draynor.dialogue; -import core.game.activity.ActivityManager; +import content.region.misthalin.draynor.handlers.DBRCutscene; import core.game.dialogue.DialoguePlugin; import core.game.dialogue.FacialExpression; import core.game.node.entity.npc.NPC; @@ -8,6 +8,8 @@ import core.game.node.entity.player.Player; import core.plugin.Initializable; import core.game.node.item.Item; +import static core.api.ContentAPIKt.getAttribute; + /** * Represents the dialogue plugin used for the draynor bank guard npc. * @author 'Vexia @@ -15,7 +17,6 @@ import core.game.node.item.Item; */ @Initializable public final class DraynorBankGuard extends DialoguePlugin { - /** * Represents the coins item needed to re-watch the recording. */ @@ -55,7 +56,7 @@ public final class DraynorBankGuard extends DialoguePlugin { public boolean handle(int interfaceId, int buttonId) { switch (stage) { case 0: - if (!player.getSavedData().getGlobalData().isDraynorRecording()) { + if (!getAttribute(player, DBRCutscene.HAS_SEEN_RECORDING, false)) { interpreter.sendOptions("Select an option", "Can I deposit my stuff here?", "That wall doesn't look very good.", "Sorry, I don't want anything."); stage = 1; } else { @@ -325,7 +326,7 @@ public final class DraynorBankGuard extends DialoguePlugin { */ private void startRecording(Player player) { end(); - ActivityManager.start(player, "dbr cutscene", false); + new DBRCutscene(player).start(); } @Override diff --git a/Server/src/main/content/region/misthalin/draynor/handlers/DBRCutscenePlugin.java b/Server/src/main/content/region/misthalin/draynor/handlers/DBRCutscenePlugin.java deleted file mode 100644 index 78c30fa3e..000000000 --- a/Server/src/main/content/region/misthalin/draynor/handlers/DBRCutscenePlugin.java +++ /dev/null @@ -1,896 +0,0 @@ -package content.region.misthalin.draynor.handlers; - -import core.game.component.Component; -import core.game.activity.ActivityPlugin; -import core.game.activity.CutscenePlugin; -import core.game.global.action.DoorActionHandler; -import core.plugin.Initializable; -import core.game.node.entity.skill.Skills; -import core.game.node.Node; -import core.game.node.entity.Entity; -import core.game.node.entity.combat.CombatStyle; -import core.game.node.entity.combat.ImpactHandler.HitsplatType; -import core.game.node.entity.impl.Projectile; -import core.game.node.entity.npc.AbstractNPC; -import core.game.node.entity.npc.NPC; -import core.game.node.entity.player.Player; -import core.game.node.entity.player.link.SpellBookManager; -import core.game.node.item.GroundItem; -import core.game.node.item.GroundItemManager; -import core.game.node.item.Item; -import core.game.node.item.ItemPlugin; -import core.game.node.scenery.Scenery; -import core.game.node.scenery.SceneryBuilder; -import core.game.system.task.Pulse; -import core.game.world.GameWorld; -import core.game.world.map.Direction; -import core.game.world.map.Location; -import core.game.world.map.RegionManager; -import core.game.world.map.build.DynamicRegion; -import core.game.world.map.path.Pathfinder; -import core.game.world.update.flag.context.Animation; -import core.game.world.update.flag.context.Graphics; -import core.net.packet.PacketRepository; -import core.net.packet.context.CameraContext; -import core.net.packet.context.CameraContext.CameraType; -import core.net.packet.out.CameraViewPacket; -import core.plugin.Plugin; -import core.plugin.ClassScanner; - -/** - * Represents the draynor bank robbery cutscene plugin. - * - * @author 'Vexia - * @version 1.0 - */ -@Initializable -public final class DBRCutscenePlugin extends CutscenePlugin { - - /** - * Represents the cracked component. - */ - private static final Component CRACKED = new Component(385); - - /** - * Represents the stealing animation used for cool mom. - */ - private static final Animation STEAL_ANIMATION = new Animation(832); - - /** - * Represents the casting animation. - */ - private static final Animation CAST_ANIMATION = new Animation(1167); - - /** - * Represents the telekentic animation to use. - */ - private static final Animation TELEKENTIC_ANIMATION = new Animation(723); - - /** - * Represents the telekentic graphics. - */ - private static final Graphics TELEKENTIC_GRAPHIC = new Graphics(142, 96); - - /** - * Represents the casting animation. - */ - private static final Animation SECOND_CAST_ANIMATION = new Animation(1167); - - /** - * Represents the death animation to use. - */ - private static final Animation DEATH_ANIMATION = new Animation(2553); - - /** - * Represents the graphics to use. - */ - private static final Graphics SHOCK_GRAPHIC = new Graphics(432, 0, 0); - - /** - * Represents the teleport animation to use. - */ - private static final Animation TELEPORT_ANIMATION = new Animation(1816); - - /** - * Represents the graphics to use for teleother. - */ - private static final Graphics[] TELE_OTHERS = new Graphics[] { new Graphics(343), new Graphics(342) }; - - /** - * Represents the pick up animation to use. - */ - private static final Animation PICK_UP_ANIMATION = new Animation(827); - - /** - * Represents the hitting animation. - */ - private static final Animation HIT_ANIMATION = new Animation(401); - - /** - * Represents the blocking animation. - */ - private static final Animation BLOCK_ANIMATION = new Animation(425); - - /** - * Represents the thunder animation. - */ - private static final Animation THUNDER_ANIMATION = new Animation(811); - - /** - * Represents the purple graphics. - */ - private static final Graphics PURPLE_GRAPHIC = new Graphics(301, 100); - - /** - * Represents the thunder graphic. - */ - private static final Graphics THUNDER_GRAPHIC = new Graphics(76); - - /** - * Represents the animation to use for the wise old man. - */ - private static final Animation WISE_JUMP = new Animation(2555); - - /** - * Represents the market guard jump animation. - */ - private static final Animation GUARD_JUMP = new Animation(2556); - - /** - * Represents the stomp animation. - */ - private static final Animation STOMP_ANIM = new Animation(1820); - - /** - * Represents the ashes item. - */ - private static final Item ASHES = new Item(592); - - /** - * Represents the coins item. - */ - private static final Item COINS = new Item(995, 60); - - /** - * Represents the party hat item. - */ - private static final Item PARTY_HAT = new Item(2422); - - /** - * Represents the recording pulse. - */ - private final RecordingPulse recordingPulse = new RecordingPulse(); - - /** - * Represents the npcs to use in the cutscene. - */ - private static final NPC[] NPCS = new NPC[] { - /** cool mom */ - NPC.create(2579, Location.create(2120, 4916, 0), Direction.NORTH), - /** olivia */ - NPC.create(2572, Location.create(2117, 4913, 0), Direction.WEST), - /** market guard */ - NPC.create(2571, Location.create(13, 48, 0), Direction.WEST), - /** purepker */ - NPC.create(2575, new Location(2136, 4914, 0), Direction.WEST), - /** first banker */ - NPC.create(2568, new Location(2130, 4909, 0), Direction.EAST), - /** second last banker. */ - NPC.create(2569, new Location(2130, 4907, 0)), - /** last banker */ - NPC.create(2570, new Location(2130, 4906, 0)), - /** efinlocks */ - NPC.create(2578, new Location(2133, 4908, 0)), - /** 1337sp34kr */ - NPC.create(2577, new Location(2134, 4906, 0), Direction.WEST), - /** quitedoll */ - NPC.create(2576, new Location(2133, 4906, 0), Direction.EAST), - /** oldman */ - NPC.create(2566, new Location(2128, 4915, 0), Direction.SOUTH) }; - - /** - * Constructs a new {@code DBRCutscenePlugin} {@code Object}. - */ - public DBRCutscenePlugin() { - super("dbr cutscene"); - } - - /** - * Constructs a new {@code DBRCutscenePlugin} {@code Object}. - * - * @param player - * the player. - */ - public DBRCutscenePlugin(final Player player) { - super("dbr cutscene"); - this.player = player; - } - - @Override - public void open() { - setNpcs(); - GameWorld.getPulser().submit(recordingPulse); - player.lock(); - player.getLocks().lockMovement(10000000); - camera(27, 45, -14, 2, 700, 100); - } - - @Override - public ActivityPlugin newInstance(Player p) throws Throwable { - return new DBRCutscenePlugin(p); - } - - @Override - public void register() { - super.register(); - try { - new DBRecordingNPC().newInstance(null); - } catch (Throwable e) { - e.printStackTrace(); - } - ClassScanner.definePlugin(new BluePhatItem()); - } - - @Override - public Location getSpawnLocation() { - return null; - } - - @Override - public Location getStartLocation() { - return base.transform(17, 51, 0); - } - - @Override - public void configure() { - region = DynamicRegion.create(8524); - setRegionBase(); - registerRegion(region.getId()); - player.getDialogueInterpreter().sendPlainMessage(true, "You close your eyes and watch the recording..."); - } - - @Override - public void end() { - super.end(); - clearNpcs(); - player.getSavedData().getGlobalData().setDraynorRecording(true); - player.getDialogueInterpreter().sendDialogue("End of recording."); - } - - /** - * Gets the cool mom npc. - * - * @return the cool mom npc. - */ - public NPC getCoolMom() { - return getNpc(2579); - } - - /** - * Gets the olivia npc. - * - * @return the olivia npc. - */ - public NPC getOlivia() { - return getNpc(2572); - } - - /** - * Gets the market guard npc. - * - * @return the market guard. - */ - public NPC getMarketGuard() { - return getNpc(2571); - } - - /** - * Gets the pure pker npc. - * - * @return the pure pker npc. - */ - public NPC getPurePker() { - return getNpc(2575); - } - - /** - * Gets the elfinlocks npc. - * - * @return the elfinocks npc. - */ - public NPC getElfinocks() { - return getNpc(2578); - } - - /** - * Gets the 1337sp34kr npc. - * - * @return the 1337sp34kr npc. - */ - public NPC get1337() { - return getNpc(2577); - } - - /** - * Gets the quitedoll npc. - * - * @return the quitedoll npc. - */ - public NPC getQuiteDoll() { - return getNpc(2576); - } - - /** - * Gets the wise old man npc. - * - * @return the wise old man npc. - */ - public NPC getWiseOldMan() { - int size = region.getPlanes()[0].getNpcs().size(); - return region.getPlanes()[0].getNpcs().get(size - 1); - } - - /** - * Gets the first banker. - * - * @return the first banker. - */ - public NPC getFirstBanker() { - return getNpc(2568); - } - - /** - * Gets the second banker. - * - * @return the second banker. - */ - public NPC getSecondBanker() { - return getNpc(2569); - } - - /** - * Gets the last banker. - * - * @return the last banker. - */ - public NPC getLastBanker() { - return getNpc(2570); - } - - /** - * Method used to walk an npc. - * - * @param npc - * the npc. - * @param location - * the location. - */ - private void walk(NPC npc, final Location location) { - Pathfinder.find(npc, location, true, Pathfinder.DUMB).walk(npc); - } - - /** - * Method used to kill an npc. - * - * @param npc - * the npc. - */ - private void die(NPC npc) { - npc.getAnimator().reset(); - npc.animate(DEATH_ANIMATION); - npc.graphics(SHOCK_GRAPHIC); - npc.getImpactHandler().manualHit(npc, npc.getSkills().getLifepoints(), HitsplatType.NORMAL); - } - - /** - * Method used to cast a shock. - * - * @param target - * the target. - */ - private void castShock(Node target) { - getWiseOldMan().animate(CAST_ANIMATION); - getWiseOldMan().graphics(new Graphics(433)); - if (target instanceof Entity) { - Projectile.create(getWiseOldMan(), (Entity) target, 434).send(); - } else { - Projectile projectile = Projectile.create(getWiseOldMan(), null, 434, 30, 30, 41, 140, 0, 0); - projectile.setEndLocation((Location) target); - projectile.send(); - } - } - - /** - * Method used to cast the telekinetic grab spell. - */ - private void castTelekinetic() { - getWiseOldMan().animate(TELEKENTIC_ANIMATION); - getWiseOldMan().graphics(TELEKENTIC_GRAPHIC); - getWiseOldMan().getSkills().setStaticLevel(Skills.MAGIC, 99); - getWiseOldMan().getSkills().setLevel(Skills.MAGIC, 99); - SpellBookManager.SpellBook.MODERN.getSpell(19).cast(getWiseOldMan(), GroundItemManager.get(PARTY_HAT.getId(), base.transform(20, 44, 0), player)); - } - - /** - * Method used to cast the thunder shock. - */ - private void castThunderShock() { - getWiseOldMan().animate(THUNDER_ANIMATION); - getWiseOldMan().graphics(PURPLE_GRAPHIC); - } - - /** - * Method used to handle the camera. - * - * @param x - * the x offset. - * @param y - * the y offset. - * @param xRot - * the xRotation. - * @param yRot - * the yRotation. - * @Param height the height. - */ - private void camera(int x, int y, int xRot, int yRot, int height, int speed) { - Location loc = base.transform(x, y, 0); - PacketRepository.send(CameraViewPacket.class, new CameraContext(player, CameraType.POSITION, loc.getX(), loc.getY(), height, 1, speed)); - PacketRepository.send(CameraViewPacket.class, new CameraContext(player, CameraType.ROTATION, loc.getX() + xRot, loc.getY() + yRot, height, 1, speed)); - } - - /** - * Method used to set npcs and their locations. - */ - private void setNpcs() { - for (NPC n : NPCS) { - n = NPC.create(n.getId(), n.getLocation(), n.getDirection()); - n.setLocation(base.transform(n.getLocation().getLocalX(), n.getLocation().getLocalY(), 0)); - n.setRespawn(false); - n.init(); - if (n.getName().equals("Banker")) { - n.getSkills().setStaticLevel(Skills.HITPOINTS, 15); - n.getSkills().setLevel(Skills.HITPOINTS, 15); - } - n.setWalks(false); - } - } - - /** - * Method used to clear all the npcs. - */ - private void clearNpcs() { - GameWorld.getPulser().submit(new Pulse(5) { - @Override - public boolean pulse() { - for (NPC n : region.getPlanes()[0].getNpcs()) { - n.clear(); - } - return true; - } - - }); - } - - /** - * Gets the npc. - * - * @param id - * the id. - * @return the npc. - */ - private NPC getNpc(int id) { - for (NPC n : region.getPlanes()[0].getNpcs()) { - if (n.getId() == id) { - return n; - } - } - return null; - } - - /** - * Represents the pulse used during recording. - * - * @author 'Vexia - * @version 1.0 - */ - public final class RecordingPulse extends Pulse { - - /** - * Represents the counter. - */ - private int counter; - - /** - * Constructs a new {@code FightPulse} {@code Object}. - */ - public RecordingPulse() { - super(1, player); - } - - @Override - public boolean pulse() { - switch (counter++) { - case 1: - getCoolMom().animate(STEAL_ANIMATION); - getMarketGuard().getSkills().setStaticLevel(Skills.HITPOINTS, 99); - getMarketGuard().getSkills().setLifepoints(99); - getMarketGuard().sendChat("Hey! Get your hands off there!"); - getCoolMom().getLocks().lockMovement(1000); - getMarketGuard().getProperties().getCombatPulse().attack(getCoolMom()); - break; - case 8: - walk(getPurePker(), base.transform(20, 45, 0)); - break; - case 10: - camera(27, 43, -37, -3, 600, 5); - break; - case 12: - getQuiteDoll().sendChat("Thx"); - break; - case 14: - walk(getQuiteDoll(), base.transform(20, 42, 0)); - get1337().sendChat("Yw"); - getPurePker().faceLocation(base.getLocation().transform(19, 45, 0)); - break; - case 16: - getElfinocks().sendChat("Buying 2k nats - no noob offers"); - break; - case 20: - get1337().sendChat("I h4ve n4ts"); - break; - case 22: - getElfinocks().sendChat("You sell?"); - break; - case 25: - get1337().sendChat("Tr4de"); - get1337().faceTemporary(getElfinocks(), 5); - walk(get1337(), base.transform(21, 43, 0)); - break; - case 26: - camera(23, 49, -37, 0, 500, 3); - break; - case 28: - walk(getQuiteDoll(), base.transform(20, 49, 0)); - walk(getPurePker(), base.transform(16, 49, 0)); - break; - case 29: - walk(getOlivia(), base.transform(12, 48, 0)); - break; - case 36: - DoorActionHandler.handleAutowalkDoor(getWiseOldMan(), RegionManager.getObject(base.transform(16, 51, 0))); - break; - case 37: - getQuiteDoll().sendChat("Huh?"); - getQuiteDoll().faceTemporary(getWiseOldMan(), 2); - getWiseOldMan().sendChat("Please don't block my line of fire!"); - castShock(getPurePker()); - break; - case 38: - die(getPurePker()); - break; - case 39: - getOlivia().sendChat("Hey, what are you doing?"); - getWiseOldMan().faceTemporary(getOlivia(), 2); - GroundItemManager.create(ASHES, base.transform(16, 49, 0), player); - GroundItemManager.create(COINS, base.transform(16, 49, 0), player); - break; - case 42: - getWiseOldMan().sendChat("Olivia - Please go away!"); - getWiseOldMan().animate(SECOND_CAST_ANIMATION); - getWiseOldMan().graphics(TELE_OTHERS[0]); - break; - case 43: - getOlivia().animate(TELEPORT_ANIMATION); - getOlivia().graphics(TELE_OTHERS[1]); - getOlivia().sendChat("Eeeek!"); - GroundItemManager.destroy(GroundItemManager.get(ASHES.getId(), base.transform(16, 49, 0), player)); - break; - case 45: - getOlivia().setInvisible(true); - break; - case 48: - getWiseOldMan().sendChat("Ah, now I can get on with it..."); - getWiseOldMan().faceLocation(base.transform(16, 49, 0)); - break; - case 49: - getWiseOldMan().animate(PICK_UP_ANIMATION); - break; - case 50: - GroundItemManager.destroy(GroundItemManager.get(COINS.getId(), base.transform(16, 49, 0), player)); - break; - case 53: - castShock(base.transform(16, 46, 0)); - break; - case 55: - Scenery wall = RegionManager.getObject(base.transform(16, 46, 0)); - if(wall != null) - SceneryBuilder.replace(wall, wall.transform(9151, 0, 10)); - if(getWiseOldMan() != null) { - getWiseOldMan().getWalkingQueue().reset(); - getWiseOldMan().getWalkingQueue().addPath(base.getX() + 16, base.getY() + 46); - getWiseOldMan().getWalkingQueue().addPath(base.getX() + 17, base.getY() + 46); - } - break; - case 58: - camera(21, 38, -36, 43, 495, 99); - break; - case 59: - getWiseOldMan().sendChat("Could everyone please not move?"); - break; - case 62: - getFirstBanker().sendChat("Hey - you can't come in here!"); - walk(getFirstBanker(), base.transform(18, 46, 0)); - getFirstBanker().faceTemporary(getWiseOldMan(), 2); - getWiseOldMan().faceTemporary(getFirstBanker(), 2); - break; - case 64: - getWiseOldMan().animate(HIT_ANIMATION); - getFirstBanker().animate(BLOCK_ANIMATION); - getFirstBanker().getImpactHandler().manualHit(getWiseOldMan(), getFirstBanker().getSkills().getLifepoints(), HitsplatType.NORMAL); - getSecondBanker().sendChat("Oi!"); - getLastBanker().sendChat("Uh-oh!"); - getSecondBanker().getWalkingQueue().reset(); - getSecondBanker().getWalkingQueue().addPath(base.getX() + 17, base.getY() + 43); - getSecondBanker().getWalkingQueue().addPath(base.getX() + 16, base.getY() + 43); - getSecondBanker().getWalkingQueue().addPath(base.getX() + 16, base.getY() + 46); - break; - case 70: - walk(getLastBanker(), base.transform(18, 40, 0)); - getSecondBanker().faceTemporary(getWiseOldMan(), 2); - getSecondBanker().animate(new Animation(422)); - getWiseOldMan().animate(new Animation(403)); - getWiseOldMan().faceTemporary(getSecondBanker(), 2); - break; - case 73: - getWiseOldMan().animate(new Animation(401)); - getSecondBanker().animate(new Animation(435)); - getSecondBanker().getImpactHandler().manualHit(getWiseOldMan(), 11, HitsplatType.NORMAL); - break; - case 75: - getSecondBanker().animate(new Animation(422)); - NPC npc = getWiseOldMan(); - if (npc != null) { - getWiseOldMan().sendChat("I do wish you'd just stop it!"); - getWiseOldMan().animate(new Animation(403)); - } - break; - case 78: - getWiseOldMan().animate(new Animation(401)); - walk(getLastBanker(), base.transform(16, 40, 0)); - walk(getElfinocks(), base.transform(20, 44, 0)); - break; - case 80: - getWiseOldMan().faceLocation(base.transform(17, 43, 0)); - getLastBanker().faceTemporary(getWiseOldMan(), 1); - getSecondBanker().getImpactHandler().manualHit(getWiseOldMan(), getSecondBanker().getSkills().getLifepoints(), HitsplatType.NORMAL); - break; - case 85: - getWiseOldMan().faceLocation(base.transform(17, 43, 0)); - getElfinocks().sendChat("Hey - how'd he get in there?"); - break; - case 86: - camera(26, 40, -35, 13, 545, 32); - getLastBanker().sendChat("Help! Help!"); - get1337().sendChat("Hax!"); - get1337().faceLocation(base.transform(20, 43, 0)); - break; - case 90: - getWiseOldMan().sendChat("My sincerest regrets, dear lady..."); - break; - case 92: - getWiseOldMan().faceTemporary(getElfinocks(), 1); - castShock(getElfinocks()); - break; - case 94: - die(getElfinocks()); - break; - case 96: - GroundItemManager.create(ASHES, base.transform(20, 44, 0), player); - GroundItemManager.create(PARTY_HAT, base.transform(20, 44, 0), player); - break; - case 99: - getWiseOldMan().sendChat("And also you, sir..."); - GroundItemManager.destroy(GroundItemManager.get(ASHES.getId(), base.transform(20, 44, 0), player)); - break; - case 101: - castShock(get1337()); - walk(getQuiteDoll(), base.transform(21, 44, 0)); - break; - case 103: - getQuiteDoll().faceTemporary(getWiseOldMan(), 1); - break; - case 104: - getQuiteDoll().sendChat("How you do that?"); - die(get1337()); - break; - case 107: - GroundItemManager.create(ASHES, base.transform(21, 43, 0), player); - break; - case 109: - getWiseOldMan().sendChat("Oh dear - another one..."); - walk(getWiseOldMan(), base.transform(18, 46, 0)); - getWiseOldMan().faceTemporary(getQuiteDoll(), 1); - GroundItemManager.destroy(GroundItemManager.get(ASHES.getId(), base.transform(21, 43, 0), player)); - break; - case 110: - GroundItemManager.destroy(GroundItemManager.get(ASHES.getId(), base.transform(21, 43, 0), player)); - break; - case 111: - castShock(getQuiteDoll()); - break; - case 114: - die(getQuiteDoll()); - break; - case 115: - GroundItemManager.create(ASHES, base.transform(21, 44, 0), player); - getWiseOldMan().sendChat("Ooh - a party hat?"); - getWiseOldMan().faceLocation(base.transform(20, 44, 0)); - break; - case 116: - GroundItemManager.destroy(GroundItemManager.get(ASHES.getId(), base.transform(21, 44, 0), player)); - castTelekinetic(); - break; - case 118: - getWiseOldMan().transform(2567); - if (getOlivia().isActive()) { - getOlivia().getImpactHandler().manualHit(getMarketGuard(), getOlivia().getSkills().getLifepoints(), HitsplatType.NORMAL); - } - getMarketGuard().setInvisible(true); - break; - case 120: - getWiseOldMan().sendChat("Now, my dear..."); - walk(getWiseOldMan(), base.transform(18, 42, 0)); - break; - case 121: - getLastBanker().sendChat("Eeek!"); - break; - case 124: - getWiseOldMan().sendChat("Just give me the money, please."); - break; - case 126: - getMarketGuard().setInvisible(false); - getMarketGuard().getProperties().setTeleportLocation(base.transform(18, 46, 0)); - break; - case 127: - camera(25, 42, -20, 0, 400, 4); - getMarketGuard().sendChat("Old man, you're for da cage!"); - walk(getMarketGuard(), base.transform(18, 43, 0)); - break; - case 128: - getWiseOldMan().faceTemporary(getMarketGuard(), 1); - break; - case 129: - getWiseOldMan().sendChat("Oh, am I?"); - break; - case 131: - getWiseOldMan().sendChat("Ahh!"); - break; - case 133: - castThunderShock(); - break; - case 136: - getMarketGuard().graphics(THUNDER_GRAPHIC); - getMarketGuard().getImpactHandler().manualHit(getWiseOldMan(), 40, HitsplatType.NORMAL); - getMarketGuard().sendChat("Aaargh!"); - break; - case 138: - getWiseOldMan().sendChat("Yah!"); - getWiseOldMan().animate(WISE_JUMP); - break; - case 139: - getMarketGuard().animate(GUARD_JUMP); - break; - case 142: - getMarketGuard().clear(); - camera(24, 42, -20, 0, 400, 4); - break; - case 143: - getWiseOldMan().sendChat("And YOU can stop spying on me!"); - getWiseOldMan().faceLocation(base.transform(24, 42, 0)); - break; - case 145: - getWiseOldMan().animate(STOMP_ANIM); - Projectile projectile = Projectile.create(getWiseOldMan(), null, 434, 50, 130, 41, 140, 0, 0); - projectile.setEndLocation((Location) base.transform(24, 42, 0)); - projectile.send(); - break; - case 148: - player.getInterfaceManager().open(CRACKED); - break; - case 150: - DBRCutscenePlugin.this.stop(true); - return true; - } - return !player.isActive(); - } - - @Override - public void stop() { - super.stop(); - clearNpcs(); - } - - /** - * Gets the counter. - * - * @return the counter. - */ - public int getCounter() { - return counter; - } - } - - /** - * Represents an npc during the draynor bank recording cutscene. - * - * @author 'Vexia - * @version 1.0 - */ - public static final class DBRecordingNPC extends AbstractNPC { - - /** - * Represents the ids to use. - */ - private static final int[] IDS = new int[] { 2579 }; - - /** - * Constructs a new {@code DBRecordingNPC} {@code Object}. - */ - public DBRecordingNPC() { - super(0, null, false); - } - - /** - * Constructs a new {@code DBRecordingNPC} {@code Object}. - * - * @param id - * the id. - * @param location - * the locastion. - */ - private DBRecordingNPC(int id, Location location) { - super(id, location); - } - - @Override - public AbstractNPC construct(int id, Location location, Object... objects) { - return new DBRecordingNPC(id, location); - } - - @Override - public void init() { - super.init(); - getSkills().setLevel(Skills.ATTACK, 1); - getSkills().setStaticLevel(Skills.ATTACK, 1); - getSkills().setLevel(Skills.DEFENCE, 2); - getSkills().setStaticLevel(Skills.DEFENCE, 2); - } - - @Override - public boolean isAttackable(Entity entity, CombatStyle style, boolean message) { - return true; - } - - @Override - public int[] getIds() { - return IDS; - } - } - - public static final class BluePhatItem extends ItemPlugin { - - @Override - public Plugin newInstance(Object arg) throws Throwable { - register(PARTY_HAT.getId()); - return this; - } - - @Override - public boolean canPickUp(Player player, GroundItem item, int type) { - if (type == 1) { - player.sendMessage("Nice try ;)"); - } - return false; - } - } -} diff --git a/Server/src/main/content/region/misthalin/draynor/handlers/DBRCutscenePlugin.kt b/Server/src/main/content/region/misthalin/draynor/handlers/DBRCutscenePlugin.kt new file mode 100644 index 000000000..467943a5d --- /dev/null +++ b/Server/src/main/content/region/misthalin/draynor/handlers/DBRCutscenePlugin.kt @@ -0,0 +1,566 @@ +package content.region.misthalin.draynor.handlers + +import core.api.* +import core.game.activity.Cutscene +import core.game.component.Component +import core.game.global.action.DoorActionHandler +import core.game.interaction.IntType +import core.game.interaction.InteractionListener +import core.game.node.entity.Entity +import core.game.node.entity.combat.ImpactHandler +import core.game.node.entity.impl.Projectile +import core.game.node.entity.npc.NPC +import core.game.node.entity.player.Player +import core.game.node.entity.player.link.SpellBookManager +import core.game.node.entity.skill.Skills +import core.game.node.item.GroundItemManager +import core.game.node.item.Item +import core.game.node.scenery.SceneryBuilder +import core.game.world.map.Direction +import core.game.world.map.Location +import core.game.world.update.flag.context.Animation +import core.game.world.update.flag.context.Graphics +import core.net.packet.PacketRepository +import core.net.packet.context.CameraContext +import core.net.packet.context.CameraContext.CameraType +import core.net.packet.out.CameraViewPacket +import org.rs09.consts.Items +import org.rs09.consts.NPCs + +class DBRCutscene(player: Player) : Cutscene(player) { + companion object { + const val HAS_SEEN_RECORDING = "draynor-recording" + + // Animations + private val STEAL_ANIMATION = Animation(832) + private val CAST_ANIMATION = Animation(1167) + private val TELEKINETIC_ANIMATION = Animation(723) + private val DEATH_ANIMATION = Animation(2553) + private val TELEPORT_ANIMATION = Animation(1816) + private val PICK_UP_ANIMATION = Animation(827) + private val HIT_ANIMATION = Animation(401) + private val BLOCK_ANIMATION = Animation(425) + private val THUNDER_ANIMATION = Animation(811) + private val WISE_JUMP = Animation(2555) + private val GUARD_JUMP = Animation(2556) + private val STOMP_ANIM = Animation(1820) + + // Graphics + private val TELEKINETIC_GRAPHIC = Graphics(142, 96) + private val SHOCK_GRAPHIC = Graphics(432, 0, 0) + private val SHOCK_CAST = Graphics(433) + private val TELE_OTHER_CAST = Graphics(343) + private val TELE_OTHER_TARGET = Graphics(342) + private val PURPLE_GRAPHIC = Graphics(301, 100) + private val THUNDER_GRAPHIC = Graphics(76) + + // Projectile ids + private const val SHOCK_PROJECTILE_ID = 434 + + // Components + private val CRACKED = Component(385) + } + + override fun setup() { + setExit(player.location) + loadRegion(8524) + + // Add all NPCs with their initial positions + addNPC(NPCs.COOL_MOM227_2579, 8, 52, Direction.NORTH) + addNPC(NPCs.OLIVIA_2572, 5, 49, Direction.WEST) + addNPC(NPCs.MARKET_GUARD_2571, 13, 48, Direction.WEST) + addNPC(NPCs.PUREPKER895_2575, 24, 50, Direction.WEST) + addNPC(NPCs.BANKER_2568, 18, 45, Direction.EAST) + addNPC(NPCs.BANKER_2569, 18, 43, Direction.NORTH) + addNPC(NPCs.BANKER_2570, 18, 42, Direction.NORTH) + addNPC(NPCs.ELFINLOCKS_2578, 21, 44, Direction.NORTH) + addNPC(NPCs.SP34KR_1337_2577, 22, 42, Direction.WEST) + addNPC(NPCs.QUTIEDOLL_2576, 21, 42, Direction.EAST) + addNPC(NPCs.WISE_OLD_MAN_2566, 16, 51, Direction.SOUTH) + + // Set banker HP + getNPC(NPCs.BANKER_2568)?.skills?.setStaticLevel(Skills.HITPOINTS, 15) + getNPC(NPCs.BANKER_2568)?.skills?.setLevel(Skills.HITPOINTS, 15) + getNPC(NPCs.BANKER_2569)?.skills?.setStaticLevel(Skills.HITPOINTS, 15) + getNPC(NPCs.BANKER_2569)?.skills?.setLevel(Skills.HITPOINTS, 15) + getNPC(NPCs.BANKER_2570)?.skills?.setStaticLevel(Skills.HITPOINTS, 15) + getNPC(NPCs.BANKER_2570)?.skills?.setLevel(Skills.HITPOINTS, 15) + } + + private fun camera(x: Int, y: Int, xRot: Int, yRot: Int, height: Int, speed: Int) { + val loc = base.transform(x, y, 0) + PacketRepository.send( + CameraViewPacket::class.java, + CameraContext(player, CameraType.POSITION, loc.x, loc.y, height, 1, speed) + ) + PacketRepository.send( + CameraViewPacket::class.java, + CameraContext(player, CameraType.ROTATION, loc.x + xRot, loc.y + yRot, height, 1, speed) + ) + } + + private fun castShock(target: Entity, wiseOldMan: Entity) { + wiseOldMan.animate(CAST_ANIMATION) + wiseOldMan.graphics(SHOCK_CAST) + val projectile = Projectile.create(wiseOldMan, target, SHOCK_PROJECTILE_ID) + projectile.send() + } + + private fun castShock(loc: Location, wiseOldMan: Entity) { + wiseOldMan.animate(CAST_ANIMATION) + wiseOldMan.graphics(SHOCK_CAST) + val projectile = Projectile.create(wiseOldMan, null, SHOCK_PROJECTILE_ID, 30, 30, 41, 140, 0, 0) + projectile.endLocation = loc + projectile.send() + } + + private fun castTelekinetic() { + val wiseOldMan = getNPC(NPCs.WISE_OLD_MAN_2566)!! + wiseOldMan.animate(TELEKINETIC_ANIMATION) + wiseOldMan.graphics(TELEKINETIC_GRAPHIC) + wiseOldMan.skills.setStaticLevel(Skills.MAGIC, 99) + wiseOldMan.skills.setLevel(Skills.MAGIC, 99) + SpellBookManager.SpellBook.MODERN.getSpell(19).cast(wiseOldMan, GroundItemManager.get(Item(Items.BLUE_PARTYHAT_2422).id, base.transform(20, 44, 0), player)) + } + + private fun die(npc: NPC) { + npc.animator.reset() + npc.animate(DEATH_ANIMATION) + npc.graphics(SHOCK_GRAPHIC) + npc.impactHandler.manualHit(npc, npc.skills.lifepoints, ImpactHandler.HitsplatType.NORMAL) + } + + override fun runStage(stage: Int) { + when (stage) { + 0 -> { + lock(player, 1000) + sendDialogue(player, "You close your eyes and watch the recording...") + fadeToBlack() + timedUpdate(3) + } + 1 -> { + fadeFromBlack() + player.isInvisible = true + teleport(player, 17, 51) + camera(27, 45, -14, 2, 700, 100) + timedUpdate(3) + } + 2 -> { + val coolMom = getNPC(NPCs.COOL_MOM227_2579)!! + val marketGuard = getNPC(NPCs.MARKET_GUARD_2571)!! + coolMom.animate(STEAL_ANIMATION) + marketGuard.skills.setStaticLevel(Skills.HITPOINTS, 99) + marketGuard.skills.setLifepoints(99) + sendChat(marketGuard, "Hey! Get your hands off there!") + coolMom.locks.lockMovement(1000) + marketGuard.properties.combatPulse.attack(coolMom) + timedUpdate(7) + } + 3 -> { + move(getNPC(NPCs.PUREPKER895_2575)!!, 20, 45) + timedUpdate(2) + } + 4 -> { + camera(27, 43, -37, -3, 600, 5) + timedUpdate(2) + } + 5 -> { + sendChat(getNPC(NPCs.QUTIEDOLL_2576)!!, "Thx") + timedUpdate(2) + } + 6 -> { + move(getNPC(NPCs.QUTIEDOLL_2576)!!, 20, 42) + sendChat(getNPC(NPCs.SP34KR_1337_2577)!!, "Yw") + getNPC(NPCs.PUREPKER895_2575)!!.faceLocation(base.transform(19, 45, 0)) + timedUpdate(2) + } + 7 -> { + sendChat(getNPC(NPCs.ELFINLOCKS_2578)!!, "Buying 2k nats - no noob offers") + timedUpdate(4) + } + 8 -> { + sendChat(getNPC(NPCs.SP34KR_1337_2577)!!, "I h4ve n4ts") + timedUpdate(2) + } + 9 -> { + sendChat(getNPC(NPCs.ELFINLOCKS_2578)!!, "You sell?") + timedUpdate(3) + } + 10 -> { + sendChat(getNPC(NPCs.SP34KR_1337_2577)!!, "Tr4de") + getNPC(NPCs.SP34KR_1337_2577)!!.faceTemporary(getNPC(NPCs.ELFINLOCKS_2578)!!, 5) + move(getNPC(NPCs.SP34KR_1337_2577)!!, 21, 43) + timedUpdate(1) + } + 11 -> { + camera(23, 49, -37, 0, 500, 3) + timedUpdate(2) + } + 12 -> { + move(getNPC(NPCs.QUTIEDOLL_2576)!!, 20, 49) + move(getNPC(NPCs.PUREPKER895_2575)!!, 16, 49) + timedUpdate(1) + } + 13 -> { + move(getNPC(NPCs.OLIVIA_2572)!!, 12, 48) + timedUpdate(7) + } + 14 -> { + val wiseOldMan = getNPC(NPCs.WISE_OLD_MAN_2566)!! + DoorActionHandler.handleAutowalkDoor(wiseOldMan, getObject(16, 51)) + timedUpdate(1) + } + 15 -> { + val wiseOldMan = getNPC(NPCs.WISE_OLD_MAN_2566)!! + val qutieDoll = getNPC(NPCs.QUTIEDOLL_2576)!! + val purePker = getNPC(NPCs.PUREPKER895_2575)!! + sendChat(qutieDoll, "Huh?") + qutieDoll.faceTemporary(wiseOldMan, 2) + sendChat(wiseOldMan, "Please don't block my line of fire!") + castShock(purePker, wiseOldMan) + timedUpdate(1) + } + 16 -> { + die(getNPC(NPCs.PUREPKER895_2575)!!) + timedUpdate(1) + } + 17 -> { + val wiseOldMan = getNPC(NPCs.WISE_OLD_MAN_2566)!! + val olivia = getNPC(NPCs.OLIVIA_2572)!! + sendChat(olivia, "Hey, what are you doing?") + wiseOldMan.faceTemporary(olivia, 2) + GroundItemManager.create(Item(Items.ASHES_592), base.transform(16, 49, 0), player) + GroundItemManager.create(Item(Items.COINS_995, 60), base.transform(16, 49, 0), player) + timedUpdate(3) + } + 18 -> { + val wiseOldMan = getNPC(NPCs.WISE_OLD_MAN_2566)!! + sendChat(wiseOldMan, "Olivia - please go away!") + wiseOldMan.animate(CAST_ANIMATION) + wiseOldMan.graphics(TELE_OTHER_CAST) + timedUpdate(1) + } + 19 -> { + val olivia = getNPC(NPCs.OLIVIA_2572)!! + olivia.animate(TELEPORT_ANIMATION) + olivia.graphics(TELE_OTHER_TARGET) + sendChat(olivia, "Eeeek!") + GroundItemManager.destroy(GroundItemManager.get(Item(Items.ASHES_592).id, base.transform(16, 49, 0), player)) + timedUpdate(2) + } + 20 -> { + getNPC(NPCs.OLIVIA_2572)!!.isInvisible = true + timedUpdate(3) + } + 21 -> { + val wiseOldMan = getNPC(NPCs.WISE_OLD_MAN_2566)!! + sendChat(wiseOldMan, "Ah, now I can get on with it...") + wiseOldMan.faceLocation(base.transform(16, 49, 0)) + timedUpdate(1) + } + 22 -> { + getNPC(NPCs.WISE_OLD_MAN_2566)!!.animate(PICK_UP_ANIMATION) + timedUpdate(1) + } + 23 -> { + GroundItemManager.destroy(GroundItemManager.get(Item(Items.COINS_995, 60).id, base.transform(16, 49, 0), player)) + timedUpdate(3) + } + 24 -> { + val wiseOldMan = getNPC(NPCs.WISE_OLD_MAN_2566)!! + castShock(base.transform(16, 46, 0), wiseOldMan) + timedUpdate(2) + } + 25 -> { + val wall = getObject(16, 46) + if (wall != null) { + SceneryBuilder.replace(wall, wall.transform(9151, 0, 10)) + } + val wiseOldMan = getNPC(NPCs.WISE_OLD_MAN_2566) + if (wiseOldMan != null) { + wiseOldMan.walkingQueue.reset() + wiseOldMan.walkingQueue.addPath(base.x + 16, base.y + 46) + wiseOldMan.walkingQueue.addPath(base.x + 17, base.y + 46) + } + timedUpdate(3) + } + 26 -> { + camera(21, 38, -36, 43, 495, 99) + timedUpdate(1) + } + 27 -> { + sendChat(getNPC(NPCs.WISE_OLD_MAN_2566)!!, "Could everyone please not move?") + timedUpdate(3) + } + 28 -> { + val firstBanker = getNPC(NPCs.BANKER_2568)!! + val wiseOldMan = getNPC(NPCs.WISE_OLD_MAN_2566)!! + sendChat(firstBanker, "Hey - you can't come in here!") + move(firstBanker, 18, 46) + firstBanker.faceTemporary(wiseOldMan, 2) + wiseOldMan.faceTemporary(firstBanker, 2) + timedUpdate(2) + } + 29 -> { + val firstBanker = getNPC(NPCs.BANKER_2568)!! + val secondBanker = getNPC(NPCs.BANKER_2569)!! + val lastBanker = getNPC(NPCs.BANKER_2570)!! + val wiseOldMan = getNPC(NPCs.WISE_OLD_MAN_2566)!! + wiseOldMan.animate(HIT_ANIMATION) + firstBanker.animate(BLOCK_ANIMATION) + firstBanker.impactHandler.manualHit(wiseOldMan, firstBanker.skills.lifepoints, ImpactHandler.HitsplatType.NORMAL) + sendChat(secondBanker, "Oi!") + sendChat(lastBanker, "Uh-oh!") + secondBanker.walkingQueue.reset() + secondBanker.walkingQueue.addPath(base.x + 17, base.y + 43) + secondBanker.walkingQueue.addPath(base.x + 16, base.y + 43) + secondBanker.walkingQueue.addPath(base.x + 16, base.y + 46) + timedUpdate(6) + } + 30 -> { + move(getNPC(NPCs.BANKER_2570)!!, 18, 40) + val secondBanker = getNPC(NPCs.BANKER_2569)!! + val wiseOldMan = getNPC(NPCs.WISE_OLD_MAN_2566)!! + secondBanker.faceTemporary(wiseOldMan, 2) + secondBanker.animate(Animation(422)) + wiseOldMan.animate(Animation(403)) + wiseOldMan.faceTemporary(secondBanker, 2) + timedUpdate(3) + } + 31 -> { + val secondBanker = getNPC(NPCs.BANKER_2569)!! + val wiseOldMan = getNPC(NPCs.WISE_OLD_MAN_2566)!! + wiseOldMan.animate(Animation(401)) + secondBanker.animate(Animation(435)) + secondBanker.impactHandler.manualHit(wiseOldMan, 11, ImpactHandler.HitsplatType.NORMAL) + timedUpdate(2) + } + 32 -> { + val secondBanker = getNPC(NPCs.BANKER_2569)!! + val wiseOldMan = getNPC(NPCs.WISE_OLD_MAN_2566)!! + secondBanker.animate(Animation(422)) + sendChat(wiseOldMan, "I do wish you'd just stop it!") + wiseOldMan.animate(Animation(403)) + timedUpdate(3) + } + 33 -> { + val wiseOldMan = getNPC(NPCs.WISE_OLD_MAN_2566)!! + wiseOldMan.animate(Animation(401)) + move(getNPC(NPCs.BANKER_2570)!!, 16, 40) + move(getNPC(NPCs.ELFINLOCKS_2578)!!, 20, 44) + timedUpdate(2) + } + 34 -> { + val wiseOldMan = getNPC(NPCs.WISE_OLD_MAN_2566)!! + val lastBanker = getNPC(NPCs.BANKER_2570)!! + val secondBanker = getNPC(NPCs.BANKER_2569)!! + wiseOldMan.faceLocation(base.transform(17, 43, 0)) + lastBanker.faceLocation(wiseOldMan.location) + secondBanker.impactHandler.manualHit(wiseOldMan, secondBanker.skills.lifepoints, ImpactHandler.HitsplatType.NORMAL) + timedUpdate(5) + } + 35 -> { + val wiseOldMan = getNPC(NPCs.WISE_OLD_MAN_2566)!! + wiseOldMan.faceLocation(base.transform(17, 43, 0)) + sendChat(getNPC(NPCs.ELFINLOCKS_2578)!!, "Hey - how'd he get in there?") + timedUpdate(1) + } + 36 -> { + camera(26, 40, -35, 13, 545, 32) + sendChat(getNPC(NPCs.BANKER_2570)!!, "Help! Help!") + sendChat(getNPC(NPCs.SP34KR_1337_2577)!!, "Hax!") + getNPC(NPCs.SP34KR_1337_2577)!!.faceLocation(base.transform(20, 43, 0)) + timedUpdate(4) + } + 37 -> { + sendChat(getNPC(NPCs.WISE_OLD_MAN_2566)!!, "My sincerest regrets, dear lady...") + timedUpdate(2) + } + 38 -> { + val wiseOldMan = getNPC(NPCs.WISE_OLD_MAN_2566)!! + wiseOldMan.faceTemporary(getNPC(NPCs.ELFINLOCKS_2578)!!, 1) + castShock(getNPC(NPCs.ELFINLOCKS_2578)!!, wiseOldMan) + timedUpdate(2) + } + 39 -> { + die(getNPC(NPCs.ELFINLOCKS_2578)!!) + timedUpdate(2) + } + 40 -> { + GroundItemManager.create(Item(Items.ASHES_592), base.transform(20, 44, 0), player) + GroundItemManager.create(Item(Items.BLUE_PARTYHAT_2422), base.transform(20, 44, 0), player) + timedUpdate(3) + } + 41 -> { + val wiseOldMan = getNPC(NPCs.WISE_OLD_MAN_2566)!! + sendChat(wiseOldMan, "And also you, sir...") + GroundItemManager.destroy(GroundItemManager.get(Item(Items.ASHES_592).id, base.transform(20, 44, 0), player)) + timedUpdate(2) + } + 42 -> { + val wiseOldMan = getNPC(NPCs.WISE_OLD_MAN_2566)!! + castShock(getNPC(NPCs.SP34KR_1337_2577)!!, wiseOldMan) + move(getNPC(NPCs.QUTIEDOLL_2576)!!, 21, 44) + timedUpdate(2) + } + 43 -> { + getNPC(NPCs.QUTIEDOLL_2576)!!.faceTemporary(getNPC(NPCs.WISE_OLD_MAN_2566)!!, 1) + timedUpdate(1) + } + 44 -> { + sendChat(getNPC(NPCs.QUTIEDOLL_2576)!!, "How you do that?") + die(getNPC(NPCs.SP34KR_1337_2577)!!) + timedUpdate(3) + } + 45 -> { + GroundItemManager.create(Item(Items.ASHES_592), base.transform(21, 43, 0), player) + timedUpdate(2) + } + 46 -> { + val wiseOldMan = getNPC(NPCs.WISE_OLD_MAN_2566)!! + sendChat(wiseOldMan, "Oh dear - another one...") + move(wiseOldMan, 18, 46) + wiseOldMan.faceLocation(getNPC(NPCs.QUTIEDOLL_2576)!!.location) + GroundItemManager.destroy(GroundItemManager.get(Item(Items.ASHES_592).id, base.transform(21, 43, 0), player)) + timedUpdate(1) + } + 47 -> { + GroundItemManager.destroy(GroundItemManager.get(Item(Items.ASHES_592).id, base.transform(21, 43, 0), player)) + timedUpdate(1) + } + 48 -> { + val wiseOldMan = getNPC(NPCs.WISE_OLD_MAN_2566)!! + castShock(getNPC(NPCs.QUTIEDOLL_2576)!!, wiseOldMan) + timedUpdate(3) + } + 49 -> { + die(getNPC(NPCs.QUTIEDOLL_2576)!!) + timedUpdate(1) + } + 50 -> { + val wiseOldMan = getNPC(NPCs.WISE_OLD_MAN_2566)!! + GroundItemManager.create(Item(Items.ASHES_592), base.transform(21, 44, 0), player) + sendChat(wiseOldMan, "Ooh - a party hat?") + wiseOldMan.faceLocation(base.transform(20, 44, 0)) + timedUpdate(1) + } + 51 -> { + GroundItemManager.destroy(GroundItemManager.get(Item(Items.ASHES_592).id, base.transform(21, 44, 0), player)) + castTelekinetic() + timedUpdate(2) + } + 52 -> { + GroundItemManager.destroy(GroundItemManager.get(Item(Items.BLUE_PARTYHAT_2422).id, base.transform(20, 44, 0), player)) + val wiseOldMan = getNPC(NPCs.WISE_OLD_MAN_2566)!! + val olivia = getNPC(NPCs.OLIVIA_2572)!! + val marketGuard = getNPC(NPCs.MARKET_GUARD_2571)!! + wiseOldMan.transform(NPCs.WISE_OLD_MAN_2567) + if (olivia.isActive) { + olivia.impactHandler.manualHit(marketGuard, olivia.skills.lifepoints, ImpactHandler.HitsplatType.NORMAL) + } + marketGuard.isInvisible = true + timedUpdate(2) + } + 53 -> { + val wiseOldMan = getNPC(NPCs.WISE_OLD_MAN_2566)!! + sendChat(wiseOldMan, "Now, my dear...") + move(wiseOldMan, 18, 42) + timedUpdate(1) + } + 54 -> { + sendChat(getNPC(NPCs.BANKER_2570)!!, "Eeek!") + timedUpdate(3) + } + 55 -> { + sendChat(getNPC(NPCs.WISE_OLD_MAN_2566)!!, "Just give me the money, please.") + timedUpdate(2) + } + 56 -> { + val marketGuard = getNPC(NPCs.MARKET_GUARD_2571)!! + marketGuard.isInvisible = false + marketGuard.properties.teleportLocation = base.transform(18, 46, 0) + timedUpdate(1) + } + 57 -> { + camera(25, 42, -20, 0, 400, 4) + val marketGuard = getNPC(NPCs.MARKET_GUARD_2571)!! + sendChat(marketGuard, "Old man, you're for da cage!") + move(marketGuard, 18, 43) + timedUpdate(1) + } + 58 -> { + getNPC(NPCs.WISE_OLD_MAN_2566)!!.faceTemporary(getNPC(NPCs.MARKET_GUARD_2571), 1) + timedUpdate(1) + } + 59 -> { + sendChat(getNPC(NPCs.WISE_OLD_MAN_2566)!!, "Oh, am I?") + timedUpdate(2) + } + 60 -> { + sendChat(getNPC(NPCs.WISE_OLD_MAN_2566)!!, "Ahh!") + timedUpdate(2) + } + 61 -> { + val wiseOldMan = getNPC(NPCs.WISE_OLD_MAN_2566)!! + wiseOldMan.animate(THUNDER_ANIMATION) + wiseOldMan.graphics(PURPLE_GRAPHIC) + timedUpdate(3) + } + 62 -> { + val marketGuard = getNPC(NPCs.MARKET_GUARD_2571)!! + val wiseOldMan = getNPC(NPCs.WISE_OLD_MAN_2566)!! + marketGuard.graphics(THUNDER_GRAPHIC) + marketGuard.impactHandler.manualHit(wiseOldMan, 40, ImpactHandler.HitsplatType.NORMAL) + sendChat(marketGuard, "Aaargh!") + timedUpdate(2) + } + 63 -> { + val wiseOldMan = getNPC(NPCs.WISE_OLD_MAN_2566)!! + sendChat(wiseOldMan, "Yah!") + wiseOldMan.animate(WISE_JUMP) + timedUpdate(1) + } + 64 -> { + getNPC(NPCs.MARKET_GUARD_2571)!!.animate(GUARD_JUMP) + timedUpdate(3) + } + 65 -> { + getNPC(NPCs.MARKET_GUARD_2571)!!.clear() + camera(24, 42, -20, 0, 400, 4) + timedUpdate(1) + } + 66 -> { + val wiseOldMan = getNPC(NPCs.WISE_OLD_MAN_2566)!! + sendChat(wiseOldMan, "And YOU can stop spying on me!") + wiseOldMan.faceLocation(base.transform(24, 42, 0)) + timedUpdate(2) + } + 67 -> { + val wiseOldMan = getNPC(NPCs.WISE_OLD_MAN_2566)!! + wiseOldMan.animate(STOMP_ANIM) + val projectile = Projectile.create(wiseOldMan, null, 434, 50, 130, 41, 140, 0, 0) + projectile.endLocation = base.transform(24, 42, 0) + projectile.send() + timedUpdate(3) + } + 68 -> { + player.interfaceManager.openOverlay(CRACKED) + timedUpdate(2) + } + 69 -> end { + player.isInvisible = false + setAttribute(player, "/save:$HAS_SEEN_RECORDING", true) + sendDialogue(player, "End of recording.") + } + } + } +} + +class DBRCutsceneListeners : InteractionListener { + override fun defineListeners() { + on(Items.BLUE_PARTYHAT_2422, IntType.GROUNDITEM, "take") { player, _ -> + sendMessage(player, "Nice try ;)") + return@on true + } + } +} \ No newline at end of file diff --git a/Server/src/main/content/region/misthalin/draynor/handlers/DraynorManorChairNPC.kt b/Server/src/main/content/region/misthalin/draynor/handlers/DraynorManorChairNPC.kt index 423e57dec..4d11f87e3 100644 --- a/Server/src/main/content/region/misthalin/draynor/handlers/DraynorManorChairNPC.kt +++ b/Server/src/main/content/region/misthalin/draynor/handlers/DraynorManorChairNPC.kt @@ -78,7 +78,7 @@ class DraynorManorChairNPC(id: Int = DRAYNOR_MANOR_CHAIR_NPC_ID, location: Locat * @return The Player object representing the closest player, or null if there are no players nearby. */ fun findClosestPlayer(): Player? { - val players = RegionManager.getLocalPlayers(this, FOLLOWING_DISTANCE) + val players = RegionManager.getLocalPlayers(location, FOLLOWING_DISTANCE) if (players.isEmpty()) { return null } diff --git a/Server/src/main/content/region/misthalin/draynor/handlers/DraynorManorHouseZone.kt b/Server/src/main/content/region/misthalin/draynor/handlers/DraynorManorHouseZone.kt index 529b497a6..89e30c54a 100644 --- a/Server/src/main/content/region/misthalin/draynor/handlers/DraynorManorHouseZone.kt +++ b/Server/src/main/content/region/misthalin/draynor/handlers/DraynorManorHouseZone.kt @@ -2,7 +2,7 @@ package content.region.misthalin.draynor.handlers import core.game.node.entity.Entity import core.game.world.map.Location -import core.game.world.map.RegionManager.getLocalNpcs +import core.game.world.map.RegionManager.getLocalNPCs import core.game.world.map.path.Pathfinder import core.game.world.map.zone.MapZone import core.game.world.map.zone.ZoneBorders @@ -24,7 +24,7 @@ class DraynorManorHouseZone : MapZone("Draynor Manor House", true), Plugin } override fun move(e: Entity, loc: Location?, destination: Location): Boolean { - for (n in getLocalNpcs(e, 5)) { + for (n in getLocalNPCs(e.location, 5)) { if (n.isInvisible() || n === e) { continue } diff --git a/Server/src/main/content/region/misthalin/draynor/handlers/DraynorTreeNPC.java b/Server/src/main/content/region/misthalin/draynor/handlers/DraynorTreeNPC.java index 2bc5f6852..f0b270b2c 100644 --- a/Server/src/main/content/region/misthalin/draynor/handlers/DraynorTreeNPC.java +++ b/Server/src/main/content/region/misthalin/draynor/handlers/DraynorTreeNPC.java @@ -63,8 +63,8 @@ public final class DraynorTreeNPC extends AbstractNPC { @Override public void tick() { - final List players = RegionManager.getLocalPlayers(this, 1); - if (players.size() != 0) { + final List players = RegionManager.getLocalPlayers(location, 1); + if (!players.isEmpty()) { if (attackDelay < GameWorld.getTicks()) { for (Player p : players) { faceTemporary(p, 2); diff --git a/Server/src/main/content/region/misthalin/lumbridge/diary/LumbridgeAchivementDiary.kt b/Server/src/main/content/region/misthalin/lumbridge/diary/LumbridgeAchivementDiary.kt index 965dcb157..1240d0d4f 100644 --- a/Server/src/main/content/region/misthalin/lumbridge/diary/LumbridgeAchivementDiary.kt +++ b/Server/src/main/content/region/misthalin/lumbridge/diary/LumbridgeAchivementDiary.kt @@ -120,7 +120,7 @@ class LumbridgeAchivementDiary : DiaryEventHookBase(DiaryType.LUMBRIDGE) { ) override fun onResourceProduced(player: Player, event: ResourceProducedEvent) { - when (player.viewport.region.id) { + when (player.location.regionId) { 12596 -> if (event.itemId == Items.CLAY_434) { finishTask( player, @@ -248,7 +248,7 @@ class LumbridgeAchivementDiary : DiaryEventHookBase(DiaryType.LUMBRIDGE) { } override fun onNpcKilled(player: Player, event: NPCKillEvent) { - when (player.viewport.region.id) { + when (player.location.regionId) { 12593, 12849 -> if (event.npc.id == NPCs.GIANT_RAT_86) { finishTask( player, @@ -280,7 +280,7 @@ class LumbridgeAchivementDiary : DiaryEventHookBase(DiaryType.LUMBRIDGE) { } override fun onFireLit(player: Player, event: LitFireEvent) { - when (player.viewport.region.id) { + when (player.location.regionId) { 12593, 12849 -> if (event.logId == Items.LOGS_1511) { finishTask( player, @@ -302,7 +302,7 @@ class LumbridgeAchivementDiary : DiaryEventHookBase(DiaryType.LUMBRIDGE) { } override fun onInteracted(player: Player, event: InteractionEvent) { - when (player.viewport.region.id) { + when (player.location.regionId) { 12337 -> if (event.target.id == Scenery.RAILING_37668 && event.option == "taunt-through" ) { @@ -364,7 +364,7 @@ class LumbridgeAchivementDiary : DiaryEventHookBase(DiaryType.LUMBRIDGE) { override fun onPickedUp(player: Player, event: PickUpEvent) { - when (player.viewport.region.id) { + when (player.location.regionId) { 12850, 12851 -> { if (event.itemId == Items.COWHIDE_1739) { if (inBorders(player, COW_PEN_AREA_1) @@ -382,7 +382,7 @@ class LumbridgeAchivementDiary : DiaryEventHookBase(DiaryType.LUMBRIDGE) { } override fun onInterfaceOpened(player: Player, event: InterfaceOpenEvent) { - when (player.viewport.region.id) { + when (player.location.regionId) { 12338 -> if (event.component.id == Components.BANK_V2_MAIN_762) { finishTask( player, @@ -412,7 +412,7 @@ class LumbridgeAchivementDiary : DiaryEventHookBase(DiaryType.LUMBRIDGE) { } override fun onJobAssigned(player: Player, event: JobAssignmentEvent) { - when (player.viewport.region.id) { + when (player.location.regionId) { 12849 -> if (event.employerNpc.id == NPCs.FISHING_TUTOR_4901) { finishTask( player, @@ -424,7 +424,7 @@ class LumbridgeAchivementDiary : DiaryEventHookBase(DiaryType.LUMBRIDGE) { } override fun onUsedWith(player: Player, event: UseWithEvent) { - when (player.viewport.region.id) { + when (player.location.regionId) { 12595 -> if (event.used == Items.EMPTY_POT_1931 && event.with == Scenery.FLOUR_BIN_36878) { finishTask( player, diff --git a/Server/src/main/content/region/misthalin/lumbridge/quest/tearsofguthix/TearsOfGuthixMinigame.kt b/Server/src/main/content/region/misthalin/lumbridge/quest/tearsofguthix/TearsOfGuthixMinigame.kt index ab27cbd71..ffe8c0b1e 100644 --- a/Server/src/main/content/region/misthalin/lumbridge/quest/tearsofguthix/TearsOfGuthixMinigame.kt +++ b/Server/src/main/content/region/misthalin/lumbridge/quest/tearsofguthix/TearsOfGuthixMinigame.kt @@ -11,7 +11,9 @@ import core.game.node.entity.Entity import core.game.node.entity.player.Player import core.game.node.entity.skill.Skills import core.game.node.item.Item +import core.game.node.scenery.SceneryBuilder import core.game.world.map.Location +import core.game.world.map.RegionManager import core.game.world.map.zone.ZoneBorders import core.game.world.map.zone.ZoneRestriction import core.game.world.update.flag.context.Animation @@ -254,14 +256,12 @@ class TearsOfGuthixMinigame : InteractionListener, EventHook, MapArea } override fun defineListeners() { - on(Scenery.WEEPING_WALL_6660, SCENERY, "collect-from") { player, node -> animate(player, 2043) val index = TearsOfGuthixGlobalTick.allWalls.indexOf(node.location) setAttribute(player, attributeIsCollecting, index) return@on true } - } // Timer step per tick while you are in the minigame. @@ -330,7 +330,6 @@ class TearsOfGuthixMinigame : InteractionListener, EventHook, MapArea * Global Tick class to randomize the walls consistently for everyone. */ class TearsOfGuthixGlobalTick : TickListener { - companion object { var ticks = 0 var globalWallState = intArrayOf(0, 0, 2, 1, 2, 1, 0, 0, 2, 1) @@ -360,43 +359,29 @@ class TearsOfGuthixGlobalTick : TickListener { globalWallState = intArrayOf(0) + wallStates /* - * Explanation: The walls are layered sceneries, which makes it rabidly fucked to change them. - * What I did was to add the tears scenery first (essentially overriding the tears scenery), - * then add the WEEPING_WALL_6660 right after it so that the interactions are still there. - * this is how a layer is like: - * 1 - WEEPING_WALL_6660 - No model, but holds the option "collect-from" - * 2 - BLUE/GREEN/ABSENT - Model of the blue/green/absent waterfall. - * 3 - WEEPING_WALL_6664 - The actual model, but not interactive. - * 6661 - 6664 is left side, 6665 to 6668 is right side + * All walls have two objects: + * 1 - WEEPING_WALL_6660 (type 0) - No model, but holds the option "collect-from" + * 2 - BLUE/GREEN/ABSENT (type 4) - Model of the blue/green/absent waterfall. + * 6665, 6661, 6665 is blue on the north side + * 6662, 6666, 6662 is green on the south side */ wallStates.forEachIndexed { index, state -> - val scenery = getScenery(allWalls[index + 1])!! - val newSceneryId = if (state == 2) { - if (index + 1 <= 5) { - Scenery.GREEN_TEARS_6662 - } else { - Scenery.GREEN_TEARS_6666 + val wallLocation = allWalls[index + 1] + val currentWaterfall = RegionManager.getObject(wallLocation.x, wallLocation.y, wallLocation.z, -1, 4)!! + val newWaterfallId = when (state) { + 2 -> { + if (index + 1 <= 5) Scenery.GREEN_TEARS_6662 else Scenery.GREEN_TEARS_6666 } - } else if (state == 1) { - if (index + 1 <= 5) { - Scenery.BLUE_TEARS_6661 - } else { - Scenery.BLUE_TEARS_6665 + 1 -> { + if (index + 1 <= 5) Scenery.BLUE_TEARS_6661 else Scenery.BLUE_TEARS_6665 } - } else { - if (index + 1 <= 5) { - Scenery.ABSENCE_OF_TEARS_6663 - } else { - Scenery.ABSENCE_OF_TEARS_6667 + else -> { + if (index + 1 <= 5) Scenery.ABSENCE_OF_TEARS_6663 else Scenery.ABSENCE_OF_TEARS_6667 } } - addScenery(core.game.node.scenery.Scenery( - newSceneryId, - scenery.location, - 4, - scenery.rotation - )) - addScenery(core.game.node.scenery.Scenery(Scenery.WEEPING_WALL_6660, scenery.location, 0, scenery.rotation)) + if (currentWaterfall.id != newWaterfallId) { + SceneryBuilder.replace(currentWaterfall, currentWaterfall.transform(newWaterfallId)) + } } } -} \ No newline at end of file +} diff --git a/Server/src/main/content/region/misthalin/varrock/diary/VarrockAchivementDiary.kt b/Server/src/main/content/region/misthalin/varrock/diary/VarrockAchivementDiary.kt index f49899210..fc103a4ea 100644 --- a/Server/src/main/content/region/misthalin/varrock/diary/VarrockAchivementDiary.kt +++ b/Server/src/main/content/region/misthalin/varrock/diary/VarrockAchivementDiary.kt @@ -120,7 +120,7 @@ class VarrockAchivementDiary : DiaryEventHookBase(DiaryType.VARROCK) { ) override fun onResourceProduced(player: Player, event: ResourceProducedEvent) { - when (player.viewport.region.id) { + when (player.location.regionId) { 12341 -> if (event.itemId == Items.RAW_TROUT_335) { finishTask( player, @@ -172,7 +172,7 @@ class VarrockAchivementDiary : DiaryEventHookBase(DiaryType.VARROCK) { } override fun onInteracted(player: Player, event: InteractionEvent) { - when (player.viewport.region.id) { + when (player.location.regionId) { 12342 -> if (event.target.id == 26934) { finishTask( player, @@ -217,8 +217,8 @@ class VarrockAchivementDiary : DiaryEventHookBase(DiaryType.VARROCK) { override fun onButtonClicked(player: Player, event: ButtonClickEvent) { /* This gets fired even on the login screen, and we don't have a region there, so... */ - player.viewport.region?.let { - when (it.id) { + player.location.regionId.let { + when (it) { 12342 -> { if (event.iface == CanoeListener.CANOE_SHAPING_INTERFACE && event.buttonId == CanoeListener.CANOE_SHAPING_BUTTONS[CanoeListener.Companion.Canoes.WAKA.ordinal]) { diff --git a/Server/src/main/content/region/misthalin/varrock/handlers/StrayDogNPC.java b/Server/src/main/content/region/misthalin/varrock/handlers/StrayDogNPC.java index e2ce0867e..c093b2ef2 100644 --- a/Server/src/main/content/region/misthalin/varrock/handlers/StrayDogNPC.java +++ b/Server/src/main/content/region/misthalin/varrock/handlers/StrayDogNPC.java @@ -69,7 +69,7 @@ public class StrayDogNPC extends AbstractNPC { if (delay < System.currentTimeMillis() && RandomFunction.random(1, 16) == 2) { getPulseManager().clear(); target = null; - players = RegionManager.getLocalPlayers(this, 7); + players = RegionManager.getLocalPlayers(location, 7); if (players.size() != 0) { target = players.get(RandomFunction.random(players.size())); getPulseManager().run(getFollowPulse(target), PulseType.STANDARD); diff --git a/Server/src/main/content/region/misthalin/varrock/quest/dragonslayer/DragonSlayerPlugin.java b/Server/src/main/content/region/misthalin/varrock/quest/dragonslayer/DragonSlayerPlugin.java index f16658df0..3d901c408 100644 --- a/Server/src/main/content/region/misthalin/varrock/quest/dragonslayer/DragonSlayerPlugin.java +++ b/Server/src/main/content/region/misthalin/varrock/quest/dragonslayer/DragonSlayerPlugin.java @@ -36,10 +36,10 @@ import content.data.Quests; */ public final class DragonSlayerPlugin extends OptionHandler { - /** - * Represents the hammer animation. - */ - private static final Animation HAMMER_ANIM = new Animation(3676); + /** + * Represents the hammer animation. + */ + private static final Animation HAMMER_ANIM = new Animation(3676); @Override public Plugin newInstance(Object arg) throws Throwable { @@ -150,7 +150,7 @@ public final class DragonSlayerPlugin extends OptionHandler { ForceMovement movement = new ForceMovement(player, player.getLocation(), player.getLocation().transform(player.getLocation().getX() == 2845 ? 2 : -2, 0, 0), new Animation(839)); movement.run(player, 10); if (player.getLocation().getX() <= 2845) { - List npcs = RegionManager.getLocalNpcs(player); + List npcs = RegionManager.getLocalNPCs(player.getLocation()); for (NPC n : npcs) { if (n.getId() == 742) { n.getProperties().getCombatPulse().attack(player); @@ -165,18 +165,18 @@ public final class DragonSlayerPlugin extends OptionHandler { player.getAchievementDiaryManager().finishTask(player, DiaryType.KARAMJA, 1, 2); break; case 2606: - if (player.getLocation().getY() < 9600 && !player.getSavedData().getQuestData().getDragonSlayerAttribute("memorized") && player.getQuestRepository().getQuest(Quests.DRAGON_SLAYER).getStage(player) != 100) { - player.getPacketDispatch().sendMessage("The door is securely locked."); - } else { - if (!player.getSavedData().getQuestData().getDragonSlayerAttribute("memorized")) { - player.getPacketDispatch().sendMessage("You found a secret door."); - player.getPacketDispatch().sendMessage("You remember where the secret door is for future reference."); - } - player.getAchievementDiaryManager().finishTask(player, DiaryType.KARAMJA, 1, 1); - player.getSavedData().getQuestData().setDragonSlayerAttribute("memorized", true); - DoorActionHandler.handleAutowalkDoor(player, (Scenery) node); - } - break; + if (player.getLocation().getY() < 9600 && !player.getSavedData().getQuestData().getDragonSlayerAttribute("memorized") && player.getQuestRepository().getQuest(Quests.DRAGON_SLAYER).getStage(player) != 100) { + player.getPacketDispatch().sendMessage("The door is securely locked."); + } else { + if (!player.getSavedData().getQuestData().getDragonSlayerAttribute("memorized")) { + player.getPacketDispatch().sendMessage("You found a secret door."); + player.getPacketDispatch().sendMessage("You remember where the secret door is for future reference."); + } + player.getAchievementDiaryManager().finishTask(player, DiaryType.KARAMJA, 1, 1); + player.getSavedData().getQuestData().setDragonSlayerAttribute("memorized", true); + DoorActionHandler.handleAutowalkDoor(player, (Scenery) node); + } + break; case 25154: ClimbActionHandler.climb(player, new Animation(828), new Location(2833, 9658, 0)); break; @@ -206,7 +206,7 @@ public final class DragonSlayerPlugin extends OptionHandler { player.getDialogueInterpreter().sendDialogue("You nail a plank over the hole, but you still need more planks to", "close the hole completely."); } else { player.getSavedData().getQuestData().setDragonSlayerAttribute("repaired", true); - setVarp(player, 177, 1967876); + setVarp(player, 177, 1967876); player.getDialogueInterpreter().sendDialogue("You nail a final plank over the hole. You have successfully patched", "the hole in the ship."); } } diff --git a/Server/src/main/content/region/misthalin/varrock/quest/familycrest/ChronozonCaveZone.kt b/Server/src/main/content/region/misthalin/varrock/quest/familycrest/ChronozonCaveZone.kt index 1ca3002f0..8abc190ea 100644 --- a/Server/src/main/content/region/misthalin/varrock/quest/familycrest/ChronozonCaveZone.kt +++ b/Server/src/main/content/region/misthalin/varrock/quest/familycrest/ChronozonCaveZone.kt @@ -40,7 +40,7 @@ class ChronozonCaveZone: MapZone("FC ChronozoneZone", true), Plugin { ){ // Chronozon is allowed to spawn (quest stage right and the player doesn't have the crest part) // Now check there is not one already - if(!RegionManager.getLocalNpcs(spawnLoc, 5).contains(chronozon)){ + if(!RegionManager.getLocalNPCs(spawnLoc, 5).contains(chronozon)){ chronozon.setPlayer(e) chronozon.isRespawn = false chronozon.location = spawnLoc diff --git a/Server/src/main/content/region/misthalin/varrock/quest/shieldofarrav/ShieldArravPlugin.java b/Server/src/main/content/region/misthalin/varrock/quest/shieldofarrav/ShieldArravPlugin.java index 80a581a22..7c26a2368 100644 --- a/Server/src/main/content/region/misthalin/varrock/quest/shieldofarrav/ShieldArravPlugin.java +++ b/Server/src/main/content/region/misthalin/varrock/quest/shieldofarrav/ShieldArravPlugin.java @@ -106,7 +106,7 @@ public final class ShieldArravPlugin extends OptionHandler { } break; case 767: - List npcs = RegionManager.getLocalNpcs(player); + List npcs = RegionManager.getLocalNPCs(player.getLocation()); NPC master = null; for (NPC n : npcs) { if (n.getId() == 643) { diff --git a/Server/src/main/content/region/misthalin/wiztower/handlers/rcguild/RuneCraftingGuildObjects.java b/Server/src/main/content/region/misthalin/wiztower/handlers/rcguild/RuneCraftingGuildObjects.java deleted file mode 100644 index 600525b1a..000000000 --- a/Server/src/main/content/region/misthalin/wiztower/handlers/rcguild/RuneCraftingGuildObjects.java +++ /dev/null @@ -1,44 +0,0 @@ -package content.region.misthalin.wiztower.handlers.rcguild; - -import core.cache.def.impl.SceneryDefinition; -import core.game.interaction.OptionHandler; -import core.game.node.Node; -import core.game.node.entity.player.Player; -import core.game.node.scenery.Scenery; -import core.game.world.map.Location; -import core.plugin.Initializable; -import core.plugin.Plugin; - -/** - * Created for 2009Scape - * User: Ethan Kyle Millard - * Date: March 25, 2020 - * Time: 7:19 PM - */ -@Initializable -public class RuneCraftingGuildObjects extends OptionHandler { - - - @Override - public boolean handle(Player player, Node node, String option) { - Scenery object = ((Scenery) node); - - switch(object.getId()) { - case 38279: - if (player.getViewport().getRegion().getRegionId() == 12337) { - player.teleport(Location.create(1696, 5460, 2)); - } else { - player.teleport(Location.create(3106, 3160, 1)); - } - break; - } - - return true; - } - - @Override - public Plugin newInstance(Object arg) throws Throwable { - SceneryDefinition.forId(38279).getHandlers().put("option:enter", this); - return this; - } -} diff --git a/Server/src/main/content/region/morytania/handlers/MortMyreGhastNPC.kt b/Server/src/main/content/region/morytania/handlers/MortMyreGhastNPC.kt index cb80df540..19086cf8d 100644 --- a/Server/src/main/content/region/morytania/handlers/MortMyreGhastNPC.kt +++ b/Server/src/main/content/region/morytania/handlers/MortMyreGhastNPC.kt @@ -36,7 +36,9 @@ class MortMyreGhastNPC : AbstractNPC { override fun handleTickActions() { super.handleTickActions() if(id == ids[0] && RandomFunction.roll(35)){ - val players = RegionManager.getLocalPlayers(this, 5).filter { !it.inCombat() } + val players = RegionManager.getLocalPlayers(location, 5).filter { + return@filter !it.inCombat() + } if(players.isNotEmpty()){ val player = players.random() submitIndividualPulse(this, object : MovementPulse(this, player){ @@ -103,4 +105,4 @@ class MortMyreGhastNPC : AbstractNPC { } } } -} \ No newline at end of file +} diff --git a/Server/src/main/content/region/wilderness/handlers/CorporealBeastNPC.java b/Server/src/main/content/region/wilderness/handlers/CorporealBeastNPC.java index 0b71e76e6..2c1a3710d 100644 --- a/Server/src/main/content/region/wilderness/handlers/CorporealBeastNPC.java +++ b/Server/src/main/content/region/wilderness/handlers/CorporealBeastNPC.java @@ -126,7 +126,7 @@ public final class CorporealBeastNPC extends NPCBehavior { public int swing(Entity entity, Entity victim, BattleState state) { // If we're below the right HP threshold, roll a chance to spawn the dark core CorporealBeastNPC corp = (CorporealBeastNPC) ((NPC) entity).behavior; - double thresh = entity.getSkills().getMaximumLifepoints() * (0.3 + (entity.getViewport().getCurrentPlane().getPlayers().size() * 0.05)); + double thresh = entity.getSkills().getMaximumLifepoints() * (0.3 + (RegionManager.getLocalPlayers(entity.getLocation()).size() * 0.05)); if (corp.forceCoreRoll || entity.getSkills().getLifepoints() < thresh) { rollDarkCore(entity, corp, victim); corp.forceCoreRoll = false; @@ -189,7 +189,7 @@ public final class CorporealBeastNPC extends NPCBehavior { int ticks = 1 + (int) Math.ceil(entity.getLocation().getDistance(location) * 0.5); GameWorld.getPulser().submit(new Pulse(ticks) { boolean secondStage = false; - List players = RegionManager.getLocalPlayers(entity); + List players = RegionManager.getLocalPlayers(entity.getLocation()); Location[] locations = null; @Override @@ -245,7 +245,7 @@ public final class CorporealBeastNPC extends NPCBehavior { public boolean doStompAttack(Entity entity) { Location l = entity.getLocation(); List stompTargets = null; - for (Player player : RegionManager.getLocalPlayers(entity, 5)) { + for (Player player : RegionManager.getLocalPlayers(entity.getLocation(), 5)) { Location p = player.getLocation(); if (p.getX() >= l.getX() && p.getY() >= l.getY() && p.getX() < l.getX() + entity.size() && p.getY() < l.getY() + entity.size()) { if (stompTargets == null) { diff --git a/Server/src/main/content/region/wilderness/handlers/DarkEnergyCoreNPC.java b/Server/src/main/content/region/wilderness/handlers/DarkEnergyCoreNPC.java index 0695955ba..2dd7b2ba7 100644 --- a/Server/src/main/content/region/wilderness/handlers/DarkEnergyCoreNPC.java +++ b/Server/src/main/content/region/wilderness/handlers/DarkEnergyCoreNPC.java @@ -9,6 +9,7 @@ import core.game.node.entity.player.Player; import core.game.system.task.Pulse; import core.game.world.GameWorld; import core.game.world.map.Location; +import core.game.world.map.RegionManager; import core.game.world.map.path.Path; import core.game.world.map.path.Pathfinder; import core.plugin.Initializable; @@ -82,7 +83,7 @@ public final class DarkEnergyCoreNPC extends AbstractNPC { } if (ticks % 2 == 0) { boolean jump = true; - for (Player p : getViewport().getCurrentPlane().getPlayers()) { + for (Player p : RegionManager.getLocalPlayers(location)) { if (p.getLocation().withinDistance(getLocation(), 1)) { jump = false; int hit = 5 + RandomFunction.random(6); @@ -93,7 +94,7 @@ public final class DarkEnergyCoreNPC extends AbstractNPC { } if (jump) { Entity victim = master.getProperties().getCombatPulse().getVictim(); - if (++fails >= 3 && victim != null && victim.getViewport().getCurrentPlane() == getViewport().getCurrentPlane()) { + if (++fails >= 3 && victim != null && getLocation().withinDistance(victim.getLocation(), 8)) { Path path = Pathfinder.find(getLocation(), victim.getLocation(), 1); if (path.isSuccessful() || !path.isMoveNear()) { jump(victim.getLocation()); diff --git a/Server/src/main/content/region/wilderness/handlers/WildernessObeliskPlugin.java b/Server/src/main/content/region/wilderness/handlers/WildernessObeliskPlugin.java index 267a5b8cb..e7e5bbcd1 100644 --- a/Server/src/main/content/region/wilderness/handlers/WildernessObeliskPlugin.java +++ b/Server/src/main/content/region/wilderness/handlers/WildernessObeliskPlugin.java @@ -47,12 +47,12 @@ public final class WildernessObeliskPlugin extends OptionHandler { if (stationObelisk == null) { return false; } - if (player.getSkullManager().isDeepWilderness()) { - if (player.getProperties().getCombatPulse().isInCombat()) { - player.sendMessage("You can't use this while in combat."); - return false; - } - } + if (player.getSkullManager().isDeepWilderness()) { + if (player.getProperties().getCombatPulse().isInCombat()) { + player.sendMessage("You can't use this while in combat."); + return false; + } + } for (int i = 0; i < 4; i++) { int x = stationObelisk.getLocation().getX(); int y = stationObelisk.getLocation().getY(); @@ -106,7 +106,7 @@ public final class WildernessObeliskPlugin extends OptionHandler { int index = RandomFunction.random(0, newObelisks.length - 1); //cutting out the last one that is now duplicated Obelisk newObelisk = newObelisks[index]; // Teleport players standing within a 3-by-3 bounding box - for (Player player : RegionManager.getLocalPlayersBoundingBox(center, 1, 1)) { + for (Player player : RegionManager.getLocalPlayers(center, 1)) { if (player.timers.getTimer("teleblock") == null) { player.getPacketDispatch().sendMessage("Ancient magic teleports you somewhere in the wilderness."); int xOffset = player.getLocation().getX() - center.getX(); diff --git a/Server/src/main/content/region/wilderness/handlers/revenants/RevenantController.kt b/Server/src/main/content/region/wilderness/handlers/revenants/RevenantController.kt index fed2f91ec..2ccfbb61c 100644 --- a/Server/src/main/content/region/wilderness/handlers/revenants/RevenantController.kt +++ b/Server/src/main/content/region/wilderness/handlers/revenants/RevenantController.kt @@ -372,7 +372,9 @@ class RevenantController : TickListener, Commands { if (routeIdx == -1) { GameWorld.Pulser.submit(object : Pulse() { override fun pulse(): Boolean { - Graphics.send(Graphics(86), revenantNPC.location) + if (revenantNPC.location.region.isActive) { + Graphics.send(Graphics(86), revenantNPC.location) + } return true } }) diff --git a/Server/src/main/content/region/wilderness/handlers/revenants/RevenantNPC.java b/Server/src/main/content/region/wilderness/handlers/revenants/RevenantNPC.java index 6e0d0e6fb..46d0013f8 100644 --- a/Server/src/main/content/region/wilderness/handlers/revenants/RevenantNPC.java +++ b/Server/src/main/content/region/wilderness/handlers/revenants/RevenantNPC.java @@ -117,7 +117,7 @@ public class RevenantNPC extends AbstractNPC { public void tick() { skills.pulse(); getWalkingQueue().update(); - if (this.getViewport().getRegion().isActive()) { + if (this.getLocation().getRegion().isActive()) { getUpdateMasks().prepare(this); } if (!DeathTask.isDead(this)) { @@ -128,7 +128,7 @@ public class RevenantNPC extends AbstractNPC { lock(3); getProperties().getCombatPulse().delayNextAttack(3); getSkills().heal(maxhp / 6); - for (Player p : RegionManager.getLocalPlayers(this)) { + for (Player p : RegionManager.getLocalPlayers(location)) { playAudio(p, Sounds.EAT_2393); } setAttribute("eat-delay", GameWorld.getTicks() + 6); diff --git a/Server/src/main/core/ServerConstants.kt b/Server/src/main/core/ServerConstants.kt index 8c5f493a4..178604188 100644 --- a/Server/src/main/core/ServerConstants.kt +++ b/Server/src/main/core/ServerConstants.kt @@ -108,7 +108,7 @@ class ServerConstants { //the location where new players are placed on login. @JvmField - var START_LOCATION: Location? = null + var START_LOCATION: Location = Location(2524, 5002, 0) //Location for all home teleports/respawn location @JvmField diff --git a/Server/src/main/core/api/ContentAPI.kt b/Server/src/main/core/api/ContentAPI.kt index 7423518a7..3f5c36f30 100644 --- a/Server/src/main/core/api/ContentAPI.kt +++ b/Server/src/main/core/api/ContentAPI.kt @@ -541,7 +541,10 @@ fun poofClear(npc: NPC) { override fun pulse(): Boolean { when (counter++) { 2 -> { - npc.isInvisible = true; Graphics.send(Graphics(86), npc.location) + npc.isInvisible = true; + if (npc.location.region.isActive) { + Graphics.send(Graphics(86), npc.location) + } } 3 -> npc.clear().also { return true } } @@ -652,7 +655,7 @@ fun addScenery (scenery: Scenery) { * Remove a scenery from the world * @param scenery the Scenery object to remove. */ -fun removeScenery (scenery: Scenery) { +fun removeScenery(scenery: Scenery) { SceneryBuilder.remove(scenery) } @@ -1017,8 +1020,7 @@ fun playAudio(player: Player, id: Int, delay: Int = 0, loops: Int = 1, location: */ @JvmOverloads fun playGlobalAudio(location: Location, id: Int, delay: Int = 0, loops: Int = 1, radius: Int = Audio.defaultAudioRadius) { - val nearbyPlayers = RegionManager.getLocalPlayers(location, radius) - for (player in nearbyPlayers) { + for (player in RegionManager.getLocalPlayers(location, radius)) { PacketRepository.send(AudioPacket::class.java, DefaultContext(player, Audio(id, delay, loops, radius), location)) } } @@ -1078,7 +1080,7 @@ fun findNPC(id: Int): NPC? { * @param z the Z coordinate to use */ fun getScenery(x: Int, y: Int, z: Int): Scenery? { - return RegionManager.getObject(z, x, y) + return RegionManager.getObject(x, y, z) } /** @@ -1106,7 +1108,7 @@ fun findNPC(refLoc: Location, id: Int): NPC? { * @returns an NPC matching the given ID or null if none is found */ fun findLocalNPC(entity: Entity, id: Int): NPC? { - return RegionManager.getLocalNpcs(entity).firstOrNull { it.id == id } + return RegionManager.getLocalNPCs(entity.location).firstOrNull { it.id == id } } /** @@ -1115,8 +1117,8 @@ fun findLocalNPC(entity: Entity, id: Int): NPC? { * @param distance The maximum distance to the entity. * @returns an NPC matching the given ID or null if none is found */ -fun findLocalNPCs(location: Location, distance: Int): MutableList { - return RegionManager.getLocalNpcs(location, distance) +fun findLocalNPCs(location: Location, distance: Int): List { + return RegionManager.getLocalNPCs(location, distance) } @@ -1126,7 +1128,7 @@ fun findLocalNPCs(location: Location, distance: Int): MutableList { * @param ids the IDs of the NPCs to look for */ fun findLocalNPCs(entity: Entity, ids: IntArray): List { - return RegionManager.getLocalNpcs(entity).filter { it.id in ids }.toList() + return RegionManager.getLocalNPCs(entity.location).filter { it.id in ids }.toList() } /** @@ -1136,7 +1138,7 @@ fun findLocalNPCs(entity: Entity, ids: IntArray): List { * @param distance The maximum distance to the entity. */ fun findLocalNPCs(entity: Entity, ids: IntArray, distance: Int): List { - return RegionManager.getLocalNpcs(entity, distance).filter { it.id in ids }.toList() + return RegionManager.getLocalNPCs(entity.location, distance).filter { it.id in ids }.toList() } /** diff --git a/Server/src/main/core/api/regionspec/contracts/FillChunkContract.kt b/Server/src/main/core/api/regionspec/contracts/FillChunkContract.kt index a26d33b92..aefe80795 100644 --- a/Server/src/main/core/api/regionspec/contracts/FillChunkContract.kt +++ b/Server/src/main/core/api/regionspec/contracts/FillChunkContract.kt @@ -1,6 +1,6 @@ package core.api.regionspec.contracts -import core.game.world.map.BuildRegionChunk +import core.game.world.map.Location import core.game.world.map.Region import core.game.world.map.RegionChunk import core.game.world.map.build.DynamicRegion @@ -14,28 +14,22 @@ open class FillChunkContract(var chunk: RegionChunk? = null) : ChunkSpecContract var chunkDelegate: (Int, Int, Int, Region) -> RegionChunk? = {_,_,_,_ -> chunk} override fun populateChunks(dyn: DynamicRegion) { - for(plane in planes) { - for(x in 0 until 8) - for(y in 0 until 8) - if(replaceCondition.invoke(x,y,plane)) { - val chunk = getChunk(x,y,plane,dyn) - dyn.replaceChunk( - plane, - x, - y, - chunk, - sourceRegion - ) + for (plane in planes) { + for (x in 0 until 8) + for (y in 0 until 8) + if (replaceCondition.invoke(x,y,plane)) { + val chunk = getChunk(x,y,plane,dyn) ?: continue + dyn.replaceChunk(plane, x, y, chunk, sourceRegion) afterSetting(chunk, x, y, plane, dyn) } } } - open fun getChunk(x: Int, y: Int, plane: Int, dyn: DynamicRegion) : BuildRegionChunk? { - return chunkDelegate.invoke(x, y, plane, sourceRegion)?.copy(dyn.planes[plane]) + open fun getChunk(x: Int, y: Int, plane: Int, dyn: DynamicRegion) : RegionChunk? { + return chunkDelegate.invoke(x, y, plane, sourceRegion)?.copy() } - open fun afterSetting(chunk: BuildRegionChunk?, x: Int, y: Int, plane: Int, dyn: DynamicRegion) {} + open fun afterSetting(chunk: RegionChunk?, x: Int, y: Int, plane: Int, dyn: DynamicRegion) {} fun from(region: Region): FillChunkContract { this.sourceRegion = region diff --git a/Server/src/main/core/game/activity/ActivityPlugin.java b/Server/src/main/core/game/activity/ActivityPlugin.java index 50ea847f5..b6d7eb5a7 100644 --- a/Server/src/main/core/game/activity/ActivityPlugin.java +++ b/Server/src/main/core/game/activity/ActivityPlugin.java @@ -4,7 +4,6 @@ import core.ServerConstants; import core.game.node.entity.Entity; import core.game.node.entity.player.Player; import core.game.world.map.Location; -import core.game.world.map.Region; import core.game.world.map.build.DynamicRegion; import core.game.world.map.zone.*; import core.game.world.map.zone.impl.MultiwayCombatZone; @@ -108,7 +107,7 @@ public abstract class ActivityPlugin extends MapZone implements Plugin { l = r.getBaseLocation(); } } - ZoneBorders borders = new ZoneBorders(region.getX() << 6, region.getY() << 6, l.getX() + Region.SIZE, l.getY() + Region.SIZE); + ZoneBorders borders = new ZoneBorders(region.getX() << 6, region.getY() << 6, l.getX() + 64, l.getY() + 64); RegionZone multiZone = multicombat ? new RegionZone(MultiwayCombatZone.getInstance(), borders) : null; RegionZone zone = new RegionZone(this, borders); for (DynamicRegion r : regions) { diff --git a/Server/src/main/core/game/activity/CutscenePlugin.java b/Server/src/main/core/game/activity/CutscenePlugin.java index 043b7c179..71bce7d2b 100644 --- a/Server/src/main/core/game/activity/CutscenePlugin.java +++ b/Server/src/main/core/game/activity/CutscenePlugin.java @@ -129,12 +129,9 @@ public abstract class CutscenePlugin extends ActivityPlugin { */ public void end() { if (region != null) { - for (int i = 0; i < region.getPlanes().length; i++) { - for (NPC n : region.getPlanes()[i].getNpcs()) { - if (n == null) { - continue; - } - n.clear(); + for (int z = 0; z < 4; z++) { + for (NPC npc : region.assembleNpcList(z)) { + npc.clear(); } } } diff --git a/Server/src/main/core/game/bots/AIPlayer.java b/Server/src/main/core/game/bots/AIPlayer.java index e54fe732a..8b575559c 100644 --- a/Server/src/main/core/game/bots/AIPlayer.java +++ b/Server/src/main/core/game/bots/AIPlayer.java @@ -34,7 +34,6 @@ import java.util.*; * @author Emperor */ public class AIPlayer extends Player { - /** * The current UID. */ @@ -318,25 +317,25 @@ public class AIPlayer extends Player { int meX = this.getLocation().getX(); int meY = this.getLocation().getY(); ArrayList nodes = new ArrayList(); - for (NPC npc : RegionManager.getLocalNpcs(this, range)) { + for (NPC npc : RegionManager.getLocalNPCs(location, range)) { if (npc.getId() == entry) nodes.add(npc); } for (int x = 0; x < range; x++) { for (int y = 0; y < range - x; y++) { - Node node = RegionManager.getObject(0, meX + x, meY + y); + Node node = RegionManager.getObject(meX + x, meY + y, 0); if (node != null) if (node.getId() == entry) nodes.add(node); - Node node2 = RegionManager.getObject(0, meX + x, meY - y); + Node node2 = RegionManager.getObject(meX + x, meY - y, 0); if (node2 != null) if (node2.getId() == entry) nodes.add(node2); - Node node3 = RegionManager.getObject(0, meX - x, meY + y); + Node node3 = RegionManager.getObject(meX - x, meY + y, 0); if (node3 != null) if (node3.getId() == entry) nodes.add(node3); - Node node4 = RegionManager.getObject(0, meX - x, meY - y); + Node node4 = RegionManager.getObject(meX - x, meY - y, 0); if (node4 != null) if (node4.getId() == entry) nodes.add(node4); @@ -351,25 +350,25 @@ public class AIPlayer extends Player { //int meX2 = this.getLocation().getX(); ArrayList nodes = new ArrayList(); - for (NPC npc : RegionManager.getLocalNpcs(this, range)) { + for (NPC npc : RegionManager.getLocalNPCs(location, range)) { if (entrys.contains(npc.getId())) nodes.add(npc); } for (int x = 0; x < range; x++) { for (int y = 0; y < range - x; y++) { - Node node = RegionManager.getObject(0, meX + x, meY + y); + Node node = RegionManager.getObject(meX + x, meY + y, 0); if (node != null) if (entrys.contains(node.getId())) nodes.add(node); - Node node2 = RegionManager.getObject(0, meX + x, meY - y); + Node node2 = RegionManager.getObject(meX + x, meY - y, 0); if (node2 != null) if (entrys.contains(node2.getId())) nodes.add(node2); - Node node3 = RegionManager.getObject(0, meX - x, meY + y); + Node node3 = RegionManager.getObject(meX - x, meY + y, 0); if (node3 != null) if (entrys.contains(node3.getId())) nodes.add(node3); - Node node4 = RegionManager.getObject(0, meX - x, meY - y); + Node node4 = RegionManager.getObject(meX - x, meY - y, 0); if (node4 != null) if (entrys.contains(node4.getId())) nodes.add(node4); @@ -411,7 +410,7 @@ public class AIPlayer extends Player { public Node getClosesCreature(int radius) { int distance = radius + 1; Node npcReturn = null; - for (NPC npc : RegionManager.getLocalNpcs(this, radius)) { + for (NPC npc : RegionManager.getLocalNPCs(location, radius)) { double distanceToNpc = npc.getLocation().getDistance(this.getLocation()); if ((distanceToNpc) < distance) { distance = (int) distanceToNpc; @@ -424,7 +423,7 @@ public class AIPlayer extends Player { public Node getClosesCreature(int radius, int entry) { int distance = radius + 1; Node npcReturn = null; - for (NPC npc : RegionManager.getLocalNpcs(this, radius)) { + for (NPC npc : RegionManager.getLocalNPCs(location, radius)) { double distanceToNpc = npc.getLocation().getDistance(this.getLocation()); if (npc.getId() == entry) { if ((distanceToNpc) < distance) { @@ -439,7 +438,7 @@ public class AIPlayer extends Player { public Node getClosesCreature(int radius, ArrayList entrys) { int distance = radius + 1; Node npcReturn = null; - for (NPC npc : RegionManager.getLocalNpcs(this, radius)) { + for (NPC npc : RegionManager.getLocalNPCs(location, radius)) { double distanceToNpc = npc.getLocation().getDistance(this.getLocation()); if (entrys.contains(npc.getId())) { if ((distanceToNpc) < distance) { @@ -575,4 +574,4 @@ public class AIPlayer extends Player { public void interact(Node n) { // InteractionPacket.handleObjectInteraction(this, 0, n.getLocation(), n.getId()); } -} \ No newline at end of file +} diff --git a/Server/src/main/core/game/bots/PvMBots.java b/Server/src/main/core/game/bots/PvMBots.java index 6aef6e01e..9962d5461 100644 --- a/Server/src/main/core/game/bots/PvMBots.java +++ b/Server/src/main/core/game/bots/PvMBots.java @@ -5,8 +5,6 @@ import java.util.List; import core.game.consumable.Consumable; import content.data.consumables.Consumables; -import core.game.consumable.Food; -import content.data.consumables.effects.HealingEffect; import core.game.node.entity.skill.Skills; import core.game.node.entity.Entity; import core.game.node.entity.npc.NPC; @@ -33,7 +31,7 @@ public class PvMBots extends AIPlayer { public List FindTargets(Entity entity, int radius) { List targets = new ArrayList<>(20); - Object[] localNPCs = RegionManager.getLocalNpcs(entity,radius).toArray(); + Object[] localNPCs = RegionManager.getLocalNPCs(entity.getLocation(), radius).toArray(); int length = localNPCs.length; if(length > 5){length = 5;} for (int i = 0; i < length; i++) { @@ -95,9 +93,9 @@ public class PvMBots extends AIPlayer { @Override public void tick() { - super.tick(); + super.tick(); - this.tick++; + this.tick++; //Despawn if (this.getSkills().getLifepoints() <= 5){ @@ -109,12 +107,12 @@ public class PvMBots extends AIPlayer { } //Npc Combat - /*if (this.tick % 10 == 0) { - if (!this.inCombat()) - AttackNpcsInRadius(this, 5); - }*/ + /*if (this.tick % 10 == 0) { + if (!this.inCombat()) + AttackNpcsInRadius(this, 5); + }*/ - if (this.tick == 100) this.tick = 0; + if (this.tick == 100) this.tick = 0; //this.eat(); //this.getPrayer().toggle() diff --git a/Server/src/main/core/game/bots/ScriptAPI.kt b/Server/src/main/core/game/bots/ScriptAPI.kt index c2ac69649..9b96aec77 100644 --- a/Server/src/main/core/game/bots/ScriptAPI.kt +++ b/Server/src/main/core/game/bots/ScriptAPI.kt @@ -41,7 +41,6 @@ import core.game.ge.GrandExchange import core.game.ge.GrandExchangeOffer import core.game.interaction.IntType import core.game.interaction.InteractionListeners -import core.tools.SystemLogger import core.game.system.config.ItemConfigParser import core.game.world.GameWorld import core.game.world.repository.Repository @@ -134,24 +133,22 @@ class ScriptAPI(private val bot: Player) { * @author Ceikry */ fun getNearestNodeFromList(acceptedNames: List, isObject: Boolean): Node? { - if (isObject) - return processEvaluationList(RegionManager.forId(bot.location.regionId).planes[bot.location.z].objectList, acceptedName = acceptedNames) - else - return processEvaluationList(RegionManager.forId(bot.location.regionId).planes[bot.location.z].entities, acceptedName = acceptedNames) + val region = RegionManager.forId(bot.location.regionId) + val list = if (isObject) region.assembleObjectList(bot.location.z) else region.assembleNodeList(bot.location.z) + return processEvaluationList(list, acceptedName = acceptedNames) } /** * Gets the nearest node with matching id. * @param id the id to look for - * @param object whether or not the node we are looking for is an object. + * @param isObject whether the node we are looking for is an object. * @return the closest node with matching id or null. * @author Ceikry */ fun getNearestNode(id: Int, isObject: Boolean): Node? { - if (isObject) - return processEvaluationList(RegionManager.forId(bot.location.regionId).planes[bot.location.z].objectList, acceptedId = id) - else - return processEvaluationList(RegionManager.forId(bot.location.regionId).planes[bot.location.z].entities, acceptedId = id) + val region = RegionManager.forId(bot.location.regionId) + val list = if (isObject) region.assembleObjectList(bot.location.z) else region.assembleNodeList(bot.location.z) + return processEvaluationList(list, acceptedId = id) } /** @@ -161,25 +158,28 @@ class ScriptAPI(private val bot: Player) { * @author Ceikry */ fun getNearestNode(entityName: String): Node? { - return processEvaluationList(RegionManager.forId(bot.location.regionId).planes[bot.location.z].entities, acceptedName = listOf(entityName)) + val region = RegionManager.forId(bot.location.regionId) + val list = region.assembleNodeList(bot.location.z) + return processEvaluationList(list, acceptedName = listOf(entityName)) } /** * Gets the nearest node with a matching name. * @param name the name to look for. - * @param object whether or not the node we are looking for is an object. + * @param isObject whether the node we are looking for is an object. * @return the nearest matching node or null. * @author Ceikry */ fun getNearestNode(name: String, isObject: Boolean): Node? { - if (isObject) - return processEvaluationList(RegionManager.forId(bot.location.regionId).planes[bot.location.z].objectList, acceptedName = listOf(name)) - else - return processEvaluationList(RegionManager.forId(bot.location.regionId).planes[bot.location.z].entities, acceptedName = listOf(name)) + val region = RegionManager.forId(bot.location.regionId) + val list = region.assembleNodeList(bot.location.z) + return processEvaluationList(list, acceptedName = listOf(name)) } fun getNearestObjectByPredicate(predicate: (Node?) -> Boolean): Node? { - return processEvaluationList(RegionManager.forId(bot.location.regionId).planes[bot.location.z].objectList, acceptedPredicate = predicate) + val region = RegionManager.forId(bot.location.regionId) + val list = region.assembleObjectList(bot.location.z) + return processEvaluationList(list, acceptedPredicate = predicate) } fun evaluateViability (e: Node?, minDistance: Double, maxDistance: Double, acceptedNames: List? = null, acceptedId: Int = -1, acceptedPredicate: ((Node?) -> Boolean)? = null): Boolean { @@ -268,12 +268,10 @@ class ScriptAPI(private val bot: Player) { fun getNearestGameObject(loc: Location, objectId: Int): Scenery? { var nearestObject: Scenery? = null val minDistance = Double.MAX_VALUE - for (o in RegionManager.forId(loc.regionId).planes[0].objects) { - for (obj in o) { - if (obj != null) { - if (distance(loc, obj) < minDistance && obj.id == objectId) { - nearestObject = obj - } + for (obj in RegionManager.forId(loc.regionId).assembleObjectList(loc.z)) { + if (obj != null) { + if (distance(loc, obj) < minDistance && obj.id == objectId) { + nearestObject = obj } } } @@ -290,7 +288,7 @@ class ScriptAPI(private val bot: Player) { */ private fun findTargets(entity: Entity, radius: Int, name: String? = null): List? { val targets: MutableList = ArrayList() - val localNPCs: Array = RegionManager.getLocalNpcs(entity, radius).toTypedArray() + val localNPCs: Array = RegionManager.getLocalNPCs(entity.location, radius).toTypedArray() var length = localNPCs.size if (length > 5) { length = 5 diff --git a/Server/src/main/core/game/global/action/DoorActionHandler.java b/Server/src/main/core/game/global/action/DoorActionHandler.java index 02234e136..2a7ee7959 100644 --- a/Server/src/main/core/game/global/action/DoorActionHandler.java +++ b/Server/src/main/core/game/global/action/DoorActionHandler.java @@ -1,6 +1,5 @@ package core.game.global.action; -import content.data.Quests; import core.game.node.entity.Entity; import core.game.node.entity.player.Player; import core.game.node.entity.player.link.diary.DiaryType; @@ -137,7 +136,7 @@ public final class DoorActionHandler { return Unit.INSTANCE; }); } - GameWorld.getPulser().submit(new Pulse(1) { + GameWorld.getPulser().submit(new Pulse(1) { boolean opened = false; @Override public boolean pulse() { @@ -161,7 +160,7 @@ public final class DoorActionHandler { } // pass the al kharid gate - if (object.getId() == 35549 || object.getId() == 35551 && player.getViewport().getRegion().getId() == 13106) { + if (object.getId() == 35549 || object.getId() == 35551 && player.getLocation().getRegionId() == 13106) { player.getAchievementDiaryManager().finishTask(player, DiaryType.LUMBRIDGE, 0, 4); } @@ -425,7 +424,7 @@ public final class DoorActionHandler { } object.setCharge(IN_USE_CHARGE); second.setCharge(IN_USE_CHARGE); - GameWorld.getPulser().submit(new Pulse(1) { + GameWorld.getPulser().submit(new Pulse(1) { boolean opened = false; @Override @@ -491,6 +490,10 @@ public final class DoorActionHandler { return null; } + private static Scenery getAdjacentDoor(Location location, int type) { + return RegionManager.getObject(location.getX(), location.getY(), location.getZ(), -1, type); + } + /** * Gets the door next to this door. * @@ -499,18 +502,17 @@ public final class DoorActionHandler { */ public static Scenery getSecondDoor(Scenery object, Entity entity) { Location l = object.getLocation(); - Player player = entity instanceof Player ? (Player) entity : null; Scenery o = null; - if ((o = RegionManager.getObject(l.transform(-1, 0, 0))) != null && o.getName().equals(object.getName())) { + if ((o = getAdjacentDoor(l.transform(-1, 0, 0), object.getType())) != null && o.getName().equals(object.getName())) { return o; } - if ((o = RegionManager.getObject(l.transform(1, 0, 0))) != null && o.getName().equals(object.getName())) { + if ((o = getAdjacentDoor(l.transform(1, 0, 0), object.getType())) != null && o.getName().equals(object.getName())) { return o; } - if ((o = RegionManager.getObject(l.transform(0, -1, 0))) != null && o.getName().equals(object.getName())) { + if ((o = getAdjacentDoor(l.transform(0, -1, 0), object.getType())) != null && o.getName().equals(object.getName())) { return o; } - if ((o = RegionManager.getObject(l.transform(0, 1, 0))) != null && o.getName().equals(object.getName())) { + if ((o = getAdjacentDoor(l.transform(0, 1, 0), object.getType())) != null && o.getName().equals(object.getName())) { return o; } return null; diff --git a/Server/src/main/core/game/interaction/MovementPulse.java b/Server/src/main/core/game/interaction/MovementPulse.java index b378a8a1f..de51f1d13 100644 --- a/Server/src/main/core/game/interaction/MovementPulse.java +++ b/Server/src/main/core/game/interaction/MovementPulse.java @@ -208,7 +208,7 @@ public abstract class MovementPulse extends Pulse { @Override public boolean update() { - if (!mover.getViewport().getRegion().isActive()) + if (!mover.getLocation().getRegion().isActive()) return false; if (!isRunning()) return true; @@ -240,7 +240,7 @@ public abstract class MovementPulse extends Pulse { private boolean tryInteract() { Location ml = mover.getLocation(); // Allow being within 1 square of moving entities to interact with them. - int radius = destination instanceof Entity && ((Entity)destination).getWalkingQueue().hasPath() ? 1 : 0; + int radius = destination instanceof Entity && ((Entity) destination).getWalkingQueue().hasPath() ? 1 : 0; if (interactLocation == null) return false; boolean atInteractLocation = Math.max(Math.abs(ml.getX() - interactLocation.getX()), Math.abs(ml.getY() - interactLocation.getY())) <= radius; @@ -341,7 +341,7 @@ public abstract class MovementPulse extends Pulse { } private boolean validate() { - if (mover == null || destination == null || mover.getViewport().getRegion() == null || hasInactiveNode()) { + if (mover == null || destination == null || mover.getLocation().getRegion() == null || hasInactiveNode()) { return false; } return isRunning(); diff --git a/Server/src/main/core/game/node/entity/Entity.java b/Server/src/main/core/game/node/entity/Entity.java index ffaa8f461..ec659429d 100644 --- a/Server/src/main/core/game/node/entity/Entity.java +++ b/Server/src/main/core/game/node/entity/Entity.java @@ -20,7 +20,6 @@ import core.game.world.map.zone.ZoneRestriction; import org.jetbrains.annotations.NotNull; import core.game.world.GameWorld; import core.game.world.map.Location; -import core.game.world.map.Viewport; import core.game.world.map.path.Path; import core.game.world.map.path.Pathfinder; import core.game.world.map.zone.ZoneMonitor; @@ -70,11 +69,6 @@ public abstract class Entity extends Node { */ private final GameAttributes attributes = new GameAttributes(); - /** - * The entity's viewport. - */ - private final Viewport viewport = new Viewport(); - /** * The pulse manager. */ @@ -114,7 +108,7 @@ public abstract class Entity extends Node { * The mapping of event types to event hooks */ private HashMap, ArrayList> hooks = new HashMap<>(); - public TimerManager timers = new TimerManager(this); + public TimerManager timers = new TimerManager(this); /** * If the entity is invisible. @@ -205,7 +199,7 @@ public abstract class Entity extends Node { */ public void init() { active = true; - TimerRegistry.addAutoTimers (this); + TimerRegistry.addAutoTimers (this); } /** @@ -218,7 +212,7 @@ public abstract class Entity extends Node { Location old = location != null ? location.transform(0, 0, 0) : Location.create(0,0,0); walkingQueue.update(); scripts.postMovement(!Objects.equals(location, old)); - timers.processTimers(); + timers.processTimers(); updateMasks.prepare(this); } @@ -242,7 +236,17 @@ public abstract class Entity extends Node { */ public void clear() { active = false; - viewport.remove(this); + if (location != null) { + // Location can be null if the Entity never got init()ed, for example if NPC(...) was called only to show a face during dialogues. + // In that situation, the NPC never actually got created so it doesn't need to be removed from any chunk. If it had been, however, we do so now. + if (this instanceof Player) { + location.getChunk().removePlayer(asPlayer()); + location.getRegion().decrementViewAmount(); + } + if (this instanceof NPC) { + location.getChunk().remove(asNpc()); + } + } pulseManager.clear(); } @@ -259,8 +263,8 @@ public abstract class Entity extends Node { */ public void fullRestore() { skills.restore(); - timers.removeTimer("poison"); - timers.removeTimer("poison:immunity"); + timers.removeTimer("poison"); + timers.removeTimer("poison:immunity"); } /** @@ -309,12 +313,12 @@ public abstract class Entity extends Node { return false; } - /** - * Should this entity prevent the mover from moving through it? - */ - public boolean shouldPreventStacking(Entity mover) { - return false; - } + /** + * Should this entity prevent the mover from moving through it? + */ + public boolean shouldPreventStacking(Entity mover) { + return false; + } /** * Checks an impact before receiving it. @@ -440,7 +444,7 @@ public abstract class Entity extends Node { * Checks if this entity is attackable by the attacking entity. * @param entity The attacking entity. * @param style The combat style used. - * @param message Whether to send the player a message indicating why the entity isn't attackable. + * @param message Whether to send the player a message indicating why the entity isn't attackable. * @return {@code True} if the attacking entity can attack this entity. */ public boolean isAttackable(Entity entity, CombatStyle style, boolean message) { @@ -651,15 +655,15 @@ public abstract class Entity extends Node { * @return {@code True} if succesful. */ public boolean face(Entity entity) { - if (entity == null) { - int ordinal = EntityFlags.getOrdinal(EFlagType.of(this), EntityFlag.FaceEntity); - if (getUpdateMasks().unregisterSynced(ordinal)) { - return getUpdateMasks().register(EntityFlag.FaceEntity, null); - } - return true; - } - return getUpdateMasks().register(EntityFlag.FaceEntity, entity, true); - } + if (entity == null) { + int ordinal = EntityFlags.getOrdinal(EFlagType.of(this), EntityFlag.FaceEntity); + if (getUpdateMasks().unregisterSynced(ordinal)) { + return getUpdateMasks().register(EntityFlag.FaceEntity, null); + } + return true; + } + return getUpdateMasks().register(EntityFlag.FaceEntity, entity, true); + } /** * Registers a new face location update flag to the update masks. @@ -667,13 +671,13 @@ public abstract class Entity extends Node { * @return {@code True} if succesful. */ public boolean faceLocation(Location location) { - if (location == null) { - int ordinal = EntityFlags.getOrdinal(EFlagType.of(this), EntityFlag.FaceLocation); - getUpdateMasks().unregisterSynced(ordinal); - return true; - } - return getUpdateMasks().register(EntityFlag.FaceLocation, location, true); - } + if (location == null) { + int ordinal = EntityFlags.getOrdinal(EFlagType.of(this), EntityFlag.FaceLocation); + getUpdateMasks().unregisterSynced(ordinal); + return true; + } + return getUpdateMasks().register(EntityFlag.FaceLocation, location, true); + } /** * Registers a new force chat update flag to the update masks. @@ -681,8 +685,8 @@ public abstract class Entity extends Node { * @return {@code True} if successful. */ public boolean sendChat(String string) { - return getUpdateMasks().register(EntityFlag.ForceChat, string); - } + return getUpdateMasks().register(EntityFlag.ForceChat, string); + } /** * Gets the current combat swing handler. @@ -879,13 +883,6 @@ public abstract class Entity extends Node { return walkingQueue; } - /** - * Gets the viewport. - * @return The viewport. - */ - public Viewport getViewport() { - return viewport; - } /** * Gets the skills. @@ -947,7 +944,7 @@ public abstract class Entity extends Node { * @return {@code True} if so. */ public boolean isTeleBlocked() { - return timers.getTimer("teleblock") != null || getLocks().isTeleportLocked() || getZoneMonitor().isRestricted(ZoneRestriction.TELEPORT); + return timers.getTimer("teleblock") != null || getLocks().isTeleportLocked() || getZoneMonitor().isRestricted(ZoneRestriction.TELEPORT); } /** @@ -966,7 +963,7 @@ public abstract class Entity extends Node { this.invisible = invisible; } - public Location getClosestOccupiedTile(@NotNull Location other) { + public Location getClosestOccupiedTile(@NotNull Location other) { List occupied = getOccupiedTiles(); Location closest = location; @@ -982,7 +979,7 @@ public abstract class Entity extends Node { } return closest; - } + } public List getOccupiedTiles() { ArrayList occupied = new ArrayList<>(); @@ -1001,7 +998,7 @@ public abstract class Entity extends Node { return scripts.getDelay() > GameWorld.getTicks(); } - public boolean isTeleporting() { - return getAttribute("tele-pulse", null) != null || properties.getTeleportLocation() != null; - } + public boolean isTeleporting() { + return getAttribute("tele-pulse", null) != null || properties.getTeleportLocation() != null; + } } diff --git a/Server/src/main/core/game/node/entity/combat/CombatPulse.kt b/Server/src/main/core/game/node/entity/combat/CombatPulse.kt index 074a656e9..7a176b1ad 100644 --- a/Server/src/main/core/game/node/entity/combat/CombatPulse.kt +++ b/Server/src/main/core/game/node/entity/combat/CombatPulse.kt @@ -19,6 +19,7 @@ import core.tools.RandomFunction import core.api.* import core.game.interaction.DestinationFlag import core.game.system.timer.impl.* +import core.game.world.map.RegionManager /** * The combat-handling pulse implementation. @@ -116,7 +117,7 @@ class CombatPulse( if (victim == null || DeathTask.isDead(entity) || DeathTask.isDead(victim)) { return true } - if (!entity!!.viewport.region.isActive || !victim!!.viewport.region.isActive) { + if (!RegionManager.forId(entity!!.location.regionId).isActive || !RegionManager.forId(victim!!.location.regionId).isActive) { return true } if (!interactable()) { diff --git a/Server/src/main/core/game/node/entity/combat/CombatSwingHandler.kt b/Server/src/main/core/game/node/entity/combat/CombatSwingHandler.kt index d4324b233..c0fcebf8b 100644 --- a/Server/src/main/core/game/node/entity/combat/CombatSwingHandler.kt +++ b/Server/src/main/core/game/node/entity/combat/CombatSwingHandler.kt @@ -17,7 +17,6 @@ import core.game.world.map.Direction import core.game.world.map.Location import core.game.world.map.RegionManager import core.game.world.map.RegionManager.getClippingFlag -import core.game.world.map.path.Pathfinder import core.game.world.map.path.Pathfinder.* import core.game.world.update.flag.context.Animation import core.tools.RandomFunction @@ -25,7 +24,6 @@ import core.game.system.config.ItemConfigParser import core.tools.Log import org.rs09.consts.Sounds import java.util.* -import kotlin.math.floor /** * Handles a combat swing. diff --git a/Server/src/main/core/game/node/entity/combat/MultihitTargets.kt b/Server/src/main/core/game/node/entity/combat/MultihitTargets.kt new file mode 100644 index 000000000..9901189a0 --- /dev/null +++ b/Server/src/main/core/game/node/entity/combat/MultihitTargets.kt @@ -0,0 +1,66 @@ +package core.game.node.entity.combat + +import content.global.skill.summoning.familiar.Familiar +import core.game.node.entity.Entity +import core.game.node.entity.npc.NPC +import core.game.node.entity.player.Player +import core.game.world.map.Direction.getDirection +import core.game.world.map.RegionManager.getLocalEntities +import core.game.world.map.zone.impl.WildernessZone + +private fun isAttackable(victim: Entity, attacker: Entity, combatStyle: CombatStyle, wilderness: Boolean): Boolean { + if (wilderness) { + return WildernessZone.getInstance().continueAttack(attacker, victim, combatStyle, false) + } + return victim.isAttackable(attacker, combatStyle, false) +} + +fun findMultihitTargets(target: Entity, attacker: Entity, combatStyle: CombatStyle): List { + val targets = getLocalEntities(target.location, 1).filter { it != attacker && it != target && isAttackable(it, attacker, combatStyle, false) } + return listOf(target) + targets.take(9) //https://runescape.wiki/w/Vesta%27s_spear "the opponent and up to 9 additional targets in a 3x3 area around the player"; https://oldschool.runescape.wiki/w/Ice_Barrage "up to nine targets" +} + +fun findMultihitTargetsForDragonHalberd(target: Entity, attacker: Entity): List { + val direction = getDirection(target.location.x - attacker.location.x, target.location.y - attacker.location.y) + val targets = ArrayList() //should not add the victim here + for (delta in listOf(-1, 1)) { + val add = getLocalEntities(target.location.transform(delta * direction.stepY, delta * direction.stepX, 0), 0) + targets += add.filter { it != attacker && isAttackable(it, attacker, CombatStyle.RANGE, false) }.take(1) //RANGE is correct because halberds are able to attack from a distance + } + return targets +} + +fun findMultihitTargetsForD2h(attacker: Entity, combatStyle: CombatStyle): List { + val targets = getLocalEntities(attacker.location, 1).filter { it != attacker && isAttackable(it, attacker, combatStyle, false) } + return targets.filter { it.location.withinDistance(attacker.location, 1) }.take(14) //runescape.wiki/w/Dragon_2h_sword?oldid=859949 "up to 14 enemies in the squares around the player (Only directly in front of and to the sides, it does not attack those diagonal of the player)" +} + +fun findMultihitTargetsForChinchompa(target: Entity, attacker: Entity): List { + val targetCandidates = getLocalEntities(target.location, 1).filter { it != attacker && it != target && isAttackable(it, attacker, CombatStyle.RANGE, false) } + val targets: List? + if (target is Player) { + val players = targetCandidates.filterIsInstance() + fun isFamiliarOf(entity: Entity, players: List): Boolean { + if (entity !is Familiar) { + return false + } + return entity.owner in players && isAttackable(entity.owner, attacker, CombatStyle.RANGE, true) + } + targets = targetCandidates.filter { (it is Player && isAttackable(it, attacker, CombatStyle.RANGE, true)) || isFamiliarOf(it, players) } + } else { + val npcs = targetCandidates.filterIsInstance() + fun isOwnerOf(entity: Entity, npcs: List): Boolean { + if (entity !is Player) { + return false + } + for (npc in npcs) { + if (npc is Familiar && npc.owner == entity) { + return isAttackable(entity, attacker, CombatStyle.RANGE, true) + } + } + return false + } + targets = targetCandidates.filter { it is NPC || isOwnerOf(it, npcs) } + } + return listOf(target) + targets.take(9) //https://runescape.wiki/w/Chinchompa "up to 9 enemies" +} diff --git a/Server/src/main/core/game/node/entity/combat/spell/CombatSpell.java b/Server/src/main/core/game/node/entity/combat/spell/CombatSpell.java index 30cf2f238..ca5e550c9 100644 --- a/Server/src/main/core/game/node/entity/combat/spell/CombatSpell.java +++ b/Server/src/main/core/game/node/entity/combat/spell/CombatSpell.java @@ -1,9 +1,6 @@ package core.game.node.entity.combat.spell; -import content.global.skill.summoning.familiar.Familiar; import core.game.node.entity.combat.BattleState; -import core.game.node.entity.combat.CombatStyle; -import core.game.node.entity.combat.InteractionType; import core.game.node.Node; import core.game.node.entity.Entity; import core.game.node.entity.impl.Animator.Priority; @@ -13,15 +10,10 @@ import core.game.node.entity.player.Player; import core.game.node.entity.player.link.SpellBookManager; import core.game.node.entity.player.link.audio.Audio; import core.game.node.item.Item; -import core.game.world.map.RegionManager; -import core.game.world.map.zone.impl.WildernessZone; import core.game.world.update.flag.context.Animation; import core.game.world.update.flag.context.Graphics; import org.rs09.consts.Sounds; -import java.util.ArrayList; -import java.util.List; - import static core.api.ContentAPIKt.playGlobalAudio; /** @@ -108,43 +100,6 @@ public abstract class CombatSpell extends MagicSpell { } - /** - * Gets a list of valid targets for a multihitting spell. - * @param entity The caster of the spell. - * @param target The primary victim. - * @param max The maximum number of extra victims that may be hit. - * @return The list of targets (the primary target is always at index 0). - */ - public List getMultihitTargets(Entity entity, Entity target, int max) { - List victims = new ArrayList<>(20); - victims.add(target); - - List surrounding = new ArrayList<>(); - surrounding.addAll(RegionManager.getSurroundingPlayers(target)); - surrounding.addAll(RegionManager.getSurroundingNPCs(target)); - - for (Entity e : surrounding) { - if (e == target || e == entity) { - continue; - } - if (CombatStyle.MAGIC.getSwingHandler().canSwing(entity, e) == InteractionType.NO_INTERACT || !e.isAttackable(entity, CombatStyle.MAGIC, false)) { - continue; - } - if (e instanceof Familiar) { - Player owner = ((Familiar) e).getOwner(); - if (owner != entity && WildernessZone.getInstance().continueAttack(entity, owner, CombatStyle.MAGIC, true)) { - victims.add(e); - } - } else { - victims.add(e); - } - if (--max < 1) { - break; - } - } - return victims; - } - /** * Visualizes the impact. * @param entity The entity. @@ -252,4 +207,4 @@ public abstract class CombatSpell extends MagicSpell { return SPLASH_GRAPHIC; } -} \ No newline at end of file +} diff --git a/Server/src/main/core/game/node/entity/impl/ForceMovement.java b/Server/src/main/core/game/node/entity/impl/ForceMovement.java index 8cfc7efd6..f450b6237 100644 --- a/Server/src/main/core/game/node/entity/impl/ForceMovement.java +++ b/Server/src/main/core/game/node/entity/impl/ForceMovement.java @@ -93,7 +93,7 @@ public class ForceMovement extends Pulse { * @param unlockAfter Whether to unlock the entity after the ForceMovement completes * @deprecated this is no longer the preferred way to use force movement. Use the ContentAPI forceMove method instead, please. */ - @Deprecated + @Deprecated public ForceMovement(Entity e, Location start, Location destination, Animation startAnim, Animation animation, Direction direction, int commenceSpeed, int pathSpeed, boolean unlockAfter) { super(1, e); this.entity = e; @@ -116,15 +116,15 @@ public class ForceMovement extends Pulse { * @param speed The path speed. * @deprecated this is no longer the preferred way to use force movement. Use the ContentAPI forceMove method instead, please. */ - @Deprecated + @Deprecated public ForceMovement(Entity e, Location start, Location end, Animation animation, int speed) { this(e, start, end, WALK_ANIMATION, animation, direction(start, end), WALKING_SPEED, speed, true); } - /** + /** * @deprecated this is no longer the preferred way to use force movement. Use the ContentAPI forceMove method instead, please. */ - @Deprecated + @Deprecated public ForceMovement(Entity e, Location destination, int startSpeed, int animSpeed){ this(e,e.getLocation(),destination,WALK_ANIMATION,WALK_ANIMATION,direction(e.getLocation(),destination),startSpeed,animSpeed, true); } @@ -137,7 +137,7 @@ public class ForceMovement extends Pulse { * @param animation the animation. * @deprecated this is no longer the preferred way to use force movement. Use the ContentAPI forceMove method instead, please. */ - @Deprecated + @Deprecated public ForceMovement(Entity e, Location start, Location destination, Animation animation) { this(e, start, destination, WALK_ANIMATION, animation, direction(start, destination), WALKING_SPEED, WALKING_SPEED, true); } @@ -149,7 +149,7 @@ public class ForceMovement extends Pulse { * @param animation the animation. * @deprecated this is no longer the preferred way to use force movement. Use the ContentAPI forceMove method instead, please. */ - @Deprecated + @Deprecated public ForceMovement(Location start, Location destination, Animation animation) { this(null, start, destination, WALK_ANIMATION, animation, direction(start, destination), WALKING_SPEED, WALKING_SPEED, true); } @@ -161,7 +161,7 @@ public class ForceMovement extends Pulse { * @return The created ForceMovement object. * @deprecated this is no longer the preferred way to use force movement. Use the ContentAPI forceMove method instead, please. */ - @Deprecated + @Deprecated public static ForceMovement run(Entity e, Location destination) { return run(e, e.getLocation(), destination, WALK_ANIMATION, WALK_ANIMATION, direction(e.getLocation(), destination), WALKING_SPEED, WALKING_SPEED, true); } @@ -174,7 +174,7 @@ public class ForceMovement extends Pulse { * @return The created ForceMovement object. * @deprecated this is no longer the preferred way to use force movement. Use the ContentAPI forceMove method instead, please. */ - @Deprecated + @Deprecated public static ForceMovement run(Entity e, Location start, Location destination) { return run(e, start, destination, WALK_ANIMATION, WALK_ANIMATION, direction(e.getLocation(), destination), WALKING_SPEED, WALKING_SPEED, true); } @@ -188,7 +188,7 @@ public class ForceMovement extends Pulse { * @return The created ForceMovement object. * @deprecated this is no longer the preferred way to use force movement. Use the ContentAPI forceMove method instead, please. */ - @Deprecated + @Deprecated public static ForceMovement run(Entity e, Location start, Location destination, Animation animation) { return run(e, start, destination, WALK_ANIMATION, animation, direction(start, destination), WALKING_SPEED, WALKING_SPEED, true); } @@ -203,7 +203,7 @@ public class ForceMovement extends Pulse { * @return The created ForceMovement object. * @deprecated this is no longer the preferred way to use force movement. Use the ContentAPI forceMove method instead, please. */ - @Deprecated + @Deprecated public static ForceMovement run(Entity e, Location start, Location destination, Animation animation, int speed) { return run(e, start, destination, WALK_ANIMATION, animation, direction(start, destination), WALKING_SPEED, speed, true); } @@ -217,7 +217,7 @@ public class ForceMovement extends Pulse { * @return The created ForceMovement object. * @deprecated this is no longer the preferred way to use force movement. Use the ContentAPI forceMove method instead, please. */ - @Deprecated + @Deprecated public static ForceMovement run(Entity e, Location start, Location destination, Animation startAnim, Animation animation) { return run(e, start, destination, startAnim, animation, direction(start, destination), WALKING_SPEED, WALKING_SPEED, true); } @@ -232,7 +232,7 @@ public class ForceMovement extends Pulse { * @return The created ForceMovement object. * @deprecated this is no longer the preferred way to use force movement. Use the ContentAPI forceMove method instead, please. */ - @Deprecated + @Deprecated public static ForceMovement run(Entity e, Location start, Location destination, Animation startAnim, Animation animation, Direction direction) { return run(e, start, destination, startAnim, animation, direction, WALKING_SPEED, WALKING_SPEED, true); } @@ -248,15 +248,15 @@ public class ForceMovement extends Pulse { * @return The created ForceMovement object. * @deprecated this is no longer the preferred way to use force movement. Use the ContentAPI forceMove method instead, please. */ - @Deprecated + @Deprecated public static ForceMovement run(Entity e, Location start, Location destination, Animation startAnim, Animation animation, Direction direction, int pathSpeed) { return run(e, start, destination, startAnim, animation, direction, WALKING_SPEED, pathSpeed, true); } - /** + /** * @deprecated this is no longer the preferred way to use force movement. Use the ContentAPI forceMove method instead, please. */ - @Deprecated + @Deprecated public static ForceMovement run(Entity e, Location start, Location destination, Animation startAnim, Animation animation, Direction direction, int commenceSpeed, int pathSpeed) { return run(e, start, destination, startAnim, animation, direction, commenceSpeed, pathSpeed, true); } @@ -271,7 +271,7 @@ public class ForceMovement extends Pulse { * @return The created ForceMovement object. * @deprecated this is no longer the preferred way to use force movement. Use the ContentAPI forceMove method instead, please. */ - @Deprecated + @Deprecated public static ForceMovement run(Entity e, Location start, Location destination, Animation startAnim, Animation animation, Direction direction, int commenceSpeed, int pathSpeed, boolean unlockAfter) { if (startAnim != null) { startAnim.setPriority(Animator.Priority.VERY_HIGH); @@ -285,10 +285,10 @@ public class ForceMovement extends Pulse { GameWorld.getPulser().submit(fm); return fm; } - /* + /* * @deprecated this is no longer the preferred way to use force movement. Use the ContentAPI forceMove method instead, please. */ - @Deprecated + @Deprecated public static ForceMovement run(Entity e, Location destination, int commenceSpeed, int pathSpeed){ return run(e,e.getLocation(),destination,WALK_ANIMATION,WALK_ANIMATION,direction(e.getLocation(),destination),commenceSpeed,pathSpeed, true); } @@ -298,7 +298,7 @@ public class ForceMovement extends Pulse { * @param e the entity. * @deprecated this is no longer the preferred way to use force movement. Use the ContentAPI forceMove method instead, please. */ - @Deprecated + @Deprecated public void run(final Entity e, final int speed) { this.entity = e; int commence = (int) start.getDistance(e.getLocation()); @@ -358,9 +358,9 @@ public class ForceMovement extends Pulse { int ticks = 1 + commenceSpeed + pathSpeed; entity.getImpactHandler().setDisabledTicks(ticks); entity.getUpdateMasks().register(EntityFlag.ForceMove, new ForceMoveCtx(start, destination, commenceSpeed * 30, pathSpeed * 30, direction)); - if(entity instanceof Player) { - entity.getWalkingQueue().updateRegion(destination, false); - } + if (entity instanceof Player) { + entity.getWalkingQueue().updateRegion(start, destination, false); + } super.start(); } diff --git a/Server/src/main/core/game/node/entity/impl/WalkingQueue.java b/Server/src/main/core/game/node/entity/impl/WalkingQueue.java index fbd9d2e36..ba0c2ded4 100644 --- a/Server/src/main/core/game/node/entity/impl/WalkingQueue.java +++ b/Server/src/main/core/game/node/entity/impl/WalkingQueue.java @@ -6,13 +6,8 @@ import core.game.node.entity.player.Player; import core.game.node.entity.skill.Skills; import core.game.node.item.Item; import core.game.node.item.GroundItem; -import core.game.world.map.Direction; -import core.game.world.map.Location; -import core.game.world.map.Point; -import core.game.world.map.RegionManager; -import core.game.world.update.flag.chunk.ItemUpdateFlag; +import core.game.world.map.*; import core.tools.Log; -import core.tools.SystemLogger; import java.util.Deque; import java.util.ArrayDeque; @@ -25,7 +20,6 @@ import static core.api.ContentAPIKt.*; * @author Emperor */ public final class WalkingQueue { - /** * The walking queue. */ @@ -62,7 +56,7 @@ public final class WalkingQueue { */ private Location footPrint; - public ArrayList routeItems = new ArrayList(); + public ArrayList routeItems = new ArrayList(); /** * Constructs a new {@code WalkingQueue} {@code Object}. @@ -80,29 +74,31 @@ public final class WalkingQueue { boolean isPlayer = entity instanceof Player; this.walkDir = -1; this.runDir = -1; - if(entity.getLocation() == null) { + Location prevLocation = entity.getLocation(); + if (prevLocation == null) { return; } if (updateTeleport()) { return; } - if (isPlayer && updateRegion(entity.getLocation(), true)) { + if (isPlayer && updateRegion(prevLocation, entity.getLocation(), true)) { + return; + } + if (hasTimerActive(entity, "frozen")) { return; } - if (hasTimerActive(entity, "frozen")) - return; Point point = walkingQueue.poll(); - boolean drawPath = entity.getAttribute("routedraw", false); + boolean drawPath = entity.getAttribute("routedraw", false); if (point == null) { updateRunEnergy(false); - if (isPlayer && drawPath) { - for (GroundItem item : routeItems) { - if (item != null) { - RegionManager.getRegionPlane(item.getLocation()).remove(item); - } - } - routeItems.clear(); - } + if (isPlayer && drawPath) { + for (GroundItem item : routeItems) { + if (item != null) { + RegionManager.getRegionChunk(item.getLocation()).remove(item); + } + } + routeItems.clear(); + } return; } if (isPlayer && ((Player) entity).getSettings().getRunEnergy() < 1.0) { @@ -174,7 +170,7 @@ public final class WalkingQueue { } footPrint = entity.getLocation(); entity.setLocation(dest); - RegionManager.move(entity); + RegionManager.move(entity, prevLocation, dest); } this.walkDir = walkDirection; this.runDir = runDirection; @@ -190,9 +186,9 @@ public final class WalkingQueue { if (player.getSettings().getWeight() > 0.0) { rate *= 1 + (player.getSettings().getWeight() / 100); } - if (hasTimerActive(player, "hamstrung")) { - rate *= 4; - } + if (hasTimerActive(player, "hamstrung")) { + rate *= 4; + } return rate; } @@ -237,6 +233,7 @@ public final class WalkingQueue { public boolean updateTeleport() { if (entity.getProperties().getTeleportLocation() != null) { reset(false); + Location prevLocation = entity.getLocation(); entity.setLocation(entity.getProperties().getTeleportLocation()); entity.getProperties().setTeleportLocation(null); if (entity instanceof Player) { @@ -245,13 +242,13 @@ public final class WalkingQueue { if (last == null) { last = p.getLocation(); } - if ((last.getRegionX() - entity.getLocation().getRegionX()) >= 4 || (last.getRegionX() - entity.getLocation().getRegionX()) <= -4) { - p.getPlayerFlags().setUpdateSceneGraph(true); - } else if ((last.getRegionY() - entity.getLocation().getRegionY()) >= 4 || (last.getRegionY() - entity.getLocation().getRegionY()) <= -4) { + int rx = last.getRegionX(), ry = last.getRegionY(); + int cx = p.getLocation().getRegionX(), cy = p.getLocation().getRegionY(); + if (Math.abs(rx - cx) >= 4 || Math.abs(ry - cy) >= 4) { p.getPlayerFlags().setUpdateSceneGraph(true); } } - RegionManager.move(entity); + RegionManager.move(entity, prevLocation, entity.getLocation()); footPrint = entity.getLocation(); entity.getProperties().setTeleporting(true); return true; @@ -264,28 +261,19 @@ public final class WalkingQueue { * return true. * @return {@code True} if the region updated, {@code false} if not. */ - public boolean updateRegion(Location location, boolean move) { + public boolean updateRegion(Location prevLocation, Location location, boolean move) { Player p = (Player) entity; Location lastRegion = p.getPlayerFlags().getLastSceneGraph(); if (lastRegion == null) { lastRegion = location; } - int rx = lastRegion.getRegionX(); - int ry = lastRegion.getRegionY(); - int cx = location.getRegionX(); - int cy = location.getRegionY(); - if ((rx - cx) >= 4) { - p.getPlayerFlags().setUpdateSceneGraph(true); - } else if ((rx - cx) <= -4) { - p.getPlayerFlags().setUpdateSceneGraph(true); - } - if ((ry - cy) >= 4) { - p.getPlayerFlags().setUpdateSceneGraph(true); - } else if ((ry - cy) <= -4) { + int rx = lastRegion.getRegionX(), ry = lastRegion.getRegionY(); + int cx = location.getRegionX(), cy = location.getRegionY(); + if (Math.abs(rx - cx) >= 4 || Math.abs(ry - cy) >= 4) { p.getPlayerFlags().setUpdateSceneGraph(true); } if (move && p.getPlayerFlags().isUpdateSceneGraph()) { - RegionManager.move(entity); + RegionManager.move(entity, prevLocation, location); return true; } return false; @@ -320,13 +308,13 @@ public final class WalkingQueue { if (point == null) { return; } - boolean drawRoute = entity.getAttribute("routedraw", false); - if (drawRoute && entity instanceof Player) { - Player p = (Player) entity; - GroundItem item = new GroundItem(new Item(13444), Location.create(x, y, p.getLocation().getZ()), p); - routeItems.add (item); - RegionManager.getRegionPlane(item.getLocation()).add(item); - } + boolean drawRoute = entity.getAttribute("routedraw", false); + if (drawRoute && entity instanceof Player) { + Player p = (Player) entity; + GroundItem item = new GroundItem(new Item(13444), Location.create(x, y, p.getLocation().getZ()), p); + routeItems.add (item); + RegionManager.getRegionChunk(item.getLocation()).add(item); + } int diffX = x - point.getX(), diffY = y - point.getY(); int max = Math.max(Math.abs(diffX), Math.abs(diffY)); for (int i = 0; i < max; i++) { @@ -363,9 +351,7 @@ public final class WalkingQueue { /** * Checks if the entity is running. - * @return {@code True} if a ctrl + click reward was performed,
the - * player has the run option enabled or the NPC is a familiar,

- * {@code false} if not. + * @return {@code True} if a ctrl + click reward was performed, the player has the run option enabled or the NPC is a familiar, {@code False} if not. */ public boolean isRunningBoth() { if (isRunDisabled()) return false; @@ -404,7 +390,6 @@ public final class WalkingQueue { */ public void reset(boolean running) { Location loc = entity.getLocation(); - if (loc == null) { log(this.getClass(), Log.ERR, "The entity location provided was null." diff --git a/Server/src/main/core/game/node/entity/npc/NPC.java b/Server/src/main/core/game/node/entity/npc/NPC.java index 18334251e..97c1fef8b 100644 --- a/Server/src/main/core/game/node/entity/npc/NPC.java +++ b/Server/src/main/core/game/node/entity/npc/NPC.java @@ -142,7 +142,7 @@ public class NPC extends Entity { public NPCBehavior behavior; - public boolean isRespawning = false; + public boolean isRespawning = false; /** * Constructs a new {@code NPC} {@code Object}. @@ -213,8 +213,8 @@ public class NPC extends Entity { getProperties().setSpawnLocation(getLocation()); initConfig(); Repository.getNpcs().add(this); - RegionManager.move(this); - if (getViewport().getRegion().isActive()) { + RegionManager.move(this, null, location); + if (getLocation().getRegion().isActive()) { Repository.addRenderableNPC(this); } interactPlugin.setDefault(); @@ -230,9 +230,9 @@ public class NPC extends Entity { } } behavior.onCreation(this); - // FIXME: hack around MovementPulse's constructor getting run while behavior is null when behavior is set between NPC constructor and init. + // FIXME: hack around MovementPulse's constructor getting run while behavior is null when behavior is set between NPC constructor and init. // FIXME: Commented out as a fix. most npcs were not being able to attack with range/magic due to setting the combat pulse. - // getProperties().setCombatPulse(new CombatPulse(this)); + // getProperties().setCombatPulse(new CombatPulse(this)); } @Override @@ -240,9 +240,7 @@ public class NPC extends Entity { super.clear(); Repository.removeRenderableNPC(this); Repository.getNpcs().remove(this); - getViewport().setCurrentPlane(null); behavior.onRemoval(this); - // getViewport().setRegion(null); } /** @@ -329,7 +327,7 @@ public class NPC extends Entity { */ public boolean openShop(Player player) { if (getName().contains("assistant")) { - NPC n = RegionManager.getNpc(this, getId() - 1); + NPC n = RegionManager.getNpc(location, getId() - 1, 16); if (n != null) { return n.openShop(player); } @@ -425,7 +423,7 @@ public class NPC extends Entity { return false; } return true; - } + } @Override public int getDragonfireProtection(boolean fire) { @@ -434,7 +432,7 @@ public class NPC extends Entity { @Override public void tick() { - if (!getViewport().getRegion().isActive()) { + if (!getLocation().getRegion().isActive()) { onRegionInactivity(); return; } @@ -444,10 +442,10 @@ public class NPC extends Entity { return; } if (isRespawning && respawnTick <= GameWorld.getTicks()) { - behavior.onRespawn(this); + behavior.onRespawn(this); onRespawn(); - fullRestore(); - isRespawning = false; + fullRestore(); + isRespawning = false; } handleTickActions(); super.tick(); @@ -549,10 +547,10 @@ public class NPC extends Entity { nextWalk = GameWorld.getTicks() + 5 + RandomFunction.randomize(10); } - public void resetWalk() { - nextWalk = GameWorld.getTicks() - 1; - getWalkingQueue().reset(); - } + public void resetWalk() { + nextWalk = GameWorld.getTicks() - 1; + getWalkingQueue().reset(); + } /** * Called when the region goes inactive. @@ -570,7 +568,7 @@ public class NPC extends Entity { } } Repository.removeRenderableNPC(this); - if (getViewport().getRegion() instanceof DynamicRegion) { + if (getLocation().getRegion() instanceof DynamicRegion) { clear(); } } @@ -597,7 +595,7 @@ public class NPC extends Entity { Player p = !(killer instanceof Player) ? null : (Player) killer; if (p != null) { p.incrementAttribute("/save:" + STATS_BASE + ":" + STATS_ENEMIES_KILLED); - PlayerStatsCounter.incrementKills(p, originalId); + PlayerStatsCounter.incrementKills(p, originalId); } handleDrops(p, killer); if (!isRespawn()) @@ -608,9 +606,9 @@ public class NPC extends Entity { setRespawnTick(GameWorld.getTicks() + definition.getConfiguration(NPCConfigParser.RESPAWN_DELAY, 17)); } - public void setRespawnTicks (int ticks) { - definition.getHandlers().put(NPCConfigParser.RESPAWN_DELAY, ticks); - } + public void setRespawnTicks (int ticks) { + definition.getHandlers().put(NPCConfigParser.RESPAWN_DELAY, ticks); + } @Override public void commenceDeath(Entity killer) { @@ -687,9 +685,9 @@ public class NPC extends Entity { } getProperties().setAttackStyle(new WeaponInterface.AttackStyle(WeaponInterface.STYLE_CONTROLLED, index)); CombatStyle style = getDefinition().getConfiguration(NPCConfigParser.COMBAT_STYLE); - if (style != null) { - getProperties().getCombatPulse().setStyle(style); - } + if (style != null) { + getProperties().getCombatPulse().setStyle(style); + } if (style == CombatStyle.MAGIC) { getProperties().setAutocastSpell(new DefaultCombatSpell(this)); int spell = definition.getConfiguration("spell_id", -1); @@ -744,10 +742,10 @@ public class NPC extends Entity { configure(); interactPlugin.setDefault(); if (id == originalId) { - int ordinal = EntityFlags.getOrdinal (EFlagType.NPC, EntityFlag.TypeSwap); - getUpdateMasks().unregisterSynced(ordinal); + int ordinal = EntityFlags.getOrdinal (EFlagType.NPC, EntityFlag.TypeSwap); + getUpdateMasks().unregisterSynced(ordinal); } - getUpdateMasks().register(EntityFlag.TypeSwap, id, id != originalId); + getUpdateMasks().register(EntityFlag.TypeSwap, id, id != originalId); return this; } @@ -783,10 +781,10 @@ public class NPC extends Entity { Location returnToSpawnLocation = getProperties().getSpawnLocation().transform(RandomFunction.random(-radius, radius+1), RandomFunction.random(-radius, radius+1), 0); int dist = (int) Location.getDistance(location, returnToSpawnLocation); int pathLimit = 14; - if (dist > pathLimit) { - Vector normalizedDir = Vector.betweenLocs(this.location, returnToSpawnLocation).normalized(); - returnToSpawnLocation = this.location.transform (normalizedDir.times(pathLimit)); - } + if (dist > pathLimit) { + Vector normalizedDir = Vector.betweenLocs(this.location, returnToSpawnLocation).normalized(); + returnToSpawnLocation = this.location.transform (normalizedDir.times(pathLimit)); + } return returnToSpawnLocation; } Location l = movementPath[movementIndex++]; @@ -1059,5 +1057,4 @@ public class NPC extends Entity { public void setNeverWalks(boolean neverWalks) { this.neverWalks = neverWalks; } - } diff --git a/Server/src/main/core/game/node/entity/npc/agg/AggressiveBehavior.java b/Server/src/main/core/game/node/entity/npc/agg/AggressiveBehavior.java index 50f93be3f..16153b211 100644 --- a/Server/src/main/core/game/node/entity/npc/agg/AggressiveBehavior.java +++ b/Server/src/main/core/game/node/entity/npc/agg/AggressiveBehavior.java @@ -116,7 +116,7 @@ public class AggressiveBehavior { */ public List getPossibleTargets(Entity entity, int radius) { List targets = new ArrayList<>(20); - for (Player player : RegionManager.getLocalPlayers(entity, radius)) { + for (Player player : RegionManager.getLocalPlayers(entity.getLocation(), radius)) { if (canSelectTarget(entity, player)) { targets.add(player); } diff --git a/Server/src/main/core/game/node/entity/npc/drop/NPCDropTables.java b/Server/src/main/core/game/node/entity/npc/drop/NPCDropTables.java index baacc117b..951f1d503 100644 --- a/Server/src/main/core/game/node/entity/npc/drop/NPCDropTables.java +++ b/Server/src/main/core/game/node/entity/npc/drop/NPCDropTables.java @@ -111,9 +111,9 @@ public final class NPCDropTables { } return; } - announceIfRare(player, item); + announceIfRare(player, item); if(item.getId() == 6199 && player instanceof Player){ - player.sendMessage("A mystery box has fallen on the ground."); + player.sendMessage("A mystery box has fallen on the ground."); } sendDropMessage(player, npc.getId(), item); if (player == null) { @@ -144,7 +144,7 @@ public final class NPCDropTables { int itemId = item.getDefinition().isUnnoted() ? item.getId() : item.getNoteChange(); if (player != null && npc.getProperties().isMultiZone() && (item.getDefinition().isTradeable() || item.getName().endsWith("charm")) && player.getCommunication().getClan() != null && player.getCommunication().isLootShare() && player.getCommunication().getLootRequirement().ordinal() >= player.getCommunication().getClan().getLootRequirement().ordinal() && !player.getIronmanManager().isIronman()) { Player looter = player; - List players = RegionManager.getLocalPlayers(npc, 16); + List players = RegionManager.getLocalPlayers(npc.getLocation(), 16); List looters = new ArrayList<>(20); for (Player p : players) { if (p != null && p.getCommunication().getClan() != null && p.getCommunication().getClan() == player.getCommunication().getClan() && p.getCommunication().isLootShare() && p.getCommunication().getLootRequirement().ordinal() >= p.getCommunication().getClan().getLootRequirement().ordinal()) { diff --git a/Server/src/main/core/game/node/entity/player/Player.java b/Server/src/main/core/game/node/entity/player/Player.java index d21c591fc..19233647d 100644 --- a/Server/src/main/core/game/node/entity/player/Player.java +++ b/Server/src/main/core/game/node/entity/player/Player.java @@ -131,22 +131,22 @@ public class Player extends Entity { public Boolean isAfkLogout; - private StorageState POHStorageState; + private StorageState POHStorageState; - public final Container POHStorageProxyContainer = new POHStorageProxyContainer(this); + public final Container POHStorageProxyContainer = new POHStorageProxyContainer(this); - public StorageState getPOHStorageState() { - if (POHStorageState == null) { - POHStorageState = new StorageState(this); - } - return POHStorageState; - } + public StorageState getPOHStorageState() { + if (POHStorageState == null) { + POHStorageState = new StorageState(this); + } + return POHStorageState; + } - public void setPOHStorageState(StorageState state) { - this.POHStorageState = state; - } + public void setPOHStorageState(StorageState state) { + this.POHStorageState = state; + } - /** + /** * The inventory. */ private final Container inventory = new Container(28).register(new InventoryListener(this)); @@ -854,11 +854,10 @@ public class Player extends Entity { getInterfaceManager().setChatbox(null); getPulseManager().clear(); getZoneMonitor().getZones().clear(); - getViewport().setCurrentPlane(RegionManager.forId(66666).getPlanes()[3]); playerFlags.setLastSceneGraph(null); playerFlags.setUpdateSceneGraph(false); - playerFlags.setLastViewport(new RegionChunk[Viewport.CHUNK_SIZE][Viewport.CHUNK_SIZE]); - renderInfo.getLocalNpcs().clear(); + playerFlags.setLastViewport(new RegionChunk[MapChunkRenderer.BUILD_AREA_SIZE][MapChunkRenderer.BUILD_AREA_SIZE]); + renderInfo.getLocalNPCs().clear(); renderInfo.getLocalPlayers().clear(); renderInfo.setLastLocation(null); renderInfo.setOnFirstCycle(true); @@ -891,8 +890,8 @@ public class Player extends Entity { * @param login If the player is logging in. */ public void updateSceneGraph(boolean login) { - Region region = getViewport().getRegion(); - if (region instanceof DynamicRegion || region == null && (region = RegionManager.forId(location.getRegionId())) instanceof DynamicRegion) { + Region region = getLocation().getRegion(); + if (region instanceof DynamicRegion) { PacketRepository.send(BuildDynamicScene.class, new DynamicSceneContext(this, login)); } else { PacketRepository.send(UpdateSceneGraph.class, new SceneGraphContext(this, login)); diff --git a/Server/src/main/core/game/node/entity/player/info/RenderInfo.java b/Server/src/main/core/game/node/entity/player/info/RenderInfo.java index 3aa6a7a07..b72c2fcc7 100644 --- a/Server/src/main/core/game/node/entity/player/info/RenderInfo.java +++ b/Server/src/main/core/game/node/entity/player/info/RenderInfo.java @@ -28,7 +28,7 @@ public final class RenderInfo { /** * The list of local NPCs. */ - private List localNpcs = new LinkedList(); + private List localNPCs = new LinkedList(); /** * The appearance time stamps (in millisecond). @@ -89,16 +89,16 @@ public final class RenderInfo { * Gets the localNpcs. * @return The localNpcs. */ - public List getLocalNpcs() { - return localNpcs; + public List getLocalNPCs() { + return localNPCs; } /** * Sets the localNpcs. - * @param localNpcs The localNpcs to set. + * @param localNPCs The localNpcs to set. */ - public void setLocalNpcs(List localNpcs) { - this.localNpcs = localNpcs; + public void setLocalNPCs(List localNPCs) { + this.localNPCs = localNPCs; } /** diff --git a/Server/src/main/core/game/node/entity/player/info/login/LoginConfiguration.java b/Server/src/main/core/game/node/entity/player/info/login/LoginConfiguration.java index fb50ee051..5e6a193ee 100644 --- a/Server/src/main/core/game/node/entity/player/info/login/LoginConfiguration.java +++ b/Server/src/main/core/game/node/entity/player/info/login/LoginConfiguration.java @@ -128,13 +128,14 @@ public final class LoginConfiguration { Repository.getLobbyPlayers().remove(player); player.setPlaying(true); UpdateSequence.getRenderablePlayers().add(player); - RegionManager.move(player); + RegionManager.move(player, null, player.getLocation()); player.getMusicPlayer().init(); player.updateAppearance(); player.getPlayerFlags().setUpdateSceneGraph(true); player.getPacketDispatch().sendInterfaceConfig(226, 1, true); - if(player.getGlobalData().getTestStage() == 3 && !player.getEmoteManager().isUnlocked(Emotes.SAFETY_FIRST)){ + if (player.getGlobalData().getTestStage() == 3 && !player.getEmoteManager().isUnlocked(Emotes.SAFETY_FIRST)) { + //TODO: port this to save version later. when you do, please also do this for the Explore emote, which has a similar problem (e.g. player_name.json has this). player.getEmoteManager().unlock(Emotes.SAFETY_FIRST); } diff --git a/Server/src/main/core/game/node/entity/player/info/login/PlayerSaver.kt b/Server/src/main/core/game/node/entity/player/info/login/PlayerSaver.kt index 351c8dc34..ffac22bf9 100644 --- a/Server/src/main/core/game/node/entity/player/info/login/PlayerSaver.kt +++ b/Server/src/main/core/game/node/entity/player/info/login/PlayerSaver.kt @@ -350,7 +350,6 @@ class PlayerSaver (val player: Player){ globalData.put("zafTime",player.savedData.globalData.zafTime.toString()) globalData.put("fritzGlass",player.savedData.globalData.isFritzGlass) globalData.put("wydinEmployee",player.savedData.globalData.isWydinEmployee) - globalData.put("draynorRecording",player.savedData.globalData.isDraynorRecording) globalData.put("geTutorial",player.savedData.globalData.isGeTutorial) globalData.put("essenceTeleporter",player.savedData.globalData.essenceTeleporter.toString()) globalData.put("recoilDamage",player.savedData.globalData.recoilDamage.toString()) diff --git a/Server/src/main/core/game/node/entity/player/link/GlobalData.java b/Server/src/main/core/game/node/entity/player/link/GlobalData.java index 62873b245..3565b4309 100644 --- a/Server/src/main/core/game/node/entity/player/link/GlobalData.java +++ b/Server/src/main/core/game/node/entity/player/link/GlobalData.java @@ -94,11 +94,6 @@ public final class GlobalData { */ private boolean wydinEmployee; - /** - * Represents if the draynor recording has been seen. - */ - private boolean draynorRecording; - /** * Represents if the ge tutorial has been done. */ @@ -313,7 +308,6 @@ public final class GlobalData { zafTime = Long.parseLong(data.get("zafTime").toString()); fritzGlass = (boolean) data.get("fritzGlass"); wydinEmployee = (boolean) data.get("wydinEmployee"); - draynorRecording = (boolean) data.get("draynorRecording"); geTutorial = (boolean) data.get("geTutorial"); essenceTeleporter = Integer.parseInt( data.get("essenceTeleporter").toString()); recoilDamage = Integer.parseInt( data.get("recoilDamage").toString()); @@ -661,22 +655,6 @@ public final class GlobalData { return zafTime; } - /** - * Gets the draynorRecording. - * @return The draynorRecording. - */ - public boolean isDraynorRecording() { - return draynorRecording; - } - - /** - * Sets the draynorRecording. - * @param draynorRecording The draynorRecording to set. - */ - public void setDraynorRecording(boolean draynorRecording) { - this.draynorRecording = draynorRecording; - } - /** * Gets the wydinEmployee. * @return The wydinEmployee. diff --git a/Server/src/main/core/game/node/entity/player/link/emote/Emotes.java b/Server/src/main/core/game/node/entity/player/link/emote/Emotes.java index 2bda2ca57..40ba72ab1 100644 --- a/Server/src/main/core/game/node/entity/player/link/emote/Emotes.java +++ b/Server/src/main/core/game/node/entity/player/link/emote/Emotes.java @@ -106,7 +106,7 @@ public enum Emotes { @Override public void play(Player player) { if(player.getLocation().getRegionId() == 13206 && !player.getAttribute("mistag-greeted", false)) { - RegionManager.getLocalNpcs(player).forEach(npc -> { + RegionManager.getLocalNPCs(player.getLocation()).forEach(npc -> { if (npc.getId() == 2084 && npc.getLocation().withinDistance(player.getLocation(), 3) && player.getQuestRepository().getQuest(Quests.THE_LOST_TRIBE).getStage(player) == 45) { player.getDialogueInterpreter().open(2084,npc,"greeting"); player.setAttribute("/save:mistag-greeted",true); @@ -422,4 +422,4 @@ public enum Emotes { return lockedMessage; } -} \ No newline at end of file +} diff --git a/Server/src/main/core/game/node/entity/player/link/prayer/Prayer.java b/Server/src/main/core/game/node/entity/player/link/prayer/Prayer.java index 8dcdf8265..b11eac8b2 100644 --- a/Server/src/main/core/game/node/entity/player/link/prayer/Prayer.java +++ b/Server/src/main/core/game/node/entity/player/link/prayer/Prayer.java @@ -112,15 +112,16 @@ public final class Prayer { killer.getImpactHandler().manualHit(player, 1 + RandomFunction.randomize(maximum), HitsplatType.NORMAL); } if (player.getProperties().isMultiZone()) { - @SuppressWarnings("rawtypes") - List targets = null; + List targets; if (killer instanceof NPC) { - targets = RegionManager.getSurroundingNPCs(player, player, killer); + targets = RegionManager.getLocalNPCs(player.getLocation(), 1); } else { - targets = RegionManager.getSurroundingPlayers(player, player, killer); + targets = RegionManager.getLocalPlayers(player.getLocation(), 1); } - for (Object o : targets) { - Entity entity = (Entity) o; + for (Entity entity : targets) { + if (entity == player || entity == killer) { + continue; + } if (entity.isAttackable(player, CombatStyle.MAGIC, false)) { entity.getImpactHandler().manualHit(player, 1 + RandomFunction.randomize(maximum), HitsplatType.NORMAL); } diff --git a/Server/src/main/core/game/node/entity/skill/Skills.java b/Server/src/main/core/game/node/entity/skill/Skills.java index 06a978589..0985d1671 100644 --- a/Server/src/main/core/game/node/entity/skill/Skills.java +++ b/Server/src/main/core/game/node/entity/skill/Skills.java @@ -41,11 +41,6 @@ public final class Skills { */ public double experienceMultiplier = 1.0; - /** - * The maximum experience multiplier. - */ - public static final double MAX_EXPERIENCE_MOD = 60.0; - /** * Represents an array of skill names. */ @@ -252,9 +247,9 @@ public final class Skills { staticLevels[slot] = newLevel; if (entity instanceof Player) { - player.updateAppearance(); - LevelUp.levelup(player, slot, amount); - updateCombatLevel(); + player.updateAppearance(); + LevelUp.levelup(player, slot, amount); + updateCombatLevel(); } } if (entity instanceof Player) { @@ -288,46 +283,6 @@ public final class Skills { //Keywords for people ctrl + Fing the project //xprate xp rate xp multiplier skilling rate return experienceMultiplier; - /*if (!(entity instanceof Player)) { - return 1.0; - } - double mod = multiplyer ? (EXPERIENCE_MULTIPLIER) : 1; - Player p = (Player) entity; - if (p.getIronmanManager().getMode() == IronmanMode.ULTIMATE) { - mod /= 4; - } else if (p.getIronmanManager().getMode() == IronmanMode.STANDARD) { - mod /= 2; - } - //A boost for combat skills that are under level 65. - if(entity instanceof Player && !this.hasLevel(slot, 65) && isCombat(slot)){ - mod *= 1.5; - } - //Grand Exchange region XP boost. - if(entity.getViewport().getRegion().getRegionId() == 12598){ - mod += 1.5; - } - // Pest control, XP halved during the game - if (entity.getViewport().getRegion().getRegionId() == 10536) { - mod *= .5; - } - if (SystemManager.getSystemConfig().isDoubleExp()) { - mod *= 2; - } - if (HolidayEvent.getCurrent() != null) { - HolidayEvent.getCurrent().addExperience(p, slot, experience); - } - p.getAntiMacroHandler().registerExperience(slot, experience); - if (TutorialSession.getExtension(p).getStage() < TutorialSession.MAX_STAGE) { - mod = 1.0; - } else { - if (playerMod && p.getExperienceMod() != 0.0) { - mod *= p.getExperienceMod(); - } - } - if (mod > MAX_EXPERIENCE_MOD ) { - return MAX_EXPERIENCE_MOD; - } - return mod;*/ } /** @@ -721,9 +676,6 @@ public final class Skills { if (prayerPoints < 0) { prayerPoints = 0; } - // if (prayerPoints > staticLevels[PRAYER]) { - // prayerPoints = staticLevels[PRAYER]; - // } if (entity instanceof Player) { PacketRepository.send(SkillLevel.class, new SkillContext((Player) entity, PRAYER)); } diff --git a/Server/src/main/core/game/node/item/GroundItemManager.java b/Server/src/main/core/game/node/item/GroundItemManager.java index 464dbbe25..98bc91ee3 100644 --- a/Server/src/main/core/game/node/item/GroundItemManager.java +++ b/Server/src/main/core/game/node/item/GroundItemManager.java @@ -3,6 +3,7 @@ package core.game.node.item; import core.game.node.entity.player.Player; import core.game.world.GameWorld; import core.game.world.map.Location; +import core.game.world.map.RegionChunk; import core.game.world.map.RegionManager; import core.game.world.update.flag.chunk.ItemUpdateFlag; import core.net.packet.PacketRepository; @@ -81,7 +82,7 @@ public final class GroundItemManager { item.getPlugin().remove(item.getDropper(), item, ItemPlugin.DROP); } item.setRemoved(false); - RegionManager.getRegionPlane(item.getLocation()).add(item); + RegionManager.getRegionChunk(item.getLocation()).add(item); if (GROUND_ITEMS.add(item)) { return item; } @@ -97,7 +98,7 @@ public final class GroundItemManager { return null; } GROUND_ITEMS.remove(item); - RegionManager.getRegionPlane(item.getLocation()).remove(item); + RegionManager.getRegionChunk(item.getLocation()).remove(item); if (item.isAutoSpawn()) { item.respawn(); } @@ -112,7 +113,18 @@ public final class GroundItemManager { * @return The ground item, or {@code null} if the ground item wasn't found. */ public static GroundItem get(int itemId, Location location, Player player) { - return RegionManager.getRegionPlane(location).getItem(itemId, location, player); + RegionChunk chunk = RegionManager.getRegionChunk(location); + for (GroundItem item : chunk.getItems()) { + if (item.getId() == itemId && location.equals(item.getLocation()) && !item.isRemoved()) { + if (!item.isPrivate()) { + return item; + } + if (player != null && item.droppedBy(player)) { + return item; + } + } + } + return null; } /** @@ -155,7 +167,7 @@ public final class GroundItemManager { } } if (!item.isRemoved()) { - RegionManager.getRegionPlane(item.getLocation()).remove(item); + RegionManager.getRegionChunk(item.getLocation()).remove(item); } } else if (!item.isRemainPrivate() && item.getDecayTime() - GameWorld.getTicks() == 100) { RegionManager.getRegionChunk(item.getLocation()).flag(new ItemUpdateFlag(item, ItemUpdateFlag.CONSTRUCT_TYPE)); diff --git a/Server/src/main/core/game/node/scenery/SceneryBuilder.java b/Server/src/main/core/game/node/scenery/SceneryBuilder.java index 3dac14bf0..db45f8942 100644 --- a/Server/src/main/core/game/node/scenery/SceneryBuilder.java +++ b/Server/src/main/core/game/node/scenery/SceneryBuilder.java @@ -15,15 +15,12 @@ import static core.api.ContentAPIKt.log; /** * An aiding class for object constructing/removing. - * * @author Emperor */ public final class SceneryBuilder { - /** * Replaces a scenery. - * - * @param remove The object to remove. + * @param remove The object to remove. * @param construct The object to add. * @return {@code True} if successful. */ @@ -34,9 +31,9 @@ public final class SceneryBuilder { /** * Replaces a scenery. * - * @param remove The object to remove. + * @param remove The object to remove. * @param construct The object to add. - * @param clip If clipping should be adjusted. + * @param clip If clipping should be adjusted. * @return {@code True} if successful. */ public static boolean replace(Scenery remove, Scenery construct, boolean clip, boolean permanent) { @@ -71,9 +68,8 @@ public final class SceneryBuilder { /** * Replaces the object client sided alone. - * - * @param remove The object to remove. - * @param construct The object to replace with. + * @param remove The object to remove. + * @param construct The object to replace with. * @param restoreTicks The restoration ticks. * @return {@code True} if successful. */ @@ -93,9 +89,8 @@ public final class SceneryBuilder { /** * Replaces a scenery temporarily. - * - * @param remove The object to remove. - * @param construct The object to add. + * @param remove The object to remove. + * @param construct The object to add. * @param restoreTicks The amount of ticks before the object gets restored. * @return {@code True} if successful. */ @@ -105,9 +100,8 @@ public final class SceneryBuilder { /** * Replaces a scenery temporarily. - * - * @param remove The object to remove. - * @param construct The object to add. + * @param remove The object to remove. + * @param construct The object to add. * @param restoreTicks The amount of ticks before the object gets restored. * @return {@code True} if successful. */ @@ -151,7 +145,6 @@ public final class SceneryBuilder { /** * Adds a scenery. - * * @param object The object to add. * @return {@code True} if successful. */ @@ -161,10 +154,8 @@ public final class SceneryBuilder { /** * Adds a scenery. - * * @param object The object to add. - * @param ticks The amount of ticks this object should last for (-1 for - * permanent). + * @param ticks The amount of ticks this object should last for (-1 for permanent). * @return {@code True} if successful. */ public static Constructed add(Scenery object, int ticks, final GroundItem... items) { @@ -189,32 +180,8 @@ public final class SceneryBuilder { return constructed; } - /** - * Removes all objects within a box - * @param objectId - the object id to remove - * @param southWest - * @param northEast - * @return - */ - public static boolean removeAll(int objectId, Location southWest, Location northEast) { - if (southWest.getX() > northEast.getX() || southWest.getY() > northEast.getY()) - return false; - - int differenceX = northEast.getX() - southWest.getX(); - int differenceY = northEast.getY() - southWest.getY(); - - for (int x = 0; x <= differenceX; x++) { - for (int y = 0; y <= differenceY; y++){ - Scenery object = new Scenery(objectId, Location.create(southWest.getX() + x, southWest.getY() + y, southWest.getZ())); - remove(object); - } - } - return true; - } - /** * Removes a scenery. - * * @param object The object to remove. * @return {@code True} if successful. */ @@ -233,21 +200,18 @@ public final class SceneryBuilder { /** * Removes a scenery. - * - * @param object the object. + * @param object the object. * @param respawnTicks the respawn ticks. * @return {@code True}if removed. */ public static boolean remove(final Scenery object, int respawnTicks) { if (remove(object)) { GameWorld.getPulser().submit(new Pulse(respawnTicks) { - @Override public boolean pulse() { add(object); return true; } - }); return true; } @@ -256,8 +220,7 @@ public final class SceneryBuilder { /** * Updates the scenery on all the player's screen. - * - * @param objects The scenerys. + * @param objects The sceneries. */ public static void update(Scenery... objects) { for (Scenery o : objects) { diff --git a/Server/src/main/core/game/system/command/MapDumpCommand.kt b/Server/src/main/core/game/system/command/MapDumpCommand.kt index 2743af899..a837ba4a1 100644 --- a/Server/src/main/core/game/system/command/MapDumpCommand.kt +++ b/Server/src/main/core/game/system/command/MapDumpCommand.kt @@ -1,7 +1,6 @@ package core.game.system.command import core.game.node.entity.player.Player -import core.game.system.command.CommandSet import core.game.world.map.Location import core.game.world.map.RegionManager import core.plugin.Initializable @@ -34,7 +33,7 @@ class MapDumpCommand : CommandPlugin() { for (x in 0 until xmax - 1) { for (y in 0 until ymax - 1) { for (z in 0 until zmax - 1) { - val temp = RegionManager.getObject(z, x, y) + val temp = RegionManager.getObject(x, y, z) if (temp != null) { GameObjectMap[Location(x, y, z)] = temp.id } diff --git a/Server/src/main/core/game/system/command/rottenpotato/RottenPotatoExtraDialogue.kt b/Server/src/main/core/game/system/command/rottenpotato/RottenPotatoExtraDialogue.kt index 5528ecf20..8281ef994 100644 --- a/Server/src/main/core/game/system/command/rottenpotato/RottenPotatoExtraDialogue.kt +++ b/Server/src/main/core/game/system/command/rottenpotato/RottenPotatoExtraDialogue.kt @@ -62,7 +62,7 @@ class RottenPotatoExtraDialogue(player: Player? = null) : core.game.dialogue.Dia end() sendInputDialogue(player, InputType.STRING_LONG,"Enter the chat message:"){ value -> val msg = value as String - RegionManager.getLocalNpcs(player).forEach { + RegionManager.getLocalNPCs(player.location).forEach { it.sendChat(msg) } } @@ -71,7 +71,7 @@ class RottenPotatoExtraDialogue(player: Player? = null) : core.game.dialogue.Dia //Kill all nearby NPCs 5 -> { end() - RegionManager.getLocalNpcs(player).forEach { + RegionManager.getLocalNPCs(player.location).forEach { it.finalizeDeath(player) } } diff --git a/Server/src/main/core/game/system/command/sets/DevelopmentCommandSet.kt b/Server/src/main/core/game/system/command/sets/DevelopmentCommandSet.kt index 25ef32fef..43a092aab 100644 --- a/Server/src/main/core/game/system/command/sets/DevelopmentCommandSet.kt +++ b/Server/src/main/core/game/system/command/sets/DevelopmentCommandSet.kt @@ -31,7 +31,7 @@ import core.tools.Log import core.game.node.entity.player.info.Rights import core.game.node.entity.skill.Skills import core.game.world.map.Location -import core.game.world.map.RegionManager.getLocalEntitys +import core.game.world.map.RegionManager import core.game.world.repository.Repository import org.json.simple.JSONArray import kotlin.collections.set @@ -43,6 +43,7 @@ import content.global.activity.penguinhns.PenguinManager.Companion.spawner import content.global.activity.penguinhns.PenguinManager.Companion.tagMapping import content.global.activity.penguinhns.PenguinManager.Companion.updateStoreFile import core.ServerStore.Companion.toJSONArray +import core.game.world.map.RegionManager.getLocalEntities import org.rs09.consts.NPCs @Initializable @@ -120,7 +121,7 @@ class DevelopmentCommandSet : CommandSet(Privilege.ADMIN) { return@define } - define("cs2", Privilege.ADMIN, "::cs2 id args", "Allows you to call arbitrary cs2 scripts during runtime") {player, args -> + define("cs2", Privilege.ADMIN, "::cs2 id args", "Allows you to call arbitrary cs2 scripts during runtime") {player, args -> var scriptArgs = ArrayList() if (args.size == 2) { runcs2(player, args[1].toIntOrNull() ?: return@define) @@ -155,10 +156,6 @@ class DevelopmentCommandSet : CommandSet(Privilege.ADMIN) { sendMessage(player, "Job cleared successfully.") } - define("region", Privilege.STANDARD, "", "Prints your current Region ID.") {player, args -> - sendMessage(player, "Region ID: ${player.viewport.region.regionId}") - } - define("spellbook", Privilege.ADMIN, "::spellbook book ID (0 = MODERN, 1 = ANCIENTS, 2 = LUNARS)", "Swaps your spellbook to the given book ID."){player, args -> if(args.size < 2){ reject(player,"Usage: ::spellbook [int]. 0 = MODERN, 1 = ANCIENTS, 2 = LUNARS") @@ -460,31 +457,6 @@ class DevelopmentCommandSet : CommandSet(Privilege.ADMIN) { target.skills.addExperience(skill, xp!!) } - define("renewpenguins", Privilege.ADMIN, "", "Generates a fresh set of weekly penguins") { player, _ -> - val spawnedOrdinals = (PenguinHNSEvent.getStoreFile()["spawned-penguins"] as JSONArray).map { it.toString().toInt() } - val penguinNPCs = arrayListOf(NPCs.BARREL_8104, NPCs.BUSH_8105,NPCs.CACTUS_8107,NPCs.CRATE_8108,NPCs.ROCK_8109,NPCs.TOADSTOOL_8110) - - spawnedOrdinals.forEach { - val peng = Penguin.values()[it] - val nearNPCs = getLocalEntitys(peng.location,1) - nearNPCs.forEach { npc -> - if (npc.id in penguinNPCs) { - poofClear(npc as NPC) - } - } - } - penguins = spawner.spawnPenguins(10) - PenguinHNSEvent.getStoreFile()["spawned-penguins"] = penguins.toJSONArray() - tagMapping.clear() - for (p in penguins) { - tagMapping.put(p, JSONArray()) - val pengCoord = Penguin.values()[p].location - player.debug("Penguin spawned at:$pengCoord") - } - updateStoreFile() - player.debug("Penguin positions have been renewed") - } - define("spawnpenguin",Privilege.ADMIN,"::spawnPenguin Ordinal","Adds a new Penguin spawn to this weeks list based on the ordinal provided 0-64"){player,args-> if (args.size!=2) reject (player,"Usage: ::spawnpenguin Ordinal") val ordinal = args[1].toIntOrNull() @@ -507,6 +479,5 @@ class DevelopmentCommandSet : CommandSet(Privilege.ADMIN) { val pengCoords = peng.location player.debug("Penguin spawned at:$pengCoords") } - - } + } } diff --git a/Server/src/main/core/game/system/command/sets/FunCommandSet.kt b/Server/src/main/core/game/system/command/sets/FunCommandSet.kt index 05b59c153..c65db80df 100644 --- a/Server/src/main/core/game/system/command/sets/FunCommandSet.kt +++ b/Server/src/main/core/game/system/command/sets/FunCommandSet.kt @@ -27,7 +27,6 @@ import org.rs09.consts.Sounds import java.awt.HeadlessException import java.awt.Toolkit import java.awt.datatransfer.StringSelection -import kotlin.streams.toList @Initializable class FunCommandSet : CommandSet(Privilege.ADMIN) { @@ -99,7 +98,7 @@ class FunCommandSet : CommandSet(Privilege.ADMIN) { if (args.size < 2) { reject(player, "Syntax error: ::npcanim ") } - npcs = RegionManager.getLocalNpcs(player.location, 10) + npcs = RegionManager.getLocalNPCs(player.location, 10) for (n in npcs) { n.lock(6) n.faceTemporary(player, 6) @@ -356,10 +355,13 @@ class FunCommandSet : CommandSet(Privilege.ADMIN) { if (args.size != 2) reject(player, "Usage: ::barrage radius[max = 50]") val radius = if (args[1].toInt() > 50) 50 else args[1].toInt() - val nearbyPlayers = RegionManager.getLocalPlayers(player, radius).stream().filter { p: Player -> p.username != player.username }.toList() + val nearbyPlayers = RegionManager.getLocalPlayers(player.location, radius) animate(player, 1978) playGlobalAudio(player.location, Sounds.ICE_CAST_171) for (p in nearbyPlayers) { + if (p.name == player.name) { + continue + } playGlobalAudio(p.location, Sounds.ICE_BARRAGE_IMPACT_168, 20) val impactAmount = if (p.skills.lifepoints < 10 ) 0 else RandomFunction.getRandom(3) impact(p, impactAmount, ImpactHandler.HitsplatType.NORMAL) diff --git a/Server/src/main/core/game/system/command/sets/MiscCommandSet.kt b/Server/src/main/core/game/system/command/sets/MiscCommandSet.kt index 0727f65db..3c1bc2443 100644 --- a/Server/src/main/core/game/system/command/sets/MiscCommandSet.kt +++ b/Server/src/main/core/game/system/command/sets/MiscCommandSet.kt @@ -115,7 +115,7 @@ class MiscCommandSet : CommandSet(Privilege.ADMIN){ */ define("loc", Privilege.STANDARD, "", "Prints quite a lot of information about your current location."){ player, _-> val l = player.location - val r = player.viewport.region + val r = l.region var obj: Scenery? = null notify(player,"Absolute: " + l + ", regional: [" + l.localX + ", " + l.localY + "], chunk: [" + l.chunkOffsetX + ", " + l.chunkOffsetY + "], flag: [" + RegionManager.isTeleportPermitted(l) + ", " + RegionManager.getClippingFlag(l) + ", " + RegionManager.isLandscape(l) + "].") notify(player,"Region: [id=" + l.regionId + ", active=" + r.isActive + ", instanced=" + (r is DynamicRegion) + "], obj=" + RegionManager.getObject(l) + ".") diff --git a/Server/src/main/core/game/system/command/sets/SpawnCommandSet.kt b/Server/src/main/core/game/system/command/sets/SpawnCommandSet.kt index 468bdaf96..7649bb414 100644 --- a/Server/src/main/core/game/system/command/sets/SpawnCommandSet.kt +++ b/Server/src/main/core/game/system/command/sets/SpawnCommandSet.kt @@ -3,13 +3,17 @@ package core.game.system.command.sets import core.api.log import core.api.sendMessage import core.cache.Cache -import core.game.node.scenery.Scenery -import core.game.node.scenery.SceneryBuilder import core.game.node.entity.npc.NPC 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.system.command.CommandPlugin -import core.plugin.Initializable import core.game.system.command.Privilege +import core.game.world.map.RegionManager +import core.game.world.map.RegionManager.getObject +import core.game.world.map.RegionManager.getRegionChunk +import core.plugin.Initializable import core.tools.Log import java.awt.Toolkit import java.awt.datatransfer.StringSelection @@ -142,5 +146,53 @@ class SpawnCommandSet : CommandSet(Privilege.ADMIN){ } } } + + define("removeobject", Privilege.ADMIN, "::removeobject", "Removes the first scenery at your current coordinates (the meaning of 'the first' is currently arbitrary, do not use this in production)") { player, args -> + if (args.size != 1) reject(player, "::removeobject doesn't support arguments") + val obj = getObject(player.location) + if (obj == null) { + sendMessage(player, "All four objects on the tile were null.") + return@define + } + sendMessage(player, "The first object found on the tile was ${obj.id}; it will now be removed.") + SceneryBuilder.remove(obj) + } + + define("objects", Privilege.STANDARD, "::objects", "Prints a list of all ten sceneries at your current coordinates") { player, args -> + if (args.size != 1) reject(player, "::objects doesn't support arguments") + val chunk = getRegionChunk(player.location) + fun dump(label: String, objects: List) { + sendMessage(player, "--- $label ---") + val nulls = ArrayList(10) + for ((i, o) in objects.withIndex()) { + if (o == null) { + nulls.add(i) + } else { + val c = if (o is Constructed) "C" else "" + val r = if (o.isRenderable) "R" else "" + val a = if (o.isActive) "A" else "" + var props = c + r + a + if (props.isNotEmpty()) { + props = " *$props" + } + sendMessage(player, " $i: $o$props") + } + } + if (nulls.isNotEmpty()) { + sendMessage(player, " ${nulls.joinToString()}: null") + } + } + val stat = ArrayList(10) + for (i in 0 until 4) { + val obj = chunk.statObjects[player.location.chunkOffsetX][player.location.chunkOffsetY][i] + if (obj != null) { + stat.add(obj) + } + } + dump("Chunk static objects", stat) + dump("Chunk dynamic objects", chunk.getObjects(player.location.chunkOffsetX, player.location.chunkOffsetY).asList()) + dump("RegionManager", listOf(getObject(player.location))) //getObject can only get one + sendMessage(player, "--- That's all, folks! ---") + } } } diff --git a/Server/src/main/core/game/system/command/sets/TeleportCommandSet.kt b/Server/src/main/core/game/system/command/sets/TeleportCommandSet.kt index 30e4107c9..e8d7b84f3 100644 --- a/Server/src/main/core/game/system/command/sets/TeleportCommandSet.kt +++ b/Server/src/main/core/game/system/command/sets/TeleportCommandSet.kt @@ -100,69 +100,6 @@ class TeleportCommandSet : CommandSet(Privilege.ADMIN){ player.properties.teleportLocation = Location.create(args[1].toInt(), args[2].toInt(), if (args.size > 3) args[3].toInt() else 0) } - /** - * Teleport to the first object with the given name in the given regionX regionY - */ - define("teleobj", Privilege.ADMIN, "::teleobj RX_RY OBJ NAME", "Teleports to the first object with the given name."){player, args -> - if(args.size < 3) reject(player, "Usage: regionX_regionY Object Name") - var objName = "" - for(i in 2 until args.size) objName += (args[i] + if(i + 1 == args.size) "" else " ") - - val tokens = args[1].split("_") - if(tokens.size != 2) reject(player, "Usage: regionX_regionY Object Name") - val regionX = tokens[0].toInt() - val regionY = tokens[1].toInt() - - val regionId = (regionX shl 8) or regionY - val region = RegionManager.forId(regionId) - - GlobalScope.launch { - for (plane in region.planes) { - for (objects in plane.objects.filterNotNull()) { - for (parent in objects.filterNotNull()) { - if (parent.childs != null) { - for (obj in parent.childs.filterNotNull()) { - if (obj.name.equals(objName, ignoreCase = true)) { - player.properties.teleportLocation = obj.location - return@launch - } - } - } else { - if (parent.name.equals(objName, ignoreCase = true)) { - player.properties.teleportLocation = parent.location - return@launch - } - } - } - } - } - } - } - - /** - * Finds a list of objects/sceneries in a region - */ - define("findobjs", Privilege.ADMIN, "::findobjs REGION ID SCENERY ID", "Finds all locations of scenery objects of id."){player, args -> - if(args.size < 4) reject(player, "Usage: region_id scenery_id") - val regionId = args[1].toInt() - val sceneryId = args[2].toInt() - val sceneryIdEnd = args[3].toInt() - - val region = RegionManager.forId(regionId) - - GlobalScope.launch { - for (plane in region.planes) { - for (objects in plane.objects.filterNotNull()) { - for (parent in objects.filterNotNull()) { - if (parent.id in sceneryId..sceneryIdEnd) { - println(parent.location) - } - } - } - } - } - } - /** * Teleport to a specific player */ diff --git a/Server/src/main/core/game/system/config/ConfigParser.kt b/Server/src/main/core/game/system/config/ConfigParser.kt index db7e1b0f3..fd88dfdfa 100644 --- a/Server/src/main/core/game/system/config/ConfigParser.kt +++ b/Server/src/main/core/game/system/config/ConfigParser.kt @@ -16,6 +16,7 @@ class ConfigParser : Commands { ItemConfigParser().load() ObjectConfigParser().load() XteaParser().load() + ObjectOverrideParser().load() InterfaceConfigParser().load() } fun postPlugin() { @@ -31,7 +32,12 @@ class ConfigParser : Commands { } fun reloadConfigs(callback: () -> Unit) { - GlobalScope.launch { + GlobalScope.launch { + RegionManager.apply { r -> + r.addSceneries.clear() + r.removeSceneries.clear() + } + Repository.npcs.toTypedArray().forEach { npc -> npc.isRespawn = false npc.clear() @@ -41,7 +47,7 @@ class ConfigParser : Commands { GroundItemManager.getItems().toTypedArray().forEach {gi -> GroundItemManager.getItems().remove(gi) - RegionManager.getRegionPlane(gi.location).remove(gi) + RegionManager.getRegionChunk(gi.location).remove(gi) } prePlugin() diff --git a/Server/src/main/core/game/system/config/ObjectOverrideParser.kt b/Server/src/main/core/game/system/config/ObjectOverrideParser.kt new file mode 100644 index 000000000..c75840039 --- /dev/null +++ b/Server/src/main/core/game/system/config/ObjectOverrideParser.kt @@ -0,0 +1,57 @@ +package core.game.system.config + +import core.ServerConstants +import core.api.log +import core.game.node.scenery.Scenery +import core.game.world.map.Location +import core.game.world.map.RegionManager +import core.tools.Log +import org.json.simple.JSONArray +import org.json.simple.JSONObject +import org.json.simple.parser.JSONParser +import java.io.FileReader + +class ObjectOverrideParser { + val parser = JSONParser() + var reader: FileReader? = null + fun load() { + var count = 0 + reader = FileReader(ServerConstants.CONFIG_PATH + "object_overrides.json") + val overridelist = parser.parse(reader) as JSONArray + for (entry in overridelist) { + val e = entry as JSONObject + + // Mandatory fields + val mode = e["mode"].toString() + val id = e["id"].toString().toInt() + val loc = Location.fromString(e["location"].toString()) + + // Optional fields + val type = e["type"]?.toString()?.toInt() ?: 10 + val direction = e["direction"]?.toString() ?: "n" + + // Now create a Scenery object and assign it to its region + val rotation = when (direction) { + "n" -> 1 + "ne" -> 2 + "nw" -> 0 + "w" -> 3 + "e" -> 4 + "sw" -> 5 + "se" -> 7 + "s" -> 6 + else -> 1 + } + val obj = Scenery(id, loc, type, rotation) + val region = RegionManager.forId(loc.regionId) + when (mode) { + "remove" -> region.removeSceneries.add(obj) //type and rotation are currently ignored, but this can be changed in Region.java if needed + "add" -> region.addSceneries.add(obj) + else -> log(this::class.java, Log.ERR, "Ignored unknown ObjectOverride mode $mode!") + } + count++ + } + log(this::class.java, Log.FINE, "Parsed $count object overrides.") + } +} + diff --git a/Server/src/main/core/game/world/GameWorld.kt b/Server/src/main/core/game/world/GameWorld.kt index 2c0b55e2c..a7b5e1f0b 100644 --- a/Server/src/main/core/game/world/GameWorld.kt +++ b/Server/src/main/core/game/world/GameWorld.kt @@ -18,7 +18,9 @@ import core.ServerStore import core.auth.AuthProvider import core.auth.Auth import core.game.system.config.ConfigParser +import core.game.world.map.RegionChunk import core.game.world.repository.Repository +import core.game.world.update.ChunkUpdateTracker import core.plugin.ClassScanner import core.storage.AccountStorageProvider import core.tools.Log @@ -164,6 +166,7 @@ object GameWorld { fun prompt(run: Boolean, directory: String?){ log(GameWorld::class.java, Log.FINE, "Prompting ${settings?.name} Game World...") Cache.init(ServerConstants.CACHE_PATH) + RegionChunk.dirtyListener = ChunkUpdateTracker //go overboard with checks to make sure dev mode authenticator never triggers on live Auth.configure() ConfigParser().prePlugin() diff --git a/Server/src/main/core/game/world/map/BuildRegionChunk.java b/Server/src/main/core/game/world/map/BuildRegionChunk.java deleted file mode 100644 index 008937301..000000000 --- a/Server/src/main/core/game/world/map/BuildRegionChunk.java +++ /dev/null @@ -1,336 +0,0 @@ -package core.game.world.map; - - -import core.game.node.entity.player.Player; -import core.game.node.item.GroundItem; -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.tools.Log; -import core.tools.SystemLogger; -import core.game.world.map.build.LandscapeParser; -import core.net.packet.IoBuffer; -import core.net.packet.out.ClearScenery; -import core.net.packet.out.ConstructGroundItem; -import core.net.packet.out.ConstructScenery; - -import static core.api.ContentAPIKt.log; - -import java.util.ArrayList; -import java.util.Arrays; - -/** - * A region chunk, used for easily modifying objects. - * @author Emperor - * - */ -public class BuildRegionChunk extends RegionChunk { - - /** - * The maximum amount of objects to be stored on one tile in the chunk. - */ - public static final int ARRAY_SIZE = 10; - - /** - * The list of changes made. - */ - private final Scenery[][][] objects; - - /** - * Constructs a new {@code BuildRegionChunk} {@code Object} - * @param base The base location. - * @param rotation The rotation. - * @param plane The region plane. - */ - public BuildRegionChunk(Location base, int rotation, RegionPlane plane) { - super(base, rotation, plane); - this.objects = new Scenery[ARRAY_SIZE][8][8]; - } - - public BuildRegionChunk(Location base, int rotation, RegionPlane plane, Scenery[][] objects) { - this(base, rotation, plane); - for (int x = 0; x < SIZE; x++) { - for (int y = 0; y < SIZE; y++) { - if(objects[x][y] != null) { - this.objects[0][x][y] = new Scenery(objects[x][y]); - } - } - } - } - - @Override - protected boolean appendUpdate(Player player, IoBuffer buffer) { - boolean updated = false;//super.appendUpdate(player, buffer); - for (int i = 0; i < objects.length; i++) { - for (int x = 0; x < SIZE; x++) { - for (int y = 0; y < SIZE; y++) { - Scenery o = objects[i][x][y]; - if (o instanceof Constructed) { - ConstructScenery.write(buffer, o); - updated = true; - } - else if (o != null && !o.isRenderable()) { - ClearScenery.write(buffer, o); - updated = true; - } - } - } - } - ArrayList totalItems = drawItems(items, player); - for (GroundItem item : totalItems) { - if (item != null && item.isActive() && item.getLocation() != null) { - if (!item.isPrivate() || item.droppedBy(player)) { - ConstructGroundItem.write(buffer, item); - updated = true; - } - } - } - return updated; - } - - @Override - public void rotate(Direction direction) { - if (rotation != 0) { - log(this.getClass(), Log.ERR, "Region chunk was already rotated!"); - return; - } - Scenery[][][] copy = new Scenery[ARRAY_SIZE][SIZE][SIZE]; - int baseX = currentBase.getLocalX(); - int baseY = currentBase.getLocalY(); - for (int x = 0; x < SIZE; x++) { - for (int y = 0; y < SIZE; y++) { - for (int i = 0; i < objects.length; i++) { - Scenery object = copy[i][x][y] = objects[i][x][y]; - if (object != null) { - SceneryBuilder.remove(object); - this.remove(object); - } - } - } - } - clear(); - switch(direction) { - case NORTH: rotation = 0; break; - case EAST: rotation = 1; break; - case SOUTH: rotation = 2; break; - case WEST: rotation = 3; break; - default: rotation = (direction.toInteger() + (direction.toInteger() % 2 == 0 ? 2 : 0)) % 4; - log(this.getClass(), Log.ERR, "Attempted to rotate a chunk in a non-cardinal direction - using fallback rotation code. This should be investigated!"); - break; - }; - for (int i = 0; i < objects.length; i++) { - for (int x = 0; x < SIZE; x++) { - for (int y = 0; y < SIZE; y++) { - Scenery object = copy[i][x][y]; - if (object != null) { - int[] pos = getRotatedPosition(x, y, object.getDefinition().getSizeX(), object.getDefinition().getSizeY(), object.getRotation(), rotation); - Scenery obj = object.transform(object.getId(), (object.getRotation() + rotation) % 4, object.getLocation().transform(pos[0] - x, pos[1] - y, 0)); - if (object instanceof Constructed) { - obj = obj.asConstructed(); - } - obj.setActive(object.isActive()); - obj.setRenderable(object.isRenderable()); - LandscapeParser.flagScenery(plane, baseX + pos[0], baseY + pos[1], obj, true, true); - } - } - } - } - } - - @Override - public BuildRegionChunk copy(RegionPlane plane) { - BuildRegionChunk chunk = new BuildRegionChunk(base, rotation, plane); - for (int i = 0; i < chunk.objects.length; i++) { - for (int x = 0; x < SIZE; x++) { - for (int y = 0; y < SIZE; y++) { - Scenery o = objects[i][x][y]; - if (o instanceof Constructed) { - chunk.objects[i][x][y] = o.transform(o.getId(), o.getRotation()).asConstructed(); - } - else if (o != null) { - chunk.objects[i][x][y] = o.transform(o.getId()); - chunk.objects[i][x][y].setActive(o.isActive()); - chunk.objects[i][x][y].setRenderable(o.isRenderable()); - } - } - } - } - return chunk; - } - - @Override public void rebuildFlags(RegionPlane from) { - for (int x = 0; x < 8; x++) { - for (int y = 0; y < 8; y++) { - Location loc = currentBase.transform(x,y,0); - Location fromLoc = base.transform(x,y,0); - plane.getFlags().getLandscape()[loc.getLocalX()][loc.getLocalY()] = from.getFlags().getLandscape()[fromLoc.getLocalX()][fromLoc.getLocalY()]; - plane.getFlags().clearFlag(x, y); - for (int i = 0; i < ARRAY_SIZE; i++) { - Scenery obj = objects[i][x][y]; - if (obj != null) - LandscapeParser.applyClippingFlagsFor(plane, loc.getLocalX(), loc.getLocalY(), obj); - } - } - } - } - - @Override - public void clear() { - super.clear(); - for (int i = 0; i < objects.length; i++) { - for (int x = 0; x < objects[i].length; x++) { - for (int y = 0; y < objects[i][x].length; y++) { - objects[i][x][y] = null; - } - } - } - } - - /** - * Removes the scenery. - * @param object The object to remove. - */ - public void remove(Scenery object) { - int chunkX = object.getLocation().getChunkOffsetX(); - int chunkY = object.getLocation().getChunkOffsetY(); - Scenery current = null; - int index = -1; - int i = 0; - while ((current == null || current.getId() != object.getId()) && i < objects.length) { - current = objects[i++][chunkX][chunkY]; - if ((current == null || current.getId() < 1) && index == -1) { - index = i - 1; - } - } - if (current != null && current.equals(object)) { - current.setActive(false); - object.setRenderable(false); - } - else { - objects[index][chunkX][chunkY] = object; - } - object.setActive(false); - object.setRenderable(false); - } - - /** - * Adds the scenery. - * @param object The object to add. - */ - public void add(Scenery object) { - int chunkX = object.getLocation().getChunkOffsetX(); - int chunkY = object.getLocation().getChunkOffsetY(); - Scenery current = null; - int index = -1; - int i = 0; - while ((current == null || current.getId() != object.getId()) && i < objects.length) { - current = objects[i++][chunkX][chunkY]; - if ((current == null || current.getId() < 1) && index == -1) { - index = i - 1; - } - } - if (current != null && current.equals(object)) { - current.setActive(true); - current.setRenderable(true); - } - else if (index == -1) { - throw new IllegalStateException("Insufficient array length for storing object!"); - } - else { - objects[index][chunkX][chunkY] = object = object.asConstructed(); - } - object.setActive(true); - object.setRenderable(true); - } - - /** - * Stores an object on the region chunk. - * @param object The object. - */ - public void store(Scenery object) { - if (object == null) { - return; - } - int chunkX = object.getLocation().getChunkOffsetX(); - int chunkY = object.getLocation().getChunkOffsetY(); - for (int i = 0; i < objects.length; i++) { - Scenery stat = objects[i][chunkX][chunkY]; - if (stat == null || stat.getId() < 1) { - objects[i][chunkX][chunkY] = object; - object.setActive(true); - object.setRenderable(true); - return; - } - } - System.err.print("Objects - ["); - for (int i = 0; i < objects.length; i++) { - System.err.print(objects[i][chunkX][chunkY]); - if (i < objects.length - 1) { - System.err.print(", "); - } - } - log(this.getClass(), Log.ERR, "]!"); - throw new IllegalStateException("Insufficient array length for storing all objects! "); - } - - /** - * Gets the objects index for the given object id. - * @param x The x-coordinate on the region chunk. - * @param y The y-coordinate on the region chunk. - * @param objectId The object id. - */ - public int getIndex(int x, int y, int objectId) { - for (int i = 0; i < objects.length; i++) { - Scenery o = get(x, y, i); - if (o != null && ((objectId > -1 && o.getId() == objectId) || (objectId == -1 && o.getDefinition().hasOptions(false)))) { - return i; - } - } - return 0; - } - - /** - * Gets a scenery. - * @param x The chunk x-coordinate. - * @param y The chunk y-coordinate. - * @param index The index (0 = default). - * @return The object. - */ - public Scenery get(int x, int y, int index) { - return objects[index][x][y]; - } - - @Override - public Scenery[] getObjects(int chunkX, int chunkY) { - Scenery[] objects = new Scenery[ARRAY_SIZE]; - for (int i = 0; i < ARRAY_SIZE; i++) { - objects[i] = this.objects[i][chunkX][chunkY]; - } - return objects; - } - - /** - * Gets the objects. - * @param index The index. - * @return The objects array. - */ - public Scenery[][] getObjects(int index) { - return objects[index]; - } - - @Override - public void setCurrentBase(Location currentBase) { - for (int i = 0; i < objects.length; i++) { - for (int x = 0; x < objects[i].length; x++) { - for (int y = 0; y < objects[i][x].length; y++) { - if (objects[i][x][y] != null) { - Location newLoc = currentBase.transform(x, y, 0); - objects[i][x][y].setLocation(newLoc); - } - } - } - } - super.setCurrentBase(currentBase); - } -} diff --git a/Server/src/main/core/game/world/map/Location.kt b/Server/src/main/core/game/world/map/Location.kt index a202e4a49..0d2d01bb9 100644 --- a/Server/src/main/core/game/world/map/Location.kt +++ b/Server/src/main/core/game/world/map/Location.kt @@ -101,6 +101,8 @@ class Location(var hash: LocationHash) : Node(null, null) { // Derived variables. Please note the following isn't a 1-1 representation of what is officially used. // Region - (Officially: Map Square) 64x64 + /** The region. */ + val region: Region get() = RegionManager.forId(regionId) /** Unique index "key" (Officially: region_uid) for a 64x64 area. Made of x and y joined bitwise together. Used in xteas.json. */ val regionId: Int get() = x shr 6 shl 8 or (y shr 6) // Cuts off the end 6 bits of both x y and joins them together. E.g. 10392 /** The chunk x index for this position (x/8). For sets of 8x8 area. THIS IS NAMED WRONG, THIS IS A CHUNK(8) NOT REGION(64). */ @@ -116,7 +118,14 @@ class Location(var hash: LocationHash) : Node(null, null) { fun isInRegion(region: Int): Boolean = regionId == region // Chunk - (Officially: Zones) 8x8 + /** The chunk. */ + val chunk: RegionChunk get() = region.chunks[chunkX][chunkY][z] + /** The chunk base on the world map. */ val chunkBase: Location get() = create(regionX shl 3, regionY shl 3, z) + /** The ABSOLUTE chunk x index for this position. (Same as regionX, but named correctly.) */ + val absChunkX: Int get() = x shr 3 // shr 3 is essentially divide by 8 + /** The ABSOLUTE chunk y index for this position. (Same as regionY, but named correctly.) */ + val absChunkY: Int get() = y shr 3 // shr 3 is essentially divide by 8 /** The chunk x index for this position RELATIVE to a region[localX](x/8). THIS IS NOT GLOBAL CHUNK X. */ val chunkX: Int get() = localX shr 3 // shr 3 is essentially divide by 8 (note this is localX) /** The chunk y index for this position RELATIVE to a region[localY](y/8). THIS IS NOT GLOBAL CHUNK Y. */ @@ -136,8 +145,6 @@ class Location(var hash: LocationHash) : Node(null, null) { /** @return The local scene y-coordinate relative to another location's regionY. */ fun getSceneY(loc: Location): Int = y - (loc.regionY - 6 shl 3) - - /** @return The location incremented by another location's coordinates. (POTENTIAL Z PROBLEMS HERE) */ // TODO: THE FOLLOWING TRANSFORM HAS SHIT Z HANDLING. fun transform(l: Location): Location = Location(x + l.x, y + l.y, (z + l.z) % 4) @@ -148,7 +155,6 @@ class Location(var hash: LocationHash) : Node(null, null) { /** Location transformed by a Vector. Limit usage to more complicated functionality. */ fun transform(vector: Vector): Location = create(x + floor(vector.x).toInt(), y + floor(vector.y).toInt()) - /** @return True if this location is 1 tile N, W, S, or E of the node. THIS CAN BE REFACTORED OUT. */ fun isNextTo(node: Node): Boolean { val l = node.location @@ -167,7 +173,7 @@ class Location(var hash: LocationHash) : Node(null, null) { /** @return The straight-line(Euclidean) distance between this and another location. */ fun getDistance(other: Location): Double = getDistance(this, other) - /** @returns ArrayList of the 8 cardinal and diagonal tiles. Order is important and follows [Direction] indexing **/ + /** @returns ArrayList of the 8 cardinal and diagonal tiles. Order is important and follows [Direction] indexing */ val surroundingTiles: ArrayList get() { val locations = ArrayList() locations.add(transform(-1, -1, 0)) @@ -181,7 +187,7 @@ class Location(var hash: LocationHash) : Node(null, null) { return locations } - /** @returns ArrayList of the 4 cardinal direction tiles. Order is important and follows [Direction] indexing **/ + /** @returns ArrayList of the 4 cardinal direction tiles. Order is important and follows [Direction] indexing */ val cardinalTiles: ArrayList get() { val locations = ArrayList() locations.add(transform(-1, 0, 0)) @@ -191,7 +197,6 @@ class Location(var hash: LocationHash) : Node(null, null) { return locations } - /** * Gets the directional components of a movement step. * TODO: would prefer this in the other place. This doesn't exactly belong here since this isn't a common function for other instances of Location diff --git a/Server/src/main/core/game/world/map/Region.java b/Server/src/main/core/game/world/map/Region.java index ff87d8e6b..0a0ca85e4 100644 --- a/Server/src/main/core/game/world/map/Region.java +++ b/Server/src/main/core/game/world/map/Region.java @@ -1,9 +1,12 @@ package core.game.world.map; import core.cache.Cache; +import core.game.node.Node; import core.game.node.entity.npc.NPC; import core.game.node.entity.player.Player; import core.game.node.entity.player.link.music.MusicZone; +import core.game.node.scenery.Scenery; +import core.game.node.scenery.SceneryBuilder; import core.game.system.communication.CommunicationInfo; import core.game.system.task.Pulse; import core.game.world.map.build.DynamicRegion; @@ -29,12 +32,21 @@ import static core.api.ContentAPIKt.log; * @author Emperor */ public class Region { - /** - * The default size of a region. + * The number of tiles per region. */ public static final int SIZE = 64; + /** + * The number of chunks per region. + */ + public static final int CHUNKS_SIZE = 8; + + /** + * The number of z levels per x,y coordinate. + */ + public static final int PLANES = 4; + /** * The region x-coordinate. */ @@ -46,9 +58,9 @@ public class Region { private final int y; /** - * The region planes. + * The chunks in this region. */ - private final RegionPlane[] planes = new RegionPlane[4]; + protected RegionChunk[][][] chunks = new RegionChunk[CHUNKS_SIZE][CHUNKS_SIZE][PLANES]; /** * The activity pulse. @@ -73,7 +85,7 @@ public class Region { /** * Keeps track of players and time in region for tolerance purposes */ - private final HashMap tolerances = new HashMap<>(); + private final HashMap tolerances = new HashMap(); /** * If the region is active. @@ -106,9 +118,10 @@ public class Region { private boolean build; /** - * If all planes should be updated when in this region (instead of just current one). + * Any scenery overrides for this region */ - private boolean updateAllPlanes; + private final ArrayList addSceneries = new ArrayList<>(); + private final ArrayList removeSceneries = new ArrayList<>(); /** * Constructs a new {@code Region} {@code Object}. @@ -118,8 +131,14 @@ public class Region { public Region(int x, int y) { this.x = x; this.y = y; - for (int plane = 0; plane < 4; plane++) { - planes[plane] = new RegionPlane(this, plane); + Location swCorner = getBaseLocation(); + for (x = 0; x < CHUNKS_SIZE; x++) { + for (y = 0; y < CHUNKS_SIZE; y++) { + for (int z = 0; z < PLANES; z++) { + Location loc = swCorner.transform(x*RegionChunk.SIZE, y*RegionChunk.SIZE, z); + chunks[x][y][z] = new RegionChunk(loc, 0); + } + } } this.activityPulse = new Pulse(50) { @Override @@ -139,79 +158,57 @@ public class Region { return Location.create(x << 6, y << 6, 0); } + /** + * Gets the chunks. + * @return The chunks. + */ + public RegionChunk[][][] getChunks() { + return chunks; + } + /** * Adds a region zone to this region. * @param zone The region zone. */ public void add(RegionZone zone) { regionZones.add(zone); - for (RegionPlane plane : planes) { - for (NPC npc : plane.getNpcs()) { - npc.getZoneMonitor().updateLocation(npc.getLocation()); - } - for (Player p : plane.getPlayers()) { - p.getZoneMonitor().updateLocation(p.getLocation()); + for (int x = 0; x < CHUNKS_SIZE; x++) { + for (int y = 0; y < CHUNKS_SIZE; y++) { + for (int z = 0; z < PLANES; z++) { + for (NPC npc : chunks[x][y][z].getNpcs()) { + npc.getZoneMonitor().updateLocation(npc.getLocation()); + } + for (Player player : chunks[x][y][z].getPlayers()) { + if (player != null) { + player.getZoneMonitor().updateLocation(player.getLocation()); + } + } + } } } } public void remove(RegionZone zone) { regionZones.remove(zone); - for (RegionPlane plane : planes) { - for (NPC npc : plane.getNpcs()) { - npc.getZoneMonitor().updateLocation(npc.getLocation()); - } - for (Player p : plane.getPlayers()) { - p.getZoneMonitor().updateLocation(p.getLocation()); + for (int x = 0; x < CHUNKS_SIZE; x++) { + for (int y = 0; y < CHUNKS_SIZE; y++) { + for (int z = 0; z < PLANES; z++) { + for (NPC npc : chunks[x][y][z].getNpcs()) { + npc.getZoneMonitor().updateLocation(npc.getLocation()); + } + for (Player player : chunks[x][y][z].getPlayers()) { + player.getZoneMonitor().updateLocation(player.getLocation()); + } + } } } } - /** - * Adds a player to this region. - * @param player The player. - */ - public void add(Player player) { - planes[player.getLocation().getZ()].add(player); - tolerances.put(player.getUsername(), System.currentTimeMillis()); - flagActive(); - } - - /** - * Adds an npc to this region. - * @param npc The npc. - */ - public void add(NPC npc) { - planes[npc.getLocation().getZ()].add(npc); - } - - /** - * Removes an NPC from this region. - * @param npc The NPC. - */ - public void remove(NPC npc) { - RegionPlane plane = npc.getViewport().getCurrentPlane(); - if (plane != null && plane != planes[npc.getLocation().getZ()]) { - plane.remove(npc); - } - planes[npc.getLocation().getZ()].remove(npc); - } - - /** - * Removes a player from this region. - * @param player The player. - */ - public void remove(Player player) { - player.getViewport().getCurrentPlane().remove(player); - tolerances.remove(player.getUsername()); - checkInactive(); - } - /** * Checks if player is tolerated by enemies in this region */ - public boolean isTolerated(Player player){ - return System.currentTimeMillis() - tolerances.getOrDefault(player.getUsername(), System.currentTimeMillis()) > TimeUnit.MINUTES.toMillis(10); + public boolean isTolerated(Player player) { + return System.currentTimeMillis() - tolerances.getOrDefault(player.getName(), System.currentTimeMillis()) > TimeUnit.MINUTES.toMillis(10); } /** @@ -231,9 +228,13 @@ public class Region { if (isViewed()) { return false; } - for (RegionPlane p : planes) { - if (!p.getPlayers().isEmpty()) { - return false; + for (int x = 0; x < CHUNKS_SIZE; x++) { + for (int y = 0; y < CHUNKS_SIZE; y++) { + for (int z = 0; z < PLANES; z++) { + if (!chunks[x][y][z].getPlayers().isEmpty()) { + return false; + } + } } } if (runPulse) { @@ -246,14 +247,6 @@ public class Region { return true; } - /** - * Checks if this region has the inactivity flagging pulse running. - * @return {@code True} if so. - */ - public boolean isPendingRemoval() { - return activityPulse.isRunning(); - } - /** * Flags the region as active. */ @@ -262,10 +255,14 @@ public class Region { if (!active) { active = true; load(this); - for (RegionPlane r : planes) { - for (NPC n : r.getNpcs()) { - if (n.isActive()) { - Repository.addRenderableNPC(n); + for (int x = 0; x < CHUNKS_SIZE; x++) { + for (int y = 0; y < CHUNKS_SIZE; y++) { + for (int z = 0; z < PLANES; z++) { + for (NPC npc : chunks[x][y][z].getNpcs()) { + if (npc.isActive()) { + Repository.addRenderableNPC(npc); + } + } } } } @@ -285,7 +282,7 @@ public class Region { * Flags the region as inactive. */ public boolean flagInactive() { - return flagInactive(false); + return flagInactive(false); } /** @@ -299,7 +296,7 @@ public class Region { /** * Loads the flags for a region. * @param r The region. - * @param build if all objects in this region should be stored (rather than just the ones with options). + * @param build If the region can be edited. */ public static void load(Region r, boolean build) { try { @@ -318,11 +315,13 @@ public class Region { return; } - byte[][][] mapscapeData = new byte[4][SIZE][SIZE]; - for (RegionPlane plane : r.planes) { - plane.getFlags().setLandscape(new boolean[SIZE][SIZE]); - //plane.getFlags().setClippingFlags(new int[SIZE][SIZE]); - //plane.getProjectileFlags().setClippingFlags(new int[SIZE][SIZE]); + byte[][][] mapscapeData = new byte[PLANES][SIZE][SIZE]; + for (int x = 0; x < CHUNKS_SIZE; x++) { + for (int y = 0; y < CHUNKS_SIZE; y++) { + for (int z = 0; z < PLANES; z++) { + r.chunks[x][y][z].getFlags().setLandscape(new boolean[RegionChunk.SIZE][RegionChunk.SIZE]); + } + } } if (mapscapeId > -1) { ByteBuffer mapscape = ByteBuffer.wrap(Cache.getIndexes()[5].getCacheFile().getContainerUnpackedData(mapscapeId)); @@ -338,50 +337,63 @@ public class Region { } r.hasFlags = true; try { - LandscapeParser.parse(r, mapscapeData, ByteBuffer.wrap(landscape), build); + LandscapeParser.parse(r, r.chunks, mapscapeData, ByteBuffer.wrap(landscape), build); } catch (Throwable t) { new Throwable("Failed parsing region " + regionId + "!", t).printStackTrace(); } } - MapscapeParser.clipMapscape(r, mapscapeData); + MapscapeParser.clipMapscape(r, r.chunks, mapscapeData); + for (Scenery object : r.removeSceneries) { + // Get the actual object, not the instance that's kept in the removeScenery array + Scenery realObject = RegionManager.getObject(object.getLocation()); //can be extended to take into account type and rotation if needed + if (realObject != null) { + SceneryBuilder.remove(realObject); + } + } + for (Scenery object : r.addSceneries) { + SceneryBuilder.add(object); + } } catch (Throwable e) { e.printStackTrace(); } } - public static boolean unload(Region r) { - return unload(r, false); - } - /** * Unloads a region. * @param r The region. */ - public static boolean unload(Region r, boolean force) { + public boolean unload(Region r, boolean force) { if (!force && r.isViewed()) { log(CommunicationInfo.class, Log.ERR, "Players viewing region!"); r.flagActive(); return false; } - for (RegionPlane p : r.planes) { - if (!force && !p.getPlayers().isEmpty()) { - log(CommunicationInfo.class, Log.ERR, "Players still in region!"); - r.flagActive(); - return false; - } - } - for (RegionPlane p : r.planes) { - p.clear(); - if (!(r instanceof DynamicRegion)) { - for (NPC n : p.getNpcs()) { - n.onRegionInactivity(); + for (int x = 0; x < CHUNKS_SIZE; x++) { + for (int y = 0; y < CHUNKS_SIZE; y++) { + for (int z = 0; z < PLANES; z++) { + if (!force && !chunks[x][y][z].getPlayers().isEmpty()) { + log(CommunicationInfo.class, Log.ERR, "Players still in region! (region id " + getId() + "; " + chunks[x][y][z].getPlayers().size() + " players on a chunk)"); + r.flagActive(); + return false; + } } } } - if (r.isBuild()) - r.setLoaded(false); + for (int x = 0; x < CHUNKS_SIZE; x++) { + for (int y = 0; y < CHUNKS_SIZE; y++) { + for (int z = 0; z < PLANES; z++) { + if (!(r instanceof DynamicRegion)) { + for (NPC npc : chunks[x][y][z].getNpcs()) { + npc.onRegionInactivity(); + } + } + chunks[x][y][z].clear(); + } + } + } + r.setLoaded(false); r.activityPulse.stop(); - return true; + return true; } /** @@ -426,17 +438,6 @@ public class Region { return active; } - /** - * Sets the active. - * @param active The active to set. - * @deprecated This should not be used, instead use the {@link #flagInactive()}, - * {@link #flagActive()} & {@link #checkInactive()} methods to safely change the activity state. - */ - @Deprecated - public void setActive(boolean active) { - this.active = active; - } - /** * Gets the region id. * @return The region id. @@ -469,14 +470,6 @@ public class Region { return y; } - /** - * Gets the planes. - * @return The planes. - */ - public RegionPlane[] getPlanes() { - return planes; - } - /** * Sets the region-wide music track. */ @@ -532,14 +525,6 @@ public class Region { return hasFlags; } - /** - * Sets the hasFlags. - * @param hasFlags The hasFlags to set. - */ - public void setHasFlags(boolean hasFlags) { - this.hasFlags = hasFlags; - } - /** * Sets the region time out duration. * @param ticks The amount of ticks before the region is flagged as inactive. @@ -564,14 +549,6 @@ public class Region { this.loaded = loaded; } - /** - * Sets the viewAmount. - * @param viewAmount The viewAmount to set. - */ - public void setViewAmount(int viewAmount) { - this.viewAmount = viewAmount; - } - /** * Gets the build. * @return the build @@ -588,19 +565,69 @@ public class Region { this.build = build; } - /** - * Gets the updateAllPlanes value. - * @return The updateAllPlanes. - */ - public boolean isUpdateAllPlanes() { - return updateAllPlanes; + public List assembleObjectList(int z) { + ArrayList list = new ArrayList<>(); + for (int x = 0; x < CHUNKS_SIZE; x++) { + for (int y = 0; y < CHUNKS_SIZE; y++) { + RegionChunk chunk = chunks[x][y][z]; + for (int offsetX = 0; offsetX < RegionChunk.SIZE; offsetX++) { + for (int offsetY = 0; offsetY < RegionChunk.SIZE; offsetY++) { + for (int i = 0; i < RegionChunk.ARRAY_SIZE; i++) { + Scenery object = chunk.getObjects()[offsetX][offsetY][i]; + if (object != null) { + list.add(object); + } + } + } + } + } + } + return list; + } + + public List assembleNpcList(int z) { + ArrayList list = new ArrayList<>(); + for (int x = 0; x < CHUNKS_SIZE; x++) { + for (int y = 0; y < CHUNKS_SIZE; y++) { + RegionChunk chunk = chunks[x][y][z]; + list.addAll(chunk.getNpcs()); + } + } + return list; + } + + public List assemblePlayerList(int z) { + ArrayList list = new ArrayList<>(); + for (int x = 0; x < CHUNKS_SIZE; x++) { + for (int y = 0; y < CHUNKS_SIZE; y++) { + RegionChunk chunk = chunks[x][y][z]; + list.addAll(chunk.getPlayers()); + } + } + return list; + } + + public List assembleNodeList(int z) { + ArrayList list = new ArrayList<>(assembleObjectList(z)); + list.addAll(assemblePlayerList(z)); + list.addAll(assembleNpcList(z)); + return list; } /** - * Sets the updateAllPlanes value. - * @param updateAllPlanes The updateAllPlanes to set. + * Getter for region aggro tolerances. */ - public void setUpdateAllPlanes(boolean updateAllPlanes) { - this.updateAllPlanes = updateAllPlanes; + public HashMap getTolerances() { + return tolerances; + } + + /** + * Getters for addScenery and removeScenery. + */ + public ArrayList getAddSceneries() { + return addSceneries; + } + public ArrayList getRemoveSceneries() { + return removeSceneries; } } diff --git a/Server/src/main/core/game/world/map/RegionChunk.java b/Server/src/main/core/game/world/map/RegionChunk.java index 7b3fb71c8..a252921f0 100644 --- a/Server/src/main/core/game/world/map/RegionChunk.java +++ b/Server/src/main/core/game/world/map/RegionChunk.java @@ -1,19 +1,25 @@ package core.game.world.map; +import core.game.node.Node; +import core.game.node.entity.npc.NPC; import core.game.node.entity.player.Player; import core.game.node.item.GroundItem; 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.build.ChunkFlags; +import core.game.world.update.ChunkDirtyListener; +import core.game.world.update.flag.chunk.ItemUpdateFlag; +import core.net.packet.PacketRepository; +import core.net.packet.context.BuildItemContext; +import core.net.packet.out.*; import core.tools.Log; -import core.tools.SystemLogger; import core.game.world.map.build.DynamicRegion; import core.game.world.map.build.LandscapeParser; import core.game.world.update.flag.UpdateFlag; import core.net.packet.IoBuffer; -import core.net.packet.out.ClearScenery; -import core.net.packet.out.ConstructGroundItem; -import core.net.packet.out.ConstructScenery; -import core.net.packet.out.UpdateAreaPosition; +import org.rs09.consts.Items; import java.util.ArrayList; import java.util.List; @@ -22,16 +28,26 @@ import static core.api.ContentAPIKt.log; /** * Represents a region chunk. - * @author Emperor + * @author Emperor, Player Name * */ public class RegionChunk { + /** + * The maximum amount of objects to be stored on one tile in the chunk. + * Four objects per tile is the authentic RuneTek engine limit. + */ + public static final int ARRAY_SIZE = 4; /** * The chunk size. */ public static final int SIZE = 8; + /** + * Notified whenever a chunk is flagged, so the rendering pipeline can reset only the chunks that were actually touched. + */ + public static ChunkDirtyListener dirtyListener = chunk -> {}; + /** * The base location of the copied region chunk. */ @@ -42,20 +58,27 @@ public class RegionChunk { */ protected Location currentBase; - /** - * The region plane. - */ - protected RegionPlane plane; - /** * The items in this chunk. */ - protected List items; + protected List items = new ArrayList<>(); /** - * The scenerys in this chunk. + * The static objects in this chunk. Only used as a reference, do not act on these unless you are the landscape- + * parsing code. + * The game protocol requires us to send separate updates for dynamically added scenery and dynamically + * removed scenery. We implement this by maintaining two lists: one of static scenery, which contains the + * reference state obtained from the cache, and one of dynamic scenery, which is the actual current state of + * the sceneries on a chunk. This means that during landscape-parsing (and, crucially, only then), we need + * to add objects to both the static and dynamic lists. All object interactions in the game, however, only + * use the dynamic list (hence why that is just called 'objects'). */ - protected Scenery[][] objects; + protected Scenery[][][] statObjects; + + /** + * The (dynamic, actual) objects in this chunk. + */ + protected Scenery[][][] objects; /** * The rotation. @@ -65,28 +88,253 @@ public class RegionChunk { /** * The update flags. */ - private List> flags = new ArrayList<>(20); + private final List> updateFlags = new ArrayList<>(20); + + /** + * The region flags. + */ + private ChunkFlags flags; + + /** + * The region projectile flags. + */ + private ChunkFlags projectileFlags; + + /** + * The players on this chunk. + */ + private final List players = new ArrayList<>(); + + /** + * The NPCs on this chunk. + */ + private final List npcs = new ArrayList<>(); /** * Constructs a new {@code RegionChunk} {@code Object}. * @param base The base location of the region chunk. * @param rotation The rotation. */ - public RegionChunk(Location base, int rotation, RegionPlane plane) { + public RegionChunk(Location base, int rotation) { this.base = base; this.currentBase = base; this.rotation = rotation; - this.plane = plane; - this.objects = new Scenery[SIZE][SIZE]; + this.statObjects = new Scenery[SIZE][SIZE][ARRAY_SIZE]; + this.objects = new Scenery[SIZE][SIZE][ARRAY_SIZE]; + this.flags = new ChunkFlags(base.getX(), base.getY(), base.getZ()); + this.projectileFlags = new ChunkFlags(base.getX(), base.getY(), base.getZ(), true); + } + + /** + * Corrects objects' Locations when copying them from a template region into an instance - public version. + */ + public void rebaseObjects() { + rebaseObjects(statObjects); + rebaseObjects(objects); + } + + /** + * Corrects objects' Locations when copying them from a template region into an instance - private version. + */ + private void rebaseObjects(Scenery[][][] objects) { + for (int i = 0; i < ARRAY_SIZE; i++) { + for (int x = 0; x < SIZE; x++) { + for (int y = 0; y < SIZE; y++) { + Scenery o = objects[x][y][i]; + if (o == null) { + continue; + } + o.setLocation(getCurrentBase().transform(x, y, 0)); + } + } + } + } + + /** + * Makes a deep copy of an object list - private version. + */ + private void copyObjects(Scenery[][][] src, Scenery[][][] dest) { + for (int x = 0; x < SIZE; x++) { + for (int y = 0; y < SIZE; y++) { + for (int i = 0; i < ARRAY_SIZE; i++) { + Scenery o = src[x][y][i]; + if (o == null) { + continue; + } + Scenery copy = o.transform(o.getId()); + if (o instanceof Constructed) { + dest[x][y][i] = copy.asConstructed(); + } else { + dest[x][y][i] = copy; + } + dest[x][y][i].setActive(o.isActive()); + dest[x][y][i].setRenderable(o.isRenderable()); + } + } + } } /** * Copies the region chunk. - * @param plane The region plane. - * @return The region chunk. + * @return The destination chunk. */ - public BuildRegionChunk copy(RegionPlane plane) { - return new BuildRegionChunk(base, rotation, plane, this.objects); + public RegionChunk copy() { + RegionChunk chunk = new RegionChunk(base, rotation); + copyObjects(statObjects, chunk.statObjects); //note that the objects' locations have not been repointed to the new chunk yet, since we don't know _where_ the new chunk will be at this point + copyObjects(objects, chunk.objects); + return chunk; + } + + /** + * Adds the scenery to the static and dynamic object lists. You never want this unless you are the landscape-parsing + * code. + * @param object The object to add. + */ + public void addStatDyn(Scenery object, int chunkOffsetX, int chunkOffsetY) { + for (int i = 0; i < ARRAY_SIZE; i++) { + Scenery current = statObjects[chunkOffsetX][chunkOffsetY][i]; + if (current == null) { + statObjects[chunkOffsetX][chunkOffsetY][i] = object; + objects[chunkOffsetX][chunkOffsetY][i] = object.transform(object.getId()); //deep copy so that active/renderable flags don't synchronize across multiple copies of e.g. a POH + return; + } + if (current.getId() == object.getId()) { + // It's possible that we already have this object. One instance of this was found at 1906, 5082, 0, + // where our cache has more than one copy of the same [Scenery 15349, [1906, 5082, 0], type=22, rot=1] + // object. Since authentically, the engine can only support one of the same type of object per tile, + // if the ID matches we can be confident that this is a duplicate. + return; + } + } + throw new IllegalStateException("RC addStatDyn insufficient array length for storing object " + object); + } + + /** + * Adds the scenery to the dynamic object list. + * @param object The object to add. + * @return The slot it was added into. + */ + public int add(Scenery object) { + int chunkOffsetX = object.getLocation().getChunkOffsetX(); + int chunkOffsetY = object.getLocation().getChunkOffsetY(); + for (int i = 0; i < ARRAY_SIZE; i++) { + Scenery current = objects[chunkOffsetX][chunkOffsetY][i]; + if (current == null) { + objects[chunkOffsetX][chunkOffsetY][i] = object.asConstructed(); + object.setRenderable(true); + object.setActive(true); + return i; + } + if (current.equals(object)) { + // Just reactivate the old instance + current.setRenderable(true); + current.setActive(true); + return i; + } + } + throw new IllegalStateException("RC add insufficient array length for storing object " + object); + } + + /** + * Adds an NPC to this chunk. + * @param npc The NPC to add. + */ + public void add(NPC npc) { + npcs.add(npc); + } + + /** + * Adds a player to this chunk. + * @param player The player. + */ + public void addPlayer(Player player) { + players.add(player); + currentBase.getRegion().flagActive(); + } + + /** + * Adds an item to this region. + * @param item The item. + */ + public void add(GroundItem item) { + getItems().add(item); + if (item.isPrivate()) { + if (item.getDropper() != null) { + PacketRepository.send(ConstructGroundItem.class, new BuildItemContext(item.getDropper(), item)); + } + return; + } + flag(new ItemUpdateFlag(item, ItemUpdateFlag.CONSTRUCT_TYPE)); + } + + /** + * Removes an NPC from this chunk. + * @param npc The NPC. + */ + public void remove(NPC npc) { + npcs.remove(npc); + } + + /** + * Removes a player from this chunk. + * @param player The player. + */ + public void removePlayer(Player player) { + players.remove(player); + currentBase.getRegion().checkInactive(); + } + + /** + * Removes an item from this chunk. + * @param item The ground item. + */ + public void remove(GroundItem item) { + Location l = item.getLocation(); + if (!items.remove(item)) { + return; + } + if (item.isPrivate()) { + if (item.getDropper() != null && item.getDropper().isPlaying() && item.getDropper().getLocation().withinDistance(l)) { + PacketRepository.send(ClearGroundItem.class, new BuildItemContext(item.getDropper(), item)); + } + return; + } + flag(new ItemUpdateFlag(item, ItemUpdateFlag.REMOVE_TYPE)); + } + + /** + * Gets the npcs. + * @return The npcs. + */ + public List getNpcs() { + return npcs; + } + + /** + * Assembles a flat object list. + * @return The objects. + */ + public List assembleObjectList() { + ArrayList list = new ArrayList<>(ARRAY_SIZE); + for (int offsetX = 0; offsetX < SIZE; offsetX++) { + for (int offsetY = 0; offsetY < SIZE; offsetY++) { + for (int i = 0; i < ARRAY_SIZE; i++) { + Scenery object = objects[offsetX][offsetY][i]; + if (object != null) { + list.add(object); + } + } + } + } + return list; + } + + /** + * Gets the players. + * @return The players. + */ + public List getPlayers() { + return players; } /** @@ -94,18 +342,27 @@ public class RegionChunk { * @param flag The flag. */ public void flag(UpdateFlag flag) { - flags.add(flag); + updateFlags.add(flag); + dirtyListener.onFlagged(this); } /** * Clears the region chunk. */ public void clear() { - flags.clear(); - if (items != null && plane.getRegion() instanceof DynamicRegion) { + updateFlags.clear(); + Region region = RegionManager.forId(currentBase.getRegionId()); + if (items != null && region instanceof DynamicRegion) { items.clear(); items = null; } + for (int x = 0; x < SIZE; x++) { + for (int y = 0; y < SIZE; y++) { + for (int i = 0; i < ARRAY_SIZE; i++) { + statObjects[x][y][i] = objects[x][y][i] = null; + } + } + } } /** @@ -123,29 +380,26 @@ public class RegionChunk { * Writes the region chunk update data on the buffer. * @param player The player we're updating for. * @param buffer The buffer to write on. - * @return {@code True} if an update occured. + * @return {@code True} if an update occurred. */ protected boolean appendUpdate(Player player, IoBuffer buffer) { boolean updated = false; - int baseX = currentBase.getLocalX(); - int baseY = currentBase.getLocalY(); for (int x = 0; x < SIZE; x++) { for (int y = 0; y < SIZE; y++) { - Scenery dyn = objects[x][y]; - if (dyn == null || plane.getObjects() == null) { - continue; - } - Scenery stat = plane.getObjects()[baseX + x][baseY + y]; - if (!dyn.isRenderable() && stat != null) { - ClearScenery.write(buffer, stat); - updated = true; - } - else if (dyn != stat) { - if (stat != null) { - ClearScenery.write(buffer, stat); + for (int i = 0; i < ARRAY_SIZE; i++) { + Scenery dyn = objects[x][y][i]; + Scenery stat = statObjects[x][y][i]; + if (dyn == stat) { + continue; + } + if (stat != null && (dyn == null || !dyn.isRenderable())) { + ClearScenery.write(buffer, stat); + updated = true; + } + if (dyn != null && dyn.isRenderable()) { + ConstructScenery.write(buffer, dyn); + updated = true; } - ConstructScenery.write(buffer, dyn); - updated = true; } } } @@ -161,9 +415,8 @@ public class RegionChunk { return updated; } - public ArrayList drawItems(List items, Player player) { - ArrayList totalItems = items != null ? new ArrayList(items) : new ArrayList(); + ArrayList totalItems = items != null ? new ArrayList<>(items) : new ArrayList(); if (player.getAttribute("chunkdraw", false)) { Location l = currentBase; @@ -175,7 +428,7 @@ public class RegionChunk { else if (x == 0 || x == SIZE - 1) add = true; if (add) - totalItems.add(new GroundItem(new Item(13444), l.transform(x, y, 0), player)); + totalItems.add(new GroundItem(new Item(Items.ABYSSAL_WHIP_13444), l.transform(x, y, 0), player)); } } } @@ -200,7 +453,7 @@ public class RegionChunk { add = true; if (add) - totalItems.add(new GroundItem(new Item(13444), l.transform(x,y,0), player)); + totalItems.add(new GroundItem(new Item(Items.ABYSSAL_WHIP_13444), l.transform(x,y,0), player)); } } @@ -225,7 +478,7 @@ public class RegionChunk { public void update(Player player) { if (isUpdated()) { IoBuffer buffer = UpdateAreaPosition.getChunkUpdateBuffer(player, currentBase); - Object[] flagsArray = flags.toArray(); + Object[] flagsArray = updateFlags.toArray(); int size = flagsArray.length; for (int i = 0; i < size; i++) { UpdateFlag flag = (UpdateFlag) flagsArray[i]; @@ -235,86 +488,13 @@ public class RegionChunk { } } - /** - * Rotates the chunk. - * @param direction The direction. - */ - public void rotate(Direction direction) { - if (rotation != 0) { - log(this.getClass(), Log.ERR, "Region chunk was already rotated!"); - return; - } - Scenery[][] copy = new Scenery[SIZE][SIZE]; - Scenery[][] staticCopy = new Scenery[SIZE][SIZE]; - int baseX = currentBase.getLocalX(); - int baseY = currentBase.getLocalY(); - for (int x = 0; x < SIZE; x++) { - for (int y = 0; y < SIZE; y++) { - copy[x][y] = objects[x][y]; - staticCopy[x][y] = plane.getObjects()[baseX + x][baseY + y]; - objects[x][y] = plane.getObjects()[baseX + x][baseY + y] = null; - plane.getFlags().clearFlag(baseX + x, baseY + y); - } - } - rotation = direction.toInteger(); - for (int x = 0; x < SIZE; x++) { - for (int y = 0; y < SIZE; y++) { - Scenery object = copy[x][y]; - Scenery stat = staticCopy[x][y]; - boolean match = object == stat; - if (stat == null) { - continue; - } - int[] pos = getRotatedPosition(x, y, stat.getDefinition().getSizeX(), stat.getDefinition().getSizeY(), stat.getRotation(), rotation); - if (object != null) { - object = object.transform(object.getId(), (object.getRotation() + rotation) % 4, object.getLocation().transform(pos[0] - x, pos[1] - y, 0)); - LandscapeParser.flagScenery(plane, baseX + pos[0], baseY + pos[1], object, true, true); - } - if (match) { - stat = object; - } else { - stat = stat.transform(stat.getId(), (stat.getRotation() + rotation) % 4, stat.getLocation().transform(pos[0] - x, pos[1] - y, 0)); - } - plane.getObjects()[baseX + pos[0]][baseY + pos[1]] = stat; - } - } - } - - /** - * Gets the new coordinates for an object/chunk tile when rotating. - * @param x The current x-coordinate. - * @param y The current y-coordinate. - * @param sizeX The x-size of the object. - * @param sizeY The y-size of the object. - * @param rotation The object rotation. - * @param chunkRotation The chunk rotation. - * @return The new x-coordinate. - */ - public static int[] getRotatedPosition(int x, int y, int sizeX, int sizeY, int rotation, int chunkRotation) { - if ((rotation & 0x1) == 1) { - int s = sizeX; - sizeX = sizeY; - sizeY = s; - } - if (chunkRotation == 0) { - return new int[] { x, y }; - } - if (chunkRotation == 1) { - return new int[] { y, 7 - x - (sizeX - 1) }; - } - if (chunkRotation == 2) { - return new int[] { 7 - x - (sizeX - 1), 7 - y - (sizeY - 1) }; - } - return new int[] { 7 - y - (sizeY - 1), x }; - } - /** * Gets the items. * @return The items. */ public List getItems() { if (items == null) { - items = new ArrayList(); + items = new ArrayList<>(); } return items; } @@ -328,29 +508,65 @@ public class RegionChunk { } /** - * Gets the scenerys located on the coordinates in this chunk. - * @param chunkX The chunk x-coordinate (0-7). - * @param chunkY The chunk y-coordinate (0-7). + * Gets the dynamic sceneries located on the coordinates in this chunk. + * @param chunkOffsetX The x coordinate within the chunk (0-7). + * @param chunkOffsetY The y coordinate within the chunk (0-7). * @return The objects. */ - public Scenery[] getObjects(int chunkX, int chunkY) { - return new Scenery[] { objects[chunkX][chunkY] }; + public Scenery[] getObjects(int chunkOffsetX, int chunkOffsetY) { + return objects[chunkOffsetX][chunkOffsetY]; + } + + /** + * Gets the static sceneries located on the coordinates in this chunk. + * @param chunkOffsetX The x coordinate within the chunk (0-7). + * @param chunkOffsetY The y coordinate within the chunk (0-7). + * @return The objects. + */ + public Scenery[] getStatObjects(int chunkOffsetX, int chunkOffsetY) { + return statObjects[chunkOffsetX][chunkOffsetY]; + } + + /** + * Gets the static objects. + * @return The static objects. + */ + public Scenery[][][] getStatObjects() { + return statObjects; } /** * Gets the objects. * @return The objects. */ - public Scenery[][] getObjects() { + public Scenery[][][] getObjects() { return objects; } /** - * Sets the objects. - * @param objects The objects to set. + * Gets the objects index for the given object id and/or scenery type. + * @param x The x-coordinate on the region chunk. + * @param y The y-coordinate on the region chunk. + * @param objectId The object id. -1 if any. + * @param type The scenery type. */ - public void setObjects(Scenery[][] objects) { - this.objects = objects; + public int getIndex(int x, int y, int objectId, int type) { + for (int i = 0; i < ARRAY_SIZE; i++) { + Scenery o = objects[x][y][i]; + if (o != null) { + if (objectId < 0) { + if (type >= 0 && o.getType() != type) { + continue; + } + return i; + } else { + if (o.getId() == objectId) { + return i; + } + } + } + } + return -1; } /** @@ -390,22 +606,14 @@ public class RegionChunk { * @return The updated. */ public boolean isUpdated() { - return !flags.isEmpty(); + return !updateFlags.isEmpty(); } /** * Resets the flags. */ - public void resetFlags() { - flags.clear(); - } - - /** - * Gets the region plane. - * @return The plane. - */ - public RegionPlane getPlane() { - return plane; + public void resetUpdateFlags() { + updateFlags.clear(); } /** @@ -424,18 +632,131 @@ public class RegionChunk { this.currentBase = currentBase; } - public void rebuildFlags(RegionPlane from) { - for (int x = 0; x < 8; x++) { - for (int y = 0; y < 8; y++) { - Location loc = currentBase.transform(x,y,0); - Location fromLoc = base.transform(x,y,0); - plane.getFlags().getLandscape()[loc.getLocalX()][loc.getLocalY()] = from.getFlags().getLandscape()[fromLoc.getLocalX()][fromLoc.getLocalY()]; - plane.getFlags().clearFlag(x, y); - Scenery obj = objects[x][y]; - if (obj != null) - LandscapeParser.flagScenery(plane, loc.getLocalX(), loc.getLocalY(), obj, false, true); + /** + * Reinitializes the clipping flags; used when a region is copied into a new dynamic region. + */ + public void resetClippingFlags() { + flags = new ChunkFlags(currentBase.getX(), currentBase.getY(), currentBase.getZ()); + projectileFlags = new ChunkFlags(currentBase.getX(), currentBase.getY(), currentBase.getZ(), true); + } + + /** + * Rebuilds the clipping flags based on those of a source chunk (used when a chunk is being replaced). + * @param from The region plane to get the new clipping flags from. + */ + public void rebuildClippingFlags(RegionChunk from) { + for (int x = 0; x < SIZE; x++) { + for (int y = 0; y < SIZE; y++) { + // Import the landscape flags from the template chunk to the new chunk + flags.getLandscape()[x][y] = from.getFlags().getLandscape()[x][y]; + projectileFlags.getLandscape()[x][y] = from.getProjectileFlags().getLandscape()[x][y]; + // Reflag any objects + for (int i = 0; i < ARRAY_SIZE; i++) { + Scenery obj = objects[x][y][i]; + if (obj != null) { + LandscapeParser.flagScenery(this, x, y, obj, false, true); + } + } } } } + /** + * Rotation consts. Greg loves these :) + */ + private final static int NORTH_ROTATION = 0; + private final static int EAST_ROTATION = 1; + private final static int SOUTH_ROTATION = 2; + private final static int WEST_ROTATION = 3; + private final static int NUMBER_OF_CARDINAL_ROTATIONS = 4; + + /** + * Rotates the chunk. + * @param direction The direction. + */ + public void rotate(Direction direction) { + if (rotation != 0) { + log(this.getClass(), Log.ERR, "Region chunk was already rotated!"); + return; + } + Scenery[][][] copy = new Scenery[SIZE][SIZE][ARRAY_SIZE]; + for (int x = 0; x < SIZE; x++) { + for (int y = 0; y < SIZE; y++) { + for (int i = 0; i < ARRAY_SIZE; i++) { + Scenery object = copy[x][y][i] = objects[x][y][i]; + if (object != null) { + SceneryBuilder.remove(object); + objects[x][y][i] = null; + } + } + } + } + clear(); + switch (direction) { + case NORTH: rotation = NORTH_ROTATION; break; + case EAST: rotation = EAST_ROTATION; break; + case SOUTH: rotation = SOUTH_ROTATION; break; + case WEST: rotation = WEST_ROTATION; break; + default: rotation = (direction.toInteger() + (direction.toInteger() % 2 == 0 ? 2 : 0)) % NUMBER_OF_CARDINAL_ROTATIONS; + log(this.getClass(), Log.ERR, "Attempted to rotate a chunk in a non-cardinal direction - using fallback rotation code. This should be investigated!"); + break; + }; + for (int x = 0; x < SIZE; x++) { + for (int y = 0; y < SIZE; y++) { + for (int i = 0; i < ARRAY_SIZE; i++) { + Scenery object = copy[x][y][i]; + if (object != null) { + int[] pos = getRotatedPosition(x, y, object.getDefinition().getSizeX(), object.getDefinition().getSizeY(), object.getRotation(), rotation); + Scenery obj = object.transform(object.getId(), (object.getRotation() + rotation) % NUMBER_OF_CARDINAL_ROTATIONS, object.getLocation().transform(pos[0] - x, pos[1] - y, 0)); + if (object instanceof Constructed) { + obj = obj.asConstructed(); + } + obj.setActive(object.isActive()); + obj.setRenderable(object.isRenderable()); + LandscapeParser.flagScenery(this, pos[0], pos[1], obj, true, true); + } + } + } + } + } + + /** + * Gets the new coordinates for an object/chunk tile when rotating. + * @param x The current x-coordinate. + * @param y The current y-coordinate. + * @param sizeX The x-size of the object. + * @param sizeY The y-size of the object. + * @param rotation The object rotation. + * @param chunkRotation The chunk rotation. + * @return The new x-coordinate. + */ + public static int[] getRotatedPosition(int x, int y, int sizeX, int sizeY, int rotation, int chunkRotation) { + if ((rotation & 0x1) == 1) { + int s = sizeX; + sizeX = sizeY; + sizeY = s; + } + if (chunkRotation == 0) { + return new int[] { x, y }; + } + if (chunkRotation == 1) { + return new int[] { y, (SIZE-1) - x - (sizeX - 1) }; + } + if (chunkRotation == 2) { + return new int[] { (SIZE-1) - x - (sizeX - 1), 7 - y - (sizeY - 1) }; + } + return new int[] { (SIZE-1) - y - (sizeY - 1), x }; + } + + public ChunkFlags getFlags() { + return flags; + } + + public ChunkFlags getProjectileFlags() { + return projectileFlags; + } + + public Region getRegion() { + return RegionManager.forId(currentBase.getRegionId()); + } } diff --git a/Server/src/main/core/game/world/map/RegionManager.kt b/Server/src/main/core/game/world/map/RegionManager.kt index 9fb7b848b..d34cc2d47 100644 --- a/Server/src/main/core/game/world/map/RegionManager.kt +++ b/Server/src/main/core/game/world/map/RegionManager.kt @@ -9,11 +9,9 @@ import core.game.node.scenery.Scenery import core.game.world.map.zone.ZoneBorders import core.tools.Log import core.tools.RandomFunction -import core.tools.SystemLogger import java.util.* import java.util.concurrent.TimeUnit import java.util.concurrent.locks.ReentrantLock -import kotlin.collections.HashMap /** * Manages the regions. @@ -27,7 +25,7 @@ object RegionManager { @JvmStatic val CLIPPING_FLAGS = HashMap>() @JvmStatic val PROJECTILE_FLAGS = HashMap>() - public val LOCK = ReentrantLock() + private val LOCK = ReentrantLock() /** * Gets the region for the given region id. @@ -36,7 +34,7 @@ object RegionManager { */ @JvmStatic fun forId(regionId: Int): Region { - if(LOCK.tryLock() || LOCK.tryLock(10000, TimeUnit.MILLISECONDS)) { + if (LOCK.tryLock() || LOCK.tryLock(10000, TimeUnit.MILLISECONDS)) { var region = REGION_CACHE[regionId] if (region == null) { region = Region((regionId shr 8) and 0xFF, regionId and 0xFF) @@ -45,37 +43,25 @@ object RegionManager { LOCK.unlock() return REGION_CACHE[regionId]!! } - log(this::class.java, Log.ERR, "UNABLE TO OBTAIN LOCK WHEN GETTING REGION BY ID. RETURNING BLANK REGION.") - return Region(0,0) + log(this::class.java, Log.ERR, "UNABLE TO OBTAIN LOCK WHEN GETTING REGION BY ID. RETURNING BLANK REGION.") + return Region(0, 0) } /** - * Pulses the active regions. + * Applies a function to all active regions */ @JvmStatic - fun pulse() { - if(LOCK.tryLock() || LOCK.tryLock(10000,TimeUnit.MILLISECONDS)) { + fun apply(callback: (r: Region) -> Unit) { + if (LOCK.tryLock() || LOCK.tryLock(10000,TimeUnit.MILLISECONDS)) { for (r in REGION_CACHE.values) { if (r.isActive) { - for (p in r.planes) { - p.pulse() - } + callback(r) } } LOCK.unlock() } } - /** - * Gets the clipping flag on the given location. - * @param l The location. - * @return The clipping flag. - */ - @JvmStatic - fun getClippingFlag(l: Location): Int { - return getClippingFlag(l.z, l.x, l.y) - } - /** * Gets the clipping flag. * @param z The plane. @@ -85,56 +71,36 @@ object RegionManager { */ @JvmStatic fun getClippingFlag(z: Int, x: Int, y: Int): Int { - val regionX = x shr 6 - val regionY = y shr 6 - val localX = x and 63 - val localY = y and 63 - return getClippingFlag(z, regionX, regionY, localX, localY) + return getClippingFlag(Location(x, y, z)) } /** - * Gets the clipping flags using Jagex-style coords - * e.g 0_50_50_13_13 gets plane 0, region 50-50 (12850), (13, 13) which is in lumbridge. + * Gets the clipping flag. + * @param loc The location. + * @param projectile Clipping flags or projectile flags. */ @JvmStatic - fun getClippingFlag(z: Int, regionX: Int, regionY: Int, localX: Int, localY: Int, projectile: Boolean = false) : Int { - val (region, index) = getFlagIndex(z, regionX, regionY, localX, localY) - var flag = getFlags(region, projectile)[index] - - if (flag == -1) { - val r = forId((regionX shr 8) or regionY) - if (!r.isLoaded) - Region.load(r) - if (!r.isHasFlags) - return -1 - flag = getFlags(region, projectile)[index] + fun getClippingFlag(loc: Location, projectile: Boolean = false) : Int { + val region = forId(loc.regionId) + Region.load(region) + if (!region.isHasFlags) { + return -1 } - - return flag + // Index by absChunkX and absChunkY coordinates; these give the position of the chunk on the world map. Together with chunkOffsetX and chunkOffsetY, they identify individual tiles. + val (key, index) = getFlagIndex(loc.absChunkX, loc.absChunkY, loc.z, loc.chunkOffsetX, loc.chunkOffsetY) + return getFlags(key, projectile)[index] } - private fun getFlagIndex(z: Int, regionX: Int, regionY: Int, localX: Int, localY: Int) : Pair { - return Pair((regionX shl 8) or regionY, (z * 64 * 64) + (localX * 64) + localY) + private fun getFlagIndex(absChunkX: Int, absChunkY: Int, z: Int, chunkOffsetX: Int, chunkOffsetY: Int) : Pair { + return Pair((z shl 22) or (absChunkX shl 11) or absChunkY, (chunkOffsetX * 8) + chunkOffsetY) } @JvmStatic - fun getFlags(regionX: Int, regionY: Int, projectile: Boolean) : Array { - val region = (regionX shl 8) or regionY - return getFlags(region, projectile) - } - - @JvmStatic - fun getFlags(regionId: Int, projectile: Boolean) : Array { + fun getFlags(chunkId: Int, projectile: Boolean) : Array { return if (projectile) - PROJECTILE_FLAGS.getOrPut (regionId) {Array(16384){0}} + PROJECTILE_FLAGS.getOrPut(chunkId) { Array(64){0} } else - CLIPPING_FLAGS.getOrPut (regionId) {Array(16384){-1}} - } - - @JvmStatic - fun resetFlags(regionId: Int) { - PROJECTILE_FLAGS.put (regionId, Array(16384){0}) - CLIPPING_FLAGS.put (regionId, Array(16384){-1}) + CLIPPING_FLAGS.getOrPut(chunkId) { Array(64){-1} } } /** @@ -144,41 +110,37 @@ object RegionManager { */ @JvmStatic fun getWaterClipFlag(z: Int, x: Int, y: Int): Int { - val flag = getClippingFlag(z, x, y) - return if (!isClipped(z, x, y)) { + return getWaterClipFlag(Location(x, y, z)) + } + + /** + * Gets the water variant of a tile's clipping flag + * Essentially strips the landscape flag off a tile and keeps other flags, and makes normally walkable tiles unwalkable. + */ + @JvmStatic + fun getWaterClipFlag(loc: Location): Int { + val flag = getClippingFlag(loc) + return if (!isClipped(loc)) { flag or 0x100 } else flag and 0x200000.inv() } /** * Checks if the tile is part of the landscape. - * @param l The location. + * @param loc The location. * @return `True` if so. */ @JvmStatic - fun isLandscape(l: Location): Boolean { - return isLandscape(l.z, l.x, l.y) - } - - /** - * Checks if the tile is part of the landscape. - * @param z The plane. - * @param x The absolute x-coordinate. - * @param y The absolute y-coordinate. - * @return `True` if so. - */ - @JvmStatic - fun isLandscape(z: Int, x: Int, y: Int): Boolean { - var x = x - var y = y - val region = forId(((x shr 6) shl 8) or (y shr 6)) + fun isLandscape(loc: Location): Boolean { + val region = forId(loc.regionId) Region.load(region) - if (!region.isHasFlags || region.planes[z].flags.landscape == null) { + if (!region.isHasFlags) { return false } - x -= x shr 6 shl 6 - y -= y shr 6 shl 6 - return region.planes[z].flags.landscape[x][y] + if (loc.chunk.flags.landscape == null) { + return false + } + return loc.chunk.flags.landscape[loc.chunkOffsetX][loc.chunkOffsetY] } /** @@ -191,24 +153,31 @@ object RegionManager { */ @JvmStatic fun addClippingFlag(z: Int, x: Int, y: Int, projectile: Boolean, flag: Int) { - var x = x - var y = y - val region = forId(((x shr 6) shl 8) or (y shr 6)) - Region.load(region) - if (!region.isHasFlags) { - return - } - x -= (x shr 6) shl 6 - y -= (y shr 6) shl 6 - if (projectile) { - region.planes[z].projectileFlags.flag(x, y, flag) - } else { - region.planes[z].flags.flag(x, y, flag) - } + return addClippingFlag(Location(x, y, z), projectile, flag) } /** * Adds a clipping flag. + * @param loc The location. + * @param projectile If the flag is being set for projectile pathfinding. + * @param flag The clipping flag. + */ + @JvmStatic + fun addClippingFlag(loc: Location, projectile: Boolean, flag: Int) { + val region = forId(loc.regionId) + Region.load(region) + if (!region.isHasFlags) { + return + } + if (projectile) { + loc.chunk.projectileFlags.flag(loc.chunkOffsetX, loc.chunkOffsetY, flag) + } else { + loc.chunk.flags.flag(loc.chunkOffsetX, loc.chunkOffsetY, flag) + } + } + + /** + * Removes a clipping flag. * @param z The plane. * @param x The absolute x-coordinate. * @param y The absolute y-coordinate. @@ -217,24 +186,31 @@ object RegionManager { */ @JvmStatic fun removeClippingFlag(z: Int, x: Int, y: Int, projectile: Boolean, flag: Int) { - var x = x - var y = y - val region = forId(((x shr 6) shl 8) or (y shr 6)) + return removeClippingFlag(Location(x, y, z), projectile, flag) + } + + /** + * Removes a clipping flag. + * @param loc The location. + * @param projectile If the flag is being set for projectile pathfinding. + * @param flag The clipping flag. + */ + @JvmStatic + fun removeClippingFlag(loc: Location, projectile: Boolean, flag: Int) { + val region = forId(loc.regionId) Region.load(region) if (!region.isHasFlags) { return } - x -= (x shr 6) shl 6 - y -= (y shr 6) shl 6 if (projectile) { - region.planes[z].projectileFlags.unflag(x, y, flag) + loc.chunk.projectileFlags.unflag(loc.chunkOffsetX, loc.chunkOffsetY, flag) } else { - region.planes[z].flags.unflag(x, y, flag) + loc.chunk.flags.unflag(loc.chunkOffsetX, loc.chunkOffsetY, flag) } } /** - * Gets the clipping flag. + * Gets the projectile flag. * @param z The plane. * @param x The absolute x-coordinate. * @param y The absolute y-coordinate. @@ -242,62 +218,34 @@ object RegionManager { */ @JvmStatic fun getProjectileFlag(z: Int, x: Int, y: Int): Int { - val regionX = x shr 6 - val regionY = y shr 6 - val localX = x and 63 - val localY = y and 63 - return getClippingFlag(z, regionX, regionY, localX, localY, true) + return getClippingFlag(Location(x, y, z), true) } /** - * Gets the clipping flag - * @param location the Location - * @return the clipping flag + * Checks if teleport is permitted. + * @param loc The Location. + * @return If teleport is permitted. */ @JvmStatic - fun isTeleportPermitted(location: Location): Boolean { - return isTeleportPermitted(location.z, location.x, location.y) - } - - /** - * Gets the clipping flag. - * @param z The plane. - * @param x The absolute x-coordinate. - * @param y The absolute y-coordinate. - * @return The clipping flags. - */ - @JvmStatic - fun isTeleportPermitted(z: Int, x: Int, y: Int): Boolean { - if (!isLandscape(z, x, y)) { + fun isTeleportPermitted(loc: Location): Boolean { + if (!isLandscape(loc)) { return false } - val flag = getClippingFlag(z, x, y) + val flag = getClippingFlag(loc) return flag and 0x12c0102 == 0 || flag and 0x12c0108 == 0 || flag and 0x12c0120 == 0 || flag and 0x12c0180 == 0 } /** * Checks if the location has any clipping flags. - * @param location The location. + * @param loc The location. * @return `True` if a clipping flag disables access for this location. */ @JvmStatic - fun isClipped(location: Location): Boolean { - return isClipped(location.z, location.x, location.y) - } - - /** - * Checks if the location has any clipping flags. - * @param z The plane. - * @param x The x-coordinate. - * @param y The y-coordinate. - * @return `True` if a clipping flag disables access for this location. - */ - @JvmStatic - fun isClipped(z: Int, x: Int, y: Int): Boolean { - if (!isLandscape(z, x, y)) { + fun isClipped(loc: Location): Boolean { + if (!isLandscape(loc)) { return true } - val flag = getClippingFlag(z, x, y) + val flag = getClippingFlag(loc) return flag and 0x12c0102 != 0 || flag and 0x12c0108 != 0 || flag and 0x12c0120 != 0 || flag and 0x12c0180 != 0 } @@ -352,53 +300,45 @@ object RegionManager { */ @JvmStatic fun getObject(l: Location): Scenery? { - return getObject(l.z, l.x, l.y) + return getObject(l.x, l.y, l.z) } /** * Gets the scenery on the current absolute coordinates. - * @param z The height. * @param x The x-coordinate. * @param y The y-coordinate. + * @param z The height. * @return The scenery, or `null` if no object was found. */ @JvmStatic - fun getObject(z: Int, x: Int, y: Int): Scenery? { - return getObject(z, x, y, -1) + fun getObject(x: Int, y: Int, z: Int): Scenery? { + return getObject(x, y, z, -1, -1) } /** * Gets the object on the given absolute coordinates. - * @param z The height. * @param x The x-coordinate. * @param y The y-coordinate. - * @param objectId The object id. + * @param z The height. + * @param objectId The object id. May be -1, which means 'any'. + * @param type The scenery type. May be -1, which means 'any'. * @return The scenery, or `null` if no object was found. */ @JvmStatic - fun getObject(z: Int, x: Int, y: Int, objectId: Int): Scenery? { - var x = x - var y = y - val regionId = ((x shr 6) shl 8) or (y shr 6) - x -= (x shr 6) shl 6 - y -= (y shr 6) shl 6 - val region = forId(regionId) + fun getObject(x: Int, y: Int, z: Int, objectId: Int = -1, type: Int = -1): Scenery? { + val loc = Location(x, y, z) + val region = forId(loc.regionId) Region.load(region) - val `object`: Scenery? = region.planes[z].getChunkObject(x, y, objectId) - return if (`object` != null && !`object`.isRenderable) { - null - } else `object` - } - - /** - * Gets the region plane for this location. - * @param l The location. - * @return The region plane. - */ - @JvmStatic - fun getRegionPlane(l: Location): RegionPlane { - val regionId = ((l.x shr 6) shl 8) or (l.y shr 6) - return forId(regionId).planes[l.z] + val chunk = region.chunks[loc.chunkX][loc.chunkY][loc.z] + val index = chunk.getIndex(loc.chunkOffsetX, loc.chunkOffsetY, objectId, type) + if (index == -1) { + return null + } + val obj = chunk.objects[loc.chunkOffsetX][loc.chunkOffsetY][index] + if (obj != null && !obj.isRenderable) { + return null + } + return obj } /** @@ -408,247 +348,73 @@ object RegionManager { */ @JvmStatic fun getRegionChunk(l: Location): RegionChunk { - val plane = getRegionPlane(l) - return plane.getRegionChunk(l.localX / RegionChunk.SIZE, l.localY / RegionChunk.SIZE) + val regionId = ((l.x shr 6) shl 8) or (l.y shr 6) + return forId(regionId).chunks[l.chunkX][l.chunkY][l.z] } /** - * Moves the entity from the current region to the new one. + * Moves the entity from the current chunk to the new one. * @param entity The entity. */ @JvmStatic - fun move(entity: Entity) { - val player = entity is Player - val regionId = ((entity.location.regionX shr 3) shl 8) or (entity.location.regionY shr 3) - val viewport = entity.viewport - val current = forId(regionId) - val z = entity.location.z - val plane = current.planes[z] - viewport.updateViewport(entity) - if (plane == viewport.currentPlane) { - entity.zoneMonitor.updateLocation(entity.walkingQueue.footPrint) - return - } - viewport.remove(entity) - if (player) { - current.add(entity as Player) - } else { - current.add(entity as NPC) - } - viewport.region = current - viewport.currentPlane = plane - val view: MutableList = LinkedList() - for (regionX in ((entity.location.regionX shr 3) - 1)..((entity.location.regionX shr 3) + 1)) { - for (regionY in ((entity.location.regionY shr 3) - 1)..((entity.location.regionY shr 3) + 1)) { - if (regionX < 0 || regionY < 0) { - continue + fun move(entity: Entity, src: Location?, dst: Location) { + if (src?.chunk != dst.chunk) { + when (entity) { + is Player -> { + val player = entity.asPlayer() + val sameRegion = src?.region == dst.region + if (src != null && sameRegion) { + src.chunk.removePlayer(player) + dst.chunk.addPlayer(player) + } else { + if (src != null) { + src.chunk.removePlayer(player) + src.region.tolerances.remove(player.name) + src.region.decrementViewAmount() + } + dst.chunk.addPlayer(entity) + dst.region.tolerances[entity.asPlayer().name] = System.currentTimeMillis() + dst.region.incrementViewAmount() + } } - val region = forId((regionX shl 8) or regionY) - val p = region.planes[z] - if (player) { - region.incrementViewAmount() - region.flagActive() + is NPC -> { + src?.chunk?.remove(entity) + dst.chunk.add(entity) } - view.add(p) + else -> log(this::class.java, Log.ERR, "Tried to move an Entity that was neither Player nor NPC, but $entity of class ${entity.javaClass}! It tried to move from $src to $dst.") } } - viewport.viewingPlanes = view entity.zoneMonitor.updateLocation(entity.walkingQueue.footPrint) } /** - * Gets the list of local NPCs with a maximum distance of 16. - * @param n The entity. - * @return The list of local NPCs. - */ - @JvmStatic - fun getLocalNpcs(n: Entity): List { - return getLocalNpcs(n, MapDistance.RENDERING.distance) - } - - /** - * Gets the location entitys. + * Gets local entities. You never call the below function directly (it's inlined); instead, you use one of the typed helpers defined directly below this function. * @param location the location. * @param distance the distance. * @return the list. */ - @JvmStatic - fun getLocalEntitys(location: Location, distance: Int): List { - val entitys: MutableList = ArrayList(20) - entitys.addAll(getLocalNpcs(location, distance)) - entitys.addAll(getLocalPlayers(location, distance)) - return entitys - } - - /** - * Gets the location entitys. - * @param entity the entity. - * @param distance the distance. - * @return the list. - */ - @JvmStatic - fun getLocalEntitys(entity: Entity, distance: Int): List { - return getLocalEntitys(entity.location, distance) - } - - /** - * Gets the local entitys. - * @param entity the entity. - * @return the entitys. - */ - @JvmStatic - fun getLocalEntitys(entity: Entity): List { - return getLocalEntitys(entity.location, MapDistance.RENDERING.distance) - } - - /** - * Gets the list of local NPCs. - * @param n The entity. - * @param distance The distance to the entity. - * @return The list of local NPCs. - */ - @JvmStatic - fun getLocalNpcs(n: Entity, distance: Int): List { - val npcs: MutableList = LinkedList() - for (r in n.viewport.viewingPlanes) { - for (npc in r.npcs) { - if (npc.location.withinDistance(n.location, distance)) { - npcs.add(npc) + private inline fun getLocalEntitiesOfType(location: Location, distance: Int): List { + val entities: ArrayList = ArrayList(20) + val radius = (distance / 8) + 1 + for (cx in (location.absChunkX - radius)..(location.absChunkX + radius)) { + for (cy in (location.absChunkY - radius)..(location.absChunkY + radius)) { + val chunk = Location(cx shl 3, cy shl 3, location.z).chunk + if (T::class.java.isAssignableFrom(Player::class.java)) { + entities.addAll(chunk.players.filterIsInstance()) + } + if (T::class.java.isAssignableFrom(NPC::class.java)) { + entities.addAll(chunk.npcs.filterIsInstance()) } } } - return npcs - } - - /** - * Gets the list of local players with a maximum distance of 15. - * @param n The entity. - * @return The list of local players. - */ - @JvmStatic - fun getLocalPlayers(n: Entity): List { - return getLocalPlayers(n, MapDistance.RENDERING.distance) - } - - /** - * Gets the list of local players. - * @param n The entity. - * @param distance The distance to the entity. - * @return The list of local players. - */ - @JvmStatic - fun getLocalPlayers(n: Entity, distance: Int): List { - val players: MutableList = LinkedList() - for (r in n.viewport.viewingPlanes) { - for (p in r.players) { - if (p.location.withinDistance(n.location, distance)) { - players.add(p) - } - } - } - return players - } - - /** - * Gets the surrounding players. - * @param n The node the players should be surrounding. - * @param ignore The nodes not to add to the list. - * @return The list of players. - */ - @JvmStatic - fun getSurroundingPlayers(n: Node, vararg ignore: Node): List { - return getSurroundingPlayers(n, 9, *ignore) - } - - /** - * Gets the surrounding players. - * @param n The node the players should be surrounding. - * @param ignore The nodes not to add to the list. - * @return The list of players. - */ - @JvmStatic - fun getSurroundingPlayers(n: Node, maximum: Int, vararg ignore: Node): List { - val players = getLocalPlayers(n.location, 2) - var count = 0 - val it = players.iterator() - while (it.hasNext()) { - val p = it.next() - if(p.isInvisible()) { - it.remove() - } - if(!p.location.withinMaxnormDistance(n.location, 1)) { - it.remove() - continue - } - if (++count >= maximum) { - it.remove() - continue - } - for (node in ignore) { - if (p === node) { - count-- - it.remove() - break - } - } - } - return players - } - - /** - * Gets the surrounding players. - * @param n The node the players should be surrounding. - * @param ignore The nodes not to add to the list. - * @return The list of players. - */ - @JvmStatic - fun getSurroundingNPCs(n: Node, vararg ignore: Node): List { - return getSurroundingNPCs(n, 9, *ignore) - } - - /** - * Gets the surrounding players. - * @param n The node the npcs should be surrounding. - * @param ignore The nodes not to add to the list. - * @return The list of npcs. - */ - @JvmStatic - fun getSurroundingNPCs(n: Node, maximum: Int, vararg ignore: Node): List { - val npcs = getLocalNpcs(n.location, 2) - var count = 0 - val it = npcs.iterator() - while (it.hasNext()) { - val p = it.next() - if(p.properties.teleportLocation != null && !p.properties.teleportLocation.withinMaxnormDistance(n.location, 1)) { - it.remove() - continue - } - if(p.getAttribute("state:death", false)) { - it.remove() - continue - } - if(p.isInvisible()) { - it.remove() - continue - } - if(!p.location.withinMaxnormDistance(n.location, 1)) { - it.remove() - continue - } - if (++count > maximum) { - it.remove() - continue - } - for (node in ignore) { - if (p === node) { - count-- - it.remove() - break - } - } - } - return npcs + val b = ZoneBorders(location.x - distance, location.y - distance, location.x + distance, location.y + distance) + return entities.filter { b.insideBorder(it.location.x, it.location.y) } } + @JvmStatic fun getLocalEntities(location: Location, distance: Int) = getLocalEntitiesOfType(location, distance) + @JvmStatic fun getLocalPlayers(location: Location, distance: Int) = getLocalEntitiesOfType(location, distance) + @JvmStatic fun getLocalNPCs(location: Location, distance: Int) = getLocalEntitiesOfType(location, distance) + @JvmStatic fun getLocalPlayers(location: Location): List = getLocalPlayers(location, MapDistance.RENDERING.distance) + @JvmStatic fun getLocalNPCs(location: Location): List = getLocalNPCs(location, MapDistance.RENDERING.distance) /** * Gets a random teleport location in the radius around the given location. @@ -701,162 +467,14 @@ object RegionManager { } /** - * Gets the current viewport for the location. - * @param l The location. - * @return The viewport. - */ - @JvmStatic - fun getViewportPlayers(l: Location): List { - var l = l - val players: MutableList = LinkedList() - l = l.chunkBase.transform(-8, -8, 0) - val b = ZoneBorders(l.x, l.y, l.x + 24, l.y + 24) - for (regionX in ((l.regionX - 6) shr 3)..((l.regionX + 6) shr 3)) { - for (regionY in ((l.regionY - 6) shr 3)..((l.regionY + 6) shr 3)) { - for (player in forId(regionX shl 8 or regionY).planes[l.z].players) { - l = player.location - if (b.insideBorder(l.x, l.y)) { - players.add(player) - } - } - } - } - return players - } - - /** - * Gets a list of players in the given region. - * @param regionId The region id. - * @return The list of players in this region. - */ - @JvmStatic - fun getRegionPlayers(regionId: Int): List { - val r = forId(regionId) - val players: MutableList = ArrayList(20) - for (plane in r.planes) { - players.addAll(plane.players) - } - return players - } - - /** - * Gets a list of local players within rendering distance of the location. - * @param l The location. - * @return The list of players. - */ - @JvmStatic - fun getLocalPlayers(l: Location): List { - return getLocalPlayers(l, MapDistance.RENDERING.distance) - } - - /** - * Gets a list of local players. - * @param l The location. - * @param distance The distance to that location. - * @return The list of players. - */ - @JvmStatic - fun getLocalPlayers(l: Location, distance: Int): MutableList { - val players: MutableList = LinkedList() - for (regionX in ((l.regionX - 6) shr 3)..((l.regionX + 6) shr 3)) { - for (regionY in ((l.regionY - 6) shr 3)..((l.regionY + 6) shr 3)) { - for (player in forId((regionX shl 8) or regionY).planes[l.z].players) { - if (player.location.withinDistance(l, distance)) { - players.add(player) - } - } - } - } - return players - } - - /** - * Gets a list of local players within a symmetric bounding box. - * @param l The location. - * @param xdist The distance from the location on the x plane that is considered within bounds. - * @param ydist The distance from the location on the y plane that is considered within bounds. - * @return The list of players. - */ - @JvmStatic - fun getLocalPlayersBoundingBox(l: Location, xdist: Int, ydist: Int): MutableList { - val players: MutableList = LinkedList() - for (regionX in ((l.regionX - 6) shr 3)..((l.regionX + 6) shr 3)) { - for (regionY in ((l.regionY - 6) shr 3)..((l.regionY + 6) shr 3)) { - for (player in forId((regionX shl 8) or regionY).planes[l.z].players) { - if (player.location.x >= l.x - xdist && - player.location.x <= l.x + xdist && - player.location.y >= l.y - ydist && - player.location.y <= l.y + ydist) { - players.add(player) - } - } - } - } - return players - } - - /** - * Gets a list of local players. - * @param l The location. - * @param distance The distance to that location. - * @return The list of players. - */ - @JvmStatic - fun getLocalPlayersMaxNorm(l: Location, distance: Int): MutableList { - val players: MutableList = LinkedList() - for (regionX in ((l.regionX - 6) shr 3)..((l.regionX + 6) shr 3)) { - for (regionY in ((l.regionY - 6) shr 3)..((l.regionY + 6) shr 3)) { - for (player in forId((regionX shl 8) or regionY).planes[l.z].players) { - if (player.location.withinMaxnormDistance(l, distance)) { - players.add(player) - } - } - } - } - return players - } - - /** - * Gets a list of local players within 16 tile distance of the location. - * @param l The location. - * @return The list of players. - */ - @JvmStatic - fun getLocalNpcs(l: Location): List { - return getLocalNpcs(l, MapDistance.RENDERING.distance) - } - - /** - * Gets the npc. - * @param entity the entity. - * @param id the id. - */ - @JvmStatic - fun getNpc(entity: Entity, id: Int): NPC? { - return getNpc(entity, id, 16) - } - - /** - * Gets the npc. - * @param entity the entity. - * @param id the id. + * Gets an npc near a location. + * @param id the id, * @param distance the distance. * @return the npc. */ @JvmStatic - fun getNpc(entity: Entity, id: Int, distance: Int): NPC? { - return getNpc(entity.location, id, distance) - } - - /** - * Gets an npc near the entity. - * @param id the id, - * @param distance the dinstance. - * @return the npc. - */ - @JvmStatic fun getNpc(location: Location, id: Int, distance: Int): NPC? { - val npcs: List = getLocalNpcs(location, distance) + val npcs: List = getLocalNPCs(location, distance) for (n in npcs) { if (n.id == id) { return n @@ -865,27 +483,6 @@ object RegionManager { return null } - /** - * Gets a list of local players. - * @param l The location. - * @param distance The distance to that location. - * @return The list of players. - */ - @JvmStatic - fun getLocalNpcs(l: Location, distance: Int): MutableList { - val npcs: MutableList = LinkedList() - for (regionX in ((l.regionX - 6) shr 3)..((l.regionX + 6) shr 3)) { - for (regionY in ((l.regionY - 6) shr 3)..((l.regionY + 6) shr 3)) { - for (n in forId(regionX shl 8 or regionY).planes[l.z].npcs) { - if (n.location.withinDistance(l, (n.size() shr 1) + distance)) { - npcs.add(n) - } - } - } - } - return npcs - } - @JvmStatic fun addRegion(id: Int, region: Region){ if(lock.tryLock() || LOCK.tryLock(10000, TimeUnit.MILLISECONDS)) { diff --git a/Server/src/main/core/game/world/map/RegionPlane.java b/Server/src/main/core/game/world/map/RegionPlane.java deleted file mode 100644 index f83724ec0..000000000 --- a/Server/src/main/core/game/world/map/RegionPlane.java +++ /dev/null @@ -1,444 +0,0 @@ -package core.game.world.map; - -import core.game.node.Node; -import core.game.node.entity.npc.NPC; -import core.game.node.entity.player.Player; -import core.game.node.item.GroundItem; -import core.game.node.item.Item; -import core.game.node.scenery.Scenery; -import core.game.world.map.build.DynamicRegion; -import core.game.world.map.build.RegionFlags; -import core.game.world.update.flag.chunk.ItemUpdateFlag; -import core.net.packet.PacketRepository; -import core.net.packet.context.BuildItemContext; -import core.net.packet.out.ClearGroundItem; -import core.net.packet.out.ConstructGroundItem; - -import java.util.ArrayList; -import java.util.Arrays; -import java.util.List; -import java.util.Objects; -import java.util.concurrent.CopyOnWriteArrayList; - -/** - * Represents one of the 4 planes of a region. - * @author Emperor - * - */ -public final class RegionPlane { - - /** - * The region size. - */ - public static final int REGION_SIZE = 64; - - /** - * The amount of chunks in this plane. - */ - public static final int CHUNK_SIZE = REGION_SIZE >> 3; - - /** - * Represents a removed scenery. - */ - public static final Scenery NULL_OBJECT = new Scenery(0, Location.create(0, 0, 0)); - - /** - * The plane. - */ - private final int plane; - - /** - * The region. - */ - private final Region region; - - /** - * The region flags. - */ - private final RegionFlags flags; - - /** - * The region projectile flags. - */ - private final RegionFlags projectileFlags; - - /** - * The region chunks. - */ - private final RegionChunk[][] chunks; - - /** - * The list of NPCs in this region. - */ - private final List npcs; - - /** - * The list of players in this region. - */ - private final List players; - - /** - * The scenerys. - */ - private Scenery[][] objects; - - /** - * Constructs a new {@code RegionPlane} {@code Object}. - * @param region The region. - * @param plane The plane. - */ - public RegionPlane(Region region, int plane) { - this.plane = plane; - this.region = region; - this.players = new CopyOnWriteArrayList(); - this.npcs = new CopyOnWriteArrayList(); - Location base = region.getBaseLocation(); - this.flags = new RegionFlags(plane, base.getX(), base.getY()); - this.projectileFlags = new RegionFlags(plane, base.getX(), base.getY(), true); - this.objects = new Scenery[REGION_SIZE][REGION_SIZE]; - this.chunks = new RegionChunk[CHUNK_SIZE][CHUNK_SIZE]; - } - - /** - * Called at the end of the update sequence, if the region is active. - */ - public void pulse() { - Arrays.stream(chunks).forEach(regionChunks -> {Arrays.stream(regionChunks).filter(Objects::nonNull).forEach(RegionChunk::resetFlags);}); - } - - /** - * Adds a scenery. - * @param object The object to add. - * @param x The x-coordinate. - * @param y The y-coordinate. - * @param landscape If this object is added through landscape parsing. - */ - public void add(Scenery object, int x, int y, boolean landscape) { - setChunkObject(x, y, object); - if (landscape) { - objects[x][y] = object; - } - if (object != null) { - object.setRenderable(true); - } - } - - /** - * Gets the region chunk. - * @param chunkX The chunk base x-coordinate. - * @param chunkY The chunk base y-coordinate. - * @return The region chunk. - */ - public RegionChunk getRegionChunk(int chunkX, int chunkY) { - RegionChunk r = chunks[chunkX][chunkY]; - if (r != null) { - return r; - } - if (region.isBuild()) { - return chunks[chunkX][chunkY] = new BuildRegionChunk(region.getBaseLocation().transform(chunkX << 3, chunkY << 3, plane), 0, this); - } - return chunks[chunkX][chunkY] = new RegionChunk(region.getBaseLocation().transform(chunkX << 3, chunkY << 3, plane), 0, this); - } - - public void setRegionChunk(int chunkX, int chunkY, RegionChunk chunk) { - chunks[chunkX][chunkY] = chunk; - } - - /** - * Removes a scenery. - * @param x The x-coordinate. - * @param y The y-coordinate. - */ - public void remove(int x, int y) { - remove(x, y, -1); - } - - /** - * Removes a scenery. - * @param x The x-coordinate. - * @param y The y-coordinate. - * @param objectId The object id. - */ - public void remove(int x, int y, int objectId) { - int chunkX = x / CHUNK_SIZE; - int chunkY = y / CHUNK_SIZE; - int offsetX = x - chunkX * CHUNK_SIZE; - int offsetY = y - chunkY * CHUNK_SIZE; - RegionChunk chunk = getRegionChunk(chunkX, chunkY); - Scenery remove = new Scenery(0, region.getBaseLocation().transform(x, y, plane), 22, 0); - remove.setRenderable(false); - if (chunk instanceof BuildRegionChunk) { - int index = ((BuildRegionChunk) chunk).getIndex(offsetX, offsetY, objectId); - ((BuildRegionChunk) chunk).getObjects(index)[offsetX][offsetY] = remove; - return; - } - chunk.getObjects()[offsetX][offsetY] = remove; - } - - /** - * Sets an object on a chunk. - * @param x The regional x-coordinate. - * @param y The regional y-coordinate. - * @param object The object to set. - */ - private void setChunkObject(int x, int y, Scenery object) { - int chunkX = x / CHUNK_SIZE; - int chunkY = y / CHUNK_SIZE; - int offsetX = x - chunkX * CHUNK_SIZE; - int offsetY = y - chunkY * CHUNK_SIZE; - RegionChunk r = getRegionChunk(chunkX, chunkY); - if (r instanceof BuildRegionChunk) { - ((BuildRegionChunk) r).store(object); - return; - } - r.getObjects()[offsetX][offsetY] = object; - } - - /** - * Gets the scenerys. - * @return The scenerys. - */ - public Scenery[][] getObjects() { - return objects; - } - - public List getObjectList() { - ArrayList list = new ArrayList(); - for (int x = 0; x < REGION_SIZE; x++) { - for (int y = 0; y < REGION_SIZE; y++) { - if (objects[x][y] != null) - list.add(objects[x][y]); - } - } - return list; - } - - /** - * Clears this region plane. - */ - public void clear() { - for (RegionChunk[] c : chunks) { - for (RegionChunk chunk : c) { - if (chunk != null) { - chunk.clear(); - } - } - } - if (region instanceof DynamicRegion && objects != null) { - for (int x = 0; x < REGION_SIZE; x++) { - for (int y = 0; y < REGION_SIZE; y++) { - objects[x][y] = null; - } - } - objects = null; - } - } - - /** - * Adds an NPC to this region. - * @param npc The NPC to add. - */ - public void add(NPC npc) { - npcs.add(npc); - } - - /** - * Adds a player to this region. - * @param player The player. - */ - public void add(Player player) { - players.add(player); - } - - /** - * Adds an item to this region. - * @param item The item. - */ - public void add(GroundItem item) { - Location l = item.getLocation(); - RegionChunk c = getRegionChunk(l.getLocalX() / RegionChunk.SIZE, l.getLocalY() / RegionChunk.SIZE); - // This adds it to the chunk to be interactable with. - if (!c.getItems().add(item)) { - return; - } - GroundItem g = (GroundItem) item; - if (g.isPrivate()) { - if (g.getDropper() != null) { - // Limit it to the region(8x8 square) you are in +/-2 regions, as the chunk loader will load it all over again. - // This is based on MapChunkRenderer which seems to "update" 2 chunks away. - // Look at RegionChunk.java ConstructGroundItem.write(buffer, item); - if (Math.abs(g.getDropper().getLocation().getRegionX() - l.getRegionX()) <= 2 && - Math.abs(g.getDropper().getLocation().getRegionY() - l.getRegionY()) <= 2 && - g.getDropper().getLocation().getZ() == l.getZ()) { - PacketRepository.send(ConstructGroundItem.class, new BuildItemContext(g.getDropper(), item)); - } - } - return; - } - c.flag(new ItemUpdateFlag(g, ItemUpdateFlag.CONSTRUCT_TYPE)); - } - - /** - * Removes an NPC from this region. - * @param npc The NPC. - */ - public void remove(NPC npc) { - npcs.remove(npc); - } - - /** - * Removes a player from this region. - * @param player The player. - */ - public void remove(Player player) { - players.remove(player); - } - - /** - * Removes an item from this region. - * @param item The ground item. - */ - public void remove(GroundItem item) { - Location l = item.getLocation(); - RegionChunk c = getRegionChunk(l.getLocalX() / RegionChunk.SIZE, l.getLocalY() / RegionChunk.SIZE); - if (!c.getItems().remove(item)) { - return; - } - if (item.isPrivate()) { - if (item.getDropper() != null && item.getDropper().isPlaying() && item.getDropper().getLocation().withinDistance(l)) { - PacketRepository.send(ClearGroundItem.class, new BuildItemContext(item.getDropper(), item)); - } - return; - } - c.flag(new ItemUpdateFlag(item, ItemUpdateFlag.REMOVE_TYPE)); - } - - /** - * Gets the region flags. - * @return The flags. - */ - public RegionFlags getFlags() { - return flags; - } - - /** - * Gets the projectileFlags. - * @return The projectileFlags. - */ - public RegionFlags getProjectileFlags() { - return projectileFlags; - } - - /** - * Gets the npcs. - * @return The npcs. - */ - public List getNpcs() { - return npcs; - } - - public List getEntities() - { - List entities = new ArrayList<>(npcs); - Arrays.stream(getObjects()).forEach(o -> entities.addAll(Arrays.asList(o))); - return entities; - } - - /** - * Gets the players. - * @return The players. - */ - public List getPlayers() { - return players; - } - - /** - * Gets the plane. - * @return The plane. - */ - public int getPlane() { - return plane; - } - - /** - * Gets the region. - * @return The region. - */ - public Region getRegion() { - return region; - } - - /** - * Gets an object from a region chunk. - * @param x The region x-coordinate. - * @param y The region y-coordinate. - * @return The scenery. - */ - public Scenery getChunkObject(int x, int y) { - return getChunkObject(x, y, -1); - } - - /** - * Gets an object from a region chunk. - * @param x The region x-coordinate. - * @param y The region y-coordinate. - * @param objectId The object id. - * @return The scenery. - */ - public Scenery getChunkObject(int x, int y, int objectId) { - int chunkX = x / CHUNK_SIZE; - int chunkY = y / CHUNK_SIZE; - int offsetX = x - chunkX * CHUNK_SIZE; - int offsetY = y - chunkY * CHUNK_SIZE; - RegionChunk chunk = getRegionChunk(chunkX, chunkY); - if (chunk instanceof BuildRegionChunk) { - BuildRegionChunk brc = (BuildRegionChunk) chunk; - return brc.get(offsetX, offsetY, brc.getIndex(offsetX, offsetY, objectId)); - } - return getRegionChunk(chunkX, chunkY).getObjects()[offsetX][offsetY]; - } - - /** - * Gets an object from a region chunk. - * @param x The region x-coordinate. - * @param y The region y-coordinate. - * @return The scenery. - */ - public List getChunkItems(int x, int y) { - int chunkX = x / CHUNK_SIZE; - int chunkY = y / CHUNK_SIZE; - return getRegionChunk(chunkX, chunkY).getItems(); - } - - /** - * Gets a ground item from this plane. - * @param itemId The item id. - * @param l The location. - * @param player The player. - * @return The item. - */ - public GroundItem getItem(int itemId, Location l, Player player) { - GroundItem groundItem = null; - for (Item item : getChunkItems(l.getLocalX(), l.getLocalY())) { - GroundItem g = (GroundItem) item; - if (g.getId() == itemId && l.equals(g.getLocation()) && !g.isRemoved()) { - if (groundItem != null && (!g.isPrivate() || player == null)) { - continue; - } - if ((!g.isPrivate() || player == null || g.droppedBy(player))) { - groundItem = g; - } - } - } - return groundItem; - } - - /** - * Gets the region chunks. - * @return The chunks. - */ - public RegionChunk[][] getChunks() { - return chunks; - } - -} diff --git a/Server/src/main/core/game/world/map/Viewport.java b/Server/src/main/core/game/world/map/Viewport.java deleted file mode 100644 index d563b0357..000000000 --- a/Server/src/main/core/game/world/map/Viewport.java +++ /dev/null @@ -1,155 +0,0 @@ -package core.game.world.map; - -import core.game.node.entity.Entity; -import core.game.node.entity.npc.NPC; -import core.game.node.entity.player.Player; - -import java.util.LinkedList; -import java.util.List; - -/** - * Represents an entity's viewport. - * @author Emperor - */ -public final class Viewport { - - /** - * The amount of chunks in a viewport. - */ - public static final int CHUNK_SIZE = 5; - - /** - * The region region. - */ - private Region region; - - /** - * The region chunks. - */ - private RegionChunk[][] chunks = new RegionChunk[CHUNK_SIZE][CHUNK_SIZE]; - - /** - * The region region plane. - */ - private RegionPlane currentPlane; - - /** - * The region planes the entity can see. - */ - private List viewingPlanes = new LinkedList<>(); - - /** - * Constructs a new {@code Viewport} {@code Object}. - */ - public Viewport() { - /* - * empty. - */ - } - - /** - * Updates the entity's viewport. - * @param entity The entity. - */ - public void updateViewport(Entity entity) { - RegionChunk chunk = RegionManager.getRegionChunk(entity.getLocation()); - int center = chunks.length >> 1; - if (chunks[center][center] == chunk) { - return; - } - int offset = center * -8; - Location l = chunk.getCurrentBase(); - for (int x = 0; x < chunks.length; x++) { - for (int y = 0; y < chunks[x].length; y++) { - chunks[x][y] = RegionManager.getRegionChunk(l.transform(offset + (8 * x), offset + (8 * y), 0)); - } - } - } - - /** - * Removes the entity from the viewingPlanes. - * @param entity The entity. - */ - public void remove(Entity entity) { - if (region == null) { - return; - } - if (entity instanceof Player) { - region.remove((Player) entity); - Region region = null; - for (RegionPlane r : viewingPlanes) { - if (region != r.getRegion()) { - region = r.getRegion(); - region.decrementViewAmount(); - region.checkInactive(); - } - } - } else { - region.remove((NPC) entity); - } - } - - /** - * Gets the region. - * @return The region. - */ - public Region getRegion() { - return region; - } - - /** - * Sets the region. - * @param region The region to set. - */ - public void setRegion(Region region) { - this.region = region; - } - - /** - * Gets the chunks. - * @return The chunks. - */ - public RegionChunk[][] getChunks() { - return chunks; - } - - /** - * Sets the chunks. - * @param chunks The chunks to set. - */ - public void setChunks(RegionChunk[][] chunks) { - this.chunks = chunks; - } - - /** - * Gets the viewingPlanes. - * @return The viewingPlanes. - */ - public List getViewingPlanes() { - return viewingPlanes; - } - - /** - * Sets the viewingPlanes. - */ - public void setViewingPlanes(List regions) { - this.viewingPlanes = regions; - } - - /** - * Gets the currentPlane. - * @return The currentPlane. - */ - public RegionPlane getCurrentPlane() { - return currentPlane; - } - - /** - * Sets the currentPlane. - * @param currentPlane The currentPlane to set. - */ - public void setCurrentPlane(RegionPlane currentPlane) { - this.currentPlane = currentPlane; - } - -} diff --git a/Server/src/main/core/game/world/map/build/RegionFlags.java b/Server/src/main/core/game/world/map/build/ChunkFlags.java similarity index 92% rename from Server/src/main/core/game/world/map/build/RegionFlags.java rename to Server/src/main/core/game/world/map/build/ChunkFlags.java index 177da33c9..a41cb9368 100644 --- a/Server/src/main/core/game/world/map/build/RegionFlags.java +++ b/Server/src/main/core/game/world/map/build/ChunkFlags.java @@ -10,7 +10,7 @@ import static java.lang.Math.max; * @author Emperor * */ -public final class RegionFlags { +public final class ChunkFlags { public static final int TILE_OBJECT = 0x40000; public static final int EMPTY_TILE = 0; @@ -20,7 +20,7 @@ public final class RegionFlags { /** * The plane. */ - private final int plane; + private final int baseZ; /** * If the region is members only. @@ -49,23 +49,25 @@ public final class RegionFlags { /** * Constructs a new {@code RegionFlags} {@code Object}. - * @param x The base x-coordinate (absolute). + * @param x The base x-coordinate (absolute). * @param y The base y-coordinate (absolute). - */ - public RegionFlags(int plane, int x, int y) { - this(plane, x, y, false); + * @param z The base z-coordinate (absolute). + */ + public ChunkFlags(int x, int y, int z) { + this(x, y, z, false); } /** * Constructs a new {@code RegionFlags} {@code Object}. - * @param x The base x-coordinate (absolute). + * @param x The base x-coordinate (absolute). * @param y The base y-coordinate (absolute). - */ - public RegionFlags(int plane, int x, int y, boolean projectile) { - this.plane = plane; + */ + public ChunkFlags(int x, int y, int z, boolean projectile) { this.baseX = x; this.baseY = y; + this.baseZ = z; this.projectile = projectile; + this.landscape = new boolean[8][8]; //all false by default, thanks Java } /** @@ -126,10 +128,10 @@ public final class RegionFlags { * @param clipdata The clip data. */ public void flag(int x, int y, int clipdata) { - if (x > -1 && x < 64 && y > -1 && y < 64) { + if (x > -1 && x < 8 && y > -1 && y < 8) { addFlag(x, y, clipdata); } else { - RegionManager.addClippingFlag(plane, baseX + x, baseY + y, projectile, clipdata); + RegionManager.addClippingFlag(baseZ, baseX + x, baseY + y, projectile, clipdata); } } @@ -160,15 +162,15 @@ public final class RegionFlags { * @param clipdata The clip data. */ public void unflag(int x, int y, int clipdata) { - if (x > -1 && x < 64 && y > -1 && y < 64) { + if (x > -1 && x < 8 && y > -1 && y < 8) { removeFlag(x, y, clipdata); } else { - RegionManager.removeClippingFlag(plane, baseX + x, baseY + y, projectile, clipdata); + RegionManager.removeClippingFlag(baseZ, baseX + x, baseY + y, projectile, clipdata); } } private Pair getFlagIndex(int x, int y) { - return new Pair<>(((baseX >> 6) << 8) | (baseY >> 6), (plane * 64 * 64) + (x * 64) + y); + return new Pair<>((baseZ << 22) | ((baseX >> 3) << 11) | (baseY >> 3), (x * 8) + y); } public int getFlag(int x, int y) { @@ -349,7 +351,7 @@ public final class RegionFlags { } /** - * Unlags a door object (type 0-3). + * Unflags a door object (type 0-3). * @param x The x-coordinate * @param y The y-coordinate. * @param rotation The rotation. @@ -515,8 +517,8 @@ public final class RegionFlags { * Gets the plane. * @return The plane. */ - public int getPlane() { - return plane; + public int getBaseZ() { + return baseZ; } /** @@ -534,4 +536,4 @@ public final class RegionFlags { public void setLandscape(boolean[][] landscape) { this.landscape = landscape; } -} \ No newline at end of file +} diff --git a/Server/src/main/core/game/world/map/build/DynamicRegion.java b/Server/src/main/core/game/world/map/build/DynamicRegion.java index 2ccbabc69..96671133a 100644 --- a/Server/src/main/core/game/world/map/build/DynamicRegion.java +++ b/Server/src/main/core/game/world/map/build/DynamicRegion.java @@ -5,7 +5,6 @@ import core.game.node.entity.player.Player; import core.game.node.entity.player.link.music.MusicZone; import core.game.node.item.GroundItem; import core.game.node.item.GroundItemManager; -import core.game.node.scenery.Scenery; import core.game.world.map.*; import core.game.world.map.zone.RegionZone; import core.game.world.map.zone.ZoneBorders; @@ -13,18 +12,15 @@ import core.game.world.map.zone.impl.MultiwayCombatZone; import org.jetbrains.annotations.NotNull; import java.util.ArrayList; -import java.util.Arrays; import java.util.Iterator; import java.util.List; - /** * Represents a dynamically constructed region. * * @author Emperor */ public final class DynamicRegion extends Region { - /** * The reserved areas. */ @@ -35,11 +31,6 @@ public final class DynamicRegion extends Region { */ private final int regionId; - /** - * The region chunks. - */ - private final RegionChunk[][][] chunks; - /** * The zone borders. */ @@ -76,14 +67,25 @@ public final class DynamicRegion extends Region { public DynamicRegion(int regionId, int x, int y) { super(x, y); this.regionId = regionId; - this.chunks = new RegionChunk[4][SIZE >> 3][SIZE >> 3]; - RegionManager.resetFlags(getId()); + // Reset any stale flags at this region's chunk coordinates + for (int chunkX = 0; chunkX < CHUNKS_SIZE; chunkX++) { + for (int chunkY = 0; chunkY < CHUNKS_SIZE; chunkY++) { + for (int cz = 0; cz < Region.PLANES; cz++) { + RegionChunk chunk = chunks[chunkX][chunkY][cz]; + for (int chunkOffsetX = 0; chunkOffsetX < 8; chunkOffsetX++) { + for (int chunkOffsetY = 0; chunkOffsetY < 8; chunkOffsetY++) { + chunk.getFlags().invalidateFlag(chunkOffsetX, chunkOffsetY); //the 'invalid' value of -1 triggers reloading + chunk.getProjectileFlags().clearFlag(chunkOffsetX, chunkOffsetY); //the 'clear' value of 0 is the default state + } + } + } + } + } } public DynamicRegion(@NotNull ZoneBorders borders) { this(-1, borders.getSouthWestX() >> 6, borders.getSouthWestY() >> 6); setBorders(borders); - setUpdateAllPlanes(true); RegionManager.addRegion(getId(), this); } @@ -120,7 +122,6 @@ public final class DynamicRegion extends Region { public static DynamicRegion[] create(ZoneBorders copy) { int baseX = copy.getSouthWestX() >> 6; int baseY = copy.getSouthWestY() >> 6; - ZoneBorders border = findZoneBorders((copy.getNorthEastX() - copy.getSouthWestX()) >> 3, (copy.getNorthEastY() - copy.getSouthWestY()) >> 3); RESERVED_AREAS.add(border); Location l = Location.create(border.getSouthWestX(), border.getSouthWestY(), 0); @@ -129,23 +130,18 @@ public final class DynamicRegion extends Region { for (int y = copy.getSouthWestY() >> 6; y < copy.getNorthEastY() >> 6; y++) { int regionId = x << 8 | y; Location loc = l.transform((x - baseX) << 6, (y - baseY) << 6, 0); - DynamicRegion region = copy(regionId, loc); - region.setBorders(border); - Region r = RegionManager.forId(region.getId()); - if (r != null) { - for (int z = 0; z < 4; z++) { - region.getPlanes()[z].getPlayers().addAll(r.getPlanes()[z].getPlayers()); - region.getPlanes()[z].getNpcs().addAll(r.getPlanes()[z].getNpcs()); - } - } - RegionManager.addRegion(region.getId(), region); - regions.add(region); + DynamicRegion dest = copy(regionId, loc); + dest.setBorders(border); + RegionManager.addRegion(dest.getId(), dest); + regions.add(dest); } } for (Region r : regions) { - for (int z = 0; z < 4; z++) { - for (Player p : r.getPlanes()[z].getPlayers()) { - p.updateSceneGraph(false); + for (int z = 0; z < PLANES; z++) { + for (Player player : r.assemblePlayerList(z)) { + if (player != null) { + player.updateSceneGraph(false); + } } } } @@ -176,11 +172,11 @@ public final class DynamicRegion extends Region { int count = 0; int width = (sizeX >> 3) << 6; int height = (sizeY >> 3) << 6; - if (width < 64) { - width = 64; + if (width < SIZE) { + width = SIZE; } - if (height < 64) { - height = 64; + if (height < SIZE) { + height = SIZE; } while (true) { int endX = x + width; @@ -197,10 +193,10 @@ public final class DynamicRegion extends Region { return new ZoneBorders(x, y, endX, endY); } if (++count % 15 == 0) { - y += 64; + y += SIZE; x = 0; } else { - x += 64; + x += SIZE; } } } @@ -221,12 +217,14 @@ public final class DynamicRegion extends Region { for (int offsetY = 0; offsetY < 8; offsetY++) { int x = regionX + (offsetX << 3); int y = regionY + (offsetY << 3); - for (int plane = 0; plane < 4; plane++) { - RegionChunk c = base.getPlanes()[plane].getRegionChunk(offsetX, offsetY); + for (int plane = 0; plane < PLANES; plane++) { + RegionChunk c = base.getChunks()[offsetX][offsetY][plane]; if (c == null) { - region.chunks[plane][offsetX][offsetY] = c = new RegionChunk(Location.create(0, 0, 0), 0, region.getPlanes()[plane]); + c = new RegionChunk(Location.create(0, 0, 0), 0); + region.chunks[offsetX][offsetY][plane] = c; } else { - region.replaceChunk(plane, offsetX, offsetY, (c = c.copy(region.getPlanes()[plane])), base); + c = c.copy(); + region.replaceChunk(plane, offsetX, offsetY, c, base); } c.setRotation(0); c.setBase(Location.create(x, y, plane)); @@ -274,71 +272,31 @@ public final class DynamicRegion extends Region { getMusicZones().add(new MusicZone(musicId, borders)); } - /** - * Rotates the region 90% clockwise. - * TODO: Finish this - */ - public void rotate() { - for (int z = 0; z < 4; z++) { - RegionChunk[][] c = Arrays.copyOf(chunks[z], 8); - for (int x = 0; x < 8; x++) { - for (int y = 0; y < 8; y++) { - RegionChunk r = chunks[z][x][y] = c[8 - y - 1][x]; - if (r != null) { - r.setRotation(r.getRotation() + 1); - } - } - } - } - } - - /** - * Sets a region chunk (without setting objects/clipping flags). - * @param z The plane. - * @param x The chunk x (0-8). - * @param y The chunk y (0-8). - * @param chunk The chunk to set. - */ - public void setChunk(int z, int x, int y, RegionChunk chunk) { - chunks[z][x][y] = chunk; - getPlanes()[z].getChunks()[x][y] = chunk; - if (chunk != null) { - chunk.setCurrentBase(getBaseLocation().transform(x << 3, y << 3, 0)); - } - } - /** * Replaces a region chunk. * @param z The plane. * @param x The x-coordinate of the chunk. (0-7) * @param y The y-coordinate of the chunk. (0-7) - * @param chunk The chunk to replace with. + * @param replacement The chunk to replace with. * @param fromRegion The region the chunk is copied from. */ - public void replaceChunk(int z, int x, int y, RegionChunk chunk, Region fromRegion) { + public void replaceChunk(int z, int x, int y, RegionChunk replacement, Region fromRegion) { Region.load(DynamicRegion.this); - RegionPlane p = getPlanes()[z]; - chunks[z][x][y] = chunk; - p.getChunks()[x][y] = chunk; - if (chunk == null) { - for (int i = x << 3; i < (x + 1) << 3; i++) { - for (int j = y << 3; j < (y + 1) << 3; j++) { - p.getFlags().invalidateFlag(i, j); - p.getProjectileFlags().invalidateFlag(i, j); - Scenery object = p.getObjects()[i][j]; - if (object != null) { - LandscapeParser.removeScenery(object); - } else { - p.add(null, i, j, true); - } - } - } - } else { - Region.load(fromRegion); - Location l = chunk.getBase(); - RegionPlane rp = fromRegion.getPlanes()[l.getZ()]; - chunk.setCurrentBase(getBaseLocation().transform(x << 3, y << 3, z)); - chunk.rebuildFlags(rp); + chunks[x][y][z] = replacement; + Region.load(fromRegion); + RegionChunk orig = fromRegion.getChunks()[x][y][z]; + Location currentBase = getBaseLocation().transform(x << 3, y << 3, z); + replacement.setCurrentBase(currentBase); + replacement.rebaseObjects(); + replacement.resetClippingFlags(); + replacement.rebuildClippingFlags(orig); + for (NPC npc : orig.getNpcs()) { + Location newLoc = currentBase.transform(npc.getLocation().getChunkOffsetX(), npc.getLocation().getChunkOffsetY(), 0); + NPC copy = NPC.create(npc.getId(), newLoc, npc.getDirection()); + copy.setAggressive(npc.isAggressive()); + copy.setRespawn(npc.isRespawn()); + copy.setWalks(npc.isWalks()); + copy.init(); } } @@ -356,20 +314,18 @@ public final class DynamicRegion extends Region { } } } - if(!super.flagInactive(force)) { - return false; - } - for (RegionPlane plane : getPlanes()) { - for (int i = 0; i < plane.getNpcs().size(); i++) { - NPC npc = plane.getNpcs().get(0); - npc.clear(); - } - for (RegionChunk[] chunks : getChunks()[plane.getPlane()]) { - for (RegionChunk chunk : chunks) { - if (chunk != null) { - for (Iterator it = chunk.getItems().iterator(); it.hasNext();) { - GroundItemManager.getItems().remove(it.next()); - } + if (!super.flagInactive(force)) { + return false; + } + for (int x = 0; x < CHUNKS_SIZE; x++) { + for (int y = 0; y < CHUNKS_SIZE; y++) { + for (int z = 0; z < PLANES; z++) { + RegionChunk chunk = chunks[x][y][z]; + for (NPC npc : chunk.getNpcs()) { + npc.clear(); + } + for (GroundItem item : chunk.getItems()) { + GroundItemManager.destroy(item); } } } @@ -378,16 +334,16 @@ public final class DynamicRegion extends Region { if (multicombat) { toggleMulticombat(); } - boolean allLinkedInactive = true; + boolean allLinkedInactive = true; if (linked != null) { for (DynamicRegion r : linked) { allLinkedInactive &= r.flagInactive(force); } } - return allLinkedInactive; + return allLinkedInactive; } else { - return true; - } + return true; + } } @Override @@ -395,14 +351,6 @@ public final class DynamicRegion extends Region { return regionId; } - /** - * Gets the chunks. - * @return The chunks. - */ - public RegionChunk[][][] getChunks() { - return chunks; - } - /** * Gets the borders. * @return The borders. diff --git a/Server/src/main/core/game/world/map/build/LandscapeParser.java b/Server/src/main/core/game/world/map/build/LandscapeParser.java index 81025e7d6..618e3637a 100644 --- a/Server/src/main/core/game/world/map/build/LandscapeParser.java +++ b/Server/src/main/core/game/world/map/build/LandscapeParser.java @@ -3,11 +3,7 @@ package core.game.world.map.build; import core.cache.def.impl.SceneryDefinition; import core.cache.misc.buffer.ByteBufferUtils; import core.game.node.scenery.Scenery; -import core.game.world.map.Location; -import core.game.world.map.Region; -import core.game.world.map.RegionManager; -import core.game.world.map.RegionPlane; -import core.game.system.config.ObjectConfigParser; +import core.game.world.map.*; import java.nio.ByteBuffer; @@ -17,14 +13,13 @@ import java.nio.ByteBuffer; * */ public final class LandscapeParser { - /** * Parses the landscape. * @param r The region. * @param mapscape The mapscape data. * @param storeObjects If all objects should be stored (rather than just the objects with options). */ - public static void parse(Region r, byte[][][] mapscape, ByteBuffer buffer, boolean storeObjects) { + public static void parse(Region r, RegionChunk[][][] chunks, byte[][][] mapscape, ByteBuffer buffer, boolean storeObjects) { int objectId = -1; for (;;) { int offset = ByteBufferUtils.getBigSmart(buffer); @@ -39,23 +34,25 @@ public final class LandscapeParser { break; } location += offset - 1; - int y = location & 0x3f; - int x = location >> 6 & 0x3f; + int localY = location & 0x3f; + int localX = location >> 6 & 0x3f; int configuration = buffer.get() & 0xFF; int rotation = configuration & 0x3; int type = configuration >> 2; int z = location >> 12; r.setObjectCount(r.getObjectCount() + 1); - if (x < 0 || y < 0 || x >= 64 || y >= 64) { - - } else { - if ((mapscape[1][x][y] & 0x2) == 2) { - z--; - } - if (z >= 0 && z <= 3) { - Scenery object = new Scenery(objectId, Location.create((r.getX() << 6) + x, (r.getY() << 6) + y, z), type, rotation); - flagScenery(r.getPlanes()[z], x, y, object, true, storeObjects); - } + if ((mapscape[1][localX][localY] & 0x2) == 2) { + z--; + } + if (z >= 0 && z <= 3) { + Location loc = Location.create((r.getX() << 6) + localX, (r.getY() << 6) + localY, z); + Scenery object = new Scenery(objectId, loc, type, rotation); + int chunkX = loc.getChunkX(); + int chunkY = loc.getChunkY(); + int chunkOffsetX = loc.getChunkOffsetX(); + int chunkOffsetY = loc.getChunkOffsetY(); + RegionChunk chunk = chunks[chunkX][chunkY][z]; + flagScenery(chunk, chunkOffsetX, chunkOffsetY, object, true, storeObjects); } } } @@ -76,34 +73,34 @@ public final class LandscapeParser { */ public static void addScenery(Scenery object, boolean landscape) { Location l = object.getLocation(); - flagScenery(RegionManager.getRegionPlane(l), l.getLocalX(), l.getLocalY(), object, landscape, false); + flagScenery(RegionManager.getRegionChunk(l), l.getChunkOffsetX(), l.getChunkOffsetY(), object, landscape, false); } /** * Flags a scenery on the plane's clipping flags. - * @param plane The plane. + * @param chunk The chunk. * @param object The object. * @param landscape If we are adding this scenery permanent. * @param storeObjects If all objects should be stored (rather than just the objects with options). */ - public static void flagScenery(RegionPlane plane, int localX, int localY, Scenery object, boolean landscape, boolean storeObjects) { - Region.load(plane.getRegion()); + public static void flagScenery(RegionChunk chunk, int chunkOffsetX, int chunkOffsetY, Scenery object, boolean landscape, boolean storeObjects) { + Region r = chunk.getRegion(); + Region.load(r); SceneryDefinition def = object.getDefinition(); object.setActive(true); + object.setRenderable(true); boolean add = storeObjects || !landscape || def.getChildObject(null).hasActions(); if (add) { - addPlaneObject(plane, object, localX, localY, landscape, storeObjects); - } - - if (!applyClippingFlagsFor(plane, localX, localY, object)) - return; - - if (!storeObjects && !add && (!def.getChildObject(null).getName().equals("null"))) { - addPlaneObject(plane, object, localX, localY, landscape, false); + if (landscape) { + chunk.addStatDyn(object, chunkOffsetX, chunkOffsetY); + } else { + chunk.add(object); + } } + applyClippingFlagsFor(chunk, chunkOffsetX, chunkOffsetY, object); } - public static boolean applyClippingFlagsFor(RegionPlane plane, int localX, int localY, Scenery object) { + public static boolean applyClippingFlagsFor(RegionChunk chunk, int chunkOffsetX, int chunkOffsetY, Scenery object) { SceneryDefinition def = object.getDefinition(); int sizeX; int sizeY; @@ -116,27 +113,27 @@ public final class LandscapeParser { } int type = object.getType(); if (type == 22) { //Tile - plane.getFlags().getLandscape()[localX][localY] = true; + chunk.getFlags().getLandscape()[chunkOffsetX][chunkOffsetY] = true; if (def.interactable != 0 || def.clipType == 1 || def.secondBool) { if (def.clipType == 1) { - plane.getFlags().flagTileObject(localX, localY); + chunk.getFlags().flagTileObject(chunkOffsetX, chunkOffsetY); if (def.isProjectileClipped()) { - plane.getProjectileFlags().flagTileObject(localX, localY); + chunk.getProjectileFlags().flagTileObject(chunkOffsetX, chunkOffsetY); } } } } else if (type >= 9) { //Default objects if (def.clipType != 0) { - plane.getFlags().flagSolidObject(localX, localY, sizeX, sizeY, def.projectileClipped); + chunk.getFlags().flagSolidObject(chunkOffsetX, chunkOffsetY, sizeX, sizeY, def.projectileClipped); if (def.isProjectileClipped()) { - plane.getProjectileFlags().flagSolidObject(localX, localY, sizeX, sizeY, def.projectileClipped); + chunk.getProjectileFlags().flagSolidObject(chunkOffsetX, chunkOffsetY, sizeX, sizeY, def.projectileClipped); } } } else if (type >= 0 && type <= 3) { //Doors/walls if (def.clipType != 0) { - plane.getFlags().flagDoorObject(localX, localY, object.getRotation(), type, def.projectileClipped); + chunk.getFlags().flagDoorObject(chunkOffsetX, chunkOffsetY, object.getRotation(), type, def.projectileClipped); if (def.isProjectileClipped()) { - plane.getProjectileFlags().flagDoorObject(localX, localY, object.getRotation(), type, def.projectileClipped); + chunk.getProjectileFlags().flagDoorObject(chunkOffsetX, chunkOffsetY, object.getRotation(), type, def.projectileClipped); } } } else { @@ -145,24 +142,6 @@ public final class LandscapeParser { return true; } - /** - * Adds an object to the region plane. - * @param plane The region plane. - * @param object The object to add. - * @param localX The local x-coordinate. - * @param localY The local y-coordinate. - * @param landscape The landscape. - */ - private static void addPlaneObject(RegionPlane plane, Scenery object, int localX, int localY, boolean landscape, boolean storeAll) { - if (landscape && !storeAll) { - Scenery current = plane.getObjects()[localX][localY]; - if (current != null && current.getDefinition().getChildObject(null).hasOptions(!object.getDefinition().getChildObject(null).hasOptions(false))) { - return; - } - } - plane.add(object, localX, localY, landscape && !storeAll); - } - /** * Removes a scenery. * @param object The object. @@ -172,18 +151,23 @@ public final class LandscapeParser { if (!object.isRenderable()) { return null; } - RegionPlane plane = RegionManager.getRegionPlane(object.getLocation()); - Region.load(plane.getRegion()); - int localX = object.getLocation().getLocalX(); - int localY = object.getLocation().getLocalY(); - Scenery current = plane.getChunkObject(localX, localY, object.getId()); - if (current == null || current.getId() != object.getId()) { + + // Get and remove the object + Location loc = object.getLocation(); + Region region = RegionManager.forId(loc.getRegionId()); + Region.load(region); + RegionChunk chunk = region.getChunks()[loc.getChunkX()][loc.getChunkY()][loc.getZ()]; + int index = chunk.getIndex(loc.getChunkOffsetX(), loc.getChunkOffsetY(), object.getId(), object.getType()); + if (index == -1) { return null; } + Scenery current = chunk.getObjects()[loc.getChunkOffsetX()][loc.getChunkOffsetY()][index]; current.setActive(false); object.setActive(false); - plane.remove(localX, localY, object.getId()); + chunk.getObjects()[loc.getChunkOffsetX()][loc.getChunkOffsetY()][index] = null; SceneryDefinition def = object.getDefinition(); + + // Remove its clipping flags int sizeX; int sizeY; if (object.getRotation() % 2 == 0) { @@ -193,28 +177,30 @@ public final class LandscapeParser { sizeX = def.sizeY; sizeY = def.sizeX; } + int chunkOffsetX = loc.getChunkOffsetX(); + int chunkOffsetY = loc.getChunkOffsetY(); int type = object.getType(); if (type == 22) { //Tile if (def.interactable != 0 || def.clipType == 1 || def.secondBool) { if (def.clipType == 1) { - plane.getFlags().unflagTileObject(localX, localY); + chunk.getFlags().unflagTileObject(chunkOffsetX, chunkOffsetY); if (def.isProjectileClipped()) { - plane.getProjectileFlags().unflagTileObject(localX, localY); + chunk.getProjectileFlags().unflagTileObject(chunkOffsetX, chunkOffsetY); } } } } else if (type >= 9) { //Default objects if (def.clipType != 0) { - plane.getFlags().unflagSolidObject(localX, localY, sizeX, sizeY, def.projectileClipped); + chunk.getFlags().unflagSolidObject(chunkOffsetX, chunkOffsetY, sizeX, sizeY, def.projectileClipped); if (def.isProjectileClipped()) { - plane.getProjectileFlags().unflagSolidObject(localX, localY, sizeX, sizeY, def.projectileClipped); + chunk.getProjectileFlags().unflagSolidObject(chunkOffsetX, chunkOffsetY, sizeX, sizeY, def.projectileClipped); } } } else if (type >= 0 && type <= 3) { //Doors/walls if (def.clipType != 0) { - plane.getFlags().unflagDoorObject(localX, localY, object.getRotation(), type, def.projectileClipped); + chunk.getFlags().unflagDoorObject(chunkOffsetX, chunkOffsetY, object.getRotation(), type, def.projectileClipped); if (def.isProjectileClipped()) { - plane.getProjectileFlags().unflagDoorObject(localX, localY, object.getRotation(), type, def.projectileClipped); + chunk.getProjectileFlags().unflagDoorObject(chunkOffsetX, chunkOffsetY, object.getRotation(), type, def.projectileClipped); } } } diff --git a/Server/src/main/core/game/world/map/build/MapscapeParser.java b/Server/src/main/core/game/world/map/build/MapscapeParser.java index 7ecc4c81c..16866d653 100644 --- a/Server/src/main/core/game/world/map/build/MapscapeParser.java +++ b/Server/src/main/core/game/world/map/build/MapscapeParser.java @@ -1,6 +1,7 @@ package core.game.world.map.build; import core.game.world.map.Region; +import core.game.world.map.RegionChunk; import java.nio.ByteBuffer; @@ -17,10 +18,25 @@ public final class MapscapeParser { * @param buffer The buffer. */ public static void parse(Region r, byte[][][] mapscape, ByteBuffer buffer) { + if (r.getChunks() == null) { + return; //this is a DynamicRegion that has been cleared + } + // The cache is packed by region, so we need to parse it along the local-x/y coordinates for (int z = 0; z < 4; z++) { - boolean[][] landscape = r.getPlanes()[z].getFlags().getLandscape(); - for (int x = 0; x < 64; x++) { - for (int y = 0; y < 64; y++) { + for (int localX = 0; localX < Region.SIZE; localX++) { + for (int localY = 0; localY < Region.SIZE; localY++) { + int chunkX = localX >> 3; + int chunkY = localY >> 3; + int chunkOffsetX = localX & 7; + int chunkOffsetY = localY & 7; + RegionChunk chunk = r.getChunks()[chunkX][chunkY][z]; + boolean[][] landscape; + if (chunk == null) { + // Still need to consume the buffer data even for null chunks + landscape = new boolean[RegionChunk.SIZE][RegionChunk.SIZE]; + } else { + landscape = chunk.getFlags().getLandscape(); + } while (true) { int value = buffer.get() & 0xFF; if (value == 0) { @@ -33,16 +49,14 @@ public final class MapscapeParser { if (value <= 49) { //Overlay data int val = buffer.get() & 0xFF; if (val != 42 && val > 0) { - landscape[x][y] = true; + landscape[chunkOffsetX][chunkOffsetY] = true; } - } - else if (value <= 81) { - mapscape[z][x][y] = (byte) (value - 49); - } - else { + } else if (value <= 81) { + mapscape[z][localX][localY] = (byte) (value - 49); + } else { int val = (byte) (value - 81) & 0xFF; //Underlay data if (val != 42 && val > 0) { - landscape[x][y] = true; + landscape[chunkOffsetX][chunkOffsetY] = true; } } } @@ -56,22 +70,30 @@ public final class MapscapeParser { * @param r The region. * @param mapscape The mapscape. */ - public static void clipMapscape(Region r, byte[][][] mapscape) { - for (int z = 0; z < 4; z++) { - for (int x = 0; x < 64; x++) { - for (int y = 0; y < 64; y++) { - r.getPlanes()[z].getFlags().flagEmptyTile(x,y); - if ((mapscape[z][x][y] & 0x1) == 1) { - int plane = z; - if ((mapscape[1][x][y] & 0x2) == 2) { - plane--; - } - if (plane > -1) { - r.getPlanes()[plane].getFlags().flagSolidTile(x, y); + public static void clipMapscape(Region r, RegionChunk[][][] chunks, byte[][][] mapscape) { + for (int z = 0; z < Region.PLANES; z++) { + for (int chunkX = 0; chunkX < RegionChunk.SIZE; chunkX++) { + for (int chunkY = 0; chunkY < RegionChunk.SIZE; chunkY++) { + RegionChunk chunk = chunks[chunkX][chunkY][z]; + for (int x = 0; x < RegionChunk.SIZE; x++) { + for (int y = 0; y < RegionChunk.SIZE; y++) { + chunk.getFlags().flagEmptyTile(x, y); + int chunkOffsetX = chunkX*RegionChunk.SIZE + x; + int chunkOffsetY = chunkY*RegionChunk.SIZE + y; + if ((mapscape[z][chunkOffsetX][chunkOffsetY] & 0x1) == 1) { + int plane = z; + if ((mapscape[1][chunkOffsetX][chunkOffsetY] & 0x2) == 2) { + plane--; + } + if (plane > -1) { + RegionChunk chunkToFlag = r.getChunks()[chunkX][chunkY][plane]; + chunkToFlag.getFlags().flagSolidTile(x, y); + } + } } } } } } } -} \ No newline at end of file +} diff --git a/Server/src/main/core/game/world/map/zone/ZoneMonitor.java b/Server/src/main/core/game/world/map/zone/ZoneMonitor.java index 65b4a0322..a7176dd04 100644 --- a/Server/src/main/core/game/world/map/zone/ZoneMonitor.java +++ b/Server/src/main/core/game/world/map/zone/ZoneMonitor.java @@ -362,7 +362,7 @@ public final class ZoneMonitor { it.remove(); } } - for (RegionZone zone : entity.getViewport().getRegion().getRegionZones()) { + for (RegionZone zone : entity.getLocation().getRegion().getRegionZones()) { if (!zone.getBorders().insideBorder(entity)) { continue; } @@ -403,7 +403,7 @@ public final class ZoneMonitor { } } } - Region r = player.getViewport().getRegion(); + Region r = player.getLocation().getRegion(); for (MusicZone zone : r.getMusicZones()) { if (zone.getBorders().insideBorder(l.getX(), l.getY())) { zone.enter(player); diff --git a/Server/src/main/core/game/world/map/zone/impl/ModeratorZone.java b/Server/src/main/core/game/world/map/zone/impl/ModeratorZone.java index 1c8b113dd..be5f59b64 100644 --- a/Server/src/main/core/game/world/map/zone/impl/ModeratorZone.java +++ b/Server/src/main/core/game/world/map/zone/impl/ModeratorZone.java @@ -64,13 +64,6 @@ public class ModeratorZone extends MapZone { return true; } - @Override - public boolean interact(Entity entity, Node target, Option option) { - if (entity instanceof Player) { - } - return super.interact(entity, target, option); - } - @Override public void configure() { register(new ZoneBorders(2840, 5204, 2853, 5224)); @@ -82,11 +75,11 @@ public class ModeratorZone extends MapZone { * @param on the toggle switch. */ public static final void toggle(final Player player, final boolean on) { - open = on ? true : false; + open = on; player.getPacketDispatch().sendMessage(getToggleMessage()); if (!open) { for (Player p : RegionManager.getLocalPlayers(center)) { - if (p == null || p.getDetails().getRights() == Rights.ADMINISTRATOR) { + if (p.getDetails().getRights() == Rights.ADMINISTRATOR) { continue; } home(p); @@ -123,10 +116,7 @@ public class ModeratorZone extends MapZone { * @return return True if so. */ public static boolean isOpen() { - if (!open) { - return false; - } - return true; + return open; } } diff --git a/Server/src/main/core/game/world/map/zone/impl/MultiwayCombatZone.java b/Server/src/main/core/game/world/map/zone/impl/MultiwayCombatZone.java index 77477b3b1..3723dcf70 100644 --- a/Server/src/main/core/game/world/map/zone/impl/MultiwayCombatZone.java +++ b/Server/src/main/core/game/world/map/zone/impl/MultiwayCombatZone.java @@ -3,7 +3,6 @@ package core.game.world.map.zone.impl; import core.game.node.entity.Entity; import core.game.node.entity.npc.NPC; import core.game.node.entity.player.Player; -import core.game.world.map.Direction; import core.game.world.map.Location; import core.game.world.map.MapDistance; import core.game.world.map.RegionManager; @@ -127,21 +126,21 @@ public final class MultiwayCombatZone extends MapZone { if (e.getProperties().isNPCWalkable()) { return true; } - for (NPC n : RegionManager.getLocalNpcs(e, MapDistance.RENDERING.getDistance() / 2)) { - if (n.isInvisible() || !n.getDefinition().hasAttackOption() || n == e) { - continue; - } - if(n.shouldPreventStacking(e)) { - int s1 = e.size(); - int s2 = n.size(); - int x = destination.getX(); - int y = destination.getY(); - Location l = n.getLocation(); - if(Pathfinder.isStandingIn(x, y, s1, s1, l.getX(), l.getY(), s2, s2)) { - return false; - } - } - } + for (NPC n : RegionManager.getLocalNPCs(e.getLocation(), MapDistance.RENDERING.getDistance() / 2)) { + if (n.isInvisible() || !n.getDefinition().hasAttackOption() || n == e) { + continue; + } + if(n.shouldPreventStacking(e)) { + int s1 = e.size(); + int s2 = n.size(); + int x = destination.getX(); + int y = destination.getY(); + Location l = n.getLocation(); + if(Pathfinder.isStandingIn(x, y, s1, s1, l.getX(), l.getY(), s2, s2)) { + return false; + } + } + } return true; } diff --git a/Server/src/main/core/game/world/objectparser/ObjectParser.java b/Server/src/main/core/game/world/objectparser/ObjectParser.java deleted file mode 100644 index 39b445e31..000000000 --- a/Server/src/main/core/game/world/objectparser/ObjectParser.java +++ /dev/null @@ -1,101 +0,0 @@ -package core.game.world.objectparser; - -import core.api.StartupListener; -import core.ServerConstants; -import core.game.node.scenery.Scenery; -import core.game.world.map.build.LandscapeParser; -import org.w3c.dom.Document; -import org.w3c.dom.Element; -import org.w3c.dom.Node; -import org.w3c.dom.NodeList; - -import javax.xml.parsers.DocumentBuilder; -import javax.xml.parsers.DocumentBuilderFactory; -import java.io.File; - -public class ObjectParser implements StartupListener { - - public void parseObjects(){ - if(ServerConstants.OBJECT_PARSER_PATH == null) return; - File f = new File(ServerConstants.OBJECT_PARSER_PATH); - if(!f.exists()){ - - return; - } - - try{ - DocumentBuilderFactory factory = DocumentBuilderFactory.newInstance(); - DocumentBuilder builder = factory.newDocumentBuilder(); - Document doc = builder.parse(f); - - NodeList parseList = doc.getElementsByTagName("ObjectAction"); - - - - for(int i = 0; i < parseList.getLength(); i++){ - Node parseNode = parseList.item(i); - if(parseNode.getNodeType() == Node.ELEMENT_NODE){ - Element parseElement = (Element) parseNode; - String type = parseElement.getAttribute("mode"); - switch(type){ - case "add": { - int id = Integer.parseInt(parseElement.getAttribute("id")); - int x = Integer.parseInt(parseElement.getAttribute("x")); - int y = Integer.parseInt(parseElement.getAttribute("y")); - int z = Integer.parseInt(parseElement.getAttribute("z")); - int objType = 10; - if (parseElement.hasAttribute("type")) { - objType = Integer.parseInt(parseElement.getAttribute("type")); - } - String rawDir = parseElement.getAttribute("direction"); - int dir = 1; - switch (rawDir) { - case "n": - dir = 1; - break; - case "ne": - dir = 2; - break; - case "nw": - dir = 0; - break; - case "w": - dir = 3; - break; - case "e": - dir = 4; - break; - case "sw": - dir = 5; - break; - case "se": - dir = 7; - break; - case "s": - dir = 6; - break; - } - LandscapeParser.addScenery(new Scenery(id, x, y, z, objType, dir)); - break; - } - case "remove": { - int id = Integer.parseInt(parseElement.getAttribute("id")); - int x = Integer.parseInt(parseElement.getAttribute("x")); - int y = Integer.parseInt(parseElement.getAttribute("y")); - int z = Integer.parseInt(parseElement.getAttribute("z")); - int objType = 10; - LandscapeParser.removeScenery(new Scenery(id,x,y,z)); - } - } - } - } - } catch (Exception e){ - e.printStackTrace(); - } - } - - @Override - public void startup() { - parseObjects(); - } -} diff --git a/Server/src/main/core/game/world/repository/Repository.kt b/Server/src/main/core/game/world/repository/Repository.kt index 0b97f197c..d2b09a4bd 100644 --- a/Server/src/main/core/game/world/repository/Repository.kt +++ b/Server/src/main/core/game/world/repository/Repository.kt @@ -127,7 +127,7 @@ object Repository { */ @JvmStatic fun findNPC(l: Location): NPC? { - for (n in RegionManager.getRegionPlane(l).npcs) { + for (n in RegionManager.getRegionChunk(l).npcs) { if (n.location == l) { return n } diff --git a/Server/src/main/core/game/world/update/ChunkDirtyListener.kt b/Server/src/main/core/game/world/update/ChunkDirtyListener.kt new file mode 100644 index 000000000..38fba5192 --- /dev/null +++ b/Server/src/main/core/game/world/update/ChunkDirtyListener.kt @@ -0,0 +1,22 @@ +package core.game.world.update + +import core.game.world.map.RegionChunk +import java.util.Collections +import java.util.IdentityHashMap + +object ChunkUpdateTracker : ChunkDirtyListener { + private val dirty = Collections.newSetFromMap(IdentityHashMap()) + + override fun onFlagged(chunk: RegionChunk) { + dirty.add(chunk) + } + + /** Resets flags on exactly the chunks flagged this tick. Must run after all per-player flushes. */ + fun resetAll() { + dirty.forEach { it.resetUpdateFlags() } + dirty.clear() + } +} +fun interface ChunkDirtyListener { + fun onFlagged(chunk: RegionChunk) +} \ No newline at end of file diff --git a/Server/src/main/core/game/world/update/MapChunkRenderer.kt b/Server/src/main/core/game/world/update/MapChunkRenderer.kt index b9e20112a..3d0d644a9 100644 --- a/Server/src/main/core/game/world/update/MapChunkRenderer.kt +++ b/Server/src/main/core/game/world/update/MapChunkRenderer.kt @@ -1,6 +1,7 @@ package core.game.world.update import core.game.node.entity.player.Player +import core.game.world.map.Location import core.game.world.map.RegionChunk import core.net.packet.PacketRepository import core.net.packet.context.ClearChunkContext @@ -12,39 +13,54 @@ import java.util.* * @author Emperor */ object MapChunkRenderer { + const val BUILD_AREA_DEPTH = 6 + const val BUILD_AREA_SIZE = 2*BUILD_AREA_DEPTH + 1 + /** * Sends the map chunk rendering packet. * @param player The player. */ @JvmStatic fun render(player: Player) { - val v = player.viewport val last = player.playerFlags.lastViewport - val updated: MutableList = ArrayList() - val current = v.chunks + val updated: ArrayList = ArrayList() + val current: ArrayList> = ArrayList() + + val anchor = player.playerFlags.lastSceneGraph ?: player.location + val center = Location.create(anchor.x, anchor.y, player.location.z) + for (dcx in -BUILD_AREA_DEPTH..BUILD_AREA_DEPTH) { + val addX = ArrayList() + for (dcy in -BUILD_AREA_DEPTH..BUILD_AREA_DEPTH) { + val newloc = center.transform(dcx * 8, dcy * 8, 0) + addX.add(newloc.chunk) + } + current.add(addX) + } + var sizeX = last.size for (x in 0 until sizeX) { val sizeY: Int = last[x].size for (y in 0 until sizeY) { val previous = last[x][y] ?: continue - if (!containsChunk(current, previous)) { - PacketRepository.send(ClearRegionChunk::class.java, ClearChunkContext(player, previous)) - } else { + if (containsChunk(current, previous)) { updated.add(previous) + continue } + PacketRepository.send(ClearRegionChunk::class.java, ClearChunkContext(player, previous)) } } + sizeX = current.size for (x in 0 until sizeX) { val sizeY: Int = current[x].size for (y in 0 until sizeY) { val chunk = current[x][y] - if (!updated.contains(chunk)) { - chunk.synchronize(player) - } else { + if (updated.contains(chunk)) { chunk.update(player) + } else { + chunk.synchronize(player) } - last[x][y] = current[x][y] + last[x][y] = chunk } } } @@ -55,7 +71,7 @@ object MapChunkRenderer { * @param c The region chunk. * @return `True` if so. */ - private fun containsChunk(list: Array>, c: RegionChunk): Boolean { + private fun containsChunk(list: ArrayList>, c: RegionChunk): Boolean { val sizeList = list.size for (x in 0 until sizeList) { val chunkSize: Int = list[x].size diff --git a/Server/src/main/core/game/world/update/NPCRenderer.kt b/Server/src/main/core/game/world/update/NPCRenderer.kt index dbb2fd01c..01997e849 100644 --- a/Server/src/main/core/game/world/update/NPCRenderer.kt +++ b/Server/src/main/core/game/world/update/NPCRenderer.kt @@ -6,6 +6,7 @@ import core.game.world.map.RegionManager import core.net.packet.IoBuffer import core.net.packet.PacketHeader import core.game.world.GameWorld +import core.game.world.map.MapDistance import java.nio.ByteBuffer import java.util.* @@ -22,7 +23,7 @@ object NPCRenderer { fun render(player: Player) { val buffer = IoBuffer(32, PacketHeader.SHORT) val info = player.renderInfo - val localNPCs = info.localNpcs + val localNPCs = info.localNPCs val maskBuffer = IoBuffer(-1, PacketHeader.NORMAL, ByteBuffer.allocate(1 shl 16)) buffer.setBitAccess() buffer.putBits(8, localNPCs.size) @@ -30,7 +31,7 @@ object NPCRenderer { val it: Iterator = localNPCs.iterator() while (it.hasNext()) { val npc = it.next() - val withinDistance = player.location.withinDistance(npc.location) + val withinDistance = player.location.withinDistance(npc.location, MapDistance.RENDERING.distance) if (npc.isHidden(player) || !withinDistance || npc.properties.isTeleporting) { buffer.putBits(1, 1).putBits(2, 3) toRemove.add(npc) @@ -51,7 +52,7 @@ object NPCRenderer { } } localNPCs.removeAll(toRemove) - for (npc in RegionManager.getLocalNpcs(player)) { + for (npc in RegionManager.getLocalNPCs(player.location, MapDistance.RENDERING.distance)) { if (localNPCs.size >= 255) { break } diff --git a/Server/src/main/core/game/world/update/PlayerRenderer.kt b/Server/src/main/core/game/world/update/PlayerRenderer.kt index e192463d7..a3130646e 100644 --- a/Server/src/main/core/game/world/update/PlayerRenderer.kt +++ b/Server/src/main/core/game/world/update/PlayerRenderer.kt @@ -39,7 +39,7 @@ object PlayerRenderer { renderLocalPlayer(player, other, buffer, flags) } var count = 0 - for (other in RegionManager.getLocalPlayers(player, 15)) { + for (other in RegionManager.getLocalPlayers(player.location, 15)) { if (other === player || !other.isActive || info.localPlayers.contains(other) || other.isInvisible) { continue } @@ -177,4 +177,4 @@ object PlayerRenderer { player.updateMasks.write(local, player, flags) } } -} \ No newline at end of file +} diff --git a/Server/src/main/core/game/world/update/UpdateSequence.kt b/Server/src/main/core/game/world/update/UpdateSequence.kt index bc17fbd65..775900633 100644 --- a/Server/src/main/core/game/world/update/UpdateSequence.kt +++ b/Server/src/main/core/game/world/update/UpdateSequence.kt @@ -59,7 +59,7 @@ class UpdateSequence playersList!!.forEach(Player::reset) npcList!!.forEach(NPC::reset) renderablePlayers.sync() - RegionManager.pulse() + ChunkUpdateTracker.resetAll() GroundItemManager.pulse() } diff --git a/Server/src/main/core/game/world/update/flag/PlayerFlags.java b/Server/src/main/core/game/world/update/flag/PlayerFlags.java index c67408a65..2b9ea7c97 100644 --- a/Server/src/main/core/game/world/update/flag/PlayerFlags.java +++ b/Server/src/main/core/game/world/update/flag/PlayerFlags.java @@ -2,7 +2,7 @@ package core.game.world.update.flag; import core.game.world.map.Location; import core.game.world.map.RegionChunk; -import core.game.world.map.Viewport; +import core.game.world.update.MapChunkRenderer; /** * A class holding a player's updating flags. @@ -18,11 +18,11 @@ public final class PlayerFlags { /** * The last viewport. */ - private RegionChunk[][] lastViewport = new RegionChunk[Viewport.CHUNK_SIZE][Viewport.CHUNK_SIZE]; + private RegionChunk[][] lastViewport = new RegionChunk[MapChunkRenderer.BUILD_AREA_SIZE][MapChunkRenderer.BUILD_AREA_SIZE]; /** * The location the player was standing on when last scene graph update - * occured. + * occurred. */ private Location lastSceneGraph; diff --git a/Server/src/main/core/net/packet/PacketProcessor.kt b/Server/src/main/core/net/packet/PacketProcessor.kt index c05798490..b94cb886c 100644 --- a/Server/src/main/core/net/packet/PacketProcessor.kt +++ b/Server/src/main/core/net/packet/PacketProcessor.kt @@ -1,6 +1,5 @@ package core.net.packet -import content.global.ame.events.maze.MazeInterface import content.global.handlers.iface.ge.StockMarket import content.global.skill.magic.SpellListener import content.global.skill.magic.SpellListeners @@ -407,7 +406,7 @@ object PacketProcessor { player = pkt.player child = pkt.child iface = pkt.iface - target = RegionManager.getObject(player.location.z, pkt.x, pkt.y) ?: return sendClearMinimap(player) + target = RegionManager.getObject(pkt.x, pkt.y, player.location.z) ?: return sendClearMinimap(player) targetId = pkt.sceneryId type = SpellListener.OBJECT } @@ -580,7 +579,7 @@ object PacketProcessor { } else if (pkt is Packet.UseWithScenery) { item = pkt.player.inventory[pkt.slot] ?: return sendClearMinimap(pkt.player) - node = RegionManager.getObject(pkt.player.location.z, pkt.x, pkt.y) ?: return sendClearMinimap(pkt.player) + node = RegionManager.getObject(pkt.x, pkt.y, pkt.player.location.z, pkt.sceneryId, -1) ?: return sendClearMinimap(pkt.player) childNode = node.asScenery().getChild(pkt.player) itemId = pkt.itemId nodeId = node.id @@ -681,7 +680,7 @@ object PacketProcessor { private fun processSceneryAction(pkt: Packet.SceneryAction) { val player = pkt.player - var scenery = RegionManager.getObject(player.location.z, pkt.x, pkt.y, pkt.id) + var scenery = RegionManager.getObject(pkt.x, pkt.y, player.location.z, pkt.id, -1) var objId = pkt.id //what follows is a series of hardcoded crimes against humanity @@ -690,11 +689,6 @@ object PacketProcessor { if (pkt.id == 6899) scenery = Scenery(6899, Location(3221, 9618)) - // Random Event Maze chests are overridden by the walls, which needs to be hacked in. - if ((scenery?.id == 3626 || scenery?.id == 3635) && (objId in 3635..3636)) { - scenery = MazeInterface.overrideScenery(scenery, objId) - } - // Family crest levers don't have varps associated with them, so their state is validated with attributes // instead, and they always appear as their down/odd variant in the server's map if (objId in 2421..2426 && objId % 2 == 0) { diff --git a/Server/src/main/core/net/packet/out/BuildDynamicScene.java b/Server/src/main/core/net/packet/out/BuildDynamicScene.java index 3cea2b5db..d8fba5d7a 100644 --- a/Server/src/main/core/net/packet/out/BuildDynamicScene.java +++ b/Server/src/main/core/net/packet/out/BuildDynamicScene.java @@ -5,6 +5,7 @@ import core.game.world.map.Region; import core.game.world.map.RegionChunk; import core.game.world.map.RegionManager; import core.game.world.map.build.DynamicRegion; +import core.game.world.update.MapChunkRenderer; import core.net.packet.IoBuffer; import core.net.packet.OutgoingPacket; import core.net.packet.PacketHeader; @@ -29,25 +30,23 @@ public final class BuildDynamicScene implements OutgoingPacket> 3) << 8 | (y >> 3)); + for (int x = baseX; x <= player.getLocation().getRegionX() + 6; x++) { + for (int y = baseY; y <= player.getLocation().getRegionY() + 6; y++) { + for (int z = 0; z < Region.PLANES; z++) { + Region r = RegionManager.forId((x >> 3) << 8 | (y >> 3)); if (r instanceof DynamicRegion) { - DynamicRegion dr = (DynamicRegion) r; - chunks[z][x - baseX][y - baseY] = dr.getChunks()[z][x - (dr.getX() << 3)][y - (dr.getY() << 3)]; + chunks[x - baseX][y - baseY][z] = r.getChunks()[x - (r.getX() << 3)][y - (r.getY() << 3)][z]; } } } } - for (int plane = 0; plane < 4; plane++) { - for (int offsetX = 0; offsetX < 13; offsetX++) { - for (int offsetY = 0; offsetY < 13; offsetY++) { - RegionChunk c = chunks[plane][offsetX][offsetY]; + for (int plane = 0; plane < Region.PLANES; plane++) { + for (int offsetX = 0; offsetX < MapChunkRenderer.BUILD_AREA_SIZE; offsetX++) { + for (int offsetY = 0; offsetY < MapChunkRenderer.BUILD_AREA_SIZE; offsetY++) { + RegionChunk c = chunks[offsetX][offsetY][plane]; if (c == null || c.getBase().getX() < 0 || c.getBase().getY() < 0) { buffer.putBits(1, 0); continue; @@ -74,5 +73,4 @@ public final class BuildDynamicScene implements OutgoingPacket { + static final int BUILD_AREA_DEPTH_IN_TILES = MapChunkRenderer.BUILD_AREA_SIZE * RegionChunk.SIZE; @Override public void send(ClearChunkContext context) { Location l = context.getPlayer().getPlayerFlags().getLastSceneGraph(); int x = context.getChunk().getCurrentBase().getSceneX(l); int y = context.getChunk().getCurrentBase().getSceneY(l); - if (x >= 0 && y >= 0 && x < 96 && y < 96) { + if (x >= 0 && y >= 0 && x < BUILD_AREA_DEPTH_IN_TILES && y < BUILD_AREA_DEPTH_IN_TILES) { IoBuffer buffer = new IoBuffer(112).put(x).putC(y); - buffer.cypherOpcode(context.getPlayer().getSession().getIsaacPair().getOutput());context.getPlayer().getSession().write(buffer); + buffer.cypherOpcode(context.getPlayer().getSession().getIsaacPair().getOutput()); + context.getPlayer().getSession().write(buffer); } } - } diff --git a/Server/src/test/kotlin/RegionSpecificationTests.kt b/Server/src/test/kotlin/RegionSpecificationTests.kt index 52b6d630d..2884f0610 100644 --- a/Server/src/test/kotlin/RegionSpecificationTests.kt +++ b/Server/src/test/kotlin/RegionSpecificationTests.kt @@ -1,6 +1,5 @@ import core.api.regionspec.* import core.api.regionspec.contracts.FillChunkContract -import core.game.world.map.BuildRegionChunk import core.game.world.map.Region import core.game.world.map.RegionChunk import core.game.world.map.RegionManager @@ -33,7 +32,7 @@ class RegionSpecificationTests { fun shouldAllowFillingRegionWithGivenChunk() { val base = RegionManager.forId(12850) Region.load(base) - val chunk = base.planes[0].getRegionChunk(2, 2) + val chunk = base.getChunks()[2][2][0] val specification = RegionSpecification(fillWith(chunk).from(base).onPlanes(0)) val region = specification.build() Assertions.assertEquals(36782, RegionManager.getObject(region.baseLocation.transform(23, 17, 0))?.id) @@ -43,7 +42,7 @@ class RegionSpecificationTests { fun shouldAllowCustomRulesForFillingChunks() { val base = RegionManager.forId(12850) Region.load(base) - val chunk = base.planes[0].getRegionChunk(2, 2) + val chunk = base.getChunks()[2][2][0] val specification = RegionSpecification( fillWith(chunk) .from(base) @@ -59,7 +58,7 @@ class RegionSpecificationTests { fun shouldAllowMultipleRulesForFillingChunks() { val base = RegionManager.forId(12850) Region.load(base) - val chunk = base.planes[0].getRegionChunk(2, 2) + val chunk = base.getChunks()[2][2][0] val specification = RegionSpecification( fillWith(chunk) .from(base) @@ -81,7 +80,7 @@ class RegionSpecificationTests { Region.load(base) val specification = RegionSpecification( fillWith { destChunkX, destChunkY, destPlane, _ -> - base.planes[destPlane].getRegionChunk(destChunkX, destChunkY) + base.chunks[destchunkX][destChunkY][destPlane] }.from(base).onPlanes(0, 1, 2, 3) ) val region = specification.build() @@ -94,7 +93,7 @@ class RegionSpecificationTests { @Test fun shouldAllowUseExistingDynamicRegion() { val base = RegionManager.forId(12850) Region.load(base) - val chunk = base.planes[0].getRegionChunk(2, 2) + val chunk = base.getChunks()[2][2][0] val dyn = DynamicRegion.create(12850) val specification = RegionSpecification ( using(dyn), @@ -109,13 +108,13 @@ class RegionSpecificationTests { @Test fun fillChunkContractShouldAllowChunkSetCallback() { class TemporaryFillContract(chunk: RegionChunk) : FillChunkContract(chunk) { var callBackRan = false - override fun afterSetting(chunk: BuildRegionChunk?, x: Int, y: Int, plane: Int, dyn: DynamicRegion) { + override fun afterSetting(chunk: RegionChunk?, x: Int, y: Int, plane: Int, dyn: DynamicRegion) { callBackRan = true } } val base = RegionManager.forId(12850) Region.load(base) - val chunk = base.planes[0].getRegionChunk(2, 2) + val chunk = base.getChunks()[2][2][0] val dyn = DynamicRegion.create(12850) val fillTemporary = TemporaryFillContract(chunk) val specification = RegionSpecification ( diff --git a/Server/src/test/kotlin/RegionTests.kt b/Server/src/test/kotlin/RegionTests.kt index 7e48c338a..68f0b428a 100644 --- a/Server/src/test/kotlin/RegionTests.kt +++ b/Server/src/test/kotlin/RegionTests.kt @@ -1,14 +1,8 @@ -import core.cache.Cache import core.game.world.map.Region -import core.game.world.map.RegionChunk import core.game.world.map.RegionManager import core.game.world.map.build.DynamicRegion -import core.game.world.map.build.LandscapeParser -import org.junit.Assert import org.junit.jupiter.api.Assertions import org.junit.jupiter.api.Test -import core.game.system.config.ServerConfigParser -import core.game.system.config.XteaParser class RegionTests { companion object { @@ -67,8 +61,8 @@ class RegionTests { Region.load(dynamic) val targetLoc = dynamic.baseLocation.transform(23, 17, 0) Assertions.assertEquals(null, RegionManager.getObject(targetLoc), "Object exists pre-copy?") - val replacement = base.planes[0].getRegionChunk(2, 2) - dynamic.replaceChunk(0, 2, 2, replacement.copy(dynamic.planes[0]), base) + val replacement = base.getChunks()[2][2][0] + dynamic.replaceChunk(0, 2, 2, replacement.copy(), base) Assertions.assertEquals(36782, RegionManager.getObject(targetLoc)?.id ?: -1, "Object does not exist at expected location!") } @@ -79,8 +73,8 @@ class RegionTests { Region.load(dynamic, true) val targetLoc = dynamic.baseLocation.transform(23, 17, 0) Assertions.assertEquals(null, RegionManager.getObject(targetLoc), "Object exists pre-copy?") - val replacement = base.planes[0].getRegionChunk(2, 2) - dynamic.replaceChunk(0, 2, 2, replacement.copy(dynamic.planes[0]), base) + val replacement = base.getChunks()[2][2][0] + dynamic.replaceChunk(0, 2, 2, replacement.copy(), base) Assertions.assertEquals(36782, RegionManager.getObject(targetLoc)?.id ?: -1, "Object does not exist at expected location!") } @@ -89,11 +83,10 @@ class RegionTests { val borders = DynamicRegion.reserveArea(8,8) val dynamic = DynamicRegion(-1, borders.southWestX shr 6, borders.southWestY shr 6) dynamic.borders = borders - dynamic.isUpdateAllPlanes = true RegionManager.addRegion(dynamic.id, dynamic) val targetLoc = dynamic.baseLocation.transform(23, 17, 0) - val replacement = base.planes[0].getRegionChunk(2,2) - dynamic.replaceChunk(0, 2, 2, replacement.copy(dynamic.planes[0]), base) + val replacement = base.getChunks()[2][2][0] + dynamic.replaceChunk(0, 2, 2, replacement.copy(), base) Assertions.assertEquals(36782, RegionManager.getObject(targetLoc)?.id ?: -1, "Object does not exist at expected location!") } @@ -102,11 +95,10 @@ class RegionTests { val borders = DynamicRegion.reserveArea(8,8) val dynamic = DynamicRegion(-1, borders.southWestX shr 6, borders.southWestY shr 6) dynamic.borders = borders - dynamic.isUpdateAllPlanes = true RegionManager.addRegion(dynamic.id, dynamic) val targetLoc = dynamic.baseLocation.transform(23, 17, 0) - val replacement = base.planes[0].getRegionChunk(2,2) - dynamic.replaceChunk(0, 2, 2, replacement.copy(dynamic.planes[0]), base) + val replacement = base.getChunks()[2][2][0] + dynamic.replaceChunk(0, 2, 2, replacement.copy(), base) base.link(dynamic) Assertions.assertEquals(36782, RegionManager.getObject(targetLoc)?.id ?: -1, "Object does not exist at expected location!") } diff --git a/Server/src/test/kotlin/TestUtils.kt b/Server/src/test/kotlin/TestUtils.kt index b8d8d45cd..5f672e627 100644 --- a/Server/src/test/kotlin/TestUtils.kt +++ b/Server/src/test/kotlin/TestUtils.kt @@ -146,10 +146,13 @@ class MockPlayer(name: String, val isBot: Boolean) : Player(PlayerDetails(name)) finishClear() } - override fun setLocation(location: Location?) { + override fun setLocation(location: Location) { super.setLocation(location) - RegionManager.move(this) + location.chunk.addPlayer(this) + location.region.incrementViewAmount() + location.region.flagActive() this.playerFlags.lastSceneGraph = location + zoneMonitor.updateLocation(walkingQueue.footPrint) } fun relog(ticksToWait: Int = -1) { diff --git a/Server/src/test/kotlin/core/PathfinderTests.kt b/Server/src/test/kotlin/core/PathfinderTests.kt index 84f6c640c..c27cdd2f4 100644 --- a/Server/src/test/kotlin/core/PathfinderTests.kt +++ b/Server/src/test/kotlin/core/PathfinderTests.kt @@ -16,12 +16,8 @@ import core.game.node.entity.impl.PulseType import core.game.node.entity.npc.NPC import core.game.node.entity.player.Player import core.game.world.GameWorld -import core.game.world.map.Region -import core.net.packet.PacketProcessor -import core.plugin.ClassScanner import core.plugin.Plugin import core.tools.Log -import org.rs09.consts.NPCs class PathfinderTests { companion object {init {TestUtils.preTestSetup(); GatheringSkillOptionListeners().defineListeners(); WoodcuttingListener().defineListeners() }; val NPC_TEST_LOC = ServerConstants.HOME_LOCATION!!.transform(2, 10, 0)} @@ -49,7 +45,7 @@ class PathfinderTests { @Test fun movementPulseShouldStopEarlyIfNextToATileOccupiedByTargetObject() { val start = Location.create(2731, 3481) - val dest = RegionManager.getObject(0, 2720, 3475, 1307) + val dest = RegionManager.getObject(2720, 3475, 0, 1307, -1) val p = TestUtils.getMockPlayer("treefindtest") p.location = start p.init() @@ -257,4 +253,4 @@ class PathfinderTests { Assertions.assertEquals(true, npc.location.getDistance(ServerConstants.HOME_LOCATION!!) <= 5) } } -} \ No newline at end of file +} diff --git a/Server/worldprops/default.conf b/Server/worldprops/default.conf index 308db7417..645811200 100644 --- a/Server/worldprops/default.conf +++ b/Server/worldprops/default.conf @@ -22,7 +22,7 @@ noauth_default_admin = true #NOTE: If we are not using auth, this determines whe #------------------------------------------------------------------------------------------------------ #The limit on how many different accounts a player can log into per day. daily_accounts_per_ip = 3 -watchdog_enabled = true +watchdog_enabled = false connectivity_check_url = "https://google.com,https://2009scape.org" connectivity_timeout = 500 # Enables a websocket listener for browser clients. The websocket stream carries the