#include #include #include #include "keyb.h" #include "vga.h" #include "sound.h" #include "text.h" #include "map.h" #include "data.h" #include "timer.h" #include "entities.h" #include "tmonster.h" #include "player.h" #include "game.h" #define GAME_LIVES_START 3 #define GAME_LIVES_MAX 9 #define GAME_TIME_MAX 60 #define GAME_MAX_PICKAXE 3 #define GAME_EXTRA_LIFE ((uint16_t)10000) #define HUD_CLEAN 0 #define HUD_LIVES 1 #define HUD_SCORE 2 #define HUD_STAGE 4 #define HUD_TIME 8 #define HUD_PICKAXE 16 #define HUD_ALL 255 #define GAMEOVER_DELAY 96 #define STAGECLEAR_DELAY 96 static uint32_t hiscore = 15000; static uint8_t hud; static volatile uint8_t clock_updated; /* game variables */ static uint8_t lives; static uint32_t score; static uint16_t extra_life; static uint8_t stage; static uint8_t time; static uint8_t pickaxe; static uint8_t stageclear; static uint8_t gameover; static uint8_t pause; static Entity *tmonster; /* 0-index */ #define START_STAGE 0 #define STAGES_LEN 13 static const uint8_t *stages[STAGES_LEN] = { binary_stage01_start, binary_stage02_start, binary_stage03_start, binary_stage04_start, binary_stage05_start, binary_stage06_start, binary_stage07_start, binary_stage08_start, binary_stage09_start, binary_stage10_start, binary_stage11_start, binary_stage12_start, binary_stage13_start, }; static void hud_render() { char b[32]; if (hud & HUD_ALL) { Rect src = { 128, 32, 144, 112 }; Rect dst = { 4, 0, 16, 16 }; /* lives */ blitrc(binary_sprites_start, &src, &dst); /* pickaxe */ src.x = 112; dst.x = 90; blitrc(binary_sprites_start, &src, &dst); put_text(132, 4, "TIME", 1); put_text(249, 4, "STAGE", 1); } if (hud & HUD_LIVES) { sprintf(b, "%d", lives); put_text(18, 4, b, 1); } if (hud & HUD_SCORE) { sprintf(b, "%06li", score); put_text(34, 4, b, 5); } if (hud & HUD_PICKAXE) { sprintf(b, "%d", pickaxe); put_text(106, 4, b, 1); } if (hud & HUD_TIME) { sprintf(b, "%02d", time); put_text(172, 4, b, time > 10 || stageclear ? 1 : 15); if (!gameover && !stageclear && time <= 10) sound_play_efx(EFX_TIME); } if (hud & HUD_STAGE) { sprintf(b, "%02d", stage + 1); put_text(297, 4, b, 1); } hud = HUD_CLEAN; } #define WALK_DELAY 8 #define WALK_CYCLE_FRAMES 4 static const Rect intro_frames[WALK_CYCLE_FRAMES] = { /* walk cycle */ { 0, 0, 144, 112 }, { 16, 0, 144, 112 }, { 0, 0, 144, 112 }, { 32, 0, 144, 112 }, }; static void run_intro() { /* for the HUD */ pickaxe = 0; time = GAME_TIME_MAX; hud = HUD_ALL; blit_target(TARGET_BUFFER); blit_erase(0); map_init(binary_intro_start); map_render(); hud_render(); put_text(88, 65, "GET ALL THAT GOLD!", 1); wait_vsync(); blit_copy_all(); Rect dst = { 80, 88 + MAP_OFFS_Y, 16, 16 }; uint8_t frame = 0, delay = 0; wait_frames(32); blit_target(TARGET_SCREEN); while (dst.x < 224) { wait_vsync(); blit_copy16(&dst); dst.x++; if (map_update_gold(dst.x + 8, dst.y + 15 - MAP_OFFS_Y)) sound_play_efx(EFX_GOLD); blitrc(binary_sprites_start, &intro_frames[frame], &dst); if (delay++ == WALK_DELAY) { delay = 0; frame++; if (frame == WALK_CYCLE_FRAMES) frame = 0; } } wait_vsync(); blit_copy16(&dst); wait_frames(64); } static void run_gameover() { wait_vsync(); blit_erase(0); /* restore the HUD after erasing the screen */ hud = HUD_ALL; hud_render(); put_text(124, 90, "GAME OVER", 1); /* wait some time */ wait_frames(255); } static void run_stageclear() { wait_vsync(); put_text(116, 100, "STAGE", 1); put_text(164, 100, "CLEAR", 1); /* wait some time */ wait_frames(96); while (time) { add_score(20); time--; hud |= HUD_TIME; wait_frames(4); wait_vsync(); hud_render(); sound_play_efx(EFX_GOLD); } /* wait some time */ wait_frames(64); wait_vsync(); blit_erase(0); /* restore the HUD after erasing the screen */ hud = HUD_ALL; wait_vsync(); hud_render(); wait_frames(32); } void run_game() { lives = GAME_LIVES_START; score = 0; extra_life = 0; stage = START_STAGE; run_intro(); next_stage: pause = 0; gameover = 0; stageclear = 0; pickaxe = 0; time = GAME_TIME_MAX; hud = HUD_ALL; tmonster = NULL; blit_target(TARGET_BUFFER); blit_erase(0); map_init(stages[stage]); map_render(); hud_render(); wait_vsync(); blit_copy_all(); entities_sort(); blit_target(TARGET_SCREEN); wait_vsync(); entities_draw(); put_text(136, 100, "READY?", 1); /* wait some time */ wait_frames(96); /* erase the READY? text */ Rect ready = { 136, 100, 184, 8 }; wait_vsync(); blit_copy(&ready); entities_draw(); timer_start(GAME_TIME_MAX, &clock_updated); while (!keys[KEY_ESC]) { if (clock_updated) { time = timer_value(); hud |= HUD_TIME; } if (hud) hud_render(); if (keys[KEY_P]) { /* pause / resume */ pause ^= 1; /* wait for the key to be released */ while (keys[KEY_P]) wait_vsync(); if (pause) timer_stop(); else timer_resume(); } if (pause) continue; if (map_is_complete()) { if (!stageclear) { timer_stop(); tmonster = NULL; entities_warp_out_all(); sound_play_efx(EFX_WARP); player_stageclear(); stageclear = STAGECLEAR_DELAY; } else { stageclear--; if (stageclear == 1) { run_stageclear(); stage++; if (stage == STAGES_LEN) { /* TODO: endgame */ break; } goto next_stage; } } } else { player_update(); /* time monster */ if (!time && !tmonster) { tmonster = entities_new(); if (tmonster) tmonster_init(tmonster); } if (time && tmonster) { tmonster->used = USED_FREE; tmonster = NULL; } } entities_update(); entities_sort(); wait_vsync(); /* prevent interrupts so updating the audio doesn't result in sprite * flickering on the less powered machines */ disable(); entities_draw(); enable(); if (gameover) { gameover--; if (gameover == 1) { run_gameover(); break; } } } /* wait for ESC to be release */ while (keys[KEY_ESC]) wait_vsync(); } void add_score(uint8_t v) { score += v; extra_life += v; if (extra_life >= GAME_EXTRA_LIFE) { extra_life -= GAME_EXTRA_LIFE; if (lives < GAME_LIVES_MAX) { sound_play_efx(EFX_ONEUP); lives++; hud |= HUD_LIVES; } } hud |= HUD_SCORE; } uint32_t get_hiscore() { return hiscore; } uint8_t dec_lives() { lives--; if (lives == 0) { gameover = GAMEOVER_DELAY; timer_stop(); } hud |= HUD_LIVES; return lives; } void reset_time() { time = GAME_TIME_MAX; hud |= HUD_TIME; timer_start(GAME_TIME_MAX, &clock_updated); } void add_pickaxe() { if (pickaxe < GAME_MAX_PICKAXE) { pickaxe++; hud |= HUD_PICKAXE; } } uint8_t use_pickaxe() { if (pickaxe) { pickaxe--; hud |= HUD_PICKAXE; return 1; } return 0; } uint8_t is_stageclear() { return stageclear; }