Ticket #1910: Damage.js

File Damage.js, 6.2 KB (added by Josh, 11 years ago)

The new component with all essential functions working (At the moment this is more relevant to #1496, but I'm posting it here for consistency)

Line 
1function Damage() {}
2
3Damage.prototype.Schema = "<a:component type='system'/><empty/>";
4
5Damage.prototype.Init = function()
6{
7 // Create dummy entity for EntitiesNearPoint
8 this.dummyTargetEntity = Engine.AddEntity('special/dummy');
9}
10
11/****************************************
12 * Damages units around a given origin.
13 * data.attacker = <entity id>
14 * data.origin = {'x':<int>, 'z':<int>}
15 * data.radius = <int>
16 * data.shape = <string>
17 * data.strengths = {'hack':<float>, 'pierce':<float>, 'crush':<float>}
18 * ***Optional Variables***
19 * data.direction = <unit vector>
20 * data.playersToDamage = <array of player ids>
21 */
22Damage.prototype.CauseSplashDamage = function(data)
23{
24 // Get nearby entities and define variables
25 var nearEnts = this.EntitiesNearPoint(data.origin, data.radius, data.playersToDamage);
26 var damageMultiplier = 1;
27 // Cycle through all the nearby entities and damage it appropriately based on its distance from the origin.
28 for each (var entity in nearEnts)
29 {
30 var entityPosition = Engine.QueryInterface(entity, IID_Position).GetPosition();
31 if(data.shape=='circular') // circular effect with quadratic falloff in every direction
32 {
33 var squaredDistanceFromOrigin = this.VectorDistanceSquared(data.origin, entityPosition);
34 damageMultiplier == 1 - ((squaredDistanceFromOrigin) / (data.radius * data.radius));
35 }
36 else if(data.shape=='linear') // linear effect with quadratic falloff in two directions (only used for certain missiles)
37 {
38 // Get position of entity relative to splash origin.
39 var relativePos = {"x": entityPosition.x - data.origin.x, "z": entityPosition.z - data.origin.z};
40
41 // The width of linear splash is one fifth of the normal splash radius.
42 var width = data.radius/5;
43
44 // Effectivly rotate the axis to align with the missile direction.
45 var parallelDist = this.VectorDot(relativePos, data.direction);//x axis
46 var perpDist = Math.abs(this.VectorCross(relativePos, data.direction));//y axis
47
48 // Check that the unit is within the distance at which it will get damaged.
49 if (parallelDist > -width && perpDist < width) // If in radius, quadratic falloff in both directions
50 multiplier = (data.radius*data.radius - parallelDist*parallelDist) / (data.radius*data.radius)
51 * (width*width - perpDist*perpDist) / (width*width);
52 else
53 multiplier = 0;
54 }
55 else // In case someone calls this function with an invalid shape.
56 {
57 warn("The "+data.shape+" splash damage shape is not implemented!");
58 }
59 // Call CauseDamage which reduces the hitpoints, posts network command, plays sounds....
60 this.CauseDamage({"strengths":data.strengths, "target":entity, "attacker":data.attacker, "multiplier":damageMultiplier, "type":"Splash"})
61 }
62};
63
64/****************************************
65 * Causes damage on a given unit
66 * data.strengths = {'hack':<float>, 'pierce':<float>, 'crush':<float>}
67 * data.target = <entity id>
68 * data.attacker = <entity id>
69 * data.multiplier = <float between 1 and 0>
70 * data.type = <string>
71 */
72Damage.prototype.CauseDamage = function(data)
73{
74 // Check the target can be damaged otherwise don't do anything.
75 var cmpDamageReceiver = Engine.QueryInterface(data.target, IID_DamageReceiver);
76 if (!cmpDamageReceiver)
77 return;
78
79 // Damage the target
80 var targetState = cmpDamageReceiver.TakeDamage(data.strengths.hack * data.multiplier, data.strengths.pierce * data.multiplier, data.strengths.crush * data.multiplier);
81
82 // If the target was killed run some cleanup
83 if (targetState.killed == true)
84 this.TargetKilled(data.attacker, data.target);
85
86 // Post the network command (make it work in multiplayer)
87 Engine.PostMessage(data.target, MT_Attacked,{ "attacker": data.attacker, "target": data.target, "type": data.type, "damage": -targetState.change });
88
89 // Play attacking sounds
90 PlaySound("attack_impact", data.attacker);
91};
92
93/****************************************
94 * Gets entities near a give point for given players.
95 * origin = {'x':<int>, 'z':<int>}
96 * radius = <int>
97 * players = <array>
98 * If players is not included, entities from all players are used.
99 */
100Damage.prototype.EntitiesNearPoint = function(origin, radius, players)
101{
102 // If there is insufficient data return an empty array.
103 if(!origin || !radius)
104 return [];
105
106 // Move the dummy entity to the origin of the query.
107 var cmpDummyPosition = Engine.QueryInterface(this.dummyTargetEntity, IID_Position);
108 cmpDummyPosition.JumpTo(origin.x, origin.z);
109
110 // If the players parameter is not specified use all players.
111 if(!players)
112 players = Engine.QueryInterface(SYSTEM_ENTITY, IID_PlayerManager).GetAllPlayers();
113
114 // Call RangeManager with dummy entity and return the result.
115 var rangeManager = Engine.QueryInterface(SYSTEM_ENTITY, IID_RangeManager);
116 var rangeQuery = rangeManager.ExecuteQuery(this.dummyTargetEntity, 0, radius, players, IID_DamageReceiver);
117 return rangeQuery;
118};
119
120/****************************************
121 * Called when some units kills something (another unit, building, animal etc)
122 * killerEntity = <entity id>
123 * targetEntity = <entity id>
124 */
125Damage.prototype.TargetKilled = function(killerEntity, targetEntity)
126{
127 // Add to killer statistics.
128 var cmpKillerPlayerStatisticsTracker = QueryOwnerInterface(killerEntity, IID_StatisticsTracker);
129 if (cmpKillerPlayerStatisticsTracker)
130 cmpKillerPlayerStatisticsTracker.KilledEntity(targetEntity);
131 // Add to loser statistics.
132 var cmpTargetPlayerStatisticsTracker = QueryOwnerInterface(targetEntity, IID_StatisticsTracker);
133 if (cmpTargetPlayerStatisticsTracker)
134 cmpTargetPlayerStatisticsTracker.LostEntity(targetEntity);
135
136 // If killer can collect loot, lets try to collect it.
137 var cmpLooter = Engine.QueryInterface(killerEntity, IID_Looter);
138 if (cmpLooter)
139 cmpLooter.Collect(targetEntity);
140};
141
142// Gets the straight line distance between p1 and p2
143Damage.prototype.VectorDistanceSquared = function(p1, p2)
144{
145 return ((p1.x - p2.x)*(p1.x - p2.x) + (p1.z - p2.z)*(p1.z - p2.z));
146};
147
148// Gets the dot product of two vectors.
149Damage.prototype.VectorDot = function(p1, p2)
150{
151 return (p1.x * p2.x + p1.z * p2.z);
152};
153
154// Gets the 2D interpreted version of the cross product of two vectors.
155Damage.prototype.VectorCross = function(p1, p2)
156{
157 return (p1.x * p2.z - p1.z * p2.x);
158};
159
160Engine.RegisterComponentType(IID_Damage, "Damage", Damage);