Merge branch 'add-pitfall-traps' into 'master'

Implement pitfall trapping for larupias, graahks, and kyatts.

See merge request 2009scape/2009scape!263
This commit is contained in:
Ceikry 2021-09-16 19:59:48 +00:00
commit 0ed25421f8
6 changed files with 335 additions and 19 deletions

View file

@ -7209,7 +7209,7 @@
},
{
"npc_id": "5105",
"loc_data": "{2780,3017,0,0,0}-{2766,3013,0,0,0}-{2785,3006,0,0,0}-{2775,2996,0,0,0}"
"loc_data": "{2780,3017,0,1,0}-{2766,3013,0,1,0}-{2785,3006,0,1,0}-{2775,2996,0,1,0}"
},
{
"npc_id": "5109",
@ -10791,4 +10791,4 @@
"npc_id": "1052",
"loc_data": "{3420,3442,0,1,0}-{3429,3436,0,1,0}-{3439,3437,0,1,0}-{3448,3444,0,1,0}-{3458,3433,0,1,0}-{3459,3419,0,1,0}-{3449,3420,0,1,0}-{3441,3421,0,1,0}-{3427,3421,0,1,0}-{3414,3422,0,1,0}-{3411,3423,0,1,0}-{3415,3414,0,1,0}-{3410,3409,0,1,0}-{3422,3405,0,1,0}-{3431,3402,0,1,0}-{3444,3405,0,1,0}-{3453,3397,0,1,0}-{3467,3402,0,1,0}-{3473,3395,0,1,0}-{3470,3385,0,1,0}-{3463,3379,0,1,0}-{3451,3380,0,1,0}-{3445,3374,0,1,0}-{3438,3365,0,1,0}-{3429,3367,0,1,0}-{3417,3367,0,1,0}-{3413,3362,0,1,0}-{3417,3358,0,1,0}-{3426,3352,0,1,0}-{3435,3349,0,1,0}-{3442,3353,0,1,0}-{3448,3358,0,1,0}-{3457,3355,0,1,0}-{3461,3348,0,1,0}-{3457,3343,0,1,0}-{3471,3344,0,1,0}-{3426,3338,0,1,0}-{3423,3335,0,1,0}"
}
]
]

View file

@ -1,6 +1,7 @@
package core.game.node.entity.impl;
import core.game.node.entity.Entity;
import core.game.node.entity.player.Player;
import core.game.system.task.Pulse;
import rs09.game.world.GameWorld;
import core.game.world.map.Direction;
@ -307,7 +308,9 @@ public class ForceMovement extends Pulse {
int ticks = 1 + commenceSpeed + pathSpeed;
entity.getImpactHandler().setDisabledTicks(ticks);
entity.getUpdateMasks().register(new ForceMovementFlag(this));
entity.getWalkingQueue().updateRegion(destination, false);
if(entity instanceof Player) {
entity.getWalkingQueue().updateRegion(destination, false);
}
super.start();
}
@ -476,4 +479,4 @@ public class ForceMovement extends Pulse {
public void setEndAnimation(Animation endAnimation) {
this.endAnimation = endAnimation;
}
}
}

View file

@ -129,6 +129,9 @@ class CombatPulse(
if (handler == null) {
handler = entity.getSwingHandler(true)
}
if (!entity.isAttackable(v, handler!!.type)) {
return true
}
if (!swing(entity, victim, handler)) {
temporaryHandler = null
updateStyle()
@ -461,4 +464,4 @@ class CombatPulse(
}
}
}
}
}

View file

@ -243,10 +243,6 @@ abstract class CombatSwingHandler(var type: CombatStyle?) {
if (el.x >= vl.x && el.x < evl.x && el.y >= vl.y && el.y < evl.y || el.z != vl.z) {
return InteractionType.NO_INTERACT
}
if (!victim.isAttackable(entity, type)) {
entity.properties.combatPulse.stop()
return InteractionType.NO_INTERACT
}
return InteractionType.STILL_INTERACT
}

View file

@ -1,10 +0,0 @@
/*
package core.game.node.entity.skill.hunter
private const val VARP = 926
class PolarKebbitHunting {
private enum class TrailType{
va
}
private enum class Trail(val offset: Int, var )
}*/

View file

