diff --git a/Core/GameEngine/CMakeLists.txt b/Core/GameEngine/CMakeLists.txt
index eff13029517..da2051f28c9 100644
--- a/Core/GameEngine/CMakeLists.txt
+++ b/Core/GameEngine/CMakeLists.txt
@@ -70,7 +70,7 @@ set(GAMEENGINE_SRC
Include/Common/LocalFile.h
Include/Common/LocalFileSystem.h
Include/Common/MapObject.h
-# Include/Common/MapReaderWriterInfo.h
+ Include/Common/MapReaderWriterInfo.h
Include/Common/MessageStream.h
Include/Common/MiniDumper.h
Include/Common/MiniLog.h
@@ -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/Common/MapReaderWriterInfo.h b/Core/GameEngine/Include/Common/MapReaderWriterInfo.h
similarity index 100%
rename from GeneralsMD/Code/GameEngine/Include/Common/MapReaderWriterInfo.h
rename to Core/GameEngine/Include/Common/MapReaderWriterInfo.h
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 98%
rename from GeneralsMD/Code/GameEngine/Source/GameLogic/Map/PolygonTrigger.cpp
rename to Core/GameEngine/Source/GameLogic/Map/PolygonTrigger.cpp
index 4f19ee9cfc2..0d12b2caaec 100644
--- a/GeneralsMD/Code/GameEngine/Source/GameLogic/Map/PolygonTrigger.cpp
+++ b/Core/GameEngine/Source/GameLogic/Map/PolygonTrigger.cpp
@@ -186,12 +186,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 +241,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 +255,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/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 99%
rename from GeneralsMD/Code/GameEngine/Source/GameLogic/Map/TerrainLogic.cpp
rename to Core/GameEngine/Source/GameLogic/Map/TerrainLogic.cpp
index 5a3d16f86ae..d19ea5020b7 100644
--- a/GeneralsMD/Code/GameEngine/Source/GameLogic/Map/TerrainLogic.cpp
+++ b/Core/GameEngine/Source/GameLogic/Map/TerrainLogic.cpp
@@ -2866,8 +2866,6 @@ void TerrainLogic::flattenTerrain(Object *obj)
}
-
-
// ------------------------------------------------------------------------------------------------
/** Dig a deep circular gorge into the terrain beneath an object. */
// ------------------------------------------------------------------------------------------------
@@ -2917,12 +2915,6 @@ void TerrainLogic::createCraterInTerrain(Object *obj)
}
-
-
-
-
-
-
// ------------------------------------------------------------------------------------------------
/** CRC */
// ------------------------------------------------------------------------------------------------
diff --git a/Generals/Code/GameEngine/CMakeLists.txt b/Generals/Code/GameEngine/CMakeLists.txt
index 0db74569602..e63015b67bd 100644
--- a/Generals/Code/GameEngine/CMakeLists.txt
+++ b/Generals/Code/GameEngine/CMakeLists.txt
@@ -62,7 +62,7 @@ set(GAMEENGINE_SRC
# Include/Common/LocalFile.h
# Include/Common/LocalFileSystem.h
# Include/Common/MapObject.h
- Include/Common/MapReaderWriterInfo.h
+# Include/Common/MapReaderWriterInfo.h
# Include/Common/MessageStream.h
# Include/Common/MiniLog.h
# Include/Common/MiscAudio.h
@@ -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/Common/MapReaderWriterInfo.h b/Generals/Code/GameEngine/Include/Common/MapReaderWriterInfo.h
deleted file mode 100644
index c370ac5b3c0..00000000000
--- a/Generals/Code/GameEngine/Include/Common/MapReaderWriterInfo.h
+++ /dev/null
@@ -1,124 +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. //
-// //
-////////////////////////////////////////////////////////////////////////////////
-
-// MapReaderWriterInfo.h
-// Version and interface info for map file writers and reader.
-// Author: John Ahlquist, October 2001
-
-#pragma once
-
-#define K_HEIGHT_MAP_VERSION_1 1 // Height map cell = 5.0
-#define K_HEIGHT_MAP_VERSION_2 2 // Height map cell = 10.0
-#define K_HEIGHT_MAP_VERSION_3 3 // Added m_borderSize
-#define K_HEIGHT_MAP_VERSION_4 4 // Major rev. See comments at bottom of file.
-#define K_BLEND_TILE_VERSION_1 1 // Height map cell = 5.0
-#define K_BLEND_TILE_VERSION_2 2 // Height map cell = 10.0
-#define K_BLEND_TILE_VERSION_3 3 // Added long diagonal blends.
-#define K_BLEND_TILE_VERSION_4 4 // Added custom edge blends.
-#define K_BLEND_TILE_VERSION_5 5 // Added custom cliff u/v coordinates.
-#define K_BLEND_TILE_VERSION_6 6 // Added extra blend layer for 3 textures in cell.
-#define K_BLEND_TILE_VERSION_7 7 // Added flag for painting passable/impassable to cells.
-#define K_OBJECTS_VERSION_1 1 // no dict
-#define K_OBJECTS_VERSION_2 2 // includes dict
-#define K_OBJECTS_VERSION_3 3 // includes dict
-#define K_MAP_OBJECT_VERSION_1 1
-#define K_WAYPOINTS_VERSION_1 1
-#define K_PLAYERLIST_VERSION_1 1
-#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_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.
-#define K_WORLDDICT_VERSION_1 1
-#define K_MAPPREVIEW_VERSION_1 1
-/** Virtual helper class, so that we can write map data using FILE* or CFile. */
-class OutputStream {
-public:
- virtual Int write(const void *pData, Int numBytes) = 0;
-};
-
-/** Virtual helper class, so that we can read in tile and map data from a
-variety of sources, such as FILE* or CFile. */
-class InputStream {
-public:
- virtual Int read(void *pData, Int numBytes) = 0;
-};
-
-/** Virtual helper class, so that we can read in tile and map data from a
-variety of sources, such as FILE* or CFile. */
-class ChunkInputStream : public InputStream{
-public:
- virtual Int read(void *pData, Int numBytes) = 0;
- virtual UnsignedInt tell() = 0;
- virtual Bool absoluteSeek(UnsignedInt pos) = 0;
- virtual Bool eof() = 0;
-};
-
-/** An instance of InputStream that uses a FILE* to read data. */
-class CachedFileInputStream : public ChunkInputStream
-{
-protected:
- int m_size;
- char* m_buffer;
- int m_pos;
-public:
- CachedFileInputStream();
- ~CachedFileInputStream();
- Bool open(AsciiString path); ///< Returns true if open succeeded.
- void close(); ///< Explict close. Destructor closes if file is left open.
- virtual Int read(void *pData, Int numBytes) override;
- virtual UnsignedInt tell() override;
- virtual Bool absoluteSeek(UnsignedInt pos) override;
- virtual Bool eof() override;
- void rewind();
-};
-
-/** An instance of InputStream that uses a FILE* to read data. */
-/* Always using Cached version, as that lets us do compression, etc easily
-class FileInputStream : public ChunkInputStream
-{
-protected:
- File *m_file;
-public:
- FileInputStream();
- ~FileInputStream();
- Bool open(AsciiString path); ///< Returns true if open succeeded.
- void close(); ///< Explict close. Destructor closes if file is left open.
- virtual Int read(void *pData, Int numBytes);
- virtual UnsignedInt tell();
- virtual Bool absoluteSeek(UnsignedInt pos);
- virtual Bool eof();
- void rewind();
-};
-*/
-
-/*
- rev K_HEIGHT_MAP_VERSION_4
-
- This is a major rev of the heightmap chunk. Here's the basic overview of what has happened:
- We now support multiple boundary areas. They are stored in a vector of ICoord2Ds. The lower-
- left corner is always (0,0), and the ICoord2D specifies the top-right coordinate.
- The boundary also contains a name.
-*/
diff --git a/Generals/Code/GameEngine/Include/GameLogic/PolygonTrigger.h b/Generals/Code/GameEngine/Include/GameLogic/PolygonTrigger.h
deleted file mode 100644
index d3e133834fc..00000000000
--- a/Generals/Code/GameEngine/Include/GameLogic/PolygonTrigger.h
+++ /dev/null
@@ -1,140 +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.
-
- 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;};
-
- 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 ad1db103499..00000000000
--- a/Generals/Code/GameEngine/Include/GameLogic/TerrainLogic.h
+++ /dev/null
@@ -1,378 +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.
-
-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 6cea7c6227a..00000000000
--- a/Generals/Code/GameEngine/Source/GameLogic/Map/PolygonTrigger.cpp
+++ /dev/null
@@ -1,544 +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),
-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;
- // 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();
- 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);
- 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 (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)
-{
- chunkWriter.openDataChunk("PolygonTriggers", K_TRIGGERS_VERSION_3);
-
- 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());
- 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 93bb5693832..00000000000
--- a/Generals/Code/GameEngine/Source/GameLogic/Map/SidesList.cpp
+++ /dev/null
@@ -1,1156 +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
-*
-*/
-static Bool ParsePlayersDataChunk(DataChunkInput &file, DataChunkInfo *info, void *userData)
-{
- Int numNames = file.readInt();
- Int i;
- for (i=0; i=MAX_PLAYER_COUNT) break;
- static_readPlayerNames[i] = file.readAsciiString();
- }
- 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
- };
-
- DEBUG_ASSERTCRASH(m_numTeams < 1024, ("hmm, seems like an awful lot of teams..."));
- 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 00386047267..00000000000
--- a/Generals/Code/GameEngine/Source/GameLogic/Map/TerrainLogic.cpp
+++ /dev/null
@@ -1,2989 +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; // irrelavant
-
- // 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;
- }
-
-}
-
-// ------------------------------------------------------------------------------------------------
-/** 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..9808d4333bd 100644
--- a/GeneralsMD/Code/GameEngine/CMakeLists.txt
+++ b/GeneralsMD/Code/GameEngine/CMakeLists.txt
@@ -65,7 +65,7 @@ set(GAMEENGINE_SRC
# Include/Common/LocalFile.h
# Include/Common/LocalFileSystem.h
# Include/Common/MapObject.h
- Include/Common/MapReaderWriterInfo.h
+# Include/Common/MapReaderWriterInfo.h
# Include/Common/MessageStream.h
# Include/Common/MiniLog.h
# Include/Common/MiscAudio.h
@@ -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..27d1ac3d496 100644
--- a/scripts/cpp/unify_move_files.py
+++ b/scripts/cpp/unify_move_files.py
@@ -531,6 +531,14 @@ 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/Common/MapReaderWriterInfo.h", Game.CORE, "GameEngine/Include/Common/MapReaderWriterInfo.h")
+ #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")