component.h 13 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518
  1. #pragma once
  2. #include "../systems/nation_id.h"
  3. #include "../units/spawn_type.h"
  4. #include "../units/troop_type.h"
  5. #include "entity.h"
  6. #include <algorithm>
  7. #include <array>
  8. #include <cstdint>
  9. #include <optional>
  10. #include <string>
  11. #include <utility>
  12. #include <vector>
  13. namespace Engine::Core {
  14. namespace Defaults {
  15. inline constexpr int kUnitDefaultHealth = 100;
  16. inline constexpr float kUnitDefaultVisionRange = 12.0F;
  17. inline constexpr float kAttackDefaultRange = 2.0F;
  18. inline constexpr int kAttackDefaultDamage = 10;
  19. inline constexpr float kAttackMeleeRange = 1.5F;
  20. inline constexpr float kAttackHeightTolerance = 2.0F;
  21. inline constexpr float kProductionDefaultBuildTime = 4.0F;
  22. inline constexpr int kProductionMaxUnits = 10000;
  23. inline constexpr float kCaptureRequiredTime = 15.0F;
  24. inline constexpr float kHoldStandUpDuration = 2.0F;
  25. inline constexpr float kGuardDefaultRadius = 10.0F;
  26. inline constexpr float kGuardReturnThreshold = 1.0F;
  27. } // namespace Defaults
  28. class TransformComponent : public Component {
  29. public:
  30. TransformComponent(float x = 0.0F, float y = 0.0F, float z = 0.0F,
  31. float rot_x = 0.0F, float rot_y = 0.0F, float rot_z = 0.0F,
  32. float scale_x = 1.0F, float scale_y = 1.0F,
  33. float scale_z = 1.0F)
  34. : position{x, y, z}, rotation{rot_x, rot_y, rot_z},
  35. scale{scale_x, scale_y, scale_z} {}
  36. struct Vec3 {
  37. float x, y, z;
  38. };
  39. Vec3 position;
  40. Vec3 rotation;
  41. Vec3 scale;
  42. float desired_yaw = 0.0F;
  43. bool has_desired_yaw = false;
  44. };
  45. class RenderableComponent : public Component {
  46. public:
  47. enum class MeshKind { None, Quad, Plane, Cube, Capsule, Ring };
  48. RenderableComponent(std::string mesh_path, std::string texture_path)
  49. : mesh_path(std::move(mesh_path)), texture_path(std::move(texture_path)) {
  50. color.fill(1.0F);
  51. }
  52. std::string mesh_path;
  53. std::string texture_path;
  54. std::string renderer_id;
  55. bool visible{true};
  56. MeshKind mesh{MeshKind::Cube};
  57. std::array<float, 3> color{};
  58. };
  59. class UnitComponent : public Component {
  60. public:
  61. UnitComponent(int health = Defaults::kUnitDefaultHealth,
  62. int max_health = Defaults::kUnitDefaultHealth,
  63. float speed = 1.0F,
  64. float vision = Defaults::kUnitDefaultVisionRange)
  65. : health(health), max_health(max_health), speed(speed),
  66. vision_range(vision) {}
  67. int health;
  68. int max_health;
  69. float speed;
  70. Game::Units::SpawnType spawn_type{Game::Units::SpawnType::Archer};
  71. int owner_id{0};
  72. float vision_range;
  73. Game::Systems::NationID nation_id{Game::Systems::NationID::RomanRepublic};
  74. };
  75. class MovementComponent : public Component {
  76. public:
  77. MovementComponent() = default;
  78. bool has_target{false};
  79. float target_x{0.0F}, target_y{0.0F};
  80. float goal_x{0.0F}, goal_y{0.0F};
  81. float vx{0.0F}, vz{0.0F};
  82. std::vector<std::pair<float, float>> path;
  83. std::size_t path_index{0};
  84. bool path_pending{false};
  85. std::uint64_t pending_request_id{0};
  86. float repath_cooldown{0.0F};
  87. float last_goal_x{0.0F}, last_goal_y{0.0F};
  88. float time_since_last_path_request{0.0F};
  89. float last_position_x{0.0F}, last_position_z{0.0F};
  90. float time_stuck{0.0F};
  91. float unstuck_cooldown{0.0F};
  92. void clear_path() {
  93. path.clear();
  94. path_index = 0;
  95. }
  96. [[nodiscard]] auto has_waypoints() const -> bool {
  97. return path_index < path.size();
  98. }
  99. [[nodiscard]] auto
  100. current_waypoint() const -> const std::pair<float, float> & {
  101. return path[path_index];
  102. }
  103. void advance_waypoint() {
  104. if (path_index < path.size()) {
  105. ++path_index;
  106. }
  107. }
  108. [[nodiscard]] auto remaining_waypoints() const -> std::size_t {
  109. return path.size() > path_index ? path.size() - path_index : 0;
  110. }
  111. void validate_path_index() {
  112. if (path_index > path.size()) {
  113. path_index = path.size();
  114. }
  115. }
  116. };
  117. class AttackComponent : public Component {
  118. public:
  119. enum class CombatMode { Ranged, Melee, Auto };
  120. AttackComponent(float range = Defaults::kAttackDefaultRange,
  121. int damage = Defaults::kAttackDefaultDamage,
  122. float cooldown = 1.0F)
  123. : range(range), damage(damage), cooldown(cooldown),
  124. melee_range(Defaults::kAttackMeleeRange), melee_damage(damage),
  125. melee_cooldown(cooldown),
  126. max_height_difference(Defaults::kAttackHeightTolerance) {}
  127. float range;
  128. int damage;
  129. float cooldown;
  130. float time_since_last{0.0F};
  131. float melee_range;
  132. int melee_damage;
  133. float melee_cooldown;
  134. CombatMode preferred_mode{CombatMode::Auto};
  135. CombatMode current_mode{CombatMode::Ranged};
  136. bool can_melee{true};
  137. bool can_ranged{false};
  138. float max_height_difference;
  139. bool in_melee_lock{false};
  140. EntityID melee_lock_target_id{0};
  141. [[nodiscard]] auto is_in_melee_range(float distance,
  142. float height_diff) const -> bool {
  143. return distance <= melee_range && height_diff <= max_height_difference;
  144. }
  145. [[nodiscard]] auto is_in_ranged_range(float distance) const -> bool {
  146. return distance <= range && distance > melee_range;
  147. }
  148. [[nodiscard]] auto get_current_damage() const -> int {
  149. return (current_mode == CombatMode::Melee) ? melee_damage : damage;
  150. }
  151. [[nodiscard]] auto get_current_cooldown() const -> float {
  152. return (current_mode == CombatMode::Melee) ? melee_cooldown : cooldown;
  153. }
  154. [[nodiscard]] auto get_current_range() const -> float {
  155. return (current_mode == CombatMode::Melee) ? melee_range : range;
  156. }
  157. };
  158. class AttackTargetComponent : public Component {
  159. public:
  160. AttackTargetComponent() = default;
  161. EntityID target_id{0};
  162. bool should_chase{false};
  163. };
  164. enum class CombatAnimationState : std::uint8_t {
  165. Idle,
  166. Advance,
  167. WindUp,
  168. Strike,
  169. Impact,
  170. Recover,
  171. Reposition
  172. };
  173. class CombatStateComponent : public Component {
  174. public:
  175. CombatStateComponent() = default;
  176. CombatAnimationState animation_state{CombatAnimationState::Idle};
  177. float state_time{0.0F};
  178. float state_duration{0.0F};
  179. float attack_offset{0.0F};
  180. std::uint8_t attack_variant{0};
  181. bool is_hit_paused{false};
  182. float hit_pause_remaining{0.0F};
  183. static constexpr float kHitPauseDuration = 0.05F;
  184. static constexpr float kAdvanceDuration = 0.12F;
  185. static constexpr float kWindUpDuration = 0.15F;
  186. static constexpr float kStrikeDuration = 0.20F;
  187. static constexpr float kImpactDuration = 0.08F;
  188. static constexpr float kRecoverDuration = 0.25F;
  189. static constexpr float kRepositionDuration = 0.15F;
  190. static constexpr std::uint8_t kMaxAttackVariants = 3;
  191. };
  192. class HitFeedbackComponent : public Component {
  193. public:
  194. HitFeedbackComponent() = default;
  195. bool is_reacting{false};
  196. float reaction_time{0.0F};
  197. float reaction_intensity{0.0F};
  198. float knockback_x{0.0F};
  199. float knockback_z{0.0F};
  200. static constexpr float kReactionDuration = 0.25F;
  201. static constexpr float kMaxKnockback = 0.15F;
  202. };
  203. class PatrolComponent : public Component {
  204. public:
  205. PatrolComponent() = default;
  206. std::vector<std::pair<float, float>> waypoints;
  207. size_t current_waypoint{0};
  208. bool patrolling{false};
  209. };
  210. } // namespace Engine::Core
  211. namespace Engine::Core {
  212. class BuildingComponent : public Component {
  213. public:
  214. BuildingComponent() = default;
  215. Game::Systems::NationID original_nation_id{
  216. Game::Systems::NationID::RomanRepublic};
  217. };
  218. class ProductionComponent : public Component {
  219. public:
  220. ProductionComponent()
  221. : build_time(Defaults::kProductionDefaultBuildTime),
  222. max_units(Defaults::kProductionMaxUnits) {}
  223. bool in_progress{false};
  224. float build_time;
  225. float time_remaining{0.0F};
  226. int produced_count{0};
  227. int max_units;
  228. Game::Units::TroopType product_type{Game::Units::TroopType::Archer};
  229. float rally_x{0.0F}, rally_z{0.0F};
  230. bool rally_set{false};
  231. int villager_cost{1};
  232. std::vector<Game::Units::TroopType> production_queue;
  233. };
  234. class AIControlledComponent : public Component {
  235. public:
  236. AIControlledComponent() = default;
  237. };
  238. class CaptureComponent : public Component {
  239. public:
  240. CaptureComponent() : required_time(Defaults::kCaptureRequiredTime) {}
  241. int capturing_player_id{-1};
  242. float capture_progress{0.0F};
  243. float required_time;
  244. bool is_being_captured{false};
  245. };
  246. class BuilderProductionComponent : public Component {
  247. public:
  248. BuilderProductionComponent() = default;
  249. bool in_progress{false};
  250. float build_time{10.0F};
  251. float time_remaining{0.0F};
  252. std::string product_type{};
  253. bool construction_complete{false};
  254. bool has_construction_site{false};
  255. float construction_site_x{0.0F};
  256. float construction_site_z{0.0F};
  257. bool at_construction_site{false};
  258. bool is_placement_preview{false};
  259. bool bypass_movement_active{false};
  260. float bypass_target_x{0.0F};
  261. float bypass_target_z{0.0F};
  262. };
  263. class PendingRemovalComponent : public Component {
  264. public:
  265. PendingRemovalComponent() = default;
  266. };
  267. class HoldModeComponent : public Component {
  268. public:
  269. HoldModeComponent() : stand_up_duration(Defaults::kHoldStandUpDuration) {}
  270. bool active{true};
  271. float exit_cooldown{0.0F};
  272. float stand_up_duration;
  273. };
  274. class GuardModeComponent : public Component {
  275. public:
  276. GuardModeComponent() : guard_radius(Defaults::kGuardDefaultRadius) {}
  277. bool active{true};
  278. EntityID guarded_entity_id{0};
  279. float guard_position_x{0.0F};
  280. float guard_position_z{0.0F};
  281. float guard_radius;
  282. bool returning_to_guard_position{false};
  283. bool has_guard_target{false};
  284. };
  285. class HealerComponent : public Component {
  286. public:
  287. HealerComponent() = default;
  288. float healing_range{8.0F};
  289. int healing_amount{5};
  290. float healing_cooldown{2.0F};
  291. float time_since_last_heal{0.0F};
  292. bool is_healing_active{false};
  293. float healing_target_x{0.0F};
  294. float healing_target_z{0.0F};
  295. };
  296. class CatapultLoadingComponent : public Component {
  297. public:
  298. enum class LoadingState { Idle, Loading, ReadyToFire, Firing };
  299. CatapultLoadingComponent() = default;
  300. LoadingState state{LoadingState::Idle};
  301. float loading_time{0.0F};
  302. float loading_duration{2.0F};
  303. float firing_time{0.0F};
  304. float firing_duration{0.5F};
  305. EntityID target_id{0};
  306. float target_locked_x{0.0F};
  307. float target_locked_y{0.0F};
  308. float target_locked_z{0.0F};
  309. bool target_position_locked{false};
  310. [[nodiscard]] auto get_loading_progress() const -> float {
  311. if (loading_duration <= 0.0F) {
  312. return 1.0F;
  313. }
  314. return std::min(loading_time / loading_duration, 1.0F);
  315. }
  316. [[nodiscard]] auto get_firing_progress() const -> float {
  317. if (firing_duration <= 0.0F) {
  318. return 1.0F;
  319. }
  320. return std::min(firing_time / firing_duration, 1.0F);
  321. }
  322. [[nodiscard]] auto is_loading() const -> bool {
  323. return state == LoadingState::Loading;
  324. }
  325. [[nodiscard]] auto is_ready_to_fire() const -> bool {
  326. return state == LoadingState::ReadyToFire;
  327. }
  328. [[nodiscard]] auto is_firing() const -> bool {
  329. return state == LoadingState::Firing;
  330. }
  331. };
  332. class FormationModeComponent : public Component {
  333. public:
  334. FormationModeComponent() = default;
  335. bool active{false};
  336. float formation_center_x{0.0F};
  337. float formation_center_z{0.0F};
  338. };
  339. class StaminaComponent : public Component {
  340. public:
  341. static constexpr float kRunSpeedMultiplier = 1.5F;
  342. static constexpr float kMinStaminaToStartRun = 10.0F;
  343. static constexpr float kDefaultMaxStamina = 100.0F;
  344. static constexpr float kDefaultRegenRate = 10.0F;
  345. static constexpr float kDefaultDepletionRate = 20.0F;
  346. StaminaComponent() noexcept = default;
  347. float stamina{kDefaultMaxStamina};
  348. float max_stamina{kDefaultMaxStamina};
  349. float regen_rate{kDefaultRegenRate};
  350. float depletion_rate{kDefaultDepletionRate};
  351. bool is_running{false};
  352. bool run_requested{false};
  353. [[nodiscard]] auto get_stamina_ratio() const noexcept -> float {
  354. return max_stamina > 0.0F ? stamina / max_stamina : 0.0F;
  355. }
  356. [[nodiscard]] auto can_start_running() const noexcept -> bool {
  357. return stamina >= kMinStaminaToStartRun;
  358. }
  359. [[nodiscard]] auto has_stamina() const noexcept -> bool {
  360. return stamina > 0.0F;
  361. }
  362. void deplete(float delta_time) noexcept {
  363. stamina = std::max(0.0F, stamina - depletion_rate * delta_time);
  364. }
  365. void regenerate(float delta_time) noexcept {
  366. stamina = std::min(max_stamina, stamina + regen_rate * delta_time);
  367. }
  368. void initialize_from_stats(float new_max_stamina, float new_regen_rate,
  369. float new_depletion_rate) noexcept {
  370. max_stamina = new_max_stamina;
  371. stamina = new_max_stamina;
  372. regen_rate = new_regen_rate;
  373. depletion_rate = new_depletion_rate;
  374. }
  375. };
  376. class TerrainContextComponent : public Component {
  377. public:
  378. TerrainContextComponent() = default;
  379. bool is_on_bridge{false};
  380. bool is_at_hill_entrance{false};
  381. float audio_cooldown{0.0F};
  382. static constexpr float kAudioCooldownTime = 5.0F;
  383. };
  384. class ElephantComponent : public Component {
  385. public:
  386. enum class ChargeState { Idle, Charging, Trampling, Recovering };
  387. ElephantComponent() = default;
  388. ChargeState charge_state{ChargeState::Idle};
  389. float charge_speed_multiplier{1.8F};
  390. float charge_duration{0.0F};
  391. float charge_cooldown{0.0F};
  392. float trample_radius{2.5F};
  393. int trample_damage{40};
  394. float trample_damage_accumulator{0.0F};
  395. bool is_panicked{false};
  396. float panic_duration{0.0F};
  397. };
  398. class ElephantStompImpactComponent : public Component {
  399. public:
  400. struct ImpactRecord {
  401. float x;
  402. float z;
  403. float time;
  404. };
  405. ElephantStompImpactComponent() = default;
  406. std::vector<ImpactRecord> impacts;
  407. };
  408. class HomeComponent : public Component {
  409. public:
  410. HomeComponent() = default;
  411. int population_contribution{50};
  412. Engine::Core::EntityID nearest_barracks_id{0};
  413. float update_cooldown{0.0F};
  414. };
  415. } // namespace Engine::Core