building_collision_registry.cpp 6.5 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221
  1. #include "building_collision_registry.h"
  2. #include "command_service.h"
  3. #include "pathfinding.h"
  4. #include <algorithm>
  5. #include <cmath>
  6. #include <cstddef>
  7. #include <map>
  8. #include <string>
  9. #include <utility>
  10. #include <vector>
  11. namespace Game::Systems {
  12. const std::map<std::string, BuildingCollisionRegistry::BuildingSize>
  13. BuildingCollisionRegistry::s_buildingSizes = {
  14. {"barracks", {4.F, 4.F}},
  15. };
  16. float BuildingCollisionRegistry::s_gridPadding =
  17. BuildingCollisionRegistry::kDefaultGridPadding;
  18. auto BuildingCollisionRegistry::instance() -> BuildingCollisionRegistry & {
  19. static BuildingCollisionRegistry instance;
  20. return instance;
  21. }
  22. auto BuildingCollisionRegistry::get_building_size(
  23. const std::string &buildingType)
  24. -> BuildingCollisionRegistry::BuildingSize {
  25. auto it = s_buildingSizes.find(buildingType);
  26. if (it != s_buildingSizes.end()) {
  27. return it->second;
  28. }
  29. return {2.0F, 2.0F};
  30. }
  31. void BuildingCollisionRegistry::register_building(
  32. unsigned int entity_id, const std::string &buildingType, float center_x,
  33. float center_z, int owner_id) {
  34. if (m_entityToIndex.find(entity_id) != m_entityToIndex.end()) {
  35. update_building_position(entity_id, center_x, center_z);
  36. return;
  37. }
  38. BuildingSize const size = get_building_size(buildingType);
  39. BuildingFootprint const footprint(center_x, center_z, size.width, size.depth,
  40. owner_id, entity_id);
  41. m_buildings.push_back(footprint);
  42. m_entityToIndex[entity_id] = m_buildings.size() - 1;
  43. if (auto *pf = CommandService::get_pathfinder()) {
  44. pf->mark_building_region_dirty(center_x, center_z, size.width, size.depth);
  45. }
  46. }
  47. void BuildingCollisionRegistry::unregister_building(unsigned int entity_id) {
  48. auto it = m_entityToIndex.find(entity_id);
  49. if (it == m_entityToIndex.end()) {
  50. return;
  51. }
  52. size_t const index = it->second;
  53. float const center_x = m_buildings[index].center_x;
  54. float const center_z = m_buildings[index].center_z;
  55. float const width = m_buildings[index].width;
  56. float const depth = m_buildings[index].depth;
  57. if (index != m_buildings.size() - 1) {
  58. std::swap(m_buildings[index], m_buildings.back());
  59. m_entityToIndex[m_buildings[index].entity_id] = index;
  60. }
  61. m_buildings.pop_back();
  62. m_entityToIndex.erase(entity_id);
  63. if (auto *pf = CommandService::get_pathfinder()) {
  64. pf->mark_building_region_dirty(center_x, center_z, width, depth);
  65. }
  66. }
  67. void BuildingCollisionRegistry::update_building_position(unsigned int entity_id,
  68. float center_x,
  69. float center_z) {
  70. auto it = m_entityToIndex.find(entity_id);
  71. if (it == m_entityToIndex.end()) {
  72. return;
  73. }
  74. size_t const index = it->second;
  75. float const old_x = m_buildings[index].center_x;
  76. float const old_z = m_buildings[index].center_z;
  77. float const width = m_buildings[index].width;
  78. float const depth = m_buildings[index].depth;
  79. m_buildings[index].center_x = center_x;
  80. m_buildings[index].center_z = center_z;
  81. if (auto *pf = CommandService::get_pathfinder()) {
  82. pf->mark_building_region_dirty(old_x, old_z, width, depth);
  83. pf->mark_building_region_dirty(center_x, center_z, width, depth);
  84. }
  85. }
  86. void BuildingCollisionRegistry::update_building_owner(unsigned int entity_id,
  87. int owner_id) {
  88. auto it = m_entityToIndex.find(entity_id);
  89. if (it == m_entityToIndex.end()) {
  90. return;
  91. }
  92. size_t const index = it->second;
  93. m_buildings[index].owner_id = owner_id;
  94. }
  95. auto BuildingCollisionRegistry::is_point_in_building(
  96. float x, float z, unsigned int ignoreEntityId) const -> bool {
  97. for (const auto &building : m_buildings) {
  98. if (ignoreEntityId != 0 && building.entity_id == ignoreEntityId) {
  99. continue;
  100. }
  101. float const half_width = building.width / 2.0F;
  102. float const half_depth = building.depth / 2.0F;
  103. float const min_x = building.center_x - half_width;
  104. float const max_x = building.center_x + half_width;
  105. float const min_z = building.center_z - half_depth;
  106. float const max_z = building.center_z + half_depth;
  107. if (x >= min_x && x <= max_x && z >= min_z && z <= max_z) {
  108. return true;
  109. }
  110. }
  111. return false;
  112. }
  113. auto BuildingCollisionRegistry::is_circle_overlapping_building(
  114. float x, float z, float radius, unsigned int ignoreEntityId) const -> bool {
  115. for (const auto &building : m_buildings) {
  116. if (ignoreEntityId != 0 && building.entity_id == ignoreEntityId) {
  117. continue;
  118. }
  119. float const half_width = building.width / 2.0F;
  120. float const half_depth = building.depth / 2.0F;
  121. float const min_x = building.center_x - half_width;
  122. float const max_x = building.center_x + half_width;
  123. float const min_z = building.center_z - half_depth;
  124. float const max_z = building.center_z + half_depth;
  125. float const closest_x = std::clamp(x, min_x, max_x);
  126. float const closest_z = std::clamp(z, min_z, max_z);
  127. float const dx = x - closest_x;
  128. float const dz = z - closest_z;
  129. float const distance_sq = dx * dx + dz * dz;
  130. if (distance_sq <= radius * radius) {
  131. return true;
  132. }
  133. }
  134. return false;
  135. }
  136. auto BuildingCollisionRegistry::get_occupied_grid_cells(
  137. const BuildingFootprint &footprint,
  138. float grid_cell_size) -> std::vector<std::pair<int, int>> {
  139. std::vector<std::pair<int, int>> cells;
  140. float const half_width = footprint.width / 2.0F;
  141. float const half_depth = footprint.depth / 2.0F;
  142. float const padding = s_gridPadding;
  143. int const min_grid_x = static_cast<int>(
  144. std::floor((footprint.center_x - half_width - padding) / grid_cell_size));
  145. int const max_grid_x = static_cast<int>(
  146. std::ceil((footprint.center_x + half_width + padding) / grid_cell_size));
  147. int const min_grid_z = static_cast<int>(
  148. std::floor((footprint.center_z - half_depth - padding) / grid_cell_size));
  149. int const max_grid_z = static_cast<int>(
  150. std::ceil((footprint.center_z + half_depth + padding) / grid_cell_size));
  151. for (int gx = min_grid_x; gx < max_grid_x; ++gx) {
  152. for (int gz = min_grid_z; gz < max_grid_z; ++gz) {
  153. cells.emplace_back(gx, gz);
  154. }
  155. }
  156. return cells;
  157. }
  158. void BuildingCollisionRegistry::clear() {
  159. m_buildings.clear();
  160. m_entityToIndex.clear();
  161. }
  162. void BuildingCollisionRegistry::set_grid_padding(float padding) {
  163. s_gridPadding = padding;
  164. if (auto *pf = CommandService::get_pathfinder()) {
  165. pf->mark_obstacles_dirty();
  166. }
  167. }
  168. auto BuildingCollisionRegistry::get_grid_padding() -> float {
  169. return s_gridPadding;
  170. }
  171. } // namespace Game::Systems