diff --git a/External/tely b/External/tely index 4ccbde6..8576ce0 160000 --- a/External/tely +++ b/External/tely @@ -1 +1 @@ -Subproject commit 4ccbde6fecb8ea17f9cde73df7c06da466311f3c +Subproject commit 8576ce00dc74f817b346994efca8610001a8eafb diff --git a/feely_pona.cpp b/feely_pona.cpp index e826eaa..6eeb716 100644 --- a/feely_pona.cpp +++ b/feely_pona.cpp @@ -5,39 +5,40 @@ Dqn_f32 const PHYSICS_STEP = 1 / 60.f; -Dqn_Rect FP_Game_GetBuildingPlacementRectForEntity(FP_Game *game, FP_GameEntityHandle handle) + +Dqn_Rect FP_Game_GetBuildingPlacementRectForEntity(FP_Game *game, FP_GamePlaceableBuilding placeable_building, FP_GameEntityHandle handle) { Dqn_Rect result = {}; FP_GameEntity *entity = FP_Game_GetEntity(game, handle); if (FP_Game_IsNilEntity(entity)) return result; - FP_EntityRenderData club_terry_render_data = FP_Entity_GetRenderData(game, FP_EntityType_ClubTerry, FP_EntityClubTerryState_Idle, entity->direction); + FP_EntityRenderData render_data = FP_Entity_GetRenderData(game, placeable_building.type, placeable_building.state, entity->direction); Dqn_Rect box = FP_Game_CalcEntityWorldHitBox(game, entity->handle); Dqn_V2 build_p = {}; switch (entity->direction) { case FP_GameDirection_Up: { - build_p = Dqn_Rect_InterpolatedPoint(box, Dqn_V2_InitNx2(0.5f, 0.f)) - Dqn_V2_InitNx2(0.f, club_terry_render_data.render_size.h * .5f + 10.f); + build_p = Dqn_Rect_InterpolatedPoint(box, Dqn_V2_InitNx2(0.5f, 0.f)) - Dqn_V2_InitNx2(0.f, render_data.render_size.h * .5f + 10.f); } break; case FP_GameDirection_Down: { - build_p = Dqn_Rect_InterpolatedPoint(box, Dqn_V2_InitNx2(0.5f, 1.f)) + Dqn_V2_InitNx2(0.f, club_terry_render_data.render_size.h * .5f + 10.f); + build_p = Dqn_Rect_InterpolatedPoint(box, Dqn_V2_InitNx2(0.5f, 1.f)) + Dqn_V2_InitNx2(0.f, render_data.render_size.h * .5f + 10.f); } break; case FP_GameDirection_Left: { - build_p = Dqn_Rect_InterpolatedPoint(box, Dqn_V2_InitNx2(0.0f, 0.5f)) - Dqn_V2_InitNx2(club_terry_render_data.render_size.w * .5f + 10.f, 0); + build_p = Dqn_Rect_InterpolatedPoint(box, Dqn_V2_InitNx2(0.0f, 0.5f)) - Dqn_V2_InitNx2(render_data.render_size.w * .5f + 10.f, 0); } break; case FP_GameDirection_Right: { - build_p = Dqn_Rect_InterpolatedPoint(box, Dqn_V2_InitNx2(1.f, 0.5f)) + Dqn_V2_InitNx2(club_terry_render_data.render_size.w * .5f + 10.f, 0); + build_p = Dqn_Rect_InterpolatedPoint(box, Dqn_V2_InitNx2(1.f, 0.5f)) + Dqn_V2_InitNx2(render_data.render_size.w * .5f + 10.f, 0); } break; case FP_GameDirection_Count: DQN_INVALID_CODE_PATH; break; } - result.size = club_terry_render_data.render_size; - result.pos = build_p - (club_terry_render_data.render_size * .5f); + result.size = render_data.render_size; + result.pos = build_p - (render_data.render_size * .5f); return result; } @@ -1248,6 +1249,25 @@ void FP_Update(TELY_Platform *platform, FP_Game *game, TELY_PlatformInput *input if (game->clicked_entity.id) { if (TELY_Platform_InputScanCodeIsPressed(input, TELY_PlatformInputScanCode_Delete)) FP_Game_DeleteEntity(game, game->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; + } else { + game->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; + } else { + game->build_mode_building_index += 1; + } + } + } else { Dqn_f32 pan_speed = 5.f; if (TELY_Platform_InputScanCodeIsDown(input, TELY_PlatformInputScanCode_Space)) @@ -1574,7 +1594,7 @@ void FP_Update(TELY_Platform *platform, FP_Game *game, TELY_PlatformInput *input } if (game->clicked_entity == entity->handle) { - if (TELY_Platform_InputScanCodeIsPressed(input, TELY_PlatformInputScanCode_F)) + if (TELY_Platform_InputScanCodeIsPressed(input, TELY_PlatformInputScanCode_H)) game->build_mode = !game->build_mode; if (entity->flags & FP_GameEntityFlag_CameraTracking) @@ -1583,27 +1603,50 @@ void FP_Update(TELY_Platform *platform, FP_Game *game, TELY_PlatformInput *input game->build_mode_can_place_building = false; if (game->build_mode) { - Dqn_Rect dest_rect = FP_Game_GetBuildingPlacementRectForEntity(game, entity->handle); - Dqn_V2 placement_pos = Dqn_Rect_Center(dest_rect); + FP_GamePlaceableBuilding placeable_building = PLACEABLE_BUILDINGS[game->build_mode_building_index]; + 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 = {}; - !game->build_mode_can_place_building && FP_Game_DFSPostOrderWalkEntityTree(game, &zone_it, game->root_entity); + FP_Game_DFSPreOrderWalkEntityTree(game, &zone_it, game->root_entity); ) { - FP_GameEntity *zone = zone_it.entity; + + FP_GameEntity *zone = zone_it.entity; + bool is_building = zone->type == FP_EntityType_KennelTerry || + zone->type == FP_EntityType_AirportTerry || + zone->type == FP_EntityType_ChurchTerry || + zone->type == FP_EntityType_ClubTerry; + + 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; + break; + } + } + if ((zone->flags & FP_GameEntityFlag_BuildZone) == 0) continue; - Dqn_Rect zone_hit_box = FP_Game_CalcEntityWorldHitBox(game, zone->handle); zone_hit_box.pos += dest_rect.size * .5f; 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->build_mode_can_place_building |= Dqn_Rect_ContainsPoint(zone_hit_box, placement_pos); } if (game->build_mode_can_place_building && - TELY_Platform_InputScanCodeIsPressed(input, TELY_PlatformInputScanCode_E)) { - FP_Entity_CreateClubTerry(game, placement_pos, "Club Terry"); + TELY_Platform_InputScanCodeIsPressed(input, TELY_PlatformInputScanCode_F)) { + if (placeable_building.type == FP_EntityType_ClubTerry) { + FP_Entity_CreateClubTerry(game, placement_pos, "Club Terry"); + } else if (placeable_building.type == FP_EntityType_ChurchTerry) { + FP_Entity_CreateChurchTerry(game, placement_pos, "Church Terry"); + } else if (placeable_building.type == FP_EntityType_AirportTerry) { + FP_Entity_CreateAirportTerry(game, placement_pos, "Airport Terry"); + } else { + DQN_ASSERT(placeable_building.type == FP_EntityType_KennelTerry); + FP_Entity_CreateKennelTerry(game, placement_pos, "Kennel Terry"); + } } } } @@ -1979,8 +2022,13 @@ void FP_Render(FP_Game *game, TELY_Platform *platform, TELY_Renderer *renderer) } } - // NOTE: Render waypoint entities ========================================================== + if (game->build_mode) { + 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) { + // NOTE: Render waypoint entities ====================================================== if (entity->flags & FP_GameEntityFlag_MobSpawner) { Dqn_V2 start = world_pos; for (FP_GameEntity *waypoint_entity = entity->first_child; waypoint_entity; waypoint_entity = waypoint_entity->next) { @@ -2051,16 +2099,22 @@ void FP_Render(FP_Game *game, TELY_Platform *platform, TELY_Renderer *renderer) } } + + } + + if (!FP_Game_IsNilEntityHandle(game, game->clicked_entity)) { // NOTE: Render building blueprint ========================================================= - if (game->clicked_entity == entity->handle && game->build_mode) { + if (game->build_mode) { + FP_GameEntity *entity = FP_Game_GetEntity(game, game->clicked_entity); TELY_Render_PushTransform(renderer, Dqn_M2x3_Identity()); Dqn_V2 label_p = Dqn_V2_InitNx2(platform->core.window_size.x * .5f, platform->core.window_size.y * .1f); TELY_Render_Text(renderer, label_p, Dqn_V2_InitNx2(0.5f, 0.5f), DQN_STRING8("Build Mode")); TELY_Render_PopTransform(renderer); - FP_EntityRenderData club_terry_render_data = FP_Entity_GetRenderData(game, FP_EntityType_ClubTerry, FP_EntityClubTerryState_Idle, entity->direction); - Dqn_Rect dest_rect = FP_Game_GetBuildingPlacementRectForEntity(game, entity->handle); + FP_GamePlaceableBuilding placeable_building = PLACEABLE_BUILDINGS[game->build_mode_building_index]; + FP_EntityRenderData club_terry_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 ? TELY_Colour_V4Alpha(TELY_COLOUR_WHITE_V4, 0.5f) : @@ -2076,6 +2130,46 @@ void FP_Render(FP_Game *game, TELY_Platform *platform, TELY_Renderer *renderer) colour); } + // NOTE: Render the building selector UI =================================================== + { + 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); + + Dqn_f32 building_ui_size = 150.f; + Dqn_f32 padding = 10.f; + Dqn_f32 total_size = DQN_ARRAY_UCOUNT(PLACEABLE_BUILDINGS) * building_ui_size + ((DQN_ARRAY_UCOUNT(PLACEABLE_BUILDINGS) - 1) * padding); + Dqn_f32 start_x = (platform->core.window_size.x * .5f) - (total_size * .5f); + DQN_FOR_UINDEX (building_index, DQN_ARRAY_UCOUNT(PLACEABLE_BUILDINGS)) { + FP_GamePlaceableBuilding building = PLACEABLE_BUILDINGS[building_index]; + FP_EntityRenderData render_data = FP_Entity_GetRenderData(game, building.type, building.state, FP_GameDirection_Down); + Dqn_Rect rect = Dqn_Rect_InitNx4(start_x + (building_index * building_ui_size) + (padding * building_index), + 32.f, + building_ui_size, + building_ui_size); + + 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) { + outline_colour = TELY_COLOUR_RED_TOMATO_V4; + texture_colour.a = 1.f; + } + + TELY_Render_RectColourV4(renderer, rect, TELY_RenderShapeMode_Fill, TELY_Colour_V4Alpha(TELY_COLOUR_WHITE_V4, 0.5f)); + TELY_Render_TextureColourV4(renderer, + render_data.sheet->tex_handle, + render_data.sheet_rect, + rect, + Dqn_V2_Zero /*rotate origin*/, + 0.f /*rotation*/, + texture_colour); + + + TELY_RenderCommandRect *cmd = TELY_Render_RectColourV4(renderer, rect, TELY_RenderShapeMode_Line, outline_colour); + cmd->thickness = 2.f; + } + } } } diff --git a/feely_pona.h b/feely_pona.h index 5c2f9bf..d5504ec 100644 --- a/feely_pona.h +++ b/feely_pona.h @@ -23,8 +23,8 @@ struct FP_Meters struct FP_GlobalAnimations { - Dqn_String8 airport_terry_alive = DQN_STRING8("airport_terry_alive"); - Dqn_String8 airport_terry_dark = DQN_STRING8("airport_terry_dark"); + Dqn_String8 airport_terry = DQN_STRING8("airport_terry"); + Dqn_String8 airport_terry_plane = DQN_STRING8("airport_terry_plane"); Dqn_String8 catfish_attack_down = DQN_STRING8("catfish_attack_down"); Dqn_String8 catfish_attack_side = DQN_STRING8("catfish_attack_side"); diff --git a/feely_pona_entity_create.cpp b/feely_pona_entity_create.cpp index 5b3c25d..9295868 100644 --- a/feely_pona_entity_create.cpp +++ b/feely_pona_entity_create.cpp @@ -175,8 +175,8 @@ FP_EntityRenderData FP_Entity_GetRenderData(FP_Game *game, FP_EntityType type, u FP_EntityAirportTerryState state = DQN_CAST(FP_EntityAirportTerryState)raw_state; switch (state) { case FP_EntityAirportTerryState_Nil: break; - case FP_EntityAirportTerryState_Idle: result.anim_name = g_anim_names.airport_terry_dark; break; - case FP_EntityAirportTerryState_FlyPassenger: result.anim_name = g_anim_names.airport_terry_alive; break; + case FP_EntityAirportTerryState_Idle: result.anim_name = g_anim_names.airport_terry; break; + case FP_EntityAirportTerryState_FlyPassenger: result.anim_name = g_anim_names.airport_terry; break; } } break; @@ -219,7 +219,7 @@ FP_EntityRenderData FP_Entity_GetRenderData(FP_Game *game, FP_EntityType type, u } break; case FP_EntityType_KennelTerry: { - result.height.meters = 4.f; + result.height.meters = 3.f; FP_EntityKennelTerryState state = DQN_CAST(FP_EntityKennelTerryState)raw_state; switch (state) { case FP_EntityKennelTerryState_Nil: break; diff --git a/feely_pona_game.h b/feely_pona_game.h index 19dc9c8..5f8d9e3 100644 --- a/feely_pona_game.h +++ b/feely_pona_game.h @@ -263,6 +263,7 @@ struct FP_Game bool build_mode; bool build_mode_can_place_building; bool debug_ui; + Dqn_usize build_mode_building_index; }; struct FP_GameAStarNode @@ -281,3 +282,15 @@ struct FP_GameFindClosestEntityResult Dqn_V2 pos; }; +struct FP_GamePlaceableBuilding +{ + FP_EntityType type; + uint32_t state; +}; + +FP_GamePlaceableBuilding const PLACEABLE_BUILDINGS[] = { + {FP_EntityType_AirportTerry, FP_EntityAirportTerryState_Idle}, + {FP_EntityType_ChurchTerry, FP_EntityChurchTerryState_Idle}, + {FP_EntityType_ClubTerry, FP_EntityClubTerryState_Idle}, + {FP_EntityType_KennelTerry, FP_EntityKennelTerryState_Idle}, +};