Ticket #2706: UpgradeComponent-final.patch
File UpgradeComponent-final.patch, 47.5 KB (added by , 8 years ago) |
---|
-
binaries/data/mods/public/globalscripts/Templates.js
270 270 }; 271 271 ret.icon = template.Identity.Icon; 272 272 ret.tooltip = template.Identity.Tooltip; 273 ret.gateConversionTooltip = template.Identity.GateConversionTooltip;274 273 ret.requiredTechnology = template.Identity.RequiredTechnology; 275 274 ret.visibleIdentityClasses = GetVisibleIdentityClasses(template.Identity); 276 275 } -
binaries/data/mods/public/gui/common/tooltips.js
291 291 trainNum = 1; 292 292 293 293 let totalCosts = multiplyEntityCosts(template, trainNum); 294 totalCosts.time = Math.ceil(template.cost.time * (entity ? Engine.GuiInterfaceCall("GetBatchTime", { "entity": entity, "batchSize": trainNum }) : 1)); 294 if (template.cost.time) 295 totalCosts.time = Math.ceil(template.cost.time * (entity ? Engine.GuiInterfaceCall("GetBatchTime", { "entity": entity, "batchSize": trainNum }) : 1)); 295 296 296 297 let costs = []; 297 298 -
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
472 472 }, 473 473 "getItems": function(unitEntState, selection) 474 474 { 475 // Allow long wall pieces to be converted to gates476 let longWallTypes = {};477 let walls = [];478 475 let gates = []; 479 476 for (let ent of selection) 480 477 { 481 478 let state = GetEntityState(ent); 482 if ( hasClass(state, "LongWall") && !state.gate && !longWallTypes[state.template])479 if (state.gate && !gates.length) 483 480 { 484 let gateTemplate = getWallGateTemplate(state.id);485 if (gateTemplate)486 {487 let 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 {505 481 gates.push({ 506 482 "gate": state.gate, 507 483 "tooltip": translate("Lock Gate"), … … 521 497 delete gates[j].gate.locked; 522 498 } 523 499 524 // Place wall conversion options after gate lock/unlock icons. 525 return gates.concat(walls); 500 return gates; 526 501 }, 527 502 "setupButton": function(data) 528 503 { 529 504 data.button.onPress = function() {data.item.callback(data.item); }; 530 505 531 let tooltips = [data.item.tooltip]; 532 if (data.item.template) 533 { 534 data.template = GetTemplateData(data.item.template); 535 data.wallCount = data.selection.reduce(count, ent => { 536 let state = GetEntityState(ent); 537 if (hasClass(state, "LongWall") && !state.gate) 538 ++count; 539 return count; 540 }, 0); 506 data.button.tooltip = data.item.tooltip; 541 507 542 tooltips.push(getEntityCostTooltip(data.template, data.wallCount));543 544 data.neededResources = Engine.GuiInterfaceCall("GetNeededResources", {545 "cost": multiplyEntityCosts(data.template, data.wallCount)546 });547 548 tooltips.push(getNeededResourcesTooltip(data.neededResources));549 }550 data.button.tooltip = tooltips.filter(tip => tip).join("\n");551 552 508 data.button.enabled = controlsPlayer(data.unitEntState.player); 553 509 let gateIcon; 554 510 if (data.item.gate) 555 511 { 556 // If already a gate,show locking actions512 // show locking actions 557 513 gateIcon = "icons/lock_" + GATE_ACTIONS[data.item.locked ? 0 : 1] + "ed.png"; 558 514 if (data.item.gate.locked === undefined) 559 515 data.guiSelection.hidden = false; … … 560 516 else 561 517 data.guiSelection.hidden = data.item.gate.locked != data.item.locked; 562 518 } 563 else564 {565 // Otherwise show gate upgrade icon566 let template = GetTemplateData(data.item.template);567 if (!template)568 return false;569 gateIcon = data.template.icon ? "portraits/" + data.template.icon : "icons/gate_closed.png";570 data.guiSelection.hidden = true;571 }572 519 573 520 data.icon.sprite = (data.neededResources ? resourcesToAlphaMask(data.neededResources) + ":" : "") + "stretched:session/" + gateIcon; 574 521 … … 609 556 } 610 557 let items = []; 611 558 if (checks.packButton) 612 items.push({ "packing": false, "packed": false, "tooltip": translate("Pack"), "callback": function() { packUnit(true); } }); 559 items.push({ 560 "packing": false, 561 "packed": false, 562 "tooltip": translate("Pack"), 563 "callback": function() { packUnit(true); } 564 }); 613 565 if (checks.unpackButton) 614 items.push({ "packing": false, "packed": true, "tooltip": translate("Unpack"), "callback": function() { packUnit(false); } }); 566 items.push({ 567 "packing": false, 568 "packed": true, 569 "tooltip": translate("Unpack"), 570 "callback": function() { packUnit(false); } 571 }); 615 572 if (checks.packCancelButton) 616 items.push({ "packing": true, "packed": false, "tooltip": translate("Cancel Packing"), "callback": function() { cancelPackUnit(true); } }); 573 items.push({ 574 "packing": true, 575 "packed": false, 576 "tooltip": translate("Cancel Packing"), 577 "callback": function() { cancelPackUnit(true); } 578 }); 617 579 if (checks.unpackCancelButton) 618 items.push({ "packing": true, "packed": true, "tooltip": translate("Cancel Unpacking"), "callback": function() { cancelPackUnit(false); } }); 580 items.push({ 581 "packing": true, 582 "packed": true, 583 "tooltip": translate("Cancel Unpacking"), 584 "callback": function() { cancelPackUnit(false); } 585 }); 619 586 return items; 620 587 }, 621 588 "setupButton": function(data) … … 1051 1018 } 1052 1019 }; 1053 1020 1021 g_SelectionPanels.Upgrade = { 1022 "getMaxNumberOfItems": function() 1023 { 1024 return 24 - getNumberOfRightPanelButtons(); 1025 }, 1026 "getItems": function(unitEntState, selection) 1027 { 1028 // Interface becomes complicated with multiple units and this is meant per-entity, so prevent it if the selection has multiple units. 1029 // TODO: if the units are all the same, this should probably still be possible. 1030 if (selection.length > 1) 1031 return false; 1032 1033 if (!unitEntState.upgrade) 1034 return false; 1035 1036 var items = []; 1037 1038 for (let upgrade of unitEntState.upgrade.upgrades) 1039 { 1040 items.push({ 1041 "entity": upgrade.entity, 1042 "cost": upgrade.cost, 1043 "time": upgrade.time, 1044 "icon": upgrade.icon, 1045 "tooltip": upgrade.tooltip, 1046 "requiredTechnology": upgrade.requiredTechnology, 1047 }); 1048 } 1049 return items; 1050 }, 1051 "setupButton" : function(data) 1052 { 1053 let template = GetTemplateData(data.item.entity); 1054 if (!template) 1055 return false; 1056 1057 let technologyEnabled = true; 1058 1059 if (data.item.requiredTechnology) 1060 technologyEnabled = Engine.GuiInterfaceCall("IsTechnologyResearched", { 1061 "tech": requiredTechnology, 1062 "player": data.unitEntState.player 1063 }); 1064 1065 let neededResources; 1066 if (data.item.cost) 1067 neededResources = Engine.GuiInterfaceCall("GetNeededResources", { 1068 "cost": data.item.cost, 1069 "player": data.unitEntState.player 1070 }); 1071 1072 let limits = getEntityLimitAndCount(data.playerState, data.item.entity); 1073 1074 let progress = data.unitEntState.upgrade.progress || 0; 1075 let isUpgrading = data.unitEntState.upgrade.template == data.item.entity; 1076 1077 let tooltip; 1078 if (!progress) 1079 { 1080 if (data.item.tooltip) 1081 tooltip = sprintf(translate("\nUpgrade into a %(name)s.%(tooltip)s"), { 1082 "name": template.name.generic, 1083 "tooltip": data.item.tooltip 1084 }); 1085 else 1086 tooltip = sprintf(translate("\nUpgrade into a %(name)s."), {"name": template.name.generic}); 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 progressOverlay.size.top = progressOverlay.size.left + Math.round(progress * (progressOverlay.size.right - progressOverlay.size.left)); 1134 1135 progressOverlay.hidden = !isUpgrading; 1136 1137 let index = data.i + getNumberOfRightPanelButtons(); 1138 setPanelObjectPosition(data.button, index, data.rowLength); 1139 return true; 1140 } 1141 }; 1142 1054 1143 /** 1055 1144 * If two panels need the same space, so they collide, 1056 1145 * the one appearing first in the order is rendered. … … 1068 1157 // RIGHT PANE 1069 1158 "Gate", // Must always be shown on gates 1070 1159 "Pack", // Must always be shown on packable entities 1160 "Upgrade", // Must always be shown on upgradable entities 1071 1161 "Training", 1072 1162 "Construction", 1073 1163 "Research", // Normal together with training -
binaries/data/mods/public/gui/session/selection_panels_helpers.js
4 4 const BARTER_ACTIONS = ["Sell", "Buy"]; 5 5 const GATE_ACTIONS = ["lock", "unlock"]; 6 6 7 // upgrade constants 8 const UPGRADING_NOT_STARTED = -2; 9 const UPGRADING_CHOSEN_OTHER = -1; 10 11 // ============================================== 12 // BARTER HELPERS 13 // Resources to sell on barter panel 7 14 var g_BarterSell = "food"; 8 15 9 16 function canMoveSelectionIntoFormation(formationTemplate) -
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
40 40 "</element>" + 41 41 "</optional>" + 42 42 "<optional>" + 43 "<element name='GateConversionTooltip'>" +44 "<text/>" +45 "</element>" +46 "</optional>" +47 "<optional>" +48 43 "<element name='Rollover'>" + 49 44 "<text/>" + 50 45 "</element>" + -
binaries/data/mods/public/simulation/components/interfaces/Upgrade.js
1 Engine.RegisterInterface("Upgrade"); -
binaries/data/mods/public/simulation/components/Pack.js
33 33 { 34 34 if (this.timer) 35 35 { 36 varcmpTimer = Engine.QueryInterface(SYSTEM_ENTITY, IID_Timer);36 let cmpTimer = Engine.QueryInterface(SYSTEM_ENTITY, IID_Timer); 37 37 cmpTimer.CancelTimer(this.timer); 38 38 this.timer = undefined; 39 39 } … … 56 56 return; 57 57 58 58 this.packing = true; 59 varcmpTimer = Engine.QueryInterface(SYSTEM_ENTITY, IID_Timer);59 let cmpTimer = Engine.QueryInterface(SYSTEM_ENTITY, IID_Timer); 60 60 this.timer = cmpTimer.SetInterval(this.entity, IID_Pack, "PackProgress", 0, PACKING_INTERVAL, {"packing": true}); 61 varcmpVisual = Engine.QueryInterface(this.entity, IID_Visual);61 let cmpVisual = Engine.QueryInterface(this.entity, IID_Visual); 62 62 if (cmpVisual) 63 63 cmpVisual.SelectAnimation("packing", true, 1.0, "packing"); 64 64 }; … … 70 70 return; 71 71 72 72 this.packing = true; 73 varcmpTimer = Engine.QueryInterface(SYSTEM_ENTITY, IID_Timer);73 let cmpTimer = Engine.QueryInterface(SYSTEM_ENTITY, IID_Timer); 74 74 this.timer = cmpTimer.SetInterval(this.entity, IID_Pack, "PackProgress", 0, PACKING_INTERVAL, {"packing": false}); 75 varcmpVisual = Engine.QueryInterface(this.entity, IID_Visual);75 let cmpVisual = Engine.QueryInterface(this.entity, IID_Visual); 76 76 if (cmpVisual) 77 77 cmpVisual.SelectAnimation("unpacking", true, 1.0, "unpacking"); 78 78 }; … … 88 88 this.SetElapsedTime(0); 89 89 90 90 // Clear animation 91 varcmpVisual = Engine.QueryInterface(this.entity, IID_Visual);91 let cmpVisual = Engine.QueryInterface(this.entity, IID_Visual); 92 92 if (cmpVisual) 93 93 cmpVisual.SelectAnimation("idle", false, 1.0, ""); 94 94 }; … … 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 let 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()); 132 if (newEntity) 133 PlaySound(this.packed ? "packed" : "unpacked", newEntity); 147 134 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 points153 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 level163 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 guards182 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 sound190 var sound = this.packed ? "packed" : "unpacked";191 PlaySound(sound, newEntity);192 193 // Destroy current entity194 Engine.DestroyEntity(this.entity);195 }196 else197 {198 this.SetElapsedTime(this.GetElapsedTime() + PACKING_INTERVAL + lateness);199 }200 135 }; 201 136 202 137 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 let cmpTempMan = Engine.QueryInterface(SYSTEM_ENTITY, IID_TemplateManager); 91 let template = cmpTempMan.GetTemplate(this.upgrading); 92 93 let 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 let 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 let ret = []; 114 115 let 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 let 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 let choice = this.upgradeTemplates[template]; 208 if (!this.template[choice].Cost) 209 return {}; 210 211 let 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 let 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 let 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 let cmpPlayer = QueryOwnerInterface(this.entity, IID_Player); 251 if (cmpPlayer) 252 { 253 let 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 let template = this.upgrading || templateArg; 267 let 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 let 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
581 581 } 582 582 }, 583 583 584 "wall-to-gate": function(player, cmd, data)585 {586 for (let ent of data.entities)587 TryTransformWallToGate(ent, data.cmpPlayer, cmd);588 },589 590 584 "lock-gate": function(player, cmd, data) 591 585 { 592 586 for (let ent of data.entities) … … 661 655 } 662 656 }, 663 657 658 "upgrade": function(player, cmd, data) 659 { 660 for (let ent of data.entities) 661 { 662 var cmpUpgrade = Engine.QueryInterface(ent, IID_Upgrade); 663 664 if (!cmpUpgrade || !cmpUpgrade.CanUpgradeTo(cmd.template)) 665 continue; 666 667 if (cmpUpgrade.WillCheckPlacementRestrictions(cmd.template) && ObstructionsBlockingTemplateChange(ent, cmd.template)) 668 { 669 var cmpGUIInterface = Engine.QueryInterface(SYSTEM_ENTITY, IID_GuiInterface); 670 cmpGUIInterface.PushNotification({ 671 "players": [data.cmpPlayer.GetPlayerID()], 672 "message": markForTranslation("Cannot upgrade as distance requirements are not verified or terrain is obstructed.") 673 }); 674 continue; 675 } 676 677 if (!CanGarrisonedChangeTemplate(ent, cmd.template)) 678 { 679 var cmpGUIInterface = Engine.QueryInterface(SYSTEM_ENTITY, IID_GuiInterface); 680 cmpGUIInterface.PushNotification({ 681 "players": [data.cmpPlayer.GetPlayerID()], 682 "message": markForTranslation("Cannot upgrade a garrisoned entity.") 683 }); 684 continue; 685 } 686 687 // Check entity limits 688 var cmpTemplateManager = Engine.QueryInterface(SYSTEM_ENTITY, IID_TemplateManager); 689 var template = cmpTemplateManager.GetTemplate(cmd.template); 690 var cmpEntityLimits = QueryPlayerIDInterface(player, IID_EntityLimits); 691 if (template.TrainingRestrictions && !cmpEntityLimits.AllowedToTrain(template.TrainingRestrictions.Category, 1) || 692 template.BuildRestrictions && !cmpEntityLimits.AllowedToBuild(template.BuildRestrictions.Category)) 693 { 694 if (g_DebugCommands) 695 warn("Invalid command: build limits check failed for player " + player + ": " + uneval(cmd)); 696 continue; 697 } 698 699 var cmpTechnologyManager = QueryOwnerInterface(ent, IID_TechnologyManager); 700 if (cmpUpgrade.GetRequiredTechnology(cmd.template) && !cmpTechnologyManager.IsTechnologyResearched(cmpUpgrade.GetRequiredTechnology(cmd.template))) 701 { 702 if (g_DebugCommands) 703 warn("Invalid command: upgrading requires unresearched technology: " + uneval(cmd)); 704 continue; 705 } 706 707 cmpUpgrade.Upgrade(cmd.template, data.cmpPlayer); 708 } 709 }, 710 711 "cancel-upgrade": function(player, cmd, data) 712 { 713 for (let ent of data.entities) 714 { 715 let cmpUpgrade = Engine.QueryInterface(ent, IID_Upgrade); 716 if (cmpUpgrade) 717 cmpUpgrade.CancelUpgrade(data.cmpPlayer); 718 } 719 }, 720 664 721 "attack-request": function(player, cmd, data) 665 722 { 666 723 // Send a chat message to human players … … 1558 1615 return entities.filter(ent => CanControlUnitOrIsAlly(ent, player, controlAll)); 1559 1616 } 1560 1617 1561 /**1562 * Try to transform a wall to a gate1563 */1564 function TryTransformWallToGate(ent, cmpPlayer, cmd)1565 {1566 var cmpIdentity = Engine.QueryInterface(ent, IID_Identity);1567 if (!cmpIdentity)1568 return;1569 1570 if (!cmpIdentity.HasClass("LongWall"))1571 {1572 if (g_DebugCommands)1573 warn("Invalid command: invalid wall conversion to gate for player: " + uneval(cmd));1574 return;1575 }1576 1577 var gate = Engine.AddEntity(cmd.template);1578 1579 var cmpCost = Engine.QueryInterface(gate, IID_Cost);1580 if (!cmpPlayer.TrySubtractResources(cmpCost.GetResourceCosts()))1581 {1582 if (g_DebugCommands)1583 warn("Invalid command: convert gate cost check failed: " + uneval(cmd));1584 1585 Engine.DestroyEntity(gate);1586 return;1587 }1588 1589 ReplaceBuildingWith(ent, gate);1590 }1591 1592 /**1593 * Unconditionally replace a building with another one1594 */1595 function ReplaceBuildingWith(ent, building)1596 {1597 // Move the building to the right place1598 var cmpPosition = Engine.QueryInterface(ent, IID_Position);1599 var cmpBuildingPosition = Engine.QueryInterface(building, IID_Position);1600 var pos = cmpPosition.GetPosition2D();1601 cmpBuildingPosition.JumpTo(pos.x, pos.y);1602 var rot = cmpPosition.GetRotation();1603 cmpBuildingPosition.SetYRotation(rot.y);1604 cmpBuildingPosition.SetXZRotation(rot.x, rot.z);1605 1606 // Copy ownership1607 var cmpOwnership = Engine.QueryInterface(ent, IID_Ownership);1608 var cmpBuildingOwnership = Engine.QueryInterface(building, IID_Ownership);1609 cmpBuildingOwnership.SetOwner(cmpOwnership.GetOwner());1610 1611 // Copy control groups1612 var cmpObstruction = Engine.QueryInterface(ent, IID_Obstruction);1613 var cmpBuildingObstruction = Engine.QueryInterface(building, IID_Obstruction);1614 cmpBuildingObstruction.SetControlGroup(cmpObstruction.GetControlGroup());1615 cmpBuildingObstruction.SetControlGroup2(cmpObstruction.GetControlGroup2());1616 1617 // Copy health level from the old entity to the new1618 var cmpHealth = Engine.QueryInterface(ent, IID_Health);1619 var cmpBuildingHealth = Engine.QueryInterface(building, IID_Health);1620 var healthFraction = Math.max(0, Math.min(1, cmpHealth.GetHitpoints() / cmpHealth.GetMaxHitpoints()));1621 var buildingHitpoints = Math.round(cmpBuildingHealth.GetMaxHitpoints() * healthFraction);1622 cmpBuildingHealth.SetHitpoints(buildingHitpoints);1623 1624 PlaySound("constructed", building);1625 1626 Engine.PostMessage(ent, MT_ConstructionFinished,1627 { "entity": ent, "newentity": building });1628 Engine.BroadcastMessage(MT_EntityRenamed, { entity: ent, newentity: building });1629 1630 Engine.DestroyEntity(ent);1631 }1632 1633 1618 Engine.RegisterGlobal("GetFormationRequirements", GetFormationRequirements); 1634 1619 Engine.RegisterGlobal("CanMoveEntsIntoFormation", CanMoveEntsIntoFormation); 1635 1620 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>