fp: Load heart & remove aggro when terry distances himself

This commit is contained in:
2023-10-01 16:11:08 +11:00
parent f258886d68
commit 0234d34673
18 changed files with 154 additions and 15 deletions
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
+1
View File
@@ -16,3 +16,4 @@ if not exist "%sprite_packer%" (
%sprite_packer% 4096x2048 %script_dir%\Data\Textures\sprite_spec.txt %script_dir%\Data\Textures\clinger_resized_25%% || exit /b 1
%sprite_packer% 4096x2048 %script_dir%\Data\Textures\sprite_spec.txt %script_dir%\Data\Textures\club_terry_resized_25%% || exit /b 1
%sprite_packer% 4096x2048 %script_dir%\Data\Textures\sprite_spec.txt %script_dir%\Data\Textures\map_resized_25%% || exit /b 1
%sprite_packer% 8192x8192 %script_dir%\Data\Textures\sprite_spec.txt %script_dir%\Data\Textures\heart_resized_25%% || exit /b 1
+73 -4
View File
@@ -277,6 +277,7 @@ void TELY_DLL_Init(void *user_data)
game->clinger_sprite_sheet = FP_LoadSpriteSheetFromSpec(platform, assets, &platform->arena, DQN_STRING8("clinger_resized_25%"));
game->club_terry_sprite_sheet = FP_LoadSpriteSheetFromSpec(platform, assets, &platform->arena, DQN_STRING8("club_terry_resized_25%"));
game->map_sprite_sheet = FP_LoadSpriteSheetFromSpec(platform, assets, &platform->arena, DQN_STRING8("map_resized_25%"));
game->heart_sprite_sheet = FP_LoadSpriteSheetFromSpec(platform, assets, &platform->arena, DQN_STRING8("heart_resized_25%"));
}
game->entities = Dqn_VArray_Init<FP_GameEntity>(&platform->arena, 1024 * 8);
@@ -388,6 +389,7 @@ void TELY_DLL_Init(void *user_data)
FP_Game_PopParentEntity(game);
}
FP_Entity_CreateHeart(game, base_mid_p, "Heart");
uint16_t font_size = 18;
game->camera.scale = Dqn_V2_InitNx1(1);
@@ -943,9 +945,27 @@ void FP_EntityActionStateMachine(FP_Game *game, TELY_Audio *audio, TELY_Platform
} break;
case FP_EntityType_Heart: {
FP_EntityHeartState *state = DQN_CAST(FP_EntityHeartState *) & action->state;
TELY_AssetSpriteSheet *sheet = &game->heart_sprite_sheet;
switch (*state) {
case FP_EntityHeartState_Nil: {
action->next_state = FP_EntityHeartState_Idle;
} break;
case FP_EntityHeartState_Idle: {
if (entering_new_state) {
TELY_AssetAnimatedSprite sprite = TELY_Asset_MakeAnimatedSprite(sheet, g_anim_names.heart, TELY_AssetFlip_No);
uint64_t duration_ms = FP_GAME_ENTITY_ACTION_INFINITE_TIMER;
FP_Game_EntityActionReset(game, entity->handle, duration_ms, sprite);
}
} break;
}
} break;
case FP_EntityType_Nil: break;
case FP_EntityType_Count: DQN_INVALID_CODE_PATH; break;
}
}
@@ -1058,9 +1078,11 @@ void FP_Update(TELY_Platform *platform, FP_Game *game, TELY_PlatformInput *input
Dqn_V2 entity_pos = FP_Game_CalcEntityWorldPos(game, entity->handle);
if (entity->flags & FP_GameEntityFlag_AggrosWhenNearTerry) {
FP_GameFindClosestEntityResult closest_result = FP_Game_FindClosestEntityWithType(game, entity->handle, FP_EntityType_Terry);
if (closest_result.dist_squared < DQN_SQUARED(FP_Game_MetersToPixelsNx1(game, 4.f))) {
FP_GameFindClosestEntityResult closest_result = FP_Game_FindClosestEntityWithType(game, entity->handle, FP_EntityType_Terry);
Dqn_f32 aggro_dist_threshold = FP_Game_MetersToPixelsNx1(game, 4.f);
if (closest_result.dist_squared < DQN_SQUARED(aggro_dist_threshold)) {
bool has_waypoint_to_terry = false;
for (FP_SentinelListLink<FP_GameWaypoint> *link = nullptr;
!has_waypoint_to_terry && FP_SentinelList_Iterate<FP_GameWaypoint>(&entity->waypoints, &link); ) {
@@ -1091,6 +1113,15 @@ void FP_Update(TELY_Platform *platform, FP_Game *game, TELY_PlatformInput *input
terry->aggro_slot[aggro_direction] = entity->handle;
}
}
} else {
if (closest_result.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->chunk_pool);
}
}
}
}
}
@@ -1127,6 +1158,39 @@ 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, 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->chunk_pool);
FP_GameWaypoint *waypoint = &link->data;
waypoint->entity = closest_heart.entity;
waypoint->type = FP_GameWaypointType_Side;
if (entity_pos.x <= club_top_left.x) {
waypoint->type_direction = FP_GameDirection_Left;
} else if (entity_pos.x >= club_top_right.x) {
waypoint->type_direction = FP_GameDirection_Right;
} else if (entity_pos.y <= club_top_left.y) {
waypoint->type_direction = FP_GameDirection_Up;
} else {
waypoint->type_direction = FP_GameDirection_Down;
}
}
}
}
while (entity->waypoints.size) {
FP_SentinelListLink<FP_GameWaypoint> *waypoint_link = entity->waypoints.sentinel->next;
FP_GameWaypoint const *waypoint = &waypoint_link->data;
@@ -1163,10 +1227,14 @@ void FP_Update(TELY_Platform *platform, FP_Game *game, TELY_PlatformInput *input
// NOTE: We have arrived at the waypoint
bool aggro_on_terry = (entity->flags & FP_GameEntityFlag_AggrosWhenNearTerry) && waypoint_entity->type == FP_EntityType_Terry;
bool club_terry_response = (entity->flags & FP_GameEntityFlag_RespondsToClubTerry) &&
((entity->flags & FP_GameEntityFlag_ExperiencedClubTerry) == 0) &&
waypoint_entity->type == FP_EntityType_ClubTerry;
if (((waypoint->flags & FP_GameWaypointFlag_NonInterruptible) == 0) && (aggro_on_terry || club_terry_response)) {
bool heart_response = (entity->flags & FP_GameEntityFlag_PointOfInterestHeart) && waypoint_entity->type == FP_EntityType_Heart;
if (((waypoint->flags & FP_GameWaypointFlag_NonInterruptible) == 0) && (aggro_on_terry || club_terry_response || heart_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 (club_terry_response) {
@@ -1230,6 +1298,7 @@ void FP_Update(TELY_Platform *platform, FP_Game *game, TELY_PlatformInput *input
case FP_EntityType_Count: DQN_INVALID_CODE_PATH; break;
case FP_EntityType_ClubTerry: break;
case FP_EntityType_Map: break;
case FP_EntityType_Heart: break;
}
}
+12 -9
View File
@@ -42,17 +42,20 @@ struct FP_GlobalAnimations
Dqn_String8 smoochie_attack_heart = DQN_STRING8("smoochie_attack_heart");
Dqn_String8 smoochie_death = DQN_STRING8("smoochie_death");
Dqn_String8 clinger_attack_down = DQN_STRING8("clinger_attack_down");
Dqn_String8 clinger_attack_side = DQN_STRING8("clinger_attack_side");
Dqn_String8 clinger_attack_up = DQN_STRING8("clinger_attack_up");
Dqn_String8 clinger_death = DQN_STRING8("clinger_death");
Dqn_String8 clinger_walk_up = DQN_STRING8("clinger_walk_up");
Dqn_String8 clinger_walk_down = DQN_STRING8("clinger_walk_down");
Dqn_String8 clinger_attack_down = DQN_STRING8("clinger_attack_down");
Dqn_String8 clinger_attack_side = DQN_STRING8("clinger_attack_side");
Dqn_String8 clinger_attack_up = DQN_STRING8("clinger_attack_up");
Dqn_String8 clinger_death = DQN_STRING8("clinger_death");
Dqn_String8 clinger_walk_up = DQN_STRING8("clinger_walk_up");
Dqn_String8 clinger_walk_down = DQN_STRING8("clinger_walk_down");
Dqn_String8 club_terry_alive = DQN_STRING8("club_terry_alive");
Dqn_String8 club_terry_dark = DQN_STRING8("club_terry_dark");
Dqn_String8 club_terry_alive = DQN_STRING8("club_terry_alive");
Dqn_String8 club_terry_dark = DQN_STRING8("club_terry_dark");
Dqn_String8 map = DQN_STRING8("map");
Dqn_String8 heart = DQN_STRING8("heart");
Dqn_String8 heart_bleed = DQN_STRING8("heart_bleed");
Dqn_String8 map = DQN_STRING8("map");
}
g_anim_names;
+7
View File
@@ -12,6 +12,7 @@ enum FP_EntityType
FP_EntityType_Merchant,
FP_EntityType_ClubTerry,
FP_EntityType_Clinger,
FP_EntityType_Heart,
FP_EntityType_Count,
};
@@ -62,3 +63,9 @@ enum FP_EntityMapState
FP_EntityMapState_Nil,
FP_EntityMapState_Idle,
};
enum FP_EntityHeartState
{
FP_EntityHeartState_Nil,
FP_EntityHeartState_Idle,
};
+24
View File
@@ -191,3 +191,27 @@ static FP_GameEntityHandle FP_Entity_CreateClubTerry(FP_Game *game, Dqn_V2 pos,
return result;
}
static FP_GameEntityHandle FP_Entity_CreateHeart(FP_Game *game, Dqn_V2 pos, DQN_FMT_STRING_ANNOTATE char const *fmt, ...)
{
va_list args;
va_start(args, fmt);
FP_GameEntity *entity = FP_Game_MakeEntityPointerFV(game, fmt, args);
FP_GameEntityHandle result = entity->handle;
va_end(args);
entity->type = FP_EntityType_Heart;
entity->local_pos = pos;
entity->sprite_height.meters = 4.f;
TELY_AssetSpriteAnimation *sprite_anim = TELY_Asset_GetSpriteAnimation(&game->heart_sprite_sheet, g_anim_names.heart);
Dqn_Rect sprite_rect = game->heart_sprite_sheet.rects.data[sprite_anim->index];
Dqn_f32 size_scale = FP_Entity_CalcSpriteScaleForDesiredHeight(game, entity->sprite_height, sprite_rect);
Dqn_V2 sprite_rect_scaled = sprite_rect.size * size_scale;
entity->local_hit_box_size = Dqn_V2_InitNx2(sprite_rect_scaled.w - (sprite_rect_scaled.w * .1f), sprite_rect_scaled.h);
FP_Entity_AddDebugEditorFlags(game, result);
entity->flags |= FP_GameEntityFlag_NonTraversable;
return result;
}
+2
View File
@@ -19,6 +19,7 @@ enum FP_GameEntityFlag
FP_GameEntityFlag_RespondsToClubTerry = 1 << 11,
FP_GameEntityFlag_PartyingAtClubTerry = 1 << 12,
FP_GameEntityFlag_ExperiencedClubTerry = 1 << 13,
FP_GameEntityFlag_PointOfInterestHeart = 1 << 11,
FP_GameEntityFlag_CameraTracking = 1 << 14,
};
@@ -230,6 +231,7 @@ struct FP_Game
TELY_AssetSpriteSheet clinger_sprite_sheet;
TELY_AssetSpriteSheet club_terry_sprite_sheet;
TELY_AssetSpriteSheet map_sprite_sheet;
TELY_AssetSpriteSheet heart_sprite_sheet;
FP_GameEntity *root_entity;
FP_GameEntity *entity_free_list;