Index: ps/trunk/binaries/data/mods/public/simulation/components/Formation.js =================================================================== --- ps/trunk/binaries/data/mods/public/simulation/components/Formation.js +++ ps/trunk/binaries/data/mods/public/simulation/components/Formation.js @@ -859,36 +859,18 @@ }; /** - * Calculate the estimated rotation of the formation - * based on the first unitAI target position when ordered to walk, - * based on the current rotation in other cases. + * Calculate the estimated rotation of the formation based on the current rotation. * Return the sine and cosine of the angle. */ Formation.prototype.GetEstimatedOrientation = function(pos) { - let cmpUnitAI = Engine.QueryInterface(this.entity, IID_UnitAI); - let r = { "sin": 0, "cos": 1 }; - let unitAIState = cmpUnitAI.GetCurrentState(); - if (unitAIState == "FORMATIONCONTROLLER.WALKING" || unitAIState == "FORMATIONCONTROLLER.COMBAT.APPROACHING") - { - let targetPos = cmpUnitAI.GetTargetPositions(); - if (!targetPos.length) - return r; - let d = targetPos[0].sub(pos).normalize(); - if (!d.x && !d.y) - return r; - r.cos = d.y; - r.sin = d.x; - } - else - { - let cmpPosition = Engine.QueryInterface(this.entity, IID_Position); - if (!cmpPosition) - return r; - let rot = cmpPosition.GetRotation().y; - r.sin = Math.sin(rot); - r.cos = Math.cos(rot); - } + let r = {}; + let cmpPosition = Engine.QueryInterface(this.entity, IID_Position); + if (!cmpPosition) + return r; + let rot = cmpPosition.GetRotation().y; + r.sin = Math.sin(rot); + r.cos = Math.cos(rot); return r; }; Index: ps/trunk/binaries/data/mods/public/simulation/templates/gaia/fauna_shark.xml =================================================================== --- ps/trunk/binaries/data/mods/public/simulation/templates/gaia/fauna_shark.xml +++ ps/trunk/binaries/data/mods/public/simulation/templates/gaia/fauna_shark.xml @@ -23,8 +23,8 @@ -1 upright true - 0.0 - 5.0 + 0 + 5 Index: ps/trunk/binaries/data/mods/public/simulation/templates/special/actor.xml =================================================================== --- ps/trunk/binaries/data/mods/public/simulation/templates/special/actor.xml +++ ps/trunk/binaries/data/mods/public/simulation/templates/special/actor.xml @@ -4,8 +4,8 @@ 0 upright false - 0.0 - 6.0 + 0 + 6 true Index: ps/trunk/binaries/data/mods/public/simulation/templates/template_bird.xml =================================================================== --- ps/trunk/binaries/data/mods/public/simulation/templates/template_bird.xml +++ ps/trunk/binaries/data/mods/public/simulation/templates/template_bird.xml @@ -8,8 +8,8 @@ 0 upright false - 0.0 - 1.0 + 0 + 1 Index: ps/trunk/binaries/data/mods/public/simulation/templates/template_formation.xml =================================================================== --- ps/trunk/binaries/data/mods/public/simulation/templates/template_formation.xml +++ ps/trunk/binaries/data/mods/public/simulation/templates/template_formation.xml @@ -53,8 +53,8 @@ 0 upright false - 0.0 - 3.0 + 0 + 3 0.75 Index: ps/trunk/binaries/data/mods/public/simulation/templates/template_gaia.xml =================================================================== --- ps/trunk/binaries/data/mods/public/simulation/templates/template_gaia.xml +++ ps/trunk/binaries/data/mods/public/simulation/templates/template_gaia.xml @@ -23,8 +23,8 @@ 0 upright false - 0.0 - 6.0 + 0 + 6 Index: ps/trunk/binaries/data/mods/public/simulation/templates/template_rubble.xml =================================================================== --- ps/trunk/binaries/data/mods/public/simulation/templates/template_rubble.xml +++ ps/trunk/binaries/data/mods/public/simulation/templates/template_rubble.xml @@ -12,8 +12,8 @@ 0 upright false - 0.0 - 6.0 + 0 + 6 true Index: ps/trunk/binaries/data/mods/public/simulation/templates/template_structure.xml =================================================================== --- ps/trunk/binaries/data/mods/public/simulation/templates/template_structure.xml +++ ps/trunk/binaries/data/mods/public/simulation/templates/template_structure.xml @@ -74,8 +74,8 @@ 0 upright false - 0.0 - 6.0 + 0 + 6 1.0 Index: ps/trunk/binaries/data/mods/public/simulation/templates/template_territory_pull.xml =================================================================== --- ps/trunk/binaries/data/mods/public/simulation/templates/template_territory_pull.xml +++ ps/trunk/binaries/data/mods/public/simulation/templates/template_territory_pull.xml @@ -10,8 +10,8 @@ 0 upright false - 0.0 - 6.0 + 0 + 6 Index: ps/trunk/binaries/data/mods/public/simulation/templates/template_trigger_point.xml =================================================================== --- ps/trunk/binaries/data/mods/public/simulation/templates/template_trigger_point.xml +++ ps/trunk/binaries/data/mods/public/simulation/templates/template_trigger_point.xml @@ -11,8 +11,8 @@ 0 upright false - 0.0 - 6.0 + 0 + 6 Index: ps/trunk/binaries/data/mods/public/simulation/templates/template_unit.xml =================================================================== --- ps/trunk/binaries/data/mods/public/simulation/templates/template_unit.xml +++ ps/trunk/binaries/data/mods/public/simulation/templates/template_unit.xml @@ -70,7 +70,7 @@ pitch false 0.0 - 6.0 + 6 Index: ps/trunk/binaries/data/mods/public/simulation/templates/template_unit_cavalry.xml =================================================================== --- ps/trunk/binaries/data/mods/public/simulation/templates/template_unit_cavalry.xml +++ ps/trunk/binaries/data/mods/public/simulation/templates/template_unit_cavalry.xml @@ -44,6 +44,9 @@ 0 0 + + 4 + 150 Index: ps/trunk/binaries/data/mods/public/simulation/templates/template_unit_champion_cavalry.xml =================================================================== --- ps/trunk/binaries/data/mods/public/simulation/templates/template_unit_champion_cavalry.xml +++ ps/trunk/binaries/data/mods/public/simulation/templates/template_unit_champion_cavalry.xml @@ -29,6 +29,9 @@ 8 10 + + 4 + Index: ps/trunk/binaries/data/mods/public/simulation/templates/template_unit_champion_elephant.xml =================================================================== --- ps/trunk/binaries/data/mods/public/simulation/templates/template_unit_champion_elephant.xml +++ ps/trunk/binaries/data/mods/public/simulation/templates/template_unit_champion_elephant.xml @@ -24,6 +24,9 @@ 30 20 + + 2 + Index: ps/trunk/binaries/data/mods/public/simulation/templates/template_unit_elephant.xml =================================================================== --- ps/trunk/binaries/data/mods/public/simulation/templates/template_unit_elephant.xml +++ ps/trunk/binaries/data/mods/public/simulation/templates/template_unit_elephant.xml @@ -13,6 +13,9 @@ 260 + + 2 + 150 Index: ps/trunk/binaries/data/mods/public/simulation/templates/template_unit_fauna.xml =================================================================== --- ps/trunk/binaries/data/mods/public/simulation/templates/template_unit_fauna.xml +++ ps/trunk/binaries/data/mods/public/simulation/templates/template_unit_fauna.xml @@ -18,6 +18,9 @@ food + + 4 + Index: ps/trunk/binaries/data/mods/public/simulation/templates/template_unit_fauna_hunt_defensive_elephant.xml =================================================================== --- ps/trunk/binaries/data/mods/public/simulation/templates/template_unit_fauna_hunt_defensive_elephant.xml +++ ps/trunk/binaries/data/mods/public/simulation/templates/template_unit_fauna_hunt_defensive_elephant.xml @@ -27,6 +27,9 @@ 50 + + 2 + Index: ps/trunk/binaries/data/mods/public/simulation/templates/template_unit_hero_cavalry.xml =================================================================== --- ps/trunk/binaries/data/mods/public/simulation/templates/template_unit_hero_cavalry.xml +++ ps/trunk/binaries/data/mods/public/simulation/templates/template_unit_hero_cavalry.xml @@ -23,6 +23,9 @@ special/formations/wedge + + 4 + Index: ps/trunk/binaries/data/mods/public/simulation/templates/template_unit_hero_elephant_melee.xml =================================================================== --- ps/trunk/binaries/data/mods/public/simulation/templates/template_unit_hero_elephant_melee.xml +++ ps/trunk/binaries/data/mods/public/simulation/templates/template_unit_hero_elephant_melee.xml @@ -31,6 +31,9 @@ Elephant Melee + + 2 + Index: ps/trunk/binaries/data/mods/public/simulation/templates/template_unit_ship.xml =================================================================== --- ps/trunk/binaries/data/mods/public/simulation/templates/template_unit_ship.xml +++ ps/trunk/binaries/data/mods/public/simulation/templates/template_unit_ship.xml @@ -32,8 +32,8 @@ upright true - 0.0 - 3.0 + 0 + 3 4.0 Index: ps/trunk/binaries/data/mods/public/simulation/templates/template_unit_ship_quinquereme.xml =================================================================== --- ps/trunk/binaries/data/mods/public/simulation/templates/template_unit_ship_quinquereme.xml +++ ps/trunk/binaries/data/mods/public/simulation/templates/template_unit_ship_quinquereme.xml @@ -66,6 +66,9 @@ 40 30 + + 2 + Index: ps/trunk/binaries/data/mods/public/simulation/templates/template_unit_ship_trireme.xml =================================================================== --- ps/trunk/binaries/data/mods/public/simulation/templates/template_unit_ship_trireme.xml +++ ps/trunk/binaries/data/mods/public/simulation/templates/template_unit_ship_trireme.xml @@ -55,6 +55,9 @@ 30 20 + + 2 + Index: ps/trunk/binaries/data/mods/public/simulation/templates/template_unit_siege.xml =================================================================== --- ps/trunk/binaries/data/mods/public/simulation/templates/template_unit_siege.xml +++ ps/trunk/binaries/data/mods/public/simulation/templates/template_unit_siege.xml @@ -31,6 +31,7 @@ pitch-roll + 2 4.0 Index: ps/trunk/binaries/data/mods/public/simulation/templates/units/maur/support_elephant.xml =================================================================== --- ps/trunk/binaries/data/mods/public/simulation/templates/units/maur/support_elephant.xml +++ ps/trunk/binaries/data/mods/public/simulation/templates/units/maur/support_elephant.xml @@ -33,6 +33,7 @@ pitch + 2 Index: ps/trunk/binaries/data/mods/public/simulation/templates/units/plane.xml =================================================================== --- ps/trunk/binaries/data/mods/public/simulation/templates/units/plane.xml +++ ps/trunk/binaries/data/mods/public/simulation/templates/units/plane.xml @@ -54,9 +54,9 @@ upright - 1.0 true - 0.0 + 0 + 1 3.0 Index: ps/trunk/source/simulation2/components/CCmpPosition.cpp =================================================================== --- ps/trunk/source/simulation2/components/CCmpPosition.cpp +++ ps/trunk/source/simulation2/components/CCmpPosition.cpp @@ -70,7 +70,8 @@ bool m_Floating; entity_pos_t m_FloatDepth; - float m_RotYSpeed; // maximum radians per second, used by InterpolatedRotY to follow RotY + // Maximum radians per second, used by InterpolatedRotY to follow RotY and the unitMotion. + fixed m_RotYSpeed; // Dynamic state: @@ -129,7 +130,7 @@ "" "" "" - "" + "" "" ""; } @@ -154,7 +155,7 @@ m_Floating = paramNode.GetChild("Floating").ToBool(); m_FloatDepth = paramNode.GetChild("FloatDepth").ToFixed(); - m_RotYSpeed = paramNode.GetChild("TurnRate").ToFixed().ToFloat(); + m_RotYSpeed = paramNode.GetChild("TurnRate").ToFixed(); m_RotX = m_RotY = m_RotZ = entity_angle_t::FromInt(0); m_InterpolatedRotX = m_InterpolatedRotY = m_InterpolatedRotZ = 0.f; @@ -189,6 +190,7 @@ serialize.NumberFixed_Unbounded("rot x", m_RotX); serialize.NumberFixed_Unbounded("rot y", m_RotY); serialize.NumberFixed_Unbounded("rot z", m_RotZ); + serialize.NumberFixed_Unbounded("rot y speed", m_RotYSpeed); serialize.NumberFixed_Unbounded("altitude", m_Y); serialize.Bool("relative", m_RelativeToGround); serialize.Bool("floating", m_Floating); @@ -246,6 +248,7 @@ deserialize.NumberFixed_Unbounded("rot x", m_RotX); deserialize.NumberFixed_Unbounded("rot y", m_RotY); deserialize.NumberFixed_Unbounded("rot z", m_RotZ); + deserialize.NumberFixed_Unbounded("rot y speed", m_RotYSpeed); deserialize.NumberFixed_Unbounded("altitude", m_Y); deserialize.Bool("relative", m_RelativeToGround); deserialize.Bool("floating", m_Floating); @@ -536,6 +539,11 @@ return CFixedVector2D(m_PrevX, m_PrevZ); } + virtual fixed GetTurnRate() const + { + return m_RotYSpeed; + } + virtual void TurnTo(entity_angle_t y) { if (m_TurretParent != INVALID_ENTITY) @@ -780,13 +788,14 @@ if (rotY != m_InterpolatedRotY) { + float rotYSpeed = m_RotYSpeed.ToFloat(); float delta = rotY - m_InterpolatedRotY; // Wrap delta to -M_PI..M_PI delta = fmodf(delta + (float)M_PI, 2*(float)M_PI); // range -2PI..2PI if (delta < 0) delta += 2*(float)M_PI; // range 0..2PI delta -= (float)M_PI; // range -M_PI..M_PI // Clamp to max rate - float deltaClamped = Clamp(delta, -m_RotYSpeed*msgData.deltaSimTime, +m_RotYSpeed*msgData.deltaSimTime); + float deltaClamped = Clamp(delta, -rotYSpeed*msgData.deltaSimTime, +rotYSpeed*msgData.deltaSimTime); // Calculate new orientation, in a peculiar way in order to make sure the // result gets close to m_orientation (rather than being n*2*M_PI out) m_InterpolatedRotY = rotY + deltaClamped - delta; Index: ps/trunk/source/simulation2/components/CCmpUnitMotion.cpp =================================================================== --- ps/trunk/source/simulation2/components/CCmpUnitMotion.cpp +++ ps/trunk/source/simulation2/components/CCmpUnitMotion.cpp @@ -616,7 +616,7 @@ * This does not send actually change the position. * @returns true if the move was obstructed. */ - bool PerformMove(fixed dt, WaypointPath& shortPath, WaypointPath& longPath, CFixedVector2D& pos) const; + bool PerformMove(fixed dt, const fixed& turnRate, WaypointPath& shortPath, WaypointPath& longPath, CFixedVector2D& pos, entity_angle_t& angle); /** * Update other components on our speed. @@ -872,28 +872,33 @@ return; CFixedVector2D initialPos = cmpPosition->GetPosition2D(); + entity_angle_t initialAngle = cmpPosition->GetRotation().Y; - // Keep track of the current unit's position during the update + // Keep track of the current unit's position and rotation during the update. CFixedVector2D pos = initialPos; + entity_angle_t angle = initialAngle; // If we're chasing a potentially-moving unit and are currently close // enough to its current position, and we can head in a straight line // to it, then throw away our current path and go straight to it bool wentStraight = TryGoingStraightToTarget(initialPos); - bool wasObstructed = PerformMove(dt, m_ShortPath, m_LongPath, pos); + bool wasObstructed = PerformMove(dt, cmpPosition->GetTurnRate(), m_ShortPath, m_LongPath, pos, angle); // Update our speed over this turn so that the visual actor shows the correct animation. if (pos == initialPos) + { + if (angle != initialAngle) + cmpPosition->TurnTo(angle); UpdateMovementState(fixed::Zero()); + } else { // Update the Position component after our movement (if we actually moved anywhere) + // When moving always set the angle in the direction of the movement. CFixedVector2D offset = pos - initialPos; - - // Face towards the target - entity_angle_t angle = atan2_approx(offset.X, offset.Y); - cmpPosition->MoveAndTurnTo(pos.X,pos.Y, angle); + angle = atan2_approx(offset.X, offset.Y); + cmpPosition->MoveAndTurnTo(pos.X, pos.Y, angle); // Calculate the mean speed over this past turn. UpdateMovementState(offset.Length() / dt); @@ -942,12 +947,18 @@ return false; } -bool CCmpUnitMotion::PerformMove(fixed dt, WaypointPath& shortPath, WaypointPath& longPath, CFixedVector2D& pos) const +bool CCmpUnitMotion::PerformMove(fixed dt, const fixed& turnRate, WaypointPath& shortPath, WaypointPath& longPath, CFixedVector2D& pos, entity_angle_t& angle) { // If there are no waypoint, behave as though we were obstructed and let HandleObstructedMove handle it. if (shortPath.m_Waypoints.empty() && longPath.m_Waypoints.empty()) return true; + // Wrap the angle to (-Pi, Pi]. + while (angle > entity_angle_t::Pi()) + angle -= entity_angle_t::Pi() * 2; + while (angle < -entity_angle_t::Pi()) + angle += entity_angle_t::Pi() * 2; + // TODO: there's some asymmetry here when units look at other // units' positions - the result will depend on the order of execution. // Maybe we should split the updates into multiple phases to minimise @@ -957,26 +968,24 @@ ENSURE(cmpPathfinder); fixed basicSpeed = m_Speed; - // If in formation, run to keep up; otherwise just walk + // If in formation, run to keep up; otherwise just walk. if (IsFormationMember()) basicSpeed = m_Speed.Multiply(m_RunMultiplier); - // Find the speed factor of the underlying terrain + // Find the speed factor of the underlying terrain. // (We only care about the tile we start on - it doesn't matter if we're moving - // partially onto a much slower/faster tile) - // TODO: Terrain-dependent speeds are not currently supported + // partially onto a much slower/faster tile). + // TODO: Terrain-dependent speeds are not currently supported. fixed terrainSpeed = fixed::FromInt(1); fixed maxSpeed = basicSpeed.Multiply(terrainSpeed); - // We want to move (at most) maxSpeed*dt units from pos towards the next waypoint - fixed timeLeft = dt; fixed zero = fixed::Zero(); while (timeLeft > zero) { - // If we ran out of path, we have to stop + // If we ran out of path, we have to stop. if (shortPath.m_Waypoints.empty() && longPath.m_Waypoints.empty()) break; @@ -987,11 +996,37 @@ target = CFixedVector2D(shortPath.m_Waypoints.back().x, shortPath.m_Waypoints.back().z); CFixedVector2D offset = target - pos; + if (turnRate > zero) + { + fixed maxRotation = turnRate.Multiply(timeLeft); + fixed angleDiff = angle - atan2_approx(offset.X, offset.Y); + if (angleDiff != zero) + { + fixed absoluteAngleDiff = angleDiff.Absolute(); + if (absoluteAngleDiff > entity_angle_t::Pi()) + absoluteAngleDiff = entity_angle_t::Pi() * 2 - absoluteAngleDiff; + + // Figure out whether rotating will increase or decrease the angle, and how far we need to rotate in that direction. + int direction = (entity_angle_t::Zero() < angleDiff && angleDiff <= entity_angle_t::Pi()) || angleDiff < -entity_angle_t::Pi() ? -1 : 1; + + // Can't rotate far enough, just rotate in the correct direction. + if (absoluteAngleDiff > maxRotation) + { + angle += maxRotation * direction; + if (angle * direction > entity_angle_t::Pi()) + angle -= entity_angle_t::Pi() * 2 * direction; + break; + } + // Rotate towards the next waypoint and continue moving. + angle = atan2_approx(offset.X, offset.Y); + timeLeft = (maxRotation - absoluteAngleDiff) / turnRate; + } + } - // Work out how far we can travel in timeLeft + // Work out how far we can travel in timeLeft. fixed maxdist = maxSpeed.Multiply(timeLeft); - // If the target is close, we can move there directly + // If the target is close, we can move there directly. fixed offsetLength = offset.Length(); if (offsetLength <= maxdist) { @@ -999,7 +1034,7 @@ { pos = target; - // Spend the rest of the time heading towards the next waypoint + // Spend the rest of the time heading towards the next waypoint. timeLeft = (maxdist - offsetLength) / maxSpeed; if (shortPath.m_Waypoints.empty()) @@ -1011,13 +1046,13 @@ } else { - // Error - path was obstructed + // Error - path was obstructed. return true; } } else { - // Not close enough, so just move in the right direction + // Not close enough, so just move in the right direction. offset.Normalize(maxdist); target = pos + offset; Index: ps/trunk/source/simulation2/components/ICmpPosition.h =================================================================== --- ps/trunk/source/simulation2/components/ICmpPosition.h +++ ps/trunk/source/simulation2/components/ICmpPosition.h @@ -185,6 +185,11 @@ virtual CFixedVector2D GetPreviousPosition2D() const = 0; /** + * Returns the turn rate in radians per second. + */ + virtual fixed GetTurnRate() const = 0; + + /** * Rotate smoothly to the given angle around the upwards axis. * @param y clockwise radians from the +Z axis. */ Index: ps/trunk/source/simulation2/components/ICmpPosition.cpp =================================================================== --- ps/trunk/source/simulation2/components/ICmpPosition.cpp +++ ps/trunk/source/simulation2/components/ICmpPosition.cpp @@ -42,6 +42,7 @@ DEFINE_INTERFACE_METHOD_CONST_0("GetPosition2D", CFixedVector2D, ICmpPosition, GetPosition2D) DEFINE_INTERFACE_METHOD_CONST_0("GetPreviousPosition", CFixedVector3D, ICmpPosition, GetPreviousPosition) DEFINE_INTERFACE_METHOD_CONST_0("GetPreviousPosition2D", CFixedVector2D, ICmpPosition, GetPreviousPosition2D) +DEFINE_INTERFACE_METHOD_CONST_0("GetTurnRate", fixed, ICmpPosition, GetTurnRate) DEFINE_INTERFACE_METHOD_1("TurnTo", void, ICmpPosition, TurnTo, entity_angle_t) DEFINE_INTERFACE_METHOD_1("SetYRotation", void, ICmpPosition, SetYRotation, entity_angle_t) DEFINE_INTERFACE_METHOD_2("SetXZRotation", void, ICmpPosition, SetXZRotation, entity_angle_t, entity_angle_t) Index: ps/trunk/source/simulation2/components/tests/test_RangeManager.h =================================================================== --- ps/trunk/source/simulation2/components/tests/test_RangeManager.h +++ ps/trunk/source/simulation2/components/tests/test_RangeManager.h @@ -62,6 +62,7 @@ virtual CFixedVector2D GetPosition2D() const { return CFixedVector2D(m_Pos.X, m_Pos.Z); } virtual CFixedVector3D GetPreviousPosition() const { return CFixedVector3D(); } virtual CFixedVector2D GetPreviousPosition2D() const { return CFixedVector2D(); } + virtual fixed GetTurnRate() const { return fixed::Zero(); } virtual void TurnTo(entity_angle_t UNUSED(y)) { } virtual void SetYRotation(entity_angle_t UNUSED(y)) { } virtual void SetXZRotation(entity_angle_t UNUSED(x), entity_angle_t UNUSED(z)) { }