diff --git a/build.bat b/build.bat index a1ed45d..bff5653 100644 --- a/build.bat +++ b/build.bat @@ -21,7 +21,7 @@ call powershell -Command "$duration = Measure-Command {%robocopy_cmd% | Out-Defa REM ================================================================================================ REM TODO: Raylib seems to have problems shutting down fsanitize=address? -set common_compile_flags=/W4 /Z7 /MT /EHsc /nologo /I %tely_dir% +set common_compile_flags=/W4 /Z7 /MT /EHsc /nologo set common_link_flags=/link /incremental:no REM raylib ========================================================================================= @@ -65,7 +65,7 @@ gdi32.lib opengl32.lib winmm.lib user32.lib shell32.lib REM DLL flags ====================================================================================== -set dll_compile_flags=%common_compile_flags% /LD %code_dir%\feely_pona_unity.cpp +set dll_compile_flags=%common_compile_flags% /LD /Tp %code_dir%\feely_pona_unity.h set dll_link_flags=%common_link_flags% REM MSVC commands ================================================================================== diff --git a/feely_pona_game.cpp b/feely_pona_game.cpp new file mode 100644 index 0000000..37e2900 --- /dev/null +++ b/feely_pona_game.cpp @@ -0,0 +1,423 @@ +#if defined(__clang__) +#pragma once +#include "playground_unity.h" +#endif + +static bool operator==(TELY_GameEntityHandle const &lhs, TELY_GameEntityHandle const &rhs) +{ + bool result = lhs.id == rhs.id; + return result; +} + +static bool operator!=(TELY_GameEntityHandle const &lhs, TELY_GameEntityHandle const &rhs) +{ + bool result = !(lhs == rhs); + return result; +} + +static Dqn_M2x3 TELY_Game_CameraModelViewM2x3(TELY_GameCamera camera, TELY_Platform *platform) +{ + Dqn_M2x3 result = Dqn_M2x3_Identity(); + if (platform) { + Dqn_V2 rotate_origin = camera.world_pos - (Dqn_V2_InitV2I(platform->core.window_size) * .5f); + result = Dqn_M2x3_Mul(result, Dqn_M2x3_Translate(rotate_origin)); + result = Dqn_M2x3_Mul(result, Dqn_M2x3_Rotate(camera.rotate_rads)); + result = Dqn_M2x3_Mul(result, Dqn_M2x3_Scale(camera.scale)); + result = Dqn_M2x3_Mul(result, Dqn_M2x3_Translate((rotate_origin * -1) + camera.world_pos)); + } + return result; +} + +static TELY_GameEntity *TELY_Game_GetEntity(TELY_Game *game, TELY_GameEntityHandle handle) +{ + TELY_GameEntity *result = nullptr; + if (!game) + return result; + + result = game->entities.data; + uint64_t index_from_handle = handle.id & TELY_GAME_ENTITY_HANDLE_INDEX_MASK; + if (index_from_handle >= game->entities.size) + return result; + + TELY_GameEntity *candidate = game->entities.data + index_from_handle; + if (candidate->handle == handle) + result = candidate; + return result; +} + +static bool TELY_Game_DFSPreOrderWalkEntityTree(TELY_Game *game, TELY_GameEntityIterator *it, TELY_GameEntity *root) +{ + if (!game || !it || !root) + return false; + + it->last_visited = it->entity; + if (it->init) { + it->iteration_count++; + } else { + it->init = true; + it->entity = root; + it->entity_parent = it->entity->parent; + it->entity_next = it->entity->next; + it->entity_first_child = it->entity->first_child; + } + + if (it->entity_first_child) { + it->entity = it->entity_first_child; + it->entity_parent = it->entity->parent; + it->entity_next = it->entity->next; + it->entity_first_child = it->entity->first_child; + } else { + while (it->entity->handle != root->handle) { + if (it->entity_next) { + it->entity = it->entity_next; + it->entity_parent = it->entity->parent; + it->entity_next = it->entity->next; + it->entity_first_child = it->entity->first_child; + break; + } else { + if (!it->entity_parent) + break; + it->entity = it->entity_parent; + it->entity_parent = it->entity->parent; + it->entity_next = it->entity->next; + it->entity_first_child = it->entity->first_child; + } + } + } + + return it->entity->handle != root->handle; +} + +static bool TELY_Game_DFSPostOrderWalkEntityTree(TELY_Game *game, TELY_GameEntityIterator *it, TELY_GameEntity *root) +{ + if (!game || !it || !root) + return false; + + bool ascending_tree = it->entity ? (it->last_visited == it->entity->last_child) : false; + it->last_visited = it->entity; + + if (it->init) { + it->iteration_count++; + } else { + it->init = true; + it->entity = root; + it->entity_parent = it->entity->parent; + it->entity_next = it->entity->next; + it->entity_first_child = it->entity->first_child; + } + + // NOTE: Descend to deepest leaf node + if (it->entity_first_child && !ascending_tree) { + while (it->entity_first_child) { + it->entity = it->entity_first_child; + it->entity_parent = it->entity->parent; + it->entity_next = it->entity->next; + it->entity_first_child = it->entity->first_child; + } + } else { + // NOTE: We are at the leaf node, try going across + if (it->entity != root && it->entity_next) { + it->entity = it->entity_next; + it->entity_parent = it->entity->parent; + it->entity_next = it->entity->next; + it->entity_first_child = it->entity->first_child; + ascending_tree = false; + } + + // NOTE: Try descend again + if (it->entity_first_child && !ascending_tree) { + while (it->entity_first_child) { + it->entity = it->entity_first_child; + it->entity_parent = it->entity->parent; + it->entity_next = it->entity->next; + it->entity_first_child = it->entity->first_child; + } + } + + // NOTE: If we could not move further across or down then we've + // exhausted the tree, start moving up. + if (it->last_visited == it->entity) { + it->entity = it->entity_parent; + it->entity_parent = it->entity->parent; + it->entity_next = it->entity->next; + it->entity_first_child = it->entity->first_child; + } + } + + return it->entity->handle != root->handle; +} + + +// NOTE: Parent entity +static void TELY_Game_PushParentEntity(TELY_Game *game, TELY_GameEntityHandle handle) +{ + DQN_ASSERTF(game->parent_entity_stack.size >= 1, "Sentinel/nil entity has not been assigned as the 0th slot yet"); + if (game) + Dqn_FArray_Add(&game->parent_entity_stack, handle); +} + +static void TELY_Game_PopParentEntity(TELY_Game *game) +{ + // NOTE: 0th slot is reserved for the nil entity + if (game && game->parent_entity_stack.size > 1) + Dqn_FArray_PopBack(&game->parent_entity_stack, 1); +} + +static TELY_GameEntityHandle TELY_Game_ActiveParentEntity(TELY_Game const *game) +{ + TELY_GameEntityHandle result = {}; + if (!game || !game->parent_entity_stack.size) + return result; + result = game->parent_entity_stack.data[game->parent_entity_stack.size - 1]; + return result; +} + +static TELY_GameEntity *TELY_Game_ActiveParentEntityPointer(TELY_Game const *game) +{ + TELY_GameEntityHandle handle = TELY_Game_ActiveParentEntity(game); + TELY_GameEntity *result = TELY_Game_GetEntity(DQN_CAST(TELY_Game *)game, handle); + return result; +} + +static TELY_GameEntity *TELY_Game_MakeEntityPointerFV(TELY_Game *game, DQN_FMT_STRING_ANNOTATE char const *fmt, va_list args) +{ + TELY_GameEntity *result = nullptr; + if (!game) + return result; + + DQN_ASSERTF(game->entities.size > 0, "Sentinel/nil entity has not been initialised yet"); + DQN_ASSERTF(game->root_entity, "Sentinel/nil entity has not been assigned yet"); + + result = game->root_entity; // TODO(doyle): Root entity or ... the nil entity? + if (game->entity_free_list) { + result = game->entity_free_list; + game->entity_free_list = game->entity_free_list->next; + result->next = nullptr; + } else { + if (game->entities.size >= (TELY_GAME_ENTITY_HANDLE_INDEX_MAX + 1)) + return result; + + result = Dqn_VArray_Make(&game->entities, 1, Dqn_ZeroMem_Yes); + if (!result) + return result; + result->handle.id = (game->entities.size - 1) & TELY_GAME_ENTITY_HANDLE_INDEX_MASK; + } + + result->size_scale = Dqn_V2_InitNx1(1); + result->parent = TELY_Game_ActiveParentEntityPointer(game); + result->name = TELY_ChunkPool_AllocFmtFV(&game->chunk_pool, fmt, args); + result->waypoints = TELY_ChunkPool_New(&game->chunk_pool, TELY_GameWaypoint); + result->waypoints->next = result->waypoints; + result->waypoints->prev = result->waypoints; + + // NOTE: Attach entity as a child to the parent + TELY_GameEntity *parent = result->parent; + if (parent->first_child) + parent->last_child->next = result; + else + parent->first_child = result; + result->prev = parent->last_child; + parent->last_child = result; + + DQN_ASSERT(!result->next); + DQN_ASSERT(result->handle.id); + DQN_ASSERT(result->parent->handle == game->parent_entity_stack.data[game->parent_entity_stack.size - 1]); + return result; +} + +static TELY_GameEntity *TELY_Game_MakeEntityPointerF(TELY_Game *game, DQN_FMT_STRING_ANNOTATE char const *fmt, ...) +{ + va_list args; + va_start(args, fmt); + TELY_GameEntity *result = TELY_Game_MakeEntityPointerFV(game, fmt, args); + va_end(args); + return result; +} + +static TELY_GameEntityHandle TELY_Game_MakeEntityF(TELY_Game *game, DQN_FMT_STRING_ANNOTATE char const *fmt, ...) +{ + va_list args; + va_start(args, fmt); + TELY_GameEntity *entity = TELY_Game_MakeEntityPointerF(game, fmt, args); + va_end(args); + + TELY_GameEntityHandle result = {}; + if (entity) + result = entity->handle; + return result; +} + +static bool TELY_Game_IsNilEntity(TELY_GameEntity *entity) +{ + bool result = entity ? ((entity->handle.id & TELY_GAME_ENTITY_HANDLE_INDEX_MASK) == 0) : true; + return result; +} + +static void TELY_Game_DetachEntityIntoFreeList(TELY_Game *game, TELY_GameEntityHandle handle) +{ + TELY_GameEntity *entity = TELY_Game_GetEntity(game, handle); + if (TELY_Game_IsNilEntity(entity)) + return; + + // NOTE: Entities in the entity tree always have a parent (except for the + // nil/root entity). If an entity is passed in to this function and there's + // no parent, it's most likely you passed in an entity already in the free + // list (in which case only the next pointer will be set). This is most + // likely a mistake so we guard against it here. + if (!DQN_CHECK(entity->parent)) + return; + + uint64_t const entity_index_from_handle = entity->handle.id & TELY_GAME_ENTITY_HANDLE_INDEX_MASK; + DQN_ASSERT(entity_index_from_handle < game->entities.size); + + uint64_t const entity_generation_raw = entity->handle.id & TELY_GAME_ENTITY_HANDLE_GENERATION_MASK; + uint64_t const entity_generation = entity_generation_raw >> TELY_GAME_ENTITY_HANDLE_GENERATION_RSHIFT; + uint64_t const new_entity_generation = entity_generation + 1; + + // NOTE: De-attach entity from adjacent children + if (entity->prev) + entity->prev->next = entity->next; + + if (entity->next) + entity->next->prev = entity->prev; + + // NOTE: De-attach from parent + TELY_GameEntity *parent = entity->parent; + if (parent->first_child == entity) + parent->first_child = entity->next; + + if (parent->last_child == entity) + parent->last_child = entity->prev; + + if (entity->name.size) + TELY_ChunkPool_Dealloc(&game->chunk_pool, entity->name.data); + + if (new_entity_generation > entity_generation) { + // NOTE: Update the incremented handle disassociating all prior handles + // to this entity which would reference older generation values + *entity = {}; + entity->handle.id = entity_index_from_handle | (new_entity_generation << TELY_GAME_ENTITY_HANDLE_GENERATION_RSHIFT); + + // NOTE: Attach entity to the free list + entity->next = game->entity_free_list; + entity->prev = nullptr; + game->entity_free_list = entity; + } else { + // NOTE: We've cycled through all possible generations for this handle + // We will not increment it and freeze it so it is no longer allocated + // out. This prevents code that is still holding onto *really* old + // handles + } +} + +static void TELY_Game_DeleteEntity(TELY_Game *game, TELY_GameEntityHandle handle) +{ + uint64_t index_from_handle = handle.id & TELY_GAME_ENTITY_HANDLE_INDEX_MASK; + if (!game || !DQN_CHECK(index_from_handle < game->entities.size)) + return; + + TELY_GameEntity *root = game->entities.data + index_from_handle; + if (root->handle != handle) + return; + + // NOTE: The iterator snaps a copy of all the internal n-ary tree pointers + // so as we delete we do not accidentally invalidate any of the pointers. + for (TELY_GameEntityIterator it = {}; TELY_Game_DFSPostOrderWalkEntityTree(game, &it, root); ) { + DQN_ASSERT(it.entity != root); + TELY_GameEntity *entity = it.entity; + TELY_Game_DetachEntityIntoFreeList(game, entity->handle); + } + + TELY_Game_DetachEntityIntoFreeList(game, root->handle); +} + +static Dqn_V2 TELY_Game_CalcEntityWorldPos(TELY_Game const *game, TELY_GameEntityHandle handle) +{ + Dqn_V2 result = {}; + if (!game) + return result; + + for (TELY_GameEntity const *entity = TELY_Game_GetEntity(DQN_CAST(TELY_Game *) game, handle); + entity != game->root_entity; + entity = entity->parent) { + result += entity->local_pos; + } + return result; +} + +static Dqn_Rect TELY_Game_CalcEntityLocalHitBox(TELY_Game const *game, TELY_GameEntityHandle handle) +{ + TELY_GameEntity *entity = TELY_Game_GetEntity(DQN_CAST(TELY_Game *)game, handle); + Dqn_V2 half_hit_box_size = entity->local_hit_box_size * .5f; + Dqn_Rect result = Dqn_Rect_InitV2x2(entity->local_hit_box_offset - half_hit_box_size, entity->local_hit_box_size); + return result; +} + +static Dqn_Rect TELY_Game_CalcEntityWorldHitBox(TELY_Game const *game, TELY_GameEntityHandle handle) +{ + TELY_GameEntity *entity = TELY_Game_GetEntity(DQN_CAST(TELY_Game *) game, handle); + Dqn_V2 world_pos = TELY_Game_CalcEntityWorldPos(game, handle); + Dqn_Rect local_hit_box = TELY_Game_CalcEntityLocalHitBox(game, entity->handle); + Dqn_Rect result = Dqn_Rect_InitV2x2(world_pos + local_hit_box.pos, local_hit_box.size); + return result; +} + +static Dqn_Rect TELY_Game_CalcEntityAttackWorldHitBox(TELY_Game const *game, TELY_GameEntityHandle handle) +{ + TELY_GameEntity *entity = TELY_Game_GetEntity(DQN_CAST(TELY_Game *) game, handle); + Dqn_V2 world_pos = TELY_Game_CalcEntityWorldPos(game, handle); + Dqn_V2 half_hit_box_size = entity->attack_box_size * .5f; + Dqn_Rect result = Dqn_Rect_InitV2x2(world_pos + entity->attack_box_offset - half_hit_box_size, entity->attack_box_size); + return result; +} + +static Dqn_Rect TELY_Game_CalcEntityArrayWorldBoundingBox(TELY_Game const *game, TELY_GameEntityHandle const *handles, Dqn_usize count) +{ + Dqn_Rect result = {}; + if (!game || !handles) + return result; + + DQN_FOR_UINDEX(index, count) { + TELY_GameEntityHandle handle = handles[index]; + TELY_GameEntity const *entity = TELY_Game_GetEntity(DQN_CAST(TELY_Game *) game, handle); + Dqn_Rect bbox = TELY_Game_CalcEntityLocalHitBox(game, entity->handle); + for (TELY_GameShape const &shape_ : entity->shapes) { + TELY_GameShape const *shape = &shape_; + switch (shape->type) { + case TELY_GameShapeType_None: { + } break; + + case TELY_GameShapeType_Circle: { + Dqn_Rect rect = + Dqn_Rect_InitV2x2(shape->p1 - shape->circle_radius, Dqn_V2_InitNx1(shape->circle_radius * 2.f)); + bbox = Dqn_Rect_Union(bbox, rect); + } break; + + case TELY_GameShapeType_Rect: /*FALLTHRU*/ + case TELY_GameShapeType_Line: { + Dqn_V2 min = Dqn_V2_Min(shape->p1, shape->p2); + Dqn_V2 max = Dqn_V2_Max(shape->p1, shape->p2); + Dqn_Rect rect = Dqn_Rect_InitV2x2(min, max - min); + + if (shape->type == TELY_GameShapeType_Rect) + rect.pos -= rect.size * .5f; + + bbox = Dqn_Rect_Union(bbox, rect); + } break; + } + } + bbox.pos += TELY_Game_CalcEntityWorldPos(game, entity->handle); + + if (index) + result = Dqn_Rect_Union(result, bbox); + else + result = bbox; + } + return result; +} + +static Dqn_Rect TELY_Game_CalcEntityWorldBoundingBox(TELY_Game *game, TELY_GameEntityHandle handle) +{ + Dqn_Rect result = TELY_Game_CalcEntityArrayWorldBoundingBox(game, &handle, 1); + return result; +} diff --git a/feely_pona_game.h b/feely_pona_game.h new file mode 100644 index 0000000..6a0fa99 --- /dev/null +++ b/feely_pona_game.h @@ -0,0 +1,171 @@ +#if defined(__clang__) +#pragma once +#include "feely_pona_unity.h" +#endif + +enum TELY_EntityFlag +{ + TELY_EntityFlag_Clickable = 1 << 0, + TELY_EntityFlag_MoveByKeyboard = 1 << 1, + TELY_EntityFlag_MoveByMouse = 1 << 2, + TELY_EntityFlag_DrawHitBox = 1 << 3, + TELY_EntityFlag_DeriveHitBoxFromChildrenBoundingBox = 1 << 4, +}; + +enum TELY_GameShapeType +{ + TELY_GameShapeType_None, + TELY_GameShapeType_Circle, + TELY_GameShapeType_Rect, + TELY_GameShapeType_Line, +}; + +struct TELY_GameShape +{ + TELY_GameShapeType type; + Dqn_V2 p1; + Dqn_V2 p2; + Dqn_V4 colour; + Dqn_f32 line_thickness; + Dqn_f32 circle_radius; + TELY_RenderShapeMode render_mode; +}; + +const uint64_t TELY_GAME_ENTITY_HANDLE_GENERATION_MASK = 0xFFFF'0000'0000'0000; +const uint64_t TELY_GAME_ENTITY_HANDLE_GENERATION_RSHIFT = 48; +const uint64_t TELY_GAME_ENTITY_HANDLE_GENERATION_MAX = TELY_GAME_ENTITY_HANDLE_GENERATION_MASK >> TELY_GAME_ENTITY_HANDLE_GENERATION_RSHIFT; + +const uint64_t TELY_GAME_ENTITY_HANDLE_INDEX_MASK = 0x0000'FFFF'FFFF'FFFF; +const uint64_t TELY_GAME_ENTITY_HANDLE_INDEX_MAX = TELY_GAME_ENTITY_HANDLE_INDEX_MASK - 1; +struct TELY_GameEntityHandle +{ + uint64_t id; +}; + +enum TELY_GameEntityState +{ + TELY_GameEntityState_Nil, + TELY_GameEntityState_Idle, + TELY_GameEntityState_Attack, + TELY_GameEntityState_Run, +}; + +struct TELY_GameEntityAnimation +{ + uint16_t index; + uint16_t frame; + uint16_t ticks; + uint16_t ticks_per_frame; +}; + +struct TELY_GameWaypoint +{ + Dqn_V2I pos; + TELY_GameWaypoint *next; + TELY_GameWaypoint *prev; +}; + +struct TELY_GameEntity +{ + Dqn_String8 name; + TELY_GameEntityHandle handle; + TELY_AssetSpriteSheet *sprite_sheet; + Dqn_Slice sprite_anims; + Dqn_V2 size_scale; + TELY_GameEntityAnimation anim; + TELY_GameEntityState state; + Dqn_V2 velocity; + + TELY_GameEntityHandle stalk_entity; + Dqn_V2I stalk_entity_last_known_tile; + TELY_GameWaypoint *waypoints; + + // NOTE: The entity hit box is positioned at the center of the entity. + Dqn_V2 local_hit_box_size; + Dqn_V2 local_hit_box_offset; + + Dqn_V2 attack_box_size; + Dqn_V2 attack_box_offset; + + uint64_t flags; + bool facing_left; + Dqn_V2 local_pos; + Dqn_f64 alive_time_s; + Dqn_FArray shapes; + TELY_GameEntity *next; + TELY_GameEntity *prev; + TELY_GameEntity *first_child; + TELY_GameEntity *last_child; + TELY_GameEntity *parent; +}; + +struct TELY_GameEntityIterator +{ + bool init; + Dqn_usize iteration_count; + + TELY_GameEntity *entity; + TELY_GameEntity *last_visited; + + TELY_GameEntity *entity_parent; + TELY_GameEntity *entity_next; + TELY_GameEntity *entity_first_child; +}; + +struct TELY_FreyaGameMath +{ + TELY_GameEntityHandle lec01_group_box; + TELY_GameEntityHandle lec01_axis; + TELY_GameEntityHandle lec01_point_a; + TELY_GameEntityHandle lec01_point_b; + + TELY_GameEntityHandle lec01_task01_group_box; + TELY_GameEntityHandle lec01_task01_radial_trigger; + Dqn_f32 lec01_task01_radial_trigger_radius; + TELY_GameEntityHandle lec01_task01_player; + + TELY_GameEntityHandle lec01_task02_group_box; + TELY_GameEntityHandle lec01_task02_ray_begin; + TELY_GameEntityHandle lec01_task02_ray_end; + TELY_GameEntityHandle lec01_task02_surface; +}; + +struct TELY_GameCamera +{ + Dqn_V2 world_pos; + Dqn_f32 rotate_rads; + Dqn_V2 scale; +}; + +struct TELY_Game +{ + TELY_Platform *platform; + TELY_RFui rfui; + TELY_UI ui; + TELY_ChunkPool chunk_pool; + TELY_AssetFontHandle inter_regular_font; + TELY_AssetFontHandle inter_italic_font; + TELY_AssetFontHandle jetbrains_mono_font; + TELY_AssetAudioHandle test_audio; + + Dqn_Slice hero_sprite_anims; + TELY_AssetSpriteSheet hero_sprite_sheet; + + Dqn_FArray parent_entity_stack; + Dqn_VArray entities; + TELY_GameEntity *root_entity; + TELY_GameEntity *entity_free_list; + + TELY_GameEntityHandle clicked_entity; + TELY_GameEntityHandle hot_entity; + TELY_GameEntityHandle active_entity; + + TELY_GameEntityHandle prev_clicked_entity; + TELY_GameEntityHandle prev_hot_entity; + TELY_GameEntityHandle prev_active_entity; + + TELY_FreyaGameMath freya_game_math; + + TELY_GameCamera camera; + +}; diff --git a/feely_pona_unity.cpp b/feely_pona_unity.cpp deleted file mode 100644 index fd0f865..0000000 --- a/feely_pona_unity.cpp +++ /dev/null @@ -1 +0,0 @@ -#include "feely_pona_unity.h" diff --git a/feely_pona_unity.h b/feely_pona_unity.h index 40cf9d8..271af4b 100644 --- a/feely_pona_unity.h +++ b/feely_pona_unity.h @@ -36,30 +36,34 @@ #define DQN_ONLY_FS #define _CRT_SECURE_NO_WARNINGS #define DQN_IMPLEMENTATION -#include "external/dqn/dqn.h" +#include "External/tely/External/dqn/dqn.h" // NOTE: TELY ====================================================================================== DQN_MSVC_WARNING_DISABLE(4505) // warning C4505: unreferenced function with internal linkage has been removed -#include "tely_profile.h" -#include "tely_platform_input.h" -#include "tely_asset.h" -#include "tely_colour.h" -#include "tely_render.h" -#include "tely_tools.h" -#include "tely_audio.h" -#include "tely_platform.h" -#include "tely_ui.h" -#include "tely_rfui.h" -#include "tely_game.h" +#include "External/tely/tely_profile.h" +#include "External/tely/tely_platform_input.h" +#include "External/tely/tely_asset.h" +#include "External/tely/tely_colour.h" +#include "External/tely/tely_render.h" +#include "External/tely/tely_tools.h" +#include "External/tely/tely_audio.h" +#include "External/tely/tely_platform.h" +#include "External/tely/tely_ui.h" +#include "External/tely/tely_rfui.h" + +#include "External/tely/tely_tools.cpp" +#include "External/tely/tely_asset.cpp" +#include "External/tely/tely_audio.cpp" +#include "External/tely/tely_render.cpp" +#include "External/tely/tely_platform_input.cpp" +#include "External/tely/tely_ui.cpp" +#include "External/tely/tely_rfui.cpp" + +// NOTE: feely_pona ================================================================================ + +#include "feely_pona_game.h" #include "feely_pona.h" -#include "tely_tools.cpp" -#include "tely_game.cpp" -#include "tely_asset.cpp" -#include "tely_audio.cpp" -#include "tely_render.cpp" -#include "tely_platform_input.cpp" -#include "tely_ui.cpp" -#include "tely_rfui.cpp" +#include "feely_pona_game.cpp" #include "feely_pona.cpp"