/*** * * Copyright (c) 1996-2001, Valve LLC. All rights reserved. * * This product contains software technology licensed from Id * Software, Inc. ("Id Technology"). Id Technology (c) 1996 Id Software, Inc. * All Rights Reserved. * * Use, distribution, and modification of this source code and/or resulting * object code is restricted to non-commercial enhancements to products from * Valve LLC. All other use, distribution, or modification is prohibited * without written permission from Valve LLC. * ****/ /* Class Hierachy CMBaseEntity CMBaseDelay CMBaseAnimating CMBaseToggle CMBaseMonster */ #include "monster_plugin.h" #define MAX_PATH_SIZE 10 // max number of nodes available for a path. // These are caps bits to indicate what an object's capabilities (currently used for save/restore and level transitions) #define FCAP_CUSTOMSAVE 0x00000001 #define FCAP_ACROSS_TRANSITION 0x00000002 // should transfer between transitions #define FCAP_MUST_SPAWN 0x00000004 // Spawn after restore #define FCAP_DONT_SAVE 0x80000000 // Don't save this #define FCAP_IMPULSE_USE 0x00000008 // can be used by the player #define FCAP_CONTINUOUS_USE 0x00000010 // can be used by the player #define FCAP_ONOFF_USE 0x00000020 // can be used by the player #define FCAP_DIRECTIONAL_USE 0x00000040 // Player sends +/- 1 when using (currently only tracktrains) #define FCAP_MASTER 0x00000080 // Can be used to "master" other entities (like multisource) // UNDONE: This will ignore transition volumes (trigger_transition), but not the PVS!!! #define FCAP_FORCE_TRANSITION 0x00000080 // ALWAYS goes across transitions #include "schedule.h" #ifndef MONSTEREVENT_H #include "monsterevent.h" #endif // C functions for external declarations that call the appropriate C++ methods #ifdef _WIN32 #define EXPORT __declspec( dllexport ) #else #define EXPORT /* */ #endif typedef enum { USE_OFF = 0, USE_ON = 1, USE_SET = 2, USE_TOGGLE = 3 } USE_TYPE; class CMBaseEntity; extern void FireTargets( const char *targetName, edict_t *pActivator, edict_t *pCaller, USE_TYPE useType, float value ); typedef void (CMBaseEntity::*BASEPTR)(void); typedef void (CMBaseEntity::*ENTITYFUNCPTR)(CMBaseEntity *pOther ); typedef void (CMBaseEntity::*USEPTR)( edict_t *pActivator, edict_t *pCaller, USE_TYPE useType, float value ); // For CLASSIFY #define CLASS_NONE 0 #define CLASS_MACHINE 1 #define CLASS_PLAYER 2 #define CLASS_HUMAN_PASSIVE 3 #define CLASS_HUMAN_MILITARY 4 #define CLASS_ALIEN_MILITARY 5 #define CLASS_ALIEN_PASSIVE 6 #define CLASS_ALIEN_MONSTER 7 #define CLASS_ALIEN_PREY 8 #define CLASS_ALIEN_PREDATOR 9 #define CLASS_INSECT 10 #define CLASS_PLAYER_ALLY 11 #define CLASS_PLAYER_BIOWEAPON 12 // hornets and snarks.launched by players #define CLASS_ALIEN_BIOWEAPON 13 // hornets and snarks.launched by the alien menace #define CLASS_RACEX_PITDRONE 14 #define CLASS_RACEX_SHOCK 15 #define CLASS_BARNACLE 99 // special because no one pays attention to it, and it eats a wide cross-section of creatures. class CMBaseEntity; class CMBaseMonster; #define SF_NORESPAWN ( 1 << 30 )// !!!set this bit on guns and stuff that should never respawn. // // EHANDLE. Safe way to point to edict_t who may die between frames // class EHANDLE { private: edict_t *m_pent; int m_serialnumber; public: edict_t *Get( void ); edict_t *Set( edict_t *pent ); operator edict_t *(); edict_t * operator = (edict_t *pEntity); edict_t * operator ->(); }; template T * GetClassPtr( T *a ); // // Base Entity. All entity types derive from this // class CMBaseEntity { public: // Constructor. Set engine to use C/C++ callback functions // pointers to engine data entvars_t *pev; // Don't need to save/restore this pointer, the engine resets it // path corners edict_t *m_pGoalEnt;// path corner we are heading towards edict_t *m_edictList[100]; int m_edictList_count; void *operator new( size_t stAllocateBlock ); virtual edict_t *CreateEntity(char *classname); // initialization functions virtual void Spawn( void ) { return; } virtual void Precache( void ) { return; } virtual void KeyValue( KeyValueData* pkvd) { pkvd->fHandled = FALSE; } virtual int ObjectCaps( void ) { return FCAP_ACROSS_TRANSITION; } virtual void Activate( void ) {} // Setup the object->object collision box (pev->mins / pev->maxs is the object->world collision box) virtual void SetObjectCollisionBox( void ); // Classify - returns the type of group (i.e, "houndeye", or "human military" so that monsters with different classnames // still realize that they are teammates. (overridden for monsters that form groups) virtual int Classify ( void ) { return CLASS_NONE; }; virtual void DeathNotice ( entvars_t *pevChild ) {}// monster maker children use this to tell the monster maker that they have died. virtual void TraceAttack( entvars_t *pevAttacker, float flDamage, Vector vecDir, TraceResult *ptr, int bitsDamageType); virtual int TakeDamage( entvars_t* pevInflictor, entvars_t* pevAttacker, float flDamage, int bitsDamageType ); virtual int TakeHealth( float flHealth, int bitsDamageType ); virtual void Killed( entvars_t *pevAttacker, int iGib ); virtual int BloodColor( void ) { return DONT_BLEED; } virtual void TraceBleed( float flDamage, Vector vecDir, TraceResult *ptr, int bitsDamageType ); virtual BOOL IsTriggered( CMBaseEntity *pActivator ) {return TRUE;} virtual CMBaseMonster *MyMonsterPointer( void ) { return NULL;} virtual int GetToggleState( void ) { return TS_AT_TOP; } virtual void AddPoints( int score, BOOL bAllowNegativeScore ) {} virtual void AddPointsToTeam( int score, BOOL bAllowNegativeScore ) {} virtual float GetDelay( void ) { return 0; } virtual int IsMoving( void ) { return pev->velocity != g_vecZero; } virtual void OverrideReset( void ) {} virtual int DamageDecal( int bitsDamageType ); // This is ONLY used by the node graph to test movement through a door virtual void SetToggleState( int state ) {} virtual void StartSneaking( void ) {} virtual void StopSneaking( void ) {} virtual BOOL OnControls( entvars_t *pev ) { return FALSE; } virtual BOOL IsSneaking( void ) { return FALSE; } virtual BOOL IsAlive( void ) { return (pev->deadflag == DEAD_NO) && pev->health > 0; } virtual BOOL IsBSPModel( void ) { return pev->solid == SOLID_BSP || pev->movetype == MOVETYPE_PUSHSTEP; } virtual BOOL ReflectGauss( void ) { return ( IsBSPModel() && !pev->takedamage ); } virtual BOOL HasTarget( string_t targetname ) { return FStrEq(STRING(targetname), STRING(pev->targetname) ); } virtual BOOL IsInWorld( void ); virtual BOOL IsPlayer( void ) { return FALSE; } virtual BOOL IsNetClient( void ) { return FALSE; } virtual const char *TeamID( void ) { return ""; } // virtual void SetActivator( CMBaseEntity *pActivator ) {} //jlb virtual CMBaseEntity *GetNextTarget( void ); // fundamental callbacks void (CMBaseEntity ::*m_pfnThink)(void); void (CMBaseEntity ::*m_pfnTouch)( edict_t *pOther ); void (CMBaseEntity ::*m_pfnUse)( edict_t *pActivator, edict_t *pCaller, USE_TYPE useType, float value ); void (CMBaseEntity ::*m_pfnBlocked)( edict_t *pOther ); virtual void Think( void ) { if (m_pfnThink) (this->*m_pfnThink)(); }; virtual void Touch( edict_t *pOther ) { if (m_pfnTouch) (this->*m_pfnTouch)( pOther ); }; virtual void Use( edict_t *pActivator, edict_t *pCaller, USE_TYPE useType, float value ) { if (m_pfnUse) (this->*m_pfnUse)( pActivator, pCaller, useType, value ); } virtual void Blocked( edict_t *pOther ) { if (m_pfnBlocked) (this->*m_pfnBlocked)( pOther ); }; void UpdateOnRemove( void ); // common member functions void EXPORT SUB_Remove( void ); void EXPORT SUB_DoNothing( void ); void EXPORT SUB_StartFadeOut ( void ); void EXPORT SUB_FadeOut ( void ); void EXPORT SUB_CallUseToggle( void ) { this->Use( this->edict(), this->edict(), USE_TOGGLE, 0 ); } int ShouldToggle( USE_TYPE useType, BOOL currentState ); void FireBullets( ULONG cShots, Vector vecSrc, Vector vecDirShooting, Vector vecSpread, float flDistance, int iBulletType, int iTracerFreq = 4, int iDamage = 0, entvars_t *pevAttacker = NULL ); Vector FireBulletsPlayer( ULONG cShots, Vector vecSrc, Vector vecDirShooting, Vector vecSpread, float flDistance, int iBulletType, int iTracerFreq = 4, int iDamage = 0, entvars_t *pevAttacker = NULL, int shared_rand = 0 ); virtual CMBaseEntity *Respawn( void ) { return NULL; } void SUB_UseTargets( edict_t *pActivator, USE_TYPE useType, float value ); // Do the bounding boxes of these two intersect? int Intersects( CMBaseEntity *pOther ); void MakeDormant( void ); int IsDormant( void ); BOOL IsLockedByMaster( void ) { return FALSE; } static CMBaseEntity *Instance( edict_t *pent ) { if ( !pent ) pent = ENT(0); if ( pent->v.euser4 == NULL ) return (CMBaseEntity *)NULL; CMBaseEntity *pEnt = GetClassPtr((CMBaseEntity *)VARS(pent)); return pEnt; } static CMBaseEntity *Instance( entvars_t *pev ) { return Instance( ENT( pev ) ); } static CMBaseEntity *Instance( int eoffset) { return Instance( ENT( eoffset) ); } /*jlb CMBaseMonster *GetMonsterPointer( entvars_t *pevMonster ) { CMBaseEntity *pEntity = Instance( pevMonster ); if ( pEntity ) return pEntity->MyMonsterPointer(); return NULL; } CMBaseMonster *GetMonsterPointer( edict_t *pentMonster ) { CMBaseEntity *pEntity = Instance( pentMonster ); if ( pEntity ) return pEntity->MyMonsterPointer(); return NULL; } jlb*/ // virtual functions used by a few classes // used by monsters that are created by the MonsterMaker virtual void UpdateOwner( void ) { return; }; // //jlb static CMBaseEntity *Create( char *szName, const Vector &vecOrigin, const Vector &vecAngles, edict_t *pentOwner = NULL ); virtual BOOL FBecomeProne( void ) {return FALSE;}; edict_t *edict() { return ENT( pev ); }; EOFFSET eoffset( ) { return OFFSET( pev ); }; int entindex( ) { return ENTINDEX( edict() ); }; virtual Vector Center( ) { return (pev->absmax + pev->absmin) * 0.5; }; // center point of entity virtual Vector EyePosition( ) { return pev->origin + pev->view_ofs; }; // position of eyes virtual Vector EarPosition( ) { return pev->origin + pev->view_ofs; }; // position of ears virtual Vector BodyTarget( const Vector &posSrc ) { return Center( ); }; // position to shoot at virtual int Illumination( ) { return GETENTITYILLUM( ENT( pev ) ); }; // virtual BOOL FVisible ( edict_t *pEntity ); // virtual BOOL FVisible ( const Vector &vecOrigin ); //We use this variables to store each ammo count. int ammo_9mm; int ammo_357; int ammo_bolts; int ammo_buckshot; int ammo_rockets; int ammo_uranium; int ammo_hornets; int ammo_argrens; //Special stuff for grenades and satchels. float m_flStartThrow; float m_flReleaseThrow; int m_chargeReady; int m_fInAttack; enum EGON_FIRESTATE { FIRE_OFF, FIRE_CHARGE }; int m_fireState; }; // Ugly technique to override base member functions // Normally it's illegal to cast a pointer to a member function of a derived class to a pointer to a // member function of a base class. static_cast is a sleezy way around that problem. #define SetThink( a ) m_pfnThink = static_cast (a) #define SetTouch( a ) m_pfnTouch = static_cast (a) #define SetUse( a ) m_pfnUse = static_cast (a) #define SetBlocked( a ) m_pfnBlocked = static_cast (a) class CMPointEntity : public CMBaseEntity { public: void Spawn( void ); virtual int ObjectCaps( void ) { return CMBaseEntity :: ObjectCaps() & ~FCAP_ACROSS_TRANSITION; } private: }; typedef struct locksounds // sounds that doors and buttons make when locked/unlocked { string_t sLockedSound; // sound a door makes when it's locked string_t sLockedSentence; // sentence group played when door is locked string_t sUnlockedSound; // sound a door makes when it's unlocked string_t sUnlockedSentence; // sentence group played when door is unlocked int iLockedSentence; // which sentence in sentence group to play next int iUnlockedSentence; // which sentence in sentence group to play next float flwaitSound; // time delay between playing consecutive 'locked/unlocked' sounds float flwaitSentence; // time delay between playing consecutive sentences BYTE bEOFLocked; // true if hit end of list of locked sentences BYTE bEOFUnlocked; // true if hit end of list of unlocked sentences } locksound_t; void PlayLockSounds(entvars_t *pev, locksound_t *pls, int flocked, int fbutton); // // generic Delay entity. // class CMBaseDelay : public CMBaseEntity { public: float m_flDelay; int m_iszKillTarget; virtual void KeyValue( KeyValueData* pkvd); // common member functions void SUB_UseTargets( edict_t *pActivator, USE_TYPE useType, float value ); void EXPORT DelayThink( void ); }; class CMBaseAnimating : public CMBaseDelay { public: // Basic Monster Animation functions float StudioFrameAdvance( float flInterval = 0.0 ); // accumulate animation frame time from last time called until now int GetSequenceFlags( void ); int LookupActivity ( int activity ); int LookupActivityHeaviest ( int activity ); int LookupSequence ( const char *label ); void ResetSequenceInfo ( ); void DispatchAnimEvents ( float flFutureInterval = 0.1 ); // Handle events that have happend since last time called up until X seconds into the future virtual void HandleAnimEvent( MonsterEvent_t *pEvent ) { return; }; float SetBoneController ( int iController, float flValue ); void InitBoneControllers ( void ); float SetBlending ( int iBlender, float flValue ); void GetBonePosition ( int iBone, Vector &origin, Vector &angles ); void GetAutomovement( Vector &origin, Vector &angles, float flInterval = 0.1 ); int FindTransition( int iEndingSequence, int iGoalSequence, int *piDir ); void GetAttachment ( int iAttachment, Vector &origin, Vector &angles ); void SetBodygroup( int iGroup, int iValue ); int GetBodygroup( int iGroup ); int ExtractBbox( int sequence, float *mins, float *maxs ); void SetSequenceBox( void ); // animation needs float m_flFrameRate; // computed FPS for current sequence float m_flGroundSpeed; // computed linear movement rate for current sequence float m_flLastEventCheck; // last time the event list was checked BOOL m_fSequenceFinished;// flag set when StudioAdvanceFrame moves across a frame boundry BOOL m_fSequenceLoops; // true if the sequence loops }; // // generic Toggle entity. // #define SF_ITEM_USE_ONLY 256 // ITEM_USE_ONLY = BUTTON_USE_ONLY = DOOR_USE_ONLY!!! class CMBaseToggle : public CMBaseAnimating { public: void KeyValue( KeyValueData *pkvd ); TOGGLE_STATE m_toggle_state; float m_flActivateFinished;//like attack_finished, but for doors float m_flMoveDistance;// how far a door should slide or rotate float m_flWait; float m_flLip; float m_flTWidth;// for plats float m_flTLength;// for plats Vector m_vecPosition1; Vector m_vecPosition2; Vector m_vecAngle1; Vector m_vecAngle2; int m_cTriggersLeft; // trigger_counter only, # of activations remaining float m_flHeight; //jlb EHANDLE m_hActivator; void (CMBaseToggle::*m_pfnCallWhenMoveDone)(void); Vector m_vecFinalDest; Vector m_vecFinalAngle; int m_bitsDamageInflict; // DMG_ damage type that the door or tigger does virtual int GetToggleState( void ) { return m_toggle_state; } virtual float GetDelay( void ) { return m_flWait; } // common member functions void LinearMove( Vector vecDest, float flSpeed ); void EXPORT LinearMoveDone( void ); void AngularMove( Vector vecDestAngle, float flSpeed ); void EXPORT AngularMoveDone( void ); BOOL IsLockedByMaster( void ); static float AxisValue( int flags, const Vector &angles ); static void AxisDir( entvars_t *pev ); static float AxisDelta( int flags, const Vector &angle1, const Vector &angle2 ); string_t m_sMaster; // If this button has a master switch, this is the targetname. // A master switch must be of the multisource type. If all // of the switches in the multisource have been triggered, then // the button will be allowed to operate. Otherwise, it will be // deactivated. }; #define SetMoveDone( a ) m_pfnCallWhenMoveDone = static_cast (a) // people gib if their health is <= this at the time of death #define GIB_HEALTH_VALUE -30 #define ROUTE_SIZE 8 // how many waypoints a monster can store at one time #define MAX_OLD_ENEMIES 4 // how many old enemies to remember #define bits_CAP_DUCK ( 1 << 0 )// crouch #define bits_CAP_JUMP ( 1 << 1 )// jump/leap #define bits_CAP_STRAFE ( 1 << 2 )// strafe ( walk/run sideways) //??? #define bits_CAP_SQUAD ( 1 << 3 )// can form squads #define bits_CAP_SWIM ( 1 << 4 )// proficiently navigate in water #define bits_CAP_CLIMB ( 1 << 5 )// climb ladders/ropes #define bits_CAP_USE ( 1 << 6 )// open doors/push buttons/pull levers #define bits_CAP_HEAR ( 1 << 7 )// can hear forced sounds #define bits_CAP_AUTO_DOORS ( 1 << 8 )// can trigger auto doors #define bits_CAP_OPEN_DOORS ( 1 << 9 )// can open manual doors #define bits_CAP_TURN_HEAD ( 1 << 10)// can turn head, always bone controller 0 #define bits_CAP_RANGE_ATTACK1 ( 1 << 11)// can do a range attack 1 #define bits_CAP_RANGE_ATTACK2 ( 1 << 12)// can do a range attack 2 #define bits_CAP_MELEE_ATTACK1 ( 1 << 13)// can do a melee attack 1 #define bits_CAP_MELEE_ATTACK2 ( 1 << 14)// can do a melee attack 2 #define bits_CAP_FLY ( 1 << 15)// can fly, move all around #define bits_CAP_DOORS_GROUP (bits_CAP_USE | bits_CAP_AUTO_DOORS | bits_CAP_OPEN_DOORS) // used by suit voice to indicate damage sustained and repaired type to player // instant damage #define DMG_GENERIC 0 // generic damage was done #define DMG_CRUSH (1 << 0) // crushed by falling or moving object #define DMG_BULLET (1 << 1) // shot #define DMG_SLASH (1 << 2) // cut, clawed, stabbed #define DMG_BURN (1 << 3) // heat burned #define DMG_FREEZE (1 << 4) // frozen #define DMG_FALL (1 << 5) // fell too far #define DMG_BLAST (1 << 6) // explosive blast damage #define DMG_CLUB (1 << 7) // crowbar, punch, headbutt #define DMG_SHOCK (1 << 8) // electric shock #define DMG_SONIC (1 << 9) // sound pulse shockwave #define DMG_ENERGYBEAM (1 << 10) // laser or other high energy beam #define DMG_NEVERGIB (1 << 12) // with this bit OR'd in, no damage type will be able to gib victims upon death #define DMG_ALWAYSGIB (1 << 13) // with this bit OR'd in, any damage type can be made to gib victims upon death. #define DMG_DROWN (1 << 14) // Drowning // time-based damage #define DMG_TIMEBASED (~(0x3fff)) // mask for time-based damage #define DMG_PARALYZE (1 << 15) // slows affected creature down #define DMG_NERVEGAS (1 << 16) // nerve toxins, very bad #define DMG_POISON (1 << 17) // blood poisioning #define DMG_RADIATION (1 << 18) // radiation exposure #define DMG_DROWNRECOVER (1 << 19) // drowning recovery #define DMG_ACID (1 << 20) // toxic chemicals or acid burns #define DMG_SLOWBURN (1 << 21) // in an oven #define DMG_SLOWFREEZE (1 << 22) // in a subzero freezer #define DMG_MORTAR (1 << 23) // Hit by air raid (done to distinguish grenade from mortar) // these are the damage types that are allowed to gib corpses #define DMG_GIB_CORPSE ( DMG_CRUSH | DMG_FALL | DMG_BLAST | DMG_SONIC | DMG_CLUB ) // these are the damage types that have client hud art #define DMG_SHOWNHUD (DMG_POISON | DMG_ACID | DMG_FREEZE | DMG_SLOWFREEZE | DMG_DROWN | DMG_BURN | DMG_SLOWBURN | DMG_NERVEGAS | DMG_RADIATION | DMG_SHOCK) // NOTE: tweak these values based on gameplay feedback: #define PARALYZE_DURATION 2 // number of 2 second intervals to take damage #define PARALYZE_DAMAGE 1.0 // damage to take each 2 second interval #define NERVEGAS_DURATION 2 #define NERVEGAS_DAMAGE 5.0 #define POISON_DURATION 5 #define POISON_DAMAGE 2.0 #define RADIATION_DURATION 2 #define RADIATION_DAMAGE 1.0 #define ACID_DURATION 2 #define ACID_DAMAGE 5.0 #define SLOWBURN_DURATION 2 #define SLOWBURN_DAMAGE 1.0 #define SLOWFREEZE_DURATION 2 #define SLOWFREEZE_DAMAGE 1.0 #define itbd_Paralyze 0 #define itbd_NerveGas 1 #define itbd_Poison 2 #define itbd_Radiation 3 #define itbd_DrownRecover 4 #define itbd_Acid 5 #define itbd_SlowBurn 6 #define itbd_SlowFreeze 7 #define CDMG_TIMEBASED 8 // when calling KILLED(), a value that governs gib behavior is expected to be // one of these three values #define GIB_NORMAL 0// gib if entity was overkilled #define GIB_NEVER 1// never gib, no matter how much death damage is done ( freezing, etc ) #define GIB_ALWAYS 2// always gib ( Houndeye Shock, Barnacle Bite ) class CMBaseMonster; extern int GetMonsterIndex(void); // // Converts a entvars_t * to a class pointer // template T * GetClassPtr( T *a ) { entvars_t *pev = (entvars_t *)a; if (pev == NULL) return NULL; // get the private data a = (T *)pev->euser4; return a; } // // Converts a entvars_t * to a class pointer // It will allocate the class and entity // template T * CreateClassPtr( T *a ) { entvars_t *pev = (entvars_t *)a; if (pev != NULL) return NULL; // don't allocate if pointer already provided // allocate entity... edict_t *temp_edict; int edict_index; int monster_index; if ((monster_index = GetMonsterIndex()) == -1) { (*g_engfuncs.pfnServerPrint)("[MONSTER] ERROR: No FREE Monster edicts in CreateClassPtr!\n" ); return NULL; } // allocate private data a = new T; if ((temp_edict = a->CreateEntity("func_wall")) == NULL) { (*g_engfuncs.pfnServerPrint)("[MONSTER] ERROR: NULL Ent in CreateClassPtr!\n" ); delete a; return NULL; } edict_index = (*g_engfuncs.pfnIndexOfEdict)(temp_edict); // store the class pointer to the edict pev... pev = VARS(temp_edict); a->pev = pev; // store the class pointer in the array here!!! monsters[monster_index].monster_index = edict_index; monsters[monster_index].monster_pent = temp_edict; monsters[monster_index].respawn_index = -1; monsters[monster_index].pMonster = (CMBaseMonster *)a; // get the private data a = (T *)pev->euser4; return a; }