From 69aa7a75578227df083853748b9265755d297706 Mon Sep 17 00:00:00 2001 From: Ceikry Date: Sat, 20 Aug 2022 08:10:26 +0000 Subject: [PATCH] Rewrote Ava's device handling Rewrote the handling for both the accumulator and the attractor in new systems Fixed a bug that would cause the devices to attract unwanted items at inappropriate times Fixed a bug that prevented equip listeners from firing on login --- .../animalmagnetism/AnimalMagnetism.java | 1 - .../animalmagnetism/AvasDevicePlugin.java | 72 ----------- .../player/info/login/LoginConfiguration.java | 9 ++ .../node/entity/player/link/GlobalData.java | 24 +--- Server/src/main/kotlin/api/ContentAPI.kt | 11 ++ .../game/node/entity/equipment/AvasDevice.kt | 119 ++++++++++++++++++ .../entity/player/info/login/PlayerSaver.kt | 1 - .../state/newsys/states/AvaDeviceState.kt | 97 -------------- 8 files changed, 140 insertions(+), 194 deletions(-) delete mode 100644 Server/src/main/java/core/game/content/quest/members/animalmagnetism/AvasDevicePlugin.java create mode 100644 Server/src/main/kotlin/rs09/game/node/entity/equipment/AvasDevice.kt delete mode 100644 Server/src/main/kotlin/rs09/game/node/entity/state/newsys/states/AvaDeviceState.kt diff --git a/Server/src/main/java/core/game/content/quest/members/animalmagnetism/AnimalMagnetism.java b/Server/src/main/java/core/game/content/quest/members/animalmagnetism/AnimalMagnetism.java index 5dab5a895..2f3459723 100644 --- a/Server/src/main/java/core/game/content/quest/members/animalmagnetism/AnimalMagnetism.java +++ b/Server/src/main/java/core/game/content/quest/members/animalmagnetism/AnimalMagnetism.java @@ -113,7 +113,6 @@ public final class AnimalMagnetism extends Quest { ClassScanner.definePlugin(new WitchDialogue()); ClassScanner.definePlugin(new ContainerHandler()); ClassScanner.definePlugin(new UndeadTreePlugin()); - ClassScanner.definePlugin(new AvasDevicePlugin()); ClassScanner.definePlugin(new HammerMagnetPlugin()); ClassScanner.definePlugin(new ResearchNoteHandler()); ClassScanner.definePlugin(new AliceHusbandDialogue()); diff --git a/Server/src/main/java/core/game/content/quest/members/animalmagnetism/AvasDevicePlugin.java b/Server/src/main/java/core/game/content/quest/members/animalmagnetism/AvasDevicePlugin.java deleted file mode 100644 index 9de59e88d..000000000 --- a/Server/src/main/java/core/game/content/quest/members/animalmagnetism/AvasDevicePlugin.java +++ /dev/null @@ -1,72 +0,0 @@ -package core.game.content.quest.members.animalmagnetism; - -import core.cache.def.impl.ItemDefinition; -import core.game.interaction.OptionHandler; -import core.game.node.Node; -import core.game.node.entity.player.Player; -import core.game.node.item.Item; -import core.plugin.Plugin; -import rs09.game.node.entity.state.newsys.states.AvaDeviceState; -import rs09.plugin.ClassScanner; - -/** - * Handles the equippage event of an ava device. - * @author Vexia - */ -public final class AvasDevicePlugin implements Plugin { - - @Override - public Plugin newInstance(Object arg) throws Throwable { - AnimalMagnetism.AVAS_ACCUMULATOR.getDefinition().getHandlers().put("equipment", this); - AnimalMagnetism.AVAS_ATTRACTOR.getDefinition().getHandlers().put("equipment", this); - ClassScanner.definePlugin(new DisableDevicePlugin()); - return this; - } - - @Override - public Object fireEvent(String identifier, Object... args) { - final Player player = (Player) args[0]; - final Item item = (Item) args[1]; - switch (identifier) { - case "equip": - AvaDeviceState state = (AvaDeviceState) player.registerState("avadevice"); - state.setDevice(item.getId()); - state.init(); - break; - case "unequip": - player.clearState("avadevice"); - if (args.length == 3) { - Item second = (Item) args[2]; - if (second.getId() == 10498 || second.getId() == 10499) { - AvaDeviceState newState = (AvaDeviceState) player.registerState("avadevice"); - newState.setDevice(second.getId()); - newState.init(); - break; - } - } - } - return true; - } - - /** - * Handles the disabling of Ava's devices -- they'll no longer randomly collect loot. - * @author Splinter - */ - public final class DisableDevicePlugin extends OptionHandler { - - @Override - public Plugin newInstance(Object arg) throws Throwable { - ItemDefinition.forId(10499).getHandlers().put("option:operate", this); - ItemDefinition.forId(10498).getHandlers().put("option:operate", this); - return this; - } - - @Override - public boolean handle(Player player, Node node, String option) { - player.getGlobalData().setAvasDisabled(!player.getGlobalData().isAvasDisabled()); - player.sendMessage("Ava's device will "+(player.getGlobalData().isAvasDisabled() ? "no longer" : "now")+" randomly collect loot for you."); - return true; - } - } - -} diff --git a/Server/src/main/java/core/game/node/entity/player/info/login/LoginConfiguration.java b/Server/src/main/java/core/game/node/entity/player/info/login/LoginConfiguration.java index 6db210996..c142b40c4 100644 --- a/Server/src/main/java/core/game/node/entity/player/info/login/LoginConfiguration.java +++ b/Server/src/main/java/core/game/node/entity/player/info/login/LoginConfiguration.java @@ -4,6 +4,7 @@ import core.game.component.CloseEvent; import core.game.component.Component; import core.game.node.entity.player.Player; import core.game.node.entity.player.link.emote.Emotes; +import core.game.node.item.Item; import core.game.world.map.RegionManager; import core.game.world.update.flag.player.AppearanceFlag; import core.net.amsc.MSPacketRepository; @@ -13,6 +14,7 @@ import core.net.packet.context.InterfaceContext; import core.net.packet.out.Interface; import core.plugin.Plugin; import rs09.ServerConstants; +import rs09.game.interaction.InteractionListeners; import rs09.game.interaction.inter.RulesAndInfo; import rs09.game.system.SystemLogger; import rs09.game.world.GameWorld; @@ -140,6 +142,13 @@ public final class LoginConfiguration { player.getEmoteManager().unlock(Emotes.SAFETY_FIRST); } player.varpManager.sendAllVarps(); + for (Item item : player.getEquipment().toArray()) { + //Run equip hooks for all items equipped on login. + //We should have already been doing this. + //Frankly, I don't even want to imagine the number of bugs us *not* doing this has caused. + if (item == null) continue; + InteractionListeners.run(item.getId(), player, item, true); + } if(ServerConstants.RULES_AND_INFO_ENABLED) RulesAndInfo.openFor(player); /*if (GameWorld.getSettings().isPvp()) { diff --git a/Server/src/main/java/core/game/node/entity/player/link/GlobalData.java b/Server/src/main/java/core/game/node/entity/player/link/GlobalData.java index c0f59a0ea..6a682f9bb 100644 --- a/Server/src/main/java/core/game/node/entity/player/link/GlobalData.java +++ b/Server/src/main/java/core/game/node/entity/player/link/GlobalData.java @@ -231,12 +231,7 @@ public final class GlobalData { * The double exp time. */ private long doubleExp; - - /** - * If Ava's device should randomly collect loot. - */ - private boolean avasDevice; - + /** * The delay of the global teleporter system. */ @@ -373,7 +368,6 @@ public final class GlobalData { lootShareDelay = Long.parseLong(data.get("lootShareDelay").toString()); lootSharePoints = Integer.parseInt( data.get("lootSharePoints").toString()); doubleExp = Long.parseLong(data.get("doubleExp").toString()); - avasDevice = (boolean) data.get("avasDevice"); globalTeleporterDelay = Long.parseLong(data.get("globalTeleporterDelay").toString()); starSpriteDelay = Long.parseLong(data.get("starSpriteDelay").toString()); runReplenishDelay = Long.parseLong(data.get("runReplenishDelay").toString()); @@ -1143,22 +1137,6 @@ public final class GlobalData { public boolean hasDoubleExp() { return doubleExp > System.currentTimeMillis(); } - - /** - * Gets the avasDevice - * @return the avasDevice - */ - public boolean isAvasDisabled() { - return avasDevice; - } - - /** - * Sets the avasDevice - * @param avasDevice - */ - public void setAvasDisabled(boolean avasDevice) { - this.avasDevice = avasDevice; - } /** * Gets the globalTeleporterDelay. diff --git a/Server/src/main/kotlin/api/ContentAPI.kt b/Server/src/main/kotlin/api/ContentAPI.kt index 3d8b26bc8..f5a31835a 100644 --- a/Server/src/main/kotlin/api/ContentAPI.kt +++ b/Server/src/main/kotlin/api/ContentAPI.kt @@ -2084,4 +2084,15 @@ fun clearHintIcon(player: Player) { val slot = getAttribute(player, "hinticon", -1) player.removeAttribute("hinticon") HintIconManager.removeHintIcon(player, slot) +} + +/** + * Gets the equipment slot the item belongs to + * @param item the Id of the item to check + * @return the EquipmentSlot, or null if the item cannot be equipped, or has no slot defined. + */ +fun equipSlot(item: Int) : EquipmentSlot? { + return EquipmentSlot + .values() + .getOrNull(itemDefinition(item).getConfiguration(ItemConfigParser.EQUIP_SLOT, -1)) } \ No newline at end of file diff --git a/Server/src/main/kotlin/rs09/game/node/entity/equipment/AvasDevice.kt b/Server/src/main/kotlin/rs09/game/node/entity/equipment/AvasDevice.kt new file mode 100644 index 000000000..d9ad9db84 --- /dev/null +++ b/Server/src/main/kotlin/rs09/game/node/entity/equipment/AvasDevice.kt @@ -0,0 +1,119 @@ +package rs09.game.node.entity.equipment + +import api.* +import api.events.EventHook +import api.events.TickEvent +import core.game.node.entity.Entity +import core.game.node.entity.combat.equipment.Ammunition +import core.game.node.entity.player.Player +import org.rs09.consts.Items +import rs09.game.Event +import rs09.game.interaction.InteractionListener +import rs09.game.system.config.ItemConfigParser +import rs09.tools.secondsToTicks +import rs09.tools.stringtools.colorize + +/** + * Handles Ava's device + * @source https://runescape.wiki/w/Ava%27s_accumulator?oldid=2097350 + */ +class AvasDevice : InteractionListener, EventHook { + override fun defineListeners() { + onEquip(devices) { player, _ -> + if (!isQuestComplete(player, "Animal Magnetism")) { + sendMessage(player, "You need to complete Animal Magnetism to equip this.") + return@onEquip false + } + + if (attractEnabled(player)) + player.hook(Event.Tick, this) + + setAttribute(player, LAST_TICK, getWorldTicks()) //set this on equip so can't be spam-re-equipped to spawn infinite items. + return@onEquip true + } + onUnequip(devices) { player, _ -> + if (attractEnabled(player)) + player.unhook(this) + return@onUnequip true + } + on(devices, ITEM, "operate") { player, _ -> + val attract = !attractEnabled(player) + setAttribute(player, ATTRACT_ENABLED, attract) + sendMessage( + player, + colorize( + "Ava's device will ${if (attract) "now" else "no longer"} randomly collect loot for you.", + "990000" + ) + ) + if (attract) { + player.hook(Event.Tick, this) + } else { + player.unhook(this) + } + return@on true + } + } + + override fun process(entity: Entity, event: TickEvent) { + if (entity !is Player) { + entity.unhook(this) + return + } + + if (getWorldTicks() - getLastTick(entity) < attractDelay) + return + else + setAttribute(entity, LAST_TICK, getWorldTicks()) + + if (isInterfered(entity)) { + sendMessage(entity, "Your armour interferes with Ava's device.") + return + } + + val wornId = getItemFromEquipment(entity, EquipmentSlot.CAPE)?.id ?: -1 + + val reward = when (wornId) { + Items.AVAS_ACCUMULATOR_10499 -> ACCUMULATOR_REWARDS + Items.AVAS_ATTRACTOR_10498 -> ATTRACTOR_REWARDS + else -> { + entity.unhook(this) + return + } + }.random() + + if (equipSlot(reward) == EquipmentSlot.AMMO) { + val equippedId = getItemFromEquipment(entity, EquipmentSlot.AMMO)?.id ?: -1 + if (reward == equippedId || equippedId == -1) { + entity.equipment.add(reward.asItem(), true, false) + return + } + } + + addItemOrDrop(entity, reward) + } + + private fun attractEnabled(entity: Entity) : Boolean { + return getAttribute(entity, ATTRACT_ENABLED, true) //defaults to enabled + } + + private fun getLastTick(entity: Entity) : Int { + return getAttribute(entity, LAST_TICK, 0) + } + + private fun isInterfered(player: Player) : Boolean { + val chestPiece = getItemFromEquipment(player, EquipmentSlot.CHEST) + val modelId = chestPiece?.definition?.maleWornModelId1 ?: -1 + return modelId != -1 && modelId in metalBodies + } + + companion object { + const val ATTRACT_ENABLED = "/save:avadevice:attract" + const val LAST_TICK = "avadevice:tick" + val devices = intArrayOf(Items.AVAS_ACCUMULATOR_10499, Items.AVAS_ATTRACTOR_10498) + val metalBodies = intArrayOf(301, 306, 3379) + val attractDelay = secondsToTicks(180) + val ATTRACTOR_REWARDS = arrayOf(Items.IRON_BAR_2351, Items.IRON_KNIFE_863,Items.IRON_DART_807,Items.IRON_DAGGER_1203,Items.IRON_BOLTS_9140,Items.IRON_ARROW_884,Items.IRON_ORE_440,Items.COPPER_ORE_436,Items.IRON_FULL_HELM_1153,Items.IRON_2H_SWORD_1309,Items.STEEL_BAR_2353) + val ACCUMULATOR_REWARDS = arrayOf(Items.STEEL_BAR_2353,Items.STEEL_2H_SWORD_1311,Items.STEEL_KNIFE_865,Items.STEEL_DAGGER_1207,Items.STEEL_MED_HELM_1141,Items.STEEL_DART_808,Items.STEEL_BOLTS_9141,Items.STEEL_ARROW_886,Items.IRON_BAR_2351) + } +} \ No newline at end of file diff --git a/Server/src/main/kotlin/rs09/game/node/entity/player/info/login/PlayerSaver.kt b/Server/src/main/kotlin/rs09/game/node/entity/player/info/login/PlayerSaver.kt index 44b49a248..ca70c7d84 100644 --- a/Server/src/main/kotlin/rs09/game/node/entity/player/info/login/PlayerSaver.kt +++ b/Server/src/main/kotlin/rs09/game/node/entity/player/info/login/PlayerSaver.kt @@ -426,7 +426,6 @@ class PlayerSaver (val player: Player){ globalData.put("lootSharePoints",player.savedData.globalData.lootSharePoints.toString()) globalData.put("lootShareDelay",player.savedData.globalData.lootShareDelay.toString()) globalData.put("doubleExp",player.savedData.globalData.doubleExp.toString()) - globalData.put("avasDevice",player.savedData.globalData.isAvasDisabled) globalData.put("globalTeleporterDelay",player.savedData.globalData.globalTeleporterDelay.toString()) globalData.put("starSpriteDelay",player.savedData.globalData.starSpriteDelay.toString()) globalData.put("runReplenishDelay",player.savedData.globalData.runReplenishDelay.toString()) diff --git a/Server/src/main/kotlin/rs09/game/node/entity/state/newsys/states/AvaDeviceState.kt b/Server/src/main/kotlin/rs09/game/node/entity/state/newsys/states/AvaDeviceState.kt deleted file mode 100644 index c752691f8..000000000 --- a/Server/src/main/kotlin/rs09/game/node/entity/state/newsys/states/AvaDeviceState.kt +++ /dev/null @@ -1,97 +0,0 @@ -package rs09.game.node.entity.state.newsys.states - -import core.game.container.impl.EquipmentContainer -import core.game.node.entity.player.Player -import core.game.node.item.Item -import core.game.system.task.Pulse -import core.tools.RandomFunction -import org.json.simple.JSONObject -import org.rs09.consts.Items -import rs09.game.node.entity.state.newsys.PlayerState -import rs09.game.node.entity.state.newsys.State -import rs09.game.system.config.ItemConfigParser -import kotlin.math.ceil - -@PlayerState("avadevice") -class AvaDeviceState(player: Player? = null) : State(player) { - val TICKS = ceil(180 / 0.6).toInt() - var device = 0 - - private val ATTRACTOR_REWARDS = arrayOf(Items.IRON_BAR_2351, - Items.IRON_KNIFE_863,Items.IRON_DART_807,Items.IRON_DAGGER_1203,Items.IRON_BOLTS_9140,Items.IRON_ARROW_884,Items.IRON_ORE_440,Items.COPPER_ORE_436,Items.IRON_FULL_HELM_1153,Items.IRON_2H_SWORD_1309,Items.STEEL_BAR_2353) - private val ACCUMULATOR_REWARDS = arrayOf(Items.STEEL_BAR_2353,Items.STEEL_2H_SWORD_1311,Items.STEEL_KNIFE_865,Items.STEEL_DAGGER_1207,Items.STEEL_MED_HELM_1141,Items.STEEL_DART_808,Items.STEEL_BOLTS_9141,Items.STEEL_ARROW_886,Items.IRON_BAR_2351) - - override fun save(root: JSONObject) { - root.put("device-id",device) - } - - override fun parse(_data: JSONObject) { - if(_data.containsKey("device-id")){ - device = _data["device-id"].toString().toInt() - } - } - - override fun newInstance(player: Player?): State { - return AvaDeviceState(player) - } - - override fun createPulse() { - player ?: return - if(device == 0 && !hasDevice(player)) return - if(player.savedData.globalData.isAvasDisabled) return - - pulse = object : Pulse(TICKS){ - override fun pulse(): Boolean { - if(!hasDevice(player)){ - player.clearState("avadevice") - pulse = null - return true - } - - if(isInterfered()) return false - - if(RandomFunction.random(10) > 4) return false - - val rewards = if(isAccumulator()) ACCUMULATOR_REWARDS else ATTRACTOR_REWARDS - - val item = Item(rewards.random()) - - if (item.definition.getConfiguration(ItemConfigParser.EQUIP_SLOT, -1) == EquipmentContainer.SLOT_ARROWS) { - val arrowSlot = player.equipment[EquipmentContainer.SLOT_ARROWS] - if (arrowSlot == null || arrowSlot.id == item.getId()) { - player.equipment.add(item, true, EquipmentContainer.SLOT_ARROWS) - return false - } - } - player.inventory.add(item, player) - - return !hasDevice(player) - } - } - } - - fun hasDevice(player: Player?) : Boolean{ - player ?: return false - return player.equipment.containsAtLeastOneItem(intArrayOf(Items.AVAS_ACCUMULATOR_10499,Items.AVAS_ATTRACTOR_10498)) - } - - fun isAccumulator() : Boolean{ - return device == Items.AVAS_ACCUMULATOR_10499 - } - - //just took this method from the old arios pulse as well (I'm being lazy) - private fun isInterfered(): Boolean { - player ?: return true - val cape = player.equipment[EquipmentContainer.SLOT_CAPE] - if (cape != null && (cape.id == 10498 || cape.id == 10499)) { - val torso = player.equipment[EquipmentContainer.SLOT_CHEST] - val modelId = torso?.definition?.maleWornModelId1 ?: -1 - if (modelId == 301 || modelId == 306 || modelId == 3379) { - player.packetDispatch.sendMessage("Your armour interferes with Ava's device.") - return true - } - } - return false - } - -}