mirror of https://github.com/nmlgc/ReC98.git
315 lines
8.2 KiB
C++
315 lines
8.2 KiB
C++
/// Makai Stage 15 Boss - Elis
|
|
/// --------------------------
|
|
|
|
extern "C" {
|
|
#include <stddef.h>
|
|
#include "platform.h"
|
|
#include "pc98.h"
|
|
#include "planar.h"
|
|
#include "master.hpp"
|
|
#include "th01/v_colors.hpp"
|
|
#include "th01/math/area.hpp"
|
|
#include "th01/math/subpixel.hpp"
|
|
#include "th01/hardware/egc.h"
|
|
#include "th01/hardware/graph.h"
|
|
#include "th01/formats/pf.hpp"
|
|
#include "th01/formats/grc.hpp"
|
|
#include "th01/formats/ptn.hpp"
|
|
#include "th01/main/entity.hpp"
|
|
#include "th01/main/playfld.hpp"
|
|
#include "th01/main/vars.hpp"
|
|
#include "th01/main/boss/entity_a.hpp"
|
|
}
|
|
#include "th01/shiftjis/fns.hpp"
|
|
#undef MISSILE_FN
|
|
// Stuffing float constants at the end of a string to work around alignment
|
|
// limitations… how disgusting.
|
|
#define MISSILE_FN "boss3_m.ptn\x00\x00\x00\xC8\x43\x00\x00\x20\x44\x9A\x99\x99\x99\x99\x99\xA9\x3F"
|
|
#include "th01/sprites/pellet.h"
|
|
#include "th01/main/shape.hpp"
|
|
#include "th01/main/particle.hpp"
|
|
#include "th01/main/boss/boss.hpp"
|
|
#include "th01/main/boss/palette.hpp"
|
|
#include "th01/main/bullet/missile.hpp"
|
|
#include "th01/main/bullet/pellet.hpp"
|
|
#include "th01/main/hud/hp.hpp"
|
|
|
|
// Coordinates
|
|
// -----------
|
|
|
|
static const pixel_t GIRL_W = 128;
|
|
static const pixel_t GIRL_H = 96;
|
|
static const pixel_t BAT_W = 48;
|
|
static const pixel_t BAT_H = 32;
|
|
|
|
static const pixel_t BASE_LEFT = (PLAYFIELD_CENTER_X - (GIRL_W / 2));
|
|
static const pixel_t BASE_TOP = (
|
|
PLAYFIELD_TOP + ((PLAYFIELD_H / 21) * 5) - (GIRL_H / 2)
|
|
);
|
|
// -----------
|
|
|
|
#define pattern_state elis_pattern_state
|
|
#define stars elis_stars
|
|
#define flash_colors elis_flash_colors
|
|
#define invincibility_frame elis_invincibility_frame
|
|
#define invincible elis_invincible
|
|
#define wave_teleport_done elis_wave_teleport_done
|
|
#define initial_hp_rendered elis_initial_hp_rendered
|
|
extern int invincibility_frame;
|
|
extern bool16 invincible;
|
|
extern bool16 wave_teleport_done;
|
|
extern bool initial_hp_rendered;
|
|
|
|
extern union {
|
|
int unknown;
|
|
} pattern_state;
|
|
|
|
// Entities
|
|
// --------
|
|
|
|
enum elis_entity_t {
|
|
// "Girl" sprites
|
|
ENT_STILL_OR_WAVE = 0,
|
|
ENT_ATTACK = 1,
|
|
|
|
ENT_BAT = 2,
|
|
};
|
|
|
|
enum still_or_wave_cel_t {
|
|
C_STILL = 0,
|
|
C_WAVE_1 = 2,
|
|
C_WAVE_2 = 3,
|
|
C_WAVE_3 = 4,
|
|
C_WAVE_4 = 5,
|
|
};
|
|
|
|
#define ent_still_or_wave boss_entities[ENT_STILL_OR_WAVE]
|
|
#define ent_attack boss_entities[ENT_ATTACK]
|
|
#define ent_bat boss_entities[ENT_BAT]
|
|
|
|
inline void elis_ent_load(void) {
|
|
ent_still_or_wave.load("boss5.bos", 0);
|
|
ent_attack.load("boss5_2.bos", 1);
|
|
ent_bat.load("boss5_3.bos", 2);
|
|
}
|
|
|
|
inline void elis_ent_free(void) {
|
|
bos_entity_free(0);
|
|
bos_entity_free(1);
|
|
bos_entity_free(2);
|
|
}
|
|
|
|
inline void ent_sync(elis_entity_t dst, elis_entity_t src) {
|
|
boss_entities[dst].pos_cur_set(
|
|
boss_entities[src].cur_left, boss_entities[src].cur_top
|
|
);
|
|
}
|
|
// --------
|
|
|
|
// .PTN
|
|
// ----
|
|
|
|
static const main_ptn_slot_t PTN_SLOT_BG_ENT = PTN_SLOT_BOSS_1;
|
|
static const main_ptn_slot_t PTN_SLOT_MISSILE = PTN_SLOT_BOSS_2;
|
|
// ----
|
|
|
|
#define bg_func_init(left, top, entity_src) { \
|
|
ent_sync(ENT_ATTACK, ENT_STILL_OR_WAVE); \
|
|
if(entity_src == (ENT_STILL_OR_WAVE + 1)) { \
|
|
left = ent_still_or_wave.cur_left; \
|
|
top = ent_still_or_wave.cur_top; \
|
|
} else if(entity_src == (ENT_ATTACK + 1)) { \
|
|
left = ent_attack.cur_left; \
|
|
top = ent_attack.cur_top; \
|
|
} \
|
|
}
|
|
|
|
void girl_bg_snap(int unncessary_parameter_that_still_needs_to_be_1_or_2)
|
|
{
|
|
int ptn_x;
|
|
int ptn_y;
|
|
screen_x_t left;
|
|
screen_y_t top;
|
|
int image;
|
|
|
|
bg_func_init(left, top, unncessary_parameter_that_still_needs_to_be_1_or_2);
|
|
|
|
image = 0;
|
|
ptn_snap_rect_from_1_8(
|
|
left, top, GIRL_W, GIRL_H, PTN_SLOT_BG_ENT, image, ptn_x, ptn_y
|
|
);
|
|
}
|
|
|
|
void girl_bg_put(int unncessary_parameter_that_still_needs_to_be_1_or_2)
|
|
{
|
|
int ptn_x;
|
|
int ptn_y;
|
|
screen_x_t left;
|
|
screen_y_t top;
|
|
int image = 0;
|
|
|
|
bg_func_init(left, top, unncessary_parameter_that_still_needs_to_be_1_or_2);
|
|
ptn_put_rect_noalpha_8(
|
|
left, top, GIRL_W, GIRL_H, PTN_SLOT_BG_ENT, image, ptn_x, ptn_y
|
|
);
|
|
}
|
|
|
|
void elis_load(void)
|
|
{
|
|
pellet_interlace = true;
|
|
Pellets.unknown_seven = 7;
|
|
elis_ent_load();
|
|
grc_load(GRC_SLOT_BOSS_1, "boss5_gr.grc");
|
|
ptn_new(PTN_SLOT_BG_ENT, ((GIRL_W / PTN_W) * (GIRL_H / PTN_H)));
|
|
Missiles.load(PTN_SLOT_MISSILE);
|
|
boss_palette_snap();
|
|
void elis_setup(void);
|
|
elis_setup();
|
|
particles_unput_update_render(PO_INITIALIZE, V_WHITE);
|
|
}
|
|
|
|
void elis_setup(void)
|
|
{
|
|
int col;
|
|
int comp;
|
|
|
|
ent_still_or_wave.pos_set(
|
|
BASE_LEFT, BASE_TOP, 48,
|
|
PLAYFIELD_LEFT, (PLAYFIELD_RIGHT + ((GIRL_W / 4) * 3)),
|
|
PLAYFIELD_TOP, (PLAYFIELD_BOTTOM - GIRL_H)
|
|
);
|
|
ent_attack.pos_set(
|
|
BASE_LEFT, BASE_TOP, 48,
|
|
PLAYFIELD_LEFT, (PLAYFIELD_RIGHT + ((GIRL_W / 4) * 3)),
|
|
PLAYFIELD_TOP, (PLAYFIELD_BOTTOM - GIRL_H)
|
|
);
|
|
ent_bat.pos_set(
|
|
BASE_LEFT, BASE_TOP, 48,
|
|
PLAYFIELD_LEFT, (PLAYFIELD_RIGHT + (BAT_W * 2)),
|
|
PLAYFIELD_TOP, (PLAYFIELD_BOTTOM - (BAT_H * 3))
|
|
);
|
|
ent_still_or_wave.hitbox_set(
|
|
((GIRL_W / 4) * 1), ((GIRL_H / 8) * 1),
|
|
((GIRL_W / 4) * 3), ((GIRL_H / 3) * 2)
|
|
);
|
|
// Note that [ent_attack] doesn't receive a hitbox!
|
|
ent_bat.hitbox_set(
|
|
((BAT_W / 6) * 1), ((BAT_H / 4) * 1),
|
|
((BAT_W / 6) * 5), ((BAT_H / 4) * 3)
|
|
);
|
|
|
|
boss_phase = 0;
|
|
boss_phase_frame = 0;
|
|
boss_hp = 14;
|
|
hud_hp_first_white = 10;
|
|
hud_hp_first_redwhite = 6;
|
|
random_seed = frame_rand;
|
|
palette_set_grayscale(boss_post_defeat_palette, 0x0, col, comp);
|
|
}
|
|
|
|
void elis_free(void)
|
|
{
|
|
elis_ent_free();
|
|
grc_free(GRC_SLOT_BOSS_1);
|
|
ptn_free(PTN_SLOT_BG_ENT);
|
|
ptn_free(PTN_SLOT_MISSILE);
|
|
}
|
|
|
|
bool16 wave_teleport(screen_x_t target_left, screen_y_t target_top)
|
|
{
|
|
ent_sync(ENT_ATTACK, ENT_STILL_OR_WAVE);
|
|
|
|
// Wave sprite
|
|
if(boss_phase_frame == 20) {
|
|
graph_accesspage_func(1);
|
|
girl_bg_put(1);
|
|
graph_accesspage_func(0);
|
|
ent_still_or_wave.move_lock_and_put_image_8(C_WAVE_1);
|
|
ent_still_or_wave.hitbox_orb_inactive = true;
|
|
} else if(boss_phase_frame == 28) {
|
|
girl_bg_put(1);
|
|
ent_still_or_wave.move_lock_and_put_image_8(C_WAVE_2);
|
|
ent_still_or_wave.hitbox_orb_inactive = true;
|
|
} else if(boss_phase_frame == 36) {
|
|
ent_still_or_wave.hitbox_orb_inactive = true;
|
|
girl_bg_put(1);
|
|
ent_still_or_wave.move_lock_and_put_image_8(C_WAVE_3);
|
|
} else if(boss_phase_frame == 44) {
|
|
ent_still_or_wave.hitbox_orb_inactive = true;
|
|
egc_copy_rect_1_to_0_16(
|
|
ent_still_or_wave.cur_left,
|
|
ent_still_or_wave.cur_top,
|
|
GIRL_W,
|
|
GIRL_H
|
|
);
|
|
} else if(boss_phase_frame == 52) {
|
|
ent_still_or_wave.hitbox_orb_inactive = true;
|
|
ent_still_or_wave.pos_cur_set(target_left, target_top);
|
|
girl_bg_snap(1);
|
|
girl_bg_put(1); // unnecessary
|
|
ent_still_or_wave.move_lock_and_put_image_8(C_WAVE_3);
|
|
} else if(boss_phase_frame == 60) {
|
|
ent_still_or_wave.hitbox_orb_inactive = true;
|
|
girl_bg_put(1);
|
|
ent_still_or_wave.move_lock_and_put_image_8(C_WAVE_2);
|
|
} else if(boss_phase_frame == 68) {
|
|
ent_still_or_wave.hitbox_orb_inactive = true;
|
|
girl_bg_put(1);
|
|
ent_still_or_wave.move_lock_and_put_image_8(C_WAVE_1);
|
|
} else if(boss_phase_frame == 76) {
|
|
ent_still_or_wave.hitbox_orb_inactive = false;
|
|
graph_accesspage_func(1);
|
|
ent_still_or_wave.move_lock_and_put_image_8(C_STILL);
|
|
graph_accesspage_func(0);
|
|
ent_still_or_wave.move_lock_and_put_image_8(C_STILL);
|
|
} else if(boss_phase_frame > 80) {
|
|
boss_phase_frame = 0;
|
|
return true;
|
|
}
|
|
|
|
// Stars
|
|
if((boss_phase_frame % 4) != 0) {
|
|
return false;
|
|
}
|
|
|
|
enum {
|
|
STAR_OFFSET_X = (GIRL_W / 4),
|
|
STAR_OFFSET_Y = (GIRL_H / 3),
|
|
STAR_AREA_W = (STAR_OFFSET_X + GIRL_W + STAR_OFFSET_X),
|
|
STAR_AREA_H = (STAR_OFFSET_Y + GIRL_H),
|
|
};
|
|
struct CStars : public CEntities<5> {
|
|
static screen_x_t random_left(void) {
|
|
return (ent_still_or_wave.cur_left + (rand() % STAR_AREA_W));
|
|
}
|
|
static screen_y_t random_top(void) {
|
|
return (ent_still_or_wave.cur_top + (rand() % STAR_AREA_H));
|
|
}
|
|
};
|
|
extern CStars stars;
|
|
|
|
for(int i = 0; i < stars.count(); i++) {
|
|
if(boss_phase_frame > 4) {
|
|
egc_copy_rect_1_to_0_16_word_w(stars.left[i], stars.top[i], 8, 8);
|
|
}
|
|
if((boss_phase_frame < 40) || (boss_phase_frame > 52)) {
|
|
stars.left[i] = (stars.random_left() - STAR_OFFSET_X);
|
|
stars.top[i] = (stars.random_top() - STAR_OFFSET_Y);
|
|
} else {
|
|
stars.left[i] = (stars.left[i] + (
|
|
(stars.random_left() - STAR_OFFSET_X - stars.left[i]) / 3
|
|
));
|
|
stars.top[i] = (stars.top[i] + (
|
|
(stars.random_top() - STAR_OFFSET_Y - stars.top[i]) / 3
|
|
));
|
|
}
|
|
if(boss_phase_frame < 68) {
|
|
shape8x8_star_put(stars.left[i], stars.top[i], 2);
|
|
}
|
|
}
|
|
return false;
|
|
}
|
|
|
|
#define select_for_rank elis_select_for_rank
|
|
#include "th01/main/select_r.cpp"
|