feat(magic): the standard spellbook's teleports #149
Loading…
Reference in a new issue
No description provided.
Delete branch "feat/teleports"
Deleting a branch is permanent. Although the deleted branch may continue to exist for a short time before it actually gets removed, it CANNOT be undone in most cases. Continue?
The seven standard-spellbook teleports — Varrock, Lumbridge, Falador, Camelot, Ardougne, Watchtower and Trollheim — cast from the spellbook, spending runes, paying Magic experience, and moving the player three ticks later.
The first transport the world has had. Everything before this walked, and the world is not built for walking: content past early game assumes a player teleports close and covers the last stretch on foot, which is why a farming run works at all. Part of #140.
Where each column comes from, and why that matters here
levelandrunesare the cache's own, read from interface 218'son_loadargs at the offsetsWorld.SpellbookTestalready pins the combat rows against. A row that drifts from the operator's cache fails the suite rather than surprising a player.destinationis the one column no client cache has ever carried, and it is the reason this took sourcing rather than typing. It is ported from a 468-era table whose rune costs match this one exactly — two independently maintained tables, ten years apart, agreeing on every rune of every row, which does not leave the coordinates free to have moved. Our ownpriv/data/teleports.exslandmarks agree to within 1–2 tiles as a third witness.Then every landing tile is asserted unblocked, floored, and outside the wilderness against the operator's own cache. That is the check that carries the weight: a date is a claim, a walkable tile is a fact, and a coordinate that survived a ten-year revision gap is proven by standing on it rather than by argument.
The ceiling is why this is a contract
A teleport refuses above wilderness level 20. That is not a nicety — it is what makes deep wilderness a commitment, and a server that forgets it has quietly deleted the risk from every encounter north of the ditch.
Contract.Teleportwalks the boundary tile by tile in its tests, 0 through 56, because the whole rule is one level and>versus>=is the mistake it invites.Nothing here re-derives where the wilderness is: it asks
World.Wilderness.level/1and lets the contract judge. A consumer restating an engine rule narrower is a mistake this codebase has already recorded twice — the chop verbs inBot.Sites, andsame_plane_bank/2.Ordering
Runes are spent at the click and the player moves three ticks later, which is the rune altar's ordering and for the same reason: a player who logs out mid-flight has paid, and that is the reading that cannot be farmed by disconnecting.
Player.delay/2covers the same three ticks so the cast cannot repeat while its animation plays.The animations were ported, so they were checked rather than trusted
714and715share frame archive207. That is the check that means something — an id which merely decodes may belong to another rig entirely, which is the trap the death-animation work paid for. Sharing an archive makes them genuinely one animation's pair;715's frames are714's in reverse.714's last frame is a 20,000-cycle hold:[6, 6, 6, 6, 20, 4, 4, 4, 4, 4, 4, 20000]. The animation is the first eleven frames — 68 cycles, about 2.27 ticks — and the hold parks the caster in the vanish pose until something replaces it, which the arrival does. So the three-tick delay is not arbitrary, and a test asserts the playing frames still fit inside it: if that ever stops being true, the pose shows.The graphic height was wrong and is corrected to 0. It had been copied from the rune altar's
100, which is what a graphic above the head wants;111is a full-body swirl that starts at the feet. The cache settles neither —Cache.SpotAnimcarries no render height — so it is flagged for a live sitting rather than asserted.What a player tries during the three ticks
Interrupting a teleport is one of the first things somebody will try, so it was measured live rather than reasoned about:
The first two are now regression tests. The death case needed care to measure at all: cast from the Lumbridge spawn tile and "respawned" is indistinguishable from "never left", because they are the same coordinate — the first attempt fooled itself exactly that way, and the test now casts from
{0, 3000, 3300}so origin, destination and respawn are three different places.#148 also carries the latent trap: any future path that cancels a teleport mid-flight without animating the player leaves them frozen in the vanish pose. Teleblock is era content and is exactly that shape.
One test that passes for the wrong reason, and says so
"a combat spell's plain click never reaches the chain at all"does not exercise the fall-through. Wind Strike carries op 1 disabled on the client — it is cast throughcomponent_on_npc, not as a button — soButton.authorise/1refuses it with{:op_disabled, 1}before the teleport handler is asked. The fall-through itself is asserted directly inWorld.TeleportSpellsTest, wherespell/1answersnilfor component 2. A green test that proves something other than its name is worse than no test, so it names what it actually proves.Scope
The standard spellbook only. Ancients, Lunar and Arceuus are 32 more rows on this same machinery — all 44 components are already read, with levels and rune costs, in
.memory— and are deliberately absent until their destinations get the same three-source treatment.Deliberately not shipped, each for a structural reason rather than a data one:
World.Playeris at the 32-key cliff.World.Timerswould reset at logout and a varp is client-visible; that is a design decision, not a missing row.Verification
Gates match clean
mainexactly, measured by stashing every working change and re-running rather than inferred.Worth flagging separately: #131's baseline is stale. It records 2 Dialyzer errors; there are now 12, and the extra ten are
callback_arg_type_mismatch/callback_type_mismatchintest/support/scripts.exthat arrived with the PvP merge — confirmed present on cleanmainwith this branch stashed. They look like a genuineWorld.Scriptcallback type problem rather than an environmental quirk.