diff --git a/Server/src/main/java/core/game/node/entity/skill/thieving/PickpocketPulse.java b/Server/src/main/java/core/game/node/entity/skill/thieving/PickpocketPulse.java deleted file mode 100644 index 772bfa033..000000000 --- a/Server/src/main/java/core/game/node/entity/skill/thieving/PickpocketPulse.java +++ /dev/null @@ -1,203 +0,0 @@ -package core.game.node.entity.skill.thieving; - -import core.game.node.entity.Entity; -import core.game.node.entity.combat.DeathTask; -import core.game.node.entity.combat.ImpactHandler.HitsplatType; -import core.game.node.entity.npc.NPC; -import core.game.node.entity.player.Player; -import core.game.node.entity.player.link.audio.Audio; -import core.game.node.entity.player.link.diary.DiaryType; -import core.game.node.entity.skill.SkillPulse; -import core.game.node.entity.skill.Skills; -import core.game.node.entity.state.EntityState; -import core.game.node.item.GroundItemManager; -import core.game.node.item.Item; -import core.game.world.map.zone.ZoneBorders; -import core.game.world.update.flag.context.Animation; -import core.tools.RandomFunction; -import rs09.game.node.entity.skill.thieving.Pickpocket; -import rs09.game.world.GameWorld; - -import java.util.List; - -/** - * Represents the pulse used to pickpocket an npc. - * - * @author Vexia - */ -public final class PickpocketPulse extends SkillPulse { - - /** - * Represents the animation specific to pickpocketing. - */ - private static final Animation ANIMATION = new Animation(881); - - /** - * Represents the npc animation. - */ - private static final Animation NPC_ANIM = new Animation(422); - - /** - * Represents the animation specific to pickpocketing. - */ - private static final Animation STUN_ANIMATION = new Animation(424); - - /** - * Represents the sound to send when failed. - */ - private static final Audio SOUND = new Audio(2727, 1, 0); - - /** - * Represents the pickpocket type. - */ - private final Pickpocket type; - - /** - * Represents the tickets to be rewarded. - */ - private int ticks; - - /** - * Constructs a new {@code PickpocketPulse} {@code Object}. - * - * @param player the player. - * @param node the node. - * @param type the type. - */ - public PickpocketPulse(Player player, NPC node, final Pickpocket type) { - super(player, node); - this.type = type; - this.resetAnimation = false; - } - - @Override - public boolean checkRequirements() { - if (!interactable() && !player.getLocks().isInteractionLocked()) { - return false; - } - if (player.getSkills().getLevel(Skills.THIEVING) < type.getLevel()) { - player.getPacketDispatch().sendMessage("You need to be a level " + type.getLevel() + " thief in order to pick this pocket."); - return false; - } - if (!hasInventorySpace()) { - player.getPacketDispatch().sendMessage("You do not have enough space in your inventory to pick this pocket."); - return false; - } - player.getLocks().lockInteractions(2); - player.faceTemporary(node, 2); - node.getWalkingQueue().reset(); - node.getLocks().lockMovement(1); - return true; - } - - @Override - public void animate() { - } - - @Override - public boolean reward() { - if (ticks == 0) { - player.animate(ANIMATION); - } - if (++ticks % 2 != 0) { - return false; - } - final boolean success = success(); - if (success) { - player.getPacketDispatch().sendMessage(type.getRewardMessage().replace("@name", node.getName().toLowerCase())); - player.getSkills().addExperience(Skills.THIEVING, type.getExperience(), true); - List loot = type.getRandomLoot(player); - loot.stream().forEach(item -> { - if (!player.getInventory().add(item)) { - GroundItemManager.create(item, player.getLocation(), player); - } - }); - if (type == Pickpocket.GUARD && node.getId() == 9) { - player.getAchievementDiaryManager().finishTask(player, DiaryType.FALADOR, 1, 6); - } - if (type == Pickpocket.GUARD && node.getId() == 5920 - && new ZoneBorders(3202, 3459, 3224, 3470, 0).insideBorder(player)) { - player.getAchievementDiaryManager().finishTask(player, DiaryType.VARROCK, 1, 12); - } - player.getLocks().unlockInteraction(); - } else { - node.animate(NPC_ANIM); - node.faceTemporary(player, 1); - node.sendChat(type.getForceMessage()); - player.animate(STUN_ANIMATION); - player.getAudioManager().send(new Audio(1842)); - player.getStateManager().set(EntityState.STUNNED, 4); - player.getAudioManager().send(SOUND); - player.setAttribute("thief-delay", GameWorld.getTicks() + 4); - player.getImpactHandler().manualHit(player, type.getStunDamage(), HitsplatType.NORMAL); - player.getPacketDispatch().sendMessage(type.getFailMessage().replace("@name", node.getName().toLowerCase())); - } - return true; - } - - @Override - public void stop() { - super.stop(); - } - - @Override - public void message(int type) { - switch (type) { - case 0: - player.getPacketDispatch().sendMessage(this.type.getStartMessage().replace("@name", node.getName().toLowerCase())); - break; - } - } - - /** - * Checks if the npc is interable. - * - * @return {@code True} if so. - */ - private boolean interactable() { - if (DeathTask.isDead(((Entity) node)) || ((NPC) node).isHidden(player) || !node.isActive() || player.getAttribute("thief-delay", 0) > GameWorld.getTicks()) { - return false; - } else if (player.inCombat()) { - player.getPacketDispatch().sendMessage("You can't pickpocket during combat."); - return false; - } else if (!hasInventorySpace()) { - player.getPacketDispatch().sendMessage("You don't have enough inventory space."); - return false; - } - return true; - } - - /** - * Checks if the pickpocket is a success. - * - * @return {@code True} if so. - */ - private boolean success() { - double level = player.getSkills().getLevel(Skills.THIEVING); - double req = type.getLevel(); - double successChance = Math.ceil((level * 50 - req * 15) / req / 3 * 4); - int roll = RandomFunction.random(99); - if (RandomFunction.random(12) < 2) { - return false; - } - if (successChance >= roll) { - return true; - } - return false; - } - - /** - * Checks if player has enough inventory space to pickpocket npc. - * @return {@code True} if player has enough inventory space. - */ - private boolean hasInventorySpace() { - if (player.getInventory().isFull() && type.getLoot().length > 0) { - if (!(type.getLoot().length == 1 && player.getInventory().hasSpaceFor( - new Item(type.getLoot()[0].getId(), type.getLoot()[0].getMaximumAmount())))) { - return false; - } - } - return true; - } - -} diff --git a/Server/src/main/java/core/game/node/entity/skill/thieving/ThievingOptionPlugin.java b/Server/src/main/java/core/game/node/entity/skill/thieving/ThievingOptionPlugin.java index 2047724d8..8ed6feb03 100644 --- a/Server/src/main/java/core/game/node/entity/skill/thieving/ThievingOptionPlugin.java +++ b/Server/src/main/java/core/game/node/entity/skill/thieving/ThievingOptionPlugin.java @@ -1,15 +1,12 @@ package core.game.node.entity.skill.thieving; -import core.cache.def.impl.NPCDefinition; import core.cache.def.impl.ObjectDefinition; 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.object.GameObject; import core.plugin.Initializable; import core.plugin.Plugin; -import rs09.game.node.entity.skill.thieving.Pickpocket; /** * Represents the plugin used to handle thieving options. @@ -21,8 +18,6 @@ public class ThievingOptionPlugin extends OptionHandler { @Override public Plugin newInstance(Object arg) throws Throwable { - NPCDefinition.setOptionHandler("pick-pocket", this); - NPCDefinition.setOptionHandler("pickpocket", this); ObjectDefinition.setOptionHandler("steal-from", this); ObjectDefinition.setOptionHandler("steal from", this); return this; @@ -31,10 +26,6 @@ public class ThievingOptionPlugin extends OptionHandler { @Override public boolean handle(Player player, Node node, String option) { switch (option) { - case "pick-pocket": - case "pickpocket": - player.getPulseManager().run(new PickpocketPulse(player, (NPC) node, Pickpocket.forNPC((NPC) node))); - break; case "steal-from": case "steal from": player.getPulseManager().run(new StallThiefPulse(player, (GameObject) node, Stall.forObject((GameObject) node))); diff --git a/Server/src/main/java/core/tools/RandomFunction.java b/Server/src/main/java/core/tools/RandomFunction.java index 1bc819331..a49afc297 100644 --- a/Server/src/main/java/core/tools/RandomFunction.java +++ b/Server/src/main/java/core/tools/RandomFunction.java @@ -1,14 +1,15 @@ package core.tools; -import java.util.*; -import java.util.stream.Collectors; -import java.util.stream.Stream; - import core.game.node.entity.npc.drop.DropFrequency; import core.game.node.item.ChanceItem; import core.game.node.item.Item; import core.game.node.item.WeightedChanceItem; +import java.util.*; +import java.util.concurrent.ThreadLocalRandom; +import java.util.stream.Collectors; +import java.util.stream.Stream; + /** * Represents a class used for random methods. * @author Vexia @@ -43,6 +44,20 @@ public class RandomFunction { return Math.min(a, b) + (n == 0 ? 0 : random(n)); } + /** + * Calculates the chance of succeeding at a skilling event + * @param low - Success chance at level 1 + * @param high - Success chance at level 99 + * @param level - Level required + * @return percent chance of success + */ + public static double getSkillSuccessChance(double low, double high, int level) { + // 99 & 98 numbers should *not* be adjusted for level cap > 99 + int value = (int)(Math.floor(low*( (99-level)/98.0 ) ) + Math.floor(high*((level-1)/98.0)) + 1); + return Math.min(Math.max(value / 256D, 0), 1) * 100.0; + } + + /** * Returns either the supplied integer, or -1 times the supplied integer. * @param value the value. @@ -82,6 +97,10 @@ public class RandomFunction { return RANDOM.nextInt(maxValue); } + public static final double randomDouble(double min, double max){ + return ThreadLocalRandom.current().nextDouble(min,max); + } + public static int nextInt(int val) { return random(val); diff --git a/Server/src/main/kotlin/rs09/game/content/global/WeightBasedTable.kt b/Server/src/main/kotlin/rs09/game/content/global/WeightBasedTable.kt new file mode 100644 index 000000000..36af62442 --- /dev/null +++ b/Server/src/main/kotlin/rs09/game/content/global/WeightBasedTable.kt @@ -0,0 +1,52 @@ +package rs09.game.content.global + +import core.game.node.entity.player.Player +import core.game.node.item.Item + +class WeightBasedTable : ArrayList() { + var totalWeight = 0.0 + val guaranteedItems = ArrayList(5) + + override fun add(element: WeightedItem): Boolean { + totalWeight += element.weight + return if(element.guaranteed) guaranteedItems.add(element) + else super.add(element) + } + + fun roll(player: Player): ArrayList{ + val items= ArrayList(3) + var tempWeight = totalWeight + items.addAll(guaranteedItems.map { it.getItem() }.toList()) + + if(player.inventory.isFull){ + return items + } + + if(isNotEmpty()) { + for (item in shuffled()) { + tempWeight -= item.weight + if (tempWeight <= 0) { + items.add(item.getItem()) + break + } + } + } + return items + } + + fun canRoll(player: Player): Boolean{ + val guaranteed = guaranteedItems.map { it.getItem() }.toTypedArray() + return (player.inventory.hasSpaceFor(*guaranteed) && guaranteed.isNotEmpty()) || !player.inventory.isFull + } + + companion object { + @JvmStatic + fun create(vararg items: WeightedItem): WeightBasedTable{ + val table = WeightBasedTable() + items.forEach { + table.add(it) + } + return table + } + } +} \ No newline at end of file diff --git a/Server/src/main/kotlin/rs09/game/content/global/WeightedItem.kt b/Server/src/main/kotlin/rs09/game/content/global/WeightedItem.kt new file mode 100644 index 000000000..c2c7cff3b --- /dev/null +++ b/Server/src/main/kotlin/rs09/game/content/global/WeightedItem.kt @@ -0,0 +1,10 @@ +package rs09.game.content.global + +import core.game.node.item.Item +import core.tools.RandomFunction + +class WeightedItem(var id: Int, var minAmt: Int, var maxAmt: Int, var weight: Double, var guaranteed: Boolean = false) { + fun getItem(): Item { + return Item(id,RandomFunction.random(minAmt,maxAmt)) + } +} \ No newline at end of file diff --git a/Server/src/main/kotlin/rs09/game/interaction/InteractionListeners.kt b/Server/src/main/kotlin/rs09/game/interaction/InteractionListeners.kt index 895531d3f..f9c4dd4cf 100644 --- a/Server/src/main/kotlin/rs09/game/interaction/InteractionListeners.kt +++ b/Server/src/main/kotlin/rs09/game/interaction/InteractionListeners.kt @@ -35,7 +35,7 @@ object InteractionListeners { @JvmStatic fun add(options: Array,type: Int,method: (Player, Node) -> Boolean){ for(opt in options){ - add(opt,type,method) + add(opt.toLowerCase(),type,method) } } diff --git a/Server/src/main/kotlin/rs09/game/node/entity/skill/thieving/Pickpockets.kt b/Server/src/main/kotlin/rs09/game/node/entity/skill/thieving/Pickpockets.kt new file mode 100644 index 000000000..825312e45 --- /dev/null +++ b/Server/src/main/kotlin/rs09/game/node/entity/skill/thieving/Pickpockets.kt @@ -0,0 +1,192 @@ +package rs09.game.node.entity.skill.thieving + +import core.game.node.entity.player.Player +import core.game.node.entity.skill.Skills +import core.tools.RandomFunction +import org.rs09.consts.Items +import org.rs09.consts.NPCs +import rs09.game.content.global.WeightBasedTable +import rs09.game.content.global.WeightedItem +import java.util.stream.IntStream + +enum class Pickpockets(val ids: IntArray, val requiredLevel: Int, val low: Double, val high: Double, val experience: Double, val stunDamageMin: Int, val stunDamageMax: Int, val stunTime: Int, val table: WeightBasedTable) { + MAN(intArrayOf(1, 2, 3, 4, 5, 6, 16, 24, 170, 3915), 1, 180.0, 240.0, 8.0, 1, 1,5, WeightBasedTable.create( + WeightedItem(Items.COINS_995,3,3,1.0,true) + )), + FARMER(intArrayOf(7, 1757, 1758), 10, 180.0, 240.0, 14.5, 1,1,5,WeightBasedTable.create( + WeightedItem(Items.COINS_995,9,9,1.0,true), + WeightedItem(Items.POTATO_SEED_5318,1,1,1.0,true) + )), + MALE_HAM_MEMBER(intArrayOf(1714), 20, 117.0, 240.0, 22.5, 1,3,4, WeightBasedTable.create( + WeightedItem(Items.COINS_995,1,21,5.5), + WeightedItem(Items.TINDERBOX_590,1,1,5.0), + WeightedItem(Items.LOGS_1511,1,1,7.0), + WeightedItem(Items.UNCUT_JADE_1627,1,1,2.5), + WeightedItem(Items.UNCUT_OPAL_1625,1,1,2.5), + WeightedItem(Items.RAW_ANCHOVIES_321,1,1,7.0), + WeightedItem(Items.RAW_CHICKEN_2138,1,1,3.5), + WeightedItem(Items.HAM_CLOAK_4304,1,1,1.0), + WeightedItem(Items.HAM_HOOD_4302,1,1,1.0), + WeightedItem(Items.HAM_LOGO_4306,1,1,1.0), + WeightedItem(Items.HAM_ROBE_4300,1,1,1.0), + WeightedItem(Items.BOOTS_4310,1,1,1.0), + WeightedItem(Items.GLOVES_4308,1,1,1.0), + WeightedItem(Items.BRONZE_PICKAXE_1265,1,1,5.0), + WeightedItem(Items.IRON_PICKAXE_1267,1,1,5.0), + WeightedItem(Items.STEEL_PICKAXE_1269,1,1,2.5), + WeightedItem(Items.GRIMY_GUAM_199,1,1,2.0), + WeightedItem(Items.GRIMY_HARRALANDER_205,1,1,2.0), + WeightedItem(Items.GRIMY_KWUARM_213,1,1,2.0), + WeightedItem(Items.GRIMY_MARRENTILL_201,1,1,1.5), + WeightedItem(Items.RUSTY_SWORD_686,1,1,3.5), + WeightedItem(Items.BROKEN_ARMOUR_698,1,1,3.5), + WeightedItem(Items.BROKEN_STAFF_689,1,1,3.2), + WeightedItem(Items.BROKEN_ARROW_687,1,1,3.1) + )), + FEMALE_HAM_MEMBER(intArrayOf(1715), 15, 135.0, 240.0, 18.5, 1,3,4, WeightBasedTable.create( + WeightedItem(Items.COINS_995,1,21,5.5), + WeightedItem(Items.TINDERBOX_590,1,1,5.0), + WeightedItem(Items.LOGS_1511,1,1,7.0), + WeightedItem(Items.UNCUT_JADE_1627,1,1,2.5), + WeightedItem(Items.UNCUT_OPAL_1625,1,1,2.5), + WeightedItem(Items.RAW_ANCHOVIES_321,1,1,7.0), + WeightedItem(Items.RAW_CHICKEN_2138,1,1,3.5), + WeightedItem(Items.HAM_CLOAK_4304,1,1,1.0), + WeightedItem(Items.HAM_HOOD_4302,1,1,1.0), + WeightedItem(Items.HAM_LOGO_4306,1,1,1.0), + WeightedItem(Items.HAM_ROBE_4300,1,1,1.0), + WeightedItem(Items.BOOTS_4310,1,1,1.0), + WeightedItem(Items.GLOVES_4308,1,1,1.0), + WeightedItem(Items.BRONZE_PICKAXE_1265,1,1,5.0), + WeightedItem(Items.IRON_PICKAXE_1267,1,1,5.0), + WeightedItem(Items.STEEL_PICKAXE_1269,1,1,2.5), + WeightedItem(Items.GRIMY_GUAM_199,1,1,2.0), + WeightedItem(Items.GRIMY_HARRALANDER_205,1,1,2.0), + WeightedItem(Items.GRIMY_KWUARM_213,1,1,2.0), + WeightedItem(Items.GRIMY_MARRENTILL_201,1,1,1.5), + WeightedItem(Items.RUSTY_SWORD_686,1,1,3.5), + WeightedItem(Items.BROKEN_ARMOUR_698,1,1,3.5), + WeightedItem(Items.BROKEN_STAFF_689,1,1,3.2), + WeightedItem(Items.BROKEN_ARROW_687,1,1,3.1) + )), + WARRIOR(intArrayOf(15, 18), 25, 84.0, 240.0, 26.0, 2, 2, 5, WeightBasedTable.create( + WeightedItem(Items.COINS_995,18,18,1.0,true) + )), + ROGUE(intArrayOf(187, 2267, 2268, 2269, 8122), 32, 74.0, 240.0,35.5, 2, 2, 5, WeightBasedTable.create( + WeightedItem(Items.COINS_995,25,40,5.0,true), + WeightedItem(Items.JUG_OF_WINE_1993,1,1,6.0), + WeightedItem(Items.AIR_RUNE_556,8,8,8.0), + WeightedItem(Items.LOCKPICK_1523,1,1,5.0), + WeightedItem(Items.IRON_DAGGERP_1219,1,1,1.0) + )), + CAVE_GOBLIN(IntStream.rangeClosed(5752, 5768).toArray(), 36, 72.0, 240.0, 40.0, 1,1,5, WeightBasedTable.create( + WeightedItem(Items.COINS_995,30,30,6.5), + WeightedItem(Items.OIL_LAMP_4522,1,1,0.5), + WeightedItem(Items.BULLSEYE_LANTERN_4544,1,1,0.5), + WeightedItem(Items.UNLIT_TORCH_596,1,1,0.5), + WeightedItem(Items.TINDERBOX_590,1,1,0.5), + WeightedItem(Items.SWAMP_TAR_1939,1,1,0.5), + WeightedItem(Items.IRON_ORE_441,1,4,0.25) + )), + MASTER_FARMER(intArrayOf(2234, 2235, NPCs.MARTIN_THE_MASTER_GARDENER_3299), 38, 90.0, 240.0, 43.0, 3, 3, 5, WeightBasedTable.create( + WeightedItem(Items.POTATO_SEED_5318,1,4,100.0), + WeightedItem(Items.HAMMERSTONE_SEED_5307,1,9,100.0), + WeightedItem(Items.ASGARNIAN_SEED_5308,1,6,95.0), + WeightedItem(Items.JUTE_SEED_5306,1,9,93.0), + WeightedItem(Items.YANILLIAN_SEED_5309,1,6,86.0), + WeightedItem(Items.KRANDORIAN_SEED_5310,1,6,80.0), + WeightedItem(Items.WILDBLOOD_SEED_5311,1,3,76.0), + WeightedItem(Items.MARIGOLD_SEED_5096,1,1,93.0), + WeightedItem(Items.NASTURTIUM_SEED_5098,1,1,90.0), + WeightedItem(Items.ROSEMARY_SEED_5097,1,1,78.0), + WeightedItem(Items.WOAD_SEED_5099,1,1,75.0), + WeightedItem(Items.LIMPWURT_SEED_5100,1,1,70.0), + WeightedItem(Items.REDBERRY_SEED_5101,1,1,50.0), + WeightedItem(Items.CADAVABERRY_SEED_5102,1,1,50.0), + WeightedItem(Items.DWELLBERRY_SEED_5103,1,1,50.0), + WeightedItem(Items.JANGERBERRY_SEED_5104,1,1,50.0), + WeightedItem(Items.WHITEBERRY_SEED_5105,1,1,50.0), + WeightedItem(Items.GUAM_SEED_5291,1,1,30.0), + WeightedItem(Items.MARRENTILL_SEED_5292,1,1,30.0), + WeightedItem(Items.TARROMIN_SEED_5293,1,1,30.0), + WeightedItem(Items.HARRALANDER_SEED_5294,1,1,30.0), + WeightedItem(Items.RANARR_SEED_5295,1,1,10.0), + WeightedItem(Items.TOADFLAX_SEED_5296,1,1,10.0), + WeightedItem(Items.IRIT_SEED_5297,1,1,10.0), + WeightedItem(Items.AVANTOE_SEED_5298,1,1,5.0), + WeightedItem(Items.KWUARM_SEED_5299,1,1,5.0), + WeightedItem(Items.SNAPDRAGON_SEED_5300,1,1,3.0), + WeightedItem(Items.CADANTINE_SEED_5301,1,1,3.0), + WeightedItem(Items.LANTADYME_SEED_5302,1,1,3.0), + WeightedItem(Items.DWARF_WEED_SEED_5303,1,1,3.0), + WeightedItem(Items.TORSTOL_SEED_5304,1,1,3.0) + )), + GUARD(intArrayOf(9, 32, 206, 296, 297, 298, 299, 344, 345, 346, 368, 678, 812, 9, 32, 296, 297, 298, 299, 2699, 2700, 2701, 2702, 2703, 3228, 3229, 3230, 3231, 3232, 3233, 3241, 3407, 3408, 4307, 4308, 4309, 4310, 4311, 5919, 5920), 40, 50.0, 240.0, 46.5, 2,2,5, WeightBasedTable.create( + WeightedItem(Items.COINS_995,30,30,1.0,true) + )), + FREMENNIK_CITIZEN(intArrayOf(2462), 45, 65.0, 240.0, 65.0, 2, 2, 5, WeightBasedTable.create( + WeightedItem(Items.COINS_995,40,40,1.0,true) + )), + BEARDED_BANDIT(intArrayOf(1880, 1881, 6174), 45, 50.0, 240.0, 65.0, 5,5,5, WeightBasedTable.create( + WeightedItem(Items.ANTIPOISON4_2446,1,1,1.0), + WeightedItem(Items.LOCKPICK_1523,1,1,2.0), + WeightedItem(Items.COINS_995,1,1,4.0) + )), + DESERT_BANDIT(intArrayOf(1926, 1921), 53, 50.0, 240.0, 79.5, 3,3,5, WeightBasedTable.create( + WeightedItem(Items.COINS_995,50,1,3.0), + WeightedItem(Items.ANTIPOISON4_2446,1,1,1.0), + WeightedItem(Items.LOCKPICK_1523,1,1,1.0) + )), + KNIGHT_OF_ADROUGNE(intArrayOf(23, 26), 55, 50.0, 240.0, 84.3, 3,3,5, WeightBasedTable.create( + WeightedItem(Items.COINS_995,50,50,1.0,true) + )), + YANILLE_WATCHMAN(intArrayOf(34), 65, 137.5, 50.0, 240.0, 3,3,5, WeightBasedTable.create( + WeightedItem(Items.COINS_995,60,60,1.0,true), + WeightedItem(Items.BREAD_2309,1,1,1.0,true) + )), + MENAPHITE_THUG(intArrayOf(1905), 65, 50.0, 240.0, 137.5, 5,5,5, WeightBasedTable.create( + WeightedItem(Items.COINS_995,60,60,1.0,true) + )), + PALADIN(intArrayOf(20, 2256), 70, 50.0, 150.0,151.75, 3,3,5, WeightBasedTable.create( + WeightedItem(Items.COINS_995,80,80,1.0,true), + WeightedItem(Items.CHAOS_RUNE_562,2,2,1.0,true) + )), + GNOME(intArrayOf(66, 67, 68, 168, 169, 2249, 2250, 2251, 2371, 2649, 2650, 6002, 6004), 75, 8.0, 120.0, 198.5, 1,1,5, WeightBasedTable.create( + WeightedItem(Items.COINS_995,300,300,2.5), + WeightedItem(Items.EARTH_RUNE_557,1,1,3.5), + WeightedItem(Items.GOLD_ORE_445,1,1,1.0), + WeightedItem(Items.FIRE_ORB_569,1,1,5.0), + WeightedItem(Items.SWAMP_TOAD_2150,1,1,8.0), + WeightedItem(Items.KING_WORM_2162,1,1,9.0) + )), + HERO(intArrayOf(21), 80, 6.0, 100.0,273.3, 6,6,4, WeightBasedTable.create( + WeightedItem(Items.COINS_995,200,300,1.5), + WeightedItem(Items.DEATH_RUNE_560,2,2,1.0), + WeightedItem(Items.BLOOD_RUNE_565,1,1,0.5), + WeightedItem(Items.FIRE_ORB_569,1,1,2.5), + WeightedItem(Items.DIAMOND_1601,1,1,2.0), + WeightedItem(Items.GOLD_ORE_444,1,1,1.5), + WeightedItem(Items.JUG_OF_WINE_1993,1,1,3.0) + )); + + + companion object { + val idMap = HashMap(values().size * 5) + + init { + values().forEach { + it.ids.forEach { id -> idMap[id] = it } + } + } + + @JvmStatic + fun forID(id: Int): Pickpockets? { + return idMap[id] + } + } + + + fun getSuccessChance(player: Player): Double{ + return RandomFunction.getSkillSuccessChance(low,high,player.skills.getLevel(Skills.THIEVING)) + } +} \ No newline at end of file diff --git a/Server/src/main/kotlin/rs09/game/node/entity/skill/thieving/ThievingListeners.kt b/Server/src/main/kotlin/rs09/game/node/entity/skill/thieving/ThievingListeners.kt new file mode 100644 index 000000000..935cc15dd --- /dev/null +++ b/Server/src/main/kotlin/rs09/game/node/entity/skill/thieving/ThievingListeners.kt @@ -0,0 +1,92 @@ +package rs09.game.node.entity.skill.thieving + +import core.game.node.entity.combat.ImpactHandler +import core.game.node.entity.impl.Animator +import core.game.node.entity.player.Player +import core.game.node.entity.player.link.audio.Audio +import core.game.node.entity.skill.Skills +import core.game.node.entity.state.EntityState +import core.game.world.update.flag.context.Animation +import core.tools.RandomFunction +import org.rs09.consts.Items +import rs09.game.interaction.InteractionListener +import rs09.tools.secondsToTicks + +class ThievingListeners : InteractionListener() { + + private val PICKPOCKET_ANIM = Animation(881,Animator.Priority.HIGH) + private val NPC_ANIM = Animation(422) + private val STUN_ANIMATION = Animation(424,Animator.Priority.VERY_HIGH) + private val SOUND = Audio(2727, 1, 0) + + override fun defineListeners() { + + on(NPC,"pickpocket","pick-pocket"){player, node -> + val pickpocketData = Pickpockets.forID(node.id) ?: return@on false + var successMod = 0 + + if(player.inCombat()){ + player.sendMessage("You can't pickpocket while in combat.") + return@on true + } + + if(player.skills.getLevel(Skills.THIEVING) < pickpocketData.requiredLevel){ + player.sendMessage("You need a Thieving level of ${pickpocketData.requiredLevel} to do that.") + return@on true + } + + if(!pickpocketData.table.canRoll(player)){ + player.sendMessage("You don't have enough inventory space to do that.") + return@on true + } + + if(pickpocketData == Pickpockets.FEMALE_HAM_MEMBER || pickpocketData == Pickpockets.MALE_HAM_MEMBER){ + successMod += getHAMItemCount(player) + } + + if(player.equipment.contains(Items.GLOVES_OF_SILENCE_10075,1)){ + successMod += 3 + } + + player.animator.animate(PICKPOCKET_ANIM) + val chance = RandomFunction.randomDouble(1.0,100.0) + val failTreshold = pickpocketData.getSuccessChance(player) + successMod + + if(chance > failTreshold){ + player.stateManager.set(EntityState.STUNNED, secondsToTicks(pickpocketData.stunTime)) + node.asNpc().face(player) + node.asNpc().animator.animate(NPC_ANIM) + player.animator.animate(STUN_ANIMATION) + player.audioManager.send(SOUND) + player.lock(secondsToTicks(pickpocketData.stunTime)) + player.impactHandler.manualHit(node.asNpc(),RandomFunction.random(pickpocketData.stunDamageMin,pickpocketData.stunDamageMax),ImpactHandler.HitsplatType.NORMAL) + node.asNpc().face(null) + } else { + player.lock(2) + pickpocketData.table.roll(player).forEach { player.inventory.add(it) } + player.skills.addExperience(Skills.THIEVING,pickpocketData.experience) + } + + return@on true + } + + } + + fun getHAMItemCount(player: Player): Int{ + var counter = 0 + for(item in player.equipment.toArray()){ + item ?: continue + counter += when(item.id){ + Items.HAM_LOGO_4306 -> 1 + Items.HAM_ROBE_4300 -> 1 + Items.HAM_HOOD_4302 -> 1 + Items.HAM_CLOAK_4304 -> 1 + Items.BOOTS_4310 -> 1 + Items.GLOVES_4308 -> 1 + else -> 0 + } + } + return counter + } + +} \ No newline at end of file