#include #include "keyb.h" #include "vga.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 #define GRAVITY_OFF 0 /* XXX: subtract 1 to get the value from gravity_seq */ #define GRAVITY_DOWN 14 #define GRAVITY_UP 1 #define GRAVITY_SEQ_LEN 24 /* 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; 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; 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 uint8_t bg[256]; static const Rect frames[2][6] = { { /* walk cycle */ { 0, 0, 144, 144 }, { 16, 0, 144, 144 }, { 0, 0, 144, 144 }, { 32, 0, 144, 144 }, /* jump */ { 48, 0, 144, 144 }, /* dying */ { 128, 0, 144, 144 }, }, { /* walk cycle */ { 64, 0, 144, 144 }, { 80, 0, 144, 144 }, { 64, 0, 144, 144 }, { 96, 0, 144, 144 }, /* jump */ { 112, 0, 144, 144 }, /* dying */ { 128, 0, 144, 144 }, }, }; 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; respawn_x = x = start_x; respawn_y = 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) { /* if there are lifes left, respawn */ if (dec_lives()) { player_init(respawn_x, respawn_y, respawn_dir); invuln = INVULN_TIME; } /* 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; 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; } } 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); } void player_erase() { Rect dst = { x, y + MAP_OFFS_Y, 16, 16 }; blit(bg, &dst); } void player_draw() { Rect dst = { x, y + MAP_OFFS_Y, 16, 16 }; read_buffer(bg, &dst); if (invuln && (invuln & 4)) return; blitrc(binary_sprites_start, &frames[dir][frame], &dst); } uint8_t player_collision(Entity *e) { if (invuln || 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() { /* TODO: pickaxe */ dying = 1; frame = FRAME_DYING; gravity = GRAVITY_UP; }