Difference between revisions of "G-Stream G2020"
m |
m |
||
| Line 184: | Line 184: | ||
4. be60: popcorn enemies plus 8x FUN_00422df0. Snapshot at 250f (second | 4. be60: popcorn enemies plus 8x FUN_00422df0. Snapshot at 250f (second | ||
| − | + | phase): | |
- Empty -> b9f0 | - Empty -> b9f0 | ||
| Line 205: | Line 205: | ||
8. af70 and b120 both lead to the boss (FUN_0045a8c0). | 8. af70 and b120 both lead to the boss (FUN_0045a8c0). | ||
| + | |||
DUMP GROUND (played 2nd) | DUMP GROUND (played 2nd) | ||
1. a3d0: opening waves. "Clear or 600f" only speeds it up; there's no | 1. a3d0: opening waves. "Clear or 600f" only speeds it up; there's no | ||
| − | + | branch. Then a300. | |
2. a300: FUN_00465fc0 every 64f for 400f. Race: clear the screen within | 2. a300: FUN_00465fc0 every 64f for 400f. Race: clear the screen within | ||
| − | + | 80f after spawning stops. | |
| + | |||
- Won -> a1b0: "WARNING!" mid-boss FUN_00432480, plus Steam | - Won -> a1b0: "WARNING!" mid-boss FUN_00432480, plus Steam | ||
achievement #7 | achievement #7 | ||
- Lost -> a290 (timed 1200f) | - Lost -> a290 (timed 1200f) | ||
- Both then go to a0a0. | - Both then go to a0a0. | ||
| + | |||
3. a0a0: 3x FUN_004260f0. Race 700f. | 3. a0a0: 3x FUN_004260f0. Race 700f. | ||
| + | |||
- Won -> 9fc0: a ~60s extra section (FUN_00469e30), plus achievement | - Won -> 9fc0: a ~60s extra section (FUN_00469e30), plus achievement | ||
#8, then the boss | #8, then the boss | ||
- Lost -> straight to the boss (9f60 -> FUN_004697a0) | - Lost -> straight to the boss (9f60 -> FUN_004697a0) | ||
| + | |||
OCEAN (played 4th) | OCEAN (played 4th) | ||
1. dc80: big opening formation. Race 1024f. | 1. dc80: big opening formation. Race 1024f. | ||
| + | |||
- Won -> da10 (sky colour fade), then d820 | - Won -> da10 (sky colour fade), then d820 | ||
- Lost -> d820 | - Lost -> d820 | ||
| + | |||
2. d820: race 660f. | 2. d820: race 660f. | ||
| + | |||
- Won -> d750 (fade) -> d5b0 | - Won -> d750 (fade) -> d5b0 | ||
- Lost -> d480 -> d250 | - Lost -> d480 -> d250 | ||
| + | |||
3. d5b0: snapshot at 1300f: | 3. d5b0: snapshot at 1300f: | ||
| + | |||
- Empty -> d120 | - Empty -> d120 | ||
- Not empty -> d250 | - Not empty -> d250 | ||
| + | |||
4. d250: second wave, race 800f. | 4. d250: second wave, race 800f. | ||
| + | |||
- Won -> cf90: "WARNING!" ~34s hazard section, plus achievement #9, | - Won -> cf90: "WARNING!" ~34s hazard section, plus achievement #9, | ||
then cc30 | then cc30 | ||
- Lost -> d120 | - Lost -> d120 | ||
| + | |||
5. d120: 2+2x FUN_0042ac90, race 600f. | 5. d120: 2+2x FUN_0042ac90, race 600f. | ||
| + | |||
- Won -> cc30 | - Won -> cc30 | ||
- Lost -> straight to the boss, skipping everything below | - Lost -> straight to the boss, skipping everything below | ||
| + | |||
6. cc30: race 450f on the second wave. | 6. cc30: race 450f on the second wave. | ||
| + | |||
- Won -> c740 -> c280 ("WARNING!" section: FUN_00432500 every 8f for | - Won -> c740 -> c280 ("WARNING!" section: FUN_00432500 every 8f for | ||
900f) -> boss | 900f) -> boss | ||
Revision as of 15:55, 16 October 2026
On 16th October 2026, community member 'Shmups Hobby' decompiled the code of Deltazeal from the Steam version using Steamless and Ghidra.
The decompiled code was then passed to Anthropic's Claude AI, which took less than 10 minutes to figure out the branching path conditions.
Claude's achievement here was noteable at the time. The conditions were not previously well understood, and since one of them is an RNG dice roll would have been difficult to prove conclusively from direct play.
The conversation is preserved below, including Function names instead of enemy descriptions because Claude could not 'see' the enemy sprites.
You can work out the rest...
- - - - -
DELTA ZEAL - BRANCHING PATH ANALYSIS Conversation export (16 October 2026) Source analysed: deltazealunpacked.exe.c (Ghidra decompilation)
==========================================================
USER
I have a decompiled C program which is the game 'Delta Zeal'. The game has branching paths per stage, and I am trying to work out the conditions for the branching paths
CLAUDE
I'd be glad to help trace the branching logic, but I don't see the decompiled code here yet. Could you attach the file(s)? If they're in a folder on your computer, you can also choose "Add folder" from the "+" menu next to the message box in the Claude desktop app, and I can read them directly.
USER
[attached: deltazealunpacked.exe.c]
CLAUDE
I found it: the branch is decided by which side of the playfield your ship is on, about 100 frames (~1.7 s at 60 fps) after the boss is beaten. Nothing else feeds into it: not score, timer, kills or items.
THE DECISION FUNCTION: FUN_00484f20
uint FUN_00484f20(void) {
piVar1 = P1 object;
if (P1 not active && (piVar1 = P2 object, P2 not active))
return FUN_004907e0(1); // game RNG: 0 or 1
return (0x9f < *(short*)(piVar1 + 0x3d6)); // ship X (integer part) >= 160
}
- The value checked: offset +0x3d4 is the ship's X position (16.16 fixed point; +0x3d8 is Y), so +0x3d6 is the whole-number part.
- The playfield is 320 wide but the screen is only ~240. FUN_00438ca0 and FUN_00438b60 scroll the view sideways by 0-80 px. In single player the camera sits at X/4, so a ship at X=160 appears at screen x=120, the exact middle. In practice it's left half of the screen vs right half.
- Two players: P1's position counts if P1's ship is on the field. P2's counts only when P1 has no ship out.
- No ship on the field (both dead or respawning at that moment): a coin flip using the game's own random generator (FUN_004907e0). It's seeded per game, so replays come out the same.
WHEN IT'S CHECKED
Each boss's code starts a small end-of-stage task (FUN_00447220 or FUN_00465f70). It counts frames, and once param_1[1] > 100 it calls FUN_00484f20 and passes the chosen next stage to FUN_0041a950, the stage-clear routine. You still have control during that window, so you can drift to the side you want.
On the Ocean boss (FUN_0044bb50), the task starts in two cases: when the boss is destroyed, or when it flies off the top of the screen (state 5 -> 6, Y < -0x9F). So the branch also applies if the boss escapes.
ROUTE MAP
The internal stage ID (DAT_01927f60+0x5c) doesn't match the order you play the stages in. I worked out the real order from three things: the title screen starts a new game on FUN_0045c200, each stage's clear routine names the next stage, and the clear-flag IDs passed to FUN_004ac590 run 0x14 to 0x1B in the same sequence.
Played | Internal ID / name | Init func | Cleared -> next
-------+------------------------+--------------+------------------------------------
1 | 3 - Cloudy Today | FUN_0045c200 | -> Dump Ground
2 | 5 - Dump Ground | FUN_0046a740 | BRANCH: X<160 (left) -> Ray Base;
| | | X>=160 (right) -> Tank the Wild
3a | 2 - Ray Base | FUN_00454d30 | -> Ocean
3b | 4 - Tank the Wild:S | FUN_004627d0 | -> Ocean
4 | 1 - Prologue: Ocean | FUN_00447aa0 | BRANCH: left -> Green Canal;
| | | right -> Mountainous Zone
5a | 6 - Green Canal | FUN_00470f70 | -> Dark Shadow
5b | 7 - Mountainous Zone | FUN_00476430 | -> Dark Shadow
6 | 8 - Dark Shadow | FUN_00479ee0 | -> Into Airdome
7 | 9 - Into Airdome | FUN_004822f0 | final stage
There are only two branch points, and both use this same left/right check.
Worth checking in-game: "PROLOGUE: OCEAN" being the 4th stage looks odd. Everything in the code agrees on the order above, but the in-game names come from the stage-name banner (FUN_0041a280), which is keyed on the internal ID. One playthrough watching the banners would settle which name goes with which stage.
Which ending task belongs to which stage: for Cloudy Today, Dump Ground, Ocean and Dark Shadow I traced it through the calls directly. For Ray Base, Tank the Wild, Green Canal and Mountainous Zone, the boss code is only reached through data tables. I matched those by where the code sits in the file and by the clear-flag order, and both agree.
USER
But within the stages there are also branches, before you reach the boss. Can you figure those out too?
CLAUDE
Yes. Three stages branch partway through: Cloudy Today, Dump Ground and Ocean. Unlike the stage-to-stage branch, which depends on where your ship is, these depend on whether the screen is clear of enemies. One branch also has a random element.
HOW IT WORKS
Cloudy Today (FUN_0045c200), Dump Ground (FUN_0046a740) and Ocean (FUN_00447aa0) are built as chains of section tasks. Each section spawns its enemies, then hands over to the next section with FUN_00490bc0(next). The branch test is:
FUN_00488fb0(0x20000000) // counts live objects with flag 0x20000000
Every enemy's flags include 0x20000000 (e.g. 0x28000001, 0x24000000), so this returns how many enemies are alive. Enemies that fly off-screen are removed as well, so "0" means everything was killed or has left. The test comes in two forms:
- Race: "if the count hits 0 before frame N, go to A; at frame N, go to B." You have to clear the wave fast.
- Snapshot: at exactly frame N, "count == 0 ? A : B". Only the moment of the check matters.
The other stages (Ray Base, Tank the Wild, Green Canal, Mountainous Zone, Into Airdome) are driven by scrolling spawn tables (FUN_0043ea90 / FUN_00454a00). I found nothing in their code that checks enemy count, randomness or player position to change section. Dark Shadow is essentially a boss sequence. As far as the code shows, those six are linear.
Frame counts are at 60 fps and are counted from the start of that section's current phase.
CLOUDY TODAY (played 1st)
1. Intro c1a0: 640f, then go to c090. 2. c090: up to frame 500, a new pair of FUN_004571d0 spawns whenever the screen is empty. Snapshot at 650f: if the screen is empty, roll RNG(0..7)
- Screen empty and the roll is 0 (1 in 8) -> bda0 - Otherwise -> be60
3. bda0: a single FUN_0042fa20.
- Cleared before 2500f (~42s) -> bd00 (900f bonus-style section),then b9f0 - Cleared later, or 6000f timeout -> b9f0
4. be60: popcorn enemies plus 8x FUN_00422df0. Snapshot at 250f (second
phase):
- Empty -> b9f0 - Not empty -> baf0
5. b9f0: 2x FUN_00433360.
- Race 500f won -> b6c0 -> (long wave) -> b4c0 -> af70 - Lost -> af70
6. baf0: race 430f.
- Won -> b1e0 - Lost -> (wave) -> b380 (timed, no check) -> b1e0
7. b1e0: race 500f.
- Won -> af70 - Lost -> b120
8. af70 and b120 both lead to the boss (FUN_0045a8c0).
DUMP GROUND (played 2nd)
1. a3d0: opening waves. "Clear or 600f" only speeds it up; there's no branch. Then a300. 2. a300: FUN_00465fc0 every 64f for 400f. Race: clear the screen within 80f after spawning stops.
- Won -> a1b0: "WARNING!" mid-boss FUN_00432480, plus Steam
achievement #7
- Lost -> a290 (timed 1200f)
- Both then go to a0a0.
3. a0a0: 3x FUN_004260f0. Race 700f.
- Won -> 9fc0: a ~60s extra section (FUN_00469e30), plus achievement
#8, then the boss
- Lost -> straight to the boss (9f60 -> FUN_004697a0)
OCEAN (played 4th)
1. dc80: big opening formation. Race 1024f.
- Won -> da10 (sky colour fade), then d820 - Lost -> d820
2. d820: race 660f.
- Won -> d750 (fade) -> d5b0 - Lost -> d480 -> d250
3. d5b0: snapshot at 1300f:
- Empty -> d120 - Not empty -> d250
4. d250: second wave, race 800f.
- Won -> cf90: "WARNING!" ~34s hazard section, plus achievement #9,
then cc30
- Lost -> d120
5. d120: 2+2x FUN_0042ac90, race 600f.
- Won -> cc30 - Lost -> straight to the boss, skipping everything below
6. cc30: race 450f on the second wave.
- Won -> c740 -> c280 ("WARNING!" section: FUN_00432500 every 8f for
900f) -> boss
- Lost -> c300 -> boss
The boss starts from c250 (FUN_0044bb50) once FUN_00489600() returns 0. That function counts entries in a separate object list, probably bullets or effects.
PATTERN AND CAVEATS
Clearing waves fast takes you down the longer route with more content. Being slow cuts sections short and takes you to the boss sooner.
The hidden sections line up with Steam achievements 7, 8 and 9, which suggests they're the intended secret routes. The game passes DAT_01fc6aac (looks like the main player's slot) to FUN_00509d00, which checks that the player is still in the game; the achievement is given only if that's true.
I don't know what each spawned enemy actually looks like in-game. The enemy functions (e.g. FUN_00433360, FUN_0042fa20) would need checking against footage. I also couldn't read the spawn tables for the scroll-driven stages, because the data section isn't in the decompile. I checked all their code for branching logic and found none, but logic stored only in those data tables wouldn't show up.
USER
Export this conversation as a text file
CLAUDE
(This file.)