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)
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;
if (game->active_menu == FP_GameActiveMenu_Build) {
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);
FP_GamePlaceableBuilding placeable_building = PLACEABLE_BUILDINGS[game->build_mode_building_index];
game->build_mode_can_place_building = false;
uint8_t *inventory_count = nullptr;
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 = {};
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);
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);
{
struct FP_MerchantToMenuMapping {
FP_GameEntityHandle merchant;
Dqn_String8 menu_anim;
Dqn_String8 building;
Dqn_V2 building_offset01;
FP_GameEntityHandle merchant;
Dqn_String8 menu_anim;
Dqn_String8 building;
Dqn_V2 building_offset01;
uint8_t *inventory_count;
uint32_t *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)},
{game->merchant_graveyard, g_anim_names.merchant_graveyard_menu, g_anim_names.airport_terry, Dqn_V2_InitNx2(0, -0.1f)},
{game->merchant_gym, g_anim_names.merchant_gym_menu, g_anim_names.church_terry_dark, Dqn_V2_InitNx2(0.04f, -0.15f)},
{game->merchant_phone_company, g_anim_names.merchant_phone_company_menu, g_anim_names.kennel_terry, Dqn_V2_InitNx2(0, +0)},
{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.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.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.airport_terry, Dqn_V2_InitNx2(0, -0.1f), &player->inventory.airports, &player->inventory.airports_base_price},
};
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)))
continue;
activated_merchant = true;
// NOTE: Render animated merchant menu =============================
uint64_t const buy_duration_ms = 500;
Dqn_Rect merchant_menu_rect = {};
activated_merchant = true;
Dqn_Rect merchant_menu_rect = {};
{
FP_GameRenderSprite *sprite = &game->player_merchant_menu;
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 ==================
uint64_t const buy_duration_ms = 500;
bool const have_enough_coins = player->coins >= *mapping.base_price;
{
// NOTE: Buy trigger + animation ===============================
{
TELY_PlatformInputScanCode key = TELY_PlatformInputScanCode_J;
bool trigger_buy_anim = false;
if (TELY_Platform_InputScanCodeIsPressed(input, key)) {
game->player_trigger_purchase_building_timestamp = game->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) {
// TODO(doyle): Do buy logic
if (have_enough_coins) {
if (TELY_Platform_InputScanCodeIsPressed(input, key)) {
game->player_trigger_purchase_building_timestamp = game->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;
} 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 =================================
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);
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,
Dqn_V2_Zero /*rotate origin*/,
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 ================
@ -2322,7 +2353,7 @@ void FP_Render(FP_Game *game, TELY_Platform *platform, TELY_Renderer *renderer)
dest_rect,
Dqn_V2_Zero /*rotate origin*/,
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 ==========================================
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());
DQN_DEFER { TELY_Render_PopTransform(renderer); };
FP_EntityRenderData render_data = FP_Entity_GetRenderData(game, FP_EntityType_Terry, FP_EntityTerryState_Idle, FP_GameDirection_Down);
Dqn_Rect dest = {};
dest.size = render_data.render_size;
dest.pos.y = ui_start_y;
dest.pos.x = ui_start_y;
player_avatar_rect.size = render_data.render_size;
TELY_Render_TextureColourV4(renderer,
render_data.sheet->tex_handle,
render_data.sheet_rect,
dest,
player_avatar_rect,
Dqn_V2_Zero,
0.f,
TELY_COLOUR_WHITE_V4);
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");
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;
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 =========================================================
{
if (game->active_menu == FP_GameActiveMenu_Build) {
FP_GameEntity *entity = FP_Game_GetEntity(game, game->clicked_entity);
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_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_TextureColourV4(renderer,
club_terry_render_data.sheet->tex_handle,
club_terry_render_data.sheet_rect,
render_data.sheet->tex_handle,
render_data.sheet_rect,
dest_rect,
Dqn_V2_Zero /*rotate origin*/,
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());
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 building_ui_size = 64.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_f32 start_x = player_avatar_rect.pos.x;
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),
ui_start_y,
Dqn_Rect_InterpolatedPoint(player_avatar_rect, Dqn_V2_InitNx2(0, 1.70)).y,
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*/,
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);
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->terry_mobile_data_plan_cap = DQN_KILOBYTES(16);
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;
}

View File

@ -133,6 +133,19 @@ struct FP_GameRenderSprite
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
{
FP_GameEntity *next;
@ -198,6 +211,7 @@ struct FP_GameEntity
Dqn_usize terry_mobile_data_plan_cap;
Dqn_usize coins;
FP_GameInventory inventory;
};
struct FP_GameEntityIterator
@ -286,7 +300,6 @@ struct FP_Game
FP_GameActiveMenu active_menu;
bool build_mode_can_place_building;
Dqn_usize build_mode_building_index;
};
struct FP_GameAStarNode