Implement ladder mechanics

Changelog:
- Change ladder to be in player state component
- Add ladder state transition
    - Hold up or down to enter ladder when overlap with ladder
    - On ground, ladder has to be on foot
    - Either jump or exit a ladder to exit ladder state
- Ignore external forces during ladder state
    - Ladder uses velocity instead of acceleration to move player
- Reduce jump speed if not on ground (such as on ladder)
- Make water transparent, finally
scene_man
En Yi 2023-05-06 18:39:10 +08:00
parent c44696c1f8
commit 9eb46d0e57
3 changed files with 104 additions and 10 deletions

View File

@ -38,7 +38,6 @@ typedef struct _CTransform_t {
typedef struct _CMovementState_t { typedef struct _CMovementState_t {
uint8_t ground_state; uint8_t ground_state;
uint8_t water_state; uint8_t water_state;
bool ladder_state;
} CMovementState_t; } CMovementState_t;
// This is to store the occupying tiles // This is to store the occupying tiles
@ -67,6 +66,7 @@ typedef struct _CPlayerState_t {
Vector2 player_dir; Vector2 player_dir;
uint8_t jump_pressed; uint8_t jump_pressed;
uint8_t is_crouch; uint8_t is_crouch;
bool ladder_state;
} CPlayerState_t; } CPlayerState_t;
typedef enum ContainerItem { typedef enum ContainerItem {

View File

@ -52,10 +52,12 @@ static void level_scene_render_func(Scene_t* scene)
{ {
DrawRectangle(x, y, TILE_SIZE, TILE_SIZE, ORANGE); DrawRectangle(x, y, TILE_SIZE, TILE_SIZE, ORANGE);
} }
else if (tilemap.tiles[i].water_level > 0)
if (tilemap.tiles[i].water_level > 0)
{ {
// Draw water tile // Draw water tile
DrawRectangle(x, y, TILE_SIZE, TILE_SIZE, BLUE); Color water_colour = ColorAlpha(BLUE, 0.5);
DrawRectangle(x, y, TILE_SIZE, TILE_SIZE, water_colour);
} }
} }
@ -158,6 +160,8 @@ static void level_scene_render_func(Scene_t* scene)
DrawText(buffer, tilemap.width * TILE_SIZE + 1, 90, 12, BLACK); DrawText(buffer, tilemap.width * TILE_SIZE + 1, 90, 12, BLACK);
sprintf(buffer, "Water: %s", p_mstate->water_state & 1? "YES":"NO"); sprintf(buffer, "Water: %s", p_mstate->water_state & 1? "YES":"NO");
DrawText(buffer, tilemap.width * TILE_SIZE + 1, 120, 12, BLACK); DrawText(buffer, tilemap.width * TILE_SIZE + 1, 120, 12, BLACK);
sprintf(buffer, "Ladder: %u", p_pstate->ladder_state);
DrawText(buffer, tilemap.width * TILE_SIZE + 1, 150, 12, BLACK);
} }
sprintf(buffer, "Spawn Entity: %u", current_spawn_selection); sprintf(buffer, "Spawn Entity: %u", current_spawn_selection);
DrawText(buffer, tilemap.width * TILE_SIZE + 1, 240, 12, BLACK); DrawText(buffer, tilemap.width * TILE_SIZE + 1, 240, 12, BLACK);
@ -266,7 +270,6 @@ static void toggle_block_system(Scene_t* scene)
tilemap.tiles[tile_idx].tile_type = ONEWAY_TILE; tilemap.tiles[tile_idx].tile_type = ONEWAY_TILE;
tilemap.tiles[tile_idx].solid = ONE_WAY; tilemap.tiles[tile_idx].solid = ONE_WAY;
} }
tilemap.tiles[tile_idx].water_level = 0;
break; break;
case TOGGLE_LADDER: case TOGGLE_LADDER:
if (tilemap.tiles[tile_idx].tile_type == LADDER) if (tilemap.tiles[tile_idx].tile_type == LADDER)
@ -292,7 +295,6 @@ static void toggle_block_system(Scene_t* scene)
{ {
tilemap.tiles[down_tile].solid = (tilemap.tiles[tile_idx].tile_type != LADDER)? ONE_WAY : NOT_SOLID; tilemap.tiles[down_tile].solid = (tilemap.tiles[tile_idx].tile_type != LADDER)? ONE_WAY : NOT_SOLID;
} }
tilemap.tiles[tile_idx].water_level = 0;
break; break;
case SPAWN_CRATE: case SPAWN_CRATE:
spawn_crate(scene, tile_idx, false); spawn_crate(scene, tile_idx, false);
@ -339,7 +341,6 @@ void level_do_action(Scene_t* scene, ActionType_t action, bool pressed)
break; break;
case ACTION_DOWN: case ACTION_DOWN:
p_playerstate->player_dir.y = (pressed)? 1 : 0; p_playerstate->player_dir.y = (pressed)? 1 : 0;
//p_playerstate->is_crouch |= (pressed)? 0b10 : 0;
break; break;
case ACTION_LEFT: case ACTION_LEFT:
p_playerstate->player_dir.x = (pressed)? -1 : 0; p_playerstate->player_dir.x = (pressed)? -1 : 0;

