Ticket #2706: UpgradeComponent.4.patch

File UpgradeComponent.4.patch, 45.6 KB (added by wraitii, 8 years ago)

Actual RC

  • binaries/data/mods/public/art/textures/ui/session/icons/upgrade.png

    Cannot display: file marked as a binary type.
    svn:mime-type = application/octet-stream
  • binaries/data/mods/public/gui/session/input.js

    Property changes on: binaries/data/mods/public/art/textures/ui/session/icons/upgrade.png
    ___________________________________________________________________
    Added: svn:mime-type
    ## -0,0 +1 ##
    +application/octet-stream
    \ No newline at end of property
     
    16141614    });
    16151615}
    16161616
    1617 // Transform a wall to a gate
    1618 function transformWallToGate(template)
     1617// Upgrade an entity to another
     1618function upgradeEntity(Template)
    16191619{
    16201620    var selection = g_Selection.toList();
    16211621    Engine.PostNetworkCommand({
    1622         "type": "wall-to-gate",
    1623         "entities": selection.filter( function(e) { return getWallGateTemplate(e) == template } ),
    1624         "template": template,
     1622        "type": "upgrade",
     1623        "entities": selection,
     1624        "template": Template,
     1625        "queued": false
    16251626    });
    16261627}
    16271628
    1628 // Gets the gate form (if any) of a given long wall piece
    1629 function getWallGateTemplate(entity)
     1629// Cancel upgrading entities
     1630function cancelUpgradeEntity()
    16301631{
    1631     // TODO: find the gate template name in a better way
    1632     var entState = GetEntityState(entity);
    1633     var index;
    1634 
    1635     if (entState && !entState.foundation && hasClass(entState, "LongWall") && (index = entState.template.indexOf("long")) >= 0)
    1636         return entState.template.substr(0, index) + "gate";
    1637     return undefined;
     1632    var selection = g_Selection.toList();
     1633    Engine.PostNetworkCommand({
     1634        "type": "cancel-upgrade",
     1635        "entities": selection,
     1636        "queued": false
     1637    });
    16381638}
    16391639
    16401640// Set the camera to follow the given unit
  • binaries/data/mods/public/gui/session/selection_panels.js

     
    372372        for (var i in selection)
    373373        {
    374374            var state = GetEntityState(selection[i]);
    375             if (hasClass(state, "LongWall") && !state.gate && !longWallTypes[state.template])
     375            if (state.gate && !gates.length)
    376376            {
    377                 var gateTemplate = getWallGateTemplate(state.id);
    378                 if (gateTemplate)
    379                 {
    380                     var tooltipString = GetTemplateDataWithoutLocalization(state.template).gateConversionTooltip;
    381                     if (!tooltipString)
    382                     {
    383                         warn(state.template + " is supposed to be convertable to a gate, but it's missing the GateConversionTooltip in the Identity template");
    384                         tooltipString = "";
    385                     }
    386                     walls.push({
    387                         "tooltip": translate(tooltipString),
    388                         "template": gateTemplate,
    389                         "callback": function (item) { transformWallToGate(item.template); }
    390                     });
    391                 }
    392 
    393                 // We only need one entity per type.
    394                 longWallTypes[state.template] = true;
    395             }
    396             else if (state.gate && !gates.length)
    397             {
    398377                gates.push({
    399378                    "gate": state.gate,
    400379                    "tooltip": translate("Lock Gate"),
     
    424403    },
    425404    "setTooltip": function(data)
    426405    {
    427         var tooltip = data.item.tooltip;
    428         if (data.item.template)
    429         {
    430             data.template = GetTemplateData(data.item.template);
    431             data.wallCount = data.selection.reduce(function (count, ent) {
    432                     var state = GetEntityState(ent);
    433                     if (hasClass(state, "LongWall") && !state.gate)
    434                         count++;
    435                     return count;
    436                 }, 0);
    437 
    438             tooltip += "\n" + getEntityCostTooltip(data.template, data.wallCount);
    439 
    440 
    441             data.neededResources = Engine.GuiInterfaceCall("GetNeededResources", multiplyEntityCosts(data.template, data.wallCount));
    442             if (data.neededResources)
    443                 tooltip += getNeededResourcesTooltip(data.neededResources);
    444         }
    445         data.button.tooltip = tooltip;
     406        data.button.tooltip = data.item.tooltip;
    446407    },
    447408    "setGraphics": function(data)
    448409    {
    449         data.affordableMask.hidden == data.neededResources ? true : false;
    450410        var gateIcon;
    451411        if (data.item.gate)
    452412        {
    453             // If already a gate, show locking actions
     413            // Show locking actions
    454414            gateIcon = "icons/lock_" + GATE_ACTIONS[data.item.locked ? 0 : 1] + "ed.png";
    455415            if (data.item.gate.locked === undefined)
    456416                data.guiSelection.hidden = false
     
    457417            else
    458418                data.guiSelection.hidden = data.item.gate.locked != data.item.locked;
    459419        }
    460         else
    461         {
    462             // otherwise show gate upgrade icon
    463             var template = GetTemplateData(data.item.template);
    464             if (!template)
    465                 return;
    466             gateIcon = data.template.icon ? "portraits/" + data.template.icon : "icons/gate_closed.png";
    467             data.guiSelection.hidden = true;
    468         }
    469 
    470420        data.icon.sprite = "stretched:session/" + gateIcon;
    471421    },
    472422    "setPosition": function(data)
     
    541491    },
    542492};
    543493
     494// UPGRADE
     495g_SelectionPanels.Upgrade = {
     496    "getMaxNumberOfItems": function()
     497    {
     498        return 24 - getNumberOfRightPanelButtons();
     499    },
     500    "getItems": function(unitEntState, selection)
     501    {
     502        // Interface becomes complicated with multiple units and this is meant per-entity, so prevent it if the selection has multiple units.
     503        if (selection.length > 1)
     504            return false;
     505        var items = [];
     506
     507        if (!unitEntState.upgrade)
     508            return false;
     509       
     510        for each (var upgrade in unitEntState.upgrade.upgrades)
     511        {
     512            var item = {};
     513            item.entType = upgrade.entity;
     514            item.template = GetTemplateData(upgrade.entity);
     515            if (!item.template) // abort if no template
     516                return false;
     517
     518            item.icon = upgrade.icon;
     519            if (!item.icon)
     520                item.icon = "portraits/" + item.template.icon;
     521
     522            if (upgrade.requiredTechnology !== null)
     523            {
     524                item.requiredTechnology = upgrade.requiredTechnology;
     525                if (!item.requiredTechnology && item.template.requiredTechnology)
     526                    item.requiredTechnology = item.template.requiredTechnology
     527            }
     528            item.cost = upgrade.cost;
     529            item.time = upgrade.time;
     530
     531            if (unitEntState.upgrade.progress === undefined)
     532            {
     533                item.upgrading = UPGRADING_NOT_STARTED;
     534                item.tooltip = sprintf(translate("Upgrade into a %(name)s.%(tooltip)s"), { name: item.template.name.generic, tooltip: (upgrade.tooltip? "\n" + upgrade.tooltip : "") });
     535                item.callback = function(data) { upgradeEntity(data.entType); };
     536            }
     537            else if (unitEntState.upgrade.template !== upgrade.entity)
     538            {
     539                item.upgrading = UPGRADING_CHOSEN_OTHER;
     540                item.tooltip = translate("Cannot upgrade when the entity is already upgrading.");
     541                item.callback = function(data) { };
     542            }
     543            else
     544            {
     545                item.upgrading = unitEntState.upgrade.progress;
     546                item.tooltip = translate("Cancel Upgrading");
     547                item.callback = function(data) { cancelUpgradeEntity(); };
     548            }
     549            items.push(item);
     550        }
     551        return items;
     552    },
     553    "addData" : function(data)
     554    {
     555        data.item.technologyEnabled = true;
     556        if (data.item.requiredTechnology)
     557            data.item.technologyEnabled = Engine.GuiInterfaceCall("IsTechnologyResearched", data.item.requiredTechnology);
     558        if (data.item.cost)
     559        {
     560            var totalCost = {};
     561            for (var i in data.item.cost)
     562                totalCost[i] = data.item.cost[i];
     563            data.item.neededResources = Engine.GuiInterfaceCall("GetNeededResources", totalCost);
     564        }
     565        data.item.limits = getEntityLimitAndCount(data.playerState, data.item.entType);
     566        return true;
     567    },
     568    "setAction": function(data)
     569    {
     570        data.button.onPress = function() { data.item.callback(data.item); };
     571    },
     572    "setTooltip": function(data)
     573    {
     574        var tooltip = data.item.tooltip;
     575
     576        if (data.item.upgrading !== UPGRADING_NOT_STARTED)
     577        {
     578            data.button.tooltip = tooltip;
     579            return;
     580        }
     581
     582        if (data.item.cost)
     583            tooltip += "\n" + getEntityCostTooltip(data.item);
     584       
     585        tooltip += formatLimitString(data.item.limits.entLimit, data.item.limits.entCount, data.item.limits.entLimitChangers);
     586        if (!data.item.technologyEnabled)
     587        {
     588            var techName = getEntityNames(GetTechnologyData(data.item.requiredTechnology));
     589            tooltip += "\n" + sprintf(translate("Requires %(technology)s"), { technology: techName });
     590        }
     591        if (data.item.neededResources)
     592            tooltip += getNeededResourcesTooltip(data.item.neededResources);
     593
     594        data.button.tooltip = tooltip;
     595    },
     596    "setGraphics": function(data)
     597    {
     598        var grayscale = "";
     599        if (data.item.upgrading == UPGRADING_CHOSEN_OTHER || !data.item.technologyEnabled || (data.item.limits.canBeAddedCount == 0 && data.item.upgrading == UPGRADING_NOT_STARTED))
     600        {
     601            data.button.enabled = false;
     602            grayscale = "grayscale:";
     603            data.affordableMask.hidden = false;
     604            data.affordableMask.sprite = "colour: 0 0 0 127";
     605        }
     606        else if (data.item.upgrading == UPGRADING_NOT_STARTED && data.item.neededResources)
     607        {
     608            data.button.enabled = false;
     609            data.affordableMask.hidden = false;
     610            data.affordableMask.sprite = resourcesToAlphaMask(data.item.neededResources);
     611        }
     612       
     613        data.icon.sprite = "stretched:" + grayscale + "session/" + data.item.icon;
     614
     615        var guiObject = Engine.GetGUIObjectByName("unitUpgradeProgressSlider["+data.i+"]");
     616        var size = guiObject.size;
     617        if (data.item.upgrading < 0)
     618            size.top = size.right;
     619        else
     620            size.top = size.left + Math.round(Math.max(0,data.item.upgrading) * (size.right - size.left));
     621        guiObject.size = size;
     622    },
     623    "setPosition": function(data)
     624    {
     625        var index = data.i + getNumberOfRightPanelButtons();
     626        setPanelObjectPosition(data.button, index, data.rowLength);
     627    },
     628};
     629
    544630// QUEUE
    545631g_SelectionPanels.Queue = {
    546632    "getMaxNumberOfItems": function()
     
    10151101    // RIGHT PANE
    10161102    "Gate", // must always be shown on gates
    10171103    "Pack", // must always be shown on packable entities
     1104    "Upgrade", // must always be shown on upgradable entities
    10181105    "Training",
    10191106    "Construction",
    10201107    "Research", // normal together with training
  • binaries/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
  • binaries/data/mods/public/gui/session/unit_commands.js

     
    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, "Barter": 0, "Construction": 0, "Command": 0, "Stance": 0, "Gate": 0, "Pack": 0};
     2var g_unitPanelButtons = {"Selection": 0, "Queue": 0, "Formation": 0, "Garrison": 0, "Training": 0, "Research": 0, "Barter": 0, "Construction": 0, "Command": 0, "Stance": 0, "Gate": 0, "Pack": 0, "Upgrade": 0};
    33
    44/**
    55 * Set the position of a panel object according to the index,
     
    289289        sum += g_unitPanelButtons["Pack"];
    290290    if (g_SelectionPanels["Gate"].used)
    291291        sum += g_unitPanelButtons["Gate"];
     292    if (g_SelectionPanels["Upgrade"].used)
     293        sum += g_unitPanelButtons["Upgrade"];
    292294    return sum;
    293295}
  • binaries/data/mods/public/gui/session/selection_panels_right/gate_panel.xml

     
    77        <object name="unitGateButton[n]" hidden="true" style="iconButton" type="button" size="0 0 46 46" tooltip_style="sessionToolTipBottom">
    88        <object name="unitGateIcon[n]" type="image" ghost="true" size="3 3 43 43"/>
    99        <object name="unitGateSelection[n]" hidden="true" type="image" ghost="true" size="3 3 43 43" sprite="stretched:session/icons/corners.png"/>
    10         <object name="unitGateUnaffordable[n]" hidden="true" type="image" ghost="true" size="3 3 43 43" sprite="colour: 255 0 0 127"/>
    1110        </object>
    1211    </repeat>
    1312    </object>
  • binaries/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>
  • binaries/data/mods/public/simulation/components/GuiInterface.js

    Property changes on: binaries/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
     
    181181        "gate": null,
    182182        "guard": null,
    183183        "pack": null,
     184        "upgrade" : null,
    184185        "player": -1,
    185186        "position": null,
    186187        "production": null,
     
    242243        };
    243244    }
    244245
     246    var cmpUpgrade = Engine.QueryInterface(ent, IID_Upgrade);
     247    if (cmpUpgrade)
     248    {
     249        ret.upgrade = {
     250            "upgrades" : cmpUpgrade.GetUpgrades(),
     251            "progress": cmpUpgrade.GetProgress(),
     252            "template": cmpUpgrade.GetUpgradingTo()
     253        };
     254    }
     255
    245256    var cmpProductionQueue = Engine.QueryInterface(ent, IID_ProductionQueue);
    246257    if (cmpProductionQueue)
    247258    {
     
    692703        };
    693704        ret.icon = template.Identity.Icon;
    694705        ret.tooltip =  template.Identity.Tooltip;
    695         ret.gateConversionTooltip =  template.Identity.GateConversionTooltip;
    696706        ret.requiredTechnology = template.Identity.RequiredTechnology;
    697707        ret.visibleIdentityClasses = GetVisibleIdentityClasses(template.Identity);
    698708    }
  • binaries/data/mods/public/simulation/components/Identity.js

     
    3030        "</element>" +
    3131    "</optional>" +
    3232    "<optional>" +
    33         "<element name='GateConversionTooltip'>" +
    34             "<text/>" +
    35         "</element>" +
    36     "</optional>" +
    37     "<optional>" +
    3833        "<element name='Rollover'>" +
    3934            "<text/>" +
    4035        "</element>" +
  • binaries/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.CancelTimer();
     121        this.SetElapsedTime(this.GetElapsedTime() + PACKING_INTERVAL + lateness);
     122        return;
     123    }
    122124
    123         this.packed = !this.packed;
    124         Engine.PostMessage(this.entity, MT_PackFinished, { packed: this.packed });
     125    this.CancelTimer();
    125126
    126         // Done un/packing, copy our parameters to the final entity
    127         var newEntity = Engine.AddEntity(this.template.Entity);
    128         if (newEntity == INVALID_ENTITY)
    129         {
    130             // Error (e.g. invalid template names)
    131             error("PackProgress: Error creating entity for '" + this.template.Entity + "'");
    132             return;
    133         }
     127    this.packed = !this.packed;
     128    Engine.PostMessage(this.entity, MT_PackFinished, { packed: this.packed });
    134129
    135         var cmpPosition = Engine.QueryInterface(this.entity, IID_Position);
    136         var cmpNewPosition = Engine.QueryInterface(newEntity, IID_Position);
    137         if (cmpPosition.IsInWorld())
    138         {
    139             var pos = cmpPosition.GetPosition2D();
    140             cmpNewPosition.JumpTo(pos.x, pos.y);
    141         }
    142         var rot = cmpPosition.GetRotation();
    143         cmpNewPosition.SetYRotation(rot.y);
    144         cmpNewPosition.SetXZRotation(rot.x, rot.z);
    145         cmpNewPosition.SetHeightOffset(cmpPosition.GetHeightOffset());
     130    var newEntity = ChangeEntityTemplate(this.entity, this.template.Entity);
    146131
    147         var cmpOwnership = Engine.QueryInterface(this.entity, IID_Ownership);
    148         var cmpNewOwnership = Engine.QueryInterface(newEntity, IID_Ownership);
    149         cmpNewOwnership.SetOwner(cmpOwnership.GetOwner());
    150 
    151         // Maintain current health level
    152         var cmpHealth = Engine.QueryInterface(this.entity, IID_Health);
    153         var cmpNewHealth = Engine.QueryInterface(newEntity, IID_Health);
    154         var healthLevel = Math.max(0, Math.min(1, cmpHealth.GetHitpoints() / cmpHealth.GetMaxHitpoints()));
    155         cmpNewHealth.SetHitpoints(Math.round(cmpNewHealth.GetMaxHitpoints() * healthLevel));
    156 
    157         var cmpUnitAI = Engine.QueryInterface(this.entity, IID_UnitAI);
    158         var cmpNewUnitAI = Engine.QueryInterface(newEntity, IID_UnitAI);
    159         if (cmpUnitAI && cmpNewUnitAI)
    160         {
    161             var pos = cmpUnitAI.GetHeldPosition();
    162             if (pos)
    163                 cmpNewUnitAI.SetHeldPosition(pos.x, pos.z);
    164             if (cmpUnitAI.GetStanceName())
    165                 cmpNewUnitAI.SwitchToStance(cmpUnitAI.GetStanceName());
    166             cmpNewUnitAI.AddOrders(cmpUnitAI.GetOrders());
    167             cmpNewUnitAI.SetGuardOf(cmpUnitAI.IsGuardOf());
    168         }
    169 
    170         // Maintain the list of guards
    171         var cmpGuard = Engine.QueryInterface(this.entity, IID_Guard);
    172         var cmpNewGuard = Engine.QueryInterface(newEntity, IID_Guard);
    173         if (cmpGuard && cmpNewGuard)
    174             cmpNewGuard.SetEntities(cmpGuard.GetEntities());
    175 
    176         Engine.BroadcastMessage(MT_EntityRenamed, { entity: this.entity, newentity: newEntity });
    177 
    178         // Play notification sound
     132    if (newEntity)
     133    {
    179134        var sound = this.packed ? "packed" : "unpacked";
    180135        PlaySound(sound, newEntity);
    181 
    182         // Destroy current entity
    183         Engine.DestroyEntity(this.entity);
    184136    }
    185     else
    186     {
    187         this.SetElapsedTime(this.GetElapsedTime() + PACKING_INTERVAL + lateness);
    188     }
    189137};
    190138
    191139Engine.RegisterComponentType(IID_Pack, "Pack", Pack);
  • binaries/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 (var choice in this.template)
     64    {
     65        var cmpIdentity = Engine.QueryInterface(this.entity, IID_Identity);
     66        var name = this.template[choice].Entity;
     67        if (cmpIdentity)
     68            name = name.replace(/\{civ\}/g, cmpIdentity.GetCiv());
     69        if (name in this.upgradeTemplates)
     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    var category = null;
     92    if (template.TrainingRestrictions)
     93        category = template.TrainingRestrictions.Category;
     94    else if (template.BuildRestrictions)
     95        category = template.BuildRestrictions.Category;
     96
     97    var cmpEntityLimits = QueryPlayerIDInterface(this.owner, IID_EntityLimits);
     98    cmpEntityLimits.ChangeCount(category,amount);
     99};
     100
     101Upgrade.prototype.CanUpgradeTo = function(template)
     102{
     103    return this.upgradeTemplates[template] !== undefined;
     104};
     105
     106Upgrade.prototype.GetUpgrades = function()
     107{
     108    var ret = [];
     109
     110    var cmpIdentity = Engine.QueryInterface(this.entity, IID_Identity);
     111
     112    for each (var choice in this.template)
     113    {
     114        var entType = choice.Entity;
     115        if (cmpIdentity)
     116            entType = entType.replace(/\{civ\}/g, cmpIdentity.GetCiv());
     117
     118        var hasCosts = false;
     119        var 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    {
     148        var cmpTimer = Engine.QueryInterface(SYSTEM_ENTITY, IID_Timer);
     149        cmpTimer.CancelTimer(this.timer);
     150        this.timer = undefined;
     151    }
     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 ("CheckPlacementRestrictions" in this.template[this.upgradeTemplates[template]]);
     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);
  • binaries/data/mods/public/simulation/components/interfaces/Upgrade.js

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

    Property changes on: binaries/data/mods/public/simulation/components/interfaces/Upgrade.js
    ___________________________________________________________________
    Added: svn:eol-style
    ## -0,0 +1 ##
    +native
    \ No newline at end of property
     
    551551        }
    552552    },
    553553
    554     "wall-to-gate": function(player, cmd, data)
    555     {
    556         for each (var ent in data.entities)
    557         {
    558             TryTransformWallToGate(ent, data.cmpPlayer, cmd.template);
    559         }
    560     },
    561 
    562554    "lock-gate": function(player, cmd, data)
    563555    {
    564556        for each (var ent in data.entities)
     
    642634            }
    643635        }
    644636    },
     637
     638    "upgrade": function(player, cmd, data)
     639    {
     640        for each (var ent in data.entities)
     641        {
     642            var cmpUpgrade = Engine.QueryInterface(ent, IID_Upgrade);
     643
     644            if (!cmpUpgrade || !cmpUpgrade.CanUpgradeTo(cmd.template))
     645                continue;
     646
     647            if (cmpUpgrade.WillCheckPlacementRestrictions(cmd.template) && ObstructionsBlockingTemplateChange(ent, cmd.template))
     648            {
     649                var notification = {"players": [data.cmpPlayer.GetPlayerID()], "message": "Cannot upgrade as distance requirements are not verified or terrain is obstructed." };
     650                var cmpGUIInterface = Engine.QueryInterface(SYSTEM_ENTITY, IID_GuiInterface);
     651                cmpGUIInterface.PushNotification(notification);
     652                continue;
     653            }
     654
     655            if (!CanGarrisonedChangeTemplate(ent, cmd.template))
     656            {
     657                var notification = {"players": [data.cmpPlayer.GetPlayerID()], "message": "Cannot upgrade a garrisoned entity." };
     658                var cmpGUIInterface = Engine.QueryInterface(SYSTEM_ENTITY, IID_GuiInterface);
     659                cmpGUIInterface.PushNotification(notification);
     660                continue;
     661            }
     662
     663            // Check entity limits
     664            var cmpTemplateManager = Engine.QueryInterface(SYSTEM_ENTITY, IID_TemplateManager);
     665            var template = cmpTemplateManager.GetTemplate(cmd.template);
     666            var cmpEntityLimits = QueryPlayerIDInterface(player, IID_EntityLimits);
     667            if ((template.TrainingRestrictions && !cmpEntityLimits.AllowedToTrain(template.TrainingRestrictions.Category, 1))
     668                || (template.BuildRestrictions && !cmpEntityLimits.AllowedToBuild(template.BuildRestrictions.Category)))
     669            {
     670                if (g_DebugCommands)
     671                    warn("Invalid command: build limits check failed for player " + player + ": " + uneval(cmd));
     672                continue;
     673            }
     674
     675            var cmpTechnologyManager = QueryOwnerInterface(ent, IID_TechnologyManager);
     676            if (cmpUpgrade.GetRequiredTechnology() && !cmpTechnologyManager.IsTechnologyResearched(cmpUpgrade.GetRequiredTechnology()))
     677            {
     678                if (g_DebugCommands)
     679                    warn("Invalid command: upgrading requires unresearched technology: " + uneval(cmd));
     680                continue;
     681            }
     682
     683            cmpUpgrade.Upgrade(cmd.template, data.cmpPlayer);
     684        }
     685    },
     686
     687    "cancel-upgrade": function(player, cmd, data)
     688    {
     689        for each (var ent in data.entities)
     690        {
     691            var cmpUpgrade = Engine.QueryInterface(ent, IID_Upgrade);
     692            if (cmpUpgrade)
     693                cmpUpgrade.CancelUpgrade(data.cmpPlayer);
     694        }
     695    },
     696
    645697    "dialog-answer": function(player, cmd, data)
    646698    {
    647699        // Currently nothing. Triggers can read it anyway, and send this
     
    15171569    return entities.filter(function(ent) { return CanControlUnitOrIsAlly(ent, player, controlAll);} );
    15181570}
    15191571
    1520 /**
    1521  * Try to transform a wall to a gate
    1522  */
    1523 function TryTransformWallToGate(ent, cmpPlayer, template)
    1524 {
    1525     var cmpIdentity = Engine.QueryInterface(ent, IID_Identity);
    1526     if (!cmpIdentity)
    1527         return;
    1528 
    1529     // Check if this is a valid long wall segment
    1530     if (!cmpIdentity.HasClass("LongWall"))
    1531     {
    1532         if (g_DebugCommands)
    1533             warn("Invalid command: invalid wall conversion to gate for player "+player+": "+uneval(cmd));
    1534         return;
    1535     }
    1536 
    1537     var civ = cmpIdentity.GetCiv();
    1538     var gate = Engine.AddEntity(template);
    1539 
    1540     var cmpCost = Engine.QueryInterface(gate, IID_Cost);
    1541     if (!cmpPlayer.TrySubtractResources(cmpCost.GetResourceCosts()))
    1542     {
    1543         if (g_DebugCommands)
    1544             warn("Invalid command: convert gate cost check failed for player "+player+": "+uneval(cmd));
    1545 
    1546         Engine.DestroyEntity(gate);
    1547         return;
    1548     }
    1549 
    1550     ReplaceBuildingWith(ent, gate);
    1551 }
    1552 
    1553 /**
    1554  * Unconditionally replace a building with another one
    1555  */
    1556 function ReplaceBuildingWith(ent, building)
    1557 {
    1558     // Move the building to the right place
    1559     var cmpPosition = Engine.QueryInterface(ent, IID_Position);
    1560     var cmpBuildingPosition = Engine.QueryInterface(building, IID_Position);
    1561     var pos = cmpPosition.GetPosition2D();
    1562     cmpBuildingPosition.JumpTo(pos.x, pos.y);
    1563     var rot = cmpPosition.GetRotation();
    1564     cmpBuildingPosition.SetYRotation(rot.y);
    1565     cmpBuildingPosition.SetXZRotation(rot.x, rot.z);
    1566 
    1567     // Copy ownership
    1568     var cmpOwnership = Engine.QueryInterface(ent, IID_Ownership);
    1569     var cmpBuildingOwnership = Engine.QueryInterface(building, IID_Ownership);
    1570     cmpBuildingOwnership.SetOwner(cmpOwnership.GetOwner());
    1571 
    1572     // Copy control groups
    1573     var cmpObstruction = Engine.QueryInterface(ent, IID_Obstruction);
    1574     var cmpBuildingObstruction = Engine.QueryInterface(building, IID_Obstruction);
    1575     cmpBuildingObstruction.SetControlGroup(cmpObstruction.GetControlGroup());
    1576     cmpBuildingObstruction.SetControlGroup2(cmpObstruction.GetControlGroup2());
    1577 
    1578     // Copy health level from the old entity to the new
    1579     var cmpHealth = Engine.QueryInterface(ent, IID_Health);
    1580     var cmpBuildingHealth = Engine.QueryInterface(building, IID_Health);
    1581     var healthFraction = Math.max(0, Math.min(1, cmpHealth.GetHitpoints() / cmpHealth.GetMaxHitpoints()));
    1582     var buildingHitpoints = Math.round(cmpBuildingHealth.GetMaxHitpoints() * healthFraction);
    1583     cmpBuildingHealth.SetHitpoints(buildingHitpoints);
    1584 
    1585     PlaySound("constructed", building);
    1586 
    1587     Engine.PostMessage(ent, MT_ConstructionFinished,
    1588         { "entity": ent, "newentity": building });
    1589     Engine.BroadcastMessage(MT_EntityRenamed, { entity: ent, newentity: building });
    1590 
    1591     Engine.DestroyEntity(ent);
    1592 }
    1593 
    15941572Engine.RegisterGlobal("GetFormationRequirements", GetFormationRequirements);
    15951573Engine.RegisterGlobal("CanMoveEntsIntoFormation", CanMoveEntsIntoFormation);
    15961574Engine.RegisterGlobal("GetDockAngle", GetDockAngle);
  • binaries/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    // Maintain current health level
     45    var cmpHealth = Engine.QueryInterface(oldEnt, IID_Health);
     46    var cmpNewHealth = Engine.QueryInterface(newEnt, IID_Health);
     47    if (cmpHealth && cmpNewHealth)
     48    {
     49        var healthLevel = Math.max(0, Math.min(1, cmpHealth.GetHitpoints() / cmpHealth.GetMaxHitpoints()));
     50        cmpNewHealth.SetHitpoints(Math.round(cmpNewHealth.GetMaxHitpoints() * healthLevel));
     51    }
     52
     53    var cmpUnitAI = Engine.QueryInterface(oldEnt, IID_UnitAI);
     54    var cmpNewUnitAI = Engine.QueryInterface(newEnt, IID_UnitAI);
     55    if (cmpUnitAI && cmpNewUnitAI)
     56    {
     57        var pos = cmpUnitAI.GetHeldPosition();
     58        if (pos)
     59            cmpNewUnitAI.SetHeldPosition(pos.x, pos.z);
     60        if (cmpUnitAI.GetStanceName())
     61            cmpNewUnitAI.SwitchToStance(cmpUnitAI.GetStanceName());
     62        cmpNewUnitAI.AddOrders(cmpUnitAI.GetOrders());
     63        cmpNewUnitAI.SetGuardOf(cmpUnitAI.IsGuardOf());
     64    }
     65
     66    // Maintain the list of guards
     67    var cmpGuard = Engine.QueryInterface(oldEnt, IID_Guard);
     68    var cmpNewGuard = Engine.QueryInterface(newEnt, IID_Guard);
     69    if (cmpGuard && cmpNewGuard)
     70        cmpNewGuard.SetEntities(cmpGuard.GetEntities());
     71
     72    TransferGarrisonedUnits(oldEnt, newEnt);
     73
     74    Engine.BroadcastMessage(MT_EntityRenamed, { entity: oldEnt, newentity: newEnt });
     75
     76    // Destroy current entity
     77    Engine.DestroyEntity(oldEnt);
     78
     79    return newEnt;
     80};
     81
     82var CanGarrisonedChangeTemplate = function(ent, template)
     83{
     84    var cmpPosition = Engine.QueryInterface(ent, IID_Position);
     85    var unitAI = Engine.QueryInterface(ent, IID_UnitAI);
     86    if (cmpPosition && !cmpPosition.IsInWorld() && unitAI && unitAI.IsGarrisoned())
     87    {
     88        // We're a garrisoned unit, assume impossibility as I've been unable to find a way to get the holder ID.
     89        // TODO: change this if that ever becomes possibles
     90        return false;
     91    }
     92    return true;
     93}
     94
     95var ObstructionsBlockingTemplateChange = function(ent, templateArg)
     96{
     97    var previewEntity = Engine.AddEntity("preview|"+templateArg);
     98
     99    if (previewEntity == INVALID_ENTITY)
     100        return true;
     101
     102    var cmpBuildRestrictions = Engine.QueryInterface(previewEntity, IID_BuildRestrictions);
     103    var cmpPosition = Engine.QueryInterface(ent, IID_Position);
     104    var cmpOwnership = Engine.QueryInterface(ent, IID_Ownership);
     105
     106    var cmpNewPosition = Engine.QueryInterface(previewEntity, IID_Position);
     107
     108    // Return false if no ownership as BuildRestrictions.CheckPlacement needs an owner and I have no idea if false or true is better
     109    // Plus there are no real entities without owners currently.
     110    if (!cmpBuildRestrictions || !cmpPosition || !cmpOwnership)
     111        return DeleteEntityAndReturn(previewEntity, cmpPosition, pos, angle, cmpNewPosition, false);
     112
     113    var pos = cmpPosition.GetPosition2D();
     114    var angle = cmpPosition.GetRotation();
     115    // move us away to prevent our own obstruction from blocking the upgrade.
     116    cmpPosition.MoveOutOfWorld();
     117
     118    cmpNewPosition.JumpTo(pos.x, pos.y);
     119    cmpNewPosition.SetYRotation(angle.y);
     120
     121    var cmpNewOwnership = Engine.QueryInterface(previewEntity, IID_Ownership);
     122    cmpNewOwnership.SetOwner(cmpOwnership.GetOwner());
     123
     124    var checkPlacement = cmpBuildRestrictions.CheckPlacement();
     125
     126    if (checkPlacement && !checkPlacement.success)
     127        return DeleteEntityAndReturn(previewEntity, cmpPosition, pos, angle, cmpNewPosition, true);
     128
     129    var cmpTemplateManager = Engine.QueryInterface(SYSTEM_ENTITY, IID_TemplateManager);
     130    var template = cmpTemplateManager.GetTemplate(cmpTemplateManager.GetCurrentTemplateName(ent));
     131    var newTemplate = cmpTemplateManager.GetTemplate(templateArg);
     132
     133    // Check if units are blocking our template change
     134    if (template.Obstruction && newTemplate.Obstruction)
     135    {
     136        // This only needs to be done if the new template is strictly bigger than the old one
     137        // "Obstructions" are annoying to test so just check.
     138        // This is kind of ugly, sorry about that.
     139        if (newTemplate.Obstruction.Obstructions
     140
     141            || (newTemplate.Obstruction.Static && template.Obstruction.Static
     142                && (newTemplate.Obstruction.Static["@width"] > template.Obstruction.Static["@width"]
     143                 || newTemplate.Obstruction.Static["@depth"] > template.Obstruction.Static["@depth"]))
     144            || (newTemplate.Obstruction.Static && template.Obstruction.Unit
     145                && (newTemplate.Obstruction.Static["@width"] > template.Obstruction.Unit["@radius"]
     146                 || newTemplate.Obstruction.Static["@depth"] > template.Obstruction.Unit["@radius"]))
     147
     148            || (newTemplate.Obstruction.Unit && template.Obstruction.Unit
     149                && newTemplate.Obstruction.Unit["@radius"] > template.Obstruction.Unit["@radius"])
     150            || (newTemplate.Obstruction.Unit && template.Obstruction.Static
     151                && (newTemplate.Obstruction.Unit["@radius"] > template.Obstruction.Static["@width"]
     152                 || newTemplate.Obstruction.Unit["@radius"] > template.Obstruction.Static["@depth"])))
     153        {
     154            var cmpNewObstruction = Engine.QueryInterface(previewEntity, IID_Obstruction); 
     155            if (cmpNewObstruction && cmpNewObstruction.GetBlockMovementFlag())
     156            {
     157                // Check for units
     158                var collisions = cmpNewObstruction.GetEntityCollisions(false, true);
     159                if (collisions.length !== 0)
     160                    return DeleteEntityAndReturn(previewEntity, cmpPosition, pos, angle, cmpNewPosition, true);
     161            }
     162        }
     163    }
     164
     165    return DeleteEntityAndReturn(previewEntity, cmpPosition, pos, angle, cmpNewPosition, false);
     166};
     167
     168var DeleteEntityAndReturn = function(ent, cmpPosition, position, angle, cmpNewPosition, ret)
     169{
     170    cmpNewPosition.MoveOutOfWorld();    // prevent preview from interfering in the world
     171    cmpPosition.JumpTo(position.x, position.y);
     172    cmpPosition.SetYRotation(angle.y);
     173
     174    Engine.DestroyEntity(ent);
     175    return ret;
     176};
     177
     178var TransferGarrisonedUnits = function(oldEnt, newEnt)
     179{
     180    // Transfer garrisoned units if possible, or unload them
     181    var cmpGarrison = Engine.QueryInterface(oldEnt, IID_GarrisonHolder);
     182    var cmpNewGarrison = Engine.QueryInterface(newEnt, IID_GarrisonHolder);
     183    if (!cmpNewGarrison || !cmpGarrison || cmpGarrison.GetEntities().length === 0 )
     184        return; // nothing to do as the code will by default unload all.
     185
     186    var garrisonedEntities = cmpGarrison.GetEntities().slice();
     187    for (var j = 0; j < garrisonedEntities.length; ++j)
     188    {
     189        var cmpUnitAI = Engine.QueryInterface(garrisonedEntities[j], IID_UnitAI);
     190        cmpGarrison.Eject(garrisonedEntities[j]);
     191        cmpUnitAI.Autogarrison(newEnt);
     192        cmpNewGarrison.Garrison(garrisonedEntities[j]);
     193    }
     194};
     195
     196Engine.RegisterGlobal("ChangeEntityTemplate", ChangeEntityTemplate);
     197Engine.RegisterGlobal("CanGarrisonedChangeTemplate", CanGarrisonedChangeTemplate);
     198Engine.RegisterGlobal("ObstructionsBlockingTemplateChange", ObstructionsBlockingTemplateChange);
  • binaries/data/mods/public/simulation/templates/template_structure_defense_wall_long.xml

    Property changes on: binaries/data/mods/public/simulation/helpers/Transform.js
    ___________________________________________________________________
    Added: svn:eol-style
    ## -0,0 +1 ##
    +native
    \ No newline at end of property
     
    5151  <Identity>
    5252    <Classes datatype="tokens">LongWall</Classes>
    5353    <Tooltip>Long wall segments can be converted to gates.</Tooltip>
    54     <GateConversionTooltip>Convert Stone Wall into City Gate</GateConversionTooltip>
    5554  </Identity>
     55  <Upgrade>
     56    <Gate>
     57      <Entity>structures/{civ}_wall_gate</Entity>
     58      <Tooltip>This will allow you to let units circulate through your fortifications.</Tooltip>
     59      <Cost>
     60        <stone>60</stone>
     61      </Cost>
     62      <Time>10000</Time>
     63    </Gate>
     64  </Upgrade>
    5665</Entity>
  • binaries/data/mods/public/simulation/templates/other/palisades_rocks_long.xml

     
    2121    <SelectionGroupName>other/wallset_palisade</SelectionGroupName>
    2222    <SpecificName>Palisade</SpecificName>
    2323    <GenericName>Wooden Wall</GenericName>
    24     <GateConversionTooltip>Convert Wooden Wall into Wooden Gate</GateConversionTooltip>
    2524    <Classes datatype="tokens">-StoneWall Palisade</Classes>
    2625    <History>A cheap, quick defensive structure constructed with sharpened tree trunks</History>
    2726    <Icon>gaia/special_palisade.png</Icon>
     
    4140  <WallPiece>
    4241    <Length>11.0</Length>
    4342  </WallPiece>
     43  <Upgrade>
     44    <Gate>
     45      <Entity>other/palisades_rocks_gate</Entity>
     46      <Cost>
     47        <stone>0</stone>
     48        <wood>20</wood>
     49      </Cost>
     50      <Time>5000</Time>
     51    </Gate>
     52  </Upgrade>
    4453</Entity>
  • binaries/data/mods/public/simulation/templates/structures/rome_siege_wall_long.xml

     
    5555    </Classes>
    5656    <Icon>structures/palisade.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>