Ticket #599: proxy.diff

File proxy.diff, 39.9 KB (added by Josh Matthews, 12 years ago)

Latest WIP

  • binaries/data/mods/public/simulation/components/BuildRestrictions.js

    commit 2c8283a0c38acf3315bf9620d1ae66aa5509356f
    Author: Josh Matthews <josh@joshmatthews.net>
    Date:   Mon Aug 13 02:12:01 2012 -0400
    
        Woo proxy
    
    diff --git a/binaries/data/mods/public/simulation/components/BuildRestrictions.js b/binaries/data/mods/public/simulation/components/BuildRestrictions.js
    index dcaab94..6bf2b2a 100644
    a b BuildRestrictions.prototype.CheckPlacement = function(player)  
    9090    default:
    9191        passClassName = "building-land";
    9292    }
     93 
     94    // Proxies for other players should never be obstructions
     95    var cmpVisual = Engine.QueryInterface(this.entity, IID_Visual);
     96    var isIgnoreable = cmpVisual &&
     97                cmpVisual.GetProxiedEntity() != INVALID_ENTITY &&
     98                cmpVisual.GetProxyPlayerTarget() != player;
    9399
    94100    var cmpObstruction = Engine.QueryInterface(this.entity, IID_Obstruction);
    95     if (!cmpObstruction || !cmpObstruction.CheckFoundation(passClassName))
     101    if (!isIgnoreable &&
     102            (!cmpObstruction || !cmpObstruction.CheckFoundation(passClassName)))
    96103    {
    97104        return false;   // Fail
    98105    }
  • binaries/data/mods/public/simulation/components/BuildingAI.js

    diff --git a/binaries/data/mods/public/simulation/components/BuildingAI.js b/binaries/data/mods/public/simulation/components/BuildingAI.js
    index a2c5985..8139cbb 100644
    a b BuildingAI.prototype.Schema =  
    1717 */
    1818BuildingAI.prototype.Init = function()
    1919{
     20    if (IsProxyEntity(this.entity))
     21        return;
     22
    2023    if (this.GetDefaultArrowCount() > 0 || this.GetGarrisonArrowMultiplier() > 0)
    2124    {
    2225        var cmpTimer = Engine.QueryInterface(SYSTEM_ENTITY, IID_Timer);
    BuildingAI.prototype.OnOwnershipChanged = function(msg)  
    3841        this.SetupGaiaRangeQuery(msg.to);
    3942};
    4043
     44BuildingAI.prototype.OnVisibilityChanged = function(msg)
     45{
     46    if (msg.source != this.entity)
     47        return;
     48    var cmpOwnHealth = Engine.QueryInterface(this.entity, IID_Health);
     49    var cmpProxyHealth = Engine.QueryInterface(msg.proxy, IID_Health);
     50    cmpProxyHealth.SetHitpoints(cmpOwnHealth.GetHitpoints());
     51}
     52
    4153/**
    4254 * Cleanup on destroy
    4355 */
    BuildingAI.prototype.OnDestroy = function()  
    6375 */
    6476BuildingAI.prototype.SetupRangeQuery = function(owner)
    6577{
     78    if (IsProxyEntity(this.entity))
     79          return;
     80
    6681    var cmpRangeManager = Engine.QueryInterface(SYSTEM_ENTITY, IID_RangeManager);
    6782    var cmpPlayerManager = Engine.QueryInterface(SYSTEM_ENTITY, IID_PlayerManager);
    6883    if (this.enemyUnitsQuery)
  • binaries/data/mods/public/simulation/components/Foundation.js

    diff --git a/binaries/data/mods/public/simulation/components/Foundation.js b/binaries/data/mods/public/simulation/components/Foundation.js
    index e7fc336..3060656 100644
    a b Foundation.prototype.IsFinished = function()  
    6363    return (this.buildProgress >= 1.0);
    6464};
    6565
     66Foundation.prototype.OnVisibilityChanged = function(msg)
     67{
     68    if (msg.source != this.entity)
     69        return;
     70    var cmpOwnHealth = Engine.QueryInterface(this.entity, IID_Health);
     71    var cmpProxyHealth = Engine.QueryInterface(msg.proxy, IID_Health);
     72    cmpProxyHealth.SetHitpoints(cmpOwnHealth.GetHitpoints());
     73}
     74
    6675Foundation.prototype.OnDestroy = function()
    6776{
    6877    // Refund a portion of the construction cost, proportional to the amount of build progress remaining
  • binaries/data/mods/public/simulation/components/GuiInterface.js

    diff --git a/binaries/data/mods/public/simulation/components/GuiInterface.js b/binaries/data/mods/public/simulation/components/GuiInterface.js
    index 9be9f46..bdd50e5 100644
    a b GuiInterface.prototype.Init = function()  
    2323    this.rallyPoints = undefined;
    2424    this.notifications = [];
    2525    this.renamedEntities = [];
     26    this.proxiedEntities = {};
    2627};
    2728
    2829/*
    GuiInterface.prototype.GetExtendedSimulationState = function(player)  
    124125
    125126GuiInterface.prototype.GetRenamedEntities = function(player)
    126127{
    127     return this.renamedEntities;
     128    var proxied = [];
     129    if (player in this.proxiedEntities)
     130        proxied = this.proxiedEntities[player];
     131    return this.renamedEntities.concat(proxied);
    128132};
    129133
    130134GuiInterface.prototype.ClearRenamedEntities = function(player)
    131135{
    132136    this.renamedEntities = [];
     137    this.proxiedEntities[player] = [];
    133138};
    134139
    135140GuiInterface.prototype.GetEntityState = function(player, ent)
    GuiInterface.prototype.OnGlobalEntityRenamed = function(msg)  
    16141619    this.renamedEntities.push(msg);
    16151620}
    16161621
     1622// Ensure that any GUI code interacting with proxies can continue
     1623// to interact with the original entity if the proxy is destroyed
     1624// (ie. the original entity becomes visible).
     1625GuiInterface.prototype.OnGlobalDestroy = function(msg)
     1626{
     1627    var proxy = GetProxiedEntity(msg.entity);
     1628    if (proxy == INVALID_ENTITY)
     1629        return;
     1630    this.renamedEntities.push({"type": "EntityRenamed", "entity": msg.entity, "newentity": proxy});
     1631}
     1632
     1633GuiInterface.prototype.OnGlobalVisibilityChanged = function(msg)
     1634{
     1635    if (msg.source == INVALID_ENTITY)
     1636        return;
     1637    if (!(msg.player in this.proxiedEntities))
     1638        this.proxiedEntities[msg.player] = [];
     1639    this.proxiedEntities[msg.player].push({"type": "EntityRenamed", "entity": msg.source, "newentity": msg.proxy});
     1640}
     1641
    16171642// List the GuiInterface functions that can be safely called by GUI scripts.
    16181643// (GUI scripts are non-deterministic and untrusted, so these functions must be
    16191644// appropriately careful. They are called with a first argument "player", which is
  • binaries/data/mods/public/simulation/components/ResourceSupply.js

    diff --git a/binaries/data/mods/public/simulation/components/ResourceSupply.js b/binaries/data/mods/public/simulation/components/ResourceSupply.js
    index 2dbf4e4..4f15946 100644
    a b ResourceSupply.prototype.GetType = function()  
    7777    return { "generic": type, "specific": subtype };
    7878};
    7979
     80ResourceSupply.prototype.OnVisibilityChanged = function(msg)
     81{
     82    if (msg.source != this.entity)
     83        return;
     84    var entity = msg.proxy;
     85    var supply = Engine.QueryInterface(entity, IID_ResourceSupply);
     86    supply.amount = this.amount;
     87}
     88
    8089Engine.RegisterComponentType(IID_ResourceSupply, "ResourceSupply", ResourceSupply);
  • binaries/data/mods/public/simulation/components/UnitAI.js

    diff --git a/binaries/data/mods/public/simulation/components/UnitAI.js b/binaries/data/mods/public/simulation/components/UnitAI.js
    index 455a688..820a8e5 100644
    a b var UnitFsmSpec = {  
    997997        },
    998998
    999999        "GATHER": {
     1000            "EntityRenamed": function(msg) {
     1001                if (this.order.data.target == msg.entity)
     1002                    this.order.data.target = msg.newentity;
     1003            },
     1004
    10001005            "APPROACHING": {
    10011006                "enter": function() {
    10021007                    this.SelectAnimation("move");
    UnitAI.prototype.OnDestroy = function()  
    19251930// Wrapper function that sets up the normal, healer, and Gaia range queries.
    19261931UnitAI.prototype.SetupRangeQueries = function()
    19271932{
     1933    if (IsProxyEntity(this.entity))
     1934        return;
     1935
    19281936    this.SetupRangeQuery();
    19291937
    19301938    if (this.IsHealer())
    UnitAI.prototype.SetupRangeQueries = function()  
    19341942        this.SetupGaiaRangeQuery();
    19351943}
    19361944
     1945// Ensure that any orders focused on a proxy are correctly
     1946// transferred to the original entity when the proxy is destroyed
     1947// (ie. the original entity becomes visible).
     1948UnitAI.prototype.OnGlobalDestroy = function(msg)
     1949{
     1950    var orig = GetProxiedEntity(msg.entity);
     1951    if (orig == INVALID_ENTITY)
     1952          return;
     1953    this.OnGlobalEntityRenamed({entity: msg.entity, newentity: orig});
     1954}
     1955
    19371956// Set up a range query for all enemy units within LOS range
    19381957// which can be attacked.
    19391958// This should be called whenever our ownership changes.
    UnitAI.prototype.OnGlobalEntityRenamed = function(msg)  
    22802299    UnitFsm.ProcessMessage(this, {"type": "EntityRenamed", "entity": msg.entity, "newentity": msg.newentity});
    22812300};
    22822301
     2302// If a proxy appears and it's targeted at this unit's owner,
     2303// pretend the original actor has been renamed to the proxy.
     2304UnitAI.prototype.OnGlobalVisibilityChanged = function(msg)
     2305{
     2306    if (msg.source == INVALID_ENTITY)
     2307        return;
     2308    var cmpVisual = Engine.QueryInterface(msg.proxy, IID_Visual);
     2309    var cmpOwner = Engine.QueryInterface(this.entity, IID_Ownership);
     2310    if (cmpVisual.GetProxyPlayerTarget() == cmpOwner.GetOwner())
     2311        this.OnGlobalEntityRenamed({entity: msg.source, newentity: msg.proxy});
     2312}
     2313
    22832314UnitAI.prototype.OnAttacked = function(msg)
    22842315{
    22852316    UnitFsm.ProcessMessage(this, {"type": "Attacked", "data": msg});
    UnitAI.prototype.FindNearbyFoundation = function()  
    24302461        if (cmpFoundation.IsFinished())
    24312462            continue;
    24322463
     2464        if (IsProxyEntity(ent))
     2465            continue;
    24332466        return ent;
    24342467    }
    24352468
  • new file inaries/data/mods/public/simulation/helpers/Proxy.js

    diff --git a/binaries/data/mods/public/simulation/helpers/Proxy.js b/binaries/data/mods/public/simulation/helpers/Proxy.js
    new file mode 100644
    index 0000000..be12585
    - +  
     1function IsProxyEntity(entity)
     2{
     3    return GetProxiedEntity(entity) != INVALID_ENTITY;
     4}
     5
     6function GetProxiedEntity(entity)
     7{
     8    var cmpVisual = Engine.QueryInterface(entity, IID_Visual);
     9    if (!cmpVisual)
     10        return INVALID_ENTITY;
     11    return cmpVisual.GetProxiedEntity();
     12}
     13
     14Engine.RegisterGlobal("IsProxyEntity", IsProxyEntity);
     15Engine.RegisterGlobal("GetProxiedEntity", GetProxiedEntity);
  • source/network/NetTurnManager.cpp

    diff --git a/source/network/NetTurnManager.cpp b/source/network/NetTurnManager.cpp
    index 0eb332d..1eec386 100644
    a b  
    3232#include "ps/SavedGame.h"
    3333#include "scriptinterface/ScriptInterface.h"
    3434#include "simulation2/Simulation2.h"
     35#include "simulation2/system/SimContext.h"
    3536
    3637#include <sstream>
    3738#include <fstream>
    void CNetTurnManager::OnSyncError(u32 turn, const std::string& expectedHash)  
    232233    bool ok = m_Simulation2.ComputeStateHash(hash, quick);
    233234    ENSURE(ok);
    234235
    235     OsPath path = psLogDir()/"oos_dump.txt";
     236    std::stringstream fname;
     237    fname << "oos_dump_" << m_Simulation2.GetSimContext().GetCurrentDisplayedPlayer() << ".txt";
     238    OsPath path = psLogDir()/fname.str();
    236239    std::ofstream file (OsString(path).c_str(), std::ofstream::out | std::ofstream::trunc);
    237240    m_Simulation2.DumpDebugState(file);
    238241    file.close();
  • source/simulation2/MessageTypes.h

    diff --git a/source/simulation2/MessageTypes.h b/source/simulation2/MessageTypes.h
    index f149f50..10ba220 100644
    a b  
    2424
    2525#include "simulation2/helpers/Player.h"
    2626#include "simulation2/helpers/Position.h"
     27#include "simulation2/helpers/Player.h"
    2728
    2829#include "simulation2/components/ICmpPathfinder.h"
    2930
    public:  
    100101    fixed turnLength;
    101102};
    102103
     104class CMessageUpdate_Visibility : public CMessage
     105{
     106public:
     107    DEFAULT_MESSAGE_IMPL(Update_Visibility)
     108
     109    CMessageUpdate_Visibility()
     110    {
     111    }
     112};
     113
    103114/**
    104115 * Final update phase, after all other updates.
    105116 */
    public:  
    380391    player_id_t player;
    381392};
    382393
     394/**
     395 * Sent by range manager every simulation update to every entity that might have
     396 * changed visibility states in the current frame.
     397 *
     398 * Also broadcast by visual actors for every new proxy created.
     399 */
     400class CMessageVisibilityChanged : public CMessage
     401{
     402public:
     403    DEFAULT_MESSAGE_IMPL(VisibilityChanged)
     404
     405    CMessageVisibilityChanged(entity_id_t source, entity_id_t proxy) :
     406        source(source), proxy(proxy)
     407    {
     408    }
     409
     410    CMessageVisibilityChanged() :
     411        source(INVALID_ENTITY), proxy(INVALID_ENTITY)
     412    {
     413    }
     414
     415    // source: entity being proxied, or INVALID_ENTITY when indicating a visibility change
     416    // proxy: new proxy entity, or INVALID_ENTITY when indicating a visibility change
     417    entity_id_t source, proxy;
     418};
     419
    383420#endif // INCLUDED_MESSAGETYPES
  • source/simulation2/Simulation2.cpp

    diff --git a/source/simulation2/Simulation2.cpp b/source/simulation2/Simulation2.cpp
    index 0021e59..bc6c5c9 100644
    a b void CSimulation2Impl::UpdateComponents(CSimContext& simContext, fixed turnLengt  
    501501        componentManager.BroadcastMessage(msgUpdate);
    502502    }
    503503    {
     504        CMessageUpdate_Visibility msgUpdate;
     505        componentManager.BroadcastMessage(msgUpdate);
     506    }
     507    {
    504508        CMessageUpdate_Final msgUpdate(turnLengthFixed);
    505509        componentManager.BroadcastMessage(msgUpdate);
    506510    }
  • source/simulation2/TypeList.h

    diff --git a/source/simulation2/TypeList.h b/source/simulation2/TypeList.h
    index 758b67b..a6ba356 100644
    a b MESSAGE(TurnStart)  
    3434MESSAGE(Update)
    3535MESSAGE(Update_MotionFormation)
    3636MESSAGE(Update_MotionUnit)
     37MESSAGE(Update_Visibility)
    3738MESSAGE(Update_Final)
    3839MESSAGE(Interpolate) // non-deterministic (use with caution)
    3940MESSAGE(RenderSubmit) // non-deterministic (use with caution)
    MESSAGE(TerrainChanged)  
    4849MESSAGE(TerritoriesChanged)
    4950MESSAGE(PathResult)
    5051MESSAGE(TechnologyModification)
     52MESSAGE(VisibilityChanged)
    5153
    5254// TemplateManager must come before all other (non-test) components,
    5355// so that it is the first to be (de)serialized
  • source/simulation2/components/CCmpRangeManager.cpp

    diff --git a/source/simulation2/components/CCmpRangeManager.cpp b/source/simulation2/components/CCmpRangeManager.cpp
    index 1363b1f..4a5a949 100644
    a b  
    1818#include "precompiled.h"
    1919
    2020#include "simulation2/system/Component.h"
     21#include "simulation2/Simulation2.h"
    2122#include "ICmpRangeManager.h"
    2223
    2324#include "simulation2/MessageTypes.h"
    2425#include "simulation2/components/ICmpPosition.h"
    2526#include "simulation2/components/ICmpTerritoryManager.h"
    2627#include "simulation2/components/ICmpVision.h"
     28#include "simulation2/components/ICmpVisual.h"
    2729#include "simulation2/helpers/Render.h"
    2830#include "simulation2/helpers/Spatial.h"
    2931
     
    3436#include "ps/CLogger.h"
    3537#include "ps/Overlay.h"
    3638#include "ps/Profile.h"
     39#include "ps/Game.h"
    3740#include "renderer/Scene.h"
    3841
    3942#define DEBUG_RANGE_MANAGER_BOUNDS 0
    public:  
    190193        componentManager.SubscribeGloballyToMessageType(MT_Destroy);
    191194
    192195        componentManager.SubscribeToMessageType(MT_Update);
     196        componentManager.SubscribeToMessageType(MT_Update_Visibility);
    193197
    194198        componentManager.SubscribeToMessageType(MT_RenderSubmit); // for debug overlays
    195199    }
    public:  
    212216    std::map<entity_id_t, EntityData> m_EntityData;
    213217    SpatialSubdivision<entity_id_t> m_Subdivision; // spatial index of m_EntityData
    214218
    215     // LOS state:
     219    // Set of entities that could have changed visibility status in the
     220    // current simulation frame
     221    std::set<entity_id_t> m_DirtyVisibility;
    216222
     223    // LOS state:
    217224    std::map<player_id_t, bool> m_LosRevealAll;
    218225    bool m_LosCircular;
    219226    i32 m_TerrainVerticesPerSide;
    public:  
    331338            if (!cmpPosition)
    332339                break;
    333340
     341            // XXXjdm On the surface this seems sensible, since proxy entities
     342            //        should only exist in fogged areas. Are there any uses of
     343            //        the range manager where we would care about fogged proxies?
     344            CmpPtr<ICmpVisual> cmpVisual(GetSimContext(), ent);
     345            if (cmpVisual && cmpVisual->GetProxiedEntity() != INVALID_ENTITY)
     346                break;
     347
    334348            // The newly-created entity will have owner -1 and position out-of-world
    335349            // (any initialisation of those values will happen later), so we can just
    336350            // use the default-constructed EntityData here
    public:  
    448462            ExecuteActiveQueries();
    449463            break;
    450464        }
     465        case MT_Update_Visibility:
     466        {
     467            CMessageVisibilityChanged msg;
     468            for (std::set<entity_id_t>::iterator it = m_DirtyVisibility.begin(); it != m_DirtyVisibility.end(); ++it)
     469            {
     470                g_Game->GetSimulation2()->PostMessage(*it, msg);
     471            }
     472            m_DirtyVisibility.clear();
     473            break;
     474        }
    451475        case MT_RenderSubmit:
    452476        {
    453477            const CMessageRenderSubmit& msgData = static_cast<const CMessageRenderSubmit&> (msg);
    public:  
    949973    virtual ELosVisibility GetLosVisibility(entity_id_t ent, player_id_t player, bool forceRetainInFog)
    950974    {
    951975        // (We can't use m_EntityData since this needs to handle LOCAL entities too)
     976        bool blankProxyExists = false;
     977
     978        CmpPtr<ICmpVisual> cmpVisual(GetSimContext(), ent);
     979        if (cmpVisual)
     980        {
     981            // Proxy entities for other players are never visible
     982            player_id_t target = cmpVisual->GetProxyPlayerTarget();
     983            if (target != INVALID_PLAYER && target != player)
     984                return VIS_HIDDEN;
     985
     986            // If a regular proxy exists, we should never show this actor.
     987            // If an empty proxy exists (ie. an invisible proxy for something
     988            // created inside FoW), we want to show this actor if it would be
     989            // visible, and show nothing if it's fogged.
     990            entity_id_t proxy = cmpVisual->GetProxyForPlayer(player);
     991            if (proxy == SYSTEM_ENTITY)
     992                blankProxyExists = true;
     993            else if (proxy != INVALID_ENTITY)
     994                return VIS_HIDDEN;
     995        }
    952996
    953997        // Entities not with positions in the world are never visible
    954998        CmpPtr<ICmpPosition> cmpPosition(GetSimContext(), ent);
    public:  
    9761020            return VIS_VISIBLE;
    9771021
    9781022        // Fogged if the 'retain in fog' flag is set, and in a non-visible explored region
    979         if (los.IsExplored(i, j))
     1023        if (!blankProxyExists && los.IsExplored(i, j))
    9801024        {
    9811025            CmpPtr<ICmpVision> cmpVision(GetSimContext(), ent);
    9821026            if (forceRetainInFog || (cmpVision && cmpVision->GetRetainInFog()))
    public:  
    10571101                    m_LosState[i+1 + j*m_TerrainVerticesPerSide] |= (LOS_EXPLORED << (2*(p-1)));
    10581102                    m_LosState[i + (j+1)*m_TerrainVerticesPerSide] |= (LOS_EXPLORED << (2*(p-1)));
    10591103                    m_LosState[i+1 + (j+1)*m_TerrainVerticesPerSide] |= (LOS_EXPLORED << (2*(p-1)));
     1104
     1105                    // Add any entities in this tile to the dirty visibility map
     1106                    CFixedVector2D start(fixed::FromInt(i*TERRAIN_TILE_SIZE), fixed::FromInt(j*TERRAIN_TILE_SIZE));
     1107                    CFixedVector2D end(fixed::FromInt((i+1)*TERRAIN_TILE_SIZE), fixed::FromInt((j+1)*TERRAIN_TILE_SIZE));
     1108                    std::vector<entity_id_t> ents = m_Subdivision.GetInRange(start, end);
     1109                    m_DirtyVisibility.insert(ents.begin(), ents.end());
    10601110                }
    10611111            }
    10621112        }
    public:  
    11171167            ASSERT(counts[idx] < 65535);
    11181168            counts[idx] = (u16)(counts[idx] + 1); // ignore overflow; the player should never have 64K units
    11191169        }
     1170
     1171        // Add any entities in this strip to the dirty visibility map
     1172        CFixedVector2D start(fixed::FromInt(i0*TERRAIN_TILE_SIZE), fixed::FromInt(j*TERRAIN_TILE_SIZE));
     1173        CFixedVector2D end(fixed::FromInt(i1*TERRAIN_TILE_SIZE), fixed::FromInt(j*TERRAIN_TILE_SIZE));
     1174        std::vector<entity_id_t> ents = m_Subdivision.GetInRange(start, end);
     1175        m_DirtyVisibility.insert(ents.begin(), ents.end());
    11201176    }
    11211177
    11221178    /**
    public:  
    11421198                m_LosState[idx] &= ~(LOS_VISIBLE << (2*(owner-1)));
    11431199            }
    11441200        }
     1201
     1202        // Add any entities in this strip to the dirty visibility map
     1203        CFixedVector2D start(fixed::FromInt(i0*TERRAIN_TILE_SIZE), fixed::FromInt(j*TERRAIN_TILE_SIZE));
     1204        CFixedVector2D end(fixed::FromInt(i1*TERRAIN_TILE_SIZE), fixed::FromInt(j*TERRAIN_TILE_SIZE));
     1205        std::vector<entity_id_t> ents = m_Subdivision.GetInRange(start, end);
     1206        m_DirtyVisibility.insert(ents.begin(), ents.end());
    11451207    }
    11461208
    11471209    /**
  • source/simulation2/components/CCmpTerritoryInfluence.cpp

    diff --git a/source/simulation2/components/CCmpTerritoryInfluence.cpp b/source/simulation2/components/CCmpTerritoryInfluence.cpp
    index dcf5057..562114b 100644
    a b  
    2323#include "simulation2/components/ICmpOwnership.h"
    2424#include "simulation2/components/ICmpPlayerManager.h"
    2525#include "simulation2/components/ICmpTechnologyManager.h"
     26#include "simulation2/components/ICmpVisual.h"
    2627
    2728class CCmpTerritoryInfluence : public ICmpTerritoryInfluence
    2829{
    public:  
    109110            CmpPtr<ICmpPlayerManager> cmpPlayerManager(GetSimContext(), SYSTEM_ENTITY);
    110111            entity_id_t playerEnt = cmpPlayerManager->GetPlayerByID(cmpOwnership->GetOwner());
    111112
    112             if (playerEnt != INVALID_ENTITY)
     113            //XXXjdm This might be the same as just checking for being a proxy;
     114            //       it's hard to envision a proxy targetted at its owner.
     115            CmpPtr<ICmpVisual> cmpVisual(GetSimContext(), GetEntityId());
     116            bool isIgnorable = cmpVisual &&
     117                    cmpVisual->GetProxiedEntity() != INVALID_ENTITY &&
     118                    cmpVisual->GetProxyPlayerTarget() != cmpOwnership->GetOwner();
     119
     120            if (playerEnt != INVALID_ENTITY && !isIgnorable)
    113121            {
    114122                CmpPtr<ICmpTechnologyManager> cmpTechnologyManager(GetSimContext(), playerEnt);
    115123                if (cmpTechnologyManager)
  • source/simulation2/components/CCmpTerritoryManager.cpp

    diff --git a/source/simulation2/components/CCmpTerritoryManager.cpp b/source/simulation2/components/CCmpTerritoryManager.cpp
    index c8b1ec7..d0d1838 100644
    a b  
    4141#include "simulation2/components/ICmpSettlement.h"
    4242#include "simulation2/components/ICmpTerrain.h"
    4343#include "simulation2/components/ICmpTerritoryInfluence.h"
     44#include "simulation2/components/ICmpVisual.h"
    4445#include "simulation2/helpers/Geometry.h"
    4546#include "simulation2/helpers/Grid.h"
    4647#include "simulation2/helpers/PriorityQueue.h"
    void CCmpTerritoryManager::CalculateTerritories()  
    378379    // Allow influence entities to override the terrain costs
    379380    RasteriseInfluences(influences, influenceGrid);
    380381
     382    player_id_t currentPlayer = GetSimContext().GetCurrentDisplayedPlayer();
     383
    381384    // Split influence entities into per-player lists, ignoring any with invalid properties
    382385    std::map<player_id_t, std::vector<entity_id_t> > influenceEntities;
    383386    std::vector<entity_id_t> rootInfluenceEntities;
    void CCmpTerritoryManager::CalculateTerritories()  
    406409        if (!cmpPosition || !cmpPosition->IsInWorld())
    407410            continue;
    408411
     412        // Ignore if a proxy for any other player but the current one, or
     413        // if a proxy for this entity exists for the current entity.
     414        CmpPtr<ICmpVisual> cmpVisual(GetSimContext(), it->first);
     415        if (cmpVisual &&
     416            (cmpVisual->GetProxiedEntity() != INVALID_ENTITY &&
     417             cmpVisual->GetProxyPlayerTarget() != currentPlayer) ||
     418            (cmpVisual->GetProxyForPlayer(currentPlayer) != INVALID_ENTITY))
     419            continue;
     420
    409421        influenceEntities[owner].push_back(it->first);
    410422
    411423        if (cmpTerritoryInfluence->IsRoot())
  • source/simulation2/components/CCmpVisualActor.cpp

    diff --git a/source/simulation2/components/CCmpVisualActor.cpp b/source/simulation2/components/CCmpVisualActor.cpp
    index 643c200..911152c 100644
    a b  
    1717
    1818#include "precompiled.h"
    1919
     20#include <sstream>
     21
     22#include "simulation2/Simulation2.h"
    2023#include "simulation2/system/Component.h"
    2124#include "ICmpVisual.h"
    2225
    2326#include "ICmpOwnership.h"
     27#include "ICmpPlayerManager.h"
    2428#include "ICmpPosition.h"
    2529#include "ICmpRangeManager.h"
    2630#include "ICmpVision.h"
     31#include "ICmpTemplateManager.h"
    2732#include "simulation2/MessageTypes.h"
    2833#include "simulation2/components/ICmpFootprint.h"
    2934#include "simulation2/components/ICmpSelectable.h"
     35#include "simulation2/serialization/SerializeTemplates.h"
    3036
    3137#include "graphics/Frustum.h"
    3238#include "graphics/Model.h"
     
    3743#include "graphics/UnitManager.h"
    3844#include "maths/Matrix3D.h"
    3945#include "maths/Vector3D.h"
     46#include "network/NetTurnManager.h"
    4047#include "ps/CLogger.h"
     48#include "ps/Game.h"
    4149#include "renderer/Scene.h"
     50#include "lib/utf8.h"
    4251
    4352class CCmpVisualActor : public ICmpVisual
    4453{
    public:  
    4958        componentManager.SubscribeToMessageType(MT_Interpolate);
    5059        componentManager.SubscribeToMessageType(MT_RenderSubmit);
    5160        componentManager.SubscribeToMessageType(MT_OwnershipChanged);
     61        componentManager.SubscribeToMessageType(MT_VisibilityChanged);
    5262        componentManager.SubscribeGloballyToMessageType(MT_TerrainChanged);
    5363    }
    5464
    public:  
    6070    fixed m_R, m_G, m_B; // shading colour
    6171
    6272    ICmpRangeManager::ELosVisibility m_Visibility; // only valid between Interpolate and RenderSubmit
     73    std::vector<ICmpRangeManager::ELosVisibility> m_LastVisibility;
     74    bool m_FirstVisUpdate;
     75
     76    typedef std::map<player_id_t, entity_id_t> ProxyEntityMap;
     77    ProxyEntityMap m_ProxyEntities;
     78    entity_id_t m_ProxiedEntity;
     79    player_id_t m_ProxyPlayerTarget;
     80
     81    int32_t m_ActorSeed;
    6382
    6483    // Current animation state
    6584    fixed m_AnimRunThreshold; // if non-zero this is the special walk/run mode
    public:  
    141160
    142161        m_R = m_G = m_B = fixed::FromInt(1);
    143162
     163        m_FirstVisUpdate = true;
     164        CmpPtr<ICmpPlayerManager> cmpPlayerManager(GetSimContext(), SYSTEM_ENTITY);
     165        for (int32_t i = 0; i < cmpPlayerManager->GetNumPlayers(); i++)
     166            m_LastVisibility.push_back(ICmpRangeManager::VIS_FOGGED);
     167
     168        m_ProxyPlayerTarget = INVALID_PLAYER;
     169        m_ProxiedEntity = INVALID_ENTITY;
     170
     171        m_ActorSeed = GetEntityId();
     172
    144173        if (!GetSimContext().HasUnitManager())
    145174            return; // do nothing further if graphics are disabled
    146175
    public:  
    151180        else
    152181            m_ActorName = paramNode.GetChild("Actor").ToString();
    153182
    154         std::set<CStr> selections;
    155         m_Unit = GetSimContext().GetUnitManager().CreateUnit(m_ActorName, GetActorSeed(), selections);
    156         if (m_Unit)
    157         {
    158             CModelAbstract& model = m_Unit->GetModel();
    159             if (model.ToCModel())
    160             {
    161                 u32 modelFlags = 0;
    162 
    163                 if (paramNode.GetChild("SilhouetteDisplay").ToBool())
    164                     modelFlags |= MODELFLAG_SILHOUETTE_DISPLAY;
    165 
    166                 if (paramNode.GetChild("SilhouetteOccluder").ToBool())
    167                     modelFlags |= MODELFLAG_SILHOUETTE_OCCLUDER;
    168 
    169                 CmpPtr<ICmpVision> cmpVision(GetSimContext(), GetEntityId());
    170                 if (cmpVision && cmpVision->GetAlwaysVisible())
    171                     modelFlags |= MODELFLAG_IGNORE_LOS;
    172 
    173                 model.ToCModel()->AddFlagsRec(modelFlags);
    174             }
    175 
    176             // Initialize the model's selection shape descriptor. This currently relies on the component initialization order; the
    177             // Footprint component must be initialized before this component (VisualActor) to support the ability to use the footprint
    178             // shape for the selection box (instead of the default recursive bounding box). See TypeList.h for the order in
    179             // which components are initialized; if for whatever reason you need to get rid of this dependency, you can always just
    180             // initialize the selection shape descriptor on-demand.
    181             InitSelectionShapeDescriptor(model, paramNode);
    182 
    183             m_Unit->SetID(GetEntityId());
    184         }
     183        ResetUnitSeed(m_ActorSeed, paramNode);
    185184
    186185        SelectAnimation("idle", false, fixed::FromInt(1), L"");
    187186    }
    public:  
    217216        serialize.NumberFixed_Unbounded("anim desync", m_AnimDesync);
    218217        serialize.NumberFixed_Unbounded("anim sync repeat time", m_AnimSyncRepeatTime);
    219218
     219        serialize.NumberI32_Unbounded("seed", m_ActorSeed);
     220        serialize.Bool("first vis update", m_FirstVisUpdate);
     221        SerializeVector<SerializeU8_Enum<ICmpRangeManager::ELosVisibility, ICmpRangeManager::VIS_VISIBLE> >()(serialize, "last visibility state", m_LastVisibility);
     222        serialize.NumberU32_Unbounded("proxied entity", m_ProxiedEntity);
     223        serialize.NumberI32_Unbounded("proxy player target", m_ProxyPlayerTarget);
     224        SerializeMap<SerializeI32_Unbounded, SerializeU32_Unbounded>()(serialize, "per-player proxy entities", m_ProxyEntities);
    220225        // TODO: store actor variables?
    221226    }
    222227
    public:  
    257262
    258263    virtual void HandleMessage(const CMessage& msg, bool UNUSED(global))
    259264    {
     265        if (msg.GetType() == MT_VisibilityChanged)
     266        {
     267            const CMessageVisibilityChanged& msgData = static_cast<const CMessageVisibilityChanged&> (msg);
     268            if (msgData.source == INVALID_ENTITY)
     269                VisibilityChanged();
     270            return;
     271        }
     272        else if (msg.GetType() == MT_Update_Final)
     273        {
     274            const CMessageUpdate_Final& msgData = static_cast<const CMessageUpdate_Final&> (msg);
     275            Update(msgData.turnLength);
     276            return;
     277        }
     278
    260279        // Quick exit for running in non-graphical mode
    261280        if (m_Unit == NULL)
    262281            return;
    263282
    264283        switch (msg.GetType())
    265284        {
    266         case MT_Update_Final:
    267         {
    268             const CMessageUpdate_Final& msgData = static_cast<const CMessageUpdate_Final&> (msg);
    269             Update(msgData.turnLength);
    270             break;
    271         }
    272285        case MT_Interpolate:
    273286        {
    274287            const CMessageInterpolate& msgData = static_cast<const CMessageInterpolate&> (msg);
    public:  
    285298        {
    286299            const CMessageOwnershipChanged& msgData = static_cast<const CMessageOwnershipChanged&> (msg);
    287300            m_Unit->GetModel().SetPlayerID(msgData.to);
     301                        //XXXjdm do we need to update the ownership of proxies too?
    288302            break;
    289303        }
    290304        case MT_TerrainChanged:
    public:  
    468482        // TODO: should copy/reset silhouette flags
    469483    }
    470484
     485    virtual entity_id_t GetProxyForPlayer(player_id_t player)
     486    {
     487        ProxyEntityMap::iterator result = m_ProxyEntities.find(player);
     488        if (result != m_ProxyEntities.end()) {
     489            return result->second;
     490        }
     491        return INVALID_ENTITY;
     492    }
     493
     494    virtual void UnlinkProxyForPlayer(player_id_t player)
     495    {
     496        m_ProxyEntities.erase(player);
     497    }
     498
     499    virtual entity_id_t GetProxiedEntity()
     500    {
     501        return m_ProxiedEntity;
     502    }
     503
     504    virtual player_id_t GetProxyPlayerTarget()
     505    {
     506        return m_ProxyPlayerTarget;
     507    }
     508
     509    virtual void SetProxyData(entity_id_t entity, player_id_t player)
     510    {
     511        m_ProxiedEntity = entity;
     512        m_ProxyPlayerTarget = player;
     513    }
     514
     515    void ResetUnitSeed(int32_t seed, const CParamNode& paramNode)
     516    {
     517        m_ActorSeed = seed;
     518        if (!GetSimContext().HasUnitManager())
     519            return;
     520        if (m_Unit)
     521            GetSimContext().GetUnitManager().DeleteUnit(m_Unit);
     522
     523        std::set<CStr> selections;
     524        m_Unit = GetSimContext().GetUnitManager().CreateUnit(m_ActorName, GetActorSeed(), selections);
     525        if (!m_Unit)
     526            return;
     527
     528        CModelAbstract& model = m_Unit->GetModel();
     529        if (model.ToCModel())
     530        {
     531            u32 modelFlags = 0;
     532
     533            if (paramNode.GetChild("SilhouetteDisplay").ToBool())
     534                modelFlags |= MODELFLAG_SILHOUETTE_DISPLAY;
     535
     536            if (paramNode.GetChild("SilhouetteOccluder").ToBool())
     537                modelFlags |= MODELFLAG_SILHOUETTE_OCCLUDER;
     538
     539            CmpPtr<ICmpVision> cmpVision(GetSimContext(), GetEntityId());
     540            if (cmpVision && cmpVision->GetAlwaysVisible())
     541                modelFlags |= MODELFLAG_IGNORE_LOS;
     542
     543            model.ToCModel()->AddFlagsRec(modelFlags);
     544        }
     545
     546        // Initialize the model's selection shape descriptor. This currently relies on the component initialization order; the
     547        // Footprint component must be initialized before this component (VisualActor) to support the ability to use the footprint
     548        // shape for the selection box (instead of the default recursive bounding box). See TypeList.h for the order in
     549        // which components are initialized; if for whatever reason you need to get rid of this dependency, you can always just
     550        // initialize the selection shape descriptor on-demand.
     551        InitSelectionShapeDescriptor(model, paramNode);
     552
     553        m_Unit->SetID(GetEntityId());
     554    }
     555
     556    virtual void ResetUnitSeed(int32_t seed, const std::string& templateName)
     557    {
     558            CSimulation2& simulation = *g_Game->GetSimulation2();
     559            CmpPtr<ICmpTemplateManager> cmpTemplateManager(simulation, SYSTEM_ENTITY);
     560            const CParamNode* rootNode = cmpTemplateManager->GetTemplate(templateName);
     561            const CParamNode& paramNode = rootNode->GetChild("VisualActor");
     562            ResetUnitSeed(seed, paramNode);
     563    }
     564
    471565private:
    472566    /// Whether the visual actor has been rendered at least once.
    473567    /// Necessary because the visibility update runs on simulation update,
    private:  
    476570
    477571    int32_t GetActorSeed()
    478572    {
    479         return GetEntityId();
     573        return m_ActorSeed;
    480574    }
    481575
    482576    /// Helper method; initializes the model selection shape descriptor from XML. Factored out for readability of @ref Init.
    private:  
    488582
    489583    void Update(fixed turnLength);
    490584    void UpdateVisibility();
     585    void VisibilityChanged();
    491586    void Interpolate(float frameTime, float frameOffset);
    492587    void RenderSubmit(SceneCollector& collector, const CFrustum& frustum, bool culling);
    493588};
    void CCmpVisualActor::Update(fixed turnLength)  
    572667        return;
    573668
    574669    UpdateVisibility();
     670    if (m_FirstVisUpdate)
     671        VisibilityChanged();
    575672
    576673    // If we're in the special movement mode, select an appropriate animation
    577674    if (!m_AnimRunThreshold.IsZero())
    void CCmpVisualActor::UpdateVisibility()  
    638735    }
    639736}
    640737
     738void CCmpVisualActor::VisibilityChanged()
     739{
     740    CmpPtr<ICmpPlayerManager> cmpPlayerManager(GetSimContext(), SYSTEM_ENTITY);
     741    for (int32_t i = 0; i < cmpPlayerManager->GetNumPlayers() - 1; i++)
     742    {
     743        player_id_t player = i + 1;
     744        CmpPtr<ICmpRangeManager> cmpRangeManager(GetSimContext(), SYSTEM_ENTITY);
     745        ICmpRangeManager::ELosVisibility vis = cmpRangeManager->GetLosVisibility(GetEntityId(), player);
     746
     747        bool forceProxy = false;
     748        if (m_FirstVisUpdate)
     749        {
     750            m_LastVisibility[i] = vis;
     751            if (vis == ICmpRangeManager::VIS_FOGGED)
     752            {
     753                forceProxy = true;
     754            }
     755        }
     756
     757        // We need to create proxies for existing entities that are sitting just outside the starting
     758        // visible territory at the beginning of the game. However, any entities created after game start
     759        // inside fogged territory should not be visible.
     760        if (vis != m_LastVisibility[i] || (forceProxy && g_Game->GetTurnManager()->GetCurrentTurn() == 1))
     761        {
     762            // We only care about entities that become fogged or visible.
     763            // In the first case, we want to create a proxy entity for a particular player that retains
     764            // a snapshot of this entity at this current moment.
     765            // In the second case, we only care if a proxy entity becomes visible, at which point it
     766            // should be destroyed (proxies can outlive their proxied entities).
     767           
     768            CSimulation2& simulation = *g_Game->GetSimulation2();
     769            // If this actor is fogged, and is not a proxy, then create a proxy
     770            if (vis == ICmpRangeManager::VIS_FOGGED && m_ProxiedEntity == INVALID_ENTITY)
     771            {
     772                // Spawn a new entity that is identical to the one being proxied
     773                CmpPtr<ICmpTemplateManager> cmpTemplateManager(simulation, SYSTEM_ENTITY);
     774                std::string templ = cmpTemplateManager->GetCurrentTemplateName(GetEntityId());
     775                std::wstring wtempl = wstring_from_utf8(templ);
     776                entity_id_t proxy = simulation.AddEntity(wtempl);
     777                CmpPtr<ICmpVisual> proxyVis(simulation, proxy);
     778                proxyVis->SetProxyData(GetEntityId(), player);
     779                // Ensure any random seed is overwritten with this entity's seed
     780                proxyVis->ResetUnitSeed(GetActorSeed(), templ);
     781                m_ProxyEntities[player] = proxy;
     782
     783                // Ownership and position information are common among all relevant actors
     784                CmpPtr<ICmpPosition> ownPos(GetSimContext(), GetEntityId());
     785                if (ownPos)
     786                {
     787                    CmpPtr<ICmpPosition> proxyPos(GetSimContext(), proxy);
     788                    CFixedVector3D pos = ownPos->GetPosition();
     789                    proxyPos->JumpTo(pos.X, pos.Z);
     790                    CFixedVector3D rot = ownPos->GetRotation();
     791                    proxyPos->SetYRotation(rot.Y);
     792                    proxyPos->SetXZRotation(rot.X, rot.Z);
     793                }
     794                CmpPtr<ICmpOwnership> ownOwnership(GetSimContext(), GetEntityId());
     795                if (ownOwnership)
     796                {
     797                    CmpPtr<ICmpOwnership> proxyOwnership(GetSimContext(), proxy);
     798                    proxyOwnership->SetOwner(ownOwnership->GetOwner());
     799                }
     800
     801                // Broadcast this proxy creation so the relevant components can duplicate all
     802                // component-specific properties
     803                CMessageVisibilityChanged msg(GetEntityId(), proxy);
     804                simulation.BroadcastMessage(msg);
     805            }
     806            else if (vis == ICmpRangeManager::VIS_VISIBLE)
     807            {
     808                if (m_ProxiedEntity != INVALID_ENTITY)
     809                {
     810                    // Does the proxied actor still exist?
     811                    CmpPtr<ICmpVisual> proxyVis(simulation, m_ProxiedEntity);
     812                    if (proxyVis)
     813                    {
     814                        // Notify the original that this proxy no longer exists
     815                        proxyVis->UnlinkProxyForPlayer(player);
     816                    }
     817                    // The proxied actor should now be visible, so this proxy should die
     818                    simulation.DestroyEntity(GetEntityId());
     819                    break;
     820                }
     821                m_ProxyEntities.erase(player);
     822            }
     823        }
     824        else if (forceProxy && m_ProxiedEntity == INVALID_ENTITY)
     825        {
     826            // This feels like a hack, but it's the easiest way to avoid rendering a physical proxy
     827            // for a newly-created fogged entitiy.
     828            m_ProxyEntities[player] = SYSTEM_ENTITY;
     829        }
     830        m_LastVisibility[i] = vis;
     831    }
     832
     833    m_FirstVisUpdate = false;
     834}
     835
    641836void CCmpVisualActor::Interpolate(float frameTime, float frameOffset)
    642837{
    643838    if (m_Unit == NULL)
    void CCmpVisualActor::Interpolate(float frameTime, float frameOffset)  
    650845    else if (!m_PreviouslyRendered)
    651846    {
    652847        UpdateVisibility();
     848        VisibilityChanged();
    653849        m_PreviouslyRendered = true;
    654850    }
    655851
  • source/simulation2/components/ICmpVisual.cpp

    diff --git a/source/simulation2/components/ICmpVisual.cpp b/source/simulation2/components/ICmpVisual.cpp
    index 2a6e580..aa986c3 100644
    a b DEFINE_INTERFACE_METHOD_1("SetAnimationSyncRepeat", void, ICmpVisual, SetAnimati  
    2828DEFINE_INTERFACE_METHOD_1("SetAnimationSyncOffset", void, ICmpVisual, SetAnimationSyncOffset, fixed)
    2929DEFINE_INTERFACE_METHOD_4("SetShadingColour", void, ICmpVisual, SetShadingColour, fixed, fixed, fixed, fixed)
    3030DEFINE_INTERFACE_METHOD_2("SetVariable", void, ICmpVisual, SetVariable, std::string, float)
     31DEFINE_INTERFACE_METHOD_0("GetProxyPlayerTarget", player_id_t, ICmpVisual, GetProxyPlayerTarget)
     32DEFINE_INTERFACE_METHOD_0("GetProxiedEntity", entity_id_t, ICmpVisual, GetProxiedEntity)
    3133END_INTERFACE_WRAPPER(Visual)
  • source/simulation2/components/ICmpVisual.h

    diff --git a/source/simulation2/components/ICmpVisual.h b/source/simulation2/components/ICmpVisual.h
    index ade286e..e19923c 100644
    a b  
    1919#define INCLUDED_ICMPVISUAL
    2020
    2121#include "simulation2/system/Interface.h"
     22#include "simulation2/helpers/Player.h"
    2223
    2324#include "ps/CStr.h"
    2425#include "maths/BoundingBoxOriented.h"
    public:  
    138139     */
    139140    virtual void Hotload(const VfsPath& name) = 0;
    140141
    141     DECLARE_INTERFACE_TYPE(Visual)
     142    /**
     143     * Return the ID of the corresponding proxy entity for the given player.
     144     * Returns INVALID_ENTITY if no such proxy exists.
     145     */
     146    virtual entity_id_t GetProxyForPlayer(player_id_t player) = 0;
     147
     148    /**
     149     * Remove any knowledge of a proxy entity for the given player. Called by proxy
     150     * entities on the source entity when the proxy is destroying itself.
     151     */
     152    virtual void UnlinkProxyForPlayer(player_id_t player) = 0;
     153
     154    /**
     155     * Get the ID of the source entity for this entity. Returns INVALID_ENTITY if this
     156     * entity is not a proxy.
     157     */
     158    virtual entity_id_t GetProxiedEntity() = 0;
     159
     160    /**
     161     * Get the ID of the player for whom this proxy entity is visible. Returns INVALID_PLAYER
     162     * if this entity is not a proxy.
     163     */
     164    virtual player_id_t GetProxyPlayerTarget() = 0;
     165
     166    /**
     167     * Initialize this entity as a proxy for the given source entity, visible only to the
     168     * given player.
     169     */
     170    virtual void SetProxyData(entity_id_t entity, player_id_t player) = 0;
     171
     172    /**
     173     * Set the actor seed of this entity and recreate the actor's unit appropriately.
     174     */
     175    virtual void ResetUnitSeed(int32_t seed, const std::string& templateName) = 0;
     176 
     177    DECLARE_INTERFACE_TYPE(Visual)
    142178};
    143179
    144180// TODO: rename this to VisualActor, because the interface is actor-specific, maybe?
  • source/simulation2/scripting/MessageTypeConversions.cpp

    diff --git a/source/simulation2/scripting/MessageTypeConversions.cpp b/source/simulation2/scripting/MessageTypeConversions.cpp
    index 21ed454..d5400e2 100644
    a b MESSAGE_1(Update_MotionFormation, fixed, turnLength)  
    9090MESSAGE_1(Update_MotionUnit, fixed, turnLength)
    9191MESSAGE_1(Update_Final, fixed, turnLength)
    9292
     93jsval CMessageUpdate_Visibility::ToJSVal(ScriptInterface& scriptInterface) const
     94{
     95    TOJSVAL_SETUP();
     96    return OBJECT_TO_JSVAL(obj);
     97}
     98
     99CMessage* CMessageUpdate_Visibility::FromJSVal(ScriptInterface& UNUSED(scriptInterface), jsval UNUSED(val))
     100{
     101    return NULL;
     102}
     103
    93104////////////////////////////////
    94105
    95106jsval CMessageInterpolate::ToJSVal(ScriptInterface& scriptInterface) const
    CMessage* CMessageTechnologyModification::FromJSVal(ScriptInterface& scriptInter  
    316327    return new CMessageTechnologyModification(component, player);
    317328}
    318329
     330////////////////////////////////
     331
     332jsval CMessageVisibilityChanged::ToJSVal(ScriptInterface& scriptInterface) const
     333{
     334    TOJSVAL_SETUP();
     335    SET_MSG_PROPERTY(source);
     336    SET_MSG_PROPERTY(proxy);
     337    return OBJECT_TO_JSVAL(obj);
     338}
     339
     340CMessage* CMessageVisibilityChanged::FromJSVal(ScriptInterface& UNUSED(scriptInterface), jsval UNUSED(val))
     341{
     342    LOGWARNING(L"CMessageVisibilityChanged::FromJSVal not implemented");
     343    return NULL;
     344}
     345
    319346////////////////////////////////////////////////////////////////
    320347
    321348CMessage* CMessageFromJSVal(int mtid, ScriptInterface& scriptingInterface, jsval val)