diff --git a/Data/Textures/heart_resized_25%.png b/Data/Textures/heart_resized_25%.png new file mode 100644 index 0000000..c81c305 --- /dev/null +++ b/Data/Textures/heart_resized_25%.png @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:3916dfadae349df2161151447665c4820583a32b2828b28c9912977e4e80ea6f +size 7622509 diff --git a/Data/Textures/heart_resized_25%.txt b/Data/Textures/heart_resized_25%.txt new file mode 100644 index 0000000..58b7b85 --- /dev/null +++ b/Data/Textures/heart_resized_25%.txt @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:9899e8cad454c234200fa4b26832b10bf1f66d17a498156a39a9c57629bb0412 +size 228 diff --git a/Data/Textures/heart_resized_25%/heart_1.png b/Data/Textures/heart_resized_25%/heart_1.png new file mode 100644 index 0000000..9a7a980 --- /dev/null +++ b/Data/Textures/heart_resized_25%/heart_1.png @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:1a4a319fbf7515c40ce079cd680ba79907de959a6cdd866f5d5fdc1134dc5c20 +size 547929 diff --git a/Data/Textures/heart_resized_25%/heart_2.png b/Data/Textures/heart_resized_25%/heart_2.png new file mode 100644 index 0000000..3fd2c7f --- /dev/null +++ b/Data/Textures/heart_resized_25%/heart_2.png @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:369c4bebdcb87b5a4acef47387a0ef8a7982f7671f1b4fc6549b39eda769b335 +size 522699 diff --git a/Data/Textures/heart_resized_25%/heart_3.png b/Data/Textures/heart_resized_25%/heart_3.png new file mode 100644 index 0000000..6a19361 --- /dev/null +++ b/Data/Textures/heart_resized_25%/heart_3.png @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:cedcf20edf7bcca6479cea130e0ca8817ff6e9af36aa2f0e37775f3ce2ef66dc +size 520317 diff --git a/Data/Textures/heart_resized_25%/heart_bleed_1.png b/Data/Textures/heart_resized_25%/heart_bleed_1.png new file mode 100644 index 0000000..9f18306 --- /dev/null +++ b/Data/Textures/heart_resized_25%/heart_bleed_1.png @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:fbb9134c359b55c19522ee3f6acdc19e1571c00fe16ae38e6c3a2784e20e0371 +size 567759 diff --git a/Data/Textures/heart_resized_25%/heart_bleed_2.png b/Data/Textures/heart_resized_25%/heart_bleed_2.png new file mode 100644 index 0000000..ce8e745 --- /dev/null +++ b/Data/Textures/heart_resized_25%/heart_bleed_2.png @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:715797b749252eb7b342695abf37cafd52349db783f67c6dd49c5503170ad2e9 +size 578241 diff --git a/Data/Textures/heart_resized_25%/heart_bleed_3.png b/Data/Textures/heart_resized_25%/heart_bleed_3.png new file mode 100644 index 0000000..3005702 --- /dev/null +++ b/Data/Textures/heart_resized_25%/heart_bleed_3.png @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:c935856ed8a53f8e7c13c996d768d18178bcc5c4c8fbc84a4b996d8b84322733 +size 571846 diff --git a/Data/Textures/heart_resized_25%/heart_bleed_4.png b/Data/Textures/heart_resized_25%/heart_bleed_4.png new file mode 100644 index 0000000..cd28a76 --- /dev/null +++ b/Data/Textures/heart_resized_25%/heart_bleed_4.png @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:04e96d7adc455553d8f8be1f41031a780cbae90f9390d2113f47be8e94653b50 +size 574795 diff --git a/Data/Textures/heart_resized_25%/heart_bleed_5.png b/Data/Textures/heart_resized_25%/heart_bleed_5.png new file mode 100644 index 0000000..f219ff7 --- /dev/null +++ b/Data/Textures/heart_resized_25%/heart_bleed_5.png @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:5c5c589946b98768cc574e162578136768348f3f619b4fab2c05312a1fe76309 +size 568947 diff --git a/Data/Textures/heart_resized_25%/heart_bleed_6.png b/Data/Textures/heart_resized_25%/heart_bleed_6.png new file mode 100644 index 0000000..e15e275 --- /dev/null +++ b/Data/Textures/heart_resized_25%/heart_bleed_6.png @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:48d2b8b15203bfbbe98748952120af6b7ba9685cce324645771d9ae09476ac08 +size 560197 diff --git a/Data/Textures/sprite_spec.txt b/Data/Textures/sprite_spec.txt index 1837e08..fdbbd07 100644 --- a/Data/Textures/sprite_spec.txt +++ b/Data/Textures/sprite_spec.txt @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:87e41f86d79fb3b93da6eaf9931c11f597631414dfa5863d6b98a38bb2cff73e -size 529 +oid sha256:0855932bf54ec8903cc44d87aee7ba96c4e903e18b5ee2444d86071777284d5a +size 555 diff --git a/build_assets.bat b/build_assets.bat index 5b4037b..aa6b9e7 100644 --- a/build_assets.bat +++ b/build_assets.bat @@ -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 diff --git a/feely_pona.cpp b/feely_pona.cpp index b9ea650..ef6baa6 100644 --- a/feely_pona.cpp +++ b/feely_pona.cpp @@ -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(&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 *link = nullptr; !has_waypoint_to_terry && FP_SentinelList_Iterate(&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 *link = nullptr; FP_SentinelList_Iterate(&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 *link = nullptr; + !has_waypoint_to && FP_SentinelList_Iterate(&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 *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 *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; } } diff --git a/feely_pona.h b/feely_pona.h index ec9b824..b21bdee 100644 --- a/feely_pona.h +++ b/feely_pona.h @@ -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; diff --git a/feely_pona_entity.h b/feely_pona_entity.h index 2fa1b9e..e0c145e 100644 --- a/feely_pona_entity.h +++ b/feely_pona_entity.h @@ -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, +}; diff --git a/feely_pona_entity_create.cpp b/feely_pona_entity_create.cpp index 8e77c85..0d6d74d 100644 --- a/feely_pona_entity_create.cpp +++ b/feely_pona_entity_create.cpp @@ -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; +} diff --git a/feely_pona_game.h b/feely_pona_game.h index c5ae81a..e277ab3 100644 --- a/feely_pona_game.h +++ b/feely_pona_game.h @@ -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;