Ticket #2706: UpgradeComponent.7.patch

File UpgradeComponent.7.patch, 45.8 KB (added by sanderd17, 8 years ago)

Update to A21 (SVN)

  • data/mods/public/globalscripts/Templates.js

     
    233233        };
    234234        ret.icon = template.Identity.Icon;
    235235        ret.tooltip =  template.Identity.Tooltip;
    236         ret.gateConversionTooltip =  template.Identity.GateConversionTooltip;
    237236        ret.requiredTechnology = template.Identity.RequiredTechnology;
    238237        ret.visibleIdentityClasses = GetVisibleIdentityClasses(template.Identity);
    239238    }
  • data/mods/public/gui/session/input.js

     
    16411641    });
    16421642}
    16431643
    1644 // Transform a wall to a gate
    1645 function transformWallToGate(template)
     1644// Upgrade an entity to another
     1645function upgradeEntity(Template)
    16461646{
    16471647    var selection = g_Selection.toList();
    16481648    Engine.PostNetworkCommand({
    1649         "type": "wall-to-gate",
    1650         "entities": selection.filter( function(e) { return getWallGateTemplate(e) == template; } ),
    1651         "template": template,
     1649        "type": "upgrade",
     1650        "entities": selection,
     1651        "template": Template,
     1652        "queued": false
    16521653    });
    16531654}
    16541655
    1655 // Gets the gate form (if any) of a given long wall piece
    1656 function getWallGateTemplate(entity)
     1656// Cancel upgrading entities
     1657function cancelUpgradeEntity()
    16571658{
    1658     // TODO: find the gate template name in a better way
    1659     var entState = GetEntityState(entity);
    1660     var index;
    1661 
    1662     if (entState && !entState.foundation && hasClass(entState, "LongWall") && (index = entState.template.indexOf("long")) >= 0)
    1663         return entState.template.substr(0, index) + "gate";
    1664     return undefined;
     1659    var selection = g_Selection.toList();
     1660    Engine.PostNetworkCommand({
     1661        "type": "cancel-upgrade",
     1662        "entities": selection,
     1663        "queued": false
     1664    });
    16651665}
    16661666
    16671667// Set the camera to follow the given unit
  • data/mods/public/gui/session/selection_panels.js

     
    501501    }
    502502};
    503503
    504 // GATE
    505 g_SelectionPanels.Gate = {
    506     "getMaxNumberOfItems": function()
    507     {
    508         return 24 - getNumberOfRightPanelButtons();
    509     },
    510     "getItems": function(unitEntState, selection)
    511     {
    512         // Allow long wall pieces to be converted to gates
    513         var longWallTypes = {};
    514         var walls = [];
    515         var gates = [];
    516         for (var ent of selection)
    517         {
    518             var state = GetEntityState(ent);
    519             if (hasClass(state, "LongWall") && !state.gate && !longWallTypes[state.template])
    520             {
    521                 var gateTemplate = getWallGateTemplate(state.id);
    522                 if (gateTemplate)
    523                 {
    524                     var tooltipString = GetTemplateDataWithoutLocalization(state.template).gateConversionTooltip;
    525                     if (!tooltipString)
    526                     {
    527                         warn(state.template + " is supposed to be convertable to a gate, but it's missing the GateConversionTooltip in the Identity template");
    528                         tooltipString = "";
    529                     }
    530                     walls.push({
    531                         "tooltip": translate(tooltipString),
    532                         "template": gateTemplate,
    533                         "callback": function (item) { transformWallToGate(item.template); }
    534                     });
    535                 }
    536 
    537                 // We only need one entity per type.
    538                 longWallTypes[state.template] = true;
    539             }
    540             else if (state.gate && !gates.length)
    541             {
    542                 gates.push({
    543                     "gate": state.gate,
    544                     "tooltip": translate("Lock Gate"),
    545                     "locked": true,
    546                     "callback": function (item) { lockGate(item.locked); }
    547                 });
    548                 gates.push({
    549                     "gate": state.gate,
    550                     "tooltip": translate("Unlock Gate"),
    551                     "locked": false,
    552                     "callback": function (item) { lockGate(item.locked); }
    553                 });
    554             }
    555             // Show both 'locked' and 'unlocked' as active if the selected gates have both lock states.
    556             else if (state.gate && state.gate.locked != gates[0].gate.locked)
    557                 for (var j = 0; j < gates.length; ++j)
    558                     delete gates[j].gate.locked;
    559         }
    560 
    561         // Place wall conversion options after gate lock/unlock icons.
    562         var items = gates.concat(walls);
    563         return items;
    564     },
    565     "setAction": function(data)
    566     {
    567         data.button.onPress = function() {data.item.callback(data.item); };
    568     },
    569     "setTooltip": function(data)
    570     {
    571         var tooltip = data.item.tooltip;
    572         if (data.item.template)
    573         {
    574             data.template = GetTemplateData(data.item.template);
    575             data.wallCount = data.selection.reduce(function (count, ent) {
    576                     var state = GetEntityState(ent);
    577                     if (hasClass(state, "LongWall") && !state.gate)
    578                         ++count;
    579                     return count;
    580                 }, 0);
    581 
    582             tooltip += "\n" + getEntityCostTooltip(data.template, data.wallCount);
    583 
    584             data.neededResources = Engine.GuiInterfaceCall("GetNeededResources", {
    585                 "cost": multiplyEntityCosts(data.template, data.wallCount)
    586             });
    587 
    588             if (data.neededResources)
    589                 tooltip += getNeededResourcesTooltip(data.neededResources);
    590         }
    591         data.button.tooltip = tooltip;
    592     },
    593     "setGraphics": function(data)
    594     {
    595         data.button.enabled = controlsPlayer(data.unitEntState.player);
    596         var gateIcon;
    597         if (data.item.gate)
    598         {
    599             // If already a gate, show locking actions
    600             gateIcon = "icons/lock_" + GATE_ACTIONS[data.item.locked ? 0 : 1] + "ed.png";
    601             if (data.item.gate.locked === undefined)
    602                 data.guiSelection.hidden = false;
    603             else
    604                 data.guiSelection.hidden = data.item.gate.locked != data.item.locked;
    605         }
    606         else
    607         {
    608             // otherwise show gate upgrade icon
    609             var template = GetTemplateData(data.item.template);
    610             if (!template)
    611                 return;
    612             gateIcon = data.template.icon ? "portraits/" + data.template.icon : "icons/gate_closed.png";
    613             data.guiSelection.hidden = true;
    614         }
    615 
    616         data.icon.sprite = (data.neededResources ? resourcesToAlphaMask(data.neededResources) + ":" : "") + "stretched:session/" + gateIcon;
    617     },
    618     "setPosition": function(data)
    619     {
    620         var index = data.i + getNumberOfRightPanelButtons();
    621         setPanelObjectPosition(data.button, index, data.rowLength);
    622     }
    623 };
    624 
    625504// PACK
    626505g_SelectionPanels.Pack = {
    627506    "getMaxNumberOfItems": function()
     
    689568    }
    690569};
    691570
     571// UPGRADE
     572g_SelectionPanels.Upgrade = {
     573    "getMaxNumberOfItems": function()
     574    {
     575        return 24 - getNumberOfRightPanelButtons();
     576    },
     577    "getItems": function(unitEntState, selection)
     578    {
     579        // Interface becomes complicated with multiple units and this is meant per-entity, so prevent it if the selection has multiple units.
     580        // TODO: if the units are all the same, this should probably still be possible.
     581        if (selection.length > 1)
     582            return false;
     583
     584        if (!unitEntState.upgrade)
     585            return false;
     586
     587        var items = [];
     588
     589        for (let upgrade of unitEntState.upgrade.upgrades)
     590        {
     591            var item = {};
     592            item.entType = upgrade.entity;
     593            item.template = GetTemplateData(upgrade.entity);
     594            if (!item.template) // abort if no template
     595                return false;
     596
     597            item.icon = upgrade.icon;
     598            if (!item.icon)
     599                item.icon = "portraits/" + item.template.icon;
     600
     601            if (upgrade.requiredTechnology !== null)
     602            {
     603                item.requiredTechnology = upgrade.requiredTechnology;
     604                if (!item.requiredTechnology && item.template.requiredTechnology)
     605                    item.requiredTechnology = item.template.requiredTechnology
     606            }
     607            item.cost = upgrade.cost;
     608            item.time = upgrade.time;
     609
     610            if (unitEntState.upgrade.progress === undefined)
     611            {
     612                item.upgrading = UPGRADING_NOT_STARTED;
     613                item.tooltip = sprintf(translate("Upgrade into a %(name)s.%(tooltip)s"), { name: item.template.name.generic, tooltip: (upgrade.tooltip? "\n" + upgrade.tooltip : "") });
     614                item.callback = function(data) { upgradeEntity(data.entType); };
     615            }
     616            else if (unitEntState.upgrade.template !== upgrade.entity)
     617            {
     618                item.upgrading = UPGRADING_CHOSEN_OTHER;
     619                item.tooltip = translate("Cannot upgrade when the entity is already upgrading.");
     620                item.callback = function(data) { };
     621            }
     622            else
     623            {
     624                item.upgrading = unitEntState.upgrade.progress;
     625                item.tooltip = translate("Cancel Upgrading");
     626                item.callback = function(data) { cancelUpgradeEntity(); };
     627            }
     628            items.push(item);
     629        }
     630        return items;
     631    },
     632    "addData" : function(data)
     633    {
     634        data.item.technologyEnabled = true;
     635        if (data.item.requiredTechnology)
     636            data.item.technologyEnabled = Engine.GuiInterfaceCall("IsTechnologyResearched", data.item.requiredTechnology);
     637        if (data.item.cost)
     638        {
     639            var totalCost = {};
     640            for (let i in data.item.cost)
     641                totalCost[i] = data.item.cost[i];
     642            data.item.neededResources = Engine.GuiInterfaceCall("GetNeededResources", totalCost);
     643        }
     644        data.item.limits = getEntityLimitAndCount(data.playerState, data.item.entType);
     645        return true;
     646    },
     647    "setAction": function(data)
     648    {
     649        data.button.onPress = function() { data.item.callback(data.item); };
     650    },
     651    "setTooltip": function(data)
     652    {
     653        var tooltip = data.item.tooltip;
     654
     655        if (data.item.upgrading !== UPGRADING_NOT_STARTED)
     656        {
     657            data.button.tooltip = tooltip;
     658            return;
     659        }
     660
     661        if (data.item.cost)
     662            tooltip += "\n" + getEntityCostTooltip(data.item);
     663
     664        tooltip += formatLimitString(data.item.limits.entLimit, data.item.limits.entCount, data.item.limits.entLimitChangers);
     665        if (!data.item.technologyEnabled)
     666        {
     667            var techName = getEntityNames(GetTechnologyData(data.item.requiredTechnology));
     668            tooltip += "\n" + sprintf(translate("Requires %(technology)s"), { technology: techName });
     669        }
     670        if (data.item.neededResources)
     671            tooltip += getNeededResourcesTooltip(data.item.neededResources);
     672
     673        data.button.tooltip = tooltip;
     674    },
     675    "setGraphics": function(data)
     676    {
     677        var grayscale = "";
     678        if (data.item.upgrading == UPGRADING_CHOSEN_OTHER || !data.item.technologyEnabled || (data.item.limits.canBeAddedCount == 0 && data.item.upgrading == UPGRADING_NOT_STARTED))
     679        {
     680            data.button.enabled = false;
     681            grayscale = "grayscale:";
     682            data.affordableMask.hidden = false;
     683            data.affordableMask.sprite = "colour: 0 0 0 127";
     684        }
     685        else if (data.item.upgrading == UPGRADING_NOT_STARTED && data.item.neededResources)
     686        {
     687            data.button.enabled = false;
     688            data.affordableMask.hidden = false;
     689            data.affordableMask.sprite = resourcesToAlphaMask(data.item.neededResources);
     690        }
     691
     692        data.icon.sprite = "stretched:" + grayscale + "session/" + data.item.icon;
     693
     694        var guiObject = Engine.GetGUIObjectByName("unitUpgradeProgressSlider["+data.i+"]");
     695        var size = guiObject.size;
     696        if (data.item.upgrading < 0)
     697            size.top = size.right;
     698        else
     699            size.top = size.left + Math.round(Math.max(0,data.item.upgrading) * (size.right - size.left));
     700        guiObject.size = size;
     701    },
     702    "setPosition": function(data)
     703    {
     704        var index = data.i + getNumberOfRightPanelButtons();
     705        setPanelObjectPosition(data.button, index, data.rowLength);
     706    },
     707};
     708
    692709// QUEUE
    693710g_SelectionPanels.Queue = {
    694711    "getMaxNumberOfItems": function()
     
    11631180    "Stance", // normal together with formation
    11641181
    11651182    // RIGHT PANE
    1166     "Gate", // must always be shown on gates
    11671183    "Pack", // must always be shown on packable entities
     1184    "Upgrade", // must always be shown on upgradable entities
    11681185    "Training",
    11691186    "Construction",
    11701187    "Research", // normal together with training
  • data/mods/public/gui/session/selection_panels_helpers.js

     
    88// Gate constants
    99const GATE_ACTIONS = ["lock", "unlock"];
    1010
     11// upgrade constants
     12const UPGRADING_NOT_STARTED = -2;
     13const UPGRADING_CHOSEN_OTHER = -1;
     14
    1115// ==============================================
    1216// BARTER HELPERS
    1317// Resources to sell on barter panel
  • data/mods/public/gui/session/selection_panels_right/upgrade_panel.xml

     
     1<?xml version="1.0" encoding="utf-8"?>
     2<object name="unitUpgradePanel"
     3    size="10 12 100% 100%"
     4>
     5    <object size="0 0 100% 100%">
     6    <repeat count="8">
     7        <object name="unitUpgradeButton[n]" hidden="true" style="iconButton" type="button" size="0 0 46 46" tooltip_style="sessionToolTipBottom">
     8        <object name="unitUpgradeIcon[n]" type="image" ghost="true" size="3 3 43 43"/>
     9        <object name="unitUpgradeUpgradeIcon[n]" type="image" ghost="true" size="3 3 43 43" sprite="stretched:session/icons/upgrade.png"/>
     10        <object name="unitUpgradeProgressSlider[n]" type="image" sprite="queueProgressSlider" ghost="true" size="3 3 43 43" z="20"/>
     11        <object name="unitUpgradeSelection[n]" hidden="true" type="image" ghost="true" size="3 3 43 43" sprite="stretched:session/icons/corners.png"/>
     12        <object name="unitUpgradeUnaffordable[n]" hidden="true" type="image" ghost="true" size="3 3 43 43" sprite="colour: 255 0 0 127"/>
     13        </object>
     14    </repeat>
     15    </object>
     16</object>
  • data/mods/public/gui/session/unit_commands.js

    Property changes on: data/mods/public/gui/session/selection_panels_right/upgrade_panel.xml
    ___________________________________________________________________
    Added: svn:eol-style
    ## -0,0 +1 ##
    +native
    \ No newline at end of property
     
    11// The number of currently visible buttons (used to optimise showing/hiding)
    2 var g_unitPanelButtons = {"Selection": 0, "Queue": 0, "Formation": 0, "Garrison": 0, "Training": 0, "Research": 0, "Alert": 0, "Barter": 0, "Construction": 0, "Command": 0, "AllyCommand": 0, "Stance": 0, "Gate": 0, "Pack": 0};
     2var g_unitPanelButtons = {"Selection": 0, "Queue": 0, "Formation": 0, "Garrison": 0, "Training": 0, "Research": 0, "Alert": 0, "Barter": 0, "Construction": 0, "Command": 0, "AllyCommand": 0, "Stance": 0, "Pack": 0, "Upgrade": 0};
    33
    44/**
    55 * Set the position of a panel object according to the index,
     
    252252{
    253253    var sum = 0;
    254254
    255     for (let prop of ["Construction", "Training", "Pack", "Gate"])
     255    for (let prop of ["Construction", "Training", "Pack", "Upgrade"])
    256256        if (g_SelectionPanels[prop].used)
    257257            sum += g_unitPanelButtons[prop];
    258258
  • data/mods/public/simulation/components/GuiInterface.js

     
    236236        "guard": null,
    237237        "mirage": null,
    238238        "pack": null,
     239        "upgrade" : null,
    239240        "player": -1,
    240241        "position": null,
    241242        "production": null,
     
    295296            "progress": cmpPack.GetProgress(),
    296297        };
    297298
     299    var cmpUpgrade = Engine.QueryInterface(ent, IID_Upgrade);
     300    if (cmpUpgrade)
     301        ret.upgrade = {
     302            "upgrades" : cmpUpgrade.GetUpgrades(),
     303            "progress": cmpUpgrade.GetProgress(),
     304            "template": cmpUpgrade.GetUpgradingTo()
     305        };
     306
    298307    let cmpProductionQueue = Engine.QueryInterface(ent, IID_ProductionQueue);
    299308    if (cmpProductionQueue)
    300309        ret.production = {
  • data/mods/public/simulation/components/Identity.js

     
    4040        "</element>" +
    4141    "</optional>" +
    4242    "<optional>" +
    43         "<element name='GateConversionTooltip'>" +
    44             "<text/>" +
    45         "</element>" +
    46     "</optional>" +
    47     "<optional>" +
    4843        "<element name='Rollover'>" +
    4944            "<text/>" +
    5045        "</element>" +
  • data/mods/public/simulation/components/Pack.js

     
    116116
    117117Pack.prototype.PackProgress = function(data, lateness)
    118118{
    119     if (this.elapsedTime >= this.GetPackTime())
     119    if (this.elapsedTime < this.GetPackTime())
    120120    {
     121        this.SetElapsedTime(this.GetElapsedTime() + PACKING_INTERVAL + lateness);
     122        return;
     123    }
     124
    121125        this.CancelTimer();
    122126
    123127        this.packed = !this.packed;
    124         this.packing = false;
    125128        Engine.PostMessage(this.entity, MT_PackFinished, { packed: this.packed });
    126129
    127         // Done un/packing, copy our parameters to the final entity
    128         var newEntity = Engine.AddEntity(this.template.Entity);
    129         if (newEntity == INVALID_ENTITY)
    130         {
    131             // Error (e.g. invalid template names)
    132             error("PackProgress: Error creating entity for '" + this.template.Entity + "'");
    133             return;
    134         }
     130    var newEntity = ChangeEntityTemplate(this.entity, this.template.Entity);
    135131
    136         var cmpPosition = Engine.QueryInterface(this.entity, IID_Position);
    137         var cmpNewPosition = Engine.QueryInterface(newEntity, IID_Position);
    138         if (cmpPosition.IsInWorld())
     132    if (newEntity)
    139133        {
    140             var pos = cmpPosition.GetPosition2D();
    141             cmpNewPosition.JumpTo(pos.x, pos.y);
    142         }
    143         var rot = cmpPosition.GetRotation();
    144         cmpNewPosition.SetYRotation(rot.y);
    145         cmpNewPosition.SetXZRotation(rot.x, rot.z);
    146         cmpNewPosition.SetHeightOffset(cmpPosition.GetHeightOffset());
    147 
    148         var cmpOwnership = Engine.QueryInterface(this.entity, IID_Ownership);
    149         var cmpNewOwnership = Engine.QueryInterface(newEntity, IID_Ownership);
    150         cmpNewOwnership.SetOwner(cmpOwnership.GetOwner());
    151 
    152         // rescale capture points
    153         var cmpCapturable = Engine.QueryInterface(this.entity, IID_Capturable);
    154         var cmpNewCapturable = Engine.QueryInterface(newEntity, IID_Capturable);
    155         if (cmpCapturable && cmpNewCapturable)
    156         {
    157             let scale = cmpCapturable.GetMaxCapturePoints() / cmpNewCapturable.GetMaxCapturePoints();
    158             let newCp = cmpCapturable.GetCapturePoints().map(function (v) { return v / scale; });
    159             cmpNewCapturable.SetCapturePoints(newCp);
    160         }
    161 
    162         // Maintain current health level
    163         var cmpHealth = Engine.QueryInterface(this.entity, IID_Health);
    164         var cmpNewHealth = Engine.QueryInterface(newEntity, IID_Health);
    165         var healthLevel = Math.max(0, Math.min(1, cmpHealth.GetHitpoints() / cmpHealth.GetMaxHitpoints()));
    166         cmpNewHealth.SetHitpoints(Math.round(cmpNewHealth.GetMaxHitpoints() * healthLevel));
    167 
    168         var cmpUnitAI = Engine.QueryInterface(this.entity, IID_UnitAI);
    169         var cmpNewUnitAI = Engine.QueryInterface(newEntity, IID_UnitAI);
    170         if (cmpUnitAI && cmpNewUnitAI)
    171         {
    172             var pos = cmpUnitAI.GetHeldPosition();
    173             if (pos)
    174                 cmpNewUnitAI.SetHeldPosition(pos.x, pos.z);
    175             if (cmpUnitAI.GetStanceName())
    176                 cmpNewUnitAI.SwitchToStance(cmpUnitAI.GetStanceName());
    177             cmpNewUnitAI.AddOrders(cmpUnitAI.GetOrders());
    178             cmpNewUnitAI.SetGuardOf(cmpUnitAI.IsGuardOf());
    179         }
    180 
    181         // Maintain the list of guards
    182         var cmpGuard = Engine.QueryInterface(this.entity, IID_Guard);
    183         var cmpNewGuard = Engine.QueryInterface(newEntity, IID_Guard);
    184         if (cmpGuard && cmpNewGuard)
    185             cmpNewGuard.SetEntities(cmpGuard.GetEntities());
    186 
    187         Engine.BroadcastMessage(MT_EntityRenamed, { entity: this.entity, newentity: newEntity });
    188 
    189         // Play notification sound
    190134        var sound = this.packed ? "packed" : "unpacked";
    191135        PlaySound(sound, newEntity);
    192 
    193         // Destroy current entity
    194         Engine.DestroyEntity(this.entity);
    195136    }
    196     else
    197     {
    198         this.SetElapsedTime(this.GetElapsedTime() + PACKING_INTERVAL + lateness);
    199     }
    200137};
    201138
    202139Engine.RegisterComponentType(IID_Pack, "Pack", Pack);
  • data/mods/public/simulation/components/Upgrade.js

     
     1function Upgrade() {}
     2
     3const UPGRADING_PROGRESS_INTERVAL = 250;
     4
     5Upgrade.prototype.Schema =
     6    "<oneOrMore>" +
     7        "<element>" +
     8            "<anyName />" +
     9            "<interleave>" +
     10                "<element name='Entity' a:help='Entity to upgrade to'>" +
     11                    "<text/>" +
     12                "</element>" +
     13                "<optional>" +
     14                    "<element name='Icon' a:help='Icon to show in the GUI'>" +
     15                        "<text/>" +
     16                    "</element>" +
     17                "</optional>" +
     18                "<optional>" +
     19                    "<element name='Tooltip' a:help='This will be added to the tooltip to help the player choose why to upgrade.'>" +
     20                        "<text/>" +
     21                    "</element>" +
     22                "</optional>" +
     23                "<optional>" +
     24                    "<element name='Time' a:help='Time required to upgrade this entity, in milliseconds'>" +
     25                        "<data type='nonNegativeInteger'/>" +
     26                    "</element>" +
     27                "</optional>" +
     28                "<optional>" +
     29                    "<element name='Cost' a:help='Resource cost to upgrade this unit'>" +
     30                        "<oneOrMore>" +
     31                            "<choice>" +
     32                                "<element name='food'><data type='nonNegativeInteger'/></element>" +
     33                                "<element name='wood'><data type='nonNegativeInteger'/></element>" +
     34                                "<element name='stone'><data type='nonNegativeInteger'/></element>" +
     35                                "<element name='metal'><data type='nonNegativeInteger'/></element>" +
     36                            "</choice>" +
     37                        "</oneOrMore>" +
     38                    "</element>" +
     39                "</optional>" +
     40                "<optional>" +
     41                    "<element name='RequiredTechnology' a:help='Define what technology is required for this upgrade'>" +
     42                        "<choice>" +
     43                            "<text/>" +
     44                            "<empty/>" +
     45                        "</choice>" +
     46                    "</element>" +
     47                "</optional>" +
     48                "<optional>" +
     49                    "<element name='CheckPlacementRestrictions' a:help='Upgrading will check for placement restrictions'><empty/></element>" +
     50                "</optional>" +
     51            "</interleave>" +
     52        "</element>" +
     53    "</oneOrMore>";
     54
     55Upgrade.prototype.Init = function()
     56{
     57    this.upgrading = false;
     58    this.elapsedTime = 0;
     59    this.timer = undefined;
     60
     61    this.upgradeTemplates = {};
     62
     63    for (let choice in this.template)
     64    {
     65        let cmpIdentity = Engine.QueryInterface(this.entity, IID_Identity);
     66        let name = this.template[choice].Entity;
     67        if (cmpIdentity)
     68            name = name.replace(/\{civ\}/g, cmpIdentity.GetCiv());
     69        if (this.upgradeTemplates.name != undefined)
     70            warn("Upgrade Component: entity " + this.entity + " has two upgrades to the same entity, only the last will be used.");
     71        this.upgradeTemplates[name] = choice;
     72    }
     73};
     74
     75// On owner change, abort the upgrade
     76// This will also deal with the "OnDestroy" case.
     77Upgrade.prototype.OnOwnershipChanged = function(msg)
     78{
     79    this.CancelUpgrade();
     80
     81    if (msg.to !== -1)
     82        this.owner = msg.to;
     83};
     84
     85Upgrade.prototype.ChangeUpgradedEntityCount = function(amount)
     86{
     87    if (!this.IsUpgrading())
     88        return;
     89    var cmpTempMan = Engine.QueryInterface(SYSTEM_ENTITY, IID_TemplateManager);
     90    var template = cmpTempMan.GetTemplate(this.upgrading);
     91    if (template.TrainingRestrictions)
     92        var category = template.TrainingRestrictions.Category;
     93    else if (template.BuildRestrictions)
     94        var category = template.BuildRestrictions.Category;
     95
     96    var cmpEntityLimits = QueryPlayerIDInterface(this.owner, IID_EntityLimits);
     97    cmpEntityLimits.ChangeCount(category,amount);
     98};
     99
     100Upgrade.prototype.CanUpgradeTo = function(template)
     101{
     102    return this.upgradeTemplates[template] !== undefined;
     103};
     104
     105Upgrade.prototype.GetUpgrades = function()
     106{
     107    var ret = [];
     108
     109    var cmpIdentity = Engine.QueryInterface(this.entity, IID_Identity);
     110
     111    for (let option in this.template)
     112    {
     113        let choice = this.template[option];
     114        let entType = choice.Entity;
     115        if (cmpIdentity)
     116            entType = entType.replace(/\{civ\}/g, cmpIdentity.GetCiv());
     117
     118        let hasCosts = false;
     119        let cost = {};
     120        if (choice.Cost)
     121        {
     122            hasCosts = true;
     123            for (var type in choice.Cost)
     124                cost[type] = ApplyValueModificationsToTemplate("Upgrade/Cost/"+type, +choice.Cost[type], this.owner, entType);
     125        }
     126        if (choice.Time)
     127        {
     128            hasCosts = true;
     129            cost["time"] = ApplyValueModificationsToTemplate("Upgrade/Time", +choice.Time/1000.0, this.owner, entType);
     130        }
     131        ret.push(
     132        {
     133            "entity": entType,
     134            "icon": choice.Icon || undefined,
     135            "cost": hasCosts ? cost : undefined,
     136            "tooltip": choice.Tooltip || undefined,
     137            "requiredTechnology": "RequiredTechnology" in choice ? choice.RequiredTechnology : null,
     138        });
     139    }
     140
     141    return ret;
     142};
     143
     144Upgrade.prototype.CancelTimer = function()
     145{
     146    if (!this.timer)
     147        return;
     148
     149    var cmpTimer = Engine.QueryInterface(SYSTEM_ENTITY, IID_Timer);
     150    cmpTimer.CancelTimer(this.timer);
     151    this.timer = undefined;
     152};
     153
     154Upgrade.prototype.IsUpgrading = function()
     155{
     156    return this.upgrading !== false;
     157};
     158
     159Upgrade.prototype.GetUpgradingTo = function()
     160{
     161    return this.upgrading;
     162};
     163
     164Upgrade.prototype.WillCheckPlacementRestrictions = function(template)
     165{
     166    if (!this.upgradeTemplates[template])
     167        return undefined;
     168
     169    return this.template[this.upgradeTemplates[template]].CheckPlacementRestrictions != undefined;
     170};
     171
     172Upgrade.prototype.GetRequiredTechnology = function(templateArg)
     173{
     174    if (!this.upgradeTemplates[templateArg])
     175        return undefined;
     176
     177    var choice = this.upgradeTemplates[templateArg];
     178
     179    if ("RequiredTechnology" in this.template[choice] && this.template[choice].RequiredTechnology === undefined)
     180    {
     181        var cmpTemplateManager = Engine.QueryInterface(SYSTEM_ENTITY, IID_TemplateManager);
     182        var template = cmpTemplateManager.GetTemplate(this.template[choice].Entity);
     183        if (template.Identity.RequiredTechnology)
     184            return template.Identity.RequiredTechnology;
     185    }
     186    else if ("RequiredTechnology" in this.template[choice])
     187        return this.template[choice].RequiredTechnology;
     188
     189    return null;
     190};
     191
     192Upgrade.prototype.GetResourceCosts = function(template)
     193{
     194    if (!this.upgradeTemplates[template])
     195        return undefined;
     196
     197    var choice = this.upgradeTemplates[template];
     198    if (!this.template[choice].Cost)
     199        return {};
     200
     201    var costs = {};
     202    for (var r in this.template[choice].Cost)
     203    {
     204        costs[r] = +this.template[choice].Cost[r];
     205        costs[r] = ApplyValueModificationsToEntity("Upgrade/Cost/"+r, costs[r], this.entity);
     206    }
     207    return costs;
     208};
     209
     210Upgrade.prototype.Upgrade = function(template)
     211{
     212    if (this.IsUpgrading())
     213        return false;
     214
     215    if (!this.upgradeTemplates[template])
     216        return false;
     217
     218    var cmpPlayer = QueryOwnerInterface(this.entity, IID_Player);
     219
     220    if (!cmpPlayer.TrySubtractResources(this.GetResourceCosts(template)))
     221        return false;
     222
     223    this.upgrading = template;
     224
     225    // Prevent cheating
     226    this.ChangeUpgradedEntityCount(1);
     227
     228    if (this.GetUpgradeTime(template) !== 0)
     229    {
     230        var cmpTimer = Engine.QueryInterface(SYSTEM_ENTITY, IID_Timer);
     231        this.timer = cmpTimer.SetInterval(this.entity, IID_Upgrade, "UpgradeProgress", 0, UPGRADING_PROGRESS_INTERVAL, { "upgrading": template });
     232    }
     233    else
     234        this.UpgradeProgress();
     235
     236    return true;
     237};
     238
     239Upgrade.prototype.CancelUpgrade = function()
     240{
     241    if (this.IsUpgrading() === false)
     242        return;
     243
     244    var cmpPlayer = QueryOwnerInterface(this.entity, IID_Player);
     245    if (cmpPlayer)
     246    {
     247        var costs = this.GetResourceCosts(this.upgrading);
     248        cmpPlayer.AddResources(costs);
     249    }
     250
     251    this.ChangeUpgradedEntityCount(-1);
     252
     253    this.upgrading = false;
     254    this.CancelTimer();
     255    this.SetElapsedTime(0);
     256};
     257
     258Upgrade.prototype.GetUpgradeTime = function(templateArg)
     259{
     260    var template = this.upgrading || templateArg;
     261    var choice = this.upgradeTemplates[template];
     262    if (!choice)
     263        return undefined;
     264    return this.template[choice].Time ? ApplyValueModificationsToEntity("Upgrade/Time", +this.template[choice].Time, this.entity) : 0;
     265};
     266
     267Upgrade.prototype.GetElapsedTime = function()
     268{
     269    return this.elapsedTime;
     270};
     271
     272Upgrade.prototype.GetProgress = function()
     273{
     274    if (!this.IsUpgrading())
     275        return undefined;
     276    return this.GetUpgradeTime() == 0 ? 1 : this.elapsedTime / this.GetUpgradeTime();
     277};
     278
     279Upgrade.prototype.SetElapsedTime = function(time)
     280{
     281    this.elapsedTime = time;
     282};
     283
     284Upgrade.prototype.UpgradeProgress = function(data, lateness)
     285{
     286    if (this.elapsedTime < this.GetUpgradeTime())
     287    {
     288        this.SetElapsedTime(this.GetElapsedTime() + UPGRADING_PROGRESS_INTERVAL + lateness);
     289        return;
     290    }
     291
     292    this.CancelTimer();
     293
     294    var newEntity = ChangeEntityTemplate(this.entity, this.upgrading);
     295
     296    if (newEntity)
     297        PlaySound("upgraded", newEntity);
     298};
     299
     300Engine.RegisterComponentType(IID_Upgrade, "Upgrade", Upgrade);
  • data/mods/public/simulation/components/interfaces/Upgrade.js

    Property changes on: data/mods/public/simulation/components/Upgrade.js
    ___________________________________________________________________
    Added: svn:eol-style
    ## -0,0 +1 ##
    +native
    \ No newline at end of property
     
     1Engine.RegisterInterface("Upgrade");
  • data/mods/public/simulation/helpers/Commands.js

    Property changes on: data/mods/public/simulation/components/interfaces/Upgrade.js
    ___________________________________________________________________
    Added: svn:eol-style
    ## -0,0 +1 ##
    +native
    \ No newline at end of property
     
    578578        }
    579579    },
    580580
    581     "wall-to-gate": function(player, cmd, data)
    582     {
    583         for (let ent of data.entities)
    584             TryTransformWallToGate(ent, data.cmpPlayer, cmd);
    585     },
    586 
    587581    "lock-gate": function(player, cmd, data)
    588582    {
    589583        for (let ent of data.entities)
     
    668662        }
    669663    },
    670664
     665    "upgrade": function(player, cmd, data)
     666    {
     667        for (let ent of data.entities)
     668        {
     669            var cmpUpgrade = Engine.QueryInterface(ent, IID_Upgrade);
     670
     671            if (!cmpUpgrade || !cmpUpgrade.CanUpgradeTo(cmd.template))
     672                continue;
     673
     674            if (cmpUpgrade.WillCheckPlacementRestrictions(cmd.template) && ObstructionsBlockingTemplateChange(ent, cmd.template))
     675            {
     676                var notification = { "players": [data.cmpPlayer.GetPlayerID()], "message": "Cannot upgrade as distance requirements are not verified or terrain is obstructed." };
     677                var cmpGUIInterface = Engine.QueryInterface(SYSTEM_ENTITY, IID_GuiInterface);
     678                cmpGUIInterface.PushNotification(notification);
     679                continue;
     680            }
     681
     682            if (!CanGarrisonedChangeTemplate(ent, cmd.template))
     683            {
     684                var notification = { "players": [data.cmpPlayer.GetPlayerID()], "message": "Cannot upgrade a garrisoned entity." };
     685                var cmpGUIInterface = Engine.QueryInterface(SYSTEM_ENTITY, IID_GuiInterface);
     686                cmpGUIInterface.PushNotification(notification);
     687                continue;
     688            }
     689
     690            // Check entity limits
     691            var cmpTemplateManager = Engine.QueryInterface(SYSTEM_ENTITY, IID_TemplateManager);
     692            var template = cmpTemplateManager.GetTemplate(cmd.template);
     693            var cmpEntityLimits = QueryPlayerIDInterface(player, IID_EntityLimits);
     694            if ((template.TrainingRestrictions && !cmpEntityLimits.AllowedToTrain(template.TrainingRestrictions.Category, 1))
     695                || (template.BuildRestrictions && !cmpEntityLimits.AllowedToBuild(template.BuildRestrictions.Category)))
     696            {
     697                if (g_DebugCommands)
     698                    warn("Invalid command: build limits check failed for player " + player + ": " + uneval(cmd));
     699                continue;
     700            }
     701
     702            var cmpTechnologyManager = QueryOwnerInterface(ent, IID_TechnologyManager);
     703            if (cmpUpgrade.GetRequiredTechnology() && !cmpTechnologyManager.IsTechnologyResearched(cmpUpgrade.GetRequiredTechnology()))
     704            {
     705                if (g_DebugCommands)
     706                    warn("Invalid command: upgrading requires unresearched technology: " + uneval(cmd));
     707                continue;
     708            }
     709
     710            cmpUpgrade.Upgrade(cmd.template, data.cmpPlayer);
     711        }
     712    },
     713
     714    "cancel-upgrade": function(player, cmd, data)
     715    {
     716        for (let ent of data.entities)
     717        {
     718            var cmpUpgrade = Engine.QueryInterface(ent, IID_Upgrade);
     719            if (cmpUpgrade)
     720                cmpUpgrade.CancelUpgrade(data.cmpPlayer);
     721        }
     722    },
     723
    671724    "attack-request": function(player, cmd, data)
    672725    {
    673726        // Send a chat message to human players
     
    15671620    return entities.filter(ent => CanControlUnitOrIsAlly(ent, player, controlAll));
    15681621}
    15691622
    1570 /**
    1571  * Try to transform a wall to a gate
    1572  */
    1573 function TryTransformWallToGate(ent, cmpPlayer, cmd)
    1574 {
    1575     var cmpIdentity = Engine.QueryInterface(ent, IID_Identity);
    1576     if (!cmpIdentity)
    1577         return;
    1578 
    1579     if (!cmpIdentity.HasClass("LongWall"))
    1580     {
    1581         if (g_DebugCommands)
    1582             warn("Invalid command: invalid wall conversion to gate for player "+player+": "+uneval(cmd));
    1583         return;
    1584     }
    1585 
    1586     var civ = cmpIdentity.GetCiv();
    1587     var gate = Engine.AddEntity(cmd.template);
    1588 
    1589     var cmpCost = Engine.QueryInterface(gate, IID_Cost);
    1590     if (!cmpPlayer.TrySubtractResources(cmpCost.GetResourceCosts()))
    1591     {
    1592         if (g_DebugCommands)
    1593             warn("Invalid command: convert gate cost check failed for player "+player+": "+uneval(cmd));
    1594 
    1595         Engine.DestroyEntity(gate);
    1596         return;
    1597     }
    1598 
    1599     ReplaceBuildingWith(ent, gate);
    1600 }
    1601 
    1602 /**
    1603  * Unconditionally replace a building with another one
    1604  */
    1605 function ReplaceBuildingWith(ent, building)
    1606 {
    1607     // Move the building to the right place
    1608     var cmpPosition = Engine.QueryInterface(ent, IID_Position);
    1609     var cmpBuildingPosition = Engine.QueryInterface(building, IID_Position);
    1610     var pos = cmpPosition.GetPosition2D();
    1611     cmpBuildingPosition.JumpTo(pos.x, pos.y);
    1612     var rot = cmpPosition.GetRotation();
    1613     cmpBuildingPosition.SetYRotation(rot.y);
    1614     cmpBuildingPosition.SetXZRotation(rot.x, rot.z);
    1615 
    1616     // Copy ownership
    1617     var cmpOwnership = Engine.QueryInterface(ent, IID_Ownership);
    1618     var cmpBuildingOwnership = Engine.QueryInterface(building, IID_Ownership);
    1619     cmpBuildingOwnership.SetOwner(cmpOwnership.GetOwner());
    1620 
    1621     // Copy control groups
    1622     var cmpObstruction = Engine.QueryInterface(ent, IID_Obstruction);
    1623     var cmpBuildingObstruction = Engine.QueryInterface(building, IID_Obstruction);
    1624     cmpBuildingObstruction.SetControlGroup(cmpObstruction.GetControlGroup());
    1625     cmpBuildingObstruction.SetControlGroup2(cmpObstruction.GetControlGroup2());
    1626 
    1627     // Copy health level from the old entity to the new
    1628     var cmpHealth = Engine.QueryInterface(ent, IID_Health);
    1629     var cmpBuildingHealth = Engine.QueryInterface(building, IID_Health);
    1630     var healthFraction = Math.max(0, Math.min(1, cmpHealth.GetHitpoints() / cmpHealth.GetMaxHitpoints()));
    1631     var buildingHitpoints = Math.round(cmpBuildingHealth.GetMaxHitpoints() * healthFraction);
    1632     cmpBuildingHealth.SetHitpoints(buildingHitpoints);
    1633 
    1634     PlaySound("constructed", building);
    1635 
    1636     Engine.PostMessage(ent, MT_ConstructionFinished,
    1637         { "entity": ent, "newentity": building });
    1638     Engine.BroadcastMessage(MT_EntityRenamed, { entity: ent, newentity: building });
    1639 
    1640     Engine.DestroyEntity(ent);
    1641 }
    1642 
    16431623Engine.RegisterGlobal("GetFormationRequirements", GetFormationRequirements);
    16441624Engine.RegisterGlobal("CanMoveEntsIntoFormation", CanMoveEntsIntoFormation);
    16451625Engine.RegisterGlobal("GetDockAngle", GetDockAngle);
  • data/mods/public/simulation/helpers/Transform.js

     
     1// Helper functions to change an entity's template and check if the transformation is possible
     2
     3// returns the ID of the new entity or INVALID_ENTITY.
     4var ChangeEntityTemplate = function(oldEnt, newTemplate)
     5{
     6    // Done un/packing, copy our parameters to the final entity
     7    var newEnt = Engine.AddEntity(newTemplate);
     8    if (newEnt == INVALID_ENTITY)
     9    {
     10        // Error (e.g. invalid template names)
     11        error("Transform.js: Error replacing entity " + oldEnt + " for a '" + newTemplate + "'");
     12        return INVALID_ENTITY;
     13    }
     14
     15    var cmpPosition = Engine.QueryInterface(oldEnt, IID_Position);
     16    var cmpNewPosition = Engine.QueryInterface(newEnt, IID_Position);
     17    if (cmpPosition && cmpNewPosition)
     18    {
     19        if (cmpPosition.IsInWorld())
     20        {
     21            var pos = cmpPosition.GetPosition2D();
     22            cmpNewPosition.JumpTo(pos.x, pos.y);
     23        }
     24        var rot = cmpPosition.GetRotation();
     25        cmpNewPosition.SetYRotation(rot.y);
     26        cmpNewPosition.SetXZRotation(rot.x, rot.z);
     27        cmpNewPosition.SetHeightOffset(cmpPosition.GetHeightOffset());
     28    }
     29
     30    var cmpOwnership = Engine.QueryInterface(oldEnt, IID_Ownership);
     31    var cmpNewOwnership = Engine.QueryInterface(newEnt, IID_Ownership);
     32    if (cmpOwnership && cmpNewOwnership)
     33        cmpNewOwnership.SetOwner(cmpOwnership.GetOwner());
     34
     35    // Copy control groups
     36    var cmpObstruction = Engine.QueryInterface(oldEnt, IID_Obstruction);
     37    var cmpNewObstruction = Engine.QueryInterface(newEnt, IID_Obstruction);
     38    if (cmpObstruction && cmpNewObstruction)
     39    {
     40        cmpNewObstruction.SetControlGroup(cmpObstruction.GetControlGroup());
     41        cmpNewObstruction.SetControlGroup2(cmpObstruction.GetControlGroup2());
     42    }
     43
     44    // rescale capture points
     45    var cmpCapturable = Engine.QueryInterface(oldEnt, IID_Capturable);
     46    var cmpNewCapturable = Engine.QueryInterface(newEnt, IID_Capturable);
     47    if (cmpCapturable && cmpNewCapturable)
     48    {
     49        let scale = cmpCapturable.GetMaxCapturePoints() / cmpNewCapturable.GetMaxCapturePoints();
     50        let newCp = cmpCapturable.GetCapturePoints().map(function (v) { return v / scale; });
     51        cmpNewCapturable.SetCapturePoints(newCp);
     52    }
     53
     54    // Maintain current health level
     55    var cmpHealth = Engine.QueryInterface(oldEnt, IID_Health);
     56    var cmpNewHealth = Engine.QueryInterface(newEnt, IID_Health);
     57    if (cmpHealth && cmpNewHealth)
     58    {
     59        var healthLevel = Math.max(0, Math.min(1, cmpHealth.GetHitpoints() / cmpHealth.GetMaxHitpoints()));
     60        cmpNewHealth.SetHitpoints(Math.round(cmpNewHealth.GetMaxHitpoints() * healthLevel));
     61    }
     62
     63    var cmpUnitAI = Engine.QueryInterface(oldEnt, IID_UnitAI);
     64    var cmpNewUnitAI = Engine.QueryInterface(newEnt, IID_UnitAI);
     65    if (cmpUnitAI && cmpNewUnitAI)
     66    {
     67        var pos = cmpUnitAI.GetHeldPosition();
     68        if (pos)
     69            cmpNewUnitAI.SetHeldPosition(pos.x, pos.z);
     70        if (cmpUnitAI.GetStanceName())
     71            cmpNewUnitAI.SwitchToStance(cmpUnitAI.GetStanceName());
     72        cmpNewUnitAI.AddOrders(cmpUnitAI.GetOrders());
     73        cmpNewUnitAI.SetGuardOf(cmpUnitAI.IsGuardOf());
     74    }
     75
     76    // Maintain the list of guards
     77    var cmpGuard = Engine.QueryInterface(oldEnt, IID_Guard);
     78    var cmpNewGuard = Engine.QueryInterface(newEnt, IID_Guard);
     79    if (cmpGuard && cmpNewGuard)
     80        cmpNewGuard.SetEntities(cmpGuard.GetEntities());
     81
     82    TransferGarrisonedUnits(oldEnt, newEnt);
     83
     84    Engine.BroadcastMessage(MT_EntityRenamed, { entity: oldEnt, newentity: newEnt });
     85
     86    // Destroy current entity
     87    Engine.DestroyEntity(oldEnt);
     88
     89    return newEnt;
     90};
     91
     92var CanGarrisonedChangeTemplate = function(ent, template)
     93{
     94    var cmpPosition = Engine.QueryInterface(ent, IID_Position);
     95    var unitAI = Engine.QueryInterface(ent, IID_UnitAI);
     96    if (cmpPosition && !cmpPosition.IsInWorld() && unitAI && unitAI.IsGarrisoned())
     97    {
     98        // We're a garrisoned unit, assume impossibility as I've been unable to find a way to get the holder ID.
     99        // TODO: change this if that ever becomes possibles
     100        return false;
     101    }
     102    return true;
     103}
     104
     105var ObstructionsBlockingTemplateChange = function(ent, templateArg)
     106{
     107    var previewEntity = Engine.AddEntity("preview|"+templateArg);
     108
     109    if (previewEntity == INVALID_ENTITY)
     110        return true;
     111
     112    var cmpBuildRestrictions = Engine.QueryInterface(previewEntity, IID_BuildRestrictions);
     113    var cmpPosition = Engine.QueryInterface(ent, IID_Position);
     114    var cmpOwnership = Engine.QueryInterface(ent, IID_Ownership);
     115
     116    var cmpNewPosition = Engine.QueryInterface(previewEntity, IID_Position);
     117
     118    // Return false if no ownership as BuildRestrictions.CheckPlacement needs an owner and I have no idea if false or true is better
     119    // Plus there are no real entities without owners currently.
     120    if (!cmpBuildRestrictions || !cmpPosition || !cmpOwnership)
     121        return DeleteEntityAndReturn(previewEntity, cmpPosition, pos, angle, cmpNewPosition, false);
     122
     123    var pos = cmpPosition.GetPosition2D();
     124    var angle = cmpPosition.GetRotation();
     125    // move us away to prevent our own obstruction from blocking the upgrade.
     126    cmpPosition.MoveOutOfWorld();
     127
     128    cmpNewPosition.JumpTo(pos.x, pos.y);
     129    cmpNewPosition.SetYRotation(angle.y);
     130
     131    var cmpNewOwnership = Engine.QueryInterface(previewEntity, IID_Ownership);
     132    cmpNewOwnership.SetOwner(cmpOwnership.GetOwner());
     133
     134    var checkPlacement = cmpBuildRestrictions.CheckPlacement();
     135
     136    if (checkPlacement && !checkPlacement.success)
     137        return DeleteEntityAndReturn(previewEntity, cmpPosition, pos, angle, cmpNewPosition, true);
     138
     139    var cmpTemplateManager = Engine.QueryInterface(SYSTEM_ENTITY, IID_TemplateManager);
     140    var template = cmpTemplateManager.GetTemplate(cmpTemplateManager.GetCurrentTemplateName(ent));
     141    var newTemplate = cmpTemplateManager.GetTemplate(templateArg);
     142
     143    // Check if units are blocking our template change
     144    if (template.Obstruction && newTemplate.Obstruction)
     145    {
     146        // This only needs to be done if the new template is strictly bigger than the old one
     147        // "Obstructions" are annoying to test so just check.
     148        // This is kind of ugly, sorry about that.
     149        if (newTemplate.Obstruction.Obstructions
     150
     151            || (newTemplate.Obstruction.Static && template.Obstruction.Static
     152                && (newTemplate.Obstruction.Static["@width"] > template.Obstruction.Static["@width"]
     153                 || newTemplate.Obstruction.Static["@depth"] > template.Obstruction.Static["@depth"]))
     154            || (newTemplate.Obstruction.Static && template.Obstruction.Unit
     155                && (newTemplate.Obstruction.Static["@width"] > template.Obstruction.Unit["@radius"]
     156                 || newTemplate.Obstruction.Static["@depth"] > template.Obstruction.Unit["@radius"]))
     157
     158            || (newTemplate.Obstruction.Unit && template.Obstruction.Unit
     159                && newTemplate.Obstruction.Unit["@radius"] > template.Obstruction.Unit["@radius"])
     160            || (newTemplate.Obstruction.Unit && template.Obstruction.Static
     161                && (newTemplate.Obstruction.Unit["@radius"] > template.Obstruction.Static["@width"]
     162                 || newTemplate.Obstruction.Unit["@radius"] > template.Obstruction.Static["@depth"])))
     163        {
     164            var cmpNewObstruction = Engine.QueryInterface(previewEntity, IID_Obstruction);
     165            if (cmpNewObstruction && cmpNewObstruction.GetBlockMovementFlag())
     166            {
     167                // Check for units
     168                var collisions = cmpNewObstruction.GetEntityCollisions(false, true);
     169                if (collisions.length !== 0)
     170                    return DeleteEntityAndReturn(previewEntity, cmpPosition, pos, angle, cmpNewPosition, true);
     171            }
     172        }
     173    }
     174
     175    return DeleteEntityAndReturn(previewEntity, cmpPosition, pos, angle, cmpNewPosition, false);
     176};
     177
     178var DeleteEntityAndReturn = function(ent, cmpPosition, position, angle, cmpNewPosition, ret)
     179{
     180    cmpNewPosition.MoveOutOfWorld();    // prevent preview from interfering in the world
     181    cmpPosition.JumpTo(position.x, position.y);
     182    cmpPosition.SetYRotation(angle.y);
     183
     184    Engine.DestroyEntity(ent);
     185    return ret;
     186};
     187
     188var TransferGarrisonedUnits = function(oldEnt, newEnt)
     189{
     190    // Transfer garrisoned units if possible, or unload them
     191    var cmpGarrison = Engine.QueryInterface(oldEnt, IID_GarrisonHolder);
     192    var cmpNewGarrison = Engine.QueryInterface(newEnt, IID_GarrisonHolder);
     193    if (!cmpNewGarrison || !cmpGarrison || cmpGarrison.GetEntities().length === 0 )
     194        return; // nothing to do as the code will by default unload all.
     195
     196    var garrisonedEntities = cmpGarrison.GetEntities().slice();
     197    for (var j = 0; j < garrisonedEntities.length; ++j)
     198    {
     199        var cmpUnitAI = Engine.QueryInterface(garrisonedEntities[j], IID_UnitAI);
     200        cmpGarrison.Eject(garrisonedEntities[j]);
     201        cmpUnitAI.Autogarrison(newEnt);
     202        cmpNewGarrison.Garrison(garrisonedEntities[j]);
     203    }
     204};
     205
     206Engine.RegisterGlobal("ChangeEntityTemplate", ChangeEntityTemplate);
     207Engine.RegisterGlobal("CanGarrisonedChangeTemplate", CanGarrisonedChangeTemplate);
     208Engine.RegisterGlobal("ObstructionsBlockingTemplateChange", ObstructionsBlockingTemplateChange);
  • data/mods/public/simulation/templates/other/palisades_rocks_long.xml

    Property changes on: data/mods/public/simulation/helpers/Transform.js
    ___________________________________________________________________
    Added: svn:eol-style
    ## -0,0 +1 ##
    +native
    \ No newline at end of property
     
    2626    <SelectionGroupName>other/wallset_palisade</SelectionGroupName>
    2727    <SpecificName>Palisade</SpecificName>
    2828    <GenericName>Wooden Wall</GenericName>
    29     <GateConversionTooltip>Convert Wooden Wall into Wooden Gate</GateConversionTooltip>
    3029    <Classes datatype="tokens">-StoneWall Palisade</Classes>
    3130    <Icon>gaia/special_palisade.png</Icon>
    3231    <RequiredTechnology>phase_village</RequiredTechnology>
     
    4544  <WallPiece>
    4645    <Length>11.0</Length>
    4746  </WallPiece>
     47  <Upgrade>
     48    <Gate>
     49      <Entity>other/palisades_rocks_gate</Entity>
     50      <Cost>
     51        <stone>0</stone>
     52        <wood>20</wood>
     53      </Cost>
     54      <Time>5000</Time>
     55    </Gate>
     56  </Upgrade>
    4857</Entity>
  • data/mods/public/simulation/templates/structures/rome_siege_wall_long.xml

     
    5555    </Classes>
    5656    <Icon>structures/palisade_wall.png</Icon>
    5757    <Tooltip>A wooden and turf palisade buildable in enemy and neutral territories.</Tooltip>
    58     <GateConversionTooltip>Convert Siege Wall into Siege Wall Gate</GateConversionTooltip>
    5958    <History>Quick building, but expensive wooden and earthen walls used to surround and siege an enemy town or fortified position. The most famous examples are the Roman sieges of the Iberian stronghold of Numantia and the Gallic stronghold of Alesia.</History>
    6059  </Identity>
    6160  <Obstruction>
  • data/mods/public/simulation/templates/template_structure_defense_wall_long.xml

     
    4343  <Identity>
    4444    <Classes datatype="tokens">LongWall</Classes>
    4545    <Tooltip>Long wall segments can be converted to gates.</Tooltip>
    46     <GateConversionTooltip>Convert Stone Wall into City Gate</GateConversionTooltip>
    4746  </Identity>
     47  <Upgrade>
     48    <Gate>
     49      <Entity>structures/{civ}_wall_gate</Entity>
     50      <Tooltip>This will allow you to let units circulate through your fortifications.</Tooltip>
     51      <Cost>
     52        <stone>60</stone>
     53      </Cost>
     54      <Time>10000</Time>
     55    </Gate>
     56  </Upgrade>
    4857</Entity>