From 5cfe44d5cd2637a47a13885fc50a8d3e41eba452 Mon Sep 17 00:00:00 2001 From: Ceikry Date: Thu, 23 Jun 2022 14:25:23 +0000 Subject: [PATCH] Implemented pelting of rotten tomatoes Added ability to define listeners for Player interactions and usewith interactions Added ability to flag listeners instant (no path walking) execution --- .../holiday/christmas/ChristmasEvent.java | 36 +----- .../core/net/packet/in/InteractionPacket.java | 7 +- .../core/net/packet/in/ItemActionPacket.java | 3 + .../game/interaction/InteractionListener.kt | 12 ++ .../game/interaction/InteractionListeners.kt | 21 +++- .../interaction/player/PeltOptionPlugin.kt | 83 ------------- .../node/entity/equipment/PlayerPeltables.kt | 117 ++++++++++++++++++ .../equipment/SnowballEquipmentPlugin.kt | 31 ----- 8 files changed, 155 insertions(+), 155 deletions(-) delete mode 100644 Server/src/main/kotlin/rs09/game/interaction/player/PeltOptionPlugin.kt create mode 100644 Server/src/main/kotlin/rs09/game/node/entity/equipment/PlayerPeltables.kt delete mode 100644 Server/src/main/kotlin/rs09/game/node/entity/equipment/SnowballEquipmentPlugin.kt diff --git a/Server/src/main/java/core/game/content/holiday/christmas/ChristmasEvent.java b/Server/src/main/java/core/game/content/holiday/christmas/ChristmasEvent.java index 341093288..318f7547b 100644 --- a/Server/src/main/java/core/game/content/holiday/christmas/ChristmasEvent.java +++ b/Server/src/main/java/core/game/content/holiday/christmas/ChristmasEvent.java @@ -101,7 +101,7 @@ public class ChristmasEvent extends HolidayEvent { @Override public Plugin newInstance(Object arg) throws Throwable { if (isActive()) { - ClassScanner.definePlugins(new ChristmasEventOptionPlugin(), new SnowmanItemHandler(), new SnowImpDialogue(), new SnowmanHatComponentPlugin(), new QueenOfSnowDialogue(), new SnowballItemPlugin(), new SnowmanNPC(), new PeltOptionHandler()); + ClassScanner.definePlugins(new ChristmasEventOptionPlugin(), new SnowmanItemHandler(), new SnowImpDialogue(), new SnowmanHatComponentPlugin(), new QueenOfSnowDialogue(), new SnowmanNPC(), new PeltOptionHandler()); } return super.newInstance(arg); } @@ -1079,38 +1079,4 @@ public class ChristmasEvent extends HolidayEvent { } } - - /** - * Handles the snow ball item plugin. - * @author Vexia - * - */ - public class SnowballItemPlugin implements Plugin { - - @Override - public Plugin newInstance(Object arg) throws Throwable { - BALL_OF_SNOW.getDefinition().getHandlers().put("equipment", this); - return this; - } - - @Override - public Object fireEvent(String identifier, Object... args) { - final Player player = (Player) args[0]; - @SuppressWarnings("unused") - Item item = (Item) args[1]; - final Item other = args.length == 2 ? null : (Item) args[2]; - if (other != null) { - identifier = "equip"; - } - switch (identifier) { - case "equip": - player.getInteraction().set(PELT_OPTION); - break; - case "unequip": - player.getInteraction().remove(PELT_OPTION); - break; - } - return true; - } - } } diff --git a/Server/src/main/java/core/net/packet/in/InteractionPacket.java b/Server/src/main/java/core/net/packet/in/InteractionPacket.java index dfc79f208..d53139937 100644 --- a/Server/src/main/java/core/net/packet/in/InteractionPacket.java +++ b/Server/src/main/java/core/net/packet/in/InteractionPacket.java @@ -322,8 +322,11 @@ public final class InteractionPacket implements IncomingPacket { PacketRepository.send(ClearMinimapFlag.class, new PlayerContext(player)); return; } - handleAIPLegion(player, 2, optionIndex, index); - target.getInteraction().handle(player, option); + + if (!InteractionListeners.run(-1, InteractionListener.Companion.getPLAYER(), option.getName().toLowerCase(), player, target)) { + handleAIPLegion(player, 2, optionIndex, index); + target.getInteraction().handle(player, option); + } } /** diff --git a/Server/src/main/java/core/net/packet/in/ItemActionPacket.java b/Server/src/main/java/core/net/packet/in/ItemActionPacket.java index 930309318..b152b8e75 100644 --- a/Server/src/main/java/core/net/packet/in/ItemActionPacket.java +++ b/Server/src/main/java/core/net/packet/in/ItemActionPacket.java @@ -100,6 +100,9 @@ public class ItemActionPacket implements IncomingPacket { if(PluginInteractionManager.handle(player,event)){ return; } + if(InteractionListeners.run(item, target, 4, player)){ + return; + } if(player.getZoneMonitor().useWith(used,player)){ return; } diff --git a/Server/src/main/kotlin/rs09/game/interaction/InteractionListener.kt b/Server/src/main/kotlin/rs09/game/interaction/InteractionListener.kt index ec7cfa173..345979d1c 100644 --- a/Server/src/main/kotlin/rs09/game/interaction/InteractionListener.kt +++ b/Server/src/main/kotlin/rs09/game/interaction/InteractionListener.kt @@ -5,6 +5,7 @@ import core.game.node.Node import core.game.node.entity.Entity import core.game.node.entity.player.Player import core.game.world.map.Location +import rs09.game.system.SystemLogger interface InteractionListener : ContentInterface{ val ITEM: Int @@ -15,6 +16,8 @@ interface InteractionListener : ContentInterface{ get() = 2 val GROUNDITEM: Int get() = 3 + val PLAYER: Int + get() = 4 fun on(id: Int, type: Int, vararg option: String, handler: (player: Player, node: Node) -> Boolean){ InteractionListeners.add(id,type,option,handler) @@ -37,6 +40,9 @@ interface InteractionListener : ContentInterface{ fun onUseAnyWith(type: Int, vararg with: Int, handler: (player: Player, used: Node, with: Node) -> Boolean) { InteractionListeners.add(type, with, handler) } + fun onUseWithPlayer(vararg used: Int, handler: (player: Player, used: Node, with: Node) -> Boolean) { + InteractionListeners.add(PLAYER, used, handler) + } // Note: wildcard listeners incur overhead on every use-with interaction, only use them as a space-time tradeoff when something // is actually supposed to have a response to every item used with it (e.g. imp boxes, certain quest npcs) fun onUseWithWildcard(type: Int, predicate: (used: Int, with: Int) -> Boolean, handler: (player: Player, used: Node, with: Node) -> Boolean) { @@ -73,6 +79,11 @@ interface InteractionListener : ContentInterface{ SpadeDigListener.registerListener(location,method) } + fun flagInstant() { + val name = this::class.java.name + InteractionListeners.instantClasses.add(name) + } + fun defineListeners() companion object @@ -81,5 +92,6 @@ interface InteractionListener : ContentInterface{ val SCENERY = 1 val NPC = 2 val GROUNDITEM = 3 + val PLAYER = 4 } } diff --git a/Server/src/main/kotlin/rs09/game/interaction/InteractionListeners.kt b/Server/src/main/kotlin/rs09/game/interaction/InteractionListeners.kt index 760029d67..6b9f14fcd 100644 --- a/Server/src/main/kotlin/rs09/game/interaction/InteractionListeners.kt +++ b/Server/src/main/kotlin/rs09/game/interaction/InteractionListeners.kt @@ -18,6 +18,7 @@ object InteractionListeners { private val useWithWildcardListeners = HashMap Boolean, (Player, Node, Node) -> Boolean>>>(10) private val destinationOverrides = HashMap Location>(100) private val equipListeners = HashMap Boolean>(10) + val instantClasses = HashSet() @JvmStatic fun add(id: Int, type: Int, option: Array, method: (Player,Node) -> Boolean){ @@ -161,7 +162,7 @@ object InteractionListeners { @JvmStatic fun run(used: Node, with: Node, type: Int,player: Player): Boolean{ val flag = when(type){ - 2 -> DestinationFlag.ENTITY + 2, 4 -> DestinationFlag.ENTITY 1 -> DestinationFlag.OBJECT else -> DestinationFlag.OBJECT } @@ -170,7 +171,8 @@ object InteractionListeners { var flipped = false - val method = get(used.id,with.id,type) ?: get(with.id,used.id,type).also { flipped = true } ?: return false + val method = if (with is Player) get(-1, used.id, 4) ?: return false + else get(used.id,with.id,type) ?: get(with.id,used.id,type).also { flipped = true } ?: return false val destOverride = if(flipped) { getOverride(type, used.id, "use") ?: getOverride(type, with.id) ?: getOverride(type, "use") @@ -179,7 +181,7 @@ object InteractionListeners { } - if(type != 0) { + if(type != 0 && !isUseWithInstant(method)) { if(player.locks.isMovementLocked) return false player.pulseManager.run(object : MovementPulse(player, with, flag, destOverride) { override fun pulse(): Boolean { @@ -206,6 +208,7 @@ object InteractionListeners { @JvmStatic fun run(id: Int, type: Int, option: String, player: Player, node: Node): Boolean{ val flag = when(type){ + 4 -> DestinationFlag.ENTITY 3 -> DestinationFlag.ITEM 2 -> DestinationFlag.ENTITY 1 -> null @@ -226,7 +229,7 @@ object InteractionListeners { return true } - if(type != 0) { + if(type != 0 && !isInstant(method)) { if(player.locks.isMovementLocked) return false player.pulseManager.run(object : MovementPulse(player, node, flag, destOverride) { override fun pulse(): Boolean { @@ -257,4 +260,14 @@ object InteractionListeners { useAnyWithListeners["$w:$type"] = handler } } + + fun isInstant(handler: (Player, Node) -> Boolean): Boolean { + val className = handler.javaClass.name.substringBefore("$") + return instantClasses.contains(className) + } + + fun isUseWithInstant(handler: (player: Player, used: Node, with: Node) -> Boolean): Boolean { + val className = handler.javaClass.name.substringBefore("$") + return instantClasses.contains(className) + } } diff --git a/Server/src/main/kotlin/rs09/game/interaction/player/PeltOptionPlugin.kt b/Server/src/main/kotlin/rs09/game/interaction/player/PeltOptionPlugin.kt deleted file mode 100644 index c9c5cff39..000000000 --- a/Server/src/main/kotlin/rs09/game/interaction/player/PeltOptionPlugin.kt +++ /dev/null @@ -1,83 +0,0 @@ -package rs09.game.interaction.player - -import core.game.interaction.Interaction -import core.game.interaction.OptionHandler -import core.game.node.Node -import core.game.node.entity.impl.Projectile -import core.game.node.entity.player.Player -import core.game.node.item.Item -import core.game.system.task.Pulse -import core.game.world.map.Location -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.plugin.Initializable -import core.plugin.Plugin -import org.rs09.consts.Items -import rs09.game.world.GameWorld -import rs09.tools.stringtools.colorize - -val snowball = Item(Items.SNOWBALL_11951) -val THROW_ANIMATION = Animation(7530) -val THROW_GRAPHICS = Graphics(860) - -@Initializable -class PeltOptionPlugin : OptionHandler() { - override fun newInstance(arg: Any?): Plugin { - return this - } - - override fun handle(player: Player?, node: Node?, option: String?): Boolean { - player ?: return false - val other = node?.asPlayer() - val delay = 41 - val speed = 60 - val distance = Location.getDistance(player.location, other?.getLocation()).toInt() - val projectileSpeed = delay + speed + distance * 5 - val hitDelay = (projectileSpeed * .02857).toInt() - - if(!Pathfinder.find(player,other,false, Pathfinder.PROJECTILE).isSuccessful){ - player.dialogueInterpreter.sendDialogue("You can't reach them!") - return true - } - - if(player.inventory.remove(snowball) || player.equipment.remove(snowball)){ - player.lock() - GameWorld.Pulser.submit(object : Pulse(hitDelay + 1){ - override fun pulse(): Boolean { - other?.animator?.graphics(Graphics(1282)) - return true - } - }) - GameWorld.Pulser.submit(object : Pulse() { - var counter = 0 - override fun pulse(): Boolean { - when (counter++) { - 0 -> { - player.lock() - player.face(other) - player.animator.forceAnimation(THROW_ANIMATION) - } - 1 -> { - Projectile.create(player, other, 861, 30, 10).send() - player.face(player) - player.unlock() - } - 5 -> other?.sendMessage(colorize("%R${player.username} has pelted you with a snowball.")) - 6 -> return true - } - return false - } - }) - } else { - player.sendMessage("You have no more snowballs.") - Interaction.sendOption(player, 0, "null") - } - return true - } - - override fun isWalk(): Boolean { - return false - } - -} \ No newline at end of file diff --git a/Server/src/main/kotlin/rs09/game/node/entity/equipment/PlayerPeltables.kt b/Server/src/main/kotlin/rs09/game/node/entity/equipment/PlayerPeltables.kt new file mode 100644 index 000000000..799c6ed0e --- /dev/null +++ b/Server/src/main/kotlin/rs09/game/node/entity/equipment/PlayerPeltables.kt @@ -0,0 +1,117 @@ +package rs09.game.node.entity.equipment + +import api.* +import core.game.interaction.Interaction +import core.game.interaction.Option +import core.game.node.Node +import core.game.node.entity.impl.Projectile +import core.game.node.entity.player.Player +import core.game.node.item.Item +import core.game.system.task.Pulse +import core.game.world.map.path.Pathfinder +import core.game.world.update.flag.context.Graphics +import org.rs09.consts.Items +import rs09.game.interaction.InteractionListener + + +class PlayerPeltables : InteractionListener { + companion object { + private const val PROJECTILE_DELAY = 41 + private const val PROJECTILE_SPEED = 60 + private const val PROJECTILE_TIME_CONST = .02857 + private const val PROJECTILE_DISTANCE_MULT = 5 + private val PELTABLES = intArrayOf(Items.ROTTEN_TOMATO_2518, Items.SNOWBALL_11951) + } + + + override fun defineListeners() { + onEquip(PELTABLES, ::setPlayerOps) + onUnequip(PELTABLES, ::removePlayerOps) + + on("pelt", PLAYER, ::handlePeltInteraction) + onUseWithPlayer(*PELTABLES) {player, used, with -> handlePeltInteraction(player, with, used) } + flagInstant() + } + + private fun setPlayerOps(player: Player, _node: Node) : Boolean { + player.interaction.set(Option("Pelt", 0)) + return true + } + + private fun removePlayerOps(player: Player, _node: Node) : Boolean { + Interaction.sendOption(player, 0, "null") + return true + } + + private fun handlePeltInteraction(player: Player, node: Node, usedPeltable: Node? = null) : Boolean { + val peltable = usedPeltable?.asItem() ?: getPeltable(player) ?: return removePlayerOps(player, node) + val gfx = getPeltableGfx(peltable.id) + + val other = node.asPlayer() + + if (!Pathfinder.find(player, other, false, Pathfinder.PROJECTILE).isSuccessful) { + sendDialogue(player, "You can't reach them!") + return true + } + + val distance = player.location.getDistance(other.location) + val projectileSpeed = PROJECTILE_DELAY + PROJECTILE_SPEED + distance * PROJECTILE_DISTANCE_MULT + val hitDelay = (projectileSpeed * PROJECTILE_TIME_CONST).toInt() + + if (removeItem(player, peltable, Container.INVENTORY) || removeItem(player, peltable, Container.EQUIPMENT)) { + lock(player, hitDelay) + submitWorldPulse(PeltingPulse(player, other, gfx, hitDelay, peltable.id)) + } + + return true + } + + class PeltingPulse(val player: Player, val other: Player, val gfx: IntArray, val hitDelay: Int, val peltable: Int) : Pulse() { + private val throwAnimation = getPeltableAnim(peltable) + private var ticks = 0 + override fun pulse(): Boolean { + when (ticks++) { + 0 -> { + player.face(other) + visualize(player, throwAnimation, gfx[0]) + } + 1 -> { + Projectile.create(player, other, gfx[1], 30, 10).send() + face(player, player) //reset face flag lel + unlock(player) + } + hitDelay -> { + if (gfx[2] != -1) other.graphics(Graphics(gfx[2])) + sendMessage(other, "${player.username} has hit you with a ${getItemName(peltable).toLowerCase()}.") + return true + } + } + return false + } + + private fun getPeltableAnim(id: Int) : Int { + return when (id) { + Items.SNOWBALL_11951 -> 7530 + Items.ROTTEN_TOMATO_2518 -> 385 + else -> -1 + } + } + } + + private fun getPeltableGfx(id: Int): IntArray { + return when (id) { + Items.SNOWBALL_11951 -> intArrayOf(-1, 861, 1282) + Items.ROTTEN_TOMATO_2518 -> intArrayOf(-1, 29, 31) + else -> IntArray(3) {-1} + } + } + + private fun getPeltable(player: Player): Item? { + val equipped = getItemFromEquipment(player, EquipmentSlot.WEAPON) ?: return null + val id = equipped.id + + if (id !in PELTABLES) return null + + return equipped + } +} \ No newline at end of file diff --git a/Server/src/main/kotlin/rs09/game/node/entity/equipment/SnowballEquipmentPlugin.kt b/Server/src/main/kotlin/rs09/game/node/entity/equipment/SnowballEquipmentPlugin.kt deleted file mode 100644 index 05020d4a5..000000000 --- a/Server/src/main/kotlin/rs09/game/node/entity/equipment/SnowballEquipmentPlugin.kt +++ /dev/null @@ -1,31 +0,0 @@ -package rs09.game.node.entity.equipment - -import core.cache.def.impl.ItemDefinition -import core.game.interaction.Interaction -import core.game.interaction.Option -import rs09.game.interaction.player.PeltOptionPlugin -import core.game.node.entity.player.Player -import core.game.node.item.Item -import core.plugin.Initializable -import core.plugin.Plugin - -@Initializable -class SnowballEquipmentPlugin : Plugin { - override fun newInstance(arg: Item?): Plugin { - ItemDefinition.forId(11951).handlers["equipment"] = this - return this - } - - override fun fireEvent(identifier: String?, vararg args: Any?): Any { - val player = args[0] as Player - when(identifier){ - "equip" -> { - player.getInteraction().set(Option("Pelt", 0).setHandler(PeltOptionPlugin())) - } - "unequip" -> { - Interaction.sendOption(player, 0, "null") - } - } - return true - } -} \ No newline at end of file