/* ** 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: UpgradeModule.h ///////////////////////////////////////////////////////////////////////////////// // Author: Graham Smallwood, March 2002 // Desc: A Module that responds to PlayerUpgrade Bitsetting by executing code once and only once /////////////////////////////////////////////////////////////////////////////////////////////////// #pragma once #ifndef __UPGRADE_MODULE_H_ #define __UPGRADE_MODULE_H_ #include "Common/Module.h" #include "Common/STLTypedefs.h" #include "Common/Upgrade.h" #include "GameClient/Drawable.h" #include "GameClient/FXList.h" #include "GameLogic/Module/BehaviorModule.h" // FORWARD REFERENCES ///////////////////////////////////////////////////////////////////////////// class Player; //------------------------------------------------------------------------------------------------- /** OBJECT DIE MODULE base class */ //------------------------------------------------------------------------------------------------- //------------------------------------------------------------------------------------------------- class UpgradeModuleInterface { public: virtual Bool isAlreadyUpgraded() const = 0; virtual Bool attemptUpgrade( UpgradeMaskType keyMask ) = 0; virtual Bool wouldUpgrade( UpgradeMaskType keyMask ) const = 0; virtual Bool resetUpgrade( UpgradeMaskType keyMask ) = 0; virtual Bool isSubObjectsUpgrade() = 0; virtual void forceRefreshUpgrade() = 0; virtual Bool testUpgradeConditions( UpgradeMaskType keyMask ) const = 0; }; //------------------------------------------------------------------------------------------------- class UpgradeMuxData // does NOT inherit from ModuleData. { public: mutable std::vector m_triggerUpgradeNames; mutable std::vector m_activationUpgradeNames; mutable std::vector m_conflictingUpgradeNames; mutable std::vector m_removalUpgradeNames; mutable const FXList* m_fxListUpgrade; mutable UpgradeMaskType m_activationMask; ///< Activation only supports a single name currently mutable UpgradeMaskType m_conflictingMask; ///< Conflicts support multiple listings, and they are an OR mutable Bool m_requiresAllTriggers; UpgradeMuxData() { m_triggerUpgradeNames.clear(); m_activationUpgradeNames.clear(); m_conflictingUpgradeNames.clear(); m_removalUpgradeNames.clear(); m_fxListUpgrade = NULL; m_activationMask.clear(); m_conflictingMask.clear(); m_requiresAllTriggers = false; } static const FieldParse* getFieldParse() { static const FieldParse dataFieldParse[] = { { "TriggeredBy", INI::parseAsciiStringVector, NULL, offsetof( UpgradeMuxData, m_activationUpgradeNames ) }, { "ConflictsWith", INI::parseAsciiStringVector, NULL, offsetof( UpgradeMuxData, m_conflictingUpgradeNames ) }, { "RemovesUpgrades",INI::parseAsciiStringVector, NULL, offsetof( UpgradeMuxData, m_removalUpgradeNames ) }, { "FXListUpgrade", INI::parseFXList, NULL, offsetof( UpgradeMuxData, m_fxListUpgrade ) }, { "RequiresAllTriggers", INI::parseBool, NULL, offsetof( UpgradeMuxData, m_requiresAllTriggers ) }, { 0, 0, 0, 0 } }; return dataFieldParse; } Bool requiresAllActivationUpgrades() const; void getUpgradeActivationMasks(UpgradeMaskType& activation, UpgradeMaskType& conflicting) const; ///< The first time someone looks at my mask, I'll figure it out. void performUpgradeFX(Object* obj) const; void muxDataProcessUpgradeRemoval(Object* obj) const; Bool isTriggeredBy(const std::string &upgrade) const; }; //------------------------------------------------------------------------------------------------- // implements some handy mix-in guts, but doesn't descend from ObjectModule... useful for Behaviors // that want to implement Upgrades class UpgradeMux : public UpgradeModuleInterface { public: UpgradeMux(); virtual Bool isAlreadyUpgraded() const ; // ***DANGER! DANGER! Don't use this, unless you are forcing an already made upgrade to refresh!! virtual void forceRefreshUpgrade(); virtual Bool attemptUpgrade( UpgradeMaskType keyMask ); virtual Bool wouldUpgrade( UpgradeMaskType keyMask ) const; virtual Bool resetUpgrade( UpgradeMaskType keyMask ); virtual Bool testUpgradeConditions( UpgradeMaskType keyMask ) const; protected: void setUpgradeExecuted(Bool e) { m_upgradeExecuted = e; } virtual void upgradeImplementation( ) = 0; ///< Here's the actual work of Upgrading virtual void getUpgradeActivationMasks(UpgradeMaskType& activation, UpgradeMaskType& conflicting) const = 0; ///< Here's the actual work of Upgrading virtual void performUpgradeFX() = 0; ///< perform the associated fx list virtual Bool requiresAllActivationUpgrades() const = 0; virtual Bool isSubObjectsUpgrade() = 0; virtual void processUpgradeRemoval() = 0; void giveSelfUpgrade(); // // this is not a snapshot class itself, but it is a base class used in conjunction with // multiple inheritance for modules, so those modules need to be able to poke inside // this class and xfer the data here during a snapshot process // virtual void upgradeMuxCRC( Xfer *xfer ); virtual void upgradeMuxXfer( Xfer *xfer); virtual void upgradeMuxLoadPostProcess( void ); private: Bool m_upgradeExecuted; ///< Upgrade only executes once }; //------------------------------------------------------------------------------------------------- struct UpgradeModuleData : public BehaviorModuleData { public: UpgradeMuxData m_upgradeMuxData; static void buildFieldParse(MultiIniFieldParse& p) { ModuleData::buildFieldParse(p); p.add(UpgradeMuxData::getFieldParse(), offsetof( UpgradeModuleData, m_upgradeMuxData )); } }; //------------------------------------------------------------------------------------------------- class UpgradeModule : public BehaviorModule, public UpgradeMux { MEMORY_POOL_GLUE_ABC( UpgradeModule ) MAKE_STANDARD_MODULE_MACRO_ABC( UpgradeModule ) MAKE_STANDARD_MODULE_DATA_MACRO_ABC(UpgradeModule, UpgradeModuleData) public: UpgradeModule( Thing *thing, const ModuleData* moduleData ); // virtual destructor prototype defined by MemoryPoolObject // module methods static Int getInterfaceMask() { return MODULEINTERFACE_UPGRADE; } // BehaviorModule virtual UpgradeModuleInterface* getUpgrade() { return this; } bool isTriggeredBy(const std::string & upgrade) const { return getUpgradeModuleData()->m_upgradeMuxData.isTriggeredBy(upgrade); } protected: virtual void processUpgradeRemoval() { // I can't take it any more. Let the record show that I think the UpgradeMux multiple inheritence is CRAP. getUpgradeModuleData()->m_upgradeMuxData.muxDataProcessUpgradeRemoval(getObject()); } virtual Bool requiresAllActivationUpgrades() const { return getUpgradeModuleData()->m_upgradeMuxData.m_requiresAllTriggers; } virtual void getUpgradeActivationMasks(UpgradeMaskType& activation, UpgradeMaskType& conflicting) const { getUpgradeModuleData()->m_upgradeMuxData.getUpgradeActivationMasks(activation, conflicting); } virtual void performUpgradeFX() { getUpgradeModuleData()->m_upgradeMuxData.performUpgradeFX(getObject()); } }; inline UpgradeModule::UpgradeModule( Thing *thing, const ModuleData* moduleData ) : BehaviorModule( thing, moduleData ) { } inline UpgradeModule::~UpgradeModule() { } #endif