551 lines
19 KiB
C
551 lines
19 KiB
C
#if defined(_CLANGD)
|
|
#pragma once
|
|
#include "feely_pona_unity.h"
|
|
#endif
|
|
|
|
enum FP_GameEntityFlag
|
|
{
|
|
FP_GameEntityFlag_Clickable = 1 << 0,
|
|
FP_GameEntityFlag_MoveByKeyboard = 1 << 1,
|
|
FP_GameEntityFlag_MoveByMouse = 1 << 2,
|
|
FP_GameEntityFlag_MoveByGamepad = 1 << 3,
|
|
FP_GameEntityFlag_DrawHitBox = 1 << 4,
|
|
FP_GameEntityFlag_DeriveHitBoxFromChildrenBoundingBox = 1 << 5,
|
|
FP_GameEntityFlag_NonTraversable = 1 << 6,
|
|
FP_GameEntityFlag_MobSpawnerWaypoint = 1 << 7,
|
|
FP_GameEntityFlag_Aggros = 1 << 8,
|
|
FP_GameEntityFlag_Attackable = 1 << 9,
|
|
FP_GameEntityFlag_RespondsToBuildings = 1 << 10,
|
|
FP_GameEntityFlag_OccupiedInBuilding = 1 << 11,
|
|
FP_GameEntityFlag_CameraTracking = 1 << 12,
|
|
FP_GameEntityFlag_BuildZone = 1 << 13,
|
|
FP_GameEntityFlag_TTL = 1 << 14,
|
|
FP_GameEntityFlag_Friendly = 1 << 15,
|
|
FP_GameEntityFlag_Foe = 1 << 16,
|
|
FP_GameEntityFlag_NoClip = 1 << 17,
|
|
FP_GameEntityFlag_RecoversHP = 1 << 18,
|
|
FP_GameEntityFlag_HasShadow = 1 << 19,
|
|
};
|
|
|
|
enum FP_GameShapeType
|
|
{
|
|
FP_GameShapeType_None,
|
|
FP_GameShapeType_Circle,
|
|
FP_GameShapeType_Rect,
|
|
FP_GameShapeType_Line,
|
|
};
|
|
|
|
struct FP_GameShape
|
|
{
|
|
FP_GameShapeType type;
|
|
Dqn_V2 p1;
|
|
Dqn_V2 p2;
|
|
Dqn_V4 colour;
|
|
Dqn_f32 line_thickness;
|
|
Dqn_f32 circle_radius;
|
|
TELY_RenderShapeMode render_mode;
|
|
};
|
|
|
|
const Dqn_usize FP_GAME_ENTITY_HANDLE_GENERATION_MASK_BIT_COUNT = 16;
|
|
const Dqn_usize FP_GAME_ENTITY_HANDLE_INDEX_MASK = DQN_USIZE_MAX >> FP_GAME_ENTITY_HANDLE_GENERATION_MASK_BIT_COUNT;
|
|
const Dqn_usize FP_GAME_ENTITY_HANDLE_INDEX_MAX = FP_GAME_ENTITY_HANDLE_INDEX_MASK;
|
|
const Dqn_usize FP_GAME_ENTITY_HANDLE_GENERATION_MASK = ~FP_GAME_ENTITY_HANDLE_INDEX_MASK;
|
|
const Dqn_usize FP_GAME_ENTITY_HANDLE_GENERATION_RSHIFT = (sizeof(Dqn_usize) * 8) - FP_GAME_ENTITY_HANDLE_GENERATION_MASK_BIT_COUNT;
|
|
const Dqn_usize FP_GAME_ENTITY_HANDLE_GENERATION_MAX = FP_GAME_ENTITY_HANDLE_GENERATION_MASK >> FP_GAME_ENTITY_HANDLE_GENERATION_RSHIFT;
|
|
|
|
struct FP_GameEntityHandle
|
|
{
|
|
Dqn_usize id;
|
|
};
|
|
|
|
enum FP_GameWaypointArrive
|
|
{
|
|
// Considered arrived when within 1 meter of the target (`value` is ignored).
|
|
FP_GameWaypointArrive_Default,
|
|
|
|
// If set, we consider the entity as arriving at the waypoint when it's
|
|
// distance to the target is:
|
|
//
|
|
// `arrived = dist <= (entity_hit_box_size * value)`
|
|
//
|
|
// A value of 0 for example means we are considered arrived when the entity
|
|
// is positioned exactly on top of the target's position.
|
|
FP_GameWaypointArrive_WhenWithinEntitySize,
|
|
};
|
|
|
|
enum FP_GameWaypointType
|
|
{
|
|
FP_GameWaypointType_At, // Move to the specified entity
|
|
FP_GameWaypointType_Side, // Move to the side of the entity specified by the direction
|
|
FP_GameWaypointType_ClosestSide, // Move to the side of the entity closest to us
|
|
FP_GameWaypointType_Queue, // Queue at the target entity
|
|
};
|
|
|
|
enum FP_GameDirection
|
|
{
|
|
FP_GameDirection_Up,
|
|
FP_GameDirection_Down,
|
|
FP_GameDirection_Left,
|
|
FP_GameDirection_Right,
|
|
FP_GameDirection_Count,
|
|
};
|
|
|
|
struct FP_GameWaypoint
|
|
{
|
|
FP_GameWaypointType type;
|
|
FP_GameDirection type_direction; // Used if type is `FP_GameWaypointType_Side`
|
|
FP_GameEntityHandle entity; // The entity to move to
|
|
FP_GameWaypointArrive arrive;
|
|
Dqn_f32 value; // Used for `arrive` threshold
|
|
Dqn_V2 offset; // Used if type is `FP_GameWaypointType_Offset`
|
|
};
|
|
|
|
struct FP_GameEntitySpawnList
|
|
{
|
|
FP_GameEntityHandle entity;
|
|
FP_GameEntitySpawnList *next;
|
|
FP_GameEntitySpawnList *prev;
|
|
};
|
|
|
|
struct FP_GameEntityActionSprite
|
|
{
|
|
TELY_AssetSpriteSheet *sheet;
|
|
TELY_AssetSpriteAnimation *anim;
|
|
};
|
|
|
|
uint64_t const FP_GAME_ENTITY_ACTION_INFINITE_TIMER = UINT64_MAX;
|
|
struct FP_GameEntityAction
|
|
{
|
|
uint32_t state;
|
|
uint32_t next_state;
|
|
TELY_AssetAnimatedSprite sprite;
|
|
bool sprite_play_once;
|
|
Dqn_f32 sprite_alpha;
|
|
uint64_t started_at_clock_ms;
|
|
uint64_t end_at_clock_ms;
|
|
};
|
|
|
|
struct FP_GameRenderSprite
|
|
{
|
|
Dqn_V2 offset;
|
|
TELY_AssetAnimatedSprite asset;
|
|
bool loop;
|
|
FP_Meters height;
|
|
uint64_t started_at_clock_ms;
|
|
};
|
|
|
|
struct FP_GameInventory
|
|
{
|
|
uint32_t airports_base_price;
|
|
uint32_t clubs_base_price;
|
|
uint32_t kennels_base_price;
|
|
uint32_t churchs_base_price;
|
|
|
|
uint32_t stamina_base_price;
|
|
uint32_t health_base_price;
|
|
uint32_t mobile_plan_base_price;
|
|
uint32_t attack_base_price;
|
|
|
|
uint8_t airports;
|
|
uint8_t clubs;
|
|
uint8_t kennels;
|
|
uint8_t churchs;
|
|
};
|
|
|
|
enum FP_GameEntityFaction
|
|
{
|
|
FP_GameEntityFaction_Nil,
|
|
FP_GameEntityFaction_Friendly,
|
|
FP_GameEntityFaction_Foe,
|
|
};
|
|
|
|
enum FP_GameInGameMenu
|
|
{
|
|
FP_GameInGameMenu_Nil,
|
|
FP_GameInGameMenu_Build,
|
|
FP_GameInGameMenu_Merchant,
|
|
};
|
|
|
|
struct FP_GameKeyBind
|
|
{
|
|
TELY_InputScanKey scan_key;
|
|
TELY_InputGamepadKey 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 move_building_ui_cursor_left;
|
|
FP_GameKeyBind move_building_ui_cursor_right;
|
|
FP_GameKeyBind range_attack;
|
|
FP_GameKeyBind build_mode;
|
|
FP_GameKeyBind strafe;
|
|
FP_GameKeyBind dash;
|
|
};
|
|
|
|
struct FP_GameEntity
|
|
{
|
|
FP_GameEntity *next;
|
|
FP_GameEntity *prev;
|
|
FP_GameEntity *first_child;
|
|
FP_GameEntity *last_child;
|
|
FP_GameEntity *parent;
|
|
|
|
FP_EntityType type;
|
|
Dqn_Str8 name;
|
|
FP_GameEntityHandle handle;
|
|
|
|
// The target size to render the sprite in meters. The width of the sprite
|
|
// is scaled uniformly with respect to the height.
|
|
FP_Meters sprite_height;
|
|
FP_GameEntityAction action;
|
|
FP_Meters base_acceleration_per_s;
|
|
Dqn_V2 constant_acceleration_per_s;
|
|
Dqn_V2 velocity;
|
|
|
|
// Extra animations that are to be executed, but, don't affect the state
|
|
// of the entity. For example, when Smoochie attacks, we have a heart
|
|
// animation that is a seperate sprite that will play out.
|
|
Dqn_FArray<FP_GameRenderSprite, 2> extra_cosmetic_anims;
|
|
|
|
FP_SentinelList<FP_GameWaypoint> waypoints;
|
|
FP_GameEntityHandle aggro_slot[FP_GameDirection_Count];
|
|
|
|
FP_GameEntityHandle building_patron;
|
|
|
|
// 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;
|
|
|
|
FP_GameDirection attack_direction;
|
|
Dqn_V2 attack_box_size;
|
|
Dqn_V2 attack_box_offset;
|
|
bool attack_processed;
|
|
Dqn_usize hit_on_clock_ms;
|
|
bool is_dying;
|
|
uint64_t last_attack_timestamp;
|
|
uint64_t attack_cooldown_ms;
|
|
|
|
Dqn_FArray<Dqn_V2, 8> spawner_waypoints;
|
|
FP_SentinelList<FP_GameEntityHandle> spawn_list;
|
|
uint64_t next_spawn_timestamp_s;
|
|
uint64_t spawn_cap;
|
|
|
|
uint64_t flags;
|
|
FP_GameDirection direction;
|
|
Dqn_V2 local_pos;
|
|
Dqn_f64 alive_time_s;
|
|
Dqn_FArray<FP_GameShape, 4> shapes;
|
|
FP_GameEntityHandle projectile_owner;
|
|
uint64_t ttl_end_timestamp;
|
|
FP_SentinelList<FP_GameEntityHandle> buildings_visited;
|
|
|
|
Dqn_usize terry_mobile_data_plan;
|
|
Dqn_usize terry_mobile_data_plan_cap;
|
|
Dqn_usize clinger_next_dash_timestamp;
|
|
bool smoochie_has_teleported;
|
|
Dqn_usize smoochie_teleport_timestamp;
|
|
|
|
Dqn_usize coins;
|
|
FP_GameInventory inventory;
|
|
uint32_t hp;
|
|
uint32_t hp_cap;
|
|
uint32_t stamina;
|
|
uint32_t stamina_cap;
|
|
uint32_t hp_recover_every_n_ticks;
|
|
uint32_t base_attack;
|
|
|
|
bool is_drunk;
|
|
Dqn_usize drunk_particles_end_ms;
|
|
|
|
bool converted_faction;
|
|
FP_GameEntityFaction faction;
|
|
|
|
uint32_t count_of_entities_targetting_sides[FP_GameDirection_Count];
|
|
FP_GameEntityHandle carried_monkey;
|
|
|
|
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
|
|
{
|
|
bool init;
|
|
Dqn_usize iteration_count;
|
|
|
|
FP_GameEntity *entity;
|
|
FP_GameEntity *last_visited;
|
|
|
|
FP_GameEntity *entity_parent;
|
|
FP_GameEntity *entity_next;
|
|
FP_GameEntity *entity_first_child;
|
|
};
|
|
|
|
struct FP_GameCamera
|
|
{
|
|
Dqn_V2 size;
|
|
Dqn_V2 world_pos;
|
|
Dqn_V2 world_pos_target;
|
|
Dqn_f32 rotate_rads;
|
|
Dqn_V2 scale;
|
|
};
|
|
|
|
enum FP_GameAudio
|
|
{
|
|
FP_GameAudio_TerryHit,
|
|
FP_GameAudio_Smooch,
|
|
FP_GameAudio_Woosh,
|
|
FP_GameAudio_Ching,
|
|
FP_GameAudio_Church,
|
|
FP_GameAudio_Plane,
|
|
FP_GameAudio_Club,
|
|
FP_GameAudio_Dog,
|
|
FP_GameAudio_MerchantTerry,
|
|
FP_GameAudio_MerchantGhost,
|
|
FP_GameAudio_MerchantGym,
|
|
FP_GameAudio_MerchantPhone,
|
|
FP_GameAudio_Message,
|
|
FP_GameAudio_Monkey,
|
|
FP_GameAudio_PortalDestroy,
|
|
FP_GameAudio_GameStart,
|
|
FP_GameAudio_PerryStart,
|
|
FP_GameAudio_Ambience1,
|
|
FP_GameAudio_Ambience2,
|
|
FP_GameAudio_Music1,
|
|
FP_GameAudio_Music2,
|
|
FP_GameAudio_RelaxMusic,
|
|
FP_GameAudio_StressMusic,
|
|
FP_GameAudio_Count,
|
|
};
|
|
|
|
enum FP_GameState
|
|
{
|
|
FP_GameState_IntroScreen,
|
|
FP_GameState_Tutorial,
|
|
FP_GameState_Play,
|
|
FP_GameState_Pause,
|
|
FP_GameState_WinGame,
|
|
FP_GameState_LoseGame,
|
|
};
|
|
|
|
enum FP_GamePerryJoined
|
|
{
|
|
FP_GamePerryJoins_NotYet,
|
|
FP_GamePerryJoins_Enters,
|
|
FP_GamePerryJoins_PostEnter,
|
|
};
|
|
|
|
struct FP_Particle
|
|
{
|
|
Dqn_Str8 anim_name;
|
|
bool alive;
|
|
Dqn_V2 pos;
|
|
Dqn_V2 velocity;
|
|
Dqn_V4 colour_begin;
|
|
Dqn_V4 colour_end;
|
|
Dqn_usize start_ms;
|
|
Dqn_usize end_ms;
|
|
};
|
|
|
|
enum FP_GameStateTutorial
|
|
{
|
|
FP_GameStateTutorial_ShowPlayer,
|
|
FP_GameStateTutorial_ShowPlayerWait,
|
|
FP_GameStateTutorial_ShowPortalOne,
|
|
FP_GameStateTutorial_ShowPortalOneWait,
|
|
FP_GameStateTutorial_ShowPortalTwo,
|
|
FP_GameStateTutorial_ShowPortalTwoWait,
|
|
FP_GameStateTutorial_ShowPortalThree,
|
|
FP_GameStateTutorial_ShowPortalThreeWait,
|
|
FP_GameStateTutorial_ShowBillboardBuild,
|
|
FP_GameStateTutorial_ShowBillboardBuildWait,
|
|
FP_GameStateTutorial_Count,
|
|
};
|
|
|
|
Dqn_V2 const FP_MONKEY_SPAWN_LOCATIONS[] =
|
|
{
|
|
Dqn_V2_InitNx2(-592, 538),
|
|
Dqn_V2_InitNx2(-1503, -568),
|
|
Dqn_V2_InitNx2(1890, 1150),
|
|
Dqn_V2_InitNx2(1815, -1192),
|
|
Dqn_V2_InitNx2(520, 1230),
|
|
Dqn_V2_InitNx2(-934, -238),
|
|
Dqn_V2_InitNx2(1915, 15),
|
|
Dqn_V2_InitNx2(247, 560),
|
|
Dqn_V2_InitNx2(-290, -1120),
|
|
Dqn_V2_InitNx2(1126, -646),
|
|
};
|
|
|
|
struct FP_GamePlay
|
|
{
|
|
TELY_ChunkPool *chunk_pool;
|
|
uint16_t tile_size;
|
|
Dqn_f32 delta_s_accumulator;
|
|
Dqn_Arena arena;
|
|
Dqn_VArray<FP_GameEntity> entities;
|
|
Dqn_FArray<FP_GameEntityHandle, 8> parent_entity_stack;
|
|
|
|
FP_GameEntity *root_entity;
|
|
FP_GameEntity *entity_free_list;
|
|
FP_GameEntity *map;
|
|
|
|
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;
|
|
FP_GameEntityHandle heart;
|
|
|
|
FP_GameEntityHandle merchant_terry;
|
|
FP_GameEntityHandle merchant_graveyard;
|
|
FP_GameEntityHandle merchant_gym;
|
|
FP_GameEntityHandle merchant_phone_company;
|
|
FP_GameEntityHandle billboard_build;
|
|
|
|
FP_GameEntityHandle clicked_entity;
|
|
FP_GameEntityHandle hot_entity;
|
|
FP_GameEntityHandle active_entity;
|
|
FP_GameEntityHandle prev_clicked_entity;
|
|
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;
|
|
uint64_t update_counter;
|
|
Dqn_PCG32 rng;
|
|
|
|
bool debug_ui;
|
|
bool debug_hide_hud;
|
|
bool debug_hide_bounding_rectangles;
|
|
bool debug_disable_mobs;
|
|
bool god_mode;
|
|
|
|
Dqn_FArray<FP_GameEntityHandle, 4> mob_spawners;
|
|
Dqn_FArray<FP_GameEntityHandle, 3> portal_monkeys;
|
|
|
|
uint32_t current_wave;
|
|
uint32_t enemies_per_wave;
|
|
uint32_t enemies_spawned_this_wave;
|
|
uint64_t wave_cooldown_timestamp_ms;
|
|
|
|
Dqn_f32 global_camera_trauma01;
|
|
FP_GameState state;
|
|
FP_GamePerryJoined perry_joined;
|
|
Dqn_f32 perry_join_bg_alpha;
|
|
Dqn_f32 perry_join_flash_alpha;
|
|
bool perry_join_splash_screen_shake_triggered;
|
|
Dqn_V2 perry_join_splash_screen_pos;
|
|
uint64_t perry_join_splash_screen_end_ms;
|
|
Dqn_V2 perry_join_splash_pos_offset;
|
|
|
|
FP_Particle particles[256];
|
|
uint32_t particle_next_index;
|
|
|
|
FP_GameStateTutorial tutorial_state;
|
|
uint64_t tutorial_wait_end_time_ms;
|
|
|
|
uint8_t monkey_spawn_shuffled_list[DQN_ARRAY_UCOUNT(FP_MONKEY_SPAWN_LOCATIONS)];
|
|
uint8_t monkey_spawn_count;
|
|
};
|
|
|
|
struct FP_Game
|
|
{
|
|
TELY_AssetFontHandle inter_regular_font;
|
|
TELY_AssetFontHandle inter_italic_font;
|
|
TELY_AssetFontHandle jetbrains_mono_font;
|
|
TELY_AssetFontHandle talkco_font;
|
|
|
|
TELY_AssetAudioHandle audio[FP_GameAudio_Count];
|
|
TELY_AudioPlaybackHandle bg_music1;
|
|
TELY_AudioPlaybackHandle bg_music2;
|
|
TELY_AssetSpriteSheet atlas_sprite_sheet;
|
|
TELY_RFui rfui;
|
|
FP_GamePlay play;
|
|
Dqn_Arena *frame_arena;
|
|
uint16_t font_size;
|
|
uint16_t large_font_size;
|
|
uint16_t large_talkco_font_size;
|
|
uint16_t xlarge_talkco_font_size;
|
|
};
|
|
|
|
struct FP_GameAStarNode
|
|
{
|
|
Dqn_usize cost;
|
|
Dqn_usize heuristic;
|
|
Dqn_V2I tile;
|
|
Dqn_V2I came_from;
|
|
bool non_traversable;
|
|
};
|
|
|
|
struct FP_GameFindClosestEntityResult
|
|
{
|
|
FP_GameEntityHandle entity;
|
|
Dqn_f32 dist_squared;
|
|
Dqn_V2 pos;
|
|
};
|
|
|
|
struct FP_GamePlaceableBuilding
|
|
{
|
|
FP_EntityType type;
|
|
uint32_t state;
|
|
};
|
|
|
|
FP_GamePlaceableBuilding const PLACEABLE_BUILDINGS[] = {
|
|
{FP_EntityType_AirportTerry, FP_EntityAirportTerryState_Idle},
|
|
{FP_EntityType_ChurchTerry, FP_EntityChurchTerryState_Idle},
|
|
{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))
|
|
|