Add Knuckles' gliding (WIP)

This commit is contained in:
Lucas S. Vieira 2025-04-09 21:06:01 -03:00
parent 060ab045d8
commit 8269d7e316
5 changed files with 119 additions and 22 deletions

View file

@ -41,6 +41,8 @@ typedef enum {
ACTION_HURT, ACTION_HURT,
ACTION_GASP, ACTION_GASP,
ACTION_FLY, ACTION_FLY,
ACTION_GLIDE,
ACTION_DROP,
} PlayerAction; } PlayerAction;
// Alias to make things look less weird // Alias to make things look less weird
@ -80,17 +82,14 @@ typedef struct {
uint32_t spinrev; // Also used as flight and glide direction toggle uint32_t spinrev; // Also used as flight and glide direction toggle
uint8_t ctrllock; uint8_t ctrllock;
uint8_t airdirlock; uint8_t airdirlock;
uint16_t framecount; uint16_t framecount; // Used for many purposes incl. flight and glide
uint8_t holding_jump; uint8_t holding_jump;
uint16_t iframes; uint16_t iframes;
uint8_t shield; uint8_t shield;
int32_t speedshoes_frames; int32_t speedshoes_frames;
uint8_t underwater;
// TODO: Change this to player value modes. uint16_t remaining_air_frames;
// For now we're only storing info on whether int8_t glide_turn_dir;
// player is underwater
uint8_t underwater;
uint16_t remaining_air_frames;
// Collision modes // Collision modes
CollMode gsmode; CollMode gsmode;

View file

@ -6,6 +6,11 @@
#define MILES_GRAVITY_FLYDOWN 0x00000080 #define MILES_GRAVITY_FLYDOWN 0x00000080
#define MILES_GRAVITY_FLYUP 0x00000200 // Fly up by SUBTRACTING this gravity #define MILES_GRAVITY_FLYUP 0x00000200 // Fly up by SUBTRACTING this gravity
#define KNUX_GLIDE_X_ACCEL 0x00000040
#define KNUX_GLIDE_X_TOPSPD 0x00018000
#define KNUX_GLIDE_GRAVITY 0x00000200 // May be added or subtracted
#define KNUX_GLIDE_TURN_STEP 0x00000020 // 20.12 scale for a range [0.5 -- 0]
// Constants for running the game at a fixed 60 FPS. // Constants for running the game at a fixed 60 FPS.
// These constants are also in a 12-scale format for fixed point math. // These constants are also in a 12-scale format for fixed point math.
typedef struct { typedef struct {

View file

@ -101,7 +101,8 @@ object_update(ObjectState *state, ObjectTableEntry *typedata, VECTOR *pos)
player_attacking = (player.action == ACTION_JUMPING || player_attacking = (player.action == ACTION_JUMPING ||
player.action == ACTION_ROLLING || player.action == ACTION_ROLLING ||
player.action == ACTION_SPINDASH || player.action == ACTION_SPINDASH ||
player.action == ACTION_DROPDASH); player.action == ACTION_DROPDASH ||
player.action == ACTION_GLIDE);
player_height = (player_attacking player_height = (player_attacking
? HEIGHT_RADIUS_ROLLING ? HEIGHT_RADIUS_ROLLING
: HEIGHT_RADIUS_NORMAL) << 1; : HEIGHT_RADIUS_NORMAL) << 1;

View file

@ -142,6 +142,7 @@ load_player(Player *player,
player->psmode = CDIR_FLOOR; player->psmode = CDIR_FLOOR;
player->remaining_air_frames = 1800; // 30 seconds player->remaining_air_frames = 1800; // 30 seconds
player->speedshoes_frames = -1; // Start inactive player->speedshoes_frames = -1; // Start inactive
player->glide_turn_dir = 0;
player_set_animation_direct(player, ANIM_STOPPED); player_set_animation_direct(player, ANIM_STOPPED);
player->anim_frame = player->anim_timer = 0; player->anim_frame = player->anim_timer = 0;
@ -702,6 +703,12 @@ _player_update_collision_tb(Player *player)
} }
sound_play_vag(sfx_relea, 0); sound_play_vag(sfx_relea, 0);
camera.lag = 0x8000 >> 12; camera.lag = 0x8000 >> 12;
} else if(player->action == ACTION_GLIDE) {
// TODO: Slide
} else if(player->action == ACTION_DROP) {
player_set_action(player, ACTION_NONE);
player->vel.vz = 0;
player->airdirlock = 0;
} }
} }
@ -1002,23 +1009,67 @@ player_update(Player *player)
player->vel.vy = (player->vel.vz * -rsin(player->angle)) >> 12; player->vel.vy = (player->vel.vz * -rsin(player->angle)) >> 12;
} else { } else {
// Air X movement // Air X movement
if(input_pressing(&player->input, PAD_RIGHT) if(player->action == ACTION_GLIDE) {
&& (player->ctrllock == 0)) { // spinrev is an acceleration "angle" for turning while gliding.
if(player->vel.vx < player->cnst->x_top_spd) if(player->glide_turn_dir == -1) { // Turning right to left
player->vel.vx += player->cnst->x_air_accel; // Disable turn, fix angle
if(!player->airdirlock) if(player->spinrev >= (ONE >> 1)) {
player->anim_dir = 1; player->glide_turn_dir = 0;
} else if(input_pressing(&player->input, PAD_LEFT) player->spinrev = (ONE >> 1);
&& (player->ctrllock == 0)) { } else {
if(player->vel.vx > -player->cnst->x_top_spd) // Still turning? Setup X speed
player->vel.vx -= player->cnst->x_air_accel; player->spinrev += KNUX_GLIDE_TURN_STEP;
if(!player->airdirlock) player->vel.vx = (player->vel.vz * rcos(player->spinrev)) >> 12;
player->anim_dir = -1; // If angle is past 90 degrees, turn animation
if(player->spinrev >= (ONE >> 2)) player->anim_dir = -1;
}
} else if(player->glide_turn_dir == 1) { // Turning left to right
// Disable turn, fix angle
if(player->spinrev <= 0) {
player->glide_turn_dir = 0;
player->spinrev = 0;
} else {
player->spinrev -= KNUX_GLIDE_TURN_STEP;
player->vel.vx = (player->vel.vz * -rcos(player->spinrev)) >> 12;
// If angle is past 90 degrees, turn animation
if(player->spinrev <= (ONE >> 2)) player->anim_dir = 1;
}
}
// Turn to the other side
if(input_pressed(&player->input, PAD_LEFT)
&& (player->vel.vx > ONE)) {
player->vel.vz = player->vel.vx;
player->glide_turn_dir = -1;
} else if(input_pressed(&player->input, PAD_RIGHT)
&& (player->vel.vx < -ONE)) {
player->vel.vz = player->vel.vx;
player->glide_turn_dir = 1;
}
// When not turning...
if(player->glide_turn_dir == 0) {
// Apply glide acceleration
player->vel.vx += player->anim_dir * KNUX_GLIDE_X_ACCEL;
}
} else if(player->ctrllock == 0) {
if(input_pressing(&player->input, PAD_RIGHT)) {
if(player->vel.vx < player->cnst->x_top_spd)
player->vel.vx += player->cnst->x_air_accel;
if(!player->airdirlock)
player->anim_dir = 1;
} else if(input_pressing(&player->input, PAD_LEFT)) {
if(player->vel.vx > -player->cnst->x_top_spd)
player->vel.vx -= player->cnst->x_air_accel;
if(!player->airdirlock)
player->anim_dir = -1;
}
} }
// Air drag. Calculated before applying gravity. // Air drag. Calculated before applying gravity.
if((player->vel.vy < 0 && player->vel.vy > -player->cnst->y_min_jump) if((player->vel.vy < 0 && player->vel.vy > -player->cnst->y_min_jump)
&& (player->action != ACTION_HURT)) { && (player->action != ACTION_HURT)
&& (player->action != ACTION_GLIDE)) {
// xsp -= (xsp div 0.125) / 256 // xsp -= (xsp div 0.125) / 256
int32_t air_drag = (div12(abs(player->vel.vx), 0x200) << 12) / 0x100000; int32_t air_drag = (div12(abs(player->vel.vx), 0x200) << 12) / 0x100000;
if(player->vel.vx > 0) if(player->vel.vx > 0)
@ -1059,6 +1110,15 @@ player_update(Player *player)
player->spinrev = 0; // Do not move up for first time player->spinrev = 0; // Do not move up for first time
player->framecount = 0; // Start counter for tiredness player->framecount = 0; // Start counter for tiredness
break; break;
case CHARA_KNUCKLES:
player_set_action(player, ACTION_GLIDE);
player->vel.vx = (4 * player->anim_dir) << 12;
if(player->vel.vy < 0) player->vel.vy = 0;
// Glide "angle" (used when turning).
// Starts pointing at direction by default (0 for right,
// 0.5 for left)
player->spinrev = (player->anim_dir > 0) ? 0 : (ONE >> 1);
player->glide_turn_dir = 0;
default: break; default: break;
} }
} }
@ -1080,6 +1140,13 @@ player_update(Player *player)
// if ascending and ysp < -1, turn on descent again // if ascending and ysp < -1, turn on descent again
if(player->spinrev && (player->vel.vy < -ONE)) if(player->spinrev && (player->vel.vy < -ONE))
player->spinrev = 0; player->spinrev = 0;
} else if(player->action == ACTION_GLIDE) {
if(!input_pressing(&player->input, PAD_CROSS)) {
// Cancel gliding
player_set_action(player, ACTION_DROP);
player->vel.vx >>= 2; // times 0.25
player->airdirlock = 1;
}
} }
// Apply gravity // Apply gravity
@ -1092,6 +1159,11 @@ player_update(Player *player)
if(player->spinrev) player->vel.vy -= MILES_GRAVITY_FLYUP; if(player->spinrev) player->vel.vy -= MILES_GRAVITY_FLYUP;
else player->vel.vy += MILES_GRAVITY_FLYDOWN; else player->vel.vy += MILES_GRAVITY_FLYDOWN;
break; break;
case ACTION_GLIDE:
if(player->vel.vy < (ONE >> 1))
player->vel.vy += KNUX_GLIDE_GRAVITY;
else player->vel.vy -= KNUX_GLIDE_GRAVITY;
break;
default: default:
player->vel.vy += player->cnst->y_gravity; player->vel.vy += player->cnst->y_gravity;
break; break;
@ -1208,6 +1280,19 @@ player_update(Player *player)
: ((player->spinrev) || (player->vel.vy < 0) : ((player->spinrev) || (player->vel.vy < 0)
? ANIM_FLYUP ? ANIM_FLYUP
: ANIM_FLYDOWN)); : ANIM_FLYDOWN));
} else if(player->action == ACTION_GLIDE) {
if(player->glide_turn_dir == 0) {
player_set_animation_direct(player, ANIM_GLIDE);
} else {
if(abs(player->vel.vx) >= (1 << 12))
player_set_animation_direct(player, ANIM_GLIDETURNA);
else player_set_animation_direct(player, ANIM_GLIDETURNB);
}
} else if(player->action == ACTION_DROP) {
player_set_animation_direct(player, ANIM_GLIDECANCEL);
player_set_frame_duration(player, 12);
player->loopback_frame = 1;
} }
} }
@ -1261,6 +1346,10 @@ player_update(Player *player)
player_set_frame_duration(player, 12); player_set_frame_duration(player, 12);
break; break;
case ANIM_GLIDECANCEL:
player_set_frame_duration(player, 12);
break;
// Single-frame animations // Single-frame animations
case ANIM_STOPPED: case ANIM_STOPPED:
case ANIM_IDLE: case ANIM_IDLE:

View file

@ -671,9 +671,12 @@ screen_level_draw(void *d)
snprintf(buffer, 255, "AIR %02d", player.remaining_air_frames / 60); snprintf(buffer, 255, "AIR %02d", player.remaining_air_frames / 60);
font_draw_sm(buffer, 248, 52); font_draw_sm(buffer, 248, 52);
snprintf(buffer, 255, "SPR %4d", level_get_num_sprites()); snprintf(buffer, 255, "REV %4d", player.spinrev);
font_draw_sm(buffer, 248, 60); font_draw_sm(buffer, 248, 60);
snprintf(buffer, 255, "SPR %4d", level_get_num_sprites());
font_draw_sm(buffer, 248, 68);
// Player debug // Player debug
if(debug_mode > 1) { if(debug_mode > 1) {
snprintf(buffer, 255, snprintf(buffer, 255,