fp: Add building selector UI
This commit is contained in:
parent
4bcbc61721
commit
45d981099c
2
External/tely
vendored
2
External/tely
vendored
@ -1 +1 @@
|
|||||||
Subproject commit 4ccbde6fecb8ea17f9cde73df7c06da466311f3c
|
Subproject commit 8576ce00dc74f817b346994efca8610001a8eafb
|
136
feely_pona.cpp
136
feely_pona.cpp
@ -5,39 +5,40 @@
|
|||||||
|
|
||||||
Dqn_f32 const PHYSICS_STEP = 1 / 60.f;
|
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 = {};
|
Dqn_Rect result = {};
|
||||||
FP_GameEntity *entity = FP_Game_GetEntity(game, handle);
|
FP_GameEntity *entity = FP_Game_GetEntity(game, handle);
|
||||||
if (FP_Game_IsNilEntity(entity))
|
if (FP_Game_IsNilEntity(entity))
|
||||||
return result;
|
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_Rect box = FP_Game_CalcEntityWorldHitBox(game, entity->handle);
|
||||||
Dqn_V2 build_p = {};
|
Dqn_V2 build_p = {};
|
||||||
switch (entity->direction) {
|
switch (entity->direction) {
|
||||||
case FP_GameDirection_Up: {
|
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;
|
} break;
|
||||||
|
|
||||||
case FP_GameDirection_Down: {
|
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;
|
} break;
|
||||||
|
|
||||||
case FP_GameDirection_Left: {
|
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;
|
} break;
|
||||||
|
|
||||||
case FP_GameDirection_Right: {
|
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;
|
} break;
|
||||||
|
|
||||||
case FP_GameDirection_Count: DQN_INVALID_CODE_PATH; break;
|
case FP_GameDirection_Count: DQN_INVALID_CODE_PATH; break;
|
||||||
}
|
}
|
||||||
|
|
||||||
result.size = club_terry_render_data.render_size;
|
result.size = render_data.render_size;
|
||||||
result.pos = build_p - (club_terry_render_data.render_size * .5f);
|
result.pos = build_p - (render_data.render_size * .5f);
|
||||||
return result;
|
return result;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -1248,6 +1249,25 @@ void FP_Update(TELY_Platform *platform, FP_Game *game, TELY_PlatformInput *input
|
|||||||
if (game->clicked_entity.id) {
|
if (game->clicked_entity.id) {
|
||||||
if (TELY_Platform_InputScanCodeIsPressed(input, TELY_PlatformInputScanCode_Delete))
|
if (TELY_Platform_InputScanCodeIsPressed(input, TELY_PlatformInputScanCode_Delete))
|
||||||
FP_Game_DeleteEntity(game, game->clicked_entity);
|
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 {
|
} else {
|
||||||
Dqn_f32 pan_speed = 5.f;
|
Dqn_f32 pan_speed = 5.f;
|
||||||
if (TELY_Platform_InputScanCodeIsDown(input, TELY_PlatformInputScanCode_Space))
|
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 (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;
|
game->build_mode = !game->build_mode;
|
||||||
|
|
||||||
if (entity->flags & FP_GameEntityFlag_CameraTracking)
|
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;
|
game->build_mode_can_place_building = false;
|
||||||
if (game->build_mode) {
|
if (game->build_mode) {
|
||||||
|
|
||||||
Dqn_Rect dest_rect = FP_Game_GetBuildingPlacementRectForEntity(game, entity->handle);
|
FP_GamePlaceableBuilding placeable_building = PLACEABLE_BUILDINGS[game->build_mode_building_index];
|
||||||
Dqn_V2 placement_pos = Dqn_Rect_Center(dest_rect);
|
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 = {};
|
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)
|
if ((zone->flags & FP_GameEntityFlag_BuildZone) == 0)
|
||||||
continue;
|
continue;
|
||||||
|
|
||||||
Dqn_Rect zone_hit_box = FP_Game_CalcEntityWorldHitBox(game, zone->handle);
|
|
||||||
zone_hit_box.pos += dest_rect.size * .5f;
|
zone_hit_box.pos += dest_rect.size * .5f;
|
||||||
zone_hit_box.size -= dest_rect.size;
|
zone_hit_box.size -= dest_rect.size;
|
||||||
zone_hit_box.size = Dqn_V2_Max(zone_hit_box.size, Dqn_V2_Zero);
|
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 &&
|
if (game->build_mode_can_place_building &&
|
||||||
TELY_Platform_InputScanCodeIsPressed(input, TELY_PlatformInputScanCode_E)) {
|
TELY_Platform_InputScanCodeIsPressed(input, TELY_PlatformInputScanCode_F)) {
|
||||||
FP_Entity_CreateClubTerry(game, placement_pos, "Club Terry");
|
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) {
|
if (game->debug_ui) {
|
||||||
|
// NOTE: Render waypoint entities ======================================================
|
||||||
if (entity->flags & FP_GameEntityFlag_MobSpawner) {
|
if (entity->flags & FP_GameEntityFlag_MobSpawner) {
|
||||||
Dqn_V2 start = world_pos;
|
Dqn_V2 start = world_pos;
|
||||||
for (FP_GameEntity *waypoint_entity = entity->first_child; waypoint_entity; waypoint_entity = waypoint_entity->next) {
|
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 =========================================================
|
// 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());
|
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);
|
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_Text(renderer, label_p, Dqn_V2_InitNx2(0.5f, 0.5f), DQN_STRING8("Build Mode"));
|
||||||
TELY_Render_PopTransform(renderer);
|
TELY_Render_PopTransform(renderer);
|
||||||
|
|
||||||
FP_EntityRenderData club_terry_render_data = FP_Entity_GetRenderData(game, FP_EntityType_ClubTerry, FP_EntityClubTerryState_Idle, entity->direction);
|
FP_GamePlaceableBuilding placeable_building = PLACEABLE_BUILDINGS[game->build_mode_building_index];
|
||||||
Dqn_Rect dest_rect = FP_Game_GetBuildingPlacementRectForEntity(game, entity->handle);
|
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 ?
|
Dqn_V4 colour = game->build_mode_can_place_building ?
|
||||||
TELY_Colour_V4Alpha(TELY_COLOUR_WHITE_V4, 0.5f) :
|
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);
|
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;
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -23,8 +23,8 @@ struct FP_Meters
|
|||||||
|
|
||||||
struct FP_GlobalAnimations
|
struct FP_GlobalAnimations
|
||||||
{
|
{
|
||||||
Dqn_String8 airport_terry_alive = DQN_STRING8("airport_terry_alive");
|
Dqn_String8 airport_terry = DQN_STRING8("airport_terry");
|
||||||
Dqn_String8 airport_terry_dark = DQN_STRING8("airport_terry_dark");
|
Dqn_String8 airport_terry_plane = DQN_STRING8("airport_terry_plane");
|
||||||
|
|
||||||
Dqn_String8 catfish_attack_down = DQN_STRING8("catfish_attack_down");
|
Dqn_String8 catfish_attack_down = DQN_STRING8("catfish_attack_down");
|
||||||
Dqn_String8 catfish_attack_side = DQN_STRING8("catfish_attack_side");
|
Dqn_String8 catfish_attack_side = DQN_STRING8("catfish_attack_side");
|
||||||
|
@ -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;
|
FP_EntityAirportTerryState state = DQN_CAST(FP_EntityAirportTerryState)raw_state;
|
||||||
switch (state) {
|
switch (state) {
|
||||||
case FP_EntityAirportTerryState_Nil: break;
|
case FP_EntityAirportTerryState_Nil: break;
|
||||||
case FP_EntityAirportTerryState_Idle: result.anim_name = g_anim_names.airport_terry_dark; 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_alive; break;
|
case FP_EntityAirportTerryState_FlyPassenger: result.anim_name = g_anim_names.airport_terry; break;
|
||||||
}
|
}
|
||||||
} break;
|
} break;
|
||||||
|
|
||||||
@ -219,7 +219,7 @@ FP_EntityRenderData FP_Entity_GetRenderData(FP_Game *game, FP_EntityType type, u
|
|||||||
} break;
|
} break;
|
||||||
|
|
||||||
case FP_EntityType_KennelTerry: {
|
case FP_EntityType_KennelTerry: {
|
||||||
result.height.meters = 4.f;
|
result.height.meters = 3.f;
|
||||||
FP_EntityKennelTerryState state = DQN_CAST(FP_EntityKennelTerryState)raw_state;
|
FP_EntityKennelTerryState state = DQN_CAST(FP_EntityKennelTerryState)raw_state;
|
||||||
switch (state) {
|
switch (state) {
|
||||||
case FP_EntityKennelTerryState_Nil: break;
|
case FP_EntityKennelTerryState_Nil: break;
|
||||||
|
@ -263,6 +263,7 @@ struct FP_Game
|
|||||||
bool build_mode;
|
bool build_mode;
|
||||||
bool build_mode_can_place_building;
|
bool build_mode_can_place_building;
|
||||||
bool debug_ui;
|
bool debug_ui;
|
||||||
|
Dqn_usize build_mode_building_index;
|
||||||
};
|
};
|
||||||
|
|
||||||
struct FP_GameAStarNode
|
struct FP_GameAStarNode
|
||||||
@ -281,3 +282,15 @@ struct FP_GameFindClosestEntityResult
|
|||||||
Dqn_V2 pos;
|
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},
|
||||||
|
};
|
||||||
|
Loading…
Reference in New Issue
Block a user