/* ** Command & Conquer Generals Zero Hour(tm) ** Copyright 2025 Electronic Arts Inc. ** ** This program is free software: you can redistribute it and/or modify ** it under the terms of the GNU General Public License as published by ** the Free Software Foundation, either version 3 of the License, or ** (at your option) any later version. ** ** This program is distributed in the hope that it will be useful, ** but WITHOUT ANY WARRANTY; without even the implied warranty of ** MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the ** GNU General Public License for more details. ** ** You should have received a copy of the GNU General Public License ** along with this program. If not, see . */ //////////////////////////////////////////////////////////////////////////////// // // // (c) 2001-2003 Electronic Arts Inc. // // // //////////////////////////////////////////////////////////////////////////////// // FILE: Locomotor.h ///////////////////////////////////////////////////////////////////////////////// // Author: Steven Johnson, Feb 2002 // Desc: Locomotor Descriptions /////////////////////////////////////////////////////////////////////////////////////////////////// #pragma once #ifndef __Locomotor_H_ #define __Locomotor_H_ // INCLUDES /////////////////////////////////////////////////////////////////////////////////////// #include "Common/NameKeyGenerator.h" #include "Common/Override.h" #include "Common/Snapshot.h" #include "GameLogic/Damage.h" #include "GameLogic/LocomotorSet.h" // FORWARD REFERENCES ///////////////////////////////////////////////////////////////////////////// class Locomotor; class LocomotorTemplate; class INI; class PhysicsBehavior; enum BodyDamageType; enum PhysicsTurningType; // if we ever re-enable jets circling for landing, we need this. so keep in around just in case. (srj) #define NO_CIRCLE_FOR_LANDING //------------------------------------------------------------------------------------------------- enum LocomotorAppearance { LOCO_LEGS_TWO, LOCO_WHEELS_FOUR, LOCO_TREADS, LOCO_HOVER, LOCO_THRUST, LOCO_WINGS, LOCO_CLIMBER, // human climber - backs down cliffs. LOCO_OTHER, LOCO_MOTORCYCLE }; enum LocomotorPriority { LOCO_MOVES_BACK=0, // In a group, this one moves toward the back LOCO_MOVES_MIDDLE=1, // In a group, this one stays in the middle LOCO_MOVES_FRONT=2 // In a group, this one moves toward the front of the group }; #ifdef DEFINE_LOCO_APPEARANCE_NAMES static const char *TheLocomotorAppearanceNames[] = { "TWO_LEGS", "FOUR_WHEELS", "TREADS", "HOVER", "THRUST", "WINGS", "CLIMBER", "OTHER", "MOTORCYCLE", NULL }; #endif //------------------------------------------------------------------------------------------------- enum LocomotorBehaviorZ { Z_NO_Z_MOTIVE_FORCE, // does whatever physics tells it, but has no z-force of its own. Z_SEA_LEVEL, // keep at surface-of-water level Z_SURFACE_RELATIVE_HEIGHT, // try to follow a specific height relative to terrain/water height Z_ABSOLUTE_HEIGHT, // try follow a specific height regardless of terrain/water height Z_FIXED_SURFACE_RELATIVE_HEIGHT, // stays fixed at surface-rel height, regardless of physics Z_FIXED_ABSOLUTE_HEIGHT, // stays fixed at absolute height, regardless of physics Z_RELATIVE_TO_GROUND_AND_BUILDINGS, // stays fixed at surface-rel height including buildings, regardless of physics Z_SMOOTH_RELATIVE_TO_HIGHEST_LAYER // try to follow a height relative to the highest layer. }; #ifdef DEFINE_LOCO_Z_NAMES static const char *TheLocomotorBehaviorZNames[] = { "NO_Z_MOTIVE_FORCE", "SEA_LEVEL", "SURFACE_RELATIVE_HEIGHT", "ABSOLUTE_HEIGHT", "FIXED_SURFACE_RELATIVE_HEIGHT", "FIXED_ABSOLUTE_HEIGHT", "FIXED_RELATIVE_TO_GROUND_AND_BUILDINGS", "RELATIVE_TO_HIGHEST_LAYER", NULL }; #endif //------------------------------------------------------------------------------------------------- class LocomotorTemplate : public Overridable { MEMORY_POOL_GLUE_WITH_USERLOOKUP_CREATE( LocomotorTemplate, "LocomotorTemplate" ) friend class Locomotor; public: LocomotorTemplate(); /// field table for loading the values from an INI const FieldParse* getFieldParse() const; void friend_setName(const AsciiString& n) { m_name = n; } void validate(); protected: private: /** Units check: -- Velocity: dist/frame -- Acceleration: dist/(frame*frame) -- Forces: (mass*dist)/(frame*frame) */ AsciiString m_name; LocomotorSurfaceTypeMask m_surfaces; ///< flags indicating the kinds of surfaces we can use Real m_maxSpeed; ///< max speed Real m_maxSpeedDamaged; ///< max speed when "damaged" Real m_minSpeed; ///< we should never brake past this Real m_maxTurnRate; ///< max rate at which we can turn, in rads/frame Real m_maxTurnRateDamaged; ///< max turn rate when "damaged" Real m_acceleration; ///< max acceleration Real m_accelerationDamaged; ///< max acceleration when damaged Real m_lift; ///< max lifting acceleration (flying objects only) Real m_liftDamaged; ///< max lift when damaged Real m_braking; ///< max braking (deceleration) Real m_minTurnSpeed; ///< we must be going >= this speed in order to turn Real m_preferredHeight; ///< our preferred height (if flying) Real m_preferredHeightDamping; ///< how aggressively to adjust to preferred height: 1.0 = very much so, 0.1 = gradually, etc Real m_circlingRadius; ///< for flying things, the radius at which they circle their "maintain" destination. (pos = cw, neg = ccw, 0 = smallest possible) Real m_speedLimitZ; ///< try to avoid going up or down at more than this speed, if possible Real m_extra2DFriction; ///< extra 2dfriction to apply (via Physics) Real m_maxThrustAngle; ///< THRUST locos only: how much we deflect our thrust angle LocomotorBehaviorZ m_behaviorZ; ///< z-axis behavior LocomotorAppearance m_appearance; ///< how we should diddle the Drawable to imitate this motion LocomotorPriority m_movePriority; ///< Where we move - front, middle, back. Real m_accelPitchLimit; ///< Maximum amount we will pitch up under acceleration (including recoil.) Real m_decelPitchLimit; ///< Maximum amount we will pitch down under deceleration (including recoil.) Real m_bounceKick; ///< How much simulating rough terrain "bounces" a wheel up. Real m_pitchStiffness; ///< How stiff the springs are forward & back. Real m_rollStiffness; ///< How stiff the springs are side to side. Real m_pitchDamping; ///< How good the shock absorbers are. Real m_rollDamping; ///< How good the shock absorbers are. Real m_pitchByZVelCoef; ///< How much we pitch in response to z-speed. Real m_thrustRoll; ///< Thrust roll around X axis Real m_wobbleRate; ///< how fast thrust things "wobble" Real m_minWobble; ///< how much thrust things "wobble" Real m_maxWobble; ///< how much thrust things "wobble" Real m_forwardVelCoef; ///< How much we pitch in response to speed. Real m_lateralVelCoef; ///< How much we roll in response to speed. Real m_forwardAccelCoef; ///< How much we pitch in response to acceleration. Real m_lateralAccelCoef; ///< How much we roll in response to acceleration. Real m_uniformAxialDamping; ///< For Attenuating the pitch and roll rates Real m_turnPivotOffset; ///< should we pivot around noncenter? (-1.0 = rear, 0.0 = center, 1.0 = front) Int m_airborneTargetingHeight; ///< The height transition at witch I should mark myself as a AA target. Real m_closeEnoughDist; ///< How close we have to approach the end of a path before stopping Bool m_isCloseEnoughDist3D; ///< And is that calculation 3D, for very rare cases that need to move straight down. Real m_ultraAccurateSlideIntoPlaceFactor; ///< how much we can fudge turning when ultra-accurate Bool m_locomotorWorksWhenDead; ///< should locomotor continue working even when object is "dead"? Bool m_allowMotiveForceWhileAirborne; ///< can we apply motive when airborne? Bool m_apply2DFrictionWhenAirborne; // apply "2d friction" even when airborne... useful for realistic-looking movement Bool m_downhillOnly; // pinewood derby, moves only by gravity pulling downhill Bool m_stickToGround; // if true, can't leave ground Bool m_canMoveBackward; // if true, can move backwards. Bool m_hasSuspension; ///< If true, calculate 4 wheel independent suspension values. Real m_maximumWheelExtension; ///< Maximum distance wheels can move down. (negative value) Real m_maximumWheelCompression; ///< Maximum distance wheels can move up. (positive value) Real m_wheelTurnAngle; ///< How far the front wheels can turn. // Fields for wander locomotor Real m_wanderWidthFactor; Real m_wanderLengthFactor; Real m_wanderAboutPointRadius; Real m_rudderCorrectionDegree; Real m_rudderCorrectionRate; Real m_elevatorCorrectionDegree; Real m_elevatorCorrectionRate; }; typedef OVERRIDE LocomotorTemplateOverride; // --------------------------------------------------------- class Locomotor : public MemoryPoolObject, public Snapshot { MEMORY_POOL_GLUE_WITH_USERLOOKUP_CREATE(Locomotor, "Locomotor" ) friend class LocomotorStore; public: void setPhysicsOptions(Object* obj); void locoUpdate_moveTowardsPosition(Object* obj, const Coord3D& goalPos, Real onPathDistToGoal, Real desiredSpeed, Bool *blocked); void locoUpdate_moveTowardsAngle(Object* obj, Real angle); /** Kill any current (2D) velocity (but stay at current position, or as close as possible) return true if we can maintain the position without being called every frame (eg, we are resting on the ground), false if not (eg, we are hovering or circling) */ Bool locoUpdate_maintainCurrentPosition(Object* obj); Real getMaxSpeedForCondition(BodyDamageType condition) const; ///< get max speed given condition Real getMaxTurnRate(BodyDamageType condition) const; ///< get max turning rate given condition Real getMaxAcceleration(BodyDamageType condition) const; ///< get acceleration given condition Real getMaxLift(BodyDamageType condition) const; ///< get acceleration given condition Real getBraking() const; ///< get braking given condition inline Real getPreferredHeight() const { return m_preferredHeight;} ///< Just return preferredheight, no damage consideration inline void restorePreferredHeightFromTemplate() { m_preferredHeight = m_template->m_preferredHeight; }; inline Real getPreferredHeightDamping() const { return m_preferredHeightDamping;} inline LocomotorAppearance getAppearance() const { return m_template->m_appearance; } inline LocomotorPriority getMovePriority() const { return m_template->m_movePriority; } inline LocomotorSurfaceTypeMask getLegalSurfaces() const { return m_template->m_surfaces; } inline AsciiString getTemplateName() const { return m_template->m_name;} inline Real getMinSpeed() const { return m_template->m_minSpeed;} inline Real getAccelPitchLimit() const { return m_template->m_accelPitchLimit;} ///< Maximum amount we will pitch up or down under acceleration (including recoil.) inline Real getDecelPitchLimit() const { return m_template->m_decelPitchLimit;} ///< Maximum amount we will pitch down under deceleration (including recoil.) inline Real getBounceKick() const { return m_template->m_bounceKick;} ///< How much simulating rough terrain "bounces" a wheel up. inline Real getPitchStiffness() const { return m_template->m_pitchStiffness;} ///< How stiff the springs are forward & back. inline Real getRollStiffness() const { return m_template->m_rollStiffness;} ///< How stiff the springs are side to side. inline Real getPitchDamping() const { return m_template->m_pitchDamping;} ///< How good the shock absorbers are. inline Real getRollDamping() const { return m_template->m_rollDamping;} ///< How good the shock absorbers are. inline Real getPitchByZVelCoef() const { return m_template->m_pitchByZVelCoef;} ///< How much we pitch in response to speed. inline Real getThrustRoll() const { return m_template->m_thrustRoll; } ///< Thrust roll inline Real getWobbleRate() const { return m_template->m_wobbleRate; } ///< how fast thrust things "wobble" inline Real getMaxWobble() const { return m_template->m_maxWobble; } ///< how much thrust things "wobble" inline Real getMinWobble() const { return m_template->m_minWobble; } ///< how much thrust things "wobble" inline Real getForwardVelCoef() const { return m_template->m_forwardVelCoef;} ///< How much we pitch in response to speed. inline Real getLateralVelCoef() const { return m_template->m_lateralVelCoef;} ///< How much we roll in response to speed. inline Real getForwardAccelCoef() const { return m_template->m_forwardAccelCoef;} ///< How much we pitch in response to acceleration. inline Real getLateralAccelCoef() const { return m_template->m_lateralAccelCoef;} ///< How much we roll in response to acceleration. inline Real getUniformAxialDamping() const { return m_template->m_uniformAxialDamping;} ///< How much we roll in response to acceleration. inline Real getTurnPivotOffset() const { return m_template->m_turnPivotOffset;} inline Bool getApply2DFrictionWhenAirborne() const { return m_template->m_apply2DFrictionWhenAirborne; } inline Bool getIsDownhillOnly() const { return m_template->m_downhillOnly; } inline Bool getAllowMotiveForceWhileAirborne() const { return m_template->m_allowMotiveForceWhileAirborne; } inline Int getAirborneTargetingHeight() const { return m_template->m_airborneTargetingHeight; } inline Bool getLocomotorWorksWhenDead() const { return m_template->m_locomotorWorksWhenDead; } inline Bool getStickToGround() const { return m_template->m_stickToGround; } inline Real getCloseEnoughDist() const { return m_closeEnoughDist; } inline Bool isCloseEnoughDist3D() const { return getFlag(IS_CLOSE_ENOUGH_DIST_3D); } inline Bool hasSuspension() const {return m_template->m_hasSuspension;} inline Bool canMoveBackwards() const {return m_template->m_canMoveBackward;} inline Real getMaxWheelExtension() const {return m_template->m_maximumWheelExtension;} inline Real getMaxWheelCompression() const {return m_template->m_maximumWheelCompression;} inline Real getWheelTurnAngle() const {return m_template->m_wheelTurnAngle;} inline Real getRudderCorrectionDegree() const { return m_template->m_rudderCorrectionDegree;} ///< How much we roll in response to acceleration. inline Real getRudderCorrectionRate() const { return m_template->m_rudderCorrectionRate;} ///< How much we roll in response to acceleration. inline Real getElevatorCorrectionDegree() const { return m_template->m_elevatorCorrectionDegree;} ///< How much we roll in response to acceleration. inline Real getElevatorCorrectionRate() const { return m_template->m_elevatorCorrectionRate;} ///< How much we roll in response to acceleration. inline Real getWanderWidthFactor() const {return m_template->m_wanderWidthFactor;} inline Real getWanderAboutPointRadius() const {return m_template->m_wanderAboutPointRadius;} Real calcMinTurnRadius(BodyDamageType condition, Real* timeToTravelThatDist) const; /// this is handy for doing things like forcing helicopters to crash realistically: cut their lift. inline void setMaxLift(Real lift) { m_maxLift = lift; } inline void setMaxSpeed(Real speed) { DEBUG_ASSERTCRASH(!(speed <= 0.0f && m_template->m_appearance == LOCO_THRUST), ("THRUST locos may not have zero speeds!\n")); m_maxSpeed = speed; } inline void setMaxAcceleration(Real accel) { m_maxAccel = accel; } inline void setMaxBraking(Real braking) { m_maxBraking = braking; } inline void setMaxTurnRate(Real turn) { m_maxTurnRate = turn; } inline void setAllowInvalidPosition(Bool allow) { setFlag(ALLOW_INVALID_POSITION, allow); } inline void setCloseEnoughDist( Real dist ) { m_closeEnoughDist = dist; } inline void setCloseEnoughDist3D( Bool setting ) { setFlag(IS_CLOSE_ENOUGH_DIST_3D, setting); } inline Bool isInvalidPositionAllowed() const { return getFlag( ALLOW_INVALID_POSITION ); } inline void setPreferredHeight( Real height ) { m_preferredHeight = height; } #ifdef CIRCLE_FOR_LANDING /** if we are climbing/diving more than this, circle as needed rather than just diving or climbing directly. (only useful for Winged things) */ inline void setAltitudeChangeThresholdForCircling(Real a) { m_circleThresh = a; } #endif /** when off (the default), things get to adjust their z-pos as their loco says (in particular, airborne things tend to try to fly at a preferred height). when on, they do their best to reach the specified zpos, even if it's not at their preferred height. this is used mainly for force missiles to swoop in on their target, and to force airplane takeoff/landing to go smoothly. */ inline void setUsePreciseZPos(Bool u) { setFlag(PRECISE_Z_POS, u); } /** when off (the default), units slow down as they approach their target. when on, units go full speed till the end, and may overshoot their target. this is useful mainly in some weird, temporary situations where we know we are going to follow this move with another one... or for carbombs. */ inline void setNoSlowDownAsApproachingDest(Bool u) { setFlag(NO_SLOW_DOWN_AS_APPROACHING_DEST, u); } /** when off (the default), units do their normal stuff. when on, we cheat and make very precise motion, regardless of loco settings. this is accomplished by cranking up the unit's turning rate, friction, lift (for airborne things), and possibly other things. This is useful mainly when doing maneuvers where precision is VITAL, such as airplane takeoff/landing. For ground units, it also allows units to have a destination off of a pathfing grid. */ inline void setUltraAccurate(Bool u) { setFlag(ULTRA_ACCURATE, u); } inline Bool isUltraAccurate() const { return getFlag(ULTRA_ACCURATE); } inline Bool isMovingBackwards(void) const {return getFlag(MOVING_BACKWARDS);} void startMove(void); ///< Indicates that a move is starting, primarily to reset the donut timer. jba. protected: void moveTowardsPositionLegs(Object* obj, PhysicsBehavior *physics, const Coord3D& goalPos, Real onPathDistToGoal, Real desiredSpeed); void moveTowardsPositionLegsWander(Object* obj, PhysicsBehavior *physics, const Coord3D& goalPos, Real onPathDistToGoal, Real desiredSpeed); void moveTowardsPositionClimb(Object* obj, PhysicsBehavior *physics, const Coord3D& goalPos, Real onPathDistToGoal, Real desiredSpeed); void moveTowardsPositionWheels(Object* obj, PhysicsBehavior *physics, const Coord3D& goalPos, Real onPathDistToGoal, Real desiredSpeed); void moveTowardsPositionTreads(Object* obj, PhysicsBehavior *physics, const Coord3D& goalPos, Real onPathDistToGoal, Real desiredSpeed); void moveTowardsPositionOther(Object* obj, PhysicsBehavior *physics, const Coord3D& goalPos, Real onPathDistToGoal, Real desiredSpeed); void moveTowardsPositionHover(Object* obj, PhysicsBehavior *physics, const Coord3D& goalPos, Real onPathDistToGoal, Real desiredSpeed); void moveTowardsPositionThrust(Object* obj, PhysicsBehavior *physics, const Coord3D& goalPos, Real onPathDistToGoal, Real desiredSpeed); void moveTowardsPositionWings(Object* obj, PhysicsBehavior *physics, const Coord3D& goalPos, Real onPathDistToGoal, Real desiredSpeed); void maintainCurrentPositionThrust(Object* obj, PhysicsBehavior *physics); void maintainCurrentPositionOther(Object* obj, PhysicsBehavior *physics); void maintainCurrentPositionLegs(Object* obj, PhysicsBehavior *physics) { maintainCurrentPositionOther(obj, physics); } void maintainCurrentPositionWheels(Object* obj, PhysicsBehavior *physics) { maintainCurrentPositionOther(obj, physics); } void maintainCurrentPositionTreads(Object* obj, PhysicsBehavior *physics) { maintainCurrentPositionOther(obj, physics); } void maintainCurrentPositionHover(Object* obj, PhysicsBehavior *physics); void maintainCurrentPositionWings(Object* obj, PhysicsBehavior *physics); PhysicsTurningType rotateTowardsPosition(Object* obj, const Coord3D& goalPos, Real *relAngle=NULL); /* return true if we can maintain the position without being called every frame (eg, we are resting on the ground), false if not (eg, we are hovering or circling) */ Bool handleBehaviorZ(Object* obj, PhysicsBehavior *physics, const Coord3D& goalPos); PhysicsTurningType rotateObjAroundLocoPivot(Object* obj, const Coord3D& goalPos, Real maxTurnRate, Real *relAngle = NULL); Real getSurfaceHtAtPt(Real x, Real y); Real calcLiftToUseAtPt(Object* obj, PhysicsBehavior *physics, Real curZ, Real surfaceAtPt, Real preferredHeight); Bool fixInvalidPosition(Object* obj, PhysicsBehavior *physics); protected: // snapshot methods virtual void crc( Xfer *xfer ); virtual void xfer( Xfer *xfer ); virtual void loadPostProcess( void ); protected: Locomotor(const LocomotorTemplate* tmpl); // Note, "Law of the Big Three" applies here //Locomotor(); -- nope, we don't have a default ctor. (srj) Locomotor(const Locomotor& that); Locomotor& operator=(const Locomotor& that); //~Locomotor(); private: // // Note: these values are saved in save files, so you MUST NOT REMOVE OR CHANGE // existing values! // enum LocoFlag { IS_BRAKING = 0, ALLOW_INVALID_POSITION, MAINTAIN_POS_IS_VALID, PRECISE_Z_POS, NO_SLOW_DOWN_AS_APPROACHING_DEST, OVER_WATER, // To allow things to move slower/faster over water and do special effects ULTRA_ACCURATE, MOVING_BACKWARDS, // If we are moving backwards. DOING_THREE_POINT_TURN, // If we are doing a 3 pt turn. CLIMBING, // If we are in the process of climbing. IS_CLOSE_ENOUGH_DIST_3D, OFFSET_INCREASING }; inline Bool getFlag(LocoFlag f) const { return (m_flags & (1 << f)) != 0; } inline void setFlag(LocoFlag f, Bool b) { if (b) m_flags |= (1< > LocomotorTemplateMap; LocomotorTemplateMap m_locomotorTemplates; }; // EXTERNALS ////////////////////////////////////////////////////////////////////////////////////// extern LocomotorStore *TheLocomotorStore; #endif // __Locomotor_H_