Ticket #1496: Damagev5.diff
File Damagev5.diff, 23.1 KB (added by , 11 years ago) |
---|
-
0ad/binaries/data/mods/public/simulation/templates/special/dummy.xml
1 <Entity> 2 <Position> 3 <Altitude>0</Altitude> 4 <Anchor>upright</Anchor> 5 <Floating>false</Floating> 6 <TurnRate>6.0</TurnRate> 7 </Position> 8 </Entity> -
0ad/binaries/data/mods/public/simulation/helpers/Damage.js
1 //Create global Damage object. 2 var Damage = {}; 3 4 /**************************************** 5 * Damages units around a given origin. 6 * data.attacker = <entity id> 7 * data.origin = {'x':<int>, 'z':<int>} 8 * data.radius = <int> 9 * data.shape = <string> 10 * data.strengths = {'hack':<float>, 'pierce':<float>, 'crush':<float>} 11 * ***Optional Variables*** 12 * data.direction = <unit vector> 13 * data.playersToDamage = <array of player ids> 14 */ 15 Damage.CauseSplashDamage = function(data) 16 { 17 // Get nearby entities and define variables 18 var nearEnts = Damage.EntitiesNearPoint(data.origin, data.radius, data.playersToDamage); 19 var damageMultiplier = 1; 20 // Cycle through all the nearby entities and damage it appropriately based on its distance from the origin. 21 for each (var entity in nearEnts) 22 { 23 var entityPosition = Engine.QueryInterface(entity, IID_Position).GetPosition(); 24 if(data.shape == 'Circular') // circular effect with quadratic falloff in every direction 25 { 26 var squaredDistanceFromOrigin = Damage.VectorDistanceSquared(data.origin, entityPosition); 27 damageMultiplier == 1 - squaredDistanceFromOrigin / (data.radius * data.radius); 28 } 29 else if(data.shape == 'Linear') // linear effect with quadratic falloff in two directions (only used for certain missiles) 30 { 31 // Get position of entity relative to splash origin. 32 var relativePos = {"x":entityPosition.x - data.origin.x, "z":entityPosition.z - data.origin.z}; 33 34 // The width of linear splash is one fifth of the normal splash radius. 35 var width = data.radius/5; 36 37 // Effectivly rotate the axis to align with the missile direction. 38 var parallelDist = Damage.VectorDot(relativePos, data.direction); // z axis 39 var perpDist = Math.abs(Damage.VectorCross(relativePos, data.direction)); // y axis 40 41 // Check that the unit is within the distance at which it will get damaged. 42 if (parallelDist > -width && perpDist < width) // If in radius, quadratic falloff in both directions 43 damageMultiplier = (data.radius * data.radius - parallelDist * parallelDist) / (data.radius * data.radius) 44 * (width * width - perpDist * perpDist) / (width * width); 45 else 46 damageMultiplier = 0; 47 } 48 else // In case someone calls this function with an invalid shape. 49 { 50 warn("The " + data.shape + " splash damage shape is not implemented!"); 51 } 52 // Call CauseDamage which reduces the hitpoints, posts network command, plays sounds.... 53 Damage.CauseDamage({"strengths":data.strengths, "target":entity, "attacker":data.attacker, "multiplier":damageMultiplier, "type":"Splash"}) 54 } 55 }; 56 57 /**************************************** 58 * Causes damage on a given unit 59 * data.strengths = {'hack':<float>, 'pierce':<float>, 'crush':<float>} 60 * data.target = <entity id> 61 * data.attacker = <entity id> 62 * data.multiplier = <float between 1 and 0> 63 * data.type = <string> 64 */ 65 Damage.CauseDamage = function(data) 66 { 67 // Check the target can be damaged otherwise don't do anything. 68 var cmpDamageReceiver = Engine.QueryInterface(data.target, IID_DamageReceiver); 69 if (!cmpDamageReceiver) 70 return; 71 72 // Damage the target 73 var targetState = cmpDamageReceiver.TakeDamage(data.strengths.hack * data.multiplier, data.strengths.pierce * data.multiplier, data.strengths.crush * data.multiplier); 74 75 // If the target was killed run some cleanup 76 if (targetState.killed) 77 Damage.TargetKilled(data.attacker, data.target); 78 79 // Post the network command (make it work in multiplayer) 80 Engine.PostMessage(data.target, MT_Attacked, {"attacker":data.attacker, "target":data.target, "type":data.type, "damage":-targetState.change}); 81 82 // Play attacking sounds 83 PlaySound("attack_impact", data.attacker); 84 }; 85 86 /**************************************** 87 * Gets entities near a give point for given players. 88 * origin = {'x':<int>, 'z':<int>} 89 * radius = <int> 90 * players = <array> 91 * If players is not included, entities from all players are used. 92 */ 93 Damage.EntitiesNearPoint = function(origin, radius, players) 94 { 95 // If there is insufficient data return an empty array. 96 if (!origin || !radius) 97 return []; 98 //Create the dummy entity used for range calculations if it doesn't exist. 99 if (!Damage.dummyTargetEntity) 100 Damage.dummyTargetEntity = Engine.AddEntity('special/dummy'); 101 // Move the dummy entity to the origin of the query. 102 var cmpDummyPosition = Engine.QueryInterface(Damage.dummyTargetEntity, IID_Position); 103 cmpDummyPosition.JumpTo(origin.x, origin.z); 104 105 // If the players parameter is not specified use all players. 106 if (!players) 107 players = Engine.QueryInterface(SYSTEM_ENTITY, IID_PlayerManager).GetAllPlayers(); 108 109 // Call RangeManager with dummy entity and return the result. 110 var rangeManager = Engine.QueryInterface(SYSTEM_ENTITY, IID_RangeManager); 111 var rangeQuery = rangeManager.ExecuteQuery(Damage.dummyTargetEntity, 0, radius, players, IID_DamageReceiver); 112 return rangeQuery; 113 }; 114 115 /**************************************** 116 * Called when some units kills something (another unit, building, animal etc) 117 * killerEntity = <entity id> 118 * targetEntity = <entity id> 119 */ 120 Damage.TargetKilled = function(killerEntity, targetEntity) 121 { 122 // Add to killer statistics. 123 var cmpKillerPlayerStatisticsTracker = QueryOwnerInterface(killerEntity, IID_StatisticsTracker); 124 if (cmpKillerPlayerStatisticsTracker) 125 cmpKillerPlayerStatisticsTracker.KilledEntity(targetEntity); 126 // Add to loser statistics. 127 var cmpTargetPlayerStatisticsTracker = QueryOwnerInterface(targetEntity, IID_StatisticsTracker); 128 if (cmpTargetPlayerStatisticsTracker) 129 cmpTargetPlayerStatisticsTracker.LostEntity(targetEntity); 130 131 // If killer can collect loot, let's try to collect it. 132 var cmpLooter = Engine.QueryInterface(killerEntity, IID_Looter); 133 if (cmpLooter) 134 cmpLooter.Collect(targetEntity); 135 }; 136 137 // Gets the straight line distance between p1 and p2 138 Damage.VectorDistanceSquared = function(p1, p2) 139 { 140 return (p1.x - p2.x) * (p1.x - p2.x) + (p1.z - p2.z) * (p1.z - p2.z); 141 }; 142 143 // Gets the dot product of two vectors. 144 Damage.VectorDot = function(p1, p2) 145 { 146 return p1.x * p2.x + p1.z * p2.z; 147 }; 148 149 // Gets the 2D interpreted version of the cross product of two vectors. 150 Damage.VectorCross = function(p1, p2) 151 { 152 return p1.x * p2.z - p1.z * p2.x; 153 }; 154 155 Engine.RegisterGlobal("Damage", Damage); -
0ad/binaries/data/mods/public/simulation/components/Player.js
447 447 /** 448 448 * Check if given player is our enemy 449 449 */ 450 Player.prototype.GetEnemies = function() 451 { 452 var enemies = []; 453 for (var i = 0; i != this.diplomacy.length; i++) {if (this.diplomacy[i] < 0){enemies.push(i);}} 454 return enemies; 455 }; 456 457 /** 458 * Check if given player is our enemy 459 */ 450 460 Player.prototype.IsEnemy = function(id) 451 461 { 452 462 return this.diplomacy[id] < 0; -
0ad/binaries/data/mods/public/simulation/components/Attack.js
257 257 return undefined; 258 258 259 259 const targetClasses = cmpIdentity.GetClassesList(); 260 260 261 261 var minPref = null; 262 262 for each (var type in this.GetAttackTypes()) 263 263 { … … 296 296 const isAllowed = function (value, i, a) { return !attack.GetRestrictedClasses(value).some(isTargetClass); } 297 297 const isPreferred = function (value, i, a) { return attack.GetPreferredClasses(value).some(isTargetClass); } 298 298 const byPreference = function (a, b) { return (types.indexOf(a) + (isPreferred(a) ? types.length : 0) ) - (types.indexOf(b) + (isPreferred(b) ? types.length : 0) ); } 299 299 300 300 // Always slaughter domestic animals instead of using a normal attack 301 301 if (isTargetClass("Domestic") && this.template.Slaughter) 302 302 return "Slaughter"; … … 319 319 { 320 320 var prepare = +(this.template[type].PrepareTime || 0); 321 321 prepare = ApplyTechModificationsToEntity("Attack/" + type + "/PrepareTime", prepare, this.entity); 322 322 323 323 var repeat = +(this.template[type].RepeatTime || 1000); 324 324 repeat = ApplyTechModificationsToEntity("Attack/" + type + "/RepeatTime", repeat, this.entity); 325 325 … … 330 330 { 331 331 // Work out the attack values with technology effects 332 332 var self = this; 333 333 334 334 var template = this.template[type]; 335 335 var splash = ""; 336 336 if (!template) … … 338 338 template = this.template[type.split(".")[0]].Splash; 339 339 splash = "/Splash"; 340 340 } 341 341 342 342 var applyTechs = function(damageType) 343 343 { 344 344 // All causes caching problems so disable it for now. 345 345 //var allComponent = ApplyTechModificationsToEntity("Attack/" + type + splash + "/All", +(template[damageType] || 0), self.entity) - self.template[type][damageType]; 346 346 return ApplyTechModificationsToEntity("Attack/" + type + splash + "/" + damageType, +(template[damageType] || 0), self.entity); 347 347 }; 348 348 349 349 return { 350 350 hack: applyTechs("Hack"), 351 351 pierce: applyTechs("Pierce"), … … 357 357 { 358 358 var max = +this.template[type].MaxRange; 359 359 max = ApplyTechModificationsToEntity("Attack/" + type + "/MaxRange", max, this.entity); 360 360 361 361 var min = +(this.template[type].MinRange || 0); 362 362 min = ApplyTechModificationsToEntity("Attack/" + type + "/MinRange", min, this.entity); 363 363 364 364 return { "max": max, "min": min }; 365 365 }; 366 366 … … 371 371 var template = this.template[type]; 372 372 if (!template) 373 373 template = this.template[type.split(".")[0]].Splash; 374 374 375 375 if (template.Bonuses) 376 376 { 377 377 var cmpIdentity = Engine.QueryInterface(target, IID_Identity); 378 378 if (!cmpIdentity) 379 379 return 1; 380 380 381 381 // Multiply the bonuses for all matching classes 382 382 for (var key in template.Bonuses) 383 383 { 384 384 var bonus = template.Bonuses[key]; 385 385 386 386 var hasClasses = true; 387 387 if (bonus.Classes){ 388 388 var classes = bonus.Classes.split(/\s+/); … … 394 394 attackBonus *= bonus.Multiplier; 395 395 } 396 396 } 397 397 398 398 return attackBonus; 399 399 }; 400 400 401 401 // Returns a 2d random distribution scaled for a spread of scale 1. 402 402 // The current implementation is a 2d gaussian with sigma = 1 403 403 Attack.prototype.GetNormalDistribution = function(){ 404 404 405 405 // Use the Box-Muller transform to get a gaussian distribution 406 406 var a = Math.random(); 407 407 var b = Math.random(); 408 408 409 409 var c = Math.sqrt(-2*Math.log(a)) * Math.cos(2*Math.PI*b); 410 410 var d = Math.sqrt(-2*Math.log(a)) * Math.sin(2*Math.PI*b); 411 411 412 412 return [c, d]; 413 413 }; 414 414 … … 429 429 // Get some data about the entity 430 430 var horizSpeed = +this.template[type].ProjectileSpeed; 431 431 var gravity = 9.81; // this affects the shape of the curve; assume it's constant for now 432 432 433 433 var spread = this.template.Ranged.Spread; 434 434 spread = ApplyTechModificationsToEntity("Attack/Ranged/Spread", spread, this.entity); 435 435 436 436 //horizSpeed /= 2; gravity /= 2; // slow it down for testing 437 437 438 438 var cmpPosition = Engine.QueryInterface(this.entity, IID_Position); 439 439 if (!cmpPosition || !cmpPosition.IsInWorld()) 440 440 return; … … 443 443 if (!cmpTargetPosition || !cmpTargetPosition.IsInWorld()) 444 444 return; 445 445 var targetPosition = cmpTargetPosition.GetPosition(); 446 446 447 447 var relativePosition = {"x": targetPosition.x - selfPosition.x, "z": targetPosition.z - selfPosition.z} 448 448 var previousTargetPosition = Engine.QueryInterface(target, IID_Position).GetPreviousPosition(); 449 449 450 450 var targetVelocity = {"x": (targetPosition.x - previousTargetPosition.x) / this.turnLength, "z": (targetPosition.z - previousTargetPosition.z) / this.turnLength} 451 451 // the component of the targets velocity radially away from the archer 452 452 var radialSpeed = this.VectorDot(relativePosition, targetVelocity) / this.VectorLength(relativePosition); 453 453 454 454 var horizDistance = this.VectorDistance(targetPosition, selfPosition); 455 455 456 456 // This is an approximation of the time ot the target, it assumes that the target has a constant radial 457 457 // velocity, but since units move in straight lines this is not true. The exact value would be more 458 458 // difficult to calculate and I think this is sufficiently accurate. (I tested and for cavalry it was … … 462 462 // Predict where the unit is when the missile lands. 463 463 var predictedPosition = {"x": targetPosition.x + targetVelocity.x * timeToTarget, 464 464 "z": targetPosition.z + targetVelocity.z * timeToTarget}; 465 465 466 466 // Compute the real target point (based on spread and target speed) 467 467 var randNorm = this.GetNormalDistribution(); 468 468 var offsetX = randNorm[0] * spread * (1 + this.VectorLength(targetVelocity) / 20); … … 481 481 // Launch the graphical projectile 482 482 var cmpProjectileManager = Engine.QueryInterface(SYSTEM_ENTITY, IID_ProjectileManager); 483 483 var id = cmpProjectileManager.LaunchProjectileAtPoint(this.entity, realTargetPosition, horizSpeed, gravity); 484 484 485 var playerId = Engine.QueryInterface(this.entity, IID_Ownership).GetOwner() 485 486 var cmpTimer = Engine.QueryInterface(SYSTEM_ENTITY, IID_Timer); 486 cmpTimer.SetTimeout(this.entity, IID_Attack, "MissileHit", timeToTarget*1000, {"type": type, "target": target, "position": realTargetPosition, "direction": missileDirection, "projectileId": id });487 cmpTimer.SetTimeout(this.entity, IID_Attack, "MissileHit", timeToTarget*1000, {"type": type, "target": target, "position": realTargetPosition, "direction": missileDirection, "projectileId": id, "playerId":playerId}); 487 488 } 488 489 else 489 490 { 490 491 // Melee attack - hurt the target immediately 491 this.CauseDamage({"type": type, "target": target});492 Damage.CauseDamage({"strengths":this.GetAttackStrengths(type), "target":target, "attacker":this.entity, "multiplier":this.GetAttackBonus(type, target), "type":type}); 492 493 } 493 494 // TODO: charge attacks (need to design how they work) 494 495 }; 495 496 496 /**497 * Called when some units kills something (another unit, building, animal etc)498 */499 Attack.prototype.TargetKilled = function(killerEntity, targetEntity)500 {501 var cmpKillerPlayerStatisticsTracker = QueryOwnerInterface(killerEntity, IID_StatisticsTracker);502 if (cmpKillerPlayerStatisticsTracker) cmpKillerPlayerStatisticsTracker.KilledEntity(targetEntity);503 var cmpTargetPlayerStatisticsTracker = QueryOwnerInterface(targetEntity, IID_StatisticsTracker);504 if (cmpTargetPlayerStatisticsTracker) cmpTargetPlayerStatisticsTracker.LostEntity(targetEntity);505 506 // if unit can collect loot, lets try to collect it507 var cmpLooter = Engine.QueryInterface(killerEntity, IID_Looter);508 if (cmpLooter)509 {510 cmpLooter.Collect(targetEntity);511 }512 };513 514 497 Attack.prototype.InterpolatedLocation = function(ent, lateness) 515 498 { 516 499 var cmpTargetPosition = Engine.QueryInterface(ent, IID_Position); … … 533 516 return (p1.x * p2.x + p1.z * p2.z); 534 517 }; 535 518 536 Attack.prototype.VectorCross = function(p1, p2)537 {538 return (p1.x * p2.z - p1.z * p2.x);539 };540 541 519 Attack.prototype.VectorLength = function(p) 542 520 { 543 521 return Math.sqrt(p.x*p.x + p.z*p.z); … … 553 531 if (!cmpFootprint) 554 532 return false; 555 533 var targetShape = cmpFootprint.GetShape(); 556 534 557 535 if (!targetShape || !targetPosition) 558 536 return false; 559 537 560 538 if (targetShape.type === 'circle') 561 539 { 562 540 return (this.VectorDistance(point, targetPosition) < targetShape.radius); … … 564 542 else 565 543 { 566 544 var targetRotation = Engine.QueryInterface(ent, IID_Position).GetRotation().y; 567 545 568 546 var dx = point.x - targetPosition.x; 569 547 var dz = point.z - targetPosition.z; 570 548 571 549 var dxr = Math.cos(targetRotation) * dx - Math.sin(targetRotation) * dz; 572 550 var dzr = Math.sin(targetRotation) * dx + Math.cos(targetRotation) * dz; 573 551 574 552 return (-targetShape.width/2 <= dxr && dxr < targetShape.width/2 && -targetShape.depth/2 <= dzr && dzr < targetShape.depth/2); 575 553 } 576 554 }; … … 586 564 var friendlyFire = this.template.Ranged.Splash.FriendlyFire; 587 565 var splashRadius = this.template.Ranged.Splash.Range; 588 566 var splashShape = this.template.Ranged.Splash.Shape; 589 590 var ents = this.GetNearbyEntities(data.target, this.VectorDistance(data.position, targetPosition) * 2 + splashRadius, friendlyFire); 591 ents.push(data.target); // Add the original unit to the list of splash damage targets 592 593 for (var i = 0; i < ents.length; i++) 567 var playersToDamage; 568 569 // If friendlyFire isn't enabled, get all player enemies to pass to "Damage.CauseSplashDamage". 570 if (friendlyFire == false) 594 571 { 595 var entityPosition = this.InterpolatedLocation(ents[i], lateness); 596 var radius = this.VectorDistance(data.position, entityPosition); 597 598 if (radius < splashRadius) 599 { 600 var multiplier = 1; 601 if (splashShape == "Circular") // quadratic falloff 602 { 603 multiplier *= 1 - ((radius * radius) / (splashRadius * splashRadius)); 604 } 605 else if (splashShape == "Linear") 606 { 607 // position of entity relative to where the missile hit 608 var relPos = {"x": entityPosition.x - data.position.x, "z": entityPosition.z - data.position.z}; 609 610 var splashWidth = splashRadius / 5; 611 var parallelDist = this.VectorDot(relPos, data.direction); 612 var perpDist = Math.abs(this.VectorCross(relPos, data.direction)); 613 614 // Check that the unit is within the distance splashWidth of the line starting at the missile's 615 // landing point which extends in the direction of the missile for length splashRadius. 616 if (parallelDist > -splashWidth && perpDist < splashWidth) 617 { 618 // Use a quadratic falloff in both directions 619 multiplier = (splashRadius*splashRadius - parallelDist*parallelDist) / (splashRadius*splashRadius) 620 * (splashWidth*splashWidth - perpDist*perpDist) / (splashWidth*splashWidth); 621 } 622 else 623 { 624 multiplier = 0; 625 } 626 } 627 var newData = {"type": data.type + ".Splash", "target": ents[i], "damageMultiplier": multiplier}; 628 this.CauseDamage(newData); 629 } 572 var cmpPlayer = Engine.QueryInterface(Engine.QueryInterface(SYSTEM_ENTITY, IID_PlayerManager).GetPlayerByID(data.playerId), IID_Player) 573 playersToDamage = cmpPlayer.GetEnemies(); 630 574 } 575 // Damage the units. 576 Damage.CauseSplashDamage({"attacker":this.entity, "origin":data.position, "radius":splashRadius, "shape":splashShape, "strengths":this.GetAttackStrengths(data.type), "direction":data.direction, "playersToDamage":undefined}); 631 577 } 632 578 633 579 if (this.testCollision(data.target, data.position, lateness)) 634 580 { 581 data.attacker = this.entity 582 data.multiplier = this.GetAttackBonus(data.type, data.target) 583 data.strengths = this.GetAttackStrengths(data.type) 584 635 585 // Hit the primary target 636 this.CauseDamage(data);637 586 Damage.CauseDamage(data); 587 638 588 // Remove the projectile 639 589 var cmpProjectileManager = Engine.QueryInterface(SYSTEM_ENTITY, IID_ProjectileManager); 640 590 cmpProjectileManager.RemoveProjectile(data.projectileId); … … 642 592 else 643 593 { 644 594 // If we didn't hit the main target look for nearby units 645 var ents = this.GetNearbyEntities(data.target, this.VectorDistance(data.position, targetPosition) * 2); 646 595 var cmpPlayer = Engine.QueryInterface(Engine.QueryInterface(SYSTEM_ENTITY, IID_PlayerManager).GetPlayerByID(data.playerId), IID_Player) 596 var ents = Damage.EntitiesNearPoint(data.position, this.VectorDistance(data.position, targetPosition) * 2, cmpPlayer.GetEnemies()); 597 647 598 for (var i = 0; i < ents.length; i++) 648 599 { 649 600 if (this.testCollision(ents[i], data.position, lateness)) 650 601 { 651 var newData = {" type": data.type, "target": ents[i]};652 this.CauseDamage(newData);602 var newData = {"strengths":this.GetAttackStrengths(data.type), "target":ents[i], "attacker":this.entity, "multiplier":this.GetAttackBonus(data.type, ents[i]), "type":data.type}; 603 Damage.CauseDamage(newData); 653 604 654 605 // Remove the projectile 655 606 var cmpProjectileManager = Engine.QueryInterface(SYSTEM_ENTITY, IID_ProjectileManager); … … 659 610 } 660 611 }; 661 612 662 Attack.prototype.GetNearbyEntities = function(startEnt, range, friendlyFire)663 {664 var cmpPlayerManager = Engine.QueryInterface(SYSTEM_ENTITY, IID_PlayerManager);665 var cmpOwnership = Engine.QueryInterface(this.entity, IID_Ownership);666 var owner = cmpOwnership.GetOwner();667 var cmpPlayer = Engine.QueryInterface(cmpPlayerManager.GetPlayerByID(owner), IID_Player);668 var numPlayers = cmpPlayerManager.GetNumPlayers();669 var players = [];670 671 for (var i = 1; i < numPlayers; ++i)672 {673 // Only target enemies unless friendly fire is on674 if (cmpPlayer.IsEnemy(i) || friendlyFire)675 players.push(i);676 }677 678 var rangeManager = Engine.QueryInterface(SYSTEM_ENTITY, IID_RangeManager);679 return rangeManager.ExecuteQuery(startEnt, 0, range, players, IID_DamageReceiver);680 }681 682 /**683 * Inflict damage on the target684 */685 Attack.prototype.CauseDamage = function(data)686 {687 var strengths = this.GetAttackStrengths(data.type);688 689 var damageMultiplier = this.GetAttackBonus(data.type, data.target);690 if (data.damageMultiplier !== undefined)691 damageMultiplier *= data.damageMultiplier;692 693 var cmpDamageReceiver = Engine.QueryInterface(data.target, IID_DamageReceiver);694 if (!cmpDamageReceiver)695 return;696 var targetState = cmpDamageReceiver.TakeDamage(strengths.hack * damageMultiplier, strengths.pierce * damageMultiplier, strengths.crush * damageMultiplier);697 // if target killed pick up loot and credit experience698 if (targetState.killed == true)699 {700 this.TargetKilled(this.entity, data.target);701 }702 703 Engine.PostMessage(data.target, MT_Attacked,704 { "attacker": this.entity, "target": data.target, "type": data.type, "damage": -targetState.change });705 706 PlaySound("attack_impact", this.entity);707 };708 709 613 Attack.prototype.OnUpdate = function(msg) 710 614 { 711 615 this.turnLength = msg.turnLength; -
0ad/binaries/data/mods/public/simulation/components/PlayerManager.js
46 46 this.playerEntities = []; 47 47 }; 48 48 49 PlayerManager.prototype.GetAllPlayers = function() 50 { 51 return this.playerEntities; 52 }; 49 53 Engine.RegisterComponentType(IID_PlayerManager, "PlayerManager", PlayerManager);