diff --git a/Server/src/main/java/core/game/interaction/MovementPulse.java b/Server/src/main/java/core/game/interaction/MovementPulse.java index 67986c00a..591d1e874 100644 --- a/Server/src/main/java/core/game/interaction/MovementPulse.java +++ b/Server/src/main/java/core/game/interaction/MovementPulse.java @@ -13,6 +13,7 @@ import core.game.world.map.path.Pathfinder; import core.net.packet.PacketRepository; import core.net.packet.context.PlayerContext; import core.net.packet.out.ClearMinimapFlag; +import kotlin.jvm.functions.Function1; import java.util.Deque; @@ -73,6 +74,8 @@ public abstract class MovementPulse extends Pulse { */ private boolean near; + private Function1 overrideMethod; + /** * Constructs a new {@code MovementPulse} {@code Object}. * @@ -141,6 +144,12 @@ public abstract class MovementPulse extends Pulse { this.destinationFlag = destinationFlag; } + public MovementPulse(Entity mover, Node destination, DestinationFlag destinationFlag, Function1 method){ + this(mover,destination,null,false); + this.destinationFlag = destinationFlag; + this.overrideMethod = method; + } + /** * Constructs a new {@code MovementPulse} {@code Object}. * @@ -221,9 +230,13 @@ public abstract class MovementPulse extends Pulse { return; } Location loc = null; - if (destinationFlag != null) { + if (destinationFlag != null && overrideMethod == null) { loc = destinationFlag.getDestination(mover, destination); } + if(overrideMethod != null){ + loc = overrideMethod.invoke(destination); + if(loc == destination.getLocation()) loc = destinationFlag.getDestination(mover,destination); + } if (loc == null && optionHandler != null) { loc = optionHandler.getDestination(mover, destination); } diff --git a/Server/src/main/java/core/game/interaction/npc/NPCTalkPlugin.java b/Server/src/main/java/core/game/interaction/npc/NPCTalkPlugin.java deleted file mode 100644 index fd98de6d1..000000000 --- a/Server/src/main/java/core/game/interaction/npc/NPCTalkPlugin.java +++ /dev/null @@ -1,78 +0,0 @@ -package core.game.interaction.npc; - -import core.cache.def.impl.NPCDefinition; -import core.game.content.dialogue.FacialExpression; -import core.game.interaction.OptionHandler; -import core.game.node.Node; -import core.game.node.entity.npc.NPC; -import core.game.node.entity.player.Player; -import core.game.node.item.Item; -import core.game.world.map.Location; -import core.plugin.Initializable; -import core.plugin.Plugin; -import rs09.game.content.activity.gnomecooking.GnomeCookingJob; -import rs09.game.content.activity.gnomecooking.GnomeTipper; - -import static rs09.game.content.activity.gnomecooking.GnomeCookingConstantsKt.*; -import static rs09.tools.stringtools.StringToolsKt.colorize; - -/** - * Handles the NPC talk-to option. - * @author Emperor - */ -@Initializable -public final class NPCTalkPlugin extends OptionHandler { - - @Override - public Location getDestination(Node n, Node node) { - NPC npc = (NPC) node; - if (npc.getAttribute("facing_booth", false)) { - int offsetX = npc.getDirection().getStepX() << 1; - int offsetY = npc.getDirection().getStepY() << 1; - return npc.getLocation().transform(offsetX, offsetY, 0); - } - return null; - } - - @Override - public boolean handle(Player player, Node node, String option) { - final NPC npc = (NPC) node; - if (!npc.getAttribute("facing_booth", false)) { - npc.faceLocation(player.getLocation()); - } - //I'm sorry for this but it was honestly the best way to do this - if(player.getAttribute(GC_BASE_ATTRIBUTE +":"+ GC_JOB_ORDINAL,-1) != -1){ - GnomeCookingJob job = GnomeCookingJob.values()[player.getAttribute(GC_BASE_ATTRIBUTE + ":" + GC_JOB_ORDINAL,-1)]; - if(node.getId() == job.getNpc_id() && !player.getAttribute(GC_BASE_ATTRIBUTE + ":" + GC_JOB_COMPLETE,false)){ - Item neededItem = player.getAttribute(GC_BASE_ATTRIBUTE + ":" + GC_NEEDED_ITEM,null); - if(neededItem != null && player.getInventory().containsItem(neededItem)) { - player.getDialogueInterpreter().sendDialogues(job.getNpc_id(), FacialExpression.OLD_HAPPY, "Thank you!"); - player.getInventory().remove(neededItem); - player.getInventory().add(GnomeTipper.getTip(job.getLevel())); - player.removeAttribute(GC_BASE_ATTRIBUTE + ":" + GC_JOB_ORDINAL); - player.removeAttribute(GC_BASE_ATTRIBUTE + ":" + GC_NEEDED_ITEM); - int curPoints = player.getAttribute(GC_BASE_ATTRIBUTE + ":" + GC_POINTS,0); - curPoints += 3; - if(curPoints == 12){ - player.getInventory().add(new Item(9474)); - player.sendMessage(colorize("%RYou have been granted a food delivery token. Use it to have food delivered.")); - } else if(curPoints % 12 == 0){ - int curRedeems = player.getAttribute(GC_BASE_ATTRIBUTE + ":" + GC_REDEEMABLE_FOOD,0); - player.setAttribute("/save:" + GC_BASE_ATTRIBUTE + ":" + GC_REDEEMABLE_FOOD,curRedeems != 10 ? ++curRedeems : curRedeems); - player.sendMessage(colorize("%RYou have been granted a single food delivery charge.")); - } - player.setAttribute("/save:" + GC_BASE_ATTRIBUTE + ":" + GC_POINTS,curPoints); - } - return true; - } - } - return player.getDialogueInterpreter().open(npc.getId(), npc); - } - - @Override - public Plugin newInstance(Object arg) throws Throwable { - NPCDefinition.setOptionHandler("talk-to", this); - NPCDefinition.setOptionHandler("talk", this); - return this; - } -} diff --git a/Server/src/main/kotlin/rs09/game/content/activity/gnomecooking/GCCompletionDialogue.kt b/Server/src/main/kotlin/rs09/game/content/activity/gnomecooking/GCCompletionDialogue.kt new file mode 100644 index 000000000..40d041e2b --- /dev/null +++ b/Server/src/main/kotlin/rs09/game/content/activity/gnomecooking/GCCompletionDialogue.kt @@ -0,0 +1,42 @@ +package rs09.game.content.activity.gnomecooking + +import core.game.content.dialogue.FacialExpression +import core.game.node.item.Item +import rs09.game.content.activity.gnomecooking.GnomeTipper.getTip +import rs09.game.content.dialogue.DialogueFile +import rs09.tools.END_DIALOGUE +import rs09.tools.stringtools.colorize + +class GCCompletionDialogue(val job: GnomeCookingJob) : DialogueFile() { + override fun handle(componentID: Int, buttonID: Int) { + when(stage){ + 0 -> { + val neededItem = player!!.getAttribute("$GC_BASE_ATTRIBUTE:$GC_NEEDED_ITEM", null) + if (neededItem != null && player!!.inventory.containsItem(neededItem)) { + player!!.dialogueInterpreter.sendDialogues(job.npc_id, FacialExpression.OLD_HAPPY, "Thank you!") + player!!.inventory.remove(neededItem) + player!!.inventory.add(getTip(job.level)) + player!!.removeAttribute("$GC_BASE_ATTRIBUTE:$GC_JOB_ORDINAL") + player!!.removeAttribute("$GC_BASE_ATTRIBUTE:$GC_NEEDED_ITEM") + var curPoints = player!!.getAttribute("$GC_BASE_ATTRIBUTE:$GC_POINTS", 0) + curPoints += 3 + if (curPoints == 12) { + player!!.inventory.add(Item(9474)) + player!!.sendMessage(colorize("%RYou have been granted a food delivery token. Use it to have food delivered.")) + } else if (curPoints % 12 == 0) { + var curRedeems = player!!.getAttribute("$GC_BASE_ATTRIBUTE:$GC_REDEEMABLE_FOOD", 0) + player!!.setAttribute( + "/save:$GC_BASE_ATTRIBUTE:$GC_REDEEMABLE_FOOD", + if (curRedeems != 10) ++curRedeems else curRedeems + ) + player!!.sendMessage(colorize("%RYou have been granted a single food delivery charge.")) + } + player!!.setAttribute("/save:$GC_BASE_ATTRIBUTE:$GC_POINTS", curPoints) + } else { + player!!.dialogueInterpreter.sendDialogues(job.npc_id, FacialExpression.ANGRY, "Where's my food?!") + } + stage = END_DIALOGUE + } + } + } +} \ No newline at end of file diff --git a/Server/src/main/kotlin/rs09/game/interaction/InteractionListener.kt b/Server/src/main/kotlin/rs09/game/interaction/InteractionListener.kt index 690d29609..f99828686 100644 --- a/Server/src/main/kotlin/rs09/game/interaction/InteractionListener.kt +++ b/Server/src/main/kotlin/rs09/game/interaction/InteractionListener.kt @@ -2,6 +2,7 @@ package rs09.game.interaction import core.game.node.Node import core.game.node.entity.player.Player +import core.game.world.map.Location abstract class InteractionListener : Listener{ val ITEM = 0 @@ -25,4 +26,13 @@ abstract class InteractionListener : Listener{ fun onUseWith(type: Int, used: IntArray, vararg with: Int, handler: (Player, Node, Node) -> Boolean){ Listeners.add(type,used,with,handler) } + + open fun defineDestinationOverrides(){} + + fun setDest(type: Int, id: Int,handler: (Node) -> Location){ + Listeners.addDestOverride(type,id,handler) + } + fun setDest(type:Int, vararg options: String, handler: (Node) -> Location){ + Listeners.addDestOverrides(type,options,handler) + } } \ No newline at end of file diff --git a/Server/src/main/kotlin/rs09/game/interaction/Listeners.kt b/Server/src/main/kotlin/rs09/game/interaction/Listeners.kt index aafbaa517..c3e580b51 100644 --- a/Server/src/main/kotlin/rs09/game/interaction/Listeners.kt +++ b/Server/src/main/kotlin/rs09/game/interaction/Listeners.kt @@ -4,10 +4,12 @@ import core.game.interaction.DestinationFlag import core.game.interaction.MovementPulse import core.game.node.Node import core.game.node.entity.player.Player +import core.game.world.map.Location object Listeners { private val listeners = HashMap Boolean>(1000) private val useWithListeners = HashMap Boolean>(1000) + private val destinationOverrides = HashMap Location>(100) @JvmStatic fun add(id: Int, type: Int, option: Array, method: (Player,Node) -> Boolean){ @@ -64,6 +66,28 @@ object Listeners { return listeners["$type:${option.toLowerCase()}"] } + @JvmStatic + fun addDestOverride(type: Int, id: Int, method: (Node) -> Location){ + destinationOverrides["$type:$id"] = method + } + + @JvmStatic + fun addDestOverrides(type: Int,options: Array, method: (Node) -> Location){ + for(opt in options){ + destinationOverrides["$type:${opt.toLowerCase()}"] = method + } + } + + @JvmStatic + fun getOverride(type: Int,id: Int): ((Node) -> Location)?{ + return destinationOverrides["$type:$id"] + } + + @JvmStatic + fun getOverride(type: Int,option: String): ((Node) -> Location)?{ + return destinationOverrides["$type:$option"] + } + @JvmStatic fun run(used: Node, with: Node, type: Int,player: Player): Boolean{ val flag = when(type){ @@ -72,11 +96,14 @@ object Listeners { else -> DestinationFlag.OBJECT } + if(player.locks.isInteractionLocked) return false + var flipped = false val method = get(used.id,with.id,type) ?: get(with.id,used.id,type).also { flipped = true } ?: return false if(type != 0) { + if(player.locks.isMovementLocked) return false player.pulseManager.run(object : MovementPulse(player, with, flag) { override fun pulse(): Boolean { player.faceLocation(with.location) @@ -100,10 +127,14 @@ object Listeners { else -> DestinationFlag.OBJECT } + if(player.locks.isInteractionLocked) return false + val method = get(id,type,option) ?: get(option,type) ?: return false + val destOverride = getOverride(type,node.id) ?: getOverride(type,option.toLowerCase()) if(type != 0) { - player.pulseManager.run(object : MovementPulse(player, node, flag) { + if(player.locks.isMovementLocked) return false + player.pulseManager.run(object : MovementPulse(player, node, flag, destOverride) { override fun pulse(): Boolean { player.faceLocation(node.location) method.invoke(player,node) diff --git a/Server/src/main/kotlin/rs09/game/interaction/npc/NPCTalkListener.kt b/Server/src/main/kotlin/rs09/game/interaction/npc/NPCTalkListener.kt new file mode 100644 index 000000000..a8621b2aa --- /dev/null +++ b/Server/src/main/kotlin/rs09/game/interaction/npc/NPCTalkListener.kt @@ -0,0 +1,44 @@ +package rs09.game.interaction.npc + +import core.game.node.entity.npc.NPC +import rs09.game.content.activity.gnomecooking.* +import rs09.game.interaction.InteractionListener + +/** + * Handles the NPC talk-to option. + * @author Ceikry + */ +class NPCTalkListener : InteractionListener() { + + override fun defineListeners() { + + on(NPC,"talk-to","talk"){player,node -> + val npc = node.asNpc() + if (!npc.getAttribute("facing_booth", false)) { + npc.faceLocation(player.location) + } + //I'm sorry for this but it was honestly the best way to do this + if (player.getAttribute(GC_BASE_ATTRIBUTE + ":" + GC_JOB_ORDINAL, -1) != -1) { + val job = GnomeCookingJob.values()[player.getAttribute(GC_BASE_ATTRIBUTE + ":" + GC_JOB_ORDINAL, -1)] + if (node.getId() == job.npc_id && !player.getAttribute(GC_BASE_ATTRIBUTE + ":" + GC_JOB_COMPLETE, false)) { + player.dialogueInterpreter.open(GCCompletionDialogue(job)) + return@on true + } + } + return@on player.dialogueInterpreter.open(npc.id, npc) + } + + } + + override fun defineDestinationOverrides() { + setDest(NPC,"talk","talk-to"){node -> + val npc = node as NPC + if (npc.getAttribute("facing_booth", false)) { + val offsetX = npc.direction.stepX shl 1 + val offsetY = npc.direction.stepY shl 1 + return@setDest npc.location.transform(offsetX, offsetY, 0) + } + return@setDest node.location + } + } +} \ No newline at end of file diff --git a/Server/src/main/kotlin/rs09/plugin/PluginManager.kt b/Server/src/main/kotlin/rs09/plugin/PluginManager.kt index c1ef61164..4ed942ac6 100644 --- a/Server/src/main/kotlin/rs09/plugin/PluginManager.kt +++ b/Server/src/main/kotlin/rs09/plugin/PluginManager.kt @@ -73,7 +73,9 @@ object PluginManager { } catch (e: Exception) {e.printStackTrace()} } result.getSubclasses("rs09.game.interaction.InteractionListener").forEach { - (it.loadClass().newInstance() as InteractionListener).defineListeners() + val clazz = it.loadClass().newInstance() as InteractionListener + clazz.defineListeners() + clazz.defineDestinationOverrides() } }