mirror of https://github.com/nmlgc/ReC98.git
753 lines
20 KiB
C++
753 lines
20 KiB
C++
/* ReC98 / TH05
|
|
* ------------
|
|
* Rendering code for all bosses
|
|
*/
|
|
|
|
#include "platform.h"
|
|
#include "x86real.h"
|
|
#include "pc98.h"
|
|
#include "planar.h"
|
|
#include "decomp.hpp"
|
|
#include "master.hpp"
|
|
#include "th01/math/area.hpp"
|
|
#include "th01/math/subpixel.hpp"
|
|
#include "th02/v_colors.hpp"
|
|
#include "th04/hardware/grcg.hpp"
|
|
extern "C" {
|
|
#include "th04/math/vector.hpp"
|
|
#include "th04/math/motion.hpp"
|
|
#include "th04/math/randring.hpp"
|
|
#include "th04/formats/bb.h"
|
|
#include "th04/formats/cdg.h"
|
|
#include "th04/main/frames.h"
|
|
#include "th04/main/playfld.hpp"
|
|
#include "th04/main/phase.hpp"
|
|
#include "th04/main/drawp.hpp"
|
|
#include "th04/main/boss/impl.hpp"
|
|
#include "th04/main/tile/tile.hpp"
|
|
#include "th04/main/tile/bb.hpp"
|
|
#include "th04/sprites/main_cdg.h"
|
|
#include "th05/sprites/main_pat.h"
|
|
#include "th05/formats/super.h"
|
|
}
|
|
#include "th04/main/boss/backdrop.hpp"
|
|
#include "th05/main/boss/boss.hpp"
|
|
#include "th05/main/boss/bosses.hpp"
|
|
|
|
/// Structures
|
|
/// ----------
|
|
#define BOSS_PARTICLE_COUNT 64
|
|
#define LINESET_LINE_COUNT 20
|
|
#define LINESET_COUNT 4
|
|
|
|
// `boss_` to differentiate this structure from `s2particle_t`, which uses the
|
|
// same sprites.
|
|
struct boss_particle_t {
|
|
PlayfieldPoint pos; // [x] == Subpixel::None() indicates a dead particle
|
|
|
|
// [pos] is reset to this value once it has left the playfield, restarting
|
|
// the particle movement.
|
|
PlayfieldPoint origin;
|
|
|
|
SPPoint velocity;
|
|
int age;
|
|
unsigned char angle;
|
|
unsigned char patnum; // if 0, calculated from [age] during rendering
|
|
};
|
|
|
|
// Each final rendered line effectively corresponds to the diameter of the
|
|
// described circle, with the two line points at +[angle] and -[angle].
|
|
struct lineset_t {
|
|
SPPoint center[LINESET_LINE_COUNT];
|
|
Subpixel velocity_y;
|
|
Subpixel radius[LINESET_LINE_COUNT];
|
|
unsigned char angle[LINESET_LINE_COUNT];
|
|
};
|
|
|
|
extern boss_particle_t boss_particles[BOSS_PARTICLE_COUNT];
|
|
extern lineset_t linesets[LINESET_COUNT];
|
|
/// ----------
|
|
|
|
void pascal near sara_bg_render(void)
|
|
{
|
|
boss_bg_render_entrance_bb_transition_and_backdrop(
|
|
tiles_render_after_custom(boss.phase_frame),
|
|
SARA_BACKDROP_LEFT,
|
|
SARA_BACKDROP_TOP,
|
|
0
|
|
);
|
|
}
|
|
|
|
void pascal near louise_bg_render(void)
|
|
{
|
|
boss_bg_render_entrance_bb_opaque_and_backdrop(
|
|
tiles_render_after_custom(boss.phase_frame),
|
|
int,
|
|
ENTRANCE_BB_FRAMES_PER_CEL,
|
|
LOUISE_BACKDROP_LEFT,
|
|
LOUISE_BACKDROP_TOP,
|
|
1,
|
|
louise_backdrop_colorfill,
|
|
0
|
|
);
|
|
}
|
|
|
|
void pascal near alice_bg_render(void)
|
|
{
|
|
boss_bg_render_entrance_bb_opaque_and_backdrop(
|
|
tiles_render_after_custom(boss.phase_frame),
|
|
int,
|
|
ENTRANCE_BB_FRAMES_PER_CEL,
|
|
ALICE_BACKDROP_LEFT,
|
|
ALICE_BACKDROP_TOP,
|
|
1,
|
|
alice_backdrop_colorfill,
|
|
V_WHITE
|
|
);
|
|
}
|
|
|
|
void pascal near mai_yuki_bg_render(void)
|
|
{
|
|
boss_bg_render_entrance_bb_opaque_and_backdrop(
|
|
tiles_render_after_custom(boss.phase_frame),
|
|
unsigned char,
|
|
ENTRANCE_BB_FRAMES_PER_CEL,
|
|
MAI_YUKI_BACKDROP_LEFT,
|
|
MAI_YUKI_BACKDROP_TOP,
|
|
1,
|
|
mai_yuki_backdrop_colorfill,
|
|
9
|
|
);
|
|
}
|
|
|
|
void pascal near yumeko_bg_render(void)
|
|
{
|
|
boss_bg_render_entrance_bb_opaque_and_backdrop(
|
|
tiles_render_all(),
|
|
unsigned char,
|
|
ENTRANCE_BB_FRAMES_PER_CEL,
|
|
YUMEKO_BACKDROP_LEFT,
|
|
YUMEKO_BACKDROP_TOP,
|
|
1,
|
|
yumeko_backdrop_colorfill,
|
|
V_WHITE
|
|
);
|
|
}
|
|
|
|
/// Stage 6 - Shinki
|
|
/// ----------------
|
|
|
|
#define SHINKI_SPINLINE_MOVE_W (PLAYFIELD_W / 6) /* pixel_t! */
|
|
#define SHINKI_SPINLINE_MOVE_SPEED to_sp(0.25f)
|
|
#define SHINKI_SPINLINE_TOP to_sp(80.0f)
|
|
#define SHINKI_SPINLINE_BOTTOM to_sp(PLAYFIELD_H - 64)
|
|
#define SHINKI_SPINLINE_MOVE_DURATION \
|
|
(SHINKI_SPINLINE_MOVE_W * SHINKI_SPINLINE_MOVE_SPEED)
|
|
|
|
static const int SHINKI_LINESET_COUNT = 2;
|
|
static const int PARTICLES_UNINITIALIZED = (-1 & 0xFF);
|
|
|
|
// Maps one of Shinki's four visible lines to its index in a line set.
|
|
static const int SHINKI_LINE_MAIN = (0 * 6);
|
|
static const int SHINKI_LINE_2 = (1 * 6);
|
|
static const int SHINKI_LINE_3 = (2 * 6);
|
|
static const int SHINKI_LINE_4 = (3 * 6);
|
|
|
|
extern unsigned char shinki_bg_linesets_zoomed_out;
|
|
extern int shinki_bg_type_a_particles_alive;
|
|
extern bool shinki_bg_type_b_initialized;
|
|
extern unsigned int shinki_bg_spinline_frames;
|
|
extern bool shinki_bg_type_c_initialized;
|
|
extern bool shinki_bg_type_d_initialized;
|
|
|
|
#define shinki_spinline_x_and_angle(set, delta) \
|
|
if(set->radius[SHINKI_LINE_MAIN] > to_sp(96.0f)) { \
|
|
set->radius[SHINKI_LINE_MAIN] -= 0.125f; \
|
|
} \
|
|
if( \
|
|
(shinki_bg_spinline_frames & ( \
|
|
(SHINKI_SPINLINE_MOVE_DURATION * 2) - 1) \
|
|
) < SHINKI_SPINLINE_MOVE_DURATION \
|
|
) { \
|
|
set->center[SHINKI_LINE_MAIN].x.v += delta; \
|
|
} else { \
|
|
set->center[SHINKI_LINE_MAIN].x.v -= delta; \
|
|
} \
|
|
set->angle[SHINKI_LINE_MAIN] += (delta / 2);
|
|
|
|
#define shinki_spinline_y_formation_top(set, delta, set_i, line_i) \
|
|
delta = (SHINKI_SPINLINE_TOP - set->center[SHINKI_LINE_MAIN].y); \
|
|
set_i = 0; \
|
|
while(set_i < SHINKI_LINESET_COUNT) { \
|
|
for(line_i = SHINKI_LINE_4; line_i >= 0; line_i--) { \
|
|
set->center[line_i].y.v += delta; \
|
|
} \
|
|
set_i++; \
|
|
set++; \
|
|
}
|
|
|
|
#define shinki_spinline_y_formation_bottom(set, delta, set_i, line_i) \
|
|
delta = (set->center[SHINKI_LINE_MAIN].y - SHINKI_SPINLINE_BOTTOM); \
|
|
set_i = 0; \
|
|
while(set_i < SHINKI_LINESET_COUNT) { \
|
|
for(line_i = SHINKI_LINE_4; line_i >= 0; line_i--) { \
|
|
set->center[line_i].y.v -= delta; \
|
|
} \
|
|
set_i++; \
|
|
set++; \
|
|
}
|
|
|
|
void near shinki_bg_particles_render(void)
|
|
{
|
|
_ES = SEG_PLANE_B;
|
|
boss_particle_t near *particle = boss_particles;
|
|
int patnum;
|
|
screen_x_t left_;
|
|
int i = 0;
|
|
while(i < BOSS_PARTICLE_COUNT) {
|
|
if(particle->pos.x.v != Subpixel::None()) {
|
|
particle->pos.x.v += particle->velocity.x.v;
|
|
particle->pos.y.v += particle->velocity.y.v;
|
|
if(particle->patnum == 0) {
|
|
patnum = (particle->age / 8);
|
|
if(patnum >= PARTICLE_CELS) {
|
|
patnum = (PARTICLE_CELS - 1);
|
|
}
|
|
patnum += PAT_PARTICLE;
|
|
} else {
|
|
patnum = particle->patnum;
|
|
}
|
|
|
|
#define left static_cast<pixel_t>(_CX)
|
|
#define top static_cast<pixel_t>(_AX)
|
|
|
|
// Lol, is this only not directly assigned to _CX because ZUN was
|
|
// scared that the _AX assignment might overwrite _CX?
|
|
left_ = particle->pos.to_screen_left(PARTICLE_W);
|
|
top = particle->pos.to_screen_top(PARTICLE_H);
|
|
left = left_;
|
|
if(!(
|
|
(left > (PLAYFIELD_LEFT - PARTICLE_W)) &&
|
|
(left < PLAYFIELD_RIGHT) &&
|
|
(top > keep_0(PLAYFIELD_TOP - PARTICLE_H)) &&
|
|
(top < PLAYFIELD_BOTTOM)
|
|
)) {
|
|
particle->pos = particle->origin;
|
|
particle->age = 0;
|
|
} else {
|
|
z_super_roll_put_16x16_mono(left, top, patnum);
|
|
particle->age++;
|
|
}
|
|
|
|
#undef top
|
|
#undef left
|
|
}
|
|
i++;
|
|
particle++;
|
|
}
|
|
}
|
|
|
|
void near shinki_bg_type_a_update_part1(void)
|
|
{
|
|
boss_particle_t near *particle;
|
|
int i;
|
|
unsigned int angle;
|
|
int line_i;
|
|
lineset_t near *set;
|
|
|
|
if(shinki_bg_type_a_particles_alive == PARTICLES_UNINITIALIZED) {
|
|
set = linesets;
|
|
i = 0;
|
|
angle = 0x40;
|
|
while(i < SHINKI_LINESET_COUNT) {
|
|
for(line_i = 0; line_i < LINESET_LINE_COUNT; line_i++) {
|
|
set->center[line_i].x.v = to_sp(PLAYFIELD_W / 2);
|
|
set->center[line_i].y.v = to_sp(PLAYFIELD_H / 2);
|
|
set->radius[line_i].set(1.0f);
|
|
set->angle[line_i] = angle;
|
|
}
|
|
i++;
|
|
set++;
|
|
angle -= 0x80;
|
|
}
|
|
|
|
particle = boss_particles;
|
|
i = 0;
|
|
while(i < BOSS_PARTICLE_COUNT) {
|
|
particle->pos.x.v = Subpixel::None();
|
|
particle->patnum = 0;
|
|
i++;
|
|
particle++;
|
|
}
|
|
shinki_bg_type_a_particles_alive = 0;
|
|
}
|
|
if(shinki_bg_type_a_particles_alive < BOSS_PARTICLE_COUNT) {
|
|
if(((boss.phase_frame % 4) == 0) == true) {
|
|
particle = &boss_particles[shinki_bg_type_a_particles_alive];
|
|
particle->pos.set((PLAYFIELD_W / 2), (PLAYFIELD_H / 2));
|
|
particle->origin.set((PLAYFIELD_W / 2), (PLAYFIELD_H / 2));
|
|
particle->angle = randring1_next16();
|
|
particle->age = 0;
|
|
vector2_at(
|
|
particle->velocity,
|
|
0.0f, 0.0f,
|
|
randring1_next16_and_ge_lt_sp(2.0f, 6.0f),
|
|
particle->angle
|
|
);
|
|
shinki_bg_type_a_particles_alive++;
|
|
}
|
|
}
|
|
if(boss.phase == 3) {
|
|
if((shinki_bg_linesets_zoomed_out % (SHINKI_LINESET_COUNT * 2)) < 2) {
|
|
linesets[0].angle[0] += 0x02;
|
|
linesets[1].angle[0] += 0x02;
|
|
} else {
|
|
linesets[0].angle[0] -= 0x02;
|
|
linesets[1].angle[0] -= 0x02;
|
|
}
|
|
}
|
|
}
|
|
|
|
// Draws the given line out of [set] with the current GRCG tile and color.
|
|
void pascal near grcg_lineset_line_put(lineset_t near &set, int i)
|
|
{
|
|
vector2_at(drawpoint,
|
|
set.center[i].x, set.center[i].y, set.radius[i], set.angle[i]
|
|
);
|
|
screen_x_t x1 = drawpoint.to_screen_left();
|
|
screen_y_t y1 = drawpoint.to_screen_top();
|
|
|
|
vector2_at(drawpoint,
|
|
set.center[i].x, set.center[i].y, set.radius[i], (set.angle[i] + 0x80)
|
|
);
|
|
screen_x_t x2 = drawpoint.to_screen_left();
|
|
screen_y_t y2 = drawpoint.to_screen_top();
|
|
|
|
grcg_line(x1, y1, x2, y2);
|
|
}
|
|
|
|
// Copies lines [0..17] to lines [1..18].
|
|
void pascal near shinki_lineset_forward_copy(lineset_t near &set)
|
|
{
|
|
for(int i = SHINKI_LINE_4; i > 0; i--) {
|
|
set.center[i] = set.center[i - 1];
|
|
set.angle[i] = set.angle[i - 1];
|
|
set.radius[i].v = set.radius[i - 1].v;
|
|
}
|
|
}
|
|
|
|
inline void shinki_bg_render_blue_particles_and_lines(void) {
|
|
grcg_setmode_rmw();
|
|
|
|
grcg_setcolor_direct(8);
|
|
shinki_bg_particles_render();
|
|
grcg_lineset_line_put(linesets[0], SHINKI_LINE_4);
|
|
grcg_lineset_line_put(linesets[0], SHINKI_LINE_3);
|
|
grcg_lineset_line_put(linesets[1], SHINKI_LINE_4);
|
|
grcg_lineset_line_put(linesets[1], SHINKI_LINE_3);
|
|
|
|
grcg_setcolor_direct(9);
|
|
grcg_lineset_line_put(linesets[0], SHINKI_LINE_2);
|
|
grcg_lineset_line_put(linesets[1], SHINKI_LINE_2);
|
|
|
|
grcg_setcolor_direct(V_WHITE);
|
|
grcg_lineset_line_put(linesets[0], SHINKI_LINE_MAIN);
|
|
grcg_lineset_line_put(linesets[1], SHINKI_LINE_MAIN);
|
|
|
|
grcg_off();
|
|
}
|
|
|
|
// Particles: Blue, shooting out from the center in random directions and at
|
|
// random speeds
|
|
// Lines: Two parallel, repeatedly zooming lines that give the impression of
|
|
// flying into a corridor, with random blue particles
|
|
void near shinki_bg_type_a_update_and_render(void)
|
|
{
|
|
// MODDERS: Just fuse into this function.
|
|
shinki_bg_type_a_update_part1();
|
|
|
|
lineset_t near *set = linesets;
|
|
int i = 0;
|
|
while(i < SHINKI_LINESET_COUNT) {
|
|
shinki_lineset_forward_copy(*set);
|
|
set->radius[SHINKI_LINE_MAIN] += 4.0f;
|
|
vector2_at(
|
|
set->center[SHINKI_LINE_MAIN],
|
|
to_sp(PLAYFIELD_W / 2),
|
|
to_sp(PLAYFIELD_H / 2),
|
|
((set->radius[SHINKI_LINE_MAIN].v * 3) / 4),
|
|
(set->angle[SHINKI_LINE_MAIN] - 0x40)
|
|
);
|
|
if(set->radius[SHINKI_LINE_MAIN] >= to_sp(224.0f)) {
|
|
shinki_bg_linesets_zoomed_out++;
|
|
set->radius[SHINKI_LINE_MAIN].set(0.0f);
|
|
}
|
|
i++;
|
|
set++;
|
|
}
|
|
shinki_bg_render_blue_particles_and_lines();
|
|
}
|
|
|
|
void near shinki_bg_type_b_update_part1(void)
|
|
{
|
|
boss_particle_t near *particle;
|
|
int i;
|
|
subpixel_t center_x;
|
|
int line_i;
|
|
lineset_t near *set;
|
|
|
|
if(!shinki_bg_type_b_initialized) {
|
|
center_x = to_sp(playfield_fraction_x(1 / 6.0f));
|
|
set = linesets;
|
|
i = 0;
|
|
while(i < SHINKI_LINESET_COUNT) {
|
|
for(line_i = SHINKI_LINE_4; line_i >= 0; line_i--) {
|
|
set->center[line_i].x.v = center_x;
|
|
set->center[line_i].y.v = to_sp((PLAYFIELD_H / 2) + 32);
|
|
set->radius[line_i].set(256.0f);
|
|
set->angle[line_i] = 0x40;
|
|
}
|
|
set->velocity_y.set(0.0f);
|
|
i++;
|
|
set++;
|
|
center_x += to_sp(playfield_fraction_x(4 / 6.0f));
|
|
}
|
|
particle = boss_particles;
|
|
i = 0;
|
|
while(i < BOSS_PARTICLE_COUNT) {
|
|
particle->pos.x.v = randring1_next16_mod(to_sp(PLAYFIELD_W));
|
|
// Huh? Not PLAYFIELD_H?
|
|
particle->pos.y.v = randring1_next16_mod(to_sp(PLAYFIELD_W));
|
|
particle->origin.x = particle->pos.x;
|
|
particle->origin.y.set(-1.0f);
|
|
particle->velocity.set(0.0f, 0.0f);
|
|
particle->patnum = randring1_next8_and_ge_lt(
|
|
PAT_PARTICLE, (PAT_PARTICLE_last + 1)
|
|
);
|
|
i++;
|
|
particle++;
|
|
}
|
|
shinki_bg_type_b_initialized++;
|
|
return;
|
|
}
|
|
|
|
particle = boss_particles;
|
|
i = 0;
|
|
while(i < BOSS_PARTICLE_COUNT) {
|
|
if(particle->velocity.y < to_sp(10.0f)) {
|
|
particle->velocity.y.v += stage_frame_mod2;
|
|
}
|
|
i++;
|
|
particle++;
|
|
}
|
|
}
|
|
|
|
// Particles: Blue, at random X positions, falling down
|
|
// Lines: Spinning and horizontally shifting at both sides of Shinki's wings,
|
|
// starting near the center of the playfield, moving up to
|
|
// [SHINKI_SPINLINE_TOP], and then pushed *down* into a vertical
|
|
// formation, until the set has reached its target velocity.
|
|
void near shinki_bg_type_b_update_and_render(void)
|
|
{
|
|
// MODDERS: Just fuse into this function.
|
|
shinki_bg_type_b_update_part1();
|
|
|
|
lineset_t near *set;
|
|
int set_i;
|
|
subpixel_t delta;
|
|
int line_i;
|
|
|
|
delta = SHINKI_SPINLINE_MOVE_SPEED;
|
|
set = linesets;
|
|
set_i = 0;
|
|
while(set_i < SHINKI_LINESET_COUNT) {
|
|
shinki_lineset_forward_copy(*set);
|
|
set->center[SHINKI_LINE_MAIN].y.v += set->velocity_y.v;
|
|
if(set->velocity_y > to_sp(-14.0f)) {
|
|
set->velocity_y.v -= (
|
|
(stage_frame_mod4 == 0) ? to_sp(0.0625f) : to_sp(0.0f)
|
|
);
|
|
}
|
|
shinki_spinline_x_and_angle(set, delta);
|
|
|
|
set_i++;
|
|
set++;
|
|
delta = -delta;
|
|
}
|
|
|
|
shinki_bg_spinline_frames++;
|
|
|
|
set = linesets;
|
|
if(set->center[SHINKI_LINE_MAIN].y < SHINKI_SPINLINE_TOP) {
|
|
shinki_spinline_y_formation_top(set, delta, set_i, line_i);
|
|
}
|
|
|
|
shinki_bg_render_blue_particles_and_lines();
|
|
}
|
|
|
|
void near shinki_bg_type_c_update_part1(void)
|
|
{
|
|
boss_particle_t near *particle;
|
|
int i;
|
|
|
|
if(!shinki_bg_type_c_initialized) {
|
|
particle = boss_particles;
|
|
if(particle->velocity.y < to_sp(0.0f)) {
|
|
i = 0;
|
|
while(i < BOSS_PARTICLE_COUNT) {
|
|
particle->origin.y.set(PLAYFIELD_H + 1.0f);
|
|
i++;
|
|
particle++;
|
|
}
|
|
shinki_bg_type_c_initialized++;
|
|
}
|
|
}
|
|
|
|
particle = boss_particles;
|
|
i = 0;
|
|
while(i < BOSS_PARTICLE_COUNT) {
|
|
if(particle->velocity.y.v > to_sp(-10.0f)) {
|
|
particle->velocity.y.v -= stage_frame_mod2;
|
|
}
|
|
i++;
|
|
particle++;
|
|
}
|
|
}
|
|
|
|
// Particles: Blue, keeping their random X positions from type B, rising
|
|
// Lines: Moving back up to [SHINKI_SPINLINE_TOP], starting at the Y velocity
|
|
// they had from the previous background type. Then, moving down to
|
|
// [SHINKI_SPINLINE_BOTTOM], and then pushed *up* into a vertical
|
|
// formation, until the set has reached its target velocity.
|
|
void near shinki_bg_type_c_update_and_render(void)
|
|
{
|
|
// MODDERS: Just fuse into this function.
|
|
shinki_bg_type_c_update_part1();
|
|
|
|
lineset_t near *set;
|
|
int set_i;
|
|
subpixel_t delta;
|
|
int line_i;
|
|
|
|
delta = SHINKI_SPINLINE_MOVE_SPEED;
|
|
set = linesets;
|
|
set_i = 0;
|
|
while(set_i < SHINKI_LINESET_COUNT) {
|
|
shinki_lineset_forward_copy(*set);
|
|
set->center[SHINKI_LINE_MAIN].y.v += set->velocity_y.v;
|
|
if(set->velocity_y < to_sp(14.0f)) {
|
|
set->velocity_y.v += stage_frame_mod2;
|
|
}
|
|
shinki_spinline_x_and_angle(set, delta);
|
|
|
|
set_i++;
|
|
set++;
|
|
delta = -delta;
|
|
}
|
|
|
|
shinki_bg_spinline_frames++;
|
|
|
|
set = linesets;
|
|
// [velocity_y] is still negative from type B in the beginning. Continue
|
|
// applying friction until [velocity_y] gets positive again
|
|
if(set->center[SHINKI_LINE_MAIN].y < SHINKI_SPINLINE_TOP) {
|
|
shinki_spinline_y_formation_top(set, delta, set_i, line_i);
|
|
} else if(set->center[SHINKI_LINE_MAIN].y > SHINKI_SPINLINE_BOTTOM) {
|
|
shinki_spinline_y_formation_bottom(set, delta, set_i, line_i);
|
|
}
|
|
|
|
shinki_bg_render_blue_particles_and_lines();
|
|
}
|
|
|
|
// Rising "smoke" particles.
|
|
void near shinki_bg_type_d_update(void)
|
|
{
|
|
boss_particle_t near *particle;
|
|
int i;
|
|
|
|
if(!shinki_bg_type_d_initialized) {
|
|
particle = boss_particles;
|
|
i = 0;
|
|
while(i < BOSS_PARTICLE_COUNT) {
|
|
particle->origin.y.v = to_sp(
|
|
SHINKI_TYPE_D_BACKDROP_TOP - PLAYFIELD_TOP
|
|
);
|
|
particle->velocity.y.set(-1.0f);
|
|
i++;
|
|
particle++;
|
|
}
|
|
shinki_bg_type_d_initialized++;
|
|
}
|
|
|
|
particle = boss_particles;
|
|
i = 0;
|
|
while(i < BOSS_PARTICLE_COUNT) {
|
|
int cel = ((PARTICLE_CELS - 1) - (particle->pos.y.v / to_sp(64.0f)));
|
|
if(cel < 0) {
|
|
cel = 0;
|
|
}
|
|
particle->patnum = (PAT_PARTICLE + cel);
|
|
i++;
|
|
particle++;
|
|
}
|
|
}
|
|
|
|
void pascal near shinki_bg_render(void)
|
|
{
|
|
if(boss.phase == PHASE_HP_FILL) {
|
|
boss_backdrop_render(
|
|
SHINKI_BACKDROP_LEFT, SHINKI_STAGE_BACKDROP_TOP, 1
|
|
);
|
|
} else if(boss.phase == PHASE_BOSS_ENTRANCE_BB) {
|
|
unsigned char entrance_cel = (
|
|
boss.phase_frame / ENTRANCE_BB_FRAMES_PER_CEL
|
|
);
|
|
if(entrance_cel < (TILES_BB_CELS / 2)) {
|
|
boss_backdrop_render(
|
|
SHINKI_BACKDROP_LEFT, SHINKI_STAGE_BACKDROP_TOP, 1
|
|
);
|
|
} else {
|
|
boss_bg_fill_col_0();
|
|
}
|
|
tiles_bb_col = V_WHITE;
|
|
tiles_bb_put(bb_boss_seg, entrance_cel);
|
|
} else if(boss.phase < 4) {
|
|
boss_bg_fill_col_0();
|
|
shinki_bg_type_a_update_and_render();
|
|
} else if(boss.phase < 8) {
|
|
boss_bg_fill_col_0();
|
|
shinki_bg_type_b_update_and_render();
|
|
} else if(boss.phase < 12) {
|
|
boss_bg_fill_col_0();
|
|
shinki_bg_type_c_update_and_render();
|
|
} else /* if(boss.phase == PHASE_NONE) */ { \
|
|
cdg_put_noalpha_8(
|
|
SHINKI_BACKDROP_LEFT, SHINKI_TYPE_D_BACKDROP_TOP, CDG_BG_2
|
|
);
|
|
shinki_bg_type_d_colorfill();
|
|
shinki_bg_type_d_update();
|
|
|
|
grcg_setcolor(GC_RMW, 6);
|
|
shinki_bg_particles_render();
|
|
grcg_off();
|
|
}
|
|
}
|
|
/// ----------------
|
|
|
|
/// Extra Stage - EX-Alice
|
|
/// ----------------------
|
|
|
|
static const screen_x_t HEXAGRAM_CENTER_X = (
|
|
PLAYFIELD_LEFT + (PLAYFIELD_W / 2)
|
|
);
|
|
static const vram_y_t HEXAGRAM_CENTER_Y = (PLAYFIELD_TOP + (PLAYFIELD_H / 2));
|
|
|
|
#define exalice_hexagram_point_set(ret, radius, angle) \
|
|
vector2_at( \
|
|
ret, to_sp(HEXAGRAM_CENTER_X), to_sp(HEXAGRAM_CENTER_Y), radius, angle \
|
|
)
|
|
|
|
void pascal near exalice_grcg_hexagram_put(subpixel_t radius, int angle)
|
|
{
|
|
#define tri_point_0 drawpoint
|
|
extern SPPoint tri_point_1;
|
|
extern SPPoint tri_point_2;
|
|
|
|
grcg_circle(HEXAGRAM_CENTER_X, HEXAGRAM_CENTER_Y, TO_PIXEL(radius));
|
|
int i = 0;
|
|
while(i < 2) {
|
|
exalice_hexagram_point_set(tri_point_0, radius, angle);
|
|
exalice_hexagram_point_set(tri_point_1, radius, (angle + (0x100 / 3)));
|
|
exalice_hexagram_point_set(tri_point_2, radius, (angle - (0x100 / 3)));
|
|
|
|
TO_PIXEL_INPLACE(tri_point_0.x.v);
|
|
TO_PIXEL_INPLACE(tri_point_0.y.v);
|
|
TO_PIXEL_INPLACE(tri_point_1.x.v);
|
|
TO_PIXEL_INPLACE(tri_point_1.y.v);
|
|
TO_PIXEL_INPLACE(tri_point_2.x.v);
|
|
TO_PIXEL_INPLACE(tri_point_2.y.v);
|
|
|
|
grcg_line(
|
|
tri_point_0.x.v, tri_point_0.y.v, tri_point_1.x.v, tri_point_1.y.v
|
|
);
|
|
grcg_line(
|
|
tri_point_1.x.v, tri_point_1.y.v, tri_point_2.x.v, tri_point_2.y.v
|
|
);
|
|
grcg_line(
|
|
tri_point_0.x.v, tri_point_0.y.v, tri_point_2.x.v, tri_point_2.y.v
|
|
);
|
|
|
|
i++;
|
|
angle += (0x100 / 6);
|
|
}
|
|
|
|
#undef tri_point_0
|
|
}
|
|
|
|
void near exalice_hexagrams_update_and_render(void)
|
|
{
|
|
enum exalice_hexagrams_state_t {
|
|
UNINITIALIZED = 0,
|
|
TURN_RIGHT = 1,
|
|
TURN_LEFT = 2,
|
|
STATE_COUNT,
|
|
};
|
|
extern exalice_hexagrams_state_t exalice_hexagrams_state;
|
|
int i;
|
|
|
|
lineset_t near &set = linesets[0];
|
|
if(exalice_hexagrams_state == UNINITIALIZED) {
|
|
for(i = 0; i < (LINESET_LINE_COUNT - 1); i++) {
|
|
set.radius[i].set(1.0f);
|
|
set.angle[i] = 0x00;
|
|
}
|
|
exalice_hexagrams_state = TURN_RIGHT;
|
|
}
|
|
for(i = (LINESET_LINE_COUNT - 2); i > 0; i--) {
|
|
set.radius[i] = set.radius[i - 1];
|
|
set.angle[i] = set.angle[i - 1];
|
|
}
|
|
set.radius[0] += 5.0f;
|
|
if(set.radius[0].v >= to_sp(320.0f)) {
|
|
set.radius[0].set(1.0f);
|
|
exalice_hexagrams_state = static_cast<exalice_hexagrams_state_t>(
|
|
STATE_COUNT - exalice_hexagrams_state
|
|
);
|
|
}
|
|
if(exalice_hexagrams_state == TURN_RIGHT) {
|
|
set.angle[0] += 0x01;
|
|
} else {
|
|
set.angle[0] -= 0x01;
|
|
}
|
|
|
|
grcg_setcolor(GC_RMW, 8);
|
|
exalice_grcg_hexagram_put(set.radius[18].v, set.angle[18]);
|
|
grcg_setcolor(GC_RMW, 9);
|
|
exalice_grcg_hexagram_put(set.radius[9].v, set.angle[9]);
|
|
if(boss.phase < 9 || boss.phase > 12) {
|
|
grcg_setcolor(GC_RMW, V_WHITE);
|
|
}
|
|
exalice_grcg_hexagram_put(set.radius[0].v, set.angle[0]);
|
|
grcg_off();
|
|
}
|
|
|
|
void pascal near exalice_bg_render(void)
|
|
{
|
|
if(boss.phase == PHASE_HP_FILL) {
|
|
tiles_render_after_custom(boss.phase_frame);
|
|
} else if(boss.phase == PHASE_BOSS_ENTRANCE_BB) {
|
|
unsigned char entrance_cel = (boss.phase_frame / 4);
|
|
boss_bg_fill_col_0();
|
|
tiles_bb_col = V_WHITE;
|
|
tiles_bb_put(bb_boss_seg, entrance_cel);
|
|
} else if(boss.phase < PHASE_BOSS_EXPLODE_BIG) {
|
|
boss_bg_fill_col_0();
|
|
exalice_hexagrams_update_and_render();
|
|
} else if(boss.phase == PHASE_BOSS_EXPLODE_BIG) {
|
|
tiles_render_all();
|
|
} else /* if(boss.phase == PHASE_NONE) */ { \
|
|
tiles_render_after_custom(boss.phase_frame);
|
|
}
|
|
}
|
|
/// ----------------------
|