Skip to content

Integrate upstream single-player Playable Bosses into Experience - #3

Closed
hyp36rmax wants to merge 14 commits into
Experiencefrom
feature/playable-bosses-upstream
Closed

hyp36rmax wants to merge 14 commits into
Experiencefrom
feature/playable-bosses-upstream

Conversation

@hyp36rmax

@hyp36rmax hyp36rmax commented Oct 18, 2026 •

Copy link
Copy Markdown
Owner

Integrates Pairomaniac/Vortyne’s optional single-player Playable Bosses from pinned upstream 4ecae6237e1fc432c0be2f2b0aeab5303120e444 into Experience. Jaguarandi and Z-Gradt retain upstream behavior, portraits, unlock progression and pre-unlock dependency. Bosses remain off by default; Local VS boss support is excluded.

Experience adaptations use the existing UI offset for OEM/JPRE, protect bosses.bin during failed Apply rollback, and preserve progression through Restore. The source-locked Retail/JPRE catalog expands to 2,048 option images while retaining all 512 previous recipes and wrapper identities. Controller, Display and networking implementations remain unchanged; Unified Overlay is excluded.

Validation at e267323800b4a39bc9bf555ba5dffe48439a495f:

  • Hosted verify, native Display and Windows packaging jobs passed: https://github.com/hyp36rmax/VO_PATCH_EX/actions/runs/39168529402
  • Local Retail/JPRE patch combinations, source locking, all catalog images, Apply/Restore/reapply/rollback and controller/display regressions passed.
  • Hosted GUI dependency, Windows launcher/Tk/self-check, manifest, runtime/helper, licenses and game-asset exclusion checks passed.
  • All four upstream mappings/blobs checked; OEM/JP pristine-file execution remains unverified because those originals were unavailable. Integrated Display retains its existing Retail/JPRE boundary.

Draft pending physical Windows gameplay acceptance: both bosses, earned/pre-unlocked saves, Dash special action, camera/replay/ending behavior and controller/display configurations. Automated checks do not establish physical gameplay acceptance. A private UAT ZIP and verified local recovery bundle are prepared. No release or Experience merge is requested.

pairomaniac and others added 14 commits October 18, 2026 02:27
…airomaniac#10)

* Add a playable bosses patch + auto unlock bosses checkbox to patcher

You can unlock and play as bosses in 1P mode with full features for that boss (selectable in machine select and visualized there once unlocked, animations for launching, animations for beating the game, animations for player data report, special animation when each is unlocked during gameplay)

* Tweaks from PR review for Bosses code

- Fix z-gradt's turning camera
- Make z-gradt's jump input do a lock-on similar to actual machines doing a jump input (it's slower overall due to z-gradt's huge size)
- Improve z-gradt's fly in animations to not clip through scenery in some stages
- Wait until Z-gradt lands to start the battle.
- Refactor the code in accordance with recommendations.

* Bosses blob in a section of its own, .vobs, appended only when ticked, so an unticked run leaves the exe as main does (OWN_SECTIONS replaces BSS_BLOBS); the player's AI bank dropped each round; NOTES and asm/README sections, MAP rows; portraittest.py reads the portraits back and checks the unticked exe

* Docs: BOSSES.md reworded to the house rules (no history, the duplicate credits bullet gone, the limits listed), the weapon names and portraits in TEXT.md, the annex count in asm/README, and NOTES' credits paragraph corrected: a finished one-player game rolls through the title machine's 0x20, the second player's machine only ticks in two-player

* Three stale comments: the A/B naming in the symbol table, a one-player game is player 1's side only, and the buffers' section is .vobs

* Credit Vortyne for Playable bosses: the README's credits, the About card and the patch's tip

* Pre-unlock set in under Playable bosses as its sub-option, the builds line dropped from the tip, README and BOSSES.md, and the About card's credit a link to Vortyne's page

