Compare commits
4 Commits
4b77b034f3
...
85fe5c8ff7
Author | SHA1 | Date | |
---|---|---|---|
85fe5c8ff7 | |||
13af438612 | |||
815d7aa808 | |||
3e47bbd704 |
2
External/tely
vendored
2
External/tely
vendored
@ -1 +1 @@
|
|||||||
Subproject commit 6fefb31c2f4a44f974d6987fc5942857891ae429
|
Subproject commit e1f11120a2a3a2673317db52d73ae30a71faf172
|
2206
feely_pona.cpp
2206
feely_pona.cpp
File diff suppressed because it is too large
Load Diff
292
feely_pona_entity.cpp
Normal file
292
feely_pona_entity.cpp
Normal file
@ -0,0 +1,292 @@
|
|||||||
|
#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;
|
||||||
|
}
|
||||||
|
|
||||||
|
static 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 = 7.5f;
|
||||||
|
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;
|
||||||
|
}
|
||||||
|
|
@ -3,293 +3,6 @@
|
|||||||
#include "feely_pona_unity.h"
|
#include "feely_pona_unity.h"
|
||||||
#endif
|
#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 = 7.5f;
|
|
||||||
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)
|
static void FP_Entity_AddDebugEditorFlags(FP_Game *game, FP_GameEntityHandle handle)
|
||||||
{
|
{
|
||||||
FP_GameEntity *entity = FP_Game_GetEntity(game, handle);
|
FP_GameEntity *entity = FP_Game_GetEntity(game, handle);
|
||||||
|
@ -3,14 +3,6 @@
|
|||||||
#include "feely_pona_unity.h"
|
#include "feely_pona_unity.h"
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
#define FP_Game_MetersToPixelsNx1(game, val) ((val) * (game).meters_to_pixels)
|
|
||||||
#define FP_Game_MetersToPixelsNx2(game, x, y) (Dqn_V2_InitNx2(x, y) * (game).meters_to_pixels)
|
|
||||||
#define FP_Game_MetersToPixelsV2(game, xy) (xy * (game).meters_to_pixels)
|
|
||||||
|
|
||||||
#define FP_Game_PixelsToMetersNx1(game, val) ((val) * (1.f/(game).meters_to_pixels))
|
|
||||||
#define FP_Game_PixelsToMetersNx2(game, x, y) (Dqn_V2_InitNx2(x, y) * (1.f/(game).meters_to_pixels))
|
|
||||||
#define FP_Game_PixelsToMetersV2(game, xy) (xy * (1.f/(game).meters_to_pixels))
|
|
||||||
|
|
||||||
static bool operator==(FP_GameEntityHandle const &lhs, FP_GameEntityHandle const &rhs)
|
static bool operator==(FP_GameEntityHandle const &lhs, FP_GameEntityHandle const &rhs)
|
||||||
{
|
{
|
||||||
bool result = lhs.id == rhs.id;
|
bool result = lhs.id == rhs.id;
|
||||||
@ -23,13 +15,6 @@ static bool operator!=(FP_GameEntityHandle const &lhs, FP_GameEntityHandle const
|
|||||||
return result;
|
return result;
|
||||||
}
|
}
|
||||||
|
|
||||||
// TODO(doyle): Use this
|
|
||||||
struct FP_GameCameraM2x3
|
|
||||||
{
|
|
||||||
Dqn_M2x3 model_view;
|
|
||||||
Dqn_M2x3 view_model;
|
|
||||||
};
|
|
||||||
|
|
||||||
static FP_GameCameraM2x3 FP_Game_CameraModelViewM2x3(FP_GameCamera camera)
|
static FP_GameCameraM2x3 FP_Game_CameraModelViewM2x3(FP_GameCamera camera)
|
||||||
{
|
{
|
||||||
FP_GameCameraM2x3 result = {};
|
FP_GameCameraM2x3 result = {};
|
||||||
@ -932,3 +917,226 @@ static void FP_GameRenderScanlines(TELY_Renderer *renderer,
|
|||||||
TELY_Render_LineColourV4(renderer, start, end, TELY_Colour_V4Alpha(TELY_COLOUR_WHITE_V4, 0.1f), scanline_thickness);
|
TELY_Render_LineColourV4(renderer, start, end, TELY_Colour_V4Alpha(TELY_COLOUR_WHITE_V4, 0.1f), scanline_thickness);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static FP_GameCanMoveToPositionResult FP_Game_CanEntityMoveToPosition(FP_Game *game, FP_GameEntityHandle entity_handle, Dqn_V2 delta_pos)
|
||||||
|
{
|
||||||
|
FP_GameCanMoveToPositionResult result = {};
|
||||||
|
FP_GameEntity *entity = FP_Game_GetEntity(game, entity_handle);
|
||||||
|
if (FP_Game_IsNilEntity(entity))
|
||||||
|
return result;
|
||||||
|
|
||||||
|
Dqn_Rect const entity_world_hit_box = FP_Game_CalcEntityWorldHitBox(game, entity->handle);
|
||||||
|
Dqn_V2 const entity_pos = FP_Game_CalcEntityWorldPos(game, entity->handle);
|
||||||
|
Dqn_f32 const SENTINEL_T = 999.f;
|
||||||
|
Dqn_f32 global_earliest_t = SENTINEL_T;
|
||||||
|
Dqn_V2 global_earliest_pos_just_before_collide = {};
|
||||||
|
Dqn_V2 const entity_new_pos = entity_pos + delta_pos;
|
||||||
|
|
||||||
|
if ((entity->flags & FP_GameEntityFlag_NoClip) == 0) {
|
||||||
|
for (FP_GameEntityIterator collider_it = {}; FP_Game_DFSPostOrderWalkEntityTree(game, &collider_it, game->play.root_entity);) {
|
||||||
|
FP_GameEntity *collider = collider_it.entity;
|
||||||
|
if (collider->handle == entity->handle)
|
||||||
|
continue;
|
||||||
|
|
||||||
|
// TODO(doyle): Calculate the list of collidables at the start of the frame
|
||||||
|
if ((collider->flags & FP_GameEntityFlag_NonTraversable) == 0)
|
||||||
|
continue;
|
||||||
|
|
||||||
|
bool skip_colliding_with_player = false;
|
||||||
|
for (FP_GameEntityHandle player : game->play.players) {
|
||||||
|
if (player == collider->handle) {
|
||||||
|
skip_colliding_with_player = true;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (skip_colliding_with_player)
|
||||||
|
continue;
|
||||||
|
|
||||||
|
bool entity_collides_with_collider = true;
|
||||||
|
switch (entity->type) {
|
||||||
|
case FP_EntityType_Catfish: /*FALLTHRU*/
|
||||||
|
case FP_EntityType_Smoochie: /*FALLTHRU*/
|
||||||
|
case FP_EntityType_Clinger: {
|
||||||
|
if (collider->type == FP_EntityType_Smoochie || collider->type == FP_EntityType_Clinger || collider->type == FP_EntityType_Catfish) {
|
||||||
|
entity_collides_with_collider = false;
|
||||||
|
} else if (FP_Entity_IsBuildingForMobs(collider)) {
|
||||||
|
#if 0
|
||||||
|
// NOTE: We disable collision on buildings we have visited to avoid some
|
||||||
|
// problems ...
|
||||||
|
if (FP_SentinelList_Find(&entity->buildings_visited, collider->handle)) {
|
||||||
|
entity_collides_with_collider = false;
|
||||||
|
}
|
||||||
|
#else
|
||||||
|
entity_collides_with_collider = false;
|
||||||
|
#endif
|
||||||
|
} else if (collider->type == FP_EntityType_Heart ||
|
||||||
|
collider->type == FP_EntityType_MerchantGym ||
|
||||||
|
collider->type == FP_EntityType_MerchantTerry ||
|
||||||
|
collider->type == FP_EntityType_MerchantGraveyard ||
|
||||||
|
collider->type == FP_EntityType_MerchantPhoneCompany) {
|
||||||
|
entity_collides_with_collider = false;
|
||||||
|
}
|
||||||
|
} break;
|
||||||
|
|
||||||
|
case FP_EntityType_Terry: {
|
||||||
|
// NOTE: Don't collide with mobs when dashing (e.g. phase through)
|
||||||
|
FP_EntityTerryState state = *DQN_CAST(FP_EntityTerryState *)&entity->action.state;
|
||||||
|
if (state == FP_EntityTerryState_Dash || state == FP_EntityTerryState_DeadGhost) {
|
||||||
|
if (collider->type == FP_EntityType_Smoochie || collider->type == FP_EntityType_Clinger || collider->type == FP_EntityType_Catfish)
|
||||||
|
entity_collides_with_collider = false;
|
||||||
|
}
|
||||||
|
} break;
|
||||||
|
|
||||||
|
case FP_EntityType_Nil: break;
|
||||||
|
case FP_EntityType_MerchantTerry: break;
|
||||||
|
case FP_EntityType_Count: break;
|
||||||
|
case FP_EntityType_ClubTerry: break;
|
||||||
|
case FP_EntityType_Map: break;
|
||||||
|
case FP_EntityType_MerchantGraveyard: break;
|
||||||
|
case FP_EntityType_MerchantGym: break;
|
||||||
|
case FP_EntityType_MerchantPhoneCompany: break;
|
||||||
|
case FP_EntityType_Heart: break;
|
||||||
|
case FP_EntityType_AirportTerry: break;
|
||||||
|
case FP_EntityType_ChurchTerry: break;
|
||||||
|
case FP_EntityType_KennelTerry: break;
|
||||||
|
case FP_EntityType_PhoneMessageProjectile: break;
|
||||||
|
case FP_EntityType_AirportTerryPlane:
|
||||||
|
case FP_EntityType_MobSpawner:
|
||||||
|
case FP_EntityType_PortalMonkey: break;
|
||||||
|
case FP_EntityType_Billboard: break;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!entity_collides_with_collider)
|
||||||
|
continue;
|
||||||
|
|
||||||
|
// 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;
|
||||||
|
|
||||||
|
if (!Dqn_Rect_ContainsPoint(swept_collider_world_hit_box, entity_new_pos))
|
||||||
|
continue;
|
||||||
|
|
||||||
|
Dqn_f32 collider_left_wall_x = swept_collider_world_hit_box.pos.x;
|
||||||
|
Dqn_f32 collider_right_wall_x = swept_collider_world_hit_box.pos.x + swept_collider_world_hit_box.size.w;
|
||||||
|
Dqn_f32 collider_top_wall_y = swept_collider_world_hit_box.pos.y;
|
||||||
|
Dqn_f32 collider_bottom_wall_y = swept_collider_world_hit_box.pos.y + swept_collider_world_hit_box.size.h;
|
||||||
|
|
||||||
|
Dqn_V2 o = entity_pos;
|
||||||
|
Dqn_V2 d = delta_pos;
|
||||||
|
|
||||||
|
// NOTE: Solve collision by determining the 't' value at which
|
||||||
|
// we hit one of the walls of the collider and move the entity
|
||||||
|
// at exactly that point.
|
||||||
|
// O + td = x
|
||||||
|
// td = x - O
|
||||||
|
// t = (x - O) / d
|
||||||
|
|
||||||
|
Dqn_f32 earliest_t = SENTINEL_T;
|
||||||
|
if (d.x != 0.f) {
|
||||||
|
Dqn_f32 left_t = (collider_left_wall_x - o.x) / d.x;
|
||||||
|
Dqn_f32 right_t = (collider_right_wall_x - o.x) / d.x;
|
||||||
|
if (left_t >= 0.f && left_t <= 1.f)
|
||||||
|
earliest_t = DQN_MIN(earliest_t, left_t);
|
||||||
|
if (right_t >= 0.f && right_t <= 1.f)
|
||||||
|
earliest_t = DQN_MIN(earliest_t, right_t);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (d.y != 0.f) {
|
||||||
|
Dqn_f32 top_t = (collider_top_wall_y - o.y) / d.y;
|
||||||
|
Dqn_f32 bottom_t = (collider_bottom_wall_y - o.y) / d.y;
|
||||||
|
if (top_t >= 0.f && top_t <= 1.f)
|
||||||
|
earliest_t = DQN_MIN(earliest_t, top_t);
|
||||||
|
if (bottom_t >= 0.f && bottom_t <= 1.f)
|
||||||
|
earliest_t = DQN_MIN(earliest_t, bottom_t);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (earliest_t < global_earliest_t) {
|
||||||
|
global_earliest_t = earliest_t;
|
||||||
|
global_earliest_pos_just_before_collide = entity_pos + (d * earliest_t);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
result.yes = global_earliest_t == SENTINEL_T;
|
||||||
|
if (!result.yes)
|
||||||
|
result.next_closest_valid_move = delta_pos * global_earliest_t;
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
static void FP_Game_MoveEntity(FP_Game *game, FP_GameEntityHandle entity_handle, Dqn_V2 acceleration_meters_per_s)
|
||||||
|
{
|
||||||
|
// f"(t) = a
|
||||||
|
// f'(t) = at + v
|
||||||
|
// f (t) = 0.5f*a(t^2) + vt + p
|
||||||
|
|
||||||
|
FP_GameEntity *entity = FP_Game_GetEntity(game, entity_handle);
|
||||||
|
if (FP_Game_IsNilEntity(entity))
|
||||||
|
return;
|
||||||
|
|
||||||
|
Dqn_f32 t = DQN_CAST(Dqn_f32)DQN_SQUARED(FP_GAME_PHYSICS_STEP);
|
||||||
|
Dqn_f32 t_squared = DQN_SQUARED(t);
|
||||||
|
|
||||||
|
Dqn_f32 velocity_falloff_coefficient = 0.82f;
|
||||||
|
Dqn_f32 acceleration_feel_good_factor = 15'000.f;
|
||||||
|
Dqn_V2 acceleration = FP_Game_MetersToPixelsV2(game->play, acceleration_meters_per_s) * acceleration_feel_good_factor;
|
||||||
|
entity->velocity = (acceleration * t) + entity->velocity * velocity_falloff_coefficient;
|
||||||
|
|
||||||
|
// NOTE: Zero out velocity with epsilon
|
||||||
|
if (DQN_ABS(entity->velocity.x) < 5.f)
|
||||||
|
entity->velocity.x = 0.f;
|
||||||
|
if (DQN_ABS(entity->velocity.y) < 5.f)
|
||||||
|
entity->velocity.y = 0.f;
|
||||||
|
|
||||||
|
Dqn_V2 const delta_pos = (acceleration * 0.5f * t_squared) + (entity->velocity * t);
|
||||||
|
Dqn_Rect const entity_world_hit_box = FP_Game_CalcEntityWorldHitBox(game, entity->handle);
|
||||||
|
Dqn_V2 const entity_pos = FP_Game_CalcEntityWorldPos(game, entity->handle);
|
||||||
|
Dqn_V2 const entity_new_pos = entity_pos + delta_pos;
|
||||||
|
|
||||||
|
FP_GameCanMoveToPositionResult move_to_result = FP_Game_CanEntityMoveToPosition(game, entity->handle, delta_pos);
|
||||||
|
if (move_to_result.yes) {
|
||||||
|
entity->local_pos += delta_pos;
|
||||||
|
} else {
|
||||||
|
entity->local_pos += move_to_result.next_closest_valid_move;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
static Dqn_Rect FP_Game_GetBuildingPlacementRectForEntity(FP_Game *game, FP_GamePlaceableBuilding placeable_building, FP_GameEntityHandle handle)
|
||||||
|
{
|
||||||
|
Dqn_Rect result = {};
|
||||||
|
FP_GameEntity *entity = FP_Game_GetEntity(game, handle);
|
||||||
|
if (FP_Game_IsNilEntity(entity))
|
||||||
|
return result;
|
||||||
|
|
||||||
|
FP_EntityRenderData render_data = FP_Entity_GetRenderData(game, placeable_building.type, placeable_building.state, entity->direction);
|
||||||
|
Dqn_Rect box = FP_Game_CalcEntityWorldHitBox(game, entity->handle);
|
||||||
|
Dqn_V2 build_p = {};
|
||||||
|
switch (entity->direction) {
|
||||||
|
case FP_GameDirection_Up: {
|
||||||
|
build_p = Dqn_Rect_InterpolatedPoint(box, Dqn_V2_InitNx2(0.5f, 0.f)) - Dqn_V2_InitNx2(0.f, render_data.render_size.h * .5f + 10.f);
|
||||||
|
} break;
|
||||||
|
|
||||||
|
case FP_GameDirection_Down: {
|
||||||
|
build_p = Dqn_Rect_InterpolatedPoint(box, Dqn_V2_InitNx2(0.5f, 1.f)) + Dqn_V2_InitNx2(0.f, render_data.render_size.h * .5f + 10.f);
|
||||||
|
} break;
|
||||||
|
|
||||||
|
case FP_GameDirection_Left: {
|
||||||
|
build_p = Dqn_Rect_InterpolatedPoint(box, Dqn_V2_InitNx2(0.0f, 0.5f)) - Dqn_V2_InitNx2(render_data.render_size.w * .5f + 10.f, 0);
|
||||||
|
} break;
|
||||||
|
|
||||||
|
case FP_GameDirection_Right: {
|
||||||
|
build_p = Dqn_Rect_InterpolatedPoint(box, Dqn_V2_InitNx2(1.f, 0.5f)) + Dqn_V2_InitNx2(render_data.render_size.w * .5f + 10.f, 0);
|
||||||
|
} break;
|
||||||
|
|
||||||
|
case FP_GameDirection_Count: DQN_INVALID_CODE_PATH; break;
|
||||||
|
}
|
||||||
|
|
||||||
|
result.size = render_data.render_size;
|
||||||
|
result.pos = build_p - (render_data.render_size * .5f);
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
@ -158,6 +158,42 @@ enum FP_GameEntityFaction
|
|||||||
FP_GameEntityFaction_Foe,
|
FP_GameEntityFaction_Foe,
|
||||||
};
|
};
|
||||||
|
|
||||||
|
enum FP_GameInGameMenu
|
||||||
|
{
|
||||||
|
FP_GameInGameMenu_Nil,
|
||||||
|
FP_GameInGameMenu_Build,
|
||||||
|
FP_GameInGameMenu_Merchant,
|
||||||
|
};
|
||||||
|
|
||||||
|
struct FP_GameKeyBind
|
||||||
|
{
|
||||||
|
TELY_PlatformInputScanCode scan_code;
|
||||||
|
TELY_PlatformInputGamepadKey gamepad_key;
|
||||||
|
};
|
||||||
|
|
||||||
|
enum FP_GameControlMode
|
||||||
|
{
|
||||||
|
FP_GameControlMode_Keyboard,
|
||||||
|
FP_GameControlMode_Gamepad,
|
||||||
|
};
|
||||||
|
|
||||||
|
struct FP_GameControls
|
||||||
|
{
|
||||||
|
FP_GameControlMode mode;
|
||||||
|
uint32_t gamepad_index;
|
||||||
|
FP_GameKeyBind up;
|
||||||
|
FP_GameKeyBind down;
|
||||||
|
FP_GameKeyBind left;
|
||||||
|
FP_GameKeyBind right;
|
||||||
|
FP_GameKeyBind attack;
|
||||||
|
FP_GameKeyBind buy_building;
|
||||||
|
FP_GameKeyBind buy_upgrade;
|
||||||
|
FP_GameKeyBind range_attack;
|
||||||
|
FP_GameKeyBind build_mode;
|
||||||
|
FP_GameKeyBind strafe;
|
||||||
|
FP_GameKeyBind dash;
|
||||||
|
};
|
||||||
|
|
||||||
struct FP_GameEntity
|
struct FP_GameEntity
|
||||||
{
|
{
|
||||||
FP_GameEntity *next;
|
FP_GameEntity *next;
|
||||||
@ -240,12 +276,23 @@ struct FP_GameEntity
|
|||||||
Dqn_FArray<FP_GameEntityHandle, 3> building_queue;
|
Dqn_FArray<FP_GameEntityHandle, 3> building_queue;
|
||||||
uint64_t building_queue_next_sort_timestamp_ms;
|
uint64_t building_queue_next_sort_timestamp_ms;
|
||||||
FP_GameEntityHandle queued_at_building;
|
FP_GameEntityHandle queued_at_building;
|
||||||
|
|
||||||
|
FP_GameInGameMenu in_game_menu;
|
||||||
|
bool build_mode_can_place_building;
|
||||||
|
Dqn_usize build_mode_building_index;
|
||||||
|
|
||||||
|
FP_GameControls controls;
|
||||||
|
|
||||||
|
Dqn_V2 merchant_terry_menu_pos;
|
||||||
|
Dqn_V2 merchant_graveyard_menu_pos;
|
||||||
|
Dqn_V2 merchant_gym_menu_pos;
|
||||||
|
Dqn_V2 merchant_phone_company_menu_pos;
|
||||||
};
|
};
|
||||||
|
|
||||||
struct FP_GameEntityIterator
|
struct FP_GameEntityIterator
|
||||||
{
|
{
|
||||||
bool init;
|
bool init;
|
||||||
Dqn_usize iteration_count;
|
Dqn_usize iteration_count;
|
||||||
|
|
||||||
FP_GameEntity *entity;
|
FP_GameEntity *entity;
|
||||||
FP_GameEntity *last_visited;
|
FP_GameEntity *last_visited;
|
||||||
@ -284,13 +331,6 @@ enum FP_GameAudio
|
|||||||
FP_GameAudio_Count,
|
FP_GameAudio_Count,
|
||||||
};
|
};
|
||||||
|
|
||||||
enum FP_GameInGameMenu
|
|
||||||
{
|
|
||||||
FP_GameInGameMenu_Nil,
|
|
||||||
FP_GameInGameMenu_Build,
|
|
||||||
FP_GameInGameMenu_Merchant,
|
|
||||||
};
|
|
||||||
|
|
||||||
enum FP_GameState
|
enum FP_GameState
|
||||||
{
|
{
|
||||||
FP_GameState_IntroScreen,
|
FP_GameState_IntroScreen,
|
||||||
@ -313,7 +353,7 @@ struct FP_GamePlay
|
|||||||
FP_GameEntity *entity_free_list;
|
FP_GameEntity *entity_free_list;
|
||||||
FP_GameEntity *map;
|
FP_GameEntity *map;
|
||||||
|
|
||||||
FP_GameEntityHandle player;
|
Dqn_FArray<FP_GameEntityHandle, 2> players;
|
||||||
FP_GameRenderSprite player_merchant_menu;
|
FP_GameRenderSprite player_merchant_menu;
|
||||||
uint64_t player_trigger_purchase_upgrade_timestamp;
|
uint64_t player_trigger_purchase_upgrade_timestamp;
|
||||||
uint64_t player_trigger_purchase_building_timestamp;
|
uint64_t player_trigger_purchase_building_timestamp;
|
||||||
@ -324,11 +364,6 @@ struct FP_GamePlay
|
|||||||
FP_GameEntityHandle merchant_gym;
|
FP_GameEntityHandle merchant_gym;
|
||||||
FP_GameEntityHandle merchant_phone_company;
|
FP_GameEntityHandle merchant_phone_company;
|
||||||
|
|
||||||
Dqn_V2 merchant_terry_menu_pos;
|
|
||||||
Dqn_V2 merchant_graveyard_menu_pos;
|
|
||||||
Dqn_V2 merchant_gym_menu_pos;
|
|
||||||
Dqn_V2 merchant_phone_company_menu_pos;
|
|
||||||
|
|
||||||
FP_GameEntityHandle clicked_entity;
|
FP_GameEntityHandle clicked_entity;
|
||||||
FP_GameEntityHandle hot_entity;
|
FP_GameEntityHandle hot_entity;
|
||||||
FP_GameEntityHandle active_entity;
|
FP_GameEntityHandle active_entity;
|
||||||
@ -336,7 +371,7 @@ struct FP_GamePlay
|
|||||||
FP_GameEntityHandle prev_hot_entity;
|
FP_GameEntityHandle prev_hot_entity;
|
||||||
FP_GameEntityHandle prev_active_entity;
|
FP_GameEntityHandle prev_active_entity;
|
||||||
|
|
||||||
FP_GameEntityHandle camera_tracking_entity;
|
Dqn_FArray<FP_GameEntityHandle, 2> camera_tracking_entity;
|
||||||
FP_GameCamera camera;
|
FP_GameCamera camera;
|
||||||
Dqn_f32 meters_to_pixels;
|
Dqn_f32 meters_to_pixels;
|
||||||
uint64_t clock_ms;
|
uint64_t clock_ms;
|
||||||
@ -346,9 +381,6 @@ struct FP_GamePlay
|
|||||||
bool debug_ui;
|
bool debug_ui;
|
||||||
bool debug_hide_hud;
|
bool debug_hide_hud;
|
||||||
bool god_mode;
|
bool god_mode;
|
||||||
FP_GameInGameMenu in_game_menu;
|
|
||||||
bool build_mode_can_place_building;
|
|
||||||
Dqn_usize build_mode_building_index;
|
|
||||||
|
|
||||||
Dqn_FArray<FP_GameEntityHandle, 4> mob_spawners;
|
Dqn_FArray<FP_GameEntityHandle, 4> mob_spawners;
|
||||||
Dqn_FArray<FP_GameEntityHandle, 3> portal_monkeys;
|
Dqn_FArray<FP_GameEntityHandle, 3> portal_monkeys;
|
||||||
@ -361,27 +393,6 @@ struct FP_GamePlay
|
|||||||
FP_GameState state;
|
FP_GameState state;
|
||||||
};
|
};
|
||||||
|
|
||||||
struct FP_GameKeyBind
|
|
||||||
{
|
|
||||||
TELY_PlatformInputScanCode scan_code;
|
|
||||||
TELY_PlatformInputGamepadKey gamepad_key;
|
|
||||||
};
|
|
||||||
|
|
||||||
struct FP_GameControls
|
|
||||||
{
|
|
||||||
FP_GameKeyBind up;
|
|
||||||
FP_GameKeyBind down;
|
|
||||||
FP_GameKeyBind left;
|
|
||||||
FP_GameKeyBind right;
|
|
||||||
FP_GameKeyBind attack;
|
|
||||||
FP_GameKeyBind buy_building;
|
|
||||||
FP_GameKeyBind buy_upgrade;
|
|
||||||
FP_GameKeyBind range_attack;
|
|
||||||
FP_GameKeyBind build_mode;
|
|
||||||
FP_GameKeyBind strafe;
|
|
||||||
FP_GameKeyBind dash;
|
|
||||||
};
|
|
||||||
|
|
||||||
struct FP_Game
|
struct FP_Game
|
||||||
{
|
{
|
||||||
TELY_AssetFontHandle inter_regular_font_large;
|
TELY_AssetFontHandle inter_regular_font_large;
|
||||||
@ -395,7 +406,6 @@ struct FP_Game
|
|||||||
TELY_AssetSpriteSheet atlas_sprite_sheet;
|
TELY_AssetSpriteSheet atlas_sprite_sheet;
|
||||||
TELY_RFui rfui;
|
TELY_RFui rfui;
|
||||||
FP_GamePlay play;
|
FP_GamePlay play;
|
||||||
FP_GameControls controls;
|
|
||||||
};
|
};
|
||||||
|
|
||||||
struct FP_GameAStarNode
|
struct FP_GameAStarNode
|
||||||
@ -426,3 +436,27 @@ FP_GamePlaceableBuilding const PLACEABLE_BUILDINGS[] = {
|
|||||||
{FP_EntityType_ClubTerry, FP_EntityClubTerryState_Idle},
|
{FP_EntityType_ClubTerry, FP_EntityClubTerryState_Idle},
|
||||||
{FP_EntityType_KennelTerry, FP_EntityKennelTerryState_Idle},
|
{FP_EntityType_KennelTerry, FP_EntityKennelTerryState_Idle},
|
||||||
};
|
};
|
||||||
|
|
||||||
|
struct FP_GameCanMoveToPositionResult
|
||||||
|
{
|
||||||
|
bool yes;
|
||||||
|
Dqn_V2 next_closest_valid_move;
|
||||||
|
};
|
||||||
|
|
||||||
|
struct FP_GameCameraM2x3
|
||||||
|
{
|
||||||
|
Dqn_M2x3 model_view;
|
||||||
|
Dqn_M2x3 view_model;
|
||||||
|
};
|
||||||
|
|
||||||
|
|
||||||
|
Dqn_f32 const FP_GAME_PHYSICS_STEP = 1 / 60.f;
|
||||||
|
|
||||||
|
#define FP_Game_MetersToPixelsNx1(game, val) ((val) * (game).meters_to_pixels)
|
||||||
|
#define FP_Game_MetersToPixelsNx2(game, x, y) (Dqn_V2_InitNx2(x, y) * (game).meters_to_pixels)
|
||||||
|
#define FP_Game_MetersToPixelsV2(game, xy) (xy * (game).meters_to_pixels)
|
||||||
|
|
||||||
|
#define FP_Game_PixelsToMetersNx1(game, val) ((val) * (1.f/(game).meters_to_pixels))
|
||||||
|
#define FP_Game_PixelsToMetersNx2(game, x, y) (Dqn_V2_InitNx2(x, y) * (1.f/(game).meters_to_pixels))
|
||||||
|
#define FP_Game_PixelsToMetersV2(game, xy) (xy * (1.f/(game).meters_to_pixels))
|
||||||
|
|
||||||
|
@ -67,6 +67,7 @@ DQN_MSVC_WARNING_DISABLE(4505) // warning C4505: unreferenced function with inte
|
|||||||
#include "feely_pona_entity.h"
|
#include "feely_pona_entity.h"
|
||||||
#include "feely_pona_game.h"
|
#include "feely_pona_game.h"
|
||||||
|
|
||||||
|
#include "feely_pona_entity.cpp"
|
||||||
#include "feely_pona_game.cpp"
|
#include "feely_pona_game.cpp"
|
||||||
#include "feely_pona_entity_create.cpp"
|
#include "feely_pona_entity_create.cpp"
|
||||||
#include "feely_pona_misc.cpp"
|
#include "feely_pona_misc.cpp"
|
||||||
|
@ -84,6 +84,7 @@ DQN_GCC_WARNING_DISABLE(-Wunused-function)
|
|||||||
#include "feely_pona_entity.h"
|
#include "feely_pona_entity.h"
|
||||||
#include "feely_pona_game.h"
|
#include "feely_pona_game.h"
|
||||||
|
|
||||||
|
#include "feely_pona_entity.cpp"
|
||||||
#include "feely_pona_game.cpp"
|
#include "feely_pona_game.cpp"
|
||||||
#include "feely_pona_entity_create.cpp"
|
#include "feely_pona_entity_create.cpp"
|
||||||
#include "feely_pona_misc.cpp"
|
#include "feely_pona_misc.cpp"
|
||||||
|
BIN
project.rdbg
BIN
project.rdbg
Binary file not shown.
Loading…
Reference in New Issue
Block a user