| 132 | } |
| 133 | |
| 134 | void ServerWeather::spawnWeatherProjectiles(float dt) { |
| 135 | if (!m_currentWeatherType || m_clientVisibleRegions.empty()) |
| 136 | return; |
| 137 | |
| 138 | auto projectileDatabase = Root::singleton().projectileDatabase(); |
| 139 | |
| 140 | // TODO: The complexity of this method is TERRIBLE, if this becomes a problem |
| 141 | // for any reason there are large numbers of ways to make this much better, |
| 142 | // but this was the lazy, simple-ish, and clear (hah) way. |
| 143 | |
| 144 | for (auto const& projectileConfig : m_currentWeatherType->projectiles) { |
| 145 | // Gather all the tops of the client regions together with the proper |
| 146 | // padding, splitting at the world wrap boundary. |
| 147 | List<pair<Vec2I, int>> baseSpawnRegions; |
| 148 | for (auto const& clientRegion : m_clientVisibleRegions) { |
| 149 | Vec2I baseRegion = {clientRegion.xMin() - projectileConfig.spawnHorizontalPad, clientRegion.xMax() + projectileConfig.spawnHorizontalPad}; |
| 150 | int height = clientRegion.yMax(); |
| 151 | for (auto const& region : m_worldGeometry.splitXRegion(baseRegion)) |
| 152 | baseSpawnRegions.append({region, height}); |
| 153 | } |
| 154 | |
| 155 | // We are going to have to eliminate vertically redundant sections of |
| 156 | // spawning regions, so gather up every left and right edge of a spawn |
| 157 | // region is a "split point" |
| 158 | List<int> splitPoints; |
| 159 | for (auto const& baseSpawnRegion : baseSpawnRegions) { |
| 160 | splitPoints.append(baseSpawnRegion.first[0]); |
| 161 | splitPoints.append(baseSpawnRegion.first[1]); |
| 162 | } |
| 163 | |
| 164 | // Split every spawn region on every split point. |
| 165 | List<pair<Vec2I, int>> splitSpawnRegions; |
| 166 | for (auto const& baseSpawnRegion : baseSpawnRegions) { |
| 167 | List<Vec2I> regions = {baseSpawnRegion.first}; |
| 168 | for (auto splitPoint : splitPoints) { |
| 169 | auto prevRegions = take(regions); |
| 170 | for (auto const& region : prevRegions) { |
| 171 | if (splitPoint > region[0] && splitPoint < region[1]) { |
| 172 | regions.append({region[0], splitPoint}); |
| 173 | regions.append({splitPoint, region[1]}); |
| 174 | } else { |
| 175 | regions.append(region); |
| 176 | } |
| 177 | } |
| 178 | } |
| 179 | for (auto const& region : regions) |
| 180 | splitSpawnRegions.append({region, baseSpawnRegion.second}); |
| 181 | } |
| 182 | |
| 183 | // Sort the split spawn regions by leftmost point then height, preparing to |
| 184 | // remove the lower overlapping sections. |
| 185 | sort(splitSpawnRegions, |
| 186 | [](pair<Vec2I, int> const& lhs, pair<Vec2I, int> rhs) { |
| 187 | return tie(lhs.first[0], lhs.second) < tie(rhs.first[0], rhs.second); |
| 188 | }); |
| 189 | |
| 190 | // For each region, at this point, if the region to the right shares the |
| 191 | // same starting X, because we've split up each region on each possible |
nothing calls this directly
no test coverage detected