/* ** 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: CreateCrateDie.cpp ///////////////////////////////////////////////////////////////////////////// // Author: Graham Smallwood, February 2002 // Desc: A chance to create a crate on death according to certain condition checks /////////////////////////////////////////////////////////////////////////////////////////////////// #include "PreRTS.h" // This must go first in EVERY cpp file int the GameEngine #include "Common/PlayerList.h" #include "Common/Player.h" #include "Common/RandomValue.h" #include "Common/ThingFactory.h" #include "Common/ThingTemplate.h" #include "Common/Xfer.h" #include "GameLogic/CrateSystem.h" #include "GameLogic/ExperienceTracker.h" #include "GameLogic/GameLogic.h" #include "GameLogic/Object.h" #include "GameLogic/PartitionManager.h" #include "GameLogic/Module/CreateCrateDie.h" #include "GameLogic/Module/AIUpdate.h" //------------------------------------------------------------------------------------------------- //------------------------------------------------------------------------------------------------- CreateCrateDie::CreateCrateDie( Thing *thing, const ModuleData* moduleData ) : DieModule( thing, moduleData ) { } //------------------------------------------------------------------------------------------------- //------------------------------------------------------------------------------------------------- CreateCrateDie::~CreateCrateDie( void ) { } void CreateCrateDieModuleData::parseCrateData( INI* ini, void *instance, void * /*store*/, const void* /*userData*/ ) { CreateCrateDieModuleData* self = (CreateCrateDieModuleData*)instance; AsciiString crateName = ini->getNextToken(); self->m_crateNameList.push_back( crateName ); } //------------------------------------------------------------------------------------------------- /** The die callback. */ //------------------------------------------------------------------------------------------------- void CreateCrateDie::onDie( const DamageInfo * damageInfo ) { if (!isDieApplicable(damageInfo)) return; CrateTemplate const *currentCrateData = NULL; Object *killer = TheGameLogic->findObjectByID( damageInfo->in.m_sourceID ); Object *me = getObject(); if( killer && killer->getRelationship( me ) == ALLIES ) return; //Nope, no crate for killing ally at all. for( AsciiStringListConstIterator iter = getCreateCrateDieModuleData()->m_crateNameList.begin(); iter != getCreateCrateDieModuleData()->m_crateNameList.end(); iter++ ) { currentCrateData = TheCrateSystem->findCrateTemplate( *iter ); if( currentCrateData ) { if( ! testCreationChance( currentCrateData ) ) continue;// always test this if( (currentCrateData->m_veterancyLevel != LEVEL_INVALID) && ! testVeterancyLevel( currentCrateData ) ) continue; //If this is set up to test and it fails if( KINDOFMASK_ANY_SET(currentCrateData->m_killedByTypeKindof) && !testKillerType( currentCrateData, killer ) ) continue; //If this is set up to test and it fails if( (currentCrateData->m_killerScience != SCIENCE_INVALID) && !testKillerScience( currentCrateData, killer ) ) continue; //If this is set up to test and it fails Object *crate = createCrate( currentCrateData );//Make the crate if( crate ) { // Design needs to set ownership of crates sometimes if( currentCrateData->m_isOwnedByMaker ) { crate->setTeam( me->getControllingPlayer()->getDefaultTeam() ); } if (killer) { // If the killer is a computer controlled player, notify that the crate exists. if (killer->getControllingPlayer() && killer->getControllingPlayer()->getPlayerType()==PLAYER_COMPUTER) { AIUpdateInterface *ai = killer->getAIUpdateInterface(); if (ai) { ai->notifyCrate( crate->getID() ); } } } } } } } Bool CreateCrateDie::testCreationChance( CrateTemplate const *currentCrateData ) { Real testAgainst = currentCrateData->m_creationChance; Real testWith = GameLogicRandomValueReal( 0, 1 ); return testWith < testAgainst; } Bool CreateCrateDie::testVeterancyLevel( CrateTemplate const *currentCrateData ) { VeterancyLevel testAgainst = currentCrateData->m_veterancyLevel; VeterancyLevel testWith = getObject()->getVeterancyLevel(); return testAgainst == testWith; } Bool CreateCrateDie::testKillerType( CrateTemplate const *currentCrateData, Object *killer ) { if( killer == NULL ) return FALSE; // Must match the whole group of bits set in the KilledBy description (most likely One). if( ! killer->getTemplate()->isKindOfMulti( currentCrateData->m_killedByTypeKindof, KINDOFMASK_NONE ) ) return FALSE; return TRUE; } Bool CreateCrateDie::testKillerScience( CrateTemplate const *currentCrateData, Object *killer ) { if( killer == NULL ) return FALSE; // killer's player must have the listed science Player *killerPlayer = killer->getControllingPlayer(); if( killerPlayer == NULL ) return FALSE; if( ! killerPlayer->hasScience( currentCrateData->m_killerScience ) ) return FALSE; return TRUE; } Object *CreateCrateDie::createCrate( CrateTemplate const *currentCrateData ) { Coord3D centerPoint = *getObject()->getPosition(); PathfindLayerEnum layer = getObject()->getLayer(); // CreationChance is used for the success of this block, but this block can have any number of potential actual crates Real multipleCratePick = GameLogicRandomValueReal( 0, 1 ); Real multipleCrateRunningTotal = 0; AsciiString crateName = ""; for( crateCreationEntryConstIterator iter = currentCrateData->m_possibleCrates.begin(); iter != currentCrateData->m_possibleCrates.end(); iter++ ) { multipleCrateRunningTotal += (*iter).crateChance; if( multipleCrateRunningTotal > multipleCratePick ) { // Run through the list of possibles, and if the sum of the chances is greater than my random pick, // then this is the correct one. (This simulates contiguous %, or weighted distribution) crateName = (*iter).crateName; break; } } // At this point, I could very well have a "" for the type, if the Designer didn't make the sum of chances 1 ThingTemplate const *crateType = TheThingFactory->findTemplate( crateName ); if( crateType == NULL ) return NULL; Bool spotFound = FALSE; Coord3D creationPoint; FindPositionOptions fpOptions; fpOptions.minRadius = 0.0f; fpOptions.maxRadius = 5.0f; fpOptions.relationshipObject = getObject(); fpOptions.flags = FPF_IGNORE_ALLY_OR_NEUTRAL_UNITS; // So the dead guy won't block, nor will his dead hulk. if (layer != LAYER_GROUND) { creationPoint = centerPoint; spotFound = true; } else if( ThePartitionManager->findPositionAround( ¢erPoint, &fpOptions, &creationPoint ) ) { spotFound = TRUE; } else { // If the tight ignore units scan fails, then try a great big scan so we appear on the edge // of the large dead thing (building rubble) fpOptions.minRadius = 0.0f; fpOptions.maxRadius = 125.0f; fpOptions.relationshipObject = NULL; fpOptions.flags = FPF_NONE; if( ThePartitionManager->findPositionAround( ¢erPoint, &fpOptions, &creationPoint ) ) { spotFound = TRUE; } } if( spotFound ) { Object *newCrate = TheThingFactory->newObject( crateType, NULL ); newCrate->setPosition( &creationPoint ); newCrate->setOrientation( GameLogicRandomValueReal( 0, 2*PI ) ); newCrate->setLayer(layer); Drawable *crateDrawable = newCrate->getDrawable(); if( crateDrawable ) { crateDrawable->setTerrainDecal(TERRAIN_DECAL_CRATE); crateDrawable->setTerrainDecalSize(2.5f * newCrate->getGeometryInfo().getMajorRadius(), 2.5f * newCrate->getGeometryInfo().getMajorRadius() ) ; crateDrawable->setTerrainDecalFadeTarget(1.0f, 0.03f); } return newCrate; } return NULL; } // ------------------------------------------------------------------------------------------------ /** CRC */ // ------------------------------------------------------------------------------------------------ void CreateCrateDie::crc( Xfer *xfer ) { // extend base class DieModule::crc( xfer ); } // end crc // ------------------------------------------------------------------------------------------------ /** Xfer method * Version Info: * 1: Initial version */ // ------------------------------------------------------------------------------------------------ void CreateCrateDie::xfer( Xfer *xfer ) { // version XferVersion currentVersion = 1; XferVersion version = currentVersion; xfer->xferVersion( &version, currentVersion ); // extend base class DieModule::xfer( xfer ); } // end xfer // ------------------------------------------------------------------------------------------------ /** Load post process */ // ------------------------------------------------------------------------------------------------ void CreateCrateDie::loadPostProcess( void ) { // extend base class DieModule::loadPostProcess(); } // end loadPostProcess