Ticket #3204: territory_neighbours.diff
File territory_neighbours.diff, 11.2 KB (added by , 9 years ago) |
---|
-
binaries/data/mods/public/simulation/components/TerritoryDecay.js
33 33 if (tileOwner != cmpOwnership.GetOwner()) 34 34 return false; 35 35 36 return cmpTerritoryManager.IsConnected(pos.x, pos.y); 36 if (cmpTerritoryManager.IsConnected(pos.x, pos.y)) 37 return true; 38 39 var neighbours = cmpTerritoryManager.GetConnectedNeighbours(pos.x, pos.y); 40 warn(uneval(neighbours)); 41 return false; 37 42 }; 38 43 39 44 TerritoryDecay.prototype.IsDecaying = function() -
source/simulation2/components/CCmpTerritoryManager.cpp
45 45 #include "simulation2/helpers/PriorityQueue.h" 46 46 #include "simulation2/helpers/Render.h" 47 47 48 #include "ps/CLogger.h" 49 48 50 class CCmpTerritoryManager; 49 51 50 52 class TerritoryOverlay : public TerrainOverlay … … 88 90 // connected flag in bit 6 (TERRITORY_CONNECTED_MASK); 89 91 // processed flag in bit 7 (TERRITORY_PROCESSED_MASK) 90 92 Grid<u8>* m_Territories; 93 // Bits 0 - 14 are used to mark players (set to 1 when that player is a neighbour) 94 // Bit 15 is used as a clean bit. The state is considered dirty when the bit is 0 95 Grid<u16>* m_NeighbourTerritories; 91 96 92 97 // Set to true when territories change; will send a TerritoriesChanged message 93 98 // during the Update phase … … 113 118 virtual void Init(const CParamNode& UNUSED(paramNode)) 114 119 { 115 120 m_Territories = NULL; 121 m_NeighbourTerritories = NULL; 116 122 m_DebugOverlay = NULL; 117 123 // m_DebugOverlay = new TerritoryOverlay(*this); 118 124 m_BoundaryLinesDirty = true; … … 135 141 virtual void Deinit() 136 142 { 137 143 SAFE_DELETE(m_Territories); 144 SAFE_DELETE(m_NeighbourTerritories); 138 145 SAFE_DELETE(m_DebugOverlay); 139 146 } 140 147 … … 224 231 } 225 232 226 233 virtual player_id_t GetOwner(entity_pos_t x, entity_pos_t z); 234 virtual std::vector<player_id_t> GetConnectedNeighbours(entity_pos_t x, entity_pos_t z); 227 235 virtual bool IsConnected(entity_pos_t x, entity_pos_t z); 228 236 229 237 // To support lazy updates of territory render data, … … 235 243 void MakeDirty() 236 244 { 237 245 SAFE_DELETE(m_Territories); 246 SAFE_DELETE(m_NeighbourTerritories); 238 247 ++m_DirtyID; 239 248 m_BoundaryLinesDirty = true; 240 249 m_TriggerEvent = true; … … 252 261 253 262 void CalculateTerritories(); 254 263 264 u16 GetConnectedNeighbourBits(u16 i, u16 j); 265 255 266 /** 256 267 * Updates @p grid based on the obstruction shapes of all entities with 257 268 * a TerritoryInfluence component. Grid cells are 0 if no influence, … … 418 429 // TODO: this is a large waste of memory; we don't really need to store 419 430 // all the intermediate grids 420 431 421 for ( std::map<player_id_t, std::vector<entity_id_t> >::iteratorit = influenceEntities.begin(); it != influenceEntities.end(); ++it)432 for (auto it = influenceEntities.begin(); it != influenceEntities.end(); ++it) 422 433 { 423 434 Grid<u32> playerGrid(tilesW, tilesH); 435 Grid<u32> entityGrid(tilesW, tilesH); 424 436 425 437 std::vector<entity_id_t>& ents = it->second; 426 for (std::vector<entity_id_t>::iterator eit = ents.begin(); eit != ents.end(); ++eit) 438 // Compute the influence map of the current entity, then add it to the player grid 439 for (auto eit = ents.begin(); eit != ents.end(); ++eit) 427 440 { 428 // Compute the influence map of the current entity, then add it to the player grid429 430 Grid<u32> entityGrid(tilesW, tilesH);431 432 441 CmpPtr<ICmpPosition> cmpPosition(GetSimContext(), *eit); 433 442 CFixedVector2D pos = cmpPosition->GetPosition2D(); 434 443 u16 i = (u16)clamp((pos.X / (int)TERRAIN_TILE_SIZE).ToInt_RoundToNegInfinity(), 0, tilesW-1); … … 437 446 CmpPtr<ICmpTerritoryInfluence> cmpTerritoryInfluence(GetSimContext(), *eit); 438 447 u32 weight = cmpTerritoryInfluence->GetWeight(); 439 448 u32 radius = cmpTerritoryInfluence->GetRadius() / TERRAIN_TILE_SIZE; 440 u32 falloff = weight / radius ; // earlier check for GetRadius() == 0 prevents divide-by-zero449 u32 falloff = weight / radius + 1; // earlier check for GetRadius() == 0 prevents divide-by-zero 441 450 442 // TODO: we should have some maximum value on weight, to avoid overflow443 // when doing all the sums444 451 445 452 // Initialise the tile under the entity 446 453 entityGrid.set(i, j, weight); … … 451 458 // Expand influences outwards 452 459 FloodFill(entityGrid, influenceGrid, openTiles, falloff); 453 460 454 // TODO: we should do a sparse grid and only add the non-zero regions, for performance 455 playerGrid.add(entityGrid); 461 // TODO: we should have overflow checks when adding, as the number of added 462 // entities can be arbitrary large 463 playerGrid.addRegion(entityGrid, i-radius, j-radius, i+radius, j+radius); 464 entityGrid.reset(); 456 465 } 457 466 458 467 playerGrids.push_back(std::make_pair(it->first, playerGrid)); … … 479 488 480 489 // Detect territories connected to a 'root' influence (typically a civ center) 481 490 // belonging to their player, and mark them with the connected flag 482 for ( std::vector<entity_id_t>::iteratorit = rootInfluenceEntities.begin(); it != rootInfluenceEntities.end(); ++it)491 for (auto it = rootInfluenceEntities.begin(); it != rootInfluenceEntities.end(); ++it) 483 492 { 484 493 // (These components must be valid else the entities wouldn't be added to this list) 485 494 CmpPtr<ICmpOwnership> cmpOwnership(GetSimContext(), *it); … … 536 545 } 537 546 } 538 547 548 u16 CCmpTerritoryManager::GetConnectedNeighbourBits(u16 i, u16 j) 549 { 550 if (!m_NeighbourTerritories) 551 m_NeighbourTerritories = new Grid<u16>(m_Territories->m_W, m_Territories->m_H); 552 553 if ((m_NeighbourTerritories->get(i, j) & NEIGHBOURS_CLEAN) != 0) 554 return m_NeighbourTerritories->get(i,j) & NEIGHBOURS_PLAYER_MASK; 555 556 player_id_t thisOwner = m_Territories->get(i, j) & TERRITORY_PLAYER_MASK; 557 // we rely on having a gaia bord around the map, so we can't test gaia territory 558 if (thisOwner == 0) 559 return 0; 560 // use a flood-fill algorithm that fills up to the borders and remembers the owners 561 Grid<bool>* markerGrid = new Grid<bool>(m_Territories->m_W, m_Territories->m_H); 562 u16 neighbourBits = 0; 563 std::vector<std::pair<u16, u16> > tileStack; 564 tileStack.push_back(std::make_pair(i, j)); 565 markerGrid->set(i, j, true); 566 while (!tileStack.empty()) 567 { 568 u16 ti = tileStack.back().first; 569 u16 tj = tileStack.back().second; 570 tileStack.pop_back(); 571 for (u16 di = ti - 1; di <= ti + 1; ++di) 572 { 573 for (u16 dj = tj - 1; dj <= tj + 1; ++dj) 574 { 575 if (markerGrid->get(di, dj)) 576 continue; 577 player_id_t owner = m_Territories->get(di, dj) & TERRITORY_PLAYER_MASK; 578 if (owner == thisOwner) 579 { 580 tileStack.push_back(std::make_pair(di, dj)); 581 markerGrid->set(di, dj, true); 582 } 583 else if ((m_Territories->get(di, dj) & TERRITORY_CONNECTED_MASK) != 0) 584 neighbourBits |= (1 << owner); 585 } 586 } 587 } 588 // now cache the calculated owners over the territory with another flood fill 589 tileStack.push_back(std::make_pair(i, j)); 590 m_NeighbourTerritories->set(i, j, neighbourBits | NEIGHBOURS_CLEAN); 591 while (!tileStack.empty()) 592 { 593 u16 ti = tileStack.back().first; 594 u16 tj = tileStack.back().second; 595 tileStack.pop_back(); 596 for (u16 di = ti - 1; di <= ti + 1; ++di) 597 { 598 for (u16 dj = tj - 1; dj <= tj + 1; ++dj) 599 { 600 if ((m_NeighbourTerritories->get(di, dj) & NEIGHBOURS_CLEAN) != 0) 601 continue; 602 player_id_t owner = m_Territories->get(di, dj) & TERRITORY_PLAYER_MASK; 603 if (owner == thisOwner) // only flood fill this area 604 { 605 m_NeighbourTerritories->set(di, dj, neighbourBits | NEIGHBOURS_CLEAN); 606 tileStack.push_back(std::make_pair(di, dj)); 607 } 608 } 609 } 610 } 611 return neighbourBits & NEIGHBOURS_PLAYER_MASK; 612 } 613 539 614 /** 540 615 * Compute the tile indexes on the grid nearest to a given point 541 616 */ … … 722 797 return m_Territories->get(i, j) & TERRITORY_PLAYER_MASK; 723 798 } 724 799 800 std::vector<player_id_t> CCmpTerritoryManager::GetConnectedNeighbours(entity_pos_t x, entity_pos_t z) 801 { 802 if (!m_Territories) 803 CalculateTerritories(); 804 u16 i, j; 805 NearestTile(x, z, i, j, m_Territories->m_W, m_Territories->m_H); 806 u16 neighbourBits = GetConnectedNeighbourBits(i, j); 807 std::vector<player_id_t> neighbours; 808 for (entity_id_t player = 0; player < 15; ++player) 809 if ((neighbourBits & (1 << player)) != 0) 810 neighbours.push_back(player); 811 812 return neighbours; 813 } 814 725 815 bool CCmpTerritoryManager::IsConnected(entity_pos_t x, entity_pos_t z) 726 816 { 727 817 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_2("GetConnectedNeighbours", std::vector<player_id_t>, ICmpTerritoryManager, GetConnectedNeighbours, entity_pos_t, entity_pos_t) 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
32 32 static const int TERRITORY_PLAYER_MASK = 0x3F; 33 33 static const int TERRITORY_CONNECTED_MASK = 0x40; 34 34 static const int TERRITORY_PROCESSED_MASK = 0x80; //< For internal use; marks a tile as processed. 35 static const int NEIGHBOURS_PLAYER_MASK = 0x7FFF; 36 static const int NEIGHBOURS_CLEAN = 0x8000; 35 37 36 38 /** 37 39 * For each tile, the TERRITORY_PLAYER_MASK bits are player ID; … … 47 49 virtual player_id_t GetOwner(entity_pos_t x, entity_pos_t z) = 0; 48 50 49 51 /** 52 * Get a list of neighbours of this area. Only return the neighbours that are also 53 * connected on their own (see the IsConnected method). 54 */ 55 virtual std::vector<player_id_t> GetConnectedNeighbours(entity_pos_t x, entity_pos_t z) = 0; 56 57 /** 50 58 * Get whether territory at given position is connected to a root object 51 59 * (civ center etc) owned by that territory's player. 52 60 */ -
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