* Pre-unlock greyed and unticked while Playable bosses is off, so what is ticked is what is written (_sync_needs, from NEEDS)

* About card: only the name a link in 'Playable bosses by Vortyne'; the credit line out of the patch's tip

* Z-Gradt's chase camera doubled where the eye is placed instead of its mode distance multiplied in place, so the game's glide and over-the-shoulder offset stay and nothing compounds; the opening shots' pull turned round to go back, for the player's Z-Gradt only, 140 not 320; the dead demo-shot hooks dropped. The select draws the bosses from the patch's own model blocks only, the game's slot kept and put back, so selcol never recolours a pool copy; Jaguarandi inside the cull band from Raiden's place; the model read opens before it allocates and checks its length; selt0's stray constant

* Revert "Z-Gradt's chase camera doubled where the eye is placed instead of its mode distance multiplied in place, so the game's glide and over-the-shoulder offset stay and nothing compounds; the opening shots' pull turned round to go back, for the player's Z-Gradt only, 140 not 320; the dead demo-shot hooks dropped. The select draws the bosses from the patch's own model blocks only, the game's slot kept and put back, so selcol never recolours a pool copy; Jaguarandi inside the cull band from Raiden's place; the model read opens before it allocates and checks its length; selt0's stray constant"

This reverts commit c72fe1d.

* Z-Gradt's chase camera the game's own, scaled 2.5 where the eye is placed with a least of 180, never closing below 56 nor aiming below 0xc00; Jaguarandi's 1.5; the replay's pull-back 120 and never downwards; the fly-in part of the way in on every outdoor arena; the scoreboard's boss names spelt right. Cleanup: the inert demo-shot hooks, four stale hand-map entries, five alias symbols and a dead parameter gone; the tick reads A's scene word only; stale per-copy comments, duplicate constants and the lock-on's prefix fixed; the site list's comments regrouped; docs' figures exact

* Playable bosses on the Japanese original: its symbols and site map (31 sites placed by hand where the map's functions hold jump tables or are split differently, recorded in votrans.py), its own section, its pin; the portrait check on every build

* Playable bosses on the Japanese rerelease too, so on every build: its symbols and site map (50 sites placed by the references each table has and by the instructions around each hook, recorded in votrans.py), three symbols for what its compile does differently (the ending camera's scan base and slot, the clone's frame), its pin; the per-build gating gone

* Japanese ports: twelve symbols the function map had paired wrongly, now taken from the code that calls them or from the bytes themselves. Rerelease: six functions (the parts draw, the wall test, the report text, the scene end, the texture and file loaders) sat one function off, so the game hung on the SEGA logo at the first texture load. Original: Z-Gradt's six part tables pointed into the fight machine instead of .data, so his upper half was missing in the hangar. Pins follow. Played: both Japanese builds start and fight with the bosses; the rerelease still draws the bosses' silhouettes wrong in the hangar and in a match, not yet traced

* The hangar's fade reads the scene camera's place where the hangar itself reads it (BS_SELCAM), not at a fixed 0xac8 past the scene's objects: on the Japanese rerelease that place is 0xc58 past them, so selfade read a zero, took the shade to its floor and drew a boss black on the select. Only the rerelease's bytes change; its pin follows

* Z-Gradt's AI state laid out per build: the rerelease keeps its globals in another order, so the laser's state, the fly-in timer and the side's tables sat elsewhere than retail's offsets from the first, and the jump's turn to face read another global as the laser out. The state's first, its length in dwords and the seven offsets the patch touches are symbols (BS_BSSA, BS_AIDW, BS_AI*), its copies reach them through a register, and the buffers hold the longest build's. Pins follow

* Z-Gradt moves on the arena it is on: its own floor query is a box 280 each way, the moon gate's size, so on a bigger arena it could not cross the field and on a smaller one it walked past the walls and through obstacles. On every arena but the moon gate's, its three calls go to zfloor_a, which asks the arena's floor as the eight do (BS_FLOORA): a wall as the query gives it, a rise over 4 above the floor under it a wall facing the way it moves, a smaller one taken with its height following the ground. The moon gate keeps the box (BS_ZBOXA). Pins follow. Played on all four builds: the edge, the walls and the obstacles stop it

* The hangar draws a boss out to where selfade's measure of it reaches the eight's 28.43 (selcull_a: 38.43 for Jaguarandi, 110.65 for Z-Gradt), so it comes into view black as the eight do, not part lit at 28.43 world units; sel_zsight goes. The win shots that search for floor under the camera step the boss's distance down a quarter of its extra a turn round, four turns, before the game's own (WINTRY), which for Jaguarandi framed only part of it. Pins follow; not yet played

* Jaguarandi's win shots start no nearer than 60: the shots that pull back start at 15 from the subject, inside its legs however the distance is scaled, so a boss's distance is now the game's times its factor and at least its minimum (winfactor; win_jmin 60, Z-Gradt's none), the floor search's later turns keeping their share of both. Pins follow; not yet played

