Refactor some formation code to let it make use of the prototypes

This was SVN commit r14693.
This commit is contained in:
sanderd17 2014-01-27 12:34:59 +00:00
parent 7bcdb9f46d
commit c751500907
4 changed files with 61 additions and 73 deletions

View file

@ -166,6 +166,18 @@ Vector2D.div = function(v, f)
return new Vector2D(v.x / f, v.y / f);
};
Vector2D.avg = function(vectorList)
{
return Vector2D.sum(vectorList).div(vectorList.length);
};
Vector2D.sum = function(vectorList)
{
var sum = new Vector2D();
vectorList.forEach(function(v) {sum.add(v);});
return sum;
};
/////////////////////////////////////////////////////////////////////
// Vector3D
//

View file

@ -174,9 +174,7 @@ Formation.prototype.GetClosestMember = function(ent, filter)
continue;
var pos = cmpPosition.GetPosition2D();
var dx = entPosition.x - pos.x;
var dy = entPosition.y - pos.y;
var dist = dx * dx + dy * dy;
var dist = entPosition.distanceToSquared(pos);
if (dist < closestDistance)
{
closestMember = member;
@ -439,6 +437,9 @@ Formation.prototype.Disband = function()
*/
Formation.prototype.MoveMembersIntoFormation = function(moveCenter, force)
{
if (!this.members.length)
return;
var active = [];
var positions = [];
@ -452,19 +453,17 @@ Formation.prototype.MoveMembersIntoFormation = function(moveCenter, force)
// query the 2D position as exact hight calculation isn't needed
// but bring the position to the right coordinates
var pos = cmpPosition.GetPosition2D();
pos.z = pos.y;
pos.y = undefined;
positions.push(pos);
}
var avgpos = this.ComputeAveragePosition(positions);
var avgpos = Vector2D.avg(positions);
// Reposition the formation if we're told to or if we don't already have a position
var cmpPosition = Engine.QueryInterface(this.entity, IID_Position);
var inWorld = cmpPosition.IsInWorld();
if (moveCenter || !inWorld)
{
cmpPosition.JumpTo(avgpos.x, avgpos.z);
cmpPosition.JumpTo(avgpos.x, avgpos.y);
// Don't make the formation controller entity show up in range queries
if (!inWorld)
{
@ -493,7 +492,7 @@ Formation.prototype.MoveMembersIntoFormation = function(moveCenter, force)
this.oldOrientation = newOrientation;
var xMax = 0;
var zMax = 0;
var yMax = 0;
for (var i = 0; i < this.offsets.length; ++i)
{
@ -508,7 +507,7 @@ Formation.prototype.MoveMembersIntoFormation = function(moveCenter, force)
cmpUnitAI.ReplaceOrder("FormationWalk", {
"target": this.entity,
"x": offset.x,
"z": offset.z
"z": offset.y
});
}
else
@ -516,14 +515,14 @@ Formation.prototype.MoveMembersIntoFormation = function(moveCenter, force)
cmpUnitAI.PushOrderFront("FormationWalk", {
"target": this.entity,
"x": offset.x,
"z": offset.z
"z": offset.y
});
}
xMax = Math.max(xMax, offset.x);
zMax = Math.max(zMax, offset.z);
yMax = Math.max(yMax, offset.y);
}
this.width = xMax * 2;
this.depth = zMax * 2;
this.depth = yMax * 2;
};
Formation.prototype.MoveToMembersCenter = function()
@ -536,14 +535,14 @@ Formation.prototype.MoveToMembersCenter = function()
if (!cmpPosition || !cmpPosition.IsInWorld())
continue;
positions.push(cmpPosition.GetPosition());
positions.push(cmpPosition.GetPosition2D());
}
var avgpos = this.ComputeAveragePosition(positions);
var avgpos = Vector2D.avg(positions);
var cmpPosition = Engine.QueryInterface(this.entity, IID_Position);
var inWorld = cmpPosition.IsInWorld();
cmpPosition.JumpTo(avgpos.x, avgpos.z);
cmpPosition.JumpTo(avgpos.x, avgpos.y);
// Don't make the formation controller show up in range queries
if (!inWorld)
@ -658,7 +657,10 @@ Formation.prototype.ComputeFormationOffsets = function(active, positions)
var width = Math.sqrt(count) * (separation.width + separation.depth) * 2.5;
for (var i = 0; i < count; ++i)
offsets.push({"x": Math.random()*width, "z": Math.random()*width});
{
positionIndices.push({"row": 1, "column": i+1});
offsets.push(new Vector2D(Math.random()*width, Math.random()*width));
}
}
// For non-special formations, calculate the positions based on the number of entities
@ -707,7 +709,9 @@ Formation.prototype.ComputeFormationOffsets = function(active, positions)
x += side * centerGap / 2;
}
var column = Math.ceil(n/2) + Math.ceil(c/2) * side;
offsets.push({"x": x, "z": z, "row": r + 1, "column": column});
offsets.push(new Vector2D(x, z));
offsets[offsets.length - 1].row = r+1;
offsets[offsets.length - 1].column = column;
left--
}
++r;
@ -719,12 +723,8 @@ Formation.prototype.ComputeFormationOffsets = function(active, positions)
// make sure the average offset is zero, as the formation is centered around that
// calculating offset distances without a zero average makes no sense, as the formation
// will jump to a different position any time
var avgoffset = this.ComputeAveragePosition(offsets);
for each (var offset in offsets)
{
offset.x -= avgoffset.x;
offset.z -= avgoffset.z;
}
var avgoffset = Vector2D.avg(offsets);
offsets.forEach(function (o) {o.sub(avgoffset);});
// sort the available places in certain ways
// the places first in the list will contain the heaviest units as defined by the order
@ -733,24 +733,24 @@ Formation.prototype.ComputeFormationOffsets = function(active, positions)
offsets.sort(function(o1, o2) { return Math.abs(o1.x) < Math.abs(o2.x);});
else if (this.sortingOrder == "fillToTheCenter")
offsets.sort(function(o1, o2) {
return Math.max(Math.abs(o1.x), Math.abs(o1.z)) < Math.max(Math.abs(o2.x), Math.abs(o2.z));
return Math.max(Math.abs(o1.x), Math.abs(o1.y)) < Math.max(Math.abs(o2.x), Math.abs(o2.y));
});
// query the 2D position of the formation, and bring to the right coordinate system
// query the 2D position of the formation
var cmpPosition = Engine.QueryInterface(this.entity, IID_Position);
var formationPos = cmpPosition.GetPosition2D();
formationPos.z = formationPos.y;
formationPos.y = undefined;
// use realistic place assignment,
// every soldier searches the closest available place in the formation
var newOffsets = [];
var realPositions = this.GetRealOffsetPositions(offsets, formationPos);
for (var i = 0; i < sortingClasses.length; ++i)
for (var i = sortingClasses.length; i; --i)
{
var t = types[sortingClasses[i]];
var usedOffsets = offsets.splice(0,t.length);
var usedRealPositions = realPositions.splice(0, t.length);
var t = types[sortingClasses[i-1]];
if (!t.length)
continue;
var usedOffsets = offsets.splice(-t.length);
var usedRealPositions = realPositions.splice(-t.length);
for each (var entPos in t)
{
var closestOffsetId = this.TakeClosestOffset(entPos, usedRealPositions);
@ -776,9 +776,7 @@ Formation.prototype.TakeClosestOffset = function(entPos, realPositions)
var offsetDistanceSq = Infinity;
for (var i = 0; i < realPositions.length; i++)
{
var dx = realPositions[i].x - pos.x;
var dz = realPositions[i].z - pos.z;
var distSq = dx * dx + dz * dz;
var distSq = pos.distanceToSquared(realPositions[i]);
if (distSq < offsetDistanceSq)
{
offsetDistanceSq = distSq;
@ -798,12 +796,7 @@ Formation.prototype.GetRealOffsetPositions = function(offsets, pos)
var {sin, cos} = this.GetEstimatedOrientation(pos);
// calculate the world positions
for each (var o in offsets)
offsetPositions.push({
"x": pos.x + o.z * sin + o.x * cos,
"z": pos.z + o.z * cos - o.x * sin,
"row": o.row,
"column": o.column
});
offsetPositions.push(new Vector2D(pos.x + o.y * sin + o.x * cos, pos.y + o.y * cos - o.x * sin));
return offsetPositions;
};
@ -823,13 +816,11 @@ Formation.prototype.GetEstimatedOrientation = function(pos)
var targetPos = cmpUnitAI.GetTargetPositions();
if (!targetPos.length)
return r;
var dx = targetPos[0].x - pos.x;
var dz = targetPos[0].z - pos.z;
if (!dx && !dz)
var d = targetPos[0].sub(pos).normalize();
if (!d.x && !d.y)
return r;
var dist = Math.sqrt(dx * dx + dz * dz);
r.cos = dz / dist;
r.sin = dx / dist;
r.cos = d.y;
r.sin = d.x;
}
else
{
@ -843,18 +834,6 @@ Formation.prototype.GetEstimatedOrientation = function(pos)
return r;
};
Formation.prototype.ComputeAveragePosition = function(positions)
{
var sx = 0;
var sz = 0;
for each (var pos in positions)
{
sx += pos.x;
sz += pos.z;
}
return { "x": sx / positions.length, "z": sz / positions.length };
};
/**
* Set formation controller's radius and speed based on its current members.
*/

View file

@ -4598,7 +4598,7 @@ UnitAI.prototype.GetTargetPositions = function()
case "WalkToPointRange":
case "MoveIntoFormation":
case "GatherNearPosition":
targetPositions.push({"x" : order.data.x, "z" : order.data.z})
targetPositions.push(new Vector2D(order.data.x, order.data.z));
break; // and continue the loop
case "WalkToTarget":
@ -4616,8 +4616,7 @@ UnitAI.prototype.GetTargetPositions = function()
var cmpTargetPosition = Engine.QueryInterface(order.data.target, IID_Position);
if (!cmpTargetPosition || !cmpTargetPosition.IsInWorld())
return targetPositions;
var targetPos = cmpTargetPosition.GetPosition2D();
targetPositions.push({"x" : targetPos.x, "z" : targetPos.y})
targetPositions.push(cmpTargetPosition.GetPosition2D());
return targetPositions;
case "Stop":
@ -4645,13 +4644,11 @@ UnitAI.prototype.ComputeWalkingDistance = function()
return 0;
// Keep track of the position at the start of each order
var pos = cmpPosition.GetPosition();
var pos = cmpPosition.GetPosition2D();
var targetPositions = this.GetTargetPositions();
for (var i = 0; i < targetPositions.length; i++)
{
var dx = targetPositions[i].x - pos.x;
var dz = targetPositions[i].z - pos.z;
distance += Math.sqrt(dx*dx + dz*dz);
distance += pos.distanceTo(targetPositions[i]);
// Remember this as the start position for the next order
pos = targetPositions[i];

View file

@ -72,8 +72,8 @@ function TestFormationExiting(mode)
});
AddMock(unit, IID_Position, {
GetPosition: function() { return { "x": 0, "y": 0,"z": 0 }; },
GetPosition2D: function() { return { "x": 0, "y": 0 }; },
GetPosition: function() { return new Vector3D(); },
GetPosition2D: function() { return new Vector2D(); },
GetRotation: function() { return { "y": 0 }; },
IsInWorld: function() { return true; },
});
@ -123,8 +123,8 @@ function TestFormationExiting(mode)
AddMock(controller, IID_Position, {
JumpTo: function(x, z) { this.x = x; this.z = z; },
GetPosition: function() { return { "x": this.x, "z": this.z }; },
GetPosition2D: function() { return { "x": this.x, "y": this.z }; },
GetPosition: function() { return new Vector3D(this.x, 0, this.z); },
GetPosition2D: function() { return new Vector2D(this.x, this.z); },
GetRotation: function() { return { "y": 0 }; },
IsInWorld: function() { return true; },
});
@ -218,8 +218,8 @@ function TestMoveIntoFormationWhileAttacking()
});
AddMock(unit + i, IID_Position, {
GetPosition: function() { return { "x": 0, "z": 0 }; },
GetPosition2D: function() { return { "x": 0, "y": 0 }; },
GetPosition: function() { return new Vector3D(); },
GetPosition2D: function() { return new Vector2D(); },
GetRotation: function() { return { "y": 0 }; },
IsInWorld: function() { return true; },
});
@ -262,8 +262,8 @@ function TestMoveIntoFormationWhileAttacking()
AddMock(controller, IID_Position, {
JumpTo: function(x, z) { this.x = x; this.z = z; },
GetPosition: function() { return { "x": this.x, "z": this.z }; },
GetPosition2D: function() { return { "x": this.x, "y": this.z }; },
GetPosition: function() { return new Vector3D(this.x, 0, this.z); },
GetPosition2D: function() { return new Vector2D(this.x, this.z); },
GetRotation: function() { return { "y": 0 }; },
IsInWorld: function() { return true; },
});