6 Commits
8 changed files with 207 additions and 100 deletions

No files matched your search

BIN
View File
Binary file not shown.
BIN
View File
Binary file not shown.
Binary file not shown.
Binary file not shown.
+1 -1
+186 -90
View File
@@ -550,6 +550,8 @@ void TELY_DLL_Init(void *user_data)
controls->build_mode.scan_code = TELY_PlatformInputScanCode_H; controls->build_mode.scan_code = TELY_PlatformInputScanCode_H;
controls->strafe.scan_code = TELY_PlatformInputScanCode_L; controls->strafe.scan_code = TELY_PlatformInputScanCode_L;
controls->dash.scan_code = TELY_PlatformInputScanCode_N; controls->dash.scan_code = TELY_PlatformInputScanCode_N;
controls->buy_building.scan_code = TELY_PlatformInputScanCode_U;
controls->buy_upgrade.scan_code = TELY_PlatformInputScanCode_I;
} }
struct FP_GetClosestPortalMonkeyResult struct FP_GetClosestPortalMonkeyResult
@@ -650,7 +652,7 @@ void FP_EntityActionStateMachine(FP_Game *game, TELY_Audio *audio, TELY_Platform
} }
if (we_are_clicked_entity) { if (we_are_clicked_entity) {
if (game->play.in_game_menu == FP_GameInGameMenu_Nil) { if (game->play.in_game_menu != FP_GameInGameMenu_Build) {
bool picked_up_monkey_this_frame = false; bool picked_up_monkey_this_frame = false;
if (FP_Game_IsNilEntityHandle(game, entity->carried_monkey)) { if (FP_Game_IsNilEntityHandle(game, entity->carried_monkey)) {
// NOTE: Check if we are nearby a monkey and picking it up // NOTE: Check if we are nearby a monkey and picking it up
@@ -718,7 +720,7 @@ void FP_EntityActionStateMachine(FP_Game *game, TELY_Audio *audio, TELY_Platform
if (we_are_clicked_entity) { if (we_are_clicked_entity) {
bool picked_up_monkey_this_frame = false; bool picked_up_monkey_this_frame = false;
if (game->play.in_game_menu == FP_GameInGameMenu_Nil) { if (game->play.in_game_menu != FP_GameInGameMenu_Build) {
if (FP_Game_IsNilEntityHandle(game, entity->carried_monkey)) { if (FP_Game_IsNilEntityHandle(game, entity->carried_monkey)) {
// NOTE: Check if we are nearby a monkey and picking it up // NOTE: Check if we are nearby a monkey and picking it up
FP_GetClosestPortalMonkeyResult closest_monkey = FP_GetClosestPortalMonkey(game, entity->handle); FP_GetClosestPortalMonkeyResult closest_monkey = FP_GetClosestPortalMonkey(game, entity->handle);
@@ -975,9 +977,8 @@ void FP_EntityActionStateMachine(FP_Game *game, TELY_Audio *audio, TELY_Platform
case FP_EntityClingerState_Death: { case FP_EntityClingerState_Death: {
if (entering_new_state) { if (entering_new_state) {
TELY_AssetAnimatedSprite sprite = TELY_Asset_MakeAnimatedSprite(sheet, g_anim_names.clinger_death, TELY_AssetFlip_No); uint64_t duration_ms = render_data.sprite.anim->count * render_data.sprite.anim->ms_per_frame;
uint64_t duration_ms = sprite.anim->count * sprite.anim->ms_per_frame; FP_Game_EntityActionReset(game, entity->handle, duration_ms, render_data.sprite);
FP_Game_EntityActionReset(game, entity->handle, duration_ms, sprite);
entity->local_hit_box_size = {}; entity->local_hit_box_size = {};
} }
@@ -1240,9 +1241,8 @@ void FP_EntityActionStateMachine(FP_Game *game, TELY_Audio *audio, TELY_Platform
case FP_EntityCatfishState_Run: { case FP_EntityCatfishState_Run: {
if (entering_new_state || action->sprite.anim->label != render_data.anim_name) { if (entering_new_state || action->sprite.anim->label != render_data.anim_name) {
TELY_AssetAnimatedSprite sprite = TELY_Asset_MakeAnimatedSprite(sheet, render_data.anim_name, render_data.flip);
uint64_t duration_ms = FP_GAME_ENTITY_ACTION_INFINITE_TIMER; uint64_t duration_ms = FP_GAME_ENTITY_ACTION_INFINITE_TIMER;
FP_Game_EntityActionReset(game, entity->handle, duration_ms, sprite); FP_Game_EntityActionReset(game, entity->handle, duration_ms, render_data.sprite);
} }
if (we_are_clicked_entity) { if (we_are_clicked_entity) {
@@ -1995,26 +1995,8 @@ void FP_Update(TELY_Platform *platform, FP_Game *game, TELY_PlatformInput *input
} }
if (entity->flags & FP_GameEntityFlag_CameraTracking) { if (entity->flags & FP_GameEntityFlag_CameraTracking) {
FP_GameCamera *camera = &game->play.camera; game->play.camera_tracking_entity = entity->handle;
game->play.camera.world_pos_target = FP_Game_CalcEntityWorldPos(game, entity->handle) * game->play.camera.scale;
// NOTE: calculate camera position based on camera shake
Dqn_V2 camera_position = camera->world_pos;
if (camera->shake_duration > 0) {
Dqn_f32 offset_x = (Dqn_PCG32_NextF32(&game->play.rng) * 1000 - 500) * camera->shake_intensity;
Dqn_f32 offset_y = (Dqn_PCG32_NextF32(&game->play.rng) * 1000 - 500) * camera->shake_intensity;
camera_position.x += offset_x;
camera_position.y += offset_y;
camera->shake_duration -= input->delta_s;
}
Dqn_f64 camera_smoothing = 5.0f;
camera->world_pos.x += (camera_position.x - camera->world_pos.x) * (camera_smoothing * input->delta_s);
camera->world_pos.y += (camera_position.y - camera->world_pos.y) * (camera_smoothing * input->delta_s);
camera->world_pos_target = FP_Game_CalcEntityWorldPos(game, entity->handle) * camera->scale;
} }
FP_GamePlaceableBuilding placeable_building = PLACEABLE_BUILDINGS[game->play.build_mode_building_index]; FP_GamePlaceableBuilding placeable_building = PLACEABLE_BUILDINGS[game->play.build_mode_building_index];
@@ -2149,6 +2131,8 @@ void FP_Update(TELY_Platform *platform, FP_Game *game, TELY_PlatformInput *input
} }
} }
entity->trauma01 = DQN_MAX(0.f, entity->trauma01 - 0.05f);
// NOTE: Derive dynmamic bounding boxes ==================================================== // NOTE: Derive dynmamic bounding boxes ====================================================
if (entity->flags & FP_GameEntityFlag_DeriveHitBoxFromChildrenBoundingBox) { if (entity->flags & FP_GameEntityFlag_DeriveHitBoxFromChildrenBoundingBox) {
Dqn_Rect children_bbox = {}; Dqn_Rect children_bbox = {};
@@ -2262,13 +2246,9 @@ void FP_Update(TELY_Platform *platform, FP_Game *game, TELY_PlatformInput *input
continue; continue;
} }
defender->hp = defender->hp >= attacker->base_attack ? defender->hp - attacker->base_attack : 0; defender->hp = defender->hp >= attacker->base_attack ? defender->hp - attacker->base_attack : 0;
defender->hit_on_frame = game->play.update_counter; defender->hit_on_clock_ms = game->play.update_counter;
defender->trauma01 = 1.f - (defender->hp / DQN_CAST(Dqn_f32)defender->hp_cap);
if (game->play.player == defender->handle) {
game->play.camera.shake_intensity = 0.1f;
game->play.camera.shake_duration = 0.25f;
}
if (defender->hp <= 0) { if (defender->hp <= 0) {
if (!defender->is_dying) { if (!defender->is_dying) {
@@ -2352,7 +2332,29 @@ void FP_Render(FP_Game *game, TELY_Platform *platform, TELY_Renderer *renderer,
TELY_RFui_PushFont(rfui, game->jetbrains_mono_font); TELY_RFui_PushFont(rfui, game->jetbrains_mono_font);
TELY_RFui_PushLabelColourV4(rfui, TELY_COLOUR_BLACK_MIDNIGHT_V4); TELY_RFui_PushLabelColourV4(rfui, TELY_COLOUR_BLACK_MIDNIGHT_V4);
FP_GameCameraM2x3 camera_xforms = FP_Game_CameraModelViewM2x3(game->play.camera); FP_GameCamera shake_camera = game->play.camera;
FP_GameEntity *camera_entity = FP_Game_GetEntity(game, game->play.camera_tracking_entity);
{
// NOTE: Calculate camera position based on camera shake
Dqn_f32 trauma01 = DQN_SQUARED(camera_entity->trauma01);
if (camera_entity->type == FP_EntityType_Terry) {
// NOTE: The heart shake is trauma^3 to emphasise the severity of losing heart health
FP_GameEntity *heart = FP_Game_GetEntity(game, game->play.heart);
trauma01 = DQN_MAX(trauma01, DQN_SQUARED(heart->trauma01) * heart->trauma01);
}
Dqn_f32 max_shake_dist = 400.f;
Dqn_f32 half_shake_dist = max_shake_dist * .5f;
Dqn_V2 shake_offset = {};
shake_offset.x = (Dqn_PCG32_NextF32(&game->play.rng) * max_shake_dist - half_shake_dist) * trauma01;
shake_offset.y = (Dqn_PCG32_NextF32(&game->play.rng) * max_shake_dist - half_shake_dist) * trauma01;
Dqn_f32 interp_rate = 5.0f * DQN_CAST(Dqn_f32)input->delta_s;
shake_camera.world_pos += shake_offset * interp_rate;
}
FP_GameCameraM2x3 camera_xforms = FP_Game_CameraModelViewM2x3(shake_camera);
TELY_Render_PushTransform(renderer, camera_xforms.model_view); TELY_Render_PushTransform(renderer, camera_xforms.model_view);
Dqn_V2 world_mouse_p = Dqn_M2x3_MulV2(camera_xforms.view_model, input->mouse_p); Dqn_V2 world_mouse_p = Dqn_M2x3_MulV2(camera_xforms.view_model, input->mouse_p);
@@ -2375,6 +2377,7 @@ void FP_Render(FP_Game *game, TELY_Platform *platform, TELY_Renderer *renderer,
TELY_Render_PushColourV4(renderer, TELY_COLOUR_BLACK_V4); TELY_Render_PushColourV4(renderer, TELY_COLOUR_BLACK_V4);
// NOTE: Draw entities ========================================================================= // NOTE: Draw entities =========================================================================
Dqn_V4 const colour_accent_yellow = TELY_Colour_V4InitRGBU32(0xFFE726);
for (FP_GameEntityIterator it = {}; FP_Game_DFSPostOrderWalkEntityTree(game, &it, game->play.root_entity); ) { for (FP_GameEntityIterator it = {}; FP_Game_DFSPostOrderWalkEntityTree(game, &it, game->play.root_entity); ) {
FP_GameEntity *entity = it.entity; FP_GameEntity *entity = it.entity;
if (entity->flags & FP_GameEntityFlag_OccupiedInBuilding) if (entity->flags & FP_GameEntityFlag_OccupiedInBuilding)
@@ -2459,14 +2462,14 @@ void FP_Render(FP_Game *game, TELY_Platform *platform, TELY_Renderer *renderer,
dest_rect.size.h *= -1.f; // NOTE: Flip the texture vertically dest_rect.size.h *= -1.f; // NOTE: Flip the texture vertically
Dqn_V4 sprite_colour = TELY_COLOUR_WHITE_V4; Dqn_V4 sprite_colour = TELY_COLOUR_WHITE_V4;
if (entity->hit_on_frame) { if (entity->hit_on_clock_ms) {
DQN_ASSERT(game->play.update_counter >= entity->hit_on_frame); DQN_ASSERT(game->play.clock_ms >= entity->hit_on_clock_ms);
Dqn_usize frames_elapsed_since_hit = game->play.update_counter - entity->hit_on_frame; Dqn_usize ms_since_hit = game->play.clock_ms - entity->hit_on_clock_ms;
Dqn_usize const HIT_CONFIRM_DURATION = 8; Dqn_usize const HIT_CONFIRM_DURATION_MS = 8 * 16;
if (frames_elapsed_since_hit < HIT_CONFIRM_DURATION) { if (ms_since_hit < HIT_CONFIRM_DURATION_MS) {
sprite_colour = TELY_COLOUR_RED_V4; sprite_colour = TELY_COLOUR_RED_V4;
sprite_colour.g = ((1.f / HIT_CONFIRM_DURATION) * frames_elapsed_since_hit); sprite_colour.g = ((1.f / HIT_CONFIRM_DURATION_MS) * ms_since_hit);
sprite_colour.b = ((1.f / HIT_CONFIRM_DURATION) * frames_elapsed_since_hit); sprite_colour.b = ((1.f / HIT_CONFIRM_DURATION_MS) * ms_since_hit);
} }
} }
@@ -2483,7 +2486,6 @@ void FP_Render(FP_Game *game, TELY_Platform *platform, TELY_Renderer *renderer,
Dqn_V2 label_p = Dqn_Rect_InterpolatedPoint(dest_rect, Dqn_V2_InitNx2(0, -0.25f)); Dqn_V2 label_p = Dqn_Rect_InterpolatedPoint(dest_rect, Dqn_V2_InitNx2(0, -0.25f));
TELY_Render_TextF(renderer, label_p, Dqn_V2_InitNx2(0.f, 0.1f), "CONVERTED"); TELY_Render_TextF(renderer, label_p, Dqn_V2_InitNx2(0.f, 0.1f), "CONVERTED");
} }
} }
DQN_FOR_UINDEX(anim_index, entity->extra_cosmetic_anims.size) { DQN_FOR_UINDEX(anim_index, entity->extra_cosmetic_anims.size) {
@@ -2661,7 +2663,7 @@ void FP_Render(FP_Game *game, TELY_Platform *platform, TELY_Renderer *renderer,
case FP_EntityBillboardState_Attack: { case FP_EntityBillboardState_Attack: {
key_bind = &game->controls.attack; key_bind = &game->controls.attack;
draw_p = Dqn_Rect_InterpolatedPoint(world_hit_box, Dqn_V2_InitNx2(0.65f, 0.2f)); draw_p = Dqn_Rect_InterpolatedPoint(world_hit_box, Dqn_V2_InitNx2(0.65f, 0.2f));
colour = TELY_Colour_V4InitRGBU32(0xFFE726); colour = colour_accent_yellow;
} break; } break;
case FP_EntityBillboardState_Dash: { case FP_EntityBillboardState_Dash: {
@@ -2709,9 +2711,14 @@ void FP_Render(FP_Game *game, TELY_Platform *platform, TELY_Renderer *renderer,
Dqn_V2 player_pos = FP_Game_CalcEntityWorldPos(game, game->play.player); Dqn_V2 player_pos = FP_Game_CalcEntityWorldPos(game, game->play.player);
{ {
static bool sound_played_flags[4] = {false, false, false, false}; static bool sound_played_flags[4] = {false, false, false, false};
FP_GameInventory *invent = &player->inventory; FP_GameInventory *invent = &player->inventory;
FP_GamePlay *play = &game->play;
struct FP_MerchantToMenuMapping { struct FP_MerchantToMenuMapping {
FP_GameEntityHandle merchant; FP_GameEntityHandle merchant;
Dqn_V2 *menu_pos;
Dqn_String8 upgrade_icon;
Dqn_String8 menu_anim; Dqn_String8 menu_anim;
Dqn_String8 building; Dqn_String8 building;
Dqn_V2 building_offset01; Dqn_V2 building_offset01;
@@ -2721,10 +2728,10 @@ void FP_Render(FP_Game *game, TELY_Platform *platform, TELY_Renderer *renderer,
FP_GameAudio audio_type; FP_GameAudio audio_type;
bool *sound_played; bool *sound_played;
} merchants[] = { } merchants[] = {
{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, FP_GameAudio_MerchantTerry, &sound_played_flags[0]}, {play->merchant_terry, &play->merchant_terry_menu_pos, g_anim_names.icon_attack, 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, FP_GameAudio_MerchantTerry, &sound_played_flags[0]},
{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, FP_GameAudio_MerchantGhost, &sound_played_flags[1]}, {play->merchant_graveyard, &play->merchant_graveyard_menu_pos, g_anim_names.icon_stamina, 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, FP_GameAudio_MerchantGhost, &sound_played_flags[1]},
{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, FP_GameAudio_MerchantGym, &sound_played_flags[2]}, {play->merchant_gym, &play->merchant_gym_menu_pos, g_anim_names.icon_health, 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, FP_GameAudio_MerchantGym, &sound_played_flags[2]},
{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, FP_GameAudio_MerchantPhone, &sound_played_flags[3]}, {play->merchant_phone_company, &play->merchant_phone_company_menu_pos, g_anim_names.icon_phone, 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, FP_GameAudio_MerchantPhone, &sound_played_flags[3]},
}; };
bool activated_merchant = false; bool activated_merchant = false;
@@ -2739,8 +2746,8 @@ void FP_Render(FP_Game *game, TELY_Platform *platform, TELY_Renderer *renderer,
} }
// NOTE: Render animated merchant menu ============================= // NOTE: Render animated merchant menu =============================
activated_merchant = true; activated_merchant = true;
Dqn_Rect merchant_menu_rect = {}; Dqn_Rect merchant_menu_rect = {};
{ {
FP_GameRenderSprite *sprite = &game->play.player_merchant_menu; FP_GameRenderSprite *sprite = &game->play.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) {
@@ -2755,8 +2762,30 @@ void FP_Render(FP_Game *game, TELY_Platform *platform, TELY_Renderer *renderer,
Dqn_usize sprite_index = sprite->asset.anim->index + anim_frame; Dqn_usize sprite_index = sprite->asset.anim->index + anim_frame;
Dqn_Rect src_rect = sprite->asset.sheet->rects.data[sprite_index]; Dqn_Rect src_rect = sprite->asset.sheet->rects.data[sprite_index];
merchant_menu_rect.size = src_rect.size; if (*mapping.menu_pos == Dqn_V2_Zero)
merchant_menu_rect.pos = world_pos - (merchant_menu_rect.size * .5f) - Dqn_V2_InitNx2(0.f, src_rect.size.y); *mapping.menu_pos = world_pos;
Dqn_Rect top_rect = Dqn_Rect_InitV2x2(world_pos - (src_rect.size * .5f) - Dqn_V2_InitNx2(0.f, src_rect.size.h), src_rect.size);
Dqn_Rect bottom_rect = Dqn_Rect_InitV2x2(world_pos - (src_rect.size * .5f) + Dqn_V2_InitNx2(0.f, src_rect.size.h), src_rect.size);
// NOTE: Move the merchant menu if we overlap with it so as to not occlude the player
{
Dqn_V2 target_pos = {};
Dqn_Rect camera_entity_hit_box = FP_Game_CalcEntityWorldHitBox(game, camera_entity->handle);
if (Dqn_Rect_Intersects(top_rect, camera_entity_hit_box)) {
target_pos = bottom_rect.pos;
} else {
target_pos = top_rect.pos;
}
// NOTE: Interpolate the menu position
*mapping.menu_pos += (target_pos - *mapping.menu_pos) * (24.f * DQN_CAST(Dqn_f32)input->delta_s);
}
merchant_menu_rect.size = src_rect.size;
merchant_menu_rect.pos = *mapping.menu_pos;
// NOTE: Bob the merchant menu
Dqn_f32 sin_t = DQN_SINF(DQN_CAST(Dqn_f32)input->timer_s * 3.f); Dqn_f32 sin_t = DQN_SINF(DQN_CAST(Dqn_f32)input->timer_s * 3.f);
merchant_menu_rect.pos.y += sin_t * 4.f; merchant_menu_rect.pos.y += sin_t * 4.f;
@@ -2774,7 +2803,7 @@ void FP_Render(FP_Game *game, TELY_Platform *platform, TELY_Renderer *renderer,
} }
} }
TELY_Render_PushColourV4(renderer, TELY_COLOUR_WHITE_V4); TELY_Render_PushColourV4(renderer, TELY_COLOUR_BLACK_V4);
TELY_Render_PushFont(renderer, game->talkco_font_large); TELY_Render_PushFont(renderer, game->talkco_font_large);
DQN_DEFER { DQN_DEFER {
TELY_Render_PopFont(renderer); TELY_Render_PopFont(renderer);
@@ -2783,21 +2812,22 @@ 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; uint64_t const buy_duration_ms = 500;
Dqn_V4 keybind_btn_shadow_colour = Dqn_V4_InitNx4(0.4f, 0.4f, 0.4f, 1.f);
{ {
bool const have_enough_coins = player->coins >= *mapping.building_base_price; bool const have_enough_coins = player->coins >= *mapping.building_base_price;
FP_GameKeyBind key_bind = game->controls.buy_building;
// NOTE: Buy trigger + animation =============================== // NOTE: Buy trigger + animation ===============================
{ {
TELY_PlatformInputScanCode key = game->controls.attack.scan_code; bool trigger_buy_anim = false;
bool trigger_buy_anim = false;
if (have_enough_coins) { if (have_enough_coins) {
if (TELY_Platform_InputScanCodeIsPressed(input, key)) { if (TELY_Platform_InputScanCodeIsPressed(input, key_bind.scan_code)) {
game->play.player_trigger_purchase_building_timestamp = game->play.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)) { } else if (TELY_Platform_InputScanCodeIsDown(input, key_bind.scan_code)) {
trigger_buy_anim = true; trigger_buy_anim = true;
if (game->play.clock_ms > game->play.player_trigger_purchase_building_timestamp) if (game->play.clock_ms > game->play.player_trigger_purchase_building_timestamp)
game->play.player_trigger_purchase_building_timestamp = game->play.clock_ms; game->play.player_trigger_purchase_building_timestamp = game->play.clock_ms;
} else if (TELY_Platform_InputScanCodeIsReleased(input, key)) { } else if (TELY_Platform_InputScanCodeIsReleased(input, key_bind.scan_code)) {
if (game->play.clock_ms > game->play.player_trigger_purchase_building_timestamp) { if (game->play.clock_ms > game->play.player_trigger_purchase_building_timestamp) {
if (mapping.inventory_count) { if (mapping.inventory_count) {
player->coins -= *mapping.building_base_price; player->coins -= *mapping.building_base_price;
@@ -2836,20 +2866,44 @@ void FP_Render(FP_Game *game, TELY_Platform *platform, TELY_Renderer *renderer,
// 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); 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); // NOTE: Render the gamepad button
Dqn_Rect button_rect = game->atlas_sprite_sheet.rects.data[anim->index]; Dqn_Rect gamepad_btn_rect = {};
Dqn_Rect dest_rect = {}; {
dest_rect.size = button_rect.size * 1.5f; TELY_AssetSpriteAnimation *anim = TELY_Asset_GetSpriteAnimation(&game->atlas_sprite_sheet, g_anim_names.merchant_button_a);
dest_rect.pos = Dqn_Rect_InterpolatedPoint(merchant_menu_rect, Dqn_V2_InitNx2(0.345f, 0.5f)); Dqn_Rect button_rect = game->atlas_sprite_sheet.rects.data[anim->index];
TELY_Render_TextureColourV4(renderer, gamepad_btn_rect.size = button_rect.size * 1.5f;
game->atlas_sprite_sheet.tex_handle, gamepad_btn_rect.pos = Dqn_Rect_InterpolatedPoint(merchant_menu_rect, Dqn_V2_InitNx2(0.345f, 0.41f));
button_rect, TELY_Render_TextureColourV4(renderer,
dest_rect, game->atlas_sprite_sheet.tex_handle,
Dqn_V2_Zero /*rotate origin*/, button_rect,
0.f /*rotation*/, gamepad_btn_rect,
tex_mod_colour); Dqn_V2_Zero /*rotate origin*/,
0.f /*rotation*/,
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), "$%u", *mapping.building_base_price); // NOTE: Render the $ cost
TELY_Render_TextF(renderer, Dqn_Rect_InterpolatedPoint(gamepad_btn_rect, Dqn_V2_InitNx2(0.5f, -0.8f)), Dqn_V2_InitNx2(0.5, 0.f), "$%u", *mapping.building_base_price);
// NOTE: Render the keyboard binding =======================================
{
DQN_ASSERT(key_bind.scan_code >= TELY_PlatformInputScanCode_A && key_bind.scan_code <= TELY_PlatformInputScanCode_Z);
char scan_code_ch = DQN_CAST(char)('A' + (key_bind.scan_code - TELY_PlatformInputScanCode_A));
Dqn_ThreadScratch scratch = Dqn_Thread_GetScratch(nullptr);
TELY_AssetFont const *font = TELY_Render_Font(renderer, assets);
Dqn_String8 key_bind_label = Dqn_String8_InitF(scratch.allocator, "[%c]", scan_code_ch);
Dqn_Rect key_bind_rect = {};
key_bind_rect.pos = Dqn_Rect_InterpolatedPoint(gamepad_btn_rect, Dqn_V2_InitNx2(-0.1f, 1.1f));
key_bind_rect.size = TELY_Asset_MeasureText(font, key_bind_label);
key_bind_rect = Dqn_Rect_Expand(key_bind_rect, 2.f);
TELY_Render_PushColourV4(renderer, TELY_Colour_V4Alpha(colour_accent_yellow, tex_mod_colour.a));
TELY_Render_RectColourV4(renderer, key_bind_rect, TELY_RenderShapeMode_Fill, TELY_Colour_V4Alpha(keybind_btn_shadow_colour, tex_mod_colour.a));
TELY_Render_Text(renderer, Dqn_Rect_InterpolatedPoint(key_bind_rect, Dqn_V2_InitNx1(0.5f)), Dqn_V2_InitNx1(0.5f), key_bind_label);
TELY_Render_PopColourV4(renderer);
}
} }
// NOTE: Render the merchant shop item building ================ // NOTE: Render the merchant shop item building ================
@@ -2872,26 +2926,25 @@ 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
{ {
bool const have_enough_coins = player->coins >= *mapping.upgrade_base_price; bool const have_enough_coins = player->coins >= *mapping.upgrade_base_price;
FP_GameKeyBind key_bind = game->controls.buy_upgrade;
// NOTE: Buy trigger + animation =============================== // NOTE: Buy trigger + animation ===============================
{ {
TELY_PlatformInputScanCode key = game->controls.range_attack.scan_code; bool trigger_buy_anim = false;
bool trigger_buy_anim = false;
if (have_enough_coins) { if (have_enough_coins) {
if (TELY_Platform_InputScanCodeIsPressed(input, key)) { if (TELY_Platform_InputScanCodeIsPressed(input, key_bind.scan_code)) {
game->play.player_trigger_purchase_upgrade_timestamp = game->play.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)) { } else if (TELY_Platform_InputScanCodeIsDown(input, key_bind.scan_code)) {
trigger_buy_anim = true; trigger_buy_anim = true;
if (game->play.clock_ms > game->play.player_trigger_purchase_upgrade_timestamp) if (game->play.clock_ms > game->play.player_trigger_purchase_upgrade_timestamp)
game->play.player_trigger_purchase_upgrade_timestamp = game->play.clock_ms; game->play.player_trigger_purchase_upgrade_timestamp = game->play.clock_ms;
} else if (TELY_Platform_InputScanCodeIsReleased(input, key)) { } else if (TELY_Platform_InputScanCodeIsReleased(input, key_bind.scan_code)) {
if (game->play.clock_ms > game->play.player_trigger_purchase_upgrade_timestamp) { if (game->play.clock_ms > game->play.player_trigger_purchase_upgrade_timestamp) {
player->coins -= *mapping.upgrade_base_price; player->coins -= *mapping.upgrade_base_price;
*mapping.upgrade_base_price *= 1; *mapping.upgrade_base_price *= 1;
TELY_Audio_Play(audio, game->audio[FP_GameAudio_Ching], 1.f); TELY_Audio_Play(audio, game->audio[FP_GameAudio_Ching], 1.f);
if (mapping.merchant == game->play.merchant_terry) { if (mapping.merchant == game->play.merchant_terry) {
// TODO(doyle): Attack damage? Or increase attack range?
player->base_attack += DQN_CAST(uint32_t)(FP_DEFAULT_DAMAGE * 1.2f); player->base_attack += DQN_CAST(uint32_t)(FP_DEFAULT_DAMAGE * 1.2f);
} else if (mapping.merchant == game->play.merchant_graveyard) { } else if (mapping.merchant == game->play.merchant_graveyard) {
player->stamina_cap += DQN_CAST(uint32_t)(FP_TERRY_DASH_STAMINA_COST * .5f); player->stamina_cap += DQN_CAST(uint32_t)(FP_TERRY_DASH_STAMINA_COST * .5f);
@@ -2924,20 +2977,63 @@ void FP_Render(FP_Game *game, TELY_Platform *platform, TELY_Renderer *renderer,
// NOTE: Render the (B) button ================================= // NOTE: Render the (B) button =================================
Dqn_V4 tex_mod_colour = have_enough_coins ? TELY_COLOUR_WHITE_V4 : TELY_Colour_V4Alpha(TELY_COLOUR_RED_TOMATO_V4, .5f); 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_b); Dqn_V2 interp_pos01 = {};
Dqn_Rect button_rect = game->atlas_sprite_sheet.rects.data[anim->index]; Dqn_Rect gamepad_btn_rect = {};
Dqn_Rect dest_rect = {}; // NOTE: Render the gamepad button
dest_rect.size = button_rect.size * 1.5f; {
dest_rect.pos = Dqn_Rect_InterpolatedPoint(merchant_menu_rect, Dqn_V2_InitNx2(0.75f, 0.5f)); TELY_AssetSpriteAnimation *anim = TELY_Asset_GetSpriteAnimation(&game->atlas_sprite_sheet, g_anim_names.merchant_button_b);
TELY_Render_TextureColourV4(renderer, Dqn_Rect button_rect = game->atlas_sprite_sheet.rects.data[anim->index];
game->atlas_sprite_sheet.tex_handle, interp_pos01 = Dqn_V2_InitNx2(0.71f, 0.41f);
button_rect, gamepad_btn_rect.size = button_rect.size * 1.5f;
dest_rect, gamepad_btn_rect.pos = Dqn_Rect_InterpolatedPoint(merchant_menu_rect, interp_pos01);
Dqn_V2_Zero /*rotate origin*/, TELY_Render_TextureColourV4(renderer,
0.f /*rotation*/, game->atlas_sprite_sheet.tex_handle,
tex_mod_colour); button_rect,
gamepad_btn_rect,
Dqn_V2_Zero /*rotate origin*/,
0.f /*rotation*/,
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), "$%u", *mapping.upgrade_base_price); }
// NOTE: Render the building
{
TELY_AssetSpriteAnimation *anim = TELY_Asset_GetSpriteAnimation(&game->atlas_sprite_sheet, mapping.upgrade_icon);
Dqn_Rect button_rect = game->atlas_sprite_sheet.rects.data[anim->index];
Dqn_Rect dest_rect = {};
dest_rect.size = button_rect.size * .75f;
dest_rect.pos = Dqn_Rect_InterpolatedPoint(merchant_menu_rect, interp_pos01 + Dqn_V2_InitNx2(0.12f, 0.15f)) - (dest_rect.size * .5f);
TELY_Render_TextureColourV4(renderer,
game->atlas_sprite_sheet.tex_handle,
button_rect,
dest_rect,
Dqn_V2_Zero /*rotate origin*/,
0.f /*rotation*/,
tex_mod_colour);
}
// NOTE: Render the $ cost
TELY_Render_TextF(renderer, Dqn_Rect_InterpolatedPoint(gamepad_btn_rect, Dqn_V2_InitNx2(1.f, -0.8f)), Dqn_V2_InitNx2(0.5, 0.f), "$%u", *mapping.upgrade_base_price);
// NOTE: Render the keyboard binding =======================================
{
DQN_ASSERT(key_bind.scan_code >= TELY_PlatformInputScanCode_A && key_bind.scan_code <= TELY_PlatformInputScanCode_Z);
char scan_code_ch = DQN_CAST(char)('A' + (key_bind.scan_code - TELY_PlatformInputScanCode_A));
Dqn_ThreadScratch scratch = Dqn_Thread_GetScratch(nullptr);
TELY_AssetFont const *font = TELY_Render_Font(renderer, assets);
Dqn_String8 key_bind_label = Dqn_String8_InitF(scratch.allocator, "[%c]", scan_code_ch);
Dqn_Rect key_bind_rect = {};
key_bind_rect.pos = Dqn_Rect_InterpolatedPoint(gamepad_btn_rect, Dqn_V2_InitNx2(-0.1f, 1.1f));
key_bind_rect.size = TELY_Asset_MeasureText(font, key_bind_label);
key_bind_rect = Dqn_Rect_Expand(key_bind_rect, 2.f);
TELY_Render_PushColourV4(renderer, TELY_Colour_V4Alpha(colour_accent_yellow, tex_mod_colour.a));
TELY_Render_RectColourV4(renderer, key_bind_rect, TELY_RenderShapeMode_Fill, TELY_Colour_V4Alpha(keybind_btn_shadow_colour, tex_mod_colour.a));
TELY_Render_Text(renderer, Dqn_Rect_InterpolatedPoint(key_bind_rect, Dqn_V2_InitNx1(0.5f)), Dqn_V2_InitNx1(0.5f), key_bind_label);
TELY_Render_PopColourV4(renderer);
}
} }
} }
} }
+1
View File
@@ -52,6 +52,7 @@ struct FP_GlobalAnimations
Dqn_String8 heart = DQN_STRING8("heart"); Dqn_String8 heart = DQN_STRING8("heart");
Dqn_String8 heart_bleed = DQN_STRING8("heart_bleed"); Dqn_String8 heart_bleed = DQN_STRING8("heart_bleed");
Dqn_String8 icon_attack = DQN_STRING8("icon_attack");
Dqn_String8 icon_health = DQN_STRING8("icon_health"); Dqn_String8 icon_health = DQN_STRING8("icon_health");
Dqn_String8 icon_money = DQN_STRING8("icon_money"); Dqn_String8 icon_money = DQN_STRING8("icon_money");
Dqn_String8 icon_phone = DQN_STRING8("icon_phone"); Dqn_String8 icon_phone = DQN_STRING8("icon_phone");
+10 -3
View File
@@ -191,11 +191,12 @@ struct FP_GameEntity
// NOTE: The entity hit box is positioned at the center of the entity. // NOTE: The entity hit box is positioned at the center of the entity.
Dqn_V2 local_hit_box_size; Dqn_V2 local_hit_box_size;
Dqn_V2 local_hit_box_offset; Dqn_V2 local_hit_box_offset;
Dqn_f32 trauma01;
Dqn_V2 attack_box_size; Dqn_V2 attack_box_size;
Dqn_V2 attack_box_offset; Dqn_V2 attack_box_offset;
bool attack_processed; bool attack_processed;
Dqn_usize hit_on_frame; Dqn_usize hit_on_clock_ms;
bool is_dying; bool is_dying;
uint64_t last_attack_timestamp; uint64_t last_attack_timestamp;
uint64_t attack_cooldown_ms; uint64_t attack_cooldown_ms;
@@ -261,8 +262,6 @@ struct FP_GameCamera
Dqn_V2 world_pos_target; Dqn_V2 world_pos_target;
Dqn_f32 rotate_rads; Dqn_f32 rotate_rads;
Dqn_V2 scale; Dqn_V2 scale;
Dqn_f32 shake_intensity;
Dqn_f64 shake_duration;
}; };
enum FP_GameAudio enum FP_GameAudio
@@ -325,6 +324,11 @@ struct FP_GamePlay
FP_GameEntityHandle merchant_gym; FP_GameEntityHandle merchant_gym;
FP_GameEntityHandle merchant_phone_company; FP_GameEntityHandle merchant_phone_company;
Dqn_V2 merchant_terry_menu_pos;
Dqn_V2 merchant_graveyard_menu_pos;
Dqn_V2 merchant_gym_menu_pos;
Dqn_V2 merchant_phone_company_menu_pos;
FP_GameEntityHandle clicked_entity; FP_GameEntityHandle clicked_entity;
FP_GameEntityHandle hot_entity; FP_GameEntityHandle hot_entity;
FP_GameEntityHandle active_entity; FP_GameEntityHandle active_entity;
@@ -332,6 +336,7 @@ struct FP_GamePlay
FP_GameEntityHandle prev_hot_entity; FP_GameEntityHandle prev_hot_entity;
FP_GameEntityHandle prev_active_entity; FP_GameEntityHandle prev_active_entity;
FP_GameEntityHandle camera_tracking_entity;
FP_GameCamera camera; FP_GameCamera camera;
Dqn_f32 meters_to_pixels; Dqn_f32 meters_to_pixels;
uint64_t clock_ms; uint64_t clock_ms;
@@ -369,6 +374,8 @@ struct FP_GameControls
FP_GameKeyBind left; FP_GameKeyBind left;
FP_GameKeyBind right; FP_GameKeyBind right;
FP_GameKeyBind attack; FP_GameKeyBind attack;
FP_GameKeyBind buy_building;
FP_GameKeyBind buy_upgrade;
FP_GameKeyBind range_attack; FP_GameKeyBind range_attack;
FP_GameKeyBind build_mode; FP_GameKeyBind build_mode;
FP_GameKeyBind strafe; FP_GameKeyBind strafe;