* Jaguarandi's win shots close in no nearer than 100: the shots that close in end at 15 from the subject, inside its legs however the distance is scaled, so a boss's distance is the game's times its factor and at least its minimum (winfactor; win_jmin 100, Z-Gradt's none), the floor search's later turns keeping their share of both. Pins follow; not yet played

* A boss's chase camera never closes below the poses' 56 (cam_min, Z-Gradt's zcam_min before), so the CPU coming near does not zoom it into Jaguarandi either; Jaguarandi's win shots close in no nearer than 250 (win_jmin, 100 before: at 100 the eye still stood inside it). Pins follow. Played on retail: the win shots frame Jaguarandi whole on the city, the hills and the ruins

* Jaguarandi keeps its size through its win pose: the routine its model header's slot 0x43c names for the pose (0x50e230) adds 0.02 to the object's scale each frame without end, so the player's Jaguarandi grew through YOU WIN and the win camera stood inside it. jwin_a, in place of the slot call in state 0x2a's handler, puts the player's scale back after it. The win shots' minimum distance goes (win_jmin, win_zmin): the growth was what the win camera could not frame, and the shots are the game's times the boss's factor again. Pins follow; not yet played

* Jaguarandi grows through its win pose as the game has it (its model's routine for the pose adds 0.02 to its scale a frame): the scale hook jwin_a goes, and its win shots keep at least 250 (win_jmin) to frame it as it grows. Reverts 35efaca. Pins follow. Played before the hook: the shots framed it whole on the city, the hills and the ruins

* Tester's notes: the bosses do not stand in the two-player hangar (they were drawn there from Raiden's place in the wrong palettes); the Quick VR Select shows the eight only - no boss portraits, no shift of the row and marks, the stock countdown, Raiden to start on (BS_SELQUICK, the game's flag for it, the row made again if the flag comes after it); a boss's colour cycle has all eight colours of each machine, not seven (each machine's first was left out, so a boss could not wear a machine's own); and the player's Z-Gradt stands still while its super laser is out, as the CPU's does (zstill, its levers neutral from its tick). Pins follow; not yet played

* The hangar's lineup is the game's own in two players and with nothing unlocked: the two boss records went in whatever the mode, and two-player showed a black strip at the row's start. The confirm's hiding of the machines goes to the bound the lineup has (selhide_a, BS_SELHIDEX), 10 or 12. Z-Gradt still moved with its laser out: its code moves by the stick as the clip decodes it, +0x10a and +0x10e, not the lever word; zstill zeroes those too. Pins follow; not yet played

* Z-Gradt stands still while its laser is out: its code moves by the stick as the clip decodes it, and holding the levers or the decoded fields from its tick left it moving; now every place it would move to is a wall while the laser state is 0 or over (zfloor_a), and its own code stops it as at an arena's edge. zstill goes. The one-player lineup has the bosses' records as before, whatever the unlocks (the last commit tied them to the level read and the bosses went missing); two players keeps the game's own. Pins follow; not yet played

* The Quick VR Select has the bosses: its own copies of the cursor's limit, the colour select and the hiding before a launch go to the hangar's hooks, and its texts for a boss cursor go to selqinfo_a, which has the game write PLAYER n and its blanks for no machine and then the boss's weapons, name and code where it puts the eight's (BS_SELQINFOA, BS_QTXTX, BS_QTXTY). The exclusions of the last commits go (BS_SELQUICK): the quick select is what some play one player on, and it lost the bosses. Pins follow; not yet played

* The quick select's four weapon lines are blanked before the game writes one of the eight's, as the hangar's are: it wrote its shorter lines over a boss's longer ones, and the tails stayed. Pins follow. Played: the quick select shows and takes both bosses

* The 1P mark left on Temjin: the marks' row is cleared before they are drawn each frame, 62 tiles from its start, in the hangar and in the quick select, and the shifted row puts the first portrait's mark before that start, so once drawn (the select started on Temjin) it stayed. The clear starts the shift earlier and goes twice that further (selclr_a), in place of the hangar's fixed widening, which the quick select's clear never had. Pins follow; not yet played

* Z-Gradt with its laser out neither turns nor walks in place: zlevers_a replaces its tick's lever decode (BS_LEVERSA) with the game's own rest routine (BS_NOLEVERSA, the fly-in's) while the player's laser state is 0 or over, so no turn, walk or dash is chosen; zfloor_a's walls stay. Pins follow. Played on all four builds

* Z-Gradt's super laser on Dash instead of Guard: the trigger in its tick read the guard gesture's byte (+0x13b, both levers held in); for the player's, zfire_a reads the turbo bytes (+0x142, +0x143), 0xff on the frame a turbo goes down, which the Dash bind sets both of, so it fires on the press and Guard does nothing on it. Pins follow. Played on all four builds

* The KO replay keeps Z-Gradt's AI globals with the objects: the game rewinds the fight from a ring of the two objects, every fourth frame, which never held the laser's state, the gold ramp or the draw's count, so the replay's laser ran out of step with its beams and particles; zring_a keeps the live block, the player's bank and the lock-on in a ring of its own at the twelve copies of object A. The lever hold leaves the win and lose screen (state 0xc) and the replay (0x14) to the decode, which feeds the poses and the recording there: held through a laser KO it lost Z-Gradt its win pose and the screen its call. Pins follow. Played on all four builds

* Z-Gradt moves with its super laser out, as the CPU's does: the lever hold (zlevers_a, BS_LEVERSA, BS_NOLEVERSA) and the wall zfloor_a answered its moves with go, with their docs. Pins follow. Played on all four builds

* Docs brought up to the code: MAP's .vobs size and the bosses row (168 sites), BOSSES's blob sizes with the replay's ring among the buffers, asm/README's section list with the player's Z-Gradt and the ring and the one hook that runs in every fight; the source section holding the player's Z-Gradt hooks named for them

* Jaguarandi's red segments stay in view: they are its luminous light group, each in the plane of a face of a neighbouring part, and the renderer's nearest-first sort let the two swap buckets as the legs moved, so they blinked and went out as it turned; jlum_a, at the mesh draw's key store, moves a luminous polygon's key one bucket nearer when the polygon lies in Jaguarandi's file, found through the loader's slot table in every state, the select's own copy included. Pins follow. Played on all four builds

* Z-Gradt's laser gauge, a fourth HUD strip under the three (zgauge_a, at the strips' epilogue): full and in the bars' colour while the laser is ready, red and draining as the beam fires, refilling as the cannon folds; Dash on it out gets the strips' own CHARGING block, the refusal sound once per press and the swollen bar, as a weapon's button gets on an empty gauge. The bosses' weapons renamed in the HUD headers and the hangar lines: Jaguarandi's as the Sega Ages release has them (BAZOOKA, TRAP BOMB, 10 LASER), Z-Gradt's after the eight's (RING BEAM, PHALANX, FLOATING MINE, LASER CANNON), one order in both. Z-Gradt's header name, ZIG-RAHT as shipped and printed by TODAY'S TOP OF WINS, written Z-GRADT. The JP rerelease's name hook moved to player 1's report copy: it sat on player 2's and the Player Data Report showed a D. Pins follow. Played on all four builds

* A boss keeps its colours through a continue: the select drew from the game's own copy of the model file when a fight had loaded it, and selcol's turn of the colour words to the hangar's rows stayed in that copy, so Z-Gradt's turrets, ring and leg caps came out in other machines' colours until a new run reloaded the file; the select now always draws from its own copy (sel_loadrb, sel_slotsin, sel_slotsout keep and put back the game's pointer). The laser gauge fills as a strip fills from its heat, the cost taken as the beam fires and climbing back in percent steps to idle, drawn as a live close-combat bar (full, the third material, pulsing in height) through the deploy and the charge, and the button read as the strips read theirs: held and not ready for the block, the sound once per hold and the swell, the timer cleared at once when ready. Z-Gradt's three strips take the live colour while their button is down (zbar_ra, zbar_la, zbar_ba, at the strips' pushes of the ready material). The HUD names shortened to the strips' ten letters as the game shortens Bal-Bas-Bow's: F MINE, CANNON. Pins follow. Played on all four builds

