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