Initial commit of Command & Conquer Generals and Command & Conquer Generals Zero Hour source code.

This commit is contained in:
LFeenanEA 2025-02-27 17:34:39 +00:00
parent 2e338c00cb
commit 3d0ee53a05
No known key found for this signature in database
GPG key ID: C6EBE8C2EA08F7E0
6072 changed files with 2283311 additions and 0 deletions

View file

@ -0,0 +1,543 @@
/*
** 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 <http://www.gnu.org/licenses/>.
*/
////////////////////////////////////////////////////////////////////////////////
// //
// (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 int 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 = NULL;
Int PolygonTrigger::s_currentID = 1;
/**
PolygonTrigger - Constructor.
*/
PolygonTrigger::PolygonTrigger(Int initialAllocation) :
m_nextPolygonTrigger(NULL),
m_points(NULL),
m_numPoints(0),
m_sizePoints(0),
m_exportWithScripts(false),
m_isWaterArea(false),
//Added By Sadullah Nader
//Initializations inserted
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(void)
{
if (m_points) {
delete [] m_points;
m_points = NULL;
}
if (m_nextPolygonTrigger) {
PolygonTrigger *cur = m_nextPolygonTrigger;
PolygonTrigger *next;
while (cur) {
next = cur->getNext();
cur->setNextPoly(NULL); // prevents recursion.
cur->deleteInstance();
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(void)
{
DEBUG_ASSERTCRASH(m_numPoints <= m_sizePoints, ("Invalid m_numPoints."));
if (m_numPoints == m_sizePoints) {
// Reallocate.
m_sizePoints += m_sizePoints;
ICoord3D *newPts = NEW ICoord3D[m_sizePoints];
Int i;
for (i=0; i<m_numPoints; i++) {
newPts[i] = m_points[i];
}
delete [] m_points;
m_points = newPts;
}
}
/**
* Find the polygon trigger with the matching ID
*/
PolygonTrigger *PolygonTrigger::getPolygonTriggerByID(Int triggerID)
{
for( PolygonTrigger *poly = PolygonTrigger::getFirstPolygonTrigger();
poly; poly = poly->getNext() )
if( poly->getID() == triggerID )
return poly;
// not found
return NULL;
}
/**
* 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 = NULL;
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; i<numPoints; i++) {
loc.x = file.readInt();
loc.y = file.readInt();
loc.z = file.readInt();
pTrig->addPoint(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 _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; i<pTrig->getNumPoints(); 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(void) 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_numPoints; i++) {
if (m_points[i].x < m_bounds.lo.x) m_bounds.lo.x = m_points[i].x;
if (m_points[i].y < m_bounds.lo.y) m_bounds.lo.y = m_points[i].y;
if (m_points[i].x > 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 = NULL;
for (PolygonTrigger *pTrig=getFirstPolygonTrigger(); 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 errror. jba."));
pPrev->m_nextPolygonTrigger = pTrig->m_nextPolygonTrigger;
} else {
DEBUG_ASSERTCRASH(pTrigger==ThePolygonTriggerListPtr, ("Logic errror. jba."));
ThePolygonTriggerListPtr = pTrig->m_nextPolygonTrigger;
}
}
pTrigger->m_nextPolygonTrigger = NULL;
}
/**
PolygonTrigger::deleteTriggers Deletes list of triggers.
*/
void PolygonTrigger::deleteTriggers(void)
{
PolygonTrigger *pList = ThePolygonTriggerListPtr;
ThePolygonTriggerListPtr = NULL;
s_currentID = 1;
pList->deleteInstance();
}
/**
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; i<m_numPoints-1; i++) {
m_points[i] = m_points[i+1];
}
m_numPoints--;
m_boundsNeedsUpdate = true;
}
void PolygonTrigger::getCenterPoint(Coord3D* pOutCoord) const
{
DEBUG_ASSERTCRASH(pOutCoord != NULL, ("pOutCoord was null. Non-Fatal, but shouldn't happen."));
if (!pOutCoord) {
return;
}
if (m_boundsNeedsUpdate) {
updateBounds();
}
(*pOutCoord).x = (m_bounds.lo.x + m_bounds.hi.x) / 2.0f;
(*pOutCoord).y = (m_bounds.lo.y + m_bounds.hi.y) / 2.0f;
(*pOutCoord).z = TheTerrainLogic->getGroundHeight(pOutCoord->x, pOutCoord->y);
}
Real PolygonTrigger::getRadius(void) 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<m_numPoints; i++) {
ICoord3D pt1 = m_points[i];
ICoord3D pt2;
if (i==m_numPoints-1) {
pt2 = m_points[0];
} else {
pt2 = m_points[i+1];
}
if (pt1.y == pt2.y) {
continue; // ignore horizontal lines.
}
if (pt1.y < point.y && pt2.y < point.y) continue;
if (pt1.y >= point.y && pt2.y >= point.y) continue;
if (pt1.x<point.x && pt2.x < point.x) continue;
// Line segment crosses ray from point x->infinity.
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(void) const
{
if( isWaterArea() )
return &m_waterHandle;
return NULL; // this polygon trigger is not a water area
}
Bool PolygonTrigger::isValid(void) const
{
if (m_numPoints == 0) {
return FALSE;
}
return TRUE;
}
// ------------------------------------------------------------------------------------------------
/** CRC */
// ------------------------------------------------------------------------------------------------
void PolygonTrigger::crc( Xfer *xfer )
{
} // end crc
// ------------------------------------------------------------------------------------------------
/** 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 );
} // end for, i
// bounds
xfer->xferIRegion2D( &m_bounds );
// radius
xfer->xferReal( &m_radius );
// bounds need update
xfer->xferBool( &m_boundsNeedsUpdate );
} // end xfer
// ------------------------------------------------------------------------------------------------
/** Load post process */
// ------------------------------------------------------------------------------------------------
void PolygonTrigger::loadPostProcess( void )
{
} // end loadPostProcess

File diff suppressed because it is too large Load diff

File diff suppressed because it is too large Load diff