View File

@ -313,10 +313,85 @@ void player_movement_input_system(Scene_t* scene)
CJump_t* p_cjump = get_component(&scene->ent_manager, p_player, CJUMP_COMP_T); CJump_t* p_cjump = get_component(&scene->ent_manager, p_player, CJUMP_COMP_T);
CMovementState_t* p_mstate = get_component(&scene->ent_manager, p_player, CMOVEMENTSTATE_T); CMovementState_t* p_mstate = get_component(&scene->ent_manager, p_player, CMOVEMENTSTATE_T);
// Ladder handling
if (!p_pstate->ladder_state)
{
if (p_pstate->player_dir.y < 0)
{
unsigned int tile_idx = get_tile_idx(
p_ctransform->position.x + p_bbox->half_size.x,
p_ctransform->position.y + p_bbox->half_size.y,
data->tilemap.width
);
if (tilemap.tiles[tile_idx].tile_type == LADDER)
{
p_pstate->ladder_state = true;
//p_ctransform->position.x = (tile_idx % tilemap.width) * TILE_SIZE;
if (p_mstate->ground_state & 1)
{
p_ctransform->position.y--;
}
}
}
else if (p_pstate->player_dir.y > 0)
{
unsigned int tile_idx;
if (p_mstate->ground_state & 1)
{
tile_idx = get_tile_idx(
p_ctransform->position.x + p_bbox->half_size.x,
p_ctransform->position.y + p_bbox->size.y,
data->tilemap.width
);
}
else
{
tile_idx = get_tile_idx(
p_ctransform->position.x + p_bbox->half_size.x,
p_ctransform->position.y + p_bbox->half_size.y,
data->tilemap.width
);
}
if (tile_idx < tilemap.n_tiles && tilemap.tiles[tile_idx].tile_type == LADDER)
{
p_pstate->ladder_state = true;
//p_ctransform->position.x = (tile_idx % tilemap.width) * TILE_SIZE;
if (p_mstate->ground_state & 1)
{
p_ctransform->position.y += TILE_SIZE / 2;
}
}
}
}
else
{
unsigned int tile_x = (p_ctransform->position.x + p_bbox->half_size.x) / TILE_SIZE;
unsigned int tile_y1 = (p_ctransform->position.y + p_bbox->half_size.y) / TILE_SIZE;
unsigned int tile_y2 = (p_ctransform->position.y + p_bbox->size.y) / TILE_SIZE;
p_pstate->ladder_state = false;
if (!(p_mstate->ground_state & 1))
{
for(unsigned int tile_y = tile_y1; tile_y <= tile_y2; tile_y++)
{
unsigned int tile_idx = tile_y * tilemap.width + tile_x;
p_pstate->ladder_state |= tilemap.tiles[tile_idx].tile_type == LADDER;
}
}
if (p_pstate->ladder_state)
{
p_ctransform->velocity.y = p_pstate->player_dir.y * 150;
p_ctransform->velocity.x = p_pstate->player_dir.x * 40;
}
}
bool in_water = (p_mstate->water_state & 1); bool in_water = (p_mstate->water_state & 1);
p_pstate->is_crouch |= (p_pstate->player_dir.y > 0)? 0b10 : 0;
if (!in_water) if (!in_water)
{ {
p_pstate->is_crouch |= (p_pstate->player_dir.y > 0)? 0b10 : 0;
p_pstate->player_dir.y = 0; p_pstate->player_dir.y = 0;
p_ctransform->accel = Vector2Scale(Vector2Normalize(p_pstate->player_dir), MOVE_ACCEL); p_ctransform->accel = Vector2Scale(Vector2Normalize(p_pstate->player_dir), MOVE_ACCEL);
} }
@ -368,10 +443,18 @@ void player_movement_input_system(Scene_t* scene)
// Check if possible to jump when jump is pressed // Check if possible to jump when jump is pressed
if (p_pstate->jump_pressed && p_cjump->jumps > 0 && p_cjump->jump_ready) if (p_pstate->jump_pressed && p_cjump->jumps > 0 && p_cjump->jump_ready)
{ {
p_pstate->ladder_state = false;
p_cjump->jumps--; p_cjump->jumps--;
if (!in_water) if (!in_water)
{ {
p_ctransform->velocity.y = -p_cjump->jump_speed; if (p_mstate->ground_state & 1)
{
p_ctransform->velocity.y = -p_cjump->jump_speed;
}
else
{
p_ctransform->velocity.y = -p_cjump->jump_speed / 1.4;
}
} }
else else
{ {
@ -419,7 +502,7 @@ void player_bbox_update_system(Scene_t* scene)
} }
else else
{ {
if (p_mstate->water_state & 1) if ((p_mstate->water_state & 1) && !p_pstate->ladder_state)
{ {
new_bbox.x = PLAYER_C_WIDTH; new_bbox.x = PLAYER_C_WIDTH;
new_bbox.y = PLAYER_C_HEIGHT; new_bbox.y = PLAYER_C_HEIGHT;
@ -704,6 +787,16 @@ void global_external_forces_system(Scene_t* scene)
} }
} }
CPlayerState_t* p_pstate;
sc_map_foreach(&scene->ent_manager.component_map[CPLAYERSTATE_T], ent_idx, p_pstate)
{
Entity_t* p_ent = get_entity(&scene->ent_manager, ent_idx);
CTransform_t* p_ctransform = get_component(&scene->ent_manager, p_ent, CTRANSFORM_COMP_T);
if (p_pstate->ladder_state)
{
p_ctransform->accel = (Vector2){0,0};
}
}
} }
void movement_update_system(Scene_t* scene) void movement_update_system(Scene_t* scene)
@ -753,7 +846,7 @@ void player_ground_air_transition_system(Scene_t* scene)
// Handle Ground<->Air Transition // Handle Ground<->Air Transition
bool in_water = (p_mstate->water_state & 1); bool in_water = (p_mstate->water_state & 1);
// Landing or in water // Landing or in water
if ((p_mstate->ground_state & 1 || in_water) && !p_pstate->jump_pressed ) if ((p_mstate->ground_state & 1 || in_water || p_pstate->ladder_state) && !p_pstate->jump_pressed )
{ {
// Recover jumps // Recover jumps
p_cjump->jumps = p_cjump->max_jumps; p_cjump->jumps = p_cjump->max_jumps;