/* ** 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: Drawable.h /////////////////////////////////////////////////////////////////////////////// // Simple base drawable // Author: Michael S. Booth, March 2001 #pragma once #ifndef _DRAWABLE_H_ #define _DRAWABLE_H_ #include "Common/AudioEventRTS.h" #include "Common/GameType.h" #include "Common/ModelState.h" #include "Common/ModuleFactory.h" #include "Common/Thing.h" #include "Common/Geometry.h" #include "GameClient/Color.h" #include "WWMath/Matrix3D.h" #include "GameClient/DrawableInfo.h" // FORWARD REFERENCES ///////////////////////////////////////////////////////////////////////////// class PositionalSound; class ThingTemplate; class Particle; class DisplayString; class FXList; class DrawModule; class ClientUpdateModule; class View; class Locomotor; class Anim2D; class Shadow; class ModuleInfo; class Anim2DTemplate; class Image; class DynamicAudioEventInfo; enum BodyDamageType; // this is a very worthwhile performance win. left conditionally defined for now, just // in case, but probably should be made permanent soon. (srj) #define DIRTY_CONDITION_FLAGS #define DEFAULT_TINT_COLOR_FADE_RATE (0.6f) // fast fade #define DEF_ATTACK_FRAMES (1) #define DEF_SUSTAIN_FRAMES (1) #define DEF_DECAY_FRAMES (4) #define SUSTAIN_INDEFINITELY (0xfffffffe) // forever and ever, amen. //----------------------------------------------------------------------------- //@TODO -- The drawable icon system needs to be implemented in a proper manner -- KM // Fact 1: Every drawable in the world shouldn't have to have a pointer // and frame counter for every possible icon type. It should be a dynamic vector. // Fact 2: It's polling every frame for every object on screen for every possible icon condition... // KM : I moved this into Drawable.cpp so I don't have to recompile the entire project // each time I add a new icon, and made the arrays dynamic... // CD: No so good, core engine components should not be made dynamic in this way enum DrawableIconType { /** NOTE: This enum MUST appear in the same order as TheDrawableIconNames array to be * indexed correctly using that array */ ICON_INVALID = -1, ICON_FIRST = 0, ICON_DEFAULT_HEAL = ICON_FIRST, ICON_STRUCTURE_HEAL, ICON_VEHICLE_HEAL, #ifdef ALLOW_DEMORALIZE ICON_DEMORALIZED, #else ICON_DEMORALIZED_OBSOLETE, #endif ICON_BOMB_TIMED, ICON_BOMB_REMOTE, ICON_DISABLED, ICON_BATTLEPLAN_BOMBARD, ICON_BATTLEPLAN_HOLDTHELINE, ICON_BATTLEPLAN_SEARCHANDDESTROY, ICON_EMOTICON, ICON_ENTHUSIASTIC, ICON_ENTHUSIASTIC_SUBLIMINAL, ICON_CARBOMB, MAX_ICONS ///< keep this last }; //----------------------------------------------------------------------------- class DrawableIconInfo : public MemoryPoolObject { MEMORY_POOL_GLUE_WITH_USERLOOKUP_CREATE(DrawableIconInfo, "DrawableIconInfo" ) public: Anim2D* m_icon[MAX_ICONS]; UnsignedInt m_keepTillFrame[MAX_ICONS]; DrawableIconInfo(); //~DrawableIconInfo(); void clear(); void killIcon(DrawableIconType t); }; //----------------------------------------------------------------------------- struct TWheelInfo { Real m_frontLeftHeightOffset; ///< Height offsets for tires due to suspension sway Real m_frontRightHeightOffset; Real m_rearLeftHeightOffset; Real m_rearRightHeightOffset; Real m_wheelAngle; ///< Wheel angle. 0 = straight, >0 left, <0 right. Int m_framesAirborneCounter; ///< Counter. Int m_framesAirborne; ///< How many frames it was in the air. }; //----------------------------------------------------------------------------- class DrawableLocoInfo : public MemoryPoolObject { MEMORY_POOL_GLUE_WITH_USERLOOKUP_CREATE(DrawableLocoInfo, "DrawableLocoInfo" ) public: Real m_pitch; ///< pitch of the entire drawable Real m_pitchRate; ///< rate of change of pitch Real m_roll; ///< roll of the entire drawable Real m_rollRate; ///< rate of change of roll Real m_yaw; ///< yaw for entire drawable Real m_accelerationPitch; ///< pitch of the drawable due to impact/acceleration Real m_accelerationPitchRate; ///< rate of change of pitch Real m_accelerationRoll; ///< roll of the entire drawable Real m_accelerationRollRate; ///< rate of change of roll Real m_overlapZVel; ///< fake Z velocity Real m_overlapZ; ///< current height (additional) Real m_wobble; ///< for wobbling Real m_yawModulator; ///< for the swimmy soft hover of a helicopter Real m_pitchModulator; ///< for the swimmy soft hover of a helicopter TWheelInfo m_wheelInfo; ///< Wheel offset & angle info for a wheeled type locomotor. DrawableLocoInfo(); }; //----------------------------------------------------------------------------- //* TintEnvelope handles the fading of the tint color up, down stable etc... //* assumes that 0,0,0, is the color for the AT REST state, used as decay target //* works like an ADSR envelope, //* except that SUSTAIN and RELEASE are randomly (or never) triggered, externally class TintEnvelope : public MemoryPoolObject, public Snapshot { MEMORY_POOL_GLUE_WITH_USERLOOKUP_CREATE(TintEnvelope, "TintEnvelope" ) public: TintEnvelope(void); void update(void); ///< does all the work void play(const RGBColor *peak, UnsignedInt atackFrames = DEF_ATTACK_FRAMES, UnsignedInt decayFrames = DEF_DECAY_FRAMES, UnsignedInt sustainAtPeak = DEF_SUSTAIN_FRAMES ); // ask MLorenzen void sustain(void) { m_envState = ENVELOPE_STATE_SUSTAIN; } void release(void) { m_envState = ENVELOPE_STATE_DECAY; } void rest(void) { m_envState = ENVELOPE_STATE_REST; } // goes away now! Bool isEffective() const { return m_affect; } const Vector3* getColor() const { return &m_currentColor; } protected: // snapshot methods virtual void crc( Xfer *xfer ); virtual void xfer( Xfer *xfer ); virtual void loadPostProcess( void ); private: void setAttackFrames(UnsignedInt frames); void setDecayFrames( UnsignedInt frames); void setPeakColor( const RGBColor *peak) {m_peakColor = Vector3( peak->red, peak->green, peak->blue );}; void setPeakColor( Real r, Real g, Real b ) {m_peakColor.Set( r, g, b );}; enum EnvelopeStatesEnum { ENVELOPE_STATE_REST, ENVELOPE_STATE_ATTACK, ENVELOPE_STATE_DECAY, ENVELOPE_STATE_SUSTAIN ///< RELEASE IS THE LOGICAL COMPLIMENT TO SUSTAIN }; Vector3 m_attackRate; ///< step amount to make tint turn on slow or fast Vector3 m_decayRate; ///< step amount to make tint turn off slow or fast Vector3 m_peakColor; ///< um, the peak color, what color we are headed toward during attack Vector3 m_currentColor; ///< um, the current color, how we are colored, now UnsignedInt m_sustainCounter; Byte m_envState; ///< a randomly switchable SUSTAIN state, release is compliment Bool m_affect; ///< set TRUE if this has any effect (has a non 0,0,0 color). }; EMPTY_DTOR(TintEnvelope) //----------------------------------------------------------------------------- enum StealthLookType { STEALTHLOOK_NONE, ///< unit is not stealthed at all STEALTHLOOK_VISIBLE_FRIENDLY, ///< unit is stealthed-but-visible due to friendly status STEALTHLOOK_DISGUISED_ENEMY, ///< we can have units that are disguised (instead of invisible) STEALTHLOOK_VISIBLE_DETECTED, ///< unit is stealthed and invisible, but a second material pass ///< is added to reveal the invisible unit as with heat vision STEALTHLOOK_VISIBLE_FRIENDLY_DETECTED, ///< unit is stealthed-but-visible due to being detected, ///< and rendered in heatvision effect second material pass STEALTHLOOK_INVISIBLE ///< unit is stealthed-and-invisible }; // ------------------------------------------------------------------------------------------------ /** Drawable status bits */ // ------------------------------------------------------------------------------------------------ enum DrawableStatus { DRAWABLE_STATUS_NONE = 0x00000000, ///< no status DRAWABLE_STATUS_DRAWS_IN_MIRROR = 0x00000001, ///< drawable can reflect DRAWABLE_STATUS_SHADOWS = 0x00000002, ///< use setShadowsEnabled() access method DRAWABLE_STATUS_TINT_COLOR_LOCKED = 0x00000004, ///< drawable tint color is "locked" and won't fade to normal DRAWABLE_STATUS_NO_STATE_PARTICLES = 0x00000008, ///< do *not* auto-create particle systems based on model condition DRAWABLE_STATUS_NO_SAVE = 0x00000010, ///< do *not* save this drawable (UI fluff only). ignored (error, actually) if attached to an object }; enum TintStatus { TINT_STATUS_DISABLED = 0x00000001,///< drawable tint color is deathly dark grey TINT_STATUS_IRRADIATED = 0x00000002,///< drawable tint color is sickly green TINT_STATUS_POISONED = 0x00000004,///< drawable tint color is open-sore red TINT_STATUS_GAINING_SUBDUAL_DAMAGE = 0x00000008,///< When gaining subdual damage, we tint SUBDUAL_DAMAGE_COLOR TINT_STATUS_FRENZY = 0x00000010,///< When frenzied, we tint FRENZY_COLOR }; //----------------------------------------------------------------------------- //Keep this enum in sync with the TerrainDecalTextureName array in drawable.cpp // // Note: these values are saved in save files, so you MUST NOT REMOVE OR CHANGE // existing values! // enum TerrainDecalType { #ifdef ALLOW_DEMORALIZE TERRAIN_DECAL_DEMORALIZED = 0, #else TERRAIN_DECAL_DEMORALIZED_OBSOLETE = 0, #endif TERRAIN_DECAL_HORDE, TERRAIN_DECAL_HORDE_WITH_NATIONALISM_UPGRADE, TERRAIN_DECAL_HORDE_VEHICLE, TERRAIN_DECAL_HORDE_WITH_NATIONALISM_UPGRADE_VEHICLE, TERRAIN_DECAL_CRATE, TERRAIN_DECAL_HORDE_WITH_FANATICISM_UPGRADE, TERRAIN_DECAL_CHEMSUIT, TERRAIN_DECAL_NONE, TERRAIN_DECAL_SHADOW_TEXTURE, //use the shadow texture as the terrain decal. TERRAIN_DECAL_MAX ///< keep this last }; //----------------------------------------------------------------------------- const Int DRAWABLE_FRAMES_PER_FLASH = LOGICFRAMES_PER_SECOND / 2; //----------------------------------------------------------------------------- /** * A Drawable is a graphical entity which is generally associated * with a GameLogic object. Drawables are managed by TheGameClient. */ class Drawable : public Thing, public Snapshot { MEMORY_POOL_GLUE_WITH_USERLOOKUP_CREATE(Drawable, "Drawable" ) public: Drawable( const ThingTemplate *thing, DrawableStatus statusBits = DRAWABLE_STATUS_NONE ); void onDestroy( void ); ///< run from GameClient::destroyDrawable void onLevelStart(); ///< run from GameLogic::startNewGame Drawable *getNextDrawable( void ) const { return m_nextDrawable; } ///< return the next drawable in the global list Drawable *getPrevDrawable( void ) const { return m_prevDrawable; } ///< return the prev drawable in the global list DrawableID getID( void ) const; ///< return this drawable's unique ID void friend_bindToObject( Object *obj ); ///< bind this drawable to an object ID. for use ONLY by GameLogic! void setIndicatorColor(Color color); void setTintStatus( TintStatus statusBits ) { BitSet( m_tintStatus, statusBits ); }; void clearTintStatus( TintStatus statusBits ) { BitClear( m_tintStatus, statusBits ); }; Bool testTintStatus( TintStatus statusBits ) const { return BitTest( m_tintStatus, statusBits ); }; TintEnvelope *getColorTintEnvelope( void ) { return m_colorTintEnvelope; } void setColorTintEnvelope( TintEnvelope &source ) { if (m_colorTintEnvelope) *m_colorTintEnvelope = source; } void imitateStealthLook( Drawable& otherDraw ); void setTerrainDecal(TerrainDecalType type); ///