building_collision_registry.cpp 6.6 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222
  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_building_sizes = {
  14. {"barracks", {4.F, 4.F}},
  15. };
  16. float BuildingCollisionRegistry::s_grid_padding =
  17. BuildingCollisionRegistry::k_default_grid_padding;
  18. auto BuildingCollisionRegistry::instance() -> BuildingCollisionRegistry & {
  19. static BuildingCollisionRegistry instance;
  20. return instance;
  21. }
  22. auto BuildingCollisionRegistry::get_building_size(
  23. const std::string &building_type)
  24. -> BuildingCollisionRegistry::BuildingSize {
  25. auto it = s_building_sizes.find(building_type);
  26. if (it != s_building_sizes.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 &building_type, float center_x,
  33. float center_z, int owner_id) {
  34. if (m_entity_to_index.find(entity_id) != m_entity_to_index.end()) {
  35. update_building_position(entity_id, center_x, center_z);
  36. return;
  37. }
  38. BuildingSize const size = get_building_size(building_type);
  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_entity_to_index[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_entity_to_index.find(entity_id);
  49. if (it == m_entity_to_index.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_entity_to_index[m_buildings[index].entity_id] = index;
  60. }
  61. m_buildings.pop_back();
  62. m_entity_to_index.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_entity_to_index.find(entity_id);
  71. if (it == m_entity_to_index.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_entity_to_index.find(entity_id);
  89. if (it == m_entity_to_index.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 ignore_entity_id) const -> bool {
  97. for (const auto &building : m_buildings) {
  98. if (ignore_entity_id != 0 && building.entity_id == ignore_entity_id) {
  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,
  115. unsigned int ignore_entity_id) const -> bool {
  116. for (const auto &building : m_buildings) {
  117. if (ignore_entity_id != 0 && building.entity_id == ignore_entity_id) {
  118. continue;
  119. }
  120. float const half_width = building.width / 2.0F;
  121. float const half_depth = building.depth / 2.0F;
  122. float const min_x = building.center_x - half_width;
  123. float const max_x = building.center_x + half_width;
  124. float const min_z = building.center_z - half_depth;
  125. float const max_z = building.center_z + half_depth;
  126. float const closest_x = std::clamp(x, min_x, max_x);
  127. float const closest_z = std::clamp(z, min_z, max_z);
  128. float const dx = x - closest_x;
  129. float const dz = z - closest_z;
  130. float const distance_sq = dx * dx + dz * dz;
  131. if (distance_sq <= radius * radius) {
  132. return true;
  133. }
  134. }
  135. return false;
  136. }
  137. auto BuildingCollisionRegistry::get_occupied_grid_cells(
  138. const BuildingFootprint &footprint,
  139. float grid_cell_size) -> std::vector<std::pair<int, int>> {
  140. std::vector<std::pair<int, int>> cells;
  141. float const half_width = footprint.width / 2.0F;
  142. float const half_depth = footprint.depth / 2.0F;
  143. float const padding = s_grid_padding;
  144. int const min_grid_x = static_cast<int>(
  145. std::floor((footprint.center_x - half_width - padding) / grid_cell_size));
  146. int const max_grid_x = static_cast<int>(
  147. std::ceil((footprint.center_x + half_width + padding) / grid_cell_size));
  148. int const min_grid_z = static_cast<int>(
  149. std::floor((footprint.center_z - half_depth - padding) / grid_cell_size));
  150. int const max_grid_z = static_cast<int>(
  151. std::ceil((footprint.center_z + half_depth + padding) / grid_cell_size));
  152. for (int gx = min_grid_x; gx < max_grid_x; ++gx) {
  153. for (int gz = min_grid_z; gz < max_grid_z; ++gz) {
  154. cells.emplace_back(gx, gz);
  155. }
  156. }
  157. return cells;
  158. }
  159. void BuildingCollisionRegistry::clear() {
  160. m_buildings.clear();
  161. m_entity_to_index.clear();
  162. }
  163. void BuildingCollisionRegistry::set_grid_padding(float padding) {
  164. s_grid_padding = padding;
  165. if (auto *pf = CommandService::get_pathfinder()) {
  166. pf->mark_obstacles_dirty();
  167. }
  168. }
  169. auto BuildingCollisionRegistry::get_grid_padding() -> float {
  170. return s_grid_padding;
  171. }
  172. } // namespace Game::Systems