#include "th04/sprites/cels.h" /// Game-specific group and spawn types /// ----------------------------------- #if GAME == 5 # include "th05/main/bullet/types.h" #else # include "th04/main/bullet/types.h" #endif /// ----------------------------------- /// States and modes /// ---------------- /// Everything here needs to be kept in sync with the ASM versions in /// bullet.hpp! #define BSS_CLOUD_FRAMES (BULLET_CLOUD_CELS * 4) #define BMS_DECAY_FRAMES (BULLET_DECAY_CELS * 4) #define BMS_SLOWDOWN_BASE_SPEED 4.5f #define BMS_SLOWDOWN_FRAMES 32 enum bullet_spawn_state_t { /// Hitbox is active /// ---------------- BSS_GRAZEABLE = 0, BSS_GRAZED = 1, BSS_ACTIVE = 2, /// ---------------- /// Delay "cloud", no hitbox /// ------------------------ BSS_CLOUD_BACKWARDS = 3, BSS_CLOUD_FORWARDS = 4, BSS_CLOUD_END = (BSS_CLOUD_FORWARDS + BSS_CLOUD_FRAMES), /// ------------------------ }; enum bullet_move_state_t { /// Hitbox is active /// ---------------- // Slows down from BMS_SLOWDOWN_BASE_SPEED to [final_speed] BMS_SLOWDOWN = 0, // Special processing according to [special_motion] BMS_SPECIAL = 1, // No special processing BMS_REGULAR = 2, /// ---------------- /// Decay, no hitbox /// ---------------- BMS_DECAY = 4, BMS_DECAY_END = (BMS_DECAY + BMS_DECAY_FRAMES), /// ---------------- }; enum bullet_special_motion_t { }; /// ---------------- // Needs to be kept in sync with the ASM version in bullet.inc! struct bullet_t { char flag; char age; PlayfieldMotion pos; unsigned char from_group; // unused int8_t unused; SubpixelLength8 speed_cur; unsigned char angle; bullet_spawn_state_t spawn_state; bullet_move_state_t move_state; bullet_special_motion_t special_motion; unsigned char speed_final; union { unsigned char slowdown_time; // with BMS_SLOWDOWN unsigned char turn_count; // with BMS_SPECIAL } ax; union { unsigned char slowdown_speed_delta; // with BMS_SLOWDOWN unsigned char turn_angle; // with BMS_SPECIAL } dx; int patnum; #if GAME == 5 // Coordinates for BSM_STRAIGHT SPPoint origin; int distance; #endif }; #define PELLET_W 8 #define PELLET_H 8 #define BULLET16_W 16 #define BULLET16_H 16 #if GAME == 5 # define PELLET_COUNT 180 # define BULLET16_COUNT 220 // Returns the patnum for the directional or vector bullet sprite starting at // [patnum_base] that shows the given [angle]. int pascal near bullet_patnum_for_angle(int patnum_base, unsigned char angle); // Updates [bullet]'s patnum based on its current angle. void pascal near bullet_update_patnum(bullet_t near *bullet); #else # define PELLET_COUNT 240 # define BULLET16_COUNT 200 // Returns the offset for a directional bullet sprite that shows the given // [angle]. int pascal near bullet_patnum_for_angle(unsigned char angle); // Turns every 4th bullet into a point item when zapping bullets. extern bool bullet_zap_drop_point_items; #endif #define BULLET_COUNT (PELLET_COUNT + BULLET16_COUNT) extern bullet_t bullets[BULLET_COUNT]; #define pellets (&bullets[0]) #define bullets16 (&bullets[PELLET_COUNT]) // Number of times a bouncing bullet can change its direction before it // automatically turns into a BMS_REGULAR bullet. Global state, not set // per-bullet! extern unsigned char bullet_turn_count_max; // Set to `true` to clear all on-screen bullets, giving out a semi-exponential // bonus for all bullets that were alive on the first frame of activity. // Lasts for BULLET_ZAP_FRAMES and resets to `false` afterwards. extern union { bool active; unsigned char frames; // doubles as the animation timer } bullet_zap; static const int BULLET_ZAP_FRAMES_PER_CEL = 4; static const int BULLET_ZAP_FRAMES = ( BULLET_ZAP_CELS * BULLET_ZAP_FRAMES_PER_CEL ); // # of frames left during which all on-screen bullets should decay. // Gives a constant point bonus for every bullet decayed during that time. extern unsigned char bullet_clear_time; /// Rendering /// --------- union pellet_render_t { struct { screen_x_t left; vram_y_t top; } top; struct { vram_offset_t vram_offset; uint16_t sprite_offset; } bottom; }; #if (GAME == 5) // Separate render list for pellets during their delay cloud stage. extern int pellet_clouds_render_count; extern bullet_t near *pellet_clouds_render[PELLET_COUNT]; #endif extern int pellets_render_count; extern pellet_render_t pellets_render[PELLET_COUNT]; // Renders the top and bottom part of all pellets, as per [pellets_render] and // [pellets_render_count]. Assumptions: // • ES is already be set to the beginning of a VRAM segment // • The GRCG is active, and set to the intended color // • pellets_render_top() is called before pellets_render_bottom() void near pellets_render_top(); void near pellets_render_bottom(); /// --------- /// Template /// -------- // Needs to be kept in sync with the ASM version in bullet.inc! struct bullet_template_t { uint8_t spawn_type; unsigned char patnum; // TH05: 0 = pellet PlayfieldPoint origin; #if GAME == 5 bullet_group_t group; bullet_special_motion_t special_motion; unsigned char spread; unsigned char spread_angle_delta; unsigned char stack; SubpixelLength8 stack_speed_delta; unsigned char angle; SubpixelLength8 speed; #else PlayfieldPoint velocity; bullet_group_t group; unsigned char angle; SubpixelLength8 speed; unsigned char count; bullet_template_delta_t delta; uint8_t unused_1; bullet_special_motion_t special_motion; uint8_t unused_2; #endif }; extern bullet_template_t bullet_template; // Separate from the template, for some reason? extern unsigned char bullet_template_turn_angle; // Modifies [bullet_template] based on [playperf] and the respective // difficulty. These don't modify the base [speed]; that is done by the spawn // functions themselves, unless overridden via the _fixedspeed() variants. void pascal near bullet_template_tune_easy(void); void pascal near bullet_template_tune_normal(void); void pascal near bullet_template_tune_hard(void); void pascal near bullet_template_tune_lunatic(void); extern nearfunc_t_near bullet_template_tune; // The actual functions for spawning bullets based on the [bullet_template]. // Both TH04 and TH05 pointlessly use separate functions for spawning "regular" // bullets (which receive a move state of BMS_SLOWDOWN or BMS_REGULAR) or // "special" ones (which are BMS_SPECIAL). #if (GAME == 5) void near bullets_add_regular(void); void near bullets_add_special(void); #else // TH04 additionally uses pointless per-difficulty wrappers around these // spawn functions that don't actually do anything difficulty-specific. void pascal near bullets_add_regular_easy(void); void pascal near bullets_add_regular_normal(void); void pascal near bullets_add_regular_hard_lunatic(void); void pascal near bullets_add_special_easy(void); void pascal near bullets_add_special_normal(void); void pascal near bullets_add_special_hard_lunatic(void); // Set to the version of the wrapper functions above that match the // current difficulty. extern nearfunc_t_near bullets_add_regular; extern nearfunc_t_near bullets_add_special; #endif // Further wrappers around the spawn functions that bypass base [speed] tuning // of the resulting bullets based on [playperf], and fire them at a constant // speed instead. void near bullets_add_regular_fixedspeed(void); void near bullets_add_special_fixedspeed(void); /// --------