/* SPDX-License-Identifier: MIT * Copyright (c) 2021 KikooDX * This file is part of * [Painfull Success CG](https://git.sr.ht/~kikoodx/painfull-success-cg), * which is MIT licensed. The MIT license requires this copyright notice to be * included in all copies and substantial portions of the software. */ #include #include #include "lazyint.h" #include "player.h" #include "conf.h" #include "vec2.h" #include "level.h" #include "input.h" #include "tiles.h" Player player_init() { return (Player){ .pos = (Vec2){}, .spd_x = 0.0, .spd_y = 0.0, .facing = 1, .stun = false, .knocked = false, .keys_left = 0, .jump_buffer = 0, .coyot = 0, }; } void player_update(Player *player, Level *level, Input input, u8 *level_id) { const Vec2 one_px_down = (Vec2){player->pos.x, player->pos.y + 1}; const bool on_ground = player_collide(*level, one_px_down, SOLID_TILE, 0) || player_collide(*level, one_px_down, SEMI_SOLID_TILE, 0); if (!player->stun) { /* Get input. */ const i8 move_x = input_is_down(input, K_RIGHT) - input_is_down(input, K_LEFT); const bool fastfall = input_is_down(input, K_DOWN); const bool jump_pressed = input_is_pressed(input, K_JUMP); const bool jump_held = input_is_down(input, K_JUMP); /* Facing direction. */ if (move_x != 0) player->facing = move_x; /* Friction. */ if (on_ground) player->spd_x *= 1 - GROUND_FRICTION; else player->spd_x *= 1 - AIR_FRICTION; /* Acceleration. */ if (on_ground) player->spd_x += (f32)(move_x) * GROUND_ACCELERATION; else player->spd_x += (f32)(move_x) * AIR_ACCELERATION; /* Move the player. */ player_move(player, *level, round(player->spd_x), 0); player_move(player, *level, 0, round(player->spd_y)); } } void player_draw(Player player) { /* Draw colored rectangle depending on player state. */ u32 color = 0; if (player.stun) color = C_RGB(230/4, 41/4, 55/4); /* Red. */ else color = C_RGB(0/4, 121/4, 241/4); /* Blue. */ const vec2_int_t x = player.pos.x + DRAW_OFFSET_X; const vec2_int_t y = player.pos.y + DRAW_OFFSET_Y; drect(x, y, x + PLAYER_WIDTH - 1, y + PLAYER_HEIGHT - 1, color); } /* Helper functions */ bool player_collide(Level level, Vec2 pos, tile_t tile, u8 margin) { const vec2_int_t left = pos.x + margin; const vec2_int_t right = pos.x + PLAYER_WIDTH - 1 - margin * 2; const vec2_int_t up = pos.y + margin; const vec2_int_t down = pos.y + PLAYER_HEIGHT - 1 - margin * 2; return ((tile == level_get_tile_at_px(level, (Vec2){left, up})) || (tile == level_get_tile_at_px(level, (Vec2){right, up})) || (tile == level_get_tile_at_px(level, (Vec2){left, down})) || (tile == level_get_tile_at_px(level, (Vec2){right, down}))); } bool player_move(Player *player, Level level, i8 spd_x, i8 spd_y) { player->pos.x += spd_x; player->pos.y += spd_y; /* If player is in solid, move back until it ain't. */ if (player_collide(level, player->pos, SOLID_TILE, 0)) { const u8 sign_spd_x = sign(spd_x); const u8 sign_spd_y = sign(spd_y); if (sign_spd_x != 0) player->spd_x = 0; if (sign_spd_y != 0) player->spd_y = 0; while (player_collide(level, player->pos, SOLID_TILE, 0)) { player->pos.x -= sign_spd_x; player->pos.y -= sign_spd_y; } return true; } return false; } i8 sign(i8 value) { if (value > 0) return 1; else if (value < 0) return -1; else return 0; } i8 round(f32 value) { return (i8)value; }