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
This commit is contained in:
Ceikry
2022-08-20 08:10:26 +00:00
committed by Ryan
parent e7804fdd42
commit 69aa7a7557
8 changed files with 140 additions and 194 deletions
@@ -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());
@@ -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<Object> {
@Override
public Plugin<Object> 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<Object> 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("<col=990000>Ava's device will "+(player.getGlobalData().isAvasDisabled() ? "no longer" : "now")+" randomly collect loot for you.</col>");
return true;
}
}
}
@@ -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()) {
@@ -232,11 +232,6 @@ public final class GlobalData {
*/
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());
@@ -1144,22 +1138,6 @@ public final class GlobalData {
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.
* @return the globalTeleporterDelay.
+11
View File
@@ -2085,3 +2085,14 @@ fun clearHintIcon(player: Player) {
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))
}
@@ -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<TickEvent> {
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)
}
}
@@ -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())
@@ -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
}
}