Changeset View
Standalone View
binaries/data/mods/public/simulation/components/Formation.js
Show First 20 Lines • Show All 67 Lines • ▼ Show 20 Lines | Formation.prototype.Schema = | |||||||||||||||||||
"</element>" + | "</element>" + | |||||||||||||||||||
"<element name='AnimationVariants' a:help='Give a list of animation variants to use for the particular formation members, based on their positions.'>" + | "<element name='AnimationVariants' a:help='Give a list of animation variants to use for the particular formation members, based on their positions.'>" + | |||||||||||||||||||
"<text a:help='example text: “1..1,1..-1:animationVariant1;2..2,1..-1;animationVariant2”, this will set animationVariant1 for the first row, and animation2 for the second row. The first part of the numbers (1..1 and 2..2) means the row range. Every row between (and including) those values will switch animationvariants. The second part of the numbers (1..-1) denote the columns inside those rows that will be affected. Note that in both cases, you can use -1 for the last row/column, -2 for the second to last, etc.'/>" + | "<text a:help='example text: “1..1,1..-1:animationVariant1;2..2,1..-1;animationVariant2”, this will set animationVariant1 for the first row, and animation2 for the second row. The first part of the numbers (1..1 and 2..2) means the row range. Every row between (and including) those values will switch animationvariants. The second part of the numbers (1..-1) denote the columns inside those rows that will be affected. Note that in both cases, you can use -1 for the last row/column, -2 for the second to last, etc.'/>" + | |||||||||||||||||||
"</element>"; | "</element>"; | |||||||||||||||||||
// Distance at which we'll switch between column/box formations. | // Distance at which we'll switch between column/box formations. | |||||||||||||||||||
var g_ColumnDistanceThreshold = 128; | var g_ColumnDistanceThreshold = 128; | |||||||||||||||||||
// Turning angle over which formation positions are recomputed. | ||||||||||||||||||||
Formation.prototype.turningAngleThreshold = Math.PI / 4; | ||||||||||||||||||||
marder: @Stan @bb
On the one hand it seems sensible to put this into the in a config or in the Scheme… | ||||||||||||||||||||
Done Inline ActionsAlthough without checking it: this.lastOrderVariant sounds like it would give me the current formation marder: Although without checking it:
```
this.lastOrderVariant
```
sounds like it would give me… | ||||||||||||||||||||
Done Inline Actions
This allows mods to override the value more easily. Freagarach: This allows mods to override the value more easily. | ||||||||||||||||||||
Done Inline Actions
Stan: | ||||||||||||||||||||
bbUnsubmitted Done Inline ActionsArguably this should be a template setting, settable per formation. bb: Arguably this should be a template setting, settable per formation. | ||||||||||||||||||||
Not Done Inline Actionsconst bb: const | ||||||||||||||||||||
Done Inline ActionsCertainly not, for it would prevent modders from overriding the value. (#6094) Freagarach: Certainly not, for it would prevent modders from overriding the value. (#6094) | ||||||||||||||||||||
Formation.prototype.variablesToSerialize = [ | Formation.prototype.variablesToSerialize = [ | |||||||||||||||||||
"lastOrderVariant", | "lastOrderVariant", | |||||||||||||||||||
"members", | "members", | |||||||||||||||||||
"memberPositions", | "memberPositions", | |||||||||||||||||||
"maxRowsUsed", | "maxRowsUsed", | |||||||||||||||||||
"maxColumnsUsed", | "maxColumnsUsed", | |||||||||||||||||||
"finishedEntities", | "finishedEntities", | |||||||||||||||||||
"idleEntities", | "idleEntities", | |||||||||||||||||||
"columnar", | "columnar", | |||||||||||||||||||
"rearrange", | "rearrange", | |||||||||||||||||||
"formationMembersWithAura", | "formationMembersWithAura", | |||||||||||||||||||
"width", | "width", | |||||||||||||||||||
"depth", | "depth", | |||||||||||||||||||
"twinFormations", | "twinFormations", | |||||||||||||||||||
"formationSeparation", | "formationSeparation", | |||||||||||||||||||
"offsets" | "offsets" | |||||||||||||||||||
]; | ]; | |||||||||||||||||||
Formation.prototype.Init = function(deserialized = false) | Formation.prototype.Init = function(deserialized = false) | |||||||||||||||||||
{ | { | |||||||||||||||||||
this.sortingClasses = this.template.SortingClasses.split(/\s+/g); | this.sortingClasses = this.template.SortingClasses.split(/\s+/g); | |||||||||||||||||||
Done Inline ActionsI did just store the radians in the template, instead on insisting on degrees and converting here. (Could be that I am too much of a mathematician preferring radians in any case). Also for the instantTurnAngle (see turnRates) we use radians. bb: I did just store the radians in the template, instead on insisting on degrees and converting… | ||||||||||||||||||||
Done Inline ActionsI opted for this because when I tried to put in pi/2 as template value it didn't work. (And I like to think in degrees) If I understood @Freagarach correctly, they suggested to use it as a multiplier for pi, but that seemed like more complicated to understand. marder: I opted for this because when I tried to put in pi/2 as template value it didn't work. (And I… | ||||||||||||||||||||
Done Inline ActionsPi is not a valid entry in the template indeed. One can round off though (and one need not be very exact anyhow). bb: `Pi` is not a valid entry in the template indeed. One can round off though (and one need not be… | ||||||||||||||||||||
this.shiftRows = this.template.ShiftRows == "true"; | this.shiftRows = this.template.ShiftRows == "true"; | |||||||||||||||||||
this.separationMultiplier = { | this.separationMultiplier = { | |||||||||||||||||||
"width": +this.template.UnitSeparationWidthMultiplier, | "width": +this.template.UnitSeparationWidthMultiplier, | |||||||||||||||||||
"depth": +this.template.UnitSeparationDepthMultiplier | "depth": +this.template.UnitSeparationDepthMultiplier | |||||||||||||||||||
}; | }; | |||||||||||||||||||
this.sloppiness = +this.template.Sloppiness; | this.sloppiness = +this.template.Sloppiness; | |||||||||||||||||||
this.widthDepthRatio = +this.template.WidthDepthRatio; | this.widthDepthRatio = +this.template.WidthDepthRatio; | |||||||||||||||||||
this.minColumns = +(this.template.MinColumns || 0); | this.minColumns = +(this.template.MinColumns || 0); | |||||||||||||||||||
▲ Show 20 Lines • Show All 386 Lines • ▼ Show 20 Lines | ||||||||||||||||||||
*/ | */ | |||||||||||||||||||
Formation.prototype.MoveMembersIntoFormation = function(moveCenter, force, variant) | Formation.prototype.MoveMembersIntoFormation = function(moveCenter, force, variant) | |||||||||||||||||||
{ | { | |||||||||||||||||||
if (!this.members.length) | if (!this.members.length) | |||||||||||||||||||
return; | return; | |||||||||||||||||||
let active = []; | let active = []; | |||||||||||||||||||
let positions = []; | let positions = []; | |||||||||||||||||||
let rotations = 0; | ||||||||||||||||||||
for (let ent of this.members) | for (let ent of this.members) | |||||||||||||||||||
{ | { | |||||||||||||||||||
let cmpPosition = Engine.QueryInterface(ent, IID_Position); | let cmpPosition = Engine.QueryInterface(ent, IID_Position); | |||||||||||||||||||
if (!cmpPosition || !cmpPosition.IsInWorld()) | if (!cmpPosition || !cmpPosition.IsInWorld()) | |||||||||||||||||||
continue; | continue; | |||||||||||||||||||
active.push(ent); | active.push(ent); | |||||||||||||||||||
// Query the 2D position as the exact height calculation isn't needed, | // Query the 2D position as the exact height calculation isn't needed, | |||||||||||||||||||
// but bring the position to the correct coordinates. | // but bring the position to the correct coordinates. | |||||||||||||||||||
positions.push(cmpPosition.GetPosition2D()); | positions.push(cmpPosition.GetPosition2D()); | |||||||||||||||||||
rotations += cmpPosition.GetRotation().y; | ||||||||||||||||||||
Done Inline ActionsDoes formation form in direction of most units are facing now? Silier: Does formation form in direction of most units are facing now? | ||||||||||||||||||||
Done Inline Actionsmarder: {F2475731}
Looks like they use the median rotation of all units | ||||||||||||||||||||
Done Inline Actionsmy bad it was always 0. Should be taken care of now marder: my bad it was always 0. Should be taken care of now | ||||||||||||||||||||
} | } | |||||||||||||||||||
let avgpos = Vector2D.average(positions); | let sharpTurnFlag = false; | |||||||||||||||||||
Done Inline Actions
Freagarach: | ||||||||||||||||||||
let cmpPosition = Engine.QueryInterface(this.entity, IID_Position); | let cmpPosition = Engine.QueryInterface(this.entity, IID_Position); | |||||||||||||||||||
// Reposition the formation if we're told to or if we don't already have a position. | // Reposition the formation if we're told to or if we don't already have a position. | |||||||||||||||||||
if (moveCenter || (cmpPosition && !cmpPosition.IsInWorld())) | if (moveCenter || (cmpPosition && !cmpPosition.IsInWorld())) | |||||||||||||||||||
this.SetupPositionAndHandleRotation(avgpos.x, avgpos.y, rotations / active.length); | { | |||||||||||||||||||
const oldRotation = cmpPosition.GetRotation().y; | ||||||||||||||||||||
const avgpos = Vector2D.average(positions); | ||||||||||||||||||||
this.lastOrderVariant = variant; | ||||||||||||||||||||
// Switch between column and box if necessary. | // Switch between column and box if necessary. | |||||||||||||||||||
let cmpFormationUnitAI = Engine.QueryInterface(this.entity, IID_UnitAI); | const cmpFormationUnitAI = Engine.QueryInterface(this.entity, IID_UnitAI); | |||||||||||||||||||
let walkingDistance = cmpFormationUnitAI.ComputeWalkingDistance(); | const walkingDistance = cmpFormationUnitAI.ComputeWalkingDistance(); | |||||||||||||||||||
bbUnsubmitted Done Inline Actionsinline bb: inline | ||||||||||||||||||||
let columnar = walkingDistance > g_ColumnDistanceThreshold; | const columnar = walkingDistance > g_ColumnDistanceThreshold; | |||||||||||||||||||
if (columnar != this.columnar) | if (columnar != this.columnar) | |||||||||||||||||||
{ | { | |||||||||||||||||||
this.columnar = columnar; | this.columnar = columnar; | |||||||||||||||||||
this.offsets = undefined; | this.offsets = undefined; | |||||||||||||||||||
} | } | |||||||||||||||||||
Done Inline Actions
bb: | ||||||||||||||||||||
let newRotation = oldRotation; | ||||||||||||||||||||
const targetPosition = cmpFormationUnitAI.GetTargetPositions()[0]; | ||||||||||||||||||||
bbUnsubmitted Done Inline ActionsWhen the distance is very small, the angle can basically face anywhere. Especially when just changing the formation shape (or even selecting the shape it already has), the orientation of the formation will be changing. bb: When the distance is very small, the angle can basically face anywhere. Especially when just… | ||||||||||||||||||||
marderAuthorUnsubmitted Done Inline ActionsShould be fixed now. The 1 is very arbitrary, but when testing there is no more rotation when rearranging the formation at the same point and all other commands work as well. marder: Should be fixed now. The 1 is very arbitrary, but when testing there is no more rotation when… | ||||||||||||||||||||
Done Inline Actions
Avoid the sqrt. bb: Avoid the sqrt. | ||||||||||||||||||||
Done Inline Actionslol. that is one unintuitive named function. marder: lol. that is one unintuitive named function. | ||||||||||||||||||||
Done Inline ActionsThe 1 is a bit magical indeed. Make it a global g_RotateDistanceThreshold. Don't think it makes sense to add this to the template (since the deviations are caused by imprecisions in the unit moving to the positions and roundoff errors) bb: The `1` is a bit magical indeed. Make it a global `g_RotateDistanceThreshold`. Don't think it… | ||||||||||||||||||||
Done Inline Actionsyeah makes sense to make it global marder: yeah makes sense to make it global | ||||||||||||||||||||
Not Done Inline Actionswhy not part of formation prototype but global? Silier: why not part of formation prototype but global? | ||||||||||||||||||||
Done Inline ActionsI think because it is a magic value either way and I can't think of a benefit for having individual values for different formations marder: I think because it is a magic value either way and I can't think of a benefit for having… | ||||||||||||||||||||
if (targetPosition !== undefined) | ||||||||||||||||||||
Done Inline ActionsThe formation might not have target positions (e.g. after construction). Freagarach: The formation might not have target positions (e.g. after construction). | ||||||||||||||||||||
newRotation = avgpos.angleTo(targetPosition); | ||||||||||||||||||||
Done Inline ActionsTurnTo is not present in the test_UnitAI.js. Freagarach: `TurnTo` is not present in the `test_UnitAI.js`. | ||||||||||||||||||||
Done Inline Actions
It may be safer to do this ^. Freagarach: It may be safer to do this ^.
(Check whether the targetPosition exists before feeding it to the… | ||||||||||||||||||||
Done Inline Actionsiirc actually not. GetTargetPositions() returns the current position if no target position is given, which means that avgpos.angleTo(cmpFormationUnitAI.GetTargetPositions()[0]); actually safely returns 0 in that case, while getTargetPosition always returns something non-zero, resulting in the unwanted behavoir that formations without a target always turn in the same direction, instead of the current one. marder: iirc actually not.
```
GetTargetPositions()
```
returns the current position if no target… | ||||||||||||||||||||
Done Inline ActionsGetTargetPositions() returns [] with no orders present? Freagarach: `GetTargetPositions()` returns `[]` with no orders present? | ||||||||||||||||||||
Done Inline Actionsno : p At least not in this context. See the output when I print out the numbers here (with no order given): warn(uneval(cmpFormationUnitAI.GetTargetPositions())) WARNING: [{x:598.5147399902344, y:800.5150451660156}] warn(uneval(avgpos)); WARNING: ({x:598.5147399902344, y:800.5150451660156}) marder: no : p
At least not in this context. See the output when I print out the numbers here (with no… | ||||||||||||||||||||
Done Inline ActionsERROR: Error in timer on entity 556, IID105, function TimerHandler: TypeError: v is undefined Vector2D.prototype.angleTo@globalscripts/vector.js:186:2 Formation.prototype.MoveMembersIntoFormation@simulation/components/Formation.js:533:26 Timer@simulation/components/UnitAI.js:1011:19 FSM.prototype.ProcessMessage@globalscripts/FSM.js:265:17 UnitAI.prototype.TimerHandler@simulation/components/UnitAI.js:4210:15 Timer.prototype.OnUpdate@simulation/components/Timer.js:139:44 Freagarach: ```
ERROR: Error in timer on entity 556, IID105, function TimerHandler: TypeError: v is… | ||||||||||||||||||||
Done Inline ActionsSo the current state is certainly not the most elegant js, but it was the only way I found to get rid of the error. marder: So the current state is certainly not the most elegant js, but it was the only way I found to… | ||||||||||||||||||||
Done Inline ActionsDo you really need to do something in that case? Can't you just early return? Stan: Do you really need to do something in that case? Can't you just early return? | ||||||||||||||||||||
bbUnsubmitted Done Inline ActionsI am a bit worried about this line (which arguably is the base of this patch). In most cases it will look okay, since in most cases we will move in the direction of the target immediately. However, ideally we did orient the formation in the direction of the path leading to the target (which could be different since the path does not need to be straight). Obviously doing so requires the pathing to be done, information not available at this point in the code. Waiting on the path isn't possible either since that would take another turn. So that will appear laggy. Probably best to leave the code like this. Optionally adding a comment. bb: I am a bit worried about this line (which arguably is the base of this patch). In most cases it… | ||||||||||||||||||||
marderAuthorUnsubmitted Done Inline ActionsI tested it with a few obstructions between the formation and the target and I saw nothing that looks particularly "buggy". Therefore don't think there needs to be a comment. If you can find a situation where there is strange behavior I can add one. marder: I tested it with a few obstructions between the formation and the target and I saw nothing that… | ||||||||||||||||||||
cmpPosition.TurnTo(newRotation); | ||||||||||||||||||||
sharpTurnFlag = !this.areAnglesSimilar(newRotation, oldRotation); | ||||||||||||||||||||
bbUnsubmitted Done Inline ActionsOnly used once, so should be inlined bb: Only used once, so should be inlined | ||||||||||||||||||||
} | ||||||||||||||||||||
this.lastOrderVariant = variant; | ||||||||||||||||||||
let offsetsChanged = false; | let offsetsChanged = false; | |||||||||||||||||||
let newOrientation = this.GetEstimatedOrientation(avgpos); | if (!this.offsets || sharpTurnFlag) | |||||||||||||||||||
if (!this.offsets) | ||||||||||||||||||||
{ | { | |||||||||||||||||||
this.offsets = this.ComputeFormationOffsets(active, positions); | this.offsets = this.ComputeFormationOffsets(active, positions); | |||||||||||||||||||
offsetsChanged = true; | offsetsChanged = true; | |||||||||||||||||||
} | } | |||||||||||||||||||
let xMax = 0; | let xMax = 0; | |||||||||||||||||||
let yMax = 0; | let yMax = 0; | |||||||||||||||||||
let xMin = 0; | let xMin = 0; | |||||||||||||||||||
▲ Show 20 Lines • Show All 259 Lines • ▼ Show 20 Lines | Formation.prototype.ComputeFormationOffsets = function(active, positions) | |||||||||||||||||||
if (sortingOrder == "fillFromTheSides") | if (sortingOrder == "fillFromTheSides") | |||||||||||||||||||
offsets.sort(function(o1, o2) { return Math.abs(o1.x) < Math.abs(o2.x);}); | offsets.sort(function(o1, o2) { return Math.abs(o1.x) < Math.abs(o2.x);}); | |||||||||||||||||||
else if (sortingOrder == "fillToTheCenter") | else if (sortingOrder == "fillToTheCenter") | |||||||||||||||||||
offsets.sort(function(o1, o2) { | offsets.sort(function(o1, o2) { | |||||||||||||||||||
return Math.max(Math.abs(o1.x), Math.abs(o1.y)) < Math.max(Math.abs(o2.x), Math.abs(o2.y)); | 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. | // Query the 2D position of the formation. | |||||||||||||||||||
let cmpPosition = Engine.QueryInterface(this.entity, IID_Position); | const realPositions = this.GetRealOffsetPositions(offsets); | |||||||||||||||||||
Done Inline Actions
Stan: | ||||||||||||||||||||
let formationPos = cmpPosition.GetPosition2D(); | ||||||||||||||||||||
// Use realistic place assignment, | // Use realistic place assignment, | |||||||||||||||||||
// every soldier searches the closest available place in the formation. | // every soldier searches the closest available place in the formation. | |||||||||||||||||||
let newOffsets = []; | let newOffsets = []; | |||||||||||||||||||
let realPositions = this.GetRealOffsetPositions(offsets, formationPos); | for (const i of sortingClasses.reverse()) | |||||||||||||||||||
for (let i = sortingClasses.length; i; --i) | ||||||||||||||||||||
{ | { | |||||||||||||||||||
let t = types[sortingClasses[i - 1]]; | const t = types[i]; | |||||||||||||||||||
if (!t.length) | if (!t.length) | |||||||||||||||||||
continue; | continue; | |||||||||||||||||||
let usedOffsets = offsets.splice(-t.length); | let usedOffsets = offsets.splice(-t.length); | |||||||||||||||||||
let usedRealPositions = realPositions.splice(-t.length); | let usedRealPositions = realPositions.splice(-t.length); | |||||||||||||||||||
for (let entPos of t) | for (let entPos of t) | |||||||||||||||||||
{ | { | |||||||||||||||||||
let closestOffsetId = this.TakeClosestOffset(entPos, usedRealPositions, usedOffsets); | let closestOffsetId = this.TakeClosestOffset(entPos, usedRealPositions, usedOffsets); | |||||||||||||||||||
usedRealPositions.splice(closestOffsetId, 1); | usedRealPositions.splice(closestOffsetId, 1); | |||||||||||||||||||
Show All 28 Lines | Formation.prototype.TakeClosestOffset = function(entPos, realPositions, offsets) | |||||||||||||||||||
} | } | |||||||||||||||||||
this.memberPositions[entPos.ent] = { "row": offsets[closestOffsetId].row, "column": offsets[closestOffsetId].column }; | this.memberPositions[entPos.ent] = { "row": offsets[closestOffsetId].row, "column": offsets[closestOffsetId].column }; | |||||||||||||||||||
return closestOffsetId; | return closestOffsetId; | |||||||||||||||||||
}; | }; | |||||||||||||||||||
/** | /** | |||||||||||||||||||
* Get the world positions for a list of offsets in this formation. | * Get the world positions for a list of offsets in this formation. | |||||||||||||||||||
*/ | */ | |||||||||||||||||||
Formation.prototype.GetRealOffsetPositions = function(offsets, pos) | Formation.prototype.GetRealOffsetPositions = function(offsets) | |||||||||||||||||||
{ | { | |||||||||||||||||||
const cmpPosition = Engine.QueryInterface(this.entity, IID_Position); | ||||||||||||||||||||
Done Inline ActionsThe linter will have a opinion on this. :) Freagarach: The linter will have a opinion on this. :) | ||||||||||||||||||||
const pos = cmpPosition.GetPosition2D(); | ||||||||||||||||||||
Done Inline ActionsWe usally don't assign variables like this. Freagarach: We usally don't assign variables like this. | ||||||||||||||||||||
const rot = cmpPosition.GetRotation().y; | ||||||||||||||||||||
const sin = Math.sin(rot); | ||||||||||||||||||||
const cos = Math.cos(rot); | ||||||||||||||||||||
let offsetPositions = []; | let offsetPositions = []; | |||||||||||||||||||
let { sin, cos } = this.GetEstimatedOrientation(pos); | ||||||||||||||||||||
// Calculate the world positions. | // Calculate the world positions. | |||||||||||||||||||
for (let o of offsets) | for (let o of offsets) | |||||||||||||||||||
offsetPositions.push(new Vector2D(pos.x + o.y * sin + o.x * cos, pos.y + o.y * cos - o.x * sin)); | offsetPositions.push(new Vector2D(pos.x + o.y * sin + o.x * cos, pos.y + o.y * cos - o.x * sin)); | |||||||||||||||||||
return offsetPositions; | return offsetPositions; | |||||||||||||||||||
}; | }; | |||||||||||||||||||
/** | /** | |||||||||||||||||||
* Calculate the estimated rotation of the formation based on the current rotation. | * Returns true if the two given angles (in radiants) make a little enough difference. | |||||||||||||||||||
* Return the sine and cosine of the angle. | * A difference is little enough if it's ok for a formation to make such a turn without | |||||||||||||||||||
* reassigning positions. | ||||||||||||||||||||
Done Inline Actions
bb: | ||||||||||||||||||||
*/ | */ | |||||||||||||||||||
Formation.prototype.GetEstimatedOrientation = function(pos) | ||||||||||||||||||||
Formation.prototype.areAnglesSimilar = function(a1, a2) | ||||||||||||||||||||
{ | { | |||||||||||||||||||
let r = {}; | const d = Math.abs(a1 - a2); | |||||||||||||||||||
let cmpPosition = Engine.QueryInterface(this.entity, IID_Position); | return d < this.turningAngleThreshold || d > 2 * Math.PI - this.turningAngleThreshold; | |||||||||||||||||||
Done Inline Actions
Stan: | ||||||||||||||||||||
bbUnsubmitted Done Inline ActionsThis assumes that the angles a1 and a2 lie in the interval [0,2Pi]. Either state this assumption in a comment, or generalise the function. bb: This assumes that the angles `a1` and `a2` lie in the interval `[0,2Pi]`. Either state this… | ||||||||||||||||||||
Done Inline Actions
I know I proposed the comment as an option too, but isn't this just a general solution? bb: I know I proposed the comment as an option too, but isn't this just a general solution? | ||||||||||||||||||||
Done Inline ActionsMh no, you would need to take the modulo for both values individually let a = 360; let b = 80; (Math.abs(a - b ) % 360; // return: 280 Math.abs((a % 360) - (b % 360)); // return: 80 marder: Mh no, you would need to take the modulo for both values individually
Not sure if this is more… | ||||||||||||||||||||
Done Inline ActionsSure the value of d might change to 2Pi - d, but that is indifferent seeing the return value on the next line. bb: Sure the value of `d` might change to `2Pi - d`, but that is indifferent seeing the return… | ||||||||||||||||||||
Done Inline Actionsmy thought was the using the modulo on both inputs would simplify the return to marder: my thought was the using the modulo on both inputs would simplify the return to
`return d <… | ||||||||||||||||||||
Done Inline ActionsThat won't work in any case since if a_1=2\pi-eps and a_2=0, we will always be in the second case of the return value. bb: That won't work in any case since if `a_1=2\pi-eps` and `a_2=0`, we will always be in the… | ||||||||||||||||||||
Done Inline Actionsah yes. true. In that case it is only the question if the the extra calculation is worth the generalization. marder: ah yes. true.
In that case it is only the question if the the extra calculation is worth the… | ||||||||||||||||||||
if (!cmpPosition) | ||||||||||||||||||||
return r; | ||||||||||||||||||||
let rot = cmpPosition.GetRotation().y; | ||||||||||||||||||||
r.sin = Math.sin(rot); | ||||||||||||||||||||
r.cos = Math.cos(rot); | ||||||||||||||||||||
return r; | ||||||||||||||||||||
}; | }; | |||||||||||||||||||
Done Inline ActionsThis seems like a property of Vector2D to me? Freagarach: This seems like a property of `Vector2D` to me?
(globalscripts/vector.js) | ||||||||||||||||||||
/** | /** | |||||||||||||||||||
* Set formation controller's speed based on its current members. | * Set formation controller's speed based on its current members. | |||||||||||||||||||
Done Inline Actions@alre not sure I understand your last comment on this. return Math.atan(vector.y, vector.x) give be a different (worse) turning behavior marder: @alre not sure I understand your last comment on this.
You said this could be replaces with… | ||||||||||||||||||||
Done Inline Actionsswitch x and y. it looks weird to me, because math.atan should take y as first argument, but I think in 0AD it's the opposite somehow. alre: switch x and y. it looks weird to me, because math.atan should take y as first argument, but I… | ||||||||||||||||||||
Done Inline Actionsreturn Math.atan(vector.x, vector.y); still gives me something different (and a bit worse behavior wise) than return Math.PI/2 - Math.atan2(vector.y, vector.x); idk, that's where my math knowledge stops. marder: ```
return Math.atan(vector.x, vector.y);
```
still gives me something different (and a bit… | ||||||||||||||||||||
*/ | */ | |||||||||||||||||||
Formation.prototype.ComputeMotionParameters = function() | Formation.prototype.ComputeMotionParameters = function() | |||||||||||||||||||
{ | { | |||||||||||||||||||
let maxRadius = 0; | ||||||||||||||||||||
Done Inline Actionsthis was unused, so I removed it. marder: this was unused, so I removed it. | ||||||||||||||||||||
bbUnsubmitted Done Inline Actions(we have more patches out there nuking this XD) bb: (we have more patches out there nuking this XD) | ||||||||||||||||||||
let minSpeed = Infinity; | let minSpeed = Infinity; | |||||||||||||||||||
let minAcceleration = Infinity; | let minAcceleration = Infinity; | |||||||||||||||||||
for (let ent of this.members) | for (let ent of this.members) | |||||||||||||||||||
{ | { | |||||||||||||||||||
let cmpUnitMotion = Engine.QueryInterface(ent, IID_UnitMotion); | let cmpUnitMotion = Engine.QueryInterface(ent, IID_UnitMotion); | |||||||||||||||||||
if (cmpUnitMotion) | if (cmpUnitMotion) | |||||||||||||||||||
{ | { | |||||||||||||||||||
minSpeed = Math.min(minSpeed, cmpUnitMotion.GetWalkSpeed()); | minSpeed = Math.min(minSpeed, cmpUnitMotion.GetWalkSpeed()); | |||||||||||||||||||
Done Inline Actions
Freagarach: | ||||||||||||||||||||
minAcceleration = Math.min(minAcceleration, cmpUnitMotion.GetAcceleration()); | minAcceleration = Math.min(minAcceleration, cmpUnitMotion.GetAcceleration()); | |||||||||||||||||||
} | } | |||||||||||||||||||
} | } | |||||||||||||||||||
Done Inline Actions
Freagarach: | ||||||||||||||||||||
minSpeed *= this.GetSpeedMultiplier(); | minSpeed *= this.GetSpeedMultiplier(); | |||||||||||||||||||
let cmpUnitMotion = Engine.QueryInterface(this.entity, IID_UnitMotion); | let cmpUnitMotion = Engine.QueryInterface(this.entity, IID_UnitMotion); | |||||||||||||||||||
cmpUnitMotion.SetSpeedMultiplier(minSpeed / cmpUnitMotion.GetWalkSpeed()); | cmpUnitMotion.SetSpeedMultiplier(minSpeed / cmpUnitMotion.GetWalkSpeed()); | |||||||||||||||||||
cmpUnitMotion.SetAcceleration(minAcceleration); | cmpUnitMotion.SetAcceleration(minAcceleration); | |||||||||||||||||||
}; | }; | |||||||||||||||||||
Formation.prototype.ShapeUpdate = function() | Formation.prototype.ShapeUpdate = function() | |||||||||||||||||||
▲ Show 20 Lines • Show All 124 Lines • Show Last 20 Lines |
@Stan @bb
On the one hand it seems sensible to put this into the in a config or in the Scheme, but I have to say pi/4 works great for my feeling and if this should be configurable, I have to figure out how to find out what the current formation at the moment is and then access the right values.
So [tips welcome] TM if you _really_ believe that this should be configurable.