Ticket #599: fogging.diff

File fogging.diff, 37.5 KB (added by Itms, 10 years ago)
  • binaries/data/mods/public/gui/session/selection.js

    diff --git binaries/data/mods/public/gui/session/selection.js binaries/data/mods/public/gui/session/selection.js
    index c678873..fd19fd6 100644
    EntitySelection.prototype.getTemplateNames = function()  
    235235 */
    236236EntitySelection.prototype.update = function()
    237237{
    238     var changed = false;
    239238    this.checkRenamedEntities();
     239
     240    var miraged = {};
     241    var changed = false;
    240242    for each (var ent in this.selected)
    241243    {
    242244        var entState = GetEntityState(ent);
    EntitySelection.prototype.update = function()  
    250252            continue;
    251253        }
    252254
     255        // Manually replace newly miraged entities by their mirages
     256        if (entState.fogging && entState.fogging.mirage)
     257        {
     258            miraged[ent] = entState.fogging.mirage;
     259            continue;
     260        }
     261
    253262        // Remove non-visible units (e.g. moved back into fog-of-war)
    254263        if (entState.visibility == "hidden")
    255264        {
    EntitySelection.prototype.update = function()  
    264273            continue;
    265274        }
    266275    }
     276
     277    this.rebuildSelection(miraged);
     278
    267279    if (changed)
    268280        this.onChange();
    269281};
  • binaries/data/mods/public/gui/session/selection_details.js

    diff --git binaries/data/mods/public/gui/session/selection_details.js binaries/data/mods/public/gui/session/selection_details.js
    index 4d8a347..baa4d90 100644
    function displaySingle(entState, template)  
    164164        Engine.GetGUIObjectByName("resourceCarryingIcon").tooltip = sprintf(translate("Gain: %(amount)s"), { amount: getTradingTooltip(entState.trader.goods.amount) });
    165165    }
    166166    // And for number of workers
    167     else if (entState.foundation)
     167    else if (entState.foundation && !entState.mirage)
    168168    {
    169169        Engine.GetGUIObjectByName("resourceCarryingIcon").hidden = false;
    170170        Engine.GetGUIObjectByName("resourceCarryingText").hidden = false;
    function displaySingle(entState, template)  
    172172        Engine.GetGUIObjectByName("resourceCarryingText").caption = entState.foundation.numBuilders + "    ";
    173173        Engine.GetGUIObjectByName("resourceCarryingIcon").tooltip = sprintf(translate("Number of builders.\nTasking another to this foundation would speed construction up by %(numb)s%%"), { numb : Math.round((Math.pow((entState.foundation.numBuilders+1)/entState.foundation.numBuilders, 0.7) - 1.0)*100) });
    174174    }
    175     else if (entState.resourceSupply && (!entState.resourceSupply.killBeforeGather || !entState.hitpoints))
     175    else if (entState.resourceSupply && (!entState.resourceSupply.killBeforeGather || !entState.hitpoints) && !entState.mirage)
    176176    {
    177177        Engine.GetGUIObjectByName("resourceCarryingIcon").hidden = false;
    178178        Engine.GetGUIObjectByName("resourceCarryingText").hidden = false;
  • binaries/data/mods/public/gui/session/unit_actions.js

    diff --git binaries/data/mods/public/gui/session/unit_actions.js binaries/data/mods/public/gui/session/unit_actions.js
    index 4c14055..1dab22c 100644
    var g_EntityCommands =  
    656656    "delete": {
    657657        "getInfo": function(entState)
    658658        {
     659            if (entState.mirage)
     660                return {
     661                    "tooltip": translate("You cannot destroy this entity because it is in the fog-of-war"),
     662                    "icon": "kill_small.png"
     663                };
     664
    659665            return {
    660666                "tooltip": translate("Delete"),
    661667                "icon": "kill_small.png"
    var g_EntityCommands =  
    663669        },
    664670        "execute": function(entState)
    665671        {
     672            if (entState.mirage)
     673                return;
     674
    666675            var selection = g_Selection.toList();
    667676            if (selection.length < 1)
    668677                return;
  • binaries/data/mods/public/simulation/components/BuildingAI.js

    diff --git binaries/data/mods/public/simulation/components/BuildingAI.js binaries/data/mods/public/simulation/components/BuildingAI.js
    index 4eef15b..ef124d9 100644
    BuildingAI.prototype.CheckTargetVisible = function(target)  
    335335
    336336    var cmpRangeManager = Engine.QueryInterface(SYSTEM_ENTITY, IID_RangeManager);
    337337
     338    // Entities that are hidden and miraged are considered visible
     339    var cmpFogging = Engine.QueryInterface(target, IID_Fogging);
     340    if (cmpFogging && cmpFogging.IsMiraged(cmpOwnership.GetOwner()))
     341        return true;
     342
    338343    if (cmpRangeManager.GetLosVisibility(target, cmpOwnership.GetOwner(), false) == "hidden")
    339344        return false;
    340345
  • new file inaries/data/mods/public/simulation/components/Fogging.js

    diff --git binaries/data/mods/public/simulation/components/Fogging.js binaries/data/mods/public/simulation/components/Fogging.js
    new file mode 100644
    index 0000000..87e6907
    - +  
     1const VIS_HIDDEN = 0;
     2const VIS_FOGGED = 1;
     3const VIS_VISIBLE = 2;
     4
     5function Fogging() {}
     6
     7Fogging.prototype.Schema =
     8    "<a:help>Allows this entity to be replaced by mirages entities in the fog-of-war.</a:help>" +
     9    "<empty/>";
     10
     11Fogging.prototype.Init = function()
     12{
     13    this.mirages = [];
     14    this.seen = [];
     15
     16    var cmpPlayerManager = Engine.QueryInterface(SYSTEM_ENTITY, IID_PlayerManager);
     17    for (var player = 0; player < cmpPlayerManager.GetNumPlayers(); ++player)
     18    {
     19        this.mirages.push(INVALID_ENTITY);
     20        this.seen.push(false);
     21    }
     22};
     23
     24Fogging.prototype.LoadMirage = function(player)
     25{
     26    var cmpTemplateManager = Engine.QueryInterface(SYSTEM_ENTITY, IID_TemplateManager);
     27    var templateName = "mirage|" + cmpTemplateManager.GetCurrentTemplateName(this.entity);
     28   
     29    // If this is an entity without visibility (e.g. a foundation), it should be
     30    // marked as seen for its owner
     31    var cmpParentOwnership = Engine.QueryInterface(this.entity, IID_Ownership);
     32    if (cmpParentOwnership && cmpParentOwnership.GetOwner() == player)
     33        this.seen[player] = true;
     34
     35    if (!this.seen[player] || this.mirages[player] != INVALID_ENTITY)
     36        return;
     37
     38    this.mirages[player] = Engine.AddEntity(templateName);
     39    var cmpMirage = Engine.QueryInterface(this.mirages[player], IID_Mirage);
     40    if (!cmpMirage)
     41    {
     42        error("Failed to load mirage entity for template " + templateName);
     43        this.mirages[player] = INVALID_ENTITY;
     44        return;
     45    }
     46
     47    // Setup basic mirage properties
     48    cmpMirage.SetPlayer(player);
     49    cmpMirage.SetParent(this.entity);
     50
     51    // Copy cmpOwnership data
     52    var cmpMirageOwnership = Engine.QueryInterface(this.mirages[player], IID_Ownership);
     53    if (!cmpParentOwnership || !cmpMirageOwnership)
     54    {
     55        error("Failed to setup the ownership data of the fogged entity " + templateName);
     56        return;
     57    }
     58    cmpMirageOwnership.SetOwner(cmpParentOwnership.GetOwner());
     59
     60    // Copy cmpPosition data
     61    var cmpParentPosition = Engine.QueryInterface(this.entity, IID_Position);
     62    var cmpMiragePosition = Engine.QueryInterface(this.mirages[player], IID_Position);
     63    if (!cmpParentPosition || !cmpMiragePosition)
     64    {
     65        error("Failed to setup the position data of the fogged entity " + templateName);
     66        return;
     67    }
     68    if (!cmpParentPosition.IsInWorld())
     69        return;
     70    var pos = cmpParentPosition.GetPosition();
     71    cmpMiragePosition.JumpTo(pos.x, pos.z);
     72    var rot = cmpParentPosition.GetRotation();
     73    cmpMiragePosition.SetYRotation(rot.y);
     74    cmpMiragePosition.SetXZRotation(rot.x, rot.z);
     75
     76    // Copy cmpVisualActor data
     77    var cmpParentVisualActor = Engine.QueryInterface(this.entity, IID_Visual);
     78    var cmpMirageVisualActor = Engine.QueryInterface(this.mirages[player], IID_Visual);
     79    if (!cmpParentVisualActor || !cmpMirageVisualActor)
     80    {
     81        error("Failed to setup the visual data of the fogged entity " + templateName);
     82        return;
     83    }
     84    cmpMirageVisualActor.SetActorSeed(cmpParentVisualActor.GetActorSeed());
     85
     86    // Store valuable information into the mirage component (especially for the GUI)
     87    var cmpFoundation = Engine.QueryInterface(this.entity, IID_Foundation);
     88    if (cmpFoundation)
     89        cmpMirage.AddFoundation(cmpFoundation.GetBuildPercentage());
     90
     91    var cmpHealth = Engine.QueryInterface(this.entity, IID_Health);
     92    if (cmpHealth)
     93        cmpMirage.AddHealth(
     94            cmpHealth.GetMaxHitpoints(),
     95            cmpHealth.GetHitpoints(),
     96            cmpHealth.IsRepairable() && (cmpHealth.GetHitpoints() < cmpHealth.GetMaxHitpoints())
     97        );
     98
     99    var cmpResourceSupply = Engine.QueryInterface(this.entity, IID_ResourceSupply);
     100    if (cmpResourceSupply)
     101        cmpMirage.AddResourceSupply(
     102            cmpResourceSupply.GetMaxAmount(),
     103            cmpResourceSupply.GetCurrentAmount(),
     104            cmpResourceSupply.GetType(),
     105            cmpResourceSupply.IsInfinite()
     106        );
     107};
     108
     109Fogging.prototype.IsMiraged = function(player)
     110{
     111    if (player >= this.mirages.length)
     112        return false;
     113
     114    return this.mirages[player] != INVALID_ENTITY;
     115};
     116
     117Fogging.prototype.GetMirage = function(player)
     118{
     119    if (player >= this.mirages.length)
     120        return INVALID_ENTITY;
     121
     122    return this.mirages[player];
     123};
     124
     125Fogging.prototype.WasSeen = function(player)
     126{
     127    if (player >= this.seen.length)
     128        return false;
     129
     130    return this.seen[player];
     131};
     132
     133Fogging.prototype.OnVisibilityChanged = function(msg)
     134{
     135    if (msg.player >= this.mirages.length)
     136        return;
     137
     138    if (msg.newVisibility == VIS_VISIBLE)
     139    {
     140        this.seen[msg.player] = true;
     141
     142        // Destroy mirages when we get back into LoS
     143        if (this.mirages[msg.player] != INVALID_ENTITY)
     144        {
     145            Engine.DestroyEntity(this.mirages[msg.player]);
     146            this.mirages[msg.player] = INVALID_ENTITY;
     147        }
     148    }
     149
     150    // Intermediate LoS state, meaning we must create a mirage
     151    if (msg.newVisibility == VIS_FOGGED)
     152        this.LoadMirage(msg.player);
     153};
     154
     155Fogging.prototype.OnDestroy = function(msg)
     156{
     157    for (var player = 0; player < this.mirages.length; ++player)
     158    {
     159        var cmpMirage = Engine.QueryInterface(this.mirages[player], IID_Mirage);
     160        if (cmpMirage)
     161            cmpMirage.SetParent(INVALID_ENTITY);
     162    }
     163};
     164
     165Engine.RegisterComponentType(IID_Fogging, "Fogging", Fogging);
  • binaries/data/mods/public/simulation/components/GuiInterface.js

    diff --git binaries/data/mods/public/simulation/components/GuiInterface.js binaries/data/mods/public/simulation/components/GuiInterface.js
    index cfca3af..06196ab 100644
    GuiInterface.prototype.GetEntityState = function(player, ent)  
    175175        "alertRaiser": null,
    176176        "buildEntities": null,
    177177        "identity": null,
     178        "fogging": null,
    178179        "foundation": null,
    179180        "garrisonHolder": null,
    180181        "gate": null,
    GuiInterface.prototype.GetEntityState = function(player, ent)  
    190191        "visibility": null,
    191192    };
    192193
     194    // Used for several components
     195    var cmpMirage = Engine.QueryInterface(ent, IID_Mirage);
     196
    193197    var cmpIdentity = Engine.QueryInterface(ent, IID_Identity);
    194198    if (cmpIdentity)
    195199    {
    GuiInterface.prototype.GetEntityState = function(player, ent)  
    216220        ret.needsRepair = cmpHealth.IsRepairable() && (cmpHealth.GetHitpoints() < cmpHealth.GetMaxHitpoints());
    217221        ret.needsHeal = !cmpHealth.IsUnhealable();
    218222    }
     223    if (cmpMirage && cmpMirage.Health())
     224    {
     225        ret.hitpoints = cmpMirage.GetHitpoints();
     226        ret.maxHitpoints = cmpMirage.GetMaxHitpoints();
     227        ret.needsRepair = cmpMirage.NeedsRepair();
     228    }
    219229
    220230    var cmpBuilder = Engine.QueryInterface(ent, IID_Builder);
    221231    if (cmpBuilder)
    GuiInterface.prototype.GetEntityState = function(player, ent)  
    251261        };
    252262    }
    253263
     264    var cmpFogging = Engine.QueryInterface(ent, IID_Fogging);
     265    if (cmpFogging)
     266    {
     267        if (cmpFogging.IsMiraged(player))
     268            ret.fogging = {"mirage": cmpFogging.GetMirage(player)};
     269        else
     270            ret.fogging = {"mirage": null};
     271    }
     272
    254273    var cmpFoundation = Engine.QueryInterface(ent, IID_Foundation);
    255274    if (cmpFoundation)
    256275    {
    GuiInterface.prototype.GetEntityState = function(player, ent)  
    259278            "numBuilders": cmpFoundation.GetNumBuilders()
    260279        };
    261280    }
     281    if (cmpMirage && cmpMirage.Foundation())
     282    {
     283        ret.foundation = {
     284            "progress": cmpMirage.GetBuildPercentage()
     285        };
     286    }
    262287
    263288    var cmpOwnership = Engine.QueryInterface(ent, IID_Ownership);
    264289    if (cmpOwnership)
    GuiInterface.prototype.GetExtendedEntityState = function(player, ent)  
    342367        "barterMarket": null,
    343368        "buildingAI": null,
    344369        "healer": null,
     370        "mirage": null,
    345371        "obstruction": null,
    346372        "turretParent":null,
    347373        "promotion": null,
    GuiInterface.prototype.GetExtendedEntityState = function(player, ent)  
    351377        "resourceSupply": null,
    352378    };
    353379
     380    var cmpMirage = Engine.QueryInterface(ent, IID_Mirage);
     381    if (cmpMirage)
     382        ret.mirage = true;
     383
    354384    var cmpIdentity = Engine.QueryInterface(ent, IID_Identity);
    355385
    356386    var cmpAttack = Engine.QueryInterface(ent, IID_Attack);
    GuiInterface.prototype.GetExtendedEntityState = function(player, ent)  
    445475            "gatherers": cmpResourceSupply.GetGatherers()
    446476        };
    447477    }
     478    if (cmpMirage && cmpMirage.ResourceSupply())
     479    {
     480        ret.resourceSupply = {
     481            "max": cmpMirage.GetMaxAmount(),
     482            "amount": cmpMirage.GetAmount(),
     483            "type": cmpMirage.GetType(),
     484            "isInfinite": cmpMirage.IsInfinite()
     485        };
     486    }
    448487
    449488    var cmpResourceGatherer = Engine.QueryInterface(ent, IID_ResourceGatherer);
    450489    if (cmpResourceGatherer)
  • new file inaries/data/mods/public/simulation/components/Mirage.js

    diff --git binaries/data/mods/public/simulation/components/Mirage.js binaries/data/mods/public/simulation/components/Mirage.js
    new file mode 100644
    index 0000000..9306149
    - +  
     1const VIS_HIDDEN = 0;
     2const VIS_FOGGED = 1;
     3const VIS_VISIBLE = 2;
     4
     5function Mirage() {}
     6
     7Mirage.prototype.Schema =
     8    "<a:help>Mirage entities replace real entities in the fog-of-war.</a:help>" +
     9    "<empty/>";
     10
     11Mirage.prototype.Init = function()
     12{
     13    this.player = null;
     14    this.parent = INVALID_ENTITY;
     15
     16    this.foundation = false;
     17    this.buildPercentage = null;
     18
     19    this.health = false;
     20    this.maxHitpoints = null;
     21    this.hitpoints = null;
     22    this.needsRepair = null;
     23
     24    this.resourceSupply = false;
     25    this.maxAmount = null;
     26    this.amount = null;
     27    this.type = null;
     28    this.isInfinite = null;
     29};
     30
     31Mirage.prototype.SetParent = function(ent)
     32{
     33    this.parent = ent;
     34};
     35
     36Mirage.prototype.GetPlayer = function()
     37{
     38    return this.player;
     39};
     40
     41Mirage.prototype.SetPlayer = function(player)
     42{
     43    this.player = player;
     44};
     45
     46// ============================
     47// Parent entity data
     48
     49// Foundation data
     50
     51Mirage.prototype.AddFoundation = function(buildPercentage)
     52{
     53    this.foundation = true;
     54    this.buildPercentage = buildPercentage;
     55};
     56
     57Mirage.prototype.Foundation = function()
     58{
     59    return this.foundation;
     60};
     61
     62Mirage.prototype.GetBuildPercentage = function()
     63{
     64    return this.buildPercentage;
     65};
     66
     67// Health data
     68
     69Mirage.prototype.AddHealth = function(maxHitpoints, hitpoints, needsRepair)
     70{
     71    this.health = true;
     72    this.maxHitpoints = maxHitpoints;
     73    this.hitpoints = Math.ceil(hitpoints);
     74    this.needsRepair = needsRepair;
     75};
     76
     77Mirage.prototype.Health = function()
     78{
     79    return this.health;
     80};
     81
     82Mirage.prototype.GetMaxHitpoints = function()
     83{
     84    return this.maxHitpoints;
     85};
     86
     87Mirage.prototype.GetHitpoints = function()
     88{
     89    return this.hitpoints;
     90};
     91
     92Mirage.prototype.NeedsRepair = function()
     93{
     94    return this.needsRepair;
     95};
     96
     97// ResourceSupply data
     98
     99Mirage.prototype.AddResourceSupply = function(maxAmount, amount, type, isInfinite)
     100{
     101    this.resourceSupply = true;
     102    this.maxAmount = maxAmount;
     103    this.amount = amount;
     104    this.type = type;
     105    this.isInfinite = isInfinite;
     106};
     107
     108Mirage.prototype.ResourceSupply = function()
     109{
     110    return this.resourceSupply;
     111};
     112
     113Mirage.prototype.GetMaxAmount = function()
     114{
     115    return this.maxAmount;
     116};
     117
     118Mirage.prototype.GetAmount = function()
     119{
     120    return this.amount;
     121};
     122
     123Mirage.prototype.GetType = function()
     124{
     125    return this.type;
     126};
     127
     128Mirage.prototype.IsInfinite = function()
     129{
     130    return this.isInfinite;
     131};
     132
     133// ============================
     134
     135Mirage.prototype.OnVisibilityChanged = function(msg)
     136{
     137    if (msg.player == this.player && msg.newVisibility == VIS_VISIBLE && this.parent == INVALID_ENTITY)
     138        Engine.DestroyEntity(this.entity);
     139};
     140
     141Mirage.prototype.OnDestroy = function(msg)
     142{
     143    if (this.parent == INVALID_ENTITY)
     144        return;
     145
     146    Engine.BroadcastMessage(MT_EntityRenamed, { entity: this.entity, newentity: this.parent });
     147};
     148
     149Engine.RegisterComponentType(IID_Mirage, "Mirage", Mirage);
  • binaries/data/mods/public/simulation/components/ResourceGatherer.js

    diff --git binaries/data/mods/public/simulation/components/ResourceGatherer.js binaries/data/mods/public/simulation/components/ResourceGatherer.js
    index 201eb67..2d5fc4c 100644
    ResourceGatherer.prototype.GetTargetGatherRate = function(target)  
    243243{
    244244    var cmpPlayer = QueryOwnerInterface(this.entity, IID_Player);
    245245
     246    var type;
    246247    var cmpResourceSupply = Engine.QueryInterface(target, IID_ResourceSupply);
    247     if (!cmpResourceSupply)
     248    var cmpMirage = Engine.QueryInterface(target, IID_Mirage);
     249    if (cmpResourceSupply)
     250        type = cmpResourceSupply.GetType();
     251    else if (cmpMirage && cmpMirage.ResourceSupply())
     252        type = cmpMirage.GetType();
     253    else
    248254        return 0;
    249255
    250     var type = cmpResourceSupply.GetType();
    251 
    252256    var rates = this.GetGatherRates();
    253257
    254258    var rate;
    ResourceGatherer.prototype.GetTargetGatherRate = function(target)  
    261265        rate = rates[type.generic] / cmpPlayer.GetCheatTimeMultiplier();
    262266    }
    263267
     268    if (cmpMirage)
     269        return rate || 0;
     270
    264271    // Apply diminishing returns with more gatherers, for e.g. infinite farms. For most resources this has no effect. (GetDiminishingReturns will return null.)
     272    // We can assume that for resources that are miraged this is the case. (else just add the diminishing returns data to the mirage data and remove the
     273    // early return above)
    265274    // Note to people looking to change <DiminishingReturns> in a template: This is a bit complicated. Basically, the lower that number is
    266275    // the steeper diminishing returns will be. I suggest playing around with Wolfram Alpha or a graphing calculator a bit.
    267276    // In each of the following links, replace 0.65 with the gather rate of your worker for the resource with diminishing returns and
  • binaries/data/mods/public/simulation/components/UnitAI.js

    diff --git binaries/data/mods/public/simulation/components/UnitAI.js binaries/data/mods/public/simulation/components/UnitAI.js
    index 8aa0c0a..22e1190 100644
    UnitAI.prototype.UnitFsmSpec = {  
    19871987                    // check that we can gather from the resource we're supposed to gather from.
    19881988                    var cmpOwnership = Engine.QueryInterface(this.entity, IID_Ownership);
    19891989                    var cmpSupply = Engine.QueryInterface(this.gatheringTarget, IID_ResourceSupply);
    1990                     if (!cmpSupply || !cmpSupply.AddGatherer(cmpOwnership.GetOwner(), this.entity))
     1990                    var cmpMirage = Engine.QueryInterface(this.gatheringTarget, IID_Mirage);
     1991                    if ((!cmpMirage || !cmpMirage.ResourceSupply()) &&
     1992                        (!cmpSupply || !cmpSupply.AddGatherer(cmpOwnership.GetOwner(), this.entity)))
    19911993                    {
    19921994                        // Save the current order's data in case we need it later
    19931995                        var oldType = this.order.data.type;
    UnitAI.prototype.TargetIsAlive = function(ent)  
    38963898    if (cmpFormation)
    38973899        return true;
    38983900
     3901    var cmpMirage = Engine.QueryInterface(ent, IID_Mirage);
     3902    if (cmpMirage)
     3903        return true;
     3904
    38993905    var cmpHealth = Engine.QueryInterface(ent, IID_Health);
    39003906    if (!cmpHealth)
    39013907        return false;
    UnitAI.prototype.CheckTargetVisible = function(target)  
    43864392    if (!cmpRangeManager)
    43874393        return false;
    43884394
     4395    // Entities that are hidden and miraged are considered visible
     4396    var cmpFogging = Engine.QueryInterface(target, IID_Fogging);
     4397    if (cmpFogging && cmpFogging.IsMiraged(cmpOwnership.GetOwner()))
     4398        return true;
     4399
    43894400    if (cmpRangeManager.GetLosVisibility(target, cmpOwnership.GetOwner(), false) == "hidden")
    43904401        return false;
    43914402
    UnitAI.prototype.PerformGather = function(target, queued, force)  
    50075018    // Save the resource type now, so if the resource gets destroyed
    50085019    // before we process the order then we still know what resource
    50095020    // type to look for more of
     5021    var type;
    50105022    var cmpResourceSupply = Engine.QueryInterface(target, IID_ResourceSupply);
    5011     var type = cmpResourceSupply.GetType();
     5023    var cmpMirage = Engine.QueryInterface(target, IID_Mirage);
     5024    if (cmpResourceSupply)
     5025        type = cmpResourceSupply.GetType();
     5026    else if (cmpMirage && cmpMirage.ResourceSupply())
     5027        type = cmpMirage.GetType();
     5028    else
     5029        error("CanGather allowed gathering from invalid entity");
    50125030
    50135031    // Also save the target entity's template, so that if it's an animal,
    50145032    // we won't go from hunting slow safe animals to dangerous fast ones
    UnitAI.prototype.CanGather = function(target)  
    55525570{
    55535571    if (this.IsTurret())
    55545572        return false;
    5555     // The target must be a valid resource supply.
     5573    // The target must be a valid resource supply, or the mirage of one.
    55565574    var cmpResourceSupply = Engine.QueryInterface(target, IID_ResourceSupply);
    5557     if (!cmpResourceSupply)
     5575    var cmpMirage = Engine.QueryInterface(target, IID_Mirage);
     5576    if (!cmpResourceSupply && !(cmpMirage && cmpMirage.ResourceSupply()))
    55585577        return false;
    55595578
    55605579    // Formation controllers should always respond to commands
  • binaries/data/mods/public/simulation/components/interfaces/Messages.js

    diff --git binaries/data/mods/public/simulation/components/interfaces/Messages.js binaries/data/mods/public/simulation/components/interfaces/Messages.js
    index c4f6c6f..ae3d8fc 100644
     
    11/**
    22 * Broadcast message
    33 * sent when one entity is changed to other:
    4  * from Foundation component when building constuction is done
    5  * and from Promotion component when unit is promoted
     4 * - from Foundation component when building construction is done
     5 * - from Promotion component when unit is promoted
     6 * - from Mirage component when a fogged entity is re-discovered
    67 * Data: { entity: <integer>, newentity: <integer> }
    78 */
    89Engine.RegisterMessageType("EntityRenamed");
  • binaries/data/mods/public/simulation/components/tests/test_GuiInterface.js

    diff --git binaries/data/mods/public/simulation/components/tests/test_GuiInterface.js binaries/data/mods/public/simulation/components/tests/test_GuiInterface.js
    index 0ae49bc..197785d 100644
    TS_ASSERT_UNEVAL_EQUALS(cmp.GetEntityState(-1, 10), {  
    398398        visibleClasses: ["class3", "class4"],
    399399        selectionGroupName: "Selection Group Name",
    400400    },
     401    fogging: null,
    401402    foundation: null,
    402403    garrisonHolder: null,
    403404    gate: null,
    TS_ASSERT_UNEVAL_EQUALS(cmp.GetExtendedEntityState(-1, 10), {  
    425426    },
    426427    buildingAI: null,
    427428    healer: null,
     429    mirage: null,
    428430    obstruction: null,
    429431    turretParent: null,
    430432    promotion: null,
  • binaries/data/mods/public/simulation/helpers/Commands.js

    diff --git binaries/data/mods/public/simulation/helpers/Commands.js binaries/data/mods/public/simulation/helpers/Commands.js
    index 6cc67a3..90e28c5 100644
    function TryConstructBuilding(player, cmpPlayer, controlAllUnits, cmd)  
    974974            "queued": cmd.queued
    975975        });
    976976    }
     977   
     978    // Load a mirage for the owner of this new entity
     979    var cmpFogging = Engine.QueryInterface(ent, IID_Fogging)
     980    if (cmpFogging)
     981        cmpFogging.LoadMirage(player);
    977982
    978983    return ent;
    979984}
  • binaries/data/mods/public/simulation/templates/template_gaia.xml

    diff --git binaries/data/mods/public/simulation/templates/template_gaia.xml binaries/data/mods/public/simulation/templates/template_gaia.xml
    index 27744da..7a5f54b 100644
     
    11<?xml version="1.0" encoding="utf-8"?>
    22<Entity parent="template_entity_quasi">
     3  <Fogging/>
    34  <Identity>
    45    <Civ>gaia</Civ>
    56    <GenericName>Gaia</GenericName>
  • binaries/data/mods/public/simulation/templates/template_structure.xml

    diff --git binaries/data/mods/public/simulation/templates/template_structure.xml binaries/data/mods/public/simulation/templates/template_structure.xml
    index 6272e33..47cc0ef 100644
     
    3737    <SinkRate>3.0</SinkRate>
    3838    <SinkAccel>9.8</SinkAccel>
    3939  </Decay>
     40  <Fogging/>
    4041  <Health>
    4142    <DeathType>corpse</DeathType>
    4243    <RegenRate>0</RegenRate>
  • source/ps/TemplateLoader.cpp

    diff --git source/ps/TemplateLoader.cpp source/ps/TemplateLoader.cpp
    index 4ef7374..3c985fc 100644
    bool CTemplateLoader::LoadTemplateFile(const std::string& templateName, int dept  
    8080        return true;
    8181    }
    8282
     83    // Handle special case "mirage|foo"
     84    if (templateName.find("mirage|") == 0)
     85    {
     86        // Load the base entity template, if it wasn't already loaded
     87        std::string baseName = templateName.substr(7);
     88        if (!LoadTemplateFile(baseName, depth+1))
     89        {
     90            LOGERROR(L"Failed to load entity template '%hs'", baseName.c_str());
     91            return false;
     92        }
     93        // Copy a subset to the requested template
     94        CopyMirageSubset(m_TemplateFileData[templateName], m_TemplateFileData[baseName]);
     95        return true;
     96    }
     97
    8398    // Handle special case "foundation|foo"
    8499    if (templateName.find("foundation|") == 0)
    85100    {
    void CTemplateLoader::CopyPreviewSubset(CParamNode& out, const CParamNode& in, b  
    383398    }
    384399}
    385400
     401void CTemplateLoader::CopyMirageSubset(CParamNode& out, const CParamNode& in)
     402{
     403    // Currently used for mirage entities replacing real ones in fog-of-war
     404
     405    std::set<std::string> permittedComponentTypes;
     406    permittedComponentTypes.insert("Footprint");
     407    permittedComponentTypes.insert("Minimap");
     408    permittedComponentTypes.insert("Ownership");
     409    permittedComponentTypes.insert("Position");
     410    permittedComponentTypes.insert("Selectable");
     411    permittedComponentTypes.insert("VisualActor");
     412
     413    CParamNode::LoadXMLString(out, "<Entity/>");
     414    out.CopyFilteredChildrenOfChild(in, "Entity", permittedComponentTypes);
     415
     416    // Select a subset of identity data. We don't want to have, for example, a CC mirage
     417    // that has also the CC class and then prevents construction of other CCs
     418    std::set<std::string> identitySubset;
     419    identitySubset.insert("Civ");
     420    identitySubset.insert("GenericName");
     421    identitySubset.insert("SpecificName");
     422    identitySubset.insert("Tooltip");
     423    identitySubset.insert("History");
     424    identitySubset.insert("Icon");
     425    CParamNode identity;
     426    CParamNode::LoadXMLString(identity, "<Identity/>");
     427    identity.CopyFilteredChildrenOfChild(in.GetChild("Entity"), "Identity", identitySubset);
     428    CParamNode::LoadXMLString(out, ("<Entity>"+utf8_from_wstring(identity.ToXML())+"</Entity>").c_str());
     429
     430    // Set the entity as mirage entity
     431    CParamNode::LoadXMLString(out, "<Entity><Mirage/></Entity>");
     432    CParamNode::LoadXMLString(out, "<Entity><Vision><Range>0</Range><RetainInFog>true</RetainInFog><AlwaysVisible>false</AlwaysVisible></Vision></Entity>");
     433}
     434
    386435void CTemplateLoader::CopyFoundationSubset(CParamNode& out, const CParamNode& in)
    387436{
    388437    // TODO: this is all kind of yucky and hard-coded; it'd be nice to have a more generic
    void CTemplateLoader::CopyFoundationSubset(CParamNode& out, const CParamNode& in  
    397446    permittedComponentTypes.insert("Obstruction");
    398447    permittedComponentTypes.insert("Selectable");
    399448    permittedComponentTypes.insert("Footprint");
     449    permittedComponentTypes.insert("Fogging");
    400450    permittedComponentTypes.insert("Armour");
    401451    permittedComponentTypes.insert("Health");
    402452    permittedComponentTypes.insert("StatusBars");
  • source/ps/TemplateLoader.h

    diff --git source/ps/TemplateLoader.h source/ps/TemplateLoader.h
    index 7217b69..30888b6 100644
    private:  
    7979    void CopyPreviewSubset(CParamNode& out, const CParamNode& in, bool corpse);
    8080
    8181    /**
     82     * Copy the components of an entity template necessary for a fogged "mirage"
     83     * entity (position, actor) into a new entity template
     84     */
     85    void CopyMirageSubset(CParamNode& out, const CParamNode& in);
     86
     87    /**
    8288     * Copy the components of an entity template necessary for a construction foundation
    8389     * (position, actor, armour, health, etc) into a new entity template
    8490     */
  • source/simulation2/TypeList.h

    diff --git source/simulation2/TypeList.h source/simulation2/TypeList.h
    index 8525a1c..eb5ac6a 100644
    COMPONENT(CommandQueue)  
    8181INTERFACE(Decay)
    8282COMPONENT(Decay)
    8383
     84INTERFACE(Fogging)
     85COMPONENT(FoggingScripted)
     86
    8487// Note: The VisualActor component relies on this component being initialized before itself, in order to support using
    8588// an entity's footprint shape for the selection boxes. This dependency is not strictly necessary, but it does avoid
    8689// some extra plumbing code to set up on-demand initialization. If you find yourself forced to break this dependency,
    COMPONENT(IdentityScripted)  
    97100INTERFACE(Minimap)
    98101COMPONENT(Minimap)
    99102
     103INTERFACE(Mirage)
     104COMPONENT(MirageScripted)
     105
    100106INTERFACE(Motion)
    101107COMPONENT(MotionBall)
    102108COMPONENT(MotionScripted)
  • source/simulation2/components/CCmpRangeManager.cpp

    diff --git source/simulation2/components/CCmpRangeManager.cpp source/simulation2/components/CCmpRangeManager.cpp
    index 4fca628..f61deab 100644
     
    2323#include "ICmpTerrain.h"
    2424#include "simulation2/system/EntityMap.h"
    2525#include "simulation2/MessageTypes.h"
     26#include "simulation2/components/ICmpFogging.h"
     27#include "simulation2/components/ICmpMirage.h"
     28#include "simulation2/components/ICmpOwnership.h"
    2629#include "simulation2/components/ICmpPosition.h"
    2730#include "simulation2/components/ICmpTerritoryManager.h"
    2831#include "simulation2/components/ICmpVision.h"
    public:  
    564567        case MT_Update:
    565568        {
    566569            m_DebugOverlayDirty = true;
    567             UpdateTerritoriesLos();
    568570            ExecuteActiveQueries();
    569571            UpdateVisibilityData();
    570572            break;
    public:  
    14011403                return VIS_VISIBLE;
    14021404        }
    14031405
     1406        // Mirage entities, whatever their position, are visible for one specific player
     1407        CmpPtr<ICmpMirage> cmpMirage(ent);
     1408        if (cmpMirage && cmpMirage->GetPlayer() != player)
     1409            return VIS_HIDDEN;
     1410
    14041411        // Visible if within a visible region
    14051412        CLosQuerier los(GetSharedLosMask(player), m_LosState, m_TerrainVerticesPerSide);
    14061413
    14071414        if (los.IsVisible(i, j))
    14081415            return VIS_VISIBLE;
    14091416
     1417        if (!los.IsExplored(i, j))         
     1418            return VIS_HIDDEN;
     1419           
    14101420        // Fogged if the 'retain in fog' flag is set, and in a non-visible explored region
    1411         if (los.IsExplored(i, j))
     1421        CmpPtr<ICmpVision> cmpVision(ent);
     1422        if (!forceRetainInFog && !(cmpVision && cmpVision->GetRetainInFog()))
     1423            return VIS_HIDDEN;
     1424
     1425        if (cmpMirage && cmpMirage->GetPlayer() == player)
     1426            return VIS_FOGGED;
     1427
     1428        CmpPtr<ICmpOwnership> cmpOwnership(ent);
     1429        if (!cmpOwnership)
     1430            return VIS_VISIBLE;
     1431       
     1432        if (cmpOwnership->GetOwner() == player)
    14121433        {
    1413             CmpPtr<ICmpVision> cmpVision(ent);
    1414             if (forceRetainInFog || (cmpVision && cmpVision->GetRetainInFog()))
     1434            CmpPtr<ICmpFogging> cmpFogging(ent);
     1435            if (!cmpFogging)
     1436                return VIS_VISIBLE;
     1437           
     1438            // Fogged entities must not disappear while the mirage is not ready
     1439            if (!cmpFogging->IsMiraged(player))
    14151440                return VIS_FOGGED;
    1416         }
    14171441
    1418         // Otherwise not visible
     1442            return VIS_HIDDEN;
     1443        }
     1444   
     1445        // Fogged entities must not disappear while the mirage is not ready
     1446        CmpPtr<ICmpFogging> cmpFogging(ent);
     1447        if (cmpFogging && cmpFogging->WasSeen(player) && !cmpFogging->IsMiraged(player))
     1448            return VIS_FOGGED;
     1449               
    14191450        return VIS_HIDDEN;
    14201451    }
    14211452
    public:  
    14871518        EntityMap<EntityData>::iterator itEnts = m_EntityData.find(ent);
    14881519        if (itEnts == m_EntityData.end())
    14891520            return;
     1521
     1522        std::vector<u8> oldVisibilities;
     1523        std::vector<u8> newVisibilities;
    14901524       
    1491         for (player_id_t player = 1; player <= MAX_LOS_PLAYER_ID; ++player)
     1525        for (player_id_t player = 1; player < MAX_LOS_PLAYER_ID+1; ++player)
    14921526        {
    14931527            u8 oldVis = (itEnts->second.visibilities >> (2*(player-1))) & 0x3;
    14941528            u8 newVis = GetLosVisibility(itEnts->first, player, false);
     1529           
     1530            oldVisibilities.push_back(oldVis);
     1531            newVisibilities.push_back(newVis);
    14951532
    14961533            if (oldVis != newVis)
    1497             {
    1498                 CMessageVisibilityChanged msg(player, ent, oldVis, newVis);
    1499                 GetSimContext().GetComponentManager().PostMessage(ent, msg);
    15001534                itEnts->second.visibilities = (itEnts->second.visibilities & ~(0x3 << 2*(player-1))) | (newVis << 2*(player-1));
    1501             }
     1535        }
     1536
     1537        for (player_id_t player = 1; player < MAX_LOS_PLAYER_ID+1; ++player)
     1538        {
     1539            if (oldVisibilities[player-1] == newVisibilities[player-1])
     1540                continue;
     1541           
     1542            CMessageVisibilityChanged msg(player, ent, oldVisibilities[player-1], newVisibilities[player-1]);
     1543            GetSimContext().GetComponentManager().PostMessage(ent, msg);
    15021544        }
    15031545    }
    15041546
  • new file source/simulation2/components/ICmpFogging.cpp

    diff --git source/simulation2/components/ICmpFogging.cpp source/simulation2/components/ICmpFogging.cpp
    new file mode 100644
    index 0000000..a981adb
    - +  
     1/* Copyright (C) 2014 Wildfire Games.
     2 * This file is part of 0 A.D.
     3 *
     4 * 0 A.D. is free software: you can redistribute it and/or modify
     5 * it under the terms of the GNU General Public License as published by
     6 * the Free Software Foundation, either version 2 of the License, or
     7 * (at your option) any later version.
     8 *
     9 * 0 A.D. is distributed in the hope that it will be useful,
     10 * but WITHOUT ANY WARRANTY; without even the implied warranty of
     11 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
     12 * GNU General Public License for more details.
     13 *
     14 * You should have received a copy of the GNU General Public License
     15 * along with 0 A.D.  If not, see <http://www.gnu.org/licenses/>.
     16 */
     17
     18#include "precompiled.h"
     19
     20#include "ICmpFogging.h"
     21
     22#include "simulation2/scripting/ScriptComponent.h"
     23#include "simulation2/system/InterfaceScripted.h"
     24
     25BEGIN_INTERFACE_WRAPPER(Fogging)
     26END_INTERFACE_WRAPPER(Fogging)
     27
     28class CCmpFoggingScripted : public ICmpFogging
     29{
     30public:
     31    DEFAULT_SCRIPT_WRAPPER(FoggingScripted)
     32
     33    virtual bool WasSeen(player_id_t player)
     34    {
     35        return m_Script.Call<bool>("WasSeen", player);
     36    }
     37
     38    virtual bool IsMiraged(player_id_t player)
     39    {
     40        return m_Script.Call<bool>("IsMiraged", player);
     41    }
     42};
     43
     44REGISTER_COMPONENT_SCRIPT_WRAPPER(FoggingScripted)
  • new file source/simulation2/components/ICmpFogging.h

    diff --git source/simulation2/components/ICmpFogging.h source/simulation2/components/ICmpFogging.h
    new file mode 100644
    index 0000000..cd759f6
    - +  
     1/* Copyright (C) 2014 Wildfire Games.
     2 * This file is part of 0 A.D.
     3 *
     4 * 0 A.D. is free software: you can redistribute it and/or modify
     5 * it under the terms of the GNU General Public License as published by
     6 * the Free Software Foundation, either version 2 of the License, or
     7 * (at your option) any later version.
     8 *
     9 * 0 A.D. is distributed in the hope that it will be useful,
     10 * but WITHOUT ANY WARRANTY; without even the implied warranty of
     11 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
     12 * GNU General Public License for more details.
     13 *
     14 * You should have received a copy of the GNU General Public License
     15 * along with 0 A.D.  If not, see <http://www.gnu.org/licenses/>.
     16 */
     17
     18#ifndef INCLUDED_ICMPFOGGING
     19#define INCLUDED_ICMPFOGGING
     20
     21#include "simulation2/system/Interface.h"
     22
     23#include "simulation2/helpers/Player.h"
     24
     25/**
     26 * Handles the fogging of out-of-sight enemy entities, by creating mirage
     27 * entities.
     28 * This allows hiding changes, especially destruction status or health.
     29 */
     30class ICmpFogging : public IComponent
     31{
     32public:
     33    virtual bool WasSeen(player_id_t player) = 0;
     34    virtual bool IsMiraged(player_id_t player) = 0;
     35
     36    DECLARE_INTERFACE_TYPE(Fogging)
     37};
     38
     39#endif // INCLUDED_ICMPFOGGING
  • new file source/simulation2/components/ICmpMirage.cpp

    diff --git source/simulation2/components/ICmpMirage.cpp source/simulation2/components/ICmpMirage.cpp
    new file mode 100644
    index 0000000..80ceaca
    - +  
     1/* Copyright (C) 2014 Wildfire Games.
     2 * This file is part of 0 A.D.
     3 *
     4 * 0 A.D. is free software: you can redistribute it and/or modify
     5 * it under the terms of the GNU General Public License as published by
     6 * the Free Software Foundation, either version 2 of the License, or
     7 * (at your option) any later version.
     8 *
     9 * 0 A.D. is distributed in the hope that it will be useful,
     10 * but WITHOUT ANY WARRANTY; without even the implied warranty of
     11 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
     12 * GNU General Public License for more details.
     13 *
     14 * You should have received a copy of the GNU General Public License
     15 * along with 0 A.D.  If not, see <http://www.gnu.org/licenses/>.
     16 */
     17
     18#include "precompiled.h"
     19
     20#include "ICmpMirage.h"
     21
     22#include "simulation2/scripting/ScriptComponent.h"
     23#include "simulation2/system/InterfaceScripted.h"
     24
     25BEGIN_INTERFACE_WRAPPER(Mirage)
     26END_INTERFACE_WRAPPER(Mirage)
     27
     28class CCmpMirageScripted : public ICmpMirage
     29{
     30public:
     31    DEFAULT_SCRIPT_WRAPPER(MirageScripted)
     32
     33    virtual player_id_t GetPlayer()
     34    {
     35        return m_Script.Call<player_id_t>("GetPlayer");
     36    }
     37};
     38
     39REGISTER_COMPONENT_SCRIPT_WRAPPER(MirageScripted)
  • new file source/simulation2/components/ICmpMirage.h

    diff --git source/simulation2/components/ICmpMirage.h source/simulation2/components/ICmpMirage.h
    new file mode 100644
    index 0000000..5edb227
    - +  
     1/* Copyright (C) 2014 Wildfire Games.
     2 * This file is part of 0 A.D.
     3 *
     4 * 0 A.D. is free software: you can redistribute it and/or modify
     5 * it under the terms of the GNU General Public License as published by
     6 * the Free Software Foundation, either version 2 of the License, or
     7 * (at your option) any later version.
     8 *
     9 * 0 A.D. is distributed in the hope that it will be useful,
     10 * but WITHOUT ANY WARRANTY; without even the implied warranty of
     11 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
     12 * GNU General Public License for more details.
     13 *
     14 * You should have received a copy of the GNU General Public License
     15 * along with 0 A.D.  If not, see <http://www.gnu.org/licenses/>.
     16 */
     17
     18#ifndef INCLUDED_ICMPMIRAGE
     19#define INCLUDED_ICMPMIRAGE
     20
     21#include "simulation2/system/Interface.h"
     22
     23#include "simulation2/helpers/Player.h"
     24
     25/**
     26 * Component allowing mirage entities to communicate with their parent entity.
     27 * See ICmpFogging.
     28 */
     29class ICmpMirage : public IComponent
     30{
     31public:
     32    virtual player_id_t GetPlayer() = 0;
     33
     34    DECLARE_INTERFACE_TYPE(Mirage)
     35};
     36
     37#endif // INCLUDED_ICMPMIRAGE