Ticket #3174: lighthouse.patch

File lighthouse.patch, 17.6 KB (added by Itms, 9 years ago)
  • binaries/data/mods/public/simulation/templates/structures/ptol_lighthouse.xml

     
    1717    <GenericName>Lighthouse</GenericName>
    1818    <SpecificName>Pharos</SpecificName>
    1919    <Classes datatype="tokens">Lighthouse Town -City</Classes>
    20     <Tooltip>Build along the shore to reveal the shorelines over the entire map (Not implemented). Very large vision range: 180 meters.</Tooltip>
     20    <Tooltip>Build along the shore to reveal the shorelines over the entire map. Very large vision range: 180 meters.</Tooltip>
    2121    <History>The Ptolemaic dynasty in Egypt built the magnificent Lighthouse of Alexandria near the harbor mouth of that Nile Delta city. This structure could be seen for many kilometers out to sea and was one of the Seven Wonders of the World.</History>
    2222    <Icon>structures/lighthouse.png</Icon>
    2323    <RequiredTechnology>phase_town</RequiredTechnology>
     
    4141  </TerritoryInfluence>
    4242  <Vision>
    4343    <Range>180</Range>
     44    <RevealShore>true</RevealShore>
    4445  </Vision>
    4546  <VisualActor>
    4647    <Actor>structures/ptolemies/lighthouse.xml</Actor>
  • source/ps/TemplateLoader.cpp

     
    512512    // Foundations should be visible themselves in fog-of-war if their base template is,
    513513    // but shouldn't have any vision range
    514514    if (out.GetChild("Entity").GetChild("Vision").IsOk())
     515    {
    515516        CParamNode::LoadXMLString(out, "<Entity><Vision><Range>0</Range></Vision></Entity>");
     517        // Foundations should not have special vision capabilities either
     518        if (out.GetChild("Entity").GetChild("Vision").GetChild("RevealShore").IsOk())
     519            CParamNode::LoadXMLString(out, "<Entity><Vision><RevealShore>false</RevealShore></Vision></Entity>");
     520    }
    516521}
    517522
    518523void CTemplateLoader::CopyConstructionSubset(CParamNode& out, const CParamNode& in)
  • source/simulation2/components/CCmpPathfinder.cpp

     
    306306    return *m_Grid;
    307307}
    308308
     309Grid<u16> CCmpPathfinder::ComputeShoreGrid(bool expandOnWater)
     310{
     311    PROFILE3("ComputeShoreGrid");
     312
     313    CmpPtr<ICmpWaterManager> cmpWaterManager(GetSystemEntity());
     314
     315    // TODO: these bits should come from ICmpTerrain
     316    CTerrain& terrain = GetSimContext().GetTerrain();
     317
     318    // avoid integer overflow in intermediate calculation
     319    const u16 shoreMax = 32767;
     320
     321    // First pass - find underwater tiles
     322    Grid<bool> waterGrid(m_MapSize, m_MapSize);
     323    for (u16 j = 0; j < m_MapSize; ++j)
     324    {
     325        for (u16 i = 0; i < m_MapSize; ++i)
     326        {
     327            fixed x, z;
     328            TileCenter(i, j, x, z);
     329
     330            bool underWater = cmpWaterManager && (cmpWaterManager->GetWaterLevel(x, z) > terrain.GetExactGroundLevelFixed(x, z));
     331            waterGrid.set(i, j, underWater);
     332        }
     333    }
     334
     335    // Second pass - find shore tiles
     336    Grid<u16> shoreGrid(m_MapSize, m_MapSize);
     337    for (u16 j = 0; j < m_MapSize; ++j)
     338    {
     339        for (u16 i = 0; i < m_MapSize; ++i)
     340        {
     341            // Find a land tile
     342            if (!waterGrid.get(i, j))
     343            {
     344                // If it's bordered by water, it's a shore tile
     345                if ((i > 0 && waterGrid.get(i-1, j)) || (i > 0 && j < m_MapSize-1 && waterGrid.get(i-1, j+1)) || (i > 0 && j > 0 && waterGrid.get(i-1, j-1))
     346                    || (i < m_MapSize-1 && waterGrid.get(i+1, j)) || (i < m_MapSize-1 && j < m_MapSize-1 && waterGrid.get(i+1, j+1)) || (i < m_MapSize-1 && j > 0 && waterGrid.get(i+1, j-1))
     347                    || (j > 0 && waterGrid.get(i, j-1)) || (j < m_MapSize-1 && waterGrid.get(i, j+1))
     348                    )
     349                    shoreGrid.set(i, j, 0);
     350                else
     351                    shoreGrid.set(i, j, shoreMax);
     352            }
     353            // If we want to expand on water, we want water tiles not to be shore tiles
     354            else if (expandOnWater)
     355                shoreGrid.set(i, j, shoreMax);
     356        }
     357    }
     358
     359    // Expand influences to find shore distance
     360    for (u16 y = 0; y < m_MapSize; ++y)
     361    {
     362        u16 min = shoreMax;
     363        for (u16 x = 0; x < m_MapSize; ++x)
     364        {
     365            if (!waterGrid.get(x, y) || expandOnWater)
     366            {
     367                u16 g = shoreGrid.get(x, y);
     368                if (g > min)
     369                    shoreGrid.set(x, y, min);
     370                else if (g < min)
     371                    min = g;
     372
     373                ++min;
     374            }
     375        }
     376        for (u16 x = m_MapSize; x > 0; --x)
     377        {
     378            if (!waterGrid.get(x-1, y) || expandOnWater)
     379            {
     380                u16 g = shoreGrid.get(x - 1, y);
     381                if (g > min)
     382                    shoreGrid.set(x - 1, y, min);
     383                else if (g < min)
     384                    min = g;
     385
     386                ++min;
     387            }
     388        }
     389    }
     390    for (u16 x = 0; x < m_MapSize; ++x)
     391    {
     392        u16 min = shoreMax;
     393        for (u16 y = 0; y < m_MapSize; ++y)
     394        {
     395            if (!waterGrid.get(x, y) || expandOnWater)
     396            {
     397                u16 g = shoreGrid.get(x, y);
     398                if (g > min)
     399                    shoreGrid.set(x, y, min);
     400                else if (g < min)
     401                    min = g;
     402
     403                ++min;
     404            }
     405        }
     406        for (u16 y = m_MapSize; y > 0; --y)
     407        {
     408            if (!waterGrid.get(x, y-1) || expandOnWater)
     409            {
     410                u16 g = shoreGrid.get(x, y - 1);
     411                if (g > min)
     412                    shoreGrid.set(x, y - 1, min);
     413                else if (g < min)
     414                    min = g;
     415
     416                ++min;
     417            }
     418        }
     419    }
     420
     421    return shoreGrid;
     422}
     423
    309424void CCmpPathfinder::UpdateGrid()
    310425{
    311426    CmpPtr<ICmpTerrain> cmpTerrain(GetSystemEntity());
     
    369484        // Obstructions or terrain changed - we need to recompute passability
    370485        // TODO: only bother recomputing the region that has actually changed
    371486
     487        Grid<u16> shoreGrid = ComputeShoreGrid();
     488
    372489        CmpPtr<ICmpWaterManager> cmpWaterManager(GetSystemEntity());
    373 
    374         // TOOD: these bits should come from ICmpTerrain
    375490        CTerrain& terrain = GetSimContext().GetTerrain();
    376491
    377         // avoid integer overflow in intermediate calculation
    378         const u16 shoreMax = 32767;
    379        
    380         // First pass - find underwater tiles
    381         Grid<bool> waterGrid(m_MapSize, m_MapSize);
    382         for (u16 j = 0; j < m_MapSize; ++j)
    383         {
    384             for (u16 i = 0; i < m_MapSize; ++i)
    385             {
    386                 fixed x, z;
    387                 TileCenter(i, j, x, z);
    388                
    389                 bool underWater = cmpWaterManager && (cmpWaterManager->GetWaterLevel(x, z) > terrain.GetExactGroundLevelFixed(x, z));
    390                 waterGrid.set(i, j, underWater);
    391             }
    392         }
    393         // Second pass - find shore tiles
    394         Grid<u16> shoreGrid(m_MapSize, m_MapSize);
    395         for (u16 j = 0; j < m_MapSize; ++j)
    396         {
    397             for (u16 i = 0; i < m_MapSize; ++i)
    398             {
    399                 // Find a land tile
    400                 if (!waterGrid.get(i, j))
    401                 {
    402                     if ((i > 0 && waterGrid.get(i-1, j)) || (i > 0 && j < m_MapSize-1 && waterGrid.get(i-1, j+1)) || (i > 0 && j > 0 && waterGrid.get(i-1, j-1))
    403                         || (i < m_MapSize-1 && waterGrid.get(i+1, j)) || (i < m_MapSize-1 && j < m_MapSize-1 && waterGrid.get(i+1, j+1)) || (i < m_MapSize-1 && j > 0 && waterGrid.get(i+1, j-1))
    404                         || (j > 0 && waterGrid.get(i, j-1)) || (j < m_MapSize-1 && waterGrid.get(i, j+1))
    405                         )
    406                     {   // If it's bordered by water, it's a shore tile
    407                         shoreGrid.set(i, j, 0);
    408                     }
    409                     else
    410                     {
    411                         shoreGrid.set(i, j, shoreMax);
    412                     }
    413                 }
    414             }
    415         }
    416 
    417         // Expand influences on land to find shore distance
    418         for (u16 y = 0; y < m_MapSize; ++y)
    419         {
    420             u16 min = shoreMax;
    421             for (u16 x = 0; x < m_MapSize; ++x)
    422             {
    423                 if (!waterGrid.get(x, y))
    424                 {
    425                     u16 g = shoreGrid.get(x, y);
    426                     if (g > min)
    427                         shoreGrid.set(x, y, min);
    428                     else if (g < min)
    429                         min = g;
    430 
    431                     ++min;
    432                 }
    433             }
    434             for (u16 x = m_MapSize; x > 0; --x)
    435             {
    436                 if (!waterGrid.get(x-1, y))
    437                 {
    438                     u16 g = shoreGrid.get(x-1, y);
    439                     if (g > min)
    440                         shoreGrid.set(x-1, y, min);
    441                     else if (g < min)
    442                         min = g;
    443 
    444                     ++min;
    445                 }
    446             }
    447         }
    448         for (u16 x = 0; x < m_MapSize; ++x)
    449         {
    450             u16 min = shoreMax;
    451             for (u16 y = 0; y < m_MapSize; ++y)
    452             {
    453                 if (!waterGrid.get(x, y))
    454                 {
    455                     u16 g = shoreGrid.get(x, y);
    456                     if (g > min)
    457                         shoreGrid.set(x, y, min);
    458                     else if (g < min)
    459                         min = g;
    460 
    461                     ++min;
    462                 }
    463             }
    464             for (u16 y = m_MapSize; y > 0; --y)
    465             {
    466                 if (!waterGrid.get(x, y-1))
    467                 {
    468                     u16 g = shoreGrid.get(x, y-1);
    469                     if (g > min)
    470                         shoreGrid.set(x, y-1, min);
    471                     else if (g < min)
    472                         min = g;
    473 
    474                     ++min;
    475                 }
    476             }
    477         }
    478 
    479492        // Apply passability classes to terrain
    480493        for (u16 j = 0; j < m_MapSize; ++j)
    481494        {
  • source/simulation2/components/CCmpPathfinder_Common.h

     
    1 /* Copyright (C) 2013 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
     
    242242
    243243    virtual const Grid<u16>& GetPassabilityGrid();
    244244
     245    virtual Grid<u16> ComputeShoreGrid(bool expandOnWater = false);
     246
    245247    virtual void ComputePath(entity_pos_t x0, entity_pos_t z0, const Goal& goal, pass_class_t passClass, cost_class_t costClass, Path& ret);
    246248
    247249    virtual u32 ComputePathAsync(entity_pos_t x0, entity_pos_t z0, const Goal& goal, pass_class_t passClass, cost_class_t costClass, entity_id_t notify);
  • source/simulation2/components/CCmpRangeManager.cpp

     
    18071807        }
    18081808    }
    18091809
     1810    virtual void RevealShore(player_id_t p, bool enable)
     1811    {
     1812        if (p <= 0 || p > MAX_LOS_PLAYER_ID)
     1813            return;
     1814
     1815        // Maximum distance to the shore
     1816        const u16 maxdist = 10;
     1817
     1818        CmpPtr<ICmpPathfinder> cmpPathfinder(GetSystemEntity());
     1819        const Grid<u16>& shoreGrid = cmpPathfinder->ComputeShoreGrid(true);
     1820        ENSURE(shoreGrid.m_W == m_TerrainVerticesPerSide-1 && shoreGrid.m_H == m_TerrainVerticesPerSide-1);
     1821
     1822        std::vector<u16>& counts = m_LosPlayerCounts.at(p);
     1823        ENSURE(!counts.empty());
     1824        u16* countsData = &counts[0];
     1825
     1826        for (u16 j = 0; j < shoreGrid.m_H; ++j)
     1827        {
     1828            for (u16 i = 0; i < shoreGrid.m_W; ++i)
     1829            {
     1830                u16 shoredist = shoreGrid.get(i, j);
     1831                if (shoredist > maxdist)
     1832                    continue;
     1833
     1834                // Maybe we could be more clever and don't add dummy strips of one tile
     1835                if (enable)
     1836                    LosAddStripHelper(p, i, i, j, countsData);
     1837                else
     1838                    LosRemoveStripHelper(p, i, i, j, countsData);
     1839            }
     1840        }
     1841    }
     1842
    18101843    /**
    18111844     * Returns whether the given vertex is outside the normal bounds of the world
    18121845     * (i.e. outside the range of a circular map)
     
    18611894                    m_LosState[idx] |= ((LOS_VISIBLE | LOS_EXPLORED) << (2*(owner-1)));
    18621895                }
    18631896
    1864                 // Mark the LoS tiles around the updated vertex
    1865                 // 1: left-up, 2: right-up, 3: left-down, 4: right-down
    1866                 int n1 = ((j-1)/LOS_TILES_RATIO)*m_LosTilesPerSide + (i-1)/LOS_TILES_RATIO;
    1867                 int n2 = ((j-1)/LOS_TILES_RATIO)*m_LosTilesPerSide + i/LOS_TILES_RATIO;
    1868                 int n3 = (j/LOS_TILES_RATIO)*m_LosTilesPerSide + (i-1)/LOS_TILES_RATIO;
    1869                 int n4 = (j/LOS_TILES_RATIO)*m_LosTilesPerSide + i/LOS_TILES_RATIO;
    1870 
    1871                 u16 sharedDirtyVisibilityMask = m_SharedDirtyVisibilityMasks[owner];
    1872 
    1873                 if (j > 0 && i > 0)
    1874                     m_DirtyVisibility[n1] |= sharedDirtyVisibilityMask;
    1875                 if (n2 != n1 && j > 0 && i < m_TerrainVerticesPerSide)
    1876                     m_DirtyVisibility[n2] |= sharedDirtyVisibilityMask;
    1877                 if (n3 != n1 && j < m_TerrainVerticesPerSide && i > 0)
    1878                     m_DirtyVisibility[n3] |= sharedDirtyVisibilityMask;
    1879                 if (n4 != n1 && j < m_TerrainVerticesPerSide && i < m_TerrainVerticesPerSide)
    1880                     m_DirtyVisibility[n4] |= sharedDirtyVisibilityMask;
     1897                MarkVisibilityDirtyAroundTile(owner, i, j);
    18811898            }
    18821899
    18831900            ASSERT(counts[idx] < 65535);
     
    19071924                m_LosState[idx] &= ~(LOS_VISIBLE << (2*(owner-1)));
    19081925
    19091926                i32 i = i0 + idx - idx0;
     1927                MarkVisibilityDirtyAroundTile(owner, i, j);
     1928            }
     1929        }
     1930    }
    19101931
    1911                 // Mark the LoS tiles around the updated vertex
    1912                 // 1: left-up, 2: right-up, 3: left-down, 4: right-down
    1913                 int n1 = ((j-1)/LOS_TILES_RATIO)*m_LosTilesPerSide + (i-1)/LOS_TILES_RATIO;
    1914                 int n2 = ((j-1)/LOS_TILES_RATIO)*m_LosTilesPerSide + i/LOS_TILES_RATIO;
    1915                 int n3 = (j/LOS_TILES_RATIO)*m_LosTilesPerSide + (i-1)/LOS_TILES_RATIO;
    1916                 int n4 = (j/LOS_TILES_RATIO)*m_LosTilesPerSide + i/LOS_TILES_RATIO;
     1932    inline void MarkVisibilityDirtyAroundTile(u8 owner, i32 i, i32 j)
     1933    {
     1934        // Mark the LoS tiles around the updated vertex
     1935        // 1: left-up, 2: right-up, 3: left-down, 4: right-down
     1936        int n1 = ((j-1)/LOS_TILES_RATIO)*m_LosTilesPerSide + (i-1)/LOS_TILES_RATIO;
     1937        int n2 = ((j-1)/LOS_TILES_RATIO)*m_LosTilesPerSide + i/LOS_TILES_RATIO;
     1938        int n3 = (j/LOS_TILES_RATIO)*m_LosTilesPerSide + (i-1)/LOS_TILES_RATIO;
     1939        int n4 = (j/LOS_TILES_RATIO)*m_LosTilesPerSide + i/LOS_TILES_RATIO;
    19171940
    1918                 u16 sharedDirtyVisibilityMask = m_SharedDirtyVisibilityMasks[owner];
     1941        u16 sharedDirtyVisibilityMask = m_SharedDirtyVisibilityMasks[owner];
    19191942
    1920                 if (j > 0 && i > 0)
    1921                     m_DirtyVisibility[n1] |= sharedDirtyVisibilityMask;
    1922                 if (n2 != n1 && j > 0 && i < m_TerrainVerticesPerSide)
    1923                     m_DirtyVisibility[n2] |= sharedDirtyVisibilityMask;
    1924                 if (n3 != n1 && j < m_TerrainVerticesPerSide && i > 0)
    1925                     m_DirtyVisibility[n3] |= sharedDirtyVisibilityMask;
    1926                 if (n4 != n1 && j < m_TerrainVerticesPerSide && i < m_TerrainVerticesPerSide)
    1927                     m_DirtyVisibility[n4] |= sharedDirtyVisibilityMask;
    1928             }
    1929         }
     1943        if (j > 0 && i > 0)
     1944            m_DirtyVisibility[n1] |= sharedDirtyVisibilityMask;
     1945        if (n2 != n1 && j > 0 && i < m_TerrainVerticesPerSide)
     1946            m_DirtyVisibility[n2] |= sharedDirtyVisibilityMask;
     1947        if (n3 != n1 && j < m_TerrainVerticesPerSide && i > 0)
     1948            m_DirtyVisibility[n3] |= sharedDirtyVisibilityMask;
     1949        if (n4 != n1 && j < m_TerrainVerticesPerSide && i < m_TerrainVerticesPerSide)
     1950            m_DirtyVisibility[n4] |= sharedDirtyVisibilityMask;
    19301951    }
    19311952
    19321953    /**
  • source/simulation2/components/CCmpVision.cpp

     
    2121#include "ICmpVision.h"
    2222
    2323#include "simulation2/MessageTypes.h"
    24 #include "simulation2/components/ICmpOwnership.h"
    2524#include "simulation2/components/ICmpPlayerManager.h"
     25#include "simulation2/components/ICmpRangeManager.h"
    2626#include "simulation2/components/ICmpValueModificationManager.h"
    2727
    2828class CCmpVision : public ICmpVision
     
    3030public:
    3131    static void ClassInit(CComponentManager& componentManager)
    3232    {
     33        componentManager.SubscribeToMessageType(MT_OwnershipChanged);
    3334        componentManager.SubscribeToMessageType(MT_ValueModification);
    3435        componentManager.SubscribeToMessageType(MT_Deserialized);
    3536    }
     
    3940    // Template state:
    4041
    4142    entity_pos_t m_Range, m_BaseRange;
     43    bool m_RevealShore;
    4244
    4345    static std::string GetSchema()
    4446    {
     
    4547        return
    4648            "<element name='Range'>"
    4749                "<data type='nonNegativeInteger'/>"
    48             "</element>";
     50            "</element>"
     51            "<optional>"
     52                "<element name='RevealShore'>"
     53                    "<data type='boolean'/>"
     54                "</element>"
     55            "</optional>";
    4956    }
    5057
    5158    virtual void Init(const CParamNode& paramNode)
    5259    {
    5360        m_BaseRange = m_Range = paramNode.GetChild("Range").ToFixed();
     61
     62        if (paramNode.GetChild("RevealShore").IsOk())
     63            m_RevealShore = paramNode.GetChild("RevealShore").ToBool();
     64        else
     65            m_RevealShore = false;
    5466    }
    5567
    5668    virtual void Deinit()
     
    7183    {
    7284        switch (msg.GetType())
    7385        {
     86        case MT_OwnershipChanged:
     87        {
     88            if (!m_RevealShore)
     89                break;
     90
     91            const CMessageOwnershipChanged& msgData = static_cast<const CMessageOwnershipChanged&> (msg);
     92            if (msgData.entity != GetEntityId())
     93                break;
     94
     95            CmpPtr<ICmpRangeManager> cmpRangeManager(GetSystemEntity());
     96            cmpRangeManager->RevealShore(msgData.from, false);
     97            cmpRangeManager->RevealShore(msgData.to, true);
     98            break;
     99        }
    74100        case MT_ValueModification:
    75101        {
    76102            const CMessageValueModification& msgData = static_cast<const CMessageValueModification&> (msg);
  • source/simulation2/components/ICmpPathfinder.h

     
    1 /* Copyright (C) 2013 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
     
    9797    virtual const Grid<u16>& GetPassabilityGrid() = 0;
    9898
    9999    /**
     100     * Get a grid representing the distance to the shore of the terrain tile.
     101     */
     102    virtual Grid<u16> ComputeShoreGrid(bool expandOnWater = false) = 0;
     103
     104    /**
    100105     * Compute a tile-based path from the given point to the goal, and return the set of waypoints.
    101106     * The waypoints correspond to the centers of horizontally/vertically adjacent tiles
    102107     * along the path.
  • source/simulation2/components/ICmpRangeManager.h

     
    353353    virtual void ExploreTerritories() = 0;
    354354
    355355    /**
     356     * Reveal the shore for specified player p.
     357     * This works like for entities: if RevealShore is called multiple times with enabled, it
     358     * will be necessary to call it the same number of times with !enabled to make the shore
     359     * fall back into the FoW.
     360     */
     361    virtual void RevealShore(player_id_t p, bool enable) = 0;
     362
     363    /**
    356364     * Set whether the whole map should be made visible to the given player.
    357365     * If player is -1, the map will be made visible to all players.
    358366     */