* Docs for the bosses in plain sentences: a Playable bosses section in the README in the widescreen section's shape (the pictures to follow), its list entry cut to a pointer and What gets written naming the portraits, the section and bosses.bin; BOSSES.md rewritten end to end, the laser gauge, the strips' tell and the colours through a continue under In the game, its long bullets split; the bosses sections of NOTES.md and asm/README.md the same, the latter's list of the file's sections a list; a Play it first item for the bosses. Four duplicate symbol keys dropped from the build tables and two comments rewrapped in the patcher; the map's regions refreshed

* Pre-unlock writes bosses.bin over a value above 2, which the blob reads as none; MAP's section addresses and the bosses' site count brought up to the file, NOTES' hook count with them

* README: the Playable bosses section's pictures - the select with each boss, the two unlock screens, Jaguarandi in a match and Z-Gradt's laser - in assets/readme, corners rounded like the rest

* README tightened for players: the patch mechanics and the duplicated add-on text out, every section shorter, the developer docs pointed to where they help; the bosses in the opening paragraph and the navigation line

* portraits.py --check and a portraits check, so the committed portrait blob cannot drift from assets/portrait_*.png unnoticed (CI installs Pillow; without it the check passes with a note); MAP's repository listing gets an assets/ row

---------

Co-authored-by: pairomaniac <pairo@segaonline.net>
…eir sections' openings, the patch compatibility said once under What the patches do; What it copies, At runtime and the add-on table folded into their sections
…rep stosw with the rep byte by hand as ui.asm has it, movsx eax, ax written as the cwde 3.x makes of it; the blob rebaked and the four all-patches MD5s repinned, DEVELOPING's version note with them. CI's asm check failed on the merge
…e does not cover, the boss portraits named there as pixel art after SEGA's renders; BOSSES.md says the same of them
Preserve Pairomaniac and Vortyne history through 4ecae62. Retain Controller Expansion and Experience UI; resolve the hangar entry offset through the existing Experience constant. Keep boss assembly and single-player rules unchanged.

Copy link
Copy Markdown
Owner Author

Superseded by the frozen Single-Player Playable Bosses integration merged through #6 and published in v0.18.1. Closing without merging this earlier implementation. Its development history remains preserved in local references and the verified cleanup recovery bundle.

@hyp36rmax hyp36rmax closed this Oct 22, 2026
@hyp36rmax
hyp36rmax deleted the feature/playable-bosses-upstream branch October 22, 2026 04:43
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants