/* ** Command & Conquer Generals(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: TerrainLogic.h /////////////////////////////////////////////////////////////////////////// // Logical terrain representation for the game logic side // Author: Colin Day, April 2001 /////////////////////////////////////////////////////////////////////////////////////////////////// #pragma once #ifndef __TERRAINLOGIC_H_ #define __TERRAINLOGIC_H_ #include "Common/GameMemory.h" #include "Common/Snapshot.h" #include "Common/STLTypedefs.h" #include "GameClient/TerrainRoads.h" typedef std::vector VecICoord2D; class DataChunkInput; struct DataChunkInfo; class MapObject; class Object; class Dict; class PolygonTrigger; class ThingTemplate; class Vector3; class Drawable; class Matrix3D; class WaterHandle; class Xfer; enum WaypointID { INVALID_WAYPOINT_ID = 0x7FFFFFFF }; //------------------------------------------------------------------------------------------------- // Waypoint /** Helper class for waypoint info in terrain logic. */ class Waypoint : public MemoryPoolObject { MEMORY_POOL_GLUE_WITH_USERLOOKUP_CREATE(Waypoint, "Waypoint") // friends do not play well with MPO (srj) //friend class TerrainLogic; public: Waypoint(WaypointID id, AsciiString name, const Coord3D *pLoc, AsciiString label1, AsciiString label2, AsciiString label3, Bool biDirectional); //~Waypoint(); enum {MAX_LINKS=8}; protected: WaypointID m_id; ///< Unique integer identifier. AsciiString m_name; ///< Name. Coord3D m_location; ///< Location. Waypoint* m_pNext; ///< Linked list of all waypoints. Waypoint* m_links[MAX_LINKS]; ///< Directed graph of waypoints. Int m_numLinks; ///< Number of links in m_links. AsciiString m_pathLabel1; AsciiString m_pathLabel2; AsciiString m_pathLabel3; Bool m_biDirectional; public: // should be protected, but friendly access needed (srj) void setNext(Waypoint *pNext) {m_pNext = pNext; } //void setLink(Int ndx, Waypoint *pLink) //{ // if (ndx>=0 && ndx <=MAX_LINKS) m_links[ndx] = pLink; //} void addLink(Waypoint* pLink) { if (m_numLinks < MAX_LINKS) { m_links[m_numLinks] = pLink; ++m_numLinks; } } public: /// Enumerate all waypoints using getNext. Waypoint *getNext(void) const {return m_pNext; } /// Enumerate the directed links from a waypoint using this,a nd getLink. Int getNumLinks(void) const {return m_numLinks; } /// Get the n'th directed link. (May be NULL). Waypoint *getLink(Int ndx) const {if (ndx>=0 && ndx <= MAX_LINKS) return m_links[ndx]; return NULL; } /// Get the waypoint's name. AsciiString getName(void) const {return m_name; } /// Get the integer id. WaypointID getID(void) const {return m_id; } /// Get the waypoint's position const Coord3D *getLocation( void ) const { return &m_location; } /// Get the waypoint's first path label AsciiString getPathLabel1( void ) const { return m_pathLabel1; } /// Get the waypoint's second path label AsciiString getPathLabel2( void ) const { return m_pathLabel2; } /// Get the waypoint's third path label AsciiString getPathLabel3( void ) const { return m_pathLabel3; } /// Get bi-directionality. Bool getBiDirectional( void ) const { return m_biDirectional; } void setLocationZ(Real z) { m_location.z = z; } }; //------------------------------------------------------------------------------------------------- // Bridge /** Helper class for bridge info in terrain logic. */ class BridgeInfo { public: BridgeInfo(); public: Coord3D from, to; /// The points that the bridge was drawn using. Real bridgeWidth; /// Width of the bridge. Coord3D fromLeft, fromRight, toLeft, toRight; /// The 4 corners of the rectangle that the bridge covers. Int bridgeIndex; ///< The index to the drawable bridges. BodyDamageType curDamageState; ObjectID bridgeObjectID; ObjectID towerObjectID[ BRIDGE_MAX_TOWERS ]; Bool damageStateChanged; }; //------------------------------------------------------------------------------------------------- // Bridge /** Helper class for bridge info in terrain logic. */ struct TBridgeAttackInfo { public: Coord3D attackPoint1, attackPoint2; /// The points that can be attacked.. }; //------------------------------------------------------------------------------------------------- //------------------------------------------------------------------------------------------------- class Bridge : public MemoryPoolObject { MEMORY_POOL_GLUE_WITH_USERLOOKUP_CREATE(Bridge, "Bridge") // friends do not play well with MPO (srj) //friend class TerrainLogic; public: public: // ctor/dtor. Bridge(BridgeInfo &theInfo, Dict *props, AsciiString bridgeTemplateName); Bridge(Object *bridgeObj); //~Bridge(); protected: Bridge* m_next; ///< Link for traversing all bridges in the current map. AsciiString m_templateName; ///< bridge template name BridgeInfo m_bridgeInfo; Region2D m_bounds; /// 2d bounds for quick screening. PathfindLayerEnum m_layer; ///< Pathfind layer for this bridge. public: // should be protected, but friendly access needed (srj) void setNext(Bridge *pNext) {m_next = pNext; } Object *createTower( Coord3D *worldPos, BridgeTowerType towerPos, const ThingTemplate *towerTemplate, Object *bridge ); public: /// return the bridge template name AsciiString getBridgeTemplateName( void ) { return m_templateName; } /// Enumerate all bridges using getNext; Bridge *getNext(void) {return m_next; } /// Get the height for an object on bridge. Note - assumes object is on bridge. Use isPointOnBridge to check. Real getBridgeHeight(const Coord3D *pLoc, Coord3D* normal); /// Get the bridges logical info. void getBridgeInfo(class BridgeInfo *pInfo) {*pInfo = m_bridgeInfo; } /// See if the point is on the bridge. Bool isPointOnBridge(const Coord3D *pLoc); Drawable *pickBridge(const Vector3 &from, const Vector3 &to, Vector3 *pos); void updateDamageState(void); ///< Updates a bridge's damage info. inline const BridgeInfo *peekBridgeInfo(void) const {return &m_bridgeInfo;} inline PathfindLayerEnum getLayer(void) const {return m_layer;} inline void setLayer(PathfindLayerEnum layer) {m_layer = layer;} const Region2D *getBounds(void) const {return &m_bounds;} Bool isCellOnEnd(const Region2D *cell); // Is pathfind cell on the sides of the bridge Bool isCellOnSide(const Region2D *cell); // Is pathfind cell on the end of the bridge Bool isCellEntryPoint(const Region2D *cell); // Is pathfind cell an entry point to the bridge inline void setBridgeObjectID( ObjectID id ) { m_bridgeInfo.bridgeObjectID = id; } inline void setTowerObjectID( ObjectID id, BridgeTowerType which ) { m_bridgeInfo.towerObjectID[ which ] = id; } }; //------------------------------------------------------------------------------------------------- /** Device independent implementation for some functionality of the * logical terrain singleton */ //------------------------------------------------------------------------------------------------- class TerrainLogic : public Snapshot, public SubsystemInterface { public: TerrainLogic(); virtual ~TerrainLogic(); virtual void init( void ); ///< Init virtual void reset( void ); ///< Reset virtual void update( void ); ///< Update virtual Bool loadMap( AsciiString filename, Bool query ); virtual void newMap( Bool saveGame ); ///< Initialize the logic for new map. virtual Real getGroundHeight( Real x, Real y, Coord3D* normal = NULL ) const; virtual Real getLayerHeight(Real x, Real y, PathfindLayerEnum layer, Coord3D* normal = NULL, Bool clip = true) const; virtual void getExtent( Region3D *extent ) const { DEBUG_CRASH(("not implemented")); } ///< @todo This should not be a stub - this should own this functionality virtual void getExtentIncludingBorder( Region3D *extent ) const { DEBUG_CRASH(("not implemented")); } ///< @todo This should not be a stub - this should own this functionality virtual void getMaximumPathfindExtent( Region3D *extent ) const { DEBUG_CRASH(("not implemented")); } ///< @todo This should not be a stub - this should own this functionality virtual Coord3D findClosestEdgePoint( const Coord3D *closestTo ) const ; virtual Coord3D findFarthestEdgePoint( const Coord3D *farthestFrom ) const ; virtual Bool isClearLineOfSight(const Coord3D& pos, const Coord3D& posOther) const; virtual AsciiString getSourceFilename( void ) { return m_filenameString; } virtual PathfindLayerEnum alignOnTerrain( Real angle, const Coord3D& pos, Bool stickToGround, Matrix3D& mtx); virtual Bool isUnderwater( Real x, Real y, Real *waterZ = NULL, Real *terrainZ = NULL ); ///< is point under water virtual Bool isCliffCell( Real x, Real y) const; ///< is point cliff cell virtual const WaterHandle* getWaterHandle( Real x, Real y ); ///< get water handle at this location virtual const WaterHandle* getWaterHandleByName( AsciiString name ); ///< get water handle by name virtual Real getWaterHeight( const WaterHandle *water ); ///< get height of water table virtual void setWaterHeight( const WaterHandle *water, Real height, Real damageAmount, Bool forcePathfindUpdate ); ///< set height of water table virtual void changeWaterHeightOverTime( const WaterHandle *water, Real finalHeight, Real transitionTimeInSeconds, Real damageAmount );///< change water height over time virtual Waypoint *getFirstWaypoint(void) { return m_waypointListHead; } /// Return the waypoint with the given name virtual Waypoint *getWaypointByName( AsciiString name ); /// Return the waypoint with the given ID virtual Waypoint *getWaypointByID( UnsignedInt id ); /// Return the closest waypoint on the labeled path virtual Waypoint *getClosestWaypointOnPath( const Coord3D *pos, AsciiString label ); /// Return true if the waypoint path containint pWay is labeled with the label. virtual Bool isPurposeOfPath( Waypoint *pWay, AsciiString label ); /// Return the trigger area with the given name virtual PolygonTrigger *getTriggerAreaByName( AsciiString name ); ///Gets the first bridge. Traverse all bridges using bridge->getNext(); virtual Bridge *getFirstBridge(void) const { return m_bridgeListHead; } /// Find the bridge at a location. NULL means no bridge. virtual Bridge *findBridgeAt(const Coord3D *pLoc) const; /// Find the bridge at a location. NULL means no bridge. Note that the layer value will be used to resolve crossing bridges. virtual Bridge *findBridgeLayerAt(const Coord3D *pLoc, PathfindLayerEnum layer, Bool clip = true) const; /// Returns true if the object is close enough to interact with the bridge for pathfinding. virtual Bool objectInteractsWithBridgeLayer(Object *obj, Int layer, Bool considerBridgeHealth = true) const; /// Returns true if the object is close to one or the other end of the bridge. virtual Bool objectInteractsWithBridgeEnd(Object *obj, Int layer) const; virtual Drawable *pickBridge(const Vector3 &from, const Vector3 &to, Vector3 *pos); virtual void addBridgeToLogic(BridgeInfo *pInfo, Dict *props, AsciiString bridgeTemplateName); ///< Adds a bridge's logical info. virtual void addLandmarkBridgeToLogic(Object *bridgeObj); ///< Adds a bridge's logical info. virtual void deleteBridge( Bridge *bridge ); ///< remove a bridge virtual void updateBridgeDamageStates(void); ///< Updates bridge's damage info. Bool anyBridgesDamageStatesChanged(void) {return m_bridgeDamageStatesChanged; } ///< Bridge damage states updated. Bool isBridgeRepaired(const Object *bridge); ///< Is bridge repaired? Bool isBridgeBroken(const Object *bridge); ///< Is bridge Broken? void getBridgeAttackPoints(const Object *bridge, TBridgeAttackInfo *info); ///< Get bridge attack points. PathfindLayerEnum getLayerForDestination(const Coord3D *pos); // this is just like getLayerForDestination, but always return the highest layer that will be <= z at that point // (unlike getLayerForDestination, which will return the closest layer) PathfindLayerEnum getHighestLayerForDestination(const Coord3D *pos, Bool onlyHealthyBridges = false); void enableWaterGrid( Bool enable ); ///< enable/disable the water grid // This is stuff to get the currently active boundary information Int getActiveBoundary(void) { return m_activeBoundary; } void setActiveBoundary(Int newActiveBoundary); void flattenTerrain(Object *obj); ///< Flatten the terrain under a building. protected: // snapshot methods virtual void crc( Xfer *xfer ); virtual void xfer( Xfer *xfer ); virtual void loadPostProcess( void ); /// Chunk parser callback. static Bool parseWaypointDataChunk(DataChunkInput &file, DataChunkInfo *info, void *userData); /// Chunk parser callback. Bool parseWaypointData(DataChunkInput &file, DataChunkInfo *info, void *userData); /// Add a waypoint to the list. void addWaypoint(MapObject *pMapObj); /// Add a directed link between waypoints. void addWaypointLink(Int id1, Int id2); /// Deletes all waypoints. void deleteWaypoints(void); /// Deletes all bridges. void deleteBridges(void); /// find the axis aligned region bounding the water table void findAxisAlignedBoundingRect( const WaterHandle *waterHandle, Region3D *region ); UnsignedByte *m_mapData; ///< array of height samples Int m_mapDX; ///< width of map samples Int m_mapDY; ///< height of map samples VecICoord2D m_boundaries; Int m_activeBoundary; Waypoint *m_waypointListHead; Bridge *m_bridgeListHead; Bool m_bridgeDamageStatesChanged; AsciiString m_filenameString; ///< filename for terrain data Bool m_waterGridEnabled; ///< TRUE when water grid is enabled static WaterHandle m_gridWaterHandle; ///< water handle for the grid water (we only presently have one) // // we will force a limit of MAX_DYNAMIC_WATER as the max dynamically changable water // tables for a map. We could use a list, but eh, this is fine and small anyway // enum { MAX_DYNAMIC_WATER = 64 }; struct DynamicWaterEntry { const WaterHandle *waterTable; ///< handle to water table to edit Real changePerFrame; ///< how much height to add to the water each frame (negative=lowering) Real targetHeight; ///< the target height we want to be at Real damageAmount; ///< amount of damage to do to objects that are underwater Real currentHeight; ///< we need to keep track of this ourselves cause some water height are represented with ints } m_waterToUpdate[ MAX_DYNAMIC_WATER ]; ///< water tables to dynamicall update Int m_numWaterToUpdate; ///< how many valid entries are in m_waterToUpdate }; // end class TerrainLogic // EXTERNALS ////////////////////////////////////////////////////////////////////////////////////// extern TerrainLogic *TheTerrainLogic; ///< singleton definition extern void makeAlignToNormalMatrix( Real angle, const Coord3D& pos, const Coord3D& normal, Matrix3D& mtx); extern Bool LineInRegion( const Coord2D *p1, const Coord2D *p2, const Region2D *clipRegion ); #endif // end __TERRAINLOGIC_H_