ReC98/th01/main/player/orb.cpp

149 lines
3.8 KiB
C++
Raw Normal View History

extern int orb_frames_outside_portal; // unused
extern double orb_velocity_y;
#define COEFFICIENT_OF_RESTITUTION 0.78
inline double gravity_for(const double& force) {
return ((orb_force_frame / 5) + orb_force);
}
pixel_t orb_velocity_y_update(void)
{
orb_velocity_y = gravity_for(orb_force);
if(orb_velocity_y > 16.0f) {
orb_velocity_y = 16.0f;
} else if(orb_velocity_y < -16.0) {
orb_velocity_y = -16.0;
}
return gravity_for(orb_force);
}
#define random_velocity_change(val, new_velocity) \
if(orb_force_frame < 17) { \
if((rand() % 50) == val) { \
orb_velocity_x = new_velocity; \
} \
}
void orb_force_new(double immediate, orb_force_t force)
{
if(force == OF_BOUNCE_FROM_SURFACE) {
orb_force = (-orb_velocity_y * COEFFICIENT_OF_RESTITUTION);
if(orb_velocity_x == OVX_0) {
random_velocity_change(0, OVX_4_LEFT);
random_velocity_change(1, OVX_4_RIGHT);
}
}
if(force == OF_BOUNCE_FROM_TOP) {
orb_force = ((-orb_velocity_y) - (orb_force_frame / 4));
}
if(force == OF_SHOT) {
orb_force = (-10.0 + (orb_velocity_y / 2.0));
}
if(force == OF_IMMEDIATE) {
orb_force = immediate;
}
orb_force_frame = 0;
}
void orb_move_x(orb_velocity_x_t velocity_x)
{
switch(velocity_x) {
case OVX_4_LEFT: orb_cur_left -= 4; break;
case OVX_4_RIGHT: orb_cur_left += 4; break;
case OVX_8_LEFT: orb_cur_left -= 8; break;
case OVX_8_RIGHT: orb_cur_left += 8; break;
}
if(orb_cur_left <= ORB_LEFT_MIN) {
if(orb_velocity_x == OVX_4_LEFT) {
orb_velocity_x = OVX_4_RIGHT;
} else if(orb_velocity_x == OVX_8_LEFT) {
orb_velocity_x = OVX_8_RIGHT;
}
}
if(orb_cur_left >= ORB_LEFT_MAX) {
if(orb_velocity_x == OVX_4_RIGHT) {
orb_velocity_x = OVX_4_LEFT;
} else if(orb_velocity_x == OVX_8_RIGHT) {
orb_velocity_x = OVX_8_LEFT;
}
}
}
inline main_ptn_id_t orb_anim_cel(void) {
return static_cast<main_ptn_id_t>(
PTN_ORB + (orb_rotation_frame / ORB_FRAMES_PER_CEL)
);
}
void pascal orb_and_pellets_and_stage_unput_update_render__vsync_wait(
int stage_id
)
{
if(!orb_in_portal) {
orb_move_x(orb_velocity_x);
orb_cur_top += orb_velocity_y_update();
orb_frames_outside_portal++;
if(orb_is_moving_left()) {
orb_rotation_frame++;
}
if(orb_is_moving_right()) {
orb_rotation_frame--;
}
// ZUN quirk: The fourth cel is only visible for a single frame?
if(orb_rotation_frame >= ((ORB_CELS * ORB_FRAMES_PER_CEL) - 2)) {
orb_rotation_frame = 0;
}
if(orb_rotation_frame < 0) {
orb_rotation_frame = ((ORB_CELS - 1) * ORB_FRAMES_PER_CEL);
}
if(orb_cur_top > ORB_TOP_MAX) {
orb_force_new(COEFFICIENT_OF_RESTITUTION, OF_BOUNCE_FROM_SURFACE);
orb_cur_top = ORB_TOP_MAX;
cardcombo_cur = 0;
}
if(orb_cur_top < ORB_TOP_MIN) {
orb_force_new(0, OF_BOUNCE_FROM_TOP);
orb_cur_top = ORB_TOP_MIN;
}
}
frame_delay(1);
Pellets.unput_update_render();
// Stage object collision is explicitly deactivated on boss stages? Since
// there's still a card combo metric on the TOTLE screen though, these
// lines add another piece of evidence that that screen was shown for
// regular stages as well, at one point in development.
if(!stage_is_boss(stage_id)) {
cards_hittest(stage_id);
obstacles_update_and_render(false);
}
if(!orb_in_portal) {
orb_player_hittest(OR_NONE);
}
if(!orb_in_portal) {
// ZUN landmine: Shouldn't you unblit *before* updating the rotation
// frame? While it's OK (and even sort of essential) to assume that all
// Orb sprites are shaped identically (as they are in the original
// data), ordering the calls like this still violates common sense.
ptn_unput_8(orb_prev_left, orb_prev_top, orb_anim_cel());
}
if(!stage_is_boss(stage_id)) {
cards_update_and_render();
}
if(!orb_in_portal && !player_is_hit) {
ptn_put_8(orb_cur_left, orb_cur_top, orb_anim_cel());
}
orb_prev_left = orb_cur_left;
orb_prev_top = orb_cur_top;
}