mirror of
https://gitea.wildfiregames.com/0ad/0ad
synced 2026-06-18 14:23:56 -07:00
Refactor some formation code to let it make use of the prototypes
This was SVN commit r14693.
This commit is contained in:
parent
7bcdb9f46d
commit
c751500907
4 changed files with 61 additions and 73 deletions
|
|
@ -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
|
||||
//
|
||||
|
|
|
|||
|
|
@ -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.
|
||||
*/
|
||||
|
|
|
|||
|
|
@ -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];
|
||||
|
|
|
|||
|
|
@ -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; },
|
||||
});
|
||||
|
|
|
|||
Loading…
Reference in a new issue