diff --git a/Server/src/main/java/core/game/content/activity/mta/TelekineticGrabSpell.java b/Server/src/main/java/core/game/content/activity/mta/TelekineticGrabSpell.java index 581341e25..b0b881b2c 100644 --- a/Server/src/main/java/core/game/content/activity/mta/TelekineticGrabSpell.java +++ b/Server/src/main/java/core/game/content/activity/mta/TelekineticGrabSpell.java @@ -79,7 +79,7 @@ public final class TelekineticGrabSpell extends MagicSpell { /** * Represents the spell id. */ - public static final int SPELL_ID = 65535; + public static final int SPELL_ID = 19; /** * Constructs a new {@code TelekineticGrabSpell} {@code Object}. diff --git a/Server/src/main/kotlin/api/ApiExtensions.kt b/Server/src/main/kotlin/api/ApiExtensions.kt index 43d695337..814337b02 100644 --- a/Server/src/main/kotlin/api/ApiExtensions.kt +++ b/Server/src/main/kotlin/api/ApiExtensions.kt @@ -1,6 +1,8 @@ package api import core.game.node.item.Item +import java.util.* +import kotlin.collections.ArrayList fun IntRange.toIntArray(): IntArray { if (last < first) @@ -53,4 +55,9 @@ fun IntArray.getNext(element: Int) : Int { fun IntArray.isNextLast(element: Int) : Boolean { return this.isLast(this.getNext(element)) +} + +fun LinkedList.tryPop(default: T) : T { + this.peek() ?: return default + return this.pop() } \ No newline at end of file diff --git a/Server/src/main/kotlin/rs09/game/content/ame/RandomEventManager.kt b/Server/src/main/kotlin/rs09/game/content/ame/RandomEventManager.kt index 478c1259c..bc0c62847 100644 --- a/Server/src/main/kotlin/rs09/game/content/ame/RandomEventManager.kt +++ b/Server/src/main/kotlin/rs09/game/content/ame/RandomEventManager.kt @@ -1,5 +1,6 @@ package rs09.game.content.ame +import api.Commands import api.LoginListener import api.events.EventHook import api.events.TickEvent @@ -11,10 +12,12 @@ import core.game.world.map.zone.ZoneRestriction import core.tools.RandomFunction import rs09.game.Event import rs09.game.system.SystemLogger +import rs09.game.system.command.Privilege import rs09.game.world.GameWorld +import rs09.game.world.repository.Repository import kotlin.random.Random -class RandomEventManager(val player: Player? = null) : LoginListener, EventHook { +class RandomEventManager(val player: Player? = null) : LoginListener, EventHook, Commands { var event: RandomEventNPC? = null var enabled: Boolean = false var nextSpawn = 0 @@ -71,6 +74,18 @@ class RandomEventManager(val player: Player? = null) : LoginListener, EventHook< nextSpawn = GameWorld.ticks + RandomFunction.random(MIN_DELAY_TICKS, MAX_DELAY_TICKS) } + override fun defineCommands() { + define("targeted-ame", Privilege.ADMIN, "targeted-ame username", "Summons a random for the given user") {player, args -> + val username = args[1] + val target = Repository.getPlayerByName(username) + + if (target == null) + reject(player, "Unable to find player $username!") + + getInstance(target!!)?.fireEvent() + } + } + companion object { const val AVG_DELAY_TICKS = 6000 // 60 minutes const val MIN_DELAY_TICKS = AVG_DELAY_TICKS / 2 diff --git a/Server/src/main/kotlin/rs09/game/content/global/worldevents/penguinhns/PenguinManager.kt b/Server/src/main/kotlin/rs09/game/content/global/worldevents/penguinhns/PenguinManager.kt index 56d60c23c..55fc568c6 100644 --- a/Server/src/main/kotlin/rs09/game/content/global/worldevents/penguinhns/PenguinManager.kt +++ b/Server/src/main/kotlin/rs09/game/content/global/worldevents/penguinhns/PenguinManager.kt @@ -43,12 +43,14 @@ class PenguinManager{ } fun rebuildVars() { - if(PenguinHNSEvent.getStoreFile().isEmpty()) { + if(!PenguinHNSEvent.getStoreFile().containsKey("spawned-penguins")) { penguins = spawner.spawnPenguins(10) PenguinHNSEvent.getStoreFile()["spawned-penguins"] = penguins.toJSONArray() + tagMapping.clear() for (p in penguins) { tagMapping.put(p, JSONArray()) } + updateStoreFile() } else { val spawnedOrdinals = (PenguinHNSEvent.getStoreFile()["spawned-penguins"] as JSONArray).map { it.toString().toInt() } spawner.spawnPenguins(spawnedOrdinals) diff --git a/Server/src/main/kotlin/rs09/game/interaction/npc/NPCTalkListener.kt b/Server/src/main/kotlin/rs09/game/interaction/npc/NPCTalkListener.kt index 85c18822d..204f7da09 100644 --- a/Server/src/main/kotlin/rs09/game/interaction/npc/NPCTalkListener.kt +++ b/Server/src/main/kotlin/rs09/game/interaction/npc/NPCTalkListener.kt @@ -33,7 +33,7 @@ class NPCTalkListener : InteractionListener { on(IntType.NPC,"talk-to","talk","talk to"){player,node -> val npc = node.asNpc() if(RandomEvents.randomIDs.contains(node.id)){ - if(RandomEventManager.getInstance(player)!!.event == null || RandomEventManager.getInstance(player)!!.event!!.id != node.id){ + if(RandomEventManager.getInstance(player)!!.event == null || RandomEventManager.getInstance(player)!!.event!! != node.asNpc()){ player.sendMessage("They aren't interested in talking to you.") } else { RandomEventManager.getInstance(player)!!.event!!.talkTo(node.asNpc()) diff --git a/Server/src/main/kotlin/rs09/game/node/entity/skill/farming/SeedOnPlantPot.kt b/Server/src/main/kotlin/rs09/game/node/entity/skill/farming/SeedOnPlantPot.kt index dfd02095a..6035cc18c 100644 --- a/Server/src/main/kotlin/rs09/game/node/entity/skill/farming/SeedOnPlantPot.kt +++ b/Server/src/main/kotlin/rs09/game/node/entity/skill/farming/SeedOnPlantPot.kt @@ -1,50 +1,118 @@ package rs09.game.node.entity.skill.farming -import core.game.interaction.NodeUsageEvent -import core.game.interaction.UseWithHandler -import core.game.node.item.Item -import core.plugin.Initializable -import core.plugin.Plugin +import api.addItem +import api.inInventory +import api.removeItem +import api.sendDialogue +import core.game.node.Node +import core.game.node.entity.player.Player import org.rs09.consts.Items +import rs09.game.interaction.IntType +import rs09.game.interaction.InteractionListener +import rs09.game.node.entity.state.newsys.states.SeedlingState -@Initializable -class SeedOnPlantPot : UseWithHandler(Items.ACORN_5312, - Items.WILLOW_SEED_5313,Items.MAPLE_SEED_5314,Items.YEW_SEED_5315,Items.MAGIC_SEED_5316,Items.APPLE_TREE_SEED_5283,Items.BANANA_TREE_SEED_5284,Items.ORANGE_TREE_SEED_5285,Items.CURRY_TREE_SEED_5286,Items.PINEAPPLE_SEED_5287,Items.PAPAYA_TREE_SEED_5288,Items.PALM_TREE_SEED_5289) { - override fun newInstance(arg: Any?): Plugin { - addHandler(Items.PLANT_POT_5354, ITEM_TYPE,this) - return this +class SeedlingListener : InteractionListener { + override fun defineListeners() { + onUseWith(IntType.ITEM, TREE_SEEDS, Items.PLANT_POT_5354, handler = ::addSeedToPot) + onUseWith(IntType.ITEM, TREE_SEEDLINGS, *WATERING_CANS, handler = ::waterSeedling) } - override fun handle(event: NodeUsageEvent?): Boolean { - val player = event?.player ?: return false - val pot = event.usedItem ?: return false - val seed = event.usedWith.asItem() ?: return false + fun addSeedToPot(player: Player, used: Node, with: Node) : Boolean { + val seed = used.asItem() ?: return false + val pot = with.asItem() ?: return false - if(!player.inventory.contains(Items.GARDENING_TROWEL_5325,1)){ - player.dialogueInterpreter.sendDialogue("You need a gardening trowel on you to do this.") + if(!inInventory(player, Items.GARDENING_TROWEL_5325)){ + sendDialogue(player, "You need a gardening trowel on you to do this.") return false } - val seedling = when(seed.id){ - Items.ACORN_5312 -> Items.OAK_SEEDLING_5358 - Items.WILLOW_SEED_5313 -> Items.WILLOW_SEEDLING_5359 - Items.MAPLE_SEED_5314 -> Items.MAPLE_SEEDLING_5360 - Items.YEW_SEED_5315 -> Items.YEW_SEEDLING_5361 - Items.MAGIC_SEED_5316 -> Items.MAGIC_SEEDLING_5362 - Items.APPLE_TREE_SEED_5283 -> Items.APPLE_SEEDLING_5480 - Items.BANANA_TREE_SEED_5284 -> Items.BANANA_SEEDLING_5481 - Items.ORANGE_TREE_SEED_5285 -> Items.ORANGE_SEEDLING_5482 - Items.CURRY_TREE_SEED_5286 -> Items.CURRY_SEEDLING_5483 - Items.PINEAPPLE_SEED_5287 -> Items.PINEAPPLE_SEEDLING_5484 - Items.PAPAYA_TREE_SEED_5288 -> Items.PAPAYA_SEEDLING_5485 - Items.PALM_TREE_SEED_5289 -> Items.PALM_SEEDLING_5486 - else -> return false + val seedling = getSeedling(seed.id) + if (seedling == -1) return false + if (!removeItem(player, seed) || !removeItem(player, pot)) return true + addItem(player, seedling) + return true + } + + fun waterSeedling(player: Player, used: Node, with: Node) : Boolean { + val seedling = used.asItem() ?: return false + val can = with.asItem() ?: return false + + val nextCan = can.id.getNext() + val wateredSeedling = if (seedling.id > 5400) seedling.id + 8 else seedling.id + 6 + + if (!removeItem(player, can) || !removeItem(player, seedling)) return false + addItem(player, wateredSeedling) + addItem(player, nextCan) + + var state = player.states["seedling"] as SeedlingState? + + if (state != null) { + state.addSeedling(wateredSeedling) + return true } - if(player.inventory.remove(pot) && player.inventory.remove(Item(seed.id,1))){ - player.inventory.add(Item(seedling)) - } + state = player.registerState("seedling") as SeedlingState? + state?.addSeedling(wateredSeedling) + state?.init() return true } + + private fun Int.getNext(): Int{ + val index = WATERING_CANS.indexOf(this) + if (index == -1) return Items.WATERING_CAN_5331 + return if (index != WATERING_CANS.size -1) WATERING_CANS[index + 1] else Items.WATERING_CAN_5331 + } + fun getSeedling(id: Int) : Int { + return when (id) { + Items.ACORN_5312 -> Items.OAK_SEEDLING_5358 + Items.WILLOW_SEED_5313 -> Items.WILLOW_SEEDLING_5359 + Items.MAPLE_SEED_5314 -> Items.MAPLE_SEEDLING_5360 + Items.YEW_SEED_5315 -> Items.YEW_SEEDLING_5361 + Items.MAGIC_SEED_5316 -> Items.MAGIC_SEEDLING_5362 + Items.APPLE_TREE_SEED_5283 -> Items.APPLE_SEEDLING_5480 + Items.BANANA_TREE_SEED_5284 -> Items.BANANA_SEEDLING_5481 + Items.ORANGE_TREE_SEED_5285 -> Items.ORANGE_SEEDLING_5482 + Items.CURRY_TREE_SEED_5286 -> Items.CURRY_SEEDLING_5483 + Items.PINEAPPLE_SEED_5287 -> Items.PINEAPPLE_SEEDLING_5484 + Items.PAPAYA_TREE_SEED_5288 -> Items.PAPAYA_SEEDLING_5485 + Items.PALM_TREE_SEED_5289 -> Items.PALM_SEEDLING_5486 + Items.SPIRIT_SEED_5317 -> Items.SPIRIT_SEEDLING_5363 + else -> -1 + } + } + + val TREE_SEEDS = intArrayOf( + Items.ACORN_5312, + Items.WILLOW_SEED_5313, + Items.MAPLE_SEED_5314, + Items.YEW_SEED_5315, + Items.MAGIC_SEED_5316, + Items.APPLE_TREE_SEED_5283, + Items.BANANA_TREE_SEED_5284, + Items.ORANGE_TREE_SEED_5285, + Items.CURRY_TREE_SEED_5286, + Items.PINEAPPLE_SEED_5287, + Items.PAPAYA_TREE_SEED_5288, + Items.PALM_TREE_SEED_5289, + Items.SPIRIT_SEED_5317 + ) + + val TREE_SEEDLINGS = intArrayOf( + Items.OAK_SEEDLING_5358, + Items.WILLOW_SEEDLING_5359, + Items.MAPLE_SEEDLING_5360, + Items.YEW_SEEDLING_5361, + Items.MAGIC_SEEDLING_5362, + Items.APPLE_SEEDLING_5480, + Items.BANANA_SEEDLING_5481, + Items.ORANGE_SEEDLING_5482, + Items.CURRY_SEEDLING_5483, + Items.PINEAPPLE_SEEDLING_5484, + Items.PAPAYA_SEEDLING_5485, + Items.PALM_SEEDLING_5486, + Items.SPIRIT_SEEDLING_5363 + ) + + private val WATERING_CANS = intArrayOf(Items.WATERING_CAN8_5340,Items.WATERING_CAN7_5339,Items.WATERING_CAN6_5338,Items.WATERING_CAN5_5337,Items.WATERING_CAN4_5336,Items.WATERING_CAN3_5335,Items.WATERING_CAN2_5334,Items.WATERING_CAN1_5333) } \ No newline at end of file diff --git a/Server/src/main/kotlin/rs09/game/node/entity/skill/farming/SeedlingWaterer.kt b/Server/src/main/kotlin/rs09/game/node/entity/skill/farming/SeedlingWaterer.kt deleted file mode 100644 index 1630ffcd0..000000000 --- a/Server/src/main/kotlin/rs09/game/node/entity/skill/farming/SeedlingWaterer.kt +++ /dev/null @@ -1,54 +0,0 @@ -package rs09.game.node.entity.skill.farming - -import core.game.interaction.NodeUsageEvent -import core.game.interaction.UseWithHandler -import core.game.node.item.Item -import core.plugin.Initializable -import core.plugin.Plugin -import org.rs09.consts.Items -import rs09.game.node.entity.state.newsys.states.SeedlingState - -private val cans = arrayListOf(Items.WATERING_CAN8_5340,Items.WATERING_CAN7_5339,Items.WATERING_CAN6_5338,Items.WATERING_CAN5_5337,Items.WATERING_CAN4_5336,Items.WATERING_CAN3_5335,Items.WATERING_CAN2_5334,Items.WATERING_CAN1_5333) -private val seedlings = arrayListOf(Items.OAK_SEEDLING_5358,Items.WILLOW_SEEDLING_5359,Items.MAPLE_SEEDLING_5360,Items.YEW_SEEDLING_5361,Items.MAGIC_SEEDLING_5362,Items.APPLE_SEEDLING_5480,Items.BANANA_SEEDLING_5481,Items.ORANGE_SEEDLING_5482,Items.CURRY_SEEDLING_5483,Items.PINEAPPLE_SEEDLING_5484,Items.PAPAYA_SEEDLING_5485,Items.PALM_SEEDLING_5486) - -@Initializable -class SeedlingWaterer : UseWithHandler(*cans.toIntArray()) { - - override fun newInstance(arg: Any?): Plugin { - for(seed in seedlings) addHandler(seed, ITEM_TYPE,this) - return this - } - - override fun handle(event: NodeUsageEvent?): Boolean { - val player = event?.player ?: return false - val seedling = event.used.asItem() ?: return false - val can = event.usedWith.asItem() ?: return false - - val nextCan = can.id.getNext() - val wateredSeedling = if(seedling.id > 5400 ) seedling.id + 8 else seedling.id + 6 - - if(player.inventory.remove(can) && player.inventory.remove(seedling)){ - player.inventory.add(Item(wateredSeedling)) - player.inventory.add(Item(nextCan)) - - var state = player.states["seedling"] as SeedlingState? - if(state == null){ - state = player.registerState("seedling") as SeedlingState? - state?.addSeedling(wateredSeedling) - state?.init() - } else { - state.addSeedling(wateredSeedling) - } - - player.sendMessage("You water the seedling.") - } - return true - } - - private fun Int.getNext(): Int{ - var index = cans.indexOf(this) - if(index == -1) return Items.WATERING_CAN_5331 - if(index != cans.size -1) return cans[index + 1] else return Items.WATERING_CAN_5331 - } - -} \ No newline at end of file diff --git a/Server/src/main/kotlin/rs09/game/node/entity/skill/farming/UseWithPatchHandler.kt b/Server/src/main/kotlin/rs09/game/node/entity/skill/farming/UseWithPatchHandler.kt index 237bd39f4..949fcee7c 100644 --- a/Server/src/main/kotlin/rs09/game/node/entity/skill/farming/UseWithPatchHandler.kt +++ b/Server/src/main/kotlin/rs09/game/node/entity/skill/farming/UseWithPatchHandler.kt @@ -232,7 +232,7 @@ class UseWithPatchHandler : InteractionListener { patches.addAll(8550..8557) //allotment wrappers patches.addAll(7847..7853) //flower patch wrappers patches.addAll(8150..8156) //herb patch wrappers - patches.addAll(8388..8390) // Tree patches + patches.addAll(8388..8391) // Tree patches patches.add(19147) //Tree patch patches.addAll(7962..7965) //fruit trees patches.addAll(8173..8176) //hops diff --git a/Server/src/main/kotlin/rs09/net/packet/PacketProcessor.kt b/Server/src/main/kotlin/rs09/net/packet/PacketProcessor.kt index 8a3e81f94..a394e2e80 100644 --- a/Server/src/main/kotlin/rs09/net/packet/PacketProcessor.kt +++ b/Server/src/main/kotlin/rs09/net/packet/PacketProcessor.kt @@ -3,6 +3,7 @@ package rs09.net.packet import api.events.ButtonClickEvent import api.getAttribute import api.sendMessage +import api.tryPop import core.cache.def.impl.ItemDefinition import core.cache.def.impl.NPCDefinition import core.cache.def.impl.SceneryDefinition @@ -17,7 +18,6 @@ import core.game.interaction.NodeUsageEvent import core.game.interaction.Option import core.game.interaction.UseWithHandler import core.game.node.Node -import core.game.node.entity.impl.PulseType import core.game.node.entity.player.Player import core.game.node.entity.player.info.Rights import core.game.node.entity.player.info.login.LoginConfiguration @@ -43,8 +43,8 @@ import discord.Discord import org.rs09.consts.Components import proto.management.ClanMessage import proto.management.JoinClanRequest +import proto.management.LeaveClanRequest import rs09.ServerConstants -import rs09.game.ge.GrandExchange import rs09.game.ge.GrandExchange.Companion.getOfferStats import rs09.game.ge.GrandExchange.Companion.getRecommendedPrice import rs09.game.ge.GrandExchangeOffer @@ -81,7 +81,7 @@ object PacketProcessor { val pw = PrintWriter(sw) var pkt: Packet while (countThisCycle-- > 0) { - pkt = queue.pop() + pkt = queue.tryPop(Packet.NoProcess()) try { process(pkt) } catch (e: Exception) { @@ -127,8 +127,14 @@ object PacketProcessor { is Packet.PlayerPrefsUpdate -> {/*TODO implement something that cares about this */} is Packet.Ping -> pkt.player.session.lastPing = System.currentTimeMillis() is Packet.JoinClan -> { - if (pkt.clanName.isNotEmpty()) - sendMessage(pkt.player, "Attempting to join channel....:clan:") + if (pkt.clanName.isEmpty() && pkt.player.communication.currentClan.isNotEmpty()) { + val builder = LeaveClanRequest.newBuilder() + builder.clanName = pkt.player.communication.currentClan + builder.username = pkt.player.name + ManagementEvents.publish(builder.build()) + return + } + sendMessage(pkt.player, "Attempting to join channel....:clan:") val builder = JoinClanRequest.newBuilder() builder.clanName = pkt.clanName builder.username = pkt.player.name @@ -228,11 +234,19 @@ object PacketProcessor { val final = pkt.count - pkt.player.interfaceManager.getPacketCount(0) pkt.player.interfaceManager.getPacketCount(final) } - is Packet.DialogueOption -> { + is Packet.ContinueOption -> { val player = pkt.player + player.debug("[CONTINUE OPT]----------") + player.debug("Iface: ${pkt.iface}") + player.debug("Child: ${pkt.child}") + player.debug("Slot: ${pkt.slot}") + player.debug("------------------------") if (player.dialogueInterpreter.dialogue == null) { player.interfaceManager.closeChatbox() player.dialogueInterpreter.actions.removeFirstOrNull()?.handle(player, pkt.child) + val component = player.interfaceManager.getComponent(pkt.iface) ?: return + if (!InterfaceListeners.run(player, component, pkt.opcode, pkt.child, pkt.slot, -1)) + component.plugin?.handle(player, component, pkt.opcode, pkt.child, pkt.slot, -1) return } player.dialogueInterpreter.handle(pkt.iface, pkt.child) @@ -427,7 +441,14 @@ object PacketProcessor { //there's more data in this packet, we're just not using it } - if (player.locks.isMovementLocked || !player.interfaceManager.close() || !player.interfaceManager.closeSingleTab() || !player.dialogueInterpreter.close()) { + var canWalk = !player.locks.isMovementLocked + + if (canWalk && player.interfaceManager.isOpened && !player.interfaceManager.opened.definition.isWalkable) + canWalk = canWalk && player.interfaceManager.close() + if (canWalk && player.interfaceManager.hasChatbox() && !player.interfaceManager.chatbox.definition.isWalkable) + player.interfaceManager.closeChatbox() + + if (!canWalk || !player.dialogueInterpreter.close()) { player.debug("[WALK ACTION]-- NO HANDLE: PLAYER LOCKED OR INTERFACES SAY NO") return sendClearMinimap(player) } diff --git a/Server/src/main/kotlin/rs09/net/packet/in/Decoders530.kt b/Server/src/main/kotlin/rs09/net/packet/in/Decoders530.kt index d70407db0..5137d8da9 100644 --- a/Server/src/main/kotlin/rs09/net/packet/in/Decoders530.kt +++ b/Server/src/main/kotlin/rs09/net/packet/in/Decoders530.kt @@ -349,12 +349,12 @@ enum class Decoders530(val opcode: Int) { return Packet.IfAction(player, opcode, -1, iface, button, -1, -1) } }, - DIALOGUE_OPT(132) { + CONTINUE_OPT(132) { override fun decode(player: Player, buffer: IoBuffer): Packet { val ifHash = buffer.intA val slot = buffer.leShort val (iface, button) = deHash(ifHash) - return Packet.DialogueOption(player, iface, button) + return Packet.ContinueOption(player, iface, button, slot, 132) } }, CLOSE_IFACE(184) { @@ -364,8 +364,8 @@ enum class Decoders530(val opcode: Int) { }, IF_GROUNDITEM_ACTION(73) { override fun decode(player: Player, buffer: IoBuffer): Packet { - val child = buffer.short - val iface = buffer.short + val ifHash = buffer.intA + val (iface, child) = deHash(ifHash) val y = buffer.short val itemId = buffer.leShortA val x = buffer.leShortA diff --git a/Server/src/main/kotlin/rs09/net/packet/in/Packet.kt b/Server/src/main/kotlin/rs09/net/packet/in/Packet.kt index d06b4d69f..3e1b27216 100644 --- a/Server/src/main/kotlin/rs09/net/packet/in/Packet.kt +++ b/Server/src/main/kotlin/rs09/net/packet/in/Packet.kt @@ -17,7 +17,7 @@ sealed class Packet { data class UseWithScenery(val player: Player, val itemId: Int, val slot: Int, val sceneryId: Int, val x: Int, val y: Int) : Packet() data class UseWithGroundItem(val player: Player, val usedId: Int, val withId: Int, val iface: Int, val child: Int, val slot: Int, val x: Int, val y: Int) : Packet() data class IfAction(val player: Player, val opcode: Int, val optIndex: Int, val iface: Int, val child: Int, val slot: Int, val itemId: Int = -1) : Packet() - data class DialogueOption(val player: Player, val iface: Int, val child: Int) : Packet() + data class ContinueOption(val player: Player, val iface: Int, val child: Int, val slot: Int, val opcode: Int) : Packet() data class CloseIface(val player: Player) : Packet() data class JoinClan(val player: Player, val clanName: String) : Packet() data class SetClanRank(val player: Player, val username: String, val rank: Int) : Packet()