Ticket #2706: UpgradeComponent.2.patch
File UpgradeComponent.2.patch, 45.0 KB (added by , 10 years ago) |
---|
-
binaries/data/mods/public/art/textures/ui/session/icons/upgrade.png
Cannot display: file marked as a binary type. svn:mime-type = application/octet-stream
-
binaries/data/mods/public/gui/session/input.js
Property changes on: binaries/data/mods/public/art/textures/ui/session/icons/upgrade.png ___________________________________________________________________ Added: svn:mime-type ## -0,0 +1 ## +application/octet-stream \ No newline at end of property
1614 1614 }); 1615 1615 } 1616 1616 1617 // Transform a wall to a gate1618 function transformWallToGate(template)1617 // Upgrade an entity to another 1618 function upgradeEntity(Template) 1619 1619 { 1620 1620 var selection = g_Selection.toList(); 1621 1621 Engine.PostNetworkCommand({ 1622 "type": "wall-to-gate", 1623 "entities": selection.filter( function(e) { return getWallGateTemplate(e) == template } ), 1624 "template": template, 1622 "type": "upgrade", 1623 "entities": selection, 1624 "template": Template, 1625 "queued": false 1625 1626 }); 1626 1627 } 1627 1628 1628 // Gets the gate form (if any) of a given long wall piece1629 function getWallGateTemplate(entity)1629 // Cancel upgrading entities 1630 function cancelUpgradeEntity() 1630 1631 { 1631 // TODO: find the gate template name in a better way 1632 var entState = GetEntityState(entity); 1633 var index; 1634 1635 if (entState && !entState.foundation && hasClass(entState, "LongWall") && (index = entState.template.indexOf("long")) >= 0) 1636 return entState.template.substr(0, index) + "gate"; 1637 return undefined; 1632 var selection = g_Selection.toList(); 1633 Engine.PostNetworkCommand({ 1634 "type": "cancel-upgrade", 1635 "entities": selection, 1636 "queued": false 1637 }); 1638 1638 } 1639 1639 1640 1640 // Set the camera to follow the given unit -
binaries/data/mods/public/gui/session/selection_panels.js
372 372 for (var i in selection) 373 373 { 374 374 var state = GetEntityState(selection[i]); 375 if ( hasClass(state, "LongWall") && !state.gate && !longWallTypes[state.template])375 if (state.gate && !gates.length) 376 376 { 377 var gateTemplate = getWallGateTemplate(state.id);378 if (gateTemplate)379 {380 var tooltipString = GetTemplateDataWithoutLocalization(state.template).gateConversionTooltip;381 if (!tooltipString)382 {383 warn(state.template + " is supposed to be convertable to a gate, but it's missing the GateConversionTooltip in the Identity template");384 tooltipString = "";385 }386 walls.push({387 "tooltip": translate(tooltipString),388 "template": gateTemplate,389 "callback": function (item) { transformWallToGate(item.template); }390 });391 }392 393 // We only need one entity per type.394 longWallTypes[state.template] = true;395 }396 else if (state.gate && !gates.length)397 {398 377 gates.push({ 399 378 "gate": state.gate, 400 379 "tooltip": translate("Lock Gate"), … … 424 403 }, 425 404 "setTooltip": function(data) 426 405 { 427 var tooltip = data.item.tooltip; 428 if (data.item.template) 429 { 430 data.template = GetTemplateData(data.item.template); 431 data.wallCount = data.selection.reduce(function (count, ent) { 432 var state = GetEntityState(ent); 433 if (hasClass(state, "LongWall") && !state.gate) 434 count++; 435 return count; 436 }, 0); 437 438 tooltip += "\n" + getEntityCostTooltip(data.template, data.wallCount); 439 440 441 data.neededResources = Engine.GuiInterfaceCall("GetNeededResources", multiplyEntityCosts(data.template, data.wallCount)); 442 if (data.neededResources) 443 tooltip += getNeededResourcesTooltip(data.neededResources); 444 } 445 data.button.tooltip = tooltip; 406 data.button.tooltip = data.item.tooltip; 446 407 }, 447 408 "setGraphics": function(data) 448 409 { 449 data.affordableMask.hidden == data.neededResources ? true : false;450 410 var gateIcon; 451 411 if (data.item.gate) 452 412 { 453 // If already a gate, show locking actions413 // Show locking actions 454 414 gateIcon = "icons/lock_" + GATE_ACTIONS[data.item.locked ? 0 : 1] + "ed.png"; 455 415 if (data.item.gate.locked === undefined) 456 416 data.guiSelection.hidden = false … … 457 417 else 458 418 data.guiSelection.hidden = data.item.gate.locked != data.item.locked; 459 419 } 460 else461 {462 // otherwise show gate upgrade icon463 var template = GetTemplateData(data.item.template);464 if (!template)465 return;466 gateIcon = data.template.icon ? "portraits/" + data.template.icon : "icons/gate_closed.png";467 data.guiSelection.hidden = true;468 }469 470 420 data.icon.sprite = "stretched:session/" + gateIcon; 471 421 }, 472 422 "setPosition": function(data) … … 541 491 }, 542 492 }; 543 493 494 // UPGRADE 495 g_SelectionPanels.Upgrade = { 496 "getMaxNumberOfItems": function() 497 { 498 return 24 - getNumberOfRightPanelButtons(); 499 }, 500 "getItems": function(unitEntState, selection) 501 { 502 // Interface becomes complicated with multiple units and this is meant per-entity, so prevent it if the selection has multiple units. 503 if (selection.length > 1) 504 return false; 505 var items = []; 506 507 if (!unitEntState.upgrade) 508 return false; 509 510 for each (var upgrade in unitEntState.upgrade.upgrades) 511 { 512 var item = {}; 513 item.entType = upgrade.entity; 514 item.template = GetTemplateData(upgrade.entity); 515 if (!item.template) // abort if no template 516 return false; 517 518 item.icon = upgrade.icon; 519 if (!item.icon) 520 item.icon = "portraits/" + item.template.icon; 521 522 if (upgrade.requiredTechnology !== null) 523 { 524 item.requiredTechnology = upgrade.requiredTechnology; 525 if (!item.requiredTechnology && item.template.requiredTechnology) 526 item.requiredTechnology = item.template.requiredTechnology 527 } 528 item.cost = upgrade.cost; 529 item.time = upgrade.time; 530 531 if (unitEntState.upgrade.progress === undefined) 532 { 533 item.upgrading = UPGRADING_NOT_STARTED; 534 item.tooltip = translate("Upgrade into a " + item.template.name.generic + (upgrade.tooltip? ".\n" + upgrade.tooltip : ".")); 535 item.callback = function(data) { upgradeEntity(data.entType); }; 536 } 537 else if (unitEntState.upgrade.template !== upgrade.entity) 538 { 539 item.upgrading = UPGRADING_CHOSEN_OTHER; 540 item.tooltip = translate("Cannot upgrade when the entity is already upgrading."); 541 item.callback = function(data) { }; 542 } 543 else 544 { 545 item.upgrading = unitEntState.upgrade.progress; 546 item.tooltip = translate("Cancel Upgrading"); 547 item.callback = function(data) { cancelUpgradeEntity(); }; 548 } 549 items.push(item); 550 } 551 return items; 552 }, 553 "addData" : function(data) 554 { 555 data.item.technologyEnabled = true; 556 if (data.item.requiredTechnology) 557 data.item.technologyEnabled = Engine.GuiInterfaceCall("IsTechnologyResearched", data.item.requiredTechnology); 558 if (data.item.cost) 559 { 560 var totalCost = {}; 561 for (var i in data.item.cost) 562 totalCost[i] = data.item.cost[i]; 563 data.item.neededResources = Engine.GuiInterfaceCall("GetNeededResources", totalCost); 564 } 565 data.item.limits = getEntityLimitAndCount(data.playerState, data.item.entType); 566 return true; 567 }, 568 "setAction": function(data) 569 { 570 data.button.onPress = function() { data.item.callback(data.item); }; 571 }, 572 "setTooltip": function(data) 573 { 574 var tooltip = data.item.tooltip; 575 576 if (data.item.upgrading !== UPGRADING_NOT_STARTED) 577 { 578 data.button.tooltip = tooltip; 579 return; 580 } 581 582 if (data.item.cost) 583 tooltip += "\n" + getEntityCostTooltip(data.item); 584 585 tooltip += formatLimitString(data.item.limits.entLimit, data.item.limits.entCount, data.item.limits.entLimitChangers); 586 if (!data.item.technologyEnabled) 587 { 588 var techName = getEntityNames(GetTechnologyData(data.item.requiredTechnology)); 589 tooltip += "\n" + sprintf(translate("Requires %(technology)s"), { technology: techName }); 590 } 591 if (data.item.neededResources) 592 tooltip += getNeededResourcesTooltip(data.item.neededResources); 593 594 data.button.tooltip = tooltip; 595 }, 596 "setGraphics": function(data) 597 { 598 var grayscale = ""; 599 if (data.item.upgrading == UPGRADING_CHOSEN_OTHER || !data.item.technologyEnabled || (data.item.limits.canBeAddedCount == 0 && data.item.upgrading == UPGRADING_NOT_STARTED)) 600 { 601 data.button.enabled = false; 602 grayscale = "grayscale:"; 603 data.affordableMask.hidden = false; 604 data.affordableMask.sprite = "colour: 0 0 0 127"; 605 } 606 else if (data.item.upgrading == UPGRADING_NOT_STARTED && data.item.neededResources) 607 { 608 data.button.enabled = false; 609 data.affordableMask.hidden = false; 610 data.affordableMask.sprite = resourcesToAlphaMask(data.item.neededResources); 611 } 612 613 data.icon.sprite = "stretched:" + grayscale + "session/" + data.item.icon; 614 615 var guiObject = Engine.GetGUIObjectByName("unitUpgradeProgressSlider["+data.i+"]"); 616 var size = guiObject.size; 617 if (data.item.upgrading < 0) 618 size.top = size.right; 619 else 620 size.top = size.left + Math.round(Math.max(0,data.item.upgrading) * (size.right - size.left)); 621 guiObject.size = size; 622 }, 623 "setPosition": function(data) 624 { 625 var index = data.i + getNumberOfRightPanelButtons(); 626 setPanelObjectPosition(data.button, index, data.rowLength); 627 }, 628 }; 629 544 630 // QUEUE 545 631 g_SelectionPanels.Queue = { 546 632 "getMaxNumberOfItems": function() … … 1015 1101 // RIGHT PANE 1016 1102 "Gate", // must always be shown on gates 1017 1103 "Pack", // must always be shown on packable entities 1104 "Upgrade", // must always be shown on upgradable entities 1018 1105 "Training", 1019 1106 "Construction", 1020 1107 "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/unit_commands.js
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, "Barter": 0, "Construction": 0, "Command": 0, "Stance": 0, "Gate": 0, "Pack": 0 };2 var g_unitPanelButtons = {"Selection": 0, "Queue": 0, "Formation": 0, "Garrison": 0, "Training": 0, "Research": 0, "Barter": 0, "Construction": 0, "Command": 0, "Stance": 0, "Gate": 0, "Pack": 0, "Upgrade": 0}; 3 3 4 4 /** 5 5 * Set the position of a panel object according to the index, … … 289 289 sum += g_unitPanelButtons["Pack"]; 290 290 if (g_SelectionPanels["Gate"].used) 291 291 sum += g_unitPanelButtons["Gate"]; 292 if (g_SelectionPanels["Upgrade"].used) 293 sum += g_unitPanelButtons["Upgrade"]; 292 294 return sum; 293 295 } -
binaries/data/mods/public/gui/session/selection_panels_right/gate_panel.xml
7 7 <object name="unitGateButton[n]" hidden="true" style="iconButton" type="button" size="0 0 46 46" tooltip_style="sessionToolTipBottom"> 8 8 <object name="unitGateIcon[n]" type="image" ghost="true" size="3 3 43 43"/> 9 9 <object name="unitGateSelection[n]" hidden="true" type="image" ghost="true" size="3 3 43 43" sprite="stretched:session/icons/corners.png"/> 10 <object name="unitGateUnaffordable[n]" hidden="true" type="image" ghost="true" size="3 3 43 43" sprite="colour: 255 0 0 127"/>11 10 </object> 12 11 </repeat> 13 12 </object> -
binaries/data/mods/public/gui/session/selection_panels_right/upgrade_panel.xml
1 <?xml version="1.0" encoding="utf-8"?> 2 <object name="unitUpgradePanel" 3 size="10 12 100% 100%" 4 > 5 <object size="0 0 100% 100%"> 6 <repeat count="8"> 7 <object name="unitUpgradeButton[n]" hidden="true" style="iconButton" type="button" size="0 0 46 46" tooltip_style="sessionToolTipBottom"> 8 <object name="unitUpgradeIcon[n]" type="image" ghost="true" size="3 3 43 43"/> 9 <object name="unitUpgradeUpgradeIcon[n]" type="image" ghost="true" size="3 3 43 43" sprite="stretched:session/icons/upgrade.png"/> 10 <object name="unitUpgradeProgressSlider[n]" type="image" sprite="queueProgressSlider" ghost="true" size="3 3 43 43" z="20"/> 11 <object name="unitUpgradeSelection[n]" hidden="true" type="image" ghost="true" size="3 3 43 43" sprite="stretched:session/icons/corners.png"/> 12 <object name="unitUpgradeUnaffordable[n]" hidden="true" type="image" ghost="true" size="3 3 43 43" sprite="colour: 255 0 0 127"/> 13 </object> 14 </repeat> 15 </object> 16 </object> -
binaries/data/mods/public/simulation/components/GuiInterface.js
Property changes on: binaries/data/mods/public/gui/session/selection_panels_right/upgrade_panel.xml ___________________________________________________________________ Added: svn:eol-style ## -0,0 +1 ## +native \ No newline at end of property
180 180 "gate": null, 181 181 "guard": null, 182 182 "pack": null, 183 "upgrade" : null, 183 184 "player": -1, 184 185 "position": null, 185 186 "production": null, … … 232 233 }; 233 234 } 234 235 236 var cmpUpgrade = Engine.QueryInterface(ent, IID_Upgrade); 237 if (cmpUpgrade) 238 { 239 ret.upgrade = { 240 "upgrades" : cmpUpgrade.GetUpgrades(), 241 "progress": cmpUpgrade.GetProgress(), 242 "template": cmpUpgrade.GetUpgradingTo() 243 }; 244 } 245 235 246 var cmpProductionQueue = Engine.QueryInterface(ent, IID_ProductionQueue); 236 247 if (cmpProductionQueue) 237 248 { … … 653 664 }; 654 665 ret.icon = template.Identity.Icon; 655 666 ret.tooltip = template.Identity.Tooltip; 656 ret.gateConversionTooltip = template.Identity.GateConversionTooltip;657 667 ret.requiredTechnology = template.Identity.RequiredTechnology; 658 668 ret.visibleIdentityClasses = GetVisibleIdentityClasses(template.Identity); 659 669 } -
binaries/data/mods/public/simulation/components/Identity.js
30 30 "</element>" + 31 31 "</optional>" + 32 32 "<optional>" + 33 "<element name='GateConversionTooltip'>" +34 "<text/>" +35 "</element>" +36 "</optional>" +37 "<optional>" +38 33 "<element name='Rollover'>" + 39 34 "<text/>" + 40 35 "</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.CancelTimer(); 121 this.SetElapsedTime(this.GetElapsedTime() + PACKING_INTERVAL + lateness); 122 return; 123 } 122 124 123 this.packed = !this.packed; 124 Engine.PostMessage(this.entity, MT_PackFinished, { packed: this.packed }); 125 this.CancelTimer(); 125 126 126 // Done un/packing, copy our parameters to the final entity 127 var newEntity = Engine.AddEntity(this.template.Entity); 128 if (newEntity == INVALID_ENTITY) 129 { 130 // Error (e.g. invalid template names) 131 error("PackProgress: Error creating entity for '" + this.template.Entity + "'"); 132 return; 133 } 127 this.packed = !this.packed; 128 Engine.PostMessage(this.entity, MT_PackFinished, { packed: this.packed }); 134 129 135 var cmpPosition = Engine.QueryInterface(this.entity, IID_Position); 136 var cmpNewPosition = Engine.QueryInterface(newEntity, IID_Position); 137 if (cmpPosition.IsInWorld()) 138 { 139 var pos = cmpPosition.GetPosition2D(); 140 cmpNewPosition.JumpTo(pos.x, pos.y); 141 } 142 var rot = cmpPosition.GetRotation(); 143 cmpNewPosition.SetYRotation(rot.y); 144 cmpNewPosition.SetXZRotation(rot.x, rot.z); 145 cmpNewPosition.SetHeightOffset(cmpPosition.GetHeightOffset()); 130 var newEntity = ChangeEntityTemplate(this.entity, this.template.Entity); 146 131 147 var cmpOwnership = Engine.QueryInterface(this.entity, IID_Ownership); 148 var cmpNewOwnership = Engine.QueryInterface(newEntity, IID_Ownership); 149 cmpNewOwnership.SetOwner(cmpOwnership.GetOwner()); 150 151 // Maintain current health level 152 var cmpHealth = Engine.QueryInterface(this.entity, IID_Health); 153 var cmpNewHealth = Engine.QueryInterface(newEntity, IID_Health); 154 var healthLevel = Math.max(0, Math.min(1, cmpHealth.GetHitpoints() / cmpHealth.GetMaxHitpoints())); 155 cmpNewHealth.SetHitpoints(Math.round(cmpNewHealth.GetMaxHitpoints() * healthLevel)); 156 157 var cmpUnitAI = Engine.QueryInterface(this.entity, IID_UnitAI); 158 var cmpNewUnitAI = Engine.QueryInterface(newEntity, IID_UnitAI); 159 if (cmpUnitAI && cmpNewUnitAI) 160 { 161 var pos = cmpUnitAI.GetHeldPosition(); 162 if (pos) 163 cmpNewUnitAI.SetHeldPosition(pos.x, pos.z); 164 if (cmpUnitAI.GetStanceName()) 165 cmpNewUnitAI.SwitchToStance(cmpUnitAI.GetStanceName()); 166 cmpNewUnitAI.AddOrders(cmpUnitAI.GetOrders()); 167 cmpNewUnitAI.SetGuardOf(cmpUnitAI.IsGuardOf()); 168 } 169 170 // Maintain the list of guards 171 var cmpGuard = Engine.QueryInterface(this.entity, IID_Guard); 172 var cmpNewGuard = Engine.QueryInterface(newEntity, IID_Guard); 173 if (cmpGuard && cmpNewGuard) 174 cmpNewGuard.SetEntities(cmpGuard.GetEntities()); 175 176 Engine.BroadcastMessage(MT_EntityRenamed, { entity: this.entity, newentity: newEntity }); 177 178 // Play notification sound 132 // Play notification sound 133 if (newEntity) 134 { 179 135 var sound = this.packed ? "packed" : "unpacked"; 180 136 PlaySound(sound, newEntity); 181 182 // Destroy current entity183 Engine.DestroyEntity(this.entity);184 137 } 185 else186 {187 this.SetElapsedTime(this.GetElapsedTime() + PACKING_INTERVAL + lateness);188 }189 138 }; 190 139 191 140 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 (var choice in this.template) 64 { 65 var cmpIdentity = Engine.QueryInterface(this.entity, IID_Identity); 66 var name = this.template[choice].Entity; 67 if (cmpIdentity) 68 name = name.replace(/\{civ\}/g, cmpIdentity.GetCiv()); 69 this.upgradeTemplates[name] = choice; 70 } 71 }; 72 73 // On owner change, abort the upgrade 74 // This will also deal with the "OnDestroy" case. 75 Upgrade.prototype.OnOwnershipChanged = function(msg) 76 { 77 this.CancelUpgrade(); 78 79 if (msg.to !== -1) 80 this.owner = msg.to; 81 }; 82 83 Upgrade.prototype.ChangeUpgradedEntityCount = function(amount) 84 { 85 if (!this.IsUpgrading()) 86 return; 87 var cmpTempMan = Engine.QueryInterface(SYSTEM_ENTITY, IID_TemplateManager); 88 var template = cmpTempMan.GetTemplate(this.upgrading); 89 var category = null; 90 if (template.TrainingRestrictions) 91 category = template.TrainingRestrictions.Category; 92 else if (template.BuildRestrictions) 93 category = template.BuildRestrictions.Category; 94 95 var cmpEntityLimits = QueryPlayerIDInterface(this.owner, IID_EntityLimits); 96 cmpEntityLimits.ChangeCount(category,amount); 97 }; 98 99 Upgrade.prototype.CanUpgradeTo = function(Template) 100 { 101 return this.upgradeTemplates[Template] !== undefined; 102 }; 103 104 Upgrade.prototype.GetUpgrades = function() 105 { 106 var ret = []; 107 108 var cmpIdentity = Engine.QueryInterface(this.entity, IID_Identity); 109 110 for each (var choice in this.template) 111 { 112 var entType = choice.Entity; 113 if (cmpIdentity) 114 entType = entType.replace(/\{civ\}/g, cmpIdentity.GetCiv()); 115 116 var hasCosts = false; 117 var cost = {}; 118 if (choice.Cost) 119 { 120 hasCosts = true; 121 for (var type in choice.Cost) 122 cost[type] = ApplyValueModificationsToTemplate("Upgrade/Cost/"+type, +choice.Cost[type], this.owner, entType); 123 } 124 if (choice.Time) 125 { 126 hasCosts = true; 127 cost["time"] = ApplyValueModificationsToTemplate("Upgrade/Time", +choice.Time/1000.0, this.owner, entType); 128 } 129 ret.push( 130 { 131 "entity": entType, 132 "icon": choice.Icon || undefined, 133 "cost": hasCosts ? cost : undefined, 134 "tooltip": choice.Tooltip || undefined, 135 "requiredTechnology": "RequiredTechnology" in choice ? choice.RequiredTechnology : null, 136 }); 137 } 138 139 return ret; 140 }; 141 142 Upgrade.prototype.CancelTimer = function() 143 { 144 if (this.timer) 145 { 146 var cmpTimer = Engine.QueryInterface(SYSTEM_ENTITY, IID_Timer); 147 cmpTimer.CancelTimer(this.timer); 148 this.timer = undefined; 149 } 150 }; 151 152 Upgrade.prototype.IsUpgrading = function() 153 { 154 return this.upgrading !== false; 155 }; 156 157 Upgrade.prototype.GetUpgradingTo = function() 158 { 159 return this.upgrading; 160 }; 161 162 Upgrade.prototype.CheckPlacementRestrictions = function(Template) 163 { 164 if (!this.upgradeTemplates[Template]) 165 return undefined; 166 167 return ("CheckPlacementRestrictions" in this.template[this.upgradeTemplates[Template]]); 168 }; 169 170 Upgrade.prototype.GetRequiredTechnology = function(Template) 171 { 172 if (!this.upgradeTemplates[Template]) 173 return undefined; 174 175 var choice = this.upgradeTemplates[Template]; 176 177 if ("RequiredTechnology" in this.template[choice] && this.template[choice].RequiredTechnology === undefined) 178 { 179 var cmpTemplateManager = Engine.QueryInterface(SYSTEM_ENTITY, IID_TemplateManager); 180 var template = cmpTemplateManager.GetTemplate(this.template[choice].Entity); 181 if (template.Identity.RequiredTechnology) 182 return template.Identity.RequiredTechnology; 183 } 184 else if ("RequiredTechnology" in this.template[choice]) 185 return this.template[choice].RequiredTechnology; 186 return null; 187 }; 188 189 Upgrade.prototype.GetResourceCosts = function(Template) 190 { 191 if (!this.upgradeTemplates[Template]) 192 return undefined; 193 194 var choice = this.upgradeTemplates[Template]; 195 if (!this.template[choice].Cost) 196 return {}; 197 198 var costs = {}; 199 for (var r in this.template[choice].Cost) 200 { 201 costs[r] = +this.template[choice].Cost[r]; 202 costs[r] = ApplyValueModificationsToEntity("Upgrade/Cost/"+r, costs[r], this.entity); 203 } 204 return costs; 205 }; 206 207 Upgrade.prototype.Upgrade = function(Template) 208 { 209 if (this.IsUpgrading()) 210 return false; 211 212 if (!this.upgradeTemplates[Template]) 213 return false; 214 215 var cmpPlayerManager = Engine.QueryInterface(SYSTEM_ENTITY, IID_PlayerManager); 216 var cmpPlayer = Engine.QueryInterface(cmpPlayerManager.GetPlayerByID(this.owner), IID_Player); 217 218 if (!cmpPlayer.TrySubtractResources(this.GetResourceCosts(Template))) 219 return false; 220 221 this.upgrading = Template; 222 223 // prevent cheating 224 this.ChangeUpgradedEntityCount(1); 225 226 if (this.GetUpgradeTime(Template) !== 0) 227 { 228 var cmpTimer = Engine.QueryInterface(SYSTEM_ENTITY, IID_Timer); 229 this.timer = cmpTimer.SetInterval(this.entity, IID_Upgrade, "UpgradeProgress", 0, UPGRADING_PROGRESS_INTERVAL, {"upgrading": Template}); 230 } 231 else 232 this.UpgradeProgress(); 233 234 return true; 235 }; 236 237 Upgrade.prototype.CancelUpgrade = function() 238 { 239 if (this.IsUpgrading() === false) 240 return; 241 242 var cmpPlayerManager = Engine.QueryInterface(SYSTEM_ENTITY, IID_PlayerManager); 243 var cmpPlayer = Engine.QueryInterface(cmpPlayerManager.GetPlayerByID(this.owner), IID_Player); 244 if (cmpPlayer) 245 { 246 var costs = this.GetResourceCosts(this.upgrading); 247 cmpPlayer.AddResources(costs); 248 } 249 250 this.ChangeUpgradedEntityCount(-1); 251 252 this.upgrading = false; 253 this.CancelTimer(); 254 this.SetElapsedTime(0); 255 }; 256 257 Upgrade.prototype.GetUpgradeTime = function(Template) 258 { 259 var template = this.upgrading || Template; 260 var choice = this.upgradeTemplates[template]; 261 if (!choice) 262 return undefined; 263 return this.template[choice].Time ? ApplyValueModificationsToEntity("Upgrade/Time", +this.template[choice].Time, this.entity) : 0; 264 }; 265 266 Upgrade.prototype.GetElapsedTime = function() 267 { 268 return this.elapsedTime; 269 }; 270 271 Upgrade.prototype.GetProgress = function() 272 { 273 // Hopefully undefined and 0 are the only thing that would trigger that. 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 // Play notification sound 297 if (newEntity) 298 PlaySound("upgraded", newEntity); 299 }; 300 301 Engine.RegisterComponentType(IID_Upgrade, "Upgrade", Upgrade); -
binaries/data/mods/public/simulation/components/interfaces/Upgrade.js
Property changes on: binaries/data/mods/public/simulation/components/Upgrade.js ___________________________________________________________________ Added: svn:eol-style ## -0,0 +1 ## +native \ No newline at end of property
1 Engine.RegisterInterface("Upgrade"); -
binaries/data/mods/public/simulation/helpers/Commands.js
Property changes on: binaries/data/mods/public/simulation/components/interfaces/Upgrade.js ___________________________________________________________________ Added: svn:eol-style ## -0,0 +1 ## +native \ No newline at end of property
551 551 } 552 552 }, 553 553 554 "wall-to-gate": function(player, cmd, data)555 {556 for each (var ent in data.entities)557 {558 TryTransformWallToGate(ent, data.cmpPlayer, cmd.template);559 }560 },561 562 554 "lock-gate": function(player, cmd, data) 563 555 { 564 556 for each (var ent in data.entities) … … 642 634 } 643 635 } 644 636 }, 637 638 "upgrade": function(player, cmd, data) 639 { 640 for each (var ent in data.entities) 641 { 642 var cmpUpgrade = Engine.QueryInterface(ent, IID_Upgrade); 643 644 var templateName = cmd.template; 645 646 if (!cmpUpgrade || !cmpUpgrade.CanUpgradeTo(templateName)) 647 continue; 648 649 if (cmpUpgrade.CheckPlacementRestrictions(templateName) && !CheckObstructionsForTemplateChange(ent, templateName)) 650 { 651 var notification = {"players": [data.cmpPlayer.GetPlayerID()], "message": "Cannot upgrade as distance requirements are not verified or terrain is obstructed." }; 652 var cmpGUIInterface = Engine.QueryInterface(SYSTEM_ENTITY, IID_GuiInterface); 653 cmpGUIInterface.PushNotification(notification); 654 continue; 655 } 656 657 if (!CheckGarrisonedCanChangeTemplate(ent, templateName)) 658 { 659 var notification = {"players": [data.cmpPlayer.GetPlayerID()], "message": "Cannot upgrade a garrisoned entity." }; 660 var cmpGUIInterface = Engine.QueryInterface(SYSTEM_ENTITY, IID_GuiInterface); 661 cmpGUIInterface.PushNotification(notification); 662 continue; 663 } 664 665 // Check entity limits 666 var cmpTempMan = Engine.QueryInterface(SYSTEM_ENTITY, IID_TemplateManager); 667 var template = cmpTempMan.GetTemplate(templateName); 668 var cmpEntityLimits = QueryPlayerIDInterface(player, IID_EntityLimits); 669 if ((template.TrainingRestrictions && !cmpEntityLimits.AllowedToTrain(template.TrainingRestrictions.Category,1)) 670 || (template.BuildRestrictions && !cmpEntityLimits.AllowedToBuild(template.BuildRestrictions.Category))) 671 { 672 if (g_DebugCommands) 673 warn("Invalid command: build limits check failed for player "+player+": "+uneval(cmd)); 674 continue; 675 } 676 677 var cmpTechnologyManager = QueryOwnerInterface(ent, IID_TechnologyManager); 678 if (cmpUpgrade.GetRequiredTechnology() && !cmpTechnologyManager.IsTechnologyResearched(cmpUpgrade.GetRequiredTechnology())) 679 { 680 if (g_DebugCommands) 681 warn("Invalid command: upgrading requires unresearched technology: " + uneval(cmd)); 682 continue; 683 } 684 685 cmpUpgrade.Upgrade(templateName, data.cmpPlayer); 686 } 687 }, 688 689 "cancel-upgrade": function(player, cmd, data) 690 { 691 for each (var ent in data.entities) 692 { 693 var cmpUpgrade = Engine.QueryInterface(ent, IID_Upgrade); 694 695 if (!cmpUpgrade) 696 continue 697 698 cmpUpgrade.CancelUpgrade(data.cmpPlayer); 699 } 700 }, 701 645 702 "dialog-answer": function(player, cmd, data) 646 703 { 647 704 // Currently nothing. Triggers can read it anyway, and send this … … 1512 1569 return entities.filter(function(ent) { return CanControlUnitOrIsAlly(ent, player, controlAll);} ); 1513 1570 } 1514 1571 1515 /**1516 * Try to transform a wall to a gate1517 */1518 function TryTransformWallToGate(ent, cmpPlayer, template)1519 {1520 var cmpIdentity = Engine.QueryInterface(ent, IID_Identity);1521 if (!cmpIdentity)1522 return;1523 1524 // Check if this is a valid long wall segment1525 if (!cmpIdentity.HasClass("LongWall"))1526 {1527 if (g_DebugCommands)1528 warn("Invalid command: invalid wall conversion to gate for player "+player+": "+uneval(cmd));1529 return;1530 }1531 1532 var civ = cmpIdentity.GetCiv();1533 var gate = Engine.AddEntity(template);1534 1535 var cmpCost = Engine.QueryInterface(gate, IID_Cost);1536 if (!cmpPlayer.TrySubtractResources(cmpCost.GetResourceCosts()))1537 {1538 if (g_DebugCommands)1539 warn("Invalid command: convert gate cost check failed for player "+player+": "+uneval(cmd));1540 1541 Engine.DestroyEntity(gate);1542 return;1543 }1544 1545 ReplaceBuildingWith(ent, gate);1546 }1547 1548 /**1549 * Unconditionally replace a building with another one1550 */1551 function ReplaceBuildingWith(ent, building)1552 {1553 // Move the building to the right place1554 var cmpPosition = Engine.QueryInterface(ent, IID_Position);1555 var cmpBuildingPosition = Engine.QueryInterface(building, IID_Position);1556 var pos = cmpPosition.GetPosition2D();1557 cmpBuildingPosition.JumpTo(pos.x, pos.y);1558 var rot = cmpPosition.GetRotation();1559 cmpBuildingPosition.SetYRotation(rot.y);1560 cmpBuildingPosition.SetXZRotation(rot.x, rot.z);1561 1562 // Copy ownership1563 var cmpOwnership = Engine.QueryInterface(ent, IID_Ownership);1564 var cmpBuildingOwnership = Engine.QueryInterface(building, IID_Ownership);1565 cmpBuildingOwnership.SetOwner(cmpOwnership.GetOwner());1566 1567 // Copy control groups1568 var cmpObstruction = Engine.QueryInterface(ent, IID_Obstruction);1569 var cmpBuildingObstruction = Engine.QueryInterface(building, IID_Obstruction);1570 cmpBuildingObstruction.SetControlGroup(cmpObstruction.GetControlGroup());1571 cmpBuildingObstruction.SetControlGroup2(cmpObstruction.GetControlGroup2());1572 1573 // Copy health level from the old entity to the new1574 var cmpHealth = Engine.QueryInterface(ent, IID_Health);1575 var cmpBuildingHealth = Engine.QueryInterface(building, IID_Health);1576 var healthFraction = Math.max(0, Math.min(1, cmpHealth.GetHitpoints() / cmpHealth.GetMaxHitpoints()));1577 var buildingHitpoints = Math.round(cmpBuildingHealth.GetMaxHitpoints() * healthFraction);1578 cmpBuildingHealth.SetHitpoints(buildingHitpoints);1579 1580 PlaySound("constructed", building);1581 1582 Engine.PostMessage(ent, MT_ConstructionFinished,1583 { "entity": ent, "newentity": building });1584 Engine.BroadcastMessage(MT_EntityRenamed, { entity: ent, newentity: building });1585 1586 Engine.DestroyEntity(ent);1587 }1588 1589 1572 Engine.RegisterGlobal("GetFormationRequirements", GetFormationRequirements); 1590 1573 Engine.RegisterGlobal("CanMoveEntsIntoFormation", CanMoveEntsIntoFormation); 1591 1574 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 false. 4 var ChangeEntityTemplate = function(oldEntityID, newTemplate) 5 { 6 // Done un/packing, copy our parameters to the final entity 7 var newEntity = Engine.AddEntity(newTemplate); 8 if (newEntity == INVALID_ENTITY) 9 { 10 // Error (e.g. invalid template names) 11 error("Transform.js: Error replacing entity " + oldEntity + " for a '" + newTemplate + "'"); 12 return false; 13 } 14 15 var cmpPosition = Engine.QueryInterface(oldEntityID, IID_Position); 16 var cmpNewPosition = Engine.QueryInterface(newEntity, 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(oldEntityID, IID_Ownership); 31 var cmpNewOwnership = Engine.QueryInterface(newEntity, IID_Ownership); 32 if (cmpOwnership && cmpNewOwnership) 33 cmpNewOwnership.SetOwner(cmpOwnership.GetOwner()); 34 35 // Copy control groups 36 var cmpObstruction = Engine.QueryInterface(oldEntityID, IID_Obstruction); 37 var cmpNewObstruction = Engine.QueryInterface(newEntity, IID_Obstruction); 38 if (cmpObstruction && cmpNewObstruction) 39 { 40 cmpNewObstruction.SetControlGroup(cmpObstruction.GetControlGroup()); 41 cmpNewObstruction.SetControlGroup2(cmpObstruction.GetControlGroup2()); 42 } 43 44 // Maintain current health level 45 var cmpHealth = Engine.QueryInterface(oldEntityID, IID_Health); 46 var cmpNewHealth = Engine.QueryInterface(newEntity, IID_Health); 47 if (cmpHealth && cmpNewHealth) 48 { 49 var healthLevel = Math.max(0, Math.min(1, cmpHealth.GetHitpoints() / cmpHealth.GetMaxHitpoints())); 50 cmpNewHealth.SetHitpoints(Math.round(cmpNewHealth.GetMaxHitpoints() * healthLevel)); 51 } 52 53 var cmpUnitAI = Engine.QueryInterface(oldEntityID, IID_UnitAI); 54 var cmpNewUnitAI = Engine.QueryInterface(newEntity, IID_UnitAI); 55 if (cmpUnitAI && cmpNewUnitAI) 56 { 57 var pos = cmpUnitAI.GetHeldPosition(); 58 if (pos) 59 cmpNewUnitAI.SetHeldPosition(pos.x, pos.z); 60 if (cmpUnitAI.GetStanceName()) 61 cmpNewUnitAI.SwitchToStance(cmpUnitAI.GetStanceName()); 62 cmpNewUnitAI.AddOrders(cmpUnitAI.GetOrders()); 63 cmpNewUnitAI.SetGuardOf(cmpUnitAI.IsGuardOf()); 64 } 65 66 // Maintain the list of guards 67 var cmpGuard = Engine.QueryInterface(oldEntityID, IID_Guard); 68 var cmpNewGuard = Engine.QueryInterface(newEntity, IID_Guard); 69 if (cmpGuard && cmpNewGuard) 70 cmpNewGuard.SetEntities(cmpGuard.GetEntities()); 71 72 TransferGarrisonedUnits(oldEntityID, newEntity); 73 74 Engine.BroadcastMessage(MT_EntityRenamed, { entity: oldEntityID, newentity: newEntity }); 75 76 // Destroy current entity 77 Engine.DestroyEntity(oldEntityID); 78 79 return newEntity; 80 }; 81 82 var CheckGarrisonedCanChangeTemplate = function(entityID, Template) 83 { 84 var cmpPosition = Engine.QueryInterface(entityID, IID_Position); 85 var unitAI = Engine.QueryInterface(entityID, IID_UnitAI); 86 if (cmpPosition && !cmpPosition.IsInWorld() && unitAI && unitAI.IsGarrisoned()) 87 { 88 // We're a garrisoned unit, assume impossibility as I've been unable to find a way to get the holder ID. 89 return false; 90 } 91 return true; 92 } 93 94 var CheckObstructionsForTemplateChange = function(entityID, Template) 95 { 96 var previewEntity = Engine.AddEntity("preview|"+Template); 97 98 if (previewEntity == INVALID_ENTITY) 99 return false; // no need to destroy since it was invalid in the first place. 100 101 var cmpBuildRestrictions = Engine.QueryInterface(previewEntity, IID_BuildRestrictions); 102 var cmpPosition = Engine.QueryInterface(entityID, IID_Position); 103 var cmpOwnership = Engine.QueryInterface(entityID, IID_Ownership); 104 105 if (!cmpBuildRestrictions || !cmpPosition || !cmpOwnership) 106 { 107 Engine.DestroyEntity(previewEntity); 108 return true; 109 } 110 111 var canTransform = true; 112 113 var pos = cmpPosition.GetPosition2D(); 114 var angle = cmpPosition.GetRotation(); 115 // move us away to avoid our obstruction blocking the upgrade. 116 cmpPosition.MoveOutOfWorld(); 117 118 var cmpNewPosition = Engine.QueryInterface(previewEntity, IID_Position); 119 cmpNewPosition.JumpTo(pos.x, pos.y); 120 cmpNewPosition.SetYRotation(angle.y); 121 122 var cmpNewOwnership = Engine.QueryInterface(previewEntity, IID_Ownership); 123 cmpNewOwnership.SetOwner(cmpOwnership.GetOwner()); 124 125 var checkPlacement = cmpBuildRestrictions.CheckPlacement(); 126 if (checkPlacement && !checkPlacement.success) 127 canTransform = false; 128 129 var cmpTempMan = Engine.QueryInterface(SYSTEM_ENTITY, IID_TemplateManager); 130 var template = cmpTempMan.GetTemplate(cmpTempMan.GetCurrentTemplateName()); 131 var newTemplate = cmpTempMan.GetTemplate(Template); 132 133 var obstruction = template.Obstruction; 134 135 // Check if units are blocking our template change 136 if (canTransform && template.Obstruction && newTemplate.Obstruction) 137 { 138 // This only needs to be done if the new template is strictly bigger than the old one 139 // "Obstructions" are annoying to test so just check. 140 if (newTemplate.Obstruction.Obstructions 141 || (template.Obstruction.Static && template.Obstruction.Static.width > newTemplate.Obstruction.Static.width 142 && template.Obstruction.Static.depth > newTemplate.Obstruction.Static.depth) 143 || (template.Obstruction.Unit && template.Obstruction.Unit.radius > newTemplate.Obstruction.Unit.radius)) 144 { 145 var cmpNewObstruction = Engine.QueryInterface(previewEntity, IID_Obstruction); 146 if (cmpNewObstruction && cmpNewObstruction.GetBlockMovementFlag()) 147 { 148 // Check for units 149 var collisions = cmpNewObstruction.GetEntityCollisions(false, true); 150 if (collisions.length !== 0) 151 canTransform = false; 152 } 153 } 154 } 155 156 // Move preview entity out of world so it won't interfere with additional calls 157 // to this function for entityID on the same sim update. 158 // Specifically, CheckPlacement() would incorrectly spot and fail on earlier preview entities 159 // if those are left in the world. 160 cmpNewPosition.MoveOutOfWorld(); 161 cmpPosition.JumpTo(pos.x, pos.y); 162 cmpPosition.SetYRotation(angle.y); 163 164 Engine.DestroyEntity(previewEntity); 165 return canTransform; 166 }; 167 168 var TransferGarrisonedUnits = function(oldEntityID, newEntityID) 169 { 170 // Transfer garrisoned units if possible, or unload them 171 var cmpGarrison = Engine.QueryInterface(oldEntityID, IID_GarrisonHolder); 172 var cmpNewGarrison = Engine.QueryInterface(newEntityID, IID_GarrisonHolder); 173 if (!cmpNewGarrison) 174 return; // nothing to do as the code will by default unload all. 175 176 if (cmpGarrison && cmpGarrison.GetEntities().length > 0) 177 { 178 var garrisonedEntities = cmpGarrison.GetEntities().slice(); 179 cmpGarrison.UnloadAll(true); 180 for (var j = 0; j < garrisonedEntities.length; ++j) 181 { 182 var cmpUnitAI = Engine.QueryInterface(garrisonedEntities[j], IID_UnitAI); 183 cmpUnitAI.Autogarrison(newEntityID); 184 cmpNewGarrison.Garrison(garrisonedEntities[j]); 185 } 186 } 187 } 188 189 Engine.RegisterGlobal("ChangeEntityTemplate", ChangeEntityTemplate); 190 Engine.RegisterGlobal("CheckGarrisonedCanChangeTemplate", CheckGarrisonedCanChangeTemplate); 191 Engine.RegisterGlobal("CheckObstructionsForTemplateChange", CheckObstructionsForTemplateChange); 192 No newline at end of file -
binaries/data/mods/public/simulation/templates/template_structure_defense_wall_long.xml
Property changes on: binaries/data/mods/public/simulation/helpers/Transform.js ___________________________________________________________________ Added: svn:eol-style ## -0,0 +1 ## +native \ No newline at end of property
51 51 <Identity> 52 52 <Classes datatype="tokens">LongWall</Classes> 53 53 <Tooltip>Long wall segments can be converted to gates.</Tooltip> 54 <GateConversionTooltip>Convert Stone Wall into City Gate</GateConversionTooltip>55 54 </Identity> 55 <Upgrade> 56 <Gate> 57 <Entity>structures/{civ}_wall_gate</Entity> 58 <Tooltip>This will allow you to let units circulate through your fortifications.</Tooltip> 59 <Cost> 60 <stone>60</stone> 61 </Cost> 62 <Time>10000</Time> 63 </Gate> 64 </Upgrade> 56 65 </Entity> -
binaries/data/mods/public/simulation/templates/other/palisades_rocks_long.xml
21 21 <SelectionGroupName>other/wallset_palisade</SelectionGroupName> 22 22 <SpecificName>Palisade</SpecificName> 23 23 <GenericName>Wooden Wall</GenericName> 24 <GateConversionTooltip>Convert Wooden Wall into Wooden Gate</GateConversionTooltip>25 24 <Classes datatype="tokens">-StoneWall Palisade</Classes> 26 25 <History>A cheap, quick defensive structure constructed with sharpened tree trunks</History> 27 26 <Icon>gaia/special_palisade.png</Icon> … … 41 40 <WallPiece> 42 41 <Length>11.0</Length> 43 42 </WallPiece> 43 <Upgrade> 44 <Gate> 45 <Entity>other/palisades_rocks_gate</Entity> 46 <Cost> 47 <stone>0</stone> 48 <wood>20</wood> 49 </Cost> 50 <Time>5000</Time> 51 </Gate> 52 </Upgrade> 44 53 </Entity> -
binaries/data/mods/public/simulation/templates/structures/rome_siege_wall_long.xml
55 55 </Classes> 56 56 <Icon>structures/palisade.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>