fp: Fix attack boxes persisting and decrement health
This commit is contained in:
parent
19f8649870
commit
4cb9ad4f39
414
feely_pona.cpp
414
feely_pona.cpp
@ -447,7 +447,7 @@ void TELY_DLL_Init(void *user_data)
|
|||||||
// NOTE: Mid lane mob spawner ==================================================================
|
// NOTE: Mid lane mob spawner ==================================================================
|
||||||
Dqn_V2 base_mid_p = Dqn_V2_InitNx2(1580, 0.f);
|
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);
|
Dqn_V2 mid_lane_mob_spawner_pos = Dqn_V2_InitNx2(game->map->local_hit_box_size.w * -0.5f, 0.f);
|
||||||
Dqn_usize spawn_cap = 16;
|
Dqn_usize spawn_cap = 1;
|
||||||
{
|
{
|
||||||
FP_GameEntityHandle mob_spawner = FP_Entity_CreateMobSpawner(game, mid_lane_mob_spawner_pos, spawn_cap, "Mob spawner");
|
FP_GameEntityHandle mob_spawner = FP_Entity_CreateMobSpawner(game, mid_lane_mob_spawner_pos, spawn_cap, "Mob spawner");
|
||||||
FP_Game_PushParentEntity(game, mob_spawner);
|
FP_Game_PushParentEntity(game, mob_spawner);
|
||||||
@ -457,6 +457,7 @@ void TELY_DLL_Init(void *user_data)
|
|||||||
}
|
}
|
||||||
|
|
||||||
// NOTE: Bottom lane spawner ===================================================================
|
// NOTE: Bottom lane spawner ===================================================================
|
||||||
|
#if 0
|
||||||
Dqn_V2 bottom_lane_mob_spawner_pos = Dqn_V2_InitNx2(mid_lane_mob_spawner_pos.x, mid_lane_mob_spawner_pos.y + 932.f);
|
Dqn_V2 bottom_lane_mob_spawner_pos = Dqn_V2_InitNx2(mid_lane_mob_spawner_pos.x, mid_lane_mob_spawner_pos.y + 932.f);
|
||||||
{
|
{
|
||||||
FP_GameEntityHandle mob_spawner = FP_Entity_CreateMobSpawner(game, bottom_lane_mob_spawner_pos, spawn_cap, "Mob spawner");
|
FP_GameEntityHandle mob_spawner = FP_Entity_CreateMobSpawner(game, bottom_lane_mob_spawner_pos, spawn_cap, "Mob spawner");
|
||||||
@ -475,6 +476,7 @@ void TELY_DLL_Init(void *user_data)
|
|||||||
FP_Entity_CreateWaypointF(game, Dqn_V2_InitNx2(-top_lane_mob_spawner_pos.x + base_mid_p.x, +915.f), "Waypoint");
|
FP_Entity_CreateWaypointF(game, Dqn_V2_InitNx2(-top_lane_mob_spawner_pos.x + base_mid_p.x, +915.f), "Waypoint");
|
||||||
FP_Game_PopParentEntity(game);
|
FP_Game_PopParentEntity(game);
|
||||||
}
|
}
|
||||||
|
#endif
|
||||||
|
|
||||||
FP_Entity_CreateHeart(game, base_mid_p, "Heart");
|
FP_Entity_CreateHeart(game, base_mid_p, "Heart");
|
||||||
|
|
||||||
@ -600,6 +602,8 @@ void FP_EntityActionStateMachine(FP_Game *game, TELY_Audio *audio, TELY_Platform
|
|||||||
FP_Game_EntityActionReset(game, entity->handle, duration_ms, render_data.sprite);
|
FP_Game_EntityActionReset(game, entity->handle, duration_ms, render_data.sprite);
|
||||||
*acceleration_meters_per_s *= 35.f;
|
*acceleration_meters_per_s *= 35.f;
|
||||||
|
|
||||||
|
entity->stamina -= FP_TERRY_DASH_STAMINA_COST;
|
||||||
|
|
||||||
#if 0
|
#if 0
|
||||||
FP_GameRenderSprite *cosmetic_sprite = Dqn_FArray_Make(&entity->extra_cosmetic_anims, Dqn_ZeroMem_Yes);
|
FP_GameRenderSprite *cosmetic_sprite = Dqn_FArray_Make(&entity->extra_cosmetic_anims, Dqn_ZeroMem_Yes);
|
||||||
if (cosmetic_sprite) {
|
if (cosmetic_sprite) {
|
||||||
@ -626,69 +630,6 @@ void FP_EntityActionStateMachine(FP_Game *game, TELY_Audio *audio, TELY_Platform
|
|||||||
}
|
}
|
||||||
} break;
|
} break;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (*state == FP_EntityTerryState_Attack || *state == FP_EntityTerryState_RangeAttack) {
|
|
||||||
DQN_ASSERT(action->sprite.anim);
|
|
||||||
|
|
||||||
uint64_t duration_ms = action->sprite.anim->count * action->sprite.anim->ms_per_frame;
|
|
||||||
DQN_ASSERT(duration_ms >= PHYSICS_STEP);
|
|
||||||
uint64_t midpoint_clock_ms = action->end_at_clock_ms - (duration_ms / 2);
|
|
||||||
|
|
||||||
// NOTE: Adding an attack_processed bool to make sure things only fire once
|
|
||||||
if (!entity->attack_processed && game->clock_ms >= midpoint_clock_ms) {
|
|
||||||
|
|
||||||
// NOTE: Position the attack box
|
|
||||||
Dqn_V2 dir_vector = {};
|
|
||||||
switch (entity->direction) {
|
|
||||||
case FP_GameDirection_Left: {
|
|
||||||
dir_vector.x = -1.f;
|
|
||||||
entity->attack_box_offset = Dqn_V2_InitNx2(entity->local_hit_box_offset.x - entity->attack_box_size.w,
|
|
||||||
entity->local_hit_box_offset.y);
|
|
||||||
} break;
|
|
||||||
|
|
||||||
case FP_GameDirection_Right: {
|
|
||||||
dir_vector.x = +1.f;
|
|
||||||
entity->attack_box_offset = Dqn_V2_InitNx2(
|
|
||||||
entity->local_hit_box_offset.x + entity->attack_box_size.w,
|
|
||||||
entity->local_hit_box_offset.y);
|
|
||||||
} break;
|
|
||||||
|
|
||||||
case FP_GameDirection_Up: {
|
|
||||||
dir_vector.y = -1.f;
|
|
||||||
entity->attack_box_offset = Dqn_V2_InitNx2(entity->local_hit_box_offset.x,
|
|
||||||
entity->local_hit_box_offset.y - entity->attack_box_size.h);
|
|
||||||
} break;
|
|
||||||
|
|
||||||
case FP_GameDirection_Down: {
|
|
||||||
dir_vector.y = +1.f;
|
|
||||||
entity->attack_box_offset = Dqn_V2_InitNx2(entity->local_hit_box_offset.x,
|
|
||||||
entity->local_hit_box_offset.y + entity->attack_box_size.h);
|
|
||||||
} break;
|
|
||||||
|
|
||||||
case FP_GameDirection_Count: break;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (*state == FP_EntityTerryState_RangeAttack) {
|
|
||||||
Dqn_Rect entity_hit_box = FP_Game_CalcEntityWorldHitBox(game, entity->handle);
|
|
||||||
Dqn_V2 projectile_pos = entity_hit_box.pos + entity->attack_box_offset;
|
|
||||||
Dqn_V2 projectile_acceleration = FP_Game_MetersToPixelsV2(game, dir_vector * 0.25f);
|
|
||||||
FP_Entity_CreatePhoneMessageProjectile(game,
|
|
||||||
entity->handle,
|
|
||||||
projectile_pos,
|
|
||||||
projectile_acceleration,
|
|
||||||
"Phone Message Projectile");
|
|
||||||
} else {
|
|
||||||
entity->attack_box_size = entity->local_hit_box_size;
|
|
||||||
TELY_Audio_Play(audio, game->audio[FP_GameAudio_TerryHit], 1.f);
|
|
||||||
}
|
|
||||||
entity->attack_processed = true;
|
|
||||||
} else {
|
|
||||||
entity->attack_box_size = {};
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
entity->attack_box_size = {};
|
|
||||||
entity->attack_processed = false;
|
|
||||||
}
|
|
||||||
} break;
|
} break;
|
||||||
|
|
||||||
case FP_EntityType_Smoochie: {
|
case FP_EntityType_Smoochie: {
|
||||||
@ -814,36 +755,6 @@ void FP_EntityActionStateMachine(FP_Game *game, TELY_Audio *audio, TELY_Platform
|
|||||||
if (entity->is_dying && *state != FP_EntitySmoochieState_Death) {
|
if (entity->is_dying && *state != FP_EntitySmoochieState_Death) {
|
||||||
FP_Game_EntityTransitionState(game, entity, FP_EntitySmoochieState_Death);
|
FP_Game_EntityTransitionState(game, entity, FP_EntitySmoochieState_Death);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (*state == FP_EntitySmoochieState_Attack) {
|
|
||||||
entity->attack_box_size = entity->local_hit_box_size;
|
|
||||||
// NOTE: Position the attack box
|
|
||||||
switch (entity->direction) {
|
|
||||||
case FP_GameDirection_Left: {
|
|
||||||
entity->attack_box_offset = Dqn_V2_InitNx2(entity->local_hit_box_offset.x - entity->attack_box_size.w,
|
|
||||||
entity->local_hit_box_offset.y);
|
|
||||||
} break;
|
|
||||||
|
|
||||||
case FP_GameDirection_Right: {
|
|
||||||
entity->attack_box_offset = Dqn_V2_InitNx2(entity->local_hit_box_offset.x + entity->attack_box_size.w,
|
|
||||||
entity->local_hit_box_offset.y);
|
|
||||||
} break;
|
|
||||||
|
|
||||||
case FP_GameDirection_Up: {
|
|
||||||
entity->attack_box_offset = Dqn_V2_InitNx2(entity->local_hit_box_offset.x,
|
|
||||||
entity->local_hit_box_offset.y - entity->attack_box_size.h);
|
|
||||||
} break;
|
|
||||||
|
|
||||||
case FP_GameDirection_Down: {
|
|
||||||
entity->attack_box_offset = Dqn_V2_InitNx2(entity->local_hit_box_offset.x,
|
|
||||||
entity->local_hit_box_offset.y + entity->attack_box_size.h);
|
|
||||||
} break;
|
|
||||||
|
|
||||||
case FP_GameDirection_Count: break;
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
entity->attack_box_size = {};
|
|
||||||
}
|
|
||||||
} break;
|
} break;
|
||||||
|
|
||||||
case FP_EntityType_Clinger: {
|
case FP_EntityType_Clinger: {
|
||||||
@ -918,35 +829,6 @@ void FP_EntityActionStateMachine(FP_Game *game, TELY_Audio *audio, TELY_Platform
|
|||||||
if (entity->is_dying && *state != FP_EntityClingerState_Death) {
|
if (entity->is_dying && *state != FP_EntityClingerState_Death) {
|
||||||
FP_Game_EntityTransitionState(game, entity, FP_EntityClingerState_Death);
|
FP_Game_EntityTransitionState(game, entity, FP_EntityClingerState_Death);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (*state == FP_EntityClingerState_Attack) { // NOTE: Position the attack box
|
|
||||||
entity->attack_box_size = entity->local_hit_box_size;
|
|
||||||
switch (entity->direction) {
|
|
||||||
case FP_GameDirection_Left: {
|
|
||||||
entity->attack_box_offset = Dqn_V2_InitNx2(entity->local_hit_box_offset.x - entity->attack_box_size.w,
|
|
||||||
entity->local_hit_box_offset.y);
|
|
||||||
} break;
|
|
||||||
|
|
||||||
case FP_GameDirection_Right: {
|
|
||||||
entity->attack_box_offset = Dqn_V2_InitNx2(entity->local_hit_box_offset.x + entity->attack_box_size.w,
|
|
||||||
entity->local_hit_box_offset.y);
|
|
||||||
} break;
|
|
||||||
|
|
||||||
case FP_GameDirection_Up: {
|
|
||||||
entity->attack_box_offset = Dqn_V2_InitNx2(entity->local_hit_box_offset.x,
|
|
||||||
entity->local_hit_box_offset.y - entity->attack_box_size.h);
|
|
||||||
} break;
|
|
||||||
|
|
||||||
case FP_GameDirection_Down: {
|
|
||||||
entity->attack_box_offset = Dqn_V2_InitNx2(entity->local_hit_box_offset.x,
|
|
||||||
entity->local_hit_box_offset.y + entity->attack_box_size.h);
|
|
||||||
} break;
|
|
||||||
|
|
||||||
case FP_GameDirection_Count: DQN_INVALID_CODE_PATH; break;
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
entity->attack_box_size = {};
|
|
||||||
}
|
|
||||||
} break;
|
} break;
|
||||||
|
|
||||||
case FP_EntityType_MerchantTerry: {
|
case FP_EntityType_MerchantTerry: {
|
||||||
@ -1186,35 +1068,6 @@ void FP_EntityActionStateMachine(FP_Game *game, TELY_Audio *audio, TELY_Platform
|
|||||||
if (entity->is_dying && *state != FP_EntityCatfishState_Death) {
|
if (entity->is_dying && *state != FP_EntityCatfishState_Death) {
|
||||||
FP_Game_EntityTransitionState(game, entity, FP_EntityCatfishState_Death);
|
FP_Game_EntityTransitionState(game, entity, FP_EntityCatfishState_Death);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (*state == FP_EntityCatfishState_Attack) { // NOTE: Position the attack box
|
|
||||||
entity->attack_box_size = entity->local_hit_box_size;
|
|
||||||
switch (entity->direction) {
|
|
||||||
case FP_GameDirection_Left: {
|
|
||||||
entity->attack_box_offset = Dqn_V2_InitNx2(entity->local_hit_box_offset.x - entity->attack_box_size.w,
|
|
||||||
entity->local_hit_box_offset.y);
|
|
||||||
} break;
|
|
||||||
|
|
||||||
case FP_GameDirection_Right: {
|
|
||||||
entity->attack_box_offset = Dqn_V2_InitNx2(entity->local_hit_box_offset.x + entity->attack_box_size.w,
|
|
||||||
entity->local_hit_box_offset.y);
|
|
||||||
} break;
|
|
||||||
|
|
||||||
case FP_GameDirection_Up: {
|
|
||||||
entity->attack_box_offset = Dqn_V2_InitNx2(entity->local_hit_box_offset.x,
|
|
||||||
entity->local_hit_box_offset.y - entity->attack_box_size.h);
|
|
||||||
} break;
|
|
||||||
|
|
||||||
case FP_GameDirection_Down: {
|
|
||||||
entity->attack_box_offset = Dqn_V2_InitNx2(entity->local_hit_box_offset.x,
|
|
||||||
entity->local_hit_box_offset.y + entity->attack_box_size.h);
|
|
||||||
} break;
|
|
||||||
|
|
||||||
case FP_GameDirection_Count: DQN_INVALID_CODE_PATH; break;
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
entity->attack_box_size = {};
|
|
||||||
}
|
|
||||||
} break;
|
} break;
|
||||||
|
|
||||||
case FP_EntityType_ChurchTerry: {
|
case FP_EntityType_ChurchTerry: {
|
||||||
@ -1271,6 +1124,106 @@ void FP_EntityActionStateMachine(FP_Game *game, TELY_Audio *audio, TELY_Platform
|
|||||||
case FP_EntityType_Count: DQN_INVALID_CODE_PATH; break;
|
case FP_EntityType_Count: DQN_INVALID_CODE_PATH; break;
|
||||||
case FP_EntityType_PhoneMessageProjectile: break;
|
case FP_EntityType_PhoneMessageProjectile: break;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
switch (entity->type) {
|
||||||
|
case FP_EntityType_Nil:
|
||||||
|
case FP_EntityType_AirportTerry:
|
||||||
|
case FP_EntityType_ChurchTerry:
|
||||||
|
case FP_EntityType_ClubTerry:
|
||||||
|
case FP_EntityType_Heart:
|
||||||
|
case FP_EntityType_KennelTerry:
|
||||||
|
case FP_EntityType_Map:
|
||||||
|
case FP_EntityType_MerchantGraveyard:
|
||||||
|
case FP_EntityType_MerchantGym:
|
||||||
|
case FP_EntityType_MerchantPhoneCompany:
|
||||||
|
case FP_EntityType_MerchantTerry:
|
||||||
|
case FP_EntityType_PhoneMessageProjectile:
|
||||||
|
case FP_EntityType_Count: break;
|
||||||
|
|
||||||
|
case FP_EntityType_Terry: /*FALLTHRU*/
|
||||||
|
case FP_EntityType_Catfish: /*FALLTHRU*/
|
||||||
|
case FP_EntityType_Clinger: /*FALLTHRU*/
|
||||||
|
case FP_EntityType_Smoochie: {
|
||||||
|
|
||||||
|
bool is_attacking = false;
|
||||||
|
bool is_range_attack = false;
|
||||||
|
if (entity->type == FP_EntityType_Catfish) {
|
||||||
|
is_attacking = entity->action.state == FP_EntityCatfishState_Attack;
|
||||||
|
} else if (entity->type == FP_EntityType_Clinger) {
|
||||||
|
is_attacking = entity->action.state == FP_EntityClingerState_Attack;
|
||||||
|
} else if (entity->type == FP_EntityType_Smoochie) {
|
||||||
|
is_attacking = entity->action.state == FP_EntitySmoochieState_Attack;
|
||||||
|
} else {
|
||||||
|
DQN_ASSERT(entity->type == FP_EntityType_Terry);
|
||||||
|
is_range_attack = entity->action.state == FP_EntityTerryState_RangeAttack;
|
||||||
|
is_attacking = is_range_attack || entity->action.state == FP_EntityTerryState_Attack;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!is_attacking) {
|
||||||
|
entity->attack_processed = false;
|
||||||
|
entity->attack_box_size = {};
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
// NOTE: Position the attack box
|
||||||
|
uint64_t duration_ms = action->sprite.anim->count * action->sprite.anim->ms_per_frame;
|
||||||
|
uint64_t midpoint_clock_ms = action->started_at_clock_ms + (duration_ms / 2);
|
||||||
|
DQN_ASSERT(duration_ms >= PHYSICS_STEP);
|
||||||
|
DQN_ASSERT(action->sprite.anim);
|
||||||
|
|
||||||
|
// NOTE: Adding an attack_processed bool to make sure things only fire once
|
||||||
|
if (!entity->attack_processed && game->clock_ms >= midpoint_clock_ms) {
|
||||||
|
|
||||||
|
// NOTE: Position the attack box
|
||||||
|
Dqn_V2 dir_vector = {};
|
||||||
|
switch (entity->direction) {
|
||||||
|
case FP_GameDirection_Left: {
|
||||||
|
dir_vector.x = -1.f;
|
||||||
|
entity->attack_box_offset = Dqn_V2_InitNx2(entity->local_hit_box_offset.x - entity->attack_box_size.w,
|
||||||
|
entity->local_hit_box_offset.y);
|
||||||
|
} break;
|
||||||
|
|
||||||
|
case FP_GameDirection_Right: {
|
||||||
|
dir_vector.x = +1.f;
|
||||||
|
entity->attack_box_offset = Dqn_V2_InitNx2(
|
||||||
|
entity->local_hit_box_offset.x + entity->attack_box_size.w,
|
||||||
|
entity->local_hit_box_offset.y);
|
||||||
|
} break;
|
||||||
|
|
||||||
|
case FP_GameDirection_Up: {
|
||||||
|
dir_vector.y = -1.f;
|
||||||
|
entity->attack_box_offset = Dqn_V2_InitNx2(entity->local_hit_box_offset.x,
|
||||||
|
entity->local_hit_box_offset.y - entity->attack_box_size.h);
|
||||||
|
} break;
|
||||||
|
|
||||||
|
case FP_GameDirection_Down: {
|
||||||
|
dir_vector.y = +1.f;
|
||||||
|
entity->attack_box_offset = Dqn_V2_InitNx2(entity->local_hit_box_offset.x,
|
||||||
|
entity->local_hit_box_offset.y + entity->attack_box_size.h);
|
||||||
|
} break;
|
||||||
|
|
||||||
|
case FP_GameDirection_Count: break;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (is_range_attack) {
|
||||||
|
Dqn_Rect entity_hit_box = FP_Game_CalcEntityWorldHitBox(game, entity->handle);
|
||||||
|
Dqn_V2 projectile_pos = entity_hit_box.pos + entity->attack_box_offset;
|
||||||
|
Dqn_V2 projectile_acceleration = FP_Game_MetersToPixelsV2(game, dir_vector * 0.25f);
|
||||||
|
FP_Entity_CreatePhoneMessageProjectile(game,
|
||||||
|
entity->handle,
|
||||||
|
projectile_pos,
|
||||||
|
projectile_acceleration,
|
||||||
|
"Phone Message Projectile");
|
||||||
|
} else {
|
||||||
|
entity->attack_box_size = entity->local_hit_box_size;
|
||||||
|
TELY_Audio_Play(audio, game->audio[FP_GameAudio_TerryHit], 1.f);
|
||||||
|
}
|
||||||
|
entity->attack_processed = true;
|
||||||
|
} else {
|
||||||
|
entity->attack_box_size = {};
|
||||||
|
}
|
||||||
|
} break;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void FP_Update(TELY_Platform *platform, FP_Game *game, TELY_PlatformInput *input)
|
void FP_Update(TELY_Platform *platform, FP_Game *game, TELY_PlatformInput *input)
|
||||||
@ -1280,6 +1233,7 @@ void FP_Update(TELY_Platform *platform, FP_Game *game, TELY_PlatformInput *input
|
|||||||
if (TELY_Platform_InputKeyIsReleased(input->mouse_left))
|
if (TELY_Platform_InputKeyIsReleased(input->mouse_left))
|
||||||
game->clicked_entity = game->prev_active_entity;
|
game->clicked_entity = game->prev_active_entity;
|
||||||
|
|
||||||
|
game->update_counter++;
|
||||||
game->clock_ms = DQN_CAST(uint64_t)(platform->input.timer_s * 1000.f);
|
game->clock_ms = DQN_CAST(uint64_t)(platform->input.timer_s * 1000.f);
|
||||||
Dqn_V2 dir_vector = {};
|
Dqn_V2 dir_vector = {};
|
||||||
|
|
||||||
@ -1787,10 +1741,16 @@ void FP_Update(TELY_Platform *platform, FP_Game *game, TELY_PlatformInput *input
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if (entity->type == FP_EntityType_Terry) {
|
// NOTE: Recover mobile data
|
||||||
entity->terry_mobile_data_plan =
|
entity->terry_mobile_data_plan =
|
||||||
DQN_MIN(entity->terry_mobile_data_plan + DQN_CAST(Dqn_usize)(FP_TERRY_MOBILE_DATA_PER_RANGE_ATTACK * .25f * PHYSICS_STEP),
|
DQN_MIN(entity->terry_mobile_data_plan + DQN_CAST(Dqn_usize)(FP_TERRY_MOBILE_DATA_PER_RANGE_ATTACK * .25f * PHYSICS_STEP),
|
||||||
entity->terry_mobile_data_plan_cap);
|
entity->terry_mobile_data_plan_cap);
|
||||||
|
|
||||||
|
// NOTE: Recover hp & stamina
|
||||||
|
entity->stamina = DQN_MIN(entity->stamina + 1, entity->stamina_cap);
|
||||||
|
|
||||||
|
if (game->update_counter % 12 == 0) {
|
||||||
|
entity->hp = DQN_MIN(entity->hp + 1, entity->hp_cap);
|
||||||
}
|
}
|
||||||
|
|
||||||
// NOTE: Derive dynmamic bounding boxes ====================================================
|
// NOTE: Derive dynmamic bounding boxes ====================================================
|
||||||
@ -1835,7 +1795,7 @@ void FP_Update(TELY_Platform *platform, FP_Game *game, TELY_PlatformInput *input
|
|||||||
}
|
}
|
||||||
} else if (entity->spawn_list.size < entity->spawn_cap) { // NOTE: Spawn new entities
|
} else if (entity->spawn_list.size < entity->spawn_cap) { // NOTE: Spawn new entities
|
||||||
if (input->timer_s >= entity->next_spawn_timestamp_s) {
|
if (input->timer_s >= entity->next_spawn_timestamp_s) {
|
||||||
uint64_t hp_adjustment = entity->current_wave - 1;
|
uint16_t hp_adjustment = DQN_CAST(uint16_t)entity->current_wave - 1;
|
||||||
entity->next_spawn_timestamp_s = DQN_CAST(uint64_t)(input->timer_s + 5.f);
|
entity->next_spawn_timestamp_s = DQN_CAST(uint64_t)(input->timer_s + 5.f);
|
||||||
|
|
||||||
Dqn_V2 entity_world_pos = FP_Game_CalcEntityWorldPos(game, entity->handle);
|
Dqn_V2 entity_world_pos = FP_Game_CalcEntityWorldPos(game, entity->handle);
|
||||||
@ -1843,17 +1803,19 @@ void FP_Update(TELY_Platform *platform, FP_Game *game, TELY_PlatformInput *input
|
|||||||
|
|
||||||
Dqn_f32 mob_choice = Dqn_PCG32_NextF32(&game->rng);
|
Dqn_f32 mob_choice = Dqn_PCG32_NextF32(&game->rng);
|
||||||
if (mob_choice <= 0.33f)
|
if (mob_choice <= 0.33f)
|
||||||
link->data = FP_Entity_CreateClinger(game, entity_world_pos, hp_adjustment, "Clinger");
|
link->data = FP_Entity_CreateClinger(game, entity_world_pos, "Clinger");
|
||||||
else if (mob_choice <= 0.66f)
|
else if (mob_choice <= 0.66f)
|
||||||
link->data = FP_Entity_CreateSmoochie(game, entity_world_pos, hp_adjustment, "Smoochie");
|
link->data = FP_Entity_CreateSmoochie(game, entity_world_pos, "Smoochie");
|
||||||
else
|
else
|
||||||
link->data = FP_Entity_CreateCatfish(game, entity_world_pos, hp_adjustment, "Catfish");
|
link->data = FP_Entity_CreateCatfish(game, entity_world_pos, "Catfish");
|
||||||
|
|
||||||
// NOTE: Setup the mob with waypoints
|
// NOTE: Setup the mob with waypoints
|
||||||
FP_GameEntity *mob = FP_Game_GetEntity(game, link->data);
|
FP_GameEntity *mob = FP_Game_GetEntity(game, link->data);
|
||||||
mob->waypoints = FP_SentinelList_Init<FP_GameWaypoint>(game->chunk_pool);
|
mob->waypoints = FP_SentinelList_Init<FP_GameWaypoint>(game->chunk_pool);
|
||||||
mob->flags |= FP_GameEntityFlag_AggrosWhenNearTerry;
|
mob->flags |= FP_GameEntityFlag_AggrosWhenNearTerry;
|
||||||
mob->flags |= FP_GameEntityFlag_RespondsToBuildings;
|
mob->flags |= FP_GameEntityFlag_RespondsToBuildings;
|
||||||
|
mob->hp_cap += hp_adjustment;
|
||||||
|
mob->hp = entity->hp_cap;
|
||||||
|
|
||||||
for (FP_GameEntity *waypoint_entity = entity->first_child; waypoint_entity; waypoint_entity = waypoint_entity->next) {
|
for (FP_GameEntity *waypoint_entity = entity->first_child; waypoint_entity; waypoint_entity = waypoint_entity->next) {
|
||||||
if ((waypoint_entity->flags & FP_GameEntityFlag_MobSpawnerWaypoint) == 0)
|
if ((waypoint_entity->flags & FP_GameEntityFlag_MobSpawnerWaypoint) == 0)
|
||||||
@ -1896,6 +1858,10 @@ void FP_Update(TELY_Platform *platform, FP_Game *game, TELY_PlatformInput *input
|
|||||||
if ((defender->flags & FP_GameEntityFlag_Attackable) == 0)
|
if ((defender->flags & FP_GameEntityFlag_Attackable) == 0)
|
||||||
continue;
|
continue;
|
||||||
|
|
||||||
|
// NOTE: Projectiles can't hurt the owner that spawned it
|
||||||
|
if (attacker->projectile_owner == defender->handle)
|
||||||
|
continue;
|
||||||
|
|
||||||
bool permit_attack = true;
|
bool permit_attack = true;
|
||||||
switch (attacker->type) {
|
switch (attacker->type) {
|
||||||
case FP_EntityType_Smoochie: {
|
case FP_EntityType_Smoochie: {
|
||||||
@ -1937,7 +1903,7 @@ void FP_Update(TELY_Platform *platform, FP_Game *game, TELY_PlatformInput *input
|
|||||||
continue;
|
continue;
|
||||||
|
|
||||||
// NOTE: Do HP =========================================================================
|
// NOTE: Do HP =========================================================================
|
||||||
defender->hp -= 1;
|
defender->hp = defender->hp >= FP_DEFAULT_DAMAGE ? defender->hp - FP_DEFAULT_DAMAGE : 0;
|
||||||
if (defender->hp <= 0) {
|
if (defender->hp <= 0) {
|
||||||
defender->is_dying = true;
|
defender->is_dying = true;
|
||||||
|
|
||||||
@ -2237,18 +2203,20 @@ void FP_Render(FP_Game *game, TELY_Platform *platform, TELY_Renderer *renderer)
|
|||||||
FP_GameEntity *player = FP_Game_GetEntity(game, game->player);
|
FP_GameEntity *player = FP_Game_GetEntity(game, game->player);
|
||||||
Dqn_V2 player_pos = FP_Game_CalcEntityWorldPos(game, game->player);
|
Dqn_V2 player_pos = FP_Game_CalcEntityWorldPos(game, game->player);
|
||||||
{
|
{
|
||||||
|
FP_GameInventory *invent = &player->inventory;
|
||||||
struct FP_MerchantToMenuMapping {
|
struct FP_MerchantToMenuMapping {
|
||||||
FP_GameEntityHandle merchant;
|
FP_GameEntityHandle merchant;
|
||||||
Dqn_String8 menu_anim;
|
Dqn_String8 menu_anim;
|
||||||
Dqn_String8 building;
|
Dqn_String8 building;
|
||||||
Dqn_V2 building_offset01;
|
Dqn_V2 building_offset01;
|
||||||
uint8_t *inventory_count;
|
uint8_t *inventory_count;
|
||||||
uint32_t *base_price;
|
uint32_t *building_base_price;
|
||||||
|
uint32_t *upgrade_base_price;
|
||||||
} merchants[] = {
|
} merchants[] = {
|
||||||
{game->merchant_terry, g_anim_names.merchant_terry_menu, g_anim_names.club_terry_dark, Dqn_V2_InitNx2(0.015f, +0.04f), &player->inventory.clubs, &player->inventory.clubs_base_price},
|
{game->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->merchant_graveyard, g_anim_names.merchant_graveyard_menu, g_anim_names.church_terry_dark, Dqn_V2_InitNx2(0.04f, -0.15f), &player->inventory.churchs, &player->inventory.churchs_base_price},
|
{game->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->merchant_gym, g_anim_names.merchant_gym_menu, g_anim_names.kennel_terry, Dqn_V2_InitNx2(0, +0), &player->inventory.kennels, &player->inventory.kennels_base_price},
|
{game->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->merchant_phone_company, g_anim_names.merchant_phone_company_menu, g_anim_names.airport_terry, Dqn_V2_InitNx2(0, -0.1f), &player->inventory.airports, &player->inventory.airports_base_price},
|
{game->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},
|
||||||
};
|
};
|
||||||
|
|
||||||
bool activated_merchant = false;
|
bool activated_merchant = false;
|
||||||
@ -2300,8 +2268,8 @@ void FP_Render(FP_Game *game, TELY_Platform *platform, TELY_Renderer *renderer)
|
|||||||
|
|
||||||
// NOTE: Render the merchant button for buildings ==================
|
// NOTE: Render the merchant button for buildings ==================
|
||||||
uint64_t const buy_duration_ms = 500;
|
uint64_t const buy_duration_ms = 500;
|
||||||
bool const have_enough_coins = player->coins >= *mapping.base_price;
|
|
||||||
{
|
{
|
||||||
|
bool const have_enough_coins = player->coins >= *mapping.building_base_price;
|
||||||
// NOTE: Buy trigger + animation ===============================
|
// NOTE: Buy trigger + animation ===============================
|
||||||
{
|
{
|
||||||
TELY_PlatformInputScanCode key = TELY_PlatformInputScanCode_J;
|
TELY_PlatformInputScanCode key = TELY_PlatformInputScanCode_J;
|
||||||
@ -2317,8 +2285,15 @@ void FP_Render(FP_Game *game, TELY_Platform *platform, TELY_Renderer *renderer)
|
|||||||
} else if (TELY_Platform_InputScanCodeIsReleased(input, key)) {
|
} else if (TELY_Platform_InputScanCodeIsReleased(input, key)) {
|
||||||
if (game->clock_ms > game->player_trigger_purchase_building_timestamp) {
|
if (game->clock_ms > game->player_trigger_purchase_building_timestamp) {
|
||||||
if (mapping.inventory_count) {
|
if (mapping.inventory_count) {
|
||||||
player->coins -= *mapping.base_price;
|
player->coins -= *mapping.building_base_price;
|
||||||
*mapping.base_price *= 2;
|
*mapping.building_base_price *= 2;
|
||||||
|
|
||||||
|
// NOTE: Raise the prices of everything else
|
||||||
|
invent->airports_base_price *= 2;
|
||||||
|
invent->clubs_base_price *= 2;
|
||||||
|
invent->kennels_base_price *= 2;
|
||||||
|
invent->churchs_base_price *= 2;
|
||||||
|
|
||||||
(*mapping.inventory_count)++;
|
(*mapping.inventory_count)++;
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
@ -2358,7 +2333,7 @@ void FP_Render(FP_Game *game, TELY_Platform *platform, TELY_Renderer *renderer)
|
|||||||
0.f /*rotation*/,
|
0.f /*rotation*/,
|
||||||
tex_mod_colour);
|
tex_mod_colour);
|
||||||
|
|
||||||
TELY_Render_TextF(renderer, Dqn_Rect_InterpolatedPoint(dest_rect, Dqn_V2_InitNx2(0.5f, -1)), Dqn_V2_InitNx2(0.5, 0.f), "$%u", *mapping.base_price);
|
TELY_Render_TextF(renderer, Dqn_Rect_InterpolatedPoint(dest_rect, Dqn_V2_InitNx2(0.5f, -1)), Dqn_V2_InitNx2(0.5, 0.f), "$%u", *mapping.building_base_price);
|
||||||
}
|
}
|
||||||
|
|
||||||
// NOTE: Render the merchant shop item building ================
|
// NOTE: Render the merchant shop item building ================
|
||||||
@ -2380,34 +2355,56 @@ void FP_Render(FP_Game *game, TELY_Platform *platform, TELY_Renderer *renderer)
|
|||||||
|
|
||||||
// NOTE: Render the merchant button for buildings
|
// NOTE: Render the merchant button for buildings
|
||||||
{
|
{
|
||||||
|
bool const have_enough_coins = player->coins >= *mapping.upgrade_base_price;
|
||||||
// NOTE: Buy trigger + animation ===============================
|
// NOTE: Buy trigger + animation ===============================
|
||||||
{
|
{
|
||||||
TELY_PlatformInputScanCode key = TELY_PlatformInputScanCode_K;
|
TELY_PlatformInputScanCode key = TELY_PlatformInputScanCode_K;
|
||||||
bool trigger_buy_anim = false;
|
bool trigger_buy_anim = false;
|
||||||
if (TELY_Platform_InputScanCodeIsPressed(input, key)) {
|
|
||||||
game->player_trigger_purchase_upgrade_timestamp = game->clock_ms + buy_duration_ms;
|
if (have_enough_coins) {
|
||||||
} else if (TELY_Platform_InputScanCodeIsDown(input, key)) {
|
if (TELY_Platform_InputScanCodeIsPressed(input, key)) {
|
||||||
trigger_buy_anim = true;
|
game->player_trigger_purchase_upgrade_timestamp = game->clock_ms + buy_duration_ms;
|
||||||
if (game->clock_ms > game->player_trigger_purchase_upgrade_timestamp) {
|
} else if (TELY_Platform_InputScanCodeIsDown(input, key)) {
|
||||||
// TODO(doyle): Do buy logic
|
trigger_buy_anim = true;
|
||||||
|
if (game->clock_ms > game->player_trigger_purchase_upgrade_timestamp)
|
||||||
|
game->player_trigger_purchase_upgrade_timestamp = game->clock_ms;
|
||||||
|
} else if (TELY_Platform_InputScanCodeIsReleased(input, key)) {
|
||||||
|
if (game->clock_ms > game->player_trigger_purchase_upgrade_timestamp) {
|
||||||
|
player->coins -= *mapping.upgrade_base_price;
|
||||||
|
*mapping.upgrade_base_price *= 2;
|
||||||
|
|
||||||
|
if (mapping.merchant == game->merchant_terry) {
|
||||||
|
// TODO(doyle): Attack damage? Or increase attack range?
|
||||||
|
} else if (mapping.merchant == game->merchant_graveyard) {
|
||||||
|
player->stamina_cap += DQN_CAST(uint16_t)(FP_TERRY_DASH_STAMINA_COST * .5f);
|
||||||
|
} else if (mapping.merchant == game->merchant_gym) {
|
||||||
|
player->hp_cap += FP_DEFAULT_DAMAGE;
|
||||||
|
player->hp = player->hp_cap;
|
||||||
|
} else if (mapping.merchant == game->merchant_phone_company) {
|
||||||
|
player->terry_mobile_data_plan_cap += DQN_KILOBYTES(1);
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
game->player_trigger_purchase_upgrade_timestamp = UINT64_MAX;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
|
||||||
|
|
||||||
if (trigger_buy_anim) {
|
if (trigger_buy_anim) {
|
||||||
uint64_t start_buy_time = game->player_trigger_purchase_upgrade_timestamp - buy_duration_ms;
|
uint64_t start_buy_time = game->player_trigger_purchase_upgrade_timestamp - buy_duration_ms;
|
||||||
uint64_t elapsed_time = game->clock_ms - start_buy_time;
|
uint64_t elapsed_time = game->clock_ms - start_buy_time;
|
||||||
|
|
||||||
Dqn_f32 buy_t = DQN_MIN(elapsed_time / DQN_CAST(Dqn_f32)buy_duration_ms, 1.f);
|
Dqn_f32 buy_t = DQN_MIN(elapsed_time / DQN_CAST(Dqn_f32)buy_duration_ms, 1.f);
|
||||||
Dqn_Rect buy_lerp_rect = {};
|
Dqn_Rect buy_lerp_rect = {};
|
||||||
buy_lerp_rect.pos = Dqn_Rect_InterpolatedPoint(merchant_menu_rect, Dqn_V2_InitNx2(0.68f, 0.215f));
|
buy_lerp_rect.pos = Dqn_Rect_InterpolatedPoint(merchant_menu_rect, Dqn_V2_InitNx2(0.68f, 0.215f));
|
||||||
buy_lerp_rect.size.w = (merchant_menu_rect.size.w * 0.211f) * buy_t;
|
buy_lerp_rect.size.w = (merchant_menu_rect.size.w * 0.211f) * buy_t;
|
||||||
buy_lerp_rect.size.h = merchant_menu_rect.size.h * .611f;
|
buy_lerp_rect.size.h = merchant_menu_rect.size.h * .611f;
|
||||||
|
|
||||||
TELY_Render_RectColourV4(renderer, buy_lerp_rect, TELY_RenderShapeMode_Fill, TELY_Colour_V4Alpha(TELY_COLOUR_RED_TOMATO_V4, 0.5f));
|
TELY_Render_RectColourV4(renderer, buy_lerp_rect, TELY_RenderShapeMode_Fill, TELY_Colour_V4Alpha(TELY_COLOUR_RED_TOMATO_V4, 0.5f));
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// NOTE: Render the (B) button =================================
|
// NOTE: Render the (B) button =================================
|
||||||
|
Dqn_V4 tex_mod_colour = have_enough_coins ? TELY_COLOUR_WHITE_V4 : TELY_Colour_V4Alpha(TELY_COLOUR_RED_TOMATO_V4, .5f);
|
||||||
{
|
{
|
||||||
TELY_AssetSpriteAnimation *anim = TELY_Asset_GetSpriteAnimation(&game->atlas_sprite_sheet, g_anim_names.merchant_button_b);
|
TELY_AssetSpriteAnimation *anim = TELY_Asset_GetSpriteAnimation(&game->atlas_sprite_sheet, g_anim_names.merchant_button_b);
|
||||||
Dqn_Rect button_rect = game->atlas_sprite_sheet.rects.data[anim->index];
|
Dqn_Rect button_rect = game->atlas_sprite_sheet.rects.data[anim->index];
|
||||||
@ -2420,9 +2417,9 @@ void FP_Render(FP_Game *game, TELY_Platform *platform, TELY_Renderer *renderer)
|
|||||||
dest_rect,
|
dest_rect,
|
||||||
Dqn_V2_Zero /*rotate origin*/,
|
Dqn_V2_Zero /*rotate origin*/,
|
||||||
0.f /*rotation*/,
|
0.f /*rotation*/,
|
||||||
TELY_COLOUR_WHITE_V4);
|
tex_mod_colour);
|
||||||
|
|
||||||
TELY_Render_TextF(renderer, Dqn_Rect_InterpolatedPoint(dest_rect, Dqn_V2_InitNx2(0.5f, -1)), Dqn_V2_InitNx2(0.5, 0.f), "$2");
|
TELY_Render_TextF(renderer, Dqn_Rect_InterpolatedPoint(dest_rect, Dqn_V2_InitNx2(0.5f, -1)), Dqn_V2_InitNx2(0.5, 0.f), "$%u", *mapping.upgrade_base_price);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -2469,6 +2466,47 @@ void FP_Render(FP_Game *game, TELY_Platform *platform, TELY_Renderer *renderer)
|
|||||||
player->terry_mobile_data_plan,
|
player->terry_mobile_data_plan,
|
||||||
player->terry_mobile_data_plan_cap);
|
player->terry_mobile_data_plan_cap);
|
||||||
|
|
||||||
|
// NOTE: Health bar ====================================================
|
||||||
|
Dqn_f32 bar_height = font_height * .75f;
|
||||||
|
{
|
||||||
|
next_pos.y += font_height;
|
||||||
|
Dqn_f32 health_t = player->hp / DQN_CAST(Dqn_f32)player->hp_cap;
|
||||||
|
Dqn_Rect health_rect = Dqn_Rect_InitNx4(next_pos.x, next_pos.y, DQN_CAST(Dqn_f32)player->hp_cap, bar_height);
|
||||||
|
Dqn_Rect curr_health_rect = Dqn_Rect_InitNx4(next_pos.x, next_pos.y, DQN_CAST(Dqn_f32)player->hp_cap * 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: Stamina bar ===================================================
|
||||||
|
{
|
||||||
|
next_pos.y += font_height;
|
||||||
|
Dqn_f32 stamina_t = player->stamina / DQN_CAST(Dqn_f32)player->stamina_cap;
|
||||||
|
Dqn_Rect stamina_rect = Dqn_Rect_InitNx4(next_pos.x, next_pos.y, DQN_CAST(Dqn_f32)player->stamina_cap, bar_height);
|
||||||
|
Dqn_Rect curr_stamina_rect = Dqn_Rect_InitNx4(next_pos.x, next_pos.y, DQN_CAST(Dqn_f32)player->stamina_cap * stamina_t, bar_height);
|
||||||
|
TELY_Render_RectColourV4(
|
||||||
|
renderer,
|
||||||
|
curr_stamina_rect,
|
||||||
|
TELY_RenderShapeMode_Fill,
|
||||||
|
TELY_COLOUR_YELLOW_SANDY_V4);
|
||||||
|
|
||||||
|
TELY_RenderCommandRect *cmd = TELY_Render_RectColourV4(
|
||||||
|
renderer,
|
||||||
|
stamina_rect,
|
||||||
|
TELY_RenderShapeMode_Line,
|
||||||
|
TELY_COLOUR_BLACK_V4);
|
||||||
|
cmd->thickness = 4.f;
|
||||||
|
}
|
||||||
|
|
||||||
next_pos.y += font_height;
|
next_pos.y += font_height;
|
||||||
TELY_Render_TextF(renderer, next_pos, Dqn_V2_Zero, "$%I64u", player->coins);
|
TELY_Render_TextF(renderer, next_pos, Dqn_V2_Zero, "$%I64u", player->coins);
|
||||||
|
|
||||||
|
@ -137,3 +137,5 @@ struct FP_EntityRenderData
|
|||||||
};
|
};
|
||||||
|
|
||||||
Dqn_usize const FP_TERRY_MOBILE_DATA_PER_RANGE_ATTACK = DQN_KILOBYTES(1);
|
Dqn_usize const FP_TERRY_MOBILE_DATA_PER_RANGE_ATTACK = DQN_KILOBYTES(1);
|
||||||
|
uint16_t const FP_TERRY_DASH_STAMINA_COST = 33;
|
||||||
|
uint16_t const FP_DEFAULT_DAMAGE = 30;
|
||||||
|
@ -291,7 +291,7 @@ static FP_GameEntityHandle FP_Entity_CreateWaypointF(FP_Game *game, Dqn_V2 pos,
|
|||||||
return result;
|
return result;
|
||||||
}
|
}
|
||||||
|
|
||||||
static FP_GameEntityHandle FP_Entity_CreateClinger(FP_Game *game, Dqn_V2 pos, uint64_t hp_adjustment, DQN_FMT_STRING_ANNOTATE char const *fmt, ...)
|
static FP_GameEntityHandle FP_Entity_CreateClinger(FP_Game *game, Dqn_V2 pos, DQN_FMT_STRING_ANNOTATE char const *fmt, ...)
|
||||||
{
|
{
|
||||||
va_list args;
|
va_list args;
|
||||||
va_start(args, fmt);
|
va_start(args, fmt);
|
||||||
@ -299,13 +299,12 @@ static FP_GameEntityHandle FP_Entity_CreateClinger(FP_Game *game, Dqn_V2 pos, ui
|
|||||||
FP_GameEntityHandle result = entity->handle;
|
FP_GameEntityHandle result = entity->handle;
|
||||||
va_end(args);
|
va_end(args);
|
||||||
|
|
||||||
entity->type = FP_EntityType_Clinger;
|
entity->type = FP_EntityType_Clinger;
|
||||||
entity->hp = 1 + hp_adjustment;
|
entity->is_dying = false;
|
||||||
entity->is_dying = false;
|
entity->base_acceleration_per_s.meters = 8.f;
|
||||||
entity->base_acceleration_per_s.meters = 8.f;
|
entity->local_pos = pos;
|
||||||
entity->local_pos = pos;
|
entity->sprite_height = FP_Entity_GetRenderData(game, entity->type, 0 /*state*/, FP_GameDirection_Down).height;
|
||||||
entity->sprite_height = FP_Entity_GetRenderData(game, entity->type, 0 /*state*/, FP_GameDirection_Down).height;
|
entity->attack_cooldown_ms = 1000;
|
||||||
entity->attack_cooldown_ms = 1000;
|
|
||||||
|
|
||||||
entity->local_hit_box_size = FP_Game_MetersToPixelsNx2(game, 0.4f, entity->sprite_height.meters * .5f);
|
entity->local_hit_box_size = FP_Game_MetersToPixelsNx2(game, 0.4f, entity->sprite_height.meters * .5f);
|
||||||
FP_Entity_AddDebugEditorFlags(game, entity->handle);
|
FP_Entity_AddDebugEditorFlags(game, entity->handle);
|
||||||
@ -314,7 +313,7 @@ static FP_GameEntityHandle FP_Entity_CreateClinger(FP_Game *game, Dqn_V2 pos, ui
|
|||||||
return result;
|
return result;
|
||||||
}
|
}
|
||||||
|
|
||||||
static FP_GameEntityHandle FP_Entity_CreateSmoochie(FP_Game *game, Dqn_V2 pos, uint64_t hp_adjustment, DQN_FMT_STRING_ANNOTATE char const *fmt, ...)
|
static FP_GameEntityHandle FP_Entity_CreateSmoochie(FP_Game *game, Dqn_V2 pos, DQN_FMT_STRING_ANNOTATE char const *fmt, ...)
|
||||||
{
|
{
|
||||||
va_list args;
|
va_list args;
|
||||||
va_start(args, fmt);
|
va_start(args, fmt);
|
||||||
@ -324,7 +323,6 @@ static FP_GameEntityHandle FP_Entity_CreateSmoochie(FP_Game *game, Dqn_V2 pos, u
|
|||||||
|
|
||||||
entity->type = FP_EntityType_Smoochie;
|
entity->type = FP_EntityType_Smoochie;
|
||||||
entity->base_acceleration_per_s.meters = 8.f;
|
entity->base_acceleration_per_s.meters = 8.f;
|
||||||
entity->hp = 1 + hp_adjustment;
|
|
||||||
entity->is_dying = false;
|
entity->is_dying = false;
|
||||||
entity->local_pos = pos;
|
entity->local_pos = pos;
|
||||||
entity->sprite_height = FP_Entity_GetRenderData(game, entity->type, 0 /*state*/, FP_GameDirection_Down).height;
|
entity->sprite_height = FP_Entity_GetRenderData(game, entity->type, 0 /*state*/, FP_GameDirection_Down).height;
|
||||||
@ -336,28 +334,6 @@ static FP_GameEntityHandle FP_Entity_CreateSmoochie(FP_Game *game, Dqn_V2 pos, u
|
|||||||
return result;
|
return result;
|
||||||
}
|
}
|
||||||
|
|
||||||
static FP_GameEntityHandle FP_Entity_CreateCatfish(FP_Game *game, Dqn_V2 pos, uint64_t hp_adjustment, 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_Catfish;
|
|
||||||
entity->base_acceleration_per_s.meters = 8.f;
|
|
||||||
entity->hp = 1 + hp_adjustment;
|
|
||||||
entity->is_dying = false;
|
|
||||||
entity->local_pos = pos;
|
|
||||||
entity->sprite_height = FP_Entity_GetRenderData(game, entity->type, 0 /*state*/, FP_GameDirection_Down).height;
|
|
||||||
entity->attack_cooldown_ms = 1000;
|
|
||||||
entity->local_hit_box_size = FP_Game_MetersToPixelsNx2(game, 0.4f, entity->sprite_height.meters * .6f);
|
|
||||||
FP_Entity_AddDebugEditorFlags(game, entity->handle);
|
|
||||||
entity->flags |= FP_GameEntityFlag_NonTraversable;
|
|
||||||
entity->flags |= FP_GameEntityFlag_Attackable;
|
|
||||||
return result;
|
|
||||||
}
|
|
||||||
|
|
||||||
static FP_GameEntityHandle FP_Entity_CreateCatfish(FP_Game *game, Dqn_V2 pos, DQN_FMT_STRING_ANNOTATE char const *fmt, ...)
|
static FP_GameEntityHandle FP_Entity_CreateCatfish(FP_Game *game, Dqn_V2 pos, DQN_FMT_STRING_ANNOTATE char const *fmt, ...)
|
||||||
{
|
{
|
||||||
va_list args;
|
va_list args;
|
||||||
@ -368,7 +344,6 @@ static FP_GameEntityHandle FP_Entity_CreateCatfish(FP_Game *game, Dqn_V2 pos, DQ
|
|||||||
|
|
||||||
entity->type = FP_EntityType_Catfish;
|
entity->type = FP_EntityType_Catfish;
|
||||||
entity->base_acceleration_per_s.meters = 8.f;
|
entity->base_acceleration_per_s.meters = 8.f;
|
||||||
entity->hp = 1;
|
|
||||||
entity->is_dying = false;
|
entity->is_dying = false;
|
||||||
entity->local_pos = pos;
|
entity->local_pos = pos;
|
||||||
entity->sprite_height = FP_Entity_GetRenderData(game, entity->type, 0 /*state*/, FP_GameDirection_Down).height;
|
entity->sprite_height = FP_Entity_GetRenderData(game, entity->type, 0 /*state*/, FP_GameDirection_Down).height;
|
||||||
@ -449,17 +424,15 @@ static FP_GameEntityHandle FP_Entity_CreateTerry(FP_Game *game, Dqn_V2 pos, DQN_
|
|||||||
entity->sprite_height = FP_Entity_GetRenderData(game, entity->type, 0 /*state*/, FP_GameDirection_Down).height;
|
entity->sprite_height = FP_Entity_GetRenderData(game, entity->type, 0 /*state*/, FP_GameDirection_Down).height;
|
||||||
entity->attack_cooldown_ms = 500;
|
entity->attack_cooldown_ms = 500;
|
||||||
entity->local_hit_box_size = FP_Game_MetersToPixelsNx2(game, 0.5f, entity->sprite_height.meters * .6f);
|
entity->local_hit_box_size = FP_Game_MetersToPixelsNx2(game, 0.5f, entity->sprite_height.meters * .6f);
|
||||||
|
entity->hp_cap = FP_DEFAULT_DAMAGE * 3;
|
||||||
|
entity->hp = entity->hp_cap;
|
||||||
|
entity->coins = 1'000'000;
|
||||||
FP_Entity_AddDebugEditorFlags(game, result);
|
FP_Entity_AddDebugEditorFlags(game, result);
|
||||||
entity->flags |= FP_GameEntityFlag_NonTraversable;
|
entity->flags |= FP_GameEntityFlag_NonTraversable;
|
||||||
entity->flags |= FP_GameEntityFlag_Attackable;
|
entity->flags |= FP_GameEntityFlag_Attackable;
|
||||||
entity->flags |= FP_GameEntityFlag_CameraTracking;
|
entity->flags |= FP_GameEntityFlag_CameraTracking;
|
||||||
entity->terry_mobile_data_plan_cap = DQN_KILOBYTES(16);
|
entity->terry_mobile_data_plan_cap = DQN_KILOBYTES(6);
|
||||||
entity->terry_mobile_data_plan = entity->terry_mobile_data_plan_cap;
|
entity->terry_mobile_data_plan = entity->terry_mobile_data_plan_cap;
|
||||||
|
|
||||||
entity->inventory.airports_base_price = 4;
|
|
||||||
entity->inventory.churchs_base_price = 2;
|
|
||||||
entity->inventory.kennels_base_price = 2;
|
|
||||||
entity->inventory.clubs_base_price = 2;
|
|
||||||
return result;
|
return result;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -219,11 +219,26 @@ static FP_GameEntity *FP_Game_MakeEntityPointerFV(FP_Game *game, DQN_FMT_STRING_
|
|||||||
result->handle.id = (game->entities.size - 1) & FP_GAME_ENTITY_HANDLE_INDEX_MASK;
|
result->handle.id = (game->entities.size - 1) & FP_GAME_ENTITY_HANDLE_INDEX_MASK;
|
||||||
}
|
}
|
||||||
|
|
||||||
result->sprite_height.meters = 1;
|
result->sprite_height.meters = 1;
|
||||||
result->parent = FP_Game_ActiveParentEntityPointer(game);
|
result->parent = FP_Game_ActiveParentEntityPointer(game);
|
||||||
result->name = TELY_ChunkPool_AllocFmtFV(game->chunk_pool, fmt, args);
|
result->name = TELY_ChunkPool_AllocFmtFV(game->chunk_pool, fmt, args);
|
||||||
result->buildings_visited = FP_SentinelList_Init<FP_GameEntityHandle>(game->chunk_pool);
|
result->buildings_visited = FP_SentinelList_Init<FP_GameEntityHandle>(game->chunk_pool);
|
||||||
result->action.sprite_alpha = 1.f;
|
result->action.sprite_alpha = 1.f;
|
||||||
|
result->stamina_cap = 100;
|
||||||
|
|
||||||
|
result->hp_cap = FP_DEFAULT_DAMAGE;
|
||||||
|
result->hp = result->hp_cap;
|
||||||
|
|
||||||
|
result->inventory.airports_base_price = 4;
|
||||||
|
result->inventory.churchs_base_price = 2;
|
||||||
|
result->inventory.kennels_base_price = 2;
|
||||||
|
result->inventory.clubs_base_price = 2;
|
||||||
|
|
||||||
|
result->inventory.stamina_base_price = 2;
|
||||||
|
result->inventory.health_base_price = 2;
|
||||||
|
result->inventory.mobile_plan_base_price = 2;
|
||||||
|
result->inventory.attack_base_price = 2;
|
||||||
|
|
||||||
|
|
||||||
// NOTE: Attach entity as a child to the parent
|
// NOTE: Attach entity as a child to the parent
|
||||||
FP_GameEntity *parent = result->parent;
|
FP_GameEntity *parent = result->parent;
|
||||||
@ -730,7 +745,6 @@ FP_GameFindClosestEntityResult FP_Game_FindClosestEntityWithType(FP_Game *game,
|
|||||||
static bool FP_Game_CanEntityAttack(FP_GameEntity *entity, uint64_t current_time_ms)
|
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;
|
bool result = (current_time_ms - entity->last_attack_timestamp) >= entity->attack_cooldown_ms;
|
||||||
|
|
||||||
return result;
|
return result;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -750,6 +764,9 @@ static void FP_Game_EntityTransitionState(FP_Game *game, FP_GameEntity *entity,
|
|||||||
if (entity->terry_mobile_data_plan < FP_TERRY_MOBILE_DATA_PER_RANGE_ATTACK)
|
if (entity->terry_mobile_data_plan < FP_TERRY_MOBILE_DATA_PER_RANGE_ATTACK)
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
} else if (desired_state == FP_EntityTerryState_Dash) {
|
||||||
|
if (entity->stamina < FP_TERRY_DASH_STAMINA_COST)
|
||||||
|
return;
|
||||||
}
|
}
|
||||||
} break;
|
} break;
|
||||||
|
|
||||||
@ -773,6 +790,17 @@ static void FP_Game_EntityTransitionState(FP_Game *game, FP_GameEntity *entity,
|
|||||||
}
|
}
|
||||||
} break;
|
} break;
|
||||||
|
|
||||||
|
case FP_EntityType_Catfish: {
|
||||||
|
if (desired_state == FP_EntityClingerState_Attack) {
|
||||||
|
if (!FP_Game_CanEntityAttack(entity, game->clock_ms)) {
|
||||||
|
// NOTE: Cooldown not met do not transition
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
entity->last_attack_timestamp = game->clock_ms;
|
||||||
|
}
|
||||||
|
} break;
|
||||||
|
|
||||||
|
|
||||||
case FP_EntityType_AirportTerry: {
|
case FP_EntityType_AirportTerry: {
|
||||||
} break;
|
} break;
|
||||||
|
|
||||||
@ -783,7 +811,6 @@ static void FP_Game_EntityTransitionState(FP_Game *game, FP_GameEntity *entity,
|
|||||||
} break;
|
} break;
|
||||||
|
|
||||||
case FP_EntityType_Nil:
|
case FP_EntityType_Nil:
|
||||||
case FP_EntityType_Catfish:
|
|
||||||
case FP_EntityType_Heart:
|
case FP_EntityType_Heart:
|
||||||
case FP_EntityType_KennelTerry:
|
case FP_EntityType_KennelTerry:
|
||||||
case FP_EntityType_Map:
|
case FP_EntityType_Map:
|
||||||
|
@ -141,6 +141,11 @@ struct FP_GameInventory
|
|||||||
uint32_t kennels_base_price;
|
uint32_t kennels_base_price;
|
||||||
uint32_t churchs_base_price;
|
uint32_t churchs_base_price;
|
||||||
|
|
||||||
|
uint32_t stamina_base_price;
|
||||||
|
uint32_t health_base_price;
|
||||||
|
uint32_t mobile_plan_base_price;
|
||||||
|
uint32_t attack_base_price;
|
||||||
|
|
||||||
uint8_t airports;
|
uint8_t airports;
|
||||||
uint8_t clubs;
|
uint8_t clubs;
|
||||||
uint8_t kennels;
|
uint8_t kennels;
|
||||||
@ -199,7 +204,6 @@ struct FP_GameEntity
|
|||||||
uint64_t wave_cooldown_timestamp_s;
|
uint64_t wave_cooldown_timestamp_s;
|
||||||
|
|
||||||
uint64_t flags;
|
uint64_t flags;
|
||||||
uint64_t hp;
|
|
||||||
FP_GameDirection direction;
|
FP_GameDirection direction;
|
||||||
Dqn_V2 local_pos;
|
Dqn_V2 local_pos;
|
||||||
Dqn_f64 alive_time_s;
|
Dqn_f64 alive_time_s;
|
||||||
@ -213,6 +217,10 @@ struct FP_GameEntity
|
|||||||
|
|
||||||
Dqn_usize coins;
|
Dqn_usize coins;
|
||||||
FP_GameInventory inventory;
|
FP_GameInventory inventory;
|
||||||
|
uint16_t hp;
|
||||||
|
uint16_t hp_cap;
|
||||||
|
uint16_t stamina;
|
||||||
|
uint16_t stamina_cap;
|
||||||
};
|
};
|
||||||
|
|
||||||
struct FP_GameEntityIterator
|
struct FP_GameEntityIterator
|
||||||
@ -295,6 +303,7 @@ struct FP_Game
|
|||||||
|
|
||||||
Dqn_f32 meters_to_pixels;
|
Dqn_f32 meters_to_pixels;
|
||||||
uint64_t clock_ms;
|
uint64_t clock_ms;
|
||||||
|
uint64_t update_counter;
|
||||||
Dqn_PCG32 rng;
|
Dqn_PCG32 rng;
|
||||||
|
|
||||||
bool debug_ui;
|
bool debug_ui;
|
||||||
|
Loading…
Reference in New Issue
Block a user