Ticket #2706: UpgradeComponentRC-fixed5.patch
File UpgradeComponentRC-fixed5.patch, 46.0 KB (added by , 8 years ago) |
---|
-
binaries/data/mods/public/globalscripts/Templates.js
233 233 }; 234 234 ret.icon = template.Identity.Icon; 235 235 ret.tooltip = template.Identity.Tooltip; 236 ret.gateConversionTooltip = template.Identity.GateConversionTooltip;237 236 ret.requiredTechnology = template.Identity.RequiredTechnology; 238 237 ret.visibleIdentityClasses = GetVisibleIdentityClasses(template.Identity); 239 238 } -
binaries/data/mods/public/gui/common/tooltips.js
288 288 trainNum = 1; 289 289 290 290 let totalCosts = multiplyEntityCosts(template, trainNum); 291 totalCosts.time = Math.ceil(template.cost.time * (entity ? Engine.GuiInterfaceCall("GetBatchTime", { "entity": entity, "batchSize": trainNum }) : 1)); 291 if (template.cost.time) 292 totalCosts.time = Math.ceil(template.cost.time * (entity ? Engine.GuiInterfaceCall("GetBatchTime", { "entity": entity, "batchSize": trainNum }) : 1)); 292 293 293 294 let costs = []; 294 295 -
binaries/data/mods/public/gui/session/input.js
1632 1632 }); 1633 1633 } 1634 1634 1635 // Transform a wall to a gate1636 function transformWallToGate(template)1635 // Upgrade an entity to another 1636 function upgradeEntity(Template) 1637 1637 { 1638 var selection = g_Selection.toList();1639 1638 Engine.PostNetworkCommand({ 1640 "type": "wall-to-gate", 1641 "entities": selection.filter(e => getWallGateTemplate(e) == template), 1642 "template": template, 1639 "type": "upgrade", 1640 "entities": g_Selection.toList(), 1641 "template": Template, 1642 "queued": false 1643 1643 }); 1644 1644 } 1645 1645 1646 // Gets the gate form (if any) of a given long wall piece1647 function getWallGateTemplate(entity)1646 // Cancel upgrading entities 1647 function cancelUpgradeEntity() 1648 1648 { 1649 // TODO: find the gate template name in a better way 1650 var entState = GetEntityState(entity); 1651 var index; 1652 1653 if (entState && !entState.foundation && hasClass(entState, "LongWall") && (index = entState.template.indexOf("long")) >= 0) 1654 return entState.template.substr(0, index) + "gate"; 1655 return undefined; 1649 Engine.PostNetworkCommand({ 1650 "type": "cancel-upgrade", 1651 "entities": g_Selection.toList(), 1652 "queued": false 1653 }); 1656 1654 } 1657 1655 1658 1656 // Set the camera to follow the given unit -
binaries/data/mods/public/gui/session/selection_panels.js
473 473 }, 474 474 "getItems": function(unitEntState, selection) 475 475 { 476 // Allow long wall pieces to be converted to gates477 let longWallTypes = {};478 let walls = [];479 476 let gates = []; 480 477 for (let ent of selection) 481 478 { 482 479 let state = GetEntityState(ent); 483 if ( hasClass(state, "LongWall") && !state.gate && !longWallTypes[state.template])480 if (state.gate && !gates.length) 484 481 { 485 let gateTemplate = getWallGateTemplate(state.id);486 if (gateTemplate)487 {488 let tooltipString = GetTemplateDataWithoutLocalization(state.template).gateConversionTooltip;489 if (!tooltipString)490 {491 warn(state.template + " is supposed to be convertable to a gate, but it's missing the GateConversionTooltip in the Identity template");492 tooltipString = "";493 }494 walls.push({495 "tooltip": translate(tooltipString),496 "template": gateTemplate,497 "callback": function (item) { transformWallToGate(item.template); }498 });499 }500 501 // We only need one entity per type.502 longWallTypes[state.template] = true;503 }504 else if (state.gate && !gates.length)505 {506 482 gates.push({ 507 483 "gate": state.gate, 508 484 "tooltip": translate("Lock Gate"), … … 522 498 delete gates[j].gate.locked; 523 499 } 524 500 525 // Place wall conversion options after gate lock/unlock icons. 526 return gates.concat(walls); 501 return gates; 527 502 }, 528 503 "setupButton": function(data) 529 504 { … … 530 505 data.button.onPress = function() {data.item.callback(data.item); }; 531 506 532 507 let tooltip = data.item.tooltip; 533 if (data.item.template) 534 { 535 data.template = GetTemplateData(data.item.template); 536 data.wallCount = data.selection.reduce(function (count, ent) { 537 let state = GetEntityState(ent); 538 if (hasClass(state, "LongWall") && !state.gate) 539 ++count; 540 return count; 541 }, 0); 508 data.button.tooltip = data.item.tooltip; 542 509 543 tooltip += "\n" + getEntityCostTooltip(data.template, data.wallCount);544 545 data.neededResources = Engine.GuiInterfaceCall("GetNeededResources", {546 "cost": multiplyEntityCosts(data.template, data.wallCount)547 });548 549 if (data.neededResources)550 tooltip += getNeededResourcesTooltip(data.neededResources);551 }552 data.button.tooltip = tooltip;553 554 510 data.button.enabled = controlsPlayer(data.unitEntState.player); 555 511 let gateIcon; 556 512 if (data.item.gate) 557 513 { 558 // If already a gate, show locking actions514 // Show locking actions 559 515 gateIcon = "icons/lock_" + GATE_ACTIONS[data.item.locked ? 0 : 1] + "ed.png"; 560 516 if (data.item.gate.locked === undefined) 561 517 data.guiSelection.hidden = false; … … 562 518 else 563 519 data.guiSelection.hidden = data.item.gate.locked != data.item.locked; 564 520 } 565 else566 {567 // Otherwise show gate upgrade icon568 let template = GetTemplateData(data.item.template);569 if (!template)570 return false;571 gateIcon = data.template.icon ? "portraits/" + data.template.icon : "icons/gate_closed.png";572 data.guiSelection.hidden = true;573 }574 521 575 522 data.icon.sprite = (data.neededResources ? resourcesToAlphaMask(data.neededResources) + ":" : "") + "stretched:session/" + gateIcon; 576 523 … … 611 558 } 612 559 let items = []; 613 560 if (checks.packButton) 614 items.push({ "packing": false, "packed": false, "tooltip": translate("Pack"), "callback": function() { packUnit(true); } }); 561 items.push({ 562 "packing": false, 563 "packed": false, 564 "tooltip": translate("Pack"), 565 "callback": function() { packUnit(true); } 566 }); 615 567 if (checks.unpackButton) 616 items.push({ "packing": false, "packed": true, "tooltip": translate("Unpack"), "callback": function() { packUnit(false); } }); 568 items.push({ 569 "packing": false, 570 "packed": true, 571 "tooltip": translate("Unpack"), 572 "callback": function() { packUnit(false); } 573 }); 617 574 if (checks.packCancelButton) 618 items.push({ "packing": true, "packed": false, "tooltip": translate("Cancel Packing"), "callback": function() { cancelPackUnit(true); } }); 575 items.push({ 576 "packing": true, 577 "packed": false, 578 "tooltip": translate("Cancel Packing"), 579 "callback": function() { cancelPackUnit(true); } 580 }); 619 581 if (checks.unpackCancelButton) 620 items.push({ "packing": true, "packed": true, "tooltip": translate("Cancel Unpacking"), "callback": function() { cancelPackUnit(false); } }); 582 items.push({ 583 "packing": true, 584 "packed": true, 585 "tooltip": translate("Cancel Unpacking"), 586 "callback": function() { cancelPackUnit(false); } 587 }); 621 588 return items; 622 589 }, 623 590 "setupButton": function(data) … … 1054 1021 } 1055 1022 }; 1056 1023 1024 g_SelectionPanels.Upgrade = { 1025 "getMaxNumberOfItems": function() 1026 { 1027 return 24 - getNumberOfRightPanelButtons(); 1028 }, 1029 "getItems": function(unitEntState, selection) 1030 { 1031 // Interface becomes complicated with multiple units and this is meant per-entity, so prevent it if the selection has multiple units. 1032 // TODO: if the units are all the same, this should probably still be possible. 1033 if (selection.length > 1) 1034 return false; 1035 1036 if (!unitEntState.upgrade) 1037 return false; 1038 1039 var items = []; 1040 1041 for (let upgrade of unitEntState.upgrade.upgrades) 1042 { 1043 var item = { 1044 "entity": upgrade.entity, 1045 "cost": upgrade.cost, 1046 "time": upgrade.time, 1047 "icon": upgrade.icon, 1048 "tooltip": upgrade.tooltip, 1049 "requiredTechnology": upgrade.requiredTechnology, 1050 }; 1051 items.push(item); 1052 } 1053 return items; 1054 }, 1055 "setupButton" : function(data) 1056 { 1057 let template = GetTemplateData(data.item.entity); 1058 if (!template) 1059 return false; 1060 1061 let technologyEnabled = true; 1062 1063 if (data.item.requiredTechnology) 1064 technologyEnabled = Engine.GuiInterfaceCall("IsTechnologyResearched", { 1065 "tech": requiredTechnology, 1066 "player": data.unitEntState.player 1067 }); 1068 1069 let neededResources; 1070 if (data.item.cost) 1071 neededResources = Engine.GuiInterfaceCall("GetNeededResources", { 1072 "cost": data.item.cost, 1073 "player": data.unitEntState.player 1074 }); 1075 1076 let limits = getEntityLimitAndCount(data.playerState, data.item.entity); 1077 1078 let progress = data.unitEntState.upgrade.progress || 0; 1079 let isUpgrading = data.unitEntState.upgrade.template == data.item.entity; 1080 1081 let tooltip; 1082 if (!progress) 1083 { 1084 tooltip = sprintf(translate("Upgrade into a %(name)s.%(tooltip)s"), { 1085 "name": template.name.generic, 1086 "tooltip": data.item.tooltip ? "\n" + data.item.tooltip : "" 1087 }); 1088 if (data.item.cost) 1089 tooltip += "\n" + getEntityCostTooltip(data.item); 1090 1091 tooltip += formatLimitString(limits.entLimit, limits.entCount, limits.entLimitChangers); 1092 if (!technologyEnabled) 1093 tooltip += "\n" + sprintf(translate("Requires %(technology)s"), { 1094 "technology": getEntityNames(GetTechnologyData(data.item.requiredTechnology)) 1095 }); 1096 if (neededResources) 1097 tooltip += getNeededResourcesTooltip(neededResources); 1098 1099 data.button.onPress = function() { upgradeEntity(data.item.entity); }; 1100 } 1101 else if (isUpgrading) 1102 { 1103 tooltip = translate("Cancel Upgrading"); 1104 data.button.onPress = function() { cancelUpgradeEntity(); }; 1105 } 1106 else 1107 { 1108 tooltip = translate("Cannot upgrade when the entity is already upgrading."); 1109 data.button.onPress = function() {}; 1110 } 1111 data.button.tooltip = tooltip; 1112 1113 let modifier = ""; 1114 if (!isUpgrading) 1115 { 1116 if (progress || !technologyEnabled || limits.canBeAddedCount == 0) 1117 { 1118 data.button.enabled = false; 1119 modifier = "color:0 0 0 127:grayscale:"; 1120 } 1121 else if (neededResources) 1122 { 1123 data.button.enabled = false; 1124 modifier = resourcesToAlphaMask(neededResources) + ":"; 1125 } 1126 } 1127 1128 data.icon.sprite = modifier + "stretched:session/" + 1129 (data.item.icon || "portraits/" + template.icon); 1130 1131 let progressOverlay = Engine.GetGUIObjectByName("unitUpgradeProgressSlider[" + data.i + "]"); 1132 if (isUpgrading) 1133 { 1134 let size = progressOverlay.size; 1135 size.top = size.left + Math.round(progress * (size.right - size.left)); 1136 progressOverlay.size = size; 1137 } 1138 progressOverlay.hidden = !isUpgrading; 1139 1140 let index = data.i + getNumberOfRightPanelButtons(); 1141 setPanelObjectPosition(data.button, index, data.rowLength); 1142 return true; 1143 }, 1144 }; 1145 1057 1146 /** 1058 1147 * If two panels need the same space, so they collide, 1059 1148 * the one appearing first in the order is rendered. … … 1071 1160 // RIGHT PANE 1072 1161 "Gate", // Must always be shown on gates 1073 1162 "Pack", // Must always be shown on packable entities 1163 "Upgrade", // Must always be shown on upgradable entities 1074 1164 "Training", 1075 1165 "Construction", 1076 1166 "Research", // Normal together with training -
binaries/data/mods/public/gui/session/selection_panels_helpers.js
8 8 // Gate constants 9 9 const GATE_ACTIONS = ["lock", "unlock"]; 10 10 11 // upgrade constants 12 const UPGRADING_NOT_STARTED = -2; 13 const UPGRADING_CHOSEN_OTHER = -1; 14 11 15 // ============================================== 12 16 // BARTER HELPERS 13 17 // Resources to sell on barter panel -
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> 13 </repeat> 14 </object> 15 </object> -
binaries/data/mods/public/gui/session/unit_commands.js
Property changes on: binaries/data/mods/public/gui/session/selection_panels_right/upgrade_panel.xml ___________________________________________________________________ Added: svn:mime-type ## -0,0 +1 ## +text/xml \ No newline at end of property
1 1 // 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}; 2 var g_unitPanelButtons = { 3 "Selection": 0, 4 "Queue": 0, 5 "Formation": 0, 6 "Garrison": 0, 7 "Training": 0, 8 "Research": 0, 9 "Alert": 0, 10 "Barter": 0, 11 "Construction": 0, 12 "Command": 0, 13 "AllyCommand": 0, 14 "Stance": 0, 15 "Gate":0, 16 "Pack": 0, 17 "Upgrade": 0 18 }; 3 19 4 20 /** 5 21 * Set the position of a panel object according to the index, … … 225 241 { 226 242 var sum = 0; 227 243 228 for (let prop of ["Construction", "Training", "Pack", "Gate" ])244 for (let prop of ["Construction", "Training", "Pack", "Gate", "Upgrade"]) 229 245 if (g_SelectionPanels[prop].used) 230 246 sum += g_unitPanelButtons[prop]; 231 247 -
binaries/data/mods/public/simulation/components/GuiInterface.js
237 237 "market": null, 238 238 "mirage": null, 239 239 "pack": null, 240 "upgrade" : null, 240 241 "player": -1, 241 242 "position": null, 242 243 "production": null, … … 303 304 "progress": cmpPack.GetProgress(), 304 305 }; 305 306 307 var cmpUpgrade = Engine.QueryInterface(ent, IID_Upgrade); 308 if (cmpUpgrade) 309 ret.upgrade = { 310 "upgrades" : cmpUpgrade.GetUpgrades(), 311 "progress": cmpUpgrade.GetProgress(), 312 "template": cmpUpgrade.GetUpgradingTo() 313 }; 314 306 315 let cmpProductionQueue = Engine.QueryInterface(ent, IID_ProductionQueue); 307 316 if (cmpProductionQueue) 308 317 ret.production = { -
binaries/data/mods/public/simulation/components/Identity.js
37 37 "<optional>" + 38 38 "<element name='Tooltip'>" + 39 39 "<text/>" + 40 "</element>" + 40 "</element>" + 41 "</optional>" + 42 "<optional>" + 43 "<element name='Rollover'>" + 44 "<text/>" + 45 "</element>" + 41 46 "</optional>" + 42 47 "<optional>" + 43 "<element name='GateConversionTooltip'>" +44 "<text/>" +45 "</element>" +46 "</optional>" +47 "<optional>" +48 "<element name='Rollover'>" +49 "<text/>" +50 "</element>" +51 "</optional>" +52 "<optional>" +53 48 "<element name='History'>" + 54 49 "<text/>" + 55 50 "</element>" + -
binaries/data/mods/public/simulation/components/interfaces/Upgrade.js
1 Engine.RegisterInterface("Upgrade"); -
binaries/data/mods/public/simulation/components/Pack.js
116 116 117 117 Pack.prototype.PackProgress = function(data, lateness) 118 118 { 119 if (this.elapsedTime >=this.GetPackTime())119 if (this.elapsedTime < this.GetPackTime()) 120 120 { 121 this.SetElapsedTime(this.GetElapsedTime() + PACKING_INTERVAL + lateness); 122 return; 123 } 124 121 125 this.CancelTimer(); 122 126 123 127 this.packed = !this.packed; 124 this.packing = false;125 128 Engine.PostMessage(this.entity, MT_PackFinished, { packed: this.packed }); 126 129 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); 135 131 136 var cmpPosition = Engine.QueryInterface(this.entity, IID_Position); 137 var cmpNewPosition = Engine.QueryInterface(newEntity, IID_Position); 138 if (cmpPosition.IsInWorld()) 139 { 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 132 if (newEntity) 133 { 190 134 var sound = this.packed ? "packed" : "unpacked"; 191 135 PlaySound(sound, newEntity); 192 193 // Destroy current entity194 Engine.DestroyEntity(this.entity);195 136 } 196 else197 {198 this.SetElapsedTime(this.GetElapsedTime() + PACKING_INTERVAL + lateness);199 }200 137 }; 201 138 202 139 Engine.RegisterComponentType(IID_Pack, "Pack", Pack); -
binaries/data/mods/public/simulation/components/Upgrade.js
1 function Upgrade() {} 2 3 const UPGRADING_PROGRESS_INTERVAL = 250; 4 5 Upgrade.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 (nb:GUI only)'><empty/></element>" + 50 "</optional>" + 51 "</interleave>" + 52 "</element>" + 53 "</oneOrMore>"; 54 55 Upgrade.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) 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. 77 Upgrade.prototype.OnOwnershipChanged = function(msg) 78 { 79 this.CancelUpgrade(); 80 81 if (msg.to !== -1) 82 this.owner = msg.to; 83 }; 84 85 Upgrade.prototype.ChangeUpgradedEntityCount = function(amount) 86 { 87 if (!this.IsUpgrading()) 88 return; 89 90 var cmpTempMan = Engine.QueryInterface(SYSTEM_ENTITY, IID_TemplateManager); 91 var template = cmpTempMan.GetTemplate(this.upgrading); 92 93 var category; 94 if (template.TrainingRestrictions) 95 category = template.TrainingRestrictions.Category; 96 else if (template.BuildRestrictions) 97 category = template.BuildRestrictions.Category; 98 99 if (!category) 100 return; 101 102 var cmpEntityLimits = QueryPlayerIDInterface(this.owner, IID_EntityLimits); 103 cmpEntityLimits.ChangeCount(category, amount); 104 }; 105 106 Upgrade.prototype.CanUpgradeTo = function(template) 107 { 108 return this.upgradeTemplates[template] !== undefined; 109 }; 110 111 Upgrade.prototype.GetUpgrades = function() 112 { 113 var ret = []; 114 115 var cmpIdentity = Engine.QueryInterface(this.entity, IID_Identity); 116 117 for (let option in this.template) 118 { 119 let choice = this.template[option]; 120 let entType = choice.Entity; 121 if (cmpIdentity) 122 entType = entType.replace(/\{civ\}/g, cmpIdentity.GetCiv()); 123 124 let hasCosts; 125 let cost = {}; 126 if (choice.Cost) 127 { 128 hasCosts = true; 129 for (let type in choice.Cost) 130 cost[type] = ApplyValueModificationsToTemplate("Upgrade/Cost/"+type, +choice.Cost[type], this.owner, entType); 131 } 132 if (choice.Time) 133 { 134 hasCosts = true; 135 cost.time = ApplyValueModificationsToTemplate("Upgrade/Time", +choice.Time/1000.0, this.owner, entType); 136 } 137 ret.push({ 138 "entity": entType, 139 "icon": choice.Icon || undefined, 140 "cost": hasCosts, 141 "tooltip": choice.Tooltip || undefined, 142 "requiredTechnology": this.GetRequiredTechnology(option), 143 }); 144 } 145 146 return ret; 147 }; 148 149 Upgrade.prototype.CancelTimer = function() 150 { 151 if (!this.timer) 152 return; 153 154 var cmpTimer = Engine.QueryInterface(SYSTEM_ENTITY, IID_Timer); 155 cmpTimer.CancelTimer(this.timer); 156 this.timer = undefined; 157 }; 158 159 Upgrade.prototype.IsUpgrading = function() 160 { 161 return !!this.upgrading; 162 }; 163 164 Upgrade.prototype.GetUpgradingTo = function() 165 { 166 return this.upgrading; 167 }; 168 169 Upgrade.prototype.WillCheckPlacementRestrictions = function(template) 170 { 171 if (!this.upgradeTemplates[template]) 172 return undefined; 173 174 // is undefined by default so use X in Y 175 return "CheckPlacementRestrictions" in this.template[this.upgradeTemplates[template]]; 176 }; 177 178 Upgrade.prototype.GetRequiredTechnology = function(templateArg) 179 { 180 let choice = this.upgradeTemplates[templateArg] || templateArg 181 182 if (this.template[choice].RequiredTechnology) 183 return this.template[choice].RequiredTechnology; 184 185 if (!("RequiredTechnology" in this.template[choice])) 186 return undefined; 187 188 let cmpTemplateManager = Engine.QueryInterface(SYSTEM_ENTITY, IID_TemplateManager); 189 let cmpIdentity = Engine.QueryInterface(this.entity, IID_Identity); 190 191 let entType = this.template[choice].Entity; 192 if (cmpIdentity) 193 entType = entType.replace(/\{civ\}/g, cmpIdentity.GetCiv()); 194 195 let template = cmpTemplateManager.GetTemplate(entType); 196 if (template.Identity.RequiredTechnology) 197 return template.Identity.RequiredTechnology; 198 199 return undefined; 200 }; 201 202 Upgrade.prototype.GetResourceCosts = function(template) 203 { 204 if (!this.upgradeTemplates[template]) 205 return undefined; 206 207 var choice = this.upgradeTemplates[template]; 208 if (!this.template[choice].Cost) 209 return {}; 210 211 var costs = {}; 212 for (let r in this.template[choice].Cost) 213 { 214 costs[r] = ApplyValueModificationsToEntity("Upgrade/Cost/"+r, +this.template[choice].Cost[r], this.entity); 215 } 216 return costs; 217 }; 218 219 Upgrade.prototype.Upgrade = function(template) 220 { 221 if (this.IsUpgrading() || !this.upgradeTemplates[template]) 222 return false; 223 224 var cmpPlayer = QueryOwnerInterface(this.entity, IID_Player); 225 226 if (!cmpPlayer.TrySubtractResources(this.GetResourceCosts(template))) 227 return false; 228 229 this.upgrading = template; 230 231 // Prevent cheating 232 this.ChangeUpgradedEntityCount(1); 233 234 if (this.GetUpgradeTime(template) !== 0) 235 { 236 var cmpTimer = Engine.QueryInterface(SYSTEM_ENTITY, IID_Timer); 237 this.timer = cmpTimer.SetInterval(this.entity, IID_Upgrade, "UpgradeProgress", 0, UPGRADING_PROGRESS_INTERVAL, { "upgrading": template }); 238 } 239 else 240 this.UpgradeProgress(); 241 242 return true; 243 }; 244 245 Upgrade.prototype.CancelUpgrade = function() 246 { 247 if (!this.IsUpgrading()) 248 return; 249 250 var cmpPlayer = QueryOwnerInterface(this.entity, IID_Player); 251 if (cmpPlayer) 252 { 253 var costs = this.GetResourceCosts(this.upgrading); 254 cmpPlayer.AddResources(costs); 255 } 256 257 this.ChangeUpgradedEntityCount(-1); 258 259 this.upgrading = false; 260 this.CancelTimer(); 261 this.SetElapsedTime(0); 262 }; 263 264 Upgrade.prototype.GetUpgradeTime = function(templateArg) 265 { 266 var template = this.upgrading || templateArg; 267 var choice = this.upgradeTemplates[template]; 268 if (!choice) 269 return undefined; 270 return this.template[choice].Time ? ApplyValueModificationsToEntity("Upgrade/Time", +this.template[choice].Time, this.entity) : 0; 271 }; 272 273 Upgrade.prototype.GetElapsedTime = function() 274 { 275 return this.elapsedTime; 276 }; 277 278 Upgrade.prototype.GetProgress = function() 279 { 280 if (!this.IsUpgrading()) 281 return undefined; 282 return this.GetUpgradeTime() == 0 ? 1 : this.elapsedTime / this.GetUpgradeTime(); 283 }; 284 285 Upgrade.prototype.SetElapsedTime = function(time) 286 { 287 this.elapsedTime = time; 288 }; 289 290 Upgrade.prototype.UpgradeProgress = function(data, lateness) 291 { 292 if (this.elapsedTime < this.GetUpgradeTime()) 293 { 294 this.SetElapsedTime(this.GetElapsedTime() + UPGRADING_PROGRESS_INTERVAL + lateness); 295 return; 296 } 297 298 this.CancelTimer(); 299 300 var newEntity = ChangeEntityTemplate(this.entity, this.upgrading); 301 302 if (newEntity) 303 PlaySound("upgraded", newEntity); 304 }; 305 306 Engine.RegisterComponentType(IID_Upgrade, "Upgrade", Upgrade); -
binaries/data/mods/public/simulation/helpers/Commands.js
580 580 } 581 581 }, 582 582 583 "wall-to-gate": function(player, cmd, data)584 {585 for (let ent of data.entities)586 TryTransformWallToGate(ent, data.cmpPlayer, cmd);587 },588 589 583 "lock-gate": function(player, cmd, data) 590 584 { 591 585 for (let ent of data.entities) … … 660 654 } 661 655 }, 662 656 657 "upgrade": function(player, cmd, data) 658 { 659 for (let ent of data.entities) 660 { 661 var cmpUpgrade = Engine.QueryInterface(ent, IID_Upgrade); 662 663 if (!cmpUpgrade || !cmpUpgrade.CanUpgradeTo(cmd.template)) 664 continue; 665 666 if (cmpUpgrade.WillCheckPlacementRestrictions(cmd.template) && ObstructionsBlockingTemplateChange(ent, cmd.template)) 667 { 668 var cmpGUIInterface = Engine.QueryInterface(SYSTEM_ENTITY, IID_GuiInterface); 669 cmpGUIInterface.PushNotification({ 670 "players": [data.cmpPlayer.GetPlayerID()], 671 "message": markForTranslation("Cannot upgrade as distance requirements are not verified or terrain is obstructed.") 672 }); 673 continue; 674 } 675 676 if (!CanGarrisonedChangeTemplate(ent, cmd.template)) 677 { 678 var cmpGUIInterface = Engine.QueryInterface(SYSTEM_ENTITY, IID_GuiInterface); 679 cmpGUIInterface.PushNotification({ 680 "players": [data.cmpPlayer.GetPlayerID()], 681 "message": markForTranslation("Cannot upgrade a garrisoned entity.") 682 }); 683 continue; 684 } 685 686 // Check entity limits 687 var cmpTemplateManager = Engine.QueryInterface(SYSTEM_ENTITY, IID_TemplateManager); 688 var template = cmpTemplateManager.GetTemplate(cmd.template); 689 var cmpEntityLimits = QueryPlayerIDInterface(player, IID_EntityLimits); 690 if (template.TrainingRestrictions && !cmpEntityLimits.AllowedToTrain(template.TrainingRestrictions.Category, 1) || 691 template.BuildRestrictions && !cmpEntityLimits.AllowedToBuild(template.BuildRestrictions.Category)) 692 { 693 if (g_DebugCommands) 694 warn("Invalid command: build limits check failed for player " + player + ": " + uneval(cmd)); 695 continue; 696 } 697 698 var cmpTechnologyManager = QueryOwnerInterface(ent, IID_TechnologyManager); 699 if (cmpUpgrade.GetRequiredTechnology(cmd.template) && !cmpTechnologyManager.IsTechnologyResearched(cmpUpgrade.GetRequiredTechnology(cmd.template))) 700 { 701 if (g_DebugCommands) 702 warn("Invalid command: upgrading requires unresearched technology: " + uneval(cmd)); 703 continue; 704 } 705 706 cmpUpgrade.Upgrade(cmd.template, data.cmpPlayer); 707 } 708 }, 709 710 "cancel-upgrade": function(player, cmd, data) 711 { 712 for (let ent of data.entities) 713 { 714 let cmpUpgrade = Engine.QueryInterface(ent, IID_Upgrade); 715 if (cmpUpgrade) 716 cmpUpgrade.CancelUpgrade(data.cmpPlayer); 717 } 718 }, 719 663 720 "attack-request": function(player, cmd, data) 664 721 { 665 722 // Send a chat message to human players … … 1557 1614 return entities.filter(ent => CanControlUnitOrIsAlly(ent, player, controlAll)); 1558 1615 } 1559 1616 1560 /**1561 * Try to transform a wall to a gate1562 */1563 function TryTransformWallToGate(ent, cmpPlayer, cmd)1564 {1565 var cmpIdentity = Engine.QueryInterface(ent, IID_Identity);1566 if (!cmpIdentity)1567 return;1568 1569 if (!cmpIdentity.HasClass("LongWall"))1570 {1571 if (g_DebugCommands)1572 warn("Invalid command: invalid wall conversion to gate for player: " + uneval(cmd));1573 return;1574 }1575 1576 var gate = Engine.AddEntity(cmd.template);1577 1578 var cmpCost = Engine.QueryInterface(gate, IID_Cost);1579 if (!cmpPlayer.TrySubtractResources(cmpCost.GetResourceCosts()))1580 {1581 if (g_DebugCommands)1582 warn("Invalid command: convert gate cost check failed: " + uneval(cmd));1583 1584 Engine.DestroyEntity(gate);1585 return;1586 }1587 1588 ReplaceBuildingWith(ent, gate);1589 }1590 1591 /**1592 * Unconditionally replace a building with another one1593 */1594 function ReplaceBuildingWith(ent, building)1595 {1596 // Move the building to the right place1597 var cmpPosition = Engine.QueryInterface(ent, IID_Position);1598 var cmpBuildingPosition = Engine.QueryInterface(building, IID_Position);1599 var pos = cmpPosition.GetPosition2D();1600 cmpBuildingPosition.JumpTo(pos.x, pos.y);1601 var rot = cmpPosition.GetRotation();1602 cmpBuildingPosition.SetYRotation(rot.y);1603 cmpBuildingPosition.SetXZRotation(rot.x, rot.z);1604 1605 // Copy ownership1606 var cmpOwnership = Engine.QueryInterface(ent, IID_Ownership);1607 var cmpBuildingOwnership = Engine.QueryInterface(building, IID_Ownership);1608 cmpBuildingOwnership.SetOwner(cmpOwnership.GetOwner());1609 1610 // Copy control groups1611 var cmpObstruction = Engine.QueryInterface(ent, IID_Obstruction);1612 var cmpBuildingObstruction = Engine.QueryInterface(building, IID_Obstruction);1613 cmpBuildingObstruction.SetControlGroup(cmpObstruction.GetControlGroup());1614 cmpBuildingObstruction.SetControlGroup2(cmpObstruction.GetControlGroup2());1615 1616 // Copy health level from the old entity to the new1617 var cmpHealth = Engine.QueryInterface(ent, IID_Health);1618 var cmpBuildingHealth = Engine.QueryInterface(building, IID_Health);1619 var healthFraction = Math.max(0, Math.min(1, cmpHealth.GetHitpoints() / cmpHealth.GetMaxHitpoints()));1620 var buildingHitpoints = Math.round(cmpBuildingHealth.GetMaxHitpoints() * healthFraction);1621 cmpBuildingHealth.SetHitpoints(buildingHitpoints);1622 1623 PlaySound("constructed", building);1624 1625 Engine.PostMessage(ent, MT_ConstructionFinished,1626 { "entity": ent, "newentity": building });1627 Engine.BroadcastMessage(MT_EntityRenamed, { entity: ent, newentity: building });1628 1629 Engine.DestroyEntity(ent);1630 }1631 1632 1617 Engine.RegisterGlobal("GetFormationRequirements", GetFormationRequirements); 1633 1618 Engine.RegisterGlobal("CanMoveEntsIntoFormation", CanMoveEntsIntoFormation); 1634 1619 Engine.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. 4 function ChangeEntityTemplate(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("Transform.js: Error replacing entity " + oldEnt + " for a '" + newTemplate + "'"); 11 return INVALID_ENTITY; 12 } 13 14 var cmpPosition = Engine.QueryInterface(oldEnt, IID_Position); 15 var cmpNewPosition = Engine.QueryInterface(newEnt, IID_Position); 16 if (cmpPosition && cmpNewPosition) 17 { 18 if (cmpPosition.IsInWorld()) 19 { 20 var pos = cmpPosition.GetPosition2D(); 21 cmpNewPosition.JumpTo(pos.x, pos.y); 22 } 23 var rot = cmpPosition.GetRotation(); 24 cmpNewPosition.SetYRotation(rot.y); 25 cmpNewPosition.SetXZRotation(rot.x, rot.z); 26 cmpNewPosition.SetHeightOffset(cmpPosition.GetHeightOffset()); 27 } 28 29 var cmpOwnership = Engine.QueryInterface(oldEnt, IID_Ownership); 30 var cmpNewOwnership = Engine.QueryInterface(newEnt, IID_Ownership); 31 if (cmpOwnership && cmpNewOwnership) 32 cmpNewOwnership.SetOwner(cmpOwnership.GetOwner()); 33 34 // Copy control groups 35 var cmpObstruction = Engine.QueryInterface(oldEnt, IID_Obstruction); 36 var cmpNewObstruction = Engine.QueryInterface(newEnt, IID_Obstruction); 37 if (cmpObstruction && cmpNewObstruction) 38 { 39 cmpNewObstruction.SetControlGroup(cmpObstruction.GetControlGroup()); 40 cmpNewObstruction.SetControlGroup2(cmpObstruction.GetControlGroup2()); 41 } 42 43 // Rescale capture points 44 var cmpCapturable = Engine.QueryInterface(oldEnt, IID_Capturable); 45 var cmpNewCapturable = Engine.QueryInterface(newEnt, IID_Capturable); 46 if (cmpCapturable && cmpNewCapturable) 47 { 48 let scale = cmpCapturable.GetMaxCapturePoints() / cmpNewCapturable.GetMaxCapturePoints(); 49 let newCp = cmpCapturable.GetCapturePoints().map(v => v / scale); 50 cmpNewCapturable.SetCapturePoints(newCp); 51 } 52 53 // Maintain current health level 54 var cmpHealth = Engine.QueryInterface(oldEnt, IID_Health); 55 var cmpNewHealth = Engine.QueryInterface(newEnt, IID_Health); 56 if (cmpHealth && cmpNewHealth) 57 { 58 var healthLevel = Math.max(0, Math.min(1, cmpHealth.GetHitpoints() / cmpHealth.GetMaxHitpoints())); 59 cmpNewHealth.SetHitpoints(Math.round(cmpNewHealth.GetMaxHitpoints() * healthLevel)); 60 } 61 62 var cmpUnitAI = Engine.QueryInterface(oldEnt, IID_UnitAI); 63 var cmpNewUnitAI = Engine.QueryInterface(newEnt, IID_UnitAI); 64 if (cmpUnitAI && cmpNewUnitAI) 65 { 66 var pos = cmpUnitAI.GetHeldPosition(); 67 if (pos) 68 cmpNewUnitAI.SetHeldPosition(pos.x, pos.z); 69 if (cmpUnitAI.GetStanceName()) 70 cmpNewUnitAI.SwitchToStance(cmpUnitAI.GetStanceName()); 71 cmpNewUnitAI.AddOrders(cmpUnitAI.GetOrders()); 72 cmpNewUnitAI.SetGuardOf(cmpUnitAI.IsGuardOf()); 73 } 74 75 // Maintain the list of guards 76 var cmpGuard = Engine.QueryInterface(oldEnt, IID_Guard); 77 var cmpNewGuard = Engine.QueryInterface(newEnt, IID_Guard); 78 if (cmpGuard && cmpNewGuard) 79 cmpNewGuard.SetEntities(cmpGuard.GetEntities()); 80 81 TransferGarrisonedUnits(oldEnt, newEnt); 82 83 Engine.BroadcastMessage(MT_EntityRenamed, { "entity": oldEnt, "newentity": newEnt }); 84 85 Engine.DestroyEntity(oldEnt); 86 87 return newEnt; 88 }; 89 90 function CanGarrisonedChangeTemplate(ent, template) 91 { 92 var cmpPosition = Engine.QueryInterface(ent, IID_Position); 93 var unitAI = Engine.QueryInterface(ent, IID_UnitAI); 94 if (cmpPosition && !cmpPosition.IsInWorld() && unitAI && unitAI.IsGarrisoned()) 95 { 96 // We're a garrisoned unit, assume impossibility as I've been unable to find a way to get the holder ID. 97 // TODO: change this if that ever becomes possibles 98 return false; 99 } 100 return true; 101 } 102 103 function ObstructionsBlockingTemplateChange(ent, templateArg) 104 { 105 var previewEntity = Engine.AddEntity("preview|"+templateArg); 106 107 if (previewEntity == INVALID_ENTITY) 108 return true; 109 110 var cmpBuildRestrictions = Engine.QueryInterface(previewEntity, IID_BuildRestrictions); 111 var cmpPosition = Engine.QueryInterface(ent, IID_Position); 112 var cmpOwnership = Engine.QueryInterface(ent, IID_Ownership); 113 114 var cmpNewPosition = Engine.QueryInterface(previewEntity, IID_Position); 115 116 // Return false if no ownership as BuildRestrictions.CheckPlacement needs an owner and I have no idea if false or true is better 117 // Plus there are no real entities without owners currently. 118 if (!cmpBuildRestrictions || !cmpPosition || !cmpOwnership) 119 return DeleteEntityAndReturn(previewEntity, cmpPosition, pos, angle, cmpNewPosition, false); 120 121 var pos = cmpPosition.GetPosition2D(); 122 var angle = cmpPosition.GetRotation(); 123 // move us away to prevent our own obstruction from blocking the upgrade. 124 cmpPosition.MoveOutOfWorld(); 125 126 cmpNewPosition.JumpTo(pos.x, pos.y); 127 cmpNewPosition.SetYRotation(angle.y); 128 129 var cmpNewOwnership = Engine.QueryInterface(previewEntity, IID_Ownership); 130 cmpNewOwnership.SetOwner(cmpOwnership.GetOwner()); 131 132 var checkPlacement = cmpBuildRestrictions.CheckPlacement(); 133 134 if (checkPlacement && !checkPlacement.success) 135 return DeleteEntityAndReturn(previewEntity, cmpPosition, pos, angle, cmpNewPosition, true); 136 137 var cmpTemplateManager = Engine.QueryInterface(SYSTEM_ENTITY, IID_TemplateManager); 138 var template = cmpTemplateManager.GetTemplate(cmpTemplateManager.GetCurrentTemplateName(ent)); 139 var newTemplate = cmpTemplateManager.GetTemplate(templateArg); 140 141 // Check if units are blocking our template change 142 if (template.Obstruction && newTemplate.Obstruction) 143 { 144 // This only needs to be done if the new template is strictly bigger than the old one 145 // "Obstructions" are annoying to test so just check. 146 if (newTemplate.Obstruction.Obstructions || 147 148 newTemplate.Obstruction.Static && template.Obstruction.Static && 149 (newTemplate.Obstruction.Static["@width"] > template.Obstruction.Static["@width"] || 150 newTemplate.Obstruction.Static["@depth"] > template.Obstruction.Static["@depth"]) || 151 newTemplate.Obstruction.Static && template.Obstruction.Unit && 152 (newTemplate.Obstruction.Static["@width"] > template.Obstruction.Unit["@radius"] || 153 newTemplate.Obstruction.Static["@depth"] > template.Obstruction.Unit["@radius"]) || 154 155 newTemplate.Obstruction.Unit && template.Obstruction.Unit && 156 newTemplate.Obstruction.Unit["@radius"] > template.Obstruction.Unit["@radius"] || 157 newTemplate.Obstruction.Unit && template.Obstruction.Static && 158 (newTemplate.Obstruction.Unit["@radius"] > template.Obstruction.Static["@width"] || 159 newTemplate.Obstruction.Unit["@radius"] > template.Obstruction.Static["@depth"])) 160 { 161 var cmpNewObstruction = Engine.QueryInterface(previewEntity, IID_Obstruction); 162 if (cmpNewObstruction && cmpNewObstruction.GetBlockMovementFlag()) 163 { 164 // Check for units 165 var collisions = cmpNewObstruction.GetEntityCollisions(false, true); 166 if (collisions.length) 167 return DeleteEntityAndReturn(previewEntity, cmpPosition, pos, angle, cmpNewPosition, true); 168 } 169 } 170 } 171 172 return DeleteEntityAndReturn(previewEntity, cmpPosition, pos, angle, cmpNewPosition, false); 173 }; 174 175 function DeleteEntityAndReturn(ent, cmpPosition, position, angle, cmpNewPosition, ret) 176 { 177 // prevent preview from interfering in the world 178 cmpNewPosition.MoveOutOfWorld(); 179 cmpPosition.JumpTo(position.x, position.y); 180 cmpPosition.SetYRotation(angle.y); 181 182 Engine.DestroyEntity(ent); 183 return ret; 184 }; 185 186 function TransferGarrisonedUnits(oldEnt, newEnt) 187 { 188 // Transfer garrisoned units if possible, or unload them 189 var cmpGarrison = Engine.QueryInterface(oldEnt, IID_GarrisonHolder); 190 var cmpNewGarrison = Engine.QueryInterface(newEnt, IID_GarrisonHolder); 191 if (!cmpNewGarrison || !cmpGarrison || !cmpGarrison.GetEntities().length) 192 return; // nothing to do as the code will by default unload all. 193 194 var garrisonedEntities = cmpGarrison.GetEntities().slice(); 195 for (let j in garrisonedEntities) 196 { 197 var cmpUnitAI = Engine.QueryInterface(garrisonedEntities[j], IID_UnitAI); 198 cmpGarrison.Eject(garrisonedEntities[j]); 199 cmpUnitAI.Autogarrison(newEnt); 200 cmpNewGarrison.Garrison(garrisonedEntities[j]); 201 } 202 }; 203 204 Engine.RegisterGlobal("ChangeEntityTemplate", ChangeEntityTemplate); 205 Engine.RegisterGlobal("CanGarrisonedChangeTemplate", CanGarrisonedChangeTemplate); 206 Engine.RegisterGlobal("ObstructionsBlockingTemplateChange", ObstructionsBlockingTemplateChange); -
binaries/data/mods/public/simulation/templates/other/palisades_rocks_long.xml
26 26 <SelectionGroupName>other/wallset_palisade</SelectionGroupName> 27 27 <SpecificName>Palisade</SpecificName> 28 28 <GenericName>Wooden Wall</GenericName> 29 <GateConversionTooltip>Convert Wooden Wall into Wooden Gate</GateConversionTooltip>30 29 <Classes datatype="tokens">-StoneWall Palisade</Classes> 31 30 <Icon>gaia/special_palisade.png</Icon> 32 31 <RequiredTechnology>phase_village</RequiredTechnology> … … 45 44 <WallPiece> 46 45 <Length>11.0</Length> 47 46 </WallPiece> 47 <Upgrade> 48 <Gate> 49 <Entity>other/palisades_rocks_gate</Entity> 50 <Cost> 51 <stone>0</stone> 52 <wood>20</wood> 53 </Cost> 54 <Time>5000</Time> 55 </Gate> 56 </Upgrade> 48 57 </Entity> -
binaries/data/mods/public/simulation/templates/structures/rome_siege_wall_long.xml
55 55 </Classes> 56 56 <Icon>structures/palisade_wall.png</Icon> 57 57 <Tooltip>A wooden and turf palisade buildable in enemy and neutral territories.</Tooltip> 58 <GateConversionTooltip>Convert Siege Wall into Siege Wall Gate</GateConversionTooltip>59 58 <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> 60 59 </Identity> 61 60 <Obstruction> -
binaries/data/mods/public/simulation/templates/template_structure_defense_wall_long.xml
38 38 <Identity> 39 39 <Classes datatype="tokens">LongWall</Classes> 40 40 <Tooltip>Long wall segments can be converted to gates.</Tooltip> 41 <GateConversionTooltip>Convert Stone Wall into City Gate</GateConversionTooltip>42 41 </Identity> 42 <Upgrade> 43 <Gate> 44 <Entity>structures/{civ}_wall_gate</Entity> 45 <Tooltip>This will allow you to let units circulate through your fortifications.</Tooltip> 46 <Cost> 47 <stone>60</stone> 48 </Cost> 49 <Time>10000</Time> 50 </Gate> 51 </Upgrade> 43 52 </Entity>