Repository navigation
Doors open within reach, switches in their rest pose, town teleporters ride - #37
Merged
codingncaffeine merged 3 commits intoNov 9, 2026
Conversation
…n the air The MP town transport stone's floating button is lever_glb_06 (m_i_glb_switch-06) placed on the teleport-hub elevator node 1.13 above the platform. Its press clips (a_i_glb_switch-06-open/close) hold Bone01 one unit down mesh -Z from the bind for the whole clip; DS1 draws the GO posed, and every placement is authored for the posed mesh. SiegeFX drew the static prop at bind, so the button hung ~1 unit off its surface. The whole lever_glb_01/02/03/06 switch family carries the same offset: the dm_r8a and cr_r1 wall switches likewise sit exactly 1.0 in front of their wall faces at bind and land flush once posed. Static skinned props (not rigid-root) now apply the root bone's skin matrix from their rest clip (chore_default, else chore_open, at t=0) when the clip holds the root still and off the bind. A root that moves through the clip (statue_cav_rise_01) is left alone. The offset is applied inside the scale_multiplier, unscaled: the other MP towns' buttons keep the template's scale 2 and sit on their platform only then. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> (cherry picked from commit 99557177da6f117dd9077c49abe5d20cde33fd89)
A door click opened the nearest door to the click point at once, from any distance (OpenDoorNear ran straight from the click handler). It now queues a walk-up-and-open like chests and levers: the acting character walks to the click and the door opens only when they are within its reach — the leaf's footprint radius plus [aspect] use_range, measured from the hinge, same floor (UseRange.DoorInRange, unit-tested). Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
… a press Every multiplayer_world town's transport stone is an elevator_instant_4s_1c gizmo. The skrit (components/elevators/elevator_instant_4s_1c.skrit) drives TWO cars between two long shaft nodes: the town car (elevator_node) and its twin in the new_hub region (e2_elevator_node). A press raises both cars to level 2 of their own shaft (duration_locationN_moving_to_lv2, default 2s), pops each to level 2 of the other shaft (SRequestNodeConnection), and sinks them to level 1 there (duration_..._to_lv1, 16s authored) - the riders step off in the hub. The 8 towns (town_center, des_r1, snow_town, oasis, mine_town, swamp_town, castle_town, ocean) each pair with one of new_hub's 8 gizmos (other_elevator); 9 more pairs link regions point to point. Actioninfo is a mood name here (multiplayer_world_teleport darkens the shaft, multiplayer_world_<dest>_teleport sets the arrival). ElevatorStore only read connect_node / connect_node_levelN, so these gizmos were skipped and the button did nothing. Now: - ElevatorStore parses the connect_node_location1/2 form (8 docks, both cars, durations, actioninfo, effects) into ElevatorDef.Instant; InstantElevatorPlan is the skrit's state machine as data. - new_hub has no stitch_helper, so no world position: it streams in when a teleporter is pressed and WorldLayout places it as an island at a pinned, deterministic offset (same on every co-op machine). The press is queued and handled from TickElevators (streaming adds triggers / rebuilds nav). - The runtime rides both cars, carries riders through the pop, applies the moods to this machine's hero, re-bakes nav, and moves objects placed on a car node with it (the button and its use points ride the car). - The town and hub gizmos of one teleporter share one runtime (scid alias). - Trigger rows mixing receive_world_message with a volume condition in boundary mode wait_for_every/first_message now treat the volume as a gate on the message (the editor annotates it "message checker"). Before, the conditions were independent sources: stepping onto the platform launched the teleporter, and arriving on it launched it straight back. Verified in --play-region town_center: town -> hub -> town round trip, rider carried both ways, button usable at the far end. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Three fixes for usable world objects, found play-testing
multiplayer_world(Elddim). The first two also apply to the single-player campaign.Doors open only within reach. A door click ran
OpenDoorNearstraight from the click handler, which opened the closed door nearest the click from any distance; the template'sDoorUseRangewas read but never checked._pendingDoorUse); the acting character walks to the click and the door opens on arrival.[aspect] use_range(default 1.5), same floor (within 3 u). The footprint term is needed because a closed door's nav obstacle keeps the walker about one leaf length from the hinge (UseRange.DoorInRange, unit-tested).Skinned props drawn in their rest pose. The switch family's clips (
a_i_glb_switch-06-open/close.prs, alsolever_glb_01/02/03) hold the root bone at (0,-0.183,-1.0) for the whole clip while the bind pose has it at z 0. DS1 draws the prop in that animated pose and the maps are placed for it; SiegeFX drew static skinned props at bind, so the teleporter call button (lever_glb_06) floated ~1 u over its platform in all 9 MP towns, and SP wall switches (dm_r8a, cr_r1) stood ~1 u off their walls.AnimationRuntime.TryRootRestOffsetreturns the root's skin matrix only when the clip holds the root still and away from bind; a moving root (e.g.statue_cav_rise_01) is left alone.PropRestPosepicks the resting clip (chore_default, else chore_open/close; also theanim_files:00 =shorthand). The static prop path appliesrestOffset * worldbeforescale_multiplier, so the offset itself is not scaled (other towns' buttons use scale 2 and only sit right that way). Cached per template+mesh, logged once as[rest-pose].lever_glb_01/02/03/06/06_message(-1 Z) andlever_gob_01(+0.757 Z, not checked against geometry); a few others are ≤ 0.007.Town teleporters ride. Every MP town's transport stone is an
elevator_instant_4s_1cgizmo driving two cars between two shafts: the town car and its twin innew_hub. A press raises both cars, swaps them between the shafts and sinks them, so the riders step off in the hub; the 8 towns pair with new_hub's 8 gizmos (other_elevator), 9 more pairs link regions point to point;actioninfois a mood name here.ElevatorStoreonly readconnect_node/connect_node_levelN; it now parses theconnect_node_location1/2form intoElevatorDef.Instant, withInstantElevatorPlanas the skrit's state machine in data.new_hubhas no stitch_helper, so no world position: it streams in when a teleporter is pressed andWorldLayoutplaces it as an island at a pinned, deterministic offset. The press is queued and handled fromTickElevators.receive_world_messagewith a volume condition in boundary modewait_for_every_message/wait_for_first_messagenow treat the volume as a gate on the message (the editor labels these "message checker"). Before, stepping onto the platform launched the teleporter by itself, and arriving launched it straight back. World.dsmap has ~30 rows of this form; they now need their message too. Receipts pass, but those SP spots were not played through.Checks
dotnet build SiegeFX.sln -c Release -warnaserror: clean.dotnet test: 147/147, with newUseRangeTests,PropRestPoseTests,InstantElevatorTests(data-free).tools/receipts.sh: all PASS; parity ledger better (template-fields 397 -> 400), no regressions.multiplayer_worldtown_center in a co-op development branch carrying these commits (checked from logs and engine screenshots, not a full play-through): doors open once the hero reaches them ([door] … reached: actor 2.1u from the hinge),lever_glb_06now sits at y 6.6 on the 6.55 platform (was ~7.6), and a scripted press rides town → hub → town with the rider carried both ways.RenderHostbecomespartialhere as in #36 (same one-line change).🤖 Generated with Claude Code