From b30fbee6051b339fefe51577223597704f689002 Mon Sep 17 00:00:00 2001 From: Andrew Kesterson Date: Wed, 13 May 2026 15:45:38 -0400 Subject: [PATCH] Implemented a menu screen but transitioning from the menu to the running game doesn't work yet --- assets/imagemap.tmj | 30 ++++- deps/libakgl | 2 +- src/akgltest.c | 261 ++++++++++++++++++++++++++++++++++++++------ 3 files changed, 256 insertions(+), 37 deletions(-) diff --git a/assets/imagemap.tmj b/assets/imagemap.tmj index ce881da..3e87e3a 100644 --- a/assets/imagemap.tmj +++ b/assets/imagemap.tmj @@ -5,9 +5,10 @@ { "id":4, "image":"../assets/ffbg2-scaled.png", + "locked":true, "name":"Image Layer 1", "offsetx":5.6843418860808e-14, - "offsety":2.01612903225805, + "offsety":0, "opacity":1, "type":"imagelayer", "visible":true, @@ -17,7 +18,6 @@ { "draworder":"topdown", "id":2, - "locked":true, "name":"Object Layer 1", "objects":[ { @@ -65,7 +65,31 @@ "width":16, "x":261.106052990594, "y":156.523012852732 + }, + { + "height":32, + "id":16, + "name":"p_foreground", + "rotation":0, + "type":"perspective", + "visible":true, + "width":790, + "x":0, + "y":410 + }, + { + "height":16, + "id":17, + "name":"p_vanishing", + "rotation":0, + "type":"perspective", + "visible":true, + "width":53, + "x":270.5, + "y":260.5 }], + "offsetx":-5, + "offsety":-37, "opacity":1, "type":"objectgroup", "visible":true, @@ -73,7 +97,7 @@ "y":0 }], "nextlayerid":5, - "nextobjectid":3, + "nextobjectid":18, "orientation":"orthogonal", "renderorder":"right-down", "tiledversion":"1.8.2", diff --git a/deps/libakgl b/deps/libakgl index b8dee45..53e4f5c 160000 --- a/deps/libakgl +++ b/deps/libakgl @@ -1 +1 @@ -Subproject commit b8dee456ca276494a483012284a239dad30d844e +Subproject commit 53e4f5c14fff730d874b2743ac296df650c1cd93 diff --git a/src/akgltest.c b/src/akgltest.c index 3a5b2cf..435d5bf 100644 --- a/src/akgltest.c +++ b/src/akgltest.c @@ -21,9 +21,30 @@ #define AKGLTEST_STATE_WAITFORINPUT 1 << 8 #define AKGLTEST_STATE_LOADING 1 << 9 #define AKGLTEST_STATE_RUNNING 1 << 10 +#define AKGLTEST_STATE_MAPMENU 1 << 11 +#define AKGLTEST_STATE_QUIT 1 << 12 -int numsprites = 8; +#define AKGLTEST_CONTROLMAP_MENU 0 +#define AKGLTEST_CONTROLMAP_INGAMEACTOR 1 + +int menuselection = 0; +int maxmenu = 2; + +int numchars = 2; +char *characterpaths[] = { + "assets/characters/littleguy.json", + "assets/characters/menupointer.json" +}; + +int numsounds = 2; +char *soundfiles[] = { + "assets/vgmenuhighlight.wav", + "assets/vgmenuselect.wav" +}; + +int numsprites = 9; char *spritepaths[] = { + "assets/sprites/menupointer.json", "assets/sprites/little_guy_walking_left.json", "assets/sprites/little_guy_walking_right.json", "assets/sprites/little_guy_walking_up.json", @@ -36,6 +57,70 @@ char *spritepaths[] = { char dirnamebuf[1024]; +akerr_ErrorContext AKERR_NOIGNORE *akgltest_set_gamemode_menu(void *appstate, SDL_Event *event) +{ + akgl_Actor *menupointer = SDL_GetPointerProperty(AKGL_REGISTRY_ACTOR, "menupointer", NULL); + PREPARE_ERROR(e); + FAIL_ZERO_RETURN(e, menupointer, AKERR_NULLPOINTER, "missing actor"); + AKGL_BITMASK_DEL(game.state.flags, AKGLTEST_STATE_WAITFORINPUT); + AKGL_BITMASK_ADD(game.state.flags, AKGLTEST_STATE_MAPMENU); + GAME_ControlMaps[AKGLTEST_CONTROLMAP_INGAMEACTOR].target = NULL; + GAME_ControlMaps[AKGLTEST_CONTROLMAP_MENU].target = menupointer; + menupointer->visible = true; + SUCCEED_RETURN(e); +} + +akerr_ErrorContext AKERR_NOIGNORE *akgltest_set_gamemode_running(void *appstate, SDL_Event *event) +{ + akgl_Actor *menupointer = SDL_GetPointerProperty(AKGL_REGISTRY_ACTOR, "menupointer", NULL); + akgl_Actor *actorptr = SDL_GetPointerProperty(AKGL_REGISTRY_ACTOR, "actor", NULL); + PREPARE_ERROR(e); + FAIL_ZERO_RETURN(e, menupointer, AKERR_NULLPOINTER, "missing actor"); + FAIL_ZERO_RETURN(e, actorptr, AKERR_NULLPOINTER, "missing actor"); + AKGL_BITMASK_DEL(game.state.flags, AKGLTEST_STATE_WAITFORINPUT); + AKGL_BITMASK_ADD(game.state.flags, AKGLTEST_STATE_MAPMENU); + GAME_ControlMaps[AKGLTEST_CONTROLMAP_INGAMEACTOR].target = actorptr; + GAME_ControlMaps[AKGLTEST_CONTROLMAP_MENU].target = NULL; + menupointer->visible = false; + actorptr->visible = true; + + AKGL_BITMASK_DEL(game.state.flags, AKGLTEST_STATE_MAPMENU); + AKGL_BITMASK_ADD(game.state.flags, AKGLTEST_STATE_RUNNING); + if ( menuselection == 0 ) { + strcpy((char *)&dirnamebuf, "assets/imagemap.tmj"); + PASS(e, akgl_tilemap_load((char *)&dirnamebuf, (akgl_Tilemap *)&gamemap)); + } else if ( menuselection == 1 ) { + strcpy((char *)&dirnamebuf, "assets/tilemap.tmj"); + PASS(e, akgl_tilemap_load((char *)&dirnamebuf, (akgl_Tilemap *)&gamemap)); + } else if ( menuselection == 2 ) { + AKGL_BITMASK_DEL(game.state.flags, AKGLTEST_STATE_RUNNING); + AKGL_BITMASK_ADD(game.state.flags, AKGLTEST_STATE_QUIT); + } + SUCCEED_RETURN(e); +} + +akerr_ErrorContext AKERR_NOIGNORE *akgltest_menupointer_logic_movement(akgl_Actor *obj, SDL_Time curtime) +{ + PREPARE_ERROR(e); + FAIL_ZERO_RETURN(e, obj, AKERR_NULLPOINTER, "NULL actor"); + if ( AKGL_BITMASK_HAS(obj->state, AKGL_ACTOR_STATE_MOVING_DOWN) ) { + if ( menuselection < maxmenu ) { + menuselection += 1; + } else { + menuselection = 0; + } + } else if ( AKGL_BITMASK_HAS(obj->state, AKGL_ACTOR_STATE_MOVING_UP) ) { + if ( menuselection == 0 ) { + menuselection = maxmenu; + } else { + menuselection -= 1; + } + } + obj->y = 85 + (menuselection * 100); + AKGL_BITMASK_DEL(obj->state, (AKGL_ACTOR_STATE_MOVING_UP | AKGL_ACTOR_STATE_MOVING_DOWN)); + SUCCEED_RETURN(e); +} + akerr_ErrorContext AKERR_NOIGNORE *music_toggle(akgl_Actor *obj, SDL_Event *event) { SDL_PropertiesID bgmprops = 0; @@ -64,13 +149,40 @@ akerr_ErrorContext AKERR_NOIGNORE *savegame(akgl_Actor *obj, SDL_Event *event) return akgl_game_save("assets/savegame.bin"); } -akerr_ErrorContext AKERR_NOIGNORE *user_breakpoint(akgl_Actor *obj, SDL_Event *event) +akerr_ErrorContext AKERR_NOIGNORE *quit_game(akgl_Actor *obj, SDL_Event *event) { PREPARE_ERROR(errctx); - SDL_Log("User breakpoint hit"); + AKGL_BITMASK_ADD(game.state.flags, AKGLTEST_STATE_QUIT); + SDL_Log("Quitting game"); SUCCEED_RETURN(errctx); } +akerr_ErrorContext AKERR_NOIGNORE *akgltest_load_assets() +{ + PREPARE_ERROR(errctx); + for ( int i = 0; i < numsprites ; i++) { + strcpy((char *)&dirnamebuf, spritepaths[i]); + SDL_Log("Loading sprite %s (%d of %d)....", (char *)&dirnamebuf, i, numsprites); + PASS(errctx, akgl_sprite_load_json((char *)&dirnamebuf)); + } + for ( int i = 0; i < numchars ; i++ ) { + strcpy((char *)&dirnamebuf, characterpaths[i]); + SDL_Log("Loading character %s (%d of %d)....", (char *)&dirnamebuf, i, numchars); + PASS(errctx, akgl_character_load_json((char *)&dirnamebuf)); + } + SUCCEED_RETURN(errctx); +} + +akerr_ErrorContext AKERR_NOIGNORE *akgltest_set_actor_visible(char *actorname, bool visible) +{ + PREPARE_ERROR(e); + akgl_Actor *actorptr = NULL; + actorptr = SDL_GetPointerProperty(AKGL_REGISTRY_ACTOR, actorname, NULL); + FAIL_ZERO_RETURN(e, actorptr, AKERR_NULLPOINTER, "%s missing in registry", actorname); + actorptr->visible = visible; + SUCCEED_RETURN(e); +} + SDL_AppResult SDL_AppInit(void **appstate, int argc, char *argv[]) { akgl_Actor *actorptr = NULL; @@ -92,12 +204,22 @@ SDL_AppResult SDL_AppInit(void **appstate, int argc, char *argv[]) CATCH(errctx, akgl_game_init_screen()); CATCH(errctx, akgl_controller_list_keyboards()); - for ( int i = 0; i < numsprites ; i++) { - strcpy((char *)&dirnamebuf, spritepaths[i]); - CATCH(errctx, akgl_sprite_load_json((char *)&dirnamebuf)); - } - strcpy((char *)&dirnamebuf, "assets/characters/littleguy.json"); - CATCH(errctx, akgl_character_load_json((char *)&dirnamebuf)); + CATCH(errctx, akgltest_load_assets()); + + CATCH(errctx, akgl_heap_next_actor(&actorptr)); + CATCH(errctx, akgl_actor_initialize((akgl_Actor *)actorptr, "menupointer")); + actorptr->basechar = SDL_GetPointerProperty( + AKGL_REGISTRY_CHARACTER, + "menupointer", + NULL); + FAIL_ZERO_BREAK(errctx, actorptr->basechar, AKERR_REGISTRY, "menupointer character missing"); + actorptr->movementlogicfunc = &akgltest_menupointer_logic_movement; + actorptr->movement_controls_face = false; + actorptr->state = (AKGL_ACTOR_STATE_ALIVE | AKGL_ACTOR_STATE_FACE_DOWN); + actorptr->x = 50; + actorptr->y = 100; + actorptr->visible = false; + CATCH(errctx, akgl_heap_next_actor(&actorptr)); CATCH(errctx, akgl_actor_initialize((akgl_Actor *)actorptr, "player")); actorptr->basechar = SDL_GetPointerProperty( @@ -109,14 +231,11 @@ SDL_AppResult SDL_AppInit(void **appstate, int argc, char *argv[]) actorptr->state = (AKGL_ACTOR_STATE_ALIVE | AKGL_ACTOR_STATE_FACE_LEFT); actorptr->x = 320; actorptr->y = 240; - actorptr->visible = true; + actorptr->visible = false; strcpy((char *)&dirnamebuf, "assets/memories.mp3"); CATCH(errctx, akgl_load_start_bgm((char *)&dirnamebuf)); CATCH(errctx, music_toggle(NULL, NULL)); - - strcpy((char *)&dirnamebuf, "assets/imagemap.tmj"); - CATCH(errctx, akgl_tilemap_load((char *)&dirnamebuf, (akgl_Tilemap *)&gamemap)); CATCH(errctx, akgl_text_loadfont("C64Pro", "assets/C64_Pro-STYLE.ttf", 18)); @@ -134,6 +253,29 @@ SDL_AppResult SDL_AppInit(void **appstate, int argc, char *argv[]) return SDL_APP_CONTINUE; } +akerr_ErrorContext AKERR_NOIGNORE *akgltest_controller_bind_universal(char *actorname, int mapid, int kbid, int jsid) +{ + akgl_Control control; + PREPARE_ERROR(e); + + PASS(e, akgl_controller_default(mapid, actorname, kbid, jsid)); + // set custom control maps */ + memset((void *)&control, 0x00, sizeof(akgl_Control)); + + // Quit + control.key = SDLK_ESCAPE; + control.event_on = SDL_EVENT_KEY_DOWN; + control.handler_on = &quit_game; + PASS(e, akgl_controller_pushmap(mapid, &control)); + + // Toggle the music + control.key = SDLK_M; + control.event_on = SDL_EVENT_KEY_DOWN; + control.handler_on = &music_toggle; + PASS(e, akgl_controller_pushmap(mapid, &control)); +} + + akerr_ErrorContext AKERR_NOIGNORE *akgltest_controller_get_defaults(void *appstate, SDL_Event *event) { akgl_Control control; @@ -148,27 +290,22 @@ akerr_ErrorContext AKERR_NOIGNORE *akgltest_controller_get_defaults(void *appsta kbid = event->key.which; } - PASS(e, akgl_controller_default(0, "player", kbid, jsid)); - // set custom control maps */ - memset((void *)&control, 0x00, sizeof(akgl_Control)); - - // Breakpoint - control.key = SDLK_ESCAPE; - control.event_on = SDL_EVENT_KEY_DOWN; - control.handler_on = &user_breakpoint; - PASS(e, akgl_controller_pushmap(0, &control)); - - // Toggle the music - control.key = SDLK_M; - control.event_on = SDL_EVENT_KEY_DOWN; - control.handler_on = &music_toggle; - PASS(e, akgl_controller_pushmap(0, &control)); + PASS(e, akgltest_controller_bind_universal("player", AKGLTEST_CONTROLMAP_MENU, kbid, jsid)); + PASS(e, akgltest_controller_bind_universal("menupointer", AKGLTEST_CONTROLMAP_INGAMEACTOR, kbid, jsid)); // Save the game control.key = SDLK_S; control.event_on = SDL_EVENT_KEY_DOWN; control.handler_on = &savegame; - PASS(e, akgl_controller_pushmap(0, &control)); + PASS(e, akgl_controller_pushmap(AKGLTEST_CONTROLMAP_INGAMEACTOR, &control)); + + // Menu selection + control.key = SDLK_RETURN; + control.event_on = SDL_EVENT_KEY_DOWN; + control.handler_on = &akgltest_set_gamemode_running; + PASS(e, akgl_controller_pushmap(AKGLTEST_CONTROLMAP_MENU, &control)); + + SUCCEED_RETURN(e); } SDL_AppResult SDL_AppEvent(void *appstate, SDL_Event *event) @@ -186,8 +323,7 @@ SDL_AppResult SDL_AppEvent(void *appstate, SDL_Event *event) if ( AKGL_BITMASK_HAS(game.state.flags, AKGLTEST_STATE_WAITFORINPUT) ) { // get the player's default keyboard or gamepad id from the event CATCH(errctx, akgltest_controller_get_defaults(appstate, event)); - AKGL_BITMASK_DEL(game.state.flags, AKGLTEST_STATE_WAITFORINPUT); - AKGL_BITMASK_ADD(game.state.flags, AKGLTEST_STATE_RUNNING); + CATCH(errctx, akgltest_set_gamemode_menu(appstate, event)); } else { CATCH(errctx, akgl_controller_handle_event(appstate, event)); } @@ -201,6 +337,60 @@ SDL_AppResult SDL_AppEvent(void *appstate, SDL_Event *event) return SDL_APP_CONTINUE; /* carry on with the program! */ } +akerr_ErrorContext AKERR_NOIGNORE *akgltest_iterate_mapmenu(void) +{ + akgl_Actor *menupointer = NULL; + PREPARE_ERROR(e); + ATTEMPT { + SDL_SetRenderDrawColor(renderer, 0, 0, 0, 255); + SDL_RenderClear(renderer); + PASS(e, akgl_text_rendertextat( + SDL_GetPointerProperty( + AKGL_REGISTRY_FONT, + "C64Pro", + NULL), + "TILE MAP DEMO", + (SDL_Color){255, 255, 255, 255}, + 0, + 150, + 100) + ); + PASS(e, akgl_text_rendertextat( + SDL_GetPointerProperty( + AKGL_REGISTRY_FONT, + "C64Pro", + NULL), + "IMAGE MAP DEMO", + (SDL_Color){255, 255, 255, 255}, + 0, + 150, + 200) + ); + PASS(e, akgl_text_rendertextat( + SDL_GetPointerProperty( + AKGL_REGISTRY_FONT, + "C64Pro", + NULL), + "LOAD SAVED GAME", + (SDL_Color){255, 255, 255, 255}, + 0, + 150, + 300) + ); + menupointer = SDL_GetPointerProperty(AKGL_REGISTRY_ACTOR, "menupointer", NULL); + FAIL_ZERO_BREAK(e, menupointer, AKERR_NULLPOINTER, "actor missing from registry"); + menupointer->visible = true; + CATCH(e, menupointer->updatefunc(menupointer)); + CATCH(e, menupointer->movementlogicfunc(menupointer, 0)); + CATCH(e, menupointer->renderfunc(menupointer, renderer)); + } CLEANUP { + SDL_RenderPresent(renderer); + } PROCESS(e) { + } FINISH(e, true); + + SUCCEED_RETURN(e); +} + akerr_ErrorContext AKERR_NOIGNORE *akgltest_iterate_waitforinput(void) { @@ -242,6 +432,7 @@ akerr_ErrorContext AKERR_NOIGNORE *akgltest_iterate_running(void) AKGL_BITMASK_ADD(opflags.flags, AKGL_ITERATOR_OP_UPDATE); AKGL_BITMASK_ADD(opflags.flags, AKGL_ITERATOR_OP_RENDER); AKGL_BITMASK_ADD(opflags.flags, AKGL_ITERATOR_OP_LAYERMASK); + AKGL_BITMASK_ADD(opflags.flags, AKGL_ITERATOR_OP_TILEMAPSCALE); for ( i = 0; i < AKGL_TILEMAP_MAX_LAYERS; i++ ) { opflags.layerid = i; @@ -278,11 +469,15 @@ SDL_AppResult SDL_AppIterate(void *appstate) PREPARE_ERROR(e); ATTEMPT { - if ( AKGL_BITMASK_HAS(game.state.flags, AKGLTEST_STATE_RUNNING) ) { + if ( AKGL_BITMASK_HAS(game.state.flags, AKGLTEST_STATE_QUIT) ) { + return SDL_APP_FAILURE; + } else if ( AKGL_BITMASK_HAS(game.state.flags, AKGLTEST_STATE_RUNNING) ) { CATCH(e, akgltest_iterate_running()); } else if ( AKGL_BITMASK_HAS(game.state.flags, AKGLTEST_STATE_WAITFORINPUT) ) { CATCH(e, akgltest_iterate_waitforinput()); - } + } else if ( AKGL_BITMASK_HAS(game.state.flags, AKGLTEST_STATE_MAPMENU) ) { + CATCH(e, akgltest_iterate_mapmenu()); + } } CLEANUP { } PROCESS(e) { } HANDLE_DEFAULT(e) {