feat: Man0g1 "Souls Gone Wild" — implement the White Wolf Gate escort duty + fix the five static bugs (#41)#204
Merged
Conversation
… duty + fix the five static bugs (#41) Static fixes (issue items 1, 3-7): - SEQ_003 -> SEQ_005 after the Roost Echo scene. Upstream truth: pms never declares SEQ_003 (StartSequence(nil)); MeteorReborn declares SEQ_003=3 but nothing ever advances 3->5, and AetheryteParent's 110006 arm gates on SEQ_005 (unlike man0l1, whose aetheryte checks SEQ_003) -- the 3->5 hop never existed upstream. - getJournalMapMarkerList: removed the undeclared-`data` nil-index, declared MRKR_KID_TRIGGER=11000602 / MRKR_GATE_TRIGGER=11000603 (MeteorReborn parity; sheet rows in bahamut-client-data quest_marker.csv) and returns Miounne's marker on the talk-to-Miounne beats. - onNotice: dropped the "Test" SendMessage + doubled EndEvent; the gate tutorial (processEventTu_001) stays. - Hereward/Geva beat now pays 2000 gil -- retail dialogue "An even two thousand gil, no more." overrides the upstream "--give 1000g" TODO. - Fufucha's debrief pays 3000 gil ("You obtain 3000 gil." on film, playthrough Part 6 01:12). The escort duty (issue item 2) -- net-new, the man0l1 machinery re-instantiated for Gridania (zero Rust changes; all content machinery is name-agnostic): - startMan0g1Content: the wire-proven 10-step entry (rescue latch -> journal -> CreateContentArea("Man0g101", zone 150) -> deferred director kick -> processEvent170 duty-start CS -> EndEvent -> DoZoneChangeContent spawnType 16). - SimpleContentMan0g101.lua: Powle leads the TRAIL, Sansa tags along, neither fights (retail: "Do something! You are an adventurer, aren't you?"); ankle-biter ambushes engage the PLAYER; either kid dying or the 30-minute expiry fails the duty (SEQ_060 rollback + gate eject + retry); kid-hit "Ouch!" barks off HP snapshots; the full retail Mirke bark set (literal-text fallback until the man0g1 quest-sheet say ids are DAT-decoded); duty banners (protect/bound/timer), minimap halo on Powle (moveState 2, #199 semantics), duty music, arrival -> escortComplete signal. - QuestDirectorMan0g101.lua: questBaseRewardSeting veil dismissal -> park on escortComplete -> processEvent180 end cutscene -> StartSequence(SEQ_070) + unbind -> ContentFinished -> stump-scene warp (zone 150 PrivateAreaMasterPast 0). - SEQ_065 rescue arm (man0l1 SEQ_050 pattern): relogs/wedged saves roll back to SEQ_060 so the gate re-arms. - seed/074: the five never-spawned trigger classes (1090200-1090204, empty upstream in BOTH pms and MeteorReborn) get the man0l1 push-trigger recipe + spawn stations; BTN_TRIGGER 1090046 gains its missing pushWithCircle and pmeteor's own spawn station; Powle/Sansa as FighterAlly-scripted battle npcs wearing the kids' real displayNameIds (1000029/1100025) + cloned appearances; zone-150 ankle-biter group (retail-calibrated lvl 5-7 / hp 60); the five missing private-area registrations the quest tail addresses (155 L0/3/4, 150 L0/1 -- absent upstream too). PROVISIONAL geometry, replaced when the White-Wolf-Gate->Lifemend- Stump walk is recorded (the man0l1 round-7f 0x00CA recipe): the duty trail runs a short arc beside the stump (wiki marker zone 150 ~(-800, 20, -1050)); the gate trigger + ambush points re-home from the recording. Sources: pms + MeteorReborn source archaeology, Mirke Menagerie Loremonger transcript, the 7-part retail playthrough (OCR-mined), bahamut-client-data sheets, and the man0l1 escort (#46/#199).
…s after the warp (#41) Live 2026-07-06 02:03:19: talking to Miounne played the cutscene then hung. The drain shows why: the pms port ships SetLoginDirector COMMENTED OUT, so the burst missed the login-routing tell (processor.rs is_login_scoped_burst) and the AfterQuestWarpDirector noticeEvent kick was direct-dispatched pre-wipe ("KickEvent dispatched directly to client (runtime drain)") in the same flush as the same-map resident-geometry wipe that destroys its owner actor. The client drops the answer for a wiped owner, onNotice/processEventTu_001 never fire, and the kick-armed staging veil never clears — the exact class the man0l1 Baderon menu-lock fix documented ("Round-3 live test"). Upstream never hit this because pmeteor's same-zone DoZoneChange doesn't wipe. - player:SetLoginDirector(director) uncommented — the zone-in bundle now re-emits the director spawn + kick post-reload on a loaded actor table (the canonical sequence in AfterQuestWarpDirector.lua, which already routes 110006 to onNotice). - seq000_onTalk's Miounne branch returns true and onTalk early-returns: the fall-through shipped a second EndEvent + UpdateENPCs AFTER the DoZoneChange (drain commands 8-9) — the mid-reload EndEvent / desktopWidgetMode-16 hazard. luac -p clean; content-test 9/9.
…dania zone halves, pin scene-PA warps to 155 (#41) Live 2026-07-06 follow-up: 'the Conjurers' guild loads no new NPCs' is literal — the ENTIRE SEQ_015 CNJ cast (Soileine + the Stillglade Echo scene: O-App-Pesi, Ingram, Hetzkin, Gugula, Biddy, Challinie, scene copies of Yda/Papalymo/Swethyna) has ZERO spawn rows in every upstream. Gridania is two seamless halves in one coordinate space (bound 029/1): 155 fst0Town01 = north (Roost + all 17 upstream scene-PA rows), 206 fst0Town01a = south (guild district, 154 populace rows — Hereward, Growery, aetheryte plaza). Boundary z ~ -1270. - seed/075: Soileine at the Fane door (206 public) + the 9-NPC Echo cast in 155 PrivateAreaMasterPast 0 around the quest's own warp anchor (positions provisional); re-homes the 074 CNJ/gate/BTN triggers to 206 (their stations are all south of the boundary — the 074 'translation' of BTN's upstream 206 row was wrong). - man0g1.lua: the five public-fired scene warps (CNJ push, Opyltyl x3, Burchard) now pin zone 155 via explicit DoZoneChange — WarpToPrivateArea resolves the CURRENT zone's PAs, so fired from the 206 half it missed and fell back to public. The SEQ_090 PA4 hop stays WarpToPrivateArea (fires from inside a 155 PA). luac -p clean; content-test 9/9.
…spawns crash the client (#41) Live 2026-07-06 ~04:38: Wine died walking up to the Conjurers' Guild. seed/075's cast rows spawn classes that ship with EMPTY classPath in BOTH upstreams (only displayNameIds are real): Soileine 1000234 (206 public — streams in as you approach the Fane door) + the six Echo vets. A classPath-less actor gives the client nothing to load — the established crash family (chigoe DepictionJudge lesson, populace flavor). Yda/Papalymo/Swethyna were already filled, which is why the Roost scene worked and the Fane approach crashed. Fill = the Miounne populace recipe (PopulaceStandard + flags 19 + talkDefault/noticeEvent), guarded WHERE classPath=''.
…h) + Fane arrangement/poses from retail refs (#41) Exit crash (live 2026-07-06 05:17:39): Swethyna's WarpToPublicArea from the 155 scene PA stranded the player in 155-public deep in the 206 half — a world-state retail never produces. The first 206-band actor partner-streamed into that half-loaded client (Kinnison, 0x46700082, data fully healthy) froze the render thread (socket pinged 27s more, then exit). Fix: every PA exit now lands the correct public half explicitly — Swethyna→206 Fane door, kids-exit→206 Growery, Nuala→206 Barracks, stump-exit→155 north (was landing 150-public). Arrangement/poses per the user's retail video frames: - Yda no longer lies on the floor: seed/075 copied her town animationId 1007 = the man0g0 collapsed-in-forest pose pack; scene rows now animationId 0. - Post-cutscene: Yda+O-App-Pesi+Swethyna tight trio in front of the player; vets pushed to the balcony ring; Soileine scene copy added (visible in both retail frames). - Public Fane: Soileine moved from the door to the reception counter, Hetzkin added beside her (retail frame). 'Foforyo'/'Marcelle'/ 'Concessa' remain unmapped (no name→class table server-side). luac -p clean; content-test 9/9.
…tlock) + Fane desk nudge (#41) Live 2026-07-06: the Botanist-guild (Greatloam Growery) instance loads with no NPCs → the SEQ_050 emote drill can't complete → softlock. Same dead-upstream family as the CNJ cast: the six kids + Opyltyl + Fufucha had ZERO rows in the SEQ_050 dance-practice PA (155 PA1) and SEQ_055 kids-chat PA (155 PA2) — the kids exist only in a different quest's scene (etc5g1_*, 206 PA5), Opyltyl only in public 206. Fufucha (1000237) + Nicollaux (1000409) also ship classPath-empty (crash class) — filled first. - seed 078: 8-NPC casts in both instance PAs (class ids match the quest onEmote/onStateChange handlers); positions provisional around each scene's warp anchor. - Fane desk: Soileine + Hetzkin pushed ~6u deeper behind the counter (were mid-room per the live report). Recovery for the stuck save: the idempotent seed loader applies 078 on rebuild+reboot without a wipe; login re-warps to PA1, or Opyltyl (206 public) re-enters the drill. Verified seed applies to a copy of the live DB (all 8 drill NPCs classPath-live, zero remaining empties); content-test 9/9; smoke SMOKE_OK.
…auto-cutscene), lift underground kids to a retail arc (#41) Live 2026-07-06 with retail ref frames: - Opyltyl was spawned inside the drill (PA1) + chat (PA2) instances by seed/078, but his SEQ_050 onTalk branch RE-WARPS the player into PA1 — brushing him mid-dance resets the drill and blocks the auto-advance to the Zezekuta/Fufucha cutscene ('he bugs it'). Retail has only Fufucha + the six kids on the grounds. DELETE both instance rows; his public 206 entry NPC stays (SEQ_040 entry + re-entry recovery intact). The emote-completion auto-cutscene (onEmote: all 6 flags -> processEvent150 -> SEQ_055) was already correct — Opyltyl was the blocker. - Underground kids: seed/078 spread them to z<=-1510 where the Growery ground rises above the anchor's y=12 (stub navmesh, no ground query) so Elyn/Nicollaux sank. Re-arced within the anchor's proven-flat band (z ~ -1494..-1502), spread in x to match the retail left-to-right order (Ryd..Nicollaux; ref frame). SEQ_055 chat kids pulled to the same band. Verified against a copy of the live DB (Opyltyl absent from both PAs, kids in-arc at y=12); content-test 9/9; smoke SMOKE_OK.
…te actually fires (#41) Live 2026-07-06 (user pointed at man0l1's working Sisipu emote step): clapping at a kid did nothing. A player emote only reaches the quest onEmote hook (processor.rs EventStart event_type 3) when the TARGET's gamedata_actor_class carries an emoteEventConditions entry matching the emote; man0l1's SISIPU_EMOTE (1000155) has that block, the kid classes (only talkDefault+noticeEvent from seed 076/078) did not, so the client routed the emote to the free EmoteStandardCommand and the drill could never register. Each kid now carries one emoteEventCondition for the emote it teaches (Nicollaux Clap 107, Sansa Bow 105, Powle Cheer 106, Aunillie Beckon 108, Ryd Surprised 101, Elyn Lookout 122 — matched to man0g1.lua onEmote's DoEmote calls). Wrong emote at a kid matches no condition and no-ops (retail). Also tightened the drill cluster to within ~6u of the known-flat spawn anchor (the seed/079 spread straddled a fence/ravine and re-buried Nicollaux). Verified against a copy of the live DB (all 6 emoteIds correct); content-test 9/9; smoke SMOKE_OK.
…rror (#41) Live 2026-07-06: after the man0g1 dance drill's last emote the quest did not auto-advance to the Zezekuta/Fufucha cutscene. Root cause is an engine binding bug, not data: LuaQuestDataHandle.GetFlags/GetFlag serve from the immutable snapshot (this.flags) taken when quest:GetData() was called, but SetFlag only QUEUED a QuestSetFlag command without touching that snapshot. So in the 6th onEmote, 'data:SetFlag(FLAG_EMOTE_DONE6)' then 'bit32.band(data:GetFlags(), 0x7E) == 0x7E' on the next line still read the pre-6th value (0x3E != 0x7E) and the completion beat (processEvent150 -> StartSequence SEQ_055) never fired. The log shows six QuestSetFlag drains and zero processEvent150. Fix: SetFlag/ClearFlag are now add_method_mut and update this.flags immediately (|= / &= ~ the bit) in addition to queuing the command (which still owns DB persistence). Same write-through-the-mirror rule the SetMod / LuaParty:AddMember bindings already follow — hardens any quest that sets-then-checks a flag in one hook. (Counter mutators have the same latent staleness but aren't hit same-call today; noted.) fmt/clippy/build/test all green; content-test 9/9; smoke SMOKE_OK.
…warp (#41 escort trigger) Live 2026-07-06: walking to the White Wolf Gate started no escort duty (softlock). Log: 'quest ENPC broadcast skipped — no live NPC class_id=1090202' at the SEQ_055->060 warp instant. Root cause: the KIDS_TRIGGER push does StartSequence(SEQ_060) THEN DoZoneChange to the gate zone (206), so SEQ_060 onStateChange armed GATE_TRIGGER (1090202, in 206) while the player was STILL in the origin zone (155) -> the cross-zone broadcast found no live NPC and it was never re-armed. The drill kids + CNJ trigger worked only because the player was already in those triggers' zone when their sequence armed. The seamless path already re-establishes active-quest ENpcs on the destination (handle_update_position 3c, man0l1's Zephyr Gate); explicit DoZoneChange warps did not. handle_zone_in_complete now runs the same idempotent re-establish (apply_quest_update_enpcs per active quest) once the destination zone is current. GUARDED: skipped inside a content instance (active_content_script set) so it can't re-fire the escort SEQ_065 one-shot FLAG_ESCORT_HANDOFF rescue arm and roll the duty back; skipped when a deferred login warp just replayed (its own RX 0x0007 re-establishes). Also fixes the post-escort SEQ_070 stump triggers, same cross-zone-arm class. fmt/clippy/build/test green; content-test 9/9; smoke SMOKE_OK.
…and-mark (#41) The provisional GATE_TRIGGER (Greatloam Growery, -235/-1455, out of bounds ~460u from the gate) never started the duty. Decoded the player's exact position at the true White Wolf Gate from the previous session's last 0x00CA packet (peer 64785): x=-194.73 y=3.54 z=-1021.33 rot=-1.642. Moved the trigger there. Zone can't be confirmed 155 vs 206 from the (rolled-off) log, so hedged: spawn 1090202 at the gate coords in BOTH zones — a single XZ is in exactly one zone so the other copy is inert, no double-fire, and whichever half it is the trigger arms via the cross-zone re-establish (665ee92). Seed-only; content-test 9/9; smoke SMOKE_OK. Reboot (no rebuild) applies.
092_remove_bluebadger_closed_gate.sql is a legitimate DELETE-only data-removal migration, but every_migration_decompresses_to_sqlite_sql only whitelisted CREATE TABLE / INSERT / ALTER TABLE / UPDATE, so it failed the non-empty-migration guard. Add DELETE to the token union (the same way UPDATE was added for the data-fix path).
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.
Closes #41.
Summary
Implements Man0g1 "Souls Gone Wild" end-to-end: all five static bugs fixed, and the fully-stubbed White Wolf Gate escort duty (SEQ_065) built as a real content instance — the man0l1 Sisipu-escort machinery re-instantiated for Gridania with zero Rust changes (every content subsystem proved name-agnostic).
Research base: pms + MeteorReborn source archaeology, the Mirke Menagerie Loremonger transcript, the 7 linked playthrough videos (OCR-mined via youtube-watcher into full storyboards), bahamut-client-data sheets, and the wire-proven man0l1 escort (#46/#199).
Static fixes (issue items 1, 3–7)
SEQ_003→SEQ_005after the Roost sceneStartSequence(nil)); MeteorReborn declares it but nothing ever advances 3→5;AetheryteParent's 110006 arm gates on SEQ_005getJournalMapMarkerListundeclared-datanil-index removedMRKR_KID_TRIGGER=11000602,MRKR_GATE_TRIGGER=11000603declared + markers returned (Miounne beats included)quest_marker.csv:108-110onNotice"Test" message + doubledEndEventremovedprocessEventTu_001) keptThe escort duty
startMan0g1Content— the wire-proven 10-step entry (rescue latch → journal →CreateContentArea("Man0g101", …, zone 150)→ deferred director kick →processEvent170duty-start CS →EndEvent→DoZoneChangeContentspawnType 16). Retail CS pair confirmed from Mirke: 170 = duty start, 180 = end cutscene (the beat the old stub played without the duty).SimpleContentMan0g101.lua— Powle leads the trail, Sansa tags along, neither fights (retail: "Do something! You are an adventurer, aren't you?"); ankle-biter ambushes engage the player (same chigoe family as man0l1 — attested on film in this duty too); either kid dying or the 30-min expiry fails (SEQ_060 rollback + gate eject + retry); kid-hit "Ouch!" barks; the full retail Mirke bark set; duty banners; minimap halo on Powle (moveState 2, man0l1 Sisipu escort (Treasures of the Main): player can't auto-attack Ankle Biters (no TP/skills/EXP) — stale registry-zone, reassign_zone missing on content-warp path #199 semantics); arrival →escortComplete.QuestDirectorMan0g101.lua— veil dismissal → park →processEvent180→SEQ_070+ unbind →ContentFinished→ stump-scene warp.seed/074— fills + spawns the five trigger classes that were never spawned upstream in either reference (1090200–1090204: empty classPath, zero spawn rows — the CNJ/kids/gate/stump pushes were all dead beyond the escort stub); adds BTN_TRIGGER's missing pushWithCircle + pmeteor's own station for it; Powle/Sansa as FighterAlly-scripted battle npcs wearing the kids' real names (displayNameIds 1000029/1100025 — non-standard mapping) + cloned appearances; zone-150 ankle-biter group; and the five missing private-area registrations the quest tail addresses (155 L0/3/4, 150 L0/1 — also absent upstream).Provisional geometry (the one open round)
The duty currently runs a short, clearly-flagged placeholder arc beside the Lifemend Stump (wiki marker zone 150 ≈ (-800, 20, -1050)). Next round: record the real White-Wolf-Gate→stump walk (inbound 0x00CA breadcrumbs, the man0l1 round-7f recipe) — the TRAIL, the gate-trigger station, and the ambush points all re-home from that recording. Also flagged for that round: whether White Wolf Gate needs a 155↔150 seamless-bound row, and live-verifying the 150 private-area stump scenes. Bark say-ids await a man0g1 quest-sheet DAT decode (literal-text fallback ships meanwhile).
Verification
fmt --check,clippy -D warnings,build --locked,test --workspace --locked,content-test9/9,smoke-local→SMOKE_OK all services.luac -pclean on all three Lua files (the content script/director only load when the duty starts, so the gates alone don't parse them).startMan0g1Content, trigger/private-area absence) each confirmed against both pms and MeteorReborn.