Add Isafdar navigation obstacles and Lletya content - #1237
Conversation
Isafdar: - Dense forest passages (56 Agility for the two Elf Camp entrances), lining up with the passage centre before squeezing through - Tripwire and sticks traps with agility roll, fail damage and messages - Leaf traps: jump over, fall into the pit below on failure or when walking onto the trap tile, climb out via the protruding rocks - Log balances (45 Agility) at the hunting grounds and Arandar, interruptible while approaching, walked at walk speed while crossing - Elven Overpass cliffside rocks (59/68/85 Agility) - Arandar heavy gates, Underground Pass door pass-through and Poison Waste cave entrances, exits and sewer ladders Lletya: - Teleport crystal Activate with charge degradation - Eluned recharges teleport crystals (750 coins, decreasing to 150) - Islwyn sells and attunes crystal bows and shields (900k, decreasing) - Shopkeeper and resident dialogues - Elf musician locked facing south Fixes: - New crystal bow no longer dropped at the target as ammo when fired - objectApproach supports arrive = false to skip waiting for the pending route, matching objectOperate
|
I can convert Regicide, Roving Elves and MEP1 to compliment this at some point. |
There was a problem hiding this comment.
Dense forests:
- anims don't seem to be correct
- if you stand to the side of one and enter you can clip yourself into the forest on the other side
- Anim finishes too early and force walk slides the last tick
Sticks:
- Can move diagonal
- Anim finishing too early and slides at end
- You can step onto them without setting them off
Tripwire:
- A little janky with teleports/anims but generally okay
Leaf traps:
- Also diagonal movement issue
- Doesn't trigger when walking over without interaction
- Also slide at end of movement
Log balance:
- Run orb disables when walking across (see other log balances)
- Dense forest: correct animations per passage type (839 climb-over, 749 double squeeze, 1237 side squeeze) with sounds, 94cs move with 30cs start delay to stop the end-of-move slide, and a collision-aware line-up walk so entering from the side can no longer clip through - Tripwire: step-over is now a lined-up single-tile exact move over the wire; failing fires the flanking arrow launcher animations and projectiles - Sticks trap: crossing walks along the trap at walk speed; failing springs the trap and flings the player off in the direction fixed by the trap's rotation, and walking onto the trap without interacting now sets it off - Leaf traps: trigger areas cover the full 3x3 leaf patches so walking (or cutting diagonally) over any leaf tile drops the player into the pit with the stumble and getup animations; jumps line up with the trap first and use the proper timings; centre pit tile corrected - Log balance: no longer toggles the run setting while crossing (run orb stays lit) and plays the balance sound each step
|
Addressed the review in bf7c86f, using a packet capture from OSRS as reference: Dense forests: animations were wrong for most ids: 3937/3999 use 839 (climb-over), 3938/3998 use 749 (double squeeze, cleared after the move), 3939 uses 1237 (side squeeze), each with their forest sound. The exact move is now 94cs with a 30cs start delay which removes the early anim finish/slide. The line-up walk is now collision-aware ( Tripwire: reworked to the captured shape: walk onto the near wire tile, single-tile exact move over the wire with anim 1236, then step off. Failing now animates the flanking arrow launchers (3920) and fires arrow projectiles at the player. Sticks: crossing walks straight along the trap at walk speed (lined up first, so no diagonals). Failing springs the trap (obj anim + stumble-back 846) and flings the player off in the fixed direction given by the trap's rotation. Walking onto the trap without interacting now sets it off the same way, with a regression test. Leaf traps: trigger areas now cover the full 3x3 leaf patches, so walking or cutting a diagonal over any leaf tile drops the player into the pit (stumble 764 → getup 534 + "Ouch!"), with a test. Jumps line up with the trap centre first and use the captured timings, which also fixes the end slide. Scripted crossings suppress the walk-on trigger via a var. Log balance: the run setting is no longer touched; crossing relies on force-walk like the other log balances so the run orb stays lit, and the balance sound plays each step. One deviation: in our map data the tripwire midline carries wall flags, so walking through the wire without interacting is collision-blocked, the capture's walk-through arrow volley can't trigger and I left that walk-on handler out rather than firing it from the approach tile. |
Tag the tripwire tiles as border areas so a walk click at the wire force-walks the player through the wall-flagged midline at walk speed, with the arrow launcher volley, damage and poison firing on the way through. Interacted step-overs suppress the volley and roll as before.
|
Follow-up in e4b6395: the walk-through deviation from the previous comment is resolved, the tripwire tiles are now tagged as |
Interacting turns the player to face the clicked object, so once the exact move carried them past it they turned back towards it. Re-face the crossing direction when the move completes (dense forest and leaf trap jumps).
749 is not the squeeze animation in this revision; 3303 is the standard squeeze-through used for dense forest. It self-terminates so the explicit anim clear after the move is no longer needed.
Only 3937 climbs over; every other dense forest passage squeezes through with animation 1237. Remove the separate double squeeze animation entry.
A full region scan found five tripwires and six sticks traps; wires at (2251,3168) and (2294,3243) and sticks at (2181,3209), (2257,3227) and (2295,3214) had no trigger areas. The last two sticks traps are the vertical variants.
A successful pass is one exact move from the player to two tiles past the trap anchor in the crossing direction with the walk animation, rather than stepping across tile by tile.
Tag the sticks strips as border areas so a walk click at them force walks the player through at walk speed, always springing the trap on the way. Successful interacted passes now land one tile past the strip so the player never ends a crossing standing inside the border area.
846 shows a crawl in this revision; 1441 is the era's recoil animation used for sprung traps. Also replace the placeholder fail damage and rate with the known values: 80 damage and a 31-156/256 agility roll.
The forced move with the walk animation looked wrong; go back to lining up and stepping across at walk speed, ending just past the strip.
Forcing the jump onto the patch's centre column made the client blend the sidestep into the jump as a glide. The patch is three tiles wide, so keep the player's lane (coerced into the patch) and only walk when they genuinely need to line up.
|
@GregHib - All adjustments have been made. |
| objectApproach("Cross", "isafdar_log_balance,isafdar_log_balance_2,arandar_log_balance", arrive = false) { (target) -> | ||
| approachRange(10) | ||
| steps.clear() | ||
| crossLog(target) | ||
| } |
There was a problem hiding this comment.
I don't think approach is needed for these (when I tested it gave me a message saying can't reach that)
There was a problem hiding this comment.
The "can't reach that" comes from clicking the far log object, which the pathfinder can't reach, the original game gives the same message there. The approach handler is still needed for the near log though: without it the interaction pathfinds to the melee reach tile diagonally behind the log (2258,3249) and walks the whole route before operating, so the player dips a tile south before crossing. The Crossing the log never paths south around it test reproduces it if the approach is removed. Happy to rework it if you'd prefer a different shape for skipping the arrival delay.
There was a problem hiding this comment.
without it the interaction pathfinds to the melee reach tile diagonally behind the log (2258,3249) and walks the whole route before operating
This doesn't seem to be the case, without an approach it walks to 2259, 3249 or 2258, 3250
There was a problem hiding this comment.
You're right, retested and the reach tile lands beside the log, so the approach handler and the arrive parameter engine addition are both removed in 5846c34. The far-end "can't reach that" matches the original game, and there's a test covering it now.
| // Approach the log at normal speed; walking away cancels | ||
| walkTo(from) | ||
| var count = 0 | ||
| while (tile != from && count++ < 50) { | ||
| pause(1) | ||
| } | ||
| if (tile != from) { | ||
| return | ||
| } |
There was a problem hiding this comment.
walkOverDelay should be fine for this
There was a problem hiding this comment.
walkOverDelay's delay is non-cancellable, and it sets the "delay" var which makes the Walk instruction ignore input, so the player can't cancel by walking away while still approaching the log (they'd be dragged the rest of the way and across). The pause loop keeps the approach interruptible; there's a test covering it (Walking away while approaching the log cancels the crossing). The crossing itself still uses walkOverDelay.
There was a problem hiding this comment.
Yeah but as the approach is removed it shouldn't be an issue as it won't activate until right next to the log (and you can't cancel once on the log)
There was a problem hiding this comment.
Done in 5846c34, approach removed and the whole thing is walkOverDelay now. One engine fix fell out of it: Movement.calculate was queueing steps with only the equipment-based forceWalk check and discarding the noRun bit that walkTo packs into the target step, so walkOverDelay(forceWalk = true) crossings actually ran at run speed (this affected the other log balances too). It now honours the packed bit.
- Store per-site sticks trap landing tiles in a tables.toml so crossing or being flung never overshoots into scenery; the trap at (2200,3169) now walks the player out to the south past the tree - Simplify the log balance approach to walkOverDelay and drop the manual balance sound; the rope_balance render emote carries its own
walkOverDelay's non-cancellable delay blocks Walk instructions, so a player couldn't change their mind while walking up to the log. Walk with a pause loop instead so stepping away cancels the crossing; covered by a test.
Drop the approach registration and the arrive parameter engine addition; the interaction's own pathing stops right next to the log and stays cancellable, and clicking the unreachable far end gives "I can't reach that" like the original game. The crossing itself ran instead of walked: Movement.calculate queued steps with only the equipment-based forceWalk check, discarding the noRun bit walkTo packs into the target step, so walkOverDelay's forceWalk never applied. Honour the packed bit so forced walks actually walk.
| character.steps.queueRoute(route, tile, tile.noCollision, noRun = strategy.forceWalk(character)) | ||
| character.steps.queueRoute(route, tile, tile.noCollision, noRun = tile.noRun || strategy.forceWalk(character)) | ||
| } else if (tile != Tile.EMPTY) { | ||
| character.steps.queueStep(tile, tile.noCollision, noRun = strategy.forceWalk(character)) | ||
| character.steps.queueStep(tile, tile.noCollision, noRun = tile.noRun || strategy.forceWalk(character)) | ||
| } |
There was a problem hiding this comment.
Seems redundant, noRun isn't set on strategy.destination (which are tiles not steps) and if you were, you can do it with strategy.forceWalk
There was a problem hiding this comment.
Moved the check into TileTargetStrategy.forceWalk as suggested in f5820e0 so Movement is untouched.
walkTo packs noRun into the strategy's target step, so let the strategy's forceWalk report it instead of unpacking the bit in Movement.
| val direction = if (tile.x < target.tile.x) Direction.EAST else Direction.WEST | ||
| val dest = Tile(target.tile.x + direction.delta.x * 2, target.tile.y, tile.level) | ||
| face(direction) | ||
| delay() | ||
| anim("dense_forest_squeeze") | ||
| exactMoveDelay(dest, delay = 90, direction = direction) |
There was a problem hiding this comment.
This isn't the correct anim, the objects are animated and open up and then the player walkOverDelay's to the other side
There was a problem hiding this comment.
Reworked in 29a03c7, both tree gate objects play the open animation with the door sounds.
| objectOperate("Enter", "poison_waste_cave_entrance,poison_waste_cave_entrance_2,poison_waste_cave_entrance_3") { | ||
| message("You enter the murky cave...") | ||
| delay(2) | ||
| tele(Tile(1989, 4174)) | ||
| } | ||
|
|
||
| objectOperate("Exit", "poison_waste_cave_exit,poison_waste_cave_exit_2,poison_waste_cave_exit_3,poison_waste_cave_exit_4") { | ||
| delay(2) | ||
| tele(Tile(2321, 3100)) | ||
| } | ||
|
|
||
| objectOperate("Climb-up", "poison_waste_sewer_ladder_up") { (target) -> | ||
| anim("climb_up") | ||
| delay(2) | ||
| val dest = if (target.tile.equals(2041, 4189) || target.tile.equals(2041, 4172)) { | ||
| target.tile.add(1, 0, 1) | ||
| } else { | ||
| target.tile.add(0, 1, 1) | ||
| } | ||
| tele(dest) | ||
| } | ||
|
|
||
| objectOperate("Climb-down", "poison_waste_sewer_ladder_down") { (target) -> | ||
| anim("climb_down") | ||
| delay(2) | ||
| val dest = if (target.tile.equals(2041, 4189) || target.tile.equals(2041, 4172)) { | ||
| target.tile.add(-1, 0, -1) | ||
| } else { | ||
| target.tile.add(0, -1, -1) | ||
| } | ||
| tele(dest) | ||
| } | ||
| } |
There was a problem hiding this comment.
Done in 29a03c7, script deleted, entrances, exits and all three ladders are entries in poison_waste.teles.toml.
| return@objectOperate | ||
| } | ||
| } | ||
| if (Level.success(levels.get(Skill.Agility), 128..250)) { // TODO unknown rate |
There was a problem hiding this comment.
https://oldschool.runescape.wiki/w/Leaves_(trap)
| if (Level.success(levels.get(Skill.Agility), 128..250)) { // TODO unknown rate | |
| if (Level.success(levels.get(Skill.Agility), 180..255)) { |
| player.anim("pit_getup") | ||
| player.say("Ouch!") | ||
| player.sound("fall_land") | ||
| player.damage(random.nextInt(20, 41)) // TODO unknown damage |
There was a problem hiding this comment.
| player.damage(random.nextInt(20, 41)) // TODO unknown damage | |
| player.damage(random.nextInt(20, 181)) // TODO unknown min damage |
| // Interacting faced the object, which is now behind; keep facing the way travelled | ||
| face(direction) |
There was a problem hiding this comment.
Automatic due to how interactions work
| // Interacting faced the object, which is now behind; keep facing the way travelled | |
| face(direction) |
There was a problem hiding this comment.
Player faces behind them unless i set the face direction.
| if (inventory.remove("coins", price) && inventory.remove("crystal_teleport_seed_uncharged")) { | ||
| inventory.add("crystal_teleport_seed_4") |
There was a problem hiding this comment.
Done in 29a03c7, the recharge is a single transaction (coins + uncharged seed out, charged seed in) with the deficient case falling back to the apology line.
| walkOverDelay(entry) | ||
| anim("tripwire_step_over") | ||
| exactMoveDelay(exit, delay = 30, direction = direction) | ||
| if (Level.success(levels.get(Skill.Agility), 128..250)) { // TODO unknown rate |
There was a problem hiding this comment.
https://oldschool.runescape.wiki/w/Tripwire
| if (Level.success(levels.get(Skill.Agility), 128..250)) { // TODO unknown rate | |
| if (Level.success(levels.get(Skill.Agility), 30..155)) { |
| this["crossing_trap"] = true | ||
| walkToDelay(entry.add(direction.inverse()), forceWalk = true) | ||
| walkOverDelay(entry) | ||
| if (Level.success(levels.get(Skill.Agility), 31..156)) { |
There was a problem hiding this comment.
? https://oldschool.runescape.wiki/w/Sticks_(trap)
| if (Level.success(levels.get(Skill.Agility), 31..156)) { | |
| if (Level.success(levels.get(Skill.Agility), 30..155)) { |
There was a problem hiding this comment.
Applied in 29a03c7 (and matched the sticks operate roll to it).
| class ElvenOverpass : Script { | ||
|
|
||
| init { | ||
| objectOperate("Climb", "elven_overpass_rocks_up,elven_overpass_rocks_down") { (target) -> |
There was a problem hiding this comment.
The timing/anims on these are off.
Dump:
elven-overpass.txt
There was a problem hiding this comment.
Reworked from the dump in 29a03c7, climbing down is the exact move facing the cliff (delay 120/90 per site) with human_climbing_down and the climbing_loop sound, climbing up walks the cliff path tile by tile under the climbing render emote, and the level gate uses the dump's message. One note: the dump's objects are 16514/16515 but our cache has 9296/9297 at those exact tiles (16514/16515 don't exist in 634 here), so the ids stay, renamed the defs top/bottom to match. The dump's overpass gates (3944/3945) are also reworked to swing open to their open variants, walk the player through and close five ticks later.
- Trap success rates from the wiki: tripwire and sticks 30..155, leaf jump 180..255; leaf fall damage up to 180 - Drop the redundant re-face after the dense forest squeeze - Eluned's recharge is a single inventory transaction - Poison Waste entrances, exits and ladders are data-only teleports - Lletya tree entrance animates both gates open, walks the player through and closes them, instead of a squeeze animation - Elven overpass rocks: climbing down is an exact move facing the cliff with the climbing-down animation; climbing up walks the cliff path under the climbing render emote; custom level message; defs renamed top/bottom to match which end they sit at - Arandar heavy gates swing open to their open variants with the big door sound, walk the player through and close five ticks later
- Restore facing the direction of travel after the dense forest squeeze - Animate the Lletya tree gate with its own tree-parting animation (12109) instead of an unrelated climb animation - Swing the Arandar heavy gate halves a full 90 degrees when opening
Each gate half swings across to the opposite side: the right gate opens as 3947 at (2384,3333) and the left as 3946 at (2387,3333).
Walking through on any column but 2385 or 2386 clips the player through the gate, so clamp the crossing tile to the opening.
Passing on any row but 3194 or 3195 clips the player through the trees, so clamp the crossing tile to the opening.
Adds the interactable objects needed to navigate Isafdar and the missing Lletya content, plus a crystal bow combat fix found while testing.
Isafdar navigation
levelparam). Players line up with the tile nearest the passage centre before squeezing through.Lletya
interacts = false), staying locked facing south.Fixes
new_crystal_bowdidn't match thestartsWith("crystal_bow")check inammo/Ammo.kt, so firing it removed the bow from the weapon slot and dropped it at the target like a thrown weapon. Now maps to the same no-ammo handling as the rest of the chain.objectApproachsupportsarrive = false(mirroringobjectOperate): approach handlers can now run without waiting for the already-queued route to finish. Needed because the log balance's cache reach tile is south of the log - without this the player always detoured a tile south before crossing.Notes
questCompleted("roving_elves"), relying onquests.requirements.skipMissinguntil the elf quests are implemented.Testing
IsafdarTest(17 tests): every obstacle type, level gates, trap failure damage, pit fall/climb-out, log pathing regression (7 start positions asserting no south detour), approach interruption.LletyaTest(6 tests): teleport crystal charge chain, Eluned/Islwyn price ladders and persistence, shopkeeper dialogue opening shops.CrystalBowTest: bow stays equipped and degrades correctly when fired.:game:and:engine:suites pass.