/* SPDX-License-Identifier: GPL-3.0-or-later */ /* Copyright (C) 2021 KikooDX */ #include "conf.h" #include "game_state.h" #include "input.h" #include "level.h" #include "levelselection.h" #include "mainmenu.h" #include "particles.h" #include "player.h" #include "titlescreen.h" #include "transition.h" #include "zxcolors.h" #include #include #include #include #include #define PANIC(msg) \ do { \ dclear(ZX_BLACK); \ dprint_opt(DWIDTH / 2, DHEIGHT / 2, ZX_RED, C_NONE, \ DTEXT_CENTER, DTEXT_MIDDLE, "ERROR: %s", msg); \ dupdate(); \ getkey(); \ return 0; \ } while (0); #define LOAD_LEVEL() \ do { \ gint_switch(level_load); \ if (fatal_error == -1) \ PANIC(fatal_error_msg); \ particles_init(); \ player = player_init(); \ } while (0); extern struct Level level; extern int level_id; extern int fatal_error; extern char *fatal_error_msg; extern const font_t font_main; static int callback(volatile int *arg); int main(void) { int i; int timer; int player_return_code; int frameskip = 0; int toskip = 0; /* int level_pack_beaten; */ enum TransitionMode transition_previous_mode; enum GameState game_state = TitleScreen; volatile int has_ticked = 1; struct Player player; struct Input input = input_init(); struct TitleScreen titlescreen = titlescreen_init(); struct LevelSelection levelselection = levelselection_init(); struct MainMenu mainmenu = mainmenu_init(); struct Transition transition = transition_init(0.0, ZX_BLACK, TransitionNone); particles_init(); /* set font */ dfont(&font_main); /* load level */ level_id = 0; gint_switch(level_load); if (fatal_error == -1) PANIC(fatal_error_msg); /* timer setup */ timer = timer_setup(TIMER_ANY, 1000000 / TARGET_FPS, callback, &has_ticked); if (timer == -1) PANIC("timer_setup failed"); timer_start(timer); /* initialize the player (has to be done after level loading */ player = player_init(); /* main game loop */ while (!keydown(KEY_EXIT)) { /* skip render frames */ i = 1 + frameskip; toskip = frameskip; while (i-- > 0) { /* frameskip adjustement */ if (has_ticked - 1 > frameskip) frameskip = has_ticked - 1; /* speed limiter */ while (!has_ticked) sleep(); while (has_ticked) has_ticked = 0; /* update */ input_update(&input); switch (game_state) { case TitleScreen: if (titlescreen_update(&titlescreen, input)) game_state = MainMenu; break; case MainMenu: if (mainmenu_update(&mainmenu, input)) game_state = LevelSelection; break; case LevelSelection: if (levelselection_update(&levelselection, input)) { game_state = Playing; /* set level according to * selected pack */ level_id = levelselection.pack_cursor * LVL_PER_PACK; LOAD_LEVEL(); } break; case Playing: if (transition.mode == TransitionNone) { particles_update(); player_return_code = player_update(&player, input); switch (player_return_code) { case -1: game_state = GamePause; break; case 1: level_id += 1; transition = transition_init( H_TRANS_SPD, ZX_BLUE, TransitionHOut); break; case 2: transition = transition_init( V_TRANS_SPD, ZX_RED, TransitionVOut); break; default: break; } } transition_previous_mode = transition_update(&transition); if (transition_previous_mode != transition.mode && transition_previous_mode != TransitionNone) { switch (transition_previous_mode) { case TransitionNone: break; case TransitionHIn: break; case TransitionHOut: /* end level pack */ if (level_id % 4 == 0) { game_state = PackDone; /* level_pack_beaten = level_id / 4; */ } LOAD_LEVEL(); transition = transition_init( transition.speed, transition.color, TransitionHIn); break; case TransitionVIn: break; case TransitionVOut: LOAD_LEVEL(); transition = transition_init( transition.speed, transition.color, TransitionVIn); break; default: break; } } break; case GamePause: game_state = Playing; break; case PackDone: game_state = LevelSelection; break; default: PANIC("missing game_state case (update)"); break; } } /* draw */ dclear(ZX_BLACK); switch (game_state) { case TitleScreen: titlescreen_draw(titlescreen); break; case MainMenu: mainmenu_draw(mainmenu); break; case LevelSelection: levelselection_draw(levelselection); break; case Playing: level_draw(); particles_draw(); trail_draw(); player_draw(player); transition_draw(transition); break; case GamePause: break; case PackDone: break; default: PANIC("missing game_state case (draw)"); break; } dupdate(); } timer_stop(timer); return 1; } static int callback(volatile int *arg) { *arg += 1; return 0; }