combat_dust_renderer.cpp 10 KB

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