diff --git a/apps/testgame/CMakeLists.txt b/apps/platformer/CMakeLists.txt similarity index 94% rename from apps/testgame/CMakeLists.txt rename to apps/platformer/CMakeLists.txt index 39a162d..1c3d3ec 100644 --- a/apps/testgame/CMakeLists.txt +++ b/apps/platformer/CMakeLists.txt @@ -1,6 +1,6 @@ cmake_minimum_required(VERSION 3.21) -project(testgame LANGUAGES C) +project(platfromer LANGUAGES C) if(NOT CMAKE_BUILD_TYPE) set(CMAKE_BUILD_TYPE Debug) diff --git a/apps/testgame/build.sh b/apps/platformer/build.sh similarity index 100% rename from apps/testgame/build.sh rename to apps/platformer/build.sh diff --git a/apps/testgame/game.c b/apps/platformer/game.c similarity index 100% rename from apps/testgame/game.c rename to apps/platformer/game.c diff --git a/apps/testgame/player.c b/apps/platformer/player.c similarity index 100% rename from apps/testgame/player.c rename to apps/platformer/player.c diff --git a/apps/testgame/player.h b/apps/platformer/player.h similarity index 100% rename from apps/testgame/player.h rename to apps/platformer/player.h diff --git a/apps/platformer/scenes/ingame.c b/apps/platformer/scenes/ingame.c new file mode 100644 index 0000000..4fe2279 --- /dev/null +++ b/apps/platformer/scenes/ingame.c @@ -0,0 +1,57 @@ +#include "ingame.h" +#include "title.h" +#include "scene.h" + +#include "twn_game_api.h" + + +static void ingame_tick(State *state) { + SceneIngame *scn = (SceneIngame *)state->scene; + + world_drawdef(scn->world); + player_calc(scn->player); + + const Vec3 right = m_vec_norm(m_vec_cross(scn->cam.target, scn->cam.up)); + const float speed = 0.04f; /* TODO: put this in a better place */ + if (input_is_action_pressed(&ctx.input, "player_left")) + scn->cam.pos = vec3_sub(scn->cam.pos, m_vec_scale(right, speed)); + + if (input_is_action_pressed(&ctx.input, "player_right")) + scn->cam.pos = vec3_add(scn->cam.pos, m_vec_scale(right, speed)); + + if (input_is_action_pressed(&ctx.input, "player_forward")) + scn->cam.pos = vec3_add(scn->cam.pos, m_vec_scale(scn->cam.target, speed)); + + if (input_is_action_pressed(&ctx.input, "player_backward")) + scn->cam.pos = vec3_sub(scn->cam.pos, m_vec_scale(scn->cam.target, speed)); + + if (input_is_action_pressed(&ctx.input, "player_jump")) + scn->cam.pos.y += speed; + + if (input_is_action_pressed(&ctx.input, "player_run")) + scn->cam.pos.y -= speed; +} + + +static void ingame_end(State *state) { + SceneIngame *scn = (SceneIngame *)state->scene; + player_destroy(scn->player); + world_destroy(scn->world); + free(state->scene); +} + + +Scene *ingame_scene(State *state) { + (void)state; + + SceneIngame *new_scene = ccalloc(1, sizeof *new_scene); + new_scene->base.tick = ingame_tick; + new_scene->base.end = ingame_end; + + new_scene->world = world_create(); + new_scene->player = player_create(new_scene->world); + + new_scene->cam = (Camera){ .pos = { 32, 0, 1 }, .up = { 0, 1, 0 }, .fov = (float)M_PI_2 }; + + return (Scene *)new_scene; +} diff --git a/apps/testgame/scenes/ingame.h b/apps/platformer/scenes/ingame.h similarity index 100% rename from apps/testgame/scenes/ingame.h rename to apps/platformer/scenes/ingame.h diff --git a/apps/testgame/scenes/scene.c b/apps/platformer/scenes/scene.c similarity index 100% rename from apps/testgame/scenes/scene.c rename to apps/platformer/scenes/scene.c diff --git a/apps/testgame/scenes/scene.h b/apps/platformer/scenes/scene.h similarity index 100% rename from apps/testgame/scenes/scene.h rename to apps/platformer/scenes/scene.h diff --git a/apps/testgame/scenes/title.c b/apps/platformer/scenes/title.c similarity index 100% rename from apps/testgame/scenes/title.c rename to apps/platformer/scenes/title.c diff --git a/apps/testgame/scenes/title.h b/apps/platformer/scenes/title.h similarity index 100% rename from apps/testgame/scenes/title.h rename to apps/platformer/scenes/title.h diff --git a/apps/testgame/state.h b/apps/platformer/state.h similarity index 100% rename from apps/testgame/state.h rename to apps/platformer/state.h diff --git a/apps/testgame/world.c b/apps/platformer/world.c similarity index 100% rename from apps/testgame/world.c rename to apps/platformer/world.c diff --git a/apps/testgame/world.h b/apps/platformer/world.h similarity index 100% rename from apps/testgame/world.h rename to apps/platformer/world.h diff --git a/apps/scenery/CMakeLists.txt b/apps/scenery/CMakeLists.txt new file mode 100644 index 0000000..2151142 --- /dev/null +++ b/apps/scenery/CMakeLists.txt @@ -0,0 +1,23 @@ +cmake_minimum_required(VERSION 3.21) + +project(scenery LANGUAGES C) + +if(NOT CMAKE_BUILD_TYPE) + set(CMAKE_BUILD_TYPE Debug) +endif() + +# add root townengine cmake file +if(${CMAKE_SOURCE_DIR} STREQUAL ${CMAKE_CURRENT_SOURCE_DIR}) + add_subdirectory(../../ ../../../.build) +endif() + +set(SOURCE_FILES + game.c + state.h + + scenes/scene.c scenes/scene.h + scenes/title.c scenes/title.h + scenes/ingame.c scenes/ingame.h +) + +use_townengine(${PROJECT_NAME} "${SOURCE_FILES}" ${CMAKE_CURRENT_SOURCE_DIR} ../../data) diff --git a/apps/scenery/build.sh b/apps/scenery/build.sh new file mode 100755 index 0000000..aefa755 --- /dev/null +++ b/apps/scenery/build.sh @@ -0,0 +1,7 @@ +#!/bin/env sh + +if [ $1 = "web" ]; then + emcmake cmake -B .build-web "${@:2}" && cmake --build .build-web +else + cmake -B .build "$@" && cmake --build .build +fi diff --git a/apps/scenery/game.c b/apps/scenery/game.c new file mode 100644 index 0000000..b56507e --- /dev/null +++ b/apps/scenery/game.c @@ -0,0 +1,73 @@ +#include "state.h" +#include "scenes/scene.h" +#include "scenes/title.h" + +#include "twn_game_api.h" + +#include +#include +#include +#include + + +void game_tick(void) { + if (ctx.initialization_needed) { + if (!ctx.udata) { + ctx.udata = ccalloc(1, sizeof (State)); + + State *state = ctx.udata; + state->ctx = &ctx; + state->scene = title_scene(state); + } + + input_add_action(&ctx.input, "debug_toggle"); + input_bind_action_scancode(&ctx.input, "debug_toggle", SDL_SCANCODE_BACKSPACE); + + input_add_action(&ctx.input, "player_left"); + input_bind_action_scancode(&ctx.input, "player_left", SDL_SCANCODE_A); + + input_add_action(&ctx.input, "player_right"); + input_bind_action_scancode(&ctx.input, "player_right", SDL_SCANCODE_D); + + input_add_action(&ctx.input, "player_forward"); + input_bind_action_scancode(&ctx.input, "player_forward", SDL_SCANCODE_W); + + input_add_action(&ctx.input, "player_backward"); + input_bind_action_scancode(&ctx.input, "player_backward", SDL_SCANCODE_S); + + input_add_action(&ctx.input, "player_jump"); + input_bind_action_scancode(&ctx.input, "player_jump", SDL_SCANCODE_SPACE); + + input_add_action(&ctx.input, "player_run"); + input_bind_action_scancode(&ctx.input, "player_run", SDL_SCANCODE_LSHIFT); + + input_add_action(&ctx.input, "ui_accept"); + input_bind_action_scancode(&ctx.input, "ui_accept", SDL_SCANCODE_RETURN); + + input_add_action(&ctx.input, "mouse_capture_toggle"); + input_bind_action_scancode(&ctx.input, "mouse_capture_toggle", SDL_SCANCODE_ESCAPE); + } + + State *state = ctx.udata; + + if (input_is_action_just_pressed(&ctx.input, "debug_toggle")) { + ctx.debug = !ctx.debug; + } + + state->scene->tick(state); + + /* there's a scene switch pending, we can do it now that the tick is done */ + if (state->next_scene != NULL) { + state->scene->end(state); + state->scene = state->next_scene; + state->is_scene_switching = false; + state->next_scene = NULL; + } +} + + +void game_end(void) { + State *state = ctx.udata; + state->scene->end(state); + free(state); +} diff --git a/apps/testgame/scenes/ingame.c b/apps/scenery/scenes/ingame.c similarity index 74% rename from apps/testgame/scenes/ingame.c rename to apps/scenery/scenes/ingame.c index 3869cf3..5d48ee2 100644 --- a/apps/testgame/scenes/ingame.c +++ b/apps/scenery/scenes/ingame.c @@ -11,9 +11,6 @@ static void ingame_tick(State *state) { SceneIngame *scn = (SceneIngame *)state->scene; - world_drawdef(scn->world); - player_calc(scn->player); - if (input_is_mouse_captured(&ctx.input)) { const float sensitivity = 0.6f; /* TODO: put this in a better place */ scn->yaw += (float)ctx.input.mouse_relative_position.x * sensitivity; @@ -55,30 +52,6 @@ static void ingame_tick(State *state) { input_set_mouse_captured(&ctx.input, !input_is_mouse_captured(&ctx.input)); } - m_sprite(m_set(path, "/assets/9slice.png"), - m_set(rect, ((Rect){ 16, 16, 128, 128 })), - m_opt(texture_region, ((Rect){ 0, 0, (float)(ctx.tick_count % 48), (float)(ctx.tick_count % 48) }))); - - m_sprite(m_set(path, "/assets/light.png"), - m_set(rect, ((Rect){ 48, 64, 64, 64 })), - m_opt(color, ((Color){ 255, 0, 0, 255 }))); - - m_sprite(m_set(path, "/assets/light.png"), - m_set(rect, ((Rect){ 64, 64, 64, 64 })), - m_opt(color, ((Color){ 0, 255, 0, 255 }))); - - m_sprite(m_set(path, "/assets/light.png"), - m_set(rect, ((Rect){ 80, 64, 64, 64 })), - m_opt(color, ((Color){ 0, 0, 255, 255 }))); - - m_sprite(m_set(path, "/assets/player/baron-walk.png"), - m_set(rect, ((Rect){ 32, 32, 64, 64 })), - m_opt(rotation, (float)M_PI * 2 * (float)(ctx.tick_count % 64) / 64 )); - - m_sprite(m_set(path, "/assets/player/baron-walk.png"), - m_set(rect, ((Rect){ 128, 32, 128, 64 })), - m_opt(rotation, (float)M_PI * 2 * (float)(ctx.tick_count % 64) / 64 )); - set_camera(&scn->cam); #define TERRAIN_FREQUENCY 0.1f @@ -111,9 +84,6 @@ static void ingame_tick(State *state) { static void ingame_end(State *state) { - SceneIngame *scn = (SceneIngame *)state->scene; - player_destroy(scn->player); - world_destroy(scn->world); free(state->scene); } @@ -125,9 +95,6 @@ Scene *ingame_scene(State *state) { new_scene->base.tick = ingame_tick; new_scene->base.end = ingame_end; - new_scene->world = world_create(); - new_scene->player = player_create(new_scene->world); - new_scene->cam = (Camera){ .pos = { 32, 0, 1 }, .up = { 0, 1, 0 }, .fov = (float)M_PI_2 }; audio_play_ex("music/mod65.xm", "soundtrack", (PlayAudioArgs){ diff --git a/apps/scenery/scenes/ingame.h b/apps/scenery/scenes/ingame.h new file mode 100644 index 0000000..f3f9012 --- /dev/null +++ b/apps/scenery/scenes/ingame.h @@ -0,0 +1,25 @@ +#ifndef INGAME_H +#define INGAME_H + +#include "twn_game_api.h" + +#include "../state.h" +#include "scene.h" + + +typedef struct SceneIngame { + Scene base; + + Camera cam; + + /* TODO: put this in a better place */ + float yaw; + float pitch; + float roll; +} SceneIngame; + + +Scene *ingame_scene(State *state); + + +#endif diff --git a/apps/scenery/scenes/scene.c b/apps/scenery/scenes/scene.c new file mode 100644 index 0000000..e39778f --- /dev/null +++ b/apps/scenery/scenes/scene.c @@ -0,0 +1,8 @@ +#include "scene.h" +#include "../state.h" + + +void switch_to(State *state, Scene *(*scene_func)(State *)) { + state->next_scene = scene_func(state); + state->is_scene_switching = true; +} diff --git a/apps/scenery/scenes/scene.h b/apps/scenery/scenes/scene.h new file mode 100644 index 0000000..4d1eab1 --- /dev/null +++ b/apps/scenery/scenes/scene.h @@ -0,0 +1,16 @@ +#ifndef SCENE_H +#define SCENE_H + + +typedef struct State State; +typedef struct Scene { + char *id; + void (*tick)(State *); + void (*end)(State *); +} Scene; + + +void switch_to(State *state, Scene *(*scene_func)(State *)); + + +#endif diff --git a/apps/scenery/scenes/title.c b/apps/scenery/scenes/title.c new file mode 100644 index 0000000..f2f5435 --- /dev/null +++ b/apps/scenery/scenes/title.c @@ -0,0 +1,64 @@ +#include "title.h" +#include "ingame.h" + +#include "twn_game_api.h" + +#include + + +static void title_tick(State *state) { + SceneTitle *scn = (SceneTitle *)state->scene; + (void)scn; + + if (input_is_action_just_pressed(&state->ctx->input, "ui_accept")) { + switch_to(state, ingame_scene); + } + + + m_sprite("/assets/title.png", ((Rect) { + ((float)RENDER_BASE_WIDTH / 2) - ((float)320 / 2), 64, 320, 128 })); + + + /* draw the tick count as an example of dynamic text */ + size_t text_str_len = snprintf(NULL, 0, "%lu", state->ctx->tick_count) + 1; + char *text_str = cmalloc(text_str_len); + snprintf(text_str, text_str_len, "%lu", state->ctx->tick_count); + + const char *font = "fonts/kenney-pixel.ttf"; + int text_h = 32; + int text_w = text_get_width(text_str, text_h, font); + + push_rectangle( + (Rect) { + .x = 0, + .y = 0, + .w = (float)text_w, + .h = (float)text_h, + }, + (Color) { 0, 0, 0, 255 } + ); + push_text( + text_str, + (Vec2){ 0, 0 }, + text_h, + (Color) { 255, 255, 255, 255 }, + font + ); + free(text_str); +} + + +static void title_end(State *state) { + free(state->scene); +} + + +Scene *title_scene(State *state) { + (void)state; + + SceneTitle *new_scene = ccalloc(1, sizeof *new_scene); + new_scene->base.tick = title_tick; + new_scene->base.end = title_end; + + return (Scene *)new_scene; +} diff --git a/apps/scenery/scenes/title.h b/apps/scenery/scenes/title.h new file mode 100644 index 0000000..05a61a7 --- /dev/null +++ b/apps/scenery/scenes/title.h @@ -0,0 +1,16 @@ +#ifndef TITLE_H +#define TITLE_H + +#include "../state.h" +#include "scene.h" + + +typedef struct SceneTitle { + Scene base; +} SceneTitle; + + +Scene *title_scene(State *state); + + +#endif diff --git a/apps/scenery/state.h b/apps/scenery/state.h new file mode 100644 index 0000000..082a856 --- /dev/null +++ b/apps/scenery/state.h @@ -0,0 +1,20 @@ +#ifndef STATE_H +#define STATE_H + + +#include "twn_game_api.h" + +#include + + +typedef struct Scene Scene; + +typedef struct State { + Context *ctx; + Scene *scene; + Scene *next_scene; + bool is_scene_switching; +} State; + + +#endif diff --git a/apps/template/game.c b/apps/template/game.c index 84869f3..a11da42 100644 --- a/apps/template/game.c +++ b/apps/template/game.c @@ -1,4 +1,4 @@ -#include "townengine/game_api.h" +#include "twn_game_api.h" #include "state.h" #include diff --git a/apps/template/state.h b/apps/template/state.h index dd2ce30..a458d39 100644 --- a/apps/template/state.h +++ b/apps/template/state.h @@ -1,7 +1,7 @@ #ifndef STATE_H #define STATE_H -#include "townengine/game_api.h" +#include "twn_game_api.h" /* populate it with state information */ struct state {