fp: Add inventory and consume coins on building purchase

This commit is contained in:
doyle 2023-10-07 15:04:37 +11:00
parent 4628731bef
commit fd0612ac1c
3 changed files with 116 additions and 57 deletions

View File

@ -1682,11 +1682,23 @@ void FP_Update(TELY_Platform *platform, FP_Game *game, TELY_PlatformInput *input
if (entity->flags & FP_GameEntityFlag_CameraTracking) 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->camera.world_pos = FP_Game_CalcEntityWorldPos(game, entity->handle) - Dqn_V2_InitV2I(platform->core.window_size) * .5f;
game->build_mode_can_place_building = false; FP_GamePlaceableBuilding placeable_building = PLACEABLE_BUILDINGS[game->build_mode_building_index];
if (game->active_menu == FP_GameActiveMenu_Build) { game->build_mode_can_place_building = false;
FP_GamePlaceableBuilding placeable_building = PLACEABLE_BUILDINGS[game->build_mode_building_index];
Dqn_Rect dest_rect = FP_Game_GetBuildingPlacementRectForEntity(game, placeable_building, entity->handle); uint8_t *inventory_count = nullptr;
Dqn_V2 placement_pos = Dqn_Rect_Center(dest_rect); if (placeable_building.type == FP_EntityType_ChurchTerry)
inventory_count = &entity->inventory.churchs;
else if (placeable_building.type == FP_EntityType_KennelTerry)
inventory_count = &entity->inventory.kennels;
else if (placeable_building.type == FP_EntityType_ClubTerry)
inventory_count = &entity->inventory.clubs;
else if (placeable_building.type == FP_EntityType_AirportTerry)
inventory_count = &entity->inventory.airports;
bool have_building_inventory = inventory_count && (*inventory_count) > 0;
if (have_building_inventory && game->active_menu == FP_GameActiveMenu_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 = {}; for (FP_GameEntityIterator zone_it = {};
FP_Game_DFSPreOrderWalkEntityTree(game, &zone_it, game->root_entity); FP_Game_DFSPreOrderWalkEntityTree(game, &zone_it, game->root_entity);
@ -1728,6 +1740,8 @@ void FP_Update(TELY_Platform *platform, FP_Game *game, TELY_PlatformInput *input
DQN_ASSERT(placeable_building.type == FP_EntityType_KennelTerry); DQN_ASSERT(placeable_building.type == FP_EntityType_KennelTerry);
FP_Entity_CreateKennelTerry(game, placement_pos, "Kennel Terry"); FP_Entity_CreateKennelTerry(game, placement_pos, "Kennel Terry");
} }
(*inventory_count)--;
} }
} }
} }
@ -2203,15 +2217,17 @@ void FP_Render(FP_Game *game, TELY_Platform *platform, TELY_Renderer *renderer)
Dqn_V2 player_pos = FP_Game_CalcEntityWorldPos(game, game->player); Dqn_V2 player_pos = FP_Game_CalcEntityWorldPos(game, game->player);
{ {
struct FP_MerchantToMenuMapping { struct FP_MerchantToMenuMapping {
FP_GameEntityHandle merchant; FP_GameEntityHandle merchant;
Dqn_String8 menu_anim; Dqn_String8 menu_anim;
Dqn_String8 building; Dqn_String8 building;
Dqn_V2 building_offset01; Dqn_V2 building_offset01;
uint8_t *inventory_count;
uint32_t *base_price;
} merchants[] = { } merchants[] = {
{game->merchant_terry, g_anim_names.merchant_terry_menu, g_anim_names.club_terry_dark, Dqn_V2_InitNx2(0.015f, +0.04f)}, {game->merchant_terry, g_anim_names.merchant_terry_menu, g_anim_names.club_terry_dark, Dqn_V2_InitNx2(0.015f, +0.04f), &player->inventory.clubs, &player->inventory.clubs_base_price},
{game->merchant_graveyard, g_anim_names.merchant_graveyard_menu, g_anim_names.airport_terry, Dqn_V2_InitNx2(0, -0.1f)}, {game->merchant_graveyard, g_anim_names.merchant_graveyard_menu, g_anim_names.church_terry_dark, Dqn_V2_InitNx2(0.04f, -0.15f), &player->inventory.churchs, &player->inventory.churchs_base_price},
{game->merchant_gym, g_anim_names.merchant_gym_menu, g_anim_names.church_terry_dark, Dqn_V2_InitNx2(0.04f, -0.15f)}, {game->merchant_gym, g_anim_names.merchant_gym_menu, g_anim_names.kennel_terry, Dqn_V2_InitNx2(0, +0), &player->inventory.kennels, &player->inventory.kennels_base_price},
{game->merchant_phone_company, g_anim_names.merchant_phone_company_menu, g_anim_names.kennel_terry, Dqn_V2_InitNx2(0, +0)}, {game->merchant_phone_company, g_anim_names.merchant_phone_company_menu, g_anim_names.airport_terry, Dqn_V2_InitNx2(0, -0.1f), &player->inventory.airports, &player->inventory.airports_base_price},
}; };
bool activated_merchant = false; bool activated_merchant = false;
@ -2222,11 +2238,10 @@ void FP_Render(FP_Game *game, TELY_Platform *platform, TELY_Renderer *renderer)
if (dist_squared > DQN_SQUARED(FP_Game_MetersToPixelsNx1(game, 4))) if (dist_squared > DQN_SQUARED(FP_Game_MetersToPixelsNx1(game, 4)))
continue; continue;
activated_merchant = true;
// NOTE: Render animated merchant menu ============================= // NOTE: Render animated merchant menu =============================
uint64_t const buy_duration_ms = 500; activated_merchant = true;
Dqn_Rect merchant_menu_rect = {}; Dqn_Rect merchant_menu_rect = {};
{ {
FP_GameRenderSprite *sprite = &game->player_merchant_menu; FP_GameRenderSprite *sprite = &game->player_merchant_menu;
if (!sprite->asset.anim || sprite->asset.anim->label != mapping.menu_anim) { if (!sprite->asset.anim || sprite->asset.anim->label != mapping.menu_anim) {
@ -2263,35 +2278,51 @@ void FP_Render(FP_Game *game, TELY_Platform *platform, TELY_Renderer *renderer)
}; };
// NOTE: Render the merchant button for buildings ================== // NOTE: Render the merchant button for buildings ==================
uint64_t const buy_duration_ms = 500;
bool const have_enough_coins = player->coins >= *mapping.base_price;
{ {
// NOTE: Buy trigger + animation =============================== // NOTE: Buy trigger + animation ===============================
{ {
TELY_PlatformInputScanCode key = TELY_PlatformInputScanCode_J; TELY_PlatformInputScanCode key = TELY_PlatformInputScanCode_J;
bool trigger_buy_anim = false; bool trigger_buy_anim = false;
if (TELY_Platform_InputScanCodeIsPressed(input, key)) {
game->player_trigger_purchase_building_timestamp = game->clock_ms + buy_duration_ms; if (have_enough_coins) {
} else if (TELY_Platform_InputScanCodeIsDown(input, key)) { if (TELY_Platform_InputScanCodeIsPressed(input, key)) {
trigger_buy_anim = true; game->player_trigger_purchase_building_timestamp = game->clock_ms + buy_duration_ms;
if (game->clock_ms > game->player_trigger_purchase_building_timestamp) { } else if (TELY_Platform_InputScanCodeIsDown(input, key)) {
// TODO(doyle): Do buy logic trigger_buy_anim = true;
if (game->clock_ms > game->player_trigger_purchase_building_timestamp)
game->player_trigger_purchase_building_timestamp = game->clock_ms;
} else if (TELY_Platform_InputScanCodeIsReleased(input, key)) {
if (game->clock_ms > game->player_trigger_purchase_building_timestamp) {
if (mapping.inventory_count) {
player->coins -= *mapping.base_price;
*mapping.base_price *= 2;
(*mapping.inventory_count)++;
}
} else {
game->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;
Dqn_f32 buy_t = DQN_MIN(elapsed_time / DQN_CAST(Dqn_f32)buy_duration_ms, 1.f);
Dqn_Rect buy_lerp_rect = {};
buy_lerp_rect.pos = Dqn_Rect_InterpolatedPoint(merchant_menu_rect, Dqn_V2_InitNx2(0.297f, 0.215f));
buy_lerp_rect.size.w = (merchant_menu_rect.size.w * 0.38f) * buy_t;
buy_lerp_rect.size.h = merchant_menu_rect.size.h * .611f;
TELY_Render_RectColourV4(renderer, buy_lerp_rect, TELY_RenderShapeMode_Fill, TELY_Colour_V4Alpha(TELY_COLOUR_BLUE_CADET_V4, 0.5f));
} }
} }
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;
Dqn_f32 buy_t = DQN_MIN(elapsed_time / DQN_CAST(Dqn_f32)buy_duration_ms, 1.f);
Dqn_Rect buy_lerp_rect = {};
buy_lerp_rect.pos = Dqn_Rect_InterpolatedPoint(merchant_menu_rect, Dqn_V2_InitNx2(0.297f, 0.215f));
buy_lerp_rect.size.w = (merchant_menu_rect.size.w * 0.38f) * buy_t;
buy_lerp_rect.size.h = merchant_menu_rect.size.h * .611f;
TELY_Render_RectColourV4(renderer, buy_lerp_rect, TELY_RenderShapeMode_Fill, TELY_Colour_V4Alpha(TELY_COLOUR_BLUE_CADET_V4, 0.5f));
}
} }
// NOTE: Render the (A) button ================================= // NOTE: Render the (A) button =================================
Dqn_V4 tex_mod_colour = have_enough_coins ? TELY_COLOUR_WHITE_V4 : TELY_Colour_V4Alpha(TELY_COLOUR_RED_TOMATO_V4, .5f);
{ {
TELY_AssetSpriteAnimation *anim = TELY_Asset_GetSpriteAnimation(&game->atlas_sprite_sheet, g_anim_names.merchant_button_a); TELY_AssetSpriteAnimation *anim = TELY_Asset_GetSpriteAnimation(&game->atlas_sprite_sheet, g_anim_names.merchant_button_a);
Dqn_Rect button_rect = game->atlas_sprite_sheet.rects.data[anim->index]; Dqn_Rect button_rect = game->atlas_sprite_sheet.rects.data[anim->index];
@ -2304,9 +2335,9 @@ void FP_Render(FP_Game *game, TELY_Platform *platform, TELY_Renderer *renderer)
dest_rect, dest_rect,
Dqn_V2_Zero /*rotate origin*/, Dqn_V2_Zero /*rotate origin*/,
0.f /*rotation*/, 0.f /*rotation*/,
TELY_COLOUR_WHITE_V4); tex_mod_colour);
TELY_Render_TextF(renderer, Dqn_Rect_InterpolatedPoint(dest_rect, Dqn_V2_InitNx2(0.5f, -1)), Dqn_V2_InitNx2(0.5, 0.f), "$2"); TELY_Render_TextF(renderer, Dqn_Rect_InterpolatedPoint(dest_rect, Dqn_V2_InitNx2(0.5f, -1)), Dqn_V2_InitNx2(0.5, 0.f), "$%u", *mapping.base_price);
} }
// NOTE: Render the merchant shop item building ================ // NOTE: Render the merchant shop item building ================
@ -2322,7 +2353,7 @@ void FP_Render(FP_Game *game, TELY_Platform *platform, TELY_Renderer *renderer)
dest_rect, dest_rect,
Dqn_V2_Zero /*rotate origin*/, Dqn_V2_Zero /*rotate origin*/,
0.f /*rotation*/, 0.f /*rotation*/,
TELY_COLOUR_WHITE_V4); tex_mod_colour);
} }
} }
@ -2385,27 +2416,26 @@ void FP_Render(FP_Game *game, TELY_Platform *platform, TELY_Renderer *renderer)
} }
// NOTE: Render player avatar HUD ========================================== // NOTE: Render player avatar HUD ==========================================
Dqn_f32 ui_start_y = 32.f; Dqn_Rect player_avatar_rect = {};
player_avatar_rect.pos = Dqn_V2_InitNx1(32.f);
{ {
TELY_Render_PushTransform(renderer, Dqn_M2x3_Identity()); TELY_Render_PushTransform(renderer, Dqn_M2x3_Identity());
DQN_DEFER { TELY_Render_PopTransform(renderer); }; DQN_DEFER { TELY_Render_PopTransform(renderer); };
FP_EntityRenderData render_data = FP_Entity_GetRenderData(game, FP_EntityType_Terry, FP_EntityTerryState_Idle, FP_GameDirection_Down); FP_EntityRenderData render_data = FP_Entity_GetRenderData(game, FP_EntityType_Terry, FP_EntityTerryState_Idle, FP_GameDirection_Down);
Dqn_Rect dest = {}; player_avatar_rect.size = render_data.render_size;
dest.size = render_data.render_size;
dest.pos.y = ui_start_y;
dest.pos.x = ui_start_y;
TELY_Render_TextureColourV4(renderer, TELY_Render_TextureColourV4(renderer,
render_data.sheet->tex_handle, render_data.sheet->tex_handle,
render_data.sheet_rect, render_data.sheet_rect,
dest, player_avatar_rect,
Dqn_V2_Zero, Dqn_V2_Zero,
0.f, 0.f,
TELY_COLOUR_WHITE_V4); TELY_COLOUR_WHITE_V4);
TELY_Render_PushFont(renderer, game->talkco_font); TELY_Render_PushFont(renderer, game->talkco_font);
Dqn_V2 next_pos = Dqn_Rect_InterpolatedPoint(dest, Dqn_V2_InitNx2(1.f, 0)); DQN_DEFER { TELY_Render_PopFont(renderer); };
Dqn_V2 next_pos = Dqn_Rect_InterpolatedPoint(player_avatar_rect, Dqn_V2_InitNx2(1.f, 0));
TELY_Render_TextF(renderer, next_pos, Dqn_V2_Zero, "Terry"); TELY_Render_TextF(renderer, next_pos, Dqn_V2_Zero, "Terry");
Dqn_f32 font_height = TELY_Render_FontHeight(renderer, &platform->assets); Dqn_f32 font_height = TELY_Render_FontHeight(renderer, &platform->assets);
@ -2432,16 +2462,14 @@ void FP_Render(FP_Game *game, TELY_Platform *platform, TELY_Renderer *renderer)
next_pos.y += font_height; next_pos.y += font_height;
TELY_Render_TextF(renderer, next_pos, Dqn_V2_Zero, "[J|K] Melee/Range"); TELY_Render_TextF(renderer, next_pos, Dqn_V2_Zero, "[J|K] Melee/Range");
TELY_Render_PopFont(renderer);
} }
if (!FP_Game_IsNilEntityHandle(game, game->clicked_entity) && game->active_menu == FP_GameActiveMenu_Build) { if (!FP_Game_IsNilEntityHandle(game, game->clicked_entity)) {
// NOTE: Render building blueprint ========================================================= // NOTE: Render building blueprint =========================================================
{ if (game->active_menu == FP_GameActiveMenu_Build) {
FP_GameEntity *entity = FP_Game_GetEntity(game, game->clicked_entity); FP_GameEntity *entity = FP_Game_GetEntity(game, game->clicked_entity);
FP_GamePlaceableBuilding placeable_building = PLACEABLE_BUILDINGS[game->build_mode_building_index]; 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); 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_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 ?
@ -2450,8 +2478,8 @@ void FP_Render(FP_Game *game, TELY_Platform *platform, TELY_Renderer *renderer)
TELY_Render_RectColourV4(renderer, dest_rect, TELY_RenderShapeMode_Fill, TELY_Colour_V4Alpha(TELY_COLOUR_BLUE_CADET_V4, 0.5f)); TELY_Render_RectColourV4(renderer, dest_rect, TELY_RenderShapeMode_Fill, TELY_Colour_V4Alpha(TELY_COLOUR_BLUE_CADET_V4, 0.5f));
TELY_Render_TextureColourV4(renderer, TELY_Render_TextureColourV4(renderer,
club_terry_render_data.sheet->tex_handle, render_data.sheet->tex_handle,
club_terry_render_data.sheet_rect, render_data.sheet_rect,
dest_rect, dest_rect,
Dqn_V2_Zero /*rotate origin*/, Dqn_V2_Zero /*rotate origin*/,
0.f /*rotation*/, 0.f /*rotation*/,
@ -2462,18 +2490,16 @@ void FP_Render(FP_Game *game, TELY_Platform *platform, TELY_Renderer *renderer)
{ {
TELY_Render_PushTransform(renderer, Dqn_M2x3_Identity()); TELY_Render_PushTransform(renderer, Dqn_M2x3_Identity());
DQN_DEFER { TELY_Render_PopTransform(renderer); }; 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->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 building_ui_size = 64.f;
Dqn_f32 padding = 10.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 = player_avatar_rect.pos.x;
Dqn_f32 start_x = (platform->core.window_size.x * .5f) - (total_size * .5f);
DQN_FOR_UINDEX (building_index, DQN_ARRAY_UCOUNT(PLACEABLE_BUILDINGS)) { DQN_FOR_UINDEX (building_index, DQN_ARRAY_UCOUNT(PLACEABLE_BUILDINGS)) {
FP_GamePlaceableBuilding building = PLACEABLE_BUILDINGS[building_index]; FP_GamePlaceableBuilding building = PLACEABLE_BUILDINGS[building_index];
FP_EntityRenderData render_data = FP_Entity_GetRenderData(game, building.type, building.state, FP_GameDirection_Down); 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), Dqn_Rect rect = Dqn_Rect_InitNx4(start_x + (building_index * building_ui_size) + (padding * building_index),
ui_start_y, Dqn_Rect_InterpolatedPoint(player_avatar_rect, Dqn_V2_InitNx2(0, 1.70)).y,
building_ui_size, building_ui_size,
building_ui_size); building_ui_size);
@ -2493,6 +2519,21 @@ void FP_Render(FP_Game *game, TELY_Platform *platform, TELY_Renderer *renderer)
0.f /*rotation*/, 0.f /*rotation*/,
texture_colour); texture_colour);
uint32_t building_count = 0;
if (building.type == FP_EntityType_ClubTerry)
building_count = player->inventory.clubs;
else if (building.type == FP_EntityType_AirportTerry)
building_count = player->inventory.airports;
else if (building.type == FP_EntityType_KennelTerry)
building_count = player->inventory.kennels;
else if (building.type == FP_EntityType_ChurchTerry)
building_count = player->inventory.churchs;
TELY_Render_PushFont(renderer, game->talkco_font);
DQN_DEFER { TELY_Render_PopFont(renderer); };
Dqn_V2 label_p = Dqn_Rect_InterpolatedPoint(rect, Dqn_V2_InitNx2(0.5f, 1.25f));
TELY_Render_TextF(renderer, label_p, Dqn_V2_InitNx2(0.5f, 0.5f), "x %u", building_count);
TELY_RenderCommandRect *cmd = TELY_Render_RectColourV4(renderer, rect, TELY_RenderShapeMode_Line, outline_colour); TELY_RenderCommandRect *cmd = TELY_Render_RectColourV4(renderer, rect, TELY_RenderShapeMode_Line, outline_colour);
cmd->thickness = 2.f; cmd->thickness = 2.f;

View File

@ -446,6 +446,11 @@ static FP_GameEntityHandle FP_Entity_CreateTerry(FP_Game *game, Dqn_V2 pos, DQN_
entity->flags |= FP_GameEntityFlag_CameraTracking; entity->flags |= FP_GameEntityFlag_CameraTracking;
entity->terry_mobile_data_plan_cap = DQN_KILOBYTES(16); entity->terry_mobile_data_plan_cap = DQN_KILOBYTES(16);
entity->terry_mobile_data_plan = entity->terry_mobile_data_plan_cap; entity->terry_mobile_data_plan = entity->terry_mobile_data_plan_cap;
entity->inventory.airports_base_price = 4;
entity->inventory.churchs_base_price = 2;
entity->inventory.kennels_base_price = 2;
entity->inventory.clubs_base_price = 2;
return result; return result;
} }

View File

@ -133,6 +133,19 @@ struct FP_GameRenderSprite
uint64_t started_at_clock_ms; uint64_t started_at_clock_ms;
}; };
struct FP_GameInventory
{
uint32_t airports_base_price;
uint32_t clubs_base_price;
uint32_t kennels_base_price;
uint32_t churchs_base_price;
uint8_t airports;
uint8_t clubs;
uint8_t kennels;
uint8_t churchs;
};
struct FP_GameEntity struct FP_GameEntity
{ {
FP_GameEntity *next; FP_GameEntity *next;
@ -198,6 +211,7 @@ struct FP_GameEntity
Dqn_usize terry_mobile_data_plan_cap; Dqn_usize terry_mobile_data_plan_cap;
Dqn_usize coins; Dqn_usize coins;
FP_GameInventory inventory;
}; };
struct FP_GameEntityIterator struct FP_GameEntityIterator
@ -286,7 +300,6 @@ struct FP_Game
FP_GameActiveMenu active_menu; FP_GameActiveMenu active_menu;
bool build_mode_can_place_building; bool build_mode_can_place_building;
Dqn_usize build_mode_building_index; Dqn_usize build_mode_building_index;
}; };
struct FP_GameAStarNode struct FP_GameAStarNode