/* ** Command & Conquer Generals Zero Hour(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: PrisonBehavior.cpp /////////////////////////////////////////////////////////////////////// // Author: Colin Day, August 2002 // Desc: Prison Behavior /////////////////////////////////////////////////////////////////////////////////////////////////// // INCLUDES /////////////////////////////////////////////////////////////////////////////////////// #include "PreRTS.h" // This must go first in EVERY cpp file int the GameEngine #include "Common/GameState.h" #include "Common/Player.h" #include "Common/RandomValue.h" #include "Common/ThingFactory.h" #include "Common/Xfer.h" #include "GameClient/Drawable.h" #include "GameClient/InGameUI.h" #include "GameClient/GameClient.h" #include "GameClient/Line2D.h" #include "GameLogic/Object.h" #include "GameLogic/Module/PrisonBehavior.h" #ifdef ALLOW_SURRENDER /////////////////////////////////////////////////////////////////////////////////////////////////// /////////////////////////////////////////////////////////////////////////////////////////////////// /////////////////////////////////////////////////////////////////////////////////////////////////// // ------------------------------------------------------------------------------------------------ // ------------------------------------------------------------------------------------------------ class PrisonVisual : public MemoryPoolObject { MEMORY_POOL_GLUE_WITH_USERLOOKUP_CREATE( PrisonVisual, "PrisonVisual" ) public: PrisonVisual( void ); // virtual destructor prototype provied by memory pool object ObjectID m_objectID; ///< object that is contained DrawableID m_drawableID; ///< associated visual prisoner drawable PrisonVisual *m_next; ///< next }; // ------------------------------------------------------------------------------------------------ // ------------------------------------------------------------------------------------------------ PrisonVisual::PrisonVisual( void ) { m_objectID = INVALID_ID; m_drawableID = INVALID_DRAWABLE_ID; m_next = NULL; } // end PrisonVisual // ------------------------------------------------------------------------------------------------ // ------------------------------------------------------------------------------------------------ PrisonVisual::~PrisonVisual( void ) { } // end ~PrisonVisual /////////////////////////////////////////////////////////////////////////////////////////////////// /////////////////////////////////////////////////////////////////////////////////////////////////// /////////////////////////////////////////////////////////////////////////////////////////////////// // ------------------------------------------------------------------------------------------------ // ------------------------------------------------------------------------------------------------ PrisonBehaviorModuleData::PrisonBehaviorModuleData( void ) { m_showPrisoners = FALSE; } // end PrisonBehaviorModuleData // ------------------------------------------------------------------------------------------------ // ------------------------------------------------------------------------------------------------ /*static*/ void PrisonBehaviorModuleData::buildFieldParse( MultiIniFieldParse &p ) { OpenContainModuleData::buildFieldParse( p ); static const FieldParse dataFieldParse[] = { { "ShowPrisoners", INI::parseBool, NULL, offsetof( PrisonBehaviorModuleData, m_showPrisoners ) }, { "YardBonePrefix", INI::parseAsciiString, NULL, offsetof( PrisonBehaviorModuleData, m_prisonYardBonePrefix ) }, { 0, 0, 0, 0 } }; p.add( dataFieldParse ); } // end buildFieldParse /////////////////////////////////////////////////////////////////////////////////////////////////// /////////////////////////////////////////////////////////////////////////////////////////////////// /////////////////////////////////////////////////////////////////////////////////////////////////// // ------------------------------------------------------------------------------------------------ // ------------------------------------------------------------------------------------------------ PrisonBehavior::PrisonBehavior( Thing *thing, const ModuleData *moduleData ) : OpenContain( thing, moduleData ) { m_visualList = NULL; } // end PrisonBehavior // ------------------------------------------------------------------------------------------------ // ------------------------------------------------------------------------------------------------ PrisonBehavior::~PrisonBehavior( void ) { } // end ~PrisonBehavior // ------------------------------------------------------------------------------------------------ // ------------------------------------------------------------------------------------------------ void PrisonBehavior::onDelete( void ) { // extend functionality OpenContain::onDelete(); // delete our list Drawable *draw; PrisonVisual *visual; while( m_visualList ) { // delete drawable if found draw = TheGameClient->findDrawableByID( m_visualList->m_drawableID ); if( draw ) TheGameClient->destroyDrawable( draw ); // delete element and set next to head visual = m_visualList->m_next; m_visualList->deleteInstance(); m_visualList = visual; } // end while } // end onDelete // ------------------------------------------------------------------------------------------------ // ------------------------------------------------------------------------------------------------ void PrisonBehavior::onContaining( Object *obj, Bool wasSelected ) { // extend functionality OpenContain::onContaining( obj, wasSelected ); // objects inside a prison are held obj->setDisabled( DISABLED_HELD ); // if we show visuals, make one const PrisonBehaviorModuleData *modData = getPrisonBehaviorModuleData(); if( modData->m_showPrisoners ) addVisual( obj ); } // end onContaining // ------------------------------------------------------------------------------------------------ // ------------------------------------------------------------------------------------------------ void PrisonBehavior::onRemoving( Object *obj ) { // if we show visuals, remove one const PrisonBehaviorModuleData *modData = getPrisonBehaviorModuleData(); if( modData->m_showPrisoners ) removeVisual( obj ); // object is no longer held inside a garrisoned building obj->clearDisabled( DISABLED_HELD ); // extend functionality OpenContain::onRemoving( obj ); } // end onRemoving /////////////////////////////////////////////////////////////////////////////////////////////////// // PRIVATE //////////////////////////////////////////////////////////////////////////////////////// /////////////////////////////////////////////////////////////////////////////////////////////////// // ------------------------------------------------------------------------------------------------ /** Pick a random location inside the prison yard */ // ------------------------------------------------------------------------------------------------ void PrisonBehavior::pickVisualLocation( Coord3D *pos ) { Object *us = getObject(); const PrisonBehaviorModuleData *modData = getPrisonBehaviorModuleData(); Int i; // sanity if( pos == NULL ) return; // initialize the picked location to that of the prison center Coord3D pickedLocation = *us->getPosition(); // get the positions of the bones that make up the prison yard area const Int MAX_YARD_BONES = 16; Coord3D yardPositions[ MAX_YARD_BONES ]; Int yardBones = us->getMultiLogicalBonePosition( modData->m_prisonYardBonePrefix.str(), MAX_YARD_BONES, yardPositions, NULL ); // // we must have at least 3 bone locations to make a yard polygon, otherwise we'll // default to the object position // if( yardBones >= 3 ) { // find the bounding region of the yard area Region2D yardRegion; yardRegion.lo.x = yardPositions[ 0 ].x; yardRegion.lo.y = yardPositions[ 0 ].y; yardRegion.hi.x = yardPositions[ 0 ].x; yardRegion.hi.y = yardPositions[ 0 ].y; for( i = 1; i < yardBones; i++ ) { if( yardPositions[ i ].x < yardRegion.lo.x ) yardRegion.lo.x = yardPositions[ i ].x; if( yardPositions[ i ].y < yardRegion.lo.y ) yardRegion.lo.y = yardPositions[ i ].y; if( yardPositions[ i ].x > yardRegion.hi.x ) yardRegion.hi.x = yardPositions[ i ].x; if( yardPositions[ i ].y > yardRegion.hi.y ) yardRegion.hi.y = yardPositions[ i ].y; } // end for i // // now that we have a yard region, the default visual position will be in the middle // of the yard region instead of the position of our object // pickedLocation.x = yardRegion.lo.x + yardRegion.width() / 2.0f; pickedLocation.y = yardRegion.lo.y + yardRegion.height() / 2.0f; // NOTE: pickedLocation.z is left alone at the object center Z // loop till we find a valid location that is inside the yard area Int maxTries = 32; Coord3D loc; for( i = 0; i < maxTries; ++i ) { // pick a location loc.x = GameLogicRandomValueReal( yardRegion.lo.x, yardRegion.hi.x ); loc.y = GameLogicRandomValueReal( yardRegion.lo.y, yardRegion.hi.y ); loc.z = pickedLocation.z; // must be inside the yard polygon if( PointInsideArea2D( &loc, yardPositions, yardBones ) == TRUE ) { // use this location, leave Z alone as the center of the prison pickedLocation = loc; break; // exit for i } // end if } // end for i } // end if // return the location picked *pos = pickedLocation; } // end pickVisualLocation // ------------------------------------------------------------------------------------------------ /** Add prisoner visual to the prison yard */ // ------------------------------------------------------------------------------------------------ void PrisonBehavior::addVisual( Object *obj ) { // sanity if( obj == NULL ) return; // create a drawable Drawable *draw = TheThingFactory->newDrawable( obj->getTemplate() ); // set the color of the drawable to that of the object if (TheGlobalData->m_timeOfDay == TIME_OF_DAY_NIGHT) draw->setIndicatorColor( obj->getNightIndicatorColor() ); else draw->setIndicatorColor( obj->getIndicatorColor() ); // pick a location insid the prison yard Coord3D pos; pickVisualLocation( &pos ); // place drawable withing the prison yard area draw->setPosition( &pos ); draw->setOrientation( GameLogicRandomValueReal( 0, TWO_PI ) ); DrawableInfo *drawInfo=draw->getDrawableInfo(); drawInfo->m_shroudStatusObjectID=getObject()->getID(); // record this object/drawable pair PrisonVisual *visual = newInstance(PrisonVisual); visual->m_objectID = obj->getID(); visual->m_drawableID = draw->getID(); visual->m_next = m_visualList; m_visualList = visual; } // end addVisual // ------------------------------------------------------------------------------------------------ /** Remove prisoner visual from the prison yard */ // ------------------------------------------------------------------------------------------------ void PrisonBehavior::removeVisual( Object *obj ) { // sanity if( obj == NULL ) return; // initialize a drawable ID to invalid DrawableID drawableID = INVALID_DRAWABLE_ID; // find visual info in our list, once found, take this opportunity to remove it from that list PrisonVisual *visual, *prevVisual = NULL; for( visual = m_visualList; visual; visual = visual->m_next ) { // is this the one we're looking for if( visual->m_objectID == obj->getID() ) { // record the information we need here drawableID = visual->m_drawableID; // remove from list if( prevVisual ) prevVisual->m_next = visual->m_next; else m_visualList = visual->m_next; // delete the element visual->deleteInstance(); break; // exit for } // end if // keep a pointer to the previous element prevVisual = visual; } // end for // find the drawable visual and destroy it Drawable *draw = TheGameClient->findDrawableByID( drawableID ); if( draw ) TheGameClient->destroyDrawable( draw ); } // end removeVisual // ------------------------------------------------------------------------------------------------ /** CRC */ // ------------------------------------------------------------------------------------------------ void PrisonBehavior::crc( Xfer *xfer ) { // extend base class OpenContain::crc( xfer ); } // end crc // ------------------------------------------------------------------------------------------------ /** Xfer method * Version Info: * 1: Initial version */ // ------------------------------------------------------------------------------------------------ void PrisonBehavior::xfer( Xfer *xfer ) { // version XferVersion currentVersion = 1; XferVersion version = currentVersion; xfer->xferVersion( &version, currentVersion ); // extend base class OpenContain::xfer( xfer ); // count and data for the prison visuals UnsignedShort visualCount = 0; PrisonVisual *visual; for( visual = m_visualList; visual; visual = visual->m_next ) visualCount++; xfer->xferUnsignedShort( &visualCount ); if( xfer->getXferMode() == XFER_SAVE ) { // write all data for( visual = m_visualList; visual; visual = visual->m_next ) { // object id xfer->xferObjectID( &visual->m_objectID ); // drawable id xfer->xferDrawableID( &visual->m_drawableID ); } // end for, visual } // end if, save else { // the visual list should be empty if( m_visualList != NULL ) { DEBUG_CRASH(( "PrisonBehavior::xfer - the visual list should be empty but is not\n" )); throw SC_INVALID_DATA; } // end if // read each item for( UnsignedShort i = 0; i < visualCount; ++i ) { // allocate a new visual and tie to list visual = newInstance(PrisonVisual); visual->m_next = m_visualList; m_visualList = visual; // read object id xfer->xferObjectID( &visual->m_objectID ); // read drawable id xfer->xferDrawableID( &visual->m_drawableID ); } // end for, i } // end else, load } // end xfer // ------------------------------------------------------------------------------------------------ /** Load post process */ // ------------------------------------------------------------------------------------------------ void PrisonBehavior::loadPostProcess( void ) { // extend base class OpenContain::loadPostProcess(); } // end loadPostProcess #endif