/* ** 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: SpawnBehavior.h ////////////////////////////////////////////////////////////////////////// // Author: Graham Smallwood, January 2002 // Colin Day, October 2002 // Desc: Behavior will create and monitor a group of spawned units and replace as needed /////////////////////////////////////////////////////////////////////////////////////////////////// #pragma once #ifndef _SPAWN_BEHAVIOR_H_ #define _SPAWN_BEHAVIOR_H_ const Int SPAWN_UPDATE_RATE = LOGICFRAMES_PER_SECOND/2; ///< This is a low priority module that only needs to be called every this many frames // INCLUDES /////////////////////////////////////////////////////////////////////////////////////// #include "Common/INI.h" #include "GameLogic/Module/BehaviorModule.h" #include "GameLogic/Module/UpdateModule.h" #include "GameLogic/Module/DieModule.h" #include "GameLogic/Module/DamageModule.h" //------------------------------------------------------------------------------------------------- class ThingTemplate; enum CanAttackResult; //------------------------------------------------------------------------------------------------- class SpawnBehaviorModuleData : public BehaviorModuleData { public: Int m_spawnNumberData; ///< How many spawn I maintain Int m_spawnStartNumberData; ///< How many spawn I start with Int m_spawnReplaceDelayData; ///< After this many frames, I can replace one Int m_initialBurst; ///< How many should I make immediately, ignoring the delay? Bool m_isOneShotData; ///< Do I just spawn once and go dormant? Bool m_canReclaimOrphans; ///< Can I reclaim orphans for purposes of spawning Bool m_aggregateHealth; ///< should I calc an offset for the healthbox, averaging all my spawn Bool m_exitByBudding; ///< do I create each new spawn atop an existing one? Bool m_spawnedRequireSpawner; ///< Spawned objects can only exist while the spawner (us) is alive and present Bool m_slavesHaveFreeWill; ///< Slaves with free will don't attack when parent attacks. DamageTypeFlags m_damageTypesToPropagateToSlaves; std::vector m_spawnTemplateNameData; DieMuxData m_dieMuxData; SpawnBehaviorModuleData() { m_spawnNumberData = 0; m_spawnReplaceDelayData = 0; //Added By Sadullah Nader //Initialization(s) inserted m_spawnStartNumberData = 0; // m_initialBurst = 0; m_isOneShotData = FALSE; m_canReclaimOrphans = FALSE; m_aggregateHealth = FALSE; m_exitByBudding = FALSE; m_spawnTemplateNameData.clear(); m_spawnedRequireSpawner = FALSE; m_slavesHaveFreeWill = FALSE; } static void buildFieldParse(MultiIniFieldParse& p) { BehaviorModuleData::buildFieldParse(p); static const FieldParse dataFieldParse[] = { { "SpawnNumber", INI::parseInt, NULL, offsetof( SpawnBehaviorModuleData, m_spawnNumberData ) }, { "SpawnReplaceDelay", INI::parseDurationUnsignedInt, NULL, offsetof( SpawnBehaviorModuleData, m_spawnReplaceDelayData ) }, { "OneShot", INI::parseBool, NULL, offsetof( SpawnBehaviorModuleData, m_isOneShotData ) }, { "CanReclaimOrphans", INI::parseBool, NULL, offsetof( SpawnBehaviorModuleData, m_canReclaimOrphans ) }, { "AggregateHealth", INI::parseBool, NULL, offsetof( SpawnBehaviorModuleData, m_aggregateHealth ) }, { "ExitByBudding", INI::parseBool, NULL, offsetof( SpawnBehaviorModuleData, m_exitByBudding ) }, { "SpawnTemplateName", INI::parseAsciiStringVectorAppend,NULL, offsetof( SpawnBehaviorModuleData, m_spawnTemplateNameData ) }, { "SpawnedRequireSpawner", INI::parseBool, NULL, offsetof( SpawnBehaviorModuleData, m_spawnedRequireSpawner ) }, { "PropagateDamageTypesToSlavesWhenExisting", INI::parseDamageTypeFlags, NULL, offsetof( SpawnBehaviorModuleData, m_damageTypesToPropagateToSlaves ) }, { "InitialBurst", INI::parseInt, NULL, offsetof( SpawnBehaviorModuleData, m_initialBurst ) }, { "SlavesHaveFreeWill", INI::parseBool, NULL, offsetof( SpawnBehaviorModuleData, m_slavesHaveFreeWill ) }, { 0, 0, 0, 0 } }; p.add(dataFieldParse); p.add(DieMuxData::getFieldParse(), offsetof( SpawnBehaviorModuleData, m_dieMuxData )); } }; // ------------------------------------------------------------------------------------------------ // ------------------------------------------------------------------------------------------------ class SpawnBehaviorInterface { public: virtual Bool maySpawnSelfTaskAI( Real maxSelfTaskersRatio ) = 0; virtual void onSpawnDeath( ObjectID deadSpawn, DamageInfo *damageInfo ) = 0; virtual Object* getClosestSlave( const Coord3D *pos ) = 0; virtual void orderSlavesToAttackTarget( Object *target, Int maxShotsToFire, CommandSourceType cmdSource ) = 0; virtual void orderSlavesToAttackPosition( const Coord3D *pos, Int maxShotsToFire, CommandSourceType cmdSource ) = 0; virtual CanAttackResult getCanAnySlavesAttackSpecificTarget( AbleToAttackType attackType, const Object *target, CommandSourceType cmdSource ) = 0; virtual CanAttackResult getCanAnySlavesUseWeaponAgainstTarget( AbleToAttackType attackType, const Object *victim, const Coord3D *pos, CommandSourceType cmdSource ) = 0; virtual Bool canAnySlavesAttack() = 0; virtual void orderSlavesToGoIdle( CommandSourceType cmdSource ) = 0; virtual void orderSlavesDisabledUntil( DisabledType type, UnsignedInt frame ) = 0; virtual void orderSlavesToClearDisabled( DisabledType type ) = 0; virtual void giveSlavesStealthUpgrade( Bool grantStealth ) = 0; virtual Bool areAllSlavesStealthed() const = 0; virtual void revealSlaves() = 0; virtual Bool doSlavesHaveFreedom() const = 0; }; // ------------------------------------------------------------------------------------------------ //------------------------------------------------------------------------------------------------- class SpawnBehavior : public UpdateModule, public SpawnBehaviorInterface, public DieModuleInterface, public DamageModuleInterface { MEMORY_POOL_GLUE_WITH_USERLOOKUP_CREATE( SpawnBehavior, "SpawnBehavior" ) MAKE_STANDARD_MODULE_MACRO_WITH_MODULE_DATA( SpawnBehavior, SpawnBehaviorModuleData ) public: SpawnBehavior( Thing *thing, const ModuleData* moduleData ); // virtual destructor prototype provided by memory pool declaration // module methods static Int getInterfaceMask( void ) { return (MODULEINTERFACE_UPDATE) | (MODULEINTERFACE_DIE) | (MODULEINTERFACE_DAMAGE); } virtual void onDelete( void ); virtual UpdateModuleInterface *getUpdate() { return this; } virtual DieModuleInterface *getDie() { return this; } virtual DamageModuleInterface *getDamage() { return this; } virtual SpawnBehaviorInterface* getSpawnBehaviorInterface() { return this; } // update methods virtual UpdateSleepTime update(); // die methods virtual void onDie( const DamageInfo *damageInfo ); // damage methods virtual void onDamage( DamageInfo *damageInfo ); virtual void onHealing( DamageInfo *damageInfo ) { } virtual void onBodyDamageStateChange( const DamageInfo* damageInfo, BodyDamageType oldState, BodyDamageType newState) { } // SpawnBehaviorInterface methods virtual Bool maySpawnSelfTaskAI( Real maxSelfTaskersRatio ); virtual void onSpawnDeath( ObjectID deadSpawn, DamageInfo *damageInfo ); ///< Something we spawned and set up to tell us it died just died. virtual Object* getClosestSlave( const Coord3D *pos ); virtual void orderSlavesToAttackTarget( Object *target, Int maxShotsToFire, CommandSourceType cmdSource ); virtual void orderSlavesToAttackPosition( const Coord3D *pos, Int maxShotsToFire, CommandSourceType cmdSource ); virtual CanAttackResult getCanAnySlavesAttackSpecificTarget( AbleToAttackType attackType, const Object *target, CommandSourceType cmdSource ); virtual CanAttackResult getCanAnySlavesUseWeaponAgainstTarget( AbleToAttackType attackType, const Object *victim, const Coord3D *pos, CommandSourceType cmdSource ); virtual Bool canAnySlavesAttack(); virtual void orderSlavesToGoIdle( CommandSourceType cmdSource ); virtual void orderSlavesDisabledUntil( DisabledType type, UnsignedInt frame ); virtual void orderSlavesToClearDisabled( DisabledType type ); virtual void giveSlavesStealthUpgrade( Bool grantStealth ); virtual Bool areAllSlavesStealthed() const; virtual void revealSlaves(); virtual Bool doSlavesHaveFreedom() const { return getSpawnBehaviorModuleData()->m_slavesHaveFreeWill; } // ********************************************************************************************** // our own methods void stopSpawning(); ///< Whoever owns this module may want to turn it off void startSpawning(); ///< Whoever owns this module may want to turn it on void computeAggregateStates(void); // void notifySelfTasking( Bool isSelfTasking ); private: Bool shouldTryToSpawn(); ///< For my own use, should I even think of spawning Bool createSpawn(); ///< Actual work of creating a guy const ThingTemplate* m_spawnTemplate; ///< What it is I spawn Int m_oneShotCountdown; ///< and if so, this is what "once" entails Int m_framesToWait; Int m_firstBatchCount; /// is a good one. Otherwise override it. typedef std::list intList; typedef std::list::iterator intListIterator; typedef std::list::reverse_iterator intListReverseIterator; typedef std::list objectIDList; typedef std::list::iterator objectIDListIterator; typedef std::list::reverse_iterator objectIDListReverseIterator; intList m_replacementTimes; ///< A list of frame times that I need to create new spawns objectIDList m_spawnIDs; ///< My darling little spawns. I need to keep track of them explicitly for the Slave type stuff Bool m_active; ///< Am I currently turned on Object *reclaimOrphanSpawn( void ); ///< find existing orphaned spawn object if present Bool m_aggregateHealth; ///< should I calc an offset for the healthbox, averaging all my spawn Bool m_initialBurstTimesInited; Int m_spawnCount; ///< so I can track for zero = kill; (aggregate) UnsignedInt m_selfTaskingSpawnCount; ///< How many of my spawn have I authorized to do their own thing? UnsignedInt m_initialBurstCountdown; std::vector::const_iterator m_templateNameIterator; }; #endif