feely_pona/feely_pona_entity_create.cpp

858 lines
43 KiB
C++

#if defined(_CLANGD)
#pragma once
#include "feely_pona_unity.h"
#endif
static bool FP_Entity_IsBuildingForMobs(FP_GameEntity *entity)
{
bool result = entity->type == FP_EntityType_AirportTerry ||
entity->type == FP_EntityType_ClubTerry ||
entity->type == FP_EntityType_ChurchTerry ||
entity->type == FP_EntityType_KennelTerry;
return result;
}
static Dqn_f32 FP_Entity_CalcSpriteScaleForDesiredHeight(FP_Game *game, FP_Meters height, Dqn_Rect sprite_rect)
{
Dqn_f32 sprite_in_meters = FP_Game_PixelsToMetersNx1(game->play, sprite_rect.size.y);
Dqn_f32 result = height.meters / sprite_in_meters;
return result;
}
FP_EntityRenderData FP_Entity_GetRenderData(FP_Game *game, FP_EntityType type, uint32_t raw_state, FP_GameDirection direction)
{
FP_EntityRenderData result = {};
switch (type) {
case FP_EntityType_Nil: {
} break;
case FP_EntityType_Map: {
result.height.meters = 41.9f;
result.anim_name = g_anim_names.map;
} break;
case FP_EntityType_Terry: {
result.height.meters = 1.8f;
FP_EntityTerryState state = DQN_CAST(FP_EntityTerryState)raw_state;
switch (state) {
case FP_EntityTerryState_Idle: result.anim_name = g_anim_names.terry_walk_idle; break;
case FP_EntityTerryState_Attack: {
switch (direction) {
case FP_GameDirection_Up: result.anim_name = g_anim_names.terry_attack_up; result.height.meters *= 1.5f; break;
case FP_GameDirection_Down: result.anim_name = g_anim_names.terry_attack_down; result.height.meters *= 1.6f; break;
case FP_GameDirection_Left: result.anim_name = g_anim_names.terry_attack_side; result.height.meters *= 1.5f; break;
case FP_GameDirection_Right: result.anim_name = g_anim_names.terry_attack_side; result.height.meters *= 1.5f; result.flip = TELY_AssetFlip_X; break;
case FP_GameDirection_Count: DQN_INVALID_CODE_PATH; break;
}
} break;
case FP_EntityTerryState_RangeAttack: {
switch (direction) {
case FP_GameDirection_Up: result.anim_name = g_anim_names.terry_attack_phone_up; result.height.meters *= 1.25f; break;
case FP_GameDirection_Down: result.anim_name = g_anim_names.terry_attack_phone_down; result.height.meters *= 1.25f; break;
case FP_GameDirection_Left: result.anim_name = g_anim_names.terry_attack_phone_side; result.height.meters *= 1.25f; result.flip = TELY_AssetFlip_X; break;
case FP_GameDirection_Right: result.anim_name = g_anim_names.terry_attack_phone_side; result.height.meters *= 1.25f; break;
case FP_GameDirection_Count: DQN_INVALID_CODE_PATH; break;
}
} break;
case FP_EntityTerryState_Dash: {
switch (direction) {
case FP_GameDirection_Up: result.anim_name = g_anim_names.terry_ghost; break;
case FP_GameDirection_Down: result.anim_name = g_anim_names.terry_ghost; break;
case FP_GameDirection_Left: result.anim_name = g_anim_names.terry_ghost; break;
case FP_GameDirection_Right: result.anim_name = g_anim_names.terry_ghost; result.flip = TELY_AssetFlip_X; break;
case FP_GameDirection_Count: DQN_INVALID_CODE_PATH; break;
}
} break;
case FP_EntityTerryState_Run: {
switch (direction) {
case FP_GameDirection_Up: result.anim_name = g_anim_names.terry_walk_up; break;
case FP_GameDirection_Down: result.anim_name = g_anim_names.terry_walk_down; break;
case FP_GameDirection_Left: result.anim_name = g_anim_names.terry_walk_left; break;
case FP_GameDirection_Right: result.anim_name = g_anim_names.terry_walk_right; break;
case FP_GameDirection_Count: DQN_INVALID_CODE_PATH; break;
}
} break;
case FP_EntityTerryState_DeadGhost: {
result.anim_name = g_anim_names.terry_death; break;
} break;
}
} break;
case FP_EntityType_Smoochie: {
result.height.meters = 1.6f;
FP_EntitySmoochieState state = DQN_CAST(FP_EntitySmoochieState)raw_state;
switch (state) {
case FP_EntitySmoochieState_Idle: result.anim_name = g_anim_names.smoochie_walk_down; break;
case FP_EntitySmoochieState_Attack: result.anim_name = g_anim_names.smoochie_attack_down; break;
case FP_EntitySmoochieState_HurtSide: result.anim_name = g_anim_names.smoochie_hurt_side; result.flip = direction == FP_GameDirection_Right ? TELY_AssetFlip_X : TELY_AssetFlip_No; break;
case FP_EntitySmoochieState_Death: result.anim_name = g_anim_names.smoochie_death; break;
case FP_EntitySmoochieState_Run: {
switch (direction) {
case FP_GameDirection_Up: result.anim_name = g_anim_names.smoochie_walk_up; break;
case FP_GameDirection_Down: result.anim_name = g_anim_names.smoochie_walk_down; break;
case FP_GameDirection_Left: result.anim_name = g_anim_names.smoochie_walk_left; break;
case FP_GameDirection_Right: result.anim_name = g_anim_names.smoochie_walk_right; break;
case FP_GameDirection_Count: DQN_INVALID_CODE_PATH; break;
}
} break;
}
} break;
case FP_EntityType_MerchantTerry: {
result.height.meters = 3.66f;
FP_EntityMerchantTerryState state = DQN_CAST(FP_EntityMerchantTerryState)raw_state;
switch (state) {
case FP_EntityMerchantTerryState_Idle: result.anim_name = g_anim_names.merchant_terry; break;
}
} break;
case FP_EntityType_MerchantGraveyard: {
result.height.meters = 3.66f;
FP_EntityMerchantGraveyardState state = DQN_CAST(FP_EntityMerchantGraveyardState)raw_state;
switch (state) {
case FP_EntityMerchantGraveyardState_Idle: result.anim_name = g_anim_names.merchant_graveyard; break;
}
} break;
case FP_EntityType_MerchantGym: {
result.height.meters = 3.66f;
result.anim_name = g_anim_names.merchant_gym;
FP_EntityMerchantGymState state = DQN_CAST(FP_EntityMerchantGymState)raw_state;
switch (state) {
case FP_EntityMerchantGymState_Idle: result.anim_name = g_anim_names.merchant_gym; break;
}
} break;
case FP_EntityType_MerchantPhoneCompany: {
result.height.meters = 5.f;
FP_EntityMerchantPhoneCompanyState state = DQN_CAST(FP_EntityMerchantPhoneCompanyState)raw_state;
switch (state) {
case FP_EntityMerchantPhoneCompanyState_Idle: result.anim_name = g_anim_names.merchant_phone_company; break;
}
} break;
case FP_EntityType_ClubTerry: {
result.height.meters = 4.f;
FP_EntityClubTerryState state = DQN_CAST(FP_EntityClubTerryState)raw_state;
switch (state) {
case FP_EntityClubTerryState_Idle: result.anim_name = g_anim_names.club_terry_dark; break;
case FP_EntityClubTerryState_PartyTime: result.anim_name = g_anim_names.club_terry_alive; break;
}
} break;
case FP_EntityType_Clinger: {
result.height.meters = 1.6f;
FP_EntityClingerState state = DQN_CAST(FP_EntityClingerState)raw_state;
switch (state) {
case FP_EntityClingerState_Idle: result.anim_name = g_anim_names.clinger_walk_down; break;
case FP_EntityClingerState_Attack: {
switch (direction) {
case FP_GameDirection_Up: result.anim_name = g_anim_names.clinger_attack_up; break;
case FP_GameDirection_Down: result.anim_name = g_anim_names.clinger_attack_down; break;
case FP_GameDirection_Left: result.anim_name = g_anim_names.clinger_attack_side; result.flip = TELY_AssetFlip_X; break;
case FP_GameDirection_Right: result.anim_name = g_anim_names.clinger_attack_side; break;
case FP_GameDirection_Count: DQN_INVALID_CODE_PATH; break;
}
} break;
case FP_EntityClingerState_Death: result.anim_name = g_anim_names.clinger_death; break;
case FP_EntityClingerState_Run: {
switch (direction) {
case FP_GameDirection_Up: result.anim_name = g_anim_names.clinger_walk_up; break;
case FP_GameDirection_Down: result.anim_name = g_anim_names.clinger_walk_down; break;
case FP_GameDirection_Left: result.anim_name = g_anim_names.clinger_walk_down; break;
case FP_GameDirection_Right: result.anim_name = g_anim_names.clinger_walk_down; break;
case FP_GameDirection_Count: DQN_INVALID_CODE_PATH; break;
}
} break;
}
} break;
case FP_EntityType_Heart: {
result.height.meters = 4.f;
FP_EntityHeartState state = DQN_CAST(FP_EntityHeartState)raw_state;
switch (state) {
case FP_EntityHeartState_Idle: result.anim_name = g_anim_names.heart; break;
}
} break;
case FP_EntityType_AirportTerry: {
result.height.meters = 4.f;
FP_EntityAirportTerryState state = DQN_CAST(FP_EntityAirportTerryState)raw_state;
switch (state) {
case FP_EntityAirportTerryState_Idle: result.anim_name = g_anim_names.airport_terry; break;
case FP_EntityAirportTerryState_FlyPassenger: result.anim_name = g_anim_names.airport_terry; break;
}
} break;
case FP_EntityType_Catfish: {
result.height.meters = 1.6f;
FP_EntityCatfishState state = DQN_CAST(FP_EntityCatfishState)raw_state;
switch (state) {
case FP_EntityCatfishState_Idle: result.anim_name = g_anim_names.catfish_walk_down; break;
case FP_EntityCatfishState_Attack: {
switch (direction) {
case FP_GameDirection_Up: result.anim_name = g_anim_names.catfish_attack_up; break;
case FP_GameDirection_Down: result.anim_name = g_anim_names.catfish_attack_down; break;
case FP_GameDirection_Left: result.anim_name = g_anim_names.catfish_attack_side; break;
case FP_GameDirection_Right: result.anim_name = g_anim_names.catfish_attack_side; result.flip = TELY_AssetFlip_X; break;
case FP_GameDirection_Count: DQN_INVALID_CODE_PATH; break;
}
} break;
case FP_EntityCatfishState_Death: result.anim_name = g_anim_names.catfish_death; break;
case FP_EntityCatfishState_Run: {
switch (direction) {
case FP_GameDirection_Up: result.anim_name = g_anim_names.catfish_walk_up; break;
case FP_GameDirection_Down: result.anim_name = g_anim_names.catfish_walk_down; break;
case FP_GameDirection_Left: result.anim_name = g_anim_names.catfish_walk_side; break;
case FP_GameDirection_Right: result.anim_name = g_anim_names.catfish_walk_side; result.flip = TELY_AssetFlip_X; break;
case FP_GameDirection_Count: DQN_INVALID_CODE_PATH; break;
}
} break;
}
} break;
case FP_EntityType_ChurchTerry: {
result.height.meters = 4.f;
FP_EntityChurchTerryState state = DQN_CAST(FP_EntityChurchTerryState)raw_state;
switch (state) {
case FP_EntityChurchTerryState_Idle: result.anim_name = g_anim_names.church_terry_dark; break;
case FP_EntityChurchTerryState_ConvertPatron: result.anim_name = g_anim_names.church_terry_alive; break;
}
} break;
case FP_EntityType_KennelTerry: {
result.height.meters = 3.f;
FP_EntityKennelTerryState state = DQN_CAST(FP_EntityKennelTerryState)raw_state;
switch (state) {
case FP_EntityKennelTerryState_Idle: result.anim_name = g_anim_names.kennel_terry; break;
}
} break;
case FP_EntityType_PhoneMessageProjectile: {
result.height.meters = 1.f;
result.anim_name = g_anim_names.terry_attack_phone_message;
} break;
case FP_EntityType_AirportTerryPlane: {
result.height.meters = 1.5f;
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_Idle: result.anim_name = g_anim_names.portal; break;
case FP_EntityMobSpawnerState_Shutdown: {
result.anim_name = g_anim_names.portal_break;
result.height.meters = 3.5f;
} break;
}
} break;
case FP_EntityType_PortalMonkey: {
result.height.meters = 1.f;
result.anim_name = g_anim_names.portal_monk; break;
} break;
case FP_EntityType_Billboard: {
result.height.meters = 12.f;
FP_EntityBillboardState state = DQN_CAST(FP_EntityBillboardState)raw_state;
switch (state) {
case FP_EntityBillboardState_Attack: result.anim_name = g_anim_names.map_billboard_attack; break;
case FP_EntityBillboardState_Dash: result.anim_name = g_anim_names.map_billboard_dash; break;
case FP_EntityBillboardState_Monkey: result.anim_name = g_anim_names.map_billboard_monkey; break;
case FP_EntityBillboardState_RangeAttack: result.anim_name = g_anim_names.map_billboard_range_attack; break;
case FP_EntityBillboardState_Strafe: result.anim_name = g_anim_names.map_billboard_strafe; break;
}
} break;
case FP_EntityType_Count: DQN_INVALID_CODE_PATH; break;
}
result.sheet = &game->atlas_sprite_sheet;
TELY_AssetSpriteAnimation *sprite_anim = TELY_Asset_GetSpriteAnimation(result.sheet, result.anim_name);
if (sprite_anim) {
result.sheet_rect = result.sheet->rects.data[sprite_anim->index];
Dqn_f32 size_scale = FP_Entity_CalcSpriteScaleForDesiredHeight(game, result.height, result.sheet_rect);
result.render_size = result.sheet_rect.size * size_scale;
result.sprite = TELY_Asset_MakeAnimatedSprite(result.sheet, result.anim_name, result.flip);
}
return result;
}
static void FP_Entity_AddDebugEditorFlags(FP_Game *game, FP_GameEntityHandle handle)
{
FP_GameEntity *entity = FP_Game_GetEntity(game, handle);
if (!FP_Game_IsNilEntity(entity)) {
entity->flags |= FP_GameEntityFlag_Clickable;
entity->flags |= FP_GameEntityFlag_MoveByKeyboard;
entity->flags |= FP_GameEntityFlag_MoveByMouse;
entity->flags |= FP_GameEntityFlag_MoveByGamepad;
}
}
static FP_GameEntityHandle FP_Entity_CreateWaypointF(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->local_pos = pos;
entity->local_hit_box_size = Dqn_V2_InitNx1(32);
FP_Entity_AddDebugEditorFlags(game, entity->handle);
entity->flags |= FP_GameEntityFlag_MobSpawnerWaypoint;
return result;
}
static FP_GameEntityHandle FP_Entity_CreateClinger(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_Clinger;
entity->is_dying = false;
entity->base_acceleration_per_s.meters = 8.f;
entity->local_pos = pos;
FP_EntityRenderData render_data = FP_Entity_GetRenderData(game, entity->type, 0, FP_GameDirection_Down);
entity->sprite_height = render_data.height;
entity->attack_cooldown_ms = 1000;
entity->faction = FP_GameEntityFaction_Foe;
entity->local_hit_box_size = FP_Game_MetersToPixelsNx2(game->play, 0.7f, entity->sprite_height.meters * .5f);
FP_Entity_AddDebugEditorFlags(game, entity->handle);
entity->flags |= FP_GameEntityFlag_NonTraversable;
entity->flags |= FP_GameEntityFlag_Attackable;
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_CreateSmoochie(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_Smoochie;
entity->base_acceleration_per_s.meters = 8.f;
entity->is_dying = false;
entity->local_pos = pos;
entity->attack_cooldown_ms = 1000;
entity->local_hit_box_size = FP_Game_MetersToPixelsNx2(game->play, 0.7f, entity->sprite_height.meters * .6f);
FP_EntityRenderData render_data = FP_Entity_GetRenderData(game, entity->type, 0, FP_GameDirection_Down);
entity->sprite_height = render_data.height;
uint64_t duration_ms = FP_GAME_ENTITY_ACTION_INFINITE_TIMER;
FP_Game_EntityActionReset(game, result, duration_ms, render_data.sprite);
FP_Entity_AddDebugEditorFlags(game, entity->handle);
entity->flags |= FP_GameEntityFlag_NonTraversable;
entity->flags |= FP_GameEntityFlag_Attackable;
entity->faction = FP_GameEntityFaction_Foe;
return result;
}
static FP_GameEntityHandle FP_Entity_CreateCatfish(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_Catfish;
entity->base_acceleration_per_s.meters = 8.f;
entity->is_dying = false;
entity->local_pos = pos;
FP_EntityRenderData render_data = FP_Entity_GetRenderData(game, entity->type, 0, FP_GameDirection_Down);
entity->sprite_height = render_data.height;
uint64_t duration_ms = FP_GAME_ENTITY_ACTION_INFINITE_TIMER;
FP_Game_EntityActionReset(game, result, duration_ms, render_data.sprite);
entity->attack_cooldown_ms = 1000;
entity->local_hit_box_size = FP_Game_MetersToPixelsNx2(game->play, 0.7f, entity->sprite_height.meters * .6f);
FP_Entity_AddDebugEditorFlags(game, entity->handle);
entity->flags |= FP_GameEntityFlag_NonTraversable;
entity->flags |= FP_GameEntityFlag_Attackable;
entity->faction = FP_GameEntityFaction_Foe;
return result;
}
static FP_GameEntityHandle FP_Entity_CreateWallAtPos(FP_Game *game, Dqn_String8 name, Dqn_V2 pos, Dqn_V2 size)
{
FP_GameEntity *entity = FP_Game_MakeEntityPointerF(game, name.data);
FP_GameEntityHandle result = entity->handle;
entity->local_pos = pos;
entity->local_hit_box_size = size;
FP_Entity_AddDebugEditorFlags(game, entity->handle);
entity->flags |= FP_GameEntityFlag_NonTraversable;
FP_GameShape *wall = Dqn_FArray_Make(&entity->shapes, Dqn_ZeroMem_Yes);
wall->type = FP_GameShapeType_Rect;
wall->p2 = entity->local_hit_box_size;
wall->colour = TELY_COLOUR_GREEN_DARK_KHAKI_V4;
return result;
}
static FP_GameEntityHandle FP_Entity_CreatePermittedBuildZone(FP_Game *game, Dqn_V2 pos, Dqn_V2 size, 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->local_pos = pos;
entity->local_hit_box_size = size;
entity->type = FP_EntityType_Nil;
entity->flags |= FP_GameEntityFlag_BuildZone;
FP_Entity_AddDebugEditorFlags(game, entity->handle);
return result;
}
static FP_GameEntityHandle FP_Entity_CreateMobSpawner(FP_Game *game, Dqn_V2 pos, Dqn_usize spawn_cap, 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_MobSpawner;
entity->local_pos = pos;
entity->local_hit_box_size = Dqn_V2_InitNx1(32);
FP_Entity_AddDebugEditorFlags(game, result);
FP_EntityRenderData render_data = FP_Entity_GetRenderData(game, entity->type, 0, FP_GameDirection_Down);
entity->sprite_height = render_data.height;
uint64_t duration_ms = FP_GAME_ENTITY_ACTION_INFINITE_TIMER;
FP_Game_EntityActionReset(game, result, duration_ms, render_data.sprite);
entity->spawn_cap = spawn_cap;
entity->spawn_list = FP_SentinelList_Init<FP_GameEntityHandle>(game->play.chunk_pool);
return result;
}
static FP_GameEntityHandle FP_Entity_CreateTerry(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_Terry;
entity->local_pos = pos;
entity->base_acceleration_per_s.meters = 16.f;
FP_EntityRenderData render_data = FP_Entity_GetRenderData(game, entity->type, 0, FP_GameDirection_Down);
entity->sprite_height = render_data.height;
uint64_t duration_ms = FP_GAME_ENTITY_ACTION_INFINITE_TIMER;
FP_Game_EntityActionReset(game, result, duration_ms, render_data.sprite);
entity->attack_cooldown_ms = 500;
entity->local_hit_box_size = FP_Game_MetersToPixelsNx2(game->play, 0.5f, entity->sprite_height.meters * .6f);
entity->hp_cap = FP_DEFAULT_DAMAGE * 3;
entity->hp = entity->hp_cap;
FP_Entity_AddDebugEditorFlags(game, result);
entity->flags |= FP_GameEntityFlag_NonTraversable;
entity->flags |= FP_GameEntityFlag_Attackable;
entity->flags |= FP_GameEntityFlag_CameraTracking;
entity->flags |= FP_GameEntityFlag_RecoversHP;
entity->terry_mobile_data_plan_cap = DQN_KILOBYTES(6);
entity->terry_mobile_data_plan = entity->terry_mobile_data_plan_cap;
entity->faction = FP_GameEntityFaction_Friendly;
return result;
}
static FP_GameEntityHandle FP_Entity_CreateMerchantTerry(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_MerchantTerry;
entity->local_pos = pos;
FP_EntityRenderData render_data = FP_Entity_GetRenderData(game, entity->type, 0, FP_GameDirection_Down);
entity->sprite_height = render_data.height;
uint64_t duration_ms = FP_GAME_ENTITY_ACTION_INFINITE_TIMER;
FP_Game_EntityActionReset(game, result, duration_ms, render_data.sprite);
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);
Dqn_V2 sprite_rect_scaled = sprite_rect.size * size_scale;
entity->local_hit_box_offset = Dqn_V2_InitNx2(0, sprite_rect_scaled.h * .15f);
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_CreateMerchantGraveyard(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_MerchantGraveyard;
entity->local_pos = pos;
FP_EntityRenderData render_data = FP_Entity_GetRenderData(game, entity->type, 0, FP_GameDirection_Down);
entity->sprite_height = render_data.height;
uint64_t duration_ms = FP_GAME_ENTITY_ACTION_INFINITE_TIMER;
FP_Game_EntityActionReset(game, result, duration_ms, render_data.sprite);
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);
Dqn_V2 sprite_rect_scaled = sprite_rect.size * size_scale;
entity->local_hit_box_offset = Dqn_V2_InitNx2(0, sprite_rect_scaled.h * .1f);
entity->local_hit_box_size = Dqn_V2_InitNx2(sprite_rect_scaled.w - (sprite_rect_scaled.w * .3f), sprite_rect_scaled.h - (sprite_rect_scaled.h * .5f));
return result;
}
static FP_GameEntityHandle FP_Entity_CreateMerchantGym(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_MerchantGym;
entity->local_pos = pos;
FP_EntityRenderData render_data = FP_Entity_GetRenderData(game, entity->type, 0, FP_GameDirection_Down);
entity->sprite_height = render_data.height;
uint64_t duration_ms = FP_GAME_ENTITY_ACTION_INFINITE_TIMER;
FP_Game_EntityActionReset(game, result, duration_ms, render_data.sprite);
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_gym);
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);
Dqn_V2 sprite_rect_scaled = sprite_rect.size * size_scale;
entity->local_hit_box_offset = Dqn_V2_InitNx2(0, sprite_rect_scaled.h * .3f);
entity->local_hit_box_size = Dqn_V2_InitNx2(sprite_rect_scaled.w - (sprite_rect_scaled.w * .3f), sprite_rect_scaled.h - (sprite_rect_scaled.h * .6f));
return result;
}
static FP_GameEntityHandle FP_Entity_CreateMerchantPhoneCompany(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_MerchantPhoneCompany;
entity->local_pos = pos;
FP_EntityRenderData render_data = FP_Entity_GetRenderData(game, entity->type, 0, FP_GameDirection_Down);
entity->sprite_height = render_data.height;
uint64_t duration_ms = FP_GAME_ENTITY_ACTION_INFINITE_TIMER;
FP_Game_EntityActionReset(game, result, duration_ms, render_data.sprite);
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_phone_company);
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);
Dqn_V2 sprite_rect_scaled = sprite_rect.size * size_scale;
entity->local_hit_box_offset = Dqn_V2_InitNx2(0, sprite_rect_scaled.h * .15f);
entity->local_hit_box_size = Dqn_V2_InitNx2(sprite_rect_scaled.w - (sprite_rect_scaled.w * .3f), sprite_rect_scaled.h - (sprite_rect_scaled.h * .4f));
return result;
}
static FP_GameEntityHandle FP_Entity_CreateClubTerry(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_ClubTerry;
entity->local_pos = pos;
FP_EntityRenderData render_data = FP_Entity_GetRenderData(game, entity->type, 0, FP_GameDirection_Down);
entity->sprite_height = render_data.height;
uint64_t duration_ms = FP_GAME_ENTITY_ACTION_INFINITE_TIMER;
FP_Game_EntityActionReset(game, result, duration_ms, render_data.sprite);
FP_Entity_AddDebugEditorFlags(game, result);
entity->flags |= FP_GameEntityFlag_NonTraversable;
TELY_AssetSpriteAnimation *sprite_anim = TELY_Asset_GetSpriteAnimation(&game->atlas_sprite_sheet, g_anim_names.club_terry_alive);
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);
Dqn_V2 sprite_rect_scaled = sprite_rect.size * size_scale;
entity->local_hit_box_offset = Dqn_V2_InitNx2(0, sprite_rect_scaled.h * .1f);
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_CreateHeart(FP_Game *game, Dqn_V2 pos, DQN_FMT_STRING_ANNOTATE char const *fmt, ...)
{
va_list args;
va_start(args, fmt);
FP_GameEntity *entity = FP_Game_MakeEntityPointerFV(game, fmt, args);
FP_GameEntityHandle result = entity->handle;
va_end(args);
entity->type = FP_EntityType_Heart;
entity->local_pos = pos;
FP_EntityRenderData render_data = FP_Entity_GetRenderData(game, entity->type, 0, FP_GameDirection_Down);
entity->sprite_height = render_data.height;
uint64_t duration_ms = FP_GAME_ENTITY_ACTION_INFINITE_TIMER;
FP_Game_EntityActionReset(game, result, duration_ms, render_data.sprite);
FP_Entity_AddDebugEditorFlags(game, result);
entity->flags |= FP_GameEntityFlag_NonTraversable;
entity->flags |= FP_GameEntityFlag_Attackable;
entity->flags |= FP_GameEntityFlag_RecoversHP;
entity->hp_cap = FP_DEFAULT_DAMAGE * 16;
entity->hp = entity->hp_cap;
entity->faction = FP_GameEntityFaction_Friendly;
entity->hp_recover_every_n_ticks *= 4;
TELY_AssetSpriteAnimation *sprite_anim = TELY_Asset_GetSpriteAnimation(&game->atlas_sprite_sheet, g_anim_names.heart);
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);
Dqn_V2 sprite_rect_scaled = sprite_rect.size * size_scale;
entity->local_hit_box_offset = Dqn_V2_InitNx2(0, sprite_rect_scaled.h * .15f);
entity->local_hit_box_size = Dqn_V2_InitNx2(sprite_rect_scaled.w - (sprite_rect_scaled.w * .3f), sprite_rect_scaled.h - (sprite_rect_scaled.h * .4f));
return result;
}
static FP_GameEntityHandle FP_Entity_CreateChurchTerry(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_ChurchTerry;
entity->local_pos = pos;
FP_EntityRenderData render_data = FP_Entity_GetRenderData(game, entity->type, 0, FP_GameDirection_Down);
entity->sprite_height = render_data.height;
uint64_t duration_ms = FP_GAME_ENTITY_ACTION_INFINITE_TIMER;
FP_Game_EntityActionReset(game, result, duration_ms, render_data.sprite);
FP_Entity_AddDebugEditorFlags(game, result);
entity->flags |= FP_GameEntityFlag_NonTraversable;
TELY_AssetSpriteAnimation *sprite_anim = TELY_Asset_GetSpriteAnimation(&game->atlas_sprite_sheet, g_anim_names.church_terry_alive);
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);
Dqn_V2 sprite_rect_scaled = sprite_rect.size * size_scale;
entity->local_hit_box_offset = Dqn_V2_InitNx2(0, sprite_rect_scaled.h * .1f);
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_CreateKennelTerry(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_KennelTerry;
entity->local_pos = pos;
FP_EntityRenderData render_data = FP_Entity_GetRenderData(game, entity->type, 0, FP_GameDirection_Down);
entity->sprite_height = render_data.height;
uint64_t duration_ms = FP_GAME_ENTITY_ACTION_INFINITE_TIMER;
FP_Game_EntityActionReset(game, result, duration_ms, render_data.sprite);
FP_Entity_AddDebugEditorFlags(game, result);
entity->flags |= FP_GameEntityFlag_NonTraversable;
TELY_AssetSpriteAnimation *sprite_anim = TELY_Asset_GetSpriteAnimation(&game->atlas_sprite_sheet, g_anim_names.church_terry_alive);
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);
Dqn_V2 sprite_rect_scaled = sprite_rect.size * size_scale;
entity->local_hit_box_offset = Dqn_V2_InitNx2(0, sprite_rect_scaled.h * .1f);
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_CreateAirportTerry(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_AirportTerry;
entity->local_pos = pos;
FP_EntityRenderData render_data = FP_Entity_GetRenderData(game, entity->type, 0, FP_GameDirection_Down);
entity->sprite_height = render_data.height;
uint64_t duration_ms = FP_GAME_ENTITY_ACTION_INFINITE_TIMER;
FP_Game_EntityActionReset(game, result, duration_ms, render_data.sprite);
FP_Entity_AddDebugEditorFlags(game, result);
entity->flags |= FP_GameEntityFlag_NonTraversable;
TELY_AssetSpriteAnimation *sprite_anim = TELY_Asset_GetSpriteAnimation(&game->atlas_sprite_sheet, g_anim_names.church_terry_alive);
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);
Dqn_V2 sprite_rect_scaled = sprite_rect.size * size_scale;
entity->local_hit_box_offset = Dqn_V2_InitNx2(0, sprite_rect_scaled.h * .1f);
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, FP_GameEntityHandle owner, 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;
entity->constant_acceleration_per_s = velocity;
entity->local_pos = pos;
FP_EntityRenderData render_data = FP_Entity_GetRenderData(game, entity->type, 0, FP_GameDirection_Down);
entity->sprite_height = render_data.height;
uint64_t duration_ms = FP_GAME_ENTITY_ACTION_INFINITE_TIMER;
FP_Game_EntityActionReset(game, result, duration_ms, render_data.sprite);
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->play.clock_ms + 1000;
entity->projectile_owner = owner;
entity->faction = FP_GameEntityFaction_Friendly;
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;
entity->local_pos = pos;
FP_EntityRenderData render_data = FP_Entity_GetRenderData(game, entity->type, 0, FP_GameDirection_Down);
entity->sprite_height = render_data.height;
uint64_t duration_ms = FP_GAME_ENTITY_ACTION_INFINITE_TIMER;
FP_Game_EntityActionReset(game, result, duration_ms, render_data.sprite);
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));
return result;
}
static FP_GameEntityHandle FP_Entity_CreateAirportTerryPlane(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_AirportTerryPlane;
FP_EntityRenderData render_data = FP_Entity_GetRenderData(game, entity->type, 0, FP_GameDirection_Down);
entity->sprite_height = render_data.height;
uint64_t duration_ms = FP_GAME_ENTITY_ACTION_INFINITE_TIMER;
FP_Game_EntityActionReset(game, result, duration_ms, render_data.sprite);
entity->local_pos = pos;
entity->flags |= FP_GameEntityFlag_NoClip;
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));
entity->base_acceleration_per_s.meters = 32.f;
return result;
}
static FP_GameEntityHandle FP_Entity_CreateBillboard(FP_Game *game, Dqn_V2 pos, FP_EntityBillboardState state, 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->action.state = state;
entity->action.next_state = state;
entity->type = FP_EntityType_Billboard;
FP_EntityRenderData render_data = FP_Entity_GetRenderData(game, entity->type, state, FP_GameDirection_Down);
entity->sprite_height = render_data.height;
uint64_t duration_ms = FP_GAME_ENTITY_ACTION_INFINITE_TIMER;
FP_Game_EntityActionReset(game, result, duration_ms, render_data.sprite);
entity->local_pos = pos;
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));
return result;
}