Pickpocket rewrite + 100% accurate success rate formula (Just have to plug in low and high values from a document I'll push later.)

This commit is contained in:
Ceikry 2021-03-16 02:45:22 -05:00
parent 3ebd9c2614
commit 8a6c8e899b
8 changed files with 370 additions and 217 deletions

View file

@ -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<NPC> {
/**
* 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<Item> 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;
}
}

View file

@ -1,15 +1,12 @@
package core.game.node.entity.skill.thieving; package core.game.node.entity.skill.thieving;
import core.cache.def.impl.NPCDefinition;
import core.cache.def.impl.ObjectDefinition; import core.cache.def.impl.ObjectDefinition;
import core.game.interaction.OptionHandler; import core.game.interaction.OptionHandler;
import core.game.node.Node; import core.game.node.Node;
import core.game.node.entity.npc.NPC;
import core.game.node.entity.player.Player; import core.game.node.entity.player.Player;
import core.game.node.object.GameObject; import core.game.node.object.GameObject;
import core.plugin.Initializable; import core.plugin.Initializable;
import core.plugin.Plugin; import core.plugin.Plugin;
import rs09.game.node.entity.skill.thieving.Pickpocket;
/** /**
* Represents the plugin used to handle thieving options. * Represents the plugin used to handle thieving options.
@ -21,8 +18,6 @@ public class ThievingOptionPlugin extends OptionHandler {
@Override @Override
public Plugin<Object> newInstance(Object arg) throws Throwable { public Plugin<Object> newInstance(Object arg) throws Throwable {
NPCDefinition.setOptionHandler("pick-pocket", this);
NPCDefinition.setOptionHandler("pickpocket", this);
ObjectDefinition.setOptionHandler("steal-from", this); ObjectDefinition.setOptionHandler("steal-from", this);
ObjectDefinition.setOptionHandler("steal from", this); ObjectDefinition.setOptionHandler("steal from", this);
return this; return this;
@ -31,10 +26,6 @@ public class ThievingOptionPlugin extends OptionHandler {
@Override @Override
public boolean handle(Player player, Node node, String option) { public boolean handle(Player player, Node node, String option) {
switch (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":
case "steal from": case "steal from":
player.getPulseManager().run(new StallThiefPulse(player, (GameObject) node, Stall.forObject((GameObject) node))); player.getPulseManager().run(new StallThiefPulse(player, (GameObject) node, Stall.forObject((GameObject) node)));

View file

@ -1,14 +1,15 @@
package core.tools; 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.entity.npc.drop.DropFrequency;
import core.game.node.item.ChanceItem; import core.game.node.item.ChanceItem;
import core.game.node.item.Item; import core.game.node.item.Item;
import core.game.node.item.WeightedChanceItem; 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. * Represents a class used for random methods.
* @author Vexia * @author Vexia
@ -43,6 +44,20 @@ public class RandomFunction {
return Math.min(a, b) + (n == 0 ? 0 : random(n)); 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. * Returns either the supplied integer, or -1 times the supplied integer.
* @param value the value. * @param value the value.
@ -82,6 +97,10 @@ public class RandomFunction {
return RANDOM.nextInt(maxValue); 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) public static int nextInt(int val)
{ {
return random(val); return random(val);

View file

@ -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<WeightedItem>() {
var totalWeight = 0.0
val guaranteedItems = ArrayList<WeightedItem>(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<Item>{
val items= ArrayList<Item>(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
}
}
}

View file

@ -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))
}
}

View file

@ -35,7 +35,7 @@ object InteractionListeners {
@JvmStatic @JvmStatic
fun add(options: Array<out String>,type: Int,method: (Player, Node) -> Boolean){ fun add(options: Array<out String>,type: Int,method: (Player, Node) -> Boolean){
for(opt in options){ for(opt in options){
add(opt,type,method) add(opt.toLowerCase(),type,method)
} }
} }

View file

@ -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<Int,Pickpockets>(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))
}
}

View file

@ -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
}
}