Ticket #996: capture.2.diff
File capture.2.diff, 43.2 KB (added by , 9 years ago) |
---|
-
binaries/data/mods/public/gui/common/functions_utility.js
166 166 // ==================================================================== 167 167 168 168 // Convert integer color values to string (for use in GUI objects) 169 function rgbToGuiColor(color )169 function rgbToGuiColor(color, alpha) 170 170 { 171 var ret; 171 172 if (color && ("r" in color) && ("g" in color) && ("b" in color)) 172 return color.r + " " + color.g + " " + color.b; 173 174 return "0 0 0"; 173 ret = color.r + " " + color.g + " " + color.b; 174 else 175 ret = "0 0 0"; 176 if (alpha) 177 ret += " " + alpha; 178 return ret; 175 179 } 176 180 177 181 // ==================================================================== -
binaries/data/mods/public/gui/common/tooltips.js
140 140 if (type === "Charge") return translate("Charge Attack:"); 141 141 if (type === "Melee") return translate("Melee Attack:"); 142 142 if (type === "Ranged") return translate("Ranged Attack:"); 143 if (type === "Capture") return translate("Capture Attack:"); 143 144 144 145 warn(sprintf("Internationalization: Unexpected attack type found with code ‘%(attackType)s’. This attack type must be internationalized.", { attackType: type })); 145 146 return translate("Attack:"); … … 169 170 }); 170 171 171 172 var attackLabel = txtFormats.header[0] + getAttackTypeLabel(type) + txtFormats.header[1]; 173 if (type == "Capture") 174 { 175 attacks.push(sprintf(translate("%(attackLabel)s %(details)s, %(rate)s"), { 176 attackLabel: attackLabel, 177 details: template.attack[type].value, 178 rate: rate 179 })); 180 continue; 181 } 172 182 if (type != "Ranged") 173 183 { 174 184 attacks.push(sprintf(translate("%(attackLabel)s %(details)s, %(rate)s"), { -
binaries/data/mods/public/gui/session/selection_details.js
86 86 Engine.GetGUIObjectByName("healthSection").hidden = true; 87 87 } 88 88 89 // TODO: Stamina 90 var player = Engine.GetPlayerID(); 91 if (entState.stamina && (entState.player == player || g_DevSettings.controlAll)) 92 Engine.GetGUIObjectByName("staminaSection").hidden = false; 89 // CapturePoints 90 if (entState.capturePoints) 91 { 92 var size = 0; 93 for (let i in entState.capturePoints) 94 { 95 var unitCaptureBar = Engine.GetGUIObjectByName("captureBar["+i+"]"); 96 var sizeObj = unitCaptureBar.size; 97 sizeObj.rleft = size; 98 99 size += 100*Math.max(0, Math.min(1, entState.capturePoints[i] / entState.maxCapturePoints)); 100 sizeObj.rright = size; 101 unitCaptureBar.size = sizeObj; 102 unitCaptureBar.sprite = "color: " + rgbToGuiColor(g_Players[i].color, 128); 103 } 104 105 Engine.GetGUIObjectByName("captureStats").caption = sprintf(translate("%(capturePoints)s / %(maxCapturePoints)s"), { 106 capturePoints: Math.ceil(entState.capturePoints[entState.player]), 107 maxCapturePoints: entState.maxCapturePoints 108 }); 109 Engine.GetGUIObjectByName("captureSection").hidden = false; 110 } 93 111 else 94 Engine.GetGUIObjectByName("staminaSection").hidden = true; 112 { 113 Engine.GetGUIObjectByName("captureSection").hidden = true; 114 } 95 115 116 // TODO: Stamina 117 96 118 // Experience 97 119 if (entState.promotion) 98 120 { … … 136 158 Engine.GetGUIObjectByName("resourceStats").caption = resources; 137 159 138 160 if (entState.hitpoints) 139 Engine.GetGUIObjectByName("resourceSection").size = Engine.GetGUIObjectByName(" staminaSection").size;161 Engine.GetGUIObjectByName("resourceSection").size = Engine.GetGUIObjectByName("captureSection").size; 140 162 else 141 163 Engine.GetGUIObjectByName("resourceSection").size = Engine.GetGUIObjectByName("healthSection").size; 142 164 -
binaries/data/mods/public/gui/session/selection_panels_middle/single_details_area.xml
20 20 </object> 21 21 </object> 22 22 23 <!-- Staminabar -->24 <object size="88 28 100% 52" name=" staminaSection">25 <object size="0 0 100% 16" name=" staminaLabel" type="text" style="StatsTextLeft" ghost="true">26 <translatableAttribute id="tooltip"> Stamina:</translatableAttribute>23 <!-- Capture bar --> 24 <object size="88 28 100% 52" name="captureSection"> 25 <object size="0 0 100% 16" name="captureLabel" type="text" style="StatsTextLeft" ghost="true"> 26 <translatableAttribute id="tooltip">Capture points:</translatableAttribute> 27 27 </object> 28 <object size="0 0 100% 16" name=" staminaStats" type="text" style="StatsTextRight" ghost="true"/>29 <object size="1 16 100% 23" name=" stamina" type="image">28 <object size="0 0 100% 16" name="captureStats" type="text" style="StatsTextRight" ghost="true"/> 29 <object size="1 16 100% 23" name="capture" type="image"> 30 30 <object type="image" sprite="barBorder" ghost="true" size="-1 -1 100%+1 100%+1"/> 31 <object type="image" sprite="staminaBackground" ghost="true"/> 32 <object type="image" sprite="staminaForeground" ghost="true" name="staminaBar"/> 31 <repeat count="9"> 32 <object type="image" sprite="playerColorBackground" ghost="true" name="captureBar[n]"/> 33 </repeat> 33 34 <object type="image" sprite="statsBarShaderHorizontal" ghost="true"/> 34 35 </object> 35 36 </object> -
binaries/data/mods/public/gui/session/unit_actions.js
99 99 { 100 100 if (!entState.attack || !targetState.hitpoints) 101 101 return false; 102 if (playerCheck(entState, targetState, ["Neutral", "Enemy"])) 103 return {"possible": Engine.GuiInterfaceCall("CanAttack", {"entity": entState.id, "target": targetState.id})}; 104 return false; 102 return {"possible": Engine.GuiInterfaceCall("CanAttack", {"entity": entState.id, "target": targetState.id})}; 105 103 }, 106 104 "hotkeyActionCheck": function(target) 107 105 { … … 672 670 "icon": "kill_small.png" 673 671 }; 674 672 673 if (entState.capturePoints && entState.capturePoints[entState.player] < entState.maxCapturePoints / 2) 674 return { 675 "tooltip": translate("You cannot destroy this entity as you own less than half the capture points"), 676 "icon": "kill_small.png" 677 }; 678 679 675 680 return { 676 681 "tooltip": translate("Delete"), 677 682 "icon": "kill_small.png" -
binaries/data/mods/public/simulation/components/Attack.js
159 159 "</element>" + 160 160 "</optional>" + 161 161 "<optional>" + 162 "<element name='Capture'>" + 163 "<interleave>" + 164 "<element name='Value' a:help='Capture points value'><ref name='nonNegativeDecimal'/></element>" + 165 "<element name='MaxRange' a:help='Maximum attack range (in meters)'><ref name='nonNegativeDecimal'/></element>" + 166 "<element name='RepeatTime' a:help='Time between attacks (in milliseconds). The attack animation will be stretched to match this time'>" + // TODO: it shouldn't be stretched 167 "<data type='positiveInteger'/>" + 168 "</element>" + 169 Attack.prototype.bonusesSchema + 170 Attack.prototype.preferredClassesSchema + 171 Attack.prototype.restrictedClassesSchema + 172 "</interleave>" + 173 "</element>" + 174 "</optional>" + 175 "<optional>" + 162 176 "<element name='Charge'>" + 163 177 "<interleave>" + 164 178 "<element name='Hack' a:help='Hack damage strength'><ref name='nonNegativeDecimal'/></element>" + … … 198 212 if (this.template.Charge) ret.push("Charge"); 199 213 if (this.template.Melee) ret.push("Melee"); 200 214 if (this.template.Ranged) ret.push("Ranged"); 215 if (this.template.Capture) ret.push("Capture"); 201 216 return ret; 202 217 }; 203 218 … … 309 324 if (cmpFormation) 310 325 return this.GetBestAttack(); 311 326 312 constcmpIdentity = Engine.QueryInterface(target, IID_Identity);327 var cmpIdentity = Engine.QueryInterface(target, IID_Identity); 313 328 if (!cmpIdentity) 314 329 return undefined; 315 330 316 const targetClasses = cmpIdentity.GetClassesList();317 const isTargetClass = function (value, i, a) { return targetClasses.indexOf(value) != -1; };318 const types = this.GetAttackTypes();319 const attack = this;320 const isAllowed = function (value, i, a) { return !attack.GetRestrictedClasses(value).some(isTargetClass); }321 const isPreferred = function (value, i, a) { return attack.GetPreferredClasses(value).some(isTargetClass); }322 const byPreference = function (a, b) { return (types.indexOf(a) + (isPreferred(a) ? types.length : 0) ) - (types.indexOf(b) + (isPreferred(b) ? types.length : 0) ); }323 331 332 var targetClasses = cmpIdentity.GetClassesList(); 333 var isTargetClass = function (className) { return targetClasses.indexOf(className) != -1; }; 334 324 335 // Always slaughter domestic animals instead of using a normal attack 325 336 if (isTargetClass("Domestic") && this.template.Slaughter) 326 337 return "Slaughter"; 327 338 328 return types.filter(isAllowed).sort(byPreference).pop(); 339 var attack = this; 340 var isAllowed = function (type) { return !attack.GetRestrictedClasses(type).some(isTargetClass); } 341 342 var types = this.GetAttackTypes().filter(isAllowed); 343 344 // check if the target is capturable 345 var captureIndex = types.indexOf("Capture") 346 if (captureIndex != -1) 347 { 348 var cmpCapturable = Engine.QueryInterface(target, IID_Capturable); 349 var cmpPlayer = QueryOwnerInterface(this.entity); 350 if (cmpPlayer && cmpCapturable && cmpCapturable.CanCapture(cmpPlayer.GetPlayerID())) 351 return "Capture"; 352 // not captureable, so remove this attack 353 types.splice(captureIndex, 1); 354 } 355 356 var isPreferred = function (className) { return attack.GetPreferredClasses(className).some(isTargetClass); } 357 var byPreference = function (a, b) { return (types.indexOf(a) + (isPreferred(a) ? types.length : 0) ) - (types.indexOf(b) + (isPreferred(b) ? types.length : 0) ); } 358 359 360 return types.sort(byPreference).pop(); 329 361 }; 330 362 331 363 Attack.prototype.CompareEntitiesByPreference = function(a, b) … … 367 399 { 368 400 return ApplyValueModificationsToEntity("Attack/" + type + splash + "/" + damageType, +(template[damageType] || 0), self.entity); 369 401 }; 370 402 403 if (type == "Capture") 404 return {value: applyMods("Value")}; 405 371 406 return { 372 407 hack: applyMods("Hack"), 373 408 pierce: applyMods("Pierce"), … … 517 552 var cmpTimer = Engine.QueryInterface(SYSTEM_ENTITY, IID_Timer); 518 553 cmpTimer.SetTimeout(this.entity, IID_Attack, "MissileHit", timeToTarget*1000, {"type": type, "target": target, "position": realTargetPosition, "direction": missileDirection, "projectileId": id, "playerId":playerId}); 519 554 } 555 else if (type == "Capture") 556 { 557 var multiplier = this.GetAttackBonus(type, target); 558 var cmpHealth = Engine.QueryInterface(target, IID_Health); 559 if (!cmpHealth || cmpHealth.GetHitpoints() == 0) 560 return; 561 multiplier *= cmpHealth.GetMaxHitpoints() / cmpHealth.GetHitpoints(); 562 563 var cmpOwnership = Engine.QueryInterface(this.entity, IID_Ownership); 564 if (!cmpOwnership || cmpOwnership.GetOwner() == -1) 565 return; 566 var owner = cmpOwnership.GetOwner(); 567 var cmpCapturable = Engine.QueryInterface(target, IID_Capturable); 568 if (!cmpCapturable || !cmpCapturable.CanCapture(owner)) 569 return; 570 571 var strength = this.GetAttackStrengths("Capture").value; 572 cmpCapturable.Reduce(strength * multiplier, owner); 573 } 520 574 else 521 575 { 522 576 // Melee attack - hurt the target immediately … … 585 639 // If friendlyFire isn't enabled, get all player enemies to pass to "Damage.CauseSplashDamage". 586 640 if (friendlyFire == "false") 587 641 { 588 var cmpPlayer = Engine.QueryInterface(Engine.QueryInterface(SYSTEM_ENTITY, IID_PlayerManager).GetPlayerByID(data.playerId), IID_Player)642 var cmpPlayer = QueryPlayerIDInterface(data.playerId); 589 643 playersToDamage = cmpPlayer.GetEnemies(); 590 644 } 591 645 // Damage the units. … … 607 661 else 608 662 { 609 663 // If we didn't hit the main target look for nearby units 610 var cmpPlayer = Engine.QueryInterface(Engine.QueryInterface(SYSTEM_ENTITY, IID_PlayerManager).GetPlayerByID(data.playerId), IID_Player)664 var cmpPlayer = QueryPlayerIDInterface(data.playerId); 611 665 var ents = Damage.EntitiesNearPoint(Vector2D.from3D(data.position), targetPosition.horizDistanceTo(data.position) * 2, cmpPlayer.GetEnemies()); 612 666 613 667 for (var i = 0; i < ents.length; i++) -
binaries/data/mods/public/simulation/components/Builder.js
7 7 "<Entities datatype='tokens'>" + 8 8 "\n structures/{civ}_barracks\n structures/{civ}_civil_centre\n structures/pers_apadana\n " + 9 9 "</Entities>" + 10 "<AllowForeignBuilder>false</AllowForeignBuilder>" + 10 11 "</a:example>" + 11 12 "<element name='Rate' a:help='Construction speed multiplier (1.0 is normal speed, higher values are faster)'>" + 12 13 "<ref name='positiveDecimal'/>" + … … 16 17 "<value>tokens</value>" + 17 18 "</attribute>" + 18 19 "<text/>" + 19 "</element>"; 20 "</element>" + 21 "<optional>" + 22 "<element name='AllowForeignBuilder' a:help='Allow this entity to build buildings also when this civ doesn't match the owner's civ, so when this entity is considered a foreigner.'>" + 23 "<data type='boolean'/>" + 24 "</element>" + 25 "</optional>"; 20 26 21 27 Builder.prototype.Init = function() 22 28 { … … 26 32 27 33 Builder.prototype.GetEntitiesList = function() 28 34 { 29 var entities = [];30 35 var string = this.template.Entities._string; 31 if ( string)32 {33 // Replace the "{civ}" codes with this entity's civ ID 34 35 if (cmpIdentity)36 string = string.replace(/\{civ\}/g, cmpIdentity.GetCiv());37 entities = string.split(/\s+/);38 39 // Remove disabled entities40 var cmpPlayer = QueryOwnerInterface(this.entity, IID_Player)41 var disabledEntities = cmpPlayer.GetDisabledTemplates();42 43 for (var i = entities.length - 1; i >= 0; --i)44 if (disabledEntities[entities[i]])45 entities.splice(i, 1);46 }47 return entities ;36 if (!string) 37 return []; 38 39 var cmpIdentity = Engine.QueryInterface(this.entity, IID_Identity); 40 var cmpPlayer = QueryOwnerInterface(this.entity, IID_Player) 41 if (!cmpIdentity || !cmpPlayer) 42 return []; 43 44 // possibly disable building of structures from other civs 45 if (cmpIdentity.GetCiv() != cmpPlayer.GetCiv() && !this.AllowForeignBuilder()) 46 return []; 47 48 // Replace the "{civ}" codes with this entity's civ ID 49 var entities = string.replace(/\{civ\}/g, cmpIdentity.GetCiv()).split(/\s+/); 50 // Remove disabled entities 51 var disabledEntities = cmpPlayer.GetDisabledTemplates(); 52 return entities.filter(function(e) { return !disabledEntities[e]; }); 48 53 }; 49 54 55 Builder.prototype.AllowForeignBuilder = function() 56 { 57 var allow = this.template.AllowForeignBuilder || false; 58 return allow == "true"; 59 }; 60 50 61 Builder.prototype.GetRange = function() 51 62 { 52 63 var cmpObstruction = Engine.QueryInterface(this.entity, IID_Obstruction); -
binaries/data/mods/public/simulation/components/Capturable.js
1 function Capturable() {} 2 3 Capturable.prototype.Schema = 4 "<element name='CapturePoints' a:help='Maximum capture points'>" + 5 "<ref name='nonNegativeDecimal'/>" + 6 "</element>"; 7 8 Capturable.prototype.Init = function() 9 { 10 // Cache this value 11 this.maxCp = +this.template.CapturePoints; 12 this.cp = []; 13 }; 14 15 //// Interface functions //// 16 17 /** 18 * Returns the current capture points array 19 */ 20 Capturable.prototype.GetCapturePoints = function() 21 { 22 return this.cp; 23 }; 24 25 Capturable.prototype.GetMaxCapturePoints = function() 26 { 27 return this.maxCp; 28 }; 29 30 /** 31 * Reduces the amount of capture points of an entity, 32 * in the favour of the player of the source 33 * Returns true when captured 34 */ 35 Capturable.prototype.Reduce = function(amount, player) 36 { 37 var cmpOwnership = Engine.QueryInterface(this.entity, IID_Ownership); 38 if (!cmpOwnership || cmpOwnership.GetOwner() == -1) 39 return; 40 41 // Before changing the value, activate Fogging if necessary to hide changes 42 var cmpFogging = Engine.QueryInterface(this.entity, IID_Fogging); 43 if (cmpFogging) 44 cmpFogging.Activate(); 45 46 var cmpPlayerSource = QueryPlayerIDInterface(player); 47 48 if (!cmpPlayerSource) 49 warn(source + " has no player component defined on its owner "); 50 51 var enemiesWithCp = 0; 52 for (let i in this.cp) 53 if (!cmpPlayerSource.IsAlly(i) && this.cp[i] > 0) 54 enemiesWithCp++; 55 56 var distributedAmount = amount / enemiesWithCp; 57 for (let i in this.cp) 58 { 59 if (cmpPlayerSource.IsAlly(i)) 60 continue; 61 if (this.cp[i] > distributedAmount) 62 this.cp[i] -= distributedAmount; 63 else 64 this.cp[i] = 0; 65 } 66 67 // give all cp taken to the player 68 var currentCp = this.cp.reduce(function(a, b) { return a + b; }); 69 this.cp[player] = this.GetMaxCapturePoints() - currentCp; 70 71 // if all cp has been taken from the owner, convert it 72 if (this.cp[cmpOwnership.GetOwner()] > 0) 73 return; 74 75 var bestPlayer = 0; 76 for (let i in this.cp) 77 if (this.cp[i] >= this.cp[bestPlayer]) 78 bestPlayer = +i; 79 80 cmpOwnership.SetOwner(bestPlayer); 81 82 return; 83 }; 84 85 /** 86 * Check if the source can (re)capture points from this building 87 */ 88 Capturable.prototype.CanCapture = function(playerID) 89 { 90 var cmpPlayerSource = QueryPlayerIDInterface(playerID); 91 92 if (!cmpPlayerSource) 93 warn(source + " has no player component defined on its owner "); 94 var cp = this.GetCapturePoints() 95 var sourceEnemyCp = 0; 96 for (let i in this.GetCapturePoints()) 97 if (!cmpPlayerSource.IsAlly(i)) 98 sourceEnemyCp += cp[i]; 99 return sourceEnemyCp > 0; 100 }; 101 102 //// Private functions //// 103 104 Capturable.prototype.OnValueModification = function(msg) 105 { 106 if (msg.component != "Capturable") 107 return; 108 109 var oldMaxCp = this.GetMaxCapturePoints(); 110 this.maxCp = Math.round(ApplyValueModificationsToEntity("Capturable/Max", +this.template.Max, this.entity)); 111 if (oldMaxCp == this.maxCp) 112 return; 113 114 var scale = this.maxCp / oldMaxCp; 115 for (let i in this.cp) 116 this.cp[i] *= scale; 117 }; 118 119 Capturable.prototype.OnOwnershipChanged = function(msg) 120 { 121 // initialise the capture points when created 122 if (msg.from != -1) 123 return; 124 125 this.cp = []; 126 var cmpPlayerManager = Engine.QueryInterface(SYSTEM_ENTITY, IID_PlayerManager); 127 // FIXME: check if this works with observers 128 for (let i = 0; i < cmpPlayerManager.GetNumPlayers(); ++i) 129 if (i == msg.to) 130 this.cp[i] = this.maxCp; 131 else 132 this.cp[i] = 0; 133 }; 134 135 Engine.RegisterComponentType(IID_Capturable, "Capturable", Capturable); -
binaries/data/mods/public/simulation/components/Fogging.js
125 125 cmpHealth.IsRepairable() && (cmpHealth.GetHitpoints() < cmpHealth.GetMaxHitpoints()) 126 126 ); 127 127 128 var cmpCapturable = Engine.QueryInterface(this.entity, IID_Capturable); 129 if (cmpCapturable) 130 cmpMirage.CopyCapturable( 131 cmpCapturable.GetMaxCapturePoints(), 132 cmpCapturable.GetCapturePoints() 133 ); 134 128 135 var cmpResourceSupply = Engine.QueryInterface(this.entity, IID_ResourceSupply); 129 136 if (cmpResourceSupply) 130 137 cmpMirage.CopyResourceSupply( -
binaries/data/mods/public/simulation/components/GarrisonHolder.js
75 75 this.visibleGarrisonPoints.push({"offset":o, "entity": null}); 76 76 } 77 77 } 78 this.captureTimers = {}; 78 79 }; 79 80 80 81 /** … … 269 270 if (this.GetGarrisonedEntitiesCount() + extraCount >= this.GetCapacity()) 270 271 return false; 271 272 273 var cmpTimer = Engine.QueryInterface(SYSTEM_ENTITY, IID_Timer); 272 274 if (!this.timer && this.GetHealRate() > 0) 273 {274 var cmpTimer = Engine.QueryInterface(SYSTEM_ENTITY, IID_Timer);275 275 this.timer = cmpTimer.SetTimeout(this.entity, IID_GarrisonHolder, "HealTimeout", 1000, {}); 276 }277 276 278 277 // Actual garrisoning happens here 279 278 this.entities.push(entity); … … 291 290 var cmpUnitAI = Engine.QueryInterface(entity, IID_UnitAI); 292 291 if (cmpUnitAI && cmpUnitAI.IsUnderAlert()) 293 292 Engine.PostMessage(cmpUnitAI.GetAlertRaiser(), MT_UnitGarrisonedAfterAlert, {"holder": this.entity, "unit": entity}); 293 294 var cmpAttack = Engine.QueryInterface(entity, IID_Attack); 295 if (cmpAttack && cmpAttack.GetAttackTypes().indexOf("Capture") != -1) 296 { 297 var repeatTime = cmpAttack.GetTimers("Capture").repeat; 298 var timer = cmpTimer.SetInterval(this.entity, IID_GarrisonHolder, "Recapture", repeatTime, repeatTime, {"entity": entity}); 299 this.captureTimers[entity] = timer; 300 } 294 301 295 302 return true; 296 303 }; … … 420 427 this.OrderWalkToRallyPoint(ejectedEntities); 421 428 this.UpdateGarrisonFlag(); 422 429 430 if (forced || success) 431 { 432 var cmpTimer = Engine.QueryInterface(SYSTEM_ENTITY, IID_Timer); 433 for (let ent of entities) 434 { 435 var timerId = this.captureTimers[ent]; 436 if (timerId) 437 { 438 cmpTimer.CancelTimer(timerId); 439 this.captureTimers[ent] = 0; 440 } 441 } 442 } 443 423 444 return success; 424 445 }; 425 446 … … 723 744 return false; 724 745 }; 725 746 747 GarrisonHolder.prototype.Recapture = function(data) 748 { 749 var cmpAttack = Engine.QueryInterface(data.entity, IID_Attack); 750 if (!cmpAttack) 751 return; // error 752 if (cmpAttack.GetAttackTypes().indexOf("Capture") == -1) 753 return; // error 754 cmpAttack.PerformAttack("Capture", this.entity); 755 }; 756 726 757 /** 727 758 * Initialise the garrisoned units 728 759 */ -
binaries/data/mods/public/simulation/components/GuiInterface.js
267 267 ret.needsRepair = cmpMirage.NeedsRepair(); 268 268 } 269 269 270 var cmpCapturable = Engine.QueryInterface(ent, IID_Capturable); 271 if (cmpCapturable) 272 { 273 ret.capturePoints = cmpCapturable.GetCapturePoints(); 274 ret.maxCapturePoints = cmpCapturable.GetMaxCapturePoints(); 275 } 276 if (cmpMirage && cmpMirage.Capturable()) 277 { 278 ret.capturePoints = cmpMirage.GetCapturePoints(); 279 ret.maxCapturePoints = cmpMirage.GetMaxCapturePoints(); 280 } 281 270 282 var cmpBuilder = Engine.QueryInterface(ent, IID_Builder); 271 283 if (cmpBuilder) 272 284 ret.builder = true; … … 1701 1713 var cmpAttack = Engine.QueryInterface(data.entity, IID_Attack); 1702 1714 if (!cmpAttack) 1703 1715 return false; 1716 var cmpEntityPlayer = QueryOwnerInterface(data.entity, IID_Player); 1717 var cmpTargetPlayer = QueryOwnerInterface(data.target, IID_Player); 1718 if (!cmpEntityPlayer || !cmpTargetPlayer) 1719 return false; 1704 1720 1705 return cmpAttack.CanAttack(data.target); 1721 1722 // if the owner is an enemy, it's up to the attack component to decide 1723 if (!cmpEntityPlayer.IsAlly(cmpTargetPlayer.GetPlayerID())) 1724 return cmpAttack.CanAttack(data.target); 1725 1726 // if the owner is an ally, we could still want to capture some capture points back 1727 var cmpCapturable = Engine.QueryInterface(data.target, IID_Capturable); 1728 if (cmpCapturable && cmpCapturable.CanCapture(cmpEntityPlayer.GetPlayerID())) 1729 return cmpAttack.CanAttack(data.target); 1730 1731 return false; 1706 1732 }; 1707 1733 1708 1734 /* -
binaries/data/mods/public/simulation/components/Mirage.js
21 21 this.hitpoints = null; 22 22 this.needsRepair = null; 23 23 24 this.capturable = false; 25 this.capturePoints = []; 26 this.maxCapturePoints = 0; 27 24 28 this.resourceSupply = false; 25 29 this.maxAmount = null; 26 30 this.amount = null; … … 94 98 return this.needsRepair; 95 99 }; 96 100 101 // Capture data 102 103 Mirage.prototype.CopyCapturable = function(capturePoints, maxCapturePoints) 104 { 105 this.capturable = true; 106 this.capturePoints = capturePoints; 107 this.maxCapturePoints = maxCapturePoints; 108 } 109 110 Mirage.prototype.Capturable = function() 111 { 112 return this.capturable; 113 } 114 115 Mirage.prototype.GetMaxCapturePoints = function() 116 { 117 return this.maxCapturePoints; 118 } 119 120 Mirage.prototype.GetCapturePoints = function() 121 { 122 return this.capturePoints; 123 } 124 97 125 // ResourceSupply data 98 126 99 127 Mirage.prototype.CopyResourceSupply = function(maxAmount, amount, type, isInfinite) -
binaries/data/mods/public/simulation/components/ProductionQueue.js
29 29 "</attribute>" + 30 30 "<text/>" + 31 31 "</element>" + 32 "</optional>"; 32 "</optional>" + 33 "<optional>" + 34 "<element name='AllowForeignUnitRecruiter' a:help='Allow this entity to produce units also when this civ doesn't match the owner's civ, so when this entity is considered a foreign.'>" + 35 "<data type='boolean'/>" + 36 "</element>" + 37 "</optional>" + 38 "<optional>" + 39 "<element name='AllowForeignTechnologyResearcher' a:help='Allow this entity to research technologies also when this civ doesn't match the owner's civ, so when this entity is considered a foreign.'>" + 40 "<data type='boolean'/>" + 41 "</element>" + 42 "</optional>"; 33 43 34 44 ProductionQueue.prototype.Init = function() 35 45 { … … 69 79 this.alertRaiser = undefined; 70 80 }; 71 81 82 ProductionQueue.prototype.AllowForeignUnitRecruiter = function() 83 { 84 var allow = this.template.AllowForeignUnitRecruiter || false; 85 return allow == "true"; 86 }; 87 88 ProductionQueue.prototype.AllowForeignTechnologyResearcher = function() 89 { 90 var allow = this.template.AllowForeignTechnologyResearcher || false; 91 return allow == "true"; 92 }; 93 72 94 ProductionQueue.prototype.PutUnderAlert = function(raiser) 73 95 { 74 96 this.alertRaiser = raiser; … … 97 119 if (!string) 98 120 return; 99 121 122 var cmpPlayer = QueryOwnerInterface(this.entity); 123 var cmpIdentity = Engine.QueryInterface(this.entity, IID_Identity); 124 if (!cmpPlayer || !cmpIdentity) 125 return; 126 127 // possibly don't allow recruiting units of other civs 128 if (cmpIdentity.GetCiv() != cmpPlayer.GetCiv() && !this.AllowForeignUnitRecruiter()) 129 return; 130 100 131 // Replace the "{civ}" codes with this entity's civ ID 101 var cmpIdentity = Engine.QueryInterface(this.entity, IID_Identity); 102 if (cmpIdentity) 103 string = string.replace(/\{civ\}/g, cmpIdentity.GetCiv()); 132 string = string.replace(/\{civ\}/g, cmpIdentity.GetCiv()); 104 133 105 134 var entitiesList = string.split(/\s+/); 106 135 107 136 // Remove disabled entities 108 var cmpPlayer = QueryOwnerInterface(this.entity, IID_Player);109 137 var disabledEntities = cmpPlayer.GetDisabledTemplates(); 110 138 111 139 for (var i = entitiesList.length - 1; i >= 0; --i) … … 154 182 if (!cmpTechnologyManager) 155 183 return []; 156 184 185 var cmpPlayer = QueryOwnerInterface(this.entity); 186 var cmpIdentity = Engine.QueryInterface(this.entity, IID_Identity); 187 if (!cmpPlayer || !cmpIdentity) 188 return []; 189 190 // possibly don't allow researching techs of other civs 191 if (cmpIdentity.GetCiv() != cmpPlayer.GetCiv() && !this.AllowForeignTechnologyResearcher()) 192 return []; 193 157 194 var techs = string.split(/\s+/); 158 195 var techList = []; 159 196 var superseded = {}; // Stores the tech which supersedes the key 160 197 161 var cmpPlayer = QueryOwnerInterface(this.entity, IID_Player); 162 if (cmpPlayer) 163 var disabledTechnologies = cmpPlayer.GetDisabledTechnologies(); 198 var disabledTechnologies = cmpPlayer.GetDisabledTechnologies(); 164 199 165 200 // Add any top level technologies to an array which corresponds to the displayed icons 166 201 // Also store what a technology is superceded by in the superceded object {"tech1":"techWhichSupercedesTech1", ...} … … 237 272 // TODO: there should probably be a limit on the number of queued batches 238 273 // TODO: there should be a way for the GUI to determine whether it's going 239 274 // to be possible to add a batch (based on resource costs and length limits) 240 var cmpPlayer = QueryOwnerInterface(this.entity , IID_Player);275 var cmpPlayer = QueryOwnerInterface(this.entity); 241 276 242 277 if (this.queue.length < MAX_QUEUE_SIZE) 243 278 { … … 314 349 var template = cmpTechTempMan.GetTemplate(templateName); 315 350 if (!template) 316 351 return; 317 var cmpPlayer = QueryOwnerInterface(this.entity , IID_Player);352 var cmpPlayer = QueryOwnerInterface(this.entity); 318 353 var time = template.researchTime * cmpPlayer.GetCheatTimeMultiplier(); 319 354 320 355 var cost = {}; … … 479 514 */ 480 515 ProductionQueue.prototype.GetBatchTime = function(batchSize) 481 516 { 482 var cmpPlayer = QueryOwnerInterface(this.entity , IID_Player);517 var cmpPlayer = QueryOwnerInterface(this.entity); 483 518 484 519 var batchTimeModifier = ApplyValueModificationsToEntity("ProductionQueue/BatchTimeModifier", +this.template.BatchTimeModifier, this.entity); 485 520 … … 651 686 // until we've used up all the time (so that we work accurately 652 687 // with items that take fractions of a second) 653 688 var time = g_ProgressInterval; 654 var cmpPlayer = QueryOwnerInterface(this.entity , IID_Player);689 var cmpPlayer = QueryOwnerInterface(this.entity); 655 690 656 691 while (time > 0 && this.queue.length) 657 692 { … … 729 764 730 765 if (!this.spawnNotified) 731 766 { 732 var cmpPlayer = QueryOwnerInterface(this.entity , IID_Player);767 var cmpPlayer = QueryOwnerInterface(this.entity); 733 768 var notification = {"players": [cmpPlayer.GetPlayerID()], "message": "Can't find free space to spawn trained units" }; 734 769 var cmpGUIInterface = Engine.QueryInterface(SYSTEM_ENTITY, IID_GuiInterface); 735 770 cmpGUIInterface.PushNotification(notification); -
binaries/data/mods/public/simulation/components/TerritoryDecay.js
1 1 function TerritoryDecay() {} 2 2 3 3 TerritoryDecay.prototype.Schema = 4 "<element name=' HealthDecayRate' a:help='Decay rate in hitpoints per second'>" +4 "<element name='DecayRate' a:help='Decay rate in hitpoints per second'>" + 5 5 "<data type='positiveInteger'/>" + 6 6 "</element>"; 7 7 … … 33 33 var tileOwner = cmpTerritoryManager.GetOwner(pos.x, pos.y); 34 34 if (tileOwner != cmpOwnership.GetOwner()) 35 35 return false; 36 // TODO: this should probably use the same territory restriction37 // logic as BuildRestrictions, to handle allies etc38 36 39 37 return cmpTerritoryManager.IsConnected(pos.x, pos.y); 40 38 }; … … 64 62 if (connected) 65 63 var decaying = false; 66 64 else 67 var decaying = (Math.round(ApplyValueModificationsToEntity("TerritoryDecay/ HealthDecayRate", +this.template.HealthDecayRate, this.entity)) > 0);65 var decaying = (Math.round(ApplyValueModificationsToEntity("TerritoryDecay/DecayRate", +this.template.DecayRate, this.entity)) > 0); 68 66 if (decaying === this.decaying) 69 67 return; 70 68 this.decaying = decaying; … … 88 86 89 87 TerritoryDecay.prototype.Decay = function() 90 88 { 91 var cmp Health = Engine.QueryInterface(this.entity, IID_Health);92 if (!cmp Health)89 var cmpCapturable = Engine.QueryInterface(this.entity, IID_Capturable); 90 if (!cmpCapturable) 93 91 return; // error 94 92 95 var decayRate = ApplyValueModificationsToEntity("TerritoryDecay/HealthDecayRate", +this.template.HealthDecayRate, this.entity); 93 var decayRate = ApplyValueModificationsToEntity( 94 "TerritoryDecay/DecayRate", 95 +this.template.DecayRate, 96 this.entity); 96 97 97 cmpHealth.Reduce(Math.round(decayRate)); 98 // Reduce capture points in favour of Gaia 99 cmpCapturable.Reduce(decayRate, 0); 98 100 }; 99 101 100 102 Engine.RegisterComponentType(IID_TerritoryDecay, "TerritoryDecay", TerritoryDecay); -
binaries/data/mods/public/simulation/components/interfaces/Capturable.js
1 Engine.RegisterInterface("Capturable"); 2 3 -
binaries/data/mods/public/simulation/data/technologies/decay_outpost.json
7 7 "icon": "blocks_three.png", 8 8 "researchTime": 40, 9 9 "tooltip": "Territory decay -50% for Outposts.", 10 "modifications": [{"value": "TerritoryDecay/ HealthDecayRate", "multiply": 0.5}],10 "modifications": [{"value": "TerritoryDecay/DecayRate", "multiply": 0.5}], 11 11 "affects": ["Outpost"], 12 12 "soundComplete": "interface/alarm/alarm_upgradearmory.xml" 13 13 } -
binaries/data/mods/public/simulation/data/technologies/romans/decay_logistics.json
7 7 "icon": "handcart_empty.png", 8 8 "researchTime": 40, 9 9 "tooltip": "Entrenched Camps and Siege Walls decay 50% slower.", 10 "modifications": [{"value": "TerritoryDecay/ HealthDecayRate", "multiply": 0.5}],10 "modifications": [{"value": "TerritoryDecay/DecayRate", "multiply": 0.5}], 11 11 "affects": ["ArmyCamp", "SiegeWall"], 12 12 "soundComplete": "interface/alarm/alarm_upgradearmory.xml" 13 13 } -
binaries/data/mods/public/simulation/helpers/Commands.js
349 349 350 350 "delete-entities": function(player, cmd, data) 351 351 { 352 for each (var ent indata.entities)352 for (let ent of data.entities) 353 353 { 354 // don't allow to delete entities who are half-captured 355 var cmpCapturable = Engine.QueryInterface(ent, IID_Capturable); 356 if (cmpCapturable) 357 { 358 var capturePoints = cmpCapturable.GetCapturePoints(); 359 var maxCapturePoints = cmpCapturable.GetMaxCapturePoints(); 360 if (capturePoints[player] < maxCapturePoints / 2) 361 return; 362 } 363 // either kill or delete the entity 354 364 var cmpHealth = Engine.QueryInterface(ent, IID_Health); 355 365 if (cmpHealth) 356 366 { -
binaries/data/mods/public/simulation/templates/structures/merc_camp_egyptian.xml
60 60 </SoundGroups> 61 61 </Sound> 62 62 <TerritoryDecay> 63 < HealthDecayRate>1</HealthDecayRate>63 <DecayRate>1</DecayRate> 64 64 </TerritoryDecay> 65 65 <TerritoryInfluence disable=""/> 66 66 <VisualActor> -
binaries/data/mods/public/simulation/templates/structures/ptol_mercenary_camp.xml
62 62 </SoundGroups> 63 63 </Sound> 64 64 <TerritoryDecay> 65 < HealthDecayRate>1</HealthDecayRate>65 <DecayRate>1</DecayRate> 66 66 </TerritoryDecay> 67 67 <TerritoryInfluence disable=""/> 68 68 <VisualActor> -
binaries/data/mods/public/simulation/templates/structures/ptol_military_colony.xml
76 76 </SoundGroups> 77 77 </Sound> 78 78 <TerritoryDecay> 79 < HealthDecayRate>1</HealthDecayRate>79 <DecayRate>1</DecayRate> 80 80 </TerritoryDecay> 81 81 <TerritoryInfluence> 82 82 <Radius>80</Radius> -
binaries/data/mods/public/simulation/templates/structures/rome_army_camp.xml
76 76 </SoundGroups> 77 77 </Sound> 78 78 <TerritoryDecay> 79 < HealthDecayRate>10</HealthDecayRate>79 <DecayRate>10</DecayRate> 80 80 </TerritoryDecay> 81 81 <TerritoryInfluence disable=""/> 82 82 <ProductionQueue> -
binaries/data/mods/public/simulation/templates/structures/rome_siege_wall_gate.xml
44 44 </Obstructions> 45 45 </Obstruction> 46 46 <TerritoryDecay> 47 < HealthDecayRate>1</HealthDecayRate>47 <DecayRate>1</DecayRate> 48 48 </TerritoryDecay> 49 49 <TerritoryInfluence disable=""/> 50 50 <VisualActor> -
binaries/data/mods/public/simulation/templates/structures/rome_siege_wall_long.xml
62 62 <Static width="37.0" depth="5.0"/> 63 63 </Obstruction> 64 64 <TerritoryDecay> 65 < HealthDecayRate>1</HealthDecayRate>65 <DecayRate>1</DecayRate> 66 66 </TerritoryDecay> 67 67 <TerritoryInfluence disable=""/> 68 68 <VisualActor> -
binaries/data/mods/public/simulation/templates/structures/rome_siege_wall_medium.xml
55 55 <Static width="25.0" depth="5.0"/> 56 56 </Obstruction> 57 57 <TerritoryDecay> 58 < HealthDecayRate>1</HealthDecayRate>58 <DecayRate>1</DecayRate> 59 59 </TerritoryDecay> 60 60 <TerritoryInfluence disable=""/> 61 61 <VisualActor> -
binaries/data/mods/public/simulation/templates/structures/rome_siege_wall_short.xml
42 42 <Static width="13.0" depth="5.0"/> 43 43 </Obstruction> 44 44 <TerritoryDecay> 45 < HealthDecayRate>1</HealthDecayRate>45 <DecayRate>1</DecayRate> 46 46 </TerritoryDecay> 47 47 <TerritoryInfluence disable=""/> 48 48 <VisualActor> -
binaries/data/mods/public/simulation/templates/structures/rome_siege_wall_tower.xml
36 36 <Static width="7.0" depth="7.0"/> 37 37 </Obstruction> 38 38 <TerritoryDecay> 39 < HealthDecayRate>1</HealthDecayRate>39 <DecayRate>1</DecayRate> 40 40 </TerritoryDecay> 41 41 <TerritoryInfluence disable=""/> 42 42 <VisualActor> -
binaries/data/mods/public/simulation/templates/structures/rome_tent.xml
50 50 </SoundGroups> 51 51 </Sound> 52 52 <TerritoryDecay> 53 < HealthDecayRate>1</HealthDecayRate>53 <DecayRate>1</DecayRate> 54 54 </TerritoryDecay> 55 55 <TerritoryInfluence disable=""/> 56 56 <VisualActor> -
binaries/data/mods/public/simulation/templates/structures/sele_military_colony.xml
75 75 </SoundGroups> 76 76 </Sound> 77 77 <TerritoryDecay> 78 < HealthDecayRate>1</HealthDecayRate>78 <DecayRate>1</DecayRate> 79 79 </TerritoryDecay> 80 80 <TerritoryInfluence> 81 81 <Radius>80</Radius> -
binaries/data/mods/public/simulation/templates/template_structure.xml
20 20 <PlacementType>land</PlacementType> 21 21 <Territory>own</Territory> 22 22 </BuildRestrictions> 23 <Capturable> 24 <CapturePoints>1000</CapturePoints> 25 </Capturable> 23 26 <Cost> 24 27 <Population>0</Population> 25 28 <PopulationBonus>0</PopulationBonus> … … 100 103 <HeightOffset>12.0</HeightOffset> 101 104 </StatusBars> 102 105 <TerritoryDecay> 103 < HealthDecayRate>5</HealthDecayRate>106 <DecayRate>5</DecayRate> 104 107 </TerritoryDecay> 105 108 <Visibility> 106 109 <RetainInFog>true</RetainInFog> -
binaries/data/mods/public/simulation/templates/template_structure_defense_outpost.xml
90 90 <HeightOffset>18.0</HeightOffset> 91 91 </StatusBars> 92 92 <TerritoryDecay> 93 < HealthDecayRate>2</HealthDecayRate>93 <DecayRate>2</DecayRate> 94 94 </TerritoryDecay> 95 95 <Vision> 96 96 <Range>80</Range> -
binaries/data/mods/public/simulation/templates/template_unit_infantry.xml
6 6 <Crush>15</Crush> 7 7 </Armour> 8 8 <Attack> 9 <Capture> 10 <Value>10</Value> 11 <MaxRange>4</MaxRange> 12 <RepeatTime>1000</RepeatTime> 13 </Capture> 9 14 <Slaughter> 10 15 <Hack>50.0</Hack> 11 16 <Pierce>0.0</Pierce>