/*
** 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_