- Timestamp:
- 07/24/11 13:42:35 (13 years ago)
- Location:
- ps/trunk
- Files:
-
- 8 edited
-
binaries/data/mods/public/simulation/templates/special/territory_block.xml (modified) (1 diff)
-
binaries/data/mods/public/simulation/templates/special/territory_pull.xml (modified) (1 diff)
-
binaries/data/mods/public/simulation/templates/template_structure_civic_civil_centre.xml (modified) (1 diff)
-
binaries/data/mods/public/simulation/templates/template_structure_civic_house.xml (modified) (1 diff)
-
source/simulation2/components/CCmpTerritoryInfluence.cpp (modified) (3 diffs)
-
source/simulation2/components/CCmpTerritoryManager.cpp (modified) (7 diffs)
-
source/simulation2/components/ICmpTerritoryInfluence.h (modified) (1 diff)
-
source/simulation2/helpers/PriorityQueue.h (modified) (7 diffs)
Legend:
- Unmodified
- Added
- Removed
-
ps/trunk/binaries/data/mods/public/simulation/templates/special/territory_block.xml
r9889 r9906 29 29 </VisualActor> 30 30 <TerritoryInfluence> 31 <OverrideCost>100</OverrideCost> 31 <OverrideCost>64</OverrideCost> 32 <Weight>0</Weight> 33 <Radius>0</Radius> 32 34 </TerritoryInfluence> 33 35 </Entity> -
ps/trunk/binaries/data/mods/public/simulation/templates/special/territory_pull.xml
r9889 r9906 30 30 <TerritoryInfluence> 31 31 <OverrideCost>0</OverrideCost> 32 <Weight>0</Weight> 33 <Radius>0</Radius> 32 34 </TerritoryInfluence> 33 35 </Entity> -
ps/trunk/binaries/data/mods/public/simulation/templates/template_structure_civic_civil_centre.xml
r9670 r9906 68 68 </Entities> 69 69 </TrainingQueue> 70 <TerritoryInfluence> 71 <Radius>256</Radius> 72 <Weight>65536</Weight> 73 </TerritoryInfluence> 70 74 </Entity> -
ps/trunk/binaries/data/mods/public/simulation/templates/template_structure_civic_house.xml
r9670 r9906 55 55 </Entities> 56 56 </TrainingQueue> 57 <TerritoryInfluence> 58 <Radius>64</Radius> 59 <Weight>65536</Weight> 60 </TerritoryInfluence> 57 61 </Entity> -
ps/trunk/source/simulation2/components/CCmpTerritoryInfluence.cpp
r9889 r9906 30 30 DEFAULT_COMPONENT_ALLOCATOR(TerritoryInfluence) 31 31 32 u8 m_Cost; 32 int m_Cost; 33 u32 m_Weight; 34 u32 m_Radius; 33 35 34 36 static std::string GetSchema() 35 37 { 36 38 return 37 "<element name='OverrideCost'>" 39 "<optional>" 40 "<element name='OverrideCost'>" 41 "<data type='nonNegativeInteger'>" 42 "<param name='maxInclusive'>255</param>" 43 "</data>" 44 "</element>" 45 "</optional>" 46 "<element name='Weight'>" 47 "<data type='nonNegativeInteger'/>" 48 "</element>" 49 "<element name='Radius'>" 38 50 "<data type='nonNegativeInteger'/>" 39 51 "</element>"; … … 42 54 virtual void Init(const CParamNode& paramNode) 43 55 { 44 m_Cost = paramNode.GetChild("OverrideCost").ToInt(); 56 if (paramNode.GetChild("OverrideCost").IsOk()) 57 m_Cost = paramNode.GetChild("OverrideCost").ToInt(); 58 else 59 m_Cost = -1; 60 61 m_Weight = paramNode.GetChild("Weight").ToInt(); 62 m_Radius = paramNode.GetChild("Radius").ToInt(); 45 63 } 46 64 … … 58 76 } 59 77 60 virtual u8GetCost()78 virtual int GetCost() 61 79 { 62 80 return m_Cost; 63 81 } 64 82 83 virtual u32 GetWeight() 84 { 85 return m_Weight; 86 } 87 88 virtual u32 GetRadius() 89 { 90 return m_Radius; 91 } 65 92 }; 66 93 -
ps/trunk/source/simulation2/components/CCmpTerritoryManager.cpp
r9889 r9906 28 28 #include "simulation2/components/ICmpObstruction.h" 29 29 #include "simulation2/components/ICmpObstructionManager.h" 30 #include "simulation2/components/ICmpOwnership.h" 30 31 #include "simulation2/components/ICmpPathfinder.h" 31 32 #include "simulation2/components/ICmpPosition.h" … … 168 169 * or 1+c if the influence have cost c (assumed between 0 and 254). 169 170 */ 170 void RasteriseInfluences( Grid<u8>& grid);171 void RasteriseInfluences(CComponentManager::InterfaceList& infls, Grid<u8>& grid); 171 172 }; 172 173 … … 175 176 176 177 /* 177 178 We compute territory influences with a kind of best-first search: 179 1) Initialise an 'open' list with tiles that contain settlements (annotated with 180 territory ID) with initial cost 0 181 2) Pick the lowest cost tile from 'item' 182 3) For each neighbour which has not already been assigned to a territory, 183 assign it to this territory and compute its new cost (effectively the 184 distance from the associated settlement) and add to 'open' 185 4) Go to 2 until 'open' is empty 186 178 We compute the territory influence of an entity with a kind of best-first search, 179 storing an 'open' list of tiles that have not yet been processed, 180 then taking the highest-weight tile (closest to origin) and updating the weight 181 of extending to each neighbour (based on radius-determining 'falloff' value, 182 adjusted by terrain movement cost), and repeating until all tiles are processed. 187 183 */ 188 184 189 typedef PriorityQueueHeap<std::pair<u16, u16>, u32> OpenQueue; 190 191 static void ProcessNeighbour(u8 pid, u16 i, u16 j, u32 pg, bool diagonal, 192 Grid<u8>& grid, OpenQueue& queue, const Grid<u8>& influenceGrid) 193 { 194 // Ignore tiles that are already claimed 195 u8 id = grid.get(i, j); 196 if (id) 185 typedef PriorityQueueHeap<std::pair<u16, u16>, u32, std::greater<u32> > OpenQueue; 186 187 static void ProcessNeighbour(u32 falloff, u16 i, u16 j, u32 pg, bool diagonal, 188 Grid<u32>& grid, OpenQueue& queue, const Grid<u8>& costGrid) 189 { 190 u32 dg = falloff * costGrid.get(i, j); 191 if (diagonal) 192 dg = (dg * 362) / 256; 193 194 // Stop if new cost g=pg-dg is not better than previous value for that tile 195 // (arranged to avoid underflow if pg < dg) 196 if (pg <= grid.get(i, j) + dg) 197 197 return; 198 198 199 // Base cost for moving onto this tile 200 u32 dg = diagonal ? 362 : 256; 201 202 // Adjust cost based on this tile's influences 203 dg *= influenceGrid.get(i, j); 204 205 u32 g = pg + dg; // cost to this tile = cost to predecessor + delta from predecessor 206 207 grid.set(i, j, pid); 199 u32 g = pg - dg; // cost to this tile = cost to predecessor - falloff from predecessor 200 201 grid.set(i, j, g); 208 202 OpenQueue::Item tile = { std::make_pair(i, j), g }; 209 203 queue.push(tile); 210 204 } 211 205 212 void CCmpTerritoryManager::CalculateTerritories() 213 { 214 PROFILE("CalculateTerritories"); 215 216 if (m_Territories) 217 return; 218 219 CmpPtr<ICmpTerrain> cmpTerrain(GetSimContext(), SYSTEM_ENTITY); 220 uint32_t tilesW = cmpTerrain->GetVerticesPerSide() - 1; 221 uint32_t tilesH = cmpTerrain->GetVerticesPerSide() - 1; 222 223 Grid<u8> influenceGrid(tilesW, tilesH); 224 225 RasteriseInfluences(influenceGrid); 226 227 SAFE_DELETE(m_Territories); 228 m_Territories = new Grid<u8>(tilesW, tilesH); 229 230 CmpPtr<ICmpPathfinder> cmpPathfinder(GetSimContext(), SYSTEM_ENTITY); 231 ICmpPathfinder::pass_class_t passClass = cmpPathfinder->GetPassabilityClass("default"); 232 const Grid<u16>& passGrid = cmpPathfinder->GetPassabilityGrid(); 233 234 // Adjust influenceGrid so it contains terrain-passability-dependent costs, 235 // unless overridden by existing values in influenceGrid 236 for (u32 j = 0; j < tilesH; ++j) 237 { 238 for (u32 i = 0; i < tilesW; ++i) 239 { 240 u8 cost; 241 u8 inflCost = influenceGrid.get(i, j); 242 if (inflCost) 243 { 244 cost = inflCost-1; // undo RasteriseInfluences's offset 245 } 246 else 247 { 248 if (passGrid.get(i, j) & passClass) 249 cost = 100; 250 else 251 cost = 1; 252 } 253 influenceGrid.set(i, j, cost); 254 } 255 } 256 257 OpenQueue openTiles; 258 259 // Initialise open list with all settlements 260 261 CComponentManager::InterfaceList settlements = GetSimContext().GetComponentManager().GetEntitiesWithInterface(IID_Settlement); 262 u8 id = 1; 263 for (CComponentManager::InterfaceList::iterator it = settlements.begin(); it != settlements.end(); ++it) 264 { 265 entity_id_t settlement = it->first; 266 CmpPtr<ICmpPosition> cmpPosition(GetSimContext(), settlement); 267 if (cmpPosition.null() || !cmpPosition->IsInWorld()) 268 continue; 269 270 // TODO: maybe we need to ignore settlements with owner -1, 271 // since they're probably destroyed 272 273 CFixedVector2D pos = cmpPosition->GetPosition2D(); 274 int i = clamp((pos.X / (int)CELL_SIZE).ToInt_RoundToNegInfinity(), 0, (int)tilesW-1); 275 int j = clamp((pos.Y / (int)CELL_SIZE).ToInt_RoundToNegInfinity(), 0, (int)tilesH-1); 276 277 // Must avoid duplicates in the priority queue; ignore the settlement 278 // if there's already one on that tile 279 if (!m_Territories->get(i, j)) 280 { 281 m_Territories->set(i, j, id); 282 OpenQueue::Item tile = { std::make_pair((u16)i, (i16)j), 0 }; 283 openTiles.push(tile); 284 } 285 286 id += 1; 287 } 206 static void FloodFill(Grid<u32>& grid, Grid<u8>& costGrid, OpenQueue& openTiles, u32 falloff) 207 { 208 u32 tilesW = grid.m_W; 209 u32 tilesH = grid.m_H; 288 210 289 211 while (!openTiles.empty()) 290 212 { 291 213 OpenQueue::Item tile = openTiles.pop(); 292 293 // Get current tile's territory ID294 u8 tid = m_Territories->get(tile.id.first, tile.id.second);295 214 296 215 // Process neighbours (if they're not off the edge of the map) … … 298 217 u16 z = tile.id.second; 299 218 if (x > 0) 300 ProcessNeighbour( tid, x-1, z, tile.rank, false, *m_Territories, openTiles, influenceGrid);219 ProcessNeighbour(falloff, x-1, z, tile.rank, false, grid, openTiles, costGrid); 301 220 if (x < tilesW-1) 302 ProcessNeighbour( tid, x+1, z, tile.rank, false, *m_Territories, openTiles, influenceGrid);221 ProcessNeighbour(falloff, x+1, z, tile.rank, false, grid, openTiles, costGrid); 303 222 if (z > 0) 304 ProcessNeighbour( tid, x, z-1, tile.rank, false, *m_Territories, openTiles, influenceGrid);223 ProcessNeighbour(falloff, x, z-1, tile.rank, false, grid, openTiles, costGrid); 305 224 if (z < tilesH-1) 306 ProcessNeighbour( tid, x, z+1, tile.rank, false, *m_Territories, openTiles, influenceGrid);225 ProcessNeighbour(falloff, x, z+1, tile.rank, false, grid, openTiles, costGrid); 307 226 if (x > 0 && z > 0) 308 ProcessNeighbour( tid, x-1, z-1, tile.rank, true, *m_Territories, openTiles, influenceGrid);227 ProcessNeighbour(falloff, x-1, z-1, tile.rank, true, grid, openTiles, costGrid); 309 228 if (x > 0 && z < tilesH-1) 310 ProcessNeighbour( tid, x-1, z+1, tile.rank, true, *m_Territories, openTiles, influenceGrid);229 ProcessNeighbour(falloff, x-1, z+1, tile.rank, true, grid, openTiles, costGrid); 311 230 if (x < tilesW-1 && z > 0) 312 ProcessNeighbour( tid, x+1, z-1, tile.rank, true, *m_Territories, openTiles, influenceGrid);231 ProcessNeighbour(falloff, x+1, z-1, tile.rank, true, grid, openTiles, costGrid); 313 232 if (x < tilesW-1 && z < tilesH-1) 314 ProcessNeighbour(tid, x+1, z+1, tile.rank, true, *m_Territories, openTiles, influenceGrid); 233 ProcessNeighbour(falloff, x+1, z+1, tile.rank, true, grid, openTiles, costGrid); 234 } 235 } 236 237 void CCmpTerritoryManager::CalculateTerritories() 238 { 239 PROFILE("CalculateTerritories"); 240 241 if (m_Territories) 242 return; 243 244 CmpPtr<ICmpTerrain> cmpTerrain(GetSimContext(), SYSTEM_ENTITY); 245 uint32_t tilesW = cmpTerrain->GetVerticesPerSide() - 1; 246 uint32_t tilesH = cmpTerrain->GetVerticesPerSide() - 1; 247 248 SAFE_DELETE(m_Territories); 249 m_Territories = new Grid<u8>(tilesW, tilesH); 250 251 // Compute terrain-passability-dependent costs per tile 252 Grid<u8> influenceGrid(tilesW, tilesH); 253 254 CmpPtr<ICmpPathfinder> cmpPathfinder(GetSimContext(), SYSTEM_ENTITY); 255 ICmpPathfinder::pass_class_t passClass = cmpPathfinder->GetPassabilityClass("default"); 256 const Grid<u16>& passGrid = cmpPathfinder->GetPassabilityGrid(); 257 for (u32 j = 0; j < tilesH; ++j) 258 { 259 for (u32 i = 0; i < tilesW; ++i) 260 { 261 u8 cost; 262 if (passGrid.get(i, j) & passClass) 263 cost = 4; // TODO: should come from some XML file 264 else 265 cost = 1; 266 influenceGrid.set(i, j, cost); 267 } 268 } 269 270 // Find all territory influence entities 271 CComponentManager::InterfaceList influences = GetSimContext().GetComponentManager().GetEntitiesWithInterface(IID_TerritoryInfluence); 272 273 // Allow influence entities to override the terrain costs 274 RasteriseInfluences(influences, influenceGrid); 275 276 // Split influence entities into per-player lists, ignoring any with invalid properties 277 std::map<player_id_t, std::vector<entity_id_t> > influenceEntities; 278 for (CComponentManager::InterfaceList::iterator it = influences.begin(); it != influences.end(); ++it) 279 { 280 // Ignore any with no weight or radius (to avoid divide-by-zero later) 281 ICmpTerritoryInfluence* cmpTerritoryInfluence = static_cast<ICmpTerritoryInfluence*>(it->second); 282 if (cmpTerritoryInfluence->GetWeight() == 0 || cmpTerritoryInfluence->GetRadius() == 0) 283 continue; 284 285 CmpPtr<ICmpOwnership> cmpOwnership(GetSimContext(), it->first); 286 if (cmpOwnership.null()) 287 continue; 288 289 // Ignore Gaia and unassigned 290 player_id_t owner = cmpOwnership->GetOwner(); 291 if (owner <= 0) 292 continue; 293 294 // Ignore if invalid position 295 CmpPtr<ICmpPosition> cmpPosition(GetSimContext(), it->first); 296 if (cmpPosition.null() || !cmpPosition->IsInWorld()) 297 continue; 298 299 influenceEntities[owner].push_back(it->first); 300 } 301 302 // For each player, store the sum of influences on each tile 303 std::vector<std::pair<player_id_t, Grid<u32> > > playerGrids; 304 // TODO: this is a large waste of memory; we don't really need to store 305 // all the intermediate grids 306 307 for (std::map<player_id_t, std::vector<entity_id_t> >::iterator it = influenceEntities.begin(); it != influenceEntities.end(); ++it) 308 { 309 Grid<u32> playerGrid(tilesW, tilesH); 310 311 std::vector<entity_id_t>& ents = it->second; 312 for (std::vector<entity_id_t>::iterator eit = ents.begin(); eit != ents.end(); ++eit) 313 { 314 // Compute the influence map of the current entity, then add it to the player grid 315 316 Grid<u32> entityGrid(tilesW, tilesH); 317 318 CmpPtr<ICmpPosition> cmpPosition(GetSimContext(), *eit); 319 CFixedVector2D pos = cmpPosition->GetPosition2D(); 320 int i = clamp((pos.X / (int)CELL_SIZE).ToInt_RoundToNegInfinity(), 0, (int)tilesW-1); 321 int j = clamp((pos.Y / (int)CELL_SIZE).ToInt_RoundToNegInfinity(), 0, (int)tilesH-1); 322 323 CmpPtr<ICmpTerritoryInfluence> cmpTerritoryInfluence(GetSimContext(), *eit); 324 u32 weight = cmpTerritoryInfluence->GetWeight(); 325 u32 radius = cmpTerritoryInfluence->GetRadius() / CELL_SIZE; 326 u32 falloff = weight / radius; // earlier check for GetRadius() == 0 prevents divide-by-zero 327 328 // TODO: we should have some maximum value on weight, to avoid overflow 329 // when doing all the sums 330 331 // Initialise the tile under the entity 332 entityGrid.set(i, j, weight); 333 OpenQueue openTiles; 334 OpenQueue::Item tile = { std::make_pair((u16)i, (i16)j), weight }; 335 openTiles.push(tile); 336 337 // Expand influences outwards 338 FloodFill(entityGrid, influenceGrid, openTiles, falloff); 339 340 // TODO: we should do a sparse grid and only add the non-zero regions, for performance 341 for (u16 j = 0; j < entityGrid.m_H; ++j) 342 for (u16 i = 0; i < entityGrid.m_W; ++i) 343 playerGrid.set(i, j, playerGrid.get(i, j) + entityGrid.get(i, j)); 344 } 345 346 playerGrids.push_back(std::make_pair(it->first, playerGrid)); 347 } 348 349 // Set m_Territories to the player ID with the highest influence for each tile 350 for (u16 j = 0; j < tilesH; ++j) 351 { 352 for (u16 i = 0; i < tilesW; ++i) 353 { 354 u32 bestWeight = 0; 355 for (size_t k = 0; k < playerGrids.size(); ++k) 356 { 357 u32 w = playerGrids[k].second.get(i, j); 358 if (w > bestWeight) 359 { 360 player_id_t id = playerGrids[k].first; 361 m_Territories->set(i, j, (u8)id); 362 bestWeight = w; 363 } 364 } 365 } 315 366 } 316 367 } … … 337 388 338 389 339 void CCmpTerritoryManager::RasteriseInfluences(Grid<u8>& grid) 340 { 341 CComponentManager::InterfaceList infls = GetSimContext().GetComponentManager().GetEntitiesWithInterface(IID_TerritoryInfluence); 390 void CCmpTerritoryManager::RasteriseInfluences(CComponentManager::InterfaceList& infls, Grid<u8>& grid) 391 { 342 392 for (CComponentManager::InterfaceList::iterator it = infls.begin(); it != infls.end(); ++it) 343 393 { 344 394 ICmpTerritoryInfluence* cmpTerritoryInfluence = static_cast<ICmpTerritoryInfluence*>(it->second); 395 396 int cost = cmpTerritoryInfluence->GetCost(); 397 if (cost == -1) 398 continue; 345 399 346 400 CmpPtr<ICmpObstruction> cmpObstruction(GetSimContext(), it->first); … … 351 405 if (!cmpObstruction->GetObstructionSquare(square)) 352 406 continue; 353 354 u8 cost = cmpTerritoryInfluence->GetCost();355 407 356 408 CFixedVector2D halfSize(square.hw, square.hh); … … 367 419 TileCenter(i, j, x, z); 368 420 if (Geometry::PointIsInSquare(CFixedVector2D(x - square.x, z - square.z), square.u, square.v, halfSize)) 369 grid.set(i, j, cost +1);421 grid.set(i, j, cost); 370 422 } 371 423 } -
ps/trunk/source/simulation2/components/ICmpTerritoryInfluence.h
r9889 r9906 24 24 { 25 25 public: 26 virtual u8 GetCost() = 0; 26 /** 27 * Returns either -1 to indicate no special terrain cost, or a value 28 * in [0, 255] to indicate overriding the normal cost of the terrain 29 * under the entity's obstruction. 30 */ 31 virtual int GetCost() = 0; 32 33 virtual u32 GetWeight() = 0; 34 35 virtual u32 GetRadius() = 0; 27 36 28 37 DECLARE_INTERFACE_TYPE(TerritoryInfluence) -
ps/trunk/source/simulation2/helpers/PriorityQueue.h
r9362 r9906 30 30 #endif 31 31 32 template <typename Item >32 template <typename Item, typename CMP> 33 33 struct QueueItemPriority 34 34 { 35 35 bool operator()(const Item& a, const Item& b) 36 36 { 37 if ( a.rank > b.rank) // higher costs are lower priority37 if (CMP()(b.rank, a.rank)) // higher costs are lower priority 38 38 return true; 39 if ( a.rank < b.rank)39 if (CMP()(a.rank, b.rank)) 40 40 return false; 41 41 // Need to tie-break to get a consistent ordering … … 58 58 * so we shouldn't use it unless we reimplement the heap functions more efficiently. 59 59 */ 60 template <typename ID, typename R >60 template <typename ID, typename R, typename CMP = std::less<R> > 61 61 class PriorityQueueHeap 62 62 { … … 71 71 { 72 72 m_Heap.push_back(item); 73 push_heap(m_Heap.begin(), m_Heap.end(), QueueItemPriority<Item >());73 push_heap(m_Heap.begin(), m_Heap.end(), QueueItemPriority<Item, CMP>()); 74 74 } 75 75 … … 94 94 #endif 95 95 m_Heap[n].rank = newrank; 96 push_heap(m_Heap.begin(), m_Heap.begin()+n+1, QueueItemPriority<Item >());96 push_heap(m_Heap.begin(), m_Heap.begin()+n+1, QueueItemPriority<Item, CMP>()); 97 97 return; 98 98 } … … 106 106 #endif 107 107 Item r = m_Heap.front(); 108 pop_heap(m_Heap.begin(), m_Heap.end(), QueueItemPriority<Item >());108 pop_heap(m_Heap.begin(), m_Heap.end(), QueueItemPriority<Item, CMP>()); 109 109 m_Heap.pop_back(); 110 110 return r; … … 131 131 * much simpler and less susceptible to MSVC's painfully slow debug STL. 132 132 */ 133 template <typename ID, typename R >133 template <typename ID, typename R, typename CMP = std::less<R> > 134 134 class PriorityQueueList 135 135 { … … 172 172 for (ssize_t i = (ssize_t)bestidx-1; i >= 0; --i) 173 173 { 174 if (QueueItemPriority<Item >()(best, m_List[i]))174 if (QueueItemPriority<Item, CMP>()(best, m_List[i])) 175 175 { 176 176 bestidx = i;
Note:
See TracChangeset
for help on using the changeset viewer.
