ReC98/th04/main/boss/explode.cpp

167 lines
3.8 KiB
C++
Raw Normal View History

#include "platform.h"
#include "x86real.h"
#include "pc98.h"
#include "master.hpp"
#include "th01/math/subpixel.hpp"
#include "th01/hardware/grcg.hpp"
#include "th02/main/hud/hud.hpp"
#include "th03/math/polar.hpp"
#include "th03/hardware/palette.hpp"
#include "th04/math/motion.hpp"
#include "th04/hardware/grcg.hpp"
#include "th04/formats/super.h"
#include "th04/main/phase.hpp"
#include "th04/main/playfld.hpp"
#include "th04/main/boss/boss.hpp"
#if (GAME == 5)
#include "th05/sprites/main_pat.h"
#else
#include "th04/sprites/main_pat.h"
#endif
// Structures
// ----------
#define EXPLOSION_SMALL_COUNT 2
struct Explosion {
bool alive;
unsigned char age;
SPPoint center;
SPPoint radius_cur;
SPPoint radius_delta;
int8_t unused; // ZUN bloat
// Offset to add to the angle for the Y coordinate, turning the circle
// into a slanted ellipse. See https://www.desmos.com/calculator/faeefi6w1u
// for a plot of the effect.
unsigned char angle_offset;
// A method is the most idiomatic way for this to compile into ZUN's
// original three instructions, believe it or not.
unsigned char angle_y(const unsigned char& angle_base) {
return (angle_offset + angle_base);
}
};
#define explosion_update(p) { \
(p).radius_cur.x.v += (p).radius_delta.x.v; \
(p).radius_cur.y.v += (p).radius_delta.y.v; \
(p).age++; \
if((p).age >= 32) { \
(p).alive = false; \
} \
}
extern Explosion explosions_small[EXPLOSION_SMALL_COUNT];
extern Explosion explosions_big;
// ----------
void near explosions_small_update_and_render(void)
{
enum {
SPRITE_W = 16,
SPRITE_H = 16,
SPRITES = 64,
};
_ES = SEG_PLANE_B;
grcg_setmode_rmw();
Explosion near* p;
int sprite;
Subpixel center_x;
Subpixel center_y;
int i;
for((p = explosions_small, i = 0); i < EXPLOSION_SMALL_COUNT; (i++, p++)) {
if(!p->alive) {
continue;
}
sprite = 0;
unsigned char angle = 0x00;
while(sprite < SPRITES) {
#define left _AX
#define top _DX
center_x.v = polar_x(p->center.x, p->radius_cur.x, angle);
center_y.v = polar_y(
p->center.y, p->radius_cur.y, p->angle_y(angle)
);
top = playfield_to_screen_top(center_y, SPRITE_H);
left = playfield_to_screen_left(center_x, SPRITE_W);
if(!playfield_clip_topleft_small(left, top, SPRITE_W, SPRITE_H)) {
z_super_roll_put_tiny_16x16(left, top, PAT_EXPLOSION_SMALL);
}
sprite++;
angle += (0x100 / SPRITES);
#undef top
#undef left
}
explosion_update(*p);
}
grcg_off();
}
void near explosions_big_update_and_render(void)
{
enum {
// ZUN bug: The sprite from MIKOD.BFT is actually 48×48 pixels large;
// assuming 64×64 will misalign the transformation to screen space and
// the clipping condition.
SPRITE_W = 64,
SPRITE_H = 64,
SPRITES = 16,
};
#define frame explosion_big_frame
extern int frame;
Explosion near& p = explosions_big;
int sprite;
// ZUN bloat: Should be a separate PlayfieldPoint and screen_point_t.
subpixel_t x;
#define x_center x
#define x_left static_cast<screen_x_t>(x)
union { Subpixel center; screen_y_t top; } y;
if(p.alive) {
sprite = 0;
unsigned char angle = 0x00;
while(sprite < SPRITES) {
x_center = polar_x(p.center.x, p.radius_cur.x, angle);
x_left = playfield_to_screen_left(x_center, SPRITE_W);
y.center.v = polar_y(p.center.y, p.radius_cur.y, p.angle_y(angle));
y.top = playfield_to_screen_top(y.center, SPRITE_H);
if(!playfield_clip_topleft_large(
x_left, y.top, SPRITE_W, SPRITE_H
)) {
super_put(x_left, y.top, PAT_EXPLOSION_BIG);
}
sprite++;
angle += (0x100 / SPRITES);
}
explosion_update(p);
frame++;
if((frame < 8) && (frame & 1)) {
palette_settone_deferred(150);
} else if((frame >= 8) && (frame < 16)) {
palette_settone_deferred(100 + (16 * 6) - (frame * 6));
} else {
palette_settone_deferred(100);
}
} else {
frame = 0;
}
#undef frame
}