@ -0,0 +1,324 @@
import java.util.concurrent.TimeUnit;
import api.ContentAPI
import core.cache.def.impl.NPCDefinition
import core.cache.def.impl.SceneryDefinition
import core.game.interaction.OptionHandler
import core.game.node.Node
import core.game.node.entity.Entity
import core.game.node.entity.combat.CombatStyle
import core.game.node.entity.impl.Animator.Priority;
import core.game.node.entity.impl.ForceMovement
import core.game.node.entity.npc.AbstractNPC
import core.game.node.entity.player.Player
import core.game.node.entity.skill.Skills
import core.game.node.item.Item
import core.game.node.scenery.Scenery
import core.game.system.task.Pulse
import core.game.world.map.Direction
import core.game.world.map.Location
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 core.tools.RandomFunction
import org.rs09.consts.Items
import org.rs09.consts.NPCs
import rs09.game.interaction.InteractionListener
import rs09.game.world.GameWorld
/*@Initializable
class HunterPitfall : OptionHandler() {
//19227,
val graahkPitIds = intArrayOf(19227)//, 19268,19267,19266,19264,19265)
val GRAAHK_ID = 5105
val hunterReq = 41
override fun handle(player: Player?, node: Node?, option: String?): Boolean {
node ?: return true
player ?: return true
player.sendMessage("Hello from HunterPitfall: ${option}")
when(option) {
"tease" -> {
(node as Entity).attack(player)
}
}
return true
}
override fun newInstance(arg: Any?): Plugin<Any> {
for(graahkPit in graahkPitIds) {
SceneryDefinition.forId(graahkPit).handlers["option:trap"] = this
}
//NPCDefinition.forId(GRAAHK_ID).handlers["option:tease"] = this
return this
}
}*/
val LARUPIA_IDS: IntArray = intArrayOf(NPCs.SPINED_LARUPIA_5104)
val GRAAHK_IDS: IntArray = intArrayOf(NPCs.HORNED_GRAAHK_5105, NPCs.HORNED_GRAAHK_5106, NPCs.HORNED_GRAAHK_5107, NPCs.HORNED_GRAAHK_5108)
val KYATT_IDS: IntArray = intArrayOf(NPCs.SABRE_TOOTHED_KYATT_5103, NPCs.SABRE_TOOTHED_KYATT_7497)
val BEAST_IDS: IntArray = intArrayOf(*LARUPIA_IDS, *GRAAHK_IDS, *KYATT_IDS)
val HUNTER_REQS = hashMapOf(
"Spined larupia" to 31,
"Horned graahk" to 41,
"Sabre-toothed kyatt" to 55,
)
//val pitVarpOffsets = hashMapOf( 19264 to 3, 19265 to 6, 19266 to 9, 19267 to 12, 19268 to 15,)
data class Pit(val varpId: Int, val varpOffset: Int, val horizontal: Boolean)
val pitVarps = hashMapOf(
// Larupia pits (the duplicate 24 is likely authentic)
Location.create(2565,2888) to Pit(917, 27, true),
Location.create(2556,2893) to Pit(917, 18, false),
Location.create(2552,2904) to Pit(917, 24, true),
Location.create(2543,2908) to Pit(917, 21, false),
Location.create(2538,2899) to Pit(917, 24, true),
// Kyatt pits
Location.create(2700,3795) to Pit(917, 0, true),
Location.create(2700,3785) to Pit(917, 3, false),
Location.create(2706,3789) to Pit(917, 6, false),
Location.create(2730,3791) to Pit(917, 9, true),
Location.create(2737,3784) to Pit(917, 12, true),
Location.create(2730,3780) to Pit(917, 15, false),
// Graahk pits
Location.create(2766,3010) to Pit(918, 3, false),
Location.create(2762,3005) to Pit(918, 6, false),
Location.create(2771,3004) to Pit(918, 9, true),
Location.create(2777,3001) to Pit(918, 12, false),
Location.create(2784,3001) to Pit(918, 15, true),
)
/*val pitJumpSpots = hashMapOf(
Location.create(2766,3010) to hashMapOf(
Location.create(2766,3009) to Direction.NORTH,
Location.create(2767,3009) to Direction.NORTH,
Location.create(2766,3012) to Direction.SOUTH,
Location.create(2767,3012) to Direction.SOUTH,
),
Location.create(2762,3005) to hashMapOf(
Location.create(2762,3004) to Direction.NORTH,
Location.create(2763,3004) to Direction.NORTH,
Location.create(2762,3007) to Direction.SOUTH,
Location.create(2763,3007) to Direction.SOUTH,
),
Location.create(2771,3004) to hashMapOf(
Location.create(2770,3004) to Direction.EAST,
Location.create(2770,3005) to Direction.EAST,
Location.create(2773,3004) to Direction.WEST,
Location.create(2773,3005) to Direction.WEST,
),
Location.create(2777,3001) to hashMapOf(
Location.create(2777,3000) to Direction.NORTH,
Location.create(2778,3000) to Direction.NORTH,
Location.create(2777,3003) to Direction.SOUTH,
Location.create(2778,3003) to Direction.SOUTH,
),
Location.create(2784,3001) to hashMapOf(
Location.create(2783,3002) to Direction.EAST,
Location.create(2783,3001) to Direction.EAST,
Location.create(2786,3002) to Direction.WEST,
Location.create(2786,3001) to Direction.WEST,
),
)*/
fun pitJumpSpots(loc: Location): HashMap<Location, Direction>? {
val pit = pitVarps[loc] ?: return null
if(pit.horizontal) {
return hashMapOf(
loc.transform(-1, 0, 0) to Direction.EAST,
loc.transform(-1, 1, 0) to Direction.EAST,
loc.transform(2, 0, 0) to Direction.WEST,
loc.transform(2, 1, 0) to Direction.WEST,
)
} else {
return hashMapOf(
loc.transform(0, -1, 0) to Direction.NORTH,
loc.transform(1, -1, 0) to Direction.NORTH,
loc.transform(0, 2, 0) to Direction.SOUTH,
loc.transform(1, 2, 0) to Direction.SOUTH,
)
}
}
val KNIFE = Item(Items.KNIFE_946)
val TEASING_STICK = Item(Items.TEASING_STICK_10029)
val LOGS = Item(Items.LOGS_1511)
val PIT = 19227
val SPIKED_PIT = 19228
val GRAAHK_PIT = 19231
val LARUPIA_PIT = 19232
val KYATT_PIT = 19233
class PitfallListeners : InteractionListener() {
override fun defineListeners() {
setDest(SCENERY, intArrayOf(PIT, SPIKED_PIT, LARUPIA_PIT, GRAAHK_PIT, KYATT_PIT), "trap", "jump", "dismantle") { player, node ->
val pit = node as Scenery
val src = player.getLocation()
var dst = pit.getLocation()
val locs = pitJumpSpots(dst)
if(locs != null) {
for(loc in locs.keys) {
if(src.getDistance(loc) <= src.getDistance(dst)) {
dst = loc
}
}
} else {
if(player is Player) {
player.sendMessage("Error: Unimplemented pit at ${pit.location}")
}
}
return@setDest dst
}
on(PIT, SCENERY, "trap") { player, node ->
val pit = node as Scenery;
// TODO: check hunter level, remove logs
if(player.skills.getLevel(Skills.HUNTER) < 31) {
player.sendMessage("You need a hunter level of 31 to set a pitfall trap.")
return@on true
}
val maxTraps = player.hunterManager.maximumTraps
if(player.getAttribute("pitfall:count", 0) >= maxTraps) {
player.sendMessage("You can't set up more than ${maxTraps} pitfall traps at your hunter level.")
return@on true
}
player.incrementAttribute("pitfall:count", 1)
if(!player.inventory.containsItem(KNIFE) || !player.inventory.remove(LOGS)) {
player.sendMessage("You need some logs and a knife to set a pitfall trap.")
return@on true
}
player.setAttribute("pitfall:timestamp:${pit.location.x}:${pit.location.y}", System.currentTimeMillis())
setPitState(player, pit.location, 1)
val collapsePulse = object : Pulse(201, player) {
override fun pulse(): Boolean {
val oldTime = player.getAttribute("pitfall:timestamp:${pit.location.x}:${pit.location.y}", System.currentTimeMillis())
if(System.currentTimeMillis() - oldTime >= TimeUnit.MINUTES.toMillis(2)) {
player.sendMessage("Your pitfall trap has collapsed.")
setPitState(player, pit.location, 0)
player.incrementAttribute("pitfall:count", -1)
}
return true
}
}
GameWorld.Pulser.submit(collapsePulse)
return@on true
}
on(SPIKED_PIT, SCENERY, "jump") { player, node ->
val pit = node as Scenery;
val src = player.getLocation()
val dir = pitJumpSpots(pit.getLocation())!![src]
if(dir != null) {
val dst = src.transform(dir, 3)
ForceMovement.run(player, src, dst, ForceMovement.WALK_ANIMATION, Animation(1603), dir, 16);
val pitfall_npc: Entity? = player.getAttribute("pitfall_npc", null)
if(pitfall_npc != null && pitfall_npc.getLocation().getDistance(src) < 3.0) {
val last_pit_loc: Location? = pitfall_npc.getAttribute("last_pit_loc", null)
if(last_pit_loc == pit.location) {
player.sendMessage("The ${pitfall_npc.name.toLowerCase()} won't jump the same pit twice in a row.")
return@on true
}
// TODO: what are the actual probabilities of a graahk jumping over a pit?
val chance = RandomFunction.getSkillSuccessChance(50.0, 100.0, player.skills.getLevel(Skills.HUNTER))
if(RandomFunction.random(0.0, 100.0) < chance) {
//ForceMovement.run(pitfall_npc, pitfall_npc.getLocation(), pit.getLocation(), ForceMovement.WALK_ANIMATION, Animation(ANIM), dir, 8);
//pitfall_npc.setLocation(pit.getLocation());
//pitfall_npc.walkingQueue.addPath(pit.location.x, pit.location.y);
ContentAPI.teleport(pitfall_npc, pit.location)
pitfall_npc.startDeath(null)
player.removeAttribute("pitfall:timestamp:${pit.location.x}:${pit.location.y}")
player.incrementAttribute("pitfall:count", -1)
setPitState(player, pit.location, 3)
//pitfall_npc.animate(Animation(5234))
} else {
//ForceMovement.run(pitfall_npc, pitfall_npc.getLocation(), dst, ForceMovement.WALK_ANIMATION, Animation(ANIM), dir, 8);
//pitfall_npc.walkingQueue.addPath(npcdst.x, npcdst.y)
val npcdst = dst.transform(dir, if(dir == Direction.SOUTH || dir == Direction.WEST) 1 else 0)
ContentAPI.teleport(pitfall_npc, npcdst)
pitfall_npc.animate(Animation(5232, Priority.HIGH))
pitfall_npc.attack(player)
pitfall_npc.setAttribute("last_pit_loc", pit.location)
}
}
}
return@on true
}
on(SPIKED_PIT, SCENERY, "dismantle") { player, node ->
val pit = node as Scenery;
player.removeAttribute("pitfall:timestamp:${pit.location.x}:${pit.location.y}")
player.incrementAttribute("pitfall:count", -1)
setPitState(player, pit.location, 0)
return@on true
}
on(LARUPIA_PIT, SCENERY, "dismantle") { player, node ->
lootCorpse(player, node as Scenery, 180.0, Items.LARUPIA_FUR_10095, Items.TATTY_LARUPIA_FUR_10093)
return@on true
}
on(GRAAHK_PIT, SCENERY, "dismantle") { player, node ->
lootCorpse(player, node as Scenery, 240.0, Items.GRAAHK_FUR_10099, Items.TATTY_GRAAHK_FUR_10097)
return@on true
}
on(KYATT_PIT, SCENERY, "dismantle") { player, node ->
lootCorpse(player, node as Scenery, 300.0, Items.KYATT_FUR_10103, Items.TATTY_KYATT_FUR_10101)
return@on true
}
on(BEAST_IDS, NPC, "tease") { player, node ->
val entity = node as Entity
val hunterReq = HUNTER_REQS[entity.name]!!
if(player.skills.getLevel(Skills.HUNTER) < hunterReq) {
player.sendMessage("You need a hunter level of ${hunterReq} to hunt ${entity.name.toLowerCase()}s.")
return@on true
}
if(!player.inventory.containsItem(TEASING_STICK)) {
player.sendMessage("You need a teasing stick to hunt ${entity.name.toLowerCase()}s.")
return@on true
}
entity.attack(player)
player.setAttribute("pitfall_npc", entity)
return@on true
}
}
fun lootCorpse(player: Player, pit: Scenery, xp: Double, goodFur: Int, badFur: Int) {
if(player.inventory.freeSlots() < 2) {
player.sendMessage("You don't have enough inventory space. You need 2 more free slots.");
return
}
setPitState(player, pit.location, 0)
player.getSkills().addExperience(Skills.HUNTER, xp, true);
player.inventory.add(Item(Items.BIG_BONES_532))
// TODO: what's the actual probability of tatty vs perfect fur?
val chance = RandomFunction.getSkillSuccessChance(50.0, 100.0, player.skills.getLevel(Skills.HUNTER))
if(RandomFunction.random(0.0, 100.0) < chance) {
player.inventory.add(Item(goodFur))
} else {
player.inventory.add(Item(badFur))
}
}
fun setPitState(player: Player, loc: Location, state: Int) {
val pit = pitVarps[loc]!!
player.varpManager.get(pit.varpId).setVarbit(pit.varpOffset, state).send(player)
}
}
@Initializable
class PitfallNPC : AbstractNPC {
constructor() : super(NPCs.HORNED_GRAAHK_5105, null, true) {}
private constructor(id: Int, location: Location) : super(id, location) {}
override fun construct(id: Int, location: Location, vararg objects: Any?): AbstractNPC {
return PitfallNPC(id, location)
}
init {
walkRadius = 22
}
override fun getIds(): IntArray {
return BEAST_IDS
}
override fun isAttackable(entity: Entity, style: CombatStyle): Boolean {
return entity is Player
}
}