Ticket #3204: territory_neighbours.5.diff

File territory_neighbours.5.diff, 39.3 KB (added by sanderd17, 9 years ago)
  • binaries/data/mods/public/simulation/components/Capturable.js

     
    1616    // Cache this value
    1717    this.maxCp = +this.template.CapturePoints;
    1818    this.cp = [];
    19     this.StartRegenTimer();
    2019};
    2120
    2221//// Interface functions ////
     
    5655 */
    5756Capturable.prototype.Reduce = function(amount, playerID)
    5857{
     58    if (amount <= 0)
     59        return 0;
     60
    5961    var cmpOwnership = Engine.QueryInterface(this.entity, IID_Ownership);
    6062    if (!cmpOwnership || cmpOwnership.GetOwner() == -1)
    6163        return 0;
     
    6971    if (cmpFogging)
    7072        cmpFogging.Activate();
    7173
    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;
    7475
    7576    if (numberOfEnemies == 0)
    7677        return 0;
     
    8889    }
    8990
    9091    // 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);
    9293    this.cp[playerID] += takenCp;
    9394
    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();
    10997    return takenCp;
    11098};
    11199
     
    128116
    129117//// Private functions ////
    130118
     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 */
     123Capturable.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
    131143Capturable.prototype.GetRegenRate = function()
    132144{
    133145    var regenRate = +this.template.RegenRate;
    134146    regenRate = ApplyValueModificationsToEntity("Capturable/RegenRate", regenRate, this.entity);
    135147
    136     var cmpTerritoryDecay = Engine.QueryInterface(this.entity, IID_TerritoryDecay);
    137     if (cmpTerritoryDecay && cmpTerritoryDecay.IsDecaying())
    138         var territoryDecayRate = cmpTerritoryDecay.GetDecayRate();
    139     else
    140         var territoryDecayRate = 0;
    141 
    142148    var cmpGarrisonHolder = Engine.QueryInterface(this.entity, IID_GarrisonHolder);
    143149    if (cmpGarrisonHolder)
    144150        var garrisonRegenRate = this.GetGarrisonRegenRate() * cmpGarrisonHolder.GetEntities().length;
     
    145151    else
    146152        var garrisonRegenRate = 0;
    147153
    148     return regenRate + garrisonRegenRate - territoryDecayRate;
     154    return regenRate + garrisonRegenRate;
    149155};
    150156
    151 Capturable.prototype.RegenCapturePoints = function()
     157Capturable.prototype.TimerTick = function()
    152158{
    153159    var cmpOwnership = Engine.QueryInterface(this.entity, IID_Ownership);
    154160    if (!cmpOwnership || cmpOwnership.GetOwner() == -1)
    155161        return;
    156162
     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
    157181    var regenRate = this.GetRegenRate();
    158182    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)
    162186
    163     if (takenCp > 0)
     187    if (modifiedCp)
    164188        return;
    165189
    166     // no capture points taken, stop the timer
     190    // nothing changed, stop the timer
    167191    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, "rate": 0});
     192    cmpTimer.CancelTimer(this.timer);
     193    this.timer = 0;
     194    Engine.PostMessage(this.entity, MT_CaptureRegenStateChanged, {"regenerating": false, "regenRate": 0, "territoryDecay": 0});
    171195};
    172196
    173197/**
    174  * Start the regeneration timer when no timer exists
    175  * When nothing can be regenerated (f.e. because the
    176  * rate is 0, or because it is fully regenerated),
    177  * the timer stops automatically after one execution.
     198 * Start the regeneration timer when no timer exists.
     199 * When nothing can be modified (f.e. because it is fully regenerated), the
     200 * timer stops automatically after one execution.
    178201 */
    179 Capturable.prototype.StartRegenTimer = function()
     202Capturable.prototype.CheckTimer = function()
    180203{
    181     if (this.regenTimer)
     204    if (this.timer)
    182205        return;
    183206
    184     var rate = this.GetRegenRate();
    185     if (rate == 0)
     207    var regenRate = this.GetRegenRate();
     208    var cmpDecay = Engine.QueryInterface(this.entity, IID_TerritoryDecay);
     209    var decay = cmpDecay && cmpDecay.IsDecaying() ? cmpDecay.GetDecayRate() : 0;
     210    if (regenRate == 0 && decay == 0)
    186211        return;
    187212
    188213    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});
     214    this.timer = cmpTimer.SetInterval(this.entity, IID_Capturable, "TimerTick", 1000, 1000, null);
     215    Engine.PostMessage(this.entity, MT_CaptureRegenStateChanged, {"ticking": true, "regenRate": regenRate, "territoryDecay": decay});
    191216};
    192217
    193218//// Message Listeners ////
     
    206231    for (let i in this.cp)
    207232        this.cp[i] *= scale;
    208233    Engine.PostMessage(this.entity, MT_CapturePointsChanged, { "capturePoints": this.cp });
    209     this.StartRegenTimer();
     234    this.CheckTimer();
    210235};
    211236
    212237Capturable.prototype.OnGarrisonedUnitsChanged = function(msg)
    213238{
    214     this.StartRegenTimer();
     239    this.CheckTimer();
    215240};
    216241
    217242Capturable.prototype.OnTerritoryDecayChanged = function(msg)
    218243{
    219244    if (msg.to)
    220         this.StartRegenTimer();
     245        this.CheckTimer();
    221246};
    222247
    223248Capturable.prototype.OnOwnershipChanged = function(msg)
    224249{
    225     this.StartRegenTimer();
     250    this.CheckTimer();
    226251
    227252    // if the new owner has no capture points, it means that either
    228253    // * it's being initialised now, or
  • binaries/data/mods/public/simulation/components/TerritoryDecay.js

     
    88TerritoryDecay.prototype.Init = function()
    99{
    1010    this.decaying = false;
     11    this.connectedNeighbours = [];
    1112};
    1213
    1314TerritoryDecay.prototype.IsConnected = function()
    1415{
     16    var numPlayers = Engine.QueryInterface(SYSTEM_ENTITY, IID_PlayerManager).GetNumPlayers();
     17    this.connectedNeighbours.fill(0, 0, numPlayers);
     18
    1519    var cmpPosition = Engine.QueryInterface(this.entity, IID_Position);
    1620    if (!cmpPosition || !cmpPosition.IsInWorld())
    1721        return false;
     
    1822
    1923    var cmpOwnership = Engine.QueryInterface(this.entity, IID_Ownership);
    2024    if (!cmpOwnership)
    21         return false;
     25        return true; // something without ownership can't decay
    2226       
    23     // Prevent special gaia buildings from decaying (e.g. fences, ruins)
    24     if (cmpOwnership.GetOwner() == 0)
    25         return true;
    26 
    2727    var cmpTerritoryManager = Engine.QueryInterface(SYSTEM_ENTITY, IID_TerritoryManager);
    28     if (!cmpTerritoryManager)
    29         return false;
    30 
    3128    var pos = cmpPosition.GetPosition2D();
    3229    var tileOwner = cmpTerritoryManager.GetOwner(pos.x, pos.y);
    3330    if (tileOwner != cmpOwnership.GetOwner())
     31    {
     32        this.connectedNeighbours[tileOwner] = 1;
    3433        return false;
     34    }
    3535
    36     return cmpTerritoryManager.IsConnected(pos.x, pos.y);
     36    if (tileOwner == 0)
     37        return true; // Gaia building on gaia ground -> don't decay
     38
     39    if (cmpTerritoryManager.IsConnected(pos.x, pos.y))
     40        return true;
     41
     42    this.connectedNeighbours = cmpTerritoryManager.GetNeighbours(pos.x, pos.y, true);
     43    return false;
    3744};
    3845
    3946TerritoryDecay.prototype.IsDecaying = function()
     
    4956        this.entity);
    5057};
    5158
     59/**
     60 * Get the number of connected bordering tiles to this region
     61 * Only valid when this.IsDecaying()
     62 */
     63TerritoryDecay.prototype.GetConnectedNeighbours = function()
     64{
     65    return this.connectedNeighbours;
     66};
     67
    5268TerritoryDecay.prototype.UpdateDecayState = function()
    5369{
    5470    if (this.IsConnected())
     
    5874    if (decaying === this.decaying)
    5975        return;
    6076    this.decaying = decaying;
    61     Engine.PostMessage(this.entity, MT_TerritoryDecayChanged, { "entity": this.entity, "to": decaying });
     77    Engine.PostMessage(this.entity, MT_TerritoryDecayChanged, { "entity": this.entity, "to": decaying, "rate": this.GetDecayRate() });
    6278};
    6379
    6480TerritoryDecay.prototype.OnTerritoriesChanged = function(msg)
     
    7389
    7490TerritoryDecay.prototype.OnOwnershipChanged = function(msg)
    7591{
    76     this.UpdateDecayState();
     92    // if it influences the territory, wait until we get a TerritoriesChanged message
     93    if (!Engine.QueryInterface(this.entity, IID_TerritoryInfluence))
     94        this.UpdateDecayState();
    7795};
    7896
    7997Engine.RegisterComponentType(IID_TerritoryDecay, "TerritoryDecay", TerritoryDecay);
  • binaries/data/mods/public/simulation/templates/special/territory_block.xml

     
    1 <?xml version="1.0" encoding="utf-8"?>
    2 <Entity>
    3   <Footprint>
    4     <Square width="64" depth="8"/>
    5     <Height>9.0</Height>
    6   </Footprint>
    7   <Obstruction>
    8     <Static width="64" depth="8"/>
    9     <Active>true</Active>
    10     <BlockMovement>false</BlockMovement>
    11     <BlockPathfinding>false</BlockPathfinding>
    12     <BlockFoundation>false</BlockFoundation>
    13     <BlockConstruction>false</BlockConstruction>
    14     <DisableBlockMovement>false</DisableBlockMovement>
    15     <DisableBlockPathfinding>false</DisableBlockPathfinding>
    16   </Obstruction>
    17   <Ownership/>
    18   <Position>
    19     <Altitude>0</Altitude>
    20     <Anchor>upright</Anchor>
    21     <Floating>false</Floating>
    22     <TurnRate>6.0</TurnRate>
    23   </Position>
    24   <Selectable>
    25     <EditorOnly/>
    26     <Overlay>
    27       <Texture>
    28         <MainTexture>circle/128x128.png</MainTexture>
    29         <MainTextureMask>circle/128x128_mask.png</MainTextureMask>
    30       </Texture>
    31     </Overlay>
    32   </Selectable>
    33   <TerritoryInfluence>
    34     <OverrideCost>64</OverrideCost>
    35     <Root>false</Root>
    36     <Weight>0</Weight>
    37     <Radius>0</Radius>
    38   </TerritoryInfluence>
    39   <VisualActor>
    40     <SilhouetteDisplay>false</SilhouetteDisplay>
    41     <SilhouetteOccluder>true</SilhouetteOccluder>
    42     <Actor>structures/hellenes/wall_medium.xml</Actor>
    43     <VisibleInAtlasOnly>true</VisibleInAtlasOnly>
    44   </VisualActor>
    45 </Entity>
  • binaries/data/mods/public/simulation/templates/special/territory_pull.xml

     
    11<?xml version="1.0" encoding="utf-8"?>
    22<Entity>
    3   <Footprint>
    4     <Square width="64" depth="8"/>
    5     <Height>9.0</Height>
    6   </Footprint>
    7   <Obstruction>
    8     <Static width="64" depth="8"/>
    9     <Active>true</Active>
    10     <BlockMovement>false</BlockMovement>
    11     <BlockPathfinding>false</BlockPathfinding>
    12     <BlockFoundation>false</BlockFoundation>
    13     <BlockConstruction>false</BlockConstruction>
    14     <DisableBlockMovement>false</DisableBlockMovement>
    15     <DisableBlockPathfinding>false</DisableBlockPathfinding>
    16   </Obstruction>
     3  <Capturable>
     4    <CapturePoints>5</CapturePoints>
     5    <RegenRate>0</RegenRate>
     6    <GarrisonRegenRate>0</GarrisonRegenRate>
     7  </Capturable>
    178  <Ownership/>
    189  <Position>
    1910    <Altitude>0</Altitude>
     
    3021      </Texture>
    3122    </Overlay>
    3223  </Selectable>
     24  <TerritoryDecay>
     25    <DecayRate>5</DecayRate>
     26  </TerritoryDecay>
    3327  <TerritoryInfluence>
    34     <OverrideCost>0</OverrideCost>
    3528    <Root>false</Root>
    36     <Weight>0</Weight>
    37     <Radius>0</Radius>
     29    <Radius>32</Radius>
     30    <Weight>30000</Weight>
    3831  </TerritoryInfluence>
    3932  <VisualActor>
    4033    <SilhouetteDisplay>false</SilhouetteDisplay>
    4134    <SilhouetteOccluder>true</SilhouetteOccluder>
    42     <Actor>structures/hellenes/wall_medium.xml</Actor>
     35    <Actor>props/special/common/waypoint_flag.xml</Actor>
    4336    <VisibleInAtlasOnly>true</VisibleInAtlasOnly>
    4437  </VisualActor>
    4538</Entity>
  • source/simulation2/components/CCmpTerritoryInfluence.cpp

     
    3333
    3434    DEFAULT_COMPONENT_ALLOCATOR(TerritoryInfluence)
    3535
    36     i32 m_Cost;
    3736    bool m_Root;
    38     u32 m_Weight;
     37    u16 m_Weight;
    3938    u32 m_Radius;
    4039
    4140    static std::string GetSchema()
    4241    {
    4342        return
    44             "<optional>"
    45                 "<element name='OverrideCost'>"
    46                     "<data type='nonNegativeInteger'>"
    47                         "<param name='maxInclusive'>255</param>"
    48                     "</data>"
    49                 "</element>"
    50             "</optional>"
    5143            "<element name='Root'>"
    5244                "<data type='boolean'/>"
    5345            "</element>"
    5446            "<element name='Weight'>"
    55                 "<data type='nonNegativeInteger'/>"
     47                "<data type='nonNegativeInteger'>"
     48                    "<param name='maxInclusive'>65536</param>" // Max value 2^16
     49                "</data>"
    5650            "</element>"
    5751            "<element name='Radius'>"
    5852                "<data type='nonNegativeInteger'/>"
     
    6155
    6256    virtual void Init(const CParamNode& paramNode)
    6357    {
    64         if (paramNode.GetChild("OverrideCost").IsOk())
    65             m_Cost = paramNode.GetChild("OverrideCost").ToInt();
    66         else
    67             m_Cost = -1;
    68 
    6958        m_Root = paramNode.GetChild("Root").ToBool();
    70         m_Weight = paramNode.GetChild("Weight").ToInt();
     59        m_Weight = (u16)paramNode.GetChild("Weight").ToInt();
    7160        m_Radius = paramNode.GetChild("Radius").ToInt();
    7261    }
    7362
     
    8473        Init(paramNode);
    8574    }
    8675
    87     virtual i32 GetCost()
    88     {
    89         return m_Cost;
    90     }
    91 
    9276    virtual bool IsRoot()
    9377    {
    9478        return m_Root;
    9579    }
    9680
    97     virtual u32 GetWeight()
     81    virtual u16 GetWeight()
    9882    {
    9983        return m_Weight;
    10084    }
  • source/simulation2/components/CCmpTerritoryManager.cpp

     
    1 /* Copyright (C) 2012 Wildfire Games.
     1/* Copyright (C) 2015 Wildfire Games.
    22 * This file is part of 0 A.D.
    33 *
    44 * 0 A.D. is free software: you can redistribute it and/or modify
     
    2525#include "graphics/TextureManager.h"
    2626#include "graphics/TerritoryBoundary.h"
    2727#include "maths/MathUtil.h"
    28 #include "maths/Vector2D.h"
    2928#include "renderer/Renderer.h"
    3029#include "renderer/Scene.h"
    3130#include "renderer/TerrainOverlay.h"
    3231#include "simulation2/MessageTypes.h"
    33 #include "simulation2/components/ICmpObstruction.h"
    34 #include "simulation2/components/ICmpObstructionManager.h"
    3532#include "simulation2/components/ICmpOwnership.h"
    3633#include "simulation2/components/ICmpPathfinder.h"
    3734#include "simulation2/components/ICmpPlayer.h"
    3835#include "simulation2/components/ICmpPlayerManager.h"
    3936#include "simulation2/components/ICmpPosition.h"
    40 #include "simulation2/components/ICmpSettlement.h"
    4137#include "simulation2/components/ICmpTerrain.h"
    4238#include "simulation2/components/ICmpTerritoryInfluence.h"
    4339#include "simulation2/helpers/Geometry.h"
    4440#include "simulation2/helpers/Grid.h"
    45 #include "simulation2/helpers/PriorityQueue.h"
    4641#include "simulation2/helpers/Render.h"
     42#include "ps/CLogger.h"
    4743
    4844class CCmpTerritoryManager;
    4945
     
    207203    // ignore any others
    208204    void MakeDirtyIfRelevantEntity(entity_id_t ent)
    209205    {
    210         CmpPtr<ICmpSettlement> cmpSettlement(GetSimContext(), ent);
    211         if (cmpSettlement)
    212             MakeDirty();
    213 
    214206        CmpPtr<ICmpTerritoryInfluence> cmpTerritoryInfluence(GetSimContext(), ent);
    215207        if (cmpTerritoryInfluence)
    216208            MakeDirty();
     
    224216    }
    225217
    226218    virtual player_id_t GetOwner(entity_pos_t x, entity_pos_t z);
     219    virtual std::vector<u32> GetNeighbours(entity_pos_t x, entity_pos_t z, bool filterConnected);
    227220    virtual bool IsConnected(entity_pos_t x, entity_pos_t z);
    228221
    229222    // To support lazy updates of territory render data,
     
    252245
    253246    void CalculateTerritories();
    254247
    255     /**
    256      * Updates @p grid based on the obstruction shapes of all entities with
    257      * a TerritoryInfluence component. Grid cells are 0 if no influence,
    258      * or 1+c if the influence have cost c (assumed between 0 and 254).
    259      */
    260     void RasteriseInfluences(CComponentManager::InterfaceList& infls, Grid<u8>& grid);
    261 
    262248    std::vector<STerritoryBoundary> ComputeBoundaries();
    263249
    264250    void UpdateBoundaryLines();
     
    270256
    271257REGISTER_COMPONENT_TYPE(TerritoryManager)
    272258
    273 /*
    274 We compute the territory influence of an entity with a kind of best-first search,
    275 storing an 'open' list of tiles that have not yet been processed,
    276 then taking the highest-weight tile (closest to origin) and updating the weight
    277 of extending to each neighbour (based on radius-determining 'falloff' value,
    278 adjusted by terrain movement cost), and repeating until all tiles are processed.
    279 */
    280 
    281 typedef PriorityQueueHeap<std::pair<u16, u16>, u32, std::greater<u32> > OpenQueue;
    282 
    283 static void ProcessNeighbour(u32 falloff, u16 i, u16 j, u32 pg, bool diagonal,
    284         Grid<u32>& grid, OpenQueue& queue, const Grid<u8>& costGrid)
     259// Tile data type, for easier accessing of coordinates
     260struct Tile
    285261{
    286     u32 dg = falloff * costGrid.get(i, j);
    287     if (diagonal)
    288         dg = (dg * 362) / 256;
     262    Tile(u16 i, u16 j) : x(i), z(j) { }
     263    u16 x, z;
     264};
    289265
    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;
     266// Floodfill templates that expand neighbours from a certain source onwards
     267// (x, z) are the coordinates of the currently expanded tile
     268// (nx, nz) are the coordinates of the current neighbour handled
     269// The user of this floodfill should use "continue" on every neighbour that
     270// shouldn't be expanded on its own. (without continue, an infinite loop will happen)
     271# define FLOODFILL(i, j, code)\
     272    do {\
     273        const int NUM_NEIGHBOURS = 8;\
     274        const int NEIGHBOURS_X[NUM_NEIGHBOURS] = {1,-1, 0, 0, 1,-1, 1,-1};\
     275        const int NEIGHBOURS_Z[NUM_NEIGHBOURS] = {0, 0, 1,-1, 1,-1,-1, 1};\
     276        std::queue<Tile> openTiles;\
     277        openTiles.emplace(i, j);\
     278        while (!openTiles.empty())\
     279        {\
     280            u16 x = openTiles.front().x;\
     281            u16 z = openTiles.front().z;\
     282            openTiles.pop();\
     283            for (int n = 0; n < NUM_NEIGHBOURS; ++n)\
     284            {\
     285                u16 nx = x + NEIGHBOURS_X[n];\
     286                u16 nz = z + NEIGHBOURS_Z[n];\
     287                /* Check the bounds, underflow will cause the values to be big again */\
     288                if (nx >= tilesW || nz >= tilesH)\
     289                    continue;\
     290                code\
     291                openTiles.emplace(nx, nz);\
     292            }\
     293        }\
     294    }\
     295    while (false)
    294296
    295     u32 g = pg - dg; // cost to this tile = cost to predecessor - falloff from predecessor
    296 
    297     grid.set(i, j, g);
    298     OpenQueue::Item tile = { std::make_pair(i, j), g };
    299     queue.push(tile);
    300 }
    301 
    302 static void FloodFill(Grid<u32>& grid, Grid<u8>& costGrid, OpenQueue& openTiles, u32 falloff)
     297/**
     298 * Compute the tile indexes on the grid nearest to a given point
     299 */
     300static void NearestTile(entity_pos_t x, entity_pos_t z, u16& i, u16& j, u16 w, u16 h)
    303301{
    304     u16 tilesW = grid.m_W;
    305     u16 tilesH = grid.m_H;
    306 
    307     while (!openTiles.empty())
    308     {
    309         OpenQueue::Item tile = openTiles.pop();
    310 
    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);
    330     }
     302    i = (u16)clamp((x / (int)TERRAIN_TILE_SIZE).ToInt_RoundToZero(), 0, w-1);
     303    j = (u16)clamp((z / (int)TERRAIN_TILE_SIZE).ToInt_RoundToZero(), 0, h-1);
    331304}
    332305
    333306void CCmpTerritoryManager::CalculateTerritories()
     
    344317    if (!cmpTerrain->IsLoaded())
    345318        return;
    346319
    347     u16 tilesW = cmpTerrain->GetTilesPerSide();
    348     u16 tilesH = cmpTerrain->GetTilesPerSide();
     320    const u16 tilesW = cmpTerrain->GetTilesPerSide();
     321    const u16 tilesH = tilesW;
    349322
    350323    m_Territories = new Grid<u8>(tilesW, tilesH);
    351324
    352     // Compute terrain-passability-dependent costs per tile
    353     Grid<u8> influenceGrid(tilesW, tilesH);
    354 
    355     CmpPtr<ICmpPathfinder> cmpPathfinder(GetSystemEntity());
    356     ICmpPathfinder::pass_class_t passClassDefault = cmpPathfinder->GetPassabilityClass("default");
    357     ICmpPathfinder::pass_class_t passClassUnrestricted = cmpPathfinder->GetPassabilityClass("unrestricted");
    358 
    359     const Grid<u16>& passGrid = cmpPathfinder->GetPassabilityGrid();
    360     for (u16 j = 0; j < tilesH; ++j)
    361     {
    362         for (u16 i = 0; i < tilesW; ++i)
    363         {
    364             u16 g = passGrid.get(i, j);
    365             u8 cost;
    366             if (g & passClassUnrestricted)
    367                 cost = 255; // off the world; use maximum cost
    368             else if (g & passClassDefault)
    369                 cost = m_ImpassableCost;
    370             else
    371                 cost = 1;
    372             influenceGrid.set(i, j, cost);
    373         }
    374     }
    375 
    376325    // Find all territory influence entities
    377326    CComponentManager::InterfaceList influences = GetSimContext().GetComponentManager().GetEntitiesWithInterface(IID_TerritoryInfluence);
    378327
    379     // Allow influence entities to override the terrain costs
    380     RasteriseInfluences(influences, influenceGrid);
    381 
    382328    // Split influence entities into per-player lists, ignoring any with invalid properties
    383329    std::map<player_id_t, std::vector<entity_id_t> > influenceEntities;
    384     std::vector<entity_id_t> rootInfluenceEntities;
    385     for (CComponentManager::InterfaceList::iterator it = influences.begin(); it != influences.end(); ++it)
     330    for (const CComponentManager::InterfacePair& pair : influences)
    386331    {
    387         // Ignore any with no weight or radius (to avoid divide-by-zero later)
    388         ICmpTerritoryInfluence* cmpTerritoryInfluence = static_cast<ICmpTerritoryInfluence*>(it->second);
    389         if (cmpTerritoryInfluence->GetWeight() == 0 || cmpTerritoryInfluence->GetRadius() == 0)
    390             continue;
     332        entity_id_t ent = pair.first;
    391333
    392         CmpPtr<ICmpOwnership> cmpOwnership(GetSimContext(), it->first);
     334        CmpPtr<ICmpOwnership> cmpOwnership(GetSimContext(), ent);
    393335        if (!cmpOwnership)
    394336            continue;
    395337
    396         // Ignore Gaia and unassigned
     338        // Ignore Gaia and unassigned or players we can't represent
    397339        player_id_t owner = cmpOwnership->GetOwner();
    398         if (owner <= 0)
     340        if (owner <= 0 || owner > TERRITORY_PLAYER_MASK)
    399341            continue;
    400342
    401         // We only have so many bits to store tile ownership, so ignore unrepresentable players
    402         if (owner > TERRITORY_PLAYER_MASK)
    403             continue;
     343        influenceEntities[owner].push_back(ent);
     344    }
    404345
    405         // Ignore if invalid position
    406         CmpPtr<ICmpPosition> cmpPosition(GetSimContext(), it->first);
    407         if (!cmpPosition || !cmpPosition->IsInWorld())
    408             continue;
     346    // Store the overall best weight for comparison
     347    Grid<u32> bestWeightGrid(tilesW, tilesH);
     348    // store the root influences to mark territory as connected
     349    std::vector<entity_id_t> rootInfluenceEntities;
    409350
    410         influenceEntities[owner].push_back(it->first);
     351    CmpPtr<ICmpPathfinder> cmpPathfinder(GetSystemEntity());
     352    if (!cmpPathfinder)
     353        return;
    411354
    412         if (cmpTerritoryInfluence->IsRoot())
    413             rootInfluenceEntities.push_back(it->first);
    414     }
     355    ICmpPathfinder::pass_class_t passClassDefault = cmpPathfinder->GetPassabilityClass("default");
     356    ICmpPathfinder::pass_class_t passClassUnrestricted = cmpPathfinder->GetPassabilityClass("unrestricted");
    415357
    416     // For each player, store the sum of influences on each tile
    417     std::vector<std::pair<player_id_t, Grid<u32> > > playerGrids;
    418     // TODO: this is a large waste of memory; we don't really need to store
    419     // all the intermediate grids
     358    const Grid<u16>& passibilityGrid = cmpPathfinder->GetPassabilityGrid();
    420359
    421     for (std::map<player_id_t, std::vector<entity_id_t> >::iterator it = influenceEntities.begin(); it != influenceEntities.end(); ++it)
     360    for (const std::pair<player_id_t, std::vector<entity_id_t> >& pair : influenceEntities)
    422361    {
     362        // entityGrid stores the weight for a single entity, and is reset per entity
     363        Grid<u32> entityGrid(tilesW, tilesH);
     364        // playerGrid stores the combined weight of all entities for this player
    423365        Grid<u32> playerGrid(tilesW, tilesH);
    424366
    425         std::vector<entity_id_t>& ents = it->second;
    426         for (std::vector<entity_id_t>::iterator eit = ents.begin(); eit != ents.end(); ++eit)
     367        u8 owner = (u8)pair.first;
     368        const std::vector<entity_id_t>& ents = pair.second;
     369        // With 2^16 entities, we're safe against overflows as the weight is also limited to 2^16
     370        ENSURE(ents.size() < 1 << 16);
     371        // Compute the influence map of the current entity, then add it to the player grid
     372        for (entity_id_t ent : ents)
    427373        {
    428             // Compute the influence map of the current entity, then add it to the player grid
     374            CmpPtr<ICmpPosition> cmpPosition(GetSimContext(), ent);
     375            if (!cmpPosition || !cmpPosition->IsInWorld())
     376                continue;
    429377
    430             Grid<u32> entityGrid(tilesW, tilesH);
     378            CmpPtr<ICmpTerritoryInfluence> cmpTerritoryInfluence(GetSimContext(), ent);
     379            u32 weight = cmpTerritoryInfluence->GetWeight();
     380            u32 radius = cmpTerritoryInfluence->GetRadius() / TERRAIN_TILE_SIZE;
     381            if (weight == 0 || radius == 0)
     382                continue;
     383            u32 falloff = weight / radius;
    431384
    432             CmpPtr<ICmpPosition> cmpPosition(GetSimContext(), *eit);
    433385            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);
     386            u16 i, j;
     387            NearestTile(pos.X, pos.Y, i, j, tilesW, tilesH);
    436388
    437             CmpPtr<ICmpTerritoryInfluence> cmpTerritoryInfluence(GetSimContext(), *eit);
    438             u32 weight = cmpTerritoryInfluence->GetWeight();
    439             u32 radius = cmpTerritoryInfluence->GetRadius() / TERRAIN_TILE_SIZE;
    440             u32 falloff = weight / radius; // earlier check for GetRadius() == 0 prevents divide-by-zero
     389            if (cmpTerritoryInfluence->IsRoot())
     390                rootInfluenceEntities.push_back(ent);
    441391
    442             // TODO: we should have some maximum value on weight, to avoid overflow
    443             // when doing all the sums
    444 
    445392            // Initialise the tile under the entity
    446393            entityGrid.set(i, j, weight);
    447             OpenQueue openTiles;
    448             OpenQueue::Item tile = { std::make_pair((u16)i, (i16)j), weight };
    449             openTiles.push(tile);
     394            if (weight > bestWeightGrid.get(i, j))
     395            {
     396                bestWeightGrid.set(i, j, weight);
     397                m_Territories->set(i, j, owner);
     398            }
    450399
    451400            // Expand influences outwards
    452             FloodFill(entityGrid, influenceGrid, openTiles, falloff);
     401            FLOODFILL(i, j,
     402                u32 dg = falloff;
     403                // enlarge the falloff for unpassable tiles
     404                u16 g = passibilityGrid.get(nx, nz);
     405                if (g & passClassUnrestricted)
     406                    dg *= 255; // off the world; use maximum cost
     407                else if (g & passClassDefault)
     408                    dg *= m_ImpassableCost;
    453409
    454             // TODO: we should do a sparse grid and only add the non-zero regions, for performance
    455             playerGrid.add(entityGrid);
    456         }
     410                // diagonal neighbour -> multiply with approx sqrt(2)
     411                if (nx != x && nz != z)
     412                    dg = (dg * 362) / 256;
    457413
    458         playerGrids.push_back(std::make_pair(it->first, playerGrid));
    459     }
     414                // Don't expand if new cost is not better than previous value for that tile
     415                // (arranged to avoid underflow if entityGrid.get(x, z) < dg)
     416                if (entityGrid.get(x, z) <= entityGrid.get(nx, nz) + dg)
     417                    continue;
    460418
    461     // Set m_Territories to the player ID with the highest influence for each tile
    462     for (u16 j = 0; j < tilesH; ++j)
    463     {
    464         for (u16 i = 0; i < tilesW; ++i)
    465         {
    466             u32 bestWeight = 0;
    467             for (size_t k = 0; k < playerGrids.size(); ++k)
    468             {
    469                 u32 w = playerGrids[k].second.get(i, j);
    470                 if (w > bestWeight)
     419                // weight of this tile = weight of predecessor - falloff from predecessor
     420                u32 newWeight = entityGrid.get(x, z) - dg;
     421                u32 totalWeight = playerGrid.get(nx, nz) - entityGrid.get(nx, nz) + newWeight;
     422                playerGrid.set(nx, nz, totalWeight);
     423                entityGrid.set(nx, nz, newWeight);
     424                // if this weight is better than the best thus far, set the owner
     425                if (totalWeight > bestWeightGrid.get(nx, nz))
    471426                {
    472                     player_id_t id = playerGrids[k].first;
    473                     m_Territories->set(i, j, (u8)id);
    474                     bestWeight = w;
     427                    bestWeightGrid.set(nx, nz, totalWeight);
     428                    m_Territories->set(nx, nz, owner);
    475429                }
    476             }
     430            );
     431
     432            entityGrid.reset();
    477433        }
    478434    }
    479435
    480436    // Detect territories connected to a 'root' influence (typically a civ center)
    481437    // 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)
     438    for (entity_id_t ent : rootInfluenceEntities)
    483439    {
     440        LOGWARNING("%i", ent);
    484441        // (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);
     442        CmpPtr<ICmpOwnership> cmpOwnership(GetSimContext(), ent);
     443        CmpPtr<ICmpPosition> cmpPosition(GetSimContext(), ent);
    487444
    488445        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);
     446        u16 i, j;
     447        NearestTile(pos.X, pos.Y, i, j, tilesW, tilesH);
    491448
    492449        u8 owner = (u8)cmpOwnership->GetOwner();
    493450
     
    494451        if (m_Territories->get(i, j) != owner)
    495452            continue;
    496453
    497         // TODO: would be nice to refactor some of the many flood fill
    498         // algorithms in this component
     454        m_Territories->set(i, j, owner | TERRITORY_CONNECTED_MASK);
    499455
    500         Grid<u8>& grid = *m_Territories;
    501 
    502         u16 maxi = (u16)(grid.m_W-1);
    503         u16 maxj = (u16)(grid.m_H-1);
    504 
    505         std::vector<std::pair<u16, u16> > tileStack;
    506 
    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         while (!tileStack.empty())
    511         {
    512             int ti = tileStack.back().first;
    513             int tj = tileStack.back().second;
    514             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);
    533         }
    534 
    535 #undef MARK_AND_PUSH
     456        FLOODFILL(i, j,
     457            // Don't expand non-owner tiles, or tiles that already have a connected mask
     458            if (m_Territories->get(nx, nz) != owner)
     459                continue;
     460            m_Territories->set(nx, nz, owner | TERRITORY_CONNECTED_MASK);
     461        );
    536462    }
    537463}
    538464
    539 /**
    540  * Compute the tile indexes on the grid nearest to a given point
    541  */
    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 tile
    550  */
    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.cpp
    558 
    559 
    560 void CCmpTerritoryManager::RasteriseInfluences(CComponentManager::InterfaceList& infls, Grid<u8>& grid)
    561 {
    562     for (CComponentManager::InterfaceList::iterator it = infls.begin(); it != infls.end(); ++it)
    563     {
    564         ICmpTerritoryInfluence* cmpTerritoryInfluence = static_cast<ICmpTerritoryInfluence*>(it->second);
    565 
    566         i32 cost = cmpTerritoryInfluence->GetCost();
    567         if (cost == -1)
    568             continue;
    569 
    570         CmpPtr<ICmpObstruction> cmpObstruction(GetSimContext(), it->first);
    571         if (!cmpObstruction)
    572             continue;
    573 
    574         ICmpObstructionManager::ObstructionSquare square;
    575         if (!cmpObstruction->GetObstructionSquare(square))
    576             continue;
    577 
    578         CFixedVector2D halfSize(square.hw, square.hh);
    579         CFixedVector2D halfBound = Geometry::GetHalfBoundingBox(square.u, square.v, halfSize);
    580 
    581         u16 i0, j0, i1, j1;
    582         NearestTile(square.x - halfBound.X, square.z - halfBound.Y, i0, j0, grid.m_W, grid.m_H);
    583         NearestTile(square.x + halfBound.X, square.z + halfBound.Y, i1, j1, grid.m_W, grid.m_H);
    584         for (u16 j = j0; j <= j1; ++j)
    585         {
    586             for (u16 i = i0; i <= i1; ++i)
    587             {
    588                 entity_pos_t x, z;
    589                 TileCenter(i, j, x, z);
    590                 if (Geometry::PointIsInSquare(CFixedVector2D(x - square.x, z - square.z), square.u, square.v, halfSize))
    591                     grid.set(i, j, (u8)cost);
    592             }
    593         }
    594 
    595     }
    596 }
    597 
    598465std::vector<STerritoryBoundary> CCmpTerritoryManager::ComputeBoundaries()
    599466{
    600467    PROFILE("ComputeBoundaries");
     
    722589    return m_Territories->get(i, j) & TERRITORY_PLAYER_MASK;
    723590}
    724591
     592std::vector<u32> CCmpTerritoryManager::GetNeighbours(entity_pos_t x, entity_pos_t z, bool filterConnected)
     593{
     594    CmpPtr<ICmpPlayerManager> cmpPlayerManager(GetSystemEntity());
     595    if (!cmpPlayerManager)
     596        return std::vector<u32>();
     597
     598    std::vector<u32> ret(cmpPlayerManager->GetNumPlayers(), 0);
     599    CalculateTerritories();
     600    if (!m_Territories)
     601        return ret;
     602
     603    u16 i, j;
     604    NearestTile(x, z, i, j, m_Territories->m_W, m_Territories->m_H);
     605
     606    // calculate the neighbours
     607    player_id_t thisOwner = m_Territories->get(i, j) & TERRITORY_PLAYER_MASK;
     608
     609    u16 tilesW = m_Territories->m_W;
     610    u16 tilesH = m_Territories->m_H;
     611
     612    // use a flood-fill algorithm that fills up to the borders and remembers the owners
     613    Grid<bool> markerGrid(tilesW, tilesH);
     614    markerGrid.set(i, j, true);
     615
     616    FLOODFILL(i, j,
     617        if (markerGrid.get(nx, nz))
     618            continue;
     619        // mark the tile as visited in any case
     620        markerGrid.set(nx, nz, true);
     621        int owner = m_Territories->get(nx, nz) & TERRITORY_PLAYER_MASK;
     622        if (owner != thisOwner)
     623        {
     624            if (owner == 0 || !filterConnected || (m_Territories->get(nx, nz) & TERRITORY_CONNECTED_MASK) != 0)
     625                ret[owner]++; // add player to the neighbour list when requested
     626            continue; // don't expand non-owner tiles further
     627        }
     628    );
     629
     630    return ret;
     631}
     632
    725633bool CCmpTerritoryManager::IsConnected(entity_pos_t x, entity_pos_t z)
    726634{
    727635    u16 i, j;
  • source/simulation2/components/ICmpTerritoryInfluence.h

     
    1 /* Copyright (C) 2011 Wildfire Games.
     1/* Copyright (C) 2015 Wildfire Games.
    22 * This file is part of 0 A.D.
    33 *
    44 * 0 A.D. is free software: you can redistribute it and/or modify
     
    2323class ICmpTerritoryInfluence : public IComponent
    2424{
    2525public:
    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 i32 GetCost() = 0;
    32 
    3326    virtual bool IsRoot() = 0;
    3427
    35     virtual u32 GetWeight() = 0;
     28    virtual u16 GetWeight() = 0;
    3629
    3730    virtual u32 GetRadius() = 0;
    3831
  • source/simulation2/components/ICmpTerritoryManager.cpp

     
    1 /* Copyright (C) 2011 Wildfire Games.
     1/* Copyright (C) 2015 Wildfire Games.
    22 * This file is part of 0 A.D.
    33 *
    44 * 0 A.D. is free software: you can redistribute it and/or modify
     
    2323
    2424BEGIN_INTERFACE_WRAPPER(TerritoryManager)
    2525DEFINE_INTERFACE_METHOD_2("GetOwner", player_id_t, ICmpTerritoryManager, GetOwner, entity_pos_t, entity_pos_t)
     26DEFINE_INTERFACE_METHOD_3("GetNeighbours", std::vector<u32>, ICmpTerritoryManager, GetNeighbours, entity_pos_t, entity_pos_t, bool)
    2627DEFINE_INTERFACE_METHOD_2("IsConnected", bool, ICmpTerritoryManager, IsConnected, entity_pos_t, entity_pos_t)
    2728END_INTERFACE_WRAPPER(TerritoryManager)
  • source/simulation2/components/ICmpTerritoryManager.h

     
    1 /* Copyright (C) 2011 Wildfire Games.
     1/* Copyright (C) 2015 Wildfire Games.
    22 * This file is part of 0 A.D.
    33 *
    44 * 0 A.D. is free software: you can redistribute it and/or modify
     
    4747    virtual player_id_t GetOwner(entity_pos_t x, entity_pos_t z) = 0;
    4848
    4949    /**
     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    /**
    5056     * Get whether territory at given position is connected to a root object
    5157     * (civ center etc) owned by that territory's player.
    5258     */
  • source/simulation2/system/ComponentManager.h

     
    269269
    270270    IComponent* QueryInterface(entity_id_t ent, InterfaceId iid) const;
    271271
    272     typedef std::vector<std::pair<entity_id_t, IComponent*> > InterfaceList;
     272    typedef std::pair<entity_id_t, IComponent*> InterfacePair;
     273    typedef std::vector<InterfacePair> InterfaceList;
    273274    typedef boost::unordered_map<entity_id_t, IComponent*> InterfaceListUnordered;
    274275
    275276    InterfaceList GetEntitiesWithInterface(InterfaceId iid) const;