diff --git a/External/tely b/External/tely index c63954d..595e3c7 160000 --- a/External/tely +++ b/External/tely @@ -1 +1 @@ -Subproject commit c63954d31b0c99203c5dc752494bbaefd59128f0 +Subproject commit 595e3c7f1e70aab8e51620682291aa2ba6c02b7f diff --git a/feely_pona.cpp b/feely_pona.cpp index d6d5f95..6006f4b 100644 --- a/feely_pona.cpp +++ b/feely_pona.cpp @@ -142,7 +142,7 @@ static void FP_Game_MoveEntity(FP_Game *game, FP_GameEntityHandle entity_handle, Dqn_f32 velocity_falloff_coefficient = 0.82f; Dqn_f32 acceleration_feel_good_factor = 15'000.f; - Dqn_V2 acceleration = FP_Game_MetersToPixelsV2(game, acceleration_meters_per_s) * acceleration_feel_good_factor; + Dqn_V2 acceleration = FP_Game_MetersToPixelsV2(game->play, acceleration_meters_per_s) * acceleration_feel_good_factor; entity->velocity = (acceleration * t) + entity->velocity * velocity_falloff_coefficient; // NOTE: Zero out velocity with epsilon @@ -161,7 +161,7 @@ static void FP_Game_MoveEntity(FP_Game *game, FP_GameEntityHandle entity_handle, Dqn_V2 global_earliest_pos_just_before_collide = {}; if ((entity->flags & FP_GameEntityFlag_NoClip) == 0) { - for (FP_GameEntityIterator collider_it = {}; FP_Game_DFSPostOrderWalkEntityTree(game, &collider_it, game->root_entity); ) { + for (FP_GameEntityIterator collider_it = {}; FP_Game_DFSPostOrderWalkEntityTree(game, &collider_it, game->play.root_entity); ) { FP_GameEntity *collider = collider_it.entity; if (collider->handle == entity->handle) continue; @@ -279,72 +279,28 @@ static void FP_Game_MoveEntity(FP_Game *game, FP_GameEntityHandle entity_handle, } } -extern "C" __declspec(dllexport) -void TELY_DLL_Reload(void *user_data) +static void FP_PlayReset(FP_Game *game, TELY_Platform *platform) { - TELY_Platform *platform = DQN_CAST(TELY_Platform *)user_data; - Dqn_Library_SetPointer(platform->core.dqn_lib); + FP_GamePlay *play = &game->play; + if (game->play.root_entity) + FP_Game_DeleteEntity(game, game->play.root_entity->handle); -} + Dqn_VArray shallow_entities_copy = play->entities; + *play = {}; -extern "C" __declspec(dllexport) -void TELY_DLL_Init(void *user_data) -{ - TELY_Platform *platform = DQN_CAST(TELY_Platform *)user_data; - TELY_DLL_Reload(user_data); + play->chunk_pool = &platform->chunk_pool; + play->meters_to_pixels = 65.416f; + play->entities = shallow_entities_copy; - FP_UnitTests(platform); - - // NOTE: TELY Game ============================================================================= - TELY_Assets *assets = &platform->assets; - FP_Game *game = Dqn_Arena_New(&platform->arena, FP_Game, Dqn_ZeroMem_Yes); - game->chunk_pool = &platform->chunk_pool; - game->meters_to_pixels = 65.416f; - Dqn_PCG32_Seed(&game->rng, 0xABCDEF); - - platform->user_data = game; - { - TELY_AssetSpriteSheet *sheet = &game->hero_sprite_sheet; - Dqn_ThreadScratch scratch = Dqn_Thread_GetScratch(nullptr); - Dqn_String8 sheet_path = Dqn_FsPath_ConvertF(scratch.arena, "%.*s/adventurer-v1.5-sheet.png", DQN_STRING_FMT(assets->textures_dir)); - sheet->tex_handle = platform->func_load_texture(assets, DQN_STRING8("Hero"), sheet_path); - sheet->sprite_count = 109; - sheet->sprites_per_row = 7; - sheet->sprite_size = Dqn_V2I_InitNx2(50, 37); - - TELY_AssetSpriteAnimation hero_anims[] = { - {DQN_STRING8("Everything"), /*index*/ 0, /*count*/ sheet->sprite_count, /*ms_per_frame*/ DQN_CAST(uint32_t)(1000.f / 12.f)}, - {DQN_STRING8("Idle"), /*index*/ 0, /*count*/ 3, /*ms_per_frame*/ DQN_CAST(uint32_t)(1000.f / 4.f)}, - {DQN_STRING8("Run"), /*index*/ 8, /*count*/ 6, /*ms_per_frame*/ DQN_CAST(uint32_t)(1000.f / 8.f)}, - {DQN_STRING8("Jump"), /*index*/ 14, /*count*/ 10, /*ms_per_frame*/ DQN_CAST(uint32_t)(1000.f / 12.f)}, - {DQN_STRING8("Floor slide"), /*index*/ 24, /*count*/ 5, /*ms_per_frame*/ DQN_CAST(uint32_t)(1000.f / 12.f)}, - {DQN_STRING8("Unknown"), /*index*/ 29, /*count*/ 9, /*ms_per_frame*/ DQN_CAST(uint32_t)(1000.f / 12.f)}, - {DQN_STRING8("Attack A"), /*index*/ 42, /*count*/ 7, /*ms_per_frame*/ DQN_CAST(uint32_t)(1000.f / 12.f)}, - {DQN_STRING8("Attack B"), /*index*/ 49, /*count*/ 4, /*ms_per_frame*/ DQN_CAST(uint32_t)(1000.f / 8.f)}, - {DQN_STRING8("Attack C"), /*index*/ 53, /*count*/ 6, /*ms_per_frame*/ DQN_CAST(uint32_t)(1000.f / 12.f)}, - {DQN_STRING8("Hurt A"), /*index*/ 59, /*count*/ 5, /*ms_per_frame*/ DQN_CAST(uint32_t)(1000.f / 12.f)}, - {DQN_STRING8("Hurt B"), /*index*/ 64, /*count*/ 5, /*ms_per_frame*/ DQN_CAST(uint32_t)(1000.f / 12.f)}, - {DQN_STRING8("Unsheath sword"), /*index*/ 69, /*count*/ 4, /*ms_per_frame*/ DQN_CAST(uint32_t)(1000.f / 12.f)}, - {DQN_STRING8("Sheath sword"), /*index*/ 73, /*count*/ 4, /*ms_per_frame*/ DQN_CAST(uint32_t)(1000.f / 12.f)}, - {DQN_STRING8("Air drift"), /*index*/ 77, /*count*/ 2, /*ms_per_frame*/ DQN_CAST(uint32_t)(1000.f / 12.f)}, - {DQN_STRING8("Air drop"), /*index*/ 79, /*count*/ 2, /*ms_per_frame*/ DQN_CAST(uint32_t)(1000.f / 12.f)}, - {DQN_STRING8("Ladder climb"), /*index*/ 81, /*count*/ 4, /*ms_per_frame*/ DQN_CAST(uint32_t)(1000.f / 12.f)}, - {DQN_STRING8("Chi push"), /*index*/ 85, /*count*/ 8, /*ms_per_frame*/ DQN_CAST(uint32_t)(1000.f / 12.f)}, - {DQN_STRING8("Leap slice A"), /*index*/ 93, /*count*/ 7, /*ms_per_frame*/ DQN_CAST(uint32_t)(1000.f / 12.f)}, - {DQN_STRING8("Leap slice B"), /*index*/ 100, /*count*/ 3, /*ms_per_frame*/ DQN_CAST(uint32_t)(1000.f / 12.f)}, - {DQN_STRING8("Leap slice C"), /*index*/ 103, /*count*/ 6, /*ms_per_frame*/ DQN_CAST(uint32_t)(1000.f / 12.f)}, - }; - - game->hero_sprite_anims = Dqn_Slice_Alloc(&platform->arena, DQN_ARRAY_UCOUNT(hero_anims), Dqn_ZeroMem_No); - DQN_MEMCPY(game->hero_sprite_anims.data, &hero_anims, sizeof(hero_anims[0]) * DQN_ARRAY_UCOUNT(hero_anims)); + if (play->entities.data) { + Dqn_VArray_Clear(&play->entities, Dqn_ZeroMem_Yes); + } else { + play->entities = Dqn_VArray_Init(&play->arena, 1024 * 8); } - // NOTE: Load sprite sheets ==================================================================== - game->atlas_sprite_sheet = FP_LoadSpriteSheetFromSpec(platform, assets, &platform->arena, DQN_STRING8("atlas")); - - game->entities = Dqn_VArray_Init(&platform->arena, 1024 * 8); - game->root_entity = Dqn_VArray_Make(&game->entities, Dqn_ZeroMem_No); - Dqn_FArray_Add(&game->parent_entity_stack, game->root_entity->handle); + play->root_entity = Dqn_VArray_Make(&play->entities, Dqn_ZeroMem_No); + Dqn_FArray_Add(&play->parent_entity_stack, play->root_entity->handle); + Dqn_PCG32_Seed(&play->rng, 0xABCDEF); // NOTE: Map =================================================================================== { @@ -363,14 +319,14 @@ void TELY_DLL_Init(void *user_data) entity->local_hit_box_size = sprite_rect_scaled; FP_Entity_AddDebugEditorFlags(game, entity->handle); FP_Game_EntityActionReset(game, entity->handle, FP_GAME_ENTITY_ACTION_INFINITE_TIMER, render_data.sprite); - game->map = entity; + play->map = entity; } // NOTE: Map walls ============================================================================= - FP_GameEntity const *map = game->map; + FP_GameEntity const *map = play->map; Dqn_Rect const map_hit_box = FP_Game_CalcEntityWorldHitBox(game, map->handle); { - Dqn_f32 wall_thickness = FP_Game_MetersToPixelsNx1(game, 1.f); + Dqn_f32 wall_thickness = FP_Game_MetersToPixelsNx1(game->play, 1.f); Dqn_f32 half_wall_thickness = wall_thickness * .5f; FP_Entity_CreateWallAtPos(game, @@ -428,14 +384,14 @@ void TELY_DLL_Init(void *user_data) Dqn_V2 base_mid_p = Dqn_V2_InitNx2(1580, 0.f); { // NOTE: Mid lane mob spawner ================================================================== - Dqn_V2 mid_lane_mob_spawner_pos = Dqn_V2_InitNx2(game->map->local_hit_box_size.w * -0.5f + 128.f, 0.f); + Dqn_V2 mid_lane_mob_spawner_pos = Dqn_V2_InitNx2(play->map->local_hit_box_size.w * -0.5f + 128.f, 0.f); Dqn_usize spawn_cap = 16; { FP_GameEntityHandle mob_spawner = FP_Entity_CreateMobSpawner(game, mid_lane_mob_spawner_pos, spawn_cap, "Mob spawner"); FP_Game_PushParentEntity(game, mob_spawner); FP_Entity_CreateWaypointF(game, Dqn_V2_InitNx2(-mid_lane_mob_spawner_pos.x + base_mid_p.x, base_mid_p.y), "Waypoint"); FP_Game_PopParentEntity(game); - Dqn_FArray_Add(&game->mob_spawners, mob_spawner); + Dqn_FArray_Add(&play->mob_spawners, mob_spawner); } // NOTE: Bottom lane spawner =================================================================== @@ -447,7 +403,7 @@ void TELY_DLL_Init(void *user_data) FP_Entity_CreateWaypointF(game, Dqn_V2_InitNx2(-bottom_lane_mob_spawner_pos.x + base_mid_p.x, 0.f), "Waypoint"); FP_Entity_CreateWaypointF(game, Dqn_V2_InitNx2(-bottom_lane_mob_spawner_pos.x + base_mid_p.x, -932.f), "Waypoint"); FP_Game_PopParentEntity(game); - Dqn_FArray_Add(&game->mob_spawners, mob_spawner); + Dqn_FArray_Add(&play->mob_spawners, mob_spawner); } // NOTE: Top lane spawner =================================================================== @@ -458,7 +414,7 @@ void TELY_DLL_Init(void *user_data) FP_Entity_CreateWaypointF(game, Dqn_V2_InitNx2(-top_lane_mob_spawner_pos.x + base_mid_p.x, 0.f), "Waypoint"); FP_Entity_CreateWaypointF(game, Dqn_V2_InitNx2(-top_lane_mob_spawner_pos.x + base_mid_p.x, +915.f), "Waypoint"); FP_Game_PopParentEntity(game); - Dqn_FArray_Add(&game->mob_spawners, mob_spawner); + Dqn_FArray_Add(&play->mob_spawners, mob_spawner); } #endif } @@ -470,16 +426,16 @@ void TELY_DLL_Init(void *user_data) FP_GameEntityHandle monkey_b = FP_Entity_CreatePortalMonkey(game, Dqn_V2_InitNx2(monkey_base_p.x, monkey_base_p.y + 100.f), "Monkey B"); FP_GameEntityHandle monkey_c = FP_Entity_CreatePortalMonkey(game, Dqn_V2_InitNx2(monkey_base_p.x, monkey_base_p.y - 100.f), "Monkey C"); - Dqn_FArray_Add(&game->portal_monkeys, monkey_a); - Dqn_FArray_Add(&game->portal_monkeys, monkey_b); - Dqn_FArray_Add(&game->portal_monkeys, monkey_c); + Dqn_FArray_Add(&play->portal_monkeys, monkey_a); + Dqn_FArray_Add(&play->portal_monkeys, monkey_b); + Dqn_FArray_Add(&play->portal_monkeys, monkey_c); } // NOTE: Hero ================================================================================== { FP_GameEntityHandle terry = FP_Entity_CreateTerry(game, Dqn_V2_InitNx2(1434, 11), "Terry"); - game->clicked_entity = terry; - game->player = terry; + play->clicked_entity = terry; + play->player = terry; } { @@ -491,37 +447,98 @@ void TELY_DLL_Init(void *user_data) Dqn_V2 base_bottom_left = Dqn_V2_InitNx2(base_top_left_pos.x, base_bottom_right_pos.y); Dqn_V2 base_bottom_right = Dqn_V2_InitNx2(base_bottom_right_pos.x, base_bottom_right_pos.y); - game->merchant_terry = FP_Entity_CreateMerchantTerry(game, base_top_left, "Merchant"); - game->merchant_graveyard = FP_Entity_CreateMerchantGraveyard(game, base_bottom_left, "Graveyard"); - game->merchant_gym = FP_Entity_CreateMerchantGym(game, base_bottom_right, "Gym"); - game->merchant_phone_company = FP_Entity_CreateMerchantPhoneCompany(game, base_top_right, "PhoneCompany"); + play->merchant_terry = FP_Entity_CreateMerchantTerry(game, base_top_left, "Merchant"); + play->merchant_graveyard = FP_Entity_CreateMerchantGraveyard(game, base_bottom_left, "Graveyard"); + play->merchant_gym = FP_Entity_CreateMerchantGym(game, base_bottom_right, "Gym"); + play->merchant_phone_company = FP_Entity_CreateMerchantPhoneCompany(game, base_top_right, "PhoneCompany"); } -#if 0 FP_Entity_CreateClubTerry(game, Dqn_V2_InitNx2(+500, -191), "Club Terry"); FP_Entity_CreateKennelTerry(game, Dqn_V2_InitNx2(-300, -191), "Kennel Terry"); FP_Entity_CreateChurchTerry(game, Dqn_V2_InitNx2(-800, -191), "Church Terry"); FP_Entity_CreateAirportTerry(game, Dqn_V2_InitNx2(-1200, -191), "Airport Terry"); -#endif - game->tile_size = 37; - Dqn_V2I max_tile = platform->core.window_size / game->tile_size; + play->tile_size = 37; + Dqn_V2I max_tile = platform->core.window_size / play->tile_size; // NOTE: Heart FP_Entity_CreateHeart(game, base_mid_p, "Heart"); + play->camera.world_pos = base_mid_p - Dqn_V2_InitV2I(platform->core.window_size * .5f); + play->camera.scale = Dqn_V2_InitNx1(1); +} + +extern "C" __declspec(dllexport) +void TELY_DLL_Reload(void *user_data) +{ + TELY_Platform *platform = DQN_CAST(TELY_Platform *)user_data; + Dqn_Library_SetPointer(platform->core.dqn_lib); + +} + +extern "C" __declspec(dllexport) +void TELY_DLL_Init(void *user_data) +{ + TELY_Platform *platform = DQN_CAST(TELY_Platform *)user_data; + TELY_DLL_Reload(user_data); + + FP_UnitTests(platform); + + // NOTE: TELY Game ============================================================================= + TELY_Assets *assets = &platform->assets; + FP_Game *game = Dqn_Arena_New(&platform->arena, FP_Game, Dqn_ZeroMem_Yes); + uint16_t font_size = 18; - game->camera.world_pos = base_mid_p - Dqn_V2_InitV2I(platform->core.window_size * .5f); - game->camera.scale = Dqn_V2_InitNx1(1); game->inter_regular_font_large = platform->func_load_font(assets, DQN_STRING8("Inter (Regular)"), DQN_STRING8("Data/Inter-Regular.otf"), DQN_CAST(uint16_t)(font_size * 5.f)); game->inter_regular_font = platform->func_load_font(assets, DQN_STRING8("Inter (Regular)"), DQN_STRING8("Data/Inter-Regular.otf"), DQN_CAST(uint16_t)(font_size)); - // game->inter_italic_font = platform->func_load_font(assets, DQN_STRING8("Inter (Italic)"), DQN_STRING8("Data/Inter-Italic.otf"), font_size); - // game->jetbrains_mono_font = platform->func_load_font(assets, DQN_STRING8("JetBrains Mono NL (Regular)"), DQN_STRING8("Data/JetBrainsMonoNL-Regular.ttf"), font_size); + game->inter_italic_font = platform->func_load_font(assets, DQN_STRING8("Inter (Italic)"), DQN_STRING8("Data/Inter-Italic.otf"), font_size); + game->jetbrains_mono_font = platform->func_load_font(assets, DQN_STRING8("JetBrains Mono NL (Regular)"), DQN_STRING8("Data/JetBrainsMonoNL-Regular.ttf"), font_size); game->talkco_font = platform->func_load_font(assets, DQN_STRING8("Talkco"), DQN_STRING8("Data/Talkco.otf"), font_size); game->talkco_font_large = platform->func_load_font(assets, DQN_STRING8("Talkco"), DQN_STRING8("Data/Talkco.otf"), DQN_CAST(uint16_t)(font_size * 1.5f)); game->audio[FP_GameAudio_TestAudio] = platform->func_load_audio(assets, DQN_STRING8("Test Audio"), DQN_STRING8("Data/Audio/Purrple Cat - Moonwinds.qoa")); game->audio[FP_GameAudio_TerryHit] = platform->func_load_audio(assets, DQN_STRING8("Terry Hit"), DQN_STRING8("Data/Audio/terry_hit.ogg")); game->audio[FP_GameAudio_Smooch] = platform->func_load_audio(assets, DQN_STRING8("Smooch"), DQN_STRING8("Data/Audio/smooch.mp3")); + + platform->user_data = game; + { + TELY_AssetSpriteSheet *sheet = &game->hero_sprite_sheet; + Dqn_ThreadScratch scratch = Dqn_Thread_GetScratch(nullptr); + Dqn_String8 sheet_path = Dqn_FsPath_ConvertF(scratch.arena, "%.*s/adventurer-v1.5-sheet.png", DQN_STRING_FMT(assets->textures_dir)); + sheet->tex_handle = platform->func_load_texture(assets, DQN_STRING8("Hero"), sheet_path); + sheet->sprite_count = 109; + sheet->sprites_per_row = 7; + sheet->sprite_size = Dqn_V2I_InitNx2(50, 37); + + TELY_AssetSpriteAnimation hero_anims[] = { + {DQN_STRING8("Everything"), /*index*/ 0, /*count*/ sheet->sprite_count, /*ms_per_frame*/ DQN_CAST(uint32_t)(1000.f / 12.f)}, + {DQN_STRING8("Idle"), /*index*/ 0, /*count*/ 3, /*ms_per_frame*/ DQN_CAST(uint32_t)(1000.f / 4.f)}, + {DQN_STRING8("Run"), /*index*/ 8, /*count*/ 6, /*ms_per_frame*/ DQN_CAST(uint32_t)(1000.f / 8.f)}, + {DQN_STRING8("Jump"), /*index*/ 14, /*count*/ 10, /*ms_per_frame*/ DQN_CAST(uint32_t)(1000.f / 12.f)}, + {DQN_STRING8("Floor slide"), /*index*/ 24, /*count*/ 5, /*ms_per_frame*/ DQN_CAST(uint32_t)(1000.f / 12.f)}, + {DQN_STRING8("Unknown"), /*index*/ 29, /*count*/ 9, /*ms_per_frame*/ DQN_CAST(uint32_t)(1000.f / 12.f)}, + {DQN_STRING8("Attack A"), /*index*/ 42, /*count*/ 7, /*ms_per_frame*/ DQN_CAST(uint32_t)(1000.f / 12.f)}, + {DQN_STRING8("Attack B"), /*index*/ 49, /*count*/ 4, /*ms_per_frame*/ DQN_CAST(uint32_t)(1000.f / 8.f)}, + {DQN_STRING8("Attack C"), /*index*/ 53, /*count*/ 6, /*ms_per_frame*/ DQN_CAST(uint32_t)(1000.f / 12.f)}, + {DQN_STRING8("Hurt A"), /*index*/ 59, /*count*/ 5, /*ms_per_frame*/ DQN_CAST(uint32_t)(1000.f / 12.f)}, + {DQN_STRING8("Hurt B"), /*index*/ 64, /*count*/ 5, /*ms_per_frame*/ DQN_CAST(uint32_t)(1000.f / 12.f)}, + {DQN_STRING8("Unsheath sword"), /*index*/ 69, /*count*/ 4, /*ms_per_frame*/ DQN_CAST(uint32_t)(1000.f / 12.f)}, + {DQN_STRING8("Sheath sword"), /*index*/ 73, /*count*/ 4, /*ms_per_frame*/ DQN_CAST(uint32_t)(1000.f / 12.f)}, + {DQN_STRING8("Air drift"), /*index*/ 77, /*count*/ 2, /*ms_per_frame*/ DQN_CAST(uint32_t)(1000.f / 12.f)}, + {DQN_STRING8("Air drop"), /*index*/ 79, /*count*/ 2, /*ms_per_frame*/ DQN_CAST(uint32_t)(1000.f / 12.f)}, + {DQN_STRING8("Ladder climb"), /*index*/ 81, /*count*/ 4, /*ms_per_frame*/ DQN_CAST(uint32_t)(1000.f / 12.f)}, + {DQN_STRING8("Chi push"), /*index*/ 85, /*count*/ 8, /*ms_per_frame*/ DQN_CAST(uint32_t)(1000.f / 12.f)}, + {DQN_STRING8("Leap slice A"), /*index*/ 93, /*count*/ 7, /*ms_per_frame*/ DQN_CAST(uint32_t)(1000.f / 12.f)}, + {DQN_STRING8("Leap slice B"), /*index*/ 100, /*count*/ 3, /*ms_per_frame*/ DQN_CAST(uint32_t)(1000.f / 12.f)}, + {DQN_STRING8("Leap slice C"), /*index*/ 103, /*count*/ 6, /*ms_per_frame*/ DQN_CAST(uint32_t)(1000.f / 12.f)}, + }; + + game->hero_sprite_anims = Dqn_Slice_Alloc(&platform->arena, DQN_ARRAY_UCOUNT(hero_anims), Dqn_ZeroMem_No); + DQN_MEMCPY(game->hero_sprite_anims.data, &hero_anims, sizeof(hero_anims[0]) * DQN_ARRAY_UCOUNT(hero_anims)); + } + + // NOTE: Load sprite sheets ==================================================================== + game->atlas_sprite_sheet = FP_LoadSpriteSheetFromSpec(platform, assets, &platform->arena, DQN_STRING8("atlas")); + FP_PlayReset(game, platform); } struct FP_GetClosestPortalMonkeyResult @@ -536,7 +553,7 @@ FP_GetClosestPortalMonkeyResult FP_GetClosestPortalMonkey(FP_Game *game, FP_Game FP_GetClosestPortalMonkeyResult result = {}; result.dist = DQN_F32_MAX; FP_GameEntityHandle best_portal_monkey = {}; - for (FP_GameEntityHandle portal_monkey_handle : game->portal_monkeys) { + for (FP_GameEntityHandle portal_monkey_handle : game->play.portal_monkeys) { FP_GameEntity *portal_monkey = FP_Game_GetEntity(game, portal_monkey_handle); if (FP_Game_IsNilEntity(portal_monkey)) continue; @@ -556,10 +573,10 @@ void FP_EntityActionStateMachine(FP_Game *game, TELY_Audio *audio, TELY_Platform { TELY_AssetSpriteSheet *sheet = &game->atlas_sprite_sheet; FP_GameEntityAction *action = &entity->action; - bool const we_are_clicked_entity = entity->handle == game->clicked_entity; + bool const we_are_clicked_entity = entity->handle == game->play.clicked_entity; bool const entity_has_velocity = entity->velocity.x || entity->velocity.y; bool const entering_new_state = entity->alive_time_s == 0.f || action->state != action->next_state; - bool const action_has_finished = !entering_new_state && game->clock_ms >= action->end_at_clock_ms; + bool const action_has_finished = !entering_new_state && game->play.clock_ms >= action->end_at_clock_ms; action->state = action->next_state; FP_EntityRenderData render_data = FP_Entity_GetRenderData(game, entity->type, entity->action.state, entity->direction); @@ -585,12 +602,12 @@ void FP_EntityActionStateMachine(FP_Game *game, TELY_Audio *audio, TELY_Platform } if (we_are_clicked_entity) { - if (game->in_game_menu == FP_GameInGameMenu_Nil) { + if (game->play.in_game_menu == FP_GameInGameMenu_Nil) { bool picked_up_monkey_this_frame = false; if (FP_Game_IsNilEntityHandle(game, entity->carried_monkey)) { // NOTE: Check if we are nearby a monkey and picking it up FP_GetClosestPortalMonkeyResult closest_monkey = FP_GetClosestPortalMonkey(game, entity->handle); - if (closest_monkey.dist < DQN_SQUARED(FP_Game_MetersToPixelsNx1(game, 2.f))) { + if (closest_monkey.dist < DQN_SQUARED(FP_Game_MetersToPixelsNx1(game->play, 2.f))) { if (TELY_Platform_InputScanCodeIsPressed(input, TELY_PlatformInputScanCode_J)) { entity->carried_monkey = closest_monkey.entity; picked_up_monkey_this_frame = true; @@ -652,11 +669,11 @@ void FP_EntityActionStateMachine(FP_Game *game, TELY_Audio *audio, TELY_Platform if (we_are_clicked_entity) { bool picked_up_monkey_this_frame = false; - if (game->in_game_menu == FP_GameInGameMenu_Nil) { + if (game->play.in_game_menu == FP_GameInGameMenu_Nil) { if (FP_Game_IsNilEntityHandle(game, entity->carried_monkey)) { // NOTE: Check if we are nearby a monkey and picking it up FP_GetClosestPortalMonkeyResult closest_monkey = FP_GetClosestPortalMonkey(game, entity->handle); - if (closest_monkey.dist < DQN_SQUARED(FP_Game_MetersToPixelsNx1(game, 2.f))) { + if (closest_monkey.dist < DQN_SQUARED(FP_Game_MetersToPixelsNx1(game->play, 2.f))) { if (TELY_Platform_InputScanCodeIsPressed(input, TELY_PlatformInputScanCode_J)) { entity->carried_monkey = closest_monkey.entity; picked_up_monkey_this_frame = true; @@ -706,21 +723,21 @@ void FP_EntityActionStateMachine(FP_Game *game, TELY_Audio *audio, TELY_Platform FP_GameRenderSprite *cosmetic_sprite = Dqn_FArray_Make(&entity->extra_cosmetic_anims, Dqn_ZeroMem_Yes); if (cosmetic_sprite) { cosmetic_sprite->asset = TELY_Asset_MakeAnimatedSprite(sheet, g_anim_names.terry_ghost, TELY_AssetFlip_No); - cosmetic_sprite->started_at_clock_ms = game->clock_ms; + cosmetic_sprite->started_at_clock_ms = game->play.clock_ms; cosmetic_sprite->height.meters = entity->sprite_height.meters; uint32_t max_rng_dist_x = DQN_CAST(uint32_t)(FP_Game_MetersToPixelsNx1(game, entity->sprite_height.meters) * 1.f); - uint32_t rng_x = Dqn_PCG32_Range(&game->rng, DQN_CAST(uint32_t)0, max_rng_dist_x); + uint32_t rng_x = Dqn_PCG32_Range(&game->play.rng, DQN_CAST(uint32_t)0, max_rng_dist_x); cosmetic_sprite->offset.x = rng_x - (max_rng_dist_x * .5f); uint32_t max_rng_dist_y = DQN_CAST(uint32_t)(FP_Game_MetersToPixelsNx1(game, entity->sprite_height.meters) * .25f); - uint32_t rng_y = Dqn_PCG32_Range(&game->rng, DQN_CAST(uint32_t)0, max_rng_dist_y); + uint32_t rng_y = Dqn_PCG32_Range(&game->play.rng, DQN_CAST(uint32_t)0, max_rng_dist_y); cosmetic_sprite->offset.y = -DQN_CAST(Dqn_f32)rng_y; } #endif } - entity->action.sprite_alpha = Dqn_PCG32_NextF32(&game->rng); + entity->action.sprite_alpha = Dqn_PCG32_NextF32(&game->play.rng); if (action_has_finished) { FP_Game_EntityTransitionState(game, entity, FP_EntityTerryState_Run); @@ -778,16 +795,16 @@ void FP_EntityActionStateMachine(FP_Game *game, TELY_Audio *audio, TELY_Platform FP_GameRenderSprite *cosmetic_sprite = Dqn_FArray_Make(&entity->extra_cosmetic_anims, Dqn_ZeroMem_Yes); if (cosmetic_sprite) { cosmetic_sprite->asset = TELY_Asset_MakeAnimatedSprite(sheet, g_anim_names.smoochie_attack_heart, TELY_AssetFlip_No); - cosmetic_sprite->started_at_clock_ms = game->clock_ms; + cosmetic_sprite->started_at_clock_ms = game->play.clock_ms; cosmetic_sprite->height.meters = entity->sprite_height.meters * .35f; cosmetic_sprite->loop = true; - uint32_t max_rng_dist_x = DQN_CAST(uint32_t)(FP_Game_MetersToPixelsNx1(game, entity->sprite_height.meters) * 1.f); - uint32_t rng_x = Dqn_PCG32_Range(&game->rng, DQN_CAST(uint32_t)0, max_rng_dist_x); + uint32_t max_rng_dist_x = DQN_CAST(uint32_t)(FP_Game_MetersToPixelsNx1(game->play, entity->sprite_height.meters) * 1.f); + uint32_t rng_x = Dqn_PCG32_Range(&game->play.rng, DQN_CAST(uint32_t)0, max_rng_dist_x); cosmetic_sprite->offset.x = rng_x - (max_rng_dist_x * .5f); - uint32_t max_rng_dist_y = DQN_CAST(uint32_t)(FP_Game_MetersToPixelsNx1(game, entity->sprite_height.meters) * .25f); - uint32_t rng_y = Dqn_PCG32_Range(&game->rng, DQN_CAST(uint32_t)0, max_rng_dist_y); + uint32_t max_rng_dist_y = DQN_CAST(uint32_t)(FP_Game_MetersToPixelsNx1(game->play, entity->sprite_height.meters) * .25f); + uint32_t rng_y = Dqn_PCG32_Range(&game->play.rng, DQN_CAST(uint32_t)0, max_rng_dist_y); cosmetic_sprite->offset.y = -DQN_CAST(Dqn_f32)rng_y; } } @@ -1055,13 +1072,13 @@ void FP_EntityActionStateMachine(FP_Game *game, TELY_Audio *audio, TELY_Platform // NOTE: Add a waypoint for the plane to the mob spawn { - uint32_t mob_spawner_index = Dqn_PCG32_Range(&game->rng, 0, DQN_CAST(uint32_t)game->mob_spawners.size); - FP_GameEntityHandle mob_spawner = game->mob_spawners.data[mob_spawner_index]; + uint32_t mob_spawner_index = Dqn_PCG32_Range(&game->play.rng, 0, DQN_CAST(uint32_t)game->play.mob_spawners.size); + FP_GameEntityHandle mob_spawner = game->play.mob_spawners.data[mob_spawner_index]; Dqn_V2 target_pos = FP_Game_CalcEntityWorldPos(game, mob_spawner); - plane->waypoints = FP_SentinelList_Init(game->chunk_pool); + plane->waypoints = FP_SentinelList_Init(game->play.chunk_pool); FP_SentinelListLink *link = FP_SentinelList_MakeBefore(&plane->waypoints, FP_SentinelList_Front(&plane->waypoints), - game->chunk_pool); + game->play.chunk_pool); FP_GameWaypoint *waypoint = &link->data; waypoint->entity = mob_spawner; waypoint->type = FP_GameWaypointType_ClosestSide; @@ -1288,7 +1305,7 @@ void FP_EntityActionStateMachine(FP_Game *game, TELY_Audio *audio, TELY_Platform DQN_ASSERT(action->sprite.anim); // NOTE: Adding an attack_processed bool to make sure things only fire once - if (!entity->attack_processed && game->clock_ms >= midpoint_clock_ms) { + if (!entity->attack_processed && game->play.clock_ms >= midpoint_clock_ms) { // NOTE: Position the attack box Dqn_V2 dir_vector = {}; @@ -1322,9 +1339,9 @@ void FP_EntityActionStateMachine(FP_Game *game, TELY_Audio *audio, TELY_Platform } if (is_range_attack) { - Dqn_Rect entity_hit_box = FP_Game_CalcEntityWorldHitBox(game, entity->handle); - Dqn_V2 projectile_pos = entity_hit_box.pos + entity->attack_box_offset; - Dqn_V2 projectile_acceleration = FP_Game_MetersToPixelsV2(game, dir_vector * 0.25f); + Dqn_Rect entity_hit_box = FP_Game_CalcEntityWorldHitBox(game, entity->handle); + Dqn_V2 projectile_pos = entity_hit_box.pos + entity->attack_box_offset; + Dqn_V2 projectile_acceleration = FP_Game_MetersToPixelsV2(game->play, dir_vector * 0.25f); FP_Entity_CreatePhoneMessageProjectile(game, entity->handle, projectile_pos, @@ -1348,13 +1365,13 @@ void FP_Update(TELY_Platform *platform, FP_Game *game, TELY_PlatformInput *input { Dqn_Profiler_ZoneScopeWithIndex("FP_Update", FP_ProfileZone_FPUpdate); - game->update_counter++; - game->clock_ms = DQN_CAST(uint64_t)(platform->input.timer_s * 1000.f); + game->play.update_counter++; + game->play.clock_ms = DQN_CAST(uint64_t)(platform->input.timer_s * 1000.f); Dqn_ProfilerZone update_zone = Dqn_Profiler_BeginZoneWithIndex(DQN_STRING8("FP_Update: Entity loop"), FP_ProfileZone_FPUpdate_EntityLoop); - if (game->state == FP_GameState_Play) { + if (game->play.state == FP_GameState_Play) { Dqn_V2 dir_vector = {}; if (TELY_Platform_InputKeyIsReleased(input->mouse_left)) - game->clicked_entity = game->prev_active_entity; + game->play.clicked_entity = game->play.prev_active_entity; // NOTE: Keyboard movement input if (TELY_Platform_InputScanCodeIsDown(input, TELY_PlatformInputScanCode_W)) @@ -1382,27 +1399,27 @@ void FP_Update(TELY_Platform *platform, FP_Game *game, TELY_PlatformInput *input } if (TELY_Platform_InputScanCodeIsPressed(input, TELY_PlatformInputScanCode_Escape)) - game->clicked_entity = {}; + game->play.clicked_entity = {}; - if (game->clicked_entity.id) { + if (game->play.clicked_entity.id) { if (TELY_Platform_InputScanCodeIsPressed(input, TELY_PlatformInputScanCode_Delete)) - FP_Game_DeleteEntity(game, game->clicked_entity); + FP_Game_DeleteEntity(game, game->play.clicked_entity); // NOTE: Building selector Dqn_usize last_building_index = DQN_ARRAY_UCOUNT(PLACEABLE_BUILDINGS) - 1; if (TELY_Platform_InputScanCodeIsPressed(input, TELY_PlatformInputScanCode_Q)) { - if (game->build_mode_building_index <= 0) { - game->build_mode_building_index = last_building_index; + if (game->play.build_mode_building_index <= 0) { + game->play.build_mode_building_index = last_building_index; } else { - game->build_mode_building_index -= 1; + game->play.build_mode_building_index -= 1; } } if (TELY_Platform_InputScanCodeIsPressed(input, TELY_PlatformInputScanCode_E)) { - if (game->build_mode_building_index >= last_building_index) { - game->build_mode_building_index = 0; + if (game->play.build_mode_building_index >= last_building_index) { + game->play.build_mode_building_index = 0; } else { - game->build_mode_building_index += 1; + game->play.build_mode_building_index += 1; } } @@ -1410,15 +1427,11 @@ void FP_Update(TELY_Platform *platform, FP_Game *game, TELY_PlatformInput *input Dqn_f32 pan_speed = 5.f; if (TELY_Platform_InputScanCodeIsDown(input, TELY_PlatformInputScanCode_Space)) pan_speed *= 2.5f; - game->camera.world_pos += dir_vector * pan_speed; - + game->play.camera.world_pos += dir_vector * pan_speed; } - if (TELY_Platform_InputScanCodeIsPressed(input, TELY_PlatformInputScanCode_F1)) - game->debug_ui = !game->debug_ui; - // NOTE: Handle input ========================================================================== - for (FP_GameEntityIterator it = {}; FP_Game_DFSPostOrderWalkEntityTree(game, &it, game->root_entity); ) { + for (FP_GameEntityIterator it = {}; FP_Game_DFSPostOrderWalkEntityTree(game, &it, game->play.root_entity); ) { FP_GameEntity *entity = it.entity; entity->alive_time_s += PHYSICS_STEP; @@ -1427,7 +1440,7 @@ void FP_Update(TELY_Platform *platform, FP_Game *game, TELY_PlatformInput *input // NOTE: Move entity by keyboard and gamepad =============================================== Dqn_V2 acceleration_meters_per_s = entity->constant_acceleration_per_s; - if (game->clicked_entity == entity->handle) { + if (game->play.clicked_entity == entity->handle) { if (entity->flags & (FP_GameEntityFlag_MoveByKeyboard | FP_GameEntityFlag_MoveByGamepad)) { bool move_entity = true; switch (entity->type) { @@ -1489,7 +1502,7 @@ void FP_Update(TELY_Platform *platform, FP_Game *game, TELY_PlatformInput *input ? FP_GameEntityFaction_Foe : FP_GameEntityFaction_Friendly; - for (FP_GameEntityIterator defender_it = {}; FP_Game_DFSPostOrderWalkEntityTree(game, &defender_it, game->root_entity); ) { + for (FP_GameEntityIterator defender_it = {}; FP_Game_DFSPostOrderWalkEntityTree(game, &defender_it, game->play.root_entity); ) { FP_GameEntity *it_entity = defender_it.entity; Dqn_V2 pos = FP_Game_CalcEntityWorldPos(game, it_entity->handle); Dqn_f32 dist = Dqn_V2_LengthSq_V2x2(pos, entity_pos); @@ -1504,7 +1517,7 @@ void FP_Update(TELY_Platform *platform, FP_Game *game, TELY_PlatformInput *input } } - Dqn_f32 aggro_dist_threshold = FP_Game_MetersToPixelsNx1(game, 4.f); + Dqn_f32 aggro_dist_threshold = FP_Game_MetersToPixelsNx1(game->play, 4.f); if (closest_defender.dist_squared < DQN_SQUARED(aggro_dist_threshold)) { bool has_waypoint_to_defender = false; for (FP_SentinelListLink *link = nullptr; @@ -1526,7 +1539,7 @@ void FP_Update(TELY_Platform *platform, FP_Game *game, TELY_PlatformInput *input FP_SentinelListLink *link = FP_SentinelList_MakeBefore(&entity->waypoints, FP_SentinelList_Front(&entity->waypoints), - game->chunk_pool); + game->play.chunk_pool); FP_GameWaypoint *waypoint = &link->data; waypoint->entity = defender->handle; if (aggro_direction != FP_GameDirection_Count) { @@ -1540,7 +1553,7 @@ void FP_Update(TELY_Platform *platform, FP_Game *game, TELY_PlatformInput *input for (FP_SentinelListLink *link = nullptr; FP_SentinelList_Iterate(&entity->waypoints, &link); ) { FP_GameEntity *maybe_terry = FP_Game_GetEntity(game, link->data.entity); if (maybe_terry->type == FP_EntityType_Terry) { - link = FP_SentinelList_Erase(&entity->waypoints, link, game->chunk_pool); + link = FP_SentinelList_Erase(&entity->waypoints, link, game->play.chunk_pool); } } } @@ -1552,7 +1565,7 @@ void FP_Update(TELY_Platform *platform, FP_Game *game, TELY_PlatformInput *input closest_building.dist_squared = DQN_F32_MAX; closest_building.pos = Dqn_V2_InitNx1(DQN_F32_MAX); - for (FP_GameEntityIterator building_it = {}; FP_Game_DFSPostOrderWalkEntityTree(game, &building_it, game->root_entity); ) { + for (FP_GameEntityIterator building_it = {}; FP_Game_DFSPostOrderWalkEntityTree(game, &building_it, game->play.root_entity); ) { FP_GameEntity *it_entity = building_it.entity; if (it_entity->type != FP_EntityType_ClubTerry && it_entity->type != FP_EntityType_AirportTerry && @@ -1585,7 +1598,7 @@ void FP_Update(TELY_Platform *platform, FP_Game *game, TELY_PlatformInput *input } if (!FP_Game_IsNilEntityHandle(game, closest_building.entity) && - closest_building.dist_squared < DQN_SQUARED(FP_Game_MetersToPixelsNx1(game, 5.f))) { + closest_building.dist_squared < DQN_SQUARED(FP_Game_MetersToPixelsNx1(game->play, 5.f))) { bool has_waypoint_to_building = false; for (FP_SentinelListLink *link = nullptr; @@ -1598,7 +1611,7 @@ void FP_Update(TELY_Platform *platform, FP_Game *game, TELY_PlatformInput *input Dqn_V2 top_left = Dqn_Rect_TopLeft(hit_box); Dqn_V2 top_right = Dqn_Rect_TopRight(hit_box); - FP_SentinelListLink *link = FP_SentinelList_MakeBefore(&entity->waypoints, FP_SentinelList_Front(&entity->waypoints), game->chunk_pool); + FP_SentinelListLink *link = FP_SentinelList_MakeBefore(&entity->waypoints, FP_SentinelList_Front(&entity->waypoints), game->play.chunk_pool); FP_GameWaypoint *waypoint = &link->data; waypoint->entity = closest_building.entity; waypoint->type = FP_GameWaypointType_Side; @@ -1614,7 +1627,7 @@ void FP_Update(TELY_Platform *platform, FP_Game *game, TELY_PlatformInput *input direction_hit_count = hit_count; least_encountered_direction = DQN_CAST(FP_GameDirection)dir_index; } else if (hit_count == direction_hit_count) { - if (Dqn_PCG32_NextF32(&game->rng) >= 0.5f) { + if (Dqn_PCG32_NextF32(&game->play.rng) >= 0.5f) { direction_hit_count = hit_count; least_encountered_direction = DQN_CAST(FP_GameDirection)dir_index; } @@ -1629,7 +1642,7 @@ void FP_Update(TELY_Platform *platform, FP_Game *game, TELY_PlatformInput *input if (entity->flags & FP_GameEntityFlag_PointOfInterestHeart) { FP_GameFindClosestEntityResult closest_heart = FP_Game_FindClosestEntityWithType(game, entity->handle, FP_EntityType_Heart); - if (closest_heart.dist_squared < DQN_SQUARED(FP_Game_MetersToPixelsNx1(game, 4.f))) { + if (closest_heart.dist_squared < DQN_SQUARED(FP_Game_MetersToPixelsNx1(game->play, 4.f))) { bool has_waypoint_to = false; for (FP_SentinelListLink *link = nullptr; @@ -1642,7 +1655,7 @@ void FP_Update(TELY_Platform *platform, FP_Game *game, TELY_PlatformInput *input Dqn_V2 club_top_left = Dqn_Rect_TopLeft(club_hit_box); Dqn_V2 club_top_right = Dqn_Rect_TopRight(club_hit_box); - FP_SentinelListLink *link = FP_SentinelList_MakeBefore(&entity->waypoints, FP_SentinelList_Front(&entity->waypoints), game->chunk_pool); + FP_SentinelListLink *link = FP_SentinelList_MakeBefore(&entity->waypoints, FP_SentinelList_Front(&entity->waypoints), game->play.chunk_pool); FP_GameWaypoint *waypoint = &link->data; waypoint->entity = closest_heart.entity; waypoint->type = FP_GameWaypointType_ClosestSide; @@ -1655,7 +1668,7 @@ void FP_Update(TELY_Platform *platform, FP_Game *game, TELY_PlatformInput *input FP_GameWaypoint const *waypoint = &waypoint_link->data; FP_GameEntity *waypoint_entity = FP_Game_GetEntity(game, waypoint_link->data.entity); if (FP_Game_IsNilEntity(waypoint_entity)) { - FP_SentinelList_Erase(&entity->waypoints, waypoint_link, game->chunk_pool); + FP_SentinelList_Erase(&entity->waypoints, waypoint_link, game->play.chunk_pool); continue; } @@ -1669,7 +1682,7 @@ void FP_Update(TELY_Platform *platform, FP_Game *game, TELY_PlatformInput *input Dqn_f32 arrival_threshold = {}; switch (waypoint->arrive) { case FP_GameWaypointArrive_Default: { - arrival_threshold = FP_Game_MetersToPixelsNx1(game, 0.5f); + arrival_threshold = FP_Game_MetersToPixelsNx1(game->play, 0.5f); } break; case FP_GameWaypointArrive_WhenWithinEntitySize: { @@ -1719,8 +1732,8 @@ void FP_Update(TELY_Platform *platform, FP_Game *game, TELY_PlatformInput *input entity->flags |= FP_GameEntityFlag_OccupiedInBuilding; can_attack = false; - FP_SentinelList_Erase(&entity->waypoints, waypoint_link, game->chunk_pool); - FP_SentinelList_Add(&entity->buildings_visited, game->chunk_pool, building->handle); + FP_SentinelList_Erase(&entity->waypoints, waypoint_link, game->play.chunk_pool); + FP_SentinelList_Add(&entity->buildings_visited, game->play.chunk_pool, building->handle); } } @@ -1785,29 +1798,29 @@ void FP_Update(TELY_Platform *platform, FP_Game *game, TELY_PlatformInput *input // NOTE: Aggro makes the entity attack, we will exit here preserving the waypoint in the entity. break; } else { - FP_SentinelList_Erase(&entity->waypoints, waypoint_link, game->chunk_pool); + FP_SentinelList_Erase(&entity->waypoints, waypoint_link, game->play.chunk_pool); } } } // NOTE: Move entity by mouse ============================================================== - if (game->active_entity == entity->handle && entity->flags & FP_GameEntityFlag_MoveByMouse) { + if (game->play.active_entity == entity->handle && entity->flags & FP_GameEntityFlag_MoveByMouse) { entity->velocity = {}; acceleration_meters_per_s = {}; entity->local_pos += input->mouse_p_delta; } - if (game->clicked_entity == entity->handle) { - if (game->in_game_menu == FP_GameInGameMenu_Nil || game->in_game_menu == FP_GameInGameMenu_Build) { + if (game->play.clicked_entity == entity->handle) { + if (game->play.in_game_menu == FP_GameInGameMenu_Nil || game->play.in_game_menu == FP_GameInGameMenu_Build) { if (TELY_Platform_InputScanCodeIsPressed(input, TELY_PlatformInputScanCode_H)) - game->in_game_menu = DQN_CAST(FP_GameInGameMenu)(DQN_CAST(uint32_t)game->in_game_menu ^ FP_GameInGameMenu_Build); + game->play.in_game_menu = DQN_CAST(FP_GameInGameMenu)(DQN_CAST(uint32_t)game->play.in_game_menu ^ FP_GameInGameMenu_Build); } if (entity->flags & FP_GameEntityFlag_CameraTracking) - game->camera.world_pos = FP_Game_CalcEntityWorldPos(game, entity->handle) - Dqn_V2_InitV2I(platform->core.window_size) * .5f; + game->play.camera.world_pos = FP_Game_CalcEntityWorldPos(game, entity->handle) - Dqn_V2_InitV2I(platform->core.window_size) * .5f; - FP_GamePlaceableBuilding placeable_building = PLACEABLE_BUILDINGS[game->build_mode_building_index]; - game->build_mode_can_place_building = false; + FP_GamePlaceableBuilding placeable_building = PLACEABLE_BUILDINGS[game->play.build_mode_building_index]; + game->play.build_mode_can_place_building = false; uint8_t *inventory_count = nullptr; if (placeable_building.type == FP_EntityType_ChurchTerry) @@ -1820,12 +1833,12 @@ void FP_Update(TELY_Platform *platform, FP_Game *game, TELY_PlatformInput *input inventory_count = &entity->inventory.airports; bool have_building_inventory = inventory_count && (*inventory_count) > 0; - if (have_building_inventory && game->in_game_menu == FP_GameInGameMenu_Build) { + if (have_building_inventory && game->play.in_game_menu == FP_GameInGameMenu_Build) { Dqn_Rect dest_rect = FP_Game_GetBuildingPlacementRectForEntity(game, placeable_building, entity->handle); Dqn_V2 placement_pos = Dqn_Rect_Center(dest_rect); for (FP_GameEntityIterator zone_it = {}; - FP_Game_DFSPreOrderWalkEntityTree(game, &zone_it, game->root_entity); + FP_Game_DFSPreOrderWalkEntityTree(game, &zone_it, game->play.root_entity); ) { FP_GameEntity *zone = zone_it.entity; @@ -1837,7 +1850,7 @@ void FP_Update(TELY_Platform *platform, FP_Game *game, TELY_PlatformInput *input Dqn_Rect zone_hit_box = FP_Game_CalcEntityWorldHitBox(game, zone->handle); if (is_building) { if (Dqn_Rect_Intersects(zone_hit_box, dest_rect)) { - game->build_mode_can_place_building = false; + game->play.build_mode_can_place_building = false; break; } } @@ -1849,10 +1862,10 @@ void FP_Update(TELY_Platform *platform, FP_Game *game, TELY_PlatformInput *input zone_hit_box.size -= dest_rect.size; zone_hit_box.size = Dqn_V2_Max(zone_hit_box.size, Dqn_V2_Zero); - game->build_mode_can_place_building |= Dqn_Rect_ContainsPoint(zone_hit_box, placement_pos); + game->play.build_mode_can_place_building |= Dqn_Rect_ContainsPoint(zone_hit_box, placement_pos); } - if (game->build_mode_can_place_building && + if (game->play.build_mode_can_place_building && TELY_Platform_InputScanCodeIsPressed(input, TELY_PlatformInputScanCode_J)) { if (placeable_building.type == FP_EntityType_ClubTerry) { FP_Entity_CreateClubTerry(game, placement_pos, "Club Terry"); @@ -1871,8 +1884,8 @@ void FP_Update(TELY_Platform *platform, FP_Game *game, TELY_PlatformInput *input } if (!FP_Game_IsNilEntityHandle(game, entity->carried_monkey) && entity->type != FP_EntityType_MobSpawner) { - FP_GameFindClosestEntityResult closest_portal = FP_Game_FindClosestEntityWithType(game, entity->carried_monkey, game->mob_spawners.data, game->mob_spawners.size); - if (closest_portal.dist_squared < DQN_SQUARED(FP_Game_MetersToPixelsNx1(game, 1.f))) { + FP_GameFindClosestEntityResult closest_portal = FP_Game_FindClosestEntityWithType(game, entity->carried_monkey, game->play.mob_spawners.data, game->play.mob_spawners.size); + if (closest_portal.dist_squared < DQN_SQUARED(FP_Game_MetersToPixelsNx1(game->play, 1.f))) { FP_GameEntity *portal = FP_Game_GetEntity(game, closest_portal.entity); portal->carried_monkey = entity->carried_monkey; entity->carried_monkey = {}; @@ -1890,7 +1903,6 @@ void FP_Update(TELY_Platform *platform, FP_Game *game, TELY_PlatformInput *input entity->direction = acceleration_meters_per_s.y > 0.f ? FP_GameDirection_Down : FP_GameDirection_Up; } - // NOTE: Tick the state machine // NOTE: This can delete the entity! Take caution FP_GameEntityHandle entity_handle = entity->handle; @@ -1902,19 +1914,19 @@ void FP_Update(TELY_Platform *platform, FP_Game *game, TELY_PlatformInput *input // NOTE: If all enemies for the current wave have been spawned and the cooldown has elapsed // start the next wave. - if (game->enemies_spawned_this_wave >= game->enemies_per_wave && game->clock_ms >= game->wave_cooldown_timestamp_ms) { - game->enemies_per_wave = DQN_MAX(5 * DQN_CAST(uint32_t)game->mob_spawners.size, DQN_CAST(uint32_t)(game->enemies_per_wave * 1.5f)); - game->enemies_spawned_this_wave = 0; - game->current_wave++; + if (game->play.enemies_spawned_this_wave >= game->play.enemies_per_wave && game->play.clock_ms >= game->play.wave_cooldown_timestamp_ms) { + game->play.enemies_per_wave = DQN_MAX(5 * DQN_CAST(uint32_t)game->play.mob_spawners.size, DQN_CAST(uint32_t)(game->play.enemies_per_wave * 1.5f)); + game->play.enemies_spawned_this_wave = 0; + game->play.current_wave++; } } // NOTE: Update entity ========================================================================= - for (FP_GameEntityIterator it = {}; FP_Game_DFSPostOrderWalkEntityTree(game, &it, game->root_entity); ) { + for (FP_GameEntityIterator it = {}; FP_Game_DFSPostOrderWalkEntityTree(game, &it, game->play.root_entity); ) { FP_GameEntity *entity = it.entity; if (entity->flags & FP_GameEntityFlag_TTL) { - if (game->clock_ms >= entity->ttl_end_timestamp) { + if (game->play.clock_ms >= entity->ttl_end_timestamp) { FP_Game_DeleteEntity(game, entity->handle); continue; } @@ -1922,14 +1934,14 @@ void FP_Update(TELY_Platform *platform, FP_Game *game, TELY_PlatformInput *input // NOTE: Recover mobile data entity->terry_mobile_data_plan = - DQN_MIN(entity->terry_mobile_data_plan + DQN_CAST(Dqn_usize)(FP_TERRY_MOBILE_DATA_PER_RANGE_ATTACK * .125f * PHYSICS_STEP), + DQN_MIN(entity->terry_mobile_data_plan + DQN_CAST(Dqn_usize)(FP_TERRY_MOBILE_DATA_PER_RANGE_ATTACK * .25f * PHYSICS_STEP), entity->terry_mobile_data_plan_cap); // NOTE: Recover hp & stamina - entity->stamina = DQN_MIN(entity->stamina + 0.25f, entity->stamina_cap); + entity->stamina = DQN_MIN(entity->stamina + 1, entity->stamina_cap); - if (game->update_counter % 12 == 0) { - entity->hp = DQN_MIN(entity->hp + 0.75f, entity->hp_cap); + if (game->play.update_counter % 12 == 0) { + entity->hp = DQN_MIN(entity->hp + 1, entity->hp_cap); } // NOTE: Derive dynmamic bounding boxes ==================================================== @@ -1959,18 +1971,18 @@ void FP_Update(TELY_Platform *platform, FP_Game *game, TELY_PlatformInput *input for (FP_SentinelListLink *link = nullptr; FP_SentinelList_Iterate(&entity->spawn_list, &link); ) { FP_GameEntity *spawned_entity = FP_Game_GetEntity(game, link->data); if (FP_Game_IsNilEntity(spawned_entity)) // NOTE: Entity is dead remove it from the linked list - link = FP_SentinelList_Erase(&entity->spawn_list, link, game->chunk_pool); + link = FP_SentinelList_Erase(&entity->spawn_list, link, game->play.chunk_pool); } - if (game->enemies_spawned_this_wave < game->enemies_per_wave && entity->spawn_list.size < entity->spawn_cap) { // NOTE: Spawn new entities + if (game->play.enemies_spawned_this_wave < game->play.enemies_per_wave && entity->spawn_list.size < entity->spawn_cap) { // NOTE: Spawn new entities if (entity->action.state != FP_EntityMobSpawnerState_Shutdown && input->timer_s >= entity->next_spawn_timestamp_s) { - uint16_t hp_adjustment = DQN_CAST(uint16_t)game->current_wave; + uint16_t hp_adjustment = DQN_CAST(uint16_t)game->play.current_wave; entity->next_spawn_timestamp_s = DQN_CAST(uint64_t)(input->timer_s + 5.f); Dqn_V2 entity_world_pos = FP_Game_CalcEntityWorldPos(game, entity->handle); - FP_SentinelListLink *link = FP_SentinelList_Make(&entity->spawn_list, game->chunk_pool); + FP_SentinelListLink *link = FP_SentinelList_Make(&entity->spawn_list, game->play.chunk_pool); - Dqn_f32 mob_choice = Dqn_PCG32_NextF32(&game->rng); + Dqn_f32 mob_choice = Dqn_PCG32_NextF32(&game->play.rng); if (mob_choice <= 0.33f) link->data = FP_Entity_CreateClinger(game, entity_world_pos, "Clinger"); else if (mob_choice <= 0.66f) @@ -1980,7 +1992,7 @@ void FP_Update(TELY_Platform *platform, FP_Game *game, TELY_PlatformInput *input // NOTE: Setup the mob with waypoints FP_GameEntity *mob = FP_Game_GetEntity(game, link->data); - mob->waypoints = FP_SentinelList_Init(game->chunk_pool); + mob->waypoints = FP_SentinelList_Init(game->play.chunk_pool); mob->flags |= FP_GameEntityFlag_Aggros; mob->flags |= FP_GameEntityFlag_RespondsToBuildings; mob->hp_cap += hp_adjustment; @@ -1991,15 +2003,15 @@ void FP_Update(TELY_Platform *platform, FP_Game *game, TELY_PlatformInput *input continue; // NOTE: Add the waypoint - FP_SentinelListLink *waypoint = FP_SentinelList_Make(&mob->waypoints, game->chunk_pool); + FP_SentinelListLink *waypoint = FP_SentinelList_Make(&mob->waypoints, game->play.chunk_pool); waypoint->data.entity = waypoint_entity->handle; waypoint->data.arrive = FP_GameWaypointArrive_WhenWithinEntitySize; waypoint->data.value = 1.5f; } - game->enemies_spawned_this_wave++; - if (game->enemies_spawned_this_wave >= game->enemies_per_wave) - game->wave_cooldown_timestamp_ms = game->clock_ms + 30'000; + game->play.enemies_spawned_this_wave++; + if (game->play.enemies_spawned_this_wave >= game->play.enemies_per_wave) + game->play.wave_cooldown_timestamp_ms = game->play.clock_ms + 30'000; } } } @@ -2021,7 +2033,7 @@ void FP_Update(TELY_Platform *platform, FP_Game *game, TELY_PlatformInput *input ? FP_GameEntityFaction_Foe : FP_GameEntityFaction_Friendly; - for (FP_GameEntityIterator defender_it = {}; FP_Game_DFSPostOrderWalkEntityTree(game, &defender_it, game->root_entity); ) { + for (FP_GameEntityIterator defender_it = {}; FP_Game_DFSPostOrderWalkEntityTree(game, &defender_it, game->play.root_entity); ) { FP_GameEntity *defender = defender_it.entity; if (defender->handle == attacker->handle) continue; @@ -2041,15 +2053,19 @@ void FP_Update(TELY_Platform *platform, FP_Game *game, TELY_PlatformInput *input continue; // NOTE: Do HP ========================================================================= + if (game->play.player == defender->handle) { + if (game->play.god_mode) + continue; + } + defender->hp = defender->hp >= FP_DEFAULT_DAMAGE ? defender->hp - FP_DEFAULT_DAMAGE : 0; if (defender->hp <= 0) { + defender->is_dying = true; FP_GameEntity *coin_receiver = FP_Game_GetEntity(game, attacker->projectile_owner); if (FP_Game_IsNilEntity(coin_receiver)) coin_receiver = attacker; - if (!defender->is_dying) - coin_receiver->coins += 1; - defender->is_dying = true; + coin_receiver->coins += 1; } // NOTE: Kickback ====================================================================== @@ -2068,17 +2084,27 @@ void FP_Update(TELY_Platform *platform, FP_Game *game, TELY_PlatformInput *input } } - if (!FP_Game_IsNilEntityHandle(game, game->clicked_entity)) { + bool all_portals_shutdown = true; + for (FP_GameEntityHandle portal_handle : game->play.mob_spawners) { + FP_GameEntity *portal = FP_Game_GetEntity(game, portal_handle); + all_portals_shutdown &= portal->action.state == FP_EntityMobSpawnerState_Shutdown; + } + + if (all_portals_shutdown) { + game->play.state = FP_GameState_WinGame; + } + + if (!FP_Game_IsNilEntityHandle(game, game->play.clicked_entity)) { TELY_AssetSpriteAnimation *sprite_anim = TELY_Asset_GetSpriteAnimation(&game->atlas_sprite_sheet, g_anim_names.map); Dqn_Rect sprite_rect = game->atlas_sprite_sheet.rects.data[sprite_anim->index]; const Dqn_usize target_width = 1800; const Dqn_usize target_height = 1046; - game->camera.world_pos.x = DQN_MIN(game->camera.world_pos.x, game->map->local_hit_box_size.w * +0.5f - target_width); - game->camera.world_pos.x = DQN_MAX(game->camera.world_pos.x, game->map->local_hit_box_size.w * -0.5f); - game->camera.world_pos.y = DQN_MAX(game->camera.world_pos.y, game->map->local_hit_box_size.h * -0.5f); - game->camera.world_pos.y = DQN_MIN(game->camera.world_pos.y, game->map->local_hit_box_size.h * +0.5f - target_height); + game->play.camera.world_pos.x = DQN_MIN(game->play.camera.world_pos.x, game->play.map->local_hit_box_size.w * +0.5f - target_width); + game->play.camera.world_pos.x = DQN_MAX(game->play.camera.world_pos.x, game->play.map->local_hit_box_size.w * -0.5f); + game->play.camera.world_pos.y = DQN_MAX(game->play.camera.world_pos.y, game->play.map->local_hit_box_size.h * -0.5f); + game->play.camera.world_pos.y = DQN_MIN(game->play.camera.world_pos.y, game->play.map->local_hit_box_size.h * +0.5f - target_height); } Dqn_Profiler_EndZone(update_zone); @@ -2098,22 +2124,22 @@ void FP_Render(FP_Game *game, TELY_Platform *platform, TELY_Renderer *renderer) TELY_RFui_PushFont(rfui, game->jetbrains_mono_font); TELY_RFui_PushLabelColourV4(rfui, TELY_COLOUR_BLACK_MIDNIGHT_V4); - Dqn_M2x3 model_view = FP_Game_CameraModelViewM2x3(game->camera, platform); + Dqn_M2x3 model_view = FP_Game_CameraModelViewM2x3(game->play.camera, platform); TELY_Render_PushTransform(renderer, model_view); - Dqn_V2 world_mouse_p = input->mouse_p + game->camera.world_pos; + Dqn_V2 world_mouse_p = input->mouse_p + game->play.camera.world_pos; // NOTE: Draw tiles ============================================================================ - Dqn_usize tile_count_x = DQN_CAST(Dqn_usize)(platform->core.window_size.w / game->tile_size); - Dqn_usize tile_count_y = DQN_CAST(Dqn_usize)(platform->core.window_size.h / game->tile_size); + Dqn_usize tile_count_x = DQN_CAST(Dqn_usize)(platform->core.window_size.w / game->play.tile_size); + Dqn_usize tile_count_y = DQN_CAST(Dqn_usize)(platform->core.window_size.h / game->play.tile_size); for (Dqn_usize x = 0; x < tile_count_x; x++) { - Dqn_V2 start = Dqn_V2_InitNx2((x + 1) * game->tile_size, 0); + Dqn_V2 start = Dqn_V2_InitNx2((x + 1) * game->play.tile_size, 0); Dqn_V2 end = Dqn_V2_InitNx2(start.x, platform->core.window_size.h); TELY_Render_LineColourV4(renderer, start, end, TELY_Colour_V4Alpha(TELY_COLOUR_WHITE_V4, .25f), 1.f); } for (Dqn_usize y = 0; y < tile_count_y; y++) { - Dqn_V2 start = Dqn_V2_InitNx2(0, (y + 1) * game->tile_size); + Dqn_V2 start = Dqn_V2_InitNx2(0, (y + 1) * game->play.tile_size); Dqn_V2 end = Dqn_V2_InitNx2(platform->core.window_size.w, start.y); TELY_Render_LineColourV4(renderer, start, end, TELY_Colour_V4Alpha(TELY_COLOUR_WHITE_V4, .25f), 1.f); } @@ -2121,7 +2147,7 @@ void FP_Render(FP_Game *game, TELY_Platform *platform, TELY_Renderer *renderer) TELY_Render_PushColourV4(renderer, TELY_COLOUR_BLACK_V4); // NOTE: Draw entities ========================================================================= - for (FP_GameEntityIterator it = {}; FP_Game_DFSPostOrderWalkEntityTree(game, &it, game->root_entity); ) { + for (FP_GameEntityIterator it = {}; FP_Game_DFSPostOrderWalkEntityTree(game, &it, game->play.root_entity); ) { FP_GameEntity *entity = it.entity; if (entity->flags & FP_GameEntityFlag_OccupiedInBuilding) continue; @@ -2158,7 +2184,7 @@ void FP_Render(FP_Game *game, TELY_Platform *platform, TELY_Renderer *renderer) if (entity->action.sprite.anim) { FP_GameEntityAction const *action = &entity->action; TELY_AssetAnimatedSprite const sprite = action->sprite; - uint64_t const elapsed_ms = game->clock_ms - action->started_at_clock_ms; + uint64_t const elapsed_ms = game->play.clock_ms - action->started_at_clock_ms; uint16_t const raw_anim_frame = DQN_CAST(uint16_t)(elapsed_ms / sprite.anim->ms_per_frame); DQN_ASSERTF(sprite.anim->count, "We will modulo by 0 or overflow to UINT64_MAX"); @@ -2191,7 +2217,7 @@ void FP_Render(FP_Game *game, TELY_Platform *platform, TELY_Renderer *renderer) } break; } - Dqn_f32 sprite_in_meters = FP_Game_PixelsToMetersNx1(game, src_rect.size.y); + Dqn_f32 sprite_in_meters = FP_Game_PixelsToMetersNx1(game->play, src_rect.size.y); Dqn_f32 size_scale = render_data.height.meters / sprite_in_meters; Dqn_Rect dest_rect = {}; @@ -2221,7 +2247,7 @@ void FP_Render(FP_Game *game, TELY_Platform *platform, TELY_Renderer *renderer) DQN_FOR_UINDEX(anim_index, entity->extra_cosmetic_anims.size) { FP_GameRenderSprite *sprite = entity->extra_cosmetic_anims.data + anim_index; - uint64_t elapsed_ms = game->clock_ms - sprite->started_at_clock_ms; + uint64_t elapsed_ms = game->play.clock_ms - sprite->started_at_clock_ms; uint16_t raw_anim_frame = DQN_CAST(uint16_t)(elapsed_ms / sprite->asset.anim->ms_per_frame); if (raw_anim_frame > sprite->asset.anim->count && !sprite->loop) { anim_index = Dqn_FArray_EraseRange(&entity->extra_cosmetic_anims, anim_index, 1, Dqn_ArrayErase_Unstable).it_index; @@ -2247,7 +2273,7 @@ void FP_Render(FP_Game *game, TELY_Platform *platform, TELY_Renderer *renderer) } break; } - Dqn_f32 sprite_in_meters = FP_Game_PixelsToMetersNx1(game, src_rect.size.y); + Dqn_f32 sprite_in_meters = FP_Game_PixelsToMetersNx1(game->play, src_rect.size.y); Dqn_f32 size_scale = sprite->height.meters / sprite_in_meters; Dqn_Rect dest_rect = {}; @@ -2279,7 +2305,7 @@ void FP_Render(FP_Game *game, TELY_Platform *platform, TELY_Renderer *renderer) entity->type == FP_EntityType_ChurchTerry && action->state == FP_EntityChurchTerryState_ConvertPatron; if (draw_timer) { Dqn_f32 duration = action->end_at_clock_ms - DQN_CAST(Dqn_f32)action->started_at_clock_ms; - Dqn_f32 elapsed = DQN_CAST(Dqn_f32)(game->clock_ms - action->started_at_clock_ms); + Dqn_f32 elapsed = DQN_CAST(Dqn_f32)(game->play.clock_ms - action->started_at_clock_ms); Dqn_f32 t01 = DQN_MIN(1.f, elapsed / duration); Dqn_Rect rect = {}; rect.pos = Dqn_Rect_InterpolatedPoint(world_hit_box, Dqn_V2_InitNx2(0, -0.3f)); @@ -2288,12 +2314,12 @@ void FP_Render(FP_Game *game, TELY_Platform *platform, TELY_Renderer *renderer) } } - if (game->in_game_menu == FP_GameInGameMenu_Build) { + if (game->play.in_game_menu == FP_GameInGameMenu_Build) { if (entity->flags & FP_GameEntityFlag_BuildZone) TELY_Render_RectColourV4(renderer, world_hit_box, TELY_RenderShapeMode_Fill, TELY_Colour_V4Alpha(TELY_COLOUR_BLUE_CADET_V4, 0.5f)); } - if (game->debug_ui) { + if (game->play.debug_ui) { // NOTE: Render waypoint entities ====================================================== if (entity->type == FP_EntityType_MobSpawner) { Dqn_V2 start = world_pos; @@ -2334,7 +2360,7 @@ void FP_Render(FP_Game *game, TELY_Platform *platform, TELY_Renderer *renderer) TELY_Render_CircleColourV4(renderer, world_pos, 4.f, TELY_RenderShapeMode_Fill, TELY_COLOUR_RED_TOMATO_V4); // NOTE: Render hot/active entity ========================================================== - if (game->clicked_entity == entity->handle) { + if (game->play.clicked_entity == entity->handle) { TELY_Render_RectColourV4(renderer, world_hit_box, TELY_RenderShapeMode_Line, TELY_COLOUR_WHITE_PALE_GOLDENROD_V4); // NOTE: Draw the waypoints that the entity is moving along @@ -2344,14 +2370,14 @@ void FP_Render(FP_Game *game, TELY_Platform *platform, TELY_Renderer *renderer) TELY_Render_LineColourV4(renderer, start, end, TELY_COLOUR_WHITE_PALE_GOLDENROD_V4, 2.f); start = end; } - } else if (game->hot_entity == entity->handle || (entity->flags & FP_GameEntityFlag_DrawHitBox)) { - Dqn_V4 hot_colour = game->hot_entity == entity->handle ? TELY_COLOUR_RED_TOMATO_V4 : TELY_Colour_V4Alpha(TELY_COLOUR_YELLOW_SANDY_V4, .5f); + } else if (game->play.hot_entity == entity->handle || (entity->flags & FP_GameEntityFlag_DrawHitBox)) { + Dqn_V4 hot_colour = game->play.hot_entity == entity->handle ? TELY_COLOUR_RED_TOMATO_V4 : TELY_Colour_V4Alpha(TELY_COLOUR_YELLOW_SANDY_V4, .5f); TELY_Render_RectColourV4(renderer, world_hit_box, TELY_RenderShapeMode_Line, hot_colour); } - if (game->hot_entity == entity->handle) { + if (game->play.hot_entity == entity->handle) { if (entity->name.size) { - Dqn_V2I player_tile = Dqn_V2I_InitNx2(world_pos.x / game->tile_size, world_pos.y / game->tile_size); + Dqn_V2I player_tile = Dqn_V2I_InitNx2(world_pos.x / game->play.tile_size, world_pos.y / game->play.tile_size); Dqn_f32 line_height = TELY_Render_FontHeight(renderer, &platform->assets); Dqn_V2 draw_p = world_mouse_p; TELY_Render_TextF(renderer, draw_p, Dqn_V2_InitNx2(0.f, 1), "%.*s", DQN_STRING_FMT(entity->name)); draw_p.y += line_height; @@ -2372,7 +2398,7 @@ void FP_Render(FP_Game *game, TELY_Platform *platform, TELY_Renderer *renderer) } } - if (game->state == FP_GameState_IntroScreen) { + if (game->play.state == FP_GameState_IntroScreen || game->play.state == FP_GameState_WinGame) { TELY_Render_PushTransform(renderer, Dqn_M2x3_Identity()); DQN_DEFER { TELY_Render_PopTransform(renderer); }; TELY_Render_RectColourV4( @@ -2387,21 +2413,34 @@ void FP_Render(FP_Game *game, TELY_Platform *platform, TELY_Renderer *renderer) Dqn_V2 draw_p = Dqn_V2_InitNx2(min_inset, min_inset); TELY_Render_PushColourV4(renderer, TELY_COLOUR_WHITE_V4); - TELY_Render_PushFont(renderer, game->inter_regular_font_large); - TELY_Render_TextF(renderer, draw_p, Dqn_V2_Zero, "Feely Pona"); draw_p.y += TELY_Render_FontHeight(renderer, assets); - TELY_Render_PopFont(renderer); + if (game->play.state == FP_GameState_IntroScreen) { + TELY_Render_PushFont(renderer, game->inter_regular_font_large); + TELY_Render_TextF(renderer, draw_p, Dqn_V2_Zero, "Feely Pona"); draw_p.y += TELY_Render_FontHeight(renderer, assets); + TELY_Render_PopFont(renderer); - TELY_Render_PushFont(renderer, game->inter_regular_font); - TELY_Render_TextF(renderer, draw_p, Dqn_V2_Zero, "Press enter to start"); draw_p.y += TELY_Render_FontHeight(renderer, assets); - TELY_Render_PopFont(renderer); - TELY_Render_PopColourV4(renderer); + TELY_Render_PushFont(renderer, game->inter_regular_font); + TELY_Render_TextF(renderer, draw_p, Dqn_V2_Zero, "Press enter to start"); draw_p.y += TELY_Render_FontHeight(renderer, assets); + TELY_Render_PopFont(renderer); + TELY_Render_PopColourV4(renderer); + } else { + TELY_Render_PushFont(renderer, game->inter_regular_font_large); + TELY_Render_TextF(renderer, draw_p, Dqn_V2_Zero, "Terry has been saved"); draw_p.y += TELY_Render_FontHeight(renderer, assets); + TELY_Render_TextF(renderer, draw_p, Dqn_V2_Zero, "from his terrible calamity"); draw_p.y += TELY_Render_FontHeight(renderer, assets); + TELY_Render_PopFont(renderer); + + TELY_Render_PushFont(renderer, game->inter_regular_font); + TELY_Render_TextF(renderer, draw_p, Dqn_V2_Zero, "He lives for yet another day and another love"); draw_p.y += TELY_Render_FontHeight(renderer, assets); + TELY_Render_TextF(renderer, draw_p, Dqn_V2_Zero, "Press enter to restart"); draw_p.y += TELY_Render_FontHeight(renderer, assets); + TELY_Render_PopFont(renderer); + TELY_Render_PopColourV4(renderer); + } if (TELY_Platform_InputScanCodeIsPressed(input, TELY_PlatformInputScanCode_Return)) - game->state = FP_GameState_Play; + game->play.state = FP_GameState_Play; } else { // NOTE: Render the merchant menus ========================================= - FP_GameEntity *player = FP_Game_GetEntity(game, game->player); - Dqn_V2 player_pos = FP_Game_CalcEntityWorldPos(game, game->player); + FP_GameEntity *player = FP_Game_GetEntity(game, game->play.player); + Dqn_V2 player_pos = FP_Game_CalcEntityWorldPos(game, game->play.player); { FP_GameInventory *invent = &player->inventory; struct FP_MerchantToMenuMapping { @@ -2413,10 +2452,10 @@ void FP_Render(FP_Game *game, TELY_Platform *platform, TELY_Renderer *renderer) uint32_t *building_base_price; uint32_t *upgrade_base_price; } merchants[] = { - {game->merchant_terry, g_anim_names.merchant_terry_menu, g_anim_names.club_terry_dark, Dqn_V2_InitNx2(0.015f, +0.04f), &invent->clubs, &invent->clubs_base_price, &invent->health_base_price}, - {game->merchant_graveyard, g_anim_names.merchant_graveyard_menu, g_anim_names.church_terry_dark, Dqn_V2_InitNx2(0.04f, -0.15f), &invent->churchs, &invent->churchs_base_price, &invent->stamina_base_price}, - {game->merchant_gym, g_anim_names.merchant_gym_menu, g_anim_names.kennel_terry, Dqn_V2_InitNx2(0, +0), &invent->kennels, &invent->kennels_base_price, &invent->attack_base_price}, - {game->merchant_phone_company, g_anim_names.merchant_phone_company_menu, g_anim_names.airport_terry, Dqn_V2_InitNx2(0, -0.1f), &invent->airports, &invent->airports_base_price, &invent->mobile_plan_base_price}, + {game->play.merchant_terry, g_anim_names.merchant_terry_menu, g_anim_names.club_terry_dark, Dqn_V2_InitNx2(0.015f, +0.04f), &invent->clubs, &invent->clubs_base_price, &invent->health_base_price}, + {game->play.merchant_graveyard, g_anim_names.merchant_graveyard_menu, g_anim_names.church_terry_dark, Dqn_V2_InitNx2(0.04f, -0.15f), &invent->churchs, &invent->churchs_base_price, &invent->stamina_base_price}, + {game->play.merchant_gym, g_anim_names.merchant_gym_menu, g_anim_names.kennel_terry, Dqn_V2_InitNx2(0, +0), &invent->kennels, &invent->kennels_base_price, &invent->attack_base_price}, + {game->play.merchant_phone_company, g_anim_names.merchant_phone_company_menu, g_anim_names.airport_terry, Dqn_V2_InitNx2(0, -0.1f), &invent->airports, &invent->airports_base_price, &invent->mobile_plan_base_price}, }; bool activated_merchant = false; @@ -2425,20 +2464,20 @@ void FP_Render(FP_Game *game, TELY_Platform *platform, TELY_Renderer *renderer) Dqn_V2 world_pos = FP_Game_CalcEntityWorldPos(game, merchant_handle); Dqn_f32 dist_squared = Dqn_V2_LengthSq_V2x2(world_pos, player_pos); - if (dist_squared > DQN_SQUARED(FP_Game_MetersToPixelsNx1(game, 4))) + if (dist_squared > DQN_SQUARED(FP_Game_MetersToPixelsNx1(game->play, 4))) continue; // NOTE: Render animated merchant menu ============================= activated_merchant = true; Dqn_Rect merchant_menu_rect = {}; { - FP_GameRenderSprite *sprite = &game->player_merchant_menu; + FP_GameRenderSprite *sprite = &game->play.player_merchant_menu; if (!sprite->asset.anim || sprite->asset.anim->label != mapping.menu_anim) { sprite->asset = TELY_Asset_MakeAnimatedSprite(&game->atlas_sprite_sheet, mapping.menu_anim, TELY_AssetFlip_No); - sprite->started_at_clock_ms = game->clock_ms; + sprite->started_at_clock_ms = game->play.clock_ms; } - uint64_t elapsed_ms = game->clock_ms - sprite->started_at_clock_ms; + uint64_t elapsed_ms = game->play.clock_ms - sprite->started_at_clock_ms; uint16_t raw_anim_frame = DQN_CAST(uint16_t)(elapsed_ms / sprite->asset.anim->ms_per_frame); uint16_t anim_frame = raw_anim_frame % sprite->asset.anim->count; @@ -2477,13 +2516,13 @@ void FP_Render(FP_Game *game, TELY_Platform *platform, TELY_Renderer *renderer) if (have_enough_coins) { if (TELY_Platform_InputScanCodeIsPressed(input, key)) { - game->player_trigger_purchase_building_timestamp = game->clock_ms + buy_duration_ms; + game->play.player_trigger_purchase_building_timestamp = game->play.clock_ms + buy_duration_ms; } else if (TELY_Platform_InputScanCodeIsDown(input, key)) { trigger_buy_anim = true; - if (game->clock_ms > game->player_trigger_purchase_building_timestamp) - game->player_trigger_purchase_building_timestamp = game->clock_ms; + if (game->play.clock_ms > game->play.player_trigger_purchase_building_timestamp) + game->play.player_trigger_purchase_building_timestamp = game->play.clock_ms; } else if (TELY_Platform_InputScanCodeIsReleased(input, key)) { - if (game->clock_ms > game->player_trigger_purchase_building_timestamp) { + if (game->play.clock_ms > game->play.player_trigger_purchase_building_timestamp) { if (mapping.inventory_count) { player->coins -= *mapping.building_base_price; *mapping.building_base_price *= 2; @@ -2497,13 +2536,13 @@ void FP_Render(FP_Game *game, TELY_Platform *platform, TELY_Renderer *renderer) (*mapping.inventory_count)++; } } else { - game->player_trigger_purchase_building_timestamp = UINT64_MAX; + game->play.player_trigger_purchase_building_timestamp = UINT64_MAX; } } if (trigger_buy_anim) { - uint64_t start_buy_time = game->player_trigger_purchase_building_timestamp - buy_duration_ms; - uint64_t elapsed_time = game->clock_ms - start_buy_time; + uint64_t start_buy_time = game->play.player_trigger_purchase_building_timestamp - buy_duration_ms; + uint64_t elapsed_time = game->play.clock_ms - start_buy_time; Dqn_f32 buy_t = DQN_MIN(elapsed_time / DQN_CAST(Dqn_f32)buy_duration_ms, 1.f); Dqn_Rect buy_lerp_rect = {}; @@ -2563,34 +2602,34 @@ void FP_Render(FP_Game *game, TELY_Platform *platform, TELY_Renderer *renderer) if (have_enough_coins) { if (TELY_Platform_InputScanCodeIsPressed(input, key)) { - game->player_trigger_purchase_upgrade_timestamp = game->clock_ms + buy_duration_ms; + game->play.player_trigger_purchase_upgrade_timestamp = game->play.clock_ms + buy_duration_ms; } else if (TELY_Platform_InputScanCodeIsDown(input, key)) { trigger_buy_anim = true; - if (game->clock_ms > game->player_trigger_purchase_upgrade_timestamp) - game->player_trigger_purchase_upgrade_timestamp = game->clock_ms; + if (game->play.clock_ms > game->play.player_trigger_purchase_upgrade_timestamp) + game->play.player_trigger_purchase_upgrade_timestamp = game->play.clock_ms; } else if (TELY_Platform_InputScanCodeIsReleased(input, key)) { - if (game->clock_ms > game->player_trigger_purchase_upgrade_timestamp) { + if (game->play.clock_ms > game->play.player_trigger_purchase_upgrade_timestamp) { player->coins -= *mapping.upgrade_base_price; *mapping.upgrade_base_price *= 2; - if (mapping.merchant == game->merchant_terry) { + if (mapping.merchant == game->play.merchant_terry) { // TODO(doyle): Attack damage? Or increase attack range? - } else if (mapping.merchant == game->merchant_graveyard) { + } else if (mapping.merchant == game->play.merchant_graveyard) { player->stamina_cap += DQN_CAST(uint16_t)(FP_TERRY_DASH_STAMINA_COST * .5f); - } else if (mapping.merchant == game->merchant_gym) { + } else if (mapping.merchant == game->play.merchant_gym) { player->hp_cap += FP_DEFAULT_DAMAGE; player->hp = player->hp_cap; - } else if (mapping.merchant == game->merchant_phone_company) { + } else if (mapping.merchant == game->play.merchant_phone_company) { player->terry_mobile_data_plan_cap += DQN_KILOBYTES(1); } } else { - game->player_trigger_purchase_upgrade_timestamp = UINT64_MAX; + game->play.player_trigger_purchase_upgrade_timestamp = UINT64_MAX; } } if (trigger_buy_anim) { - uint64_t start_buy_time = game->player_trigger_purchase_upgrade_timestamp - buy_duration_ms; - uint64_t elapsed_time = game->clock_ms - start_buy_time; + uint64_t start_buy_time = game->play.player_trigger_purchase_upgrade_timestamp - buy_duration_ms; + uint64_t elapsed_time = game->play.clock_ms - start_buy_time; Dqn_f32 buy_t = DQN_MIN(elapsed_time / DQN_CAST(Dqn_f32)buy_duration_ms, 1.f); Dqn_Rect buy_lerp_rect = {}; @@ -2625,10 +2664,10 @@ void FP_Render(FP_Game *game, TELY_Platform *platform, TELY_Renderer *renderer) } if (activated_merchant) { - game->in_game_menu = FP_GameInGameMenu_Merchant; + game->play.in_game_menu = FP_GameInGameMenu_Merchant; } else { - if (game->in_game_menu == FP_GameInGameMenu_Merchant) { - game->in_game_menu = FP_GameInGameMenu_Nil; + if (game->play.in_game_menu == FP_GameInGameMenu_Merchant) { + game->play.in_game_menu = FP_GameInGameMenu_Nil; } } } @@ -2864,9 +2903,9 @@ void FP_Render(FP_Game *game, TELY_Platform *platform, TELY_Renderer *renderer) DQN_DEFER { TELY_Render_PopFont(renderer); }; uint64_t time_until_next_wave_ms = 0; - if (game->enemies_spawned_this_wave >= game->enemies_per_wave) { - if (game->wave_cooldown_timestamp_ms > game->clock_ms) { - time_until_next_wave_ms = game->wave_cooldown_timestamp_ms - game->clock_ms; + if (game->play.enemies_spawned_this_wave >= game->play.enemies_per_wave) { + if (game->play.wave_cooldown_timestamp_ms > game->play.clock_ms) { + time_until_next_wave_ms = game->play.wave_cooldown_timestamp_ms - game->play.clock_ms; } } @@ -2876,21 +2915,21 @@ void FP_Render(FP_Game *game, TELY_Platform *platform, TELY_Renderer *renderer) Dqn_V2_InitNx2(mid_x, player_avatar_rect.pos.y), Dqn_V2_InitNx1(0.5f), "%.1fs remaining until Wave %u!", - (time_until_next_wave_ms / 1000.f), game->current_wave + 1); + (time_until_next_wave_ms / 1000.f), game->play.current_wave + 1); } else { - TELY_Render_TextF(renderer, Dqn_V2_InitNx2(mid_x, player_avatar_rect.pos.y), Dqn_V2_InitNx1(0.5f), "Wave %u", game->current_wave); + TELY_Render_TextF(renderer, Dqn_V2_InitNx2(mid_x, player_avatar_rect.pos.y), Dqn_V2_InitNx1(0.5f), "Wave %u", game->play.current_wave); } } - if (!FP_Game_IsNilEntityHandle(game, game->clicked_entity)) { + if (!FP_Game_IsNilEntityHandle(game, game->play.clicked_entity)) { // NOTE: Render building blueprint ========================================================= - if (game->in_game_menu == FP_GameInGameMenu_Build) { - FP_GameEntity *entity = FP_Game_GetEntity(game, game->clicked_entity); - FP_GamePlaceableBuilding placeable_building = PLACEABLE_BUILDINGS[game->build_mode_building_index]; + if (game->play.in_game_menu == FP_GameInGameMenu_Build) { + FP_GameEntity *entity = FP_Game_GetEntity(game, game->play.clicked_entity); + FP_GamePlaceableBuilding placeable_building = PLACEABLE_BUILDINGS[game->play.build_mode_building_index]; FP_EntityRenderData render_data = FP_Entity_GetRenderData(game, placeable_building.type, placeable_building.state, entity->direction); Dqn_Rect dest_rect = FP_Game_GetBuildingPlacementRectForEntity(game, placeable_building, entity->handle); - Dqn_V4 colour = game->build_mode_can_place_building ? + Dqn_V4 colour = game->play.build_mode_can_place_building ? TELY_Colour_V4Alpha(TELY_COLOUR_WHITE_V4, 0.5f) : TELY_Colour_V4Alpha(TELY_COLOUR_RED_V4, 0.5f); @@ -2908,7 +2947,7 @@ void FP_Render(FP_Game *game, TELY_Platform *platform, TELY_Renderer *renderer) { TELY_Render_PushTransform(renderer, Dqn_M2x3_Identity()); DQN_DEFER { TELY_Render_PopTransform(renderer); }; - game->build_mode_building_index = DQN_CLAMP(game->build_mode_building_index, 0, DQN_ARRAY_UCOUNT(PLACEABLE_BUILDINGS) - 1); + game->play.build_mode_building_index = DQN_CLAMP(game->play.build_mode_building_index, 0, DQN_ARRAY_UCOUNT(PLACEABLE_BUILDINGS) - 1); Dqn_f32 building_ui_size = 64.f; Dqn_f32 padding = 10.f; @@ -2923,7 +2962,7 @@ void FP_Render(FP_Game *game, TELY_Platform *platform, TELY_Renderer *renderer) Dqn_V4 texture_colour = TELY_Colour_V4Alpha(TELY_COLOUR_WHITE_V4, .5f); Dqn_V4 outline_colour = TELY_COLOUR_WHITE_PALE_GOLDENROD_V4; - if (game->build_mode_building_index == building_index) { + if (game->play.build_mode_building_index == building_index) { outline_colour = TELY_COLOUR_RED_TOMATO_V4; texture_colour.a = 1.f; } @@ -2963,13 +3002,13 @@ void FP_Render(FP_Game *game, TELY_Platform *platform, TELY_Renderer *renderer) Dqn_V2 screen_size = Dqn_V2_InitNx2(platform->core.window_size.w, platform->core.window_size.h); Dqn_f32 scanline_gap = 4.0f; Dqn_f32 scanline_thickness = 3.0f; - FP_GameRenderCameraFollowScanlines(renderer, screen_size, game->camera.world_pos, scanline_gap, scanline_thickness); + FP_GameRenderCameraFollowScanlines(renderer, screen_size, game->play.camera.world_pos, scanline_gap, scanline_thickness); } // NOTE: UI ==================================================================================== TELY_Render_PushTransform(renderer, Dqn_M2x3_Identity()); DQN_DEFER { TELY_Render_PopTransform(renderer); }; - if (game->debug_ui) { + if (game->play.debug_ui) { // NOTE: Info bar ========================================================================== { TELY_RFuiResult info_bar = TELY_RFui_Row(rfui, DQN_STRING8("Info Bar")); @@ -3018,7 +3057,7 @@ void FP_Render(FP_Game *game, TELY_Platform *platform, TELY_Renderer *renderer) TELY_RFui_PushParent(rfui, profiler_layout.widget); DQN_DEFER { TELY_RFui_PopParent(rfui); }; - TELY_RFui_TextF(rfui, "Camera: %.1f, %.1f", game->camera.world_pos.x, game->camera.world_pos.y); + TELY_RFui_TextF(rfui, "Camera: %.1f, %.1f", game->play.camera.world_pos.x, game->play.camera.world_pos.y); Dqn_ProfilerAnchor *anchors = Dqn_Profiler_AnchorBuffer(Dqn_ProfilerAnchorBuffer_Back); for (size_t anchor_index = 1; anchor_index < DQN_PROFILER_ANCHOR_BUFFER_SIZE; anchor_index++) { @@ -3047,8 +3086,64 @@ void FP_Render(FP_Game *game, TELY_Platform *platform, TELY_Renderer *renderer) } } } + + TELY_Render_PushTransform(renderer, Dqn_M2x3_Identity()); + DQN_DEFER { TELY_Render_PopTransform(renderer); }; + + FP_GameEntity *player = FP_Game_GetEntity(game, game->play.player); + Dqn_V2 draw_p = Dqn_V2_InitNx2(32.f, platform->core.window_size.h * .5f); + TELY_Render_PushFont(renderer, game->inter_regular_font); + TELY_Render_TextF(renderer, draw_p, Dqn_V2_Zero, "DEBUG MENU"); draw_p.y += TELY_Render_FontHeight(renderer, assets); + TELY_Render_TextF(renderer, draw_p, Dqn_V2_Zero, " F1 Debug info"); draw_p.y += TELY_Render_FontHeight(renderer, assets); + TELY_Render_TextF(renderer, draw_p, Dqn_V2_Zero, " F2 Add coins x10,000"); draw_p.y += TELY_Render_FontHeight(renderer, assets); + TELY_Render_TextF(renderer, draw_p, Dqn_V2_Zero, " F3 Win game"); draw_p.y += TELY_Render_FontHeight(renderer, assets); + TELY_Render_TextF(renderer, draw_p, Dqn_V2_Zero, " F4 Reset game"); draw_p.y += TELY_Render_FontHeight(renderer, assets); + TELY_Render_TextF(renderer, draw_p, Dqn_V2_Zero, " F5 Increase health"); draw_p.y += TELY_Render_FontHeight(renderer, assets); + TELY_Render_TextF(renderer, draw_p, Dqn_V2_Zero, " F6 Increase stamina"); draw_p.y += TELY_Render_FontHeight(renderer, assets); + TELY_Render_TextF(renderer, draw_p, Dqn_V2_Zero, " F7 Increase mobile data"); draw_p.y += TELY_Render_FontHeight(renderer, assets); + TELY_Render_TextF(renderer, draw_p, Dqn_V2_Zero, " F8 %s god mode", game->play.god_mode ? "Disable" : "Enable"); draw_p.y += TELY_Render_FontHeight(renderer, assets); + TELY_Render_TextF(renderer, draw_p, Dqn_V2_Zero, " F9 %s noclip", player->flags & FP_GameEntityFlag_NoClip ? "Disable" : "Enable"); draw_p.y += TELY_Render_FontHeight(renderer, assets); + TELY_Render_PopFont(renderer); + TELY_Render_PopColourV4(renderer); + + if (TELY_Platform_InputScanCodeIsPressed(input, TELY_PlatformInputScanCode_F1)) + game->play.debug_ui = !game->play.debug_ui; + + if (TELY_Platform_InputScanCodeIsPressed(input, TELY_PlatformInputScanCode_F2)) { + if (!FP_Game_IsNilEntity(player)) + player->coins += 10'000; + } + + if (TELY_Platform_InputScanCodeIsPressed(input, TELY_PlatformInputScanCode_F3)) + game->play.state = FP_GameState_WinGame; + + if (TELY_Platform_InputScanCodeIsPressed(input, TELY_PlatformInputScanCode_F4)) + FP_PlayReset(game, platform); + + if (TELY_Platform_InputScanCodeIsPressed(input, TELY_PlatformInputScanCode_F5)) { + if (!FP_Game_IsNilEntity(player)) + player->hp_cap += FP_DEFAULT_DAMAGE; + } + + if (TELY_Platform_InputScanCodeIsPressed(input, TELY_PlatformInputScanCode_F6)) { + if (!FP_Game_IsNilEntity(player)) + player->stamina_cap += DQN_CAST(uint16_t)(FP_TERRY_DASH_STAMINA_COST * .5f); + } + + if (TELY_Platform_InputScanCodeIsPressed(input, TELY_PlatformInputScanCode_F7)) { + if (!FP_Game_IsNilEntity(player)) + player->terry_mobile_data_plan_cap += DQN_KILOBYTES(1); + } + + if (TELY_Platform_InputScanCodeIsPressed(input, TELY_PlatformInputScanCode_F8)) + game->play.god_mode = !game->play.god_mode; + + if (TELY_Platform_InputScanCodeIsPressed(input, TELY_PlatformInputScanCode_F9)) + player->flags ^= FP_GameEntityFlag_NoClip; + } + extern "C" __declspec(dllexport) void TELY_DLL_FrameUpdate(void *user_data) { @@ -3061,13 +3156,13 @@ void TELY_DLL_FrameUpdate(void *user_data) // ============================================================================================= - game->prev_clicked_entity = game->clicked_entity; - game->prev_hot_entity = game->hot_entity; - game->prev_active_entity = game->active_entity; - game->hot_entity = {}; - game->active_entity = {}; - Dqn_FArray_Clear(&game->parent_entity_stack); - Dqn_FArray_Add(&game->parent_entity_stack, game->root_entity->handle); + game->play.prev_clicked_entity = game->play.clicked_entity; + game->play.prev_hot_entity = game->play.hot_entity; + game->play.prev_active_entity = game->play.active_entity; + game->play.hot_entity = {}; + game->play.active_entity = {}; + Dqn_FArray_Clear(&game->play.parent_entity_stack); + Dqn_FArray_Add(&game->play.parent_entity_stack, game->play.root_entity->handle); // ============================================================================================= @@ -3081,13 +3176,13 @@ void TELY_DLL_FrameUpdate(void *user_data) // ============================================================================================= - if (game->state == FP_GameState_Play) { + if (game->play.state == FP_GameState_Play) { if (TELY_Platform_InputKeyWasDown(input->mouse_left) && TELY_Platform_InputKeyIsDown(input->mouse_left)) { - if (game->prev_active_entity.id) - game->active_entity = game->prev_active_entity; + if (game->play.prev_active_entity.id) + game->play.active_entity = game->play.prev_active_entity; } else { - Dqn_V2 world_mouse_p = input->mouse_p + game->camera.world_pos; - for (FP_GameEntityIterator it = {}; FP_Game_DFSPreOrderWalkEntityTree(game, &it, game->root_entity); ) { + Dqn_V2 world_mouse_p = input->mouse_p + game->play.camera.world_pos; + for (FP_GameEntityIterator it = {}; FP_Game_DFSPreOrderWalkEntityTree(game, &it, game->play.root_entity); ) { FP_GameEntity *entity = it.entity; if (entity->local_hit_box_size.x <= 0 || entity->local_hit_box_size.y <= 0) continue; @@ -3099,18 +3194,18 @@ void TELY_DLL_FrameUpdate(void *user_data) if (!Dqn_Rect_ContainsPoint(world_hit_box, world_mouse_p)) continue; - game->hot_entity = entity->handle; + game->play.hot_entity = entity->handle; if (TELY_Platform_InputKeyIsPressed(input->mouse_left)) { - game->active_entity = entity->handle; - game->clicked_entity = entity->handle; + game->play.active_entity = entity->handle; + game->play.clicked_entity = entity->handle; } } } } - for (game->delta_s_accumulator += DQN_CAST(Dqn_f32)input->delta_s; - game->delta_s_accumulator > PHYSICS_STEP; - game->delta_s_accumulator -= PHYSICS_STEP) { + for (game->play.delta_s_accumulator += DQN_CAST(Dqn_f32)input->delta_s; + game->play.delta_s_accumulator > PHYSICS_STEP; + game->play.delta_s_accumulator -= PHYSICS_STEP) { FP_Update(platform, game, input); } diff --git a/feely_pona_entity_create.cpp b/feely_pona_entity_create.cpp index 69709b0..ab01b0d 100644 --- a/feely_pona_entity_create.cpp +++ b/feely_pona_entity_create.cpp @@ -14,7 +14,7 @@ static bool FP_Entity_IsBuildingForMobs(FP_GameEntity *entity) static Dqn_f32 FP_Entity_CalcSpriteScaleForDesiredHeight(FP_Game *game, FP_Meters height, Dqn_Rect sprite_rect) { - Dqn_f32 sprite_in_meters = FP_Game_PixelsToMetersNx1(game, sprite_rect.size.y); + Dqn_f32 sprite_in_meters = FP_Game_PixelsToMetersNx1(game->play, sprite_rect.size.y); Dqn_f32 result = height.meters / sprite_in_meters; return result; } @@ -318,7 +318,7 @@ static FP_GameEntityHandle FP_Entity_CreateClinger(FP_Game *game, Dqn_V2 pos, DQ entity->attack_cooldown_ms = 1000; entity->faction = FP_GameEntityFaction_Foe; - entity->local_hit_box_size = FP_Game_MetersToPixelsNx2(game, 0.7f, entity->sprite_height.meters * .5f); + entity->local_hit_box_size = FP_Game_MetersToPixelsNx2(game->play, 0.7f, entity->sprite_height.meters * .5f); FP_Entity_AddDebugEditorFlags(game, entity->handle); entity->flags |= FP_GameEntityFlag_NonTraversable; entity->flags |= FP_GameEntityFlag_Attackable; @@ -341,7 +341,7 @@ static FP_GameEntityHandle FP_Entity_CreateSmoochie(FP_Game *game, Dqn_V2 pos, D entity->is_dying = false; entity->local_pos = pos; entity->attack_cooldown_ms = 1000; - entity->local_hit_box_size = FP_Game_MetersToPixelsNx2(game, 0.7f, entity->sprite_height.meters * .6f); + entity->local_hit_box_size = FP_Game_MetersToPixelsNx2(game->play, 0.7f, entity->sprite_height.meters * .6f); FP_EntityRenderData render_data = FP_Entity_GetRenderData(game, entity->type, 0, FP_GameDirection_Down); entity->sprite_height = render_data.height; @@ -375,7 +375,7 @@ static FP_GameEntityHandle FP_Entity_CreateCatfish(FP_Game *game, Dqn_V2 pos, DQ FP_Game_EntityActionReset(game, result, duration_ms, render_data.sprite); entity->attack_cooldown_ms = 1000; - entity->local_hit_box_size = FP_Game_MetersToPixelsNx2(game, 0.7f, entity->sprite_height.meters * .6f); + entity->local_hit_box_size = FP_Game_MetersToPixelsNx2(game->play, 0.7f, entity->sprite_height.meters * .6f); FP_Entity_AddDebugEditorFlags(game, entity->handle); entity->flags |= FP_GameEntityFlag_NonTraversable; entity->flags |= FP_GameEntityFlag_Attackable; @@ -434,7 +434,7 @@ static FP_GameEntityHandle FP_Entity_CreateMobSpawner(FP_Game *game, Dqn_V2 pos, FP_Game_EntityActionReset(game, result, duration_ms, render_data.sprite); entity->spawn_cap = spawn_cap; - entity->spawn_list = FP_SentinelList_Init(game->chunk_pool); + entity->spawn_list = FP_SentinelList_Init(game->play.chunk_pool); return result; } @@ -456,7 +456,7 @@ static FP_GameEntityHandle FP_Entity_CreateTerry(FP_Game *game, Dqn_V2 pos, DQN_ FP_Game_EntityActionReset(game, result, duration_ms, render_data.sprite); entity->attack_cooldown_ms = 500; - entity->local_hit_box_size = FP_Game_MetersToPixelsNx2(game, 0.5f, entity->sprite_height.meters * .6f); + entity->local_hit_box_size = FP_Game_MetersToPixelsNx2(game->play, 0.5f, entity->sprite_height.meters * .6f); entity->hp_cap = FP_DEFAULT_DAMAGE * 3; entity->hp = entity->hp_cap; entity->coins = 0;//1'000'000; @@ -756,7 +756,7 @@ static FP_GameEntityHandle FP_Entity_CreatePhoneMessageProjectile(FP_Game *game, entity->local_hit_box_size = Dqn_V2_InitNx2(render_data.render_size.w, render_data.render_size.h - (render_data.render_size.h * .4f)); entity->attack_box_offset = entity->local_hit_box_offset; entity->attack_box_size = entity->local_hit_box_size; - entity->ttl_end_timestamp = game->clock_ms + 1000; + entity->ttl_end_timestamp = game->play.clock_ms + 1000; entity->projectile_owner = owner; entity->faction = FP_GameEntityFaction_Friendly; return result; diff --git a/feely_pona_game.cpp b/feely_pona_game.cpp index 6c9ca82..de27a66 100644 --- a/feely_pona_game.cpp +++ b/feely_pona_game.cpp @@ -3,13 +3,13 @@ #include "feely_pona_unity.h" #endif -#define FP_Game_MetersToPixelsNx1(game, val) ((val) * (game)->meters_to_pixels) -#define FP_Game_MetersToPixelsNx2(game, x, y) (Dqn_V2_InitNx2(x, y) * (game)->meters_to_pixels) -#define FP_Game_MetersToPixelsV2(game, xy) (xy * (game)->meters_to_pixels) +#define FP_Game_MetersToPixelsNx1(game, val) ((val) * (game).meters_to_pixels) +#define FP_Game_MetersToPixelsNx2(game, x, y) (Dqn_V2_InitNx2(x, y) * (game).meters_to_pixels) +#define FP_Game_MetersToPixelsV2(game, xy) (xy * (game).meters_to_pixels) -#define FP_Game_PixelsToMetersNx1(game, val) ((val) * (1.f/(game)->meters_to_pixels)) -#define FP_Game_PixelsToMetersNx2(game, x, y) (Dqn_V2_InitNx2(x, y) * (1.f/(game)->meters_to_pixels)) -#define FP_Game_PixelsToMetersV2(game, xy) (xy * (1.f/(game)->meters_to_pixels)) +#define FP_Game_PixelsToMetersNx1(game, val) ((val) * (1.f/(game).meters_to_pixels)) +#define FP_Game_PixelsToMetersNx2(game, x, y) (Dqn_V2_InitNx2(x, y) * (1.f/(game).meters_to_pixels)) +#define FP_Game_PixelsToMetersV2(game, xy) (xy * (1.f/(game).meters_to_pixels)) static bool operator==(FP_GameEntityHandle const &lhs, FP_GameEntityHandle const &rhs) { @@ -50,12 +50,12 @@ static FP_GameEntity *FP_Game_GetEntity(FP_Game *game, FP_GameEntityHandle handl if (!game) return result; - result = game->entities.data; + result = game->play.entities.data; uint64_t index_from_handle = handle.id & FP_GAME_ENTITY_HANDLE_INDEX_MASK; - if (index_from_handle >= game->entities.size) + if (index_from_handle >= game->play.entities.size) return result; - FP_GameEntity *candidate = game->entities.data + index_from_handle; + FP_GameEntity *candidate = game->play.entities.data + index_from_handle; if (candidate->handle == handle) result = candidate; return result; @@ -167,24 +167,24 @@ static bool FP_Game_DFSPostOrderWalkEntityTree(FP_Game *game, FP_GameEntityItera // NOTE: Parent entity static void FP_Game_PushParentEntity(FP_Game *game, FP_GameEntityHandle handle) { - DQN_ASSERTF(game->parent_entity_stack.size >= 1, "Sentinel/nil entity has not been assigned as the 0th slot yet"); + DQN_ASSERTF(game->play.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); + Dqn_FArray_Add(&game->play.parent_entity_stack, handle); } static void FP_Game_PopParentEntity(FP_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); + if (game && game->play.parent_entity_stack.size > 1) + Dqn_FArray_PopBack(&game->play.parent_entity_stack, 1); } static FP_GameEntityHandle FP_Game_ActiveParentEntity(FP_Game const *game) { FP_GameEntityHandle result = {}; - if (!game || !game->parent_entity_stack.size) + if (!game || !game->play.parent_entity_stack.size) return result; - result = game->parent_entity_stack.data[game->parent_entity_stack.size - 1]; + result = game->play.parent_entity_stack.data[game->play.parent_entity_stack.size - 1]; return result; } @@ -201,28 +201,28 @@ static FP_GameEntity *FP_Game_MakeEntityPointerFV(FP_Game *game, DQN_FMT_STRING_ 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"); + DQN_ASSERTF(game->play.entities.size > 0, "Sentinel/nil entity has not been initialised yet"); + DQN_ASSERTF(game->play.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; + result = game->play.root_entity; // TODO(doyle): Root entity or ... the nil entity? + if (game->play.entity_free_list) { + result = game->play.entity_free_list; + game->play.entity_free_list = game->play.entity_free_list->next; + result->next = nullptr; } else { - if (game->entities.size >= (FP_GAME_ENTITY_HANDLE_INDEX_MAX + 1)) + if (game->play.entities.size >= (FP_GAME_ENTITY_HANDLE_INDEX_MAX + 1)) return result; - result = Dqn_VArray_Make(&game->entities, Dqn_ZeroMem_Yes); + result = Dqn_VArray_Make(&game->play.entities, Dqn_ZeroMem_Yes); if (!result) return result; - result->handle.id = (game->entities.size - 1) & FP_GAME_ENTITY_HANDLE_INDEX_MASK; + result->handle.id = (game->play.entities.size - 1) & FP_GAME_ENTITY_HANDLE_INDEX_MASK; } result->sprite_height.meters = 1; result->parent = FP_Game_ActiveParentEntityPointer(game); - result->name = TELY_ChunkPool_AllocFmtFV(game->chunk_pool, fmt, args); - result->buildings_visited = FP_SentinelList_Init(game->chunk_pool); + result->name = TELY_ChunkPool_AllocFmtFV(game->play.chunk_pool, fmt, args); + result->buildings_visited = FP_SentinelList_Init(game->play.chunk_pool); result->action.sprite_alpha = 1.f; result->stamina_cap = 93; @@ -251,7 +251,7 @@ static FP_GameEntity *FP_Game_MakeEntityPointerFV(FP_Game *game, DQN_FMT_STRING_ 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]); + DQN_ASSERT(result->parent->handle == game->play.parent_entity_stack.data[game->play.parent_entity_stack.size - 1]); return result; } @@ -308,7 +308,7 @@ static void FP_Game_DetachEntityIntoFreeList(FP_Game *game, FP_GameEntityHandle return; uint64_t const entity_index_from_handle = entity->handle.id & FP_GAME_ENTITY_HANDLE_INDEX_MASK; - DQN_ASSERT(entity_index_from_handle < game->entities.size); + DQN_ASSERT(entity_index_from_handle < game->play.entities.size); uint64_t const entity_generation_raw = entity->handle.id & FP_GAME_ENTITY_HANDLE_GENERATION_MASK; uint64_t const entity_generation = entity_generation_raw >> FP_GAME_ENTITY_HANDLE_GENERATION_RSHIFT; @@ -330,22 +330,23 @@ static void FP_Game_DetachEntityIntoFreeList(FP_Game *game, FP_GameEntityHandle parent->last_child = entity->prev; if (entity->name.size) - TELY_ChunkPool_Dealloc(game->chunk_pool, entity->name.data); + TELY_ChunkPool_Dealloc(game->play.chunk_pool, entity->name.data); - FP_SentinelList_Deinit(&entity->spawn_list, game->chunk_pool); - FP_SentinelList_Deinit(&entity->waypoints, game->chunk_pool); - FP_SentinelList_Deinit(&entity->buildings_visited, game->chunk_pool); + FP_SentinelList_Deinit(&entity->spawn_list, game->play.chunk_pool); + FP_SentinelList_Deinit(&entity->waypoints, game->play.chunk_pool); + FP_SentinelList_Deinit(&entity->buildings_visited, game->play.chunk_pool); 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->parent = game->play.root_entity; entity->handle.id = entity_index_from_handle | (new_entity_generation << FP_GAME_ENTITY_HANDLE_GENERATION_RSHIFT); // NOTE: Attach entity to the free list - entity->next = game->entity_free_list; + entity->next = game->play.entity_free_list; entity->prev = nullptr; - game->entity_free_list = entity; + game->play.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 @@ -357,10 +358,10 @@ static void FP_Game_DetachEntityIntoFreeList(FP_Game *game, FP_GameEntityHandle static void FP_Game_DeleteEntity(FP_Game *game, FP_GameEntityHandle handle) { uint64_t index_from_handle = handle.id & FP_GAME_ENTITY_HANDLE_INDEX_MASK; - if (!game || !DQN_CHECK(index_from_handle < game->entities.size)) + if (!game || !DQN_CHECK(index_from_handle < game->play.entities.size)) return; - FP_GameEntity *root = game->entities.data + index_from_handle; + FP_GameEntity *root = game->play.entities.data + index_from_handle; if (root->handle != handle) return; @@ -381,8 +382,8 @@ static Dqn_V2 FP_Game_CalcEntityWorldPos(FP_Game const *game, FP_GameEntityHandl if (!game) return result; - FP_GameEntity const *first = FP_Game_GetEntity(DQN_CAST(FP_Game *) game, handle); - for (FP_GameEntity const *entity = first; entity != game->root_entity; entity = entity->parent) + FP_GameEntity const *first = FP_Game_GetEntity(DQN_CAST(FP_Game *)game, handle); + for (FP_GameEntity const *entity = first; entity != game->play.root_entity; entity = entity->parent) result += entity->local_pos; return result; } @@ -472,19 +473,19 @@ static void FP_Game_EntityActionReset(FP_Game *game, FP_GameEntityHandle entity_ if (!entity) return; entity->action.sprite = sprite; - entity->action.started_at_clock_ms = game->clock_ms; - entity->action.end_at_clock_ms = DQN_MAX(duration_ms, game->clock_ms + duration_ms); + entity->action.started_at_clock_ms = game->play.clock_ms; + entity->action.end_at_clock_ms = DQN_MAX(duration_ms, game->play.clock_ms + duration_ms); } static Dqn_V2I FP_Game_WorldPosToTilePos(FP_Game *game, Dqn_V2 world_pos) { - Dqn_V2I result = Dqn_V2I_InitNx2(world_pos.x / game->tile_size, world_pos.y / game->tile_size); + Dqn_V2I result = Dqn_V2I_InitNx2(world_pos.x / game->play.tile_size, world_pos.y / game->play.tile_size); return result; } static Dqn_V2 FP_Game_TilePosToWorldPos(FP_Game *game, Dqn_V2I tile_pos) { - Dqn_V2 result = Dqn_V2_InitNx2(tile_pos.x * game->tile_size, tile_pos.y * game->tile_size); + Dqn_V2 result = Dqn_V2_InitNx2(tile_pos.x * game->play.tile_size, tile_pos.y * game->play.tile_size); return result; } @@ -495,8 +496,8 @@ static Dqn_Slice FP_Game_AStarPathFind(FP_Game *game, Dqn_V2I dest_tile) { Dqn_Profiler_ZoneScopeWithIndex("FP_Update: A*", FP_ProfileZone_FPUpdate_AStar); - Dqn_usize tile_count_x = DQN_CAST(Dqn_usize)(platform->core.window_size.w / game->tile_size); - Dqn_usize tile_count_y = DQN_CAST(Dqn_usize)(platform->core.window_size.h / game->tile_size); + Dqn_usize tile_count_x = DQN_CAST(Dqn_usize)(platform->core.window_size.w / game->play.tile_size); + Dqn_usize tile_count_y = DQN_CAST(Dqn_usize)(platform->core.window_size.h / game->play.tile_size); Dqn_Slice result = {}; if (dest_tile.x < 0 || dest_tile.x > tile_count_x || @@ -509,7 +510,7 @@ static Dqn_Slice FP_Game_AStarPathFind(FP_Game *game, // NOTE: Enumerate the entities that are collidable ============================================ bool dest_tile_is_non_traversable = false; auto zone_enum_collidables = Dqn_Profiler_BeginZoneWithIndex(DQN_STRING8("FP_Update: A* enumerate collidables"), FP_ProfileZone_FPUpdate_AStarEnumerateCollidables); - for (FP_GameEntityIterator it = {}; FP_Game_DFSPreOrderWalkEntityTree(game, &it, game->root_entity); ) { + for (FP_GameEntityIterator it = {}; FP_Game_DFSPreOrderWalkEntityTree(game, &it, game->play.root_entity); ) { FP_GameEntity const *walk_entity = it.entity; if (entity == walk_entity->handle) continue; @@ -644,7 +645,7 @@ static Dqn_Slice FP_Game_AStarPathFind(FP_Game *game, for (uint32_t old_index = 1 /*Sentinel*/; old_index < astar_info.occupied; old_index++) { Dqn_DSMapSlot const *slot = astar_info.slots + old_index; FP_GameAStarNode const *node = &slot->value; - Dqn_V2 pos = FP_Game_TilePosToWorldPos(game, node->tile) + (game->tile_size * .5f); + Dqn_V2 pos = FP_Game_TilePosToWorldPos(game, node->tile) + (game->play.tile_size * .5f); TELY_Render_CircleColourV4(renderer, pos, 4.f, TELY_RenderShapeMode_Fill, TELY_COLOUR_BLUE_CADET_V4); } #endif @@ -737,7 +738,7 @@ FP_GameFindClosestEntityResult FP_Game_FindClosestEntityWithType(FP_Game *game, result.dist_squared = DQN_F32_MAX; result.pos = Dqn_V2_InitNx1(DQN_F32_MAX); - for (FP_GameEntityIterator it = {}; FP_Game_DFSPostOrderWalkEntityTree(game, &it, game->root_entity); ) { + for (FP_GameEntityIterator it = {}; FP_Game_DFSPostOrderWalkEntityTree(game, &it, game->play.root_entity); ) { FP_GameEntity *it_entity = it.entity; if (it_entity->type != type) continue; @@ -791,11 +792,11 @@ static void FP_Game_EntityTransitionState(FP_Game *game, FP_GameEntity *entity, case FP_EntityType_Terry: { if (desired_state == FP_EntityTerryState_Attack || desired_state == FP_EntityTerryState_RangeAttack) { - if (!FP_Game_CanEntityAttack(entity, game->clock_ms)) { + if (!FP_Game_CanEntityAttack(entity, game->play.clock_ms)) { // NOTE: Cooldown not met do not transition return; } - entity->last_attack_timestamp = game->clock_ms; + entity->last_attack_timestamp = game->play.clock_ms; if (desired_state == FP_EntityTerryState_RangeAttack) { if (entity->terry_mobile_data_plan < FP_TERRY_MOBILE_DATA_PER_RANGE_ATTACK) @@ -809,31 +810,31 @@ static void FP_Game_EntityTransitionState(FP_Game *game, FP_GameEntity *entity, case FP_EntityType_Smoochie: { if (desired_state == FP_EntitySmoochieState_Attack) { - if (!FP_Game_CanEntityAttack(entity, game->clock_ms)) { + if (!FP_Game_CanEntityAttack(entity, game->play.clock_ms)) { // NOTE: Cooldown not met do not transition return; } - entity->last_attack_timestamp = game->clock_ms; + entity->last_attack_timestamp = game->play.clock_ms; } } break; case FP_EntityType_Clinger: { if (desired_state == FP_EntityClingerState_Attack) { - if (!FP_Game_CanEntityAttack(entity, game->clock_ms)) { + if (!FP_Game_CanEntityAttack(entity, game->play.clock_ms)) { // NOTE: Cooldown not met do not transition return; } - entity->last_attack_timestamp = game->clock_ms; + entity->last_attack_timestamp = game->play.clock_ms; } } break; case FP_EntityType_Catfish: { if (desired_state == FP_EntityClingerState_Attack) { - if (!FP_Game_CanEntityAttack(entity, game->clock_ms)) { + if (!FP_Game_CanEntityAttack(entity, game->play.clock_ms)) { // NOTE: Cooldown not met do not transition return; } - entity->last_attack_timestamp = game->clock_ms; + entity->last_attack_timestamp = game->play.clock_ms; } } break; @@ -893,3 +894,4 @@ static void FP_GameRenderCameraFollowScanlines(TELY_Renderer *renderer, TELY_Render_LineColourV4(renderer, start, end, TELY_Colour_V4Alpha(TELY_COLOUR_BLACK_V4, 0.1f), scanline_thickness); } } + diff --git a/feely_pona_game.h b/feely_pona_game.h index bf81507..e0cc1b5 100644 --- a/feely_pona_game.h +++ b/feely_pona_game.h @@ -274,27 +274,17 @@ enum FP_GameState { FP_GameState_IntroScreen, FP_GameState_Play, + FP_GameState_WinGame, }; -struct FP_Game +struct FP_GamePlay { - Dqn_f32 delta_s_accumulator; - uint16_t tile_size; TELY_ChunkPool *chunk_pool; - TELY_AssetFontHandle inter_regular_font_large; - TELY_AssetFontHandle inter_regular_font; - TELY_AssetFontHandle inter_italic_font; - TELY_AssetFontHandle jetbrains_mono_font; - TELY_AssetFontHandle talkco_font; - TELY_AssetFontHandle talkco_font_large; - TELY_AssetAudioHandle audio[FP_GameAudio_Count]; - - Dqn_Slice hero_sprite_anims; - TELY_AssetSpriteSheet hero_sprite_sheet; - Dqn_FArray parent_entity_stack; + uint16_t tile_size; + Dqn_f32 delta_s_accumulator; + Dqn_Arena arena; Dqn_VArray entities; - - TELY_AssetSpriteSheet atlas_sprite_sheet; + Dqn_FArray parent_entity_stack; FP_GameEntity *root_entity; FP_GameEntity *entity_free_list; @@ -318,14 +308,13 @@ struct FP_Game FP_GameEntityHandle prev_active_entity; FP_GameCamera camera; - TELY_RFui rfui; - Dqn_f32 meters_to_pixels; uint64_t clock_ms; uint64_t update_counter; Dqn_PCG32 rng; bool debug_ui; + bool god_mode; FP_GameInGameMenu in_game_menu; bool build_mode_can_place_building; Dqn_usize build_mode_building_index; @@ -341,6 +330,22 @@ struct FP_Game FP_GameState state; }; +struct FP_Game +{ + TELY_AssetFontHandle inter_regular_font_large; + TELY_AssetFontHandle inter_regular_font; + TELY_AssetFontHandle inter_italic_font; + TELY_AssetFontHandle jetbrains_mono_font; + TELY_AssetFontHandle talkco_font; + TELY_AssetFontHandle talkco_font_large; + TELY_AssetAudioHandle audio[FP_GameAudio_Count]; + Dqn_Slice hero_sprite_anims; + TELY_AssetSpriteSheet hero_sprite_sheet; + TELY_AssetSpriteSheet atlas_sprite_sheet; + TELY_RFui rfui; + FP_GamePlay play; +}; + struct FP_GameAStarNode { Dqn_usize cost; diff --git a/feely_pona_misc.cpp b/feely_pona_misc.cpp index 123c37f..71a88a5 100644 --- a/feely_pona_misc.cpp +++ b/feely_pona_misc.cpp @@ -350,11 +350,11 @@ void FP_UnitTests(TELY_Platform *platform) TELY_ChunkPool chunk_pool = {}; chunk_pool.arena = &platform->arena; - FP_Game *game = Dqn_Arena_New(&platform->arena, FP_Game, Dqn_ZeroMem_Yes); - game->chunk_pool = &chunk_pool; - game->entities = Dqn_VArray_Init(&platform->arena, 1024 * 8); - game->root_entity = Dqn_VArray_Make(&game->entities, Dqn_ZeroMem_No); - Dqn_FArray_Add(&game->parent_entity_stack, game->root_entity->handle); + FP_Game *game = Dqn_Arena_New(&platform->arena, FP_Game, Dqn_ZeroMem_Yes); + game->play.chunk_pool = &chunk_pool; + game->play.entities = Dqn_VArray_Init(&platform->arena, 1024 * 8); + game->play.root_entity = Dqn_VArray_Make(&game->play.entities, Dqn_ZeroMem_No); + Dqn_FArray_Add(&game->play.parent_entity_stack, game->play.root_entity->handle); { // NOTE: Setup entity-tree ================================================================= @@ -384,7 +384,7 @@ void FP_UnitTests(TELY_Platform *platform) FP_GameEntity *pre_order_walk[9] = {}; Dqn_usize pre_order_walk_count = 0; - for (FP_GameEntityIterator it = {}; FP_Game_DFSPreOrderWalkEntityTree(game, &it, game->root_entity);) { + for (FP_GameEntityIterator it = {}; FP_Game_DFSPreOrderWalkEntityTree(game, &it, game->play.root_entity);) { DQN_ASSERT(pre_order_walk_count < DQN_ARRAY_UCOUNT(pre_order_walk)); pre_order_walk[pre_order_walk_count++] = it.entity; } @@ -404,7 +404,7 @@ void FP_UnitTests(TELY_Platform *platform) FP_GameEntity *post_order_walk[9] = {}; Dqn_usize post_order_walk_count = 0; - for (FP_GameEntityIterator it = {}; FP_Game_DFSPostOrderWalkEntityTree(game, &it, game->root_entity);) { + for (FP_GameEntityIterator it = {}; FP_Game_DFSPostOrderWalkEntityTree(game, &it, game->play.root_entity);) { DQN_ASSERT(post_order_walk_count < DQN_ARRAY_UCOUNT(post_order_walk)); post_order_walk[post_order_walk_count++] = it.entity; } @@ -421,11 +421,11 @@ void FP_UnitTests(TELY_Platform *platform) DQN_ASSERT(post_order_walk[8] == f); // NOTE: Cleanup =========================================================================== - FP_Game_DeleteEntity(game, game->root_entity->handle); - DQN_ASSERT(game->root_entity->first_child == nullptr); - DQN_ASSERT(game->root_entity->last_child == nullptr); - DQN_ASSERT(game->root_entity->next == nullptr); - DQN_ASSERT(game->root_entity->prev == nullptr); - DQN_ASSERT(game->root_entity->parent == nullptr); + FP_Game_DeleteEntity(game, game->play.root_entity->handle); + DQN_ASSERT(game->play.root_entity->first_child == nullptr); + DQN_ASSERT(game->play.root_entity->last_child == nullptr); + DQN_ASSERT(game->play.root_entity->next == nullptr); + DQN_ASSERT(game->play.root_entity->prev == nullptr); + DQN_ASSERT(game->play.root_entity->parent == nullptr); } }