9 Commits

14 changed files with 1641 additions and 1369 deletions
BIN
View File
Binary file not shown.
BIN
View File
Binary file not shown.
Binary file not shown.
Binary file not shown.
+1 -1
+477 -457
View File
File diff suppressed because it is too large Load Diff
+1
View File
@@ -52,6 +52,7 @@ struct FP_GlobalAnimations
Dqn_String8 heart = DQN_STRING8("heart");
Dqn_String8 heart_bleed = DQN_STRING8("heart_bleed");
Dqn_String8 icon_attack = DQN_STRING8("icon_attack");
Dqn_String8 icon_health = DQN_STRING8("icon_health");
Dqn_String8 icon_money = DQN_STRING8("icon_money");
Dqn_String8 icon_phone = DQN_STRING8("icon_phone");
+292
View 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;
}
-287
View File
@@ -3,293 +3,6 @@
#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 = 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)
{
FP_GameEntity *entity = FP_Game_GetEntity(game, handle);
+223 -15
View File
@@ -3,14 +3,6 @@
#include "feely_pona_unity.h"
#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)
{
bool result = lhs.id == rhs.id;
@@ -23,13 +15,6 @@ static bool operator!=(FP_GameEntityHandle const &lhs, FP_GameEntityHandle const
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)
{
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);
}
}
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;
}
+67 -34
View File
@@ -158,6 +158,34 @@ enum FP_GameEntityFaction
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;
};
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_GameEntity
{
FP_GameEntity *next;
@@ -191,11 +219,12 @@ struct FP_GameEntity
// NOTE: The entity hit box is positioned at the center of the entity.
Dqn_V2 local_hit_box_size;
Dqn_V2 local_hit_box_offset;
Dqn_f32 trauma01;
Dqn_V2 attack_box_size;
Dqn_V2 attack_box_offset;
bool attack_processed;
Dqn_usize hit_on_frame;
Dqn_usize hit_on_clock_ms;
bool is_dying;
uint64_t last_attack_timestamp;
uint64_t attack_cooldown_ms;
@@ -239,6 +268,17 @@ struct FP_GameEntity
Dqn_FArray<FP_GameEntityHandle, 3> building_queue;
uint64_t building_queue_next_sort_timestamp_ms;
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
@@ -261,8 +301,6 @@ struct FP_GameCamera
Dqn_V2 world_pos_target;
Dqn_f32 rotate_rads;
Dqn_V2 scale;
Dqn_f32 shake_intensity;
Dqn_f64 shake_duration;
};
enum FP_GameAudio
@@ -285,13 +323,6 @@ enum FP_GameAudio
FP_GameAudio_Count,
};
enum FP_GameInGameMenu
{
FP_GameInGameMenu_Nil,
FP_GameInGameMenu_Build,
FP_GameInGameMenu_Merchant,
};
enum FP_GameState
{
FP_GameState_IntroScreen,
@@ -314,7 +345,7 @@ struct FP_GamePlay
FP_GameEntity *entity_free_list;
FP_GameEntity *map;
FP_GameEntityHandle player;
Dqn_FArray<FP_GameEntityHandle, 2> players;
FP_GameRenderSprite player_merchant_menu;
uint64_t player_trigger_purchase_upgrade_timestamp;
uint64_t player_trigger_purchase_building_timestamp;
@@ -332,6 +363,7 @@ struct FP_GamePlay
FP_GameEntityHandle prev_hot_entity;
FP_GameEntityHandle prev_active_entity;
Dqn_FArray<FP_GameEntityHandle, 2> camera_tracking_entity;
FP_GameCamera camera;
Dqn_f32 meters_to_pixels;
uint64_t clock_ms;
@@ -341,9 +373,6 @@ struct FP_GamePlay
bool debug_ui;
bool debug_hide_hud;
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, 3> portal_monkeys;
@@ -356,25 +385,6 @@ struct FP_GamePlay
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 range_attack;
FP_GameKeyBind build_mode;
FP_GameKeyBind strafe;
FP_GameKeyBind dash;
};
struct FP_Game
{
TELY_AssetFontHandle inter_regular_font_large;
@@ -388,7 +398,6 @@ struct FP_Game
TELY_AssetSpriteSheet atlas_sprite_sheet;
TELY_RFui rfui;
FP_GamePlay play;
FP_GameControls controls;
};
struct FP_GameAStarNode
@@ -419,3 +428,27 @@ FP_GamePlaceableBuilding const PLACEABLE_BUILDINGS[] = {
{FP_EntityType_ClubTerry, FP_EntityClubTerryState_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))
+1
View File
@@ -67,6 +67,7 @@ DQN_MSVC_WARNING_DISABLE(4505) // warning C4505: unreferenced function with inte
#include "feely_pona_entity.h"
#include "feely_pona_game.h"
#include "feely_pona_entity.cpp"
#include "feely_pona_game.cpp"
#include "feely_pona_entity_create.cpp"
#include "feely_pona_misc.cpp"
+1
View File
@@ -84,6 +84,7 @@ DQN_GCC_WARNING_DISABLE(-Wunused-function)
#include "feely_pona_entity.h"
#include "feely_pona_game.h"
#include "feely_pona_entity.cpp"
#include "feely_pona_game.cpp"
#include "feely_pona_entity_create.cpp"
#include "feely_pona_misc.cpp"
BIN
View File
Binary file not shown.