Changeset View
Changeset View
Standalone View
Standalone View
ps/trunk/binaries/data/mods/public/simulation/components/Formation.js
Show First 20 Lines • Show All 60 Lines • ▼ Show 20 Lines | "<optional>" + | ||||
"</element>" + | "</element>" + | ||||
"</optional>" + | "</optional>" + | ||||
"<element name='UnitSeparationWidthMultiplier' a:help='Place the units in the formation closer or further to each other. The standard separation is the footprint size.'>" + | "<element name='UnitSeparationWidthMultiplier' a:help='Place the units in the formation closer or further to each other. The standard separation is the footprint size.'>" + | ||||
"<ref name='nonNegativeDecimal'/>" + | "<ref name='nonNegativeDecimal'/>" + | ||||
"</element>" + | "</element>" + | ||||
"<element name='UnitSeparationDepthMultiplier' a:help='Place the units in the formation closer or further to each other. The standard separation is the footprint size.'>" + | "<element name='UnitSeparationDepthMultiplier' a:help='Place the units in the formation closer or further to each other. The standard separation is the footprint size.'>" + | ||||
"<ref name='nonNegativeDecimal'/>" + | "<ref name='nonNegativeDecimal'/>" + | ||||
"</element>" + | "</element>" + | ||||
"<element name='Animations' 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:animation1;2..2,1..-1;animation2\", this will set animation1 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 animations. 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>"; | ||||
var g_ColumnDistanceThreshold = 128; // distance at which we'll switch between column/box formations | var g_ColumnDistanceThreshold = 128; // distance at which we'll switch between column/box formations | ||||
Formation.prototype.Init = function() | Formation.prototype.variablesToSerialize = [ | ||||
"lastOrderVariant", | |||||
"members", | |||||
"memberPositions", | |||||
"maxRowsUsed", | |||||
"maxColumnsUsed", | |||||
"inPosition", | |||||
"columnar", | |||||
"rearrange", | |||||
"formationMembersWithAura", | |||||
"width", | |||||
"depth", | |||||
"oldOrientation", | |||||
"twinFormations", | |||||
"formationSeparation", | |||||
"offsets" | |||||
]; | |||||
Formation.prototype.Init = function(deserialized = false) | |||||
{ | { | ||||
this.formationShape = this.template.FormationShape; | |||||
this.sortingClasses = this.template.SortingClasses.split(/\s+/g); | this.sortingClasses = this.template.SortingClasses.split(/\s+/g); | ||||
this.sortingOrder = this.template.SortingOrder; | |||||
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.sloppyness = +this.template.Sloppyness; | this.sloppyness = +this.template.Sloppyness; | ||||
this.widthDepthRatio = +this.template.WidthDepthRatio; | this.widthDepthRatio = +this.template.WidthDepthRatio; | ||||
this.minColumns = +(this.template.MinColumns || 0); | this.minColumns = +(this.template.MinColumns || 0); | ||||
this.maxColumns = +(this.template.MaxColumns || 0); | this.maxColumns = +(this.template.MaxColumns || 0); | ||||
this.maxRows = +(this.template.MaxRows || 0); | this.maxRows = +(this.template.MaxRows || 0); | ||||
this.centerGap = +(this.template.CenterGap || 0); | this.centerGap = +(this.template.CenterGap || 0); | ||||
this.animations = []; | if (this.template.AnimationVariants) | ||||
if (this.template.Animations) | |||||
{ | { | ||||
let differentAnimations = this.template.Animations.split(/\s*;\s*/); | this.animationvariants = []; | ||||
// loop over the different rectangulars that will map to different animations | let differentAnimationVariants = this.template.AnimationVariants.split(/\s*;\s*/); | ||||
for (var rectAnimation of differentAnimations) | // loop over the different rectangulars that will map to different animation variants | ||||
{ | for (let rectAnimationVariant of differentAnimationVariants) | ||||
var rect, replacementAnimationName; | { | ||||
[rect, replacementAnimationName] = rectAnimation.split(/\s*:\s*/); | let rect, replacementAnimationVariant; | ||||
var rows, columns; | [rect, replacementAnimationVariant] = rectAnimationVariant.split(/\s*:\s*/); | ||||
let rows, columns; | |||||
[rows, columns] = rect.split(/\s*,\s*/); | [rows, columns] = rect.split(/\s*,\s*/); | ||||
var minRow, maxRow, minColumn, maxColumn; | let minRow, maxRow, minColumn, maxColumn; | ||||
[minRow, maxRow] = rows.split(/\s*\.\.\s*/); | [minRow, maxRow] = rows.split(/\s*\.\.\s*/); | ||||
[minColumn, maxColumn] = columns.split(/\s*\.\.\s*/); | [minColumn, maxColumn] = columns.split(/\s*\.\.\s*/); | ||||
this.animations.push({ | this.animationvariants.push({ | ||||
"minRow": +minRow, | "minRow": +minRow, | ||||
"maxRow": +maxRow, | "maxRow": +maxRow, | ||||
"minColumn": +minColumn, | "minColumn": +minColumn, | ||||
"maxColumn": +maxColumn, | "maxColumn": +maxColumn, | ||||
"animation": replacementAnimationName | "name": replacementAnimationVariant | ||||
}); | }); | ||||
} | } | ||||
} | } | ||||
this.lastOrderVariant = undefined; | this.lastOrderVariant = undefined; | ||||
this.members = []; // entity IDs currently belonging to this formation | this.members = []; // entity IDs currently belonging to this formation | ||||
this.memberPositions = {}; | this.memberPositions = {}; | ||||
this.maxRowsUsed = 0; | this.maxRowsUsed = 0; | ||||
this.maxColumnsUsed = []; | this.maxColumnsUsed = []; | ||||
this.inPosition = []; // entities that have reached their final position | this.inPosition = []; // entities that have reached their final position | ||||
this.columnar = false; // whether we're travelling in column (vs box) formation | this.columnar = false; // whether we're travelling in column (vs box) formation | ||||
this.rearrange = true; // whether we should rearrange all formation members | this.rearrange = true; // whether we should rearrange all formation members | ||||
this.formationMembersWithAura = []; // Members with a formation aura | this.formationMembersWithAura = []; // Members with a formation aura | ||||
this.width = 0; | this.width = 0; | ||||
this.depth = 0; | this.depth = 0; | ||||
this.oldOrientation = {"sin": 0, "cos": 0}; | this.oldOrientation = {"sin": 0, "cos": 0}; | ||||
this.twinFormations = []; | this.twinFormations = []; | ||||
// distance from which two twin formations will merge into one. | // distance from which two twin formations will merge into one. | ||||
this.formationSeparation = 0; | this.formationSeparation = 0; | ||||
if (deserialized) | |||||
return; | |||||
Engine.QueryInterface(SYSTEM_ENTITY, IID_Timer) | Engine.QueryInterface(SYSTEM_ENTITY, IID_Timer) | ||||
.SetInterval(this.entity, IID_Formation, "ShapeUpdate", 1000, 1000, null); | .SetInterval(this.entity, IID_Formation, "ShapeUpdate", 1000, 1000, null); | ||||
}; | }; | ||||
Formation.prototype.Serialize = function() | |||||
{ | |||||
let result = {}; | |||||
for (let key of this.variablesToSerialize) | |||||
result[key] = this[key]; | |||||
return result; | |||||
}; | |||||
Formation.prototype.Deserialize = function(data) | |||||
{ | |||||
this.Init(true); | |||||
for (let key in data) | |||||
this[key] = data[key]; | |||||
}; | |||||
/** | /** | ||||
* Set the value from which two twin formations will become one. | * Set the value from which two twin formations will become one. | ||||
*/ | */ | ||||
Formation.prototype.SetFormationSeparation = function(value) | Formation.prototype.SetFormationSeparation = function(value) | ||||
{ | { | ||||
this.formationSeparation = value; | this.formationSeparation = value; | ||||
}; | }; | ||||
▲ Show 20 Lines • Show All 54 Lines • ▼ Show 20 Lines | |||||
* the arbitrary first one. | * the arbitrary first one. | ||||
*/ | */ | ||||
Formation.prototype.GetPrimaryMember = function() | Formation.prototype.GetPrimaryMember = function() | ||||
{ | { | ||||
return this.members[0]; | return this.members[0]; | ||||
}; | }; | ||||
/** | /** | ||||
* Get the formation animation for a certain member of this formation | * Get the formation animation variant for a certain member of this formation | ||||
* @param entity The entity ID to get the animation for | * @param entity The entity ID to get the animation for | ||||
* @return The name of the transformed animation as defined in the template | * @return The name of the animation variant as defined in the template | ||||
* E.g. "testudo_row1" or undefined if does not exist | * E.g. "testudo_row1" or undefined if does not exist | ||||
*/ | */ | ||||
Formation.prototype.GetFormationAnimation = function(entity) | Formation.prototype.GetFormationAnimationVariant = function(entity) | ||||
{ | { | ||||
var animationGroup = this.animations; | if (!this.animationvariants || !this.animationvariants.length || this.columnar || !this.memberPositions[entity]) | ||||
if (!animationGroup.length || this.columnar || !this.memberPositions[entity]) | |||||
return undefined; | return undefined; | ||||
var row = this.memberPositions[entity].row; | let row = this.memberPositions[entity].row; | ||||
var column = this.memberPositions[entity].column; | let column = this.memberPositions[entity].column; | ||||
for (var i = 0; i < animationGroup.length; ++i) | for (let i = 0; i < this.animationvariants.length; ++i) | ||||
{ | { | ||||
var minRow = animationGroup[i].minRow; | let minRow = this.animationvariants[i].minRow; | ||||
if (minRow < 0) | if (minRow < 0) | ||||
minRow += this.maxRowsUsed + 1; | minRow += this.maxRowsUsed + 1; | ||||
if (row < minRow) | if (row < minRow) | ||||
continue; | continue; | ||||
var maxRow = animationGroup[i].maxRow; | let maxRow = this.animationvariants[i].maxRow; | ||||
if (maxRow < 0) | if (maxRow < 0) | ||||
maxRow += this.maxRowsUsed + 1; | maxRow += this.maxRowsUsed + 1; | ||||
if (row > maxRow) | if (row > maxRow) | ||||
continue; | continue; | ||||
var minColumn = animationGroup[i].minColumn; | let minColumn = this.animationvariants[i].minColumn; | ||||
if (minColumn < 0) | if (minColumn < 0) | ||||
minColumn += this.maxColumnsUsed[row] + 1; | minColumn += this.maxColumnsUsed[row] + 1; | ||||
if (column < minColumn) | if (column < minColumn) | ||||
continue; | continue; | ||||
var maxColumn = animationGroup[i].maxColumn; | let maxColumn = this.animationvariants[i].maxColumn; | ||||
if (maxColumn < 0) | if (maxColumn < 0) | ||||
maxColumn += this.maxColumnsUsed[row] + 1; | maxColumn += this.maxColumnsUsed[row] + 1; | ||||
if (column > maxColumn) | if (column > maxColumn) | ||||
continue; | continue; | ||||
return animationGroup[i].animation; | return this.animationvariants[i].name; | ||||
} | } | ||||
return undefined; | return undefined; | ||||
}; | }; | ||||
/** | /** | ||||
* Permits formation members to register that they've reached their destination. | * Permits formation members to register that they've reached their destination. | ||||
*/ | */ | ||||
Formation.prototype.SetInPosition = function(ent) | Formation.prototype.SetInPosition = function(ent) | ||||
▲ Show 20 Lines • Show All 378 Lines • ▼ Show 20 Lines | for (var c = 0; c < sortingClasses.length; ++c) | ||||
} | } | ||||
} | } | ||||
if (!done) | if (!done) | ||||
types["Unknown"].push({"ent": active[i], "pos": positions[i]}); | types["Unknown"].push({"ent": active[i], "pos": positions[i]}); | ||||
} | } | ||||
var count = active.length; | var count = active.length; | ||||
var shape = this.formationShape; | let shape = this.template.FormationShape; | ||||
var shiftRows = this.shiftRows; | var shiftRows = this.shiftRows; | ||||
var centerGap = this.centerGap; | var centerGap = this.centerGap; | ||||
var sortingOrder = this.sortingOrder; | let sortingOrder = this.template.SortingOrder; | ||||
var offsets = []; | var offsets = []; | ||||
// Choose a sensible size/shape for the various formations, depending on number of units | // Choose a sensible size/shape for the various formations, depending on number of units | ||||
var cols; | var cols; | ||||
if (this.columnar) | if (this.columnar) | ||||
{ | { | ||||
shape = "square"; | shape = "square"; | ||||
cols = Math.min(count,3); | cols = Math.min(count,3); | ||||
shiftRows = false; | shiftRows = false; | ||||
centerGap = 0; | centerGap = 0; | ||||
sortingOrder = null; | sortingOrder = null; | ||||
} | } | ||||
else | else | ||||
{ | { | ||||
var depth = Math.sqrt(count / this.widthDepthRatio); | let depth = Math.sqrt(count / this.widthDepthRatio); | ||||
if (this.maxRows && depth > this.maxRows) | if (this.maxRows && depth > this.maxRows) | ||||
depth = this.maxRows; | depth = this.maxRows; | ||||
cols = Math.ceil(count / Math.ceil(depth) + (this.shiftRows ? 0.5 : 0)); | cols = Math.ceil(count / Math.ceil(depth) + (this.shiftRows ? 0.5 : 0)); | ||||
if (cols < this.minColumns) | if (cols < this.minColumns) | ||||
cols = Math.min(count, this.minColumns); | cols = Math.min(count, this.minColumns); | ||||
if (this.maxColumns && cols > this.maxColumns && this.maxRows != depth) | if (this.maxColumns && cols > this.maxColumns && this.maxRows != depth) | ||||
cols = this.maxColumns; | cols = this.maxColumns; | ||||
} | } | ||||
▲ Show 20 Lines • Show All 53 Lines • ▼ Show 20 Lines | while (left > 0) | ||||
var x = side * Math.ceil(c/2) * separation.width; | var x = side * Math.ceil(c/2) * separation.width; | ||||
if (centerGap) | if (centerGap) | ||||
{ | { | ||||
if (x == 0) // don't use the center position with a center gap | if (x == 0) // don't use the center position with a center gap | ||||
continue; | continue; | ||||
x += side * centerGap / 2; | x += side * centerGap / 2; | ||||
} | } | ||||
var column = Math.ceil(n/2) + Math.ceil(c/2) * side; | var column = Math.ceil(n/2) + Math.ceil(c/2) * side; | ||||
var r1 = randFloat(-1, 1) * this.sloppyness; | let r1 = randFloat(-1, 1) * this.sloppyness; | ||||
var r2 = randFloat(-1, 1) * this.sloppyness; | let r2 = randFloat(-1, 1) * this.sloppyness; | ||||
offsets.push(new Vector2D(x + r1, z + r2)); | offsets.push(new Vector2D(x + r1, z + r2)); | ||||
offsets[offsets.length - 1].row = r+1; | offsets[offsets.length - 1].row = r+1; | ||||
offsets[offsets.length - 1].column = column; | offsets[offsets.length - 1].column = column; | ||||
left--; | left--; | ||||
} | } | ||||
++r; | ++r; | ||||
this.maxColumnsUsed[r] = n; | this.maxColumnsUsed[r] = n; | ||||
▲ Show 20 Lines • Show All 278 Lines • Show Last 20 Lines |
Wildfire Games · Phabricator