Ticket #2706: UpgradeComponent.10.patch
File UpgradeComponent.10.patch, 45.3 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/session/input.js
1641 1641 }); 1642 1642 } 1643 1643 1644 // Transform a wall to a gate1645 function transformWallToGate(template)1644 // Upgrade an entity to another 1645 function upgradeEntity(Template) 1646 1646 { 1647 1647 var selection = g_Selection.toList(); 1648 1648 Engine.PostNetworkCommand({ 1649 "type": "wall-to-gate", 1650 "entities": selection.filter( function(e) { return getWallGateTemplate(e) == template; } ), 1651 "template": template, 1649 "type": "upgrade", 1650 "entities": selection, 1651 "template": Template, 1652 "queued": false 1652 1653 }); 1653 1654 } 1654 1655 1655 // Gets the gate form (if any) of a given long wall piece1656 function getWallGateTemplate(entity)1656 // Cancel upgrading entities 1657 function cancelUpgradeEntity() 1657 1658 { 1658 // TODO: find the gate template name in a better way 1659 var entState = GetEntityState(entity); 1660 var index; 1661 1662 if (entState && !entState.foundation && hasClass(entState, "LongWall") && (index = entState.template.indexOf("long")) >= 0) 1663 return entState.template.substr(0, index) + "gate"; 1664 return undefined; 1659 var selection = g_Selection.toList(); 1660 Engine.PostNetworkCommand({ 1661 "type": "cancel-upgrade", 1662 "entities": selection, 1663 "queued": false 1664 }); 1665 1665 } 1666 1666 1667 1667 // Set the camera to follow the given unit -
binaries/data/mods/public/gui/session/selection_panels.js
446 446 }, 447 447 "getItems": function(unitEntState, selection) 448 448 { 449 // Allow long wall pieces to be converted to gates450 let longWallTypes = {};451 let walls = [];452 449 let gates = []; 453 450 for (let ent of selection) 454 451 { 455 452 let state = GetEntityState(ent); 456 if ( hasClass(state, "LongWall") && !state.gate && !longWallTypes[state.template])453 if (state.gate && !gates.length) 457 454 { 458 let gateTemplate = getWallGateTemplate(state.id);459 if (gateTemplate)460 {461 let tooltipString = GetTemplateDataWithoutLocalization(state.template).gateConversionTooltip;462 if (!tooltipString)463 {464 warn(state.template + " is supposed to be convertable to a gate, but it's missing the GateConversionTooltip in the Identity template");465 tooltipString = "";466 }467 walls.push({468 "tooltip": translate(tooltipString),469 "template": gateTemplate,470 "callback": function (item) { transformWallToGate(item.template); }471 });472 }473 474 // We only need one entity per type.475 longWallTypes[state.template] = true;476 }477 else if (state.gate && !gates.length)478 {479 455 gates.push({ 480 456 "gate": state.gate, 481 457 "tooltip": translate("Lock Gate"), … … 495 471 delete gates[j].gate.locked; 496 472 } 497 473 498 // Place wall conversion options after gate lock/unlock icons. 499 return gates.concat(walls); 474 return gates; 500 475 }, 501 476 "setupButton": function(data) 502 477 { … … 503 478 data.button.onPress = function() {data.item.callback(data.item); }; 504 479 505 480 let tooltip = data.item.tooltip; 506 if (data.item.template) 507 { 508 data.template = GetTemplateData(data.item.template); 509 data.wallCount = data.selection.reduce(function (count, ent) { 510 let state = GetEntityState(ent); 511 if (hasClass(state, "LongWall") && !state.gate) 512 ++count; 513 return count; 514 }, 0); 481 data.button.tooltip = data.item.tooltip; 515 482 516 tooltip += "\n" + getEntityCostTooltip(data.template, data.wallCount);517 518 data.neededResources = Engine.GuiInterfaceCall("GetNeededResources", {519 "cost": multiplyEntityCosts(data.template, data.wallCount)520 });521 522 if (data.neededResources)523 tooltip += getNeededResourcesTooltip(data.neededResources);524 }525 data.button.tooltip = tooltip;526 527 483 data.button.enabled = controlsPlayer(data.unitEntState.player); 528 484 let gateIcon; 529 485 if (data.item.gate) 530 486 { 531 // If already a gate, show locking actions487 // Show locking actions 532 488 gateIcon = "icons/lock_" + GATE_ACTIONS[data.item.locked ? 0 : 1] + "ed.png"; 533 489 if (data.item.gate.locked === undefined) 534 490 data.guiSelection.hidden = false; … … 535 491 else 536 492 data.guiSelection.hidden = data.item.gate.locked != data.item.locked; 537 493 } 538 else539 {540 // Otherwise show gate upgrade icon541 let template = GetTemplateData(data.item.template);542 if (!template)543 return false;544 gateIcon = data.template.icon ? "portraits/" + data.template.icon : "icons/gate_closed.png";545 data.guiSelection.hidden = true;546 }547 494 548 495 data.icon.sprite = (data.neededResources ? resourcesToAlphaMask(data.neededResources) + ":" : "") + "stretched:session/" + gateIcon; 549 496 … … 584 531 } 585 532 let items = []; 586 533 if (checks.packButton) 587 items.push({ "packing": false, "packed": false, "tooltip": translate("Pack"), "callback": function() { packUnit(true); } }); 534 items.push({ 535 "packing": false, 536 "packed": false, 537 "tooltip": translate("Pack"), 538 "callback": function() { packUnit(true); } 539 }); 588 540 if (checks.unpackButton) 589 items.push({ "packing": false, "packed": true, "tooltip": translate("Unpack"), "callback": function() { packUnit(false); } }); 541 items.push({ 542 "packing": false, 543 "packed": true, 544 "tooltip": translate("Unpack"), 545 "callback": function() { packUnit(false); } 546 }); 590 547 if (checks.packCancelButton) 591 items.push({ "packing": true, "packed": false, "tooltip": translate("Cancel Packing"), "callback": function() { cancelPackUnit(true); } }); 548 items.push({ 549 "packing": true, 550 "packed": false, 551 "tooltip": translate("Cancel Packing"), 552 "callback": function() { cancelPackUnit(true); } 553 }); 592 554 if (checks.unpackCancelButton) 593 items.push({ "packing": true, "packed": true, "tooltip": translate("Cancel Unpacking"), "callback": function() { cancelPackUnit(false); } }); 555 items.push({ 556 "packing": true, 557 "packed": true, 558 "tooltip": translate("Cancel Unpacking"), 559 "callback": function() { cancelPackUnit(false); } 560 }); 594 561 return items; 595 562 }, 596 563 "setupButton": function(data) … … 1027 994 } 1028 995 }; 1029 996 997 g_SelectionPanels.Upgrade = { 998 "getMaxNumberOfItems": function() 999 { 1000 return 24 - getNumberOfRightPanelButtons(); 1001 }, 1002 "getItems": function(unitEntState, selection) 1003 { 1004 // Interface becomes complicated with multiple units and this is meant per-entity, so prevent it if the selection has multiple units. 1005 // TODO: if the units are all the same, this should probably still be possible. 1006 if (selection.length > 1) 1007 return false; 1030 1008 1009 if (!unitEntState.upgrade) 1010 return false; 1031 1011 1012 var items = []; 1013 1014 for (let upgrade of unitEntState.upgrade.upgrades) 1015 { 1016 var item = { 1017 "entity": upgrade.entity, 1018 "cost": upgrade.cost, 1019 "time": upgrade.time, 1020 "icon": upgrade.icon, 1021 "tooltip": upgrade.tooltip, 1022 "requiredTechnology": upgrade.requiredTechnology, 1023 }; 1024 items.push(item); 1025 } 1026 return items; 1027 }, 1028 "setupButton" : function(data) 1029 { 1030 let template = GetTemplateData(data.item.entity); 1031 if (!template) 1032 return false; 1033 1034 let isUpgrading = data.unitEntState.upgrade.template == data.item.entity; 1035 let progress = data.unitEntState.upgrade.progress || 0; 1036 1037 let technologyEnabled = true; 1038 let requiredTechnology = data.item.requiredTechnology || template.requiredTechnology; 1039 if (requiredTechnology) 1040 technologyEnabled = Engine.GuiInterfaceCall("IsTechnologyResearched", requiredTechnology); 1041 1042 let neededResources; 1043 if (data.item.cost) 1044 neededResources = Engine.GuiInterfaceCall("GetNeededResources", { "cost": data.item.cost }); 1045 1046 let limits = getEntityLimitAndCount(data.playerState, data.item.entity); 1047 1048 let tooltip; 1049 if (!progress) 1050 { 1051 tooltip = sprintf(translate("Upgrade into a %(name)s.%(tooltip)s"), { 1052 "name": template.name.generic, 1053 "tooltip": (data.item.tooltip? "\n" + data.item.tooltip : "") 1054 }); 1055 if (data.item.cost) 1056 tooltip += "\n" + getEntityCostTooltip(data.item); 1057 1058 tooltip += formatLimitString(limits.entLimit, limits.entCount, limits.entLimitChangers); 1059 if (!technologyEnabled) 1060 tooltip += "\n" + sprintf(translate("Requires %(technology)s"), { 1061 "technology": getEntityNames(GetTechnologyData(requiredTechnology)) 1062 }); 1063 1064 if (data.item.neededResources) 1065 tooltip += getNeededResourcesTooltip(neededResources); 1066 1067 data.button.onPress = function() { upgradeEntity(data.item.entity); }; 1068 } 1069 else if (isUpgrading) 1070 { 1071 tooltip = translate("Cancel Upgrading"); 1072 data.button.onPress = function() { cancelUpgradeEntity(); }; 1073 } 1074 else 1075 { 1076 tooltip = translate("Cannot upgrade when the entity is already upgrading."); 1077 data.button.onPress = function() {}; 1078 } 1079 data.button.tooltip = tooltip; 1080 1081 let modifier = ""; 1082 if (!isUpgrading) 1083 { 1084 if (progress || !technologyEnabled || limits.canBeAddedCount == 0) 1085 { 1086 data.button.enabled = false; 1087 modifier = "color:0 0 0 127:grayscale:"; 1088 } 1089 else if (neededResources) 1090 { 1091 data.button.enabled = false; 1092 modifier = resourcesToAlphaMask(neededResources); 1093 } 1094 } 1095 1096 data.icon.sprite = modifier + "stretched:session/" + 1097 (data.item.icon || "portraits/" + template.icon); 1098 1099 let progressOverlay = Engine.GetGUIObjectByName("unitUpgradeProgressSlider[" + data.i + "]"); 1100 if (isUpgrading) 1101 { 1102 let size = progressOverlay.size; 1103 size.top = size.left + Math.round(progress * (size.right - size.left)); 1104 progressOverlay.size = size; 1105 progressOverlay.hidden = false; 1106 } 1107 else 1108 progressOverlay.hidden = true; 1109 1110 let index = data.i + getNumberOfRightPanelButtons(); 1111 setPanelObjectPosition(data.button, index, data.rowLength); 1112 return true; 1113 }, 1114 }; 1115 1116 1117 1032 1118 /** 1033 1119 * If two panels need the same space, so they collide, 1034 1120 * the one appearing first in the order is rendered. … … 1046 1132 // RIGHT PANE 1047 1133 "Gate", // Must always be shown on gates 1048 1134 "Pack", // Must always be shown on packable entities 1135 "Upgrade", // Must always be shown on upgradable entities 1049 1136 "Training", 1050 1137 "Construction", 1051 1138 "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"/> 10 <object name="unitUpgradeProgressSlider[n]" type="image" sprite="queueProgressSlider" ghost="true" size="3 3 43 43" z="20"/> 11 <object name="unitUpgradeSelection[n]" hidden="true" type="image" ghost="true" size="3 3 43 43" sprite="stretched:session/icons/corners.png"/> 12 <object name="unitUpgradeUnaffordable[n]" hidden="true" type="image" ghost="true" size="3 3 43 43" sprite="colour: 255 0 0 127"/> 13 </object> 14 </repeat> 15 </object> 16 </object> -
binaries/data/mods/public/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 = { "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 }; 3 3 4 4 /** 5 5 * Set the position of a panel object according to the index, … … 225 225 { 226 226 var sum = 0; 227 227 228 for (let prop of ["Construction", "Training", "Pack", "Gate" ])228 for (let prop of ["Construction", "Training", "Pack", "Gate", "Upgrade"]) 229 229 if (g_SelectionPanels[prop].used) 230 230 sum += g_unitPanelButtons[prop]; 231 231 -
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/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'><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 != 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. 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 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 100 Upgrade.prototype.CanUpgradeTo = function(template) 101 { 102 return this.upgradeTemplates[template] !== undefined; 103 }; 104 105 Upgrade.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 144 Upgrade.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 154 Upgrade.prototype.IsUpgrading = function() 155 { 156 return this.upgrading !== false; 157 }; 158 159 Upgrade.prototype.GetUpgradingTo = function() 160 { 161 return this.upgrading; 162 }; 163 164 Upgrade.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 172 Upgrade.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 192 Upgrade.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 210 Upgrade.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 239 Upgrade.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 258 Upgrade.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 267 Upgrade.prototype.GetElapsedTime = function() 268 { 269 return this.elapsedTime; 270 }; 271 272 Upgrade.prototype.GetProgress = function() 273 { 274 if (!this.IsUpgrading()) 275 return undefined; 276 return this.GetUpgradeTime() == 0 ? 1 : this.elapsedTime / this.GetUpgradeTime(); 277 }; 278 279 Upgrade.prototype.SetElapsedTime = function(time) 280 { 281 this.elapsedTime = time; 282 }; 283 284 Upgrade.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 300 Engine.RegisterComponentType(IID_Upgrade, "Upgrade", Upgrade); -
binaries/data/mods/public/simulation/components/interfaces/Upgrade.js
1 Engine.RegisterInterface("Upgrade"); -
binaries/data/mods/public/simulation/helpers/Commands.js
578 578 } 579 579 }, 580 580 581 "wall-to-gate": function(player, cmd, data)582 {583 for (let ent of data.entities)584 TryTransformWallToGate(ent, data.cmpPlayer, cmd);585 },586 587 581 "lock-gate": function(player, cmd, data) 588 582 { 589 583 for (let ent of data.entities) … … 668 662 } 669 663 }, 670 664 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 = { 677 "players": [data.cmpPlayer.GetPlayerID()], 678 "message": "Cannot upgrade as distance requirements are not verified or terrain is obstructed." 679 }; 680 var cmpGUIInterface = Engine.QueryInterface(SYSTEM_ENTITY, IID_GuiInterface); 681 cmpGUIInterface.PushNotification(notification); 682 continue; 683 } 684 685 if (!CanGarrisonedChangeTemplate(ent, cmd.template)) 686 { 687 var notification = { "players": [data.cmpPlayer.GetPlayerID()], "message": "Cannot upgrade a garrisoned entity." }; 688 var cmpGUIInterface = Engine.QueryInterface(SYSTEM_ENTITY, IID_GuiInterface); 689 cmpGUIInterface.PushNotification(notification); 690 continue; 691 } 692 693 // Check entity limits 694 var cmpTemplateManager = Engine.QueryInterface(SYSTEM_ENTITY, IID_TemplateManager); 695 var template = cmpTemplateManager.GetTemplate(cmd.template); 696 var cmpEntityLimits = QueryPlayerIDInterface(player, IID_EntityLimits); 697 if ((template.TrainingRestrictions && !cmpEntityLimits.AllowedToTrain(template.TrainingRestrictions.Category, 1)) || 698 (template.BuildRestrictions && !cmpEntityLimits.AllowedToBuild(template.BuildRestrictions.Category))) 699 { 700 if (g_DebugCommands) 701 warn("Invalid command: build limits check failed for player " + player + ": " + uneval(cmd)); 702 continue; 703 } 704 705 var cmpTechnologyManager = QueryOwnerInterface(ent, IID_TechnologyManager); 706 if (cmpUpgrade.GetRequiredTechnology() && !cmpTechnologyManager.IsTechnologyResearched(cmpUpgrade.GetRequiredTechnology())) 707 { 708 if (g_DebugCommands) 709 warn("Invalid command: upgrading requires unresearched technology: " + uneval(cmd)); 710 continue; 711 } 712 713 cmpUpgrade.Upgrade(cmd.template, data.cmpPlayer); 714 } 715 }, 716 717 "cancel-upgrade": function(player, cmd, data) 718 { 719 for (let ent of data.entities) 720 { 721 let cmpUpgrade = Engine.QueryInterface(ent, IID_Upgrade); 722 if (cmpUpgrade) 723 cmpUpgrade.CancelUpgrade(data.cmpPlayer); 724 } 725 }, 671 726 "attack-request": function(player, cmd, data) 672 727 { 673 728 // Send a chat message to human players … … 1567 1622 return entities.filter(ent => CanControlUnitOrIsAlly(ent, player, controlAll)); 1568 1623 } 1569 1624 1570 /**1571 * Try to transform a wall to a gate1572 */1573 function TryTransformWallToGate(ent, cmpPlayer, cmd)1574 {1575 var cmpIdentity = Engine.QueryInterface(ent, IID_Identity);1576 if (!cmpIdentity)1577 return;1578 1579 if (!cmpIdentity.HasClass("LongWall"))1580 {1581 if (g_DebugCommands)1582 warn("Invalid command: invalid wall conversion to gate for player "+player+": "+uneval(cmd));1583 return;1584 }1585 1586 var gate = Engine.AddEntity(cmd.template);1587 1588 var cmpCost = Engine.QueryInterface(gate, IID_Cost);1589 if (!cmpPlayer.TrySubtractResources(cmpCost.GetResourceCosts()))1590 {1591 if (g_DebugCommands)1592 warn("Invalid command: convert gate cost check failed for player "+player+": "+uneval(cmd));1593 1594 Engine.DestroyEntity(gate);1595 return;1596 }1597 1598 ReplaceBuildingWith(ent, gate);1599 }1600 1601 /**1602 * Unconditionally replace a building with another one1603 */1604 function ReplaceBuildingWith(ent, building)1605 {1606 // Move the building to the right place1607 var cmpPosition = Engine.QueryInterface(ent, IID_Position);1608 var cmpBuildingPosition = Engine.QueryInterface(building, IID_Position);1609 var pos = cmpPosition.GetPosition2D();1610 cmpBuildingPosition.JumpTo(pos.x, pos.y);1611 var rot = cmpPosition.GetRotation();1612 cmpBuildingPosition.SetYRotation(rot.y);1613 cmpBuildingPosition.SetXZRotation(rot.x, rot.z);1614 1615 // Copy ownership1616 var cmpOwnership = Engine.QueryInterface(ent, IID_Ownership);1617 var cmpBuildingOwnership = Engine.QueryInterface(building, IID_Ownership);1618 cmpBuildingOwnership.SetOwner(cmpOwnership.GetOwner());1619 1620 // Copy control groups1621 var cmpObstruction = Engine.QueryInterface(ent, IID_Obstruction);1622 var cmpBuildingObstruction = Engine.QueryInterface(building, IID_Obstruction);1623 cmpBuildingObstruction.SetControlGroup(cmpObstruction.GetControlGroup());1624 cmpBuildingObstruction.SetControlGroup2(cmpObstruction.GetControlGroup2());1625 1626 // Copy health level from the old entity to the new1627 var cmpHealth = Engine.QueryInterface(ent, IID_Health);1628 var cmpBuildingHealth = Engine.QueryInterface(building, IID_Health);1629 var healthFraction = Math.max(0, Math.min(1, cmpHealth.GetHitpoints() / cmpHealth.GetMaxHitpoints()));1630 var buildingHitpoints = Math.round(cmpBuildingHealth.GetMaxHitpoints() * healthFraction);1631 cmpBuildingHealth.SetHitpoints(buildingHitpoints);1632 1633 PlaySound("constructed", building);1634 1635 Engine.PostMessage(ent, MT_ConstructionFinished,1636 { "entity": ent, "newentity": building });1637 Engine.BroadcastMessage(MT_EntityRenamed, { entity: ent, newentity: building });1638 1639 Engine.DestroyEntity(ent);1640 }1641 1642 1625 Engine.RegisterGlobal("GetFormationRequirements", GetFormationRequirements); 1643 1626 Engine.RegisterGlobal("CanMoveEntsIntoFormation", CanMoveEntsIntoFormation); 1644 1627 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 var 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 92 var 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 105 var 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 178 var 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 188 var 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 206 Engine.RegisterGlobal("ChangeEntityTemplate", ChangeEntityTemplate); 207 Engine.RegisterGlobal("CanGarrisonedChangeTemplate", CanGarrisonedChangeTemplate); 208 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>