Ticket #3204: territory_neighbours.3.diff
File territory_neighbours.3.diff, 26.1 KB (added by , 9 years ago) |
---|
-
binaries/data/mods/public/simulation/components/Capturable.js
16 16 // Cache this value 17 17 this.maxCp = +this.template.CapturePoints; 18 18 this.cp = []; 19 this.StartRegenTimer();20 19 }; 21 20 22 21 //// Interface functions //// … … 56 55 */ 57 56 Capturable.prototype.Reduce = function(amount, playerID) 58 57 { 58 if (amount <= 0) 59 return 0; 60 59 61 var cmpOwnership = Engine.QueryInterface(this.entity, IID_Ownership); 60 62 if (!cmpOwnership || cmpOwnership.GetOwner() == -1) 61 63 return 0; … … 69 71 if (cmpFogging) 70 72 cmpFogging.Activate(); 71 73 72 var enemiesFilter = function(v, i) { return v > 0 && cmpPlayerSource.IsEnemy(i); }; 73 var numberOfEnemies = this.cp.filter(enemiesFilter).length; 74 var numberOfEnemies = this.cp.filter((v, i) => v > 0 && cmpPlayerSource.IsEnemy(i)).length; 74 75 75 76 if (numberOfEnemies == 0) 76 77 return 0; … … 88 89 } 89 90 90 91 // give all cp taken to the player 91 var takenCp = this.maxCp - this.cp.reduce( function(a, b) { return a + b; });92 var takenCp = this.maxCp - this.cp.reduce((a, b) => a + b); 92 93 this.cp[playerID] += takenCp; 93 94 94 this.StartRegenTimer(); 95 96 Engine.PostMessage(this.entity, MT_CapturePointsChanged, { "capturePoints": this.cp }) 97 98 if (this.cp[cmpOwnership.GetOwner()] > 0) 99 return takenCp; 100 101 // if all cp has been taken from the owner, convert it to the best player 102 var bestPlayer = 0; 103 for (let i in this.cp) 104 if (this.cp[i] >= this.cp[bestPlayer]) 105 bestPlayer = +i; 106 107 cmpOwnership.SetOwner(bestPlayer); 108 95 this.CheckTimer(); 96 this.RegisterCapturePointsChanged(); 109 97 return takenCp; 110 98 }; 111 99 … … 128 116 129 117 //// Private functions //// 130 118 119 /** 120 * this has to be called whenever the capture points are changed. 121 * It notifies other components of the change, and switches ownership when needed 122 */ 123 Capturable.prototype.RegisterCapturePointsChanged = function() 124 { 125 Engine.PostMessage(this.entity, MT_CapturePointsChanged, { "capturePoints": this.cp }) 126 127 var cmpOwnership = Engine.QueryInterface(this.entity, IID_Ownership); 128 if (!cmpOwnership) 129 return 130 var owner = cmpOwnership.GetOwner(); 131 if (owner == -1 || this.cp[owner] > 0) 132 return; 133 134 // if all cp has been taken from the owner, convert it to the best player 135 var bestPlayer = 0; 136 for (let i in this.cp) 137 if (this.cp[i] >= this.cp[bestPlayer]) 138 bestPlayer = +i; 139 140 cmpOwnership.SetOwner(bestPlayer); 141 }; 142 131 143 Capturable.prototype.GetRegenRate = function() 132 144 { 133 145 var regenRate = +this.template.RegenRate; 134 146 regenRate = ApplyValueModificationsToEntity("Capturable/RegenRate", regenRate, this.entity); 135 147 136 var cmpTerritoryDecay = Engine.QueryInterface(this.entity, IID_TerritoryDecay);137 if (cmpTerritoryDecay && cmpTerritoryDecay.IsDecaying())138 var territoryDecayRate = cmpTerritoryDecay.GetDecayRate();139 else140 var territoryDecayRate = 0;141 142 148 var cmpGarrisonHolder = Engine.QueryInterface(this.entity, IID_GarrisonHolder); 143 149 if (cmpGarrisonHolder) 144 150 var garrisonRegenRate = this.GetGarrisonRegenRate() * cmpGarrisonHolder.GetEntities().length; … … 145 151 else 146 152 var garrisonRegenRate = 0; 147 153 148 return regenRate + garrisonRegenRate - territoryDecayRate;154 return regenRate + garrisonRegenRate; 149 155 }; 150 156 151 Capturable.prototype. RegenCapturePoints= function()157 Capturable.prototype.TimerTick = function() 152 158 { 153 159 var cmpOwnership = Engine.QueryInterface(this.entity, IID_Ownership); 154 160 if (!cmpOwnership || cmpOwnership.GetOwner() == -1) 155 161 return; 156 162 163 var owner = cmpOwnership.GetOwner(); 164 var modifiedCp = 0; 165 166 // special handle for the territory decay 167 // reduce cp from the owner in favour of all neighbours (also allies) 168 var cmpTerritoryDecay = Engine.QueryInterface(this.entity, IID_TerritoryDecay); 169 if (cmpTerritoryDecay && cmpTerritoryDecay.IsDecaying()) 170 { 171 var neighbours = cmpTerritoryDecay.GetConnectedNeighbours(); 172 var totalNeighbours = neighbours.reduce((a, b) => a + b); 173 var decay = Math.min(cmpTerritoryDecay.GetDecayRate(), this.cp[owner]); 174 this.cp[owner] -= decay; 175 modifiedCp += decay; 176 for (let p in neighbours) 177 this.cp[p] += decay * neighbours[p] / totalNeighbours; 178 this.RegisterCapturePointsChanged(); 179 } 180 157 181 var regenRate = this.GetRegenRate(); 158 182 if (regenRate < 0) 159 var takenCp = this.Reduce(-regenRate, 0);160 else 161 var takenCp = this.Reduce(regenRate, cmpOwnership.GetOwner())183 modifiedCp += this.Reduce(-regenRate, 0) 184 else if (regenRate > 0) 185 modifiedCp += this.Reduce(regenRate, owner) 162 186 163 if ( takenCp > 0)187 if (modifiedCp) 164 188 return; 165 189 166 // no capture points taken, stop the timer190 // nothing changed, stop the timer 167 191 var cmpTimer = Engine.QueryInterface(SYSTEM_ENTITY, IID_Timer); 168 cmpTimer.CancelTimer(this. regenTimer);169 this. regenTimer = 0;170 Engine.PostMessage(this.entity, MT_CaptureRegenStateChanged, {"regenerating": false, "r ate": 0});192 cmpTimer.CancelTimer(this.timer); 193 this.timer = 0; 194 Engine.PostMessage(this.entity, MT_CaptureRegenStateChanged, {"regenerating": false, "regenRate": 0, "territoryDecay": 0}); 171 195 }; 172 196 173 197 /** 174 198 * Start the regeneration timer when no timer exists 175 * When nothing can be regenerated (f.e. because the176 * rate is 0, or because itis fully regenerated),199 * When nothing can be modified (f.e. because it 200 * is fully regenerated), 177 201 * the timer stops automatically after one execution. 178 202 */ 179 Capturable.prototype. StartRegenTimer = function()203 Capturable.prototype.CheckTimer = function() 180 204 { 181 if (this. regenTimer)205 if (this.timer) 182 206 return; 183 207 184 var rate = this.GetRegenRate(); 185 if (rate == 0) 208 var regenRate = this.GetRegenRate(); 209 var cmpDecay = Engine.QueryInterface(this.entity, IID_TerritoryDecay); 210 var decay = cmpDecay && cmpDecay.IsDecaying() ? cmpDecay.GetDecayRate() : 0; 211 if (regenRate == 0 && decay == 0) 186 212 return; 187 213 188 214 var cmpTimer = Engine.QueryInterface(SYSTEM_ENTITY, IID_Timer); 189 this. regenTimer = cmpTimer.SetInterval(this.entity, IID_Capturable, "RegenCapturePoints", 1000, 1000, null);190 Engine.PostMessage(this.entity, MT_CaptureRegenStateChanged, {" regenerating": true, "rate": rate});215 this.timer = cmpTimer.SetInterval(this.entity, IID_Capturable, "TimerTick", 1000, 1000, null); 216 Engine.PostMessage(this.entity, MT_CaptureRegenStateChanged, {"ticking": true, "regenRate": regenRate, "territoryDecay": decay}); 191 217 }; 192 218 193 219 //// Message Listeners //// … … 206 232 for (let i in this.cp) 207 233 this.cp[i] *= scale; 208 234 Engine.PostMessage(this.entity, MT_CapturePointsChanged, { "capturePoints": this.cp }); 209 this. StartRegenTimer();235 this.CheckTimer(); 210 236 }; 211 237 212 238 Capturable.prototype.OnGarrisonedUnitsChanged = function(msg) 213 239 { 214 this. StartRegenTimer();240 this.CheckTimer(); 215 241 }; 216 242 217 243 Capturable.prototype.OnTerritoryDecayChanged = function(msg) 218 244 { 219 245 if (msg.to) 220 this. StartRegenTimer();246 this.CheckTimer(); 221 247 }; 222 248 223 249 Capturable.prototype.OnOwnershipChanged = function(msg) 224 250 { 225 this. StartRegenTimer();251 this.CheckTimer(); 226 252 227 253 // if the new owner has no capture points, it means that either 228 254 // * it's being initialised now, or -
binaries/data/mods/public/simulation/components/TerritoryDecay.js
8 8 TerritoryDecay.prototype.Init = function() 9 9 { 10 10 this.decaying = false; 11 this.connectedNeighbours = []; 11 12 }; 12 13 13 14 TerritoryDecay.prototype.IsConnected = function() 14 15 { 16 var numPlayers = Engine.QueryInterface(SYSTEM_ENTITY, IID_PlayerManager).GetNumPlayers(); 17 this.connectedNeighbours.fill(0, 0, numPlayers); 18 15 19 var cmpPosition = Engine.QueryInterface(this.entity, IID_Position); 16 20 if (!cmpPosition || !cmpPosition.IsInWorld()) 17 21 return false; … … 31 35 var pos = cmpPosition.GetPosition2D(); 32 36 var tileOwner = cmpTerritoryManager.GetOwner(pos.x, pos.y); 33 37 if (tileOwner != cmpOwnership.GetOwner()) 38 { 39 this.connectedNeighbours[tileOwner] = 1; 34 40 return false; 41 } 35 42 36 return cmpTerritoryManager.IsConnected(pos.x, pos.y); 43 if (cmpTerritoryManager.IsConnected(pos.x, pos.y)) 44 return true; 45 46 this.connectedNeighbours = cmpTerritoryManager.GetNeighbours(pos.x, pos.y, true); 47 return false; 37 48 }; 38 49 39 50 TerritoryDecay.prototype.IsDecaying = function() … … 49 60 this.entity); 50 61 }; 51 62 63 /** 64 * Get the number of connected bordering tiles to this region 65 * Only valid when this.IsDecaying() 66 */ 67 TerritoryDecay.prototype.GetConnectedNeighbours = function() 68 { 69 return this.connectedNeighbours; 70 }; 71 52 72 TerritoryDecay.prototype.UpdateDecayState = function() 53 73 { 54 74 if (this.IsConnected()) … … 58 78 if (decaying === this.decaying) 59 79 return; 60 80 this.decaying = decaying; 61 Engine.PostMessage(this.entity, MT_TerritoryDecayChanged, { "entity": this.entity, "to": decaying });81 Engine.PostMessage(this.entity, MT_TerritoryDecayChanged, { "entity": this.entity, "to": decaying, "rate": this.GetDecayRate() }); 62 82 }; 63 83 64 84 TerritoryDecay.prototype.OnTerritoriesChanged = function(msg) -
source/simulation2/components/CCmpTerritoryManager.cpp
224 224 } 225 225 226 226 virtual player_id_t GetOwner(entity_pos_t x, entity_pos_t z); 227 virtual std::vector<u32> GetNeighbours(entity_pos_t x, entity_pos_t z, bool filterConnected); 227 228 virtual bool IsConnected(entity_pos_t x, entity_pos_t z); 228 229 229 230 // To support lazy updates of territory render data, … … 280 281 281 282 typedef PriorityQueueHeap<std::pair<u16, u16>, u32, std::greater<u32> > OpenQueue; 282 283 283 static void ProcessNeighbour(u32 falloff, u16 i, u16 j, u32 pg, bool diagonal, 284 Grid<u32>& grid, OpenQueue& queue, const Grid<u8>& costGrid) 284 /** 285 * Compute the tile indexes on the grid nearest to a given point 286 */ 287 static void NearestTile(entity_pos_t x, entity_pos_t z, u16& i, u16& j, u16 w, u16 h) 285 288 { 286 u32 dg = falloff * costGrid.get(i, j);287 if (diagonal)288 dg = (dg * 362) / 256; 289 i = (u16)clamp((x / (int)TERRAIN_TILE_SIZE).ToInt_RoundToZero(), 0, w-1); 290 j = (u16)clamp((z / (int)TERRAIN_TILE_SIZE).ToInt_RoundToZero(), 0, h-1); 291 } 289 292 290 // Stop if new cost g=pg-dg is not better than previous value for that tile 291 // (arranged to avoid underflow if pg < dg) 292 if (pg <= grid.get(i, j) + dg) 293 return; 293 /** 294 * Returns the position of the center of the given tile 295 */ 296 static void TileCenter(u16 i, u16 j, entity_pos_t& x, entity_pos_t& z) 297 { 298 x = entity_pos_t::FromInt(i*(int)TERRAIN_TILE_SIZE + (int)TERRAIN_TILE_SIZE/2); 299 z = entity_pos_t::FromInt(j*(int)TERRAIN_TILE_SIZE + (int)TERRAIN_TILE_SIZE/2); 300 } 294 301 295 u32 g = pg - dg; // cost to this tile = cost to predecessor - falloff from predecessor 302 // TODO: would be nice not to duplicate those two functions from CCmpObstructionManager.cpp 296 303 297 grid.set(i, j, g);298 OpenQueue::Item tile = { std::make_pair(i, j), g };299 queue.push(tile);300 }301 302 304 static void FloodFill(Grid<u32>& grid, Grid<u8>& costGrid, OpenQueue& openTiles, u32 falloff) 303 305 { 304 306 u16 tilesW = grid.m_W; … … 308 310 { 309 311 OpenQueue::Item tile = openTiles.pop(); 310 312 311 // Process neighbours (if they're not off the edge of the map) 312 u16 x = tile.id.first; 313 u16 z = tile.id.second; 314 if (x > 0) 315 ProcessNeighbour(falloff, (u16)(x-1), z, tile.rank, false, grid, openTiles, costGrid); 316 if (x < tilesW-1) 317 ProcessNeighbour(falloff, (u16)(x+1), z, tile.rank, false, grid, openTiles, costGrid); 318 if (z > 0) 319 ProcessNeighbour(falloff, x, (u16)(z-1), tile.rank, false, grid, openTiles, costGrid); 320 if (z < tilesH-1) 321 ProcessNeighbour(falloff, x, (u16)(z+1), tile.rank, false, grid, openTiles, costGrid); 322 if (x > 0 && z > 0) 323 ProcessNeighbour(falloff, (u16)(x-1), (u16)(z-1), tile.rank, true, grid, openTiles, costGrid); 324 if (x > 0 && z < tilesH-1) 325 ProcessNeighbour(falloff, (u16)(x-1), (u16)(z+1), tile.rank, true, grid, openTiles, costGrid); 326 if (x < tilesW-1 && z > 0) 327 ProcessNeighbour(falloff, (u16)(x+1), (u16)(z-1), tile.rank, true, grid, openTiles, costGrid); 328 if (x < tilesW-1 && z < tilesH-1) 329 ProcessNeighbour(falloff, (u16)(x+1), (u16)(z+1), tile.rank, true, grid, openTiles, costGrid); 313 // Process neighbours 314 int x = tile.id.first; 315 int z = tile.id.second; 316 u32 pg = tile.rank; 317 for (int i = -1; i < 2; ++i) 318 { 319 int ti = x + i; 320 if (ti < 0 || ti >= tilesW) 321 continue; 322 for (int j = -1; j < 2; ++j) 323 { 324 if (i == 0 && j == 0) 325 continue; 326 int tj = z + j; 327 if (tj < 0 || tj >= tilesH) 328 continue; 329 330 u32 dg = falloff * costGrid.get(ti, tj); 331 if (i * j != 0) // diagonal tile 332 dg = (dg * 362) / 256; 333 334 // Stop if new cost g=pg-dg is not better than previous value for that tile 335 // (arranged to avoid underflow if pg < dg) 336 if (pg <= grid.get(ti, tj) + dg) 337 continue; 338 339 u32 g = pg - dg; // cost to this tile = cost to predecessor - falloff from predecessor 340 341 grid.set(ti, tj, g); 342 OpenQueue::Item tile = { std::make_pair(ti, tj), g }; 343 openTiles.push(tile); 344 } 345 } 330 346 } 331 347 } 332 348 … … 382 398 // Split influence entities into per-player lists, ignoring any with invalid properties 383 399 std::map<player_id_t, std::vector<entity_id_t> > influenceEntities; 384 400 std::vector<entity_id_t> rootInfluenceEntities; 385 for ( CComponentManager::InterfaceList::iterator it = influences.begin(); it != influences.end(); ++it)401 for (auto& it : influences) 386 402 { 387 403 // Ignore any with no weight or radius (to avoid divide-by-zero later) 388 ICmpTerritoryInfluence* cmpTerritoryInfluence = static_cast<ICmpTerritoryInfluence*>(it ->second);404 ICmpTerritoryInfluence* cmpTerritoryInfluence = static_cast<ICmpTerritoryInfluence*>(it.second); 389 405 if (cmpTerritoryInfluence->GetWeight() == 0 || cmpTerritoryInfluence->GetRadius() == 0) 390 406 continue; 391 407 392 CmpPtr<ICmpOwnership> cmpOwnership(GetSimContext(), it ->first);408 CmpPtr<ICmpOwnership> cmpOwnership(GetSimContext(), it.first); 393 409 if (!cmpOwnership) 394 410 continue; 395 411 … … 403 419 continue; 404 420 405 421 // Ignore if invalid position 406 CmpPtr<ICmpPosition> cmpPosition(GetSimContext(), it ->first);422 CmpPtr<ICmpPosition> cmpPosition(GetSimContext(), it.first); 407 423 if (!cmpPosition || !cmpPosition->IsInWorld()) 408 424 continue; 409 425 410 influenceEntities[owner].push_back(it ->first);426 influenceEntities[owner].push_back(it.first); 411 427 412 428 if (cmpTerritoryInfluence->IsRoot()) 413 rootInfluenceEntities.push_back(it ->first);429 rootInfluenceEntities.push_back(it.first); 414 430 } 415 431 416 432 // For each player, store the sum of influences on each tile … … 418 434 // TODO: this is a large waste of memory; we don't really need to store 419 435 // all the intermediate grids 420 436 421 for ( std::map<player_id_t, std::vector<entity_id_t> >::iterator it = influenceEntities.begin(); it != influenceEntities.end(); ++it)437 for (auto& it : influenceEntities) 422 438 { 423 439 Grid<u32> playerGrid(tilesW, tilesH); 440 Grid<u32> entityGrid(tilesW, tilesH); 424 441 425 std::vector<entity_id_t>& ents = it->second; 426 for (std::vector<entity_id_t>::iterator eit = ents.begin(); eit != ents.end(); ++eit) 442 std::vector<entity_id_t>& ents = it.second; 443 // Compute the influence map of the current entity, then add it to the player grid 444 for (auto& eit : ents) 427 445 { 428 // Compute the influence map of the current entity, then add it to the player grid 429 430 Grid<u32> entityGrid(tilesW, tilesH); 431 432 CmpPtr<ICmpPosition> cmpPosition(GetSimContext(), *eit); 446 CmpPtr<ICmpPosition> cmpPosition(GetSimContext(), eit); 433 447 CFixedVector2D pos = cmpPosition->GetPosition2D(); 434 u16 i = (u16)clamp((pos.X / (int)TERRAIN_TILE_SIZE).ToInt_RoundToNegInfinity(), 0, tilesW-1);435 u16 j = (u16)clamp((pos.Y / (int)TERRAIN_TILE_SIZE).ToInt_RoundToNegInfinity(), 0, tilesH-1);448 u16 i, j; 449 NearestTile(pos.X, pos.Y, i, j, tilesW, tilesH); 436 450 437 CmpPtr<ICmpTerritoryInfluence> cmpTerritoryInfluence(GetSimContext(), *eit);451 CmpPtr<ICmpTerritoryInfluence> cmpTerritoryInfluence(GetSimContext(), eit); 438 452 u32 weight = cmpTerritoryInfluence->GetWeight(); 439 453 u32 radius = cmpTerritoryInfluence->GetRadius() / TERRAIN_TILE_SIZE; 440 u32 falloff = weight / radius ; // earlier check for GetRadius() == 0 prevents divide-by-zero454 u32 falloff = weight / radius + 1; // earlier check for GetRadius() == 0 prevents divide-by-zero 441 455 442 // TODO: we should have some maximum value on weight, to avoid overflow443 // when doing all the sums444 456 445 457 // Initialise the tile under the entity 446 458 entityGrid.set(i, j, weight); 447 459 OpenQueue openTiles; 448 OpenQueue::Item tile = { std::make_pair( (u16)i, (i16)j), weight };460 OpenQueue::Item tile = { std::make_pair(i, j), weight }; 449 461 openTiles.push(tile); 450 462 451 463 // Expand influences outwards 452 464 FloodFill(entityGrid, influenceGrid, openTiles, falloff); 453 465 454 // TODO: we should do a sparse grid and only add the non-zero regions, for performance 455 playerGrid.add(entityGrid); 466 // TODO: we should have overflow checks when adding, as the number of added 467 // entities can be arbitrary large 468 playerGrid.addRegion(entityGrid, i-radius, j-radius, i+radius, j+radius); 469 entityGrid.reset(); 456 470 } 457 471 458 playerGrids. push_back(std::make_pair(it->first, playerGrid));472 playerGrids.emplace_back(it.first, playerGrid); 459 473 } 460 474 461 475 // Set m_Territories to the player ID with the highest influence for each tile … … 467 481 for (size_t k = 0; k < playerGrids.size(); ++k) 468 482 { 469 483 u32 w = playerGrids[k].second.get(i, j); 470 if (w > bestWeight) 471 { 472 player_id_t id = playerGrids[k].first; 473 m_Territories->set(i, j, (u8)id); 474 bestWeight = w; 475 } 484 if (w <= bestWeight) 485 continue; 486 m_Territories->set(i, j, (u8)playerGrids[k].first); 487 bestWeight = w; 476 488 } 477 489 } 478 490 } … … 479 491 480 492 // Detect territories connected to a 'root' influence (typically a civ center) 481 493 // belonging to their player, and mark them with the connected flag 482 for ( std::vector<entity_id_t>::iterator it = rootInfluenceEntities.begin(); it != rootInfluenceEntities.end(); ++it)494 for (auto& it : rootInfluenceEntities) 483 495 { 484 496 // (These components must be valid else the entities wouldn't be added to this list) 485 CmpPtr<ICmpOwnership> cmpOwnership(GetSimContext(), *it);486 CmpPtr<ICmpPosition> cmpPosition(GetSimContext(), *it);497 CmpPtr<ICmpOwnership> cmpOwnership(GetSimContext(), it); 498 CmpPtr<ICmpPosition> cmpPosition(GetSimContext(), it); 487 499 488 500 CFixedVector2D pos = cmpPosition->GetPosition2D(); 489 u16 i = (u16)clamp((pos.X / (int)TERRAIN_TILE_SIZE).ToInt_RoundToNegInfinity(), 0, tilesW-1);490 u16 j = (u16)clamp((pos.Y / (int)TERRAIN_TILE_SIZE).ToInt_RoundToNegInfinity(), 0, tilesH-1);501 u16 i, j; 502 NearestTile(pos.X, pos.Y, i, j, tilesW, tilesH); 491 503 492 504 u8 owner = (u8)cmpOwnership->GetOwner(); 493 505 … … 499 511 500 512 Grid<u8>& grid = *m_Territories; 501 513 502 u16 maxi = (u16)(grid.m_W-1);503 u16 maxj = (u16)(grid.m_H-1);504 505 514 std::vector<std::pair<u16, u16> > tileStack; 515 tileStack.emplace_back(i, j); 506 516 507 #define MARK_AND_PUSH(i, j) STMT(grid.set(i, j, owner | TERRITORY_CONNECTED_MASK); tileStack.push_back(std::make_pair(i, j)); )508 509 MARK_AND_PUSH(i, j);510 517 while (!tileStack.empty()) 511 518 { 512 int ti= tileStack.back().first;513 int tj= tileStack.back().second;519 int x = tileStack.back().first; 520 int z = tileStack.back().second; 514 521 tileStack.pop_back(); 515 516 if (ti > 0 && grid.get(ti-1, tj) == owner)517 MARK_AND_PUSH(ti-1, tj);518 if (ti < maxi && grid.get(ti+1, tj) == owner)519 MARK_AND_PUSH(ti+1, tj);520 if (tj > 0 && grid.get(ti, tj-1) == owner)521 MARK_AND_PUSH(ti, tj-1);522 if (tj < maxj && grid.get(ti, tj+1) == owner)523 MARK_AND_PUSH(ti, tj+1);524 525 if (ti > 0 && tj > 0 && grid.get(ti-1, tj-1) == owner)526 MARK_AND_PUSH(ti-1, tj-1);527 if (ti > 0 && tj < maxj && grid.get(ti-1, tj+1) == owner)528 MARK_AND_PUSH(ti-1, tj+1);529 if (ti < maxi && tj > 0 && grid.get(ti+1, tj-1) == owner)530 MARK_AND_PUSH(ti+1, tj-1);531 if (ti < maxi && tj < maxj && grid.get(ti+1, tj+1) == owner)532 MARK_AND_PUSH(ti+1, tj+1);522 for (int i = -1; i < 2; ++i) 523 { 524 int ti = x + i; 525 if (ti < 0 || ti >= tilesW) // check grid bounds 526 continue; 527 for (int j = -1; j < 2; ++j) 528 { 529 if (i == 0 && j == 0) // don't process the same tile 530 continue; 531 int tj = z + j; 532 if (tj < 0 || tj >= tilesH) // check grid bounds 533 continue; 534 if (grid.get(ti, tj) != owner) 535 continue; 536 grid.set(ti, tj, owner | TERRITORY_CONNECTED_MASK); 537 tileStack.emplace_back(ti, tj); 538 } 539 } 533 540 } 534 535 #undef MARK_AND_PUSH536 541 } 537 542 } 538 543 539 /**540 * Compute the tile indexes on the grid nearest to a given point541 */542 static void NearestTile(entity_pos_t x, entity_pos_t z, u16& i, u16& j, u16 w, u16 h)543 {544 i = (u16)clamp((x / (int)TERRAIN_TILE_SIZE).ToInt_RoundToZero(), 0, w-1);545 j = (u16)clamp((z / (int)TERRAIN_TILE_SIZE).ToInt_RoundToZero(), 0, h-1);546 }547 548 /**549 * Returns the position of the center of the given tile550 */551 static void TileCenter(u16 i, u16 j, entity_pos_t& x, entity_pos_t& z)552 {553 x = entity_pos_t::FromInt(i*(int)TERRAIN_TILE_SIZE + (int)TERRAIN_TILE_SIZE/2);554 z = entity_pos_t::FromInt(j*(int)TERRAIN_TILE_SIZE + (int)TERRAIN_TILE_SIZE/2);555 }556 557 // TODO: would be nice not to duplicate those two functions from CCmpObstructionManager.cpp558 559 560 544 void CCmpTerritoryManager::RasteriseInfluences(CComponentManager::InterfaceList& infls, Grid<u8>& grid) 561 545 { 562 for ( CComponentManager::InterfaceList::iterator it = infls.begin(); it != infls.end(); ++it)546 for (auto& it : infls) 563 547 { 564 ICmpTerritoryInfluence* cmpTerritoryInfluence = static_cast<ICmpTerritoryInfluence*>(it ->second);548 ICmpTerritoryInfluence* cmpTerritoryInfluence = static_cast<ICmpTerritoryInfluence*>(it.second); 565 549 566 550 i32 cost = cmpTerritoryInfluence->GetCost(); 567 551 if (cost == -1) 568 552 continue; 569 553 570 CmpPtr<ICmpObstruction> cmpObstruction(GetSimContext(), it ->first);554 CmpPtr<ICmpObstruction> cmpObstruction(GetSimContext(), it.first); 571 555 if (!cmpObstruction) 572 556 continue; 573 557 … … 722 706 return m_Territories->get(i, j) & TERRITORY_PLAYER_MASK; 723 707 } 724 708 709 std::vector<u32> CCmpTerritoryManager::GetNeighbours(entity_pos_t x, entity_pos_t z, bool filterConnected) 710 { 711 CmpPtr<ICmpPlayerManager> cmpPlayerManager(GetSystemEntity()); 712 if (!cmpPlayerManager) 713 return std::vector<u32>(); 714 715 std::vector<u32> ret(cmpPlayerManager->GetNumPlayers(), 0); 716 CalculateTerritories(); 717 if (!m_Territories) 718 return ret; 719 720 u16 i, j; 721 NearestTile(x, z, i, j, m_Territories->m_W, m_Territories->m_H); 722 723 // calculate the neighbours 724 player_id_t thisOwner = m_Territories->get(i, j) & TERRITORY_PLAYER_MASK; 725 726 u16 tilesW = m_Territories->m_W; 727 u16 tilesH = m_Territories->m_H; 728 729 // use a flood-fill algorithm that fills up to the borders and remembers the owners 730 Grid<bool>* markerGrid = new Grid<bool>(tilesW, tilesH); 731 std::vector<std::pair<u16, u16> > tileStack; 732 tileStack.emplace_back(i, j); 733 markerGrid->set(i, j, true); 734 while (!tileStack.empty()) 735 { 736 int x = tileStack.back().first; 737 int z = tileStack.back().second; 738 tileStack.pop_back(); 739 for (int i = -1; i < 2; ++i) 740 { 741 int ti = x + i; 742 if (ti < 0 || ti >= tilesW) 743 continue; 744 for (int j = -1; j < 2; ++j) 745 { 746 if (i == 0 && j == 0) 747 continue; 748 int tj = z + j; 749 if (tj < 0 || tj >= tilesH) 750 continue; 751 752 if (markerGrid->get(ti, tj)) 753 continue; 754 // mark the tile as visited in any case 755 markerGrid->set(ti, tj, true); 756 int owner = m_Territories->get(ti, tj) & TERRITORY_PLAYER_MASK; 757 if (owner == thisOwner) 758 tileStack.emplace_back(ti, tj); // expand the tile 759 else if (owner == 0 || !filterConnected || (m_Territories->get(ti, tj) & TERRITORY_CONNECTED_MASK) != 0) 760 ret[owner]++; // add player to the neighbours when requested 761 } 762 } 763 } 764 return ret; 765 } 766 725 767 bool CCmpTerritoryManager::IsConnected(entity_pos_t x, entity_pos_t z) 726 768 { 727 769 u16 i, j; -
source/simulation2/components/ICmpTerritoryManager.cpp
23 23 24 24 BEGIN_INTERFACE_WRAPPER(TerritoryManager) 25 25 DEFINE_INTERFACE_METHOD_2("GetOwner", player_id_t, ICmpTerritoryManager, GetOwner, entity_pos_t, entity_pos_t) 26 DEFINE_INTERFACE_METHOD_3("GetNeighbours", std::vector<u32>, ICmpTerritoryManager, GetNeighbours, entity_pos_t, entity_pos_t, bool) 26 27 DEFINE_INTERFACE_METHOD_2("IsConnected", bool, ICmpTerritoryManager, IsConnected, entity_pos_t, entity_pos_t) 27 28 END_INTERFACE_WRAPPER(TerritoryManager) -
source/simulation2/components/ICmpTerritoryManager.h
47 47 virtual player_id_t GetOwner(entity_pos_t x, entity_pos_t z) = 0; 48 48 49 49 /** 50 * get the number of neighbour tiles for per player for the selected position 51 * @return A list with the number of neighbour tiles per player 52 */ 53 virtual std::vector<u32> GetNeighbours(entity_pos_t x, entity_pos_t z, bool filterConnected) = 0; 54 55 /** 50 56 * Get whether territory at given position is connected to a root object 51 57 * (civ center etc) owned by that territory's player. 52 58 */ -
source/simulation2/helpers/Grid.h
92 92 m_Data[i] += g.m_Data[i]; 93 93 } 94 94 95 // Add a region of a grid to this grid 96 void addRegion(const Grid& g, int minI, int minJ, int maxI, int maxJ) 97 { 98 #if GRID_BOUNDS_DEBUG 99 ENSURE(g.m_W == m_W && g.m_H == m_H); 100 ENSURE(minI <= maxI && minJ <= maxJ); 101 #endif 102 minI = std::min(std::max(minI, 0), (int)g.m_W); 103 maxI = std::min(std::max(maxI, 0), (int)g.m_W); 104 minJ = std::min(std::max(minJ, 0), (int)g.m_H); 105 maxJ = std::min(std::max(maxJ, 0), (int)g.m_H); 106 for (int j = minJ; j <= maxJ; ++j) 107 { 108 for (int i = minI; i <= maxI; ++i) 109 { 110 m_Data[j*m_W + i] += g.m_Data[j*m_W + i]; 111 } 112 } 113 } 114 95 115 void set(int i, int j, const T& value) 96 116 { 97 117 #if GRID_BOUNDS_DEBUG