Reason: That switch statement. How should we even?
Well, the code *is* fairly good. After looking very deep into it, and
spending 35% of that function on blank lines (for logical grouping) and
explanatory comments, that is…
Part of P0152, funded by -Tom- and [Anonymous].
Deciding whether to decompile this one or not seemed to be a tough
choice. Should we *really* introduce two more translation units just
for the sake of decompiling another function that's identical to its
TH04 counterpart anyway?
Well, turns out it actually isn't: TH05 does in fact *not* immediately
clip bullets that are spawned on top of the player. Which might sound
like it has a notably different effect on gameplay… except that it
doesn't.
So yeah, good we've decompiled it, and got to show that more clearly.
Part of P0152, funded by -Tom- and [Anonymous].
Reason: Self-modifying. -.- And saving SI and DI on the stack way too
late.
Luckily, it's mostly identical to TH04's version I decompiled earlier,
only (thankfully) combining regular and special bullets into one
function, and integrating TH05-specific improvements.
Part of P0152, funded by -Tom- and [Anonymous].
Only used for the revenge bullets fired from Stage 3 Alice's barrier.
Not worth creating a separate translation unit, especially since the
function above is undecompilable as well…
Part of P0152, funded by -Tom- and [Anonymous].
TH05's undecompilable bullet spawn code needs them, adding yet another
dumb file that we'll always have to drag along…
Part of P0152, funded by -Tom- and [Anonymous].
Spent 4 pushes on the basic types and constants in 2020, still ended up
up getting this wrong and documenting the opposite of what TH05 actually
does…
Part of P0152, funded by -Tom- and [Anonymous].
… (24 + (difficulty * 8) + rank) in TH04, and (42 + (difficulty * 8))
in TH05. Also, TH05 only doesn't have TH04's bullet zap animation
because ZUN didn't consistently use constants…
Completes P0151, funded by Blue Bolt and -Tom-.
Slow Mode only does one thing: It looks at how many 8×8 pellets or
16×16 bullets are alive, and reduces the frame rate by 33% (i.e., turns
every second frame into two frames) if that number is…
Part of P0151, funded by Blue Bolt and -Tom-.
First place to confirm the hitbox of both the 8×8 pellets and the 16×16
sprite bullets!
Well, "hitbox". It's really more of a kill delta of 8×8 pixels between
the center points of a bullet and the player. You can distribute these
pixels to any combination of bullet and player "hitboxes" that make up
8×8. 4×4 around both the player and bullets? 1×1 for bullets, and 8×8
for the player? All equally valid… or perhaps none of them, once you
keep in mind that other entity types might have different kill deltas,
which turns the concept of a "hitbox" into just a confusing abstraction.
Part of P0150, funded by Blue Bolt.
TH05's version, which we saw way back in ac7e6bc, is almost identical
and can be used for any speed, whereas this one is hardcoded to modify
only the speed in the bullet template.
Part of P0150, funded by Blue Bolt.
`switch` statements compiling to binary searches if the range of values
is nasty enough? That's so cool. Apart from a few places in TH02,
this is the only place in PC-98 Touhou to show off that Turbo C++
optimization.
That code's still unexpectedly janky for what you'd expect from the 4th
game in the series, though.
Part of P0150, funded by Blue Bolt.
Still way premature to say things like "the base speed of a bullet
aroup is not affected by difficulty, only by rank". (Which also would
be a way less meaningful statement than you might think it is.)
Part of P0150, funded by Blue Bolt.
Too bad that it would have made no sense to call them "goofy bullets".
"Regular" is also a better name than "normal" (which is a difficulty!).
Completes P0149, funded by Blue Bolt, Ember2528, and -Tom-.
motion_t is also used for certain animations in MAINE.EXE, so not all
instances refer to entities in playfield space. Explicitly specifying
the latter now allows us to gain…
Part of P0149, funded by Blue Bolt, Ember2528, and -Tom-.
Yup, P0148 didn't actually solve the issue it was meant to solve, and
I still had to research this workaround.
Part of P0149, funded by Blue Bolt, Ember2528, and -Tom-.
Well. The more random bullets at constrained angles are added to a
bullet group, the closer that group will invariably approximate a
spread shape. That's why it probably appeared as a "spread" to me in
2020. But the implementation for this group type
• neither uses any of the spread code,
• nor does it ensure a consistent angle between the bullets (which a
hypothetical "random angle spread" group type would probably do).
Also, look at its number within the enum! How could I ever get that
confused?!
Part of P0149, funded by Blue Bolt, Ember2528, and -Tom-.
And actually document them correctly.
Clear: Custom duration, awards constant points per bullet during the
entire duration, plays a decay animation
Zap: Fixed duration, awards a semi-exponential bonus for all bullets
alive on the first frame, plays a, um, "zapping" animation… in
TH04, because it's bugged in TH05 :zunpet:
Part of P0149, funded by Blue Bolt, Ember2528, and -Tom-.
Boom! Decompilable after all. And look what that made us finally point
out: In all 4 games that use this function, its return value is
undefined if BGM is inactive. (That is, if the user disabled it, or if
no FM sound board is installed.)
Part of P0148, funded by [Anonymous].
Well, we *could* pointlessly decompile this function into an unreadable
mess, but only if we throw away the semantics of the return value, and
replace its type with `void` or `uint32_t`.
(Turbo C++ can't cast registers to a struct value, and adding a
constructor to SPPoint would break everything else, if it even works.)
ZUN's original usage code doesn't care either way, because it only ever
accesses the returned value directly through DX:AX.
Part of P0148, funded by [Anonymous].
… Wait, we *can* decompile functions with both parameters, no stack
frame, and a Pascal calling convention?! Good that I already forgot how
impossible it was to decompile the first function I looked at that fell
into this category!
Part of P0148, funded by [Anonymous].
Including the pointless DOS I/O variation in TH05's MAIN.EXE.
I'm slowly running out of characters to remove from the first segment
name in that file, though…
Part of P0148, funded by [Anonymous].
The bullet update function really needs this one declared in C land to
link without a fixup overflow? Guess we'll have another technical debt
push before then…
Part of P0148, funded by [Anonymous].
Palette hacks not included yet. Yup, that's how all phases can use
hardware color #0 as their background color.
Completes P0147, funded by -Tom- and Ember2528.
64 slots reserved, 16 slots actually used, another 768 bytes of
conventional RAM wasted…
(And yes, that naming convention is indeed meant as a shoutout to all
the RPG Maker veterans out there.)
Part of P0147, funded by -Tom- and Ember2528.
Oh wow, we never used that one; I only added it in 042b780 to make it
easier for tool authors to #include "th04/resident.hpp". Bad idea –
if we don't ensure that it *actually works* (and it didn't, as of
d1f3dcd), it's of no help to them either.
Part of P0147, funded by -Tom- and Ember2528.
That could have easily been architected without forcing every boss to
manually set the "background to be drawn while bombing" function
pointer.
And without the laziness of just redrawing all tiles during the blocky
opening animation of certain bombs…
Part of P0147, funded by -Tom- and Ember2528.
Not exclusively used for the boss entrance animations, even though its
data is declared in that general vicinity. It's also used for all bombs
in TH04, and Reimu's and Yuuka's bomb in TH05.
Part of P0147, funded by -Tom- and Ember2528.
Sure, it's still a micro-optimization, but at least it's __fastcall for
once! And doesn't assume ES to have been set to a VRAM segment. So not
*that* optimized 😛
Part of P0147, funded by -Tom- and Ember2528.