Ticket #3204: territory_neighbours.diff

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

     
    3333    if (tileOwner != cmpOwnership.GetOwner())
    3434        return false;
    3535
    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;
    3742};
    3843
    3944TerritoryDecay.prototype.IsDecaying = function()
  • source/simulation2/components/CCmpTerritoryManager.cpp

     
    4545#include "simulation2/helpers/PriorityQueue.h"
    4646#include "simulation2/helpers/Render.h"
    4747
     48#include "ps/CLogger.h"
     49
    4850class CCmpTerritoryManager;
    4951
    5052class TerritoryOverlay : public TerrainOverlay
     
    8890    // connected flag in bit 6 (TERRITORY_CONNECTED_MASK);
    8991    // processed flag in bit 7 (TERRITORY_PROCESSED_MASK)
    9092    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;
    9196
    9297    // Set to true when territories change; will send a TerritoriesChanged message
    9398    // during the Update phase
     
    113118    virtual void Init(const CParamNode& UNUSED(paramNode))
    114119    {
    115120        m_Territories = NULL;
     121        m_NeighbourTerritories = NULL;
    116122        m_DebugOverlay = NULL;
    117123//      m_DebugOverlay = new TerritoryOverlay(*this);
    118124        m_BoundaryLinesDirty = true;
     
    135141    virtual void Deinit()
    136142    {
    137143        SAFE_DELETE(m_Territories);
     144        SAFE_DELETE(m_NeighbourTerritories);
    138145        SAFE_DELETE(m_DebugOverlay);
    139146    }
    140147
     
    224231    }
    225232
    226233    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);
    227235    virtual bool IsConnected(entity_pos_t x, entity_pos_t z);
    228236
    229237    // To support lazy updates of territory render data,
     
    235243    void MakeDirty()
    236244    {
    237245        SAFE_DELETE(m_Territories);
     246        SAFE_DELETE(m_NeighbourTerritories);
    238247        ++m_DirtyID;
    239248        m_BoundaryLinesDirty = true;
    240249        m_TriggerEvent = true;
     
    252261
    253262    void CalculateTerritories();
    254263
     264    u16 GetConnectedNeighbourBits(u16 i, u16 j);
     265
    255266    /**
    256267     * Updates @p grid based on the obstruction shapes of all entities with
    257268     * a TerritoryInfluence component. Grid cells are 0 if no influence,
     
    418429    // TODO: this is a large waste of memory; we don't really need to store
    419430    // all the intermediate grids
    420431
    421     for (std::map<player_id_t, std::vector<entity_id_t> >::iterator it = influenceEntities.begin(); it != influenceEntities.end(); ++it)
     432    for (auto it = influenceEntities.begin(); it != influenceEntities.end(); ++it)
    422433    {
    423434        Grid<u32> playerGrid(tilesW, tilesH);
     435        Grid<u32> entityGrid(tilesW, tilesH);
    424436
    425437        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)
    427440        {
    428             // Compute the influence map of the current entity, then add it to the player grid
    429 
    430             Grid<u32> entityGrid(tilesW, tilesH);
    431 
    432441            CmpPtr<ICmpPosition> cmpPosition(GetSimContext(), *eit);
    433442            CFixedVector2D pos = cmpPosition->GetPosition2D();
    434443            u16 i = (u16)clamp((pos.X / (int)TERRAIN_TILE_SIZE).ToInt_RoundToNegInfinity(), 0, tilesW-1);
     
    437446            CmpPtr<ICmpTerritoryInfluence> cmpTerritoryInfluence(GetSimContext(), *eit);
    438447            u32 weight = cmpTerritoryInfluence->GetWeight();
    439448            u32 radius = cmpTerritoryInfluence->GetRadius() / TERRAIN_TILE_SIZE;
    440             u32 falloff = weight / radius; // earlier check for GetRadius() == 0 prevents divide-by-zero
     449            u32 falloff = weight / radius + 1; // earlier check for GetRadius() == 0 prevents divide-by-zero
    441450
    442             // TODO: we should have some maximum value on weight, to avoid overflow
    443             // when doing all the sums
    444451
    445452            // Initialise the tile under the entity
    446453            entityGrid.set(i, j, weight);
     
    451458            // Expand influences outwards
    452459            FloodFill(entityGrid, influenceGrid, openTiles, falloff);
    453460
    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();
    456465        }
    457466
    458467        playerGrids.push_back(std::make_pair(it->first, playerGrid));
     
    479488
    480489    // Detect territories connected to a 'root' influence (typically a civ center)
    481490    // 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)
     491    for (auto it = rootInfluenceEntities.begin(); it != rootInfluenceEntities.end(); ++it)
    483492    {
    484493        // (These components must be valid else the entities wouldn't be added to this list)
    485494        CmpPtr<ICmpOwnership> cmpOwnership(GetSimContext(), *it);
     
    536545    }
    537546}
    538547
     548u16 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
    539614/**
    540615 * Compute the tile indexes on the grid nearest to a given point
    541616 */
     
    722797    return m_Territories->get(i, j) & TERRITORY_PLAYER_MASK;
    723798}
    724799
     800std::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
    725815bool CCmpTerritoryManager::IsConnected(entity_pos_t x, entity_pos_t z)
    726816{
    727817    u16 i, j;
  • source/simulation2/components/ICmpTerritoryManager.cpp

     
    2323
    2424BEGIN_INTERFACE_WRAPPER(TerritoryManager)
    2525DEFINE_INTERFACE_METHOD_2("GetOwner", player_id_t, ICmpTerritoryManager, GetOwner, entity_pos_t, entity_pos_t)
     26DEFINE_INTERFACE_METHOD_2("GetConnectedNeighbours", std::vector<player_id_t>, ICmpTerritoryManager, GetConnectedNeighbours, entity_pos_t, entity_pos_t)
    2627DEFINE_INTERFACE_METHOD_2("IsConnected", bool, ICmpTerritoryManager, IsConnected, entity_pos_t, entity_pos_t)
    2728END_INTERFACE_WRAPPER(TerritoryManager)
  • source/simulation2/components/ICmpTerritoryManager.h

     
    3232    static const int TERRITORY_PLAYER_MASK = 0x3F;
    3333    static const int TERRITORY_CONNECTED_MASK = 0x40;
    3434    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;
    3537
    3638    /**
    3739     * For each tile, the TERRITORY_PLAYER_MASK bits are player ID;
     
    4749    virtual player_id_t GetOwner(entity_pos_t x, entity_pos_t z) = 0;
    4850
    4951    /**
     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    /**
    5058     * Get whether territory at given position is connected to a root object
    5159     * (civ center etc) owned by that territory's player.
    5260     */
  • source/simulation2/helpers/Grid.h

     
    9292            m_Data[i] += g.m_Data[i];
    9393    }
    9494
     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
    95115    void set(int i, int j, const T& value)
    96116    {
    97117#if GRID_BOUNDS_DEBUG