Add climbing

This commit is contained in:
Lucas S. Vieira 2025-04-14 20:33:31 -03:00
parent 66d1617fe8
commit 8204f4587e
2 changed files with 112 additions and 12 deletions

View file

@ -16,6 +16,8 @@
#define HEIGHT_RADIUS_ROLLING 14
#define PUSH_RADIUS 10
#define HEIGHT_RADIUS_CLIMB 10
#define PLAYER_HURT_IFRAMES 120
#define PLAYER_FLY_MAXFRAMES 480
@ -46,7 +48,7 @@ typedef enum {
ACTION_GLIDE,
ACTION_DROP,
ACTION_CLIMB,
ACTION_CLIMBUP,
ACTION_CLAMBER,
} PlayerAction;
// Alias to make things look less weird
@ -110,6 +112,12 @@ typedef struct {
CollisionEvent ev_ceil1;
CollisionEvent ev_ceil2;
// Collision detectors for Knuckles.
// Used for dropping of the bottom of a wall
// or clambering it up.
CollisionEvent ev_climbdrop;
CollisionEvent ev_clamber;
VECTOR startpos;
VECTOR respawnpos;
} Player;

View file

@ -157,6 +157,8 @@ load_player(Player *player,
player->ev_right = (CollisionEvent){ 0 };
player->ev_ceil1 = (CollisionEvent){ 0 };
player->ev_ceil2 = (CollisionEvent){ 0 };
player->ev_climbdrop = (CollisionEvent){ .collided = 1 };
player->ev_clamber = (CollisionEvent){ .collided = 1 };
player->col_ledge = 0;
player->action = ACTION_NONE;
@ -339,6 +341,27 @@ _player_update_collision_lr(Player *player)
anchorx = (player->pos.vx >> 12),
anchory = (player->pos.vy >> 12) - 8;
// SPECIAL HORIZONTAL COLLISION: KNUCKLES CLIMB DROP / CLAMBERING
// This is a special collision case that only occurs to Knuckles.
// These will only happen when Knuckles is climbing which basically
// ignores all other horizontal collision
if(player->action == ACTION_CLIMB) {
LinecastDirection dir =
(player->anim_dir > 0) ? CDIR_RWALL : CDIR_LWALL;
uint16_t radius = PUSH_RADIUS + ((player->anim_dir < 0) ? 1 : 0);
int16_t drop_anchory = anchory + HEIGHT_RADIUS_CLIMB;
int16_t clamber_anchory = anchory - HEIGHT_RADIUS_CLIMB;
player->ev_climbdrop = linecast(&leveldata, &map128, &map16,
anchorx, drop_anchory,
dir, radius, CDIR_FLOOR);
player->ev_clamber = linecast(&leveldata, &map128, &map16,
anchorx, clamber_anchory,
dir, radius, CDIR_FLOOR);
return;
}
// Adjust y anchor to y + 8 when on totally flat ground
int32_t push_anchory = anchory
+ ((player->grnd && player->angle == 0) ? 8 : 0);
@ -1059,7 +1082,17 @@ player_update(Player *player)
// Apply glide acceleration
player->vel.vx += player->anim_dir * KNUX_GLIDE_X_ACCEL;
}
} else if(player->ctrllock == 0) {
// If you're facing a certain direction and you hit the wall on
// that direction, then just climb
if(((player->anim_dir > 0) && player->ev_right.collided) ||
((player->anim_dir < 0) && player->ev_left.collided)) {
player->vel.vx = 0;
player->vel.vz = 0;
player_set_action(player, ACTION_CLIMB);
}
} else if((player->ctrllock == 0)
&& (player->action != ACTION_CLIMB)) {
if(input_pressing(&player->input, PAD_RIGHT)) {
if(player->vel.vx < player->cnst->x_top_spd)
player->vel.vx += player->cnst->x_air_accel;
@ -1155,6 +1188,31 @@ player_update(Player *player)
player->vel.vx >>= 2; // times 0.25
player->airdirlock = 1;
}
} else if(player->action == ACTION_CLIMB) {
if(!player->ev_climbdrop.collided) {
// Fall off bottom of wall if no wall is found at Y + height radius
player_set_action(player, ACTION_DROP);
}
/* else if(!player->ev_clamber.collided) { */
/* // Clamber if no wall is found at Y - height radius */
/* } */
else {
// Climbing movement
if(input_pressing(&player->input, PAD_UP))
player->vel.vy = -ONE;
else if(input_pressing(&player->input, PAD_DOWN))
player->vel.vy = ONE;
else player->vel.vy = 0;
// Jump back
if(input_pressed(&player->input, PAD_CROSS)) {
player_set_action(player, ACTION_JUMPING);
player->anim_dir *= -1;
player->vel.vx = (4 << 12) * player->anim_dir;
player->vel.vy = -(4 << 12);
player->holding_jump = 1;
}
}
}
// Apply gravity
@ -1172,6 +1230,9 @@ player_update(Player *player)
player->vel.vy += KNUX_GLIDE_GRAVITY;
else player->vel.vy -= KNUX_GLIDE_GRAVITY;
break;
case ACTION_CLIMB:
// NO GRAVITY!
break;
default:
player->vel.vy += player->cnst->y_gravity;
break;
@ -1184,7 +1245,6 @@ player_update(Player *player)
player->vel.vx -= (player->cnst->y_jump_strength * rsin(player->angle)) >> 12;
player->vel.vy -= (player->cnst->y_jump_strength * rcos(player->angle)) >> 12;
player->grnd = 0;
player_set_animation_direct(player, ANIM_ROLLING);
sound_play_vag(sfx_jump, 0);
player_set_action(player, ACTION_JUMPING);
player->holding_jump = 1;
@ -1262,17 +1322,14 @@ player_update(Player *player)
}
} else {
player->idle_timer = ANIM_IDLE_TIMER_MAX;
if(player->action == ACTION_SPRING) {
if(player->action == ACTION_JUMPING) {
player_set_animation_direct(player, ANIM_ROLLING);
} else if(player->action == ACTION_SPRING) {
if(player->vel.vy < 0) {
player_set_animation_direct(player, ANIM_SPRING);
} else {
player->airdirlock = 0;
player_set_animation_direct(player, ANIM_DROP);
/* if(abs(player->vel.vz) >= (10 << 12)) { */
/* player_set_animation_direct(player, ANIM_PEELOUT); */
/* } else if(abs(player->vel.vz) >= (6 << 12)) { */
/* player_set_animation_direct(player, ANIM_RUNNING); */
/* } else player_set_animation_direct(player, ANIM_WALKING); */
}
} else if(player->action == ACTION_HURT) {
player_set_animation_direct(player, ANIM_HURT);
@ -1302,7 +1359,28 @@ player_update(Player *player)
player_set_animation_direct(player, ANIM_GLIDECANCEL);
player_set_frame_duration(player, 12);
player->loopback_frame = 1;
} else if(player->action == ACTION_CLIMB) {
if(player->vel.vy == 0)
player_set_animation_direct(player, ANIM_CLIMBSTOP);
else if(player->vel.vy < 0)
player_set_animation_direct(player, ANIM_CLIMBUP);
else player_set_animation_direct(player, ANIM_CLIMBDOWN);
} else {
// NO ACTION
// Only handle cases where we have certain animations that would
// make falling weird
if(player->cur_anim) {
switch(player->cur_anim->hname) {
case ANIM_CLIMBSTOP:
case ANIM_CLIMBUP:
case ANIM_CLIMBDOWN:
case ANIM_GLIDE:
case ANIM_GLIDETURNA:
case ANIM_GLIDETURNB:
player_set_animation_direct(player, ANIM_WALKING);
break;
}
}
}
}
@ -1366,6 +1444,11 @@ player_update(Player *player)
player_set_frame_duration(player, 12);
break;
case ANIM_CLIMBUP:
case ANIM_CLIMBDOWN:
player_set_frame_duration(player, 4);
break;
// Single-frame animations
case ANIM_STOPPED:
case ANIM_IDLE:
@ -1514,7 +1597,7 @@ player_update(Player *player)
if(debug_mode > 1) {
char buffer[255];
snprintf(buffer, 255,
"COL %s%s.%s%s.%s.%s\n"
"COL %s%s.%s%s.%s.%s %s.%s\n"
,
// Collisions
player->ev_ceil1.collided ? "C" : " ",
@ -1522,7 +1605,9 @@ player_update(Player *player)
player->ev_grnd1.collided ? "G" : " ",
player->ev_grnd2.collided ? "G" : " ",
player->ev_left.collided ? "L" : " ",
player->ev_right.collided ? "R" : " ");
player->ev_right.collided ? "R" : " ",
player->ev_climbdrop.collided ? "D" : " ",
player->ev_clamber.collided ? "U" : " ");
font_draw_sm(buffer, 8, 74);
}
@ -1718,5 +1803,12 @@ player_set_action(Player *player, PlayerAction action)
} else if(player->action == ACTION_GLIDE) {
player->sliding = 0;
}
if(action == ACTION_CLIMB) {
// "Fake" collision to start as detecting a wall
// so Knuckles doesn't drop instantly
player->ev_climbdrop.collided = player->ev_clamber.collided = 1;
}
player->action = action;
}