diff --git a/Core/GameEngine/Source/Common/System/GameMemoryInitPools_GeneralsMD.inl b/Core/GameEngine/Source/Common/System/GameMemoryInitPools_GeneralsMD.inl index baa31f2de83..fa614607881 100644 --- a/Core/GameEngine/Source/Common/System/GameMemoryInitPools_GeneralsMD.inl +++ b/Core/GameEngine/Source/Common/System/GameMemoryInitPools_GeneralsMD.inl @@ -701,5 +701,6 @@ static PoolSizeRec PoolSizes[] = { "CarrierDroneAIUpdate", 16, 16 }, { "DrawBridgeTowerUpdate", 8, 8 }, { "DrawBridgeUpdate", 4, 4 }, + { "CrateApplyUpgrade", 64, 32 }, { 0, 0, 0 } }; diff --git a/GeneralsMD/Code/GameEngine/CMakeLists.txt b/GeneralsMD/Code/GameEngine/CMakeLists.txt index 39d410a8303..78426594282 100644 --- a/GeneralsMD/Code/GameEngine/CMakeLists.txt +++ b/GeneralsMD/Code/GameEngine/CMakeLists.txt @@ -510,6 +510,7 @@ set(GAMEENGINE_SRC Include/GameLogic/Module/BridgeTowerBody.h Include/GameLogic/Module/DrawBridgeTowerUpdate.h Include/GameLogic/Module/DrawBridgeUpdate.h + Include/GameLogic/Module/CrateApplyUpgrade.h Include/GameLogic/Object.h Include/GameLogic/ObjectCreationList.h Include/GameLogic/BuffSystem.h @@ -1147,6 +1148,7 @@ set(GAMEENGINE_SRC Source/GameLogic/Object/Upgrade/UpgradeModule.cpp Source/GameLogic/Object/Upgrade/WeaponBonusUpgrade.cpp Source/GameLogic/Object/Upgrade/WeaponSetUpgrade.cpp + Source/GameLogic/Object/Upgrade/CrateApplyUpgrade.cpp Source/GameLogic/Object/Weapon.cpp Source/GameLogic/Object/WeaponSet.cpp Source/GameLogic/ScriptEngine/ScriptActions.cpp diff --git a/GeneralsMD/Code/GameEngine/Include/GameLogic/Module/AutoHealBehavior.h b/GeneralsMD/Code/GameEngine/Include/GameLogic/Module/AutoHealBehavior.h index a05c750670d..050c294bf2c 100644 --- a/GeneralsMD/Code/GameEngine/Include/GameLogic/Module/AutoHealBehavior.h +++ b/GeneralsMD/Code/GameEngine/Include/GameLogic/Module/AutoHealBehavior.h @@ -61,6 +61,8 @@ class AutoHealBehaviorModuleData : public UpdateModuleData KindOfMaskType m_forbiddenKindOf; //Only these types can heal -- defaults to everything. const ParticleSystemTemplate* m_radiusParticleSystemTmpl; //Optional particle system meant to apply to entire effect for entire duration. const ParticleSystemTemplate* m_unitHealPulseParticleSystemTmpl; //Optional particle system applying to each object getting healed each heal pulse. + Bool m_grantSalvageUpgrade; //Apply a salvage upgrade to unit when healing + Bool m_grantPromotion; //Give unit a levelup when healing AutoHealBehaviorModuleData() { @@ -76,6 +78,8 @@ class AutoHealBehaviorModuleData : public UpdateModuleData m_skipSelfForHealing = FALSE; SET_ALL_KINDOFMASK_BITS( m_kindOf ); m_forbiddenKindOf.clear(); + m_grantSalvageUpgrade = false; + m_grantPromotion = false; } static void buildFieldParse(MultiIniFieldParse& p) @@ -94,6 +98,8 @@ class AutoHealBehaviorModuleData : public UpdateModuleData { "StartHealingDelay", INI::parseDurationUnsignedInt, nullptr, offsetof( AutoHealBehaviorModuleData, m_startHealingDelay ) }, { "AffectsWholePlayer", INI::parseBool, nullptr, offsetof( AutoHealBehaviorModuleData, m_affectsWholePlayer ) }, { "SkipSelfForHealing", INI::parseBool, nullptr, offsetof( AutoHealBehaviorModuleData, m_skipSelfForHealing ) }, + { "GrantSalvageUpgrade", INI::parseBool, nullptr, offsetof( AutoHealBehaviorModuleData, m_grantSalvageUpgrade ) }, + { "GrantPromotion", INI::parseBool, nullptr, offsetof( AutoHealBehaviorModuleData, m_grantPromotion ) }, { 0, 0, 0, 0 } }; @@ -173,7 +179,12 @@ class AutoHealBehavior : public UpdateModule, private: - void pulseHealObject( Object *obj ); + Bool canApplyWeaponSalvage(const Object* obj) const; + Bool canApplyArmorSalvage(const Object* obj) const; + Bool canApplyLevelUp(const Object* obj) const; + + // Return if healing occured + Bool pulseHealObject( Object *obj ); ParticleSystemID m_radiusParticleSystemID; diff --git a/GeneralsMD/Code/GameEngine/Include/GameLogic/Module/CrateApplyUpgrade.h b/GeneralsMD/Code/GameEngine/Include/GameLogic/Module/CrateApplyUpgrade.h new file mode 100644 index 00000000000..5765e8257ab --- /dev/null +++ b/GeneralsMD/Code/GameEngine/Include/GameLogic/Module/CrateApplyUpgrade.h @@ -0,0 +1,56 @@ +// FILE: CrateApplyUpgrade.h /////////////////////////////////////////////////////////////////////////// +// Desc: apply salvage crate effetcs on upgrade +/////////////////////////////////////////////////////////////////////////////////////////////////// + +#pragma once + +// INCLUDES /////////////////////////////////////////////////////////////////////////////////////// +#include "GameLogic/Module/UpgradeModule.h" + +// FORWARD REFERENCES ///////////////////////////////////////////////////////////////////////////// +class Thing; +class Player; + +//------------------------------------------------------------------------------------------------- +//------------------------------------------------------------------------------------------------- +class CrateApplyUpgradeModuleData : public UpgradeModuleData +{ + +public: + Bool m_applySalvageUpgrade; + Bool m_applyLevelUp; + + CrateApplyUpgradeModuleData( void ); + + static void buildFieldParse(MultiIniFieldParse& p); + +}; + +//------------------------------------------------------------------------------------------------- +/** The upgrade module */ +//------------------------------------------------------------------------------------------------- +class CrateApplyUpgrade : public UpgradeModule +{ + + MEMORY_POOL_GLUE_WITH_USERLOOKUP_CREATE(CrateApplyUpgrade, "CrateApplyUpgrade" ) + MAKE_STANDARD_MODULE_MACRO_WITH_MODULE_DATA(CrateApplyUpgrade, CrateApplyUpgradeModuleData); + +public: + + CrateApplyUpgrade( Thing *thing, const ModuleData* moduleData ); + // virtual destructor prototype defined by MemoryPoolObject + + virtual Bool wouldUpgrade(const UpgradeMaskType& keyMask) const override; + +protected: + + virtual void upgradeImplementation( void ); ///< Here's the actual work of Upgrading + virtual Bool isSubObjectsUpgrade() { return false; } + + Bool canApplyAnyCrateUpgade() const; + + Bool canApplyWeaponUpgrade() const; + Bool canApplyArmorUpgrade() const; + Bool canApplyLevelUp() const; + +}; diff --git a/GeneralsMD/Code/GameEngine/Source/Common/Thing/ModuleFactory.cpp b/GeneralsMD/Code/GameEngine/Source/Common/Thing/ModuleFactory.cpp index 3d28ceae6c9..03534f5358d 100644 --- a/GeneralsMD/Code/GameEngine/Source/Common/Thing/ModuleFactory.cpp +++ b/GeneralsMD/Code/GameEngine/Source/Common/Thing/ModuleFactory.cpp @@ -240,6 +240,7 @@ #include "GameLogic/Module/UnitProductionBonusUpgrade.h" #include "GameLogic/Module/ExperienceScalarUpgrade.h" #include "GameLogic/Module/MaxHealthUpgrade.h" +#include "GameLogic/Module/CrateApplyUpgrade.h" // create includes #include "GameLogic/Module/LockWeaponCreate.h" @@ -552,6 +553,7 @@ void ModuleFactory::init( void ) addModule( WeaponBonusUpgrade ); addModule( ExperienceScalarUpgrade ); addModule( MaxHealthUpgrade ); + addModule( CrateApplyUpgrade ); // create modules addModule( LockWeaponCreate ); diff --git a/GeneralsMD/Code/GameEngine/Source/GameLogic/Object/Behavior/AutoHealBehavior.cpp b/GeneralsMD/Code/GameEngine/Source/GameLogic/Object/Behavior/AutoHealBehavior.cpp index 948120ac01e..65bc5739c4e 100644 --- a/GeneralsMD/Code/GameEngine/Source/GameLogic/Object/Behavior/AutoHealBehavior.cpp +++ b/GeneralsMD/Code/GameEngine/Source/GameLogic/Object/Behavior/AutoHealBehavior.cpp @@ -43,7 +43,9 @@ #include "GameLogic/GameLogic.h" #include "GameLogic/Object.h" #include "GameLogic/PartitionManager.h" - +#include "GameLogic/ExperienceTracker.h" +#include "Common/AudioEventRTS.h" +#include "Common/MiscAudio.h" //------------------------------------------------------------------------------------------------- //------------------------------------------------------------------------------------------------- @@ -252,17 +254,20 @@ UpdateSleepTime AutoHealBehavior::update( void ) MemoryPoolObjectHolder hold( iter ); for( obj = iter->first(); obj; obj = iter->next() ) { - // do not heal if we are at max health already + // do not heal if we are at max health already, still apply if salvage/promotion is granted BodyModuleInterface *body = obj->getBodyModule(); - if( body->getHealth() < body->getMaxHealth() ) + if( (body->getHealth() < body->getMaxHealth()) + || canApplyArmorSalvage( obj ) + || canApplyWeaponSalvage( obj ) + || canApplyLevelUp( obj ) ) { if( obj->isAnyKindOf( d->m_kindOf ) && !obj->isAnyKindOf( d->m_forbiddenKindOf ) ) { if( !d->m_skipSelfForHealing || obj != getObject() ) { - pulseHealObject( obj ); + Bool healed = pulseHealObject( obj ); - if( d->m_singleBurst && TheGameLogic->getDrawIconUI() ) + if( healed && d->m_singleBurst && TheGameLogic->getDrawIconUI() ) { if( TheAnim2DCollection && TheGlobalData->m_getHealedAnimationName.isEmpty() == FALSE ) { @@ -290,22 +295,102 @@ UpdateSleepTime AutoHealBehavior::update( void ) } //------------------------------------------------------------------------------------------------- -//------------------------------------------------------------------------------------------------- -void AutoHealBehavior::pulseHealObject( Object *obj ) +Bool AutoHealBehavior::canApplyWeaponSalvage(const Object* obj) const { - if (m_stopped) - return; + const AutoHealBehaviorModuleData* data = getAutoHealBehaviorModuleData(); + return data->m_grantSalvageUpgrade && obj->isKindOf(KINDOF_WEAPON_SALVAGER) && !obj->testWeaponSetFlag(WEAPONSET_CRATEUPGRADE_TWO); +} - const AutoHealBehaviorModuleData *data = getAutoHealBehaviorModuleData(); +Bool AutoHealBehavior::canApplyArmorSalvage(const Object* obj) const +{ + const AutoHealBehaviorModuleData* data = getAutoHealBehaviorModuleData(); + return data->m_grantSalvageUpgrade && obj->isKindOf(KINDOF_ARMOR_SALVAGER) && !obj->testArmorSetFlag(ARMORSET_CRATE_UPGRADE_TWO); +} +Bool AutoHealBehavior::canApplyLevelUp(const Object* obj) const +{ + const AutoHealBehaviorModuleData* data = getAutoHealBehaviorModuleData(); + return data->m_grantPromotion && obj->getExperienceTracker()->isTrainable() && obj->getExperienceTracker()->getVeterancyLevel() < LEVEL_HEROIC; +} - if ( data->m_radius == 0.0f ) - obj->attemptHealing(data->m_healingAmount, getObject()); +// ------------------------------------------------------------------------------------------------ +static void applyWeaponSalvage(Object* unit) +{ + if (unit->testWeaponSetFlag(WEAPONSET_CRATEUPGRADE_ONE)) + { + unit->clearWeaponSetFlag(WEAPONSET_CRATEUPGRADE_ONE); + unit->setWeaponSetFlag(WEAPONSET_CRATEUPGRADE_TWO); + } else - obj->attemptHealingFromSoleBenefactor( data->m_healingAmount, getObject(), data->m_healingDelay ); + { + unit->setWeaponSetFlag(WEAPONSET_CRATEUPGRADE_ONE); + } +} +// ------------------------------------------------------------------------------------------------ +static void applyArmorSalvage(Object* unit) +{ + if (unit->testArmorSetFlag(ARMORSET_CRATE_UPGRADE_ONE)) + { + unit->clearArmorSetFlag(ARMORSET_CRATE_UPGRADE_ONE); + unit->setArmorSetFlag(ARMORSET_CRATE_UPGRADE_TWO); + unit->clearAndSetModelConditionState(MODELCONDITION_ARMORSET_CRATEUPGRADE_ONE, MODELCONDITION_ARMORSET_CRATEUPGRADE_TWO); + } + else + { + unit->setArmorSetFlag(ARMORSET_CRATE_UPGRADE_ONE); + unit->setModelConditionState(MODELCONDITION_ARMORSET_CRATEUPGRADE_ONE); + } +} + +// ------------------------------------------------------------------------------------------------ +static void doLevelGain(Object* unit) +{ + unit->getExperienceTracker()->gainExpForLevel(1); +} + +static void doSalvageEffect(Object* unit) { + //Play the salvage installation crate pickup sound. + AudioEventRTS soundToPlay = TheAudio->getMiscAudio()->m_crateSalvage; + soundToPlay.setObjectID(unit->getID()); + TheAudio->addAudioEvent(&soundToPlay); +} + +//------------------------------------------------------------------------------------------------- +//------------------------------------------------------------------------------------------------- +Bool AutoHealBehavior::pulseHealObject(Object* obj) +{ + if (m_stopped) + return false; + + const AutoHealBehaviorModuleData* data = getAutoHealBehaviorModuleData(); + bool needsHeal{ true }; + + if (data->m_grantSalvageUpgrade || data->m_grantPromotion) { + // Need to check for full HP + BodyModuleInterface* body = obj->getBodyModule(); + needsHeal = (body != nullptr) && (body->getHealth() < body->getMaxHealth()); + } + if (needsHeal) { + if (data->m_radius == 0.0f) + obj->attemptHealing(data->m_healingAmount, getObject()); + else + obj->attemptHealingFromSoleBenefactor(data->m_healingAmount, getObject(), data->m_healingDelay); + } + + if (canApplyArmorSalvage(obj)) { + applyArmorSalvage(obj); + doSalvageEffect(obj); + } + else if (canApplyWeaponSalvage(obj)) { + applyWeaponSalvage(obj); + doSalvageEffect(obj); + } + else if (canApplyLevelUp(obj)) { + doLevelGain(obj); + } - if( data->m_unitHealPulseParticleSystemTmpl ) + if( needsHeal && data->m_unitHealPulseParticleSystemTmpl ) { ParticleSystem *system = TheParticleSystemManager->createParticleSystem( data->m_unitHealPulseParticleSystemTmpl ); if( system ) @@ -315,6 +400,7 @@ void AutoHealBehavior::pulseHealObject( Object *obj ) } m_soonestHealFrame = TheGameLogic->getFrame() + data->m_healingDelay;// In case onDamage tries to wake us up early + return needsHeal; } // ------------------------------------------------------------------------------------------------ diff --git a/GeneralsMD/Code/GameEngine/Source/GameLogic/Object/Upgrade/CrateApplyUpgrade.cpp b/GeneralsMD/Code/GameEngine/Source/GameLogic/Object/Upgrade/CrateApplyUpgrade.cpp new file mode 100644 index 00000000000..9077643868f --- /dev/null +++ b/GeneralsMD/Code/GameEngine/Source/GameLogic/Object/Upgrade/CrateApplyUpgrade.cpp @@ -0,0 +1,202 @@ +// FILE: CrateObjectUpgrade.cpp ///////////////////////////////////////////////////////////////////////////// +// Desc: apply salvage crate effects on upgrade +/////////////////////////////////////////////////////////////////////////////////////////////////// + +// INCLUDES /////////////////////////////////////////////////////////////////////////////////////// +#include "PreRTS.h" // This must go first in EVERY cpp file in the GameEngine + +#include "Common/ModelState.h" +#include "Common/Player.h" +#include "Common/Xfer.h" +#include "GameClient/Drawable.h" +#include "GameLogic/Module/CrateApplyUpgrade.h" +#include "GameLogic/Object.h" +#include "GameLogic/ObjectCreationList.h" +#include "GameLogic/ArmorSet.h" +#include "GameLogic/ExperienceTracker.h" + +#include "Common/AudioEventRTS.h" +#include "Common/MiscAudio.h" + +//------------------------------------------------------------------------------------------------- +//------------------------------------------------------------------------------------------------- +CrateApplyUpgradeModuleData::CrateApplyUpgradeModuleData( void ) +{ + m_applySalvageUpgrade = false; + m_applyLevelUp = false; +} + +//------------------------------------------------------------------------------------------------- +//------------------------------------------------------------------------------------------------- +/* static */ void CrateApplyUpgradeModuleData::buildFieldParse(MultiIniFieldParse& p) +{ + UpgradeModuleData::buildFieldParse( p ); + + static const FieldParse dataFieldParse[] = + { + //{ "UpgradeObject", INI::parseObjectCreationList, nullptr, offsetof( ObjectCreationUpgradeModuleData, m_ocl ) }, + { "SalvageCrate", INI::parseBool, nullptr, offsetof(CrateApplyUpgradeModuleData, m_applySalvageUpgrade)}, + { "LevelUp", INI::parseBool, nullptr, offsetof(CrateApplyUpgradeModuleData, m_applyLevelUp)}, + { nullptr, nullptr, nullptr, 0 } + }; + p.add(dataFieldParse); +} + +/////////////////////////////////////////////////////////////////////////////////////////////////// +/////////////////////////////////////////////////////////////////////////////////////////////////// +/////////////////////////////////////////////////////////////////////////////////////////////////// + +//------------------------------------------------------------------------------------------------- +//------------------------------------------------------------------------------------------------- +CrateApplyUpgrade::CrateApplyUpgrade( Thing *thing, const ModuleData* moduleData ) : + UpgradeModule( thing, moduleData ) +{ + +} + +Bool CrateApplyUpgrade::wouldUpgrade(const UpgradeMaskType& keyMask) const +{ + return UpgradeMux::wouldUpgrade(keyMask) && canApplyAnyCrateUpgade(); +} + +//------------------------------------------------------------------------------------------------- +//------------------------------------------------------------------------------------------------- +CrateApplyUpgrade::~CrateApplyUpgrade( void ) +{ + +} + +// ------------------------------------------------------------------------------------------------ +static void applyWeaponSet(Object* unit) +{ + if (unit->testWeaponSetFlag(WEAPONSET_CRATEUPGRADE_ONE)) + { + unit->clearWeaponSetFlag(WEAPONSET_CRATEUPGRADE_ONE); + unit->setWeaponSetFlag(WEAPONSET_CRATEUPGRADE_TWO); + } + else + { + unit->setWeaponSetFlag(WEAPONSET_CRATEUPGRADE_ONE); + } +} + +// ------------------------------------------------------------------------------------------------ +static void applyArmorSet(Object* unit) +{ + if (unit->testArmorSetFlag(ARMORSET_CRATE_UPGRADE_ONE)) + { + unit->clearArmorSetFlag(ARMORSET_CRATE_UPGRADE_ONE); + unit->setArmorSetFlag(ARMORSET_CRATE_UPGRADE_TWO); + + unit->clearAndSetModelConditionState(MODELCONDITION_ARMORSET_CRATEUPGRADE_ONE, MODELCONDITION_ARMORSET_CRATEUPGRADE_TWO); + } + else + { + unit->setArmorSetFlag(ARMORSET_CRATE_UPGRADE_ONE); + + unit->setModelConditionState(MODELCONDITION_ARMORSET_CRATEUPGRADE_ONE); + } +} + +// ------------------------------------------------------------------------------------------------ +static void doLevelGain(Object* unit) +{ + unit->getExperienceTracker()->gainExpForLevel(1); +} + +static void doSalvageEffect(Object* unit) { + //Play the salvage installation crate pickup sound. + AudioEventRTS soundToPlay = TheAudio->getMiscAudio()->m_crateSalvage; + soundToPlay.setObjectID(unit->getID()); + TheAudio->addAudioEvent(&soundToPlay); +} +//------------------------------------------------------------------------------------------------- +//------------------------------------------------------------------------------------------------- +void CrateApplyUpgrade::upgradeImplementation(void) +{ + Object* self = getObject(); + + // check if armor salvager + if (canApplyArmorUpgrade()) { + applyArmorSet(self); + doSalvageEffect(self); + } + // Check weapon salvager + else if (canApplyWeaponUpgrade()) { + applyWeaponSet(self); + doSalvageEffect(self); + } + // Check Levelup + else if (canApplyLevelUp()) { + // set upgrade as already executed here, since leveling up a unit reevaluated upgrades and triggers a recursion + setUpgradeExecuted(true); + doLevelGain(self); + } + +} + +Bool CrateApplyUpgrade::canApplyAnyCrateUpgade() const +{ + return canApplyWeaponUpgrade() || canApplyArmorUpgrade() || canApplyLevelUp(); +} + +Bool CrateApplyUpgrade::canApplyWeaponUpgrade() const +{ + const Object* self = getObject(); + const CrateApplyUpgradeModuleData* data = getCrateApplyUpgradeModuleData(); + return data->m_applySalvageUpgrade && self->isKindOf(KINDOF_WEAPON_SALVAGER) && !self->testWeaponSetFlag(WEAPONSET_CRATEUPGRADE_TWO); +} + +Bool CrateApplyUpgrade::canApplyArmorUpgrade() const +{ + const Object* self = getObject(); + const CrateApplyUpgradeModuleData* data = getCrateApplyUpgradeModuleData(); + return data->m_applySalvageUpgrade && self->isKindOf(KINDOF_ARMOR_SALVAGER) && !self->testArmorSetFlag(ARMORSET_CRATE_UPGRADE_TWO); +} + +Bool CrateApplyUpgrade::canApplyLevelUp() const +{ + const Object* self = getObject(); + const CrateApplyUpgradeModuleData* data = getCrateApplyUpgradeModuleData(); + return data->m_applyLevelUp && self->getExperienceTracker()->isTrainable() && self->getExperienceTracker()->getVeterancyLevel() < LEVEL_HEROIC; +} + +// ------------------------------------------------------------------------------------------------ +/** CRC */ +// ------------------------------------------------------------------------------------------------ +void CrateApplyUpgrade::crc( Xfer *xfer ) +{ + + // extend base class + UpgradeModule::crc( xfer ); + +} + +// ------------------------------------------------------------------------------------------------ +/** Xfer method + * Version Info: + * 1: Initial version */ +// ------------------------------------------------------------------------------------------------ +void CrateApplyUpgrade::xfer( Xfer *xfer ) +{ + + // version + XferVersion currentVersion = 1; + XferVersion version = currentVersion; + xfer->xferVersion( &version, currentVersion ); + + // extend base class + UpgradeModule::xfer( xfer ); + +} + +// ------------------------------------------------------------------------------------------------ +/** Load post process */ +// ------------------------------------------------------------------------------------------------ +void CrateApplyUpgrade::loadPostProcess( void ) +{ + + // extend base class + UpgradeModule::loadPostProcess(); + +}