diff --git a/Core/GameEngine/CMakeLists.txt b/Core/GameEngine/CMakeLists.txt
index c462ee691f2..cb195ba0274 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
@@ -487,16 +487,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
@@ -855,9 +855,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/Core/GameEngine/Include/Common/GameDefines.h b/Core/GameEngine/Include/Common/GameDefines.h
index 0fcd3b1df3c..d58ffcb0306 100644
--- a/Core/GameEngine/Include/Common/GameDefines.h
+++ b/Core/GameEngine/Include/Common/GameDefines.h
@@ -20,6 +20,8 @@
#include "WWLib/WWDefines.h"
+// Note: Retail compatibility must not be broken before this project officially does.
+// Use RETAIL_COMPATIBLE_CRC, RETAIL_COMPATIBLE_DATA and RETAIL_COMPATIBLE_XFER_SAVE to guard breaking changes.
#ifndef PRESERVE_BUILDING_RESUMPTION_DELAY
#define PRESERVE_BUILDING_RESUMPTION_DELAY (0) // The fix for this unfavorable behavior was approved by the Game Design Committee.
#endif
@@ -103,6 +105,10 @@
#define RETAIL_COMPATIBLE_CRC (1) // Game is expected to be CRC compatible with retail Generals 1.08, Zero Hour 1.04
#endif
+#ifndef RETAIL_COMPATIBLE_DATA
+#define RETAIL_COMPATIBLE_DATA (1) // Game and tool data files are expected to be compatible with retail Generals 1.08 and Zero Hour 1.04
+#endif
+
#ifndef RETAIL_COMPATIBLE_XFER_SAVE
#define RETAIL_COMPATIBLE_XFER_SAVE (1) // Game is expected to be Xfer Save compatible with retail Generals 1.08, Zero Hour 1.04
#endif
diff --git a/GeneralsMD/Code/GameEngine/Include/Common/MapReaderWriterInfo.h b/Core/GameEngine/Include/Common/MapReaderWriterInfo.h
similarity index 97%
rename from GeneralsMD/Code/GameEngine/Include/Common/MapReaderWriterInfo.h
rename to Core/GameEngine/Include/Common/MapReaderWriterInfo.h
index 773beecd74d..0d42140ef47 100644
--- a/GeneralsMD/Code/GameEngine/Include/Common/MapReaderWriterInfo.h
+++ b/Core/GameEngine/Include/Common/MapReaderWriterInfo.h
@@ -39,7 +39,7 @@
#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_BLEND_TILE_VERSION_8 8 // Added flag for painting passable/impassable to cells.
+#define K_BLEND_TILE_VERSION_8 8 // Added flag for painting passable/impassable to cells. Added in Zero Hour.
#define K_OBJECTS_VERSION_1 1 // no dict
#define K_OBJECTS_VERSION_2 2 // includes dict
#define K_OBJECTS_VERSION_3 3 // includes dict
@@ -49,7 +49,7 @@
#define K_TRIGGERS_VERSION_1 1
#define K_TRIGGERS_VERSION_2 2 // Added m_isWaterArea
#define K_TRIGGERS_VERSION_3 3 // Added m_isRiver & m_riverStart
-#define K_TRIGGERS_VERSION_4 4 // Added layer name.
+#define K_TRIGGERS_VERSION_4 4 // Added layer name. Added in Zero Hour.
#define K_LIGHTING_VERSION_1 1
#define K_LIGHTING_VERSION_2 2 // Added 2 additional global lights for objects.
#define K_LIGHTING_VERSION_3 3 // Added 2 additional global lights for terrain.
diff --git a/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 97%
rename from GeneralsMD/Code/GameEngine/Source/GameLogic/Map/PolygonTrigger.cpp
rename to Core/GameEngine/Source/GameLogic/Map/PolygonTrigger.cpp
index 6f45a4cc4b9..337630511c2 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,12 @@ Bool PolygonTrigger::ParsePolygonTriggersDataChunk(DataChunkInput &file, DataChu
*/
void PolygonTrigger::WritePolygonTriggersDataChunk(DataChunkOutput &chunkWriter)
{
- chunkWriter.openDataChunk("PolygonTriggers", K_TRIGGERS_VERSION_4);
+#if RTS_GENERALS && RETAIL_COMPATIBLE_DATA
+ const DataChunkVersionType version = K_TRIGGERS_VERSION_3;
+#else
+ const DataChunkVersionType version = K_TRIGGERS_VERSION_4;
+#endif
+ chunkWriter.openDataChunk("PolygonTriggers", version);
PolygonTrigger *pTrig;
Int count = 0;
@@ -249,7 +256,9 @@ void PolygonTrigger::WritePolygonTriggersDataChunk(DataChunkOutput &chunkWriter)
chunkWriter.writeInt(count);
for (pTrig=PolygonTrigger::getFirstPolygonTrigger(); pTrig; pTrig = pTrig->getNext()) {
chunkWriter.writeAsciiString(pTrig->getTriggerName());
- chunkWriter.writeAsciiString(pTrig->getLayerName());
+ if (version >= K_TRIGGERS_VERSION_4) {
+ chunkWriter.writeAsciiString(pTrig->getLayerName());
+ }
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 f2173b15e4b..7335f4b057a 100644
--- a/GeneralsMD/Code/GameEngine/Source/GameLogic/Map/TerrainLogic.cpp
+++ b/Core/GameEngine/Source/GameLogic/Map/TerrainLogic.cpp
@@ -2828,8 +2828,6 @@ void TerrainLogic::flattenTerrain(Object *obj)
}
-
-
// ------------------------------------------------------------------------------------------------
/** Dig a deep circular gorge into the terrain beneath an object. */
// ------------------------------------------------------------------------------------------------
@@ -2879,12 +2877,6 @@ void TerrainLogic::createCraterInTerrain(Object *obj)
}
-
-
-
-
-
-
// ------------------------------------------------------------------------------------------------
/** CRC */
// ------------------------------------------------------------------------------------------------
diff --git a/Generals/Code/GameEngine/CMakeLists.txt b/Generals/Code/GameEngine/CMakeLists.txt
index 5ba4b5f62a5..d8133efeb61 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 9089411e091..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) override = 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
index 53547241570..cf17f0f5a5e 100644
--- a/Generals/Code/GameEngine/Source/GameLogic/Map/PolygonTrigger.cpp
+++ b/Generals/Code/GameEngine/Source/GameLogic/Map/PolygonTrigger.cpp
@@ -48,6 +48,8 @@ m_numPoints(0),
m_sizePoints(0),
m_exportWithScripts(false),
m_isWaterArea(false),
+m_shouldRender(true),
+m_selected(false),
m_isRiver(FALSE),
m_riverStart(0)
{
@@ -140,6 +142,7 @@ Bool PolygonTrigger::ParsePolygonTriggersDataChunk(DataChunkInput &file, DataChu
Bool isRiver;
Int riverStart;
AsciiString triggerName;
+ AsciiString layerName;
// Remove any existing polygon triggers, if any.
PolygonTrigger::deleteTriggers(); // just in case.
PolygonTrigger *pPrevTrig = nullptr;
@@ -148,6 +151,9 @@ Bool PolygonTrigger::ParsePolygonTriggersDataChunk(DataChunkInput &file, DataChu
while (count>0) {
count--;
triggerName = file.readAsciiString();
+ if (info->version >= K_TRIGGERS_VERSION_4) {
+ layerName = file.readAsciiString();
+ }
triggerID = file.readInt();
isWater = false;
if (info->version >= K_TRIGGERS_VERSION_2) {
@@ -163,6 +169,9 @@ Bool PolygonTrigger::ParsePolygonTriggersDataChunk(DataChunkInput &file, DataChu
numPoints = file.readInt();
PolygonTrigger *pTrig = newInstance(PolygonTrigger)(numPoints+1);
pTrig->setTriggerName(triggerName);
+ if (info->version >= K_TRIGGERS_VERSION_4) {
+ pTrig->setLayerName(layerName);
+ }
pTrig->setWaterArea(isWater);
pTrig->setRiver(isRiver);
pTrig->setRiverStart(riverStart);
@@ -177,6 +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 {
@@ -224,7 +241,12 @@ Bool PolygonTrigger::ParsePolygonTriggersDataChunk(DataChunkInput &file, DataChu
*/
void PolygonTrigger::WritePolygonTriggersDataChunk(DataChunkOutput &chunkWriter)
{
- chunkWriter.openDataChunk("PolygonTriggers", K_TRIGGERS_VERSION_3);
+#if RTS_GENERALS && RETAIL_COMPATIBLE_DATA
+ const DataChunkVersionType version = K_TRIGGERS_VERSION_3;
+#else
+ const DataChunkVersionType version = K_TRIGGERS_VERSION_4;
+#endif
+ chunkWriter.openDataChunk("PolygonTriggers", version);
PolygonTrigger *pTrig;
Int count = 0;
@@ -234,6 +256,9 @@ void PolygonTrigger::WritePolygonTriggersDataChunk(DataChunkOutput &chunkWriter)
chunkWriter.writeInt(count);
for (pTrig=PolygonTrigger::getFirstPolygonTrigger(); pTrig; pTrig = pTrig->getNext()) {
chunkWriter.writeAsciiString(pTrig->getTriggerName());
+ if (version >= K_TRIGGERS_VERSION_4) {
+ chunkWriter.writeAsciiString(pTrig->getLayerName());
+ }
chunkWriter.writeInt(pTrig->getID());
chunkWriter.writeByte(pTrig->isWaterArea());
chunkWriter.writeByte(pTrig->isRiver());
diff --git a/Generals/Code/GameEngine/Source/GameLogic/Map/SidesList.cpp b/Generals/Code/GameEngine/Source/GameLogic/Map/SidesList.cpp
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
index c00d3096810..dff9694d14a 100644
--- a/Generals/Code/GameEngine/Source/GameLogic/Map/TerrainLogic.cpp
+++ b/Generals/Code/GameEngine/Source/GameLogic/Map/TerrainLogic.cpp
@@ -2346,7 +2346,7 @@ void TerrainLogic::setWaterHeight( const WaterHandle *water, Real height, Real d
Coord3D center;
center.x = affectedRegion.lo.x + affectedRegion.width() / 2.0f;
center.y = affectedRegion.lo.y + affectedRegion.height() / 2.0f;
- center.z = 0.0f; // irrelavant
+ center.z = 0.0f; // irrelevant
// the max radius to scan around us is the diagonal of the bounding region
Real maxDist = sqrt( affectedRegion.width() * affectedRegion.width() +
@@ -2828,6 +2828,55 @@ void TerrainLogic::flattenTerrain(Object *obj)
}
+// ------------------------------------------------------------------------------------------------
+/** Dig a deep circular gorge into the terrain beneath an object. */
+// ------------------------------------------------------------------------------------------------
+void TerrainLogic::createCraterInTerrain(Object *obj)
+{
+ if (obj->getGeometryInfo().getIsSmall())
+ return;
+
+ const Coord3D *pos = obj->getPosition();
+ Real radius = obj->getGeometryInfo().getMajorRadius();
+
+ if ( radius <= 0.0f )
+ return; // sanity
+
+ ICoord2D iMin, iMax;
+ iMin.x = REAL_TO_INT_FLOOR( ( pos->x - radius ) / MAP_XY_FACTOR );
+ iMin.y = REAL_TO_INT_FLOOR( ( pos->y - radius ) / MAP_XY_FACTOR );
+ iMax.x = REAL_TO_INT_FLOOR( ( pos->x + radius ) / MAP_XY_FACTOR );
+ iMax.y = REAL_TO_INT_FLOOR( ( pos->y + radius ) / MAP_XY_FACTOR );
+
+ Real deltaX, deltaY;
+
+ for (Int i = iMin.x; i <= iMax.x; i++ )
+ {
+ for ( Int j=0; j <= iMax.y; j++ )
+ {
+ deltaX = ( i * MAP_XY_FACTOR ) - pos->x;
+ deltaY = ( j * MAP_XY_FACTOR ) - pos->y;
+
+ Real distance = sqrt( sqr( deltaX ) + sqr( deltaY ) );
+
+ if ( distance < radius ) //inside circle
+ {
+ ICoord2D gridPos;
+ gridPos.x = i;
+ gridPos.y = j;
+
+
+ Real displacementAmount = radius * (1.0f - distance / radius );
+
+ Int targetHeight = MAX( 1, TheTerrainVisual->getRawMapHeight( &gridPos ) - displacementAmount );
+
+ TheTerrainVisual->setRawMapHeight( &gridPos, targetHeight );
+ }
+ }
+ }
+
+}
+
// ------------------------------------------------------------------------------------------------
/** CRC */
// ------------------------------------------------------------------------------------------------
diff --git a/GeneralsMD/Code/GameEngine/CMakeLists.txt b/GeneralsMD/Code/GameEngine/CMakeLists.txt
index 607e05e77e1..87ad9d74fe4 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 a242d9bfda7..0949b390d31 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")