fp: Add monkeys and carrying to portal
This commit is contained in:
parent
7e1bcee5df
commit
631579d29b
BIN
Data/Textures/atlas.png
(Stored with Git LFS)
BIN
Data/Textures/atlas.png
(Stored with Git LFS)
Binary file not shown.
BIN
Data/Textures/atlas.txt
(Stored with Git LFS)
BIN
Data/Textures/atlas.txt
(Stored with Git LFS)
Binary file not shown.
BIN
Data/Textures/atlas/particle_church_cross.png
(Stored with Git LFS)
Normal file
BIN
Data/Textures/atlas/particle_church_cross.png
(Stored with Git LFS)
Normal file
Binary file not shown.
BIN
Data/Textures/atlas/particle_church_halo.png
(Stored with Git LFS)
Normal file
BIN
Data/Textures/atlas/particle_church_halo.png
(Stored with Git LFS)
Normal file
Binary file not shown.
BIN
Data/Textures/atlas/particle_drunk_bottle.png
(Stored with Git LFS)
Normal file
BIN
Data/Textures/atlas/particle_drunk_bottle.png
(Stored with Git LFS)
Normal file
Binary file not shown.
BIN
Data/Textures/atlas/particle_drunk_martini.png
(Stored with Git LFS)
Normal file
BIN
Data/Textures/atlas/particle_drunk_martini.png
(Stored with Git LFS)
Normal file
Binary file not shown.
BIN
Data/Textures/atlas/particle_hit_1_1.png
(Stored with Git LFS)
Normal file
BIN
Data/Textures/atlas/particle_hit_1_1.png
(Stored with Git LFS)
Normal file
Binary file not shown.
BIN
Data/Textures/atlas/particle_hit_2_1.png
(Stored with Git LFS)
Normal file
BIN
Data/Textures/atlas/particle_hit_2_1.png
(Stored with Git LFS)
Normal file
Binary file not shown.
BIN
Data/Textures/atlas/particle_hit_3_1.png
(Stored with Git LFS)
Normal file
BIN
Data/Textures/atlas/particle_hit_3_1.png
(Stored with Git LFS)
Normal file
Binary file not shown.
BIN
Data/Textures/atlas/portal_break_1.png
(Stored with Git LFS)
Normal file
BIN
Data/Textures/atlas/portal_break_1.png
(Stored with Git LFS)
Normal file
Binary file not shown.
BIN
Data/Textures/atlas/portal_break_10.png
(Stored with Git LFS)
Normal file
BIN
Data/Textures/atlas/portal_break_10.png
(Stored with Git LFS)
Normal file
Binary file not shown.
BIN
Data/Textures/atlas/portal_break_11.png
(Stored with Git LFS)
Normal file
BIN
Data/Textures/atlas/portal_break_11.png
(Stored with Git LFS)
Normal file
Binary file not shown.
BIN
Data/Textures/atlas/portal_break_12.png
(Stored with Git LFS)
Normal file
BIN
Data/Textures/atlas/portal_break_12.png
(Stored with Git LFS)
Normal file
Binary file not shown.
BIN
Data/Textures/atlas/portal_break_13.png
(Stored with Git LFS)
Normal file
BIN
Data/Textures/atlas/portal_break_13.png
(Stored with Git LFS)
Normal file
Binary file not shown.
BIN
Data/Textures/atlas/portal_break_14.png
(Stored with Git LFS)
Normal file
BIN
Data/Textures/atlas/portal_break_14.png
(Stored with Git LFS)
Normal file
Binary file not shown.
BIN
Data/Textures/atlas/portal_break_2.png
(Stored with Git LFS)
Normal file
BIN
Data/Textures/atlas/portal_break_2.png
(Stored with Git LFS)
Normal file
Binary file not shown.
BIN
Data/Textures/atlas/portal_break_3.png
(Stored with Git LFS)
Normal file
BIN
Data/Textures/atlas/portal_break_3.png
(Stored with Git LFS)
Normal file
Binary file not shown.
BIN
Data/Textures/atlas/portal_break_4.png
(Stored with Git LFS)
Normal file
BIN
Data/Textures/atlas/portal_break_4.png
(Stored with Git LFS)
Normal file
Binary file not shown.
BIN
Data/Textures/atlas/portal_break_5.png
(Stored with Git LFS)
Normal file
BIN
Data/Textures/atlas/portal_break_5.png
(Stored with Git LFS)
Normal file
Binary file not shown.
BIN
Data/Textures/atlas/portal_break_6.png
(Stored with Git LFS)
Normal file
BIN
Data/Textures/atlas/portal_break_6.png
(Stored with Git LFS)
Normal file
Binary file not shown.
BIN
Data/Textures/atlas/portal_break_7.png
(Stored with Git LFS)
Normal file
BIN
Data/Textures/atlas/portal_break_7.png
(Stored with Git LFS)
Normal file
Binary file not shown.
BIN
Data/Textures/atlas/portal_break_8.png
(Stored with Git LFS)
Normal file
BIN
Data/Textures/atlas/portal_break_8.png
(Stored with Git LFS)
Normal file
Binary file not shown.
BIN
Data/Textures/atlas/portal_break_9.png
(Stored with Git LFS)
Normal file
BIN
Data/Textures/atlas/portal_break_9.png
(Stored with Git LFS)
Normal file
Binary file not shown.
BIN
Data/Textures/atlas/portal_monk_1.png
(Stored with Git LFS)
BIN
Data/Textures/atlas/portal_monk_1.png
(Stored with Git LFS)
Binary file not shown.
BIN
Data/Textures/atlas/portal_monk_2.png
(Stored with Git LFS)
BIN
Data/Textures/atlas/portal_monk_2.png
(Stored with Git LFS)
Binary file not shown.
BIN
Data/Textures/sprite_spec.txt
(Stored with Git LFS)
BIN
Data/Textures/sprite_spec.txt
(Stored with Git LFS)
Binary file not shown.
247
feely_pona.cpp
247
feely_pona.cpp
@ -424,40 +424,10 @@ void TELY_DLL_Init(void *user_data)
|
||||
}
|
||||
}
|
||||
|
||||
// NOTE: Hero ==================================================================================
|
||||
{
|
||||
FP_GameEntityHandle terry = FP_Entity_CreateTerry(game, Dqn_V2_InitNx2(1434, 11), "Terry");
|
||||
game->clicked_entity = terry;
|
||||
game->player = terry;
|
||||
}
|
||||
|
||||
{
|
||||
Dqn_V2 base_top_left_pos = Dqn_V2_InitNx2(1018, -335);
|
||||
Dqn_V2 base_bottom_right_pos = Dqn_V2_InitNx2(2050, +351);
|
||||
|
||||
Dqn_V2 base_top_left = base_top_left_pos;
|
||||
Dqn_V2 base_top_right = Dqn_V2_InitNx2(base_bottom_right_pos.x, base_top_left_pos.y);
|
||||
Dqn_V2 base_bottom_left = Dqn_V2_InitNx2(base_top_left_pos.x, base_bottom_right_pos.y);
|
||||
Dqn_V2 base_bottom_right = Dqn_V2_InitNx2(base_bottom_right_pos.x, base_bottom_right_pos.y);
|
||||
|
||||
game->merchant_terry = FP_Entity_CreateMerchantTerry(game, base_top_left, "Merchant");
|
||||
game->merchant_graveyard = FP_Entity_CreateMerchantGraveyard(game, base_bottom_left, "Graveyard");
|
||||
game->merchant_gym = FP_Entity_CreateMerchantGym(game, base_bottom_right, "Gym");
|
||||
game->merchant_phone_company = FP_Entity_CreateMerchantPhoneCompany(game, base_top_right, "PhoneCompany");
|
||||
}
|
||||
|
||||
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");
|
||||
|
||||
game->tile_size = 37;
|
||||
Dqn_V2I max_tile = platform->core.window_size / game->tile_size;
|
||||
|
||||
|
||||
// NOTE: Mid lane mob spawner ==================================================================
|
||||
Dqn_V2 base_mid_p = Dqn_V2_InitNx2(1580, 0.f);
|
||||
Dqn_V2 mid_lane_mob_spawner_pos = Dqn_V2_InitNx2(game->map->local_hit_box_size.w * -0.5f, 0.f);
|
||||
{
|
||||
// NOTE: Mid lane mob spawner ==================================================================
|
||||
Dqn_V2 mid_lane_mob_spawner_pos = Dqn_V2_InitNx2(game->map->local_hit_box_size.w * -0.5f + 128.f, 0.f);
|
||||
Dqn_usize spawn_cap = 16;
|
||||
{
|
||||
FP_GameEntityHandle mob_spawner = FP_Entity_CreateMobSpawner(game, mid_lane_mob_spawner_pos, spawn_cap, "Mob spawner");
|
||||
@ -490,10 +460,53 @@ void TELY_DLL_Init(void *user_data)
|
||||
Dqn_FArray_Add(&game->mob_spawners, mob_spawner);
|
||||
}
|
||||
#endif
|
||||
}
|
||||
|
||||
// NOTE: Monkey ============================================================
|
||||
{
|
||||
Dqn_V2 monkey_base_p = Dqn_V2_InitNx2(base_mid_p.x + 400.f, base_mid_p.y + 16.f);
|
||||
FP_GameEntityHandle monkey_a = FP_Entity_CreatePortalMonkey(game, Dqn_V2_InitNx2(monkey_base_p.x, monkey_base_p.y), "Monkey A");
|
||||
FP_GameEntityHandle monkey_b = FP_Entity_CreatePortalMonkey(game, Dqn_V2_InitNx2(monkey_base_p.x, monkey_base_p.y + 100.f), "Monkey B");
|
||||
FP_GameEntityHandle monkey_c = FP_Entity_CreatePortalMonkey(game, Dqn_V2_InitNx2(monkey_base_p.x, monkey_base_p.y - 100.f), "Monkey C");
|
||||
|
||||
Dqn_FArray_Add(&game->portal_monkeys, monkey_a);
|
||||
Dqn_FArray_Add(&game->portal_monkeys, monkey_b);
|
||||
Dqn_FArray_Add(&game->portal_monkeys, monkey_c);
|
||||
}
|
||||
|
||||
// NOTE: Hero ==================================================================================
|
||||
{
|
||||
FP_GameEntityHandle terry = FP_Entity_CreateTerry(game, Dqn_V2_InitNx2(1434, 11), "Terry");
|
||||
game->clicked_entity = terry;
|
||||
game->player = terry;
|
||||
}
|
||||
|
||||
{
|
||||
Dqn_V2 base_top_left_pos = Dqn_V2_InitNx2(1018, -335);
|
||||
Dqn_V2 base_bottom_right_pos = Dqn_V2_InitNx2(2050, +351);
|
||||
|
||||
Dqn_V2 base_top_left = base_top_left_pos;
|
||||
Dqn_V2 base_top_right = Dqn_V2_InitNx2(base_bottom_right_pos.x, base_top_left_pos.y);
|
||||
Dqn_V2 base_bottom_left = Dqn_V2_InitNx2(base_top_left_pos.x, base_bottom_right_pos.y);
|
||||
Dqn_V2 base_bottom_right = Dqn_V2_InitNx2(base_bottom_right_pos.x, base_bottom_right_pos.y);
|
||||
|
||||
game->merchant_terry = FP_Entity_CreateMerchantTerry(game, base_top_left, "Merchant");
|
||||
game->merchant_graveyard = FP_Entity_CreateMerchantGraveyard(game, base_bottom_left, "Graveyard");
|
||||
game->merchant_gym = FP_Entity_CreateMerchantGym(game, base_bottom_right, "Gym");
|
||||
game->merchant_phone_company = FP_Entity_CreateMerchantPhoneCompany(game, base_top_right, "PhoneCompany");
|
||||
}
|
||||
|
||||
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");
|
||||
|
||||
game->tile_size = 37;
|
||||
Dqn_V2I max_tile = platform->core.window_size / game->tile_size;
|
||||
|
||||
// NOTE: Heart
|
||||
FP_Entity_CreateHeart(game, base_mid_p, "Heart");
|
||||
|
||||
|
||||
uint16_t font_size = 18;
|
||||
game->camera.scale = Dqn_V2_InitNx1(1);
|
||||
game->inter_regular_font = platform->func_load_font(assets, DQN_STRING8("Inter (Regular)"), DQN_STRING8("Data/Inter-Regular.otf"), font_size);
|
||||
@ -506,6 +519,36 @@ void TELY_DLL_Init(void *user_data)
|
||||
game->audio[FP_GameAudio_Smooch] = platform->func_load_audio(assets, DQN_STRING8("Smooch"), DQN_STRING8("Data/Audio/smooch.mp3"));
|
||||
}
|
||||
|
||||
struct FP_GetClosestPortalMonkeyResult
|
||||
{
|
||||
FP_GameEntityHandle entity;
|
||||
Dqn_f32 dist;
|
||||
};
|
||||
|
||||
FP_GetClosestPortalMonkeyResult FP_GetClosestPortalMonkey(FP_Game *game, FP_GameEntityHandle handle)
|
||||
{
|
||||
// NOTE: Check if we are nearby a monkey and picking it up
|
||||
FP_GetClosestPortalMonkeyResult result = {};
|
||||
result.dist = DQN_F32_MAX;
|
||||
FP_GameEntityHandle best_portal_monkey = {};
|
||||
for (FP_GameEntityHandle portal_monkey_handle : game->portal_monkeys) {
|
||||
FP_GameEntity *portal_monkey = FP_Game_GetEntity(game, portal_monkey_handle);
|
||||
if (FP_Game_IsNilEntity(portal_monkey))
|
||||
continue;
|
||||
if (portal_monkey->action.state == FP_EntityPortalMonkeyState_DisablingPortal)
|
||||
continue;
|
||||
Dqn_V2 entity_pos = FP_Game_CalcEntityWorldPos(game, handle);
|
||||
Dqn_V2 portal_monkey_pos = FP_Game_CalcEntityWorldPos(game, portal_monkey_handle);
|
||||
Dqn_f32 dist_squared = Dqn_V2_LengthSq_V2x2(entity_pos, portal_monkey_pos);
|
||||
if (dist_squared < result.dist) {
|
||||
result.dist = dist_squared;
|
||||
result.entity = portal_monkey_handle;
|
||||
}
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
void FP_EntityActionStateMachine(FP_Game *game, TELY_Audio *audio, TELY_PlatformInput *input, FP_GameEntity *entity, Dqn_V2 *acceleration_meters_per_s)
|
||||
{
|
||||
TELY_AssetSpriteSheet *sheet = &game->atlas_sprite_sheet;
|
||||
@ -529,6 +572,16 @@ void FP_EntityActionStateMachine(FP_Game *game, TELY_Audio *audio, TELY_Platform
|
||||
case FP_EntityType_Terry: {
|
||||
FP_EntityTerryState *state = DQN_CAST(FP_EntityTerryState *) & action->state;
|
||||
|
||||
{
|
||||
FP_GameEntity *portal_monkey = FP_Game_GetEntity(game, entity->carried_monkey);
|
||||
if (!FP_Game_IsNilEntity(portal_monkey)) {
|
||||
Dqn_Rect hit_box = FP_Game_CalcEntityWorldHitBox(game, entity->handle);
|
||||
Dqn_f32 trig_t = DQN_CAST(Dqn_f32)input->timer_s * 2.f;
|
||||
Dqn_V2 offset = Dqn_V2_InitNx2(DQN_COSF(trig_t), DQN_SINF(trig_t)) * 18.f;
|
||||
portal_monkey->local_pos = Dqn_Rect_InterpolatedPoint(hit_box, Dqn_V2_InitNx2(0.5f, -0.75f)) + offset;
|
||||
}
|
||||
}
|
||||
|
||||
switch (*state) {
|
||||
case FP_EntityTerryState_Nil: {
|
||||
FP_Game_EntityTransitionState(game, entity, FP_EntityTerryState_Idle);
|
||||
@ -542,6 +595,19 @@ void FP_EntityActionStateMachine(FP_Game *game, TELY_Audio *audio, TELY_Platform
|
||||
|
||||
if (we_are_clicked_entity) {
|
||||
if (game->active_menu == FP_GameActiveMenu_Nil) {
|
||||
bool picked_up_monkey_this_frame = false;
|
||||
if (FP_Game_IsNilEntityHandle(game, entity->carried_monkey)) {
|
||||
// NOTE: Check if we are nearby a monkey and picking it up
|
||||
FP_GetClosestPortalMonkeyResult closest_monkey = FP_GetClosestPortalMonkey(game, entity->handle);
|
||||
if (closest_monkey.dist < DQN_SQUARED(FP_Game_MetersToPixelsNx1(game, 2.f))) {
|
||||
if (TELY_Platform_InputScanCodeIsPressed(input, TELY_PlatformInputScanCode_J)) {
|
||||
entity->carried_monkey = closest_monkey.entity;
|
||||
picked_up_monkey_this_frame = true;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (FP_Game_IsNilEntityHandle(game, entity->carried_monkey)) {
|
||||
if (TELY_Platform_InputScanCodeIsPressed(input, TELY_PlatformInputScanCode_J) ||
|
||||
TELY_Platform_InputGamepadButtonCodeIsPressed(input, 0, TELY_PlatformInputGamepadButtonCode_X)) {
|
||||
FP_Game_EntityTransitionState(game, entity, FP_EntityTerryState_Attack);
|
||||
@ -549,8 +615,14 @@ void FP_EntityActionStateMachine(FP_Game *game, TELY_Audio *audio, TELY_Platform
|
||||
TELY_Platform_InputGamepadButtonCodeIsPressed(input, 0, TELY_PlatformInputGamepadButtonCode_Y)) {
|
||||
FP_Game_EntityTransitionState(game, entity, FP_EntityTerryState_RangeAttack);
|
||||
}
|
||||
} else {
|
||||
if (!picked_up_monkey_this_frame && TELY_Platform_InputScanCodeIsPressed(input, TELY_PlatformInputScanCode_J)) {
|
||||
FP_GameEntity *portal_monkey = FP_Game_GetEntity(game, entity->carried_monkey);
|
||||
portal_monkey->local_pos = FP_Game_CalcEntityWorldPos(game, entity->handle);
|
||||
entity->carried_monkey = {};
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
if (action->next_state == action->state && (acceleration_meters_per_s->x || acceleration_meters_per_s->y)) {
|
||||
FP_Game_EntityTransitionState(game, entity, FP_EntityTerryState_Run);
|
||||
@ -588,7 +660,20 @@ void FP_EntityActionStateMachine(FP_Game *game, TELY_Audio *audio, TELY_Platform
|
||||
}
|
||||
|
||||
if (we_are_clicked_entity) {
|
||||
bool picked_up_monkey_this_frame = false;
|
||||
if (game->active_menu == FP_GameActiveMenu_Nil) {
|
||||
if (FP_Game_IsNilEntityHandle(game, entity->carried_monkey)) {
|
||||
// NOTE: Check if we are nearby a monkey and picking it up
|
||||
FP_GetClosestPortalMonkeyResult closest_monkey = FP_GetClosestPortalMonkey(game, entity->handle);
|
||||
if (closest_monkey.dist < DQN_SQUARED(FP_Game_MetersToPixelsNx1(game, 2.f))) {
|
||||
if (TELY_Platform_InputScanCodeIsPressed(input, TELY_PlatformInputScanCode_J)) {
|
||||
entity->carried_monkey = closest_monkey.entity;
|
||||
picked_up_monkey_this_frame = true;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (FP_Game_IsNilEntityHandle(game, entity->carried_monkey)) {
|
||||
if (TELY_Platform_InputScanCodeIsPressed(input, TELY_PlatformInputScanCode_J) ||
|
||||
TELY_Platform_InputGamepadButtonCodeIsPressed(input, 0, TELY_PlatformInputGamepadButtonCode_X)) {
|
||||
FP_Game_EntityTransitionState(game, entity, FP_EntityTerryState_Attack);
|
||||
@ -597,11 +682,20 @@ void FP_EntityActionStateMachine(FP_Game *game, TELY_Audio *audio, TELY_Platform
|
||||
FP_Game_EntityTransitionState(game, entity, FP_EntityTerryState_RangeAttack);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (FP_Game_IsNilEntityHandle(game, entity->carried_monkey)) {
|
||||
if (TELY_Platform_InputScanCodeIsPressed(input, TELY_PlatformInputScanCode_LeftControl) ||
|
||||
TELY_Platform_InputGamepadButtonCodeIsPressed(input, 0, TELY_PlatformInputGamepadButtonCode_A)) {
|
||||
FP_Game_EntityTransitionState(game, entity, FP_EntityTerryState_Dash);
|
||||
}
|
||||
} else {
|
||||
if (!picked_up_monkey_this_frame && TELY_Platform_InputScanCodeIsPressed(input, TELY_PlatformInputScanCode_J)) {
|
||||
FP_GameEntity *portal_monkey = FP_Game_GetEntity(game, entity->carried_monkey);
|
||||
portal_monkey->local_pos = FP_Game_CalcEntityWorldPos(game, entity->handle);
|
||||
entity->carried_monkey = {};
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (!entity_has_velocity) {
|
||||
@ -645,6 +739,25 @@ void FP_EntityActionStateMachine(FP_Game *game, TELY_Audio *audio, TELY_Platform
|
||||
}
|
||||
} break;
|
||||
|
||||
|
||||
case FP_EntityType_PortalMonkey: {
|
||||
FP_EntityPortalMonkeyState *state = DQN_CAST(FP_EntityPortalMonkeyState *) & action->state;
|
||||
switch (*state) {
|
||||
case FP_EntityPortalMonkeyState_Nil: {
|
||||
FP_Game_EntityTransitionState(game, entity, FP_EntitySmoochieState_Idle);
|
||||
} break;
|
||||
|
||||
case FP_EntityPortalMonkeyState_Idle: {
|
||||
} break;
|
||||
|
||||
case FP_EntityPortalMonkeyState_BeingCarried: {
|
||||
} break;
|
||||
|
||||
case FP_EntityPortalMonkeyState_DisablingPortal: {
|
||||
} break;
|
||||
}
|
||||
} break;
|
||||
|
||||
case FP_EntityType_Smoochie: {
|
||||
FP_EntitySmoochieState *state = DQN_CAST(FP_EntitySmoochieState *) & action->state;
|
||||
switch (*state) {
|
||||
@ -1184,6 +1297,35 @@ void FP_EntityActionStateMachine(FP_Game *game, TELY_Audio *audio, TELY_Platform
|
||||
|
||||
case FP_EntityType_Count: DQN_INVALID_CODE_PATH; break;
|
||||
case FP_EntityType_PhoneMessageProjectile: break;
|
||||
|
||||
case FP_EntityType_MobSpawner: {
|
||||
FP_EntityMobSpawnerState *state = DQN_CAST(FP_EntityMobSpawnerState *)&action->state;
|
||||
switch (*state) {
|
||||
case FP_EntityMobSpawnerState_Nil: {
|
||||
FP_Game_EntityTransitionState(game, entity, FP_EntityMobSpawnerState_Idle);
|
||||
} break;
|
||||
|
||||
case FP_EntityMobSpawnerState_Idle: {
|
||||
if (entering_new_state) {
|
||||
uint64_t duration_ms = FP_GAME_ENTITY_ACTION_INFINITE_TIMER;
|
||||
FP_Game_EntityActionReset(game, entity->handle, duration_ms, render_data.sprite);
|
||||
}
|
||||
|
||||
if (!FP_Game_IsNilEntityHandle(game, entity->carried_monkey)) {
|
||||
FP_Game_EntityTransitionState(game, entity, FP_EntityMobSpawnerState_Shutdown);
|
||||
}
|
||||
} break;
|
||||
|
||||
case FP_EntityMobSpawnerState_Shutdown: {
|
||||
if (entering_new_state) {
|
||||
uint64_t duration_ms = FP_GAME_ENTITY_ACTION_INFINITE_TIMER;
|
||||
FP_Game_EntityActionReset(game, entity->handle, duration_ms, render_data.sprite);
|
||||
entity->action.sprite_play_once = true;
|
||||
FP_Game_DeleteEntity(game, entity->carried_monkey);
|
||||
}
|
||||
} break;
|
||||
}
|
||||
} break;
|
||||
}
|
||||
|
||||
switch (entity->type) {
|
||||
@ -1284,6 +1426,7 @@ void FP_EntityActionStateMachine(FP_Game *game, TELY_Audio *audio, TELY_Platform
|
||||
entity->attack_box_size = {};
|
||||
}
|
||||
} break;
|
||||
|
||||
case FP_EntityType_AirportTerryPlane: break;
|
||||
}
|
||||
}
|
||||
@ -1318,6 +1461,7 @@ void FP_Update(TELY_Platform *platform, FP_Game *game, TELY_PlatformInput *input
|
||||
dir_vector.y += input->left_stick[gamepad].y;
|
||||
}
|
||||
|
||||
// TODO(doyle): Some bug, diagonal is still faster
|
||||
if (dir_vector.x && dir_vector.y) {
|
||||
dir_vector.x *= 0.7071067811865475244f;
|
||||
dir_vector.y *= 0.7071067811865475244f;
|
||||
@ -1421,9 +1565,8 @@ void FP_Update(TELY_Platform *platform, FP_Game *game, TELY_PlatformInput *input
|
||||
}
|
||||
|
||||
// NOTE: Determine AI movement =============================================================
|
||||
if (acceleration_meters_per_s.x == 0 && acceleration_meters_per_s.y == 0) {
|
||||
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;
|
||||
@ -1815,6 +1958,18 @@ void FP_Update(TELY_Platform *platform, FP_Game *game, TELY_PlatformInput *input
|
||||
}
|
||||
}
|
||||
|
||||
if (!FP_Game_IsNilEntityHandle(game, entity->carried_monkey) && entity->type != FP_EntityType_MobSpawner) {
|
||||
FP_GameFindClosestEntityResult closest_portal = FP_Game_FindClosestEntityWithType(game, entity->carried_monkey, game->mob_spawners.data, game->mob_spawners.size);
|
||||
if (closest_portal.dist_squared < DQN_SQUARED(FP_Game_MetersToPixelsNx1(game, 1.f))) {
|
||||
FP_GameEntity *portal = FP_Game_GetEntity(game, closest_portal.entity);
|
||||
portal->carried_monkey = entity->carried_monkey;
|
||||
entity->carried_monkey = {};
|
||||
}
|
||||
acceleration_meters_per_s *= 2.f; // TODO(doyle): Penalise the player
|
||||
} else {
|
||||
acceleration_meters_per_s *= 1.f; // TODO(doyle): Penalise the player
|
||||
}
|
||||
|
||||
// NOTE: Tick the state machine
|
||||
// NOTE: This can delete the entity! Take caution
|
||||
FP_GameEntityHandle entity_handle = entity->handle;
|
||||
@ -1877,8 +2032,7 @@ void FP_Update(TELY_Platform *platform, FP_Game *game, TELY_PlatformInput *input
|
||||
}
|
||||
|
||||
// NOTE: Mob spawner =======================================================================
|
||||
if (entity->flags & FP_GameEntityFlag_MobSpawner) {
|
||||
|
||||
if (entity->type == FP_EntityType_MobSpawner) {
|
||||
// NOTE: Flush any spawn entities that are dead
|
||||
for (FP_SentinelListLink<FP_GameEntityHandle> *link = nullptr; FP_SentinelList_Iterate<FP_GameEntityHandle>(&entity->spawn_list, &link); ) {
|
||||
FP_GameEntity *spawned_entity = FP_Game_GetEntity(game, link->data);
|
||||
@ -1887,7 +2041,7 @@ void FP_Update(TELY_Platform *platform, FP_Game *game, TELY_PlatformInput *input
|
||||
}
|
||||
|
||||
if (game->enemies_spawned_this_wave < game->enemies_per_wave && entity->spawn_list.size < entity->spawn_cap) { // NOTE: Spawn new entities
|
||||
if (input->timer_s >= entity->next_spawn_timestamp_s) {
|
||||
if (entity->action.state != FP_EntityMobSpawnerState_Shutdown && input->timer_s >= entity->next_spawn_timestamp_s) {
|
||||
uint16_t hp_adjustment = DQN_CAST(uint16_t)game->current_wave;
|
||||
entity->next_spawn_timestamp_s = DQN_CAST(uint64_t)(input->timer_s + 5.f);
|
||||
|
||||
@ -2072,7 +2226,12 @@ void FP_Render(FP_Game *game, TELY_Platform *platform, TELY_Renderer *renderer)
|
||||
TELY_AssetAnimatedSprite const sprite = action->sprite;
|
||||
|
||||
uint64_t elapsed_ms = game->clock_ms - action->started_at_clock_ms;
|
||||
uint16_t anim_frame = DQN_CAST(uint16_t)(elapsed_ms / sprite.anim->ms_per_frame) % sprite.anim->count;
|
||||
uint16_t anim_frame = 0;
|
||||
if (action->sprite_play_once) {
|
||||
anim_frame = DQN_MIN(DQN_CAST(uint16_t)(elapsed_ms / sprite.anim->ms_per_frame), sprite.anim->count);
|
||||
} else {
|
||||
anim_frame = DQN_CAST(uint16_t)(elapsed_ms / sprite.anim->ms_per_frame) % sprite.anim->count;
|
||||
}
|
||||
|
||||
Dqn_usize sprite_index = sprite.anim->index + anim_frame;
|
||||
Dqn_Rect src_rect = {};
|
||||
@ -2196,7 +2355,7 @@ void FP_Render(FP_Game *game, TELY_Platform *platform, TELY_Renderer *renderer)
|
||||
|
||||
if (game->debug_ui) {
|
||||
// NOTE: Render waypoint entities ======================================================
|
||||
if (entity->flags & FP_GameEntityFlag_MobSpawner) {
|
||||
if (entity->type == FP_EntityType_MobSpawner) {
|
||||
Dqn_V2 start = world_pos;
|
||||
for (FP_GameEntity *waypoint_entity = entity->first_child; waypoint_entity; waypoint_entity = waypoint_entity->next) {
|
||||
if ((waypoint_entity->flags & FP_GameEntityFlag_MobSpawnerWaypoint) == 0)
|
||||
@ -2206,10 +2365,8 @@ void FP_Render(FP_Game *game, TELY_Platform *platform, TELY_Renderer *renderer)
|
||||
TELY_Render_LineColourV4(renderer, start, end, TELY_COLOUR_BLUE_CADET_V4, 2.f);
|
||||
start = end;
|
||||
}
|
||||
}
|
||||
|
||||
if (entity->flags & FP_GameEntityFlag_MobSpawner)
|
||||
TELY_Render_RectColourV4(renderer, world_hit_box, TELY_RenderShapeMode_Line, TELY_COLOUR_BLUE_CADET_V4);
|
||||
}
|
||||
|
||||
if (entity->flags & FP_GameEntityFlag_MobSpawnerWaypoint)
|
||||
TELY_Render_CircleColourV4(renderer, world_pos, 16.f /*radius*/, TELY_RenderShapeMode_Line, TELY_COLOUR_BLUE_CADET_V4);
|
||||
|
@ -77,6 +77,7 @@ struct FP_GlobalAnimations
|
||||
Dqn_String8 particle_heart = DQN_STRING8("particle_heart");
|
||||
Dqn_String8 particle_purchase = DQN_STRING8("particle_purchase");
|
||||
Dqn_String8 portal = DQN_STRING8("portal");
|
||||
Dqn_String8 portal_break = DQN_STRING8("portal_break");
|
||||
Dqn_String8 portal_monk = DQN_STRING8("portal_monk");
|
||||
|
||||
Dqn_String8 shadow_long_circle = DQN_STRING8("shadow_long_circle");
|
||||
|
@ -19,6 +19,8 @@ enum FP_EntityType
|
||||
FP_EntityType_MerchantGym,
|
||||
FP_EntityType_MerchantPhoneCompany,
|
||||
FP_EntityType_MerchantTerry,
|
||||
FP_EntityType_MobSpawner,
|
||||
FP_EntityType_PortalMonkey,
|
||||
FP_EntityType_Smoochie,
|
||||
FP_EntityType_Terry,
|
||||
FP_EntityType_PhoneMessageProjectile,
|
||||
@ -35,6 +37,13 @@ enum FP_EntityTerryState
|
||||
FP_EntityTerryState_Dash,
|
||||
};
|
||||
|
||||
enum FP_EntityMobSpawnerState
|
||||
{
|
||||
FP_EntityMobSpawnerState_Nil,
|
||||
FP_EntityMobSpawnerState_Idle,
|
||||
FP_EntityMobSpawnerState_Shutdown,
|
||||
};
|
||||
|
||||
enum FP_EntitySmoochieState
|
||||
{
|
||||
FP_EntitySmoochieState_Nil,
|
||||
@ -133,6 +142,14 @@ enum FP_EntityHeartState
|
||||
FP_EntityHeartState_Idle,
|
||||
};
|
||||
|
||||
enum FP_EntityPortalMonkeyState
|
||||
{
|
||||
FP_EntityPortalMonkeyState_Nil,
|
||||
FP_EntityPortalMonkeyState_Idle,
|
||||
FP_EntityPortalMonkeyState_BeingCarried,
|
||||
FP_EntityPortalMonkeyState_DisablingPortal,
|
||||
};
|
||||
|
||||
struct FP_EntityRenderData
|
||||
{
|
||||
FP_Meters height;
|
||||
|
@ -255,6 +255,21 @@ FP_EntityRenderData FP_Entity_GetRenderData(FP_Game *game, FP_EntityType type, u
|
||||
result.anim_name = g_anim_names.airport_terry_plane;
|
||||
} break;
|
||||
|
||||
case FP_EntityType_MobSpawner: {
|
||||
result.height.meters = 3.f;
|
||||
FP_EntityMobSpawnerState state = DQN_CAST(FP_EntityMobSpawnerState)raw_state;
|
||||
switch (state) {
|
||||
case FP_EntityMobSpawnerState_Nil: break;
|
||||
case FP_EntityMobSpawnerState_Idle: result.anim_name = g_anim_names.portal; break;
|
||||
case FP_EntityMobSpawnerState_Shutdown: result.anim_name = g_anim_names.portal_break; break;
|
||||
}
|
||||
} break;
|
||||
|
||||
case FP_EntityType_PortalMonkey: {
|
||||
result.height.meters = 1.f;
|
||||
result.anim_name = g_anim_names.portal_monk; break;
|
||||
} break;
|
||||
|
||||
case FP_EntityType_Count: DQN_INVALID_CODE_PATH; break;
|
||||
}
|
||||
|
||||
@ -403,10 +418,11 @@ static FP_GameEntityHandle FP_Entity_CreateMobSpawner(FP_Game *game, Dqn_V2 pos,
|
||||
FP_GameEntityHandle result = entity->handle;
|
||||
va_end(args);
|
||||
|
||||
entity->type = FP_EntityType_MobSpawner;
|
||||
entity->local_pos = pos;
|
||||
entity->local_hit_box_size = Dqn_V2_InitNx1(32);
|
||||
entity->sprite_height = FP_Entity_GetRenderData(game, entity->type, 0 /*state*/, FP_GameDirection_Down).height;
|
||||
FP_Entity_AddDebugEditorFlags(game, result);
|
||||
entity->flags |= FP_GameEntityFlag_MobSpawner;
|
||||
|
||||
entity->spawn_cap = spawn_cap;
|
||||
entity->spawn_list = FP_SentinelList_Init<FP_GameEntityHandle>(game->chunk_pool);
|
||||
@ -478,7 +494,6 @@ static FP_GameEntityHandle FP_Entity_CreateMerchantGraveyard(FP_Game *game, Dqn_
|
||||
FP_Entity_AddDebugEditorFlags(game, result);
|
||||
entity->flags |= FP_GameEntityFlag_NonTraversable;
|
||||
|
||||
|
||||
TELY_AssetSpriteAnimation *sprite_anim = TELY_Asset_GetSpriteAnimation(&game->atlas_sprite_sheet, g_anim_names.merchant_graveyard);
|
||||
Dqn_Rect sprite_rect = game->atlas_sprite_sheet.rects.data[sprite_anim->index];
|
||||
Dqn_f32 size_scale = FP_Entity_CalcSpriteScaleForDesiredHeight(game, entity->sprite_height, sprite_rect);
|
||||
@ -688,6 +703,30 @@ static FP_GameEntityHandle FP_Entity_CreatePhoneMessageProjectile(FP_Game *game,
|
||||
return result;
|
||||
}
|
||||
|
||||
static FP_GameEntityHandle FP_Entity_CreatePortalMonkey(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_PortalMonkey;
|
||||
FP_EntityRenderData render_data = FP_Entity_GetRenderData(game, entity->type, 0, FP_GameDirection_Down);
|
||||
|
||||
entity->local_pos = pos;
|
||||
entity->sprite_height = render_data.height;
|
||||
FP_Entity_AddDebugEditorFlags(game, result);
|
||||
|
||||
entity->local_hit_box_offset = Dqn_V2_InitNx2(0, render_data.render_size.h * .1f);
|
||||
entity->local_hit_box_size = Dqn_V2_InitNx2(render_data.render_size.w, render_data.render_size.h - (render_data.render_size.h * .4f));
|
||||
|
||||
uint64_t duration_ms = FP_GAME_ENTITY_ACTION_INFINITE_TIMER;
|
||||
FP_Game_EntityActionReset(game, result, duration_ms, render_data.sprite);
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
static FP_GameEntityHandle FP_Entity_CreateAirportTerryPlane(FP_Game *game, Dqn_V2 pos, DQN_FMT_STRING_ANNOTATE char const *fmt, ...)
|
||||
{
|
||||
va_list args;
|
||||
|
@ -754,6 +754,31 @@ FP_GameFindClosestEntityResult FP_Game_FindClosestEntityWithType(FP_Game *game,
|
||||
return result;
|
||||
}
|
||||
|
||||
static FP_GameFindClosestEntityResult FP_Game_FindClosestEntityWithType(FP_Game *game, FP_GameEntityHandle src, FP_GameEntityHandle *entities, Dqn_usize size)
|
||||
{
|
||||
FP_GameFindClosestEntityResult result = {};
|
||||
result.dist_squared = DQN_F32_MAX;
|
||||
result.pos = Dqn_V2_InitNx1(DQN_F32_MAX);
|
||||
|
||||
Dqn_V2 src_pos = FP_Game_CalcEntityWorldPos(game, src);
|
||||
DQN_FOR_UINDEX (index, size) {
|
||||
FP_GameEntityHandle entity_handle = entities[index];
|
||||
FP_GameEntity *entity = FP_Game_GetEntity(game, entity_handle);
|
||||
if (FP_Game_IsNilEntity(entity))
|
||||
continue;
|
||||
|
||||
Dqn_V2 pos = FP_Game_CalcEntityWorldPos(game, entity->handle);
|
||||
Dqn_f32 dist = Dqn_V2_LengthSq_V2x2(pos, src_pos);
|
||||
if (dist < result.dist_squared) {
|
||||
result.pos = pos;
|
||||
result.dist_squared = dist;
|
||||
result.entity = entity->handle;
|
||||
}
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
static bool FP_Game_CanEntityAttack(FP_GameEntity *entity, uint64_t current_time_ms)
|
||||
{
|
||||
bool result = (current_time_ms - entity->last_attack_timestamp) >= entity->attack_cooldown_ms;
|
||||
|
@ -12,19 +12,18 @@ enum FP_GameEntityFlag
|
||||
FP_GameEntityFlag_DrawHitBox = 1 << 4,
|
||||
FP_GameEntityFlag_DeriveHitBoxFromChildrenBoundingBox = 1 << 5,
|
||||
FP_GameEntityFlag_NonTraversable = 1 << 6,
|
||||
FP_GameEntityFlag_MobSpawner = 1 << 7,
|
||||
FP_GameEntityFlag_MobSpawnerWaypoint = 1 << 8,
|
||||
FP_GameEntityFlag_Aggros = 1 << 9,
|
||||
FP_GameEntityFlag_Attackable = 1 << 10,
|
||||
FP_GameEntityFlag_RespondsToBuildings = 1 << 11,
|
||||
FP_GameEntityFlag_OccupiedInBuilding = 1 << 12,
|
||||
FP_GameEntityFlag_PointOfInterestHeart = 1 << 13,
|
||||
FP_GameEntityFlag_CameraTracking = 1 << 14,
|
||||
FP_GameEntityFlag_BuildZone = 1 << 15,
|
||||
FP_GameEntityFlag_TTL = 1 << 16,
|
||||
FP_GameEntityFlag_Friendly = 1 << 17,
|
||||
FP_GameEntityFlag_Foe = 1 << 18,
|
||||
FP_GameEntityFlag_NoClip = 1 << 19,
|
||||
FP_GameEntityFlag_MobSpawnerWaypoint = 1 << 7,
|
||||
FP_GameEntityFlag_Aggros = 1 << 8,
|
||||
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,
|
||||
};
|
||||
|
||||
enum FP_GameShapeType
|
||||
@ -124,6 +123,7 @@ struct FP_GameEntityAction
|
||||
uint32_t state;
|
||||
uint32_t next_state;
|
||||
TELY_AssetAnimatedSprite sprite;
|
||||
bool sprite_play_once;
|
||||
Dqn_f32 sprite_alpha;
|
||||
uint64_t started_at_clock_ms;
|
||||
uint64_t end_at_clock_ms;
|
||||
@ -232,6 +232,7 @@ struct FP_GameEntity
|
||||
FP_GameEntityFaction faction;
|
||||
|
||||
uint32_t count_of_entities_targetting_sides[FP_GameDirection_Count];
|
||||
FP_GameEntityHandle carried_monkey;
|
||||
};
|
||||
|
||||
struct FP_GameEntityIterator
|
||||
@ -323,6 +324,7 @@ struct FP_Game
|
||||
Dqn_usize build_mode_building_index;
|
||||
|
||||
Dqn_FArray<FP_GameEntityHandle, 4> mob_spawners;
|
||||
Dqn_FArray<FP_GameEntityHandle, 3> portal_monkeys;
|
||||
|
||||
uint32_t current_wave;
|
||||
uint32_t enemies_per_wave;
|
||||
|
Loading…
Reference in New Issue
Block a user