fp: Add projectile attack
This commit is contained in:
parent
aed29a9f50
commit
5f8344ddda
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/sprite_spec.txt
(Stored with Git LFS)
BIN
Data/Textures/sprite_spec.txt
(Stored with Git LFS)
Binary file not shown.
@ -207,6 +207,7 @@ static void FP_Game_MoveEntity(FP_Game *game, FP_GameEntityHandle entity_handle,
|
||||
case FP_EntityType_AirportTerry: break;
|
||||
case FP_EntityType_ChurchTerry: break;
|
||||
case FP_EntityType_KennelTerry: break;
|
||||
case FP_EntityType_PhoneMessageProjectile: break;
|
||||
}
|
||||
|
||||
if (!entity_collides_with_collider)
|
||||
@ -214,6 +215,9 @@ static void FP_Game_MoveEntity(FP_Game *game, FP_GameEntityHandle entity_handle,
|
||||
|
||||
// NOTE: Sweep collider with half the radius of the source entity
|
||||
Dqn_Rect collider_world_hit_box = FP_Game_CalcEntityWorldHitBox(game, collider->handle);
|
||||
if (Dqn_V2_Area(collider_world_hit_box.size) <= 0)
|
||||
continue;
|
||||
|
||||
Dqn_Rect swept_collider_world_hit_box = collider_world_hit_box;
|
||||
swept_collider_world_hit_box.pos -= (entity_world_hit_box.size * .5f);
|
||||
swept_collider_world_hit_box.size += entity_world_hit_box.size;
|
||||
@ -524,6 +528,9 @@ void FP_EntityActionStateMachine(FP_Game *game, TELY_Audio *audio, TELY_Platform
|
||||
if (TELY_Platform_InputScanCodeIsPressed(input, TELY_PlatformInputScanCode_J) ||
|
||||
TELY_Platform_InputGamepadButtonCodeIsPressed(input, 0, TELY_PlatformInputGamepadButtonCode_X)) {
|
||||
FP_Game_EntityTransitionState(game, entity, FP_EntityTerryState_Attack);
|
||||
} else if (TELY_Platform_InputScanCodeIsPressed(input, TELY_PlatformInputScanCode_K) ||
|
||||
TELY_Platform_InputGamepadButtonCodeIsPressed(input, 0, TELY_PlatformInputGamepadButtonCode_Y)) {
|
||||
FP_Game_EntityTransitionState(game, entity, FP_EntityTerryState_RangeAttack);
|
||||
} else if (acceleration_meters_per_s->x || acceleration_meters_per_s->y) {
|
||||
FP_Game_EntityTransitionState(game, entity, FP_EntityTerryState_Run);
|
||||
}
|
||||
@ -541,7 +548,7 @@ void FP_EntityActionStateMachine(FP_Game *game, TELY_Audio *audio, TELY_Platform
|
||||
}
|
||||
} break;
|
||||
|
||||
case FP_EntityTerryState_AttackPhone: {
|
||||
case FP_EntityTerryState_RangeAttack: {
|
||||
if (entering_new_state) {
|
||||
uint64_t duration_ms = render_data.sprite.anim->count * render_data.sprite.anim->ms_per_frame;
|
||||
FP_Game_EntityActionReset(game, entity->handle, duration_ms, render_data.sprite);
|
||||
@ -562,6 +569,9 @@ void FP_EntityActionStateMachine(FP_Game *game, TELY_Audio *audio, TELY_Platform
|
||||
if (TELY_Platform_InputScanCodeIsPressed(input, TELY_PlatformInputScanCode_J) ||
|
||||
TELY_Platform_InputGamepadButtonCodeIsPressed(input, 0, TELY_PlatformInputGamepadButtonCode_X)) {
|
||||
FP_Game_EntityTransitionState(game, entity, FP_EntityTerryState_Attack);
|
||||
} else if (TELY_Platform_InputScanCodeIsPressed(input, TELY_PlatformInputScanCode_K) ||
|
||||
TELY_Platform_InputGamepadButtonCodeIsPressed(input, 0, TELY_PlatformInputGamepadButtonCode_Y)) {
|
||||
FP_Game_EntityTransitionState(game, entity, FP_EntityTerryState_RangeAttack);
|
||||
} else if (TELY_Platform_InputScanCodeIsPressed(input, TELY_PlatformInputScanCode_LeftControl) ||
|
||||
TELY_Platform_InputGamepadButtonCodeIsPressed(input, 0, TELY_PlatformInputGamepadButtonCode_A)) {
|
||||
FP_Game_EntityTransitionState(game, entity, FP_EntityTerryState_Dash);
|
||||
@ -585,7 +595,7 @@ void FP_EntityActionStateMachine(FP_Game *game, TELY_Audio *audio, TELY_Platform
|
||||
} break;
|
||||
}
|
||||
|
||||
if (*state == FP_EntityTerryState_Attack || *state == FP_EntityTerryState_AttackPhone) {
|
||||
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;
|
||||
@ -594,33 +604,46 @@ void FP_EntityActionStateMachine(FP_Game *game, TELY_Audio *audio, TELY_Platform
|
||||
|
||||
// NOTE: Adding an attack_processed bool to make sure things only fire once
|
||||
if (!entity->attack_processed && game->clock_ms >= midpoint_clock_ms) {
|
||||
entity->attack_box_size = entity->local_hit_box_size;
|
||||
TELY_Audio_Play(audio, game->audio[FP_GameAudio_TerryHit], 1.f);
|
||||
|
||||
// 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, 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 = {};
|
||||
@ -723,6 +746,7 @@ void FP_EntityActionStateMachine(FP_Game *game, TELY_Audio *audio, TELY_Platform
|
||||
if (entering_new_state) {
|
||||
uint64_t duration_ms = render_data.sprite.anim->count * render_data.sprite.anim->ms_per_frame;
|
||||
FP_Game_EntityActionReset(game, entity->handle, duration_ms, render_data.sprite);
|
||||
entity->local_hit_box_size = {};
|
||||
}
|
||||
|
||||
if (action_has_finished)
|
||||
@ -827,6 +851,7 @@ void FP_EntityActionStateMachine(FP_Game *game, TELY_Audio *audio, TELY_Platform
|
||||
TELY_AssetAnimatedSprite sprite = TELY_Asset_MakeAnimatedSprite(sheet, g_anim_names.clinger_death, TELY_AssetFlip_No);
|
||||
uint64_t duration_ms = sprite.anim->count * sprite.anim->ms_per_frame;
|
||||
FP_Game_EntityActionReset(game, entity->handle, duration_ms, sprite);
|
||||
entity->local_hit_box_size = {};
|
||||
}
|
||||
|
||||
if (action_has_finished)
|
||||
@ -1082,6 +1107,7 @@ void FP_EntityActionStateMachine(FP_Game *game, TELY_Audio *audio, TELY_Platform
|
||||
if (entering_new_state) {
|
||||
uint64_t duration_ms = render_data.sprite.anim->count * render_data.sprite.anim->ms_per_frame;
|
||||
FP_Game_EntityActionReset(game, entity->handle, duration_ms, render_data.sprite);
|
||||
entity->local_hit_box_size = {};
|
||||
}
|
||||
|
||||
if (action_has_finished)
|
||||
@ -1241,7 +1267,7 @@ void FP_Update(TELY_Platform *platform, FP_Game *game, TELY_PlatformInput *input
|
||||
continue;
|
||||
|
||||
// NOTE: Move entity by keyboard and gamepad ===============================================
|
||||
Dqn_V2 acceleration_meters_per_s = {};
|
||||
Dqn_V2 acceleration_meters_per_s = entity->constant_acceleration_per_s;
|
||||
if (game->clicked_entity == entity->handle) {
|
||||
if (entity->flags & (FP_GameEntityFlag_MoveByKeyboard | FP_GameEntityFlag_MoveByGamepad)) {
|
||||
bool move_entity = true;
|
||||
@ -1270,6 +1296,11 @@ void FP_Update(TELY_Platform *platform, FP_Game *game, TELY_PlatformInput *input
|
||||
case FP_EntityType_MerchantPhoneCompany: break;
|
||||
case FP_EntityType_Heart: break;
|
||||
case FP_EntityType_MerchantGym: break;
|
||||
case FP_EntityType_AirportTerry: break;
|
||||
case FP_EntityType_Catfish: break;
|
||||
case FP_EntityType_ChurchTerry: break;
|
||||
case FP_EntityType_KennelTerry: break;
|
||||
case FP_EntityType_PhoneMessageProjectile: break;
|
||||
}
|
||||
|
||||
if (move_entity) {
|
||||
@ -1596,6 +1627,13 @@ void FP_Update(TELY_Platform *platform, FP_Game *game, TELY_PlatformInput *input
|
||||
for (FP_GameEntityIterator it = {}; FP_Game_DFSPostOrderWalkEntityTree(game, &it, game->root_entity); ) {
|
||||
FP_GameEntity *entity = it.entity;
|
||||
|
||||
if (entity->flags & FP_GameEntityFlag_TTL) {
|
||||
if (game->clock_ms >= entity->ttl_end_timestamp) {
|
||||
FP_Game_DeleteEntity(game, entity->handle);
|
||||
continue;
|
||||
}
|
||||
}
|
||||
|
||||
// NOTE: Derive dynmamic bounding boxes ====================================================
|
||||
if (entity->flags & FP_GameEntityFlag_DeriveHitBoxFromChildrenBoundingBox) {
|
||||
Dqn_Rect children_bbox = {};
|
||||
@ -1728,6 +1766,7 @@ void FP_Update(TELY_Platform *platform, FP_Game *game, TELY_PlatformInput *input
|
||||
case FP_EntityType_AirportTerry:
|
||||
case FP_EntityType_ChurchTerry:
|
||||
case FP_EntityType_KennelTerry: break;
|
||||
case FP_EntityType_PhoneMessageProjectile: break;
|
||||
}
|
||||
|
||||
if (!permit_attack)
|
||||
@ -1743,6 +1782,7 @@ void FP_Update(TELY_Platform *platform, FP_Game *game, TELY_PlatformInput *input
|
||||
defender->is_dying = true;
|
||||
|
||||
// NOTE: Kickback ======================================================================
|
||||
#if 0
|
||||
Dqn_V2 defender_world_pos = Dqn_Rect_Center(defender_box);
|
||||
Dqn_V2 attack_dir_vector = {};
|
||||
if (attacker_world_pos.x < defender_world_pos.x)
|
||||
@ -1752,6 +1792,7 @@ void FP_Update(TELY_Platform *platform, FP_Game *game, TELY_PlatformInput *input
|
||||
|
||||
Dqn_V2 attack_acceleration_meters_per_s = attack_dir_vector * 60.f;
|
||||
FP_Game_MoveEntity(game, defender->handle, attack_acceleration_meters_per_s);
|
||||
#endif
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -20,6 +20,7 @@ enum FP_EntityType
|
||||
FP_EntityType_MerchantTerry,
|
||||
FP_EntityType_Smoochie,
|
||||
FP_EntityType_Terry,
|
||||
FP_EntityType_PhoneMessageProjectile,
|
||||
FP_EntityType_Count,
|
||||
};
|
||||
|
||||
@ -28,7 +29,7 @@ enum FP_EntityTerryState
|
||||
FP_EntityTerryState_Nil,
|
||||
FP_EntityTerryState_Idle,
|
||||
FP_EntityTerryState_Attack,
|
||||
FP_EntityTerryState_AttackPhone,
|
||||
FP_EntityTerryState_RangeAttack,
|
||||
FP_EntityTerryState_Run,
|
||||
FP_EntityTerryState_Dash,
|
||||
};
|
||||
|
@ -39,12 +39,12 @@ FP_EntityRenderData FP_Entity_GetRenderData(FP_Game *game, FP_EntityType type, u
|
||||
}
|
||||
} break;
|
||||
|
||||
case FP_EntityTerryState_AttackPhone: {
|
||||
case FP_EntityTerryState_RangeAttack: {
|
||||
switch (direction) {
|
||||
case FP_GameDirection_Up: result.anim_name = g_anim_names.terry_attack_phone_up; break;
|
||||
case FP_GameDirection_Down: result.anim_name = g_anim_names.terry_attack_phone_down; break;
|
||||
case FP_GameDirection_Left: result.anim_name = g_anim_names.terry_attack_phone_side; break;
|
||||
case FP_GameDirection_Right: result.anim_name = g_anim_names.terry_attack_phone_side; result.flip = TELY_AssetFlip_X; break;
|
||||
case FP_GameDirection_Left: result.anim_name = g_anim_names.terry_attack_phone_side; result.flip = TELY_AssetFlip_X; break;
|
||||
case FP_GameDirection_Right: result.anim_name = g_anim_names.terry_attack_phone_side; break;
|
||||
case FP_GameDirection_Count: DQN_INVALID_CODE_PATH; break;
|
||||
}
|
||||
} break;
|
||||
@ -227,6 +227,11 @@ FP_EntityRenderData FP_Entity_GetRenderData(FP_Game *game, FP_EntityType type, u
|
||||
}
|
||||
} break;
|
||||
|
||||
case FP_EntityType_PhoneMessageProjectile: {
|
||||
result.height.meters = 1.f;
|
||||
result.anim_name = g_anim_names.terry_attack_phone_message;
|
||||
} break;
|
||||
|
||||
case FP_EntityType_Count: DQN_INVALID_CODE_PATH; break;
|
||||
}
|
||||
|
||||
@ -660,3 +665,32 @@ static FP_GameEntityHandle FP_Entity_CreateAirportTerry(FP_Game *game, Dqn_V2 po
|
||||
entity->local_hit_box_size = Dqn_V2_InitNx2(sprite_rect_scaled.w, sprite_rect_scaled.h - (sprite_rect_scaled.h * .4f));
|
||||
return result;
|
||||
}
|
||||
|
||||
static FP_GameEntityHandle FP_Entity_CreatePhoneMessageProjectile(FP_Game *game, Dqn_V2 pos, Dqn_V2 velocity, 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_PhoneMessageProjectile;
|
||||
FP_EntityRenderData render_data = FP_Entity_GetRenderData(game, entity->type, 0, FP_GameDirection_Down);
|
||||
|
||||
entity->constant_acceleration_per_s = velocity;
|
||||
entity->local_pos = pos;
|
||||
entity->sprite_height = render_data.height;
|
||||
FP_Entity_AddDebugEditorFlags(game, result);
|
||||
entity->flags |= FP_GameEntityFlag_TTL;
|
||||
|
||||
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));
|
||||
entity->attack_box_offset = entity->local_hit_box_offset;
|
||||
entity->attack_box_size = entity->local_hit_box_size;
|
||||
entity->ttl_end_timestamp = game->clock_ms + 1000;
|
||||
|
||||
uint64_t duration_ms = FP_GAME_ENTITY_ACTION_INFINITE_TIMER;
|
||||
FP_Game_EntityActionReset(game, result, duration_ms, render_data.sprite);
|
||||
|
||||
return result;
|
||||
}
|
||||
|
@ -22,6 +22,7 @@ enum FP_GameEntityFlag
|
||||
FP_GameEntityFlag_PointOfInterestHeart = 1 << 11,
|
||||
FP_GameEntityFlag_CameraTracking = 1 << 14,
|
||||
FP_GameEntityFlag_BuildZone = 1 << 15,
|
||||
FP_GameEntityFlag_TTL = 1 << 16,
|
||||
};
|
||||
|
||||
enum FP_GameShapeType
|
||||
@ -150,6 +151,7 @@ struct FP_GameEntity
|
||||
FP_Meters sprite_height;
|
||||
FP_GameEntityAction action;
|
||||
FP_Meters base_acceleration_per_s;
|
||||
Dqn_V2 constant_acceleration_per_s;
|
||||
Dqn_V2 velocity;
|
||||
|
||||
// Extra animations that are to be executed, but, don't affect the state
|
||||
@ -189,6 +191,8 @@ struct FP_GameEntity
|
||||
Dqn_V2 local_pos;
|
||||
Dqn_f64 alive_time_s;
|
||||
Dqn_FArray<FP_GameShape, 4> shapes;
|
||||
Dqn_FArray<FP_GameEntityHandle, 4> projectiles;
|
||||
uint64_t ttl_end_timestamp;
|
||||
};
|
||||
|
||||
struct FP_GameEntityIterator
|
||||
|
Loading…
Reference in New Issue
Block a user