/* ** 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: ResourceGatheringManager.cpp /////////////////////////////////////////////////////////// // The part of a Player's brain that keeps track of all Resource type Objects and makes // gathering type decisions based on them. // Author: Graham Smallwood, January, 2002 #include "PreRTS.h" // This must go first in EVERY cpp file int the GameEngine #include "Common/ResourceGatheringManager.h" #include "Common/ActionManager.h" #include "Common/Xfer.h" #include "GameLogic/GameLogic.h" #include "GameLogic/Object.h" #include "GameLogic/PartitionManager.h" #include "GameLogic/Module/SupplyTruckAIUpdate.h" #include "GameLogic/Module/SupplyCenterDockUpdate.h" #include "GameLogic/Module/SupplyWarehouseDockUpdate.h" #include "GameLogic/Module/UpdateModule.h" ResourceGatheringManager::ResourceGatheringManager() { } ResourceGatheringManager::~ResourceGatheringManager() { m_supplyWarehouses.erase( m_supplyWarehouses.begin(), m_supplyWarehouses.end() ); m_supplyCenters.erase( m_supplyCenters.begin(), m_supplyCenters.end() ); } void ResourceGatheringManager::addSupplyCenter( Object *newCenter ) { if( newCenter == NULL ) return; m_supplyCenters.push_back( newCenter->getID() ); } void ResourceGatheringManager::removeSupplyCenter( Object *oldCenter ) { if( oldCenter == NULL ) return; ObjectID targetID = oldCenter->getID(); objectIDListIterator iterator = m_supplyCenters.begin(); while( iterator != m_supplyCenters.end() ) { if( targetID == *iterator ) { iterator = m_supplyCenters.erase( iterator ); } else iterator++; } } void ResourceGatheringManager::addSupplyWarehouse( Object *newWarehouse ) { if( newWarehouse == NULL ) return; m_supplyWarehouses.push_back( newWarehouse->getID() ); } void ResourceGatheringManager::removeSupplyWarehouse( Object *oldWarehouse ) { if( oldWarehouse == NULL ) return; ObjectID targetID = oldWarehouse->getID(); objectIDListIterator iterator = m_supplyWarehouses.begin(); while( iterator != m_supplyWarehouses.end() ) { if( targetID == *iterator ) { iterator = m_supplyWarehouses.erase( iterator ); } else iterator++; } } static Real computeRelativeCost( Object *queryObject, Object *destObject, Real *pureDistanceSquared ) { /** @todo This gets filled with Pathfinding computations, analysis of Boxes remaining, Threat calculations, paths of other trucks, and other fancy stuff. */ //A good score is a very small number. if( queryObject == NULL || destObject == NULL ) return FLT_MAX; if( !TheActionManager->canTransferSuppliesAt(queryObject, destObject) ) return FLT_MAX;// Handles emptyness and alliances DockUpdateInterface *dockInterface = destObject->getDockUpdateInterface(); if( !dockInterface->isClearToApproach( queryObject ) ) return FLT_MAX; // since we don't care about the distance as a distance per se, but rather as // a goodness-factor, save some time by getting the dist-sqr (srj) Real distSquared = ThePartitionManager->getDistanceSquared(queryObject, destObject, FROM_CENTER_3D); // I need the distance, but I don't want to count on the coincidence that // the abstract 'cost' this function returns happens to be just the distance, since it could // become more complicated if( pureDistanceSquared ) *pureDistanceSquared = distSquared; return distSquared; } Object *ResourceGatheringManager::findBestSupplyWarehouse( Object *queryObject ) { Object *bestWarehouse = NULL; Real maxDistanceSquared = 100000; if( ( queryObject == NULL ) || ( queryObject->getAI() == NULL ) ) return NULL; SupplyTruckAIInterface *supplyTruckAI = queryObject->getAI()->getSupplyTruckAIInterface(); if( supplyTruckAI ) { // Check for a dock override being set. ObjectID dockID = supplyTruckAI->getPreferredDockID(); Object *dock = TheGameLogic->findObjectByID(dockID); if( dock ) { static const NameKeyType key_warehouseUpdate = NAMEKEY("SupplyWarehouseDockUpdate"); SupplyWarehouseDockUpdate *warehouseModule = (SupplyWarehouseDockUpdate*)dock->findUpdateModule( key_warehouseUpdate ); //If remotely okay, let User win. if( warehouseModule && computeRelativeCost( queryObject, dock, NULL ) != FLT_MAX ) return dock; } // Please note, there is not a separate Warehouse and Center memory by Design. Because // we lack a UI way to click Warehouse and drag to center to set up a specific path, the // practical realization has been made that you do not want separate memory. // Design wants a harvester to give up and return to base if it is "too far" to the warehouse. // Note, the "PreferedDock" will override this, and there is no distance max on Centers. maxDistanceSquared = supplyTruckAI->getWarehouseScanDistance() * supplyTruckAI->getWarehouseScanDistance(); } //Otherwise, search for a good one. Real bestCost = FLT_MAX; objectIDListIterator iterator = m_supplyWarehouses.begin(); while( iterator != m_supplyWarehouses.end() ) { ObjectID currentID = *iterator; Object *currentWarehouse =TheGameLogic->findObjectByID(currentID); if( currentWarehouse == NULL ) { iterator = m_supplyWarehouses.erase( iterator ); } else { Real distanceSquared; Real currentCost = computeRelativeCost( queryObject, currentWarehouse, &distanceSquared ); if( (currentCost < bestCost) && (distanceSquared < maxDistanceSquared) ) { bestWarehouse = currentWarehouse; bestCost = currentCost; } iterator++; } } return bestWarehouse; } Object *ResourceGatheringManager::findBestSupplyCenter( Object *queryObject ) { Object *bestCenter = NULL; if( ( queryObject == NULL ) || ( queryObject->getAI() == NULL ) ) return NULL; SupplyTruckAIInterface *supplyTruckAI = queryObject->getAI()->getSupplyTruckAIInterface(); if( supplyTruckAI ) { // Check for a dock override being set. ObjectID dockID = supplyTruckAI->getPreferredDockID(); Object *dock = TheGameLogic->findObjectByID(dockID); if( dock ) { static const NameKeyType key_centerUpdate = NAMEKEY("SupplyCenterDockUpdate"); SupplyWarehouseDockUpdate *centerModule = (SupplyWarehouseDockUpdate*)dock->findUpdateModule( key_centerUpdate ); //If remotely okay, let User win. if( centerModule && computeRelativeCost( queryObject, dock, NULL ) != FLT_MAX ) return dock; } // Please note, there is not a separate Warehouse and Center memory by Design. Because // we lack a UI way to click Warehouse and drag to center to set up a specific path, the // practical realization has been made that you do not want separate memory. } //Otherwise, search for a good one. Real bestCost = FLT_MAX; objectIDListIterator iterator = m_supplyCenters.begin(); while( iterator != m_supplyCenters.end() ) { ObjectID currentID = *iterator; Object *currentCenter =TheGameLogic->findObjectByID(currentID); if( currentCenter == NULL ) { iterator = m_supplyWarehouses.erase( iterator ); } else { Real currentCost = computeRelativeCost( queryObject, currentCenter, NULL ); if( currentCost < bestCost ) { bestCenter = currentCenter; bestCost = currentCost; } iterator++; } } return bestCenter; } // ------------------------------------------------------------------------------------------------ /** CRC */ // ------------------------------------------------------------------------------------------------ void ResourceGatheringManager::crc( Xfer *xfer ) { } // end crc // ------------------------------------------------------------------------------------------------ /** Xfer method * Version Info: * 1: Initial version */ // ------------------------------------------------------------------------------------------------ void ResourceGatheringManager::xfer( Xfer *xfer ) { // version XferVersion currentVersion = 1; XferVersion version = currentVersion; xfer->xferVersion( &version, currentVersion ); // supply warehouses xfer->xferSTLObjectIDList( &m_supplyWarehouses ); // supply centers xfer->xferSTLObjectIDList( &m_supplyCenters ); } // end xfer // ------------------------------------------------------------------------------------------------ /** Load post process */ // ------------------------------------------------------------------------------------------------ void ResourceGatheringManager::loadPostProcess( void ) { } // end loadPostProcess