#include #include "keyb.h" #include "vga.h" #include "sound.h" #include "map.h" #include "data.h" #include "game.h" #include "entities.h" #include "player.h" #define FRAME_STANDING 0 #define FRAME_JUMPING 4 #define FRAME_DYING 5 #define WALK_CYCLE_FRAMES 4 /* used for "coyote time" */ #define MAX_MOMENTUM 8 /* how many frames the player is invulerable after loosing a life */ #define INVULN_TIME 96 #define IS_NOT_GOING_UP(x) (gravity == GRAVITY_OFF || (x)>=GRAVITY_DOWN) static uint16_t x, y, ox, oy, erase; static uint8_t dir, frame, delay, gravity, jump, momentum; static uint16_t respawn_x, respawn_y; static uint8_t respawn_dir; static uint8_t invuln, dying, dead; const uint8_t gravity_seq[GRAVITY_SEQ_LEN] = { 6, 4, 4, 2, 2, 2, 2, 1, 1, 1, 0, 0, 0, 0, 0, 0, 1, 1, 1, 2, 2, 2, 2, 4 }; static const Rect frames[2][6] = { { /* walk cycle */ { 0, 0, 144, 112 }, { 16, 0, 144, 112 }, { 0, 0, 144, 112 }, { 32, 0, 144, 112 }, /* jump */ { 48, 0, 144, 112 }, /* dying */ { 128, 0, 144, 112 }, }, { /* walk cycle */ { 64, 0, 144, 112 }, { 80, 0, 144, 112 }, { 64, 0, 144, 112 }, { 96, 0, 144, 112 }, /* jump */ { 112, 0, 144, 112 }, /* dying */ { 128, 0, 144, 112 }, }, }; void player_init(uint16_t start_x, uint8_t start_y, uint8_t start_dir) { respawn_dir = dir = start_dir; frame = FRAME_STANDING; delay = 0; jump = 0; momentum = 0; invuln = 0; dying = 0; dead = 0; erase = 0; respawn_x = ox = x = start_x; respawn_y = oy = y = start_y; gravity = GRAVITY_OFF; } static void player_dying() { if (gravity != GRAVITY_OFF) { uint8_t steps = gravity_seq[gravity - 1]; if (gravity > GRAVITY_DOWN) { /* going down! */ for (uint8_t i = 0; i < steps; i++) { /* exit screen */ if (y == MAP_H * MAP_TILE_H - 16) { /* oh, well */ player_erase(); /* if there are lifes left, respawn */ if (dec_lives()) { player_init(respawn_x, respawn_y, respawn_dir); invuln = INVULN_TIME; } else dead = 1; /* stop falling */ gravity = GRAVITY_OFF; return; } y++; } } else { /* going up! */ if (y < steps) y = 0; else y -= steps; } if (gravity != GRAVITY_OFF && gravity != GRAVITY_SEQ_LEN) gravity++; } } void player_update() { uint8_t moved = 0; ox = x; oy = y; if (dying) { player_dying(); return; } if (invuln) invuln--; if (momentum) momentum--; if (gravity == GRAVITY_OFF && !map_is_blocked(x + 4, y + 16) && !map_is_blocked(x + 11, y + 16)) { gravity = GRAVITY_DOWN; frame = FRAME_JUMPING; moved = 1; } if (keys[KEY_UP]) { if ((gravity == GRAVITY_OFF || momentum) && !jump) { jump = 1; momentum = 0; frame = FRAME_JUMPING; gravity = GRAVITY_UP; sound_play_efx(EFX_JUMP); } } else jump = 0; if (keys[KEY_RIGHT] && !keys[KEY_LEFT]) { moved = 1; dir = DIR_RIGHT; /* XXX: off limits */ if (x < MAP_W * MAP_TILE_W - 16) { if ((IS_NOT_GOING_UP(gravity) && !map_is_blocked(x + 16, y + 15)) || (!IS_NOT_GOING_UP(gravity) && !(map_is_blocked(x + 16, y + 15) && map_is_blocked(x + 16, y + 7)))) { if (gravity == GRAVITY_OFF && momentum < MAX_MOMENTUM) momentum += 4; x++; } } } if (keys[KEY_LEFT] && !keys[KEY_RIGHT]) { moved = 1; dir = DIR_LEFT; /* XXX: off limits */ if (x) { if ((IS_NOT_GOING_UP(gravity) && !map_is_blocked(x - 1, y + 15)) || (!IS_NOT_GOING_UP(gravity) && !(map_is_blocked(x - 1, y + 15) && map_is_blocked(x - 1, y + 7)))) { if (gravity == GRAVITY_OFF && momentum < MAX_MOMENTUM) momentum += 4; x--; } } } if (gravity != GRAVITY_OFF) { uint8_t steps = gravity_seq[gravity - 1]; moved = 1; if (gravity > GRAVITY_DOWN) { /* going down! */ for (uint8_t i = 0; i < steps; i++) { /* hit the floor */ if ((map_is_blocked(x + 11, y + 16) || map_is_blocked(x + 4, y + 16)) && !map_is_blocked(x + 4, y + 15) && !map_is_blocked(x + 11, y + 15)) { gravity = GRAVITY_OFF; frame = FRAME_STANDING; /* is a deadly block? */ if (map_is_deadly(x + 11, y + 16) || map_is_deadly(x + 4, y + 16)) { player_hit(); return; } break; } y++; } } else { /* going up! */ if (y < steps) y = 0; else y -= steps; } if (gravity != GRAVITY_OFF && gravity != GRAVITY_SEQ_LEN) gravity++; } if (moved) { if (delay++ == WALK_DELAY) { delay = 0; if (frame != FRAME_JUMPING) { frame++; if (frame == WALK_CYCLE_FRAMES) frame = FRAME_STANDING; } } } else { delay = 0; frame = FRAME_STANDING; } if (map_update_gold(x + (dir == DIR_LEFT ? 7 : 8), y + 15)) { add_score(10); sound_play_efx(EFX_GOLD); } } void player_erase() { if (!erase) return; Rect dst = { ox, oy + MAP_OFFS_Y, 16, 16 }; blit_copy16(&dst); erase = 0; } void player_draw() { if (dead || (invuln && (invuln & 4))) return; Rect dst = { x, y + MAP_OFFS_Y, 16, 16 }; blitrc(binary_sprites_start, &frames[dir][frame], &dst); erase = 1; } uint8_t player_collision(Entity *e) { if (invuln) return 0; return player_collision_pickup(e); } uint8_t player_collision_pickup(Entity *e) { if (dying) return 0; if (y < e->y + 16 && e->y < y + 16 && x < e->x + 8 && e->x < x + 8) return 1; return 0; } void player_hit() { if (is_stageclear()) return; if (use_pickaxe()) { invuln = INVULN_TIME; sound_play_efx(EFX_HIT); } else { dying = 1; invuln = 0; frame = FRAME_DYING; gravity = GRAVITY_UP; sound_play_efx(EFX_DEATH); } } uint16_t player_x() { return x; } uint16_t player_y() { return y; } void player_stageclear() { invuln = 0; player_draw(); }