movedefs.lua is a file in the
Gamedata/ directory of a Spring Game.
This file defines MoveClasses which are used to determine pathing for units.
The engine source code which parses the data from this file is viewable here:
A mobile unit is assigned a MoveClass using the
movementClass = tag. The engine determines the unit's pathfinding based on the properties of the MoveClass, so analogous properties (e.g.
footprintX = ) in the UnitDef should be the same for optimal behaviour.
All MoveClasses must belong to one of four possible types. A MoveClass is assigned a type simply by including it within the name string e.g.
Tank_MyMoveClassName. Types are used in TypeMaps of Spring maps, which allow mappers to set per-type speed multipliers.
Bot- A ground based MoveClass. The default type if no matching type string is found.
Tank- A ground based MoveClass. Identical to
Botother than interaction with TypeMaps.
Boat- Are synonyms. All ships / boat MoveClasses must belong to this type to behave as ships.
Hover- All hovercraft MoveClasses (traverse any depth of water without speed reduction) must belong to this class, as well as having
canHover= in their UnitDef to behave as hovers.
- string name Default: ""
- The name of the MoveClass. Usually passed by the sub-table variable name rather than using this tag (See #Example). Must contain "tank", "hover", "ship" or "boat" respectively for non-Bot types.
- int footprintX Default: 1
- How wide the MoveClass is in footprint units, left to right. 1 footprint unit = 16 elmos. Cannot be below
1. For mobile units this should be the same as the
footprintX= of its UnitDef.
- int footprintZ Default: footprintX
- How wide the MoveClass is in footprint units, top to bottom. 1 footprint unit = 16 elmos. Cannot be below
1. For mobile units this should be the same as the
footprintZ= of its UnitDef.
- float crushStrength Default: 10.0
- Power of a unit with this MoveClass to crush features and units. Any features with a
crushResistance= less than this value will be crushed on contact. Any units with crushing enabled via Spring.SetUnitBlocking), in an impact where collider impulse exceeds collidee impulse and whose
crushResistance= is less than this value will also be crushed.
- float maxSlope Default: 60.0 (15.0 for hover types)
- The maximum steepness of slope a unit with this MoveClass can climb in degrees. For further details see #How_slope_is_determined.
- float slopeMod Default: 4.0 / (maxSlope + 0.001)
- How much a slope will slow a unit,
0means no slowdown.
- bool avoidMobilesOnPath Default: true
- Do the units attempt to avoid other mobile units when pathing?
- Pro: groups moving without a ctrl-order clump much less
- Contra: groups moving with a ctrl-order are less organized
These tags are used in unit avoidance.
- bool heatMapping Default: false
- This enables/disables generation and usage of path heats.
- float heatMod Default: 0.05
- The pathfinder cost modifier that is multiplied by the heat of the current square.
- int heatProduced Default: 30
- How much heat to place on a square if a path goes through it.
heat set = max(currentSquareHeat, heatProduced).
Tank & Bot-Only Tags
- float depthMod Default: 0.1
- How much water will slow amphibious units. The deeper the water, the slower the movement.Replaced with a subtable in Spring 86.x
- float maxWaterDepth Default: 0.0
- The maximum depth of water in elmos the unit can wade through.
- float minWaterDepth Default: 10.0
- The minimum depth of water in elmos the ship requires to float in.
- bool subMarine Default: false
- Is the ship a submarine; can it move underneath other ships?
Only read for
Tank MoveClasses, the depthModParams group contains several sub-tags. Depthmod is a system for changing the speed of units depending on how deep they are underwater.
new formula is given by if h < depthModParams.minHeight: 1.0 if h > depthModParams.maxHeight: 0.0 else: depthScale = MAX(0.01, MIN(depthModParams.maxScale, (a * h * h) + (b * h) + c)) depthMod = 1 / depthScale where h = unit's absolute height below water surface a = depthModParams.quadraticCoeff b = depthModParams.linearCoeff c = depthModParams.constantCoeff
- float minHeight Default: 0.0
- The minimum depth where the depthMod curve applies. If the unit is shallower than the minHeight, no change in speed occurs.
- float maxHeight Default: MAX_FLOAT
- The maximum depth where the depthMod curve applies. If the unit is deeper than the maxHeight, the unit is immobilized.
The following tags control the behavior of the unit within the active portion of the curve (depth is greater than
minHeight = but less than
maxHeight = ). It would be best to simply open up your graphing program of choice and use the provided formula, but if you're scared of graphs you should read the descriptions below:
- float maxScale Default: MAX_FLOAT
- This is the cap on how severe the depth effect can be. That is if the value is 2, the slowest the unit will go because of depth is half-speed. 4 = a quarter speed, and so on.
- float quadraticCoeff Default: 0.0
- The application of the speed-divider effect is modelled as a quadratic. This is the "depth-squared" component of that. That is, a value set here will be applied exponentially as the unit gets deeper, therefore it is best to use very small numbers for this tag.
- float linearCoeff Default: depthMod
- The application of the speed-divider effect is modelled as a quadratic. This component makes divisor increase linearly with the depth of the unit. So with a value of 0.1, for example, a unit would go from 1/2 speed to 1/3 speed and 1/3 speed to 1/4 speed each time it goes 10 units deeper.
- float constantCoeff Default: 1.0
- The application of the speed-divider effect is modelled as a quadratic. This is the constant component of the divisor. If you want a gradual decrease of speed and your
minHeight= 0 (the waterline) then this value must be 1. Using less than 1 will cause the unit to go faster as it hits the water, and using greater than 1 will cause the unit to abruptly slow down the moment it gets its feet wet. If you're not using
minHeight= 0, you'll have to adjust this value accordingly to get a smooth application of the depthMod divider.
New in version 86.0
These previously hard-coded variables allow control over how the pathfinder (default only, not QTPFS, see Modrules.lua) calculates the cost for a MoveClass for squares containing mobile units, if and only if
avoidMobilesOnPath = true.
- float mobileBusyMult Default: 0.1
- The cost multiplier for mobile units which are 'Busy'; Not currently being built and with a non-empty command queue, but not moving on a path. Clamped to values above
- float mobileIdleMult Default: 0.35
- The cost multiplier for mobile units which are 'Idle'; Either currently being built or with an empty command queue. Clamped to values above
- float mobileMoveMult Default: 0.65
- The cost multiplier for mobile units which are 'Moving'; Trying to follow their own path. Clamped to values above
How slope is determined
Spring doesn't represent terrain slopes in degrees, internally it uses
(1.0 - the terrain normal's y-component) so the slope of vertical faces is
1 and that of horizontal ones
0. A unit's
maxSlope value (which is in
degrees in its movedef) is converted so that the "can we go here?" (ie
maxSlope > terrainSlope?") comparisons are meaningful, the formula
1.0 - cos(DEG2RAD(maxSlope * 1.5)). If you want to know the terrain angle in degrees, compute
RAD2DEG(arccos(1.0 - terrainSlope)). For a bot with a
maxSlope of 36 degrees (~0.41221 in "Spring format"), the maximum tolerable terrain angle is approximately
RAD2DEG(arccos(1.0 - 0.41221))
or 54 degrees. If
30, the MTTA would be 45, and if it was
60 (the highest sensible value due to that 1.5 scalar and the absence of overhanging cliffs) the bot could mount any terrain. In other words you do not need the long mathematical derivation, just remember that
maxTerrainAngle = maxSlope * 1.5
--kloot, Thu Jul 31, 2008