Ticket #2706: UpgradeComponent.6.patch

File UpgradeComponent.6.patch, 45.9 KB (added by wraitii, 8 years ago)

Updated following elexis' comments.

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

     
    226226        };
    227227        ret.icon = template.Identity.Icon;
    228228        ret.tooltip =  template.Identity.Tooltip;
    229         ret.gateConversionTooltip =  template.Identity.GateConversionTooltip;
    230229        ret.requiredTechnology = template.Identity.RequiredTechnology;
    231230        ret.visibleIdentityClasses = GetVisibleIdentityClasses(template.Identity);
    232231    }
  • 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, "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, "Gate": 0, "Pack": 0, "Upgrade": 0};
    33
    44/**
    55 * Set the position of a panel object according to the index,
     
    279279        sum += g_unitPanelButtons["Pack"];
    280280    if (g_SelectionPanels["Gate"].used)
    281281        sum += g_unitPanelButtons["Gate"];
     282    if (g_SelectionPanels["Upgrade"].used)
     283        sum += g_unitPanelButtons["Upgrade"];
    282284    return sum;
    283285}
  • 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.js

     
    472472    },
    473473    "getItems": function(unitEntState, selection)
    474474    {
    475         // Allow long wall pieces to be converted to gates
    476         var longWallTypes = {};
    477         var walls = [];
    478475        var gates = [];
    479476        for (var ent of selection)
    480477        {
    481478            var state = GetEntityState(ent);
    482             if (hasClass(state, "LongWall") && !state.gate && !longWallTypes[state.template])
     479            if (state.gate && !gates.length)
    483480            {
    484                 var gateTemplate = getWallGateTemplate(state.id);
    485                 if (gateTemplate)
    486                 {
    487                     var tooltipString = GetTemplateDataWithoutLocalization(state.template).gateConversionTooltip;
    488                     if (!tooltipString)
    489                     {
    490                         warn(state.template + " is supposed to be convertable to a gate, but it's missing the GateConversionTooltip in the Identity template");
    491                         tooltipString = "";
    492                     }
    493                     walls.push({
    494                         "tooltip": translate(tooltipString),
    495                         "template": gateTemplate,
    496                         "callback": function (item) { transformWallToGate(item.template); }
    497                     });
    498                 }
    499 
    500                 // We only need one entity per type.
    501                 longWallTypes[state.template] = true;
    502             }
    503             else if (state.gate && !gates.length)
    504             {
    505481                gates.push({
    506482                    "gate": state.gate,
    507483                    "tooltip": translate("Lock Gate"),
     
    520496                for (var j = 0; j < gates.length; ++j)
    521497                    delete gates[j].gate.locked;
    522498        }
    523 
    524         // Place wall conversion options after gate lock/unlock icons.
    525         var items = gates.concat(walls);
    526         return items;
     499        return gates;
    527500    },
    528501    "setAction": function(data)
    529502    {
     
    531504    },
    532505    "setTooltip": function(data)
    533506    {
    534         var tooltip = data.item.tooltip;
    535         if (data.item.template)
    536         {
    537             data.template = GetTemplateData(data.item.template);
    538             data.wallCount = data.selection.reduce(function (count, ent) {
    539                     var state = GetEntityState(ent);
    540                     if (hasClass(state, "LongWall") && !state.gate)
    541                         count++;
    542                     return count;
    543                 }, 0);
    544 
    545             tooltip += "\n" + getEntityCostTooltip(data.template, data.wallCount);
    546 
    547 
    548             data.neededResources = Engine.GuiInterfaceCall("GetNeededResources", multiplyEntityCosts(data.template, data.wallCount));
    549             if (data.neededResources)
    550                 tooltip += getNeededResourcesTooltip(data.neededResources);
    551         }
    552         data.button.tooltip = tooltip;
     507        data.button.tooltip = data.item.tooltip;
    553508    },
    554509    "setGraphics": function(data)
    555510    {
    556         data.affordableMask.hidden = !data.neededResources;
    557511        var gateIcon;
    558512        if (data.item.gate)
    559513        {
    560             // If already a gate, show locking actions
     514            // Show locking actions
    561515            gateIcon = "icons/lock_" + GATE_ACTIONS[data.item.locked ? 0 : 1] + "ed.png";
    562516            if (data.item.gate.locked === undefined)
    563517                data.guiSelection.hidden = false;
     
    564518            else
    565519                data.guiSelection.hidden = data.item.gate.locked != data.item.locked;
    566520        }
    567         else
    568         {
    569             // otherwise show gate upgrade icon
    570             var template = GetTemplateData(data.item.template);
    571             if (!template)
    572                 return;
    573             gateIcon = data.template.icon ? "portraits/" + data.template.icon : "icons/gate_closed.png";
    574             data.guiSelection.hidden = true;
    575         }
    576 
    577521        data.icon.sprite = "stretched:session/" + gateIcon;
    578522    },
    579523    "setPosition": function(data)
     
    648592    },
    649593};
    650594
     595// UPGRADE
     596g_SelectionPanels.Upgrade = {
     597    "getMaxNumberOfItems": function()
     598    {
     599        return 24 - getNumberOfRightPanelButtons();
     600    },
     601    "getItems": function(unitEntState, selection)
     602    {
     603        // Interface becomes complicated with multiple units and this is meant per-entity, so prevent it if the selection has multiple units.
     604        // TODO: if the units are all the same, this should probably still be possible.
     605        if (selection.length > 1)
     606            return false;
     607
     608        if (!unitEntState.upgrade)
     609            return false;
     610
     611        var items = [];
     612
     613        for (let upgrade of unitEntState.upgrade.upgrades)
     614        {
     615            var item = {};
     616            item.entType = upgrade.entity;
     617            item.template = GetTemplateData(upgrade.entity);
     618            if (!item.template) // abort if no template
     619                return false;
     620
     621            item.icon = upgrade.icon;
     622            if (!item.icon)
     623                item.icon = "portraits/" + item.template.icon;
     624
     625            if (upgrade.requiredTechnology !== null)
     626            {
     627                item.requiredTechnology = upgrade.requiredTechnology;
     628                if (!item.requiredTechnology && item.template.requiredTechnology)
     629                    item.requiredTechnology = item.template.requiredTechnology
     630            }
     631            item.cost = upgrade.cost;
     632            item.time = upgrade.time;
     633
     634            if (unitEntState.upgrade.progress === undefined)
     635            {
     636                item.upgrading = UPGRADING_NOT_STARTED;
     637                item.tooltip = sprintf(translate("Upgrade into a %(name)s.%(tooltip)s"), { name: item.template.name.generic, tooltip: (upgrade.tooltip? "\n" + upgrade.tooltip : "") });
     638                item.callback = function(data) { upgradeEntity(data.entType); };
     639            }
     640            else if (unitEntState.upgrade.template !== upgrade.entity)
     641            {
     642                item.upgrading = UPGRADING_CHOSEN_OTHER;
     643                item.tooltip = translate("Cannot upgrade when the entity is already upgrading.");
     644                item.callback = function(data) { };
     645            }
     646            else
     647            {
     648                item.upgrading = unitEntState.upgrade.progress;
     649                item.tooltip = translate("Cancel Upgrading");
     650                item.callback = function(data) { cancelUpgradeEntity(); };
     651            }
     652            items.push(item);
     653        }
     654        return items;
     655    },
     656    "addData" : function(data)
     657    {
     658        data.item.technologyEnabled = true;
     659        if (data.item.requiredTechnology)
     660            data.item.technologyEnabled = Engine.GuiInterfaceCall("IsTechnologyResearched", data.item.requiredTechnology);
     661        if (data.item.cost)
     662        {
     663            var totalCost = {};
     664            for (let i in data.item.cost)
     665                totalCost[i] = data.item.cost[i];
     666            data.item.neededResources = Engine.GuiInterfaceCall("GetNeededResources", totalCost);
     667        }
     668        data.item.limits = getEntityLimitAndCount(data.playerState, data.item.entType);
     669        return true;
     670    },
     671    "setAction": function(data)
     672    {
     673        data.button.onPress = function() { data.item.callback(data.item); };
     674    },
     675    "setTooltip": function(data)
     676    {
     677        var tooltip = data.item.tooltip;
     678
     679        if (data.item.upgrading !== UPGRADING_NOT_STARTED)
     680        {
     681            data.button.tooltip = tooltip;
     682            return;
     683        }
     684
     685        if (data.item.cost)
     686            tooltip += "\n" + getEntityCostTooltip(data.item);
     687
     688        tooltip += formatLimitString(data.item.limits.entLimit, data.item.limits.entCount, data.item.limits.entLimitChangers);
     689        if (!data.item.technologyEnabled)
     690        {
     691            var techName = getEntityNames(GetTechnologyData(data.item.requiredTechnology));
     692            tooltip += "\n" + sprintf(translate("Requires %(technology)s"), { technology: techName });
     693        }
     694        if (data.item.neededResources)
     695            tooltip += getNeededResourcesTooltip(data.item.neededResources);
     696
     697        data.button.tooltip = tooltip;
     698    },
     699    "setGraphics": function(data)
     700    {
     701        var grayscale = "";
     702        if (data.item.upgrading == UPGRADING_CHOSEN_OTHER || !data.item.technologyEnabled || (data.item.limits.canBeAddedCount == 0 && data.item.upgrading == UPGRADING_NOT_STARTED))
     703        {
     704            data.button.enabled = false;
     705            grayscale = "grayscale:";
     706            data.affordableMask.hidden = false;
     707            data.affordableMask.sprite = "colour: 0 0 0 127";
     708        }
     709        else if (data.item.upgrading == UPGRADING_NOT_STARTED && data.item.neededResources)
     710        {
     711            data.button.enabled = false;
     712            data.affordableMask.hidden = false;
     713            data.affordableMask.sprite = resourcesToAlphaMask(data.item.neededResources);
     714        }
     715
     716        data.icon.sprite = "stretched:" + grayscale + "session/" + data.item.icon;
     717
     718        var guiObject = Engine.GetGUIObjectByName("unitUpgradeProgressSlider["+data.i+"]");
     719        var size = guiObject.size;
     720        if (data.item.upgrading < 0)
     721            size.top = size.right;
     722        else
     723            size.top = size.left + Math.round(Math.max(0,data.item.upgrading) * (size.right - size.left));
     724        guiObject.size = size;
     725    },
     726    "setPosition": function(data)
     727    {
     728        var index = data.i + getNumberOfRightPanelButtons();
     729        setPanelObjectPosition(data.button, index, data.rowLength);
     730    },
     731};
     732
    651733// QUEUE
    652734g_SelectionPanels.Queue = {
    653735    "getMaxNumberOfItems": function()
     
    11391221    // RIGHT PANE
    11401222    "Gate", // must always be shown on gates
    11411223    "Pack", // must always be shown on packable entities
     1224    "Upgrade", // must always be shown on upgradable entities
    11421225    "Training",
    11431226    "Construction",
    11441227    "Research", // normal together with training
  • data/mods/public/gui/session/input.js

     
    16431643    });
    16441644}
    16451645
    1646 // Transform a wall to a gate
    1647 function transformWallToGate(template)
     1646// Upgrade an entity to another
     1647function upgradeEntity(Template)
    16481648{
    16491649    var selection = g_Selection.toList();
    16501650    Engine.PostNetworkCommand({
    1651         "type": "wall-to-gate",
    1652         "entities": selection.filter( function(e) { return getWallGateTemplate(e) == template; } ),
    1653         "template": template,
     1651        "type": "upgrade",
     1652        "entities": selection,
     1653        "template": Template,
     1654        "queued": false
    16541655    });
    16551656}
    16561657
    1657 // Gets the gate form (if any) of a given long wall piece
    1658 function getWallGateTemplate(entity)
     1658// Cancel upgrading entities
     1659function cancelUpgradeEntity()
    16591660{
    1660     // TODO: find the gate template name in a better way
    1661     var entState = GetEntityState(entity);
    1662     var index;
    1663 
    1664     if (entState && !entState.foundation && hasClass(entState, "LongWall") && (index = entState.template.indexOf("long")) >= 0)
    1665         return entState.template.substr(0, index) + "gate";
    1666     return undefined;
     1661    var selection = g_Selection.toList();
     1662    Engine.PostNetworkCommand({
     1663        "type": "cancel-upgrade",
     1664        "entities": selection,
     1665        "queued": false
     1666    });
    16671667}
    16681668
    16691669// Set the camera to follow the given unit
  • 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/selection_panels_right/gate_panel.xml

    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
     
    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="color: 255 0 0 127"/>
    1110        </object>
    1211    </repeat>
    1312    </object>
  • 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/Pack.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
     
    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/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/GuiInterface.js

     
    232232        "guard": null,
    233233        "mirage": null,
    234234        "pack": null,
     235        "upgrade" : null,
    235236        "player": -1,
    236237        "position": null,
    237238        "production": null,
     
    295296        };
    296297    }
    297298
     299    var cmpUpgrade = Engine.QueryInterface(ent, IID_Upgrade);
     300    if (cmpUpgrade)
     301    {
     302        ret.upgrade = {
     303            "upgrades" : cmpUpgrade.GetUpgrades(),
     304            "progress": cmpUpgrade.GetProgress(),
     305            "template": cmpUpgrade.GetUpgradingTo()
     306        };
     307    }
     308
    298309    var cmpProductionQueue = Engine.QueryInterface(ent, IID_ProductionQueue);
    299310    if (cmpProductionQueue)
    300311    {
  • data/mods/public/simulation/components/interfaces/Upgrade.js

     
     1Engine.RegisterInterface("Upgrade");
  • data/mods/public/simulation/helpers/Transform.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
     
     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/helpers/Commands.js

    Property changes on: data/mods/public/simulation/helpers/Transform.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 each (var ent in data.entities)
    584         {
    585             TryTransformWallToGate(ent, data.cmpPlayer, cmd.template);
    586         }
    587     },
    588 
    589581    "lock-gate": function(player, cmd, data)
    590582    {
    591583        for each (var ent in data.entities)
     
    670662        }
    671663    },
    672664
     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
    673724    "attack-request": function(player, cmd, data)
    674725    {
    675726        // Send a chat message to human players
     
    15631614    return entities.filter(function(ent) { return CanControlUnitOrIsAlly(ent, player, controlAll);} );
    15641615}
    15651616
    1566 /**
    1567  * Try to transform a wall to a gate
    1568  */
    1569 function TryTransformWallToGate(ent, cmpPlayer, template)
    1570 {
    1571     var cmpIdentity = Engine.QueryInterface(ent, IID_Identity);
    1572     if (!cmpIdentity)
    1573         return;
    1574 
    1575     // Check if this is a valid long wall segment
    1576     if (!cmpIdentity.HasClass("LongWall"))
    1577     {
    1578         if (g_DebugCommands)
    1579             warn("Invalid command: invalid wall conversion to gate for player "+player+": "+uneval(cmd));
    1580         return;
    1581     }
    1582 
    1583     var civ = cmpIdentity.GetCiv();
    1584     var gate = Engine.AddEntity(template);
    1585 
    1586     var cmpCost = Engine.QueryInterface(gate, IID_Cost);
    1587     if (!cmpPlayer.TrySubtractResources(cmpCost.GetResourceCosts()))
    1588     {
    1589         if (g_DebugCommands)
    1590             warn("Invalid command: convert gate cost check failed for player "+player+": "+uneval(cmd));
    1591 
    1592         Engine.DestroyEntity(gate);
    1593         return;
    1594     }
    1595 
    1596     ReplaceBuildingWith(ent, gate);
    1597 }
    1598 
    1599 /**
    1600  * Unconditionally replace a building with another one
    1601  */
    1602 function ReplaceBuildingWith(ent, building)
    1603 {
    1604     // Move the building to the right place
    1605     var cmpPosition = Engine.QueryInterface(ent, IID_Position);
    1606     var cmpBuildingPosition = Engine.QueryInterface(building, IID_Position);
    1607     var pos = cmpPosition.GetPosition2D();
    1608     cmpBuildingPosition.JumpTo(pos.x, pos.y);
    1609     var rot = cmpPosition.GetRotation();
    1610     cmpBuildingPosition.SetYRotation(rot.y);
    1611     cmpBuildingPosition.SetXZRotation(rot.x, rot.z);
    1612 
    1613     // Copy ownership
    1614     var cmpOwnership = Engine.QueryInterface(ent, IID_Ownership);
    1615     var cmpBuildingOwnership = Engine.QueryInterface(building, IID_Ownership);
    1616     cmpBuildingOwnership.SetOwner(cmpOwnership.GetOwner());
    1617 
    1618     // Copy control groups
    1619     var cmpObstruction = Engine.QueryInterface(ent, IID_Obstruction);
    1620     var cmpBuildingObstruction = Engine.QueryInterface(building, IID_Obstruction);
    1621     cmpBuildingObstruction.SetControlGroup(cmpObstruction.GetControlGroup());
    1622     cmpBuildingObstruction.SetControlGroup2(cmpObstruction.GetControlGroup2());
    1623 
    1624     // Copy health level from the old entity to the new
    1625     var cmpHealth = Engine.QueryInterface(ent, IID_Health);
    1626     var cmpBuildingHealth = Engine.QueryInterface(building, IID_Health);
    1627     var healthFraction = Math.max(0, Math.min(1, cmpHealth.GetHitpoints() / cmpHealth.GetMaxHitpoints()));
    1628     var buildingHitpoints = Math.round(cmpBuildingHealth.GetMaxHitpoints() * healthFraction);
    1629     cmpBuildingHealth.SetHitpoints(buildingHitpoints);
    1630 
    1631     PlaySound("constructed", building);
    1632 
    1633     Engine.PostMessage(ent, MT_ConstructionFinished,
    1634         { "entity": ent, "newentity": building });
    1635     Engine.BroadcastMessage(MT_EntityRenamed, { entity: ent, newentity: building });
    1636 
    1637     Engine.DestroyEntity(ent);
    1638 }
    1639 
    16401617Engine.RegisterGlobal("GetFormationRequirements", GetFormationRequirements);
    16411618Engine.RegisterGlobal("CanMoveEntsIntoFormation", CanMoveEntsIntoFormation);
    16421619Engine.RegisterGlobal("GetDockAngle", GetDockAngle);
  • data/mods/public/simulation/templates/template_structure_defense_wall_long.xml

     
    5656  <Identity>
    5757    <Classes datatype="tokens">LongWall</Classes>
    5858    <Tooltip>Long wall segments can be converted to gates.</Tooltip>
    59     <GateConversionTooltip>Convert Stone Wall into City Gate</GateConversionTooltip>
    6059  </Identity>
     60  <Upgrade>
     61    <Gate>
     62      <Entity>structures/{civ}_wall_gate</Entity>
     63      <Tooltip>This will allow you to let units circulate through your fortifications.</Tooltip>
     64      <Cost>
     65        <stone>60</stone>
     66      </Cost>
     67      <Time>10000</Time>
     68    </Gate>
     69  </Upgrade>
    6170</Entity>
  • data/mods/public/simulation/templates/other/palisades_rocks_long.xml

     
    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    <History>A cheap, quick defensive structure constructed with sharpened tree trunks</History>
    3231    <Icon>gaia/special_palisade.png</Icon>
     
    4645  <WallPiece>
    4746    <Length>11.0</Length>
    4847  </WallPiece>
     48  <Upgrade>
     49    <Gate>
     50      <Entity>other/palisades_rocks_gate</Entity>
     51      <Cost>
     52        <stone>0</stone>
     53        <wood>20</wood>
     54      </Cost>
     55      <Time>5000</Time>
     56    </Gate>
     57  </Upgrade>
    4958</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>