fp: Add monkeys and carrying to portal
This commit is contained in:
BIN
Binary file not shown.
BIN
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
+202
-45
@@ -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;
|
||||
|
||||
+15
-13
@@ -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;
|
||||
|
||||
Reference in New Issue
Block a user