Ticket #2706: UpgradeComponent.9.patch
File UpgradeComponent.9.patch, 49.6 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
503 503 504 504 // GATE 505 505 g_SelectionPanels.Gate = { 506 "getMaxNumberOfItems": function() 507 { 508 return 24 - getNumberOfRightPanelButtons(); 509 }, 510 "getItems": function(unitEntState, selection) 511 { 512 // Allow long wall pieces to be converted to gates 513 var longWallTypes = {}; 514 var walls = []; 515 var gates = []; 516 for (var ent of selection) 517 { 518 var state = GetEntityState(ent); 519 if (hasClass(state, "LongWall") && !state.gate && !longWallTypes[state.template]) 520 { 521 var gateTemplate = getWallGateTemplate(state.id); 522 if (gateTemplate) 523 { 524 var tooltipString = GetTemplateDataWithoutLocalization(state.template).gateConversionTooltip; 525 if (!tooltipString) 526 { 527 warn(state.template + " is supposed to be convertable to a gate, but it's missing the GateConversionTooltip in the Identity template"); 528 tooltipString = ""; 529 } 530 walls.push({ 531 "tooltip": translate(tooltipString), 532 "template": gateTemplate, 533 "callback": function (item) { transformWallToGate(item.template); } 534 }); 535 } 506 "getMaxNumberOfItems": function() 507 { 508 return 24 - getNumberOfRightPanelButtons(); 509 }, 510 "getItems": function(unitEntState, selection) 511 { 512 // Allow long wall pieces to be converted to gates 513 var longWallTypes = {}; 514 var walls = []; 515 var gates = []; 516 for (var ent of selection) 517 { 518 var state = GetEntityState(ent); 519 if (state.gate && !gates.length) 520 { 521 gates.push({ 522 "gate": state.gate, 523 "tooltip": translate("Lock Gate"), 524 "locked": true, 525 "callback": function (item) { lockGate(item.locked); } 526 }); 527 gates.push({ 528 "gate": state.gate, 529 "tooltip": translate("Unlock Gate"), 530 "locked": false, 531 "callback": function (item) { lockGate(item.locked); } 532 }); 533 } 534 // Show both 'locked' and 'unlocked' as active if the selected gates have both lock states. 535 else if (state.gate && state.gate.locked != gates[0].gate.locked) 536 for (var j = 0; j < gates.length; ++j) 537 delete gates[j].gate.locked; 538 } 536 539 537 // We only need one entity per type. 538 longWallTypes[state.template] = true; 539 } 540 else if (state.gate && !gates.length) 541 { 542 gates.push({ 543 "gate": state.gate, 544 "tooltip": translate("Lock Gate"), 545 "locked": true, 546 "callback": function (item) { lockGate(item.locked); } 547 }); 548 gates.push({ 549 "gate": state.gate, 550 "tooltip": translate("Unlock Gate"), 551 "locked": false, 552 "callback": function (item) { lockGate(item.locked); } 553 }); 554 } 555 // Show both 'locked' and 'unlocked' as active if the selected gates have both lock states. 556 else if (state.gate && state.gate.locked != gates[0].gate.locked) 557 for (var j = 0; j < gates.length; ++j) 558 delete gates[j].gate.locked; 559 } 540 // Place wall conversion options after gate lock/unlock icons. 541 var items = gates.concat(walls); 542 return items; 543 }, 544 "setAction": function(data) 545 { 546 data.button.onPress = function() {data.item.callback(data.item); }; 547 }, 548 "setTooltip": function(data) 549 { 550 data.button.tooltip = data.item.tooltip; 551 }, 552 "setGraphics": function(data) 553 { 554 data.button.enabled = controlsPlayer(data.unitEntState.player); 555 var gateIcon; 556 if (data.item.gate) 557 { 558 // If already a gate, show locking actions 559 gateIcon = "icons/lock_" + GATE_ACTIONS[data.item.locked ? 0 : 1] + "ed.png"; 560 if (data.item.gate.locked === undefined) 561 data.guiSelection.hidden = false; 562 else 563 data.guiSelection.hidden = data.item.gate.locked != data.item.locked; 564 } 560 565 561 // Place wall conversion options after gate lock/unlock icons. 562 var items = gates.concat(walls); 563 return items; 564 }, 565 "setAction": function(data) 566 { 567 data.button.onPress = function() {data.item.callback(data.item); }; 568 }, 569 "setTooltip": function(data) 570 { 571 var tooltip = data.item.tooltip; 572 if (data.item.template) 573 { 574 data.template = GetTemplateData(data.item.template); 575 data.wallCount = data.selection.reduce(function (count, ent) { 576 var state = GetEntityState(ent); 577 if (hasClass(state, "LongWall") && !state.gate) 578 ++count; 579 return count; 580 }, 0); 566 data.icon.sprite = (data.neededResources ? resourcesToAlphaMask(data.neededResources) + ":" : "") + "stretched:session/" + gateIcon; 567 }, 568 "setPosition": function(data) 569 { 570 var index = data.i + getNumberOfRightPanelButtons(); 571 setPanelObjectPosition(data.button, index, data.rowLength); 572 } 573 }; 581 574 582 tooltip += "\n" + getEntityCostTooltip(data.template, data.wallCount);583 575 584 data.neededResources = Engine.GuiInterfaceCall("GetNeededResources", {585 "cost": multiplyEntityCosts(data.template, data.wallCount)586 });587 588 if (data.neededResources)589 tooltip += getNeededResourcesTooltip(data.neededResources);590 }591 data.button.tooltip = tooltip;592 },593 "setGraphics": function(data)594 {595 data.button.enabled = controlsPlayer(data.unitEntState.player);596 var gateIcon;597 if (data.item.gate)598 {599 // If already a gate, show locking actions600 gateIcon = "icons/lock_" + GATE_ACTIONS[data.item.locked ? 0 : 1] + "ed.png";601 if (data.item.gate.locked === undefined)602 data.guiSelection.hidden = false;603 else604 data.guiSelection.hidden = data.item.gate.locked != data.item.locked;605 }606 else607 {608 // otherwise show gate upgrade icon609 var template = GetTemplateData(data.item.template);610 if (!template)611 return;612 gateIcon = data.template.icon ? "portraits/" + data.template.icon : "icons/gate_closed.png";613 data.guiSelection.hidden = true;614 }615 616 data.icon.sprite = (data.neededResources ? resourcesToAlphaMask(data.neededResources) + ":" : "") + "stretched:session/" + gateIcon;617 },618 "setPosition": function(data)619 {620 var index = data.i + getNumberOfRightPanelButtons();621 setPanelObjectPosition(data.button, index, data.rowLength);622 }623 };624 625 576 // PACK 626 577 g_SelectionPanels.Pack = { 627 578 "getMaxNumberOfItems": function() … … 654 605 } 655 606 var items = []; 656 607 if (checks.packButton) 657 items.push({ "packing": false, "packed": false, "tooltip": translate("Pack"), "callback": function() { packUnit(true); } }); 608 items.push({ 609 "packing": false, 610 "packed": false, 611 "tooltip": translate("Pack"), 612 "callback": function() { packUnit(true); } 613 }); 658 614 if (checks.unpackButton) 659 items.push({ "packing": false, "packed": true, "tooltip": translate("Unpack"), "callback": function() { packUnit(false); } }); 615 items.push({ 616 "packing": false, 617 "packed": true, 618 "tooltip": translate("Unpack"), 619 "callback": function() { packUnit(false); } 620 }); 660 621 if (checks.packCancelButton) 661 items.push({ "packing": true, "packed": false, "tooltip": translate("Cancel Packing"), "callback": function() { cancelPackUnit(true); } }); 622 items.push({ 623 "packing": true, 624 "packed": false, 625 "tooltip": translate("Cancel Packing"), 626 "callback": function() { cancelPackUnit(true); } 627 }); 662 628 if (checks.unpackCancelButton) 663 items.push({ "packing": true, "packed": true, "tooltip": translate("Cancel Unpacking"), "callback": function() { cancelPackUnit(false); } }); 629 items.push({ 630 "packing": true, 631 "packed": true, 632 "tooltip": translate("Cancel Unpacking"), 633 "callback": function() { cancelPackUnit(false); } 634 }); 664 635 return items; 665 636 }, 666 637 "setAction": function(data) … … 689 660 } 690 661 }; 691 662 663 // UPGRADE 664 g_SelectionPanels.Upgrade = { 665 "getMaxNumberOfItems": function() 666 { 667 return 24 - getNumberOfRightPanelButtons(); 668 }, 669 "getItems": function(unitEntState, selection) 670 { 671 // Interface becomes complicated with multiple units and this is meant per-entity, so prevent it if the selection has multiple units. 672 // TODO: if the units are all the same, this should probably still be possible. 673 if (selection.length > 1) 674 return false; 675 676 if (!unitEntState.upgrade) 677 return false; 678 679 var items = []; 680 681 for (let upgrade of unitEntState.upgrade.upgrades) 682 { 683 var item = {}; 684 item.entType = upgrade.entity; 685 item.template = GetTemplateData(upgrade.entity); 686 if (!item.template) 687 return false; 688 689 item.icon = upgrade.icon; 690 if (!item.icon) 691 item.icon = "portraits/" + item.template.icon; 692 693 if (upgrade.requiredTechnology !== null) 694 { 695 item.requiredTechnology = upgrade.requiredTechnology; 696 if (!item.requiredTechnology && item.template.requiredTechnology) 697 item.requiredTechnology = item.template.requiredTechnology 698 } 699 item.cost = upgrade.cost; 700 item.time = upgrade.time; 701 702 if (unitEntState.upgrade.progress === undefined) 703 { 704 item.upgrading = UPGRADING_NOT_STARTED; 705 item.tooltip = sprintf(translate("Upgrade into a %(name)s.%(tooltip)s"), { name: item.template.name.generic, tooltip: (upgrade.tooltip? "\n" + upgrade.tooltip : "") }); 706 item.callback = function(data) { upgradeEntity(data.entType); }; 707 } 708 else if (unitEntState.upgrade.template !== upgrade.entity) 709 { 710 item.upgrading = UPGRADING_CHOSEN_OTHER; 711 item.tooltip = translate("Cannot upgrade when the entity is already upgrading."); 712 item.callback = function(data) { }; 713 } 714 else 715 { 716 item.upgrading = unitEntState.upgrade.progress; 717 item.tooltip = translate("Cancel Upgrading"); 718 item.callback = function(data) { cancelUpgradeEntity(); }; 719 } 720 items.push(item); 721 } 722 return items; 723 }, 724 "addData" : function(data) 725 { 726 data.item.technologyEnabled = true; 727 if (data.item.requiredTechnology) 728 data.item.technologyEnabled = Engine.GuiInterfaceCall("IsTechnologyResearched", data.item.requiredTechnology); 729 if (data.item.cost) 730 { 731 var totalCost = {}; 732 for (let i in data.item.cost) 733 totalCost[i] = data.item.cost[i]; 734 data.item.neededResources = Engine.GuiInterfaceCall("GetNeededResources", { "cost": totalCost }); 735 } 736 data.item.limits = getEntityLimitAndCount(data.playerState, data.item.entType); 737 return true; 738 }, 739 "setAction": function(data) 740 { 741 data.button.onPress = function() { data.item.callback(data.item); }; 742 }, 743 "setTooltip": function(data) 744 { 745 var tooltip = data.item.tooltip; 746 747 if (data.item.upgrading !== UPGRADING_NOT_STARTED) 748 { 749 data.button.tooltip = tooltip; 750 return; 751 } 752 753 if (data.item.cost) 754 tooltip += "\n" + getEntityCostTooltip(data.item); 755 756 tooltip += formatLimitString(data.item.limits.entLimit, data.item.limits.entCount, data.item.limits.entLimitChangers); 757 if (!data.item.technologyEnabled) 758 { 759 var techName = getEntityNames(GetTechnologyData(data.item.requiredTechnology)); 760 tooltip += "\n" + sprintf(translate("Requires %(technology)s"), { "technology": techName }); 761 } 762 if (data.item.neededResources) 763 tooltip += getNeededResourcesTooltip(data.item.neededResources); 764 765 data.button.tooltip = tooltip; 766 }, 767 "setGraphics": function(data) 768 { 769 var grayscale = ""; 770 if (data.item.upgrading == UPGRADING_CHOSEN_OTHER || 771 !data.item.technologyEnabled || 772 data.item.limits.canBeAddedCount == 0 && data.item.upgrading == UPGRADING_NOT_STARTED) 773 { 774 data.button.enabled = false; 775 grayscale = "grayscale:"; 776 data.affordableMask.hidden = false; 777 data.affordableMask.sprite = "colour: 0 0 0 127"; 778 } 779 else if (data.item.upgrading == UPGRADING_NOT_STARTED && data.item.neededResources) 780 { 781 data.button.enabled = false; 782 data.affordableMask.hidden = false; 783 data.affordableMask.sprite = resourcesToAlphaMask(data.item.neededResources); 784 } 785 786 data.icon.sprite = "stretched:" + grayscale + "session/" + data.item.icon; 787 788 var guiObject = Engine.GetGUIObjectByName("unitUpgradeProgressSlider["+data.i+"]"); 789 var size = guiObject.size; 790 if (data.item.upgrading < 0) 791 size.top = size.right; 792 else 793 size.top = size.left + Math.round(Math.max(0,data.item.upgrading) * (size.right - size.left)); 794 guiObject.size = size; 795 }, 796 "setPosition": function(data) 797 { 798 var index = data.i + getNumberOfRightPanelButtons(); 799 setPanelObjectPosition(data.button, index, data.rowLength); 800 }, 801 }; 802 692 803 // QUEUE 693 804 g_SelectionPanels.Queue = { 694 805 "getMaxNumberOfItems": function() … … 1152 1263 "Stance", // normal together with formation 1153 1264 1154 1265 // RIGHT PANE 1155 "Gate", // must always be shown on gate s1266 "Gate", // must always be shown on gate 1156 1267 "Pack", // must always be shown on packable entities 1268 "Upgrade", // must always be shown on upgradable entities 1157 1269 "Training", 1158 1270 "Construction", 1159 1271 "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, … … 252 252 { 253 253 var sum = 0; 254 254 255 for (let prop of ["Construction", "Training", "Pack", "Gate" ])255 for (let prop of ["Construction", "Training", "Pack", "Gate", "Upgrade"]) 256 256 if (g_SelectionPanels[prop].used) 257 257 sum += g_unitPanelButtons[prop]; 258 258 -
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
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/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>