Most notably from `th01/core/str_val`, where it's really in the way.
That mistake will haunt me until this all is done, probably…
Part of P0158, funded by Yanga.
The one with lasers fired from the sword across the whole playfield,
either from left to right or from right to left, together with aimed
3-way spreads, every 10 frames.
Part of P0155, funded by Ember2528.
Similarly to cb6367a, you also can't tell from a function's ASM whether
a parameter that's assigned to an 8-bit destination is actually an 8-bit
parameter, or a narrowed 16-bit parameter. That can really only be
gleaned from looking at all call sites…
Part of P0155, funded by Ember2528.
At least I *hope* it's an improvement. `point` should be reserved for
actual structures with .x and .y members.
Part of P0149, funded by Blue Bolt, Ember2528, and -Tom-.
We're going to need some name for the longer, boss-specific danmaku
animations. Turns out that these are exactly called "patterns" in
Sparen's glossary:
https://sparen.github.io/ph3tutorials/ddsg0.html#sub4
So what we called a "pattern" is actually called a "group". Whoops!
Part of P0142, funded by Yanga.
Amazing how both an `int` and an `unsigned char` parameter generate the
exact same ASM if they're only passed on to a single function that
widens them to a 16-bit type.
Part of P0142, funded by Yanga.
This gets rid of a couple of per-entity sprite bitplane types, makes
sprite declarations easier to read by putting width and height next to
each other… and points out a number of array dimension mistakes -.-
Even in places where we can't use it.
Part of P0138, funded by [Anonymous] and Blue Bolt.
Two ZUN bugs in a single function call! These end up causing
effectively random sprite pixels to disappear during the transition to
the boss clear tally screen, if the boss was killed while any shootout
lasers were on screen. And once again, one of those bugs would have
been a non-issue with strong enough typing…
Oh well, that's all there is to this class.
Completes P0122, funded by Yanga.
Not to be confused with stationary lasers (as used by YuugenMagan, for
example), which are just regular lines with collision detection. 🥴
Also, 22 unused bytes out of 69, with another 11 that could have easily
been exchanged for better code…
Part of P0122, funded by Yanga.
Not only getting rid of the "useless" (and thankfully, consistent)
bitplane parameter, but also allowing those shortened macros to be
redefined for the upcoming little ZUN inconsistency.
Part of P0120, funded by Yanga.
I'm still impressed that something like this perfectly inlines in every
place that uses this functionality so far. Maybe something about each
parameter only being used once inside the single expression of the
function?
Part of P0120, funded by Yanga.
We've been establishing `screen` as meaning "a coordinate rooted at the
top-left corner of the display", whereas most of the Subpixels in >TH01
are rooted at the top-left of the playfield.
Part of P0112, funded by [Anonymous] and Blue Bolt.
… and this one, while I'm at it. I've been using pretty much every
possible type for VRAM offset variables, depending on my mood that day,
since signedness apparently never matters for those.
Except that it does. And so, just like with most of our high-level
types, we also have to account for ZUN's little signedness
inconsistencies here. Oh well, at least it's now only one of two types,
and there's no need to choose between `int` or `unsigned int` or
`short` or `unsigned short` or `int16_t` or `uint16_t` or `size_t` or…
Part of P0111, funded by [Anonymous] and Blue Bolt.
Whew, time to look at every `int` variable we ever declared! The best
moment to do this would have been a year ago, but well, better late
than never. No need to communicate that in comments anymore.
These shouldn't be used for widths, heights, or sprite-space
coordinates. Maybe we'll cover that another time, this commit is
already large enough.
Part of P0111, funded by [Anonymous] and Blue Bolt.
How optimized is TH01? Well, since the player Y is constant, checking
for Y ≥ 368 first would rule out a collision for the majority of
entities during gameplay after just 1 check.
TH01 still checks X first.
Part of P0102, funded by Yanga.
Or, in more relevant news: That's the function that forced TH01's
pellet sprites to be defined in C land. First sprite to make that jump.
Part of P0102, funded by Yanga.
… pellet unblitting uses a *doubly* sloppy 16×8 rectangle. 🤦
The resulting terrible flickering is probably why the Stage 15 and 20
battles enable this weird "interlace" mode that only renders and
hit-tests half of the pellets each frame… except that player shots
are still hit-tested every frame?
So yeah, your eyes aren't deceiving you, the game does effectively drop
its perceived frame rate in the Elis, Kikuri, Sariel, and Konngara
fights, and it does so deliberately.
And *then* you realize that those weird hit tests are actually a
futile attempt to mitigate the disastrous effects of a way too large
unblitting rectangle. Congratulations, you've found the most stupid
piece of code in this game.
Part of P0102, funded by Yanga.
Note how Turbo C++ auto-generates that call to `operator new`, which
you don't see in the decompilation anymore. So yeah, as soon as you add
a constructor, Turbo C++ enforces heap allocation for any instance of
that class, even function-local ones that would otherwise be
stack-allocated.
That's where the bad reputation of C++ comes from, I guess?
Part of P0102, funded by Yanga.
First off: 👏 Just 👏 pass 👏 a 👏 reference 👏 to 👏 the 👏
currently 👏 iterated 👏 pellet 👏 if 👏 you 👏 need 👏 to 👏
modify 👏 it 👏
Second: Don't we hit-test pellets vs. shots already, in the CShots
class? So what is this garbage…?!
Completes P0101, funded by Yanga and Ember2528.
Actually quite cute with their hardcoded limitations, since the created
patterns do follow a clear logic.
But yeah, no 6-way spreads for you, we only support 2-, 3-, 4-, and
5-way ones here :zunpet:
Part of P0101, funded by Yanga and Ember2528.
Look, an unused pellet motion type! Which doesn't work as intended
thanks to no fewer than 3 ZUN bugs in one single if() expression.
Part of P0100, funded by Yanga.
A 4227-byte structure, with 100 pellet_t instances followed by a few
other data members. A textbook example of why you can't just quickly
get full position independence by parsing individual lines of ASM.
Part of P0100, funded by Yanga.
And immediately, we discover another two hardcoded sprites, with, of
course, another set of functions for blitting and unblitting them…
Part of P0099, funded by Ember2528.
Which works in both Borland C++, Open Watcom, and Visual C++.
Not that we're about to port any of the games to these compilers, just
something I noticed while evaluating 32-bit compilers for ReC98's own
32-bit pipeline tools. Modders might want to look into that though,
since 100% position independence also makes it easier to change
compilers.
So even TH01 wasn't 100% C++ after all. Turns out that this function
was the only instance in all of REIIDEN.EXE where ReC98 previously had
different encodings for identical x86 instructions.
Part of P0096, funded by Ember2528.