// Bosses // ------ static const pixel_t BOSS_W = 64; static const pixel_t BOSS_H = 64; static const int BOSS_DEFEAT_INVINCIBILITY_FRAMES = 255; #if (GAME == 5) static const unsigned long BOSS_BONUS_UNIT_VALUE = 1000; #else static const unsigned int BOSS_BONUS_UNIT_VALUE = 1280; // 16 bytes of globally additional state that bosses can use freely? // Architecturally, that's a step back even compared to TH01. extern unsigned char boss_statebyte[16]; #endif typedef struct { PlayfieldMotion pos; int hp; unsigned char sprite; unsigned char phase; int phase_frame; unsigned char damage_this_frame; unsigned char mode; // Used for both movement and bullet angles. unsigned char angle; unsigned char mode_change; int phase_end_hp; } boss_stuff_t; extern boss_stuff_t boss; extern SPPoint boss_hitbox_radius; extern bool boss_phase_timed_out; void near boss_reset(void); // Grants a score bonus of [units * BOSS_BONUS_UNIT_VALUE], rendered as one new // point number popup per unit around the boss sprite. void pascal near boss_score_bonus(unsigned int units); // Callbacks extern farfunc_t_near boss_update; extern nearfunc_t_near boss_fg_render; // Also responsible to set [bg_render_bombing_func] to the // [boss_bg_render_func]! extern farfunc_t_near boss_update_func; extern nearfunc_t_near boss_backdrop_colorfill; extern nearfunc_t_near boss_bg_render_func; extern nearfunc_t_near boss_fg_render_func; #define BOSS_DEC(name) \ void pascal far name##_update(void); \ void pascal near name##_bg_render(void); \ void pascal near name##_fg_render(void); // Renders the boss battle background image at the given screen position, // surrounded by the given background [col]. void pascal near boss_backdrop_render( screen_x_t left, vram_y_t top, uint4_t col ); // Collision detection // ------------------- // Processes any collision of player shots (and, in TH05, the player itself) // within a box with the given radius around the current position of the boss. // Returns the total amount of damage dealt to it this frame, and plays the // given sound effect if that amount is nonzero. int pascal near boss_hittest_shots_damage( subpixel_t radius_x, subpixel_t radius_y, int se_on_hit ); // Calls boss_hittest_shots_damage() with the boss hitbox radius and regular // hit sound effect, and subtracts the result from the current boss HP. // Returns `true` if that subtraction ended the current boss phase. // // TH04's version also takes ownership of [boss.phase_frame], incrementing it // on every call. bool near boss_hittest_shots(void); // Calls boss_hittest_shots_damage() with the boss hitbox radius and // invincibility sound effect, and does nothing with the returned damage. // // TH04's version also takes ownership of [boss.phase_frame], incrementing it // on every call. void near boss_hittest_shots_invincible(void); // ------ /// Explosions /// ---------- #define EXPLOSION_SMALL_COUNT 2 struct explosion_t { char flag; unsigned char age; SPPoint center; SPPoint radius_cur; SPPoint radius_delta; int8_t unused; // 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; }; enum explosion_type_t { ET_NONE = -1, ET_CIRCLE = 0, ET_NW_SE = 1, ET_SW_NE = 2, ET_HORIZONTAL = 3, ET_VERTICAL = 4, _explosion_type_t_FORCE_INT16 = 0x7FFF }; extern explosion_t explosions_small[EXPLOSION_SMALL_COUNT]; extern explosion_t explosions_big; void pascal near boss_explode_small(explosion_type_t type); #if (GAME == 5) void near boss_explode_big_circle(void); // Wrapper for easy compatibility with TH04 code. inline void boss_explode_big(explosion_type_t type) { boss_explode_big_circle(); } #else void pascal near boss_explode_big(explosion_type_t type); #endif void near explosions_small_update_and_render(void); void near explosions_big_update_and_render(void); void explosions_small_reset(void); /// ---------- void near boss_items_drop(); // Shows a small explosion with the given type, clears any bullets if the // current phase wasn't timed out, and starts the next one, which will end upon // reaching the given amount of HP. void pascal near boss_phase_next( explosion_type_t explosion_type, int next_end_hp ); // Defeat sequence // --------------- #ifdef OVERLAY_FADE_CELS enum boss_defeat_frames_t { // Small explosion phase BDF_SMALL_1 = 1, BDF_SMALL_2 = 16, BDF_BIG = 32, // Big explosion phase. And yes, the (hardcoded) amount of frames to // spend in this one dependes on the (hardcoded) length of the overlay // fade animation, which in turn depends on its (hardcoded) amount of // gaiji cels. BDF_DIALOG = (GAME == 5), BDF_FADEOUT = 416, BDF_NEXT_STAGE = (BDF_FADEOUT + OVERLAY_FADE_DURATION), }; #endif // A dumb helper function… #if (GAME == 5) inline void boss_phase_explode_big() { boss.phase++; // = PHASE_BOSS_EXPLODE_BIG } #else inline void boss_phase_explode_big() { boss.phase = PHASE_EXPLODE_BIG; } #endif // …to abstract away the TH04/TH05 difference in *this* dumb helper function. #define boss_defeat_explode_big(type, bonus_units) { \ boss_explode_big(type); \ boss_phase_explode_big(); \ bullet_zap.active = boss.mode_change; \ if(boss.mode_change) { \ boss_score_bonus(bonus_units); \ } \ boss.sprite = PAT_ENEMY_KILL; \ boss.phase_frame = 0; \ } // Runs a frame of the boss defeat sequence. TH04's version also: // • initializes Gengetsu at the end of the last defeat phase during the first // time it's shown on the Extra stage, // • and takes ownership of [boss.phase_frame], incrementing it on every call. #if (GAME == 5) void pascal near boss_defeat_update(unsigned int bonus_units); #else void near boss_defeat_update(void); #endif // ---------------