/* ** 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: ProductionUpdate.h /////////////////////////////////////////////////////////////////////// // Author: Colin Day, March 2002 // Desc: This module allows things to be "constructed" from a building /////////////////////////////////////////////////////////////////////////////////////////////////// #pragma once #ifndef __PRODUCTIONUPDATE_H_ #define __PRODUCTIONUPDATE_H_ // USER INCLUDES ////////////////////////////////////////////////////////////////////////////////// #include "Common/ModelState.h" #include "GameLogic/Module/DieModule.h" #include "GameLogic/Module/UpdateModule.h" // FORWARD REFERNCES ////////////////////////////////////////////////////////////////////////////// class ProductionEntry; class ThingTemplate; class UpgradeTemplate; /////////////////////////////////////////////////////////////////////////////////////////////////// enum ProductionID { PRODUCTIONID_INVALID = 0 }; enum ProductionType { PRODUCTION_INVALID = 0, PRODUCTION_UNIT, PRODUCTION_UPGRADE }; //------------------------------------------------------------------------------------------------- /** A ProductionEntry is a single entry representing something that we are supposed to * produce */ //------------------------------------------------------------------------------------------------- class ProductionEntry : public MemoryPoolObject { friend class ProductionUpdate; MEMORY_POOL_GLUE_WITH_USERLOOKUP_CREATE( ProductionEntry, "ProductionEntry" ) public: ProductionEntry( void ); // virtual destructor provided by memory pool object /// query what kind of thing is being produced by this entry const ThingTemplate *getProductionObject( void ) const { return m_objectToProduce; } /// query what kind of upgrade is being produced by this entry const UpgradeTemplate *getProductionUpgrade( void ) const { return m_upgradeToResearch; } /// query the production type ProductionType getProductionType( void ) const { return m_type; } /// how much progress is done on this entry Real getPercentComplete( void ) const { return m_percentComplete; } /// get the unique (to the producer object) production ID ProductionID getProductionID( void ) const { return m_productionID; } Int getProductionQuantity() const { return m_productionQuantityTotal; } //How many I try to make Int getProductionQuantityRemaining() const { return m_productionQuantityTotal - m_productionQuantityProduced; }//How many I have made void oneProductionSuccessful() { ++m_productionQuantityProduced; m_exitDoor = DOOR_NONE_AVAILABLE; }//increment, and mark door to re-reserve ExitDoorType getExitDoor() const { return m_exitDoor; } void setExitDoor(ExitDoorType exitDoor) { m_exitDoor = exitDoor; } protected: ProductionType m_type; ///< production type union { const ThingTemplate *m_objectToProduce; ///< what we're going to produce const UpgradeTemplate *m_upgradeToResearch; ///< what upgrade we're researching }; ProductionID m_productionID; ///< our very own production ID! Real m_percentComplete; ///< percent our construction is complete Int m_framesUnderConstruction; ///< counter for how many frames we've been under construction (incremented once per update) Int m_productionQuantityTotal; ///< it is now possible to construct multiple units simultaneously. Int m_productionQuantityProduced; ///< And we need to allow pausing within an entry, so we keep track of number of sub-successes ExitDoorType m_exitDoor; ProductionEntry *m_next; ///< next in list ProductionEntry *m_prev; ///< prev in list }; //------------------------------------------------------------------------------------------------- struct QuantityModifier { AsciiString m_templateName; Int m_quantity; }; //------------------------------------------------------------------------------------------------- class ProductionUpdateModuleData : public UpdateModuleData { public: Int m_numDoorAnimations; ///< has a door animation(s) upon unit built and exit UnsignedInt m_doorOpeningTime; ///< in frames, time it takes the door to open UnsignedInt m_doorWaitOpenTime; ///< in frames, time we should leave the door open UnsignedInt m_doorClosingTime; ///< in frames, time it takes to close the door UnsignedInt m_constructionCompleteDuration; ///< in frames, how long we state in "construction complete" condition after making something std::vector m_quantityModifiers; ///< Quantity modifiers modify the number of specified object to created whenever produced. Int m_maxQueueEntries; ///< max things that can be queued at once. DisabledMaskType m_disabledTypesToProcess; ProductionUpdateModuleData( void ); static void buildFieldParse(MultiIniFieldParse& p); static void parseAppendQuantityModifier( INI* ini, void *instance, void *store, const void *userData ); }; //------------------------------------------------------------------------------------------------- enum CanMakeType; // ------------------------------------------------------------------------------------------------ // ------------------------------------------------------------------------------------------------ class ProductionUpdateInterface { public: virtual CanMakeType canQueueCreateUnit( const ThingTemplate *unitType ) const = 0; virtual CanMakeType canQueueUpgrade( const UpgradeTemplate *upgrade ) const = 0; virtual ProductionID requestUniqueUnitID( void ) = 0; virtual Bool queueUpgrade( const UpgradeTemplate *upgrade ) = 0; virtual void cancelUpgrade( const UpgradeTemplate *upgrade ) = 0; virtual Bool isUpgradeInQueue( const UpgradeTemplate *upgrade ) const = 0; virtual UnsignedInt countUnitTypeInQueue( const ThingTemplate *unitType ) const = 0; virtual Bool queueCreateUnit( const ThingTemplate *unitType, ProductionID productionID ) = 0; virtual void cancelUnitCreate( ProductionID productionID ) = 0; virtual void cancelAllUnitsOfType( const ThingTemplate *unitType) = 0; virtual void cancelAndRefundAllProduction( void ) = 0; virtual UnsignedInt getProductionCount( void ) const = 0; virtual const ProductionEntry *firstProduction( void ) const = 0; virtual const ProductionEntry *nextProduction( const ProductionEntry *p ) const = 0; virtual void setHoldDoorOpen(ExitDoorType exitDoor, Bool holdIt) = 0; //These functions keep track of the special power construction of a new building via a special power instead of standard production interface. //This was added for the sneak attack building functionality. virtual const CommandButton* getSpecialPowerConstructionCommandButton() const = 0; virtual void setSpecialPowerConstructionCommandButton( const CommandButton *commandButton ) = 0; }; //------------------------------------------------------------------------------------------------- /** Production modules do actual unit construction */ //------------------------------------------------------------------------------------------------- class ProductionUpdate : public UpdateModule, public ProductionUpdateInterface, public DieModuleInterface { MEMORY_POOL_GLUE_WITH_USERLOOKUP_CREATE( ProductionUpdate, "ProductionUpdate" ) MAKE_STANDARD_MODULE_MACRO_WITH_MODULE_DATA( ProductionUpdate, ProductionUpdateModuleData ) public: ProductionUpdate( Thing *thing, const ModuleData* moduleData ); // virtual destructor prototype provided by MemoryPoolObject static Int getInterfaceMask() { return UpdateModule::getInterfaceMask() | (MODULEINTERFACE_DIE); } // Disabled conditions to process (AI will still process held status) virtual DisabledMaskType getDisabledTypesToProcess() const { return getProductionUpdateModuleData()->m_disabledTypesToProcess; } virtual ProductionUpdateInterface* getProductionUpdateInterface( void ) { return this; } virtual DieModuleInterface* getDie() { return this; } static ProductionUpdateInterface *getProductionUpdateInterfaceFromObject( Object *obj ); virtual CanMakeType canQueueCreateUnit( const ThingTemplate *unitType ) const; virtual CanMakeType canQueueUpgrade( const UpgradeTemplate *upgrade ) const; /** this method is used to request a unique ID to assign to the production of a single unit. It is unique to all units that can be created from this source object, but is not unique amoung multiple source objects */ virtual ProductionID requestUniqueUnitID( void ) { ProductionID tmp = m_uniqueID; m_uniqueID = (ProductionID)(m_uniqueID+1); return tmp; } virtual Bool queueUpgrade( const UpgradeTemplate *upgrade ); ///< queue upgrade "research" virtual void cancelUpgrade( const UpgradeTemplate *upgrade ); ///< cancel upgrade "research" virtual Bool isUpgradeInQueue( const UpgradeTemplate *upgrade ) const; ///< is the upgrade in our production queue already virtual UnsignedInt countUnitTypeInQueue( const ThingTemplate *unitType ) const; ///< count number of units with matching unit type in the production queue virtual Bool queueCreateUnit( const ThingTemplate *unitType, ProductionID productionID ); ///< queue unit to be produced virtual void cancelUnitCreate( ProductionID productionID ); ///< cancel construction of unit with matching production ID virtual void cancelAllUnitsOfType( const ThingTemplate *unitType); ///< cancel all production of type unitType virtual void cancelAndRefundAllProduction( void ); ///< cancel and refund anything in the production queue virtual UnsignedInt getProductionCount( void ) const { return m_productionCount; } ///< return # of things in the production queue // walking the production list from outside virtual const ProductionEntry *firstProduction( void ) const { return m_productionQueue; } virtual const ProductionEntry *nextProduction( const ProductionEntry *p ) const { return p ? p->m_next : NULL; } virtual void setHoldDoorOpen(ExitDoorType exitDoor, Bool holdIt); virtual UpdateSleepTime update( void ); ///< the update //These functions keep track of the special power construction of a new building via a special power instead of standard production interface. //This was added for the sneak attack building functionality. virtual const CommandButton* getSpecialPowerConstructionCommandButton() const { return m_specialPowerConstructionCommandButton; } virtual void setSpecialPowerConstructionCommandButton( const CommandButton *commandButton ) { m_specialPowerConstructionCommandButton = commandButton; } // DieModuleInterface virtual void onDie( const DamageInfo *damageInfo ); protected: void addToProductionQueue( ProductionEntry *production ); ///< add to *END* of production queue list void removeFromProductionQueue( ProductionEntry *production ); ///< remove production from the queue list void updateDoors(); ///< update the door behavior struct DoorInfo { UnsignedInt m_doorOpenedFrame; ///< for producer objects that have a door open/close animation when they make something UnsignedInt m_doorWaitOpenFrame; ///< frame we entered into wait open state UnsignedInt m_doorClosedFrame; ///< frame any door was closed on Bool m_holdOpen; ///< if T, don't allow door to close }; const CommandButton *m_specialPowerConstructionCommandButton; ///< In a mode to construct a specific building via a special power. (NO NEED TO SAVE DATA ON THIS FIELD) ProductionEntry* m_productionQueue; ///< queue of things we want to build ProductionEntry* m_productionQueueTail; ///< tail pointer for m_productionQueue ProductionID m_uniqueID; ///< unique ID counter for producing units UnsignedInt m_productionCount; ///< # of things in the production queue UnsignedInt m_constructionCompleteFrame; ///< frame construction was complete on DoorInfo m_doors[DOOR_COUNT_MAX]; ModelConditionFlags m_clearFlags; ///< flags to clear from model ModelConditionFlags m_setFlags; ///< flags to set in model Bool m_flagsDirty; ///< clearFlags/setFlags needs to be set into the model }; #endif // end __PRODUCTIONUPDATE_H_