#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 extra_cosmetic_anims; FP_SentinelList 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 spawner_waypoints; FP_SentinelList 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 shapes; FP_GameEntityHandle projectile_owner; uint64_t ttl_end_timestamp; FP_SentinelList 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 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_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 entities; Dqn_FArray parent_entity_stack; FP_GameEntity *root_entity; FP_GameEntity *entity_free_list; FP_GameEntity *map; Dqn_FArray 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 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 mob_spawners; Dqn_FArray 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))