diff --git a/Core/GameEngine/CMakeLists.txt b/Core/GameEngine/CMakeLists.txt
index c0c85c3c6e4..971299c5677 100644
--- a/Core/GameEngine/CMakeLists.txt
+++ b/Core/GameEngine/CMakeLists.txt
@@ -291,6 +291,7 @@ set(GAMEENGINE_SRC
# Include/GameLogic/Module/CostModifierUpgrade.h
# Include/GameLogic/Module/CountermeasuresBehavior.h
# Include/GameLogic/Module/BattlePlanBonusBehavior.h
+# Include/GameLogic/Module/EnergyShieldBehavior.h
# Include/GameLogic/Module/CrateCollide.h
# Include/GameLogic/Module/CreateCrateDie.h
# Include/GameLogic/Module/CreateModule.h
@@ -344,6 +345,7 @@ set(GAMEENGINE_SRC
# Include/GameLogic/Module/HighlanderBody.h
# Include/GameLogic/Module/HijackerUpdate.h
# Include/GameLogic/Module/HiveStructureBody.h
+# Include/GameLogic/Module/ShieldBody.h
# Include/GameLogic/Module/HordeUpdate.h
# Include/GameLogic/Module/ImmortalBody.h
# Include/GameLogic/Module/InactiveBody.h
@@ -854,6 +856,7 @@ set(GAMEENGINE_SRC
# Source/GameLogic/Object/Behavior/BunkerBusterBehavior.cpp
# Source/GameLogic/Object/Behavior/CountermeasuresBehavior.cpp
# Source/GameLogic/Object/Behavior/BattlePlanBonusBehavior.cpp
+# Source/GameLogic/Object/Behavior/EnergyShieldBehavior.cpp
# Source/GameLogic/Object/Behavior/DumbProjectileBehavior.cpp
# Source/GameLogic/Object/Behavior/FireWeaponWhenDamagedBehavior.cpp
# Source/GameLogic/Object/Behavior/FireWeaponWhenDeadBehavior.cpp
@@ -880,6 +883,7 @@ set(GAMEENGINE_SRC
# Source/GameLogic/Object/Body/BodyModule.cpp
# Source/GameLogic/Object/Body/HighlanderBody.cpp
# Source/GameLogic/Object/Body/HiveStructureBody.cpp
+# Source/GameLogic/Object/Body/ShieldBody.cpp
# Source/GameLogic/Object/Body/ImmortalBody.cpp
# Source/GameLogic/Object/Body/InactiveBody.cpp
# Source/GameLogic/Object/Body/StructureBody.cpp
diff --git a/Core/GameEngine/Source/Common/System/GameMemoryInitPools_GeneralsMD.inl b/Core/GameEngine/Source/Common/System/GameMemoryInitPools_GeneralsMD.inl
index aed9ab5adea..0856e058579 100644
--- a/Core/GameEngine/Source/Common/System/GameMemoryInitPools_GeneralsMD.inl
+++ b/Core/GameEngine/Source/Common/System/GameMemoryInitPools_GeneralsMD.inl
@@ -109,6 +109,7 @@ static PoolSizeRec PoolSizes[] =
{ "NeutronBlastBehavior", 4096, 32 },
{ "CountermeasuresBehavior", 256, 32 },
{ "BattlePlanBonusBehavior", 256, 32 },
+ { "EnergyShieldBehavior", 256, 32 },
{ "BaseRegenerateUpdate", 128, 32 },
{ "BoneFXDamage", 64, 32 },
{ "BoneFXUpdate", 64, 32 },
@@ -253,6 +254,7 @@ static PoolSizeRec PoolSizes[] =
{ "SquishCollide", 512, 32 },
{ "StructureBody", 512, 64 },
{ "HiveStructureBody", 64, 32 }, //Stinger sites
+ { "ShieldBody", 128, 32 },
{ "StructureCollapseUpdate", 32, 32 },
{ "StructureToppleUpdate", 32, 32 },
{ "SupplyCenterCreate", 32, 32 },
diff --git a/Generals/Code/GameEngine/Source/Common/Thing/ModuleFactory.cpp b/Generals/Code/GameEngine/Source/Common/Thing/ModuleFactory.cpp
index 7504e206edb..aa407c5bd06 100644
--- a/Generals/Code/GameEngine/Source/Common/Thing/ModuleFactory.cpp
+++ b/Generals/Code/GameEngine/Source/Common/Thing/ModuleFactory.cpp
@@ -230,6 +230,7 @@
#include "GameLogic/Module/ImmortalBody.h"
#include "GameLogic/Module/StructureBody.h"
#include "GameLogic/Module/HiveStructureBody.h"
+#include "GameLogic/Module/ShieldBody.h"
// contain includes
// (none)
diff --git a/GeneralsMD/Code/GameEngine/CMakeLists.txt b/GeneralsMD/Code/GameEngine/CMakeLists.txt
index 2c662daf497..27df30b3cce 100644
--- a/GeneralsMD/Code/GameEngine/CMakeLists.txt
+++ b/GeneralsMD/Code/GameEngine/CMakeLists.txt
@@ -294,6 +294,7 @@ set(GAMEENGINE_SRC
Include/GameLogic/Module/UnitProductionBonusUpgrade.h
Include/GameLogic/Module/CountermeasuresBehavior.h
Include/GameLogic/Module/BattlePlanBonusBehavior.h
+ Include/GameLogic/Module/EnergyShieldBehavior.h
Include/GameLogic/Module/CrateCollide.h
Include/GameLogic/Module/CreateCrateDie.h
Include/GameLogic/Module/CreateModule.h
@@ -349,6 +350,7 @@ set(GAMEENGINE_SRC
Include/GameLogic/Module/HighlanderBody.h
Include/GameLogic/Module/HijackerUpdate.h
Include/GameLogic/Module/HiveStructureBody.h
+ Include/GameLogic/Module/ShieldBody.h
Include/GameLogic/Module/HordeUpdate.h
Include/GameLogic/Module/ImmortalBody.h
Include/GameLogic/Module/InactiveBody.h
@@ -871,6 +873,7 @@ set(GAMEENGINE_SRC
Source/GameLogic/Object/Behavior/BunkerBusterBehavior.cpp
Source/GameLogic/Object/Behavior/CountermeasuresBehavior.cpp
Source/GameLogic/Object/Behavior/BattlePlanBonusBehavior.cpp
+ Source/GameLogic/Object/Behavior/EnergyShieldBehavior.cpp
Source/GameLogic/Object/Behavior/DumbProjectileBehavior.cpp
Source/GameLogic/Object/Behavior/FreeFallProjectileBehavior.cpp
Source/GameLogic/Object/Behavior/FireWeaponWhenDamagedBehavior.cpp
@@ -900,6 +903,7 @@ set(GAMEENGINE_SRC
Source/GameLogic/Object/Body/BodyModule.cpp
Source/GameLogic/Object/Body/HighlanderBody.cpp
Source/GameLogic/Object/Body/HiveStructureBody.cpp
+ Source/GameLogic/Object/Body/ShieldBody.cpp
Source/GameLogic/Object/Body/ImmortalBody.cpp
Source/GameLogic/Object/Body/InactiveBody.cpp
Source/GameLogic/Object/Body/StructureBody.cpp
diff --git a/GeneralsMD/Code/GameEngine/Include/Common/GlobalData.h b/GeneralsMD/Code/GameEngine/Include/Common/GlobalData.h
index 50e2f0a1acf..17b2e86091b 100644
--- a/GeneralsMD/Code/GameEngine/Include/Common/GlobalData.h
+++ b/GeneralsMD/Code/GameEngine/Include/Common/GlobalData.h
@@ -216,6 +216,8 @@ class GlobalData : public SubsystemInterface
Real m_ammoPipScaleFactor;
Real m_containerPipScaleFactor;
+ Real m_progressBarYOffset;
+
UnsignedInt m_historicDamageLimit;
//Settings for terrain tracks left by vehicles with treads or wheels
diff --git a/GeneralsMD/Code/GameEngine/Include/Common/KindOf.h b/GeneralsMD/Code/GameEngine/Include/Common/KindOf.h
index ef816463902..4a8b48697ad 100644
--- a/GeneralsMD/Code/GameEngine/Include/Common/KindOf.h
+++ b/GeneralsMD/Code/GameEngine/Include/Common/KindOf.h
@@ -179,6 +179,8 @@ enum KindOfType CPP_11(: Int)
KINDOF_ENABLE_INFANTRY_LIGHTING, ///< Enable infantry-style ambient lighting for this object
KINDOF_DISABLE_INFANTRY_LIGHTING, ///< Use regular lighting on this infantry object
+ KINDOF_SHOW_PROGRESS_BAR, ///< Show progress bar for this unit (Shields, deploy, teleport, etc.)
+
KINDOF_VTOL,
KINDOF_LARGE_AIRCRAFT,
KINDOF_MEDIUM_AIRCRAFT,
@@ -209,8 +211,6 @@ enum KindOfType CPP_11(: Int)
KINDOF_EXTRA14,
KINDOF_EXTRA15,
KINDOF_EXTRA16,
- KINDOF_EXTRA17,
- KINDOF_EXTRA18,
KINDOF_COUNT // total number of kindofs
diff --git a/GeneralsMD/Code/GameEngine/Include/Common/ThingTemplate.h b/GeneralsMD/Code/GameEngine/Include/Common/ThingTemplate.h
index 42b49c06801..beb8bec34fb 100644
--- a/GeneralsMD/Code/GameEngine/Include/Common/ThingTemplate.h
+++ b/GeneralsMD/Code/GameEngine/Include/Common/ThingTemplate.h
@@ -257,6 +257,28 @@ static const char* AmmoPipsStyleNames[] =
};
#endif // end DEFINE_AMMO_PIPS_STYLE_NAMES
+// ---
+//enum ProgressBarStyle CPP_11(: Int)
+//{
+// PROGRESS_BAR_NONE = 0, ///< Default. No progress bar
+// PROGRESS_BAR_SHIELD, ///< large white bar
+// PROGRESS_BAR_SHIELD, ///< like default, but show a single pip only (full or empty)
+// AMMO_PIPS_THIN, ///< like default, but half width
+//
+// AMMO_PIPS_NUM_TYPES // leave this last
+//};
+//#ifdef DEFINE_PROGRESS_BAR_STYLE_NAMES
+//static const char* ProgressBarStyleNames[] =
+//{
+// "DEFAULT",
+// "PERCENTAGE_BAR",
+// "SINGLE",
+// "THIN",
+//
+// NULL
+//};
+//#endif // end DEFINE_PROGRESS_BAR_STYLE_NAMES
+
//-------------------------------------------------------------------------------------------------
enum ModuleParseMode CPP_11(: Int)
{
diff --git a/GeneralsMD/Code/GameEngine/Include/GameClient/Drawable.h b/GeneralsMD/Code/GameEngine/Include/GameClient/Drawable.h
index 411308013e4..4ec86d0377a 100644
--- a/GeneralsMD/Code/GameEngine/Include/GameClient/Drawable.h
+++ b/GeneralsMD/Code/GameEngine/Include/GameClient/Drawable.h
@@ -762,6 +762,9 @@ class Drawable : public Thing,
void drawContained( const IRegion2D *healthBarRegion ); ///< draw icons
void drawVeterancy( const IRegion2D *healthBarRegion ); ///< draw veterency information
+ //new:
+ void drawProgress(const IRegion2D* healthBarRegion); ///< draw progress bar (shield, deploy, teleport, etc.)
+
void drawEmoticon( const IRegion2D* healthBarRegion );
void drawHealthBar( const IRegion2D* healthBarRegion ); ///< draw heath bar
void drawHealing( const IRegion2D* healthBarRegion ); ///< draw icons
diff --git a/GeneralsMD/Code/GameEngine/Include/GameLogic/Module/ActiveBody.h b/GeneralsMD/Code/GameEngine/Include/GameLogic/Module/ActiveBody.h
index af3bda50c91..4aa2266d000 100644
--- a/GeneralsMD/Code/GameEngine/Include/GameLogic/Module/ActiveBody.h
+++ b/GeneralsMD/Code/GameEngine/Include/GameLogic/Module/ActiveBody.h
@@ -143,8 +143,15 @@ class ActiveBody : public BodyModule
protected:
+ UnsignedInt m_nextDamageFXTime;
+ DamageType m_lastDamageFXDone;
+ DamageInfo m_lastDamageInfo; ///< store the last DamageInfo object that we received
+ UnsignedInt m_lastDamageTimestamp; ///< frame of last damage dealt
+ UnsignedInt m_lastHealingTimestamp; ///< frame of last healing dealt
+ Bool m_lastDamageCleared;
+
void validateArmorAndDamageFX() const;
- void doDamageFX( const DamageInfo *damageInfo );
+ virtual void doDamageFX( const DamageInfo *damageInfo );
void createParticleSystems( const AsciiString &boneBaseName,
const ParticleSystemTemplate *systemTemplate,
@@ -160,6 +167,8 @@ class ActiveBody : public BodyModule
virtual void applyChronoParticleSystems(void);
+ inline const Armor getCurrentArmor() const { return m_curArmor; }
+
private:
Real m_currentHealth; ///< health of the object
@@ -170,14 +179,9 @@ class ActiveBody : public BodyModule
Real m_currentChronoDamage; ///< Same as Subdual, but for CHRONO_GUN
BodyDamageType m_curDamageState; ///< last known damage state
- UnsignedInt m_nextDamageFXTime;
- DamageType m_lastDamageFXDone;
- DamageInfo m_lastDamageInfo; ///< store the last DamageInfo object that we received
- UnsignedInt m_lastDamageTimestamp; ///< frame of last damage dealt
- UnsignedInt m_lastHealingTimestamp; ///< frame of last healing dealt
+
Bool m_frontCrushed;
Bool m_backCrushed;
- Bool m_lastDamageCleared;
Bool m_indestructible; ///< is this object indestructible?
Bool m_damageFXOverride;
diff --git a/GeneralsMD/Code/GameEngine/Include/GameLogic/Module/BehaviorModule.h b/GeneralsMD/Code/GameEngine/Include/GameLogic/Module/BehaviorModule.h
index f5dacd3dd34..781fb9cb057 100644
--- a/GeneralsMD/Code/GameEngine/Include/GameLogic/Module/BehaviorModule.h
+++ b/GeneralsMD/Code/GameEngine/Include/GameLogic/Module/BehaviorModule.h
@@ -83,6 +83,7 @@ class StealthUpdate;
class SpyVisionUpdate;
// -----------------
class BattlePlanBonusBehaviorInterface;
+class EnergyShieldBehaviorInterface;
//-------------------------------------------------------------------------------------------------
class BehaviorModuleData : public ModuleData
@@ -142,6 +143,7 @@ class BehaviorModuleInterface
virtual const CountermeasuresBehaviorInterface* getCountermeasuresBehaviorInterface() const = 0;
virtual BattlePlanBonusBehaviorInterface* getBattlePlanBonusBehaviorInterface() = 0;
+ virtual EnergyShieldBehaviorInterface* getEnergyShieldBehaviorInterface() = 0;
};
@@ -197,7 +199,8 @@ class BehaviorModule : public ObjectModule, public BehaviorModuleInterface
virtual SpawnBehaviorInterface* getSpawnBehaviorInterface() { return NULL; }
virtual CountermeasuresBehaviorInterface* getCountermeasuresBehaviorInterface() { return NULL; }
virtual const CountermeasuresBehaviorInterface* getCountermeasuresBehaviorInterface() const { return NULL; }
- virtual BattlePlanBonusBehaviorInterface* getBattlePlanBonusBehaviorInterface() { return NULL; };
+ virtual BattlePlanBonusBehaviorInterface* getBattlePlanBonusBehaviorInterface() { return NULL; }
+ virtual EnergyShieldBehaviorInterface* getEnergyShieldBehaviorInterface() { return NULL; }
protected:
diff --git a/GeneralsMD/Code/GameEngine/Include/GameLogic/Module/EnergyShieldBehavior.h b/GeneralsMD/Code/GameEngine/Include/GameLogic/Module/EnergyShieldBehavior.h
new file mode 100644
index 00000000000..e3c1c18a589
--- /dev/null
+++ b/GeneralsMD/Code/GameEngine/Include/GameLogic/Module/EnergyShieldBehavior.h
@@ -0,0 +1,187 @@
+/*
+** 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: EnergyShieldBehavior.h /////////////////////////////////////////////////////////////////////////
+// Author: Colin Day, December 2001
+// Desc: Update that heals itself
+//------------------------------------------
+// Modified by Kris Morness, September 2002
+// Kris: Added the ability to add effects, radius healing, and restricting the type of objects
+// subjected to the heal (or repair).
+///////////////////////////////////////////////////////////////////////////////////////////////////
+
+#pragma once
+
+#ifndef __EnergyShieldBehavior_H_
+#define __EnergyShieldBehavior_H_
+
+// INCLUDES ///////////////////////////////////////////////////////////////////////////////////////
+#include "GameClient/ParticleSys.h"
+#include "GameLogic/Module/BehaviorModule.h"
+#include "GameLogic/Module/UpgradeModule.h"
+#include "GameLogic/Module/UpdateModule.h"
+#include "GameLogic/Module/DamageModule.h"
+#include "Common/BitFlagsIO.h"
+
+
+class ShieldBody;
+class FXList;
+
+//-------------------------------------------------------------------------------------------------
+class EnergyShieldBehaviorModuleData : public UpdateModuleData
+{
+public:
+ UpgradeMuxData m_upgradeMuxData;
+ Bool m_initiallyActive;
+ //Real m_shieldMaxHealth; ///< MaxHealth of the shield
+ //Real m_shieldMaxHealthPercent; ///< MaxHealth as percentage of activeBody MaxHealth (takes priority)
+
+ UnsignedInt m_shieldRechargeDelay; ///< frames of no damage taken until shield recharges
+ UnsignedInt m_shieldRechargeRate; ///< every this often, we heal the shield ...
+ Real m_shieldRechargeAmount; ///< by this much.
+ Real m_shieldRechargeAmountPercent; ///< Same as above but percentage of shieldMaxHealth (takes priority)
+
+ RGBAColorInt m_barColor;
+ RGBAColorInt m_barBGColor;
+ Bool m_showBarWhenEmpty;
+ Bool m_showBarWhenUnselected;
+
+ ModelConditionFlagType m_shieldConditionFlag;
+ ModelConditionFlagType m_shieldHitConditionFlag;
+ AsciiString m_shieldSubObjName;
+ AsciiString m_shieldHitSubObjName;
+ UnsignedInt m_showShieldWhenHitDuration;
+ //Bool m_alwaysShowShield;
+
+ FXList* m_fxShieldUp;
+ FXList* m_fxShieldDown;
+
+
+
+ //DamageTypeFlags m_damageTypesToPassThrough;
+
+ EnergyShieldBehaviorModuleData();
+
+ static void buildFieldParse(MultiIniFieldParse& p);
+
+ //static void parseShieldHealthPercent(INI* ini, void* instance, void* store, const void* userData);
+ //static void parseShieldRechargeAmountPercent(INI* ini, void* instance, void* store, const void* userData);
+
+};
+//-------------------------------------------------------------------------------------------------
+//-------------------------------------------------------------------------------------------------
+class EnergyShieldBehaviorInterface
+{
+public:
+ virtual void applyDamage(Real amount) = 0;
+ virtual bool isActive() const = 0;
+ virtual bool shouldShowHealthBar(bool selected) const = 0;
+ virtual Real getShieldPercent() const = 0;
+ virtual RGBAColorInt getHealthBarColor() const = 0;
+ virtual RGBAColorInt getHealthBarBackgroundColor() const = 0;
+};
+//-------------------------------------------------------------------------------------------------
+//-------------------------------------------------------------------------------------------------
+class EnergyShieldBehavior : public UpdateModule,
+ public UpgradeMux,
+ public EnergyShieldBehaviorInterface
+ //public DamageModuleInterface
+{
+
+ MEMORY_POOL_GLUE_WITH_USERLOOKUP_CREATE( EnergyShieldBehavior, "EnergyShieldBehavior" )
+ MAKE_STANDARD_MODULE_MACRO_WITH_MODULE_DATA( EnergyShieldBehavior, EnergyShieldBehaviorModuleData )
+
+public:
+
+ EnergyShieldBehavior( Thing *thing, const ModuleData* moduleData );
+ // virtual destructor prototype provided by memory pool declaration
+
+ // module methods
+ static Int getInterfaceMask() { return UpdateModule::getInterfaceMask() | MODULEINTERFACE_UPGRADE; } // | MODULEINTERFACE_DAMAGE;
+
+ // BehaviorModule
+ virtual UpgradeModuleInterface* getUpgrade() { return this; }
+ virtual EnergyShieldBehaviorInterface* getEnergyShieldBehaviorInterface() { return this; }
+ //virtual DamageModuleInterface* getDamage() { return this; }
+
+ // DamageModuleInterface
+ //virtual void onDamage( DamageInfo *damageInfo );
+ //virtual void onHealing( DamageInfo *damageInfo ) { }
+ //virtual void onBodyDamageStateChange(const DamageInfo* damageInfo, BodyDamageType oldState, BodyDamageType newState) { }
+
+ // UpdateModuleInterface
+ virtual UpdateSleepTime update();
+ //virtual DisabledMaskType getDisabledTypesToProcess() const { return DISABLEDMASK_ALL; }
+
+ //EnergyShieldBehaviorInterface
+ virtual void applyDamage(Real amount);
+ virtual bool isActive() const { return isUpgradeActive(); }
+ virtual bool shouldShowHealthBar(bool selected) const;
+ virtual Real getShieldPercent() const;
+ virtual RGBAColorInt getHealthBarColor() const { return getEnergyShieldBehaviorModuleData()->m_barColor; }
+ virtual RGBAColorInt getHealthBarBackgroundColor() const { return getEnergyShieldBehaviorModuleData()->m_barBGColor; };
+
+protected:
+
+ virtual void upgradeImplementation();
+
+ virtual void getUpgradeActivationMasks(UpgradeMaskType& activation, UpgradeMaskType& conflicting) const
+ {
+ getEnergyShieldBehaviorModuleData()->m_upgradeMuxData.getUpgradeActivationMasks(activation, conflicting);
+ }
+
+ virtual void performUpgradeFX()
+ {
+ getEnergyShieldBehaviorModuleData()->m_upgradeMuxData.performUpgradeFX(getObject());
+ }
+
+ virtual void processUpgradeRemoval()
+ {
+ // I can't take it any more. Let the record show that I think the UpgradeMux multiple inheritence is CRAP.
+ getEnergyShieldBehaviorModuleData()->m_upgradeMuxData.muxDataProcessUpgradeRemoval(getObject());
+ }
+
+ virtual Bool requiresAllActivationUpgrades() const
+ {
+ return getEnergyShieldBehaviorModuleData()->m_upgradeMuxData.m_requiresAllTriggers;
+ }
+
+ inline Bool isUpgradeActive() const { return isAlreadyUpgraded(); }
+
+ virtual Bool isSubObjectsUpgrade() { return false; }
+
+
+private:
+
+ ShieldBody* m_body;
+ //Real m_currentShieldHealth;
+ UnsignedInt m_healingStepCountdown;
+ UnsignedInt m_shieldHitCountdown;
+
+ void showShield(bool show, bool isHit = false);
+ Bool alwaysShowShield() const;
+};
+
+#endif // __EnergyShieldBehavior_H_
+
diff --git a/GeneralsMD/Code/GameEngine/Include/GameLogic/Module/ShieldBody.h b/GeneralsMD/Code/GameEngine/Include/GameLogic/Module/ShieldBody.h
new file mode 100644
index 00000000000..11fbfc5f572
--- /dev/null
+++ b/GeneralsMD/Code/GameEngine/Include/GameLogic/Module/ShieldBody.h
@@ -0,0 +1,123 @@
+/*
+** 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: ShieldBody.h //////////////////////////////////////////////////////////////////////////
+// Author: Colin Day, November 2001
+// Desc: Structure bodies are active bodies specifically for structures that are built
+// and/or interactable (is that a world) with the player.
+///////////////////////////////////////////////////////////////////////////////////////////////////
+
+#pragma once
+
+#ifndef __ShieldBody_H_
+#define __ShieldBody_H_
+
+// INCLUDES ///////////////////////////////////////////////////////////////////////////////////////
+#include "GameLogic/Module/ActiveBody.h"
+#include "GameLogic/Module/UpdateModule.h"
+
+// FORWARD REFERENCES /////////////////////////////////////////////////////////////////////////////
+class Object;
+class EnergyShieldBehavior;
+
+//-------------------------------------------------------------------------------------------------
+class ShieldBodyModuleData : public ActiveBodyModuleData
+{
+public:
+
+ Bool m_startsActive; ///< Initially active without upgrade;
+ Real m_shieldMaxHealth; ///< MaxHealth of the shield
+ Real m_shieldMaxHealthPercent; ///< MaxHealth as percentage of activeBody MaxHealth (takes priority)
+ ArmorSetType m_shieldArmorSetFlag; ///< armorset to use for damage absorbed by the shield
+
+ DamageTypeFlags m_damageTypesToPassThrough;
+ DamageTypeFlags m_defaultDamageTypesToPassThrough;
+
+ ShieldBodyModuleData();
+
+ static void buildFieldParse(MultiIniFieldParse& p);
+
+ static void parseShieldHealthPercent(INI* ini, void* instance, void* store, const void* userData);
+};
+
+//-------------------------------------------------------------------------------------------------
+/** Structure body module */
+//-------------------------------------------------------------------------------------------------
+class ShieldBody : public ActiveBody //, public UpdateModuleInterface
+{
+
+ MEMORY_POOL_GLUE_WITH_USERLOOKUP_CREATE( ShieldBody, "ShieldBody" )
+ MAKE_STANDARD_MODULE_MACRO_WITH_MODULE_DATA( ShieldBody, ShieldBodyModuleData )
+
+public:
+
+ ShieldBody( Thing *thing, const ModuleData* moduleData );
+ // virtual destructor prototype provided by memory pool declaration
+
+ //static Int getInterfaceMask() { return UpdateModule::getInterfaceMask(); }
+
+ //// UpdateModuleInterface
+ //virtual UpdateSleepTime update(void);
+
+ //virtual UpdateModuleInterface* getUpdate() { return this; }
+
+ //virtual DisabledMaskType getDisabledTypesToProcess() const { return DISABLEDMASK_ALL; }
+
+
+ //void setConstructorObject( Object *obj );
+ //ObjectID getConstructorObjectID( void ) { return m_constructorObjectID; }
+
+ inline Real getShieldMaxHealth() const { return getShieldBodyModuleData()->m_shieldMaxHealth; }
+ inline Real getShieldCurrentHealth() const { return m_currentShieldHealth; }
+ Bool rechargeShieldHealth(Real amount); ///< returns True if on full health;
+
+ inline Bool isActive() const { return m_active; }
+ inline void setActive(Bool value) { m_active = value; }
+
+ Real getShieldPercent();
+
+protected:
+
+ virtual void attemptDamage(DamageInfo* damageInfo); ///< try to damage this object
+ virtual void doDamageFX(const DamageInfo* damageInfo);
+
+ virtual void onDisabledEdge(Bool nowDisabled);
+
+ //ObjectID m_constructorObjectID; ///< object that built this structure
+
+private:
+ Real m_currentShieldHealth;
+
+ EnergyShieldBehaviorInterface* m_shieldBehaviorModule;
+
+ Bool m_active;
+
+ void enableShieldEffects(); // when the shield hp is < 0
+ void disableShieldEffects(); // when the shield hp is 0
+
+ void findShieldBehaviorModule();
+};
+
+#endif // __ShieldBody_H_
+
diff --git a/GeneralsMD/Code/GameEngine/Include/GameLogic/Object.h b/GeneralsMD/Code/GameEngine/Include/GameLogic/Object.h
index 72b85b283e2..429a8f1b9f7 100644
--- a/GeneralsMD/Code/GameEngine/Include/GameLogic/Object.h
+++ b/GeneralsMD/Code/GameEngine/Include/GameLogic/Object.h
@@ -504,6 +504,9 @@ class Object : public Thing, public Snapshot
Weapon* findWaypointFollowingCapableWeapon();
Bool getAmmoPipShowingInfo(Int& numTotal, Int& numFull) const;
+ // Progress bar for various things
+ Bool getProgressBarShowingInfo(bool selected, Real& progress, Int& type, RGBAColorInt& color, RGBAColorInt& colorBG) const;
+
void notifyFiringTrackerShotFired( const Weapon* weaponFired, ObjectID victimID ) ;
/**
@@ -737,7 +740,7 @@ class Object : public Thing, public Snapshot
UnsignedInt m_smcUntil;
- enum { NUM_SLEEP_HELPERS = 8 };
+ enum { NUM_SLEEP_HELPERS = 9 };
ObjectRepulsorHelper* m_repulsorHelper;
ObjectSMCHelper* m_smcHelper;
ObjectWeaponStatusHelper* m_wsHelper;
diff --git a/GeneralsMD/Code/GameEngine/Source/Common/GlobalData.cpp b/GeneralsMD/Code/GameEngine/Source/Common/GlobalData.cpp
index 46e04975780..0a70e795821 100644
--- a/GeneralsMD/Code/GameEngine/Source/Common/GlobalData.cpp
+++ b/GeneralsMD/Code/GameEngine/Source/Common/GlobalData.cpp
@@ -219,6 +219,8 @@ GlobalData* GlobalData::m_theOriginal = NULL;
{ "AmmoPipScreenOffset", INI::parseCoord2D, NULL, offsetof( GlobalData, m_ammoPipScreenOffset ) },
{ "ContainerPipScreenOffset", INI::parseCoord2D, NULL, offsetof( GlobalData, m_containerPipScreenOffset ) },
+ { "ProgressBarYOffset", INI::parseReal, NULL, offsetof(GlobalData, m_progressBarYOffset) },
+
{ "HistoricDamageLimit", INI::parseDurationUnsignedInt, NULL, offsetof( GlobalData, m_historicDamageLimit ) },
{ "MaxTerrainTracks", INI::parseInt, NULL, offsetof( GlobalData, m_maxTerrainTracks ) },
diff --git a/GeneralsMD/Code/GameEngine/Source/Common/System/KindOf.cpp b/GeneralsMD/Code/GameEngine/Source/Common/System/KindOf.cpp
index e48e5ce0952..f3c7e99f06e 100644
--- a/GeneralsMD/Code/GameEngine/Source/Common/System/KindOf.cpp
+++ b/GeneralsMD/Code/GameEngine/Source/Common/System/KindOf.cpp
@@ -165,6 +165,8 @@ const char* KindOfMaskType::s_bitNameList[] =
"ENABLE_INFANTRY_LIGHTING",
"DISABLE_INFANTRY_LIGHTING",
+ "SHOW_PROGRESS_BAR",
+
"VTOL",
"LARGE_AIRCRAFT",
"MEDIUM_AIRCRAFT",
@@ -195,8 +197,6 @@ const char* KindOfMaskType::s_bitNameList[] =
"EXTRA14",
"EXTRA15",
"EXTRA16",
- "EXTRA17",
- "EXTRA18",
NULL
};
diff --git a/GeneralsMD/Code/GameEngine/Source/Common/Thing/ModuleFactory.cpp b/GeneralsMD/Code/GameEngine/Source/Common/Thing/ModuleFactory.cpp
index 85445660282..1858b32032c 100644
--- a/GeneralsMD/Code/GameEngine/Source/Common/Thing/ModuleFactory.cpp
+++ b/GeneralsMD/Code/GameEngine/Source/Common/Thing/ModuleFactory.cpp
@@ -51,6 +51,7 @@
#include "GameLogic/Module/BridgeTowerBehavior.h"
#include "GameLogic/Module/CountermeasuresBehavior.h"
#include "GameLogic/Module/BattlePlanBonusBehavior.h"
+#include "GameLogic/Module/EnergyShieldBehavior.h"
#include "GameLogic/Module/DumbProjectileBehavior.h"
#include "GameLogic/Module/FreeFallProjectileBehavior.h"
#include "GameLogic/Module/InstantDeathBehavior.h"
@@ -270,6 +271,7 @@
#include "GameLogic/Module/ImmortalBody.h"
#include "GameLogic/Module/StructureBody.h"
#include "GameLogic/Module/HiveStructureBody.h"
+#include "GameLogic/Module/ShieldBody.h"
#include "GameLogic/Module/UndeadBody.h"
// contain includes
@@ -345,6 +347,7 @@ void ModuleFactory::init( void )
addModule( BridgeTowerBehavior );
addModule( CountermeasuresBehavior );
addModule( BattlePlanBonusBehavior );
+ addModule( EnergyShieldBehavior );
addModule( DumbProjectileBehavior );
addModule( FreeFallProjectileBehavior );
addModule( PhysicsBehavior );
@@ -565,6 +568,7 @@ void ModuleFactory::init( void )
addModule( ImmortalBody );
addModule( StructureBody );
addModule( HiveStructureBody );
+ addModule( ShieldBody );
addModule( UndeadBody );
// contain modules
diff --git a/GeneralsMD/Code/GameEngine/Source/GameClient/Drawable.cpp b/GeneralsMD/Code/GameEngine/Source/GameClient/Drawable.cpp
index 047461e418d..b429e288515 100644
--- a/GeneralsMD/Code/GameEngine/Source/GameClient/Drawable.cpp
+++ b/GeneralsMD/Code/GameEngine/Source/GameClient/Drawable.cpp
@@ -2876,6 +2876,8 @@ void Drawable::drawIconUI( void )
//Moved this to last so that it shows up over contained and ammo icons.
drawVeterancy( healthBarRegion );
+ drawProgress( healthBarRegion );
+
#ifdef KRIS_BRUTAL_HACK_FOR_AIRCRAFT_CARRIER_DEBUGGING
drawUIText();
#endif
@@ -3100,6 +3102,61 @@ void Drawable::drawAmmo( const IRegion2D *healthBarRegion )
}
}
+}
+// ------------------------------------------------------------------------------------------------
+// ------------------------------------------------------------------------------------------------
+void Drawable::drawProgress( const IRegion2D *healthBarRegion )
+{
+ if (!healthBarRegion)
+ return;
+
+ const Object* obj = getObject();
+
+ //if (!(
+ // TheGlobalData->m_showObjectHealth &&
+ // (isSelected() || (TheInGameUI && (TheInGameUI->getMousedOverDrawableID() == getID())))
+ // //&& obj->getControllingPlayer() == ThePlayerList->getLocalPlayer() // Shields are visible for all
+ // ))
+ // return;
+ if (!TheGlobalData->m_showObjectHealth)
+ return;
+
+ Bool selected = isSelected() || (TheInGameUI && (TheInGameUI->getMousedOverDrawableID() == getID()));
+
+ Real progress;
+ Int type; //not used yet
+
+ RGBAColorInt barColor;
+ RGBAColorInt barColorBG;
+
+ if (!obj->getProgressBarShowingInfo(selected, progress, type, barColor, barColorBG))
+ return;
+
+ Color color, outlineColor;
+
+ color = GameMakeColor(barColor.red, barColor.green, barColor.blue, barColor.alpha);
+ outlineColor = GameMakeColor(barColorBG.red, barColorBG.green, barColorBG.blue, barColorBG.alpha);
+
+
+ Real healthBoxWidth = healthBarRegion->hi.x - healthBarRegion->lo.x;
+
+ Real healthBoxHeight = max(3, healthBarRegion->hi.y - healthBarRegion->lo.y) * 1.5f;
+ Real healthBoxOutlineSize = 1.0f;
+
+ Real yOffset = -6 + TheGlobalData->m_progressBarYOffset;
+
+ // draw the health box outline
+ TheDisplay->drawOpenRect(healthBarRegion->lo.x, healthBarRegion->lo.y + yOffset, healthBoxWidth, healthBoxHeight,
+ healthBoxOutlineSize, outlineColor);
+
+ if (progress > 0) {
+
+ // draw a filled bar for the ammo count
+ TheDisplay->drawFillRect(healthBarRegion->lo.x + 1, healthBarRegion->lo.y + yOffset + 1,
+ (healthBoxWidth - 2) * progress, healthBoxHeight - 2,
+ color);
+ }
+
}
// ------------------------------------------------------------------------------------------------
diff --git a/GeneralsMD/Code/GameEngine/Source/GameLogic/Object/Behavior/EnergyShieldBehavior.cpp b/GeneralsMD/Code/GameEngine/Source/GameLogic/Object/Behavior/EnergyShieldBehavior.cpp
new file mode 100644
index 00000000000..8bc95535e7a
--- /dev/null
+++ b/GeneralsMD/Code/GameEngine/Source/GameLogic/Object/Behavior/EnergyShieldBehavior.cpp
@@ -0,0 +1,365 @@
+/*
+** 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: EnergyShieldBehavior.cpp ///////////////////////////////////////////////////////////////////////
+// Author:
+// Desc:
+///////////////////////////////////////////////////////////////////////////////////////////////////
+
+
+// INCLUDES ///////////////////////////////////////////////////////////////////////////////////////
+#include "PreRTS.h" // This must go first in EVERY cpp file int the GameEngine
+#include "Common/Thing.h"
+#include "Common/ThingTemplate.h"
+#include "Common/INI.h"
+#include "Common/Player.h"
+#include "Common/Xfer.h"
+#include "GameClient/ParticleSys.h"
+#include "GameClient/Anim2D.h"
+#include "GameClient/InGameUI.h"
+#include "GameLogic/Module/EnergyShieldBehavior.h"
+#include "GameLogic/Module/BodyModule.h"
+#include "GameLogic/Module/ShieldBody.h"
+#include "GameLogic/GameLogic.h"
+#include "GameLogic/Object.h"
+#include "GameLogic/PartitionManager.h"
+#include "GameClient/FXList.h"
+
+//-------------------------------------------------------------------------------------------------
+//-------------------------------------------------------------------------------------------------
+EnergyShieldBehaviorModuleData::EnergyShieldBehaviorModuleData()
+{
+ m_barColor.red = 255;
+ m_barColor.green = 255;
+ m_barColor.blue = 255;
+ m_barColor.alpha = 255;
+
+ m_barBGColor.red = 255;
+ m_barBGColor.green = 255;
+ m_barBGColor.blue = 255;
+ m_barBGColor.alpha = 255;
+
+ m_shieldSubObjName.clear();
+ m_shieldConditionFlag = MODELCONDITION_INVALID;
+ m_shieldHitConditionFlag = MODELCONDITION_INVALID;
+}
+//-------------------------------------------------------------------------------------------------
+//-------------------------------------------------------------------------------------------------
+
+/*static*/ void EnergyShieldBehaviorModuleData::buildFieldParse(MultiIniFieldParse& p)
+{
+ static const FieldParse dataFieldParse[] =
+ {
+ { "StartsActive", INI::parseBool, NULL, offsetof(EnergyShieldBehaviorModuleData, m_initiallyActive) },
+
+ { "ShieldRechargeDelay", INI::parseDurationUnsignedInt, NULL, offsetof(EnergyShieldBehaviorModuleData, m_shieldRechargeDelay) },
+ { "ShieldRechargeRate", INI::parseDurationUnsignedInt, NULL, offsetof(EnergyShieldBehaviorModuleData, m_shieldRechargeRate) },
+ { "ShieldRechargeAmount", INI::parseReal, NULL, offsetof(EnergyShieldBehaviorModuleData, m_shieldRechargeAmount) },
+ { "ShieldRechargeAmountPercent", INI::parsePercentToReal, NULL, offsetof(EnergyShieldBehaviorModuleData, m_shieldRechargeAmountPercent) },
+
+ { "ShieldHealthBarColor", INI::parseRGBAColorInt, NULL, offsetof(EnergyShieldBehaviorModuleData, m_barColor) },
+ { "ShieldHealthBarBackgroundColor", INI::parseRGBAColorInt, NULL, offsetof(EnergyShieldBehaviorModuleData, m_barBGColor) },
+ { "ShowHealthBarBackgroundWhenEmpty", INI::parseBool, NULL, offsetof(EnergyShieldBehaviorModuleData, m_showBarWhenEmpty) },
+ { "ShowHealthBarWhenUnselected", INI::parseBool, NULL, offsetof(EnergyShieldBehaviorModuleData, m_showBarWhenUnselected) },
+
+ { "ShieldSubObjectName", INI::parseAsciiString, NULL, offsetof(EnergyShieldBehaviorModuleData, m_shieldSubObjName) },
+ { "ShieldHitSubObjectName", INI::parseAsciiString, NULL, offsetof(EnergyShieldBehaviorModuleData, m_shieldHitSubObjName) },
+
+ { "ShieldModelCondition", INI::parseIndexList, ModelConditionFlags::getBitNames(), offsetof(EnergyShieldBehaviorModuleData, m_shieldConditionFlag) },
+ { "ShieldHitModelCondition", INI::parseIndexList, ModelConditionFlags::getBitNames(), offsetof(EnergyShieldBehaviorModuleData, m_shieldHitConditionFlag) },
+ { "ShieldHitConditionDuration", INI::parseDurationUnsignedInt, NULL, offsetof(EnergyShieldBehaviorModuleData, m_showShieldWhenHitDuration) },
+
+ { "ShieldUpFX", INI::parseFXList, NULL, offsetof(EnergyShieldBehaviorModuleData, m_fxShieldUp) },
+ { "ShieldDownFX", INI::parseFXList, NULL, offsetof(EnergyShieldBehaviorModuleData, m_fxShieldDown) },
+
+ //{ "DamageTypesToPassThroughShield", INI::parseDamageTypeFlags, NULL, offsetof(EnergyShieldBehaviorModuleData, m_damageTypesToPassThrough) },
+ { 0, 0, 0, 0 }
+ };
+
+ UpdateModuleData::buildFieldParse(p);
+ p.add(dataFieldParse);
+ p.add(UpgradeMuxData::getFieldParse(), offsetof(EnergyShieldBehaviorModuleData, m_upgradeMuxData));
+}
+
+//-------------------------------------------------------------------------------------------------
+//-------------------------------------------------------------------------------------------------
+EnergyShieldBehavior::EnergyShieldBehavior( Thing *thing, const ModuleData* moduleData ) : UpdateModule( thing, moduleData )
+{
+
+ ShieldBody* body = dynamic_cast(getObject()->getBodyModule());
+ DEBUG_ASSERTCRASH((body != nullptr), ("EnergyShieldBehavior requires ShieldBody!"));
+
+ m_body = body;
+
+ const EnergyShieldBehaviorModuleData *d = getEnergyShieldBehaviorModuleData();
+
+ if (d->m_initiallyActive)
+ {
+ giveSelfUpgrade();
+ }
+
+ setWakeFrame(getObject(), UPDATE_SLEEP_FOREVER); // We wake when we get attacked
+}
+
+//-------------------------------------------------------------------------------------------------
+//-------------------------------------------------------------------------------------------------
+EnergyShieldBehavior::~EnergyShieldBehavior( void )
+{
+
+}
+//-------------------------------------------------------------------------------------------------
+//-------------------------------------------------------------------------------------------------
+void EnergyShieldBehavior::applyDamage(Real amount)
+{
+ //DEBUG_LOG((">>> EnergyShieldBehavior::applyDamage - amount = %f", amount));
+
+ const EnergyShieldBehaviorModuleData* data = getEnergyShieldBehaviorModuleData();
+ m_healingStepCountdown = data->m_shieldRechargeDelay;
+ setWakeFrame(getObject(), UPDATE_SLEEP_NONE);
+
+ // amount = 0 means the shield was already down
+
+ if (amount > 0)
+ {
+ // Show shield subobject
+ if (data->m_shieldSubObjName.isNotEmpty() || data->m_shieldConditionFlag != MODELCONDITION_INVALID) {
+
+ if (getShieldPercent() <= 0) { // Shield is down, hide it
+
+ showShield(false); // shield
+ showShield(false, true); // shield hit
+
+ FXList::doFXObj(data->m_fxShieldDown, getObject());
+
+ m_shieldHitCountdown = 0;
+ }
+ else if (data->m_showShieldWhenHitDuration > 0) { // Shield took damage, show it
+ if (m_shieldHitCountdown <= 0) { // If it's not already up, show it.
+ showShield(true, true);
+ m_shieldHitCountdown = data->m_showShieldWhenHitDuration;
+ }
+ }
+ }
+ }
+}
+//-------------------------------------------------------------------------------------------------
+//-------------------------------------------------------------------------------------------------
+void EnergyShieldBehavior::showShield(bool show, bool isHit)
+{
+ Drawable* draw = getObject()->getDrawable();
+ if (!draw)
+ return;
+
+ const EnergyShieldBehaviorModuleData* data = getEnergyShieldBehaviorModuleData();
+
+ AsciiString subObjName;
+ ModelConditionFlagType conditionFlag;
+
+ if (isHit) {
+ subObjName = data->m_shieldHitSubObjName;
+ conditionFlag = data->m_shieldHitConditionFlag;
+ }
+ else {
+ subObjName = data->m_shieldSubObjName;
+ conditionFlag = data->m_shieldConditionFlag;
+ }
+
+ if (show) {
+ if (conditionFlag != MODELCONDITION_INVALID) {
+ draw->setModelConditionState(conditionFlag);
+ }
+
+ if (subObjName.isNotEmpty()) {
+ draw->showSubObject(subObjName, true);
+ draw->updateSubObjects();
+ }
+ }
+ else {
+ if (conditionFlag != MODELCONDITION_INVALID) {
+ draw->clearModelConditionState(conditionFlag);
+ }
+
+ if (subObjName.isNotEmpty()) {
+ draw->showSubObject(subObjName, false);
+ draw->updateSubObjects();
+ }
+ }
+}
+
+//-------------------------------------------------------------------------------------------------
+//-------------------------------------------------------------------------------------------------
+Real EnergyShieldBehavior::getShieldPercent() const
+{
+ if (isActive() && m_body != NULL)
+ {
+ return m_body->getShieldPercent();
+ }
+ return 0.0;
+}
+//-------------------------------------------------------------------------------------------------
+//-------------------------------------------------------------------------------------------------
+bool EnergyShieldBehavior::shouldShowHealthBar(bool selected) const
+{
+ if (!isActive() || m_body == NULL)
+ return FALSE;
+
+ const EnergyShieldBehaviorModuleData* data = getEnergyShieldBehaviorModuleData();
+
+ if (!selected && !getEnergyShieldBehaviorModuleData()->m_showBarWhenUnselected)
+ return FALSE;
+
+ if (!data->m_showBarWhenEmpty && m_body->getShieldCurrentHealth() <= 0.0f)
+ return FALSE;
+
+ return TRUE;
+}
+//-------------------------------------------------------------------------------------------------
+//-------------------------------------------------------------------------------------------------
+Bool EnergyShieldBehavior::alwaysShowShield() const
+{
+ return getEnergyShieldBehaviorModuleData()->m_shieldConditionFlag != MODELCONDITION_INVALID ||
+ getEnergyShieldBehaviorModuleData()->m_shieldSubObjName.isNotEmpty();
+}
+//-------------------------------------------------------------------------------------------------
+//-------------------------------------------------------------------------------------------------
+void EnergyShieldBehavior::upgradeImplementation()
+{
+ if (m_body) {
+ m_body->setActive(true);
+ m_body->rechargeShieldHealth(m_body->getShieldMaxHealth());
+
+ // if (alwaysShowShield()) {
+ showShield(true, false);
+ //}
+
+ }
+ //setWakeFrame(getObject(), UPDATE_SLEEP_NONE);
+}
+
+//-------------------------------------------------------------------------------------------------
+//-------------------------------------------------------------------------------------------------
+//void EnergyShieldBehavior::onDisabledEdge(Bool nowDisabled)
+//{
+//
+//}
+//-------------------------------------------------------------------------------------------------
+//-------------------------------------------------------------------------------------------------
+
+//-------------------------------------------------------------------------------------------------
+/** The update callback. */
+//-------------------------------------------------------------------------------------------------
+UpdateSleepTime EnergyShieldBehavior::update( void )
+{
+ //DEBUG_LOG((">>> EnergyShieldBehavior::update1 - m_healingStepCountdown = %d", m_healingStepCountdown));
+
+ const EnergyShieldBehaviorModuleData* data = getEnergyShieldBehaviorModuleData();
+
+ if (m_shieldHitCountdown > 0 && --m_shieldHitCountdown == 0) {
+ showShield(false, true);
+ }
+
+ if (m_healingStepCountdown > 0 && --m_healingStepCountdown == 0) {
+
+ if (m_body) {
+ Real rechargeAmount;
+ if (data->m_shieldRechargeAmountPercent) {
+ rechargeAmount = data->m_shieldRechargeAmountPercent * m_body->getShieldMaxHealth();
+ }
+ else {
+ rechargeAmount = data->m_shieldRechargeAmount;
+ }
+ //DEBUG_LOG((">>> EnergyShieldBehavior::update2 - rechargeAmount = %f", rechargeAmount));
+
+ if (m_body->getShieldCurrentHealth() <= 0)
+ FXList::doFXObj(data->m_fxShieldUp, getObject());
+
+ Bool full = m_body->rechargeShieldHealth(rechargeAmount);
+ showShield(true, false);
+
+ if (!full)
+ m_healingStepCountdown = data->m_shieldRechargeRate;
+ }
+ }
+
+ if (m_healingStepCountdown == 0 && m_shieldHitCountdown == 0)
+ return UPDATE_SLEEP_FOREVER;
+ else
+ return UPDATE_SLEEP_NONE;
+}
+
+// ------------------------------------------------------------------------------------------------
+/** CRC */
+// ------------------------------------------------------------------------------------------------
+void EnergyShieldBehavior::crc( Xfer *xfer )
+{
+
+ // extend base class
+ UpdateModule::crc( xfer );
+
+ // extend base class
+ UpgradeMux::upgradeMuxCRC( xfer );
+
+} // end crc
+
+// ------------------------------------------------------------------------------------------------
+/** Xfer method
+ * Version Info:
+ * 1: Initial version */
+// ------------------------------------------------------------------------------------------------
+void EnergyShieldBehavior::xfer( Xfer *xfer )
+{
+
+ // version
+ XferVersion currentVersion = 1;
+ XferVersion version = currentVersion;
+ xfer->xferVersion( &version, currentVersion );
+
+ // extend base class
+ UpdateModule::xfer( xfer );
+
+ // extend base class
+ UpgradeMux::upgradeMuxXfer( xfer );
+
+ // current shield health
+ //xfer->xferReal(&m_currentShieldHealth);
+ xfer->xferUnsignedInt(&m_healingStepCountdown);
+
+ //hit frames
+ xfer->xferUnsignedInt(&m_shieldHitCountdown);
+
+} // end xfer
+
+// ------------------------------------------------------------------------------------------------
+/** Load post process */
+// ------------------------------------------------------------------------------------------------
+void EnergyShieldBehavior::loadPostProcess( void )
+{
+
+ // extend base class
+ UpdateModule::loadPostProcess();
+
+ // extend base class
+ UpgradeMux::upgradeMuxLoadPostProcess();
+
+} // end loadPostProcess
diff --git a/GeneralsMD/Code/GameEngine/Source/GameLogic/Object/Body/ShieldBody.cpp b/GeneralsMD/Code/GameEngine/Source/GameLogic/Object/Body/ShieldBody.cpp
new file mode 100644
index 00000000000..cdd6b3cbbee
--- /dev/null
+++ b/GeneralsMD/Code/GameEngine/Source/GameLogic/Object/Body/ShieldBody.cpp
@@ -0,0 +1,444 @@
+/*
+** 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: ShieldBody.cpp ////////////////////////////////////////////////////////////////////////
+// Author: Colin Day, November 2001
+// Desc: Structure bodies are active bodies specifically for structures that are built
+// and/or interactable (is that a world) with the player.
+///////////////////////////////////////////////////////////////////////////////////////////////////
+
+// INCLUDES ///////////////////////////////////////////////////////////////////////////////////////
+#include "PreRTS.h" // This must go first in EVERY cpp file int the GameEngine
+#include "Common/Xfer.h"
+#include "GameLogic/Object.h"
+#include "GameLogic/Damage.h"
+#include "GameLogic/Module/ShieldBody.h"
+#include "GameLogic/Module/EnergyShieldBehavior.h"
+#include "GameLogic/ArmorSet.h"
+#include "GameLogic/GameLogic.h"
+#include "Common/Player.h"
+
+// PUBLIC FUNCTIONS ///////////////////////////////////////////////////////////////////////////////
+
+//-------------------------------------------------------------------------------------------------
+//-------------------------------------------------------------------------------------------------
+ShieldBodyModuleData::ShieldBodyModuleData()
+{
+ m_damageTypesToPassThrough = DAMAGE_TYPE_FLAGS_NONE;
+ m_shieldArmorSetFlag = ARMORSET_NONE;
+
+ // Init default pass through types
+ m_defaultDamageTypesToPassThrough = DAMAGE_TYPE_FLAGS_NONE;
+ m_defaultDamageTypesToPassThrough = setDamageTypeFlag(m_defaultDamageTypesToPassThrough, DAMAGE_STATUS);
+ m_defaultDamageTypesToPassThrough = setDamageTypeFlag(m_defaultDamageTypesToPassThrough, DAMAGE_DEPLOY);
+ m_defaultDamageTypesToPassThrough = setDamageTypeFlag(m_defaultDamageTypesToPassThrough, DAMAGE_UNRESISTABLE);
+ m_defaultDamageTypesToPassThrough = setDamageTypeFlag(m_defaultDamageTypesToPassThrough, DAMAGE_HEALING);
+ m_defaultDamageTypesToPassThrough = setDamageTypeFlag(m_defaultDamageTypesToPassThrough, DAMAGE_PENALTY);
+ m_defaultDamageTypesToPassThrough = setDamageTypeFlag(m_defaultDamageTypesToPassThrough, DAMAGE_DISARM);
+ m_defaultDamageTypesToPassThrough = setDamageTypeFlag(m_defaultDamageTypesToPassThrough, DAMAGE_HAZARD_CLEANUP);
+ m_defaultDamageTypesToPassThrough = setDamageTypeFlag(m_defaultDamageTypesToPassThrough, DAMAGE_TOPPLING);
+ m_defaultDamageTypesToPassThrough = setDamageTypeFlag(m_defaultDamageTypesToPassThrough, DAMAGE_SUBDUAL_UNRESISTABLE);
+ m_defaultDamageTypesToPassThrough = setDamageTypeFlag(m_defaultDamageTypesToPassThrough, DAMAGE_CHRONO_UNRESISTABLE);
+}
+
+//-------------------------------------------------------------------------------------------------
+//-------------------------------------------------------------------------------------------------
+void ShieldBodyModuleData::buildFieldParse(MultiIniFieldParse& p)
+{
+ ActiveBodyModuleData::buildFieldParse(p);
+
+ static const FieldParse dataFieldParse[] =
+ {
+ { "StartsActive", INI::parseBool, NULL, offsetof(ShieldBodyModuleData, m_startsActive) },
+
+ { "ShieldMaxHealth", INI::parseReal, NULL, offsetof(ShieldBodyModuleData, m_shieldMaxHealth) },
+ { "ShieldMaxHealthPercent", parseShieldHealthPercent, NULL, 0}, // offsetof(ShieldBodyModuleData, m_shieldMaxHealthPercent) },
+
+ { "ShieldArmorSetFlag", INI::parseIndexList, ArmorSetFlags::getBitNames(), offsetof(ShieldBodyModuleData, m_shieldArmorSetFlag) },
+
+ { "ShieldPassThroughDamageTypes", INI::parseDamageTypeFlags, NULL, offsetof(ShieldBodyModuleData, m_damageTypesToPassThrough) },
+ { "DefaultShieldPassThroughDamageTypes", INI::parseDamageTypeFlags, NULL, offsetof(ShieldBodyModuleData, m_defaultDamageTypesToPassThrough) },
+ { 0, 0, 0, 0 }
+ };
+ p.add(dataFieldParse);
+}
+
+//-------------------------------------------------------------------------------------------------
+//-------------------------------------------------------------------------------------------------
+void ShieldBodyModuleData::parseShieldHealthPercent(INI* ini, void* instance, void* store, const void* /*userData*/)
+{
+ ShieldBodyModuleData* self = (ShieldBodyModuleData*)instance;
+ Real healthPercent = INI::scanPercentToReal(ini->getNextToken());
+ self->m_shieldMaxHealth = self->m_maxHealth * healthPercent;
+}
+
+//-------------------------------------------------------------------------------------------------
+//-------------------------------------------------------------------------------------------------
+ShieldBody::ShieldBody( Thing *thing, const ModuleData* moduleData ) : ActiveBody( thing, moduleData )
+{
+ const ShieldBodyModuleData* data = getShieldBodyModuleData();
+
+ if (data->m_startsActive) {
+ m_active = true;
+ m_currentShieldHealth = data->m_shieldMaxHealth;
+ enableShieldEffects();
+ }
+
+
+} // end ShieldBody
+
+//-------------------------------------------------------------------------------------------------
+//-------------------------------------------------------------------------------------------------
+ShieldBody::~ShieldBody( void )
+{
+
+} // end ~ShieldBody
+
+//-------------------------------------------------------------------------------------------------
+//-------------------------------------------------------------------------------------------------
+Real ShieldBody::getShieldPercent()
+{
+ const ShieldBodyModuleData* data = getShieldBodyModuleData();
+ return m_currentShieldHealth / data->m_shieldMaxHealth;
+}
+//-------------------------------------------------------------------------------------------------
+//-------------------------------------------------------------------------------------------------
+Bool ShieldBody::rechargeShieldHealth(Real amount)
+{
+ const ShieldBodyModuleData* data = getShieldBodyModuleData();
+
+ // state before recharge
+ Bool shieldWasUp = m_currentShieldHealth > 0;
+
+ m_currentShieldHealth = MIN(data->m_shieldMaxHealth, m_currentShieldHealth + amount);
+
+ //DEBUG_LOG((">>> ShieldBody::rechargeShieldHealth - m_currentShieldHealth = %f", m_currentShieldHealth));
+
+ if (!shieldWasUp && m_currentShieldHealth > 0) {
+ enableShieldEffects();
+ }
+
+ return m_currentShieldHealth >= data->m_shieldMaxHealth;
+}
+//-------------------------------------------------------------------------------------------------
+//-------------------------------------------------------------------------------------------------
+void ShieldBody::findShieldBehaviorModule() {
+
+ EnergyShieldBehaviorInterface* esbi = NULL;
+
+ for (BehaviorModule** u = getObject()->getBehaviorModules(); *u; ++u)
+ {
+ if ((esbi = (*u)->getEnergyShieldBehaviorInterface()) != NULL) {
+ m_shieldBehaviorModule = esbi;
+ return;
+ }
+ }
+
+}
+//-------------------------------------------------------------------------------------------------
+//-------------------------------------------------------------------------------------------------
+void ShieldBody::enableShieldEffects() {
+ const ShieldBodyModuleData* data = getShieldBodyModuleData();
+ if (data->m_shieldArmorSetFlag != ARMORSET_NONE) {
+ getObject()->setArmorSetFlag(data->m_shieldArmorSetFlag);
+ validateArmorAndDamageFX();
+ }
+}
+//-------------------------------------------------------------------------------------------------
+//-------------------------------------------------------------------------------------------------
+void ShieldBody::disableShieldEffects() {
+ const ShieldBodyModuleData* data = getShieldBodyModuleData();
+ if (data->m_shieldArmorSetFlag != ARMORSET_NONE) {
+ getObject()->clearArmorSetFlag(data->m_shieldArmorSetFlag);
+ validateArmorAndDamageFX();
+ }
+}
+//-------------------------------------------------------------------------------------------------
+//-------------------------------------------------------------------------------------------------
+void ShieldBody::doDamageFX(const DamageInfo* damageInfo)
+{
+ // If our damage is absorbed by the shield, we play the damageFX early to have the proper numbers
+ // To avoid playing the damageFX again later on, we check if our shield is still up
+ // and damage is 0, which means we got a full absorb
+
+ if (m_currentShieldHealth > 0 && damageInfo->out.m_actualDamageDealt == 0)
+ return;
+
+ ActiveBody::doDamageFX(damageInfo);
+
+}
+//-------------------------------------------------------------------------------------------------
+//-------------------------------------------------------------------------------------------------
+void ShieldBody::onDisabledEdge(Bool nowDisabled)
+{
+ if (!isActive()) { // If the shield isn't enabled, no need to do anything
+ return;
+ }
+
+ if (nowDisabled) {
+ m_currentShieldHealth = 0;
+ disableShieldEffects();
+
+ if (m_shieldBehaviorModule == NULL) {
+ findShieldBehaviorModule();
+ }
+
+ if (!m_shieldBehaviorModule) {
+ return;
+ }
+
+ m_shieldBehaviorModule->applyDamage(getShieldMaxHealth());
+ }
+}
+//-------------------------------------------------------------------------------------------------
+//-------------------------------------------------------------------------------------------------
+void ShieldBody::attemptDamage(DamageInfo* damageInfo)
+{
+ // Shield is not enabled, just pass through
+ if (!isActive()) {
+ ActiveBody::attemptDamage(damageInfo);
+ return;
+ }
+
+ validateArmorAndDamageFX();
+
+ // We need to do all the early return checks from ActiveBody here as well
+ // For anything that should prevent even hitting the shield
+
+ // sanity
+ if (damageInfo == NULL)
+ return;
+
+ if (isIndestructible())
+ return;
+
+ // initialize these, just in case we bail out early
+ damageInfo->out.m_actualDamageDealt = 0.0f;
+ damageInfo->out.m_actualDamageClipped = 0.0f;
+
+ // we cannot damage again objects that are already dead
+ Object* obj = getObject();
+ if (obj->isEffectivelyDead())
+ return;
+
+ // Units that get disabled by Chrono damage cannot take damage:
+ if (obj->isDisabledByType(DISABLED_CHRONO) &&
+ !(damageInfo->in.m_damageType == DAMAGE_CHRONO_GUN || damageInfo->in.m_damageType == DAMAGE_CHRONO_UNRESISTABLE))
+ return;
+
+ // --
+ // BEGIN SHIELD CALCULATIONS
+ // ---
+
+ const ShieldBodyModuleData* data = getShieldBodyModuleData();
+
+ // Pass through full damage
+ if (getDamageTypeFlag(data->m_damageTypesToPassThrough, damageInfo->in.m_damageType) ||
+ getDamageTypeFlag(data->m_defaultDamageTypesToPassThrough, damageInfo->in.m_damageType))
+ {
+ // We need to switch our armorset for this one
+ if (data->m_shieldArmorSetFlag != ARMORSET_NONE) {
+ getObject()->clearArmorSetFlag(data->m_shieldArmorSetFlag);
+ }
+
+ ActiveBody::attemptDamage(damageInfo);
+
+ // And switch back
+ if (data->m_shieldArmorSetFlag != ARMORSET_NONE) {
+ getObject()->setArmorSetFlag(data->m_shieldArmorSetFlag);
+ //validateArmorAndDamageFX();
+ }
+ return;
+ }
+
+ // Calculate Shield damage:
+ // TODO: If we have other ways to use the shield, we could add some conditions
+ if (m_shieldBehaviorModule == NULL) {
+ findShieldBehaviorModule();
+ }
+
+ if (!m_shieldBehaviorModule) {
+ DEBUG_LOG(("ShieldBody::attemptDamage - Warning, no EnergyShieldBehaviorModule found."));
+ ActiveBody::attemptDamage(damageInfo);
+ return;
+ }
+
+ //Shield is already down, just pass through, but stop recovery
+ if (m_currentShieldHealth <= 0) {
+ m_shieldBehaviorModule->applyDamage(0.0f);
+ ActiveBody::attemptDamage(damageInfo);
+ return;
+ }
+
+ Real rawDamage = damageInfo->in.m_amount;
+ Real damageToShield = getCurrentArmor().adjustDamage(damageInfo->in.m_damageType, damageInfo->in.m_amount);
+
+ //Note: we apply the damage scalar only to the damage to the actual shield.
+ // It's later applied again for overkill damage
+ if (damageInfo->in.m_damageType != DAMAGE_UNRESISTABLE)
+ {
+ damageToShield *= m_damageScalar;
+ }
+
+ Real damageAmountToPass = 0.0; // The damage (done to HP) we pass to the base function
+
+ Bool shieldStillUp = TRUE;
+ if (damageToShield > 0) {
+ Real remainingShieldHealth = m_currentShieldHealth - damageToShield;
+
+ shieldStillUp = remainingShieldHealth > 0;
+
+ m_currentShieldHealth = MAX(0, remainingShieldHealth);
+
+ // Apply Damage to shield and Stop shield recharge - Notify shield behavior
+ m_shieldBehaviorModule->applyDamage(MAX(0, damageToShield));
+ //DEBUG_LOG(("ShieldBody::attemptDamage - m_currentShieldHealth = %f.", m_currentShieldHealth));
+
+ //If the shield is at 0, we still need to disable it
+ if (!shieldStillUp) {
+
+ // Disable Shield Armor
+ disableShieldEffects();
+
+ Real overkillDamage = abs(remainingShieldHealth);
+
+ // the shield overkill damage was already mitigated by shield armor
+ // so we want it's relative amount of the raw damage (i.e. we invert the armor)
+ Real relativeRawDamage = (overkillDamage / damageToShield) * rawDamage;
+
+ damageAmountToPass = relativeRawDamage;
+ }
+ }
+
+ if (shieldStillUp) {
+ // Only play the damageFX on a full absorb
+ damageInfo->out.m_actualDamageDealt = damageToShield; //only used for damageFX
+ doDamageFX(damageInfo);
+ damageInfo->out.m_actualDamageDealt = 0;
+ }
+
+
+ damageInfo->in.m_amount = damageAmountToPass;
+
+ // extend
+ ActiveBody::attemptDamage(damageInfo);
+ //DEBUG_LOG(("ShieldBody::attemptDamage - getHealth() = %f.", getHealth()));
+
+ if (shieldStillUp) {
+ // We passed on 0 damage to ActiveBody.
+ // This means we need to do a couple of things that were skipped
+
+ if (m_lastDamageTimestamp != TheGameLogic->getFrame() && m_lastDamageTimestamp != TheGameLogic->getFrame() - 1) {
+ m_lastDamageInfo = *damageInfo;
+ m_lastDamageCleared = false;
+ m_lastDamageTimestamp = TheGameLogic->getFrame();
+ }
+ else {
+ // Multiple damages applied in one/next frame. We prefer the one that tells who the attacker is.
+ Object* srcObj1 = TheGameLogic->findObjectByID(m_lastDamageInfo.in.m_sourceID);
+ Object* srcObj2 = TheGameLogic->findObjectByID(damageInfo->in.m_sourceID);
+ if (srcObj2) {
+ if (srcObj1) {
+ if (srcObj2->isKindOf(KINDOF_VEHICLE) || srcObj2->isKindOf(KINDOF_INFANTRY) ||
+ srcObj2->isFactionStructure()) {
+ m_lastDamageInfo = *damageInfo;
+ m_lastDamageCleared = false;
+ m_lastDamageTimestamp = TheGameLogic->getFrame();
+ }
+ }
+ else {
+ m_lastDamageInfo = *damageInfo;
+ m_lastDamageCleared = false;
+ m_lastDamageTimestamp = TheGameLogic->getFrame();
+ }
+
+ }
+ else {
+ // no change.
+ }
+ }
+
+ // Notify the player that they have been attacked by this player
+ if (m_lastDamageInfo.in.m_sourceID != INVALID_ID)
+ {
+ Object* srcObj = TheGameLogic->findObjectByID(m_lastDamageInfo.in.m_sourceID);
+ if (srcObj)
+ {
+ Player* srcPlayer = srcObj->getControllingPlayer();
+ obj->getControllingPlayer()->setAttackedBy(srcPlayer->getPlayerIndex());
+ }
+ }
+
+ }
+
+}
+// ------------------------------------------------------------------------------------------------
+/** CRC */
+// ------------------------------------------------------------------------------------------------
+void ShieldBody::crc( Xfer *xfer )
+{
+
+ // extend base class
+ ActiveBody::crc( xfer );
+
+} // end crc
+
+// ------------------------------------------------------------------------------------------------
+/** Xfer method
+ * Version Info:
+ * 1: Initial version */
+// ------------------------------------------------------------------------------------------------
+void ShieldBody::xfer( Xfer *xfer )
+{
+
+ // version
+ XferVersion currentVersion = 1;
+ XferVersion version = currentVersion;
+ xfer->xferVersion( &version, currentVersion );
+
+ // base class
+ ActiveBody::xfer( xfer );
+
+ // current shield health
+ xfer->xferReal(&m_currentShieldHealth);
+ //xfer->xferUnsignedInt(&m_healingStepCountdown);
+
+ // shield activation
+ xfer->xferBool(&m_active);
+
+ // constructor object id
+ //xfer->xferObjectID( &m_constructorObjectID );
+
+} // end xfer
+
+// ------------------------------------------------------------------------------------------------
+/** Load post process */
+// ------------------------------------------------------------------------------------------------
+void ShieldBody::loadPostProcess( void )
+{
+
+ // extend base class
+ ActiveBody::loadPostProcess();
+
+} // end loadPostProcess
diff --git a/GeneralsMD/Code/GameEngine/Source/GameLogic/Object/Object.cpp b/GeneralsMD/Code/GameEngine/Source/GameLogic/Object/Object.cpp
index 075905525c7..9a573a4d4d2 100644
--- a/GeneralsMD/Code/GameEngine/Source/GameLogic/Object/Object.cpp
+++ b/GeneralsMD/Code/GameEngine/Source/GameLogic/Object/Object.cpp
@@ -98,6 +98,7 @@
#include "GameLogic/Module/ToppleUpdate.h"
#include "GameLogic/Module/UpdateModule.h"
#include "GameLogic/Module/UpgradeModule.h"
+#include "GameLogic/Module/EnergyShieldBehavior.h"
#include "GameLogic/Object.h"
#include "GameLogic/PartitionManager.h"
@@ -1503,6 +1504,40 @@ Bool Object::getAmmoPipShowingInfo(Int& numTotal, Int& numFull) const
}
}
+//=============================================================================
+Bool Object::getProgressBarShowingInfo(bool selected, Real& progress, Int& type, RGBAColorInt& color, RGBAColorInt& colorBG) const
+{
+ if (!isKindOf(KINDOF_SHOW_PROGRESS_BAR))
+ return FALSE;
+
+ // We put every case of Progress bars here.
+ // Maybe we should require a KindOf for performance?
+
+ type = 0; // TODO
+ color = { 255, 255, 255, 255 }; // Default = white
+ colorBG = { 255, 255, 255, 255 }; // Default = white
+
+ // Energy Shields
+ // TODO: This is kinda bad, there should be a better way to do this, if we have multiple shield sources.
+ // This should come from the Body?
+ EnergyShieldBehaviorInterface* esbi = NULL;
+
+ for (BehaviorModule** u = m_behaviors; *u; ++u)
+ {
+ if ((esbi = (*u)->getEnergyShieldBehaviorInterface()) != NULL) {
+ if (esbi->shouldShowHealthBar(selected)) {
+ progress = esbi->getShieldPercent();
+ color = esbi->getHealthBarColor();
+ colorBG = esbi->getHealthBarBackgroundColor();
+ return true;
+ }
+ }
+ }
+
+ return false;
+
+}
+
//=============================================================================
/*
NOTE: getAbleToAttackSpecificObject NO LONGER internally calls isAbleToAttack(),
diff --git a/GeneralsMD/Code/GameEngine/Source/GameLogic/Object/Upgrade/ArmorUpgrade.cpp b/GeneralsMD/Code/GameEngine/Source/GameLogic/Object/Upgrade/ArmorUpgrade.cpp
index 9b7cffe0099..c60b7560e11 100644
--- a/GeneralsMD/Code/GameEngine/Source/GameLogic/Object/Upgrade/ArmorUpgrade.cpp
+++ b/GeneralsMD/Code/GameEngine/Source/GameLogic/Object/Upgrade/ArmorUpgrade.cpp
@@ -120,22 +120,16 @@ void ArmorUpgrade::upgradeImplementation( )
// Very simple; just need to flag the Object as having the player upgrade, and the WeaponSet chooser
// will do the work of picking the right one from ini. This comment is as long as the code.
- DEBUG_LOG(("ArmorUpgrade::upgradeImplementation 0\n"));
-
const ArmorUpgradeModuleData* data = getArmorUpgradeModuleData();
Object *obj = getObject();
if( !obj )
return;
- DEBUG_LOG(("ArmorUpgrade::upgradeImplementation 1\n"));
-
BodyModuleInterface* body = obj->getBodyModule();
if (body) {
body->setArmorSetFlag(data->m_armorSetFlag);
- DEBUG_LOG(("ArmorUpgrade::upgradeImplementation 2 - flagsToClear = %d\n", data->m_armorSetFlagsToClear));
-
if (data->m_armorSetFlagsToClear.any()) {
// We loop over each armorset type and see if we have it set.
// Andi: Not sure if this is cleaner solution than storing an array of flags.