fp: Load heart & remove aggro when terry distances himself
This commit is contained in:
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.
@@ -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
@@ -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
@@ -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;
|
||||
|
||||
|
||||
@@ -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,
|
||||
};
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
|
||||
@@ -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;
|
||||
|
||||
Reference in New Issue
Block a user