Compare commits
2 Commits
634a7c8cb4
...
25736461fb
| Author | SHA1 | Date | |
|---|---|---|---|
| 25736461fb | |||
| a651bc7e14 |
BIN
Binary file not shown.
BIN
Binary file not shown.
BIN
Binary file not shown.
BIN
Binary file not shown.
BIN
Binary file not shown.
BIN
Binary file not shown.
Binary file not shown.
BIN
Binary file not shown.
Binary file not shown.
Binary file not shown.
Vendored
+1
-1
Submodule External/tely updated: 595e3c7f1e...8d39ef2d83
+149
-73
@@ -187,6 +187,12 @@ static void FP_Game_MoveEntity(FP_Game *game, FP_GameEntityHandle entity_handle,
|
||||
#else
|
||||
entity_collides_with_collider = false;
|
||||
#endif
|
||||
} else if (collider->type == FP_EntityType_Heart ||
|
||||
collider->type == FP_EntityType_MerchantGym ||
|
||||
collider->type == FP_EntityType_MerchantTerry ||
|
||||
collider->type == FP_EntityType_MerchantGraveyard ||
|
||||
collider->type == FP_EntityType_MerchantPhoneCompany) {
|
||||
entity_collides_with_collider = false;
|
||||
}
|
||||
} break;
|
||||
|
||||
@@ -453,17 +459,18 @@ static void FP_PlayReset(FP_Game *game, TELY_Platform *platform)
|
||||
play->merchant_phone_company = FP_Entity_CreateMerchantPhoneCompany(game, base_top_right, "PhoneCompany");
|
||||
}
|
||||
|
||||
#if 0
|
||||
FP_Entity_CreateClubTerry(game, Dqn_V2_InitNx2(+500, -191), "Club Terry");
|
||||
FP_Entity_CreateKennelTerry(game, Dqn_V2_InitNx2(-300, -191), "Kennel Terry");
|
||||
FP_Entity_CreateChurchTerry(game, Dqn_V2_InitNx2(-800, -191), "Church Terry");
|
||||
FP_Entity_CreateAirportTerry(game, Dqn_V2_InitNx2(-1200, -191), "Airport Terry");
|
||||
#endif
|
||||
|
||||
play->tile_size = 37;
|
||||
Dqn_V2I max_tile = platform->core.window_size / play->tile_size;
|
||||
|
||||
// NOTE: Heart
|
||||
FP_Entity_CreateHeart(game, base_mid_p, "Heart");
|
||||
|
||||
game->play.heart = FP_Entity_CreateHeart(game, base_mid_p, "Heart");
|
||||
play->camera.world_pos = base_mid_p - Dqn_V2_InitV2I(platform->core.window_size * .5f);
|
||||
play->camera.scale = Dqn_V2_InitNx1(1);
|
||||
}
|
||||
@@ -501,6 +508,13 @@ void TELY_DLL_Init(void *user_data)
|
||||
game->audio[FP_GameAudio_Woosh] = platform->func_load_audio(assets, DQN_STRING8("Woosh"), DQN_STRING8("Data/Audio/woosh.ogg"));
|
||||
game->audio[FP_GameAudio_Ching] = platform->func_load_audio(assets, DQN_STRING8("Ching"), DQN_STRING8("Data/Audio/ching.ogg"));
|
||||
game->audio[FP_GameAudio_Church] = platform->func_load_audio(assets, DQN_STRING8("Church"), DQN_STRING8("Data/Audio/church.ogg"));
|
||||
game->audio[FP_GameAudio_Plane] = platform->func_load_audio(assets, DQN_STRING8("Plane"), DQN_STRING8("Data/Audio/airport.ogg"));
|
||||
game->audio[FP_GameAudio_Club] = platform->func_load_audio(assets, DQN_STRING8("Club"), DQN_STRING8("Data/Audio/club_terry.ogg"));
|
||||
game->audio[FP_GameAudio_Dog] = platform->func_load_audio(assets, DQN_STRING8("Dog"), DQN_STRING8("Data/Audio/dog.ogg"));
|
||||
game->audio[FP_GameAudio_MerchantTerry] = platform->func_load_audio(assets, DQN_STRING8("Door"), DQN_STRING8("Data/Audio/merchant_terry.ogg"));
|
||||
game->audio[FP_GameAudio_MerchantGhost] = platform->func_load_audio(assets, DQN_STRING8("Ghost"), DQN_STRING8("Data/Audio/merchant_ghost.ogg"));
|
||||
game->audio[FP_GameAudio_MerchantGym] = platform->func_load_audio(assets, DQN_STRING8("Gym"), DQN_STRING8("Data/Audio/merchant_gym.ogg"));
|
||||
game->audio[FP_GameAudio_MerchantPhone] = platform->func_load_audio(assets, DQN_STRING8("Phone"), DQN_STRING8("Data/Audio/merchant_tech.ogg"));
|
||||
|
||||
platform->user_data = game;
|
||||
{
|
||||
@@ -1136,7 +1150,7 @@ void FP_EntityActionStateMachine(FP_Game *game, TELY_Audio *audio, TELY_Platform
|
||||
if (entity->waypoints.size == 0) {
|
||||
FP_GameEntity *patron = FP_Game_GetEntity(game, entity->building_patron);
|
||||
patron->local_pos = entity->local_pos;
|
||||
patron->flags &= ~(FP_GameEntityFlag_OccupiedInBuilding | FP_GameEntityFlag_PointOfInterestHeart);
|
||||
patron->flags &= ~(FP_GameEntityFlag_OccupiedInBuilding);
|
||||
FP_Game_DeleteEntity(game, entity->handle);
|
||||
return;
|
||||
}
|
||||
@@ -1232,7 +1246,7 @@ void FP_EntityActionStateMachine(FP_Game *game, TELY_Audio *audio, TELY_Platform
|
||||
if (action_has_finished) {
|
||||
if (!FP_Game_IsNilEntityHandle(game, entity->building_patron)) {
|
||||
FP_GameEntity *patron = FP_Game_GetEntity(game, entity->building_patron);
|
||||
patron->flags &= ~(FP_GameEntityFlag_OccupiedInBuilding | FP_GameEntityFlag_RespondsToBuildings | FP_GameEntityFlag_PointOfInterestHeart);
|
||||
patron->flags &= ~(FP_GameEntityFlag_OccupiedInBuilding | FP_GameEntityFlag_RespondsToBuildings);
|
||||
patron->faction = FP_GameEntityFaction_Friendly;
|
||||
patron->converted_faction = true;
|
||||
}
|
||||
@@ -1508,7 +1522,6 @@ void FP_Update(TELY_Platform *platform, FP_Game *game, TELY_PlatformInput *input
|
||||
|
||||
// NOTE: Determine AI movement =============================================================
|
||||
Dqn_V2 entity_pos = FP_Game_CalcEntityWorldPos(game, entity->handle);
|
||||
if (acceleration_meters_per_s.x == 0 && acceleration_meters_per_s.y == 0) {
|
||||
if (entity->flags & FP_GameEntityFlag_Aggros && entity->faction != FP_GameEntityFaction_Nil) {
|
||||
FP_GameFindClosestEntityResult closest_defender = {};
|
||||
closest_defender.dist_squared = DQN_F32_MAX;
|
||||
@@ -1535,7 +1548,15 @@ void FP_Update(TELY_Platform *platform, FP_Game *game, TELY_PlatformInput *input
|
||||
}
|
||||
|
||||
Dqn_f32 aggro_dist_threshold = FP_Game_MetersToPixelsNx1(game->play, 4.f);
|
||||
if (closest_defender.dist_squared < DQN_SQUARED(aggro_dist_threshold)) {
|
||||
Dqn_f32 dist_to_defender = DQN_SQRTF(closest_defender.dist_squared);
|
||||
if (dist_to_defender > (aggro_dist_threshold * 1.5f)) {
|
||||
for (FP_SentinelListLink<FP_GameWaypoint> *link = nullptr; FP_SentinelList_Iterate<FP_GameWaypoint>(&entity->waypoints, &link); ) {
|
||||
FP_GameEntity *maybe_terry = FP_Game_GetEntity(game, link->data.entity);
|
||||
if (maybe_terry->type == FP_EntityType_Terry) {
|
||||
link = FP_SentinelList_Erase(&entity->waypoints, link, game->play.chunk_pool);
|
||||
}
|
||||
}
|
||||
} else if (dist_to_defender < aggro_dist_threshold) {
|
||||
bool has_waypoint_to_defender = false;
|
||||
for (FP_SentinelListLink<FP_GameWaypoint> *link = nullptr;
|
||||
!has_waypoint_to_defender && FP_SentinelList_Iterate<FP_GameWaypoint>(&entity->waypoints, &link); ) {
|
||||
@@ -1551,15 +1572,6 @@ void FP_Update(TELY_Platform *platform, FP_Game *game, TELY_PlatformInput *input
|
||||
waypoint->entity = defender->handle;
|
||||
waypoint->type = FP_GameWaypointType_ClosestSide;
|
||||
}
|
||||
} else {
|
||||
if (closest_defender.dist_squared > DQN_SQUARED(aggro_dist_threshold * 2.f)) {
|
||||
for (FP_SentinelListLink<FP_GameWaypoint> *link = nullptr; FP_SentinelList_Iterate<FP_GameWaypoint>(&entity->waypoints, &link); ) {
|
||||
FP_GameEntity *maybe_terry = FP_Game_GetEntity(game, link->data.entity);
|
||||
if (maybe_terry->type == FP_EntityType_Terry) {
|
||||
link = FP_SentinelList_Erase(&entity->waypoints, link, game->play.chunk_pool);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1643,29 +1655,6 @@ void FP_Update(TELY_Platform *platform, FP_Game *game, TELY_PlatformInput *input
|
||||
}
|
||||
}
|
||||
|
||||
if (entity->flags & FP_GameEntityFlag_PointOfInterestHeart) {
|
||||
FP_GameFindClosestEntityResult closest_heart = FP_Game_FindClosestEntityWithType(game, entity->handle, FP_EntityType_Heart);
|
||||
if (closest_heart.dist_squared < DQN_SQUARED(FP_Game_MetersToPixelsNx1(game->play, 4.f))) {
|
||||
|
||||
bool has_waypoint_to = false;
|
||||
for (FP_SentinelListLink<FP_GameWaypoint> *link = nullptr;
|
||||
!has_waypoint_to && FP_SentinelList_Iterate<FP_GameWaypoint>(&entity->waypoints, &link); ) {
|
||||
has_waypoint_to = link->data.entity == closest_heart.entity;
|
||||
}
|
||||
|
||||
if (!has_waypoint_to) {
|
||||
Dqn_Rect club_hit_box = FP_Game_CalcEntityWorldHitBox(game, closest_heart.entity);
|
||||
Dqn_V2 club_top_left = Dqn_Rect_TopLeft(club_hit_box);
|
||||
Dqn_V2 club_top_right = Dqn_Rect_TopRight(club_hit_box);
|
||||
|
||||
FP_SentinelListLink<FP_GameWaypoint> *link = FP_SentinelList_MakeBefore(&entity->waypoints, FP_SentinelList_Front(&entity->waypoints), game->play.chunk_pool);
|
||||
FP_GameWaypoint *waypoint = &link->data;
|
||||
waypoint->entity = closest_heart.entity;
|
||||
waypoint->type = FP_GameWaypointType_ClosestSide;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
while (entity->waypoints.size) {
|
||||
FP_SentinelListLink<FP_GameWaypoint> *waypoint_link = entity->waypoints.sentinel->next;
|
||||
FP_GameWaypoint const *waypoint = &waypoint_link->data;
|
||||
@@ -1682,11 +1671,37 @@ void FP_Update(TELY_Platform *platform, FP_Game *game, TELY_PlatformInput *input
|
||||
// NOTE: Check if we've arrived at the waypoint
|
||||
Dqn_f32 dist_to_waypoint_sq = Dqn_V2_LengthSq(entity_to_waypoint);
|
||||
|
||||
// NOTE: Calculate the approaching direction
|
||||
FP_GameDirection approach_dir = FP_GameDirection_Up;
|
||||
{
|
||||
Dqn_V2 dir_vectors[FP_GameDirection_Count] = {};
|
||||
dir_vectors[FP_GameDirection_Up] = Dqn_V2_InitNx2(+0, -1);
|
||||
dir_vectors[FP_GameDirection_Down] = Dqn_V2_InitNx2(+0, +1);
|
||||
dir_vectors[FP_GameDirection_Left] = Dqn_V2_InitNx2(-1, +0);
|
||||
dir_vectors[FP_GameDirection_Right] = Dqn_V2_InitNx2(+1, +0);
|
||||
|
||||
Dqn_V2 target_entity_pos = FP_Game_CalcEntityWorldPos(game, waypoint_entity->handle);
|
||||
Dqn_V2 entity_to_target = target_entity_pos - entity_pos;
|
||||
Dqn_V2 entity_to_target_norm = Dqn_V2_Normalise(entity_to_target);
|
||||
Dqn_f32 approach_dir_scalar_projection_onto_entity_to_waypoint_vector = -1.1f;
|
||||
DQN_FOR_UINDEX (dir_index, FP_GameDirection_Count) {
|
||||
Dqn_V2 attack_dir = dir_vectors[dir_index];
|
||||
Dqn_f32 scalar_projection = Dqn_V2_Dot(attack_dir, entity_to_target_norm);
|
||||
if (scalar_projection > approach_dir_scalar_projection_onto_entity_to_waypoint_vector) {
|
||||
approach_dir = DQN_CAST(FP_GameDirection)dir_index;
|
||||
approach_dir_scalar_projection_onto_entity_to_waypoint_vector = scalar_projection;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Dqn_f32 arrival_threshold = {};
|
||||
switch (waypoint->arrive) {
|
||||
case FP_GameWaypointArrive_Default: {
|
||||
Dqn_Rect waypoint_hit_box = FP_Game_CalcEntityWorldHitBox(game, waypoint_entity->handle);
|
||||
arrival_threshold = waypoint_hit_box.size.w * .5f;
|
||||
if (approach_dir == FP_GameDirection_Up || approach_dir == FP_GameDirection_Down)
|
||||
arrival_threshold = 10.f;
|
||||
else
|
||||
arrival_threshold = 10.f;
|
||||
} break;
|
||||
|
||||
case FP_GameWaypointArrive_WhenWithinEntitySize: {
|
||||
@@ -1712,9 +1727,7 @@ void FP_Update(TELY_Platform *platform, FP_Game *game, TELY_PlatformInput *input
|
||||
waypoint_entity->type == FP_EntityType_ClubTerry ||
|
||||
waypoint_entity->type == FP_EntityType_AirportTerry ||
|
||||
waypoint_entity->type == FP_EntityType_ChurchTerry;
|
||||
bool heart_response = (entity->flags & FP_GameEntityFlag_PointOfInterestHeart) && waypoint_entity->type == FP_EntityType_Heart;
|
||||
|
||||
if (((waypoint->flags & FP_GameWaypointFlag_NonInterruptible) == 0) && (aggro || building_response || heart_response)) {
|
||||
if (((waypoint->flags & FP_GameWaypointFlag_NonInterruptible) == 0) && (aggro || building_response)) {
|
||||
bool can_attack = !entity->is_dying; // TODO(doyle): State transition needs to check if it's valid to move to making this not necessary
|
||||
if (building_response) {
|
||||
FP_GameEntity *building = waypoint_entity;
|
||||
@@ -1742,27 +1755,6 @@ void FP_Update(TELY_Platform *platform, FP_Game *game, TELY_PlatformInput *input
|
||||
}
|
||||
|
||||
if (can_attack) {
|
||||
Dqn_V2 attack_dir_vectors[FP_GameDirection_Count] = {};
|
||||
attack_dir_vectors[FP_GameDirection_Up] = Dqn_V2_InitNx2(+0, -1);
|
||||
attack_dir_vectors[FP_GameDirection_Down] = Dqn_V2_InitNx2(+0, +1);
|
||||
attack_dir_vectors[FP_GameDirection_Left] = Dqn_V2_InitNx2(-1, +0);
|
||||
attack_dir_vectors[FP_GameDirection_Right] = Dqn_V2_InitNx2(+1, +0);
|
||||
|
||||
Dqn_V2 defender_entity_pos = FP_Game_CalcEntityWorldPos(game, waypoint_entity->handle);
|
||||
Dqn_V2 entity_to_defender = defender_entity_pos - entity_pos;
|
||||
Dqn_V2 entity_to_defender_norm = Dqn_V2_Normalise(entity_to_defender);
|
||||
|
||||
FP_GameDirection best_attack_dir = FP_GameDirection_Up;
|
||||
Dqn_f32 best_attack_dir_scalar_projection_onto_entity_to_waypoint_vector = -1.1f;
|
||||
DQN_FOR_UINDEX (dir_index, FP_GameDirection_Count) {
|
||||
Dqn_V2 attack_dir = attack_dir_vectors[dir_index];
|
||||
Dqn_f32 scalar_projection = Dqn_V2_Dot(attack_dir, entity_to_defender_norm);
|
||||
if (scalar_projection > best_attack_dir_scalar_projection_onto_entity_to_waypoint_vector) {
|
||||
best_attack_dir = DQN_CAST(FP_GameDirection)dir_index;
|
||||
best_attack_dir_scalar_projection_onto_entity_to_waypoint_vector = scalar_projection;
|
||||
}
|
||||
}
|
||||
|
||||
switch (entity->type) {
|
||||
case FP_EntityType_Terry: /*FALLTHRU*/
|
||||
case FP_EntityType_Smoochie: /*FALLTHRU*/
|
||||
@@ -1780,7 +1772,7 @@ void FP_Update(TELY_Platform *platform, FP_Game *game, TELY_PlatformInput *input
|
||||
DQN_ASSERT(entity->type == FP_EntityType_Clinger);
|
||||
FP_Game_EntityTransitionState(game, entity, FP_EntityClingerState_Attack);
|
||||
}
|
||||
entity->direction = best_attack_dir;
|
||||
entity->direction = approach_dir;
|
||||
} break;
|
||||
|
||||
case FP_EntityType_Nil: break;
|
||||
@@ -1805,7 +1797,6 @@ void FP_Update(TELY_Platform *platform, FP_Game *game, TELY_PlatformInput *input
|
||||
FP_SentinelList_Erase(&entity->waypoints, waypoint_link, game->play.chunk_pool);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// NOTE: Move entity by mouse ==============================================================
|
||||
if (game->play.active_entity == entity->handle && entity->flags & FP_GameEntityFlag_MoveByMouse) {
|
||||
@@ -1873,14 +1864,17 @@ void FP_Update(TELY_Platform *platform, FP_Game *game, TELY_PlatformInput *input
|
||||
TELY_Platform_InputScanCodeIsPressed(input, TELY_PlatformInputScanCode_J)) {
|
||||
if (placeable_building.type == FP_EntityType_ClubTerry) {
|
||||
FP_Entity_CreateClubTerry(game, placement_pos, "Club Terry");
|
||||
TELY_Audio_Play(audio, game->audio[FP_GameAudio_Club], 1.f);
|
||||
} else if (placeable_building.type == FP_EntityType_ChurchTerry) {
|
||||
FP_Entity_CreateChurchTerry(game, placement_pos, "Church Terry");
|
||||
TELY_Audio_Play(audio, game->audio[FP_GameAudio_Church], 1.f);
|
||||
} else if (placeable_building.type == FP_EntityType_AirportTerry) {
|
||||
FP_Entity_CreateAirportTerry(game, placement_pos, "Airport Terry");
|
||||
TELY_Audio_Play(audio, game->audio[FP_GameAudio_Plane], 1.f);
|
||||
} else {
|
||||
DQN_ASSERT(placeable_building.type == FP_EntityType_KennelTerry);
|
||||
FP_Entity_CreateKennelTerry(game, placement_pos, "Kennel Terry");
|
||||
TELY_Audio_Play(audio, game->audio[FP_GameAudio_Dog], 1.f);
|
||||
}
|
||||
|
||||
(*inventory_count)--;
|
||||
@@ -1943,10 +1937,12 @@ void FP_Update(TELY_Platform *platform, FP_Game *game, TELY_PlatformInput *input
|
||||
entity->terry_mobile_data_plan_cap);
|
||||
|
||||
// NOTE: Recover hp & stamina
|
||||
if (game->play.update_counter % 4 == 0) {
|
||||
entity->stamina = DQN_MIN(entity->stamina + 1, entity->stamina_cap);
|
||||
}
|
||||
|
||||
if (entity->flags & FP_GameEntityFlag_RecoversHP) {
|
||||
if (game->play.update_counter % 12 == 0) {
|
||||
if (game->play.update_counter % entity->hp_recover_every_n_ticks == 0) {
|
||||
entity->hp = DQN_MIN(entity->hp + 1, entity->hp_cap);
|
||||
}
|
||||
}
|
||||
@@ -2102,6 +2098,15 @@ void FP_Update(TELY_Platform *platform, FP_Game *game, TELY_PlatformInput *input
|
||||
game->play.state = FP_GameState_WinGame;
|
||||
}
|
||||
|
||||
{
|
||||
FP_GameEntity *heart = FP_Game_GetEntity(game, game->play.heart);
|
||||
FP_GameEntity *player = FP_Game_GetEntity(game, game->play.player);
|
||||
if (heart->hp <= 0) {
|
||||
player->hp = 0;
|
||||
game->play.state = FP_GameState_LoseGame;
|
||||
}
|
||||
}
|
||||
|
||||
if (!FP_Game_IsNilEntityHandle(game, game->play.clicked_entity)) {
|
||||
TELY_AssetSpriteAnimation *sprite_anim = TELY_Asset_GetSpriteAnimation(&game->atlas_sprite_sheet, g_anim_names.map);
|
||||
Dqn_Rect sprite_rect = game->atlas_sprite_sheet.rects.data[sprite_anim->index];
|
||||
@@ -2303,7 +2308,7 @@ void FP_Render(FP_Game *game, TELY_Platform *platform, TELY_Renderer *renderer,
|
||||
TELY_COLOUR_WHITE_V4);
|
||||
}
|
||||
|
||||
if (entity->handle != game->play.player && entity->hp != entity->hp_cap && entity->hp) {
|
||||
if (entity->handle != game->play.player && entity->handle != game->play.heart && entity->hp != entity->hp_cap && entity->hp) {
|
||||
Dqn_f32 bar_height = 12.f;
|
||||
TELY_AssetSpriteAnimation *anim = TELY_Asset_GetSpriteAnimation(&game->atlas_sprite_sheet, g_anim_names.icon_health);
|
||||
Dqn_Rect icon_tex_rect = game->atlas_sprite_sheet.rects.data[anim->index];
|
||||
@@ -2422,7 +2427,7 @@ void FP_Render(FP_Game *game, TELY_Platform *platform, TELY_Renderer *renderer,
|
||||
}
|
||||
}
|
||||
|
||||
if (game->play.state == FP_GameState_IntroScreen || game->play.state == FP_GameState_WinGame) {
|
||||
if (game->play.state == FP_GameState_IntroScreen || game->play.state == FP_GameState_WinGame || game->play.state == FP_GameState_LoseGame) {
|
||||
TELY_Render_PushTransform(renderer, Dqn_M2x3_Identity());
|
||||
DQN_DEFER { TELY_Render_PopTransform(renderer); };
|
||||
TELY_Render_RectColourV4(
|
||||
@@ -2449,6 +2454,20 @@ void FP_Render(FP_Game *game, TELY_Platform *platform, TELY_Renderer *renderer,
|
||||
|
||||
if (TELY_Platform_InputScanCodeIsPressed(input, TELY_PlatformInputScanCode_Return))
|
||||
game->play.state = FP_GameState_Play;
|
||||
} else if (game->play.state == FP_GameState_LoseGame) {
|
||||
TELY_Render_PushFont(renderer, game->inter_regular_font_large);
|
||||
TELY_Render_TextF(renderer, draw_p, Dqn_V2_Zero, "Terry's heart has been crushed"); draw_p.y += TELY_Render_FontHeight(renderer, assets);
|
||||
TELY_Render_TextF(renderer, draw_p, Dqn_V2_Zero, "from the hoard of monsters"); draw_p.y += TELY_Render_FontHeight(renderer, assets);
|
||||
TELY_Render_PopFont(renderer);
|
||||
|
||||
TELY_Render_PushFont(renderer, game->inter_regular_font);
|
||||
TELY_Render_TextF(renderer, draw_p, Dqn_V2_Zero, "Sayounara amigo"); draw_p.y += TELY_Render_FontHeight(renderer, assets);
|
||||
TELY_Render_TextF(renderer, draw_p, Dqn_V2_Zero, "Press enter to restart"); draw_p.y += TELY_Render_FontHeight(renderer, assets);
|
||||
TELY_Render_PopFont(renderer);
|
||||
TELY_Render_PopColourV4(renderer);
|
||||
|
||||
if (TELY_Platform_InputScanCodeIsPressed(input, TELY_PlatformInputScanCode_Return))
|
||||
FP_PlayReset(game, platform);
|
||||
} else {
|
||||
TELY_Render_PushFont(renderer, game->inter_regular_font_large);
|
||||
TELY_Render_TextF(renderer, draw_p, Dqn_V2_Zero, "Terry has been saved"); draw_p.y += TELY_Render_FontHeight(renderer, assets);
|
||||
@@ -2470,6 +2489,7 @@ void FP_Render(FP_Game *game, TELY_Platform *platform, TELY_Renderer *renderer,
|
||||
FP_GameEntity *player = FP_Game_GetEntity(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};
|
||||
FP_GameInventory *invent = &player->inventory;
|
||||
struct FP_MerchantToMenuMapping {
|
||||
FP_GameEntityHandle merchant;
|
||||
@@ -2479,11 +2499,13 @@ void FP_Render(FP_Game *game, TELY_Platform *platform, TELY_Renderer *renderer,
|
||||
uint8_t *inventory_count;
|
||||
uint32_t *building_base_price;
|
||||
uint32_t *upgrade_base_price;
|
||||
FP_GameAudio audio_type;
|
||||
bool *sound_played;
|
||||
} 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},
|
||||
{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},
|
||||
{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},
|
||||
{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},
|
||||
{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]},
|
||||
{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]},
|
||||
{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]},
|
||||
{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]},
|
||||
};
|
||||
|
||||
bool activated_merchant = false;
|
||||
@@ -2492,8 +2514,10 @@ void FP_Render(FP_Game *game, TELY_Platform *platform, TELY_Renderer *renderer,
|
||||
Dqn_V2 world_pos = FP_Game_CalcEntityWorldPos(game, merchant_handle);
|
||||
Dqn_f32 dist_squared = Dqn_V2_LengthSq_V2x2(world_pos, player_pos);
|
||||
|
||||
if (dist_squared > DQN_SQUARED(FP_Game_MetersToPixelsNx1(game->play, 4)))
|
||||
if (dist_squared > DQN_SQUARED(FP_Game_MetersToPixelsNx1(game->play, 4))) {
|
||||
*mapping.sound_played = false;
|
||||
continue;
|
||||
}
|
||||
|
||||
// NOTE: Render animated merchant menu =============================
|
||||
activated_merchant = true;
|
||||
@@ -2524,6 +2548,11 @@ void FP_Render(FP_Game *game, TELY_Platform *platform, TELY_Renderer *renderer,
|
||||
Dqn_V2_Zero /*rotate origin*/,
|
||||
0.f /*rotation*/,
|
||||
TELY_COLOUR_WHITE_V4);
|
||||
|
||||
if (activated_merchant && !*mapping.sound_played) {
|
||||
TELY_Audio_Play(audio, game->audio[mapping.audio_type], 1.f);
|
||||
*mapping.sound_played = true;
|
||||
}
|
||||
}
|
||||
|
||||
TELY_Render_PushColourV4(renderer, TELY_COLOUR_WHITE_V4);
|
||||
@@ -3029,6 +3058,53 @@ void FP_Render(FP_Game *game, TELY_Platform *platform, TELY_Renderer *renderer,
|
||||
}
|
||||
}
|
||||
|
||||
// NOTE: Render the heart health
|
||||
{
|
||||
TELY_Render_PushTransform(renderer, Dqn_M2x3_Identity());
|
||||
DQN_DEFER { TELY_Render_PopTransform(renderer); };
|
||||
|
||||
Dqn_f32 font_height = TELY_Render_FontHeight(renderer, assets);
|
||||
Dqn_f32 bar_height = font_height * 1.25f;
|
||||
{
|
||||
TELY_AssetSpriteAnimation *anim = TELY_Asset_GetSpriteAnimation(&game->atlas_sprite_sheet, g_anim_names.heart);
|
||||
FP_GameEntity *heart = FP_Game_GetEntity(game, game->play.heart);
|
||||
|
||||
Dqn_f32 max_width = platform->core.window_size.x * .5f;
|
||||
|
||||
Dqn_V2 draw_p = Dqn_V2_InitNx2(platform->core.window_size.x * .25f, player_avatar_rect.pos.y + bar_height);
|
||||
Dqn_f32 health_t = heart->hp / DQN_CAST(Dqn_f32)heart->hp_cap;
|
||||
Dqn_Rect health_rect = Dqn_Rect_InitNx4(draw_p.x, draw_p.y, max_width, bar_height);
|
||||
Dqn_Rect curr_health_rect = Dqn_Rect_InitNx4(draw_p.x, draw_p.y, max_width * health_t, bar_height);
|
||||
|
||||
TELY_Render_RectColourV4(renderer, curr_health_rect, TELY_RenderShapeMode_Fill, TELY_COLOUR_RED_TOMATO_V4);
|
||||
|
||||
TELY_RenderCommandRect *cmd = TELY_Render_RectColourV4(
|
||||
renderer,
|
||||
health_rect,
|
||||
TELY_RenderShapeMode_Line,
|
||||
TELY_COLOUR_BLACK_V4);
|
||||
cmd->thickness = 4.f;
|
||||
|
||||
// NOTE: Draw the heart icon
|
||||
Dqn_Rect icon_tex_rect = game->atlas_sprite_sheet.rects.data[anim->index];
|
||||
Dqn_Rect heart_icon_rect = {};
|
||||
heart_icon_rect.size = icon_tex_rect.size * .4f;
|
||||
heart_icon_rect.pos = Dqn_V2_InitNx2(draw_p.x - (heart_icon_rect.size.w * .7f), draw_p.y - (heart_icon_rect.size.y * .4f));
|
||||
|
||||
TELY_Render_TextureColourV4(renderer,
|
||||
game->atlas_sprite_sheet.tex_handle,
|
||||
icon_tex_rect,
|
||||
heart_icon_rect,
|
||||
Dqn_V2_Zero /*rotate origin*/,
|
||||
0.f /*rotation*/,
|
||||
TELY_COLOUR_WHITE_V4);
|
||||
|
||||
TELY_Render_PushFont(renderer, game->talkco_font_large);
|
||||
DQN_DEFER { TELY_Render_PopFont(renderer); };
|
||||
TELY_Render_TextF(renderer, Dqn_Rect_InterpolatedPoint(heart_icon_rect, Dqn_V2_InitNx2(1.f, 0.65f)), Dqn_V2_Zero, "Terry's Heart");
|
||||
}
|
||||
}
|
||||
|
||||
// NOTE: Add scanlines into the game for A E S T H E T I C S
|
||||
Dqn_V2 screen_size = Dqn_V2_InitNx2(platform->core.window_size.w, platform->core.window_size.h);
|
||||
Dqn_f32 scanline_gap = 4.0f;
|
||||
|
||||
@@ -638,6 +638,12 @@ static FP_GameEntityHandle FP_Entity_CreateHeart(FP_Game *game, Dqn_V2 pos, DQN_
|
||||
|
||||
FP_Entity_AddDebugEditorFlags(game, result);
|
||||
entity->flags |= FP_GameEntityFlag_NonTraversable;
|
||||
entity->flags |= FP_GameEntityFlag_Attackable;
|
||||
entity->flags |= FP_GameEntityFlag_RecoversHP;
|
||||
entity->hp_cap = FP_DEFAULT_DAMAGE * 16;
|
||||
entity->hp = entity->hp_cap;
|
||||
entity->faction = FP_GameEntityFaction_Friendly;
|
||||
entity->hp_recover_every_n_ticks *= 4;
|
||||
|
||||
TELY_AssetSpriteAnimation *sprite_anim = TELY_Asset_GetSpriteAnimation(&game->atlas_sprite_sheet, g_anim_names.heart);
|
||||
Dqn_Rect sprite_rect = game->atlas_sprite_sheet.rects.data[sprite_anim->index];
|
||||
|
||||
@@ -234,6 +234,7 @@ static FP_GameEntity *FP_Game_MakeEntityPointerFV(FP_Game *game, DQN_FMT_STRING_
|
||||
result->inventory.kennels_base_price = 50;
|
||||
result->inventory.clubs_base_price = 50;
|
||||
result->base_attack = FP_DEFAULT_DAMAGE;
|
||||
result->hp_recover_every_n_ticks = 12;
|
||||
|
||||
result->inventory.stamina_base_price = 10;
|
||||
result->inventory.health_base_price = 10;
|
||||
@@ -742,6 +743,7 @@ static Dqn_V2 FP_Game_CalcWaypointWorldPos(FP_Game *game, FP_GameEntityHandle sr
|
||||
}
|
||||
}
|
||||
|
||||
#if 0
|
||||
Dqn_V2 entity_pos = FP_Game_CalcEntityWorldPos(game, waypoint_entity->handle);
|
||||
Dqn_V2 src_pos = FP_Game_CalcEntityWorldPos(game, src_entity);
|
||||
Dqn_f32 curr_dist_to_entity = Dqn_V2_LengthSq_V2x2(entity_pos, src_pos);
|
||||
@@ -750,6 +752,7 @@ static Dqn_V2 FP_Game_CalcWaypointWorldPos(FP_Game *game, FP_GameEntityHandle sr
|
||||
// we assume we're at the entity already.
|
||||
result = FP_Game_CalcEntityWorldPos(game, src_entity);
|
||||
}
|
||||
#endif
|
||||
}
|
||||
} break;
|
||||
|
||||
|
||||
+17
-8
@@ -17,14 +17,13 @@ enum FP_GameEntityFlag
|
||||
FP_GameEntityFlag_Attackable = 1 << 9,
|
||||
FP_GameEntityFlag_RespondsToBuildings = 1 << 10,
|
||||
FP_GameEntityFlag_OccupiedInBuilding = 1 << 11,
|
||||
FP_GameEntityFlag_PointOfInterestHeart = 1 << 12,
|
||||
FP_GameEntityFlag_CameraTracking = 1 << 13,
|
||||
FP_GameEntityFlag_BuildZone = 1 << 14,
|
||||
FP_GameEntityFlag_TTL = 1 << 15,
|
||||
FP_GameEntityFlag_Friendly = 1 << 16,
|
||||
FP_GameEntityFlag_Foe = 1 << 17,
|
||||
FP_GameEntityFlag_NoClip = 1 << 18,
|
||||
FP_GameEntityFlag_RecoversHP = 1 << 19,
|
||||
FP_GameEntityFlag_CameraTracking = 1 << 12,
|
||||
FP_GameEntityFlag_BuildZone = 1 << 13,
|
||||
FP_GameEntityFlag_TTL = 1 << 14,
|
||||
FP_GameEntityFlag_Friendly = 1 << 15,
|
||||
FP_GameEntityFlag_Foe = 1 << 16,
|
||||
FP_GameEntityFlag_NoClip = 1 << 17,
|
||||
FP_GameEntityFlag_RecoversHP = 1 << 18,
|
||||
};
|
||||
|
||||
enum FP_GameShapeType
|
||||
@@ -228,6 +227,7 @@ struct FP_GameEntity
|
||||
uint32_t hp_cap;
|
||||
uint32_t stamina;
|
||||
uint32_t stamina_cap;
|
||||
uint32_t hp_recover_every_n_ticks;
|
||||
uint32_t base_attack;
|
||||
|
||||
bool converted_faction;
|
||||
@@ -265,6 +265,13 @@ enum FP_GameAudio
|
||||
FP_GameAudio_Woosh,
|
||||
FP_GameAudio_Ching,
|
||||
FP_GameAudio_Church,
|
||||
FP_GameAudio_Plane,
|
||||
FP_GameAudio_Club,
|
||||
FP_GameAudio_Dog,
|
||||
FP_GameAudio_MerchantTerry,
|
||||
FP_GameAudio_MerchantGhost,
|
||||
FP_GameAudio_MerchantGym,
|
||||
FP_GameAudio_MerchantPhone,
|
||||
FP_GameAudio_Count,
|
||||
};
|
||||
|
||||
@@ -280,6 +287,7 @@ enum FP_GameState
|
||||
FP_GameState_IntroScreen,
|
||||
FP_GameState_Play,
|
||||
FP_GameState_WinGame,
|
||||
FP_GameState_LoseGame,
|
||||
};
|
||||
|
||||
struct FP_GamePlay
|
||||
@@ -299,6 +307,7 @@ struct FP_GamePlay
|
||||
FP_GameRenderSprite player_merchant_menu;
|
||||
uint64_t player_trigger_purchase_upgrade_timestamp;
|
||||
uint64_t player_trigger_purchase_building_timestamp;
|
||||
FP_GameEntityHandle heart;
|
||||
|
||||
FP_GameEntityHandle merchant_terry;
|
||||
FP_GameEntityHandle merchant_graveyard;
|
||||
|
||||
Reference in New Issue
Block a user