From 82d9f62f6b9a0d38a529c058a47f79d35737a77c Mon Sep 17 00:00:00 2001 From: Omar Aglan Date: Sat, 22 Aug 2026 22:14:37 +0300 Subject: [PATCH 1/2] unify(map): Merge GameLogic map headers and implementations --- .../Include/Common/MapReaderWriterInfo.h | 1 + .../Include/GameLogic/PolygonTrigger.h | 16 ++++++ .../Include/GameLogic/TerrainLogic.h | 3 ++ .../Source/GameLogic/Map/PolygonTrigger.cpp | 32 +++++++++++ .../Source/GameLogic/Map/SidesList.cpp | 20 +++++++ .../Source/GameLogic/Map/TerrainLogic.cpp | 53 ++++++++++++++++++- .../Include/GameLogic/PolygonTrigger.h | 4 ++ .../Include/GameLogic/TerrainLogic.h | 2 + .../Source/GameLogic/Map/PolygonTrigger.cpp | 16 ++++++ .../Source/GameLogic/Map/SidesList.cpp | 10 ++++ .../Source/GameLogic/Map/TerrainLogic.cpp | 10 +--- 11 files changed, 158 insertions(+), 9 deletions(-) diff --git a/Generals/Code/GameEngine/Include/Common/MapReaderWriterInfo.h b/Generals/Code/GameEngine/Include/Common/MapReaderWriterInfo.h index c370ac5b3c0..6496fb891bb 100644 --- a/Generals/Code/GameEngine/Include/Common/MapReaderWriterInfo.h +++ b/Generals/Code/GameEngine/Include/Common/MapReaderWriterInfo.h @@ -48,6 +48,7 @@ #define K_TRIGGERS_VERSION_1 1 #define K_TRIGGERS_VERSION_2 2 // Added m_isWaterArea #define K_TRIGGERS_VERSION_3 3 // Added m_isRiver & m_riverStart +#define K_TRIGGERS_VERSION_4 4 // Added layer name. #define K_LIGHTING_VERSION_1 1 #define K_LIGHTING_VERSION_2 2 // Added 2 additional global lights for objects. #define K_LIGHTING_VERSION_3 3 // Added 2 additional global lights for terrain. diff --git a/Generals/Code/GameEngine/Include/GameLogic/PolygonTrigger.h b/Generals/Code/GameEngine/Include/GameLogic/PolygonTrigger.h index d3e133834fc..7a908e7d1ec 100644 --- a/Generals/Code/GameEngine/Include/GameLogic/PolygonTrigger.h +++ b/Generals/Code/GameEngine/Include/GameLogic/PolygonTrigger.h @@ -81,6 +81,11 @@ class PolygonTrigger : public MemoryPoolObject, Bool m_exportWithScripts; Bool m_isWaterArea; ///< Used to specify water areas in the map. Bool m_isRiver; ///< Used to specify that a water area is a river. +#if !(RTS_GENERALS && RETAIL_COMPATIBLE_CRC) + AsciiString m_layerName; ///< Used to specify the layer in the World Builder. + Bool m_shouldRender; + Bool m_selected; +#endif static PolygonTrigger* ThePolygonTriggerListPtr; static Int s_currentID; ///< Current id for new triggers. @@ -116,6 +121,17 @@ class PolygonTrigger : public MemoryPoolObject, void deletePoint(Int ndx); void setTriggerName(AsciiString name) {m_triggerName = name;}; +#if !(RTS_GENERALS && RETAIL_COMPATIBLE_CRC) + void setLayerName(AsciiString name) {m_layerName = name;}; + AsciiString getLayerName() const {return m_layerName;} + + void setShouldRender(Bool toggle) {m_shouldRender = toggle;} + Bool getShouldRender() {return m_shouldRender;} + + void setSelected(Bool toggle) {m_selected = toggle;} + Bool getSelected() {return m_selected;} +#endif + void getCenterPoint(Coord3D* pOutCoord) const; Real getRadius() const; diff --git a/Generals/Code/GameEngine/Include/GameLogic/TerrainLogic.h b/Generals/Code/GameEngine/Include/GameLogic/TerrainLogic.h index ad1db103499..c362afecf1f 100644 --- a/Generals/Code/GameEngine/Include/GameLogic/TerrainLogic.h +++ b/Generals/Code/GameEngine/Include/GameLogic/TerrainLogic.h @@ -312,6 +312,9 @@ class TerrainLogic : public Snapshot, void setActiveBoundary(Int newActiveBoundary); void flattenTerrain(Object *obj); ///< Flatten the terrain under a building. +#if !(RTS_GENERALS && RETAIL_COMPATIBLE_CRC) + void createCraterInTerrain(Object *obj); ///< Flatten the terrain under a building. +#endif protected: diff --git a/Generals/Code/GameEngine/Source/GameLogic/Map/PolygonTrigger.cpp b/Generals/Code/GameEngine/Source/GameLogic/Map/PolygonTrigger.cpp index 6cea7c6227a..869825775b3 100644 --- a/Generals/Code/GameEngine/Source/GameLogic/Map/PolygonTrigger.cpp +++ b/Generals/Code/GameEngine/Source/GameLogic/Map/PolygonTrigger.cpp @@ -48,6 +48,10 @@ m_numPoints(0), m_sizePoints(0), m_exportWithScripts(false), m_isWaterArea(false), +#if !(RTS_GENERALS && RETAIL_COMPATIBLE_CRC) +m_shouldRender(true), +m_selected(false), +#endif m_isRiver(FALSE), m_riverStart(0) { @@ -140,6 +144,9 @@ Bool PolygonTrigger::ParsePolygonTriggersDataChunk(DataChunkInput &file, DataChu Bool isRiver; Int riverStart; AsciiString triggerName; +#if !(RTS_GENERALS && RETAIL_COMPATIBLE_CRC) + AsciiString layerName; +#endif // Remove any existing polygon triggers, if any. PolygonTrigger::deleteTriggers(); // just in case. PolygonTrigger *pPrevTrig = nullptr; @@ -148,6 +155,11 @@ Bool PolygonTrigger::ParsePolygonTriggersDataChunk(DataChunkInput &file, DataChu while (count>0) { count--; triggerName = file.readAsciiString(); +#if !(RTS_GENERALS && RETAIL_COMPATIBLE_CRC) + if (info->version >= K_TRIGGERS_VERSION_4) { + layerName = file.readAsciiString(); + } +#endif triggerID = file.readInt(); isWater = false; if (info->version >= K_TRIGGERS_VERSION_2) { @@ -163,6 +175,11 @@ Bool PolygonTrigger::ParsePolygonTriggersDataChunk(DataChunkInput &file, DataChu numPoints = file.readInt(); PolygonTrigger *pTrig = newInstance(PolygonTrigger)(numPoints+1); pTrig->setTriggerName(triggerName); +#if !(RTS_GENERALS && RETAIL_COMPATIBLE_CRC) + if (info->version >= K_TRIGGERS_VERSION_4) { + pTrig->setLayerName(layerName); + } +#endif pTrig->setWaterArea(isWater); pTrig->setRiver(isRiver); pTrig->setRiverStart(riverStart); @@ -177,6 +194,14 @@ Bool PolygonTrigger::ParsePolygonTriggersDataChunk(DataChunkInput &file, DataChu loc.z = file.readInt(); pTrig->addPoint(loc); } +#if !(RTS_GENERALS && RETAIL_COMPATIBLE_CRC) + if (numPoints<2) { + DEBUG_LOG(("Deleting polygon trigger '%s' with %d points.", + pTrig->getTriggerName().str(), numPoints)); + deleteInstance(pTrig); + continue; + } +#endif if (pPrevTrig) { pPrevTrig->setNextPoly(pTrig); } else { @@ -224,7 +249,11 @@ Bool PolygonTrigger::ParsePolygonTriggersDataChunk(DataChunkInput &file, DataChu */ void PolygonTrigger::WritePolygonTriggersDataChunk(DataChunkOutput &chunkWriter) { +#if RTS_GENERALS && RETAIL_COMPATIBLE_CRC chunkWriter.openDataChunk("PolygonTriggers", K_TRIGGERS_VERSION_3); +#else + chunkWriter.openDataChunk("PolygonTriggers", K_TRIGGERS_VERSION_4); +#endif PolygonTrigger *pTrig; Int count = 0; @@ -234,6 +263,9 @@ void PolygonTrigger::WritePolygonTriggersDataChunk(DataChunkOutput &chunkWriter) chunkWriter.writeInt(count); for (pTrig=PolygonTrigger::getFirstPolygonTrigger(); pTrig; pTrig = pTrig->getNext()) { chunkWriter.writeAsciiString(pTrig->getTriggerName()); +#if !(RTS_GENERALS && RETAIL_COMPATIBLE_CRC) + chunkWriter.writeAsciiString(pTrig->getLayerName()); +#endif chunkWriter.writeInt(pTrig->getID()); chunkWriter.writeByte(pTrig->isWaterArea()); chunkWriter.writeByte(pTrig->isRiver()); diff --git a/Generals/Code/GameEngine/Source/GameLogic/Map/SidesList.cpp b/Generals/Code/GameEngine/Source/GameLogic/Map/SidesList.cpp index 93bb5693832..88679b2bc53 100644 --- a/Generals/Code/GameEngine/Source/GameLogic/Map/SidesList.cpp +++ b/Generals/Code/GameEngine/Source/GameLogic/Map/SidesList.cpp @@ -429,13 +429,29 @@ static AsciiString static_readPlayerNames[MAX_PLAYER_COUNT]; * Input: DataChunkInput * */ +#if !(RTS_GENERALS && RETAIL_COMPATIBLE_CRC) +#define K_PLAYERS_NAMES_FOR_SCRIPTS_VERSION_1 1 +#define K_PLAYERS_NAMES_FOR_SCRIPTS_VERSION_2 2 +#endif + static Bool ParsePlayersDataChunk(DataChunkInput &file, DataChunkInfo *info, void *userData) { +#if !(RTS_GENERALS && RETAIL_COMPATIBLE_CRC) + Int readDicts = 0; + if (info->version >= K_PLAYERS_NAMES_FOR_SCRIPTS_VERSION_2) { + readDicts = file.readInt(); + } +#endif Int numNames = file.readInt(); Int i; for (i=0; i=MAX_PLAYER_COUNT) break; static_readPlayerNames[i] = file.readAsciiString(); +#if !(RTS_GENERALS && RETAIL_COMPATIBLE_CRC) + if (readDicts) { + Dict sideDict = file.readDict(); + } +#endif } DEBUG_ASSERTCRASH(file.atEndOfChunk(), ("Unexpected data left over.")); return true; @@ -1121,7 +1137,11 @@ void TeamsInfoRec::addTeam(const Dict* d) TEAM_ALLOC_CHUNK = 8 ///< how many teams to alloc at a time }; +#if RTS_GENERALS && RETAIL_COMPATIBLE_CRC DEBUG_ASSERTCRASH(m_numTeams < 1024, ("hmm, seems like an awful lot of teams...")); +#else + DEBUG_ASSERTCRASH(m_numTeams < 2048, ("%d teams have been allocated (so far). This seems excessive.", m_numTeams )); +#endif if (m_numTeams >= m_numTeamsAllocated) { // pool[]ify diff --git a/Generals/Code/GameEngine/Source/GameLogic/Map/TerrainLogic.cpp b/Generals/Code/GameEngine/Source/GameLogic/Map/TerrainLogic.cpp index 00386047267..2545c2956ce 100644 --- a/Generals/Code/GameEngine/Source/GameLogic/Map/TerrainLogic.cpp +++ b/Generals/Code/GameEngine/Source/GameLogic/Map/TerrainLogic.cpp @@ -2384,7 +2384,7 @@ void TerrainLogic::setWaterHeight( const WaterHandle *water, Real height, Real d Coord3D center; center.x = affectedRegion.lo.x + affectedRegion.width() / 2.0f; center.y = affectedRegion.lo.y + affectedRegion.height() / 2.0f; - center.z = 0.0f; // irrelavant + center.z = 0.0f; // irrelevant // the max radius to scan around us is the diagonal of the bounding region Real maxDist = sqrt( affectedRegion.width() * affectedRegion.width() + @@ -2866,6 +2866,57 @@ void TerrainLogic::flattenTerrain(Object *obj) } +#if !(RTS_GENERALS && RETAIL_COMPATIBLE_CRC) +// ------------------------------------------------------------------------------------------------ +/** Dig a deep circular gorge into the terrain beneath an object. */ +// ------------------------------------------------------------------------------------------------ +void TerrainLogic::createCraterInTerrain(Object *obj) +{ + if (obj->getGeometryInfo().getIsSmall()) + return; + + const Coord3D *pos = obj->getPosition(); + Real radius = obj->getGeometryInfo().getMajorRadius(); + + if ( radius <= 0.0f ) + return; // sanity + + ICoord2D iMin, iMax; + iMin.x = REAL_TO_INT_FLOOR( ( pos->x - radius ) / MAP_XY_FACTOR ); + iMin.y = REAL_TO_INT_FLOOR( ( pos->y - radius ) / MAP_XY_FACTOR ); + iMax.x = REAL_TO_INT_FLOOR( ( pos->x + radius ) / MAP_XY_FACTOR ); + iMax.y = REAL_TO_INT_FLOOR( ( pos->y + radius ) / MAP_XY_FACTOR ); + + Real deltaX, deltaY; + + for (Int i = iMin.x; i <= iMax.x; i++ ) + { + for ( Int j=0; j <= iMax.y; j++ ) + { + deltaX = ( i * MAP_XY_FACTOR ) - pos->x; + deltaY = ( j * MAP_XY_FACTOR ) - pos->y; + + Real distance = sqrt( sqr( deltaX ) + sqr( deltaY ) ); + + if ( distance < radius ) //inside circle + { + ICoord2D gridPos; + gridPos.x = i; + gridPos.y = j; + + + Real displacementAmount = radius * (1.0f - distance / radius ); + + Int targetHeight = MAX( 1, TheTerrainVisual->getRawMapHeight( &gridPos ) - displacementAmount ); + + TheTerrainVisual->setRawMapHeight( &gridPos, targetHeight ); + } + } + } + +} +#endif + // ------------------------------------------------------------------------------------------------ /** CRC */ // ------------------------------------------------------------------------------------------------ diff --git a/GeneralsMD/Code/GameEngine/Include/GameLogic/PolygonTrigger.h b/GeneralsMD/Code/GameEngine/Include/GameLogic/PolygonTrigger.h index d618de16a06..1d2acf9152d 100644 --- a/GeneralsMD/Code/GameEngine/Include/GameLogic/PolygonTrigger.h +++ b/GeneralsMD/Code/GameEngine/Include/GameLogic/PolygonTrigger.h @@ -81,9 +81,11 @@ class PolygonTrigger : public MemoryPoolObject, Bool m_exportWithScripts; Bool m_isWaterArea; ///< Used to specify water areas in the map. Bool m_isRiver; ///< Used to specify that a water area is a river. +#if !(RTS_GENERALS && RETAIL_COMPATIBLE_CRC) AsciiString m_layerName; ///< Used to specify the layer in the World Builder. Bool m_shouldRender; Bool m_selected; +#endif static PolygonTrigger* ThePolygonTriggerListPtr; static Int s_currentID; ///< Current id for new triggers. @@ -119,6 +121,7 @@ class PolygonTrigger : public MemoryPoolObject, void deletePoint(Int ndx); void setTriggerName(AsciiString name) {m_triggerName = name;}; +#if !(RTS_GENERALS && RETAIL_COMPATIBLE_CRC) void setLayerName(AsciiString name) {m_layerName = name;}; AsciiString getLayerName() const {return m_layerName;} @@ -127,6 +130,7 @@ class PolygonTrigger : public MemoryPoolObject, void setSelected(Bool toggle) {m_selected = toggle;} Bool getSelected() {return m_selected;} +#endif void getCenterPoint(Coord3D* pOutCoord) const; Real getRadius() const; diff --git a/GeneralsMD/Code/GameEngine/Include/GameLogic/TerrainLogic.h b/GeneralsMD/Code/GameEngine/Include/GameLogic/TerrainLogic.h index 90dda7f2411..d3654a946ee 100644 --- a/GeneralsMD/Code/GameEngine/Include/GameLogic/TerrainLogic.h +++ b/GeneralsMD/Code/GameEngine/Include/GameLogic/TerrainLogic.h @@ -312,7 +312,9 @@ class TerrainLogic : public Snapshot, void setActiveBoundary(Int newActiveBoundary); void flattenTerrain(Object *obj); ///< Flatten the terrain under a building. +#if !(RTS_GENERALS && RETAIL_COMPATIBLE_CRC) void createCraterInTerrain(Object *obj); ///< Flatten the terrain under a building. +#endif protected: diff --git a/GeneralsMD/Code/GameEngine/Source/GameLogic/Map/PolygonTrigger.cpp b/GeneralsMD/Code/GameEngine/Source/GameLogic/Map/PolygonTrigger.cpp index 4f19ee9cfc2..340edda9abc 100644 --- a/GeneralsMD/Code/GameEngine/Source/GameLogic/Map/PolygonTrigger.cpp +++ b/GeneralsMD/Code/GameEngine/Source/GameLogic/Map/PolygonTrigger.cpp @@ -48,8 +48,10 @@ m_numPoints(0), m_sizePoints(0), m_exportWithScripts(false), m_isWaterArea(false), +#if !(RTS_GENERALS && RETAIL_COMPATIBLE_CRC) m_shouldRender(true), m_selected(false), +#endif m_isRiver(FALSE), m_riverStart(0) { @@ -142,7 +144,9 @@ Bool PolygonTrigger::ParsePolygonTriggersDataChunk(DataChunkInput &file, DataChu Bool isRiver; Int riverStart; AsciiString triggerName; +#if !(RTS_GENERALS && RETAIL_COMPATIBLE_CRC) AsciiString layerName; +#endif // Remove any existing polygon triggers, if any. PolygonTrigger::deleteTriggers(); // just in case. PolygonTrigger *pPrevTrig = nullptr; @@ -151,9 +155,11 @@ Bool PolygonTrigger::ParsePolygonTriggersDataChunk(DataChunkInput &file, DataChu while (count>0) { count--; triggerName = file.readAsciiString(); +#if !(RTS_GENERALS && RETAIL_COMPATIBLE_CRC) if (info->version >= K_TRIGGERS_VERSION_4) { layerName = file.readAsciiString(); } +#endif triggerID = file.readInt(); isWater = false; if (info->version >= K_TRIGGERS_VERSION_2) { @@ -169,9 +175,11 @@ Bool PolygonTrigger::ParsePolygonTriggersDataChunk(DataChunkInput &file, DataChu numPoints = file.readInt(); PolygonTrigger *pTrig = newInstance(PolygonTrigger)(numPoints+1); pTrig->setTriggerName(triggerName); +#if !(RTS_GENERALS && RETAIL_COMPATIBLE_CRC) if (info->version >= K_TRIGGERS_VERSION_4) { pTrig->setLayerName(layerName); } +#endif pTrig->setWaterArea(isWater); pTrig->setRiver(isRiver); pTrig->setRiverStart(riverStart); @@ -186,12 +194,14 @@ Bool PolygonTrigger::ParsePolygonTriggersDataChunk(DataChunkInput &file, DataChu loc.z = file.readInt(); pTrig->addPoint(loc); } +#if !(RTS_GENERALS && RETAIL_COMPATIBLE_CRC) if (numPoints<2) { DEBUG_LOG(("Deleting polygon trigger '%s' with %d points.", pTrig->getTriggerName().str(), numPoints)); deleteInstance(pTrig); continue; } +#endif if (pPrevTrig) { pPrevTrig->setNextPoly(pTrig); } else { @@ -239,7 +249,11 @@ Bool PolygonTrigger::ParsePolygonTriggersDataChunk(DataChunkInput &file, DataChu */ void PolygonTrigger::WritePolygonTriggersDataChunk(DataChunkOutput &chunkWriter) { +#if RTS_GENERALS && RETAIL_COMPATIBLE_CRC + chunkWriter.openDataChunk("PolygonTriggers", K_TRIGGERS_VERSION_3); +#else chunkWriter.openDataChunk("PolygonTriggers", K_TRIGGERS_VERSION_4); +#endif PolygonTrigger *pTrig; Int count = 0; @@ -249,7 +263,9 @@ void PolygonTrigger::WritePolygonTriggersDataChunk(DataChunkOutput &chunkWriter) chunkWriter.writeInt(count); for (pTrig=PolygonTrigger::getFirstPolygonTrigger(); pTrig; pTrig = pTrig->getNext()) { chunkWriter.writeAsciiString(pTrig->getTriggerName()); +#if !(RTS_GENERALS && RETAIL_COMPATIBLE_CRC) chunkWriter.writeAsciiString(pTrig->getLayerName()); +#endif chunkWriter.writeInt(pTrig->getID()); chunkWriter.writeByte(pTrig->isWaterArea()); chunkWriter.writeByte(pTrig->isRiver()); diff --git a/GeneralsMD/Code/GameEngine/Source/GameLogic/Map/SidesList.cpp b/GeneralsMD/Code/GameEngine/Source/GameLogic/Map/SidesList.cpp index b2c05e49597..7590e64a86b 100644 --- a/GeneralsMD/Code/GameEngine/Source/GameLogic/Map/SidesList.cpp +++ b/GeneralsMD/Code/GameEngine/Source/GameLogic/Map/SidesList.cpp @@ -429,23 +429,29 @@ static AsciiString static_readPlayerNames[MAX_PLAYER_COUNT]; * Input: DataChunkInput * */ +#if !(RTS_GENERALS && RETAIL_COMPATIBLE_CRC) #define K_PLAYERS_NAMES_FOR_SCRIPTS_VERSION_1 1 #define K_PLAYERS_NAMES_FOR_SCRIPTS_VERSION_2 2 +#endif static Bool ParsePlayersDataChunk(DataChunkInput &file, DataChunkInfo *info, void *userData) { +#if !(RTS_GENERALS && RETAIL_COMPATIBLE_CRC) Int readDicts = 0; if (info->version >= K_PLAYERS_NAMES_FOR_SCRIPTS_VERSION_2) { readDicts = file.readInt(); } +#endif Int numNames = file.readInt(); Int i; for (i=0; i=MAX_PLAYER_COUNT) break; static_readPlayerNames[i] = file.readAsciiString(); +#if !(RTS_GENERALS && RETAIL_COMPATIBLE_CRC) if (readDicts) { Dict sideDict = file.readDict(); } +#endif } DEBUG_ASSERTCRASH(file.atEndOfChunk(), ("Unexpected data left over.")); return true; @@ -1131,7 +1137,11 @@ void TeamsInfoRec::addTeam(const Dict* d) TEAM_ALLOC_CHUNK = 8 ///< how many teams to alloc at a time }; +#if RTS_GENERALS && RETAIL_COMPATIBLE_CRC + DEBUG_ASSERTCRASH(m_numTeams < 1024, ("hmm, seems like an awful lot of teams...")); +#else DEBUG_ASSERTCRASH(m_numTeams < 2048, ("%d teams have been allocated (so far). This seems excessive.", m_numTeams )); +#endif if (m_numTeams >= m_numTeamsAllocated) { // pool[]ify diff --git a/GeneralsMD/Code/GameEngine/Source/GameLogic/Map/TerrainLogic.cpp b/GeneralsMD/Code/GameEngine/Source/GameLogic/Map/TerrainLogic.cpp index 5a3d16f86ae..286573d7b70 100644 --- a/GeneralsMD/Code/GameEngine/Source/GameLogic/Map/TerrainLogic.cpp +++ b/GeneralsMD/Code/GameEngine/Source/GameLogic/Map/TerrainLogic.cpp @@ -2866,8 +2866,7 @@ void TerrainLogic::flattenTerrain(Object *obj) } - - +#if !(RTS_GENERALS && RETAIL_COMPATIBLE_CRC) // ------------------------------------------------------------------------------------------------ /** Dig a deep circular gorge into the terrain beneath an object. */ // ------------------------------------------------------------------------------------------------ @@ -2916,12 +2915,7 @@ void TerrainLogic::createCraterInTerrain(Object *obj) } } - - - - - - +#endif // ------------------------------------------------------------------------------------------------ /** CRC */ From a0d5d1d83f588d2822d13edfc7d222f2c2fe2577 Mon Sep 17 00:00:00 2001 From: Omar Aglan Date: Sat, 22 Aug 2026 23:01:31 +0300 Subject: [PATCH 2/2] unify(map): Move GameLogic map headers and implementations to Core --- Core/GameEngine/CMakeLists.txt | 12 +- .../Include/GameLogic/PolygonTrigger.h | 0 .../GameEngine/Include/GameLogic/SidesList.h | 0 .../Include/GameLogic/TerrainLogic.h | 0 .../Source/GameLogic/Map/PolygonTrigger.cpp | 0 .../Source/GameLogic/Map/SidesList.cpp | 0 .../Source/GameLogic/Map/TerrainLogic.cpp | 0 Generals/Code/GameEngine/CMakeLists.txt | 12 +- .../Include/GameLogic/PolygonTrigger.h | 156 - .../GameEngine/Include/GameLogic/SidesList.h | 389 --- .../Include/GameLogic/TerrainLogic.h | 381 --- .../Source/GameLogic/Map/PolygonTrigger.cpp | 576 ---- .../Source/GameLogic/Map/SidesList.cpp | 1176 ------- .../Source/GameLogic/Map/TerrainLogic.cpp | 3040 ----------------- GeneralsMD/Code/GameEngine/CMakeLists.txt | 12 +- scripts/cpp/unify_move_files.py | 7 + 16 files changed, 25 insertions(+), 5736 deletions(-) rename {GeneralsMD/Code => Core}/GameEngine/Include/GameLogic/PolygonTrigger.h (100%) rename {GeneralsMD/Code => Core}/GameEngine/Include/GameLogic/SidesList.h (100%) rename {GeneralsMD/Code => Core}/GameEngine/Include/GameLogic/TerrainLogic.h (100%) rename {GeneralsMD/Code => Core}/GameEngine/Source/GameLogic/Map/PolygonTrigger.cpp (100%) rename {GeneralsMD/Code => Core}/GameEngine/Source/GameLogic/Map/SidesList.cpp (100%) rename {GeneralsMD/Code => Core}/GameEngine/Source/GameLogic/Map/TerrainLogic.cpp (100%) delete mode 100644 Generals/Code/GameEngine/Include/GameLogic/PolygonTrigger.h delete mode 100644 Generals/Code/GameEngine/Include/GameLogic/SidesList.h delete mode 100644 Generals/Code/GameEngine/Include/GameLogic/TerrainLogic.h delete mode 100644 Generals/Code/GameEngine/Source/GameLogic/Map/PolygonTrigger.cpp delete mode 100644 Generals/Code/GameEngine/Source/GameLogic/Map/SidesList.cpp delete mode 100644 Generals/Code/GameEngine/Source/GameLogic/Map/TerrainLogic.cpp diff --git a/Core/GameEngine/CMakeLists.txt b/Core/GameEngine/CMakeLists.txt index eff13029517..9838009eb9f 100644 --- a/Core/GameEngine/CMakeLists.txt +++ b/Core/GameEngine/CMakeLists.txt @@ -486,16 +486,16 @@ set(GAMEENGINE_SRC # Include/GameLogic/ObjectScriptStatusBits.h # Include/GameLogic/ObjectTypes.h # Include/GameLogic/PartitionManager.h -# Include/GameLogic/PolygonTrigger.h + Include/GameLogic/PolygonTrigger.h # Include/GameLogic/Powers.h Include/GameLogic/RankInfo.h # Include/GameLogic/ScriptActions.h # Include/GameLogic/ScriptConditions.h # Include/GameLogic/ScriptEngine.h # Include/GameLogic/Scripts.h -# Include/GameLogic/SidesList.h + Include/GameLogic/SidesList.h # Include/GameLogic/Squad.h -# Include/GameLogic/TerrainLogic.h + Include/GameLogic/TerrainLogic.h # Include/GameLogic/TurretAI.h # Include/GameLogic/VictoryConditions.h # Include/GameLogic/Weapon.h @@ -853,9 +853,9 @@ set(GAMEENGINE_SRC # Source/GameLogic/AI/AITNGuard.cpp # Source/GameLogic/AI/Squad.cpp # Source/GameLogic/AI/TurretAI.cpp -# Source/GameLogic/Map/PolygonTrigger.cpp -# Source/GameLogic/Map/SidesList.cpp -# Source/GameLogic/Map/TerrainLogic.cpp + Source/GameLogic/Map/PolygonTrigger.cpp + Source/GameLogic/Map/SidesList.cpp + Source/GameLogic/Map/TerrainLogic.cpp # Source/GameLogic/Object/Armor.cpp # Source/GameLogic/Object/Behavior/AutoHealBehavior.cpp # Source/GameLogic/Object/Behavior/BattleBusSlowDeathBehavior.cpp diff --git a/GeneralsMD/Code/GameEngine/Include/GameLogic/PolygonTrigger.h b/Core/GameEngine/Include/GameLogic/PolygonTrigger.h similarity index 100% rename from GeneralsMD/Code/GameEngine/Include/GameLogic/PolygonTrigger.h rename to Core/GameEngine/Include/GameLogic/PolygonTrigger.h diff --git a/GeneralsMD/Code/GameEngine/Include/GameLogic/SidesList.h b/Core/GameEngine/Include/GameLogic/SidesList.h similarity index 100% rename from GeneralsMD/Code/GameEngine/Include/GameLogic/SidesList.h rename to Core/GameEngine/Include/GameLogic/SidesList.h diff --git a/GeneralsMD/Code/GameEngine/Include/GameLogic/TerrainLogic.h b/Core/GameEngine/Include/GameLogic/TerrainLogic.h similarity index 100% rename from GeneralsMD/Code/GameEngine/Include/GameLogic/TerrainLogic.h rename to Core/GameEngine/Include/GameLogic/TerrainLogic.h diff --git a/GeneralsMD/Code/GameEngine/Source/GameLogic/Map/PolygonTrigger.cpp b/Core/GameEngine/Source/GameLogic/Map/PolygonTrigger.cpp similarity index 100% rename from GeneralsMD/Code/GameEngine/Source/GameLogic/Map/PolygonTrigger.cpp rename to Core/GameEngine/Source/GameLogic/Map/PolygonTrigger.cpp diff --git a/GeneralsMD/Code/GameEngine/Source/GameLogic/Map/SidesList.cpp b/Core/GameEngine/Source/GameLogic/Map/SidesList.cpp similarity index 100% rename from GeneralsMD/Code/GameEngine/Source/GameLogic/Map/SidesList.cpp rename to Core/GameEngine/Source/GameLogic/Map/SidesList.cpp diff --git a/GeneralsMD/Code/GameEngine/Source/GameLogic/Map/TerrainLogic.cpp b/Core/GameEngine/Source/GameLogic/Map/TerrainLogic.cpp similarity index 100% rename from GeneralsMD/Code/GameEngine/Source/GameLogic/Map/TerrainLogic.cpp rename to Core/GameEngine/Source/GameLogic/Map/TerrainLogic.cpp diff --git a/Generals/Code/GameEngine/CMakeLists.txt b/Generals/Code/GameEngine/CMakeLists.txt index 0db74569602..7f793ca8763 100644 --- a/Generals/Code/GameEngine/CMakeLists.txt +++ b/Generals/Code/GameEngine/CMakeLists.txt @@ -437,16 +437,16 @@ set(GAMEENGINE_SRC Include/GameLogic/ObjectScriptStatusBits.h Include/GameLogic/ObjectTypes.h Include/GameLogic/PartitionManager.h - Include/GameLogic/PolygonTrigger.h +# Include/GameLogic/PolygonTrigger.h Include/GameLogic/Powers.h # Include/GameLogic/RankInfo.h Include/GameLogic/ScriptActions.h Include/GameLogic/ScriptConditions.h Include/GameLogic/ScriptEngine.h Include/GameLogic/Scripts.h - Include/GameLogic/SidesList.h +# Include/GameLogic/SidesList.h Include/GameLogic/Squad.h - Include/GameLogic/TerrainLogic.h +# Include/GameLogic/TerrainLogic.h Include/GameLogic/TurretAI.h Include/GameLogic/VictoryConditions.h Include/GameLogic/Weapon.h @@ -787,9 +787,9 @@ set(GAMEENGINE_SRC Source/GameLogic/AI/AITNGuard.cpp Source/GameLogic/AI/Squad.cpp Source/GameLogic/AI/TurretAI.cpp - Source/GameLogic/Map/PolygonTrigger.cpp - Source/GameLogic/Map/SidesList.cpp - Source/GameLogic/Map/TerrainLogic.cpp +# Source/GameLogic/Map/PolygonTrigger.cpp +# Source/GameLogic/Map/SidesList.cpp +# Source/GameLogic/Map/TerrainLogic.cpp Source/GameLogic/Object/Armor.cpp Source/GameLogic/Object/Behavior/AutoHealBehavior.cpp Source/GameLogic/Object/Behavior/BehaviorModule.cpp diff --git a/Generals/Code/GameEngine/Include/GameLogic/PolygonTrigger.h b/Generals/Code/GameEngine/Include/GameLogic/PolygonTrigger.h deleted file mode 100644 index 7a908e7d1ec..00000000000 --- a/Generals/Code/GameEngine/Include/GameLogic/PolygonTrigger.h +++ /dev/null @@ -1,156 +0,0 @@ -/* -** 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. // -// // -//////////////////////////////////////////////////////////////////////////////// - - -// PolygonTrigger.h -// Class to encapsulate polygon triggers for maps. -// Note - Polygons are used for two reasons - one is area triggers for -// scripts, so units can be tested for entering or exiting areas, and -// second to specify areas that are filled with water in the map. -// See the m_isWaterArea to differentiate. -// Author: John Ahlquist, November 2001 - -#pragma once - -#include "Common/GameMemory.h" -#include "Common/Snapshot.h" -#include "Common/STLTypedefs.h" - -class DataChunkInput; -class DataChunkOutput; -struct DataChunkInfo; -class PolygonTrigger; -class Xfer; - -// ------------------------------------------------------------------------------------------------ -/** Water handles are used to represent instances of areas of water, no matter which type - * of implementation the water is (grid, trigger area, etc) */ -// ------------------------------------------------------------------------------------------------ -class WaterHandle -{ - -public: - - WaterHandle() { m_polygon = nullptr; } - - ///@todo we need to formalize the water systems - PolygonTrigger *m_polygon; ///< valid when water is a polygon area, nullptr if water is a grid - -}; - -// ------------------------------------------------------------------------------------------------ -// ------------------------------------------------------------------------------------------------ -class PolygonTrigger : public MemoryPoolObject, - public Snapshot -{ - MEMORY_POOL_GLUE_WITH_USERLOOKUP_CREATE(PolygonTrigger, "PolygonTrigger") - -protected: - PolygonTrigger* m_nextPolygonTrigger; ///< linked list. - AsciiString m_triggerName; ///< The name of this polygon area. - Int m_triggerID; ///< Unique int id for the trigger. - WaterHandle m_waterHandle; ///< handle to use this polygon as a water table - ICoord3D* m_points; ///< Points that are the polygon. - Int m_numPoints; ///< Num points in m_points. - Int m_sizePoints; ///< Space allocated for m_points. - mutable IRegion2D m_bounds; ///< 2D bounding box for quick checks. - mutable Real m_radius; - Int m_riverStart; ///< Identifies the start point of the river. - mutable Bool m_boundsNeedsUpdate; - Bool m_exportWithScripts; - Bool m_isWaterArea; ///< Used to specify water areas in the map. - Bool m_isRiver; ///< Used to specify that a water area is a river. -#if !(RTS_GENERALS && RETAIL_COMPATIBLE_CRC) - AsciiString m_layerName; ///< Used to specify the layer in the World Builder. - Bool m_shouldRender; - Bool m_selected; -#endif - - static PolygonTrigger* ThePolygonTriggerListPtr; - static Int s_currentID; ///< Current id for new triggers. - -protected: - void reallocate(); - void updateBounds() const; - - // snapshot methods - virtual void crc( Xfer *xfer ) override; - virtual void xfer( Xfer *xfer ) override; - virtual void loadPostProcess() override; - -public: - PolygonTrigger(Int initialAllocation); - //~PolygonTrigger(); ///< Note that deleting the head of a list deletes all linked objects in the list. - -public: - static PolygonTrigger *getFirstPolygonTrigger() {return ThePolygonTriggerListPtr;} - static PolygonTrigger *getPolygonTriggerByID(Int triggerID); - static Bool ParsePolygonTriggersDataChunk(DataChunkInput &file, DataChunkInfo *info, void *userData); - /// Writes Triggers Info - static void WritePolygonTriggersDataChunk(DataChunkOutput &chunkWriter); - static void deleteTriggers(); - -public: - static void addPolygonTrigger(PolygonTrigger *pTrigger); - static void removePolygonTrigger(PolygonTrigger *pTrigger); - void setNextPoly(PolygonTrigger *nextPoly) {m_nextPolygonTrigger = nextPoly;} ///< Link the next map object. - void addPoint(const ICoord3D &point); - void setPoint(const ICoord3D &point, Int ndx); - void insertPoint(const ICoord3D &point, Int ndx); - void deletePoint(Int ndx); - void setTriggerName(AsciiString name) {m_triggerName = name;}; - -#if !(RTS_GENERALS && RETAIL_COMPATIBLE_CRC) - void setLayerName(AsciiString name) {m_layerName = name;}; - AsciiString getLayerName() const {return m_layerName;} - - void setShouldRender(Bool toggle) {m_shouldRender = toggle;} - Bool getShouldRender() {return m_shouldRender;} - - void setSelected(Bool toggle) {m_selected = toggle;} - Bool getSelected() {return m_selected;} -#endif - - void getCenterPoint(Coord3D* pOutCoord) const; - Real getRadius() const; - -public: - const ICoord3D *getPoint(Int ndx) const {if (ndx<0) ndx=0; if (ndx>=m_numPoints) ndx=m_numPoints-1; return m_points+ndx;} ///< Get a point. - Int getNumPoints() const {return m_numPoints;} - Int getID() const {return m_triggerID;} - PolygonTrigger *getNext() {return m_nextPolygonTrigger;} - const PolygonTrigger *getNext() const {return m_nextPolygonTrigger;} - const AsciiString& getTriggerName() const {return m_triggerName;} ///< Gets the trigger name. - Bool pointInTrigger(ICoord3D &point) const; - Bool doExportWithScripts() const {return m_exportWithScripts;} - void setDoExportWithScripts(Bool val) {m_exportWithScripts = val;} - Bool isWaterArea() const {return m_isWaterArea;} - void setWaterArea(Bool val) {m_isWaterArea = val;} - Bool isRiver() const {return m_isRiver;} - void setRiver(Bool val) {m_isRiver = val;} - Int getRiverStart() const {return m_riverStart;} - void setRiverStart(Int val) {m_riverStart = val;} - const WaterHandle* getWaterHandle() const; - Bool isValid() const; -}; diff --git a/Generals/Code/GameEngine/Include/GameLogic/SidesList.h b/Generals/Code/GameEngine/Include/GameLogic/SidesList.h deleted file mode 100644 index 5ba8d205448..00000000000 --- a/Generals/Code/GameEngine/Include/GameLogic/SidesList.h +++ /dev/null @@ -1,389 +0,0 @@ -/* -** 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. // -// // -//////////////////////////////////////////////////////////////////////////////// - - -// SidesList.h -// Class to encapsulate Sides and Build Lists for maps. -// Author: John Ahlquist, November 2001 - -#pragma once - -#include "Common/Dict.h" -#include "Common/Errors.h" -#include "Common/GameType.h" -#include "Common/Snapshot.h" -#include "Common/GameMemory.h" -#include "Common/STLTypedefs.h" - -class DataChunkInput; -struct DataChunkInfo; -class DataChunkOutput; -class BuildListInfo; -class RenderObjClass; -class ScriptList; -class Shadow; - -// ---------------------------------------------------------------------------------------------- -/** - This is a class that describes a Side, including build list. - Note that a side corresponds to a Player in the game. The lightweight - Side is used to give the WB Editor somewhere to hang build lists. -*/ -class SidesInfo -{ -protected: - BuildListInfo* m_pBuildList; ///< linked list. - Dict m_dict; ///< general player dict. - ScriptList *m_scripts; ///< linked list. - -public: - SidesInfo(); - SidesInfo(const SidesInfo& thatref); - ~SidesInfo(); - void init(const Dict* d); - void clear() { init(nullptr); } - Dict* getDict() { return &m_dict; } - void addToBuildList(BuildListInfo *pBuildList, Int position); - Int removeFromBuildList(BuildListInfo *pBuildList); - void reorderInBuildList(BuildListInfo *pBuildList, Int newPosition); - BuildListInfo* getBuildList() {return m_pBuildList;} ///< Gets the build list. - void releaseBuildList() {m_pBuildList=nullptr;} ///< Used when the build list is passed to class Player. - ScriptList *getScriptList() {return(m_scripts);}; - void setScriptList(ScriptList *pScriptList) {m_scripts = pScriptList;}; - - // ug, I hate having to overload stuff, but this makes it a lot easier to make copies safely - SidesInfo& operator=(const SidesInfo& that); -}; - -// ---------------------------------------------------------------------------------------------- -class TeamsInfo -{ -private: - Dict m_dict; -public: - Dict* getDict() { return &m_dict; } - void init(const Dict* d) { m_dict.clear(); if (d) m_dict = *d; } - void clear() { init(nullptr); } -}; - - -// ---------------------------------------------------------------------------------------------- -// a wrapper class to make this a little cleaner. -class TeamsInfoRec -{ -private: - Int m_numTeams; - Int m_numTeamsAllocated; - TeamsInfo* m_teams; - -public: - TeamsInfoRec(); - TeamsInfoRec(const TeamsInfoRec& thatref); - ~TeamsInfoRec(); - TeamsInfoRec& operator=(const TeamsInfoRec& thatref); - void clear(); - TeamsInfo *findTeamInfo(AsciiString name, Int* index); - void addTeam(const Dict* d); - void removeTeam(Int i); - Int getNumTeams() const { return m_numTeams; } - TeamsInfo * getTeamInfo(Int team) - { - if (team>=0&&team=0&&side=0&&side=0 && ndx < MAX_RESOURCE_GATHERERS) return m_resourceGatherers[ndx]; return INVALID_ID;} - void setGathererID(Int ndx, ObjectID id) {if (ndx>=0 && ndx < MAX_RESOURCE_GATHERERS) m_resourceGatherers[ndx] = id;} - Int getDesiredGatherers() {return m_desiredGatherers;}; - void setDesiredGatherers(Int desired) {m_desiredGatherers = desired;} - Int getCurrentGatherers() {return m_currentGatherers;}; - void setCurrentGatherers(Int cur) {m_currentGatherers = cur;} - - BuildListInfo *duplicate(); -}; - -inline void BuildListInfo::decrementNumRebuilds() -{ - if (m_numRebuilds > 0 && m_numRebuilds != UNLIMITED_REBUILDS) - m_numRebuilds--; -} - -inline void BuildListInfo::incrementNumRebuilds() -{ - if (m_numRebuilds != UNLIMITED_REBUILDS) - m_numRebuilds++; -} - -inline Bool BuildListInfo::isBuildable() -{ - if (getNumRebuilds() > 0 || getNumRebuilds() == BuildListInfo::UNLIMITED_REBUILDS) - return true; - - return false; -} diff --git a/Generals/Code/GameEngine/Include/GameLogic/TerrainLogic.h b/Generals/Code/GameEngine/Include/GameLogic/TerrainLogic.h deleted file mode 100644 index c362afecf1f..00000000000 --- a/Generals/Code/GameEngine/Include/GameLogic/TerrainLogic.h +++ /dev/null @@ -1,381 +0,0 @@ -/* -** 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 - -#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 CPP_11(: Int) -{ - 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() const {return m_pNext; } - /// Enumerate the directed links from a waypoint using this,a nd getLink. - Int getNumLinks() const {return m_numLinks; } - /// Get the n'th directed link. (May be nullptr). - Waypoint *getLink(Int ndx) const {if (ndx>=0 && ndx <= MAX_LINKS) return m_links[ndx]; return nullptr; } - /// Get the waypoint's name. - AsciiString getName() const {return m_name; } - /// Get the integer id. - WaypointID getID() const {return m_id; } - /// Get the waypoint's position - const Coord3D *getLocation() const { return &m_location; } - /// Get the waypoint's first path label - const AsciiString& getPathLabel1() const { return m_pathLabel1; } - /// Get the waypoint's second path label - const AsciiString& getPathLabel2() const { return m_pathLabel2; } - /// Get the waypoint's third path label - const AsciiString& getPathLabel3() const { return m_pathLabel3; } - /// Get bi-directionality. - Bool getBiDirectional() 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() { return m_templateName; } - /// Enumerate all bridges using getNext; - Bridge *getNext() {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(); ///< Updates a bridge's damage info. - const BridgeInfo *peekBridgeInfo() const {return &m_bridgeInfo;} - PathfindLayerEnum getLayer() const {return m_layer;} - void setLayer(PathfindLayerEnum layer) {m_layer = layer;} - const Region2D *getBounds() 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 - - void setBridgeObjectID( ObjectID id ) { m_bridgeInfo.bridgeObjectID = id; } - 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() override; - - virtual void init() override; ///< Init - virtual void reset() override; ///< Reset - virtual void update() override; ///< 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 = nullptr ) const; - virtual Real getLayerHeight(Real x, Real y, PathfindLayerEnum layer, Coord3D* normal = nullptr, 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() { return m_filenameString; } - - virtual PathfindLayerEnum alignOnTerrain( Real angle, const Coord3D& pos, Bool stickToGround, Matrix3D& mtx); - - virtual Bool isUnderwater( Real x, Real y, Real *waterZ = nullptr, Real *terrainZ = nullptr ); ///< 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() { 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 containing 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() 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(); ///< Updates bridge's damage info. - - Bool anyBridgesDamageStatesChanged() {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() { return m_activeBoundary; } - void setActiveBoundary(Int newActiveBoundary); - - void flattenTerrain(Object *obj); ///< Flatten the terrain under a building. -#if !(RTS_GENERALS && RETAIL_COMPATIBLE_CRC) - void createCraterInTerrain(Object *obj); ///< Flatten the terrain under a building. -#endif - -protected: - - // snapshot methods - virtual void crc( Xfer *xfer ) override; - virtual void xfer( Xfer *xfer ) override; - virtual void loadPostProcess() override; - - /// 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(); - /// Deletes all bridges. - void deleteBridges(); - - /// 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 changeable 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 - -}; - -// 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 ); diff --git a/Generals/Code/GameEngine/Source/GameLogic/Map/PolygonTrigger.cpp b/Generals/Code/GameEngine/Source/GameLogic/Map/PolygonTrigger.cpp deleted file mode 100644 index 869825775b3..00000000000 --- a/Generals/Code/GameEngine/Source/GameLogic/Map/PolygonTrigger.cpp +++ /dev/null @@ -1,576 +0,0 @@ -/* -** 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. // -// // -//////////////////////////////////////////////////////////////////////////////// - -// PolygonTrigger.cpp -// Class to encapsulate polygon trigger areas. -// Author: John Ahlquist, November 2001 - -#include "PreRTS.h" // This must go first in EVERY cpp file in the GameEngine - -#include "Common/DataChunk.h" -#include "Common/MapObject.h" -#include "Common/MapReaderWriterInfo.h" -#include "Common/Xfer.h" -#include "GameLogic/PolygonTrigger.h" -#include "GameLogic/TerrainLogic.h" - -/* ********* PolygonTrigger class ****************************/ -PolygonTrigger *PolygonTrigger::ThePolygonTriggerListPtr = nullptr; -Int PolygonTrigger::s_currentID = 1; -/** - PolygonTrigger - Constructor. -*/ -PolygonTrigger::PolygonTrigger(Int initialAllocation) : -m_nextPolygonTrigger(nullptr), -m_points(nullptr), -m_numPoints(0), -m_sizePoints(0), -m_exportWithScripts(false), -m_isWaterArea(false), -#if !(RTS_GENERALS && RETAIL_COMPATIBLE_CRC) -m_shouldRender(true), -m_selected(false), -#endif -m_isRiver(FALSE), -m_riverStart(0) -{ - if (initialAllocation < 2) initialAllocation = 2; - m_points = NEW ICoord3D[initialAllocation]; // pool[]ify - m_sizePoints = initialAllocation; - m_triggerID = s_currentID++; - - m_waterHandle.m_polygon = this; - -} - - -/** - PolygonTrigger - Destructor - note - if linked, deletes linked items. -*/ -PolygonTrigger::~PolygonTrigger() -{ - delete [] m_points; - m_points = nullptr; - - if (m_nextPolygonTrigger) { - PolygonTrigger *cur = m_nextPolygonTrigger; - PolygonTrigger *next; - while (cur) { - next = cur->getNext(); - cur->setNextPoly(nullptr); // prevents recursion. - deleteInstance(cur); - cur = next; - } - } -} - - -/** - PolygonTrigger::reallocate - increases the size of the points list. - NOTE: It is expected that this will only get called in the editor, as in the game - the poly triggers don't change. -*/ -void PolygonTrigger::reallocate() -{ - DEBUG_ASSERTCRASH(m_numPoints <= m_sizePoints, ("Invalid m_numPoints.")); - if (m_numPoints == m_sizePoints) { - if (m_sizePoints > INT_MAX / 2) { - DEBUG_CRASH(("Too many points to allocate.")); - return; - } - // Reallocate. - m_sizePoints += m_sizePoints; - ICoord3D *newPts = NEW ICoord3D[m_sizePoints]; - Int i; - for (i=0; igetNext() ) - if( poly->getID() == triggerID ) - return poly; - - // not found - return nullptr; - -} - -/** -* PolygonTrigger::ParsePolygonTriggersDataChunk - read a polygon triggers chunk. -* Format is the newer CHUNKY format. -* See PolygonTrigger::WritePolygonTriggersDataChunk for the writer. -* Input: DataChunkInput -* -*/ -Bool PolygonTrigger::ParsePolygonTriggersDataChunk(DataChunkInput &file, DataChunkInfo *info, void *userData) -{ - Int count; - Int numPoints; - Int triggerID; - Int maxTriggerId = 0; - Bool isWater; - Bool isRiver; - Int riverStart; - AsciiString triggerName; -#if !(RTS_GENERALS && RETAIL_COMPATIBLE_CRC) - AsciiString layerName; -#endif - // Remove any existing polygon triggers, if any. - PolygonTrigger::deleteTriggers(); // just in case. - PolygonTrigger *pPrevTrig = nullptr; - ICoord3D loc; - count = file.readInt(); - while (count>0) { - count--; - triggerName = file.readAsciiString(); -#if !(RTS_GENERALS && RETAIL_COMPATIBLE_CRC) - if (info->version >= K_TRIGGERS_VERSION_4) { - layerName = file.readAsciiString(); - } -#endif - triggerID = file.readInt(); - isWater = false; - if (info->version >= K_TRIGGERS_VERSION_2) { - isWater = file.readByte(); - } - isRiver = false; - riverStart = 0; - if (info->version >= K_TRIGGERS_VERSION_3) { - isRiver = file.readByte(); - riverStart = file.readInt(); - } - - numPoints = file.readInt(); - PolygonTrigger *pTrig = newInstance(PolygonTrigger)(numPoints+1); - pTrig->setTriggerName(triggerName); -#if !(RTS_GENERALS && RETAIL_COMPATIBLE_CRC) - if (info->version >= K_TRIGGERS_VERSION_4) { - pTrig->setLayerName(layerName); - } -#endif - pTrig->setWaterArea(isWater); - pTrig->setRiver(isRiver); - pTrig->setRiverStart(riverStart); - pTrig->m_triggerID = triggerID; - if (triggerID > maxTriggerId) { - maxTriggerId = triggerID; - } - Int i; - for (i=0; iaddPoint(loc); - } -#if !(RTS_GENERALS && RETAIL_COMPATIBLE_CRC) - if (numPoints<2) { - DEBUG_LOG(("Deleting polygon trigger '%s' with %d points.", - pTrig->getTriggerName().str(), numPoints)); - deleteInstance(pTrig); - continue; - } -#endif - if (pPrevTrig) { - pPrevTrig->setNextPoly(pTrig); - } else { - PolygonTrigger::addPolygonTrigger(pTrig); - } - pPrevTrig = pTrig; - } - if (info->version == K_TRIGGERS_VERSION_1) - { - // before water areas existed, so create a default one. - PolygonTrigger *pTrig = newInstance(PolygonTrigger)(4); - pTrig->setWaterArea(true); -#ifdef RTS_DEBUG - pTrig->setTriggerName("AutoAddedWaterAreaTrigger"); -#endif - pTrig->m_triggerID = maxTriggerId++; - loc.x = -30*MAP_XY_FACTOR; - loc.y = -30*MAP_XY_FACTOR; - loc.z = 7; // The old water position. - pTrig->addPoint(loc); - loc.x = 30*MAP_XY_FACTOR + TheGlobalData->m_waterExtentX; - pTrig->addPoint(loc); - loc.y = 30*MAP_XY_FACTOR + TheGlobalData->m_waterExtentY; - pTrig->addPoint(loc); - loc.x = -30*MAP_XY_FACTOR; - pTrig->addPoint(loc); - if (pPrevTrig) { - pPrevTrig->setNextPoly(pTrig); - } else { - PolygonTrigger::addPolygonTrigger(pTrig); - } - pPrevTrig = pTrig; - } - s_currentID = maxTriggerId+1; - DEBUG_ASSERTCRASH(file.atEndOfChunk(), ("Incorrect data file length.")); - return true; -} - -/** -* PolygonTrigger::WritePolygonTriggersDataChunk - Writes a Polygon triggers chunk. -* Format is the newer CHUNKY format. -* See PolygonTrigger::ParsePolygonTriggersDataChunk for the reader. -* Input: DataChunkInput -* -*/ -void PolygonTrigger::WritePolygonTriggersDataChunk(DataChunkOutput &chunkWriter) -{ -#if RTS_GENERALS && RETAIL_COMPATIBLE_CRC - chunkWriter.openDataChunk("PolygonTriggers", K_TRIGGERS_VERSION_3); -#else - chunkWriter.openDataChunk("PolygonTriggers", K_TRIGGERS_VERSION_4); -#endif - - PolygonTrigger *pTrig; - Int count = 0; - for (pTrig=PolygonTrigger::getFirstPolygonTrigger(); pTrig; pTrig = pTrig->getNext()) { - count++; - } - chunkWriter.writeInt(count); - for (pTrig=PolygonTrigger::getFirstPolygonTrigger(); pTrig; pTrig = pTrig->getNext()) { - chunkWriter.writeAsciiString(pTrig->getTriggerName()); -#if !(RTS_GENERALS && RETAIL_COMPATIBLE_CRC) - chunkWriter.writeAsciiString(pTrig->getLayerName()); -#endif - chunkWriter.writeInt(pTrig->getID()); - chunkWriter.writeByte(pTrig->isWaterArea()); - chunkWriter.writeByte(pTrig->isRiver()); - chunkWriter.writeInt(pTrig->getRiverStart()); - chunkWriter.writeInt(pTrig->getNumPoints()); - Int i; - for (i=0; igetNumPoints(); i++) { - ICoord3D loc = *pTrig->getPoint(i); - chunkWriter.writeInt( loc.x); - chunkWriter.writeInt( loc.y); - chunkWriter.writeInt( loc.z); - } - } - - chunkWriter.closeDataChunk(); -} - -/** - PolygonTrigger::updateBounds - Updates the bounds. -*/ -void PolygonTrigger::updateBounds() const -{ - const Int BIG_INT=0x7ffff0; - m_bounds.lo.x = m_bounds.lo.y = BIG_INT; - m_bounds.hi.x = m_bounds.hi.y = -BIG_INT; - Int i; - for (i=0; i m_bounds.hi.x) m_bounds.hi.x = m_points[i].x; - if (m_points[i].y > m_bounds.hi.y) m_bounds.hi.y = m_points[i].y; - } - m_boundsNeedsUpdate = 0; - Real halfWidth = (m_bounds.hi.x - m_bounds.lo.x) / 2.0f; - Real halfHeight = (m_bounds.hi.y + m_bounds.lo.y) / 2.0f; - - m_radius = sqrt(halfHeight*halfHeight + halfWidth*halfWidth); -} - - -/** - PolygonTrigger::addPolygonTrigger adds a trigger to the list of triggers. -*/ -void PolygonTrigger::addPolygonTrigger(PolygonTrigger *pTrigger) -{ - for (PolygonTrigger *pTrig=getFirstPolygonTrigger(); pTrig; pTrig = pTrig->getNext()) { - DEBUG_ASSERTCRASH(pTrig != pTrigger, ("Attempting to add trigger already in list.")); - if (pTrig==pTrigger) return; - } - pTrigger->m_nextPolygonTrigger = ThePolygonTriggerListPtr; - ThePolygonTriggerListPtr = pTrigger; -} - -/** - PolygonTrigger::removePolygonTrigger removes a trigger to the list of - triggers. note - does NOT delete pTrigger. -*/ -void PolygonTrigger::removePolygonTrigger(PolygonTrigger *pTrigger) -{ - PolygonTrigger *pPrev = nullptr; - PolygonTrigger *pTrig=getFirstPolygonTrigger(); - for (; pTrig; pTrig = pTrig->getNext()) { - if (pTrig==pTrigger) break; - pPrev = pTrig; - } - DEBUG_ASSERTCRASH(pTrig, ("Attempting to remove a polygon not in the list.")); - if (pTrig) { - if (pPrev) { - DEBUG_ASSERTCRASH(pTrigger==pPrev->m_nextPolygonTrigger, ("Logic error. jba.")); - pPrev->m_nextPolygonTrigger = pTrig->m_nextPolygonTrigger; - } else { - DEBUG_ASSERTCRASH(pTrigger==ThePolygonTriggerListPtr, ("Logic error. jba.")); - ThePolygonTriggerListPtr = pTrig->m_nextPolygonTrigger; - } - } - pTrigger->m_nextPolygonTrigger = nullptr; -} - -/** - PolygonTrigger::deleteTriggers Deletes list of triggers. -*/ -void PolygonTrigger::deleteTriggers() -{ - PolygonTrigger *pList = ThePolygonTriggerListPtr; - ThePolygonTriggerListPtr = nullptr; - s_currentID = 1; - deleteInstance(pList); -} - -/** - PolygonTrigger::addPoint adds a point at the end of the polygon. - NOTE: It is expected that this will only get called in the editor, as in the game - the poly triggers don't change. -*/ -void PolygonTrigger::addPoint(const ICoord3D &point) -{ - DEBUG_ASSERTCRASH(m_numPoints <= m_sizePoints, ("Invalid m_numPoints.")); - if (m_numPoints == m_sizePoints) { - reallocate(); - } - m_points[m_numPoints] = point; - m_numPoints++; - m_boundsNeedsUpdate = true; -} - -/** - PolygonTrigger::setPoint sets the point at index ndx. - NOTE: It is expected that this will only get called in the editor, as in the game - the poly triggers don't change. -*/ -void PolygonTrigger::setPoint(const ICoord3D &point, Int ndx) -{ - DEBUG_ASSERTCRASH(ndx>=0 && ndx <= m_numPoints, ("Invalid ndx.")); - if (ndx<0) return; - if (ndx == m_numPoints) { // we are setting first available unused point - addPoint(point); - return; - } - if (ndx>m_numPoints) { // Can't skip points. - return; - } - m_points[ndx] = point; - m_boundsNeedsUpdate = true; -} - -/** - PolygonTrigger::insertPoint . - NOTE: It is expected that this will only get called in the editor, as in the game - the poly triggers don't change. -*/ -void PolygonTrigger::insertPoint(const ICoord3D &point, Int ndx) -{ - DEBUG_ASSERTCRASH(ndx>=0 && ndx <= m_numPoints, ("Invalid ndx.")); - if (ndx<0) return; - if (ndx == m_numPoints) { // we are setting first available unused point - addPoint(point); - return; - } - if (m_numPoints == m_sizePoints) { - reallocate(); - } - Int i; - for (i=m_numPoints; i>ndx; i--) { - m_points[i] = m_points[i-1]; - } - m_points[ndx] = point; - m_numPoints++; - m_boundsNeedsUpdate = true; -} - -/** - PolygonTrigger::deletePoint . - NOTE: It is expected that this will only get called in the editor, as in the game - the poly triggers don't change. -*/ -void PolygonTrigger::deletePoint(Int ndx) -{ - DEBUG_ASSERTCRASH(ndx>=0 && ndx < m_numPoints, ("Invalid ndx.")); - if (ndx<0 || ndx>=m_numPoints) return; - Int i; - for (i=ndx; igetGroundHeight(pOutCoord->x, pOutCoord->y); -} - -Real PolygonTrigger::getRadius() const -{ - if (m_boundsNeedsUpdate) { - updateBounds(); - } - return m_radius; -} - - -/** - PolygonTrigger - pointInTrigger. -*/ -Bool PolygonTrigger::pointInTrigger(ICoord3D &point) const -{ - if (m_boundsNeedsUpdate) { - updateBounds(); - } - if (point.x < m_bounds.lo.x) return false; - if (point.y < m_bounds.lo.y) return false; - if (point.x > m_bounds.hi.x) return false; - if (point.y > m_bounds.hi.y) return false; - - Bool inside = false; - Int i; - for (i=0; i= point.y && pt2.y >= point.y) continue; - if (pt1.xinfinity. - Int dy = pt2.y-pt1.y; - Int dx = pt2.x-pt1.x; - - Real intersectionX = pt1.x + (dx * (point.y-pt1.y)) / ((Real)dy); - if (intersectionX >= point.x) { - inside = !inside; - } - } - return inside; -} - -// ------------------------------------------------------------------------------------------------ -const WaterHandle* PolygonTrigger::getWaterHandle() const -{ - - if( isWaterArea() ) - return &m_waterHandle; - - return nullptr; // this polygon trigger is not a water area - -} - -Bool PolygonTrigger::isValid() const -{ - if (m_numPoints == 0) { - return FALSE; - } - - return TRUE; -} - -// ------------------------------------------------------------------------------------------------ -/** CRC */ -// ------------------------------------------------------------------------------------------------ -void PolygonTrigger::crc( Xfer *xfer ) -{ - -} - -// ------------------------------------------------------------------------------------------------ -/** Xfer method - * Version Info: - * 1: Initial version */ -// ------------------------------------------------------------------------------------------------ -void PolygonTrigger::xfer( Xfer *xfer ) -{ - - // version - XferVersion currentVersion = 1; - XferVersion version = currentVersion; - xfer->xferVersion( &version, currentVersion ); - - // number of data points - xfer->xferInt( &m_numPoints ); - - // xfer all data points - ICoord3D *point; - for( Int i = 0; i < m_numPoints; ++i ) - { - - // get this point - point = &m_points[ i ]; - - // xfer point - xfer->xferICoord3D( point ); - - } - - // bounds - xfer->xferIRegion2D( &m_bounds ); - - // radius - xfer->xferReal( &m_radius ); - - // bounds need update - xfer->xferBool( &m_boundsNeedsUpdate ); - -} - -// ------------------------------------------------------------------------------------------------ -/** Load post process */ -// ------------------------------------------------------------------------------------------------ -void PolygonTrigger::loadPostProcess() -{ - -} diff --git a/Generals/Code/GameEngine/Source/GameLogic/Map/SidesList.cpp b/Generals/Code/GameEngine/Source/GameLogic/Map/SidesList.cpp deleted file mode 100644 index 88679b2bc53..00000000000 --- a/Generals/Code/GameEngine/Source/GameLogic/Map/SidesList.cpp +++ /dev/null @@ -1,1176 +0,0 @@ -/* -** 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: SidesList.cpp ///////////////////////////////////////////////////////// -//----------------------------------------------------------------------------- -// -// Westwood Studios Pacific. -// -// Confidential Information -// Copyright (C) 2001 - All Rights Reserved -// -//----------------------------------------------------------------------------- -// -// Project: RTS3 -// -// File name: SidesList.cpp -// -// Created: John Ahlquist, Nov 2001 -// -// Desc: Contains the information describing Sides (player, ai, neutral etc.) -// in a scenario, including build lists for non-player sides. -// -//----------------------------------------------------------------------------- - -#include "PreRTS.h" // This must go first in EVERY cpp file in the GameEngine - -#include "Common/DataChunk.h" -#include "Common/GameState.h" -#include "Common/PlayerTemplate.h" -#include "Common/WellKnownKeys.h" -#include "Common/Xfer.h" -#include "GameLogic/AI.h" -#include "GameLogic/Scripts.h" -#include "GameLogic/SidesList.h" - -static const Int K_SIDES_DATA_VERSION_1 = 1; -static const Int K_SIDES_DATA_VERSION_2 = 2; // includes Team list. -static const Int K_SIDES_DATA_VERSION_3 = 3; // includes Team list. - -/* ********* SidesInfo class ****************************/ -/** - SidesInfo - Constructor. -*/ -SidesInfo::SidesInfo() : - m_pBuildList(nullptr), - m_scripts(nullptr) -{ -} - -SidesInfo::SidesInfo(const SidesInfo& thatref) : - m_pBuildList(nullptr), - m_scripts(nullptr) -{ - *this = thatref; -} - -/** - SidesInfo - Destructor - -*/ -SidesInfo::~SidesInfo() -{ - clear(); -} - -void SidesInfo::init(const Dict* d) -{ - deleteInstance(m_pBuildList); - m_pBuildList = nullptr; - - m_dict.clear(); - - deleteInstance(m_scripts); - m_scripts = nullptr; - - if (d) - m_dict = *d; -} - -// ug, I hate having to overload stuff, but this makes it a lot easier to make copies safely -SidesInfo& SidesInfo::operator=(const SidesInfo& that) -{ - if (this != &that) - { - this->clear(); - this->m_dict = that.m_dict; - - BuildListInfo* thisBLTail = nullptr; - for (BuildListInfo* thatBL = that.m_pBuildList; thatBL; thatBL = thatBL->getNext()) - { - BuildListInfo* thisBL = newInstance( BuildListInfo ); - *thisBL = *thatBL; - thisBL->setNextBuildList(nullptr); - - if (thisBLTail) - thisBLTail->setNextBuildList(thisBL); - else - this->m_pBuildList = thisBL; - - thisBLTail = thisBL; - } - - if (that.m_scripts) - this->m_scripts = that.m_scripts->duplicate(); - else - this->m_scripts = nullptr; - } - return *this; -} - -/** -* SidesInfo::addToBuildList - Adds a build list entry as the nth entry. -* -*/ -void SidesInfo::addToBuildList(BuildListInfo *pBuildList, Int position) -{ - DEBUG_ASSERTLOG(pBuildList->getNext()==nullptr, ("WARNING***Adding already linked element.")); - BuildListInfo *pCur = nullptr; - while (position) { - position--; - if (pCur==nullptr) { - pCur = m_pBuildList; - } else { - if (pCur->getNext()) { - pCur = pCur->getNext(); - } else { - break; // at end of list. - } - } - } - if (pCur==nullptr) { - // add to front of list. - pBuildList->setNextBuildList(m_pBuildList); - m_pBuildList = pBuildList; - } else { - pBuildList->setNextBuildList(pCur->getNext()); - pCur->setNextBuildList(pBuildList); - } -} - -/** -* SidesInfo::reorderInBuildList - Reorders a build list entry as the nth entry. -* -*/ -void SidesInfo::reorderInBuildList(BuildListInfo *pBuildList, Int newPosition) -{ - /*Int oldPos =*/ removeFromBuildList(pBuildList); - addToBuildList(pBuildList, newPosition); -} - -/** -* SidesInfo::removeFromBuildList - Removes a build list entry. -* Returns the position in the list that the item occupied. -* -*/ -Int SidesInfo::removeFromBuildList(BuildListInfo *pBuildList) -{ - DEBUG_ASSERTCRASH(pBuildList, ("Removing null list.")); - if (pBuildList==nullptr) return 0; - - Int position = 0; - - if (pBuildList == m_pBuildList) { - // First item in list, so update head. - m_pBuildList = pBuildList->getNext(); - } else { - position = 1; - // Not the first item, so find the preceding list element. - BuildListInfo *pPrev = m_pBuildList; - while (pPrev && (pPrev->getNext()!=pBuildList) ) { - pPrev = pPrev->getNext(); - position++; - } - DEBUG_ASSERTCRASH(pPrev, ("Removing item not in list.")); - if (pPrev) { - pPrev->setNextBuildList(pBuildList->getNext()); - } - } - pBuildList->setNextBuildList(nullptr); - return position; -} - -/* ********* SidesList class ****************************/ -/*extern*/ SidesList *TheSidesList = nullptr; ///< singleton instance of SidesList -/** - SidesList - Constructor. -*/ -SidesList::SidesList() : m_numSides(0), m_numSkirmishSides(0) -{ -} - -/** - SidesList - Destructor - -*/ -SidesList::~SidesList() -{ -} - -/** - SidesList - reset - -*/ -void SidesList::reset() -{ - clear(); -} - -/** - SidesList - clear - -*/ -void SidesList::clear() -{ - emptySides(); - emptyTeams(); -} - - - -/** -* SidesList::ParseSidesDataChunk - read a Sides chunk. -* Format is the newer CHUNKY format. -* See SidesList::WriteSidesDataChunk for the writer. -* Input: DataChunkInput -* -*/ -Bool SidesList::ParseSidesDataChunk(DataChunkInput &file, DataChunkInfo *info, void *userData) -{ - DEBUG_ASSERTCRASH(TheSidesList, ("TheSidesList is null")); - - if (TheSidesList==nullptr) - return false; - - TheSidesList->clear(); - Int count = file.readInt(); - Int i, j; - TheSidesList->emptySides(); - for (i=0; i= MAX_PLAYER_COUNT) break; - Dict d = file.readDict(); - TheSidesList->addSide(&d); - BuildListInfo* pBuildList; - Int count = file.readInt(); - for (j=0; jsetBuildingName(file.readAsciiString()); - pBuildList->setTemplateName(file.readAsciiString()); - Coord3D loc; - loc.x = file.readReal(); - loc.y = file.readReal(); - loc.z = file.readReal(); - loc.z = 0; // force to ground level - pBuildList->setLocation(loc); - pBuildList->setAngle(file.readReal()); - pBuildList->setInitiallyBuilt(file.readByte()); - pBuildList->setNumRebuilds(file.readInt()); - if (info->version >= K_SIDES_DATA_VERSION_3) - { - pBuildList->setScript(file.readAsciiString()); - pBuildList->setHealth(file.readInt()); - pBuildList->setWhiner(file.readByte()); - pBuildList->setUnsellable(file.readByte()); - pBuildList->setRepairable(file.readByte()); - } - TheSidesList->getSideInfo(i)->addToBuildList(pBuildList, j); - } - } - if (info->version >= K_SIDES_DATA_VERSION_2) - { - count = file.readInt(); - TheSidesList->emptyTeams(); - for (i=0; iaddTeam(&d); - } - } - - file.registerParser( "PlayerScriptsList", info->label, ScriptList::ParseScriptsDataChunk ); - if (!file.parse(nullptr)) { - throw(ERROR_CORRUPT_FILE_FORMAT); - } - ScriptList *scripts[MAX_PLAYER_COUNT]; - count = ScriptList::getReadScripts(scripts); - for (i=0; igetNumSides()) { - deleteInstance(TheSidesList->getSideInfo(i)->getScriptList()); - TheSidesList->getSideInfo(i)->setScriptList(scripts[i]); - scripts[i] = nullptr; - } else { - // Read in more players worth than we have. - deleteInstance(scripts[i]); - scripts[i] = nullptr; - } - } - TheSidesList->validateSides(); - - DEBUG_ASSERTCRASH(file.atEndOfChunk(), ("Incorrect data file length.")); - return true; -} - - -/** -* SidesList::WriteSidesDataChunk - Writes a Sides chunk. -* Format is the newer CHUNKY format. -* See SidesList::ParseSidesDataChunk for the reader. -* Input: DataChunkInput -* -*/ -void SidesList::WriteSidesDataChunk(DataChunkOutput &chunkWriter) -{ - DEBUG_ASSERTCRASH(TheSidesList, ("TheSidesList is null")); - if (TheSidesList==nullptr) - return; - /**********HEIGHT MAP DATA ***********************/ - chunkWriter.openDataChunk("SidesList", K_SIDES_DATA_VERSION_3); - - chunkWriter.writeInt(TheSidesList->getNumSides()); - Int i; - for (i=0; igetNumSides(); i++) { - chunkWriter.writeDict(*TheSidesList->getSideInfo(i)->getDict()); - BuildListInfo* pBuildList = TheSidesList->getSideInfo(i)->getBuildList(); - Int count = 0; - while (pBuildList) { - count++; - pBuildList = pBuildList->getNext(); - } - chunkWriter.writeInt(count); - pBuildList = TheSidesList->getSideInfo(i)->getBuildList(); - while (pBuildList) { - chunkWriter.writeAsciiString(pBuildList->getBuildingName()); - chunkWriter.writeAsciiString(pBuildList->getTemplateName()); - chunkWriter.writeReal(pBuildList->getLocation()->x); - chunkWriter.writeReal(pBuildList->getLocation()->y); - chunkWriter.writeReal(pBuildList->getLocation()->z); - chunkWriter.writeReal(pBuildList->getAngle()); - chunkWriter.writeByte(pBuildList->isInitiallyBuilt()); - chunkWriter.writeInt(pBuildList->getNumRebuilds()); - // BEGIN stuff new to K_SIDES_DATA_VERSION_3 - chunkWriter.writeAsciiString(pBuildList->getScript()); - chunkWriter.writeInt(pBuildList->getHealth()); - chunkWriter.writeByte(pBuildList->getWhiner()); - chunkWriter.writeByte(pBuildList->getUnsellable()); - chunkWriter.writeByte(pBuildList->getRepairable()); - // END stuff new to K_SIDES_DATA_VERSION_3 - - pBuildList = pBuildList->getNext(); - } - } - - // BEGIN stuff new to K_SIDES_DATA_VERSION_2 - chunkWriter.writeInt(TheSidesList->getNumTeams()); - for (i=0; igetNumTeams(); i++) { - chunkWriter.writeDict(*TheSidesList->getTeamInfo(i)->getDict()); - } - // END stuff new to K_SIDES_DATA_VERSION_2 - - ScriptList *scripts[MAX_PLAYER_COUNT]; - for (i=0; igetNumSides(); i++) { - scripts[i] = TheSidesList->getSideInfo(i)->getScriptList(); - } - ScriptList::WriteScriptsDataChunk(chunkWriter, scripts, TheSidesList->getNumSides()); - chunkWriter.closeDataChunk(); - - Bool modified = TheSidesList->validateSides(); - DEBUG_ASSERTLOG(!modified, ("*** had to clean up sideslist on read")); - modified = false; // silence compiler warnings in release build - -} - -TeamsInfo *SidesList::findTeamInfo(AsciiString name, Int* index /*= nullptr*/) -{ - return m_teamrec.findTeamInfo(name, index); -} - -SidesInfo *SidesList::findSideInfo(AsciiString name, Int* index /*= nullptr*/) -{ - for (int i = 0; i < m_numSides; i++) - { - if (m_sides[i].getDict()->getAsciiString(TheKey_playerName) == name) - { - if (index) - *index = i; - return &m_sides[i]; - } - } - return nullptr; -} - -SidesInfo *SidesList::findSkirmishSideInfo(AsciiString name, Int* index /*= nullptr*/) -{ - for (int i = 0; i < m_numSkirmishSides; i++) - { - if (m_skirmishSides[i].getDict()->getAsciiString(TheKey_playerName) == name) - { - if (index) - *index = i; - return &m_skirmishSides[i]; - } - } - return nullptr; -} - -static AsciiString static_readPlayerNames[MAX_PLAYER_COUNT]; - -/** -* ParsePlayersDataChunk - read players names data chunk. -* Format is the newer CHUNKY format. -* Input: DataChunkInput -* -*/ -#if !(RTS_GENERALS && RETAIL_COMPATIBLE_CRC) -#define K_PLAYERS_NAMES_FOR_SCRIPTS_VERSION_1 1 -#define K_PLAYERS_NAMES_FOR_SCRIPTS_VERSION_2 2 -#endif - -static Bool ParsePlayersDataChunk(DataChunkInput &file, DataChunkInfo *info, void *userData) -{ -#if !(RTS_GENERALS && RETAIL_COMPATIBLE_CRC) - Int readDicts = 0; - if (info->version >= K_PLAYERS_NAMES_FOR_SCRIPTS_VERSION_2) { - readDicts = file.readInt(); - } -#endif - Int numNames = file.readInt(); - Int i; - for (i=0; i=MAX_PLAYER_COUNT) break; - static_readPlayerNames[i] = file.readAsciiString(); -#if !(RTS_GENERALS && RETAIL_COMPATIBLE_CRC) - if (readDicts) { - Dict sideDict = file.readDict(); - } -#endif - } - DEBUG_ASSERTCRASH(file.atEndOfChunk(), ("Unexpected data left over.")); - return true; -} - -/** -* ParseTeamsDataChunk - read teams data chunk. -* Format is the newer CHUNKY format. -* Input: DataChunkInput -* -*/ -static Bool ParseTeamsDataChunk(DataChunkInput &file, DataChunkInfo *info, void *userData) -{ - SidesList *sides = (SidesList *)userData; - while (!file.atEndOfChunk()) { - Dict teamDict = file.readDict(); - AsciiString teamName = teamDict.getAsciiString(TheKey_teamName); - AsciiString player = teamDict.getAsciiString(TheKey_teamOwner); - if (sides->findSkirmishSideInfo(player)) { - // player exists, so just add it. - sides->addSkirmishTeam(&teamDict); - //DEBUG_LOG(("Adding team %s", teamName.str())); - } else { - //DEBUG_LOG(("Couldn't add team %s, no player %s", teamName.str(), player.str())); - } - } - DEBUG_ASSERTCRASH(file.atEndOfChunk(), ("Unexpected data left over.")); - return true; -} - -void SidesList::prepareForMP_or_Skirmish() -{ - m_skirmishTeamrec.clear(); - Int i; - for (i = 0; i < getNumTeams(); i++) - { - Dict *tdict = getTeamInfo(i)->getDict(); - m_skirmishTeamrec.addTeam(tdict); - } - m_teamrec.clear(); - - for (i = 0; i < MAX_PLAYER_COUNT; i++) { - m_skirmishSides[i].clear(); - } - m_numSkirmishSides = 0; - - for (i = 0; i < m_numSides; i++) - { - m_skirmishSides[m_numSkirmishSides] = m_sides[i]; - m_numSkirmishSides++; - if (m_sides[i].getDict()->getAsciiString(TheKey_playerFaction) == "FactionCivilian") { - // Don't remove FactionCivilian. - continue; - } - if (m_numSides == 1) break; // can't remove the last side. - removeSide(i); - i--; - } - Bool gotScripts = false; - for (i=0; igetAsciiString(TheKey_playerFaction) == "FactionCivilian") { - // Don't consider FactionCivilian. - continue; - } - if (m_skirmishSides[i].getScriptList()==nullptr) continue; - if (m_skirmishSides[i].getScriptList()->getScript() != nullptr || - m_skirmishSides[i].getScriptList()->getScriptGroup()!=nullptr) { - gotScripts = true; - } - } - if (!gotScripts) { - AsciiString path = "data\\Scripts\\SkirmishScripts.scb"; - DEBUG_LOG(("Skirmish map using standard scripts")); - m_skirmishTeamrec.clear(); - CachedFileInputStream theInputStream; - if (theInputStream.open(path)) { - ChunkInputStream *pStrm = &theInputStream; - DataChunkInput file( pStrm ); - file.registerParser( "PlayerScriptsList", AsciiString::TheEmptyString, ScriptList::ParseScriptsDataChunk ); - file.registerParser( "ScriptsPlayers", AsciiString::TheEmptyString, ParsePlayersDataChunk ); - file.registerParser( "ScriptTeams", AsciiString::TheEmptyString, ParseTeamsDataChunk ); - if (!file.parse(this)) { - DEBUG_LOG(("ERROR - Unable to read in skirmish scripts.")); - return; - } - ScriptList *scripts[MAX_PLAYER_COUNT]; - Int count = ScriptList::getReadScripts(scripts); - Int i; - for (i=0; igetDict()->getAsciiString(TheKey_playerName); - if (name == static_readPlayerNames[i]) { - curSide = j; - break; - } - } - if (curSide == -1) - { - deleteInstance(scripts[i]); - scripts[i] = nullptr; - continue; - } - - deleteInstance(getSkirmishSideInfo(curSide)->getScriptList()); - getSkirmishSideInfo(curSide)->setScriptList(scripts[i]); - scripts[i] = nullptr; - } - for (i=0; igetDict()->getAsciiString(TheKey_teamName); - if (tname.startsWith("team")) - { - const char* rest = tname.str() + 4; - for (int j = 0; j < m_numSides; j++) - { - AsciiString pname = m_sides[j].getDict()->getAsciiString(TheKey_playerName); - if (strcmp(pname.str(), rest) == 0) - { - return true; - } - } - } - return false; -} - -void SidesList::emptySides() -{ - Int i; - - m_numSides = 0; - m_numSkirmishSides = 0; - for (i = 0; i < MAX_PLAYER_COUNT; i++) { - m_sides[i].clear(); - m_skirmishSides[i].clear(); - } -} - -void SidesList::emptyTeams() -{ - m_teamrec.clear(); - m_skirmishTeamrec.clear(); -} - -void SidesList::addSide(const Dict* d) -{ - DEBUG_ASSERTCRASH(m_numSides < MAX_PLAYER_COUNT, ("too many players")); - if (m_numSides < MAX_PLAYER_COUNT) - m_sides[m_numSides++].init(d); -} - -void SidesList::addTeam(const Dict* d) -{ - m_teamrec.addTeam(d); -} - -void SidesList::addSkirmishTeam(const Dict* d) -{ - m_skirmishTeamrec.addTeam(d); -} - -void SidesList::removeSide(Int i) -{ - if (i < 0 || i >= m_numSides || m_numSides <= 1) - return; - - for ( ; i < m_numSides-1; i++) - m_sides[i] = m_sides[i+1]; - - for ( ; i < MAX_PLAYER_COUNT; i++) - m_sides[i].clear(); - - --m_numSides; -} - -void SidesList::removeTeam(Int i) -{ - m_teamrec.removeTeam(i); -} - -Bool SidesList::validateAllyEnemyList(const AsciiString& tname, AsciiString& allies) -{ - // owners/allies/enemies must be players. - - Bool modified = false; - - AsciiString str, newstr, token; - - str = allies; - newstr.clear(); - while (str.nextToken(&token)) - { - if (token == tname) - { - modified = true; - continue; // no allies/enemies with self - } - - SidesInfo *si = findSideInfo(token); - if (!si) - { - modified = true; - continue; // player not found. - } - - if (!newstr.isEmpty()) - newstr.concat(" "); - newstr.concat(token); - } - - allies = newstr; - return modified; -} - -void SidesList::addPlayerByTemplate(AsciiString playerTemplateName) -{ - AsciiString playerName; - UnicodeString playerDisplayName; - Bool isHuman = false; - - if (playerTemplateName.isEmpty()) - { - playerName.set(""); // magic code for "neutral" - playerDisplayName = L"Neutral"; - isHuman = false; - } - else - { - playerName.set("Plyr"); - if (playerTemplateName.startsWith("Faction")) - { - playerName.concat(playerTemplateName.str() + 7); - } - else - { - playerName.concat(playerTemplateName); - } - playerDisplayName.translate(playerName); - isHuman = true; - // special-case "civilian"... - if (playerName == "PlyrCivilian") - isHuman = false; - } - - Dict d; - - d.clear(); - d.setAsciiString(TheKey_playerName, playerName); - d.setBool(TheKey_playerIsHuman, isHuman); - d.setUnicodeString(TheKey_playerDisplayName, playerDisplayName); - d.setAsciiString(TheKey_playerFaction, playerTemplateName); - d.setAsciiString(TheKey_playerAllies, AsciiString::TheEmptyString); - d.setAsciiString(TheKey_playerEnemies, AsciiString::TheEmptyString); - - addSide(&d); - - AsciiString playerTeamName; - playerTeamName.set("team"); - playerTeamName.concat(playerName); - - d.clear(); - d.setAsciiString(TheKey_teamName, playerTeamName); - d.setAsciiString(TheKey_teamOwner, playerName); - d.setBool(TheKey_teamIsSingleton, true); - addTeam(&d); -} - -Bool SidesList::validateSides() -{ - Bool modified = false; - - // ensure we have at least one player, and at least one neutral player. - Int i; - Int neutral = -1; - Int num = getNumSides(); - for (i = 0; i < num; i++) - { - if (getSideInfo(i)->getDict()->getAsciiString(TheKey_playerName).isEmpty()) - { - neutral = i; - break; - } - } - if (neutral == -1) - { - addPlayerByTemplate(AsciiString::TheEmptyString); - modified = true; - } - - // now ensure that every player has a proper "default team" - for (i = 0; i < getNumSides(); i++) - { - Dict *pdict = getSideInfo(i)->getDict(); - AsciiString pname = pdict->getAsciiString(TheKey_playerName); - AsciiString tname("team"); - tname.concat(pname); - TeamsInfo *ti = findTeamInfo(tname); - if (ti) - { - // make sure the team owner points back to the player. - if (ti->getDict()->getAsciiString(TheKey_teamOwner) != pname) - { - DEBUG_CRASH(("hmm, team owner mismatch (%s) (%s), this should not normally be possible",ti->getDict()->getAsciiString(TheKey_teamOwner).str(), pname.str())); - ti->getDict()->setAsciiString(TheKey_teamOwner, pname); - modified = true; - } - // default teams are always singletons. - if (!ti->getDict()->getBool(TheKey_teamIsSingleton)) - { - DEBUG_CRASH(("hmm, this should not normally be possible")); - ti->getDict()->setBool(TheKey_teamIsSingleton, true); - modified = true; - } - } - else - { - DEBUG_LOG(("*** default team for player %s missing (should not be possible), adding it...",tname.str())); - Dict d; - d.setAsciiString(TheKey_teamName, tname); - d.setAsciiString(TheKey_teamOwner, pname); - d.setBool(TheKey_teamIsSingleton, true); - addTeam(&d); - modified = true; - } - - AsciiString allies = pdict->getAsciiString(TheKey_playerAllies); - AsciiString enemies = pdict->getAsciiString(TheKey_playerEnemies); - - // ensure all teams have valid allies & enemies. - // (note that owners can be teams or players, but allies/enemies can only be teams.) - if (validateAllyEnemyList(pname, allies)) - { - DEBUG_LOG(("bad allies...")); - pdict->setAsciiString(TheKey_playerAllies, allies); - modified = true; - } - - if (validateAllyEnemyList(pname, enemies)) - { - DEBUG_LOG(("bad enemies...")); - pdict->setAsciiString(TheKey_playerEnemies, enemies); - modified = true; - } - } - - // ensure there's no overlap between team names and player names. - // (if there is, the player wins and the team is whacked.) -validate_team_names: - for (i = 0; i < getNumTeams(); i++) - { - Dict *tdict = getTeamInfo(i)->getDict(); - AsciiString tname = tdict->getAsciiString(TheKey_teamName); - if (findSideInfo(tname)) - { - DEBUG_CRASH(("name %s is duplicate between player and team, removing...",tname.str())); - removeTeam(i); - modified = true; - goto validate_team_names; - } - } - - for (i = 0; i < getNumTeams(); i++) - { - Dict *tdict = getTeamInfo(i)->getDict(); - AsciiString tname = tdict->getAsciiString(TheKey_teamName); - AsciiString towner = tdict->getAsciiString(TheKey_teamOwner); - SidesInfo* si = findSideInfo(towner); - if (si == nullptr || towner == tname) - { - DEBUG_LOG(("bad owner %s; reparenting to neutral...",towner.str())); - tdict->setAsciiString(TheKey_teamOwner, AsciiString::TheEmptyString); - modified = true; - } -// if (tdict->getType(NAMEKEY("teamAllies")) != Dict::DICT_NONE) -// tdict->remove(NAMEKEY("teamAllies")); -// if (tdict->getType(NAMEKEY("teamEnemies")) != Dict::DICT_NONE) -// tdict->remove(NAMEKEY("teamEnemies")); - - } - - return modified; -} - -// ------------------------------------------------------------------------------------------------ -/** CRC */ -// ------------------------------------------------------------------------------------------------ -void SidesList::crc( Xfer *xfer ) -{ - -} - -// ------------------------------------------------------------------------------------------------ -/** Xfer method - * Version Info: - * 1: Initial version */ -// ------------------------------------------------------------------------------------------------ -void SidesList::xfer( Xfer *xfer ) -{ - - // version - XferVersion currentVersion = 1; - XferVersion version = currentVersion; - xfer->xferVersion( &version, currentVersion ); - - // xfer num sides - Int sideCount = getNumSides(); - xfer->xferInt( &sideCount ); - if( sideCount != getNumSides() ) - { - - DEBUG_CRASH(( "SidesList::xfer - The sides list size has changed, this was not supposed to happen, you must version this method and figure out how to translate between old and new versions now" )); - throw SC_INVALID_DATA; - - } - - // side data - ScriptList *scriptList; - Bool scriptListPresent; - for( Int i = 0; i < sideCount; ++i ) - { - - // xfer script list data that can change - scriptList = getSideInfo( i )->getScriptList(); - scriptListPresent = scriptList ? TRUE : FALSE; - xfer->xferBool( &scriptListPresent ); - if( (scriptList == nullptr && scriptListPresent == TRUE) || - (scriptList != nullptr && scriptListPresent == FALSE) ) - { - - DEBUG_CRASH(( "SidesList::xfer - script list missing/present mismatch" )); - throw SC_INVALID_DATA; - - } - if( scriptListPresent ) - xfer->xferSnapshot( scriptList ); - - } - -} - -// ------------------------------------------------------------------------------------------------ -/** Load post process */ -// ------------------------------------------------------------------------------------------------ -void SidesList::loadPostProcess() -{ - -} - -/* ********* BuildListInfo class ****************************/ -/** - BuildListInfo - Constructor. -*/ -BuildListInfo::BuildListInfo() : -m_nextBuildList(nullptr), -m_renderObj(nullptr), -m_shadowObj(nullptr), -m_isInitiallyBuilt(false), -m_numRebuilds(0), -m_angle(0), -m_script(AsciiString::TheEmptyString), -m_health(100), -m_whiner(true), -m_unsellable(false), -m_repairable(true), -m_objectID(INVALID_ID), -m_objectTimestamp(0), -m_underConstruction(false), -m_isSupplyBuilding(false), -m_desiredGatherers(0), -m_currentGatherers(0), -m_automaticallyBuild(true), -m_priorityBuild(false), -m_buildingName(AsciiString::TheEmptyString) -{ - m_location.zero(); - m_rallyPointOffset.x = 0.0f; - m_rallyPointOffset.y = 0.0f; - m_selected = FALSE; - - Int i; - for (i=0; igetNext(); - cur->setNextBuildList(nullptr); // prevents recursion. - deleteInstance(cur); - cur = next; - } - } -} - -void BuildListInfo::parseStructure(INI *ini, void *instance, void* /*store*/, const void* /*userData*/) -{ - const char* c = ini->getNextToken(); - AsciiString tTemplateName(c); - - static const FieldParse myFieldParse[] = - { - { "Name", INI::parseAsciiString, nullptr, offsetof( BuildListInfo, m_buildingName ) }, - { "Location", INI::parseCoord2D, nullptr, offsetof( BuildListInfo, m_location ) }, - { "Rebuilds", INI::parseInt, nullptr, offsetof( BuildListInfo, m_numRebuilds ) }, - { "Angle", INI::parseAngleReal, nullptr, offsetof( BuildListInfo, m_angle ) }, - { "InitiallyBuilt", INI::parseBool, nullptr, offsetof( BuildListInfo, m_isInitiallyBuilt ) }, - { "RallyPointOffset", INI::parseCoord2D, nullptr, offsetof( BuildListInfo, m_rallyPointOffset ) }, - { "AutomaticallyBuild", INI::parseBool, nullptr, offsetof( BuildListInfo, m_automaticallyBuild ) }, - { nullptr, nullptr, nullptr, 0 } - }; - - BuildListInfo *buildInfo = newInstance( BuildListInfo ); - buildInfo->setTemplateName(tTemplateName); - ini->initFromINI(buildInfo, myFieldParse); - ((AISideBuildList*)instance)->addInfo(buildInfo); -} - - -/** - BuildListInfo - Duplicate - note - if linked, duplicates linked items. -*/ -BuildListInfo *BuildListInfo::duplicate() -{ - BuildListInfo *first = newInstance( BuildListInfo ); - *first = *this; - first->m_nextBuildList = nullptr; - BuildListInfo *next = this->m_nextBuildList; - BuildListInfo *cur = first; - while (next) { - BuildListInfo *link = newInstance( BuildListInfo ); - *link = *next; - link->m_nextBuildList = nullptr; - cur->m_nextBuildList = link; - cur = link; - next = next->m_nextBuildList; - } - return first; -} - -// ------------------------------------------------------------------------------------------------ -/** CRC */ -// ------------------------------------------------------------------------------------------------ -void BuildListInfo::crc( Xfer *xfer ) -{ - -} - -// ------------------------------------------------------------------------------------------------ -/** Xfer method - * Version Info: - * 1: Initial version */ -// ------------------------------------------------------------------------------------------------ -void BuildListInfo::xfer( Xfer *xfer ) -{ - - // version - XferVersion currentVersion = 2; - XferVersion version = currentVersion; - xfer->xferVersion( &version, currentVersion ); - - xfer->xferAsciiString( &m_buildingName ); - xfer->xferAsciiString( &m_templateName ); - xfer->xferCoord3D( &m_location ); - xfer->xferCoord2D( &m_rallyPointOffset ); - xfer->xferReal( &m_angle ); - xfer->xferBool( &m_isInitiallyBuilt ); - xfer->xferUnsignedInt( &m_numRebuilds ); - xfer->xferAsciiString( &m_script ); - xfer->xferInt( &m_health ); - xfer->xferBool( &m_whiner ); - xfer->xferBool( &m_unsellable ); - xfer->xferBool( &m_repairable ); - xfer->xferBool( &m_automaticallyBuild ); - // m_renderObj we don't need to xfer this, its for the editor only - // m_shadowObj we don't need to xfer this, its for the editor only - // m_selected we don't need to xfer this, its for the editor only - xfer->xferObjectID( &m_objectID ); - xfer->xferUnsignedInt( &m_objectTimestamp ); - xfer->xferBool( &m_underConstruction ); - xfer->xferUser( m_resourceGatherers, sizeof( ObjectID ) * MAX_RESOURCE_GATHERERS ); - xfer->xferBool( &m_isSupplyBuilding ); - xfer->xferInt( &m_desiredGatherers ); - xfer->xferBool( &m_priorityBuild ); - if (version>=2) { - xfer->xferInt(&m_currentGatherers); - } - -} - -// ------------------------------------------------------------------------------------------------ -/** Load post process */ -// ------------------------------------------------------------------------------------------------ -void BuildListInfo::loadPostProcess() -{ - -} - -/* ********* TeamsInfoRec class ****************************/ -TeamsInfoRec::TeamsInfoRec() : - m_numTeams(0), m_numTeamsAllocated(0), m_teams(nullptr) -{ -} - -TeamsInfoRec::TeamsInfoRec(const TeamsInfoRec& thatref) : - m_numTeams(0), m_numTeamsAllocated(0), m_teams(nullptr) -{ - *this = thatref; -} - -TeamsInfoRec::~TeamsInfoRec() -{ - clear(); -} - -// ug, I hate having to overload stuff, but this makes it a lot easier to make copies safely -TeamsInfoRec& TeamsInfoRec::operator=(const TeamsInfoRec& thatref) -{ - const TeamsInfoRec* that = &thatref; - if (this != that) - { - this->clear(); - for (int i = 0; i < that->m_numTeams; i++) - { - this->addTeam(that->m_teams[i].getDict()); - } - } - return *this; -} - -void TeamsInfoRec::clear() -{ - Int i; - - for (i = 0; i < m_numTeams; ++i) - m_teams[i].clear(); - - m_numTeams = 0; - m_numTeamsAllocated = 0; - delete [] m_teams; - m_teams = nullptr; -} - -TeamsInfo *TeamsInfoRec::findTeamInfo(AsciiString name, Int* index /*= nullptr*/) -{ - for (int i = 0; i < m_numTeams; ++i) - { - if (m_teams[i].getDict()->getAsciiString(TheKey_teamName) == name) - { - if (index) - *index = i; - return &m_teams[i]; - } - } - return nullptr; -} - -void TeamsInfoRec::addTeam(const Dict* d) -{ - enum - { - TEAM_ALLOC_CHUNK = 8 ///< how many teams to alloc at a time - }; - -#if RTS_GENERALS && RETAIL_COMPATIBLE_CRC - DEBUG_ASSERTCRASH(m_numTeams < 1024, ("hmm, seems like an awful lot of teams...")); -#else - DEBUG_ASSERTCRASH(m_numTeams < 2048, ("%d teams have been allocated (so far). This seems excessive.", m_numTeams )); -#endif - if (m_numTeams >= m_numTeamsAllocated) - { - // pool[]ify - const Int newNumTeamsAllocated = m_numTeams + TEAM_ALLOC_CHUNK; - TeamsInfo* nti = NEW TeamsInfo[newNumTeamsAllocated]; - Int i; - - for (i = 0; i < m_numTeams; ++i) - nti[i] = m_teams[i]; - - delete [] m_teams; - m_teams = nti; - m_numTeamsAllocated = newNumTeamsAllocated; - } - - m_teams[m_numTeams].init(d); - - ++m_numTeams; -} - -void TeamsInfoRec::removeTeam(Int i) -{ - if (i < 0 || i >= m_numTeams || m_numTeams <= 1) - return; - - --m_numTeams; - - for ( ; i < m_numTeams; ++i) - m_teams[i] = m_teams[i+1]; - - m_teams[m_numTeams].clear(); -} diff --git a/Generals/Code/GameEngine/Source/GameLogic/Map/TerrainLogic.cpp b/Generals/Code/GameEngine/Source/GameLogic/Map/TerrainLogic.cpp deleted file mode 100644 index 2545c2956ce..00000000000 --- a/Generals/Code/GameEngine/Source/GameLogic/Map/TerrainLogic.cpp +++ /dev/null @@ -1,3040 +0,0 @@ -/* -** 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.cpp ///////////////////////////////////////////////////////////////////////// -// Logical terrain representation for the game logic side -// Author: Colin Day, April 2001 -/////////////////////////////////////////////////////////////////////////////////////////////////// - -#include "PreRTS.h" // This must go first in EVERY cpp file in the GameEngine - - -#include "Common/DataChunk.h" -#include "Common/GameState.h" -#include "Common/MapObject.h" -#include "Common/Radar.h" -#include "Common/ThingFactory.h" -#include "Common/ThingTemplate.h" -#include "Common/WellKnownKeys.h" -#include "Common/Xfer.h" - -#include "GameClient/TerrainVisual.h" -#include "GameClient/View.h" - -#include "GameLogic/AI.h" -#include "GameLogic/AIPathfind.h" -#include "GameLogic/GameLogic.h" -#include "GameLogic/Damage.h" -#include "GameLogic/Object.h" -#include "GameLogic/PartitionManager.h" -#include "GameLogic/PolygonTrigger.h" -#include "GameLogic/Scripts.h" -#include "GameLogic/SidesList.h" -#include "GameLogic/TerrainLogic.h" -#include "GameLogic/Module/BodyModule.h" -#include "GameLogic/Module/BridgeBehavior.h" -#include "GameLogic/Module/BridgeTowerBehavior.h" -#include "GameLogic/GhostObject.h" - -#include "WWMath/plane.h" -#include "WWMath/tri.h" - - -// GLOBALS //////////////////////////////////////////////////////////////////////////////////////// -TerrainLogic *TheTerrainLogic = nullptr; - -// STATIC ///////////////////////////////////////////////////////////////////////////////////////// -WaterHandle TerrainLogic::m_gridWaterHandle; - -// Waypoint /////////////////////////////////////////////////////////////////////////////////////// - -//------------------------------------------------------------------------------------------------- -//------------------------------------------------------------------------------------------------- -Waypoint::Waypoint(WaypointID id, AsciiString name, const Coord3D *pLoc, AsciiString label1, AsciiString label2, - AsciiString label3, Bool biDirectional) : -m_name(name), -m_pNext(nullptr), -m_location(*pLoc), -m_id(id), -m_pathLabel1(label1), -m_pathLabel2(label2), -m_pathLabel3(label3), -m_numLinks(0), -m_biDirectional(biDirectional) -{ - Int i; - for (i=0; inewObject( towerTemplate, bridge->getTeam() ); - - // location information - Real angle = 0; - switch( towerType ) - { - - // -------------------------------------------------------------------------------------------- - case BRIDGE_TOWER_FROM_LEFT: - angle = bridge->getOrientation() + PI; - break; - - // -------------------------------------------------------------------------------------------- - case BRIDGE_TOWER_FROM_RIGHT: - angle = bridge->getOrientation() + PI; - break; - - // -------------------------------------------------------------------------------------------- - case BRIDGE_TOWER_TO_LEFT: - angle = bridge->getOrientation(); - break; - - // -------------------------------------------------------------------------------------------- - case BRIDGE_TOWER_TO_RIGHT: - angle = bridge->getOrientation(); - break; - - // -------------------------------------------------------------------------------------------- - default: - DEBUG_CRASH(( "Bridge::createTower - Unknown bridge tower type '%d'", towerType )); - return nullptr; - - } - - // set the position and angle - tower->setPosition( worldPos ); - tower->setOrientation( angle ); - - // tie it to the bridge - BridgeBehaviorInterface *bridgeInterface = BridgeBehavior::getBridgeBehaviorInterfaceFromObject( bridge ); - DEBUG_ASSERTCRASH( bridgeInterface != nullptr, ("Bridge::createTower - no 'BridgeBehaviorInterface' found") ); - if( bridgeInterface ) - bridgeInterface->setTower( towerType, tower ); - - // tie the bridge to us - BridgeTowerBehaviorInterface *bridgeTowerInterface = BridgeTowerBehavior::getBridgeTowerBehaviorInterfaceFromObject( tower ); - DEBUG_ASSERTCRASH( bridgeTowerInterface != nullptr, ("Bridge::createTower - no 'BridgeTowerBehaviorInterface' found") ); - if( bridgeTowerInterface ) - { - - // set bridge object - bridgeTowerInterface->setBridge( bridge ); - - // save our position type - bridgeTowerInterface->setTowerType( towerType ); - - } - - // if the bridge is indestructible, so is this tower - BodyModuleInterface *bridgeBody = bridge->getBodyModule(); - if( bridgeBody->isIndestructible() ) - { - BodyModuleInterface *towerBody = tower->getBodyModule(); - - towerBody->setIndestructible( TRUE ); - - } - - // return the newly created tower - return tower; - -} - -//------------------------------------------------------------------------------------------------- -//------------------------------------------------------------------------------------------------- -Bridge::Bridge(BridgeInfo &theInfo, Dict *props, AsciiString bridgeTemplateName) : -m_bridgeInfo(theInfo) -{ - - // save the template name - m_templateName = bridgeTemplateName; - - //Coord3D fromLeft, fromRight, toLeft, toRight; /// The 4 corners of the rectangle that the bridge covers. - m_bounds.lo.x = m_bridgeInfo.fromLeft.x; - m_bounds.lo.y = m_bridgeInfo.fromLeft.y; - m_bounds.hi = m_bounds.lo; - if (m_bounds.lo.x > m_bridgeInfo.fromRight.x) m_bounds.lo.x = m_bridgeInfo.fromRight.x; - if (m_bounds.lo.y > m_bridgeInfo.fromRight.y) m_bounds.lo.y = m_bridgeInfo.fromRight.y; - if (m_bounds.hi.x < m_bridgeInfo.fromRight.x) m_bounds.hi.x = m_bridgeInfo.fromRight.x; - if (m_bounds.hi.y < m_bridgeInfo.fromRight.y) m_bounds.hi.y = m_bridgeInfo.fromRight.y; - if (m_bounds.lo.x > m_bridgeInfo.toLeft.x) m_bounds.lo.x = m_bridgeInfo.toLeft.x; - if (m_bounds.lo.y > m_bridgeInfo.toLeft.y) m_bounds.lo.y = m_bridgeInfo.toLeft.y; - if (m_bounds.hi.x < m_bridgeInfo.toLeft.x) m_bounds.hi.x = m_bridgeInfo.toLeft.x; - if (m_bounds.hi.y < m_bridgeInfo.toLeft.y) m_bounds.hi.y = m_bridgeInfo.toLeft.y; - if (m_bounds.lo.x > m_bridgeInfo.toRight.x) m_bounds.lo.x = m_bridgeInfo.toRight.x; - if (m_bounds.lo.y > m_bridgeInfo.toRight.y) m_bounds.lo.y = m_bridgeInfo.toRight.y; - if (m_bounds.hi.x < m_bridgeInfo.toRight.x) m_bounds.hi.x = m_bridgeInfo.toRight.x; - if (m_bounds.hi.y < m_bridgeInfo.toRight.y) m_bounds.hi.y = m_bridgeInfo.toRight.y; - - m_bridgeInfo.curDamageState = BODY_PRISTINE; - - - static const ThingTemplate* genericBridgeTemplate = TheThingFactory->findTemplate("GenericBridge"); - if (!genericBridgeTemplate) { - DEBUG_LOG(("*** GenericBridge template not found.")); - return; - } - Object *bridge = TheThingFactory->newObject(genericBridgeTemplate, nullptr); - Coord3D center; - center.x = (m_bridgeInfo.fromLeft.x + m_bridgeInfo.toRight.x)/2.0f; - center.y = (m_bridgeInfo.fromLeft.y + m_bridgeInfo.toRight.y)/2.0f; - center.z = (m_bridgeInfo.fromLeft.z + m_bridgeInfo.toRight.z)/2.0f; - bridge->setPosition(¢er); - m_bridgeInfo.bridgeObjectID = bridge->getID(); - bridge->updateObjValuesFromMapProperties(props); - - // - // we'll say the angle of this object representing the bridge is from the 'from' side - // to the 'to' side. - // - Coord2D v; - v.x = m_bridgeInfo.toLeft.x - m_bridgeInfo.fromLeft.x; - v.y = m_bridgeInfo.toLeft.y - m_bridgeInfo.fromLeft.y; - bridge->setOrientation( v.toAngle() ); - - v.x = m_bridgeInfo.toLeft.x - m_bridgeInfo.toRight.x; - v.y = m_bridgeInfo.toLeft.y - m_bridgeInfo.toRight.y; - v.normalize(); - - // get the template of the bridge - TerrainRoadType *bridgeTemplate = TheTerrainRoads->findBridge( bridgeTemplateName ); - if( bridgeTemplate == nullptr ) { - DEBUG_LOG(( "*** Bridge Template Not Found '%s'.", bridgeTemplateName.str() )); - return; - } - -#define no_BRIDGE_TOWERS // since they aren't destructable, don't need towers. -#if BRIDGE_TOWERS - // initialize each of the tower positions to that of the bridge info bounding rect - Coord3D towerPos[ BRIDGE_MAX_TOWERS ]; - towerPos[ BRIDGE_TOWER_FROM_LEFT ] = m_bridgeInfo.fromLeft; - towerPos[ BRIDGE_TOWER_FROM_RIGHT ] = m_bridgeInfo.fromRight; - towerPos[ BRIDGE_TOWER_TO_LEFT ] = m_bridgeInfo.toLeft; - towerPos[ BRIDGE_TOWER_TO_RIGHT ] = m_bridgeInfo.toRight; - - // create objects targetable objects for the 4 tower pieces - const ThingTemplate *towerTemplate; - BridgeTowerType type; - Object *tower; - Real offset = PATHFIND_CELL_SIZE_F/2.0f; - for( Int i = 0; i < BRIDGE_MAX_TOWERS; ++i ) - { - - // create the tower - type = (BridgeTowerType)i; - towerTemplate = TheThingFactory->findTemplate( bridgeTemplate->getTowerObjectName( type ) ); - if (towerTemplate) { - offset = towerTemplate->getTemplateGeometryInfo().getMajorRadius(); - } - Coord3D pos = towerPos[type]; - switch( type ) - { - case BRIDGE_TOWER_FROM_LEFT: - case BRIDGE_TOWER_TO_LEFT: - pos.x += v.x*offset; - pos.y += v.y*offset; - break; - case BRIDGE_TOWER_FROM_RIGHT: - case BRIDGE_TOWER_TO_RIGHT: - pos.x -= v.x*offset; - pos.y -= v.y*offset; - break; - - } - tower = createTower( &pos, type, towerTemplate, bridge ); - - // store the tower object ID - m_bridgeInfo.towerObjectID[ i ] = tower->getID(); - - } -#endif - - m_next = nullptr; -} - -//------------------------------------------------------------------------------------------------- -//------------------------------------------------------------------------------------------------- -Bridge::Bridge(Object *bridgeObj) -{ - - // save the template name - m_templateName = bridgeObj->getTemplate()->getName(); - - DEBUG_ASSERTLOG( bridgeObj->getGeometryInfo().getGeomType()==GEOMETRY_BOX, ("Bridges need to be rectangles.")); - - const Coord3D *pos = bridgeObj->getPosition(); - Real angle = bridgeObj->getOrientation(); - - Real halfsizeX = bridgeObj->getGeometryInfo().getMajorRadius(); - Real halfsizeY = bridgeObj->getGeometryInfo().getMinorRadius(); - m_bridgeInfo.bridgeWidth = 2*halfsizeY; - - Real c = (Real)Cos(angle); - Real s = (Real)Sin(angle); - - m_bridgeInfo.fromLeft.set(pos->x-halfsizeX*c-halfsizeY*s, pos->y + halfsizeY*c - halfsizeX*s, pos->z); - m_bridgeInfo.toLeft.set(pos->x+halfsizeX*c-halfsizeY*s, pos->y + halfsizeY*c + halfsizeX*s, pos->z); - m_bridgeInfo.fromRight.set(pos->x-halfsizeX*c+halfsizeY*s, pos->y - halfsizeY*c - halfsizeX*s, pos->z); - m_bridgeInfo.toRight.set(pos->x+halfsizeX*c+halfsizeY*s, pos->y - halfsizeY*c + halfsizeX*s, pos->z); - - m_bridgeInfo.from.x = (m_bridgeInfo.fromLeft.x + m_bridgeInfo.fromRight.x)/2.0f; - m_bridgeInfo.from.y = (m_bridgeInfo.fromLeft.y + m_bridgeInfo.fromRight.y)/2.0f; - m_bridgeInfo.from.z = (m_bridgeInfo.fromLeft.z + m_bridgeInfo.fromRight.z)/2.0f; - - m_bridgeInfo.to.x = (m_bridgeInfo.toLeft.x + m_bridgeInfo.toRight.x)/2.0f; - m_bridgeInfo.to.y = (m_bridgeInfo.toLeft.y + m_bridgeInfo.toRight.y)/2.0f; - m_bridgeInfo.to.z = (m_bridgeInfo.toLeft.z + m_bridgeInfo.toRight.z)/2.0f; - - //Coord3D fromLeft, fromRight, toLeft, toRight; /// The 4 corners of the rectangle that the bridge covers. - m_bounds.lo.x = m_bridgeInfo.fromLeft.x; - m_bounds.lo.y = m_bridgeInfo.fromLeft.y; - m_bounds.hi = m_bounds.lo; - if (m_bounds.lo.x > m_bridgeInfo.fromRight.x) m_bounds.lo.x = m_bridgeInfo.fromRight.x; - if (m_bounds.lo.y > m_bridgeInfo.fromRight.y) m_bounds.lo.y = m_bridgeInfo.fromRight.y; - if (m_bounds.hi.x < m_bridgeInfo.fromRight.x) m_bounds.hi.x = m_bridgeInfo.fromRight.x; - if (m_bounds.hi.y < m_bridgeInfo.fromRight.y) m_bounds.hi.y = m_bridgeInfo.fromRight.y; - if (m_bounds.lo.x > m_bridgeInfo.toLeft.x) m_bounds.lo.x = m_bridgeInfo.toLeft.x; - if (m_bounds.lo.y > m_bridgeInfo.toLeft.y) m_bounds.lo.y = m_bridgeInfo.toLeft.y; - if (m_bounds.hi.x < m_bridgeInfo.toLeft.x) m_bounds.hi.x = m_bridgeInfo.toLeft.x; - if (m_bounds.hi.y < m_bridgeInfo.toLeft.y) m_bounds.hi.y = m_bridgeInfo.toLeft.y; - if (m_bounds.lo.x > m_bridgeInfo.toRight.x) m_bounds.lo.x = m_bridgeInfo.toRight.x; - if (m_bounds.lo.y > m_bridgeInfo.toRight.y) m_bounds.lo.y = m_bridgeInfo.toRight.y; - if (m_bounds.hi.x < m_bridgeInfo.toRight.x) m_bounds.hi.x = m_bridgeInfo.toRight.x; - if (m_bounds.hi.y < m_bridgeInfo.toRight.y) m_bounds.hi.y = m_bridgeInfo.toRight.y; - - m_bridgeInfo.curDamageState = BODY_PRISTINE; - - m_bridgeInfo.bridgeObjectID = bridgeObj->getID(); - - // get the template of the bridge - AsciiString bridgeTemplateName = bridgeObj->getTemplate()->getName(); - TerrainRoadType *bridgeTemplate = TheTerrainRoads->findBridge( bridgeTemplateName ); - if( bridgeTemplate == nullptr ) { - DEBUG_LOG(( "*** Bridge Template Not Found '%s'.", bridgeTemplateName.str() )); - return; - } - - Coord2D v; - v.x = m_bridgeInfo.toLeft.x - m_bridgeInfo.toRight.x; - v.y = m_bridgeInfo.toLeft.y - m_bridgeInfo.toRight.y; - v.normalize(); - - // initialize each of the tower positions to that of the bridge info bounding rect - Coord3D towerPos[ BRIDGE_MAX_TOWERS ]; - towerPos[ BRIDGE_TOWER_FROM_LEFT ] = m_bridgeInfo.fromLeft; - towerPos[ BRIDGE_TOWER_FROM_RIGHT ] = m_bridgeInfo.fromRight; - towerPos[ BRIDGE_TOWER_TO_LEFT ] = m_bridgeInfo.toLeft; - towerPos[ BRIDGE_TOWER_TO_RIGHT ] = m_bridgeInfo.toRight; - - Real offset = PATHFIND_CELL_SIZE_F/2.0f; - // create objects targetable objects for the 4 tower pieces - const ThingTemplate *towerTemplate; - BridgeTowerType type; - Object *tower; - for( Int i = 0; i < BRIDGE_MAX_TOWERS; ++i ) - { - - type = (BridgeTowerType)i; - towerTemplate = TheThingFactory->findTemplate( bridgeTemplate->getTowerObjectName( type ) ); - if (towerTemplate) { - offset = towerTemplate->getTemplateGeometryInfo().getMajorRadius(); - } - Coord3D pos = towerPos[type]; - switch( type ) - { - case BRIDGE_TOWER_FROM_LEFT: - case BRIDGE_TOWER_TO_LEFT: - pos.x += v.x*offset; - pos.y += v.y*offset; - break; - case BRIDGE_TOWER_FROM_RIGHT: - case BRIDGE_TOWER_TO_RIGHT: - pos.x -= v.x*offset; - pos.y -= v.y*offset; - break; - - } - tower = createTower( &pos, type, towerTemplate, bridgeObj ); - if( tower ) - { - // store the tower object ID - m_bridgeInfo.towerObjectID[ i ] = tower->getID(); - } - - } - - m_next = nullptr; -} - -//------------------------------------------------------------------------------------------------- -//------------------------------------------------------------------------------------------------- -Bridge::~Bridge() -{ - -} - - -//------------------------------------------------------------------------------------------------- -/** isPointOnBridge - see if point is on bridge. */ -//------------------------------------------------------------------------------------------------- -Bool Bridge::isPointOnBridge(const Coord3D *pLoc) -{ - if (pLoc->x < m_bounds.lo.x) return(false); - if (pLoc->x > m_bounds.hi.x) return(false); - if (pLoc->y < m_bounds.lo.y) return(false); - if (pLoc->y > m_bounds.hi.y) return(false); - - Vector3 testPt(pLoc->x, pLoc->y, pLoc->z); - Vector3 left1(m_bridgeInfo.fromLeft.x, m_bridgeInfo.fromLeft.y, m_bridgeInfo.fromLeft.z); - Vector3 right1(m_bridgeInfo.fromRight.x, m_bridgeInfo.fromRight.y, m_bridgeInfo.fromRight.z); - Vector3 left2(m_bridgeInfo.toLeft.x, m_bridgeInfo.toLeft.y, m_bridgeInfo.toLeft.z); - Vector3 right2(m_bridgeInfo.toRight.x, m_bridgeInfo.toRight.y, m_bridgeInfo.toRight.z); - - unsigned char flags; - - if (Point_In_Triangle_2D(left1, right1, left2, testPt, 0, 1, flags)) { - return true; - } - if (Point_In_Triangle_2D(right1, left2, right2, testPt, 0, 1, flags)) { - return true; - } - return(false); -} - -/*------------------------------------------------------------------------------------------------- -/** Clip a floating point line to the region provided. The source line runs from p1 to p2, and is clipped - * using the clipRegion. - * - * Return values: - * TRUE - Line intersects the region - * FALSE - Line does not intersect the region - */ -//------------------------------------------------------------------------------------------------- -Bool LineInRegion( const Coord2D *p1, const Coord2D *p2, const Region2D *clipRegion ) -{ - enum { CLIP_LEFT = 0x01, - CLIP_RIGHT = 0x02, - CLIP_BOTTOM = 0x04, - CLIP_TOP = 0x08 }; - Real x1, y1, x2, y2; - Real clipLeft; - Real clipRight; - Real clipTop; - Real clipBottom; - Int clipCode1; - Int clipCode2; - Real diff; - - // Use clip window that includes bottom right pixel - clipLeft = clipRegion->lo.x; - clipRight = clipRegion->hi.x; - clipTop = clipRegion->lo.y; - clipBottom = clipRegion->hi.y; - - x1 = p1->x; - y1 = p1->y; - x2 = p2->x; - y2 = p2->y; - - // Test first point - clipCode1 = 0; - - if (x1 < clipLeft) - clipCode1 = CLIP_LEFT; - else - if (x1 > clipRight) - clipCode1 = CLIP_RIGHT; - - if (y1 < clipTop) - clipCode1 |= CLIP_TOP; - else - if (y1 > clipBottom) - clipCode1 |= CLIP_BOTTOM; - - - // Test second point - clipCode2 = 0; - - if (x2 < clipLeft) - clipCode2 = CLIP_LEFT; - else - if (x2 > clipRight) - clipCode2 = CLIP_RIGHT; - - if (y2 < clipTop) - clipCode2 |= CLIP_TOP; - else - if (y2 > clipBottom) - clipCode2 |= CLIP_BOTTOM; - - - // Both points inside window? - if ((clipCode1 | clipCode2) == 0) - { - return TRUE; - } - - // Both points outside window? - if (clipCode1 & clipCode2) - return FALSE; - - // First point outside window? - if (clipCode1) - { - if (clipCode1 & CLIP_TOP) - { - if ((diff = (y2 - y1)) == 0) - return FALSE; - x1 += (x2 - x1) * (clipTop - y1) / diff; - y1 = clipTop; - } - else - if (clipCode1 & CLIP_BOTTOM) - { - if ((diff = (y2 - y1)) == 0) - return FALSE; - x1 += (x2 - x1) * (clipBottom - y1) / diff; - y1 = clipBottom; - } - - if (x1 > clipRight) - { - if ((diff = (x2 - x1)) == 0) - return FALSE; - y1 += (y2 - y1) * (clipRight - x1) / diff; - x1 = clipRight; - } - else - if (x1 < clipLeft) - { - if ((diff = (x2 - x1)) == 0) - return FALSE; - y1 += (y2 - y1) * (clipLeft - x1) / diff; - x1 = clipLeft; - } - } - - // Second point outside window? - if (clipCode2) - { - if (clipCode2 & CLIP_TOP) - { - if ((diff = (y2 - y1)) == 0) - return FALSE; - x2 += (x2 - x1) * (clipTop - y2) / diff; - y2 = clipTop; - } - else - if (clipCode2 & CLIP_BOTTOM) - { - if ((diff = (y2 - y1)) == 0) - return FALSE; - x2 += (x2 - x1) * (clipBottom - y2) / diff; - y2 = clipBottom; - } - - if (x2 > clipRight) - { - if ((diff = (x2 - x1)) == 0) - return FALSE; - y2 += (y2 - y1) * (clipRight - x2) / diff; - x2 = clipRight; - } - else - if (x2 < clipLeft) - { - if ((diff = (x2 - x1)) == 0) - return FALSE; - y2 += (y2 - y1) * (clipLeft - x2) / diff; - x2 = clipLeft; - } - } - - // Line is visible - return (x1 >= clipLeft && x1 <= clipRight && - y1 >= clipTop && y1 <= clipBottom && - x2 >= clipLeft && x2 <= clipRight && - y2 >= clipTop && y2 <= clipBottom); - -} - -static Bool PointInRegion2D( const Coord3D *pt, const Region2D *clipRegion ) -{ - return (pt->x>=clipRegion->lo.x && - pt->y>=clipRegion->lo.y && - pt->x<=clipRegion->hi.x && - pt->y<=clipRegion->hi.y); -} - - -//------------------------------------------------------------------------------------------------- -/** isCellOnEnd - see if cell is on the end of the bridge. */ -//------------------------------------------------------------------------------------------------- -Bool Bridge::isCellOnEnd(const Region2D *cell) -{ - Coord3D endVector; - endVector.x = m_bridgeInfo.fromRight.x - m_bridgeInfo.fromLeft.x; - endVector.y = m_bridgeInfo.fromRight.y - m_bridgeInfo.fromLeft.y; - endVector.z = m_bridgeInfo.fromRight.z - m_bridgeInfo.fromLeft.z; - endVector.normalize(); - // Offset by 1 pathfind cell. - endVector.x *= PATHFIND_CELL_SIZE; - endVector.y *= PATHFIND_CELL_SIZE; - - Coord3D fromLeft = m_bridgeInfo.fromLeft; - fromLeft.x += endVector.x; - fromLeft.y += endVector.y; - - Coord3D fromRight = m_bridgeInfo.fromRight; - fromRight.x -= endVector.x; - fromRight.y -= endVector.y; - - Coord3D toLeft = m_bridgeInfo.toLeft; - toLeft.x += endVector.x; - toLeft.y += endVector.y; - - Coord3D toRight = m_bridgeInfo.toRight; - toRight.x -= endVector.x; - toRight.y -= endVector.y; - -/* if (PointInRegion2D(&fromLeft, cell)) return false; - if (PointInRegion2D(&fromRight, cell)) return false; - if (PointInRegion2D(&toLeft, cell)) return false; - if (PointInRegion2D(&toRight, cell)) return false; */ - Coord2D line1, line2; - line1.x = fromLeft.x; - line1.y = fromLeft.y; - line2.x = fromRight.x; - line2.y = fromRight.y; - if (LineInRegion(&line1, &line2, cell)) { - return true; - } - line1.x = toLeft.x; - line1.y = toLeft.y; - line2.x = toRight.x; - line2.y = toRight.y; - if (LineInRegion(&line1, &line2, cell)) { - return true; - } - return(false); -} - -//------------------------------------------------------------------------------------------------- -/** isCellOnSide - see if cell is on the end of the bridge. */ -//------------------------------------------------------------------------------------------------- -Bool Bridge::isCellOnSide(const Region2D *cell) -{ - Coord3D endVector; - endVector.x = m_bridgeInfo.fromRight.x - m_bridgeInfo.fromLeft.x; - endVector.y = m_bridgeInfo.fromRight.y - m_bridgeInfo.fromLeft.y; - endVector.z = m_bridgeInfo.fromRight.z - m_bridgeInfo.fromLeft.z; - endVector.normalize(); - // Offset by 1 pathfind cell. - endVector.x *= PATHFIND_CELL_SIZE*0.51f; - endVector.y *= PATHFIND_CELL_SIZE*0.51f; - - Coord3D fromLeft = m_bridgeInfo.fromLeft; - fromLeft.x -= endVector.x; - fromLeft.y -= endVector.y; - - Coord3D fromRight = m_bridgeInfo.fromRight; - fromRight.x += endVector.x; - fromRight.y += endVector.y; - - Coord3D toLeft = m_bridgeInfo.toLeft; - toLeft.x -= endVector.x; - toLeft.y -= endVector.y; - - Coord3D toRight = m_bridgeInfo.toRight; - toRight.x += endVector.x; - toRight.y += endVector.y; - - Coord2D line1, line2; - line1.x = fromLeft.x; - line1.y = fromLeft.y; - line2.x = toLeft.x; - line2.y = toLeft.y; - if (LineInRegion(&line1, &line2, cell)) { - return true; - } - line1.x = fromRight.x; - line1.y = fromRight.y; - line2.x = toRight.x; - line2.y = toRight.y; - if (LineInRegion(&line1, &line2, cell)) { - return true; - } - fromLeft.x -= endVector.x; - fromLeft.y -= endVector.y; - - fromRight.x += endVector.x; - fromRight.y += endVector.y; - - toLeft.x -= endVector.x; - toLeft.y -= endVector.y; - - toRight.x += endVector.x; - toRight.y += endVector.y; - - line1.x = fromLeft.x; - line1.y = fromLeft.y; - line2.x = toLeft.x; - line2.y = toLeft.y; - if (LineInRegion(&line1, &line2, cell)) { - return true; - } - line1.x = fromRight.x; - line1.y = fromRight.y; - line2.x = toRight.x; - line2.y = toRight.y; - if (LineInRegion(&line1, &line2, cell)) { - return true; - } - return(false); -} - -//------------------------------------------------------------------------------------------------- -/** isCellEntryPoint - Is a pathfind cell a spot to move onto the bridge. */ -//------------------------------------------------------------------------------------------------- -Bool Bridge::isCellEntryPoint(const Region2D *cell) -{ - Coord3D endVector; - endVector.x = m_bridgeInfo.fromRight.x - m_bridgeInfo.fromLeft.x; - endVector.y = m_bridgeInfo.fromRight.y - m_bridgeInfo.fromLeft.y; - endVector.z = m_bridgeInfo.fromRight.z - m_bridgeInfo.fromLeft.z; - endVector.normalize(); - // Offset by 1 pathfind cell. - endVector.x *= PATHFIND_CELL_SIZE; - endVector.y *= PATHFIND_CELL_SIZE; - Coord3D bridgeVector; - bridgeVector.x = m_bridgeInfo.to.x - m_bridgeInfo.from.x; - bridgeVector.y = m_bridgeInfo.to.y - m_bridgeInfo.from.y; - bridgeVector.z = m_bridgeInfo.to.z - m_bridgeInfo.from.z; - bridgeVector.normalize(); - // Offset by 1/2 pathfind cell. - bridgeVector.x *= PATHFIND_CELL_SIZE/2; - bridgeVector.y *= PATHFIND_CELL_SIZE/2; - - Coord3D fromLeft = m_bridgeInfo.fromLeft; - fromLeft.x -= bridgeVector.x; - fromLeft.y -= bridgeVector.y; - fromLeft.x += endVector.x; - fromLeft.y += endVector.y; - - Coord3D fromRight = m_bridgeInfo.fromRight; - fromRight.x -= bridgeVector.x; - fromRight.y -= bridgeVector.y; - fromRight.x -= endVector.x; - fromRight.y -= endVector.y; - - Coord3D toLeft = m_bridgeInfo.toLeft; - toLeft.x += bridgeVector.x; - toLeft.y += bridgeVector.y; - toLeft.x += endVector.x; - toLeft.y += endVector.y; - - Coord3D toRight = m_bridgeInfo.toRight; - toRight.x += bridgeVector.x; - toRight.y += bridgeVector.y; - toRight.x -= endVector.x; - toRight.y -= endVector.y; - -/* if (PointInRegion2D(&fromLeft, cell)) return false; - if (PointInRegion2D(&fromRight, cell)) return false; - if (PointInRegion2D(&toLeft, cell)) return false; - if (PointInRegion2D(&toRight, cell)) return false; - */ - Coord2D line1, line2; - line1.x = fromLeft.x; - line1.y = fromLeft.y; - line2.x = fromRight.x; - line2.y = fromRight.y; - if (LineInRegion(&line1, &line2, cell)) { - return true; - } - line1.x = toLeft.x; - line1.y = toLeft.y; - line2.x = toRight.x; - line2.y = toRight.y; - if (LineInRegion(&line1, &line2, cell)) { - return true; - } - return(false); - -} - -//------------------------------------------------------------------------------------------------- -/** pickBridge - see if point is on bridge. */ -//------------------------------------------------------------------------------------------------- -Drawable *Bridge::pickBridge(const Vector3 &from, const Vector3 &to, Vector3 *pos) -{ - - Vector3 left1(m_bridgeInfo.fromLeft.x, m_bridgeInfo.fromLeft.y, m_bridgeInfo.fromLeft.z); - Vector3 right1(m_bridgeInfo.fromRight.x, m_bridgeInfo.fromRight.y, m_bridgeInfo.fromRight.z); - Vector3 left2(m_bridgeInfo.toLeft.x, m_bridgeInfo.toLeft.y, m_bridgeInfo.toLeft.z); - - PlaneClass plane(left1, right1, left2); - Real t; - plane.Compute_Intersection(from, to, &t); - Vector3 intersectPos; - intersectPos = from + (to-from) * t; - - Coord3D loc; - loc.x = intersectPos.X; - loc.y = intersectPos.Y; - loc.z = intersectPos.Z; - - if (isPointOnBridge(&loc)) { - *pos = intersectPos; - //DEBUG_LOG(("Picked bridge %.2f, %.2f, %.2f", intersectPos.X, intersectPos.Y, intersectPos.Z)); - Object *bridge = TheGameLogic->findObjectByID(m_bridgeInfo.bridgeObjectID); - if (bridge) { - return bridge->getDrawable(); - } - } - return nullptr; -} - -//------------------------------------------------------------------------------------------------- -/** updateDamageState - Update the damage state. */ -//------------------------------------------------------------------------------------------------- -void Bridge::updateDamageState() -{ - m_bridgeInfo.damageStateChanged = false; - if (m_bridgeInfo.bridgeObjectID==0) return; - Object *bridge = TheGameLogic->findObjectByID(m_bridgeInfo.bridgeObjectID); - if (bridge) { - // get object damage state - { - BodyDamageType damageState = bridge->getBodyModule()->getDamageState(); - BodyDamageType curState = m_bridgeInfo.curDamageState; - if (damageState != curState) { - m_bridgeInfo.curDamageState = damageState; - if (damageState == BODY_RUBBLE) { - TheAI->pathfinder()->changeBridgeState(m_layer, false); - m_bridgeInfo.damageStateChanged = true; - Object *obj; - for (obj = TheGameLogic->getFirstObject(); obj; obj=obj->getNextObject()) { - if (obj->getLayer() == m_layer) { - // don't consider the bridge health, 'cuz it's already dead. (srj) - const Bool considerBridgeHealth = false; - if (TheTerrainLogic->objectInteractsWithBridgeLayer(obj, obj->getLayer(), considerBridgeHealth)) - { - // srj sez: if we use this threshold, then stuff on the bridge apron doesn't die but - // might sink thru the eyecandy of bridge drbris, looking funny. so now we just indiscriminately - // kill everything that was on the bridge, regardless of height they might fall. - //Real deltaHeight = obj->getPosition()->z - TheTerrainLogic->getGroundHeight(obj->getPosition()->x, obj->getPosition()->y); - //if (deltaHeight>PATHFIND_CELL_SIZE_F * 0.5f) - { - // The object fell off the bridge. - // Destroy it. - DamageInfo extraDamageInfo; - extraDamageInfo.in.m_damageType = DAMAGE_FALLING; - extraDamageInfo.in.m_deathType = DEATH_SPLATTED; - extraDamageInfo.in.m_sourceID = obj->getID(); - extraDamageInfo.in.m_amount = HUGE_DAMAGE_AMOUNT; - obj->attemptDamage(&extraDamageInfo); - } - } - } - } - } - if (curState==BODY_RUBBLE) { - - // - // we do not set the bridge as usable if scaffolding is up ... the scaffolding - // code will take care of that - // - BridgeBehaviorInterface *bbi = BridgeBehavior::getBridgeBehaviorInterfaceFromObject( bridge ); - if( bbi == nullptr || bbi->isScaffoldPresent() == FALSE ) - TheAI->pathfinder()->changeBridgeState(m_layer, true); - m_bridgeInfo.damageStateChanged = true; - } - } - } - } else { - m_bridgeInfo.bridgeObjectID = INVALID_ID; - DEBUG_CRASH(("Bridge object disappeared - unexpected. jba.")); - } - -} - - -//------------------------------------------------------------------------------------------------- -/** getHeight - Get the height for an object on bridge.. */ -//------------------------------------------------------------------------------------------------- -Real Bridge::getBridgeHeight(const Coord3D *pLoc, Coord3D* normal) -{ - Vector3 left1(m_bridgeInfo.fromLeft.x, m_bridgeInfo.fromLeft.y, m_bridgeInfo.fromLeft.z); - Vector3 right1(m_bridgeInfo.fromRight.x, m_bridgeInfo.fromRight.y, m_bridgeInfo.fromRight.z); - Vector3 left2(m_bridgeInfo.toLeft.x, m_bridgeInfo.toLeft.y, m_bridgeInfo.toLeft.z); - PlaneClass plane(left1, right1, left2); - const Real factor = 1000.0f; - Vector3 bottom(pLoc->x, pLoc->y, 0); - Vector3 top(pLoc->x, pLoc->y, factor); - Real t; - plane.Compute_Intersection(bottom, top, &t); - if (normal) { - normal->x = plane.N.X; - normal->y = plane.N.Y; - normal->z = plane.N.Z; - } - - return t*factor; -} - -//------------------------------------------------------------------------------------------------- -//------------------------------------------------------------------------------------------------- -TerrainLogic::TerrainLogic() -{ - Int i; - - m_activeBoundary = 0; - m_waterGridEnabled = FALSE; - for( i = 0; i < MAX_DYNAMIC_WATER; ++i ) - { - - m_waterToUpdate[ i ].waterTable = nullptr; - m_waterToUpdate[ i ].changePerFrame = 0.0f; - m_waterToUpdate[ i ].targetHeight = 0.0f; - m_waterToUpdate[ i ].damageAmount = 0.0f; - m_waterToUpdate[ i ].currentHeight = 0.0f; - - } - m_numWaterToUpdate = 0; - - m_waypointListHead = nullptr; - m_bridgeListHead = nullptr; - m_mapData = nullptr; - m_bridgeDamageStatesChanged = FALSE; - m_mapDX = 0; - m_mapDY = 0; - - -} - -//------------------------------------------------------------------------------------------------- -//------------------------------------------------------------------------------------------------- -TerrainLogic::~TerrainLogic() -{ - - reset(); // just in case - -} - -//------------------------------------------------------------------------------------------------- -/** Init */ -//------------------------------------------------------------------------------------------------- -void TerrainLogic::init() -{ - -} - -//------------------------------------------------------------------------------------------------- -/** Reset */ -//------------------------------------------------------------------------------------------------- -void TerrainLogic::reset() -{ - - deleteWaypoints(); - deleteBridges(); - PolygonTrigger::deleteTriggers(); - m_numWaterToUpdate = 0; - -} - -//------------------------------------------------------------------------------------------------- -/** Update */ -//------------------------------------------------------------------------------------------------- -void TerrainLogic::update() -{ - - // bridge damage states have not changed this frame now - m_bridgeDamageStatesChanged = false; - - // update any water tables that we need to - if( m_numWaterToUpdate ) - { - const WaterHandle *water; - Real changePerFrame, - damageAmount, - targetHeight, - currentHeight; - Bool finalTransition, - doDamageThisFrame = (TheGameLogic->getFrame() % LOGICFRAMES_PER_SECOND) == 0; - - for( Int i = m_numWaterToUpdate - 1; i >= 0; --i ) - { - - // get the water info - water = m_waterToUpdate[ i ].waterTable; - changePerFrame = m_waterToUpdate[ i ].changePerFrame; - targetHeight = m_waterToUpdate[ i ].targetHeight; - damageAmount = m_waterToUpdate[ i ].damageAmount; - currentHeight = m_waterToUpdate[ i ].currentHeight; - - // - // check to see if this change per frame will get us to our target height, if so - // we adjust the changePerFrame to make us be exactly at our target height, and after - // the change we will remove our entry from this update phase - // - finalTransition = FALSE; - if( changePerFrame > 0 ) - { - - if( currentHeight + changePerFrame >= targetHeight ) - finalTransition = TRUE; - - } - else - { - - if( currentHeight + changePerFrame <= targetHeight ) - finalTransition = TRUE; - - } - - if( finalTransition == TRUE ) - { - - // - // make the final water height change, note we do damage on the final transition - // in all situations by passing a valid damage amount - // - setWaterHeight( water, targetHeight, damageAmount, TRUE ); - - // - // remove our water entry from the per frame water list, we're processing this array - // backwards which makes cleanup easy, we just move everything after our index - // position up one - // - for( Int j = i; j < m_numWaterToUpdate; j++ ) - m_waterToUpdate[ i ] = m_waterToUpdate[ j ]; - m_numWaterToUpdate -= 1; - - } - else - { - - // - // we're not doing damage every frame (0 damage) from the water - // because it's an expensive process - // - if( doDamageThisFrame == FALSE ) - damageAmount = 0.0f; - - // - // because some water implementation store the height as integers, some changes - // are too small to keep track of in the actual water data structures so we have to - // keep track of it ourselves - // - currentHeight += changePerFrame; - m_waterToUpdate[ i ].currentHeight = currentHeight; - - // update actual water - setWaterHeight( water, currentHeight, damageAmount, FALSE ); - - } - - } - - } - -} - -//------------------------------------------------------------------------------------------------- -/** newMap */ -//------------------------------------------------------------------------------------------------- -void TerrainLogic::newMap( Bool saveGame ) -{ - - // Set waypoint's z value, now that the height map is loaded. - for( Waypoint *way = m_waypointListHead; way; way = way->getNext() ) - { - const Coord3D* loc = way->getLocation(); - way->setLocationZ(getGroundHeight(loc->x, loc->y)); - } - // - // until we have a real way to specify different water planes in the map, we will check - // for a special waypoint name that we will put in maps that we want to have a - // water grid - /// @todo Mark W, remove this when you have water plane placements in the map done (Colin) - // - Waypoint *waypoint = getWaypointByName( "WaveGuide1" ); - Bool enable = FALSE; - if( waypoint ) - enable = TRUE; - enableWaterGrid( enable ); - -} - -// ------------------------------------------------------------------------------------------------ -// ------------------------------------------------------------------------------------------------ -void TerrainLogic::enableWaterGrid( Bool enable ) -{ - - // set our internal variable we can query - m_waterGridEnabled = enable; - - // - // set the vertex animated water properties, that is, the clamps, the water position, - // the grid resolution etc ... - // - if( enable == TRUE ) - { - - /** @todo we should have this stuff stored with the map and have a real interface for - design to edit such things so that people can put gridded water in any map without all - this hard coded nasty stuff, but this is what "they" want for now */ - - Int waterSettingIndex = -1; - for( Int i = 0; i < GlobalData::MAX_WATER_GRID_SETTINGS; i++ ) - { - - if( TheGlobalData->m_mapName.compareNoCase( TheGlobalData->m_vertexWaterAvailableMaps[ i ].str() ) == 0 ) - { - - waterSettingIndex = i; - break; // exit for i - - } - - // - // no exact map name (including path) was found, try to look for a match in just the - // mapname.map without any path information. This is necessary for save/load due to - // the fact that the map Data\CHI01\CHI01.map will turn into Save\CHI01.map when - // loading the map from a save game file - // - AsciiString strippedMapNameOnly; - AsciiString strippedCompareMapNameOnly; - const char *c; - - // create stripped map name - c = strrchr( TheGlobalData->m_mapName.str(), '\\' ); - if( c ) - strippedMapNameOnly.set( c ); - else - strippedMapNameOnly = TheGlobalData->m_mapName; - - // create stripped compare name - c = strrchr( TheGlobalData->m_vertexWaterAvailableMaps[ i ].str(), '\\' ); - if( c ) - strippedCompareMapNameOnly.set( c ); - else - strippedCompareMapNameOnly = TheGlobalData->m_vertexWaterAvailableMaps[ i ]; - - // now try this compare - if( strippedMapNameOnly.compareNoCase( strippedCompareMapNameOnly.str() ) == 0 ) - { - - waterSettingIndex = i; - break; // exit for i - - } - - } - - // check for no match found - if( waterSettingIndex == -1 ) - { - - DEBUG_CRASH(( "!!!!!! Deformable water won't work because there was no group of vertex water data defined in GameData.INI for this map name '%s' !!!!!! (C. Day)", - TheGlobalData->m_mapName.str() )); - return; - - } - - TheTerrainVisual->setWaterGridHeightClamps( nullptr, - TheGlobalData->m_vertexWaterHeightClampLow[ waterSettingIndex ], - TheGlobalData->m_vertexWaterHeightClampHi[ waterSettingIndex ] ); - TheTerrainVisual->setWaterTransform( nullptr, - TheGlobalData->m_vertexWaterAngle[ waterSettingIndex ], - TheGlobalData->m_vertexWaterXPosition[ waterSettingIndex ], - TheGlobalData->m_vertexWaterYPosition[ waterSettingIndex ], - TheGlobalData->m_vertexWaterZPosition[ waterSettingIndex ] ); - TheTerrainVisual->setWaterGridResolution( nullptr, - TheGlobalData->m_vertexWaterXGridCells[ waterSettingIndex ], - TheGlobalData->m_vertexWaterYGridCells[ waterSettingIndex ], - TheGlobalData->m_vertexWaterGridSize[ waterSettingIndex ] ); - TheTerrainVisual->setWaterAttenuationFactors( nullptr, - TheGlobalData->m_vertexWaterAttenuationA[ waterSettingIndex ], - TheGlobalData->m_vertexWaterAttenuationB[ waterSettingIndex ], - TheGlobalData->m_vertexWaterAttenuationC[ waterSettingIndex ], - TheGlobalData->m_vertexWaterAttenuationRange[ waterSettingIndex ] ); - - } - - // notify the terrain visual of the change - TheTerrainVisual->enableWaterGrid( enable ); - -} - -//------------------------------------------------------------------------------------------------- -/** device independent terrain logic load. If query is true, we are just loading it to get -look at some data rather than running a game, so don't pass this load to the client. */ -//------------------------------------------------------------------------------------------------- -Bool TerrainLogic::loadMap( AsciiString filename, Bool query ) -{ - - // sanity - if( filename.isEmpty() ) - return FALSE; - - // copy filename - m_filenameString = filename; - - // Add waypoint objects. - MapObject *pObj; - for (pObj = MapObject::getFirstMapObject(); pObj; pObj = pObj->getNext()) { - if (pObj->isWaypoint()) { - addWaypoint(pObj); - } - } - - CachedFileInputStream theInputStream; - if (theInputStream.open(AsciiString(m_filenameString.str()))) - try { - ChunkInputStream *pStrm = &theInputStream; - pStrm->absoluteSeek(0); - DataChunkInput file( pStrm ); - if (file.isValidFileType()) { // Backwards compatible files aren't valid data chunk files. - // Read the waypoints. - file.registerParser( "WaypointsList", AsciiString::TheEmptyString, parseWaypointDataChunk ); - if (!file.parse(this)) { - DEBUG_CRASH(("Unable to read waypoint info.")); - return false; - } - } - theInputStream.close(); - } catch (...) { - // Eat the error - legacy files are not valid chunk format (and don't have waypoint info.) - DEBUG_LOG(("Unable to read waypoint info.")); - } -#if 0 //def DEBUG_LOGGING - // Dump out the waypoint links. - Waypoint *pWay; - // Traverse all waypoints. - int count = 0; - for (pWay = getFirstWaypoint(); pWay; pWay = pWay->getNext()) { - count++; - Coord3D loc; - pWay->getLocation(&loc); - DEBUG_LOG_RAW(("Waypoint %d - '%s' id=%d ", count, pWay->getName().str(), pWay->getID())); - DEBUG_LOG_RAW(("{%.2f, %.2f, %.2f} ", loc.x, loc.y, loc.z)); - Int i; - if (pWay->getNumLinks()) { - DEBUG_LOG_RAW(("Links to: ")); - for (i=0; igetNumLinks(); i++) { - Waypoint *pLink = pWay->getLink(i); - DEBUG_LOG_RAW(("'%s' id=%d ", pLink->getName().str(), pLink->getID())); - } - } else { - DEBUG_LOG_RAW(("No links.")); - } - DEBUG_LOG_RAW(("\n")); - } - DEBUG_LOG(("Total of %d waypoints.", count)); -#endif - - if (!query) { - // tell the game interface a new terrain file has been loaded up - TheTerrainVisual->load( getSourceFilename() ); - } - - return TRUE; // success - -} - -//------------------------------------------------------------------------------------------------- -/** Reads in the waypoint chunk */ -//------------------------------------------------------------------------------------------------- -Bool TerrainLogic::parseWaypointDataChunk(DataChunkInput &file, DataChunkInfo *info, void *userData) -{ - TerrainLogic *pThis = (TerrainLogic *)userData; - return pThis->parseWaypointData(file, info, userData); -} - -//------------------------------------------------------------------------------------------------- -/** Reads in the waypoint chunk */ -//------------------------------------------------------------------------------------------------- -Bool TerrainLogic::parseWaypointData(DataChunkInput &file, DataChunkInfo *info, void *userData) -{ - Int numWaypointLinks = file.readInt(); - Int i; - for (i=0; igetLocation(); - // Snap the waypoint down to the terrain. - loc.z = getGroundHeight(loc.x, loc.y); - Bool exists; - AsciiString label1, label2, label3; - label1 = pMapObj->getProperties()->getAsciiString(TheKey_waypointPathLabel1, &exists); - label2 = pMapObj->getProperties()->getAsciiString(TheKey_waypointPathLabel2, &exists); - label3 = pMapObj->getProperties()->getAsciiString(TheKey_waypointPathLabel3, &exists); - Bool biDirectional; - biDirectional = pMapObj->getProperties()->getBool(TheKey_waypointPathBiDirectional, &exists); - DEBUG_ASSERTCRASH(pMapObj->isWaypoint(), ("not a waypoint")); - Waypoint *pWay = newInstance(Waypoint)(pMapObj->getWaypointID(), pMapObj->getWaypointName(), - &loc, label1, label2, label3, biDirectional); - pWay->setNext(m_waypointListHead); - m_waypointListHead = pWay; -} - -//------------------------------------------------------------------------------------------------- -/** Links 2 waypoints. */ -//------------------------------------------------------------------------------------------------- -void TerrainLogic::addWaypointLink(Int id1, Int id2) -{ - Waypoint *pWay1 = nullptr; - Waypoint *pWay2 = nullptr; - Waypoint *pWay; - // Traverse all waypoints. - /// @todo ID's should be UnsignedInts (MSB) - for (pWay = getFirstWaypoint(); pWay; pWay = pWay->getNext()) { - if (pWay->getID() == (UnsignedInt)id1) { - pWay1 = pWay; - } - if (pWay->getID() == (UnsignedInt)id2) { - pWay2 = pWay; - } - } - if (pWay1 && pWay2 && (pWay1 != pWay2)) { - Int i; - for (i=0; igetNumLinks(); i++) { - if (pWay1->getLink(i) == pWay2) { - return; // already linked; - } - } - pWay1->addLink(pWay2); - - if (pWay1->getBiDirectional()) { - // Link the other way. - for (i=0; igetNumLinks(); i++) { - if (pWay2->getLink(i) == pWay1) { - return; // already linked; - } - } - pWay2->addLink(pWay1); - } - } -} - -//------------------------------------------------------------------------------------------------- -/** Deletes the waypoints list. */ -//------------------------------------------------------------------------------------------------- -void TerrainLogic::deleteWaypoints() -{ - Waypoint *pNext = nullptr; - Waypoint *pWay; - // Traverse all waypoints. - for (pWay = getFirstWaypoint(); pWay; pWay = pNext) { - pNext = pWay->getNext(); - pWay->setNext(nullptr); - deleteInstance(pWay); - } - m_waypointListHead = nullptr; -} - -//------------------------------------------------------------------------------------------------- -Bool TerrainLogic::isClearLineOfSight(const Coord3D& pos, const Coord3D& posOther) const -{ - DEBUG_CRASH(("implement ME")); - return false; -} - -//------------------------------------------------------------------------------------------------- -/** default get height for terrain logic */ -//------------------------------------------------------------------------------------------------- -Real TerrainLogic::getGroundHeight( Real x, Real y, Coord3D* normal ) const -{ - if( normal ) - normal->zero(); - - return 0; - -} - -//------------------------------------------------------------------------------------------------- -/** default get height for terrain logic */ -//------------------------------------------------------------------------------------------------- -Real TerrainLogic::getLayerHeight( Real x, Real y, PathfindLayerEnum layer, Coord3D* normal, Bool clip ) const -{ - if( normal ) - normal->zero(); - - return 0; - -} - -//------------------------------------------------------------------------------------------------- -/** default isCliffCell for terrain logic */ -//------------------------------------------------------------------------------------------------- -Bool TerrainLogic::isCliffCell( Real x, Real y) const -{ - - return false; - -} - -//------------------------------------------------------------------------------------------------- -void makeAlignToNormalMatrix( Real angle, const Coord3D& pos, const Coord3D& normal, Matrix3D& mtx) -{ - Coord3D x, y, z; - - z = normal; - - /* - It is extremely important that the resulting matrix is such that - the xvector points in the angle we specified; specifically, - that atan2(xvec.y, xvec.x) == angle. So we must construct - the matrix carefully to ensure this! - */ - x.x = Cos( angle ); - x.y = Sin( angle ); - x.z = 0.0f; -//x.normalize(); -- redundant; is normalized by definition - - // dot of two unit vectors is cos of angle between them; - // we want there to be a 90-deg angle between the x and z - // vectors, so calc x.z to satisfy this (ie, cos==0) - /* - xx*zx + xy*zy + xz*zz = 0 - xz = (-xx*zz - xy*zy)/zz - */ - if (z.z != 0.0f) - { - x.z = -(x.x*z.x + x.y*z.y) / z.z; - x.normalize(); - } - - DEBUG_ASSERTCRASH(fabs(x.x*z.x + x.y*z.y + x.z*z.z)<0.0001,("dot is not zero (%f)",fabs(x.x*z.x + x.y*z.y + x.z*z.z))); - - // now computing the y vector is trivial. - y.crossProduct( z, x, y ); - y.normalize(); - - mtx.Set( x.x, y.x, z.x, pos.x, - x.y, y.y, z.y, pos.y, - x.z, y.z, z.z, pos.z ); -} - -//------------------------------------------------------------------------------------------------- -/** given angle and position, return the matrix aligning this - * position with the ground */ -//------------------------------------------------------------------------------------------------- -PathfindLayerEnum TerrainLogic::alignOnTerrain( Real angle, const Coord3D& pos, Bool stickToGround, Matrix3D& mtx) -{ - Coord3D terrainNormal; - PathfindLayerEnum layer; - - layer = getLayerForDestination(&pos); - - // get the normal of the terrain at our position - Real terrainAtPos = getLayerHeight(pos.x, pos.y, layer, &terrainNormal ); - if (layer != LAYER_GROUND) { - /// @todo - fix brutal hack for bridges that are too high. jba - terrainAtPos += 2.5f; - } - makeAlignToNormalMatrix(angle, pos, terrainNormal, mtx); - if (stickToGround) - mtx.Set_Z_Translation(terrainAtPos); - - return layer; -} - -//------------------------------------------------------------------------------------------------- -/** Adds a bridge's info get height function for logical terrain */ -//------------------------------------------------------------------------------------------------- -void TerrainLogic::addBridgeToLogic(BridgeInfo *pInfo, Dict *props, AsciiString bridgeTemplateName) -{ - Bridge *pBridge = newInstance(Bridge)(*pInfo, props, bridgeTemplateName); - pBridge->setNext(m_bridgeListHead); - m_bridgeListHead = pBridge; - PathfindLayerEnum layer = TheAI->pathfinder()->addBridge(pBridge); - pBridge->setLayer(layer); - - if (TheTacticalView) { - TheTacticalView->onBridgeChanged(); - } -} - -//------------------------------------------------------------------------------------------------- -/** Adds a bridge's info get height function for logical terrain */ -//------------------------------------------------------------------------------------------------- -void TerrainLogic::addLandmarkBridgeToLogic(Object *bridgeObj) -{ - Bridge *pBridge = newInstance(Bridge)(bridgeObj); - pBridge->setNext(m_bridgeListHead); - m_bridgeListHead = pBridge; - PathfindLayerEnum layer = TheAI->pathfinder()->addBridge(pBridge); - pBridge->setLayer(layer); - - if (TheTacticalView) { - TheTacticalView->onBridgeChanged(); - } -} - -//------------------------------------------------------------------------------------------------- -/** Given a name, return the associated waypoint. */ -//------------------------------------------------------------------------------------------------- -Waypoint *TerrainLogic::getWaypointByName( AsciiString name ) -{ - for( Waypoint *way = m_waypointListHead; way; way = way->getNext() ) - if (way->getName() == name) - return way; - - return nullptr; -} - -//------------------------------------------------------------------------------------------------- -/** Given a unique integer ID, return the associated waypoint. */ -//------------------------------------------------------------------------------------------------- -Waypoint *TerrainLogic::getWaypointByID( UnsignedInt id ) -{ - for( Waypoint *way = m_waypointListHead; way; way = way->getNext() ) - if (way->getID() == id) - return way; - - return nullptr; -} - -//------------------------------------------------------------------------------------------------- -/** Return the closest waypoint on the labeled path. */ -//------------------------------------------------------------------------------------------------- -Waypoint *TerrainLogic::getClosestWaypointOnPath( const Coord3D *pos, AsciiString label ) -{ - Real distSqr = 0; - Waypoint *pClosestWay = nullptr; - if (label.isEmpty()) { - DEBUG_LOG(("***Warning - asking for empty path label.")); - return nullptr; - } - - for( Waypoint *way = m_waypointListHead; way; way = way->getNext() ) { - Bool match = false; - if (label.compareNoCase(way->getPathLabel1())==0) match = true; - if (label.compareNoCase(way->getPathLabel2())==0) match = true; - if (label.compareNoCase(way->getPathLabel3())==0) match = true; - if (match) { - Coord3D curPos = *way->getLocation(); - Real newDistSqr = (curPos.x-pos->x)*(curPos.x-pos->x) + (curPos.y-pos->y)*(curPos.y-pos->y); - if (pClosestWay==nullptr) { - pClosestWay = way; - distSqr = newDistSqr; - } else if (newDistSqr < distSqr) { - pClosestWay = way; - distSqr = newDistSqr; - } - } - } - - return pClosestWay; -} - -//------------------------------------------------------------------------------------------------- -/** Return true if the waypoint path containing pWay is labeled with the label. */ -//------------------------------------------------------------------------------------------------- -Bool TerrainLogic::isPurposeOfPath( Waypoint *pWay, AsciiString label ) -{ - if (label.isEmpty() || pWay==nullptr) { - DEBUG_LOG(("***Warning - asking for empth path label.")); - return false; - } - - Bool match = false; - if (label == pWay->getPathLabel1()) match = true; - if (label == pWay->getPathLabel2()) match = true; - if (label == pWay->getPathLabel3()) match = true; - - return match; -} - - -//------------------------------------------------------------------------------------------------- -/** Given a name, return the associated trigger area, or null if one doesn't exist. */ -//------------------------------------------------------------------------------------------------- -PolygonTrigger *TerrainLogic::getTriggerAreaByName( AsciiString name ) -{ - for (PolygonTrigger* pTrig = PolygonTrigger::getFirstPolygonTrigger(); pTrig; pTrig = pTrig->getNext()) { - const AsciiString& trigName = pTrig->getTriggerName(); - if (name == trigName) - return pTrig; - } - return nullptr; -} - - -//------------------------------------------------------------------------------------------------- -/** Finds the bridge at a given x/y coordinate. */ -//------------------------------------------------------------------------------------------------- -Bridge * TerrainLogic::findBridgeAt( const Coord3D *pLoc) const -{ - - Bridge *pBridge = getFirstBridge(); - while (pBridge) { - if (pBridge->isPointOnBridge(pLoc)) { - return(pBridge); - } - pBridge = pBridge->getNext(); - } - return(nullptr); -} - -//------------------------------------------------------------------------------------------------- -/** Finds the bridge at a given x/y coordinate. On a layer. */ -//------------------------------------------------------------------------------------------------- -Bridge * TerrainLogic::findBridgeLayerAt( const Coord3D *pLoc, PathfindLayerEnum layer, Bool clip) const -{ - if (layer == LAYER_GROUND) - return nullptr; - - Bridge *pBridge = getFirstBridge(); - while (pBridge) - { - if (pBridge->getLayer() == layer && (!clip || pBridge->isPointOnBridge(pLoc))) - { - return(pBridge); - } - pBridge = pBridge->getNext(); - } - return(nullptr); -} - -//------------------------------------------------------------------------------------------------- -/** Returns the layer id for the bridge, if any, at this destination. Otherwisee -return LAYER_GROUND. */ -//------------------------------------------------------------------------------------------------- -PathfindLayerEnum TerrainLogic::getLayerForDestination(const Coord3D *pos) -{ - Bridge *pBridge = getFirstBridge(); - PathfindLayerEnum bestLayer = LAYER_GROUND; - Real bestDistance = fabs(pos->z - getGroundHeight(pos->x, pos->y)); - - if (bestDistance > TheAI->pathfinder()->getWallHeight()/2) { - // check wall. - if (TheAI->pathfinder()->isPointOnWall(pos)) { - Real delta = fabs(pos->z-TheAI->pathfinder()->getWallHeight()); - if (deltaisPointOnBridge(pos) ) { - Real delta = fabs(pos->z-pBridge->getBridgeHeight(pos, nullptr)); - if (deltagetLayer(); - bestDistance = delta; - } - } - pBridge = pBridge->getNext(); - } - return(bestLayer); -} - -//------------------------------------------------------------------------------------------------- -// 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 TerrainLogic::getHighestLayerForDestination(const Coord3D *pos, Bool onlyHealthyBridges) -{ - PathfindLayerEnum bestLayer = LAYER_GROUND; - Real bestDistance = pos->z - getGroundHeight(pos->x, pos->y); // NOT fabs in this case. - - if (bestDistance > TheAI->pathfinder()->getWallHeight()/2) { - // check wall. - if (TheAI->pathfinder()->isPointOnWall(pos)) { - Real delta = pos->z - TheAI->pathfinder()->getWallHeight(); - // must be ABOVE (or on) the wall for this call. (srj) - if (delta >= 0 && fabs(delta) < fabs(bestDistance)) { - bestLayer = (PathfindLayerEnum)LAYER_WALL; - bestDistance = delta; - } - } - } - - for (Bridge *pBridge = getFirstBridge(); pBridge != nullptr; pBridge = pBridge->getNext()) { - - if (onlyHealthyBridges && pBridge->peekBridgeInfo()->curDamageState == BODY_RUBBLE) - continue; - - if (pBridge->isPointOnBridge(pos) ) { - Real delta = pos->z - pBridge->getBridgeHeight(pos, nullptr); - // must be ABOVE (or on) the bridge for this call. (srj) - if (delta >= 0 && fabs(delta) < fabs(bestDistance)) { - bestLayer = pBridge->getLayer(); - bestDistance = delta; - } - } - } - return(bestLayer); -} - -//------------------------------------------------------------------------------------------------- -/** Determines whether the object interacts with the bridge on specified layer. */ -//------------------------------------------------------------------------------------------------- -Bool TerrainLogic::objectInteractsWithBridgeLayer(Object *obj, Int layer, Bool considerBridgeHealth) const -{ - if (layer == LAYER_GROUND) return false; - if (layer == LAYER_WALL) { - if (obj->getLayer() == LAYER_WALL) { - return true; // objects on the wall can't fall off :) - } - if (TheAI->pathfinder()->isPointOnWall(obj->getPosition())) { - return true; - } - return false; - } - Bridge *pBridge = getFirstBridge(); - - while (pBridge ) { - if (pBridge->getLayer() == layer) { - Bool match = false; - if (pBridge->isPointOnBridge(obj->getPosition()) ) { - match = true; - } - - Real radius = obj->getGeometryInfo().getMinorRadius(); - radius += PATHFIND_CELL_SIZE_F/2.0f; - Region2D bounds; - bounds.lo.x = obj->getPosition()->x; - bounds.lo.y = obj->getPosition()->y; - bounds.hi = bounds.lo; - bounds.lo.x -= radius; - bounds.lo.y -= radius; - bounds.hi.x += radius; - bounds.hi.y += radius; - if (pBridge->isCellOnEnd(&bounds)) { - match = true; - } - - if (match) { - Real bridgeHeight = pBridge->getBridgeHeight(obj->getPosition(), nullptr); - Real delta = fabs(obj->getPosition()->z-bridgeHeight); - if (delta>LAYER_Z_CLOSE_ENOUGH_F) { - return false; - } - - // make sure it's not destroyed. can't interact with dead bridges. - if (considerBridgeHealth && pBridge->peekBridgeInfo()->curDamageState == BODY_RUBBLE) - { - return false; - } - - return true; - } - return false; - } - - pBridge = pBridge->getNext(); - } - return(false); -} - -//------------------------------------------------------------------------------------------------- -/** Determines whether the object interacts with the bridge on specified layer. */ -//------------------------------------------------------------------------------------------------- -Bool TerrainLogic::objectInteractsWithBridgeEnd(Object *obj, Int layer) const -{ - if (layer == LAYER_GROUND) return false; - Bridge *pBridge = getFirstBridge(); - - while (pBridge ) { - if (pBridge->getLayer() == layer) { - Bool match = false; - - Real radius = obj->getGeometryInfo().getMinorRadius(); - radius += PATHFIND_CELL_SIZE_F/2.0f; - Region2D bounds; - bounds.lo.x = obj->getPosition()->x; - bounds.lo.y = obj->getPosition()->y; - bounds.hi = bounds.lo; - bounds.lo.x -= radius; - bounds.lo.y -= radius; - bounds.hi.x += radius; - bounds.hi.y += radius; - if (pBridge->isCellOnEnd(&bounds)) { - match = true; - } - - if (match) { - Real bridgeHeight = pBridge->getBridgeHeight(obj->getPosition(), nullptr); - Real delta = fabs(obj->getPosition()->z-bridgeHeight); - if (delta>LAYER_Z_CLOSE_ENOUGH_F) - { - return false; - } - return true; - } - return false; - } - - pBridge = pBridge->getNext(); - } - return(false); -} - -//------------------------------------------------------------------------------------------------- -/** Updates the damage state of the bridge from the logic. */ -//------------------------------------------------------------------------------------------------- -void TerrainLogic::updateBridgeDamageStates() -{ - Bridge *pBridge = getFirstBridge(); - while (pBridge) { - pBridge->updateDamageState(); - pBridge = pBridge->getNext(); - } - m_bridgeDamageStatesChanged = true; - if (TheTacticalView) { - TheTacticalView->onBridgeChanged(); - } -} - -//------------------------------------------------------------------------------------------------- -/** Checks if a bridge is repaired. */ -//------------------------------------------------------------------------------------------------- -Bool TerrainLogic::isBridgeRepaired(const Object *bridge) -{ - if (!bridge) return false; - ObjectID id = bridge->getID(); - Bridge *pBridge = getFirstBridge(); - while (pBridge) { - const BridgeInfo *info = pBridge->peekBridgeInfo(); - if (info->bridgeObjectID == id) { - // found the right bridge. - if (info->damageStateChanged) { - // Damage state just changed. - if (info->curDamageState != BODY_RUBBLE) { - return true; - } - } - return false; - } - pBridge = pBridge->getNext(); - } - return false; -} - -//------------------------------------------------------------------------------------------------- -/** Checks if a bridge is broken. */ -//------------------------------------------------------------------------------------------------- -Bool TerrainLogic::isBridgeBroken( const Object *bridge ) -{ - if (!bridge) return false; - ObjectID id = bridge->getID(); - Bridge *pBridge = getFirstBridge(); - while (pBridge) { - const BridgeInfo *info = pBridge->peekBridgeInfo(); - if (info->bridgeObjectID == id) { - // found the right bridge. - if (info->damageStateChanged) { - // Damage state just changed. - if (info->curDamageState == BODY_RUBBLE) { - return true; - } - } - return false; - } - pBridge = pBridge->getNext(); - } - return false; -} - -//------------------------------------------------------------------------------------------------- -/** Gets the attack points for a bridge. */ -//------------------------------------------------------------------------------------------------- -void TerrainLogic::getBridgeAttackPoints(const Object *bridge, TBridgeAttackInfo *attackInfo) -{ - ObjectID id = bridge->getID(); - Bridge *pBridge = getFirstBridge(); - while (pBridge) { - const BridgeInfo *info = pBridge->peekBridgeInfo(); - if (info->bridgeObjectID == id) { - // found the right bridge. - Coord3D delta; - delta.x = info->to.x - info->from.x; - delta.y = info->to.y - info->from.y; - delta.z = info->to.z - info->from.z; - delta.normalize(); - Coord3D width; - width.x = info->fromRight.x - info->fromLeft.x; - width.y = info->fromRight.y - info->fromLeft.y; - width.z = info->fromRight.z - info->fromLeft.z; - Real len = width.length(); - len /= 2.0f; - attackInfo->attackPoint1.x = info->from.x + delta.x*len; - attackInfo->attackPoint1.y = info->from.y + delta.y*len; - attackInfo->attackPoint1.z = info->from.z + delta.z*len; - - attackInfo->attackPoint2.x = info->to.x - delta.x*len; - attackInfo->attackPoint2.y = info->to.y - delta.y*len; - attackInfo->attackPoint2.z = info->to.z - delta.z*len; - - return; - } - pBridge = pBridge->getNext(); - } - attackInfo->attackPoint1 = *bridge->getPosition(); - attackInfo->attackPoint2 = *bridge->getPosition(); -} - -//------------------------------------------------------------------------------------------------- -/** Picks a bridge, and returns it's drawable. */ -//------------------------------------------------------------------------------------------------- -Drawable *TerrainLogic::pickBridge(const Vector3 &from, const Vector3 &to, Vector3 *pos) -{ - Drawable *curDraw = nullptr; - Vector3 curPos(0,0,0); - - Bridge *pBridge = getFirstBridge(); - while (pBridge) { - Vector3 thisPos; - Drawable *thisDraw = pBridge->pickBridge(from, to , &thisPos); - if (!curDraw) { - curDraw = thisDraw; - curPos = thisPos; - } - pBridge = pBridge->getNext(); - } - *pos = curPos; - return(curDraw); -} - -//------------------------------------------------------------------------------------------------- -/** Deletes the bridges list. */ -//------------------------------------------------------------------------------------------------- -void TerrainLogic::deleteBridges() -{ - Bool bridgesChanged = m_bridgeListHead != nullptr; - - Bridge *pNext = nullptr; - Bridge *pBridge; - // Traverse all waypoints. - for (pBridge = getFirstBridge(); pBridge; pBridge = pNext) { - pNext = pBridge->getNext(); - pBridge->setNext(nullptr); - deleteInstance(pBridge); - } - m_bridgeListHead = nullptr; - - if (bridgesChanged && TheTacticalView) { - TheTacticalView->onBridgeChanged(); - } -} - -//------------------------------------------------------------------------------------------------- -/** Delete the bridge specified */ -//------------------------------------------------------------------------------------------------- -void TerrainLogic::deleteBridge( Bridge *bridge ) -{ - - // sanity - if( bridge == nullptr ) - return; - - // check for removing the head - if( m_bridgeListHead == bridge ) - { - - m_bridgeListHead = bridge->getNext(); - - } - else - { - - for( Bridge *otherBridge = getFirstBridge(); - otherBridge; - otherBridge = otherBridge->getNext() ) - { - - // - // if the next bridge is the one in question to delete, set this bridge to point - // to the next pointer of the bridge we are deleting - // - if( otherBridge->getNext() == bridge ) - { - - otherBridge->setNext( bridge->getNext() ); - break; // exit for - - } - - } - - } - - // delete object associated with bridge if present - BridgeInfo bridgeInfo; - bridge->getBridgeInfo( &bridgeInfo ); - TheAI->pathfinder()->changeBridgeState(bridge->getLayer(), false); - - Object *bridgeObj = TheGameLogic->findObjectByID( bridgeInfo.bridgeObjectID ); - if( bridgeObj ) - TheGameLogic->destroyObject( bridgeObj ); - - // delete the bridge in question - deleteInstance(bridge); - - if (TheTacticalView) { - TheTacticalView->onBridgeChanged(); - } -} - -//------------------------------------------------------------------------------------------------- -/** Returns the ground aligned point on the bounding box closest to the given point*/ -//------------------------------------------------------------------------------------------------- -Coord3D TerrainLogic::findClosestEdgePoint ( const Coord3D *closestTo ) const -{ - Region3D mapExtent; - getExtent( &mapExtent ); - - Real distances[4]; - distances[0] = fabs( closestTo->y - mapExtent.lo.y );//top - distances[1] = fabs( closestTo->x - mapExtent.hi.x );//right - distances[2] = fabs( closestTo->y - mapExtent.hi.y );//bottom - distances[3] = fabs( closestTo->x - mapExtent.lo.x );//left - Real bestDistance = distances[0]; - Int bestDistanceIndex = 0; - for( Int lameIndex = 1; lameIndex < 4; lameIndex++ ) - { - if( distances[lameIndex] < bestDistance ) - { - bestDistance = distances[lameIndex]; - bestDistanceIndex = lameIndex; - } - } - - Coord3D retVal = *closestTo; - if( bestDistanceIndex == 0 ) - { - retVal.y = mapExtent.lo.y; - } - else if( bestDistanceIndex == 1 ) - { - retVal.x = mapExtent.hi.x; - } - else if( bestDistanceIndex == 2 ) - { - retVal.y = mapExtent.hi.y; - } - else - { - retVal.x = mapExtent.lo.x; - } - - retVal.z = getGroundHeight( retVal.x, retVal.y ); - - return retVal; - -} - - - - -//------------------------------------------------------------------------------------------------- -/** Returns the ground aligned point on the bounding box farthest from the given point*/ -//------------------------------------------------------------------------------------------------- -// Lorenzen was here -Coord3D TerrainLogic::findFarthestEdgePoint( const Coord3D *farthestFrom ) const -{ - Region3D mapExtent; - getExtent( &mapExtent ); - - Coord3D retVal = *farthestFrom; - - if (farthestFrom->x < (mapExtent.width()/2) ) - retVal.x = mapExtent.hi.x; - else - retVal.x = mapExtent.lo.x; - - if (farthestFrom->y < (mapExtent.height()/2) ) - retVal.y = mapExtent.hi.y; - else - retVal.y = mapExtent.lo.y; - - - retVal.z = getGroundHeight( retVal.x, retVal.y ); - - return retVal; - -} - - - - - -//------------------------------------------------------------------------------------------------- -/** See if a location is underwater, and what the water height is. */ -//------------------------------------------------------------------------------------------------- -Bool TerrainLogic::isUnderwater( Real x, Real y, Real *waterZ, Real *terrainZ ) -{ - - // get the water handle at this location - const WaterHandle *waterHandle = getWaterHandle( x, y ); - - // if no water here, no height, no nuttin - if( waterHandle == nullptr ) - { - // but we have to return the terrain Z if requested! - if (terrainZ) - *terrainZ=getGroundHeight(x,y); - return FALSE; - } - - // - // if this water handle is a grid water use the grid height function, otherwise look into - // the polygon trigger - // - Real wZ = 0.0f; - if( waterHandle == &m_gridWaterHandle ) - TheTerrainVisual->getWaterGridHeight( x, y, &wZ ); - else - wZ = getWaterHeight( waterHandle ); - - // fill out the waterZ parameter with the water height - if( waterZ ) - *waterZ = wZ; - - // see if the terrain height here is below the water - Real terrainHeight = getGroundHeight( x, y ); - if (terrainZ) - *terrainZ = terrainHeight; - - return terrainHeight < wZ; - -} - -// ------------------------------------------------------------------------------------------------ -/** Get the water table with the highest water Z value at the location */ -// ------------------------------------------------------------------------------------------------ -const WaterHandle* TerrainLogic::getWaterHandle( Real x, Real y ) -{ - const WaterHandle *waterHandle = nullptr; - Real waterZ = 0.0f; - ICoord3D iLoc; - - iLoc.x = REAL_TO_INT_FLOOR( x + 0.5f ); - iLoc.y = REAL_TO_INT_FLOOR( y + 0.5f ); - iLoc.z = 0; - - // Look for water areas in the polygon triggers - for( PolygonTrigger *pTrig = PolygonTrigger::getFirstPolygonTrigger(); - pTrig; - pTrig = pTrig->getNext() ) - { - - if( !pTrig->isWaterArea() ) - continue; - - // See if point is in a water area - if( pTrig->pointInTrigger( iLoc ) ) - { - - if( pTrig->getPoint( 0 )->z >= waterZ ) - { - - waterZ = pTrig->getPoint( 0 )->z; - waterHandle = pTrig->getWaterHandle(); - - } - - } - - } - - /**@todo: Remove this after we have all water types included - in water triggers. For now do special check for water grid mesh. */ - // TheSuperHackers @logic-client-separation helmutbuhler 11/04/2025 - // We shouldn't depend on TerrainVisual here. - Real meshZ; - if( TheTerrainVisual->getWaterGridHeight( x, y, &meshZ ) ) - { - - // - // point falls on water grid, return the special handle for the grid water, since we - // only have one of them and don't yet support multiple gridded water sections - // - if( meshZ >= waterZ ) - { - - waterZ = meshZ; - waterHandle = &m_gridWaterHandle; - - } - - } - - return waterHandle; - -} - -// ------------------------------------------------------------------------------------------------ -/** Get water handle by name assigned from the editor */ -// ------------------------------------------------------------------------------------------------ -const WaterHandle* TerrainLogic::getWaterHandleByName( AsciiString name ) -{ - if (name.compare(WATER_GRID) == 0) - return &TerrainLogic::m_gridWaterHandle; - - PolygonTrigger *trig = PolygonTrigger::getFirstPolygonTrigger(); - while (trig) - { - if (trig->getTriggerName().compare(name) == 0 && trig->isWaterArea()) - return trig->getWaterHandle(); - trig = trig->getNext(); - } - - return nullptr; - -} - -// ------------------------------------------------------------------------------------------------ -// ------------------------------------------------------------------------------------------------ -Real TerrainLogic::getWaterHeight( const WaterHandle *water ) -{ - - // sanity - if( water == nullptr ) - return 0.0f; - - // - // when querying the water height given a handle, we cannot query gridded water in this - // way because it's variable across the whole surface of the water - // - if( water == &m_gridWaterHandle ) - { - - DEBUG_CRASH(( "TerrainLogic::getWaterHeight( WaterHandle *water ) - water is a grid handle, cannot make this query" )); - return 0.0f; - - } - - // sanity - DEBUG_ASSERTCRASH( water->m_polygon != nullptr, ("getWaterHeight: polygon trigger in water handle is null") ); - - // return the height of the water using the polygon trigger - return water->m_polygon->getPoint( 0 )->z; - -} - -// ------------------------------------------------------------------------------------------------ -/** Set the water height. If the water rises, then any objects that now find themselves - * underwater will be damaged by the amount provided in the parameter 'damageAmount' */ -// ------------------------------------------------------------------------------------------------ -void TerrainLogic::setWaterHeight( const WaterHandle *water, Real height, Real damageAmount, - Bool forcePathfindUpdate ) -{ - - // sanity - if( water == nullptr ) - return; - - // - // if this is a handle to gridded water simple change the transform to raise/lower the whole - // water table. Note: The other meaning this *could* have if we want it to is to leave - // the water table transform where it is and actually change the height of the water at - // every grid point - // - Real previousHeight = 0.0f; - if( water == &m_gridWaterHandle ) - { - - // get transform information - Matrix3D transform; - TheTerrainVisual->getWaterTransform( water, &transform ); - - // save the old height - previousHeight = transform.Get_Z_Translation(); - - // set the new height - transform.Set_Z_Translation( height ); - TheTerrainVisual->setWaterTransform( &transform ); - - } - else - { - - // save the previous height - previousHeight = getWaterHeight( water ); - - // set the new height at all the points in the polygon trigger - const ICoord3D *p; - ICoord3D newPoint; - Int numPoints = water->m_polygon->getNumPoints(); - for( Int i = 0; i < numPoints; ++i ) - { - - p = water->m_polygon->getPoint( i ); - newPoint.x = p->x; - newPoint.y = p->y; - newPoint.z = height; - water->m_polygon->setPoint( newPoint, i ); - - } - height = getWaterHeight(water); - - } - - // find the bounding rectangle of this water area - Region3D affectedRegion; - affectedRegion.zero(); - findAxisAlignedBoundingRect( water, &affectedRegion ); - - // changes in the water level force us to recalculate the pathfinding map - if( forcePathfindUpdate || previousHeight != height ) - { - - // do the pathfind remapping - TheAI->pathfinder()->forceMapRecalculation(); - - } - - // - // if the water height has risen, we need apply water damage to things that are now - // under the water - // - if( damageAmount > 0.0f && height > previousHeight ) - { - - // find the center of the water "area" given the bounding region - Coord3D center; - center.x = affectedRegion.lo.x + affectedRegion.width() / 2.0f; - center.y = affectedRegion.lo.y + affectedRegion.height() / 2.0f; - center.z = 0.0f; // irrelevant - - // the max radius to scan around us is the diagonal of the bounding region - Real maxDist = sqrt( affectedRegion.width() * affectedRegion.width() + - affectedRegion.height() * affectedRegion.height() ); - - // scan the objects in the area of the water affected - ObjectIterator *iter = ThePartitionManager->iterateObjectsInRange( ¢er, - maxDist, - FROM_CENTER_2D, - nullptr ); - MemoryPoolObjectHolder hold( iter ); - Object *obj; - const Coord3D *objPos; - for( obj = iter->first(); obj; obj = iter->next() ) - { - - // get other object position - objPos = obj->getPosition(); - - // if this object is underwater, do some damage - if( isUnderwater( objPos->x, objPos->y ) ) - { - - // do a lot of water damage - DamageInfo damageInfo; - damageInfo.in.m_damageType = DAMAGE_WATER; - damageInfo.in.m_deathType = DEATH_NORMAL; - damageInfo.in.m_sourceID = INVALID_ID; - damageInfo.in.m_amount = damageAmount; - obj->attemptDamage( &damageInfo ); - - } - - } - - } - -} - -// ------------------------------------------------------------------------------------------------ -/** Change the height of a water table over time */ -// ------------------------------------------------------------------------------------------------ -void TerrainLogic::changeWaterHeightOverTime( const WaterHandle *water, - Real finalHeight, - Real transitionTimeInSeconds, - Real damageAmount ) -{ - - // if we don't have room, oops! - if( m_numWaterToUpdate >= MAX_DYNAMIC_WATER ) - { - - DEBUG_CRASH(( "Only '%d' simultaneous water table changes are supported", MAX_DYNAMIC_WATER )); - return; - - } - - // sanity - if( water == nullptr ) - return; - - // if this water table already has an entry in the array to update, remove it - for( Int i = 0; i < m_numWaterToUpdate; i++ ) - { - - if( m_waterToUpdate[ i ].waterTable == water ) - { - - // put the entry at the end of the list here - m_waterToUpdate[ i ] = m_waterToUpdate[ m_numWaterToUpdate - 1 ]; - - // we now have one less entry - --m_numWaterToUpdate; - - // - // process this index over again just to be complete, but we should never find "another" - // duplicate water entry - // - --i; - - } - - } - - // get the current height of the water - Real currentHeight = getWaterHeight( water ); - - // add the entry into the array of water to update - m_waterToUpdate[ m_numWaterToUpdate ].waterTable = water; - m_waterToUpdate[ m_numWaterToUpdate ].changePerFrame = (finalHeight - currentHeight) / - (LOGICFRAMES_PER_SECOND * transitionTimeInSeconds); - m_waterToUpdate[ m_numWaterToUpdate ].targetHeight = finalHeight; - m_waterToUpdate[ m_numWaterToUpdate ].damageAmount = damageAmount; - m_waterToUpdate[ m_numWaterToUpdate ].currentHeight = currentHeight; - - // we now have one more entry to update - ++m_numWaterToUpdate; - -} - -// ------------------------------------------------------------------------------------------------ -/** Find the axis aligned bounding region around a water table */ -// ------------------------------------------------------------------------------------------------ -void TerrainLogic::findAxisAlignedBoundingRect( const WaterHandle *water, Region3D *region ) -{ - - // sanity - if( water == nullptr || region == nullptr ) - return; - - // setup the lo and high of the region to the *opposite* side of the map plus some big number - #define BUFFER 99999.9f /// just to have extreme regions outside of the map - Region3D mapExtent; - getExtent( &mapExtent ); - region->lo.x = mapExtent.hi.x + BUFFER; - region->lo.y = mapExtent.hi.y + BUFFER; - region->hi.x = mapExtent.lo.x - BUFFER; - region->hi.y = mapExtent.lo.y - BUFFER; - // for water grid we must access the transform - if( water == &m_gridWaterHandle ) - { - Int i; - ICoord3D p[ 4 ]; - - // compute the 4 corners of the table according to the grids and grid spacing - Real gridX, gridY, cellSize; - TheTerrainVisual->getWaterGridResolution( water, &gridX, &gridY, &cellSize ); - p[ 0 ].x = 0; - p[ 0 ].y = 0; - p[ 1 ].x = gridX * cellSize; - p[ 1 ].y = 0; - p[ 2 ].x = gridX * cellSize; - p[ 2 ].y = gridY * cellSize; - p[ 3 ].x = 0; - p[ 3 ].y = gridY * cellSize; - - // transform the 4 points using the transform matrix of the water - Vector3 v; - Matrix3D transform; - TheTerrainVisual->getWaterTransform( water, &transform ); - for( i = 0; i < 4; i++ ) - { - - v.Set( p[ i ].x, p[ i ].y, p[ i ].z ); - transform.Transform_Vector( transform, v, &v ); - - // do the region compares - if( v.X < region->lo.x ) - region->lo.x = v.X; - if( v.X > region->hi.x ) - region->hi.x = v.X; - if( v.Y < region->lo.y ) - region->lo.y = v.Y; - if( v.Y > region->hi.y ) - region->hi.y = v.Y; - - } - - } - else - { - - // go through each polygon point and find the extents - const ICoord3D *p; - Int numPoints = water->m_polygon->getNumPoints(); - for( Int i = 0; i < numPoints; i++ ) - { - - // get this point - p = water->m_polygon->getPoint( i ); - - // compare to our region - if( p->x < region->lo.x ) - region->lo.x = p->x; - if( p->x > region->hi.x ) - region->hi.x = p->x; - - if( p->y < region->lo.y ) - region->lo.y = p->y; - if( p->y > region->hi.y ) - region->hi.y = p->y; - - if( p->z < region->lo.z ) - region->lo.z = p->z; - if( p->z > region->hi.z ) - region->hi.z = p->z; - - } - - } - -} - -void TerrainLogic::setActiveBoundary(Int newActiveBoundary) -{ - if (newActiveBoundary < 0 || newActiveBoundary >= m_boundaries.size()) { - // probably should DEBUG_ASSERT here - return; - } - - if (newActiveBoundary == m_activeBoundary) { - // since this is fairly expensive (causes reset of PartitionManager as well as pathfinding), - // we should probably return - return; - } - - if (m_boundaries[newActiveBoundary].x == 0 || m_boundaries[newActiveBoundary].y == 0) { - return; - } - - ShroudStatusStoreRestore partitionStore; - - // Can't have any lingering looks persist over the resize, so flush the queue now - ThePartitionManager->processEntirePendingUndoShroudRevealQueue(); - - //Store fogged cells - ThePartitionManager->storeFoggedCells(partitionStore, TRUE); - - m_activeBoundary = newActiveBoundary; - - //Remove ghost objects from partition manager so that their partition data - //can be released when parent object detaches. - TheGhostObjectManager->releasePartitionData(); - - //Remove objects from partition manager so that any remaining cleared - //cells must be permanently revealed. - Object *obj = TheGameLogic->getFirstObject(); - while (obj) { - obj->friend_prepareForMapBoundaryAdjust(); - obj = obj->getNextObject(); - } - - //Store permanently revealed cells. - ThePartitionManager->storeFoggedCells(partitionStore, FALSE); - - ThePartitionManager->reset(); - ThePartitionManager->init(); - TheRadar->newMap(TheTerrainLogic); - - ThePartitionManager->restoreFoggedCells(partitionStore, FALSE); - - //Tell the ghost object manager to not allow creation/modification of - //ghost objects. This will prevent new ones from being recreated and allow - //us to restore the existing ones. - TheGhostObjectManager->lockGhostObjects(TRUE); - - obj = TheGameLogic->getFirstObject(); - while (obj) { - obj->friend_notifyOfNewMapBoundary(); - obj = obj->getNextObject(); - } - - ThePartitionManager->restoreFoggedCells(partitionStore, TRUE); - //reinsert ghost objects into the partition manager. - TheGhostObjectManager->restorePartitionData(); - - //Allow creation of new ghost objects since we restored all existing ones. - TheGhostObjectManager->lockGhostObjects(FALSE); - - // Don't do a newMap on the pathfinder - It uses the largest active boundary to start. jba. - //TheAI->pathfinder()->newMap(); - - TheTacticalView->forceCameraAreaConstraintRecalc(); -} - -// ------------------------------------------------------------------------------------------------ -/** Flatten the terrain beneath a structure. */ -// ------------------------------------------------------------------------------------------------ -void TerrainLogic::flattenTerrain(Object *obj) -{ - if (obj->getGeometryInfo().getIsSmall()) { - return; - } - - const Coord3D *pos = obj->getPosition(); - switch(obj->getGeometryInfo().getGeomType()) - { - case GEOMETRY_BOX: - { - Real angle = obj->getOrientation(); - - Real halfsizeX = obj->getGeometryInfo().getMajorRadius(); - Real halfsizeY = obj->getGeometryInfo().getMinorRadius(); - - - Real c = (Real)Cos(angle); - Real s = (Real)Sin(angle); - - Vector3 topLeft(pos->x-halfsizeX*c-halfsizeY*s, pos->y + halfsizeY*c - halfsizeX*s, 0); - Vector3 topRight(pos->x+halfsizeX*c-halfsizeY*s, pos->y + halfsizeY*c + halfsizeX*s, 0); - Vector3 bottomRight(pos->x+halfsizeX*c+halfsizeY*s, pos->y - halfsizeY*c + halfsizeX*s, 0); - Vector3 bottomLeft(pos->x-halfsizeX*c+halfsizeY*s, pos->y - halfsizeY*c - halfsizeX*s, 0); - - Real minX = topLeft.X; - if (minX>topRight.X) minX = topRight.X; - if (minX>bottomRight.X) minX = bottomRight.X; - if (minX>bottomLeft.X) minX = bottomLeft.X; - Real maxX = topLeft.X; - if (maxXtopRight.Y) minY = topRight.Y; - if (minY>bottomRight.Y) minY = bottomRight.Y; - if (minY>bottomLeft.Y) minY = bottomLeft.Y; - Real maxY = topLeft.Y; - if (maxYx, pos->y)/MAP_HEIGHT_SCALE); - if (rawDataHeight>centerHeight) rawDataHeight = centerHeight; - - for (i=iMin.x; i<=iMax.x; i++) { - for (j=0; j<=iMax.y; j++) { - Vector3 testPt(i*MAP_XY_FACTOR, j*MAP_XY_FACTOR, 0); - Bool match = false; - unsigned char flags; - if (Point_In_Triangle_2D(topLeft, topRight, bottomLeft, testPt, 0, 1, flags)) { - match = true; - } - if (Point_In_Triangle_2D(topRight, bottomRight, bottomLeft, testPt, 0, 1, flags)) { - match = true; - } - if (match) { - ICoord2D gridPos; - gridPos.x = i; - gridPos.y = j; - TheTerrainVisual->setRawMapHeight(&gridPos, rawDataHeight); - gridPos.x = i-1; - gridPos.y = j; - TheTerrainVisual->setRawMapHeight(&gridPos, rawDataHeight); - gridPos.x = i+1; - gridPos.y = j; - TheTerrainVisual->setRawMapHeight(&gridPos, rawDataHeight); - gridPos.x = i; - gridPos.y = j-1; - TheTerrainVisual->setRawMapHeight(&gridPos, rawDataHeight); - gridPos.x = i; - gridPos.y = j+1; - TheTerrainVisual->setRawMapHeight(&gridPos, rawDataHeight); - - //Added the corners so it does a whole 3X3 square... ML - gridPos.x = i-1; - gridPos.y = j-1; - TheTerrainVisual->setRawMapHeight(&gridPos, rawDataHeight); - gridPos.x = i+1; - gridPos.y = j+1; - TheTerrainVisual->setRawMapHeight(&gridPos, rawDataHeight); - gridPos.x = i+1; - gridPos.y = j-1; - TheTerrainVisual->setRawMapHeight(&gridPos, rawDataHeight); - gridPos.x = i-1; - gridPos.y = j+1; - TheTerrainVisual->setRawMapHeight(&gridPos, rawDataHeight); - - } - } - } - - - break; - } - case GEOMETRY_SPHERE: // not quite right, but close enough - case GEOMETRY_CYLINDER: - { - // fill in all cells that overlap as obstacle cells - Real radius = obj->getGeometryInfo().getMajorRadius(); - Real radiusSqr = sqr(radius); - ICoord2D iMin, iMax; - iMin.x = REAL_TO_INT_FLOOR((pos->x-radius)/MAP_XY_FACTOR); - iMin.y = REAL_TO_INT_FLOOR((pos->y-radius)/MAP_XY_FACTOR); - iMax.x = REAL_TO_INT_FLOOR((pos->x+radius)/MAP_XY_FACTOR); - iMax.y = REAL_TO_INT_FLOOR((pos->y+radius)/MAP_XY_FACTOR); - - Int i, j; - Real totalHeight = 0; - Int numSamples = 0; - for (i=iMin.x; i<=iMax.x; i++) { - for (j=0; j<=iMax.y; j++) { - Vector3 testPt(i*MAP_XY_FACTOR, j*MAP_XY_FACTOR, 0); - Bool match = false; - Real dx = testPt.X - pos->x; - Real dy = testPt.Y - pos->y; - if ( dx*dx+dy*dyx; - Real dy = testPt.Y - pos->y; - if ( dx*dx+dy*dysetRawMapHeight(&gridPos, rawDataHeight); - gridPos.x = i-1; - gridPos.y = j; - TheTerrainVisual->setRawMapHeight(&gridPos, rawDataHeight); - gridPos.x = i+1; - gridPos.y = j; - TheTerrainVisual->setRawMapHeight(&gridPos, rawDataHeight); - gridPos.x = i; - gridPos.y = j-1; - TheTerrainVisual->setRawMapHeight(&gridPos, rawDataHeight); - gridPos.x = i; - gridPos.y = j+1; - TheTerrainVisual->setRawMapHeight(&gridPos, rawDataHeight); - - //Added the corners so it does a whole 3X3 square... ML - gridPos.x = i-1; - gridPos.y = j-1; - TheTerrainVisual->setRawMapHeight(&gridPos, rawDataHeight); - gridPos.x = i+1; - gridPos.y = j+1; - TheTerrainVisual->setRawMapHeight(&gridPos, rawDataHeight); - gridPos.x = i+1; - gridPos.y = j-1; - TheTerrainVisual->setRawMapHeight(&gridPos, rawDataHeight); - gridPos.x = i-1; - gridPos.y = j+1; - TheTerrainVisual->setRawMapHeight(&gridPos, rawDataHeight); - - - } - } - } - - } - break; - } - -} - -#if !(RTS_GENERALS && RETAIL_COMPATIBLE_CRC) -// ------------------------------------------------------------------------------------------------ -/** Dig a deep circular gorge into the terrain beneath an object. */ -// ------------------------------------------------------------------------------------------------ -void TerrainLogic::createCraterInTerrain(Object *obj) -{ - if (obj->getGeometryInfo().getIsSmall()) - return; - - const Coord3D *pos = obj->getPosition(); - Real radius = obj->getGeometryInfo().getMajorRadius(); - - if ( radius <= 0.0f ) - return; // sanity - - ICoord2D iMin, iMax; - iMin.x = REAL_TO_INT_FLOOR( ( pos->x - radius ) / MAP_XY_FACTOR ); - iMin.y = REAL_TO_INT_FLOOR( ( pos->y - radius ) / MAP_XY_FACTOR ); - iMax.x = REAL_TO_INT_FLOOR( ( pos->x + radius ) / MAP_XY_FACTOR ); - iMax.y = REAL_TO_INT_FLOOR( ( pos->y + radius ) / MAP_XY_FACTOR ); - - Real deltaX, deltaY; - - for (Int i = iMin.x; i <= iMax.x; i++ ) - { - for ( Int j=0; j <= iMax.y; j++ ) - { - deltaX = ( i * MAP_XY_FACTOR ) - pos->x; - deltaY = ( j * MAP_XY_FACTOR ) - pos->y; - - Real distance = sqrt( sqr( deltaX ) + sqr( deltaY ) ); - - if ( distance < radius ) //inside circle - { - ICoord2D gridPos; - gridPos.x = i; - gridPos.y = j; - - - Real displacementAmount = radius * (1.0f - distance / radius ); - - Int targetHeight = MAX( 1, TheTerrainVisual->getRawMapHeight( &gridPos ) - displacementAmount ); - - TheTerrainVisual->setRawMapHeight( &gridPos, targetHeight ); - } - } - } - -} -#endif - -// ------------------------------------------------------------------------------------------------ -/** CRC */ -// ------------------------------------------------------------------------------------------------ -void TerrainLogic::crc( Xfer *xfer ) -{ - -} - -// ------------------------------------------------------------------------------------------------ -/** Xfer - * Version Info: - * 1: Initial version - * 2: Added water updates over time (CBD) - */ -// ------------------------------------------------------------------------------------------------ -void TerrainLogic::xfer( Xfer *xfer ) -{ - - // version - const XferVersion currentVersion = 2; - XferVersion version = currentVersion; - xfer->xferVersion( &version, currentVersion ); - - // active boundary - Int activeBoundary = m_activeBoundary; - xfer->xferInt( &activeBoundary ); - if( xfer->getXferMode() == XFER_LOAD ) - setActiveBoundary( activeBoundary ); - - // updatable water tables - if( version >= 2 ) - { - - // number of water entries in our update array - xfer->xferInt( &m_numWaterToUpdate ); - - // water update entry data - for( UnsignedInt i = 0; i < m_numWaterToUpdate; ++i ) - { - - // water handle - if( xfer->getXferMode() == XFER_SAVE ) - { - - // write ID of polygon trigger that this water handle is representing - Int triggerID = m_waterToUpdate[ i ].waterTable->m_polygon->getID(); - xfer->xferInt( &triggerID ); - - } - else if (xfer->getXferMode() == XFER_LOAD) - { - - // read trigger id - Int triggerID; - xfer->xferInt( &triggerID ); - - // find polygon trigger - PolygonTrigger *poly = PolygonTrigger::getPolygonTriggerByID( triggerID ); - - // sanity - if( poly == nullptr ) - { - - DEBUG_CRASH(( "TerrainLogic::xfer - Unable to find polygon trigger for water table with trigger ID '%d'", - triggerID )); - throw SC_INVALID_DATA; - - } - - // set water handle - m_waterToUpdate[ i ].waterTable = poly->getWaterHandle(); - - // sanity - if( m_waterToUpdate[ i ].waterTable == nullptr ) - { - - DEBUG_CRASH(( "TerrainLogic::xfer - Polygon trigger to use for water handle has no water handle!" )); - throw SC_INVALID_DATA; - - } - - } - - // change per frame - xfer->xferReal( &m_waterToUpdate[ i ].changePerFrame ); - - // target height - xfer->xferReal( &m_waterToUpdate[ i ].targetHeight ); - - // damage amount - xfer->xferReal( &m_waterToUpdate[ i ].damageAmount ); - - // current height - xfer->xferReal( &m_waterToUpdate[ i ].currentHeight ); - - } - - } - -} - -// ------------------------------------------------------------------------------------------------ -/** Load post process */ -// ------------------------------------------------------------------------------------------------ -void TerrainLogic::loadPostProcess() -{ - Bridge* pBridge = getFirstBridge(); - Bridge* pNext; - while (pBridge) - { - pNext = pBridge->getNext(); - Object* obj = TheGameLogic->findObjectByID(pBridge->peekBridgeInfo()->bridgeObjectID); - if (obj == nullptr) - { - deleteBridge(pBridge); - } - pBridge = pNext; - } - -} - diff --git a/GeneralsMD/Code/GameEngine/CMakeLists.txt b/GeneralsMD/Code/GameEngine/CMakeLists.txt index a636a00a345..113245f258f 100644 --- a/GeneralsMD/Code/GameEngine/CMakeLists.txt +++ b/GeneralsMD/Code/GameEngine/CMakeLists.txt @@ -477,16 +477,16 @@ set(GAMEENGINE_SRC Include/GameLogic/ObjectScriptStatusBits.h Include/GameLogic/ObjectTypes.h Include/GameLogic/PartitionManager.h - Include/GameLogic/PolygonTrigger.h +# Include/GameLogic/PolygonTrigger.h Include/GameLogic/Powers.h # Include/GameLogic/RankInfo.h Include/GameLogic/ScriptActions.h Include/GameLogic/ScriptConditions.h Include/GameLogic/ScriptEngine.h Include/GameLogic/Scripts.h - Include/GameLogic/SidesList.h +# Include/GameLogic/SidesList.h Include/GameLogic/Squad.h - Include/GameLogic/TerrainLogic.h +# Include/GameLogic/TerrainLogic.h Include/GameLogic/TurretAI.h Include/GameLogic/VictoryConditions.h Include/GameLogic/Weapon.h @@ -834,9 +834,9 @@ set(GAMEENGINE_SRC Source/GameLogic/AI/AITNGuard.cpp Source/GameLogic/AI/Squad.cpp Source/GameLogic/AI/TurretAI.cpp - Source/GameLogic/Map/PolygonTrigger.cpp - Source/GameLogic/Map/SidesList.cpp - Source/GameLogic/Map/TerrainLogic.cpp +# Source/GameLogic/Map/PolygonTrigger.cpp +# Source/GameLogic/Map/SidesList.cpp +# Source/GameLogic/Map/TerrainLogic.cpp Source/GameLogic/Object/Armor.cpp Source/GameLogic/Object/Behavior/AutoHealBehavior.cpp Source/GameLogic/Object/Behavior/BattleBusSlowDeathBehavior.cpp diff --git a/scripts/cpp/unify_move_files.py b/scripts/cpp/unify_move_files.py index 567a2465030..24cc7802a07 100644 --- a/scripts/cpp/unify_move_files.py +++ b/scripts/cpp/unify_move_files.py @@ -531,6 +531,13 @@ def main(): #unify_file(Game.ZEROHOUR, "GameEngineDevice/Source/W3DDevice/GameClient/GUI/Gadget/W3DTextEntry.cpp", Game.CORE, "GameEngineDevice/Source/W3DDevice/GameClient/GUI/Gadget/W3DTextEntry.cpp") #unify_file(Game.ZEROHOUR, "GameEngineDevice/Source/W3DDevice/GameClient/GUI/Gadget/W3DVerticalSlider.cpp", Game.CORE, "GameEngineDevice/Source/W3DDevice/GameClient/GUI/Gadget/W3DVerticalSlider.cpp") + #unify_file(Game.ZEROHOUR, "GameEngine/Include/GameLogic/PolygonTrigger.h", Game.CORE, "GameEngine/Include/GameLogic/PolygonTrigger.h") + #unify_file(Game.ZEROHOUR, "GameEngine/Include/GameLogic/SidesList.h", Game.CORE, "GameEngine/Include/GameLogic/SidesList.h") + #unify_file(Game.ZEROHOUR, "GameEngine/Include/GameLogic/TerrainLogic.h", Game.CORE, "GameEngine/Include/GameLogic/TerrainLogic.h") + #unify_file(Game.ZEROHOUR, "GameEngine/Source/GameLogic/Map/PolygonTrigger.cpp", Game.CORE, "GameEngine/Source/GameLogic/Map/PolygonTrigger.cpp") + #unify_file(Game.ZEROHOUR, "GameEngine/Source/GameLogic/Map/SidesList.cpp", Game.CORE, "GameEngine/Source/GameLogic/Map/SidesList.cpp") + #unify_file(Game.ZEROHOUR, "GameEngine/Source/GameLogic/Map/TerrainLogic.cpp", Game.CORE, "GameEngine/Source/GameLogic/Map/TerrainLogic.cpp") + #unify_file(Game.ZEROHOUR, "GameEngine/Include/GameLogic/CaveSystem.h", Game.CORE, "GameEngine/Include/GameLogic/CaveSystem.h") #unify_file(Game.ZEROHOUR, "GameEngine/Include/GameLogic/CrateSystem.h", Game.CORE, "GameEngine/Include/GameLogic/CrateSystem.h") #unify_file(Game.ZEROHOUR, "GameEngine/Include/GameLogic/Damage.h", Game.CORE, "GameEngine/Include/GameLogic/Damage.h")