combat_dust_renderer.cpp 8.3 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266
  1. #include "combat_dust_renderer.h"
  2. #include "../../game/core/component.h"
  3. #include "../../game/core/world.h"
  4. #include "../../game/systems/camera_visibility_service.h"
  5. #include "../../game/systems/projectile_system.h"
  6. #include "../../game/systems/stone_projectile.h"
  7. #include "../scene_renderer.h"
  8. #include <algorithm>
  9. #include <unordered_set>
  10. namespace Render::GL {
  11. namespace {
  12. constexpr float kDustRadius = 2.0F;
  13. constexpr float kDustIntensity = 0.6F;
  14. constexpr float kDustYOffset = 0.05F;
  15. constexpr float kDustColorR = 0.6F;
  16. constexpr float kDustColorG = 0.55F;
  17. constexpr float kDustColorB = 0.45F;
  18. constexpr float kVisibilityCheckRadius = 3.0F;
  19. constexpr float kFlameRadius = 3.0F;
  20. constexpr float kFlameIntensity = 0.8F;
  21. constexpr float kFlameYOffset = 0.5F;
  22. constexpr float kFlameColorR = 1.0F;
  23. constexpr float kFlameColorG = 0.4F;
  24. constexpr float kFlameColorB = 0.1F;
  25. constexpr float kBuildingHealthThreshold = 0.5F;
  26. constexpr float kStoneImpactRadius = 0.6F;
  27. constexpr float kStoneImpactIntensity = 1.5F;
  28. constexpr float kStoneImpactColorR = 0.75F;
  29. constexpr float kStoneImpactColorG = 0.65F;
  30. constexpr float kStoneImpactColorB = 0.45F;
  31. constexpr float kStoneImpactYOffset = 0.1F;
  32. constexpr float kStoneImpactDuration = 10.0F;
  33. constexpr float kStoneImpactTriggerProgress = 0.99F;
  34. std::unordered_set<const void *> g_tracked_projectiles;
  35. } // namespace
  36. auto StoneImpactTracker::instance() -> StoneImpactTracker & {
  37. static StoneImpactTracker instance;
  38. return instance;
  39. }
  40. void StoneImpactTracker::add_impact(const QVector3D &position,
  41. float current_time, float radius,
  42. float intensity) {
  43. StoneImpactEffect effect;
  44. effect.position = position;
  45. effect.start_time = current_time;
  46. effect.duration = kStoneImpactDuration;
  47. effect.radius = radius;
  48. effect.intensity = intensity;
  49. m_impacts.push_back(effect);
  50. }
  51. void StoneImpactTracker::update(float current_time) {
  52. m_impacts.erase(
  53. std::remove_if(m_impacts.begin(), m_impacts.end(),
  54. [current_time](const StoneImpactEffect &impact) {
  55. return (current_time - impact.start_time) >
  56. impact.duration;
  57. }),
  58. m_impacts.end());
  59. }
  60. void render_combat_dust(Renderer *renderer, ResourceManager *,
  61. Engine::Core::World *world) {
  62. if (renderer == nullptr || world == nullptr) {
  63. return;
  64. }
  65. float animation_time = renderer->get_animation_time();
  66. auto &visibility = Game::Systems::CameraVisibilityService::instance();
  67. auto units = world->get_entities_with<Engine::Core::AttackComponent>();
  68. for (auto *unit : units) {
  69. if (unit->has_component<Engine::Core::PendingRemovalComponent>()) {
  70. continue;
  71. }
  72. auto *transform = unit->get_component<Engine::Core::TransformComponent>();
  73. auto *attack = unit->get_component<Engine::Core::AttackComponent>();
  74. auto *unit_comp = unit->get_component<Engine::Core::UnitComponent>();
  75. if (transform == nullptr || attack == nullptr) {
  76. continue;
  77. }
  78. if (unit_comp != nullptr && unit_comp->health <= 0) {
  79. continue;
  80. }
  81. if (!attack->in_melee_lock) {
  82. continue;
  83. }
  84. if (!visibility.is_entity_visible(transform->position.x,
  85. transform->position.z,
  86. kVisibilityCheckRadius)) {
  87. continue;
  88. }
  89. QVector3D position(transform->position.x, kDustYOffset,
  90. transform->position.z);
  91. QVector3D color(kDustColorR, kDustColorG, kDustColorB);
  92. renderer->combat_dust(position, color, kDustRadius, kDustIntensity,
  93. animation_time);
  94. }
  95. auto builders =
  96. world->get_entities_with<Engine::Core::BuilderProductionComponent>();
  97. for (auto *builder : builders) {
  98. if (builder->has_component<Engine::Core::PendingRemovalComponent>()) {
  99. continue;
  100. }
  101. auto *transform =
  102. builder->get_component<Engine::Core::TransformComponent>();
  103. auto *production =
  104. builder->get_component<Engine::Core::BuilderProductionComponent>();
  105. auto *unit_comp = builder->get_component<Engine::Core::UnitComponent>();
  106. if (transform == nullptr || production == nullptr) {
  107. continue;
  108. }
  109. if (unit_comp != nullptr && unit_comp->health <= 0) {
  110. continue;
  111. }
  112. if (!production->in_progress) {
  113. continue;
  114. }
  115. if (!visibility.is_entity_visible(transform->position.x,
  116. transform->position.z,
  117. kVisibilityCheckRadius)) {
  118. continue;
  119. }
  120. QVector3D position(transform->position.x, kDustYOffset,
  121. transform->position.z);
  122. QVector3D color(kDustColorR, kDustColorG, kDustColorB);
  123. renderer->combat_dust(position, color, kDustRadius, kDustIntensity,
  124. animation_time);
  125. }
  126. auto buildings = world->get_entities_with<Engine::Core::BuildingComponent>();
  127. for (auto *building : buildings) {
  128. if (building->has_component<Engine::Core::PendingRemovalComponent>()) {
  129. continue;
  130. }
  131. auto *transform =
  132. building->get_component<Engine::Core::TransformComponent>();
  133. auto *unit_comp = building->get_component<Engine::Core::UnitComponent>();
  134. if (transform == nullptr || unit_comp == nullptr) {
  135. continue;
  136. }
  137. if (unit_comp->health <= 0) {
  138. continue;
  139. }
  140. float health_ratio = static_cast<float>(unit_comp->health) /
  141. static_cast<float>(unit_comp->max_health);
  142. if (health_ratio > kBuildingHealthThreshold) {
  143. continue;
  144. }
  145. if (!visibility.is_entity_visible(transform->position.x,
  146. transform->position.z,
  147. kVisibilityCheckRadius)) {
  148. continue;
  149. }
  150. float flame_intensity = kFlameIntensity * (1.0F - health_ratio);
  151. QVector3D position(transform->position.x, kFlameYOffset,
  152. transform->position.z);
  153. QVector3D color(kFlameColorR, kFlameColorG, kFlameColorB);
  154. renderer->building_flame(position, color, kFlameRadius, flame_intensity,
  155. animation_time);
  156. }
  157. auto *projectile_sys = world->get_system<Game::Systems::ProjectileSystem>();
  158. auto &impact_tracker = StoneImpactTracker::instance();
  159. if (projectile_sys != nullptr) {
  160. const auto &projectiles = projectile_sys->projectiles();
  161. for (const auto &projectile : projectiles) {
  162. auto *stone_proj = dynamic_cast<const Game::Systems::StoneProjectile *>(
  163. projectile.get());
  164. if (stone_proj == nullptr) {
  165. continue;
  166. }
  167. float progress = stone_proj->get_progress();
  168. if (progress < kStoneImpactTriggerProgress) {
  169. continue;
  170. }
  171. const void *proj_ptr = static_cast<const void *>(stone_proj);
  172. if (g_tracked_projectiles.find(proj_ptr) != g_tracked_projectiles.end()) {
  173. continue;
  174. }
  175. g_tracked_projectiles.insert(proj_ptr);
  176. QVector3D impact_pos = stone_proj->get_end();
  177. if (!visibility.is_entity_visible(impact_pos.x(), impact_pos.z(),
  178. kVisibilityCheckRadius * 2.0F)) {
  179. continue;
  180. }
  181. QVector3D position(impact_pos.x(), impact_pos.y() + kStoneImpactYOffset,
  182. impact_pos.z());
  183. impact_tracker.add_impact(position, animation_time, kStoneImpactRadius,
  184. kStoneImpactIntensity);
  185. }
  186. }
  187. std::erase_if(g_tracked_projectiles, [projectile_sys](const void *ptr) {
  188. if (projectile_sys == nullptr) {
  189. return true;
  190. }
  191. const auto &projectiles = projectile_sys->projectiles();
  192. for (const auto &p : projectiles) {
  193. if (static_cast<const void *>(p.get()) == ptr) {
  194. return false;
  195. }
  196. }
  197. return true;
  198. });
  199. impact_tracker.update(animation_time);
  200. QVector3D color(kStoneImpactColorR, kStoneImpactColorG, kStoneImpactColorB);
  201. for (const auto &impact : impact_tracker.impacts()) {
  202. if (!visibility.is_entity_visible(impact.position.x(), impact.position.z(),
  203. impact.radius)) {
  204. continue;
  205. }
  206. float impact_time = animation_time - impact.start_time;
  207. renderer->stone_impact(impact.position, color, impact.radius,
  208. impact.intensity, impact_time);
  209. }
  210. }
  211. } // namespace Render::GL