diff --git a/backup.lst b/backup.lst index 38968c19..cad728e9 100644 --- a/backup.lst +++ b/backup.lst @@ -17,6 +17,7 @@ client\ client\hud\ client\global\ common\ +dlls\ game_shared\ game_launch\ gameui\ @@ -35,7 +36,6 @@ server\game\ server\global\ server\monsters\ launch\ -spirit\ snd_dx\ vid_gl\ xtools\ diff --git a/bshift/apache.cpp b/bshift/apache.cpp index 44a15ff3..d25f7425 100644 --- a/bshift/apache.cpp +++ b/bshift/apache.cpp @@ -840,7 +840,7 @@ BOOL CApache :: FireGun( ) if (!m_pBeam) { m_pBeam = CBeam::BeamCreate( "sprites/lgtning.spr", 80 ); - m_pBeam->PointEntInit( pev->origin, edict( ) ); + m_pBeam->PointEntInit( pev->origin, entindex( ) ); m_pBeam->SetEndAttachment( 1 ); m_pBeam->SetColor( 255, 180, 96 ); m_pBeam->SetBrightness( 192 ); diff --git a/bshift/cbase.cpp b/bshift/cbase.cpp index f2ea464d..20672cb3 100644 --- a/bshift/cbase.cpp +++ b/bshift/cbase.cpp @@ -27,6 +27,10 @@ extern Vector VecBModelOrigin( entvars_t* pevBModel ); extern DLL_GLOBAL Vector g_vecAttackDir; extern DLL_GLOBAL int g_iSkillLevel; +extern void PM_Move ( struct playermove_s *ppmove, int server ); +extern void PM_Init ( struct playermove_s *ppmove ); +extern char PM_FindTextureType( const char *name ); + static DLL_FUNCTIONS gFunctionTable = { GameDLLInit, //pfnGameInit @@ -61,17 +65,17 @@ static DLL_FUNCTIONS gFunctionTable = PlayerPostThink, //pfnPlayerPostThink StartFrame, //pfnStartFrame - DispatchCreate, //pfnCreate + ParmsNewLevel, //pfnParmsNewLevel ParmsChangeLevel, //pfnParmsChangeLevel GetGameDescription, //pfnGetGameDescription Returns string describing current .dll game. - DispatchFrame, // pfnPhysicsEntity + PlayerCustomization, // pfnPlayerCustomization SpectatorConnect, //pfnSpectatorConnect Called when spectator joins server SpectatorDisconnect, //pfnSpectatorDisconnect Called when spectator leaves the server SpectatorThink, //pfnSpectatorThink Called when spectator sends a command packet (usercmd_t) - ServerClassifyEdict, // pfnClassifyEdict + Sys_Error, // pfnSys_Error PM_Move, // pfnPM_Move PM_Init, // pfnPM_Init Server version of player movement initialization @@ -88,9 +92,11 @@ static DLL_FUNCTIONS gFunctionTable = CmdStart, // pfnCmdStart CmdEnd, // pfnCmdEnd - OnFreeEntPrivateData, // pfnOnFreeEntPrivateData + ConnectionlessPacket, // pfnConnectionlessPacket GetHullBounds, // pfnGetHullBounds - ShouldCollide, // pfnShouldCollide + CreateInstancedBaselines, // pfnCreateInstancedBaselines + InconsistentFile, // pfnInconsistentFile + AllowLagCompensation, // pfnAllowLagCompensation }; static void SetObjectCollisionBox( entvars_t *pev ); @@ -100,7 +106,7 @@ static void SetObjectCollisionBox( entvars_t *pev ); //======================================================================= int GetEntityAPI( DLL_FUNCTIONS *pFunctionTable, int interfaceVersion ) { - if ( !pFunctionTable || interfaceVersion != SV_INTERFACE_VERSION ) + if ( !pFunctionTable || interfaceVersion != INTERFACE_VERSION ) { return FALSE; } @@ -595,7 +601,6 @@ CBaseEntity *CBaseEntity::GetNextTarget( void ) TYPEDESCRIPTION CBaseEntity::m_SaveData[] = { DEFINE_FIELD( CBaseEntity, m_pGoalEnt, FIELD_CLASSPTR ), - DEFINE_FIELD( CBaseEntity, m_iClassType, FIELD_INTEGER ), DEFINE_FIELD( CBaseEntity, m_pfnThink, FIELD_FUNCTION ), // UNDONE: Build table of these!!! DEFINE_FIELD( CBaseEntity, m_pfnTouch, FIELD_FUNCTION ), DEFINE_FIELD( CBaseEntity, m_pfnUse, FIELD_FUNCTION ), @@ -619,9 +624,6 @@ int CBaseEntity::Restore( CRestore &restore ) if ( status ) status = restore.ReadFields( "BASE", this, m_SaveData, ARRAYSIZE(m_SaveData) ); - // restore edict class here - SetObjectClass( m_iClassType ); - if ( pev->modelindex != 0 && !FStringNull(pev->model) ) { Vector mins, maxs; diff --git a/bshift/cbase.h b/bshift/cbase.h index 1fe562d7..4e10ef60 100644 --- a/bshift/cbase.h +++ b/bshift/cbase.h @@ -26,8 +26,6 @@ CBaseEntity CBaseGroup */ -#include "entity_state.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) @@ -79,7 +77,6 @@ extern void SaveReadFields( SAVERESTOREDATA *pSaveData, const char *pname, void extern void SaveGlobalState( SAVERESTOREDATA *pSaveData ); extern void RestoreGlobalState( SAVERESTOREDATA *pSaveData ); extern void ResetGlobalState( void ); -extern int ServerClassifyEdict( edict_t *pentToClassify ); extern void OnFreeEntPrivateData( edict_s *pEdict ); extern int ShouldCollide( edict_t *pentTouched, edict_t *pentOther ); @@ -150,13 +147,6 @@ public: // path corners CBaseEntity *m_pGoalEnt;// path corner we are heading towards CBaseEntity *m_pLink;// used for temporary link-list operations. - - int m_iClassType; // edict classtype - - virtual void SetObjectClass( int iClassType = ED_SPAWNED ) - { - m_iClassType = iClassType; - } // initialization functions virtual void Spawn( void ) { return; } diff --git a/bshift/client.cpp b/bshift/client.cpp index 2adffab5..932ce860 100644 --- a/bshift/client.cpp +++ b/bshift/client.cpp @@ -661,6 +661,15 @@ void PlayerPostThink( edict_t *pEntity ) pPlayer->PostThink( ); } +void PlayerCustomization( edict_t *pEntity, void *pUnused ) +{ + // completely ignored in Xash3D +} + +void ParmsNewLevel( void ) +{ +} + void ParmsChangeLevel( void ) { // retrieve the pointer to the save data @@ -840,6 +849,19 @@ const char *GetGameDescription() return "Half-Life"; } +/* +================ +Sys_Error + +Engine is going to shut down, allows setting a breakpoint in game .dll to catch that occasion +================ +*/ +void Sys_Error( const char *error_string ) +{ + // Default case, do nothing. MOD AUTHORS: Add code ( e.g., _asm { int 3 }; here to cause a breakpoint for debugging your game .dlls +} + + /* ================ SpectatorConnect @@ -888,104 +910,6 @@ void SpectatorThink( edict_t *pEntity ) pPlayer->SpectatorThink( ); } -// FIXME: implement VirtualClass GetClass instead -int AutoClassify( edict_t *pentToClassify ) -{ - CBaseEntity *pClass; - - pClass = CBaseEntity::Instance( pentToClassify ); - if( !pClass ) return ED_SPAWNED; - - const char *classname = STRING( pClass->pev->classname ); - - if ( !strnicmp( "worldspawn", classname, 10 )) - { - return ED_WORLDSPAWN; - } - - if ( !strnicmp( "bodyque", classname, 7 )) - { - return ED_NORMAL; - } - - // first pass: determine type by explicit parms - if ( pClass->pev->solid == SOLID_TRIGGER ) - { - if ( FClassnameIs( pClass->pev, "ambient_generic" ) || FClassnameIs( pClass->pev, "target_speaker" )) // FIXME - { - pClass->pev->flags |= FL_PHS_FILTER; - return ED_AMBIENT; - } - else if( pClass->pev->movetype == MOVETYPE_TOSS ) - return ED_NORMAL; // it's item or weapon - if ( FBitSet( pClass->pev->effects, EF_NODRAW )) - return ED_TRIGGER; // never sending to client - return ED_NORMAL; // e.g. friction modifier - } - else if ( pClass->pev->movetype == MOVETYPE_PHYSIC ) - return ED_RIGIDBODY; - else if ( pClass->pev->solid == SOLID_BSP ) - { - if ( pClass->pev->flags & FL_CONVEYOR ) - return ED_MOVER; - else if ( pClass->pev->flags & FL_WORLDBRUSH ) - return ED_BSPBRUSH; - else if ( pClass->pev->movetype == MOVETYPE_PUSH ) - return ED_MOVER; - else if ( pClass->pev->movetype == MOVETYPE_NONE ) - return ED_BSPBRUSH; - } - else if ( pClass->pev->flags & FL_MONSTER ) - return ED_MONSTER; - else if ( pClass->pev->flags & FL_CLIENT ) - return ED_CLIENT; - if ( pClass->pev->flags & FL_CONVEYOR ) - return ED_MOVER; - else if ( !pClass->pev->modelindex && !pClass->pev->aiment ) - { - if ( pClass->pev->noise || pClass->pev->noise1 || pClass->pev->noise2 || pClass->pev->noise3 ) - { - pClass->pev->flags |= FL_PHS_FILTER; - return ED_AMBIENT; - } - return ED_STATIC; // never sending to client - } - - // mark as normal - if ( pClass->pev->modelindex || pClass->pev->noise ) - return ED_NORMAL; - - // fail to classify :-( - return ED_SPAWNED; -} - -int ServerClassifyEdict( edict_t *pentToClassify ) -{ - // NOTE: we can't use FNullEnt here to handle 'worldspawn' properly - // but must skip clients because they not spawned at this point - if( !pentToClassify || pentToClassify->free || !pentToClassify->pvPrivateData ) - return ED_SPAWNED; - - CBaseEntity *pClass; - - pClass = CBaseEntity::Instance( pentToClassify ); - if( !pClass ) return ED_SPAWNED; - - // user-defined - if( pClass->m_iClassType != ED_SPAWNED ) - return pClass->m_iClassType; - - int m_iNewClass = AutoClassify( pentToClassify ); - - if( m_iNewClass != ED_SPAWNED ) - { - // tell server.dll about new class - pClass->SetObjectClass( m_iNewClass ); - } - - return m_iNewClass; -} - //////////////////////////////////////////////////////// // PAS and PVS routines for client messaging // @@ -1004,12 +928,12 @@ From the eye position, we set up the PAS and PVS to use for filtering network me NOTE: Do not cache the values of pas and pvs, as they depend on reusable memory in the engine, they are only good for this one frame ================ */ -void SetupVisibility( edict_t *pViewEntity, edict_t *pClient, byte **pvs, byte **pas, int portal ) +void SetupVisibility( edict_t *pViewEntity, edict_t *pClient, byte **pvs, byte **pas, int mergepvs ) { Vector org = g_vecZero; edict_t *pView = pClient; - if( portal ) + if( mergepvs ) { // Entity's added from portal camera PVS if( FNullEnt( pViewEntity )) return; // broken portal ? @@ -1019,14 +943,14 @@ void SetupVisibility( edict_t *pViewEntity, edict_t *pClient, byte **pvs, byte * if( !pCamera ) return; // determine visible point - if( pCamera->m_iClassType == ED_PORTAL ) + if( FClassnameIs( pCamera->pev, "info_portal" )) { // don't build visibility for mirrors if( pCamera->pev->origin == pCamera->pev->oldorigin ) return; else org = pCamera->pev->oldorigin; } - else if( pCamera->m_iClassType == ED_SKYPORTAL ) + else if( FClassnameIs( pCamera->pev, "env_sky" )) { org = pCamera->pev->origin; } @@ -1051,10 +975,13 @@ void SetupVisibility( edict_t *pViewEntity, edict_t *pClient, byte **pvs, byte * org = pView->v.origin + pView->v.view_ofs; } - *pvs = ENGINE_SET_PVS( (float *)&org, portal ); - *pas = ENGINE_SET_PAS( (float *)&org, portal ); + *pvs = ENGINE_SET_PVS( (float *)&org, mergepvs ); + *pas = ENGINE_SET_PAS( (float *)&org, mergepvs ); } +#include "entity_state.h" +#include "entity_types.h" + /* AddToFullPack @@ -1082,19 +1009,27 @@ int AddToFullPack( entity_state_t *state, edict_t *pView, edict_t *pHost, edict_ BOOL bIsPortalPass = FALSE; - if( pViewEntity && pViewEntity->m_iClassType == ED_PORTAL ) + if( pViewEntity && FClassnameIs( pViewEntity->pev, "info_portal" )) bIsPortalPass = TRUE; // view from portal camera pEntity = (CBaseEntity *)CBaseEntity::Instance( pEdict ); - if( !pEntity ) return 0; + if ( !pEntity ) return 0; // NOTE: always add himslef to list - if( !bIsPortalPass && ( pHost == pEdict )) + if ( !bIsPortalPass && ( pHost == pEdict )) goto addEntity; + // don't send if flagged for NODRAW and it's not the host getting the message + if (( pEdict->v.effects == EF_NODRAW ) && ( pEdict != pHost )) + return 0; + + // Ignore ents without valid / visible models + if ( !pEdict->v.modelindex || !STRING( pEdict->v.model )) + return 0; + // completely ignore dormant entity - if( pEdict->v.flags & FL_DORMANT ) + if ( pEdict->v.flags & FL_DORMANT ) return 0; // don't send spectators to other players @@ -1103,32 +1038,8 @@ int AddToFullPack( entity_state_t *state, edict_t *pView, edict_t *pHost, edict_ return 0; } - // FIXME: temporary hack - if( pEdict->v.flags & FL_CUSTOMENTITY ) - { - pEntity->m_iClassType = ED_BEAM; - state->ed_type = ED_BEAM; - } - - // quick reject by type - switch( pEntity->m_iClassType ) - { - case ED_SKYPORTAL: - goto addEntity; // no additional check requires - case ED_BEAM: - case ED_MOVER: - case ED_NORMAL: - case ED_PORTAL: - case ED_CLIENT: - case ED_MONSTER: - case ED_AMBIENT: - case ED_BSPBRUSH: - case ED_RIGIDBODY: break; - default: return 0; // skipped - } - // check for ambients distance - if( pEntity->m_iClassType == ED_AMBIENT ) + if(FClassnameIs( pViewEntity->pev, "ambient_generic" )) { Vector entorigin; @@ -1142,22 +1053,26 @@ int AddToFullPack( entity_state_t *state, edict_t *pView, edict_t *pHost, edict_ } else delta = g_vecZero; - // check visibility - if ( !ENGINE_CHECK_PVS( pEdict, pSet )) + // Ignore if not the host and not touching a PVS/PAS leaf + // If pSet is NULL, then the test will always succeed and the entity will be added to the update + if ( pEdict != pHost ) { - float m_flRadius = 1024; // g-cont: tune distance by taste :) - - if ( pEntity->pev->flags & FL_PHS_FILTER ) + if ( !ENGINE_CHECK_VISIBILITY( pEdict, pSet )) { - if( pEntity->pev->armorvalue > 0 ) - m_flRadius = pEntity->pev->armorvalue; + float m_flRadius = 1024; // g-cont: tune distance by taste :) - if( delta.Length() > m_flRadius ) + if ( pEntity->pev->flags & FL_PHS_FILTER ) + { + if( pEntity->pev->armorvalue > 0 ) + m_flRadius = pEntity->pev->armorvalue; + + if( delta.Length() > m_flRadius ) + return 0; + } + else + { return 0; - } - else //if( pEntity->m_iClassType != ED_BEAM ) - { - return 0; + } } } @@ -1171,13 +1086,6 @@ int AddToFullPack( entity_state_t *state, edict_t *pView, edict_t *pHost, edict_ return 0; } - if( !pEntity->pev->modelindex || FStringNull( pEntity->pev->model )) - { - // can't ignore ents withouts models, because portals and mirrors can't working otherwise - // and null.mdl it's no more needs to be set: ED_CLASS rejection is working fine - // so we wan't reject this entities here. - } - if( pHost->v.groupinfo ) { UTIL_SetGroupTrace( pHost->v.groupinfo, GROUP_OP_AND ); @@ -1200,10 +1108,11 @@ int AddToFullPack( entity_state_t *state, edict_t *pView, edict_t *pHost, edict_ } addEntity: - - // setup some special edict flags (engine will clearing them after the current frame) - if( state->modelindex != pEntity->pev->modelindex || ( pEntity->pev->effects & EF_NOINTERP )) - state->ed_flags |= ESF_NODELTA; + if( pEdict->v.flags & FL_CLIENT ) + state->entityType = ET_PLAYER; + else if( pEdict->v.flags & FL_CUSTOMENTITY ) + state->entityType = ET_BEAM; + else state->entityType = ET_NORMAL; // always keep an actual state->number = pEdict->serialnumber; @@ -1258,7 +1167,7 @@ addEntity: for( i = 0; i < 4; i++ ) state->controller[i] = pEntity->pev->controller[i]; - if( state->ed_type == ED_CLIENT ) + if( pEdict->v.flags & FL_CLIENT ) { if( pEntity->pev->aiment ) state->aiment = ENTINDEX( pEntity->pev->aiment ); @@ -1276,17 +1185,13 @@ addEntity: state->weaponmodel = MODEL_INDEX( STRING( pEntity->pev->weaponmodel )); else state->weaponmodel = 0; } - else if( state->ed_type == ED_AMBIENT ) + else if( pEdict->v.movetype == MOVETYPE_PUSH ) { - // add here specified fields e.g for trigger_teleport wind sound etc - } - else if( state->ed_type == ED_MOVER || state->ed_type == ED_BSPBRUSH || state->ed_type == ED_PORTAL ) - { - state->body = DirToBits( pEntity->pev->movedir ); + state->body = DirToBits( pEntity->pev->movedir ); // legacy state->velocity = pEntity->pev->velocity; // this is conveyor - send speed to render for right texture scrolling - state->framerate = pEntity->pev->speed; + state->framerate = pEntity->pev->speed; // legacy } return 1; } @@ -1300,14 +1205,11 @@ Creates baselines used for network encoding, especially for player data since pl */ void CreateBaseline( entity_state_t *baseline, edict_t *entity, int playermodelindex ) { - // FIXME: temporary hack - if( entity->v.flags & FL_CUSTOMENTITY ) - { - baseline->ed_type = ED_BEAM; - } - - // always set nodelta's for baseline - baseline->ed_flags |= ESF_NODELTA; + if( entity->v.flags & FL_CLIENT ) + baseline->entityType = ET_PLAYER; + else if( entity->v.flags & FL_CUSTOMENTITY ) + baseline->entityType = ET_BEAM; + else baseline->entityType = ET_NORMAL; baseline->origin = entity->v.origin; baseline->angles = entity->v.angles; @@ -1325,7 +1227,7 @@ void CreateBaseline( entity_state_t *baseline, edict_t *entity, int playermodeli baseline->mins = entity->v.mins; baseline->maxs = entity->v.maxs; - if( baseline->ed_type == ED_CLIENT ) + if( baseline->entityType == ET_PLAYER ) { baseline->colormap = entity->serialnumber; baseline->modelindex = playermodelindex; // "model" field from userinfo @@ -1695,6 +1597,29 @@ void CmdEnd ( const edict_t *player ) } } +/* +================================ +ConnectionlessPacket + + Return 1 if the packet is valid. Set response_buffer_size if you want to send a response packet. Incoming, it holds the max + size of the response_buffer, so you must zero it out if you choose not to respond. +================================ +*/ +int ConnectionlessPacket( const struct netadr_s *net_from, const char *args, char *response_buffer, int *response_buffer_size ) +{ + // Parse stuff from args + int max_buffer_size = *response_buffer_size; + + // Zero it out since we aren't going to respond. + // If we wanted to response, we'd write data into response_buffer + *response_buffer_size = 0; + + // Since we don't listen for anything here, just respond that it's a bogus message + // If we didn't reject the message, we'd return 1 for success instead. + return 0; +} + + /* ================================ GetHullBounds @@ -1729,6 +1654,65 @@ int GetHullBounds( int hullnumber, float *mins, float *maxs ) return iret; } +/* +================================ +CreateInstancedBaselines + +Create pseudo-baselines for items that aren't placed in the map at spawn time, but which are likely +to be created during play ( e.g., grenades, ammo packs, projectiles, corpses, etc. ) +================================ +*/ +void CreateInstancedBaselines ( void ) +{ + int iret = 0; + entity_state_t state; + + memset( &state, 0, sizeof( state ) ); + + // Create any additional baselines here for things like grendates, etc. + // iret = ENGINE_INSTANCE_BASELINE( pc->pev->classname, &state ); + + // Destroy objects. + //UTIL_Remove( pc ); +} + +/* +================================ +InconsistentFile + +One of the ENGINE_FORCE_UNMODIFIED files failed the consistency check for the specified player + Return 0 to allow the client to continue, 1 to force immediate disconnection ( with an optional disconnect message of up to 256 characters ) +================================ +*/ +int InconsistentFile( const edict_t *player, const char *filename, char *disconnect_message ) +{ + // Server doesn't care? + if ( CVAR_GET_FLOAT( "mp_consistency" ) != 1 ) + return 0; + + // Default behavior is to kick the player + sprintf( disconnect_message, "Server is enforcing file consistency for %s\n", filename ); + + // Kick now with specified disconnect message. + return 1; +} + +/* +================================ +AllowLagCompensation + + The game .dll should return 1 if lag compensation should be allowed ( could also just set + the sv_unlag cvar. + Most games right now should return 0, until client-side weapon prediction code is written + and tested for them ( note you can predict weapons, but not do lag compensation, too, + if you want. +================================ +*/ +int AllowLagCompensation( void ) +{ + return 1; +} + /* ================================ ShouldCollide diff --git a/bshift/client.h b/bshift/client.h index 1c149663..11abc9e6 100644 --- a/bshift/client.h +++ b/bshift/client.h @@ -33,6 +33,7 @@ extern void RegisterEncoders( void ); extern void ClientPrecache( void ); extern const char *GetGameDescription( void ); +extern void PlayerCustomization( edict_t *pEntity, void *pUnused ); extern int GetWeaponData( edict_t *player, struct weapon_data_s *info ); extern void SpectatorConnect ( edict_t *pEntity ); @@ -43,12 +44,17 @@ extern void Sys_Error( const char *error_string ); extern void SetupVisibility( edict_t *pViewEntity, edict_t *pClient, byte **pvs, byte **pas, int portal ); extern void UpdateClientData( const struct edict_s *ent, int sendweapons, struct clientdata_s *cd ); -extern int AddToFullPack( entity_state_t *state, edict_t *pView, edict_t *pHost, edict_t *pEdict, int hostflags, byte *pSet ); -extern void CreateBaseline( entity_state_t *baseline, edict_t *entity, int playermodelindex ); +extern int AddToFullPack( struct entity_state_s *state, edict_t *pView, edict_t *pHost, edict_t *pEdict, int hostflags, byte *pSet ); +extern void CreateBaseline( struct entity_state_s *baseline, edict_t *entity, int playermodelindex ); extern void CmdStart( const edict_t *player, const struct usercmd_s *cmd, unsigned int random_seed ); extern void CmdEnd ( const edict_t *player ); +extern int ConnectionlessPacket( const struct netadr_s *net_from, const char *args, char *response_buffer, int *response_buffer_size ); + +extern int GetHullBounds( int hullnumber, float *mins, float *maxs ); +extern void CreateInstancedBaselines ( void ); +extern int InconsistentFile( const edict_t *player, const char *filename, char *disconnect_message ); +extern int AllowLagCompensation( void ); -extern int GetHullBounds( int hullnumber, float *mins, float *maxs ); #endif // CLIENT_H diff --git a/bshift/effects.cpp b/bshift/effects.cpp index 8a5488e4..52f15db6 100644 --- a/bshift/effects.cpp +++ b/bshift/effects.cpp @@ -230,8 +230,6 @@ CBeam *CBeam::BeamCreate( const char *pSpriteName, int width ) void CBeam::BeamInit( const char *pSpriteName, int width ) { - SetObjectClass( ED_BEAM ); - pev->flags |= FL_CUSTOMENTITY; SetColor( 255, 255, 255 ); SetBrightness( 255 ); @@ -468,7 +466,6 @@ void CLightning::Spawn( void ) if ( ServerSide() ) { - SetObjectClass( ED_BEAM ); SetThink( NULL ); if ( pev->dmg > 0 ) { @@ -962,7 +959,6 @@ void CLaser::Spawn( void ) return; } - SetObjectClass( ED_BEAM ); pev->solid = SOLID_NOT; // Remove model & collisions Precache( ); diff --git a/bshift/enginecallback.h b/bshift/enginecallback.h index f315e915..64b82018 100644 --- a/bshift/enginecallback.h +++ b/bshift/enginecallback.h @@ -70,7 +70,6 @@ extern enginefuncs_t g_engfuncs; #define RANDOM_FLOAT (*g_engfuncs.pfnRandomFloat) #define GETPLAYERAUTHID (*g_engfuncs.pfnGetPlayerAuthId) #define CLASSIFY_EDICT (*g_engfuncs.pfnClassifyEdict) -#define COM_Parse (*g_engfuncs.pfnParseToken) inline void MESSAGE_BEGIN( int msg_dest, int msg_type, const float *pOrigin = NULL, edict_t *ed = NULL ) { (*g_engfuncs.pfnMessageBegin)(msg_dest, msg_type, pOrigin, ed); @@ -92,6 +91,7 @@ inline void WRITE_FLOAT( float flValue ) #define CVAR_GET_STRING (*g_engfuncs.pfnCVarGetString) #define CVAR_SET_FLOAT (*g_engfuncs.pfnCVarSetFloat) #define CVAR_SET_STRING (*g_engfuncs.pfnCVarSetString) +#define CVAR_GET_POINTER (*g_engfuncs.pfnCVarGetPointer) #define ALERT (*g_engfuncs.pfnAlertMessage) #define ENGINE_FPRINTF (*g_engfuncs.pfnEngineFprintf) #define ALLOC_PRIVATE (*g_engfuncs.pfnPvAllocEntPrivateData) @@ -136,9 +136,9 @@ inline void *GET_PRIVATE( edict_t *pent ) #define FILE_EXISTS (*g_engfuncs.pfnFileExists) #define GET_GAME_DIR (*g_engfuncs.pfnGetGameDir) #define IS_MAP_VALID (*g_engfuncs.pfnIsMapValid) -#define SET_BONE_POSITION (*g_engfuncs.pfnSetBonePos) +#define NUMBER_OF_ENTITIES (*g_engfuncs.pfnNumberOfEntities) #define DROP_CLIENT (*g_engfuncs.pfnDropClient) -#define ENGINE_CHECK_PVS (*g_engfuncs.pfnCheckVisibility) +#define ENGINE_CHECK_VISIBILITY (*g_engfuncs.pfnCheckVisibility) #define ENGINE_SET_PVS (*g_engfuncs.pfnSetFatPVS) #define ENGINE_SET_PAS (*g_engfuncs.pfnSetFatPAS) diff --git a/bshift/extdll.h b/bshift/extdll.h index d48cbca2..f290e461 100644 --- a/bshift/extdll.h +++ b/bshift/extdll.h @@ -33,7 +33,6 @@ #pragma warning(disable : 4100) // unreferenced formal parameter #include "windows.h" -#include "basetypes.h" #define FALSE 0 #define TRUE 1 @@ -64,9 +63,9 @@ typedef unsigned long ULONG; // Vector class #include "vector.h" - // Shared header describing protocol between engine and DLLs -#include "entity_def.h" -#include "svgame_api.h" +// Shared header describing protocol between engine and DLLs +#include "edict.h" +#include "eiface.h" // Shared header between the client DLL and the game DLLs #include "cdll_dll.h" diff --git a/bshift/multiplay_gamerules.cpp b/bshift/multiplay_gamerules.cpp index d5201764..de4adec0 100644 --- a/bshift/multiplay_gamerules.cpp +++ b/bshift/multiplay_gamerules.cpp @@ -841,7 +841,7 @@ float CHalfLifeMultiplay :: FlWeaponTryRespawn( CBasePlayerItem *pWeapon ) { if ( pWeapon && pWeapon->m_iId && (pWeapon->iFlags() & ITEM_FLAG_LIMITINWORLD) ) { - if ( gpGlobals->numEntities < (gpGlobals->maxEntities - ENTITY_INTOLERANCE) ) + if ( NUMBER_OF_ENTITIES() < (gpGlobals->maxEntities - ENTITY_INTOLERANCE) ) return 0; // we're past the entity tolerance level, so delay the respawn @@ -1122,6 +1122,85 @@ void DestroyMapCycle( mapcycle_t *cycle ) cycle->next_item = NULL; } +static char com_token[ 1500 ]; + +/* +============== +COM_Parse + +Parse a token out of a string +============== +*/ +char *COM_Parse (char *data) +{ + int c; + int len; + + len = 0; + com_token[0] = 0; + + if (!data) + return NULL; + +// skip whitespace +skipwhite: + while ( (c = *data) <= ' ') + { + if (c == 0) + return NULL; // end of file; + data++; + } + +// skip // comments + if (c=='/' && data[1] == '/') + { + while (*data && *data != '\n') + data++; + goto skipwhite; + } + + +// handle quoted strings specially + if (c == '\"') + { + data++; + while (1) + { + c = *data++; + if (c=='\"' || !c) + { + com_token[len] = 0; + return data; + } + com_token[len] = c; + len++; + } + } + +// parse single characters + if (c=='{' || c=='}'|| c==')'|| c=='(' || c=='\'' || c == ',' ) + { + com_token[len] = c; + len++; + com_token[len] = 0; + return data+1; + } + +// parse a regular word + do + { + com_token[len] = c; + data++; + len++; + c = *data; + if (c=='{' || c=='}'|| c==')'|| c=='(' || c=='\'' || c == ',' ) + break; + } while (c>32); + + com_token[len] = 0; + return data; +} + /* ============== COM_TokenWaiting @@ -1158,9 +1237,8 @@ int ReloadMapCycleFile( char *filename, mapcycle_t *cycle ) char szBuffer[ MAX_RULE_BUFFER ]; char szMap[ 32 ]; int length; - char *pToken; char *aFileList = (char *)LOAD_FILE_FOR_ME( filename, &length ); - const char *pFileList = aFileList; + char *pFileList = aFileList; int hasbuffer; mapcycle_item_s *item, *newlist = NULL, *next; @@ -1172,22 +1250,20 @@ int ReloadMapCycleFile( char *filename, mapcycle_t *cycle ) hasbuffer = 0; memset( szBuffer, 0, MAX_RULE_BUFFER ); - pToken = COM_Parse( &pFileList ); - if ( !pToken ) break; - - if ( strlen( pToken ) <= 0 ) + pFileList = COM_Parse( pFileList ); + if ( strlen( com_token ) <= 0 ) break; - strcpy( szMap, pToken ); + strcpy( szMap, com_token ); // Any more tokens on this line? if ( COM_TokenWaiting( pFileList ) ) { - pToken = COM_Parse( &pFileList ); - if ( strlen( pToken ) > 0 ) + pFileList = COM_Parse( pFileList ); + if ( strlen( com_token ) > 0 ) { hasbuffer = 1; - strcpy( szBuffer, pToken ); + strcpy( szBuffer, com_token ); } } diff --git a/bshift/player.cpp b/bshift/player.cpp index b9271e35..8e1f061d 100644 --- a/bshift/player.cpp +++ b/bshift/player.cpp @@ -2961,7 +2961,6 @@ void CBasePlayer::Spawn( void ) g_pGameRules->SetDefaultPlayerTeam( this ); g_pGameRules->GetPlayerSpawnSpot( this ); - SetObjectClass( ED_CLIENT ); // critical stuff!!! SET_MODEL(ENT(pev), "models/player.mdl"); g_ulModelIndexPlayer = pev->modelindex; pev->sequence = LookupActivity( ACT_IDLE ); diff --git a/bshift/rpg.cpp b/bshift/rpg.cpp index 1ea58eba..ba2a76fb 100644 --- a/bshift/rpg.cpp +++ b/bshift/rpg.cpp @@ -201,7 +201,6 @@ CRpgRocket *CRpgRocket::CreateRpgRocket( Vector vecOrigin, Vector vecAngles, CBa { CRpgRocket *pRocket = GetClassPtr( (CRpgRocket *)NULL ); - pRocket->SetObjectClass( ED_NORMAL ); UTIL_SetOrigin( pRocket->pev, vecOrigin ); pRocket->pev->angles = vecAngles; pRocket->Spawn(); diff --git a/bshift/triggers.cpp b/bshift/triggers.cpp index 3837dcd4..42d8a6a8 100644 --- a/bshift/triggers.cpp +++ b/bshift/triggers.cpp @@ -2203,9 +2203,6 @@ IMPLEMENT_SAVERESTORE(CTriggerCamera,CBaseDelay); void CTriggerCamera::Spawn( void ) { - // force camera to send on client - SetObjectClass( ED_NORMAL ); - pev->movetype = MOVETYPE_NOCLIP; pev->solid = SOLID_NOT; // Remove model & collisions pev->renderamt = 0; // The engine won't draw this model if this is set to 0 and blending is on diff --git a/bshift/util.cpp b/bshift/util.cpp index d91a0f4a..cf001785 100644 --- a/bshift/util.cpp +++ b/bshift/util.cpp @@ -61,16 +61,6 @@ DLL_GLOBAL int DirToBits( const Vector dir ) return best; } -char *COM_ParseToken( const char **data ) -{ - char *com_token = COM_Parse( data ); - - // debug -// ALERT( at_console, "ParseToken: %s\n", com_token ); - - return com_token; -} - /* ===================== UTIL_WeaponTimeBase diff --git a/bshift/util.h b/bshift/util.h index 52fd3a1b..d8e2cab0 100644 --- a/bshift/util.h +++ b/bshift/util.h @@ -20,7 +20,6 @@ #include -#include "te_shared.h" #include "shake.h" #include "game_shared.h" #include "activity.h" @@ -47,8 +46,6 @@ inline edict_t *FIND_ENTITY_BY_TARGET(edict_t *entStart, const char *pszName) extern int DirToBits( const Vector dir ); -char *COM_ParseToken( const char **data ); - // Keeps clutter down a bit, when writing key-value pairs #define WRITEKEY_INT(pf, szKeyName, iKeyValue) \ ENGINE_FPRINTF(pf, "\"%s\" \"%d\"\n", szKeyName, iKeyValue) @@ -229,6 +226,10 @@ inline void UTIL_MakeVectorsPrivate( const Vector &vecAngles, float *p_vForward, g_engfuncs.pfnAngleVectors( vecAngles, p_vForward, p_vRight, p_vUp ); } +typedef enum { point_hull = 0, human_hull = 1, large_hull = 2, head_hull = 3 }; +typedef enum { ignore_monsters = 1, dont_ignore_monsters = 0, missile = 2 } IGNORE_MONSTERS; +typedef enum { ignore_glass = 1, dont_ignore_glass = 0 } IGNORE_GLASS; + extern void UTIL_MakeAimVectors ( const Vector &vecAngles ); // like MakeVectors, but assumes pitch isn't inverted extern void UTIL_MakeInvVectors ( const Vector &vec, globalvars_t *pgv ); diff --git a/client/client.dsp b/client/client.dsp index 973635f4..3df80de5 100644 --- a/client/client.dsp +++ b/client/client.dsp @@ -43,7 +43,7 @@ RSC=rc.exe # PROP Ignore_Export_Lib 1 # PROP Target_Dir "" # ADD BASE CPP /nologo /MT /W3 /GX /O2 /D "WIN32" /D "NDEBUG" /D "_WINDOWS" /YX /c -# ADD CPP /nologo /W3 /GX /O2 /I "./" /I "../common" /I "global" /I "hud" /I "../game_shared" /D "WIN32" /D "NDEBUG" /D "_WINDOWS" /FD /c +# ADD CPP /nologo /W3 /GX /O2 /I "./" /I "../common" /I "global" /I "hud" /I "../game_shared" /I "../dlls" /D "WIN32" /D "NDEBUG" /D "_WINDOWS" /FD /c # SUBTRACT CPP /Fr /YX # ADD BASE MTL /nologo /D "NDEBUG" /win32 # ADD MTL /nologo /D "NDEBUG" /mktyplib203 /win32 @@ -54,7 +54,7 @@ BSC32=bscmake.exe # ADD BSC32 /nologo LINK32=link.exe # ADD BASE LINK32 kernel32.lib user32.lib gdi32.lib winspool.lib comdlg32.lib advapi32.lib shell32.lib ole32.lib oleaut32.lib uuid.lib odbc32.lib odbccp32.lib /nologo /subsystem:windows /dll /machine:I386 -# ADD LINK32 msvcrt.lib /nologo /subsystem:windows /dll /machine:I386 /nodefaultlib:"libc" /def:".\client.def" /libpath:"..\common\libs" +# ADD LINK32 msvcrt.lib /nologo /subsystem:windows /dll /machine:I386 /nodefaultlib:"libc.lib" /def:".\client.def" /libpath:"..\common\libs" # SUBTRACT LINK32 /map # Begin Custom Build TargetDir=\Xash3D\src_main\temp\client\!release @@ -82,7 +82,7 @@ SOURCE="$(InputPath)" # PROP Target_Dir "" # ADD BASE CPP /nologo /MT /W3 /GX /Zi /O2 /I "..\common\vgui" /I "..\client" /I "..\client\render" /I ".\hud" /I "..\common\engine" /I "..\common" /I "..\server" /D "WIN32" /D "NDEBUG" /D "_WINDOWS" /D "CLIENT_DLL" /YX /FD /c # SUBTRACT BASE CPP /Fr -# ADD CPP /nologo /MDd /W3 /Gm /Gi /GX /ZI /Od /I "./" /I "../common" /I "global" /I "hud" /I "../game_shared" /D "WIN32" /D "_DEBUG" /D "_WINDOWS" /FR /FD /c +# ADD CPP /nologo /MDd /W3 /Gm /Gi- /GX /ZI /Od /I "./" /I "../common" /I "global" /I "hud" /I "../game_shared" /I "../dlls" /D "WIN32" /D "_DEBUG" /D "_WINDOWS" /FR /FD /c # SUBTRACT CPP /YX # ADD BASE MTL /nologo /D "NDEBUG" /mktyplib203 /win32 # ADD MTL /nologo /D "_DEBUG" /mktyplib203 /win32 diff --git a/client/global/aurora.cpp b/client/global/aurora.cpp index a47dba33..076a3918 100644 --- a/client/global/aurora.cpp +++ b/client/global/aurora.cpp @@ -332,7 +332,7 @@ ParticleSystem::ParticleSystem( int iEntIndex, char *szFilename ) if( !szFile ) { - ALERT( at_error, "particle %s not found.\n", szFilename ); + Con_Printf( "Error: particle %s not found.\n", szFilename ); return; } else @@ -467,7 +467,7 @@ ParticleType *ParticleSystem::ParseType( const char **szFile ) { // there's already a type with this name if (pTemp->m_bIsDefined) - ALERT( at_warning, "Particle type %s is defined more than once!\n", szToken); + Con_Printf( "Warning: particle type %s is defined more than once!\n", szToken); // copy all our data into the existing type, throw away the type we were making *pTemp = *pType; @@ -979,7 +979,7 @@ void ParticleSystem::DrawParticle( particle *part, Vector &right, Vector &up ) int numFrames = GetModelFrames( pDraw->pType->m_SpriteIndex ); - // ALERT( at_console, "UpdParticle %d: age %f, life %f, R:%f G:%f, B, %f \n", pDraw->pType->m_hSprite, part->age, part->age_death, pDraw->m_fRed, pDraw->m_fGreen, pDraw->m_fBlue); + // Con_Printf( "UpdParticle %d: age %f, life %f, R:%f G:%f, B, %f \n", pDraw->pType->m_hSprite, part->age, part->age_death, pDraw->m_fRed, pDraw->m_fGreen, pDraw->m_fBlue); // if we've reached the end of the sprite's frames, loop back while ( pDraw->frame > numFrames ) diff --git a/client/global/cl_tent.cpp b/client/global/cl_tent.cpp index db97447e..905825bf 100644 --- a/client/global/cl_tent.cpp +++ b/client/global/cl_tent.cpp @@ -713,7 +713,7 @@ void TE_AddClientMessage( void ) if( channel <= 0 || channel > ( MAX_CHANNELS - 1 )) { // invalid channel specified, use internal counter - if( channel != 0 ) ALERT( at_warning, "HUD_Message: invalid channel %i\n", channel ); + if( channel != 0 ) Con_Printf( "ERROR: HUD_Message: invalid channel %i\n", channel ); channel = msgindex; msgindex = (msgindex + 1) & (MAX_CHANNELS - 1); } diff --git a/client/global/dll_int.cpp b/client/global/dll_int.cpp index 7de4284f..5306b526 100644 --- a/client/global/dll_int.cpp +++ b/client/global/dll_int.cpp @@ -16,6 +16,8 @@ #include "ev_hldm.h" #include "r_weather.h" #include "pm_shared.h" +#include "pm_movevars.h" +#include "entity_types.h" cl_enginefuncs_t g_engfuncs; cl_globalvars_t *gpGlobals; @@ -220,21 +222,10 @@ int HUD_UpdateClientData( client_data_t *pcldata, float flTime ) return gHUD.UpdateClientData( pcldata, flTime ); } -int HUD_Redraw( float flTime, int state ) +int HUD_Redraw( float flTime, int intermission ) { - switch( state ) - { - case CL_ACTIVE: - case CL_PAUSED: - gHUD.Redraw( flTime ); - break; - case CL_LOADING: - // called while map is loading - break; - case CL_CHANGELEVEL: - // called once when changelevel in-action - break; - } + gHUD.Redraw( flTime ); + return 1; } @@ -265,6 +256,9 @@ void HUD_TxferLocalOverrides( entity_state_t *state, const clientdata_t *client // Spectating or not dead == get control over view angles. g_iAlive = ( client->iuser1 || ( client->deadflag == DEAD_NO ) ) ? 1 : 0; + + // FIXME: temporary hack so gaitsequence it's works properly + state->velocity = client->velocity; } /* @@ -331,7 +325,7 @@ void HUD_ProcessPlayerState( struct entity_state_s *dst, const struct entity_sta } } -int HUD_AddVisibleEntity( cl_entity_t *pEnt, int ed_type ) +int HUD_AddVisibleEntity( cl_entity_t *pEnt, int entityType ) { float oldScale, oldRenderAmt; float shellScale = 1.0f; @@ -345,7 +339,7 @@ int HUD_AddVisibleEntity( cl_entity_t *pEnt, int ed_type ) pEnt->curstate.renderamt = 255; // clear amount } - result = CL_AddEntity( pEnt, ed_type, -1 ); + result = CL_AddEntity( pEnt, entityType, -1 ); if ( pEnt->curstate.renderfx == kRenderFxGlowShell ) { @@ -354,7 +348,7 @@ int HUD_AddVisibleEntity( cl_entity_t *pEnt, int ed_type ) pEnt->curstate.renderamt = 128; // render glowshell - result |= CL_AddEntity( pEnt, ed_type, g_pTempEnts->hSprGlowShell ); + result |= CL_AddEntity( pEnt, entityType, g_pTempEnts->hSprGlowShell ); // restore parms pEnt->curstate.scale = oldScale; @@ -369,7 +363,7 @@ int HUD_AddVisibleEntity( cl_entity_t *pEnt, int ed_type ) // add in muzzleflash effect if ( pEnt->curstate.effects & EF_MUZZLEFLASH ) { - if( ed_type == ED_VIEWMODEL ) + if( entityType == ET_VIEWENTITY ) pEnt->curstate.effects &= ~EF_MUZZLEFLASH; g_pTempEnts->WeaponFlash( pEnt, 1 ); } @@ -384,7 +378,7 @@ int HUD_AddVisibleEntity( cl_entity_t *pEnt, int ed_type ) // add dimlight if ( pEnt->curstate.effects & EF_DIMLIGHT ) { - if ( ed_type == ED_CLIENT ) + if ( entityType == ET_PLAYER ) { EV_UpadteFlashlight( pEnt ); } diff --git a/client/global/enginecallback.h b/client/global/enginecallback.h index 0845669d..10a749c5 100644 --- a/client/global/enginecallback.h +++ b/client/global/enginecallback.h @@ -42,7 +42,8 @@ #define Cmd_RemoveCommand (*g_engfuncs.pfnDelCommand) #define CMD_ARGC (*g_engfuncs.pfnCmdArgc) #define CMD_ARGV (*g_engfuncs.pfnCmdArgv) -#define ALERT (*g_engfuncs.pfnAlertMessage) +#define Con_Printf (*g_engfuncs.Con_Printf) +#define Con_DPrintf (*g_engfuncs.Con_DPrintf) #define IN_GAME (*g_engfuncs.pfnIsInGame) inline void SPR_Set( HSPRITE hPic, int r, int g, int b ) diff --git a/client/global/ev_hldm.cpp b/client/global/ev_hldm.cpp index 1adae366..1cc6a2d5 100644 --- a/client/global/ev_hldm.cpp +++ b/client/global/ev_hldm.cpp @@ -1002,6 +1002,7 @@ void EV_FirePython( event_args_t *args ) //====================== #define GAUSS_PRIMARY_CHARGE_VOLUME 256 // how loud gauss is while charging #define GAUSS_PRIMARY_FIRE_VOLUME 450 // how loud gauss is when discharged +#define SND_CHANGE_PITCH (1<<7) // duplicated in protocol.h change sound pitch void EV_SpinGauss( event_args_t *args ) { @@ -1063,7 +1064,7 @@ void EV_FireGauss( event_args_t *args ) return; } -// ALERT( at_console, "firing gauss with %f\n", flDamage ); +// Con_Printf( "firing gauss with %f\n", flDamage ); EV_GetGunPosition( args, vecSrc, origin ); m_iBeam = g_engfuncs.pEventAPI->EV_FindModelIndex( "sprites/smoke.spr" ); @@ -1136,7 +1137,7 @@ void EV_FireGauss( event_args_t *args ) if ( n < 0.5f ) // 60 degrees { - // ALERT( at_console, "reflect %f\n", n ); + // Con_Printf( "reflect %f\n", n ); // reflect Vector r; @@ -1648,12 +1649,12 @@ void EV_UpdateLaserSpot( void ) m_pLaserSpot->entity.baseline.rendercolor.r = 200; m_pLaserSpot->entity.baseline.rendercolor.g = 12; m_pLaserSpot->entity.baseline.rendercolor.b = 12; -// ALERT( at_console, "CLaserSpot::Create()\n" ); +// Con_Printf( "CLaserSpot::Create()\n" ); } else if( !( m_pPlayer->curstate.effects & EF_LASERSPOT ) && m_pLaserSpot ) { // destroy laserspot -// ALERT( at_console, "CLaserSpot::Killed()\n" ); +// Con_Printf( "CLaserSpot::Killed()\n" ); m_pLaserSpot->die = 0.0f; m_pLaserSpot = NULL; return; @@ -1746,7 +1747,7 @@ void EV_SnarkFire( event_args_t *args ) //====================== void EV_FireNull( event_args_t *args ) { - ALERT( at_console, "Called null event!\n" ); + Con_Printf( "Called null event!\n" ); } //====================== // NULL END diff --git a/client/global/extdll.h b/client/global/extdll.h index 4ea9d2a2..3aa35285 100644 --- a/client/global/extdll.h +++ b/client/global/extdll.h @@ -13,7 +13,6 @@ #pragma warning(disable : 4100) // unreferenced formal parameter #include "windows.h" -#include "basetypes.h" // Misc C-runtime library headers #include @@ -31,6 +30,6 @@ // Shared header describing protocol between engine and DLLs #include "cl_entity.h" #include "clgame_api.h" -#include "game_shared.h" +#include "cdll_dll.h" #endif//EXTDLL_H \ No newline at end of file diff --git a/client/global/input.cpp b/client/global/input.cpp index f628b869..4737c4d3 100644 --- a/client/global/input.cpp +++ b/client/global/input.cpp @@ -140,7 +140,7 @@ void IN_KeyDown( kbutton_t *b ) } else { - ALERT( at_error, "three keys down for a button '%c' '%c' '%c'!\n", b->down[0], b->down[1], c ); + Con_Printf( "Error: three keys down for a button '%c' '%c' '%c'!\n", b->down[0], b->down[1], c ); return; } @@ -236,7 +236,7 @@ static float CL_KeyState( kbutton_t *key ) #if 0 if( msec ) { - ALERT( at_console, "%i ", msec ); + Con_Printf( "%i ", msec ); } #endif val = bound( 0, (float)msec / frame_msec, 1 ); diff --git a/client/global/r_beams.cpp b/client/global/r_beams.cpp index a6934151..d94344c5 100644 --- a/client/global/r_beams.cpp +++ b/client/global/r_beams.cpp @@ -1435,8 +1435,8 @@ void CViewRenderBeams::DrawLaser( Beam_t *pbeam, int frame, int rendermode, floa color2[1] *= flFade; color2[2] *= flFade; - //ALERT( at_console, "Fade: %f", flFade ); - //ALERT( at_console, "Dist: %f", flDistance ); + //Con_Printf( "Fade: %f", flFade ); + //Con_Printf( "Dist: %f", flDistance ); } DrawSegs( NOISE_DIVISIONS, pbeam->rgNoise, spriteIndex, frame, rendermode, pbeam->attachment[0], @@ -1533,7 +1533,7 @@ void CViewRenderBeams::DrawBeam( Beam_t *pbeam ) DrawLaser( pbeam, frame, rendermode, color, pbeam->modelIndex ); break; default: - ALERT( at_warning, "CViewRenderBeams::DrawBeam: Unknown beam type %i\n", pbeam->type ); + Con_Printf( "CViewRenderBeams::DrawBeam: Unknown beam type %i\n", pbeam->type ); break; } } @@ -1570,7 +1570,7 @@ bool CViewRenderBeams::RecomputeBeamEndpoints( Beam_t *pbeam ) } else { - // ALERT( at_warning, "can't find start entity\n" ); + // Con_Printf( "Warning: can't find start entity\n" ); // return false; } @@ -1612,7 +1612,7 @@ void CViewRenderBeams::AddServerBeam( cl_entity_t *pEnvBeam ) { if( m_nNumServerBeams >= MAX_BEAMS ) { - ALERT( at_error, "Too many static beams %d!\n", m_nNumServerBeams ); + Con_Printf( "ERROR: Too many static beams %d!\n", m_nNumServerBeams ); return; } @@ -2338,7 +2338,7 @@ void DrawBeamFollow( int modelIndex, BeamTrail_t* pHead, int frame, int rendermo while ( pHead ) { - // ALERT( at_console, "%.2f ", fraction ); + // Con_Printf( "%.2f ", fraction ); g_engfuncs.pTriAPI->Color4ub( nColor[0], nColor[1], nColor[2], 255 ); g_engfuncs.pTriAPI->TexCoord2f( 1.0f, 1.0f ); g_engfuncs.pTriAPI->Vertex3fv( last2 ); diff --git a/client/global/r_beams.h b/client/global/r_beams.h index 338a1fd2..f97338e3 100644 --- a/client/global/r_beams.h +++ b/client/global/r_beams.h @@ -6,14 +6,32 @@ #ifndef R_BEAMS_H #define R_BEAMS_H -#include "beam_def.h" -#include "te_shared.h" +#include "customentity.h" #define NOISE_DIVISIONS 64 // don't touch - many tripmines cause the crash when it equal 128 #define NOISE_MASK (NOISE_DIVISIONS-1) #define MAX_BEAM_ENTS 2 // start & end entity #define MAX_BEAMS 128 // Max simultaneous beams #define MAX_BEAMTRAILS 2048 // default max # of particles at one time + +// beam flags +#define FBEAM_STARTENTITY 0x00000001 +#define FBEAM_ENDENTITY 0x00000002 +#define FBEAM_FADEIN 0x00000004 +#define FBEAM_FADEOUT 0x00000008 + +// BEGIN SHARED FLAGS +#define FBEAM_SINENOISE 0x00000010 +#define FBEAM_SOLID 0x00000020 +#define FBEAM_SHADEIN 0x00000040 +#define FBEAM_SHADEOUT 0x00000080 +// END SHARED FLAGS + +#define FBEAM_ONLYNOISEONCE 0x00000100 // Only calculate our noise once +#define FBEAM_STARTVISIBLE 0x10000000 // Has this client actually seen this beam's start entity yet? +#define FBEAM_ENDVISIBLE 0x20000000 // Has this client actually seen this beam's end entity yet? +#define FBEAM_ISACTIVE 0x40000000 +#define FBEAM_FOREVER 0x80000000 struct BeamTrail_t { diff --git a/client/global/r_particle.cpp b/client/global/r_particle.cpp index 13aa9b7a..04d2972a 100644 --- a/client/global/r_particle.cpp +++ b/client/global/r_particle.cpp @@ -14,6 +14,12 @@ #include "r_particle.h" #include "r_tempents.h" +// particle velocities +static const float r_avertexnormals[NUMVERTEXNORMALS][3] = +{ +#include "anorms.h" +}; + // particle ramps static int ramp1[8] = { 0x6f, 0x6d, 0x6b, 0x69, 0x67, 0x65, 0x63, 0x61 }; static int ramp2[8] = { 0x6f, 0x6e, 0x6d, 0x6c, 0x6b, 0x6a, 0x68, 0x66 }; @@ -80,7 +86,7 @@ void CParticleSystem :: Clear( void ) m_vecAvelocities[i][2] = RANDOM_LONG( 0, 255 ) * 0.01f; // also build avertexnormals - m_vecAvertexNormals[i] = Vector( bytedirs[i] ); + m_vecAvertexNormals[i] = Vector( r_avertexnormals[i] ); } // build the local copy of particle palette @@ -122,7 +128,7 @@ CBaseParticle *CParticleSystem :: AllocParticle( HSPRITE m_hSpr ) if( !m_pFreeParticles ) { - ALERT( at_console, "Overflow %d particles\n", MAX_PARTICLES ); + Con_Printf( "Overflow %d particles\n", MAX_PARTICLES ); return NULL; } diff --git a/client/global/r_particle.h b/client/global/r_particle.h index 425d8e64..361450eb 100644 --- a/client/global/r_particle.h +++ b/client/global/r_particle.h @@ -6,6 +6,7 @@ #ifndef R_PARTICLE_H #define R_PARTICLE_H +#define NUMVERTEXNORMALS 162 #define MAX_PARTICLES 4096 #define SIMSHIFT 10 diff --git a/client/global/r_tempents.cpp b/client/global/r_tempents.cpp index 19c6c958..b4a4a5da 100644 --- a/client/global/r_tempents.cpp +++ b/client/global/r_tempents.cpp @@ -8,6 +8,7 @@ #include "studio_event.h" #include "triangle_api.h" #include "effects_api.h" +#include "entity_types.h" #include "pm_movevars.h" #include "r_particle.h" #include "r_tempents.h" @@ -113,7 +114,7 @@ int CTempEnts::TE_Update( TEMPENTITY *pTemp ) // before first frame when movevars not initialized if( !gpMovevars ) { - ALERT( at_error, "TempEntUpdate: no movevars!!!\n" ); + Con_Printf( "ERROR: TempEntUpdate: no movevars!!!\n" ); return true; } @@ -429,7 +430,7 @@ void CTempEnts :: Update( void ) { while( current ) { - CL_AddEntity( ¤t->entity, ED_TEMPENTITY, -1 ); + CL_AddEntity( ¤t->entity, ET_TEMPENTITY, -1 ); current = current->next; } } @@ -452,7 +453,7 @@ void CTempEnts :: Update( void ) else { // renderer rejected entity for some reasons... - if( !CL_AddEntity( ¤t->entity, ED_TEMPENTITY, -1 )) + if( !CL_AddEntity( ¤t->entity, ET_TEMPENTITY, -1 )) { if(!( current->flags & FTENT_PERSIST )) { @@ -554,7 +555,7 @@ TEMPENTITY *CTempEnts::TempEntAlloc( const Vector& org, int modelIndex ) if ( !m_pFreeTempEnts ) { - ALERT( at_console, "Overflow %d temporary ents!\n", MAX_TEMP_ENTITIES ); + Con_Printf( "Overflow %d temporary ents!\n", MAX_TEMP_ENTITIES ); return NULL; } @@ -594,7 +595,7 @@ TEMPENTITY *CTempEnts::TempEntAllocHigh( const Vector& org, int modelIndex ) { // didn't find anything? The tent list is either full of high-priority tents // or all tents in the list are still due to live for > 10 seconds. - ALERT( at_console, "Couldn't alloc a high priority TENT!\n" ); + Con_Printf( "Couldn't alloc a high priority TENT!\n" ); return NULL; } @@ -825,20 +826,20 @@ void CTempEnts::AttachTentToPlayer( int client, int modelIndex, float zoffset, f if ( client <= 0 || client > gpGlobals->maxClients ) { - ALERT( at_warning, "Bad client in AttachTentToPlayer()!\n" ); + Con_Printf( "Bad client in AttachTentToPlayer()!\n" ); return; } cl_entity_t *pClient = GetEntityByIndex( client ); if ( !pClient ) { - ALERT( at_warning, "Couldn't get ClientEntity for %i\n", client ); + Con_Printf( "Couldn't get ClientEntity for %i\n", client ); return; } if( GetModelType( modelIndex ) == mod_bad ) { - ALERT( at_console, "No model %d!\n", modelIndex ); + Con_Printf( "No model %d!\n", modelIndex ); return; } @@ -848,7 +849,7 @@ void CTempEnts::AttachTentToPlayer( int client, int modelIndex, float zoffset, f pTemp = TempEntAllocHigh( position, modelIndex ); if ( !pTemp ) { - ALERT( at_warning, "No temp ent.\n" ); + Con_Printf( "No temp ent.\n" ); return; } @@ -889,7 +890,7 @@ void CTempEnts::KillAttachedTents( int client ) { if ( client <= 0 || client > gpGlobals->maxClients ) { - ALERT( at_warning, "Bad client in KillAttachedTents()!\n" ); + Con_Printf( "Bad client in KillAttachedTents()!\n" ); return; } @@ -1081,7 +1082,7 @@ void CTempEnts::MuzzleFlash( cl_entity_t *pEnt, int iAttachment, int type ) if( pos == pEnt->origin ) { - ALERT( at_error, "Invalid muzzleflash entity!\n" ); + Con_Printf( "Invalid muzzleflash entity!\n" ); return; } @@ -1110,7 +1111,7 @@ void CTempEnts::MuzzleFlash( cl_entity_t *pEnt, int iAttachment, int type ) } // render now (guranteed that muzzleflash will be draw) - CL_AddEntity( &pTemp->entity, ED_TEMPENTITY, -1 ); + CL_AddEntity( &pTemp->entity, ET_TEMPENTITY, -1 ); } void CTempEnts::BloodSprite( const Vector &org, int colorIndex, int modelIndex, int modelIndex2, float size ) @@ -1255,7 +1256,7 @@ TEMPENTITY *CTempEnts::DefaultSprite( const Vector &pos, int spriteIndex, float if( !spriteIndex || GetModelType( spriteIndex ) != mod_sprite ) { - ALERT( at_console, "No Sprite %d!\n", spriteIndex ); + Con_Printf( "No Sprite %d!\n", spriteIndex ); return NULL; } @@ -1291,7 +1292,7 @@ TEMPENTITY *CTempEnts::TempSprite( const Vector &pos, const Vector &dir, float s if( GetModelType( modelIndex ) == mod_bad ) { - ALERT( at_console, "No model %d!\n", modelIndex ); + Con_Printf( "No model %d!\n", modelIndex ); return NULL; } @@ -1391,7 +1392,7 @@ void CTempEnts::Sprite_Spray( const Vector &pos, const Vector &dir, int modelInd if( GetModelType( modelIndex ) == mod_bad ) { - ALERT( at_console, "No model %d!\n", modelIndex ); + Con_Printf( "No model %d!\n", modelIndex ); return; } @@ -1430,7 +1431,7 @@ void CTempEnts::Sprite_Trail( int type, const Vector &vecStart, const Vector &ve if( GetModelType( modelIndex ) == mod_bad ) { - ALERT( at_console, "No model %d!\n", modelIndex ); + Con_Printf( "No model %d!\n", modelIndex ); return; } diff --git a/client/global/r_tempents.h b/client/global/r_tempents.h index 67ec06ca..422b838f 100644 --- a/client/global/r_tempents.h +++ b/client/global/r_tempents.h @@ -9,7 +9,6 @@ //----------------------------------------------------------------------------- // Purpose: implementation for temp entities //----------------------------------------------------------------------------- -#include "te_shared.h" #include "tmpent_def.h" #include "effects_api.h" diff --git a/client/global/r_weather.cpp b/client/global/r_weather.cpp index a1812b91..afee9cd0 100644 --- a/client/global/r_weather.cpp +++ b/client/global/r_weather.cpp @@ -182,7 +182,7 @@ void ProcessRain( void ) // just in case.. if ( deathHeight > vecStart[2] ) { - ALERT( at_error, "rain: can't create drip in water\n" ); + Con_Printf( "rain: can't create drip in water\n" ); continue; } @@ -191,7 +191,7 @@ void ProcessRain( void ) if ( !newClDrip ) { gHUD.Rain.dripsPerSecond = 0; // disable rain - ALERT( at_error, "rain: failed to allocate object!\n"); + Con_Printf( "rain: failed to allocate object!\n"); return; } @@ -222,22 +222,22 @@ void ProcessRain( void ) else { if ( cl_debugrain->value ) - ALERT( at_error, "rain: drip limit overflow! %i > %i\n", dripcounter, MAXDRIPS ); + Con_Printf( "rain: drip limit overflow! %i > %i\n", dripcounter, MAXDRIPS ); return; } } if ( cl_debugrain->value ) // print debug info { - ALERT( at_console, "Rain info: Drips exist: %i\n", dripcounter ); - ALERT( at_console, "Rain info: FX's exist: %i\n", fxcounter ); - ALERT( at_console, "Rain info: Attempted/Dropped: %i, %i\n", debug_attempted, debug_dropped ); + Con_Printf( "Rain info: Drips exist: %i\n", dripcounter ); + Con_Printf( "Rain info: FX's exist: %i\n", fxcounter ); + Con_Printf( "Rain info: Attempted/Dropped: %i, %i\n", debug_attempted, debug_dropped ); if ( debug_howmany ) { float ave = debug_lifetime / (float)debug_howmany; - ALERT( at_console, "Rain info: Average drip life time: %f\n", ave); + Con_Printf( "Rain info: Average drip life time: %f\n", ave); } - else ALERT( at_console, "Rain info: Average drip life time: --\n" ); + else Con_Printf( "Rain info: Average drip life time: --\n" ); } return; } @@ -251,14 +251,14 @@ void WaterLandingEffect( cl_drip *drip ) { if ( fxcounter >= MAXFX ) { - ALERT( at_console, "Rain error: FX limit overflow!\n" ); + Con_Printf( "Rain error: FX limit overflow!\n" ); return; } cl_rainfx *newFX = new cl_rainfx; if ( !newFX ) { - ALERT( at_console, "Rain error: failed to allocate FX object!\n"); + Con_Printf( "Rain error: failed to allocate FX object!\n"); return; } @@ -427,7 +427,7 @@ void ParseRainFile( void ) strcpy( mapname, STRING( gpGlobals->mapname )); if ( strlen( mapname ) == 0 ) { - ALERT( at_error, "rain: unable to read map name\n" ); + Con_Printf( "rain: unable to read map name\n" ); return; } @@ -442,7 +442,7 @@ void ParseRainFile( void ) if ( !pfile ) { if (cl_debugrain->value > 1 ) - ALERT( at_error, "rain: couldn't open rain settings file %s\n", mapname ); + Con_Printf( "rain: couldn't open rain settings file %s\n", mapname ); return; } @@ -491,7 +491,7 @@ void ParseRainFile( void ) token = COM_ParseToken( &pfile ); gHUD.Rain.globalHeight = atof( token ); } - else ALERT( at_error, "rain: unknown token %s in file %s\n", token, mapname ); + else Con_Printf( "rain: unknown token %s in file %s\n", token, mapname ); } FREE_FILE( afile ); } @@ -667,7 +667,7 @@ void DrawFXObjects( void ) // UNDONE: calc lighting right g_engfuncs.pEfxAPI->R_LightForPoint( (const float *)curFX->origin, color ); -// ALERT( at_console, "color %g %g %g\n", color[0], color[1], color[2] ); +// Con_Printf( "color %g %g %g\n", color[0], color[1], color[2] ); g_engfuncs.pTriAPI->Color4f( 0.4 + color[0], 0.4 + color[1], 0.4 + color[2], alpha ); g_engfuncs.pTriAPI->Begin( TRI_QUADS ); diff --git a/client/global/studio.cpp b/client/global/studio.cpp index 6efb6984..50f975e4 100644 --- a/client/global/studio.cpp +++ b/client/global/studio.cpp @@ -112,7 +112,7 @@ void HUD_StudioEvent( const mstudioevent_t *event, cl_entity_t *entity ) EV_EjectShell( event, entity ); break; default: - ALERT( at_console, "Unhandled client-side attachment %i ( %s )\n", event->event, event->options ); + Con_Printf( "Unhandled client-side attachment %i ( %s )\n", event->event, event->options ); break; } } @@ -160,7 +160,7 @@ int HUD_StudioDoInterp( cl_entity_t *e ) { if( r_studio_lerping->integer ) { - return (e->curstate.flags & EF_NOINTERP) ? false : true; + return (e->curstate.effects & EF_NOINTERP) ? false : true; } return false; } \ No newline at end of file diff --git a/client/global/utils.cpp b/client/global/utils.cpp index 22a5ef54..a6390888 100644 --- a/client/global/utils.cpp +++ b/client/global/utils.cpp @@ -10,11 +10,6 @@ DLL_GLOBAL const Vector g_vecZero = Vector( 0.0f, 0.0f, 0.0f ); -const float bytedirs[NUMVERTEXNORMALS][3] = -{ -#include "anorms.h" -}; - #ifdef _DEBUG void DBG_AssertFunction( BOOL fExpr, const char* szExpr, const char* szFile, int szLine, const char* szMessage ) { @@ -24,27 +19,11 @@ void DBG_AssertFunction( BOOL fExpr, const char* szExpr, const char* szFile, int if( szMessage != NULL ) sprintf( szOut, "ASSERT FAILED:\n %s \n(%s@%d)\n%s", szExpr, szFile, szLine, szMessage ); else sprintf( szOut, "ASSERT FAILED:\n %s \n(%s@%d)", szExpr, szFile, szLine ); - ALERT( at_error, szOut ); + Con_Printf( szOut ); } #endif // DEBUG - -Vector BitsToDir( int bits ) -{ - Vector dir; - - if( bits < 0 || bits >= NUMVERTEXNORMALS ) - return Vector( 0, 0, 0 ); - - dir.x = bytedirs[bits][0]; - dir.y = bytedirs[bits][1]; - dir.z = bytedirs[bits][2]; - - return dir; -} - // NOTE: modify these functions with caution - typedef struct { char *name; @@ -69,7 +48,7 @@ void END_READ( void ) { if( gMsg.badRead ) { - ALERT( at_console, "%s was received with errors\n", gMsg.name ); + Con_Printf( "%s was received with errors\n", gMsg.name ); } } @@ -186,11 +165,6 @@ float READ_ANGLE( void ) return (float)(READ_SHORT() * (360.0 / 65536)); } -Vector READ_DIR( void ) -{ - return BitsToDir( READ_BYTE() ); -} - /* ============================================================================== @@ -307,11 +281,6 @@ void SetScreenFade( Vector fadeColor, float alpha, float duration, float holdTim sf->fadeEnd += sf->fadeReset; } } - - if( fadeFlags & FFADE_PURGE ) - { - ClearAllFades(); - } } void DrawScreenFade( void ) @@ -545,7 +514,7 @@ BOOL Sys_LoadLibrary( const char* dllname, dllhandle_t* handle, const dllfunctio } } - ALERT( at_loading, "%s loaded succesfully!\n", dllname ); + Con_Printf( "%s loaded succesfully!\n", dllname ); *handle = dllhandle; return true; } diff --git a/client/global/utils.h b/client/global/utils.h index b1e22ed4..881ec835 100644 --- a/client/global/utils.h +++ b/client/global/utils.h @@ -15,6 +15,14 @@ extern cl_enginefuncs_t g_engfuncs; #define FILE_GLOBAL static #define DLL_GLOBAL +// euler angle order +#define PITCH 0 +#define YAW 1 +#define ROLL 2 + +#define RAD2DEG( x ) ((float)(x) * (float)(180.f / M_PI)) +#define DEG2RAD( x ) ((float)(x) * (float)(M_PI / 180.f)) + // // How did I ever live without ASSERT? // @@ -35,7 +43,7 @@ void DBG_AssertFunction( BOOL fExpr, const char* szExpr, const char* szFile, int extern DLL_GLOBAL const Vector g_vecZero; extern cl_globalvars_t *gpGlobals; -extern movevars_t *gpMovevars; +extern struct movevars_s *gpMovevars; extern int HUD_VidInit( void ); extern void HUD_Init( void ); @@ -51,7 +59,7 @@ extern void HUD_Frame( double time ); extern void HUD_Shutdown( void ); extern void HUD_RenderCallback( int fTrans ); extern void HUD_CreateEntities( void ); -extern int HUD_AddVisibleEntity( cl_entity_t *pEnt, int ed_type ); +extern int HUD_AddVisibleEntity( cl_entity_t *pEnt, int entityType ); extern void HUD_ParticleEffect( const float *org, const float *dir, int color, int count ); extern void HUD_StudioEvent( const mstudioevent_t *event, cl_entity_t *entity ); extern void HUD_StudioFxTransform( cl_entity_t *ent, float transform[4][4] ); @@ -189,8 +197,6 @@ inline cl_entity_t *DBG_GetEntityByIndex( int entnum, const char *file, const in } #endif -extern Vector BitsToDir( int bits ); - // message reading extern void BEGIN_READ( const char *pszName, int iSize, void *pBuf ); extern int READ_CHAR( void ); @@ -202,7 +208,6 @@ extern float READ_FLOAT( void ); extern char* READ_STRING( void ); extern float READ_COORD( void ); extern float READ_ANGLE( void ); -extern Vector READ_DIR( void ); extern void END_READ( void ); // misc utilities @@ -228,7 +233,6 @@ extern void Tracer_Draw( HSPRITE hSpr, Vector& start, Vector& delta, float width // mathlib extern void AngleMatrix( const vec3_t angles, float (*matrix)[4] ); -extern const float bytedirs[NUMVERTEXNORMALS][3]; // from cl_view.c extern void DrawProgressBar( void ); diff --git a/client/global/view.cpp b/client/global/view.cpp index 3bed22c4..e4f900be 100644 --- a/client/global/view.cpp +++ b/client/global/view.cpp @@ -87,9 +87,8 @@ void V_ThirdPerson( void ) { // no thirdperson in multiplayer if( gpGlobals->maxClients > 1 ) return; - if( !gHUD.viewFlags ) - gHUD.m_iLastCameraMode = gHUD.m_iCameraMode = 1; - else gHUD.m_iLastCameraMode = 1; // set new view after release camera + + gHUD.m_iCameraMode = 1; } //========================== @@ -97,9 +96,7 @@ void V_ThirdPerson( void ) //========================== void V_FirstPerson( void ) { - if( !gHUD.viewFlags ) - gHUD.m_iLastCameraMode = gHUD.m_iCameraMode = 0; - else gHUD.m_iLastCameraMode = 0; // set new view after release camera + gHUD.m_iCameraMode = 0; } /* @@ -233,7 +230,7 @@ float V_CalcFov( float fov_x, float width, float height ) // check to avoid division by zero if( fov_x < 1 || fov_x > 179 ) { - ALERT( at_error, "V_CalcFov: invalid fov %g!\n", fov_x ); + Con_Printf( "V_CalcFov: invalid fov %g!\n", fov_x ); fov_x = 90; } @@ -555,37 +552,7 @@ void V_CalcCameraRefdef( ref_params_t *pparams ) { if( pparams->intermission ) return; // disable in intermission mode - if( gHUD.viewFlags & CAMERA_ON ) - { - // this is a viewentity sets with BUzer's custom camera code - cl_entity_t *viewentity = GetEntityByIndex( gHUD.viewEntityIndex ); - if( viewentity ) - { - studiohdr_t *viewmonster = (studiohdr_t *)GetModelPtr( viewentity ); - float m_fLerp = GetLerpFrac(); - - if( viewentity->curstate.movetype == MOVETYPE_STEP ) - v_origin = LerpPoint( viewentity->prevstate.origin, viewentity->curstate.origin, m_fLerp ); - else v_origin = viewentity->origin; // already interpolated - - // calc monster view if supposed - if( gHUD.viewFlags & MONSTER_VIEW && viewmonster ) - v_origin += viewmonster->eyeposition; - - if( viewentity->curstate.movetype == MOVETYPE_STEP ) - v_angles = LerpAngle( viewentity->prevstate.angles, viewentity->curstate.angles, m_fLerp ); - else v_angles = viewentity->angles; // already interpolated - - if( gHUD.viewFlags & INVERSE_X ) // inverse X coordinate - v_angles[0] = -v_angles[0]; - pparams->crosshairangle[ROLL] = 1; // crosshair is hided - - // refresh position - pparams->viewangles = v_angles; - pparams->vieworg = v_origin; - } - } - else if( GetEntityByIndex( pparams->viewentity ) != GetLocalPlayer( )) + if( GetEntityByIndex( pparams->viewentity ) != GetLocalPlayer( )) { // this is a viewentity which sets by SET_VIEW builtin cl_entity_t *viewentity = GetEntityByIndex( pparams->viewentity ); @@ -625,7 +592,7 @@ cl_entity_t *V_FindIntermisionSpot( ref_params_t *pparams ) for( int i = 0; i < pparams->num_entities; i++ ) { ent = GetEntityByIndex( i ); - if( ent && !stricmp( STRING( ent->classname ), "info_intermission" )) + if( ent && !stricmp( ent->curstate.classname, "info_intermission" )) { if( j > 15 ) break; // spotlist is full spotindex[j] = ent->index; // save entindex diff --git a/client/hud/hud.cpp b/client/hud/hud.cpp index 7656dfd4..b75a2717 100644 --- a/client/hud/hud.cpp +++ b/client/hud/hud.cpp @@ -135,7 +135,7 @@ void CHud :: VidInit( void ) } else { - ALERT( at_warning, "hud.txt couldn't load\n" ); + Con_Printf( "Warning: hud.txt couldn't load\n" ); CVAR_SET_FLOAT( "hud_draw", 0 ); return; } @@ -262,10 +262,6 @@ int CHud :: Redraw( float flTime ) CLIENT_COMMAND( "screenshot\n" ); m_flShotTime = 0; } - - // custom view active, and flag "draw hud" isn't set - if(( viewFlags & CAMERA_ON ) && !( viewFlags & DRAW_HUD )) - return 1; if( CVAR_GET_FLOAT( "hud_draw" )) { diff --git a/client/hud/hud.h b/client/hud/hud.h index 908087b7..69ecb96b 100644 --- a/client/hud/hud.h +++ b/client/hud/hud.h @@ -538,16 +538,13 @@ public: int m_Teamplay; int m_iRes; int m_iCameraMode; - int m_iLastCameraMode; int m_iFontHeight; int DrawHudNumber( int x, int y, int iFlags, int iNumber, int r, int g, int b ); int DrawHudString( int x, int y, int iMaxX, char *szString, int r, int g, int b ); int DrawHudStringReverse( int xpos, int ypos, int iMinX, char *szString, int r, int g, int b ); int DrawHudNumberString( int xpos, int ypos, int iMinX, int iNumber, int r, int g, int b ); int GetNumWidth( int iNumber, int iFlags ); - int viewEntityIndex; int m_iHUDColor; - int viewFlags; private: // the memory for these arrays are allocated in the first call to CHud::VidInit() // when the hud.txt and associated sprites are loaded. freed in ~CHud() @@ -603,7 +600,6 @@ public: int _cdecl MsgFunc_RainData( const char *pszName, int iSize, void *pbuf ); int _cdecl MsgFunc_HUDColor( const char *pszName, int iSize, void *pbuf); int _cdecl MsgFunc_SetFog( const char *pszName, int iSize, void *pbuf ); - int _cdecl MsgFunc_CamData( const char *pszName, int iSize, void *pbuf ); int _cdecl MsgFunc_SetBody( const char *pszName, int iSize, void *pbuf ); int _cdecl MsgFunc_SetSkin( const char *pszName, int iSize, void *pbuf ); int _cdecl MsgFunc_WeaponAnim( const char *pszName, int iSize, void *pbuf ); diff --git a/client/hud/hud_ammo.cpp b/client/hud/hud_ammo.cpp index bdf8b4da..10bfba69 100644 --- a/client/hud/hud_ammo.cpp +++ b/client/hud/hud_ammo.cpp @@ -432,10 +432,10 @@ void WeaponsResource :: SelectSlot( int iSlot, int fAdvance, int iDirection ) if( gHUD.m_fPlayerDead || gHUD.m_iHideHUDDisplay & ( HIDEHUD_WEAPONS|HIDEHUD_ALL )) return; - if(!(gHUD.m_iWeaponBits & ITEM_SUIT)) + if(!(gHUD.m_iWeaponBits & (1<<(WEAPON_SUIT)))) return; - if(!(gHUD.m_iWeaponBits & ~ITEM_SUIT)) + if(!(gHUD.m_iWeaponBits & ~(1<<(WEAPON_SUIT)))) return; WEAPON *p = NULL; @@ -828,7 +828,7 @@ int CHudAmmo::Draw( float flTime ) int a, x, y, r, g, b; int AmmoWidth; - if(!(gHUD.m_iWeaponBits & ITEM_SUIT)) + if(!(gHUD.m_iWeaponBits & (1<<(WEAPON_SUIT)))) return 1; if((gHUD.m_iHideHUDDisplay & (HIDEHUD_WEAPONS|HIDEHUD_ALL))) diff --git a/client/hud/hud_battery.cpp b/client/hud/hud_battery.cpp index 4f55bf3b..4bd12161 100644 --- a/client/hud/hud_battery.cpp +++ b/client/hud/hud_battery.cpp @@ -84,7 +84,7 @@ int CHudBattery :: Draw( float flTime ) UnpackRGB( r, g, b, gHUD.m_iHUDColor ); - if(!(gHUD.m_iWeaponBits & ITEM_SUIT)) + if(!(gHUD.m_iWeaponBits & (1<<(WEAPON_SUIT)))) return 1; // Has health changed? Flash the health # diff --git a/client/hud/hud_flashlight.cpp b/client/hud/hud_flashlight.cpp index e9c3d89c..2b12c105 100644 --- a/client/hud/hud_flashlight.cpp +++ b/client/hud/hud_flashlight.cpp @@ -95,7 +95,7 @@ int CHudFlashlight :: Draw( float flTime ) int r, g, b, x, y, a; wrect_t rc; - if(!(gHUD.m_iWeaponBits & ITEM_SUIT)) + if(!(gHUD.m_iWeaponBits & (1<<(WEAPON_SUIT)))) return 1; if( m_fOn ) diff --git a/client/hud/hud_geiger.cpp b/client/hud/hud_geiger.cpp index 8d6d85a1..a345f3ad 100644 --- a/client/hud/hud_geiger.cpp +++ b/client/hud/hud_geiger.cpp @@ -68,12 +68,12 @@ int CHudGeiger::Draw( float flTime ) // peicewise linear is better than continuous formula for this if( m_iGeigerRange > 800 ) { - pct = 0; //ALERT( at_console, "range > 800\n" ); + pct = 0; //Con_Printf( "range > 800\n" ); } else if( m_iGeigerRange > 600 ) { pct = 2; - flvol = 0.4; //ALERT( at_console, "range > 600\n" ); + flvol = 0.4; //Con_Printf( "range > 600\n" ); rg[0] = 1; rg[1] = 1; i = 2; @@ -81,7 +81,7 @@ int CHudGeiger::Draw( float flTime ) else if( m_iGeigerRange > 500 ) { pct = 4; - flvol = 0.5; //ALERT( at_console, "range > 500\n" ); + flvol = 0.5; //Con_Printf( "range > 500\n" ); rg[0] = 1; rg[1] = 2; i = 2; @@ -89,7 +89,7 @@ int CHudGeiger::Draw( float flTime ) else if( m_iGeigerRange > 400 ) { pct = 8; - flvol = 0.6; //ALERT( at_console, "range > 400\n" ); + flvol = 0.6; //Con_Printf( "range > 400\n" ); rg[0] = 1; rg[1] = 2; rg[2] = 3; @@ -98,7 +98,7 @@ int CHudGeiger::Draw( float flTime ) else if( m_iGeigerRange > 300 ) { pct = 8; - flvol = 0.7; //ALERT( at_console, "range > 300\n" ); + flvol = 0.7; //Con_Printf( "range > 300\n" ); rg[0] = 2; rg[1] = 3; rg[2] = 4; @@ -107,7 +107,7 @@ int CHudGeiger::Draw( float flTime ) else if( m_iGeigerRange > 200 ) { pct = 28; - flvol = 0.78; //ALERT( at_console, "range > 200\n" ); + flvol = 0.78; //Con_Printf( "range > 200\n" ); rg[0] = 2; rg[1] = 3; rg[2] = 4; @@ -116,7 +116,7 @@ int CHudGeiger::Draw( float flTime ) else if( m_iGeigerRange > 150 ) { pct = 40; - flvol = 0.80; //ALERT( at_console, "range > 150\n" ); + flvol = 0.80; //Con_Printf( "range > 150\n" ); rg[0] = 3; rg[1] = 4; rg[2] = 5; @@ -125,7 +125,7 @@ int CHudGeiger::Draw( float flTime ) else if( m_iGeigerRange > 100 ) { pct = 60; - flvol = 0.85; //ALERT( at_console, "range > 100\n" ); + flvol = 0.85; //Con_Printf( "range > 100\n" ); rg[0] = 3; rg[1] = 4; rg[2] = 5; diff --git a/client/hud/hud_health.cpp b/client/hud/hud_health.cpp index 9f0743ff..484b3944 100644 --- a/client/hud/hud_health.cpp +++ b/client/hud/hud_health.cpp @@ -189,7 +189,7 @@ int CHudHealth :: Draw( float flTime ) ScaleColors( r, g, b, a ); // Only draw health if we have the suit. - if( gHUD.m_iWeaponBits & ITEM_SUIT ) + if( gHUD.m_iWeaponBits & (1<<(WEAPON_SUIT)) ) { HealthWidth = gHUD.GetSpriteRect(gHUD.m_HUD_number_0).right - gHUD.GetSpriteRect(gHUD.m_HUD_number_0).left; int CrossWidth = gHUD.GetSpriteRect(m_HUD_cross).right - gHUD.GetSpriteRect(m_HUD_cross).left; diff --git a/client/hud/hud_msg.cpp b/client/hud/hud_msg.cpp index 63cef58e..cf3a5ff5 100644 --- a/client/hud/hud_msg.cpp +++ b/client/hud/hud_msg.cpp @@ -39,7 +39,6 @@ DECLARE_HUDMESSAGE( ViewMode ); DECLARE_HUDMESSAGE( Particle ); DECLARE_HUDMESSAGE( Concuss ); DECLARE_HUDMESSAGE( GameMode ); -DECLARE_HUDMESSAGE( CamData ); DECLARE_HUDMESSAGE( TempEntity ); DECLARE_HUDMESSAGE( ServerName ); DECLARE_HUDMESSAGE( ScreenShake ); @@ -61,7 +60,6 @@ int CHud :: InitMessages( void ) HOOK_MESSAGE( Particle ); HOOK_MESSAGE( TempEntity ); HOOK_MESSAGE( SetFog ); - HOOK_MESSAGE( CamData ); HOOK_MESSAGE( RainData ); HOOK_MESSAGE( WeaponAnim ); HOOK_MESSAGE( SetBody ); @@ -71,8 +69,6 @@ int CHud :: InitMessages( void ) HOOK_COMMAND( "hud_changelevel", ChangeLevel ); // send by engine - viewEntityIndex = 0; // trigger_viewset stuff - viewFlags = 0; m_flFOV = 0; m_iHUDColor = RGB_YELLOWISH; // 255, 160, 0 @@ -80,7 +76,6 @@ int CHud :: InitMessages( void ) CVAR_REGISTER( "default_fov", "90", 0, "default client fov" ); CVAR_REGISTER( "hud_draw", "1", 0, "disable hud rendering" ); CVAR_REGISTER( "hud_takesshots", "0", 0, "take screenshots at 30 fps" ); - CVAR_REGISTER( "hud_scale", "0", FCVAR_ARCHIVE|FCVAR_LATCH, "scale hud at current resolution" ); // clear any old HUD list if( m_pHudList ) @@ -273,27 +268,6 @@ int CHud :: MsgFunc_Concuss( const char *pszName, int iSize, void *pbuf ) return 1; } -int CHud :: MsgFunc_CamData( const char *pszName, int iSize, void *pbuf ) -{ - BEGIN_READ( pszName, iSize, pbuf ); - - gHUD.viewEntityIndex = READ_SHORT(); - gHUD.viewFlags = READ_SHORT(); - - if( gHUD.viewFlags ) - m_iCameraMode = 1; - else m_iCameraMode = m_iLastCameraMode; - - // update pparams->viewentity too for right hearing - if( gHUD.viewEntityIndex ) - gpViewParams->viewentity = gHUD.viewEntityIndex; - else gpViewParams->viewentity = GetLocalPlayer()->index; - - END_READ(); - - return 1; -} - int CHud :: MsgFunc_RainData( const char *pszName, int iSize, void *pbuf ) { BEGIN_READ( pszName, iSize, pbuf ); @@ -424,41 +398,15 @@ int CHud::MsgFunc_ScreenShake( const char *pszName, int iSize, void *pbuf ) { BEGIN_READ( pszName, iSize, pbuf ); - ShakeCommand_t eCommand = (ShakeCommand_t)READ_SHORT(); float amplitude = (float)(unsigned short)READ_SHORT() * (1.0f / (float)(1<<12)); float duration = (float)(unsigned short)READ_SHORT() * (1.0f / (float)(1<<12)); float frequency = (float)(unsigned short)READ_SHORT() * (1.0f / (float)(1<<8)); - if( eCommand == SHAKE_STOP ) - { - m_Shake.amplitude = 0; - m_Shake.frequency = 0; - m_Shake.duration = 0; - } - else - { - if(( eCommand == SHAKE_START) || ( eCommand == SHAKE_FREQUENCY )) - { - m_Shake.frequency = frequency; - } - - if(( eCommand == SHAKE_START) || ( eCommand == SHAKE_AMPLITUDE )) - { - // don't overwrite larger existing shake unless we are told to. - if(( amplitude > m_Shake.amplitude ) || ( eCommand == SHAKE_AMPLITUDE )) - { - m_Shake.amplitude = amplitude; - } - } - - // only reset the timer for a new shake. - if( eCommand == SHAKE_START ) - { - m_Shake.duration = duration; - m_Shake.nextShake = 0; - m_Shake.time = m_flTime + duration; - } - } + m_Shake.frequency = frequency; + m_Shake.amplitude = amplitude; + m_Shake.duration = duration; + m_Shake.time = m_flTime + duration; + m_Shake.nextShake = 0; END_READ(); diff --git a/common/basetypes.h b/common/basetypes.h deleted file mode 100644 index 52ac0115..00000000 --- a/common/basetypes.h +++ /dev/null @@ -1,50 +0,0 @@ -//======================================================================= -// Copyright XashXT Group 2006 © -// basetypes.h - general typedefs -//======================================================================= -#ifndef BASETYPES_H -#define BASETYPES_H - -typedef unsigned char byte; -typedef unsigned short word; -typedef unsigned long dword; -typedef unsigned __int64 qword; -typedef unsigned int uint; -typedef int BOOL; -typedef signed __int64 int64; -typedef int func_t; -typedef int sound_t; -typedef int string_t; -typedef int shader_t; -typedef struct cvar_s cvar_t; -typedef struct edict_s edict_t; -typedef struct pmove_s pmove_t; -typedef struct movevars_s movevars_t; -typedef struct usercmd_s usercmd_t; -typedef struct cl_priv_s cl_priv_t; -typedef struct sv_priv_s sv_priv_t; -typedef struct netadr_s netadr_t; -typedef unsigned short CRC16_t; -typedef unsigned long CRC32_t; -typedef float vec_t; - -#define DLLEXPORT __declspec( dllexport ) - -#define FALSE 0 -#define TRUE 1 - -#ifndef NULL -#define NULL ((void *)0) -#endif - -#ifndef BIT -#define BIT( n ) (1<<( n )) -#endif - -// color strings -#define ColorIndex( c ) ((( c ) - '0' ) & 7 ) -#define IsColorString( p ) ( p && *( p ) == '^' && *(( p ) + 1) && *(( p ) + 1) >= '0' && *(( p ) + 1 ) <= '9' ) - -typedef struct { byte r; byte g; byte b; } color24; - -#endif//BASETYPES_H \ No newline at end of file diff --git a/common/beam_def.h b/common/beam_def.h index b02c85af..972c23f3 100644 --- a/common/beam_def.h +++ b/common/beam_def.h @@ -14,20 +14,7 @@ typedef enum BEAM_HOSE, } kBeamType_t; -// beam flags -#define FBEAM_STARTENTITY 0x00000001 -#define FBEAM_ENDENTITY 0x00000002 -#define FBEAM_FADEIN 0x00000004 -#define FBEAM_FADEOUT 0x00000008 -#define FBEAM_SINENOISE 0x00000010 -#define FBEAM_SOLID 0x00000020 -#define FBEAM_SHADEIN 0x00000040 -#define FBEAM_SHADEOUT 0x00000080 -#define FBEAM_ONLYNOISEONCE 0x00000100 // Only calculate our noise once -#define FBEAM_STARTVISIBLE 0x10000000 // Has this client actually seen this beam's start entity yet? -#define FBEAM_ENDVISIBLE 0x20000000 // Has this client actually seen this beam's end entity yet? -#define FBEAM_ISACTIVE 0x40000000 -#define FBEAM_FOREVER 0x80000000 + // Start/End Entity is encoded as 12 bits of entity index, and 4 bits of attachment (4:12) #define BEAMENT_ENTITY( x ) ((x) & 0xFFF) diff --git a/common/bspfile.h b/common/bspfile.h index e0f39505..6c483aab 100644 --- a/common/bspfile.h +++ b/common/bspfile.h @@ -18,6 +18,10 @@ BRUSH MODELS #define Q1BSP_VERSION 29 // quake1 regular version (beta is 28) #define HLBSP_VERSION 30 // half-life regular version +// worldcraft predefined angles +#define ANGLE_UP -1 +#define ANGLE_DOWN -2 + // bmodel limits #define MAX_MAP_HULLS 4 // MAX_HULLS diff --git a/common/cl_entity.h b/common/cl_entity.h index 461a3745..df4aae08 100644 --- a/common/cl_entity.h +++ b/common/cl_entity.h @@ -53,14 +53,8 @@ struct cl_entity_s { int index; // Index into cl_entities ( always match actual slot ) int player; // True if this entity is a "player" - string_t classname; // classname come from server - // Add also targetname, target, message and netname ? int serverframe; // TEMPORARY PLACED HERE - link_t area; // used by physics code - vec3_t absmin; - vec3_t absmax; - entity_state_t baseline; // The original state from which to delta during an uncompressed message entity_state_t prevstate; // The state information from the penultimate message received from the server entity_state_t curstate; // The state information from the last message received from server diff --git a/common/clgame_api.h b/common/clgame_api.h index 32832a02..7b607909 100644 --- a/common/clgame_api.h +++ b/common/clgame_api.h @@ -122,7 +122,7 @@ typedef struct cl_globalvars_s int maxEntities; int numEntities; // actual ents count - const char *pStringBase; // actual only when sys_sharedstrings is 1 + const char *pStringBase; void *pSaveData; // (SAVERESTOREDATA *) pointer } cl_globalvars_t; @@ -193,7 +193,8 @@ typedef struct cl_enginefuncs_s const char *(*pfnCmd_Args)( void ); // was Con_Printf float (*pfnGetLerpFrac)( void ); // was Con_DPrintf void (*pfnDelCommand)( const char *cmd_name ); // was Con_NPrintf - void (*pfnAlertMessage)( ALERT_TYPE, char *szFmt, ... ); // was Con_NXPrintf + void (*Con_Printf)( char *fmt, ... ); // was Con_NXPrintf + void (*Con_DPrintf)( char *fmt, ... ); // !!! const char* (*pfnPhysInfo_ValueForKey)( const char *key ); const char* (*pfnServerInfo_ValueForKey)( const char *key ); @@ -268,13 +269,13 @@ typedef struct void (*pfnShutdown)( void ); void (*pfnDrawTriangles)( int fTrans ); void (*pfnCreateEntities)( void ); - int (*pfnAddVisibleEntity)( struct cl_entity_s *pEnt, int ed_type ); + int (*pfnAddVisibleEntity)( struct cl_entity_s *pEnt, int entityType ); void (*pfnStudioEvent)( const mstudioevent_t *event, struct cl_entity_s *entity ); void (*pfnStudioFxTransform)( struct cl_entity_s *pEdict, float transform[4][4] ); void (*pfnCalcRefdef)( ref_params_t *parms ); void (*pfnPM_Move)( struct playermove_s *ppmove, int server ); void (*pfnPM_Init)( struct playermove_s *ppmove ); - char (*pfnPM_FindTextureType)( const char *name ); + char (*pfnPM_FindTextureType)( char *name ); void (*pfnCmdStart)( const struct cl_entity_s *player, int runfuncs ); void (*pfnCmdEnd)( const struct cl_entity_s *player, const usercmd_t *cmd, unsigned int random_seed ); void (*pfnCreateMove)( usercmd_t *cmd, int msec, int active ); diff --git a/common/com_model.h b/common/com_model.h index 8216af21..04d6ef7f 100644 --- a/common/com_model.h +++ b/common/com_model.h @@ -67,7 +67,7 @@ typedef struct msurface_s // lighting info byte *samples; // [numstyles*surfsize] int numstyles; - byte styles[LM_STYLES]; // index into d_lightstylevalue[] for animated lights + byte styles[4]; // index into d_lightstylevalue[] for animated lights // no one surface can be effected by more than 4 // animated lights. } msurface_t; diff --git a/common/const.h b/common/const.h index c651832b..b99e35ba 100644 --- a/common/const.h +++ b/common/const.h @@ -5,56 +5,569 @@ #ifndef CONST_H #define CONST_H -// euler angle order -#define PITCH 0 -#define YAW 1 -#define ROLL 2 +// +// Constants shared by the engine and dlls +// This header file included by engine files and DLL files. +// Most came from server.h -#define LM_STYLES 4 // MAXLIGHTMAPS +// edict->flags +#define FL_FLY (1<<0) // Changes the SV_Movestep() behavior to not need to be on ground +#define FL_SWIM (1<<1) // Changes the SV_Movestep() behavior to not need to be on ground (but stay in water) +#define FL_CONVEYOR (1<<2) +#define FL_CLIENT (1<<3) +#define FL_INWATER (1<<4) +#define FL_MONSTER (1<<5) +#define FL_GODMODE (1<<6) +#define FL_NOTARGET (1<<7) +#define FL_SKIPLOCALHOST (1<<8) // Don't send entity to local host, it's predicting this entity itself +#define FL_ONGROUND (1<<9) // At rest / on the ground +#define FL_PARTIALGROUND (1<<10) // not all corners are valid +#define FL_WATERJUMP (1<<11) // player jumping out of water +#define FL_FROZEN (1<<12) // Player is frozen for 3rd person camera +#define FL_FAKECLIENT (1<<13) // JAC: fake client, simulated server side; don't send network messages to them +#define FL_DUCKING (1<<14) // Player flag -- Player is fully crouched +#define FL_FLOAT (1<<15) // Apply floating force to this entity when in water +#define FL_GRAPHED (1<<16) // worldgraph has this ent listed as something that blocks a connection -#define RAD2DEG( x ) ((float)(x) * (float)(180.f / M_PI)) -#define DEG2RAD( x ) ((float)(x) * (float)(M_PI / 180.f)) +// UNDONE: Do we need these? +#define FL_IMMUNE_WATER (1<<17) +#define FL_IMMUNE_SLIME (1<<18) +#define FL_IMMUNE_LAVA (1<<19) -// worldcraft predefined angles -#define ANGLE_UP -1 -#define ANGLE_DOWN -2 +#define FL_PROXY (1<<20) // This is a spectator proxy +#define FL_ALWAYSTHINK (1<<21) // Brush model flag -- call think every frame regardless of nextthink - ltime (for constantly changing velocity/path) +#define FL_BASEVELOCITY (1<<22) // Base velocity has been applied this frame (used to convert base velocity into momentum) +#define FL_MONSTERCLIP (1<<23) // Only collide in with monsters who have FL_MONSTERCLIP set +#define FL_ONTRAIN (1<<24) // Player is _controlling_ a train, so movement commands should be ignored on client during prediction. +#define FL_WORLDBRUSH (1<<25) // Not moveable/removeable brush entity (really part of the world, but represented as an entity for transparency or something) +#define FL_SPECTATOR (1<<26) // This client is a spectator, don't run touch functions, etc. +#define FL_CHECK_PHS (1<<27) // This entity requested phs bitvector instead of pvsbitvector in AddToFullPack calls +#define FL_PLAYERCLIP (1<<28) // Only collide in with clients who have FL_PLAYERCLIP set +#define FL_CUSTOMENTITY (1<<29) // This is a custom entity +#define FL_KILLME (1<<30) // This entity is marked for death -- This allows the engine to kill ents at the appropriate time +#define FL_DORMANT (1<<31) // Entity is dormant, no updates to client -// sound specific -#define VOL_NORM 1.0f // volume values +// Goes into globalvars_t.trace_flags +#define FTRACE_SIMPLEBOX (1<<0) // Traceline with a simple box +#define FTRACE_IGNORE_GLASS (1<<1) // traceline will be ignored entities with rendermode != kRenderNormal -// pitch values -#define PITCH_LOW 95 // other values are possible - 0-255, where 255 is very high -#define PITCH_NORM 100 // non-pitch shifted -#define PITCH_HIGH 120 +// walkmove modes +#define WALKMOVE_NORMAL 0 // normal walkmove +#define WALKMOVE_WORLDONLY 1 // doesn't hit ANY entities, no matter what the solid type +#define WALKMOVE_CHECKONLY 2 // move, but don't touch triggers -// attenuation values -#define ATTN_NONE 0.0f -#define ATTN_NORM 0.8f -#define ATTN_IDLE 2.0f -#define ATTN_STATIC 1.25f +// edict->movetype values +#define MOVETYPE_NONE 0 // never moves +//#define MOVETYPE_ANGLENOCLIP 1 +//#define MOVETYPE_ANGLECLIP 2 +#define MOVETYPE_WALK 3 // Player only - moving on the ground +#define MOVETYPE_STEP 4 // gravity, special edge handling -- monsters use this +#define MOVETYPE_FLY 5 // No gravity, but still collides with stuff +#define MOVETYPE_TOSS 6 // gravity/collisions +#define MOVETYPE_PUSH 7 // no clip to world, push and crush +#define MOVETYPE_NOCLIP 8 // No gravity, no collisions, still do velocity/avelocity +#define MOVETYPE_FLYMISSILE 9 // extra size to monsters +#define MOVETYPE_BOUNCE 10 // Just like Toss, but reflect velocity when contacting surfaces +#define MOVETYPE_BOUNCEMISSILE 11 // bounce w/o gravity +#define MOVETYPE_FOLLOW 12 // track movement of aiment +#define MOVETYPE_PUSHSTEP 13 // BSP model that needs physics/world collisions (uses nearest hull for world collision) -// common conversion tools -#define ATTN_TO_SNDLVL( a ) (int)((a) ? (50 + 20 / ((float)a)) : 0 ) -#define SNDLVL_TO_ATTN( a ) ((a > 50) ? (20.0f / (float)(a - 50)) : 4.0 ) +// edict->solid values +// NOTE: Some movetypes will cause collisions independent of SOLID_NOT/SOLID_TRIGGER when the entity moves +// SOLID only effects OTHER entities colliding with this one when they move - UGH! +#define SOLID_NOT 0 // no interaction with other objects +#define SOLID_TRIGGER 1 // touch on edge, but not blocking +#define SOLID_BBOX 2 // touch on edge, block +#define SOLID_SLIDEBOX 3 // touch on edge, but not an onground +#define SOLID_BSP 4 // bsp clip, touch on edge, block -#define SND_CHANGE_VOL (1<<0) // change sound vol -#define SND_CHANGE_PITCH (1<<1) // change sound pitch -#define SND_STOP (1<<2) // stop the sound -#define SND_SPAWNING (1<<3) // we're spawing, used in some cases for ambients -#define SND_LOCALSOUND (1<<4) // not paused, not looped, for internal use -#define SND_STOP_LOOPING (1<<5) // stop all looping sounds on the entity. -#define SND_SPEAKER (1<<6) // being played again by a microphone through a speaker +// edict->deadflag values +#define DEAD_NO 0 // alive +#define DEAD_DYING 1 // playing death animation or still falling off of a ledge waiting to hit ground +#define DEAD_DEAD 2 // dead. lying still. +#define DEAD_RESPAWNABLE 3 // wait for respawn +#define DEAD_DISCARDBODY 4 -// 7 channels available -#define CHAN_REPLACE -1 // force to replace sound for any channel -#define CHAN_AUTO 0 -#define CHAN_WEAPON 1 -#define CHAN_VOICE 2 -#define CHAN_ITEM 3 -#define CHAN_BODY 4 -#define CHAN_STREAM 5 // allocate stream channel from the static or dynamic area -#define CHAN_STATIC 6 // allocate channel from the static area -#define CHAN_VOICE_BASE 7 // allocate channel for network voice data +#define DAMAGE_NO 0 // can't be damaged +#define DAMAGE_YES 1 // attempt to damage +#define DAMAGE_AIM 2 // special case for aiming damage + +// entity effects +#define EF_BRIGHTFIELD (1<<0) // swirling cloud of particles +#define EF_MUZZLEFLASH (1<<1) // single frame ELIGHT on entity attachment 0 +#define EF_BRIGHTLIGHT (1<<2) // DLIGHT centered at entity origin +#define EF_DIMLIGHT (1<<3) // player flashlight +#define EF_INVLIGHT (1<<4) // get lighting from ceiling +#define EF_NOINTERP (1<<5) // don't interpolate the next frame +#define EF_LIGHT (1<<6) // rocket flare glow sprite +#define EF_NODRAW (1<<7) // don't draw entity +#define EF_NOREFLECT (1<<8) // entity won't reflecting in mirrors +#define EF_REFLECTONLY (1<<9) // entity will be drawing only in mirrors +#define EF_NOWATERCSG (1<<10) // do not remove sides for func_water entity +#define EF_MINLIGHT (1<<11) // allways have some light (e.g. viewentity) +#define EF_FULLBRIGHT (1<<12) // just get fullbright +#define EF_NOSHADOW (1<<13) // ignore shadow for this entity +#define EF_MERGE_VISIBILITY (1<<14) // this entity allowed to merge vis (e.g. env_sky or portal camera) + +// user-specified entity effects +#define EF_LASERSPOT (1<<16) // tempentity laserspot at attachment #1 from player or npc + +// entity flags +#define EFLAG_SLERP 1 // do studio interpolation of this entity + +// classic quake flags (must be not collide with any dll spawnflags - engine uses this) +// please include string "allow_inhibited_entities" into your gameinfo.txt if you want to enable this feature +#define SF_NOT_EASY (1<<8) +#define SF_NOT_MEDIUM (1<<9) +#define SF_NOT_HARD (1<<10) +#define SF_NOT_DEATHMATCH (1<<11) + +// +// temp entity events (engine ignore it) +// +#define TE_BEAMPOINTS 0 // beam effect between two points +// coord coord coord (start position) +// coord coord coord (end position) +// short (sprite index) +// byte (starting frame) +// byte (frame rate in 0.1's) +// byte (life in 0.1's) +// byte (line width in 0.1's) +// byte (noise amplitude in 0.01's) +// byte,byte,byte (color) +// byte (brightness) +// byte (scroll speed in 0.1's) + +#define TE_BEAMENTPOINT 1 // beam effect between point and entity +// short (start entity) +// coord coord coord (end position) +// short (sprite index) +// byte (starting frame) +// byte (frame rate in 0.1's) +// byte (life in 0.1's) +// byte (line width in 0.1's) +// byte (noise amplitude in 0.01's) +// byte,byte,byte (color) +// byte (brightness) +// byte (scroll speed in 0.1's) + +#define TE_GUNSHOT 2 // particle effect plus ricochet sound +// coord coord coord (position) + +#define TE_EXPLOSION 3 // additive sprite, 2 dynamic lights, flickering particles, explosion sound, move vertically 8 pps +// coord coord coord (position) +// short (sprite index) +// byte (scale in 0.1's) +// byte (framerate) +// byte (flags) +// +// The Explosion effect has some flags to control performance/aesthetic features: +#define TE_EXPLFLAG_NONE 0 // all flags clear makes default Half-Life explosion +#define TE_EXPLFLAG_NOADDITIVE 1 // sprite will be drawn opaque (ensure that the sprite you send is a non-additive sprite) +#define TE_EXPLFLAG_NODLIGHTS 2 // do not render dynamic lights +#define TE_EXPLFLAG_NOSOUND 4 // do not play client explosion sound +#define TE_EXPLFLAG_NOPARTICLES 8 // do not draw particles +#define TE_EXPLFLAG_DRAWALPHA 16 // sprite will be drawn alpha +#define TE_EXPLFLAG_ROTATE 32 // rotate the sprite randomly + +#define TE_TAREXPLOSION 4 // Quake1 "tarbaby" explosion with sound +// coord coord coord (position) + +#define TE_SMOKE 5 // alphablend sprite, move vertically 30 pps +// coord coord coord (position) +// short (sprite index) +// byte (scale in 0.1's) +// byte (framerate) + +#define TE_TRACER 6 // tracer effect from point to point +// coord, coord, coord (start) +// coord, coord, coord (end) + +#define TE_LIGHTNING 7 // TE_BEAMPOINTS with simplified parameters +// coord, coord, coord (start) +// coord, coord, coord (end) +// byte (life in 0.1's) +// byte (width in 0.1's) +// byte (amplitude in 0.01's) +// short (sprite model index) + +#define TE_BEAMENTS 8 +// short (start entity) +// short (end entity) +// short (sprite index) +// byte (starting frame) +// byte (frame rate in 0.1's) +// byte (life in 0.1's) +// byte (line width in 0.1's) +// byte (noise amplitude in 0.01's) +// byte,byte,byte (color) +// byte (brightness) +// byte (scroll speed in 0.1's) + +#define TE_SPARKS 9 // 8 random tracers with gravity, ricochet sprite +// coord coord coord (position) + +#define TE_LAVASPLASH 10 // Quake1 lava splash +// coord coord coord (position) + +#define TE_TELEPORT 11 // Quake1 teleport splash +// coord coord coord (position) + +#define TE_EXPLOSION2 12 // Quake1 colormaped (base palette) particle explosion with sound +// coord coord coord (position) +// byte (starting color) +// byte (num colors) + +#define TE_BSPDECAL 13 // Decal from the .BSP file +// coord, coord, coord (x,y,z), decal position (center of texture in world) +// short (texture index of precached decal texture name) +// short (entity index) +// [optional - only included if previous short is non-zero (not the world)] short (index of model of above entity) + +#define TE_IMPLOSION 14 // tracers moving toward a point +// coord, coord, coord (position) +// byte (radius) +// byte (count) +// byte (life in 0.1's) + +#define TE_SPRITETRAIL 15 // line of moving glow sprites with gravity, fadeout, and collisions +// coord, coord, coord (start) +// coord, coord, coord (end) +// short (sprite index) +// byte (count) +// byte (life in 0.1's) +// byte (scale in 0.1's) +// byte (velocity along vector in 10's) +// byte (randomness of velocity in 10's) + +#define TE_BEAM 16 // obsolete + +#define TE_SPRITE 17 // additive sprite, plays 1 cycle +// coord, coord, coord (position) +// short (sprite index) +// byte (scale in 0.1's) +// byte (brightness) + +#define TE_BEAMSPRITE 18 // A beam with a sprite at the end +// coord, coord, coord (start position) +// coord, coord, coord (end position) +// short (beam sprite index) +// short (end sprite index) + +#define TE_BEAMTORUS 19 // screen aligned beam ring, expands to max radius over lifetime +// coord coord coord (center position) +// coord coord coord (axis and radius) +// short (sprite index) +// byte (starting frame) +// byte (frame rate in 0.1's) +// byte (life in 0.1's) +// byte (line width in 0.1's) +// byte (noise amplitude in 0.01's) +// byte,byte,byte (color) +// byte (brightness) +// byte (scroll speed in 0.1's) + +#define TE_BEAMDISK 20 // disk that expands to max radius over lifetime +// coord coord coord (center position) +// coord coord coord (axis and radius) +// short (sprite index) +// byte (starting frame) +// byte (frame rate in 0.1's) +// byte (life in 0.1's) +// byte (line width in 0.1's) +// byte (noise amplitude in 0.01's) +// byte,byte,byte (color) +// byte (brightness) +// byte (scroll speed in 0.1's) + +#define TE_BEAMCYLINDER 21 // cylinder that expands to max radius over lifetime +// coord coord coord (center position) +// coord coord coord (axis and radius) +// short (sprite index) +// byte (starting frame) +// byte (frame rate in 0.1's) +// byte (life in 0.1's) +// byte (line width in 0.1's) +// byte (noise amplitude in 0.01's) +// byte,byte,byte (color) +// byte (brightness) +// byte (scroll speed in 0.1's) + +#define TE_BEAMFOLLOW 22 // create a line of decaying beam segments until entity stops moving +// short (entity:attachment to follow) +// short (sprite index) +// byte (life in 0.1's) +// byte (line width in 0.1's) +// byte,byte,byte (color) +// byte (brightness) + +#define TE_GLOWSPRITE 23 +// coord, coord, coord (pos) short (model index) byte (scale / 10) + +#define TE_BEAMRING 24 // connect a beam ring to two entities +// short (start entity) +// short (end entity) +// short (sprite index) +// byte (starting frame) +// byte (frame rate in 0.1's) +// byte (life in 0.1's) +// byte (line width in 0.1's) +// byte (noise amplitude in 0.01's) +// byte,byte,byte (color) +// byte (brightness) +// byte (scroll speed in 0.1's) + +#define TE_STREAK_SPLASH 25 // oriented shower of tracers +// coord coord coord (start position) +// coord coord coord (direction vector) +// byte (color) +// short (count) +// short (base speed) +// short (ramdon velocity) + +#define TE_BEAMHOSE 26 // obsolete + +#define TE_DLIGHT 27 // dynamic light, effect world, minor entity effect +// coord, coord, coord (pos) +// byte (radius in 10's) +// byte byte byte (color) +// byte (brightness) +// byte (life in 10's) +// byte (decay rate in 10's) + +#define TE_ELIGHT 28 // point entity light, no world effect +// short (entity:attachment to follow) +// coord coord coord (initial position) +// coord (radius) +// byte byte byte (color) +// byte (life in 0.1's) +// coord (decay rate) + +#define TE_TEXTMESSAGE 29 +// short 1.2.13 x (-1 = center) +// short 1.2.13 y (-1 = center) +// byte Effect 0 = fade in/fade out +// 1 = flickery credits +// 2 = write out (training room) +// 4 bytes r,g,b,a color1 (text color) +// 4 bytes r,g,b,a color2 (effect color) +// ushort 8.8 fadein time +// ushort 8.8 fadeout time +// ushort 8.8 hold time +// optional ushort 8.8 fxtime (time the highlight lags behing the leading text in effect 2) +// string text message (512 chars max sz string) +#define TE_LINE 30 +// coord, coord, coord startpos +// coord, coord, coord endpos +// short life in 0.1 s +// 3 bytes r, g, b + +#define TE_BOX 31 +// coord, coord, coord boxmins +// coord, coord, coord boxmaxs +// short life in 0.1 s +// 3 bytes r, g, b + +#define TE_KILLBEAM 99 // kill all beams attached to entity +// short (entity) + +#define TE_LARGEFUNNEL 100 +// coord coord coord (funnel position) +// short (sprite index) +// short (flags) + +#define TE_BLOODSTREAM 101 // particle spray +// coord coord coord (start position) +// coord coord coord (spray vector) +// byte (color) +// byte (speed) + +#define TE_SHOWLINE 102 // line of particles every 5 units, dies in 30 seconds +// coord coord coord (start position) +// coord coord coord (end position) + +#define TE_BLOOD 103 // particle spray +// coord coord coord (start position) +// coord coord coord (spray vector) +// byte (color) +// byte (speed) + +#define TE_DECAL 104 // Decal applied to a brush entity (not the world) +// coord, coord, coord (x,y,z), decal position (center of texture in world) +// byte (texture index of precached decal texture name) +// short (entity index) + +#define TE_FIZZ 105 // create alpha sprites inside of entity, float upwards +// short (entity) +// short (sprite index) +// byte (density) + +#define TE_MODEL 106 // create a moving model that bounces and makes a sound when it hits +// coord, coord, coord (position) +// coord, coord, coord (velocity) +// angle (initial yaw) +// short (model index) +// byte (bounce sound type) +// byte (life in 0.1's) + +#define TE_EXPLODEMODEL 107 // spherical shower of models, picks from set +// coord, coord, coord (origin) +// coord (velocity) +// short (model index) +// short (count) +// byte (life in 0.1's) + +#define TE_BREAKMODEL 108 // box of models or sprites +// coord, coord, coord (position) +// coord, coord, coord (size) +// coord, coord, coord (velocity) +// byte (random velocity in 10's) +// short (sprite or model index) +// byte (count) +// byte (life in 0.1 secs) +// byte (flags) + +#define TE_GUNSHOTDECAL 109 // decal and ricochet sound +// coord, coord, coord (position) +// short (entity index???) +// byte (decal???) + +#define TE_SPRITE_SPRAY 110 // spay of alpha sprites +// coord, coord, coord (position) +// coord, coord, coord (velocity) +// short (sprite index) +// byte (count) +// byte (speed) +// byte (noise) + +#define TE_ARMOR_RICOCHET 111 // quick spark sprite, client ricochet sound. +// coord, coord, coord (position) +// byte (scale in 0.1's) + +#define TE_PLAYERDECAL 112 // ??? +// byte (playerindex) +// coord, coord, coord (position) +// short (entity???) +// byte (decal number???) +// [optional] short (model index???) + +#define TE_BUBBLES 113 // create alpha sprites inside of box, float upwards +// coord, coord, coord (min start position) +// coord, coord, coord (max start position) +// coord (float height) +// short (model index) +// byte (count) +// coord (speed) + +#define TE_BUBBLETRAIL 114 // create alpha sprites along a line, float upwards +// coord, coord, coord (min start position) +// coord, coord, coord (max start position) +// coord (float height) +// short (model index) +// byte (count) +// coord (speed) + +#define TE_BLOODSPRITE 115 // spray of opaque sprite1's that fall, single sprite2 for 1..2 secs (this is a high-priority tent) +// coord, coord, coord (position) +// short (sprite1 index) +// short (sprite2 index) +// byte (color) +// byte (scale) + +#define TE_WORLDDECAL 116 // Decal applied to the world brush +// coord, coord, coord (x,y,z), decal position (center of texture in world) +// byte (texture index of precached decal texture name) + +#define TE_WORLDDECALHIGH 117 // Decal (with texture index > 256) applied to world brush +// coord, coord, coord (x,y,z), decal position (center of texture in world) +// byte (texture index of precached decal texture name - 256) + +#define TE_DECALHIGH 118 // Same as TE_DECAL, but the texture index was greater than 256 +// coord, coord, coord (x,y,z), decal position (center of texture in world) +// byte (texture index of precached decal texture name - 256) +// short (entity index) + +#define TE_PROJECTILE 119 // Makes a projectile (like a nail) (this is a high-priority tent) +// coord, coord, coord (position) +// coord, coord, coord (velocity) +// short (modelindex) +// byte (life) +// byte (owner) projectile won't collide with owner (if owner == 0, projectile will hit any client). + +#define TE_SPRAY 120 // Throws a shower of sprites or models +// coord, coord, coord (position) +// coord, coord, coord (direction) +// short (modelindex) +// byte (count) +// byte (speed) +// byte (noise) +// byte (rendermode) + +#define TE_PLAYERSPRITES 121 // sprites emit from a player's bounding box (ONLY use for players!) +// byte (playernum) +// short (sprite modelindex) +// byte (count) +// byte (variance) (0 = no variance in size) (10 = 10% variance in size) + +#define TE_PARTICLEBURST 122 // very similar to lavasplash. +// coord (origin) +// short (radius) +// byte (particle color) +// byte (duration * 10) (will be randomized a bit) + +#define TE_FIREFIELD 123 // makes a field of fire. +// coord (origin) +// short (radius) (fire is made in a square around origin. -radius, -radius to radius, radius) +// short (modelindex) +// byte (count) +// byte (flags) +// byte (duration (in seconds) * 10) (will be randomized a bit) +// +// to keep network traffic low, this message has associated flags that fit into a byte: +#define TEFIRE_FLAG_ALLFLOAT 1 // all sprites will drift upwards as they animate +#define TEFIRE_FLAG_SOMEFLOAT 2 // some of the sprites will drift upwards. (50% chance) +#define TEFIRE_FLAG_LOOP 4 // if set, sprite plays at 15 fps, otherwise plays at whatever rate stretches the animation over the sprite's duration. +#define TEFIRE_FLAG_ALPHA 8 // if set, sprite is rendered alpha blended at 50% else, opaque +#define TEFIRE_FLAG_PLANAR 16 // if set, all fire sprites have same initial Z instead of randomly filling a cube. + +#define TE_PLAYERATTACHMENT 124 // attaches a TENT to a player (this is a high-priority tent) +// byte (entity index of player) +// coord (vertical offset) ( attachment origin.z = player origin.z + vertical offset ) +// short (model index) +// short (life * 10 ); + +#define TE_KILLPLAYERATTACHMENTS 125 // will expire all TENTS attached to a player. +// byte (entity index of player) + +#define TE_MULTIGUNSHOT 126 // much more compact shotgun message +// This message is used to make a client approximate a 'spray' of gunfire. +// Any weapon that fires more than one bullet per frame and fires in a bit of a spread is +// a good candidate for MULTIGUNSHOT use. (shotguns) +// +// NOTE: This effect makes the client do traces for each bullet, these client traces ignore +// entities that have studio models.Traces are 4096 long. +// +// coord (origin) +// coord (origin) +// coord (origin) +// coord (direction) +// coord (direction) +// coord (direction) +// coord (x noise * 100) +// coord (y noise * 100) +// byte (count) +// byte (bullethole decal texture index) + +#define TE_USERTRACER 127 // larger message than the standard tracer, but allows some customization. +// coord (origin) +// coord (origin) +// coord (origin) +// coord (velocity) +// coord (velocity) +// coord (velocity) +// byte ( life * 10 ) +// byte ( color ) this is an index into an array of color vectors in the engine. (0 - ) +// byte ( length * 10 ) #define MSG_BROADCAST 0 // unreliable to all #define MSG_ONE 1 // reliable to one (msg_entity) @@ -67,7 +580,7 @@ #define MSG_ONE_UNRELIABLE 8 // Send to one client, but don't put in reliable stream, put in unreliable datagram ( could be dropped ) #define MSG_SPEC 9 // Sends to all spectator proxies -#define CONTENTS_NONE 0 +// contents of a spot in the world #define CONTENTS_EMPTY -1 #define CONTENTS_SOLID -2 #define CONTENTS_WATER -3 @@ -84,172 +597,42 @@ #define CONTENTS_CURRENT_DOWN -14 #define CONTENTS_TRANSLUCENT -15 #define CONTENTS_LADDER -16 -#define CONTENTS_FLYFIELD -17 -#define CONTENTS_GRAVITY_FLYFIELD -18 -#define CONTENTS_FOG -19 +#define CONTENT_FLYFIELD -17 +#define CONTENT_GRAVITY_FLYFIELD -18 +#define CONTENT_FOG -19 -// pev->flags -#define FL_FLY (1<<0) // changes the SV_Movestep() behavior to not need to be on ground -#define FL_SWIM (1<<1) // same as AI_FLY but stay in water -#define FL_CONVEYOR (1<<2) // not used in Xash3D -#define FL_CLIENT (1<<3) // this a client entity -#define FL_INWATER (1<<4) // npc in water -#define FL_MONSTER (1<<5) // monster bit -#define FL_GODMODE (1<<6) // invulnerability npc or client -#define FL_NOTARGET (1<<7) // mark all npc's as neytral -#define FL_SKIPLOCALHOST (1<<8) // Don't send entity to local host, it's predicting this entity itself -#define FL_ONGROUND (1<<9) // at rest / on the ground -#define FL_PARTIALGROUND (1<<10) // not corners are valid -#define FL_WATERJUMP (1<<11) // water jumping -#define FL_FROZEN (1<<12) // stop moving, but continue thinking (e.g. for thirdperson camera) -#define FL_FAKECLIENT (1<<13) // JAC: fake client, simulated server side; don't send network messages to them -#define FL_DUCKING (1<<14) // monster (or player) is ducked -#define FL_FLOAT (1<<15) // Apply floating force to this entity when in water -#define FL_GRAPHED (1<<16) // worldgraph has this ent listed as something that blocks a connection -#define FL_IMMUNE_WATER (1<<17) // not used -#define FL_IMMUNE_SLIME (1<<18) // not used -#define FL_IMMUNE_LAVA (1<<19) // not used -#define FL_PROXY (1<<20) // This is a spectator proxy -#define FL_ALWAYSTHINK (1<<21) // Brush model flag -- call think every frame regardless of nextthink - ltime (for constantly changing velocity/path) -#define FL_BASEVELOCITY (1<<22) // Base velocity has been applied this frame (used to convert base velocity into momentum) -#define FL_MONSTERCLIP (1<<23) // Only collide in with monsters who have FL_MONSTERCLIP set -#define FL_ONTRAIN (1<<24) // Player is _controlling_ a train, so movement commands should be ignored on client during prediction. -#define FL_WORLDBRUSH (1<<25) // Not moveable/removeable brush entity (really part of the world, but represented as an entity for transparency or something) -#define FL_SPECTATOR (1<<26) // This client is a spectator, don't run touch functions, etc. -#define FL_PHS_FILTER (1<<27) // This entity requested phs bitvector in AddToFullPack calls -#define FL_PLAYERCLIP (1<<28) // Only collide in with clients who have FL_PLAYERCLIP set -#define FL_CUSTOMENTITY (1<<29) // This is a custom entity -#define FL_KILLME (1<<30) // This entity is marked for death -- This allows the engine to kill ents at the appropriate time -#define FL_DORMANT (1<<31) // Entity is dormant, no updates to client +// same as CONTENTS_* +#define CONTENT_EMPTY -1 +#define CONTENT_SOLID -2 +#define CONTENT_WATER -3 +#define CONTENT_SLIME -4 +#define CONTENT_LAVA -5 +#define CONTENT_SKY -6 -// pev->spawnflags -#define SF_START_ON (1<<0) +// channels +#define CHAN_AUTO 0 +#define CHAN_WEAPON 1 +#define CHAN_VOICE 2 +#define CHAN_ITEM 3 +#define CHAN_BODY 4 +#define CHAN_STREAM 5 // allocate stream channel from the static or dynamic area +#define CHAN_STATIC 6 // allocate channel from the static area -// classic quake flags (must be not collide with any dll spawnflags - engine uses this) -// please include string "allow_inhibited_entities" into your gameinfo.txt if you want to enable this feature -#define SF_NOT_EASY (1<<8) -#define SF_NOT_MEDIUM (1<<9) -#define SF_NOT_HARD (1<<10) -#define SF_NOT_DEATHMATCH (1<<11) +// attenuation values +#define ATTN_NONE 0.0f +#define ATTN_NORM 0.8f +#define ATTN_IDLE 2.0f +#define ATTN_STATIC 1.25f -// pev->effects -#define EF_BRIGHTFIELD (1<<0) // swirling cloud of particles -#define EF_MUZZLEFLASH (1<<1) // single frame ELIGHT on entity attachment 0 -#define EF_BRIGHTLIGHT (1<<2) // DLIGHT centered at entity origin -#define EF_DIMLIGHT (1<<3) // player flashlight -#define EF_INVLIGHT (1<<4) // get lighting from ceiling -#define EF_NOINTERP (1<<5) // don't interpolate the next frame -#define EF_LIGHT (1<<6) // dynamic light (rockets use) -#define EF_NODRAW (1<<7) // don't draw entity -#define EF_ROTATE (1<<8) // rotate bonus item -#define EF_MINLIGHT (1<<9) // allways have some light (viewmodel) -#define EF_FULLBRIGHT (1<<10) // completely ignore light values -#define EF_NOWATERCSG (1<<11) // do not remove sides for func_water entity -#define EF_NOSHADOW (1<<12) // ignore shadow for this entity -#define EF_PLANARSHADOW (1<<13) // use fast planarshadow method instead of shadow casters -#define EF_OCCLUSIONTEST (1<<14) // use occlusion test for this entity (e.g. glares) -#define EF_LASERSPOT (1<<15) // tempentity laserspot at attachment #1 from player or npc -#define EF_NOREFLECT (1<<16) // entity won't reflecting in mirrors -#define EF_REFLECTONLY (1<<17) // entity will be drawing only in mirrors +// pitch values +#define PITCH_NORM 100 // non-pitch shifted +#define PITCH_LOW 95 // other values are possible - 0-255, where 255 is very high +#define PITCH_HIGH 120 -// The explosion effect has some flags to control performance/aesthetic features: -#define TE_EXPLFLAG_NONE 0 // all flags clear makes default Half-Life explosion -#define TE_EXPLFLAG_NOADDITIVE 1 // sprite will be drawn opaque (ensure that the sprite you send is a non-additive sprite) -#define TE_EXPLFLAG_NODLIGHTS 2 // do not render dynamic lights -#define TE_EXPLFLAG_NOSOUND 4 // do not play client explosion sound -#define TE_EXPLFLAG_NOPARTICLES 8 // do not draw particles -#define TE_EXPLFLAG_DRAWALPHA 16 // sprite will be drawn alpha -#define TE_EXPLFLAG_ROTATE 32 // rotate the sprite randomly +// volume values +#define VOL_NORM 1.0f -// pev->takedamage -#define DAMAGE_NO 0 // can't be damaged -#define DAMAGE_YES 1 // attempt to damage -#define DAMAGE_AIM 2 // special case for aiming damage - -// pev->deadflag -#define DEAD_NO 0 // alive -#define DEAD_DYING 1 // playing death animation or still falling off of a ledge waiting to hit ground -#define DEAD_DEAD 2 // dead. lying still. -#define DEAD_RESPAWNABLE 3 // wait for respawn -#define DEAD_DISCARDBODY 4 - -#define PM_MAXHULLS 4 // 4 hulls - quake1, half-life - -// filter console messages -typedef enum -{ - at_console = 1, // format: [msg] - at_warning, // format: Warning: [msg] - at_error, // format: Error: [msg] - at_loading, // print messages during loading - at_aiconsole, // same as at_console, but only shown if developer level is 5! - at_logged // server print to console ( only in multiplayer games ). (NOT IMPLEMENTED) -} ALERT_TYPE; - -// filter client messages -typedef enum -{ - print_console, // dev. console messages - print_center, // at center of the screen - print_chat, // level high -} PRINT_TYPE; - -// model manager types -typedef enum -{ - mod_bad, - mod_world, - mod_brush, - mod_studio, - mod_sprite -} modtype_t; - -// monster's walkmove modes -typedef enum -{ - WALKMOVE_NORMAL = 0,// normal walkmove - WALKMOVE_WORLDONLY, // doesn't hit ANY entities, no matter what the solid type - WALKMOVE_CHECKONLY // move, but don't touch triggers -} walkmove_t; - -// monster's move to origin stuff -#define MOVE_START_TURN_DIST 64 // when this far away from moveGoal, start turning to face next goal -#define MOVE_STUCK_DIST 32 // if a monster can't step this far, it is stuck. -#define MOVE_NORMAL 0 // normal move in the direction monster is facing -#define MOVE_STRAFE 1 // moves in direction specified, no matter which way monster is facing - -// edict movetype -typedef enum -{ - MOVETYPE_NONE = 0, // never moves - MOVETYPE_CONVEYOR, // Xash3D: simulate conveyor belt, push all stuff - MOVETYPE_COMPOUND, // Xash3D: compound two entities - MOVETYPE_WALK, // Player only - moving on the ground - MOVETYPE_STEP, // gravity, special edge handling - MOVETYPE_FLY, // No gravity, but still collides with stuff - MOVETYPE_TOSS, // gravity/collisions - MOVETYPE_PUSH, // no clip to world, push on box contact - MOVETYPE_NOCLIP, // origin and angles change with no interaction - MOVETYPE_FLYMISSILE,// extra size to monsters - MOVETYPE_BOUNCE, // Just like Toss, but reflect velocity when contacting surfaces - MOVETYPE_BOUNCEMISSILE,// bounce w/o gravity - MOVETYPE_FOLLOW, // studio attached models - MOVETYPE_PUSHSTEP, // BSP model that needs physics/world collisions - MOVETYPE_PHYSIC, // phys simulation -} movetype_t; - -// edict collision filter -typedef enum -{ - SOLID_NOT = 0, // no interaction with other objects - SOLID_TRIGGER, // only touch when inside, after moving - SOLID_BBOX, // touch on edge - SOLID_SLIDEBOX, // touch on edge, but not an onground - SOLID_BSP, // bsp clip, touch on edge - SOLID_MESH, // custom convex mesh -} solid_t; - -// pev->buttons (client only) +// buttons #define IN_ATTACK (1<<0) #define IN_JUMP (1<<1) #define IN_DUCK (1<<2) @@ -267,19 +650,45 @@ typedef enum #define IN_ALT1 (1<<14) #define IN_SCORE (1<<15) // Used by client.dll for when scoreboard is held down -// rendering constants +// Break Model Defines +#define BREAK_TYPEMASK 0x4F +#define BREAK_GLASS 0x01 +#define BREAK_METAL 0x02 +#define BREAK_FLESH 0x04 +#define BREAK_WOOD 0x08 +#define BREAK_SMOKE 0x10 +#define BREAK_TRANS 0x20 +#define BREAK_CONCRETE 0x40 +#define BREAK_2 0x80 + +// Colliding temp entity sounds +#define BOUNCE_GLASS BREAK_GLASS +#define BOUNCE_METAL BREAK_METAL +#define BOUNCE_FLESH BREAK_FLESH +#define BOUNCE_WOOD BREAK_WOOD +#define BOUNCE_SHRAP 0x10 +#define BOUNCE_SHELL 0x20 +#define BOUNCE_CONCRETE BREAK_CONCRETE +#define BOUNCE_SHOTSHELL 0x80 + +// Temp entity bounce sound types +#define TE_BOUNCE_NULL 0 +#define TE_BOUNCE_SHELL 1 +#define TE_BOUNCE_SHOTSHELL 2 + +// Rendering constants typedef enum { kRenderNormal, // src kRenderTransColor, // c*a+dest*(1-a) kRenderTransTexture, // src*a+dest*(1-a) - kRenderGlow, // src*a+dest -- no Z buffer checks + kRenderGlow, // src*a+dest -- No Z buffer checks kRenderTransAlpha, // src*srca+dest*(1-srca) kRenderTransAdd, // src*a+dest kRenderTransInverse // src*(1-a)+dest*a } kRenderMode_t; -typedef enum +enum { kRenderFxNone = 0, kRenderFxPulseSlow, @@ -302,80 +711,33 @@ typedef enum kRenderFxExplode, // Scale up really big! kRenderFxGlowShell, // Glowing Shell kRenderFxClampMinScale, // Keep this sprite from getting very small (SPRITES only!) - kRenderFxReflection, // G-Cont - oldstyle mirror transform kRenderFxAurora, // set particle trail for this entity - kRenderFxNoReflect, // don't reflecting in mirrors -} kRenderFx_t; +}; -// link_t is only used for entity area links now -typedef struct link_s +// shared typedefs +typedef unsigned char byte; +typedef unsigned short word; +typedef unsigned long dword; +typedef unsigned int uint; +typedef float vec_t; + +typedef int string_t; +typedef struct cvar_s cvar_t; +typedef struct edict_s edict_t; + +typedef struct { - struct link_s *prev; - struct link_s *next; - int entnum; // svs.edicts \ cls.entities actual number -} link_t; - -// breakmodel defines -#define BREAK_TYPEMASK 0x4F -#define BREAK_GLASS 0x01 -#define BREAK_METAL 0x02 -#define BREAK_FLESH 0x04 -#define BREAK_WOOD 0x08 - -#define BREAK_SMOKE 0x10 -#define BREAK_TRANS 0x20 -#define BREAK_CONCRETE 0x40 -#define BREAK_2 0x80 - -// colliding temp entity sounds -#define BOUNCE_GLASS BREAK_GLASS -#define BOUNCE_METAL BREAK_METAL -#define BOUNCE_FLESH BREAK_FLESH -#define BOUNCE_WOOD BREAK_WOOD -#define BOUNCE_SHRAP 0x10 -#define BOUNCE_SHELL 0x20 -#define BOUNCE_CONCRETE BREAK_CONCRETE -#define BOUNCE_SHOTSHELL 0x80 - -// Temp entity bounce sound types -#define TE_BOUNCE_NULL 0 -#define TE_BOUNCE_SHELL 1 -#define TE_BOUNCE_SHOTSHELL 2 - -// studio models event range -#define EVENT_SPECIFIC 0 -#define EVENT_SCRIPTED 1000 -#define EVENT_SHARED 2000 // both client and server valid for playing -#define EVENT_CLIENT 5000 // less than this value it's a server-side studio events - -// game print flags -#define PRINT_LOW 0 // pickup messages -#define PRINT_MEDIUM 1 // death messages -#define PRINT_HIGH 2 // critical messages -#define PRINT_CHAT 3 // chat messages - -// client screen state -#define CL_LOADING 1 // draw loading progress-bar -#define CL_ACTIVE 2 // draw normal hud -#define CL_PAUSED 3 // pause when active -#define CL_CHANGELEVEL 4 // draw 'loading' during changelevel - -// renderer flags -#define RDF_NOWORLDMODEL (1<<0) // used for player configuration screen -#define RDF_PORTALINVIEW (1<<1) // cull entities using vis too because pvs\areabits are merged serverside -#define RDF_SKYPORTALINVIEW (1<<2) // draw skyportal instead of regular sky -#define RDF_NOFOVADJUSTMENT (1<<3) // do not adjust fov for widescreen -#define RDF_THIRDPERSON (1<<4) // enable chase cam instead firstperson - -// decal flags -#define FDECAL_PERMANENT 0x01 // This decal should not be removed in favor of any new decals -#define FDECAL_CUSTOM 0x02 // This is a custom clan logo and should not be saved/restored -#define FDECAL_DYNAMIC 0x04 // Indicates the decal is dynamic -#define FDECAL_DONTSAVE 0x08 // Decal was loaded from adjacent level, don't save it for this level -#define FDECAL_CLIPTEST 0x10 // Decal needs to be clip-tested -#define FDECAL_NOCLIP 0x20 // Decal is not clipped by containing polygon -#define FDECAL_USESAXIS 0x40 // Uses the s axis field to determine orientation (footprints) -#define FDECAL_ANIMATED 0x80 // this is decal has multiple frames + byte r, g, b; +} color24; +// model types +typedef enum +{ + mod_bad, + mod_world, + mod_brush, + mod_studio, + mod_sprite +} modtype_t; #endif//CONST_H \ No newline at end of file diff --git a/common/customentity.h b/common/customentity.h new file mode 100644 index 00000000..0212a338 --- /dev/null +++ b/common/customentity.h @@ -0,0 +1,26 @@ +//======================================================================= +// Copyright XashXT Group 2010 © +// customentity.h - beam encode specific +//======================================================================= +#ifndef CUSTOMENTITY_H +#define CUSTOMENTITY_H + +// beam types, encoded as a byte +enum +{ + BEAM_POINTS = 0, + BEAM_ENTPOINT, + BEAM_ENTS, + BEAM_HOSE, +}; + +#define BEAM_FSINE 0x10 +#define BEAM_FSOLID 0x20 +#define BEAM_FSHADEIN 0x40 +#define BEAM_FSHADEOUT 0x80 + +// Start/End Entity is encoded as 12 bits of entity index, and 4 bits of attachment (4:12) +#define BEAMENT_ENTITY( x ) ((x) & 0xFFF) +#define BEAMENT_ATTACHMENT( x ) (((x)>>12) & 0xF) + +#endif//CUSTOMENTITY_H \ No newline at end of file diff --git a/common/cvardef.h b/common/cvardef.h index b68e9162..5762469b 100644 --- a/common/cvardef.h +++ b/common/cvardef.h @@ -6,11 +6,15 @@ #define CVARDEF_H // cvar flags -#define FCVAR_ARCHIVE BIT(0) // set to cause it to be saved to config.cfg -#define FCVAR_USERINFO BIT(1) // added to userinfo when changed -#define FCVAR_SERVERINFO BIT(2) // added to serverinfo when changed -#define FCVAR_LATCH BIT(3) // create latched cvar -#define FCVAR_PHYSICINFO BIT(4) // added to physicinfo (movevars) when changed +#define FCVAR_ARCHIVE (1<<0) // set to cause it to be saved to config.cfg +#define FCVAR_USERINFO (1<<1) // changes the client's info string +#define FCVAR_SERVER (1<<2) // changes the serevrinfo string, notifies players when changed +#define FCVAR_EXTDLL (1<<3) // defined by external DLL +#define FCVAR_CLIENTDLL (1<<4) // defined by the client dll +#define FCVAR_PROTECTED (1<<5) // It's a server cvar, but we don't send the data since it's a password, etc. +#define FCVAR_SPONLY (1<<6) // This cvar cannot be changed by clients connected to a multiplayer server. +#define FCVAR_PRINTABLEONLY (1<<7) // This cvar's string cannot contain unprintable characters ( player name ) +#define FCVAR_UNLOGGED (1<<8) // If this is a FCVAR_SERVER, don't log changes to the log file / console /* ======================================================================== @@ -22,6 +26,7 @@ struct cvar_s { char *name; char *string; // normal string + uint flags; // state flags float value; // com.atof( string ) int integer; // com.atoi( string ) bool modified; // set each time the cvar is changed diff --git a/common/edict.h b/common/edict.h new file mode 100644 index 00000000..b9287bfa --- /dev/null +++ b/common/edict.h @@ -0,0 +1,23 @@ +//======================================================================= +// Copyright XashXT Group 2010 © +// edict.h - server edicts +//======================================================================= +#ifndef EDICT_H +#define EDICT_H + +#include "progdefs.h" + +struct edict_s +{ + int free; // unused entity when true + float freetime; // sv.time when the object was freed + int serialnumber; // must match with entity num + + struct sv_priv_s *pvEngineData; // alloced, freed and used by engine only + void *pvPrivateData; // alloced and freed by engine, used by DLLs + entvars_t v; // C exported fields from progs + + // other fields from progs come immediately after +}; + +#endif//EDICT_H \ No newline at end of file diff --git a/common/effects_api.h b/common/effects_api.h index 84c34ba7..9262a2e5 100644 --- a/common/effects_api.h +++ b/common/effects_api.h @@ -29,7 +29,7 @@ typedef struct efxapi_s void (*R_LightForPoint)( const float *rgflOrigin, float *lightValue ); int (*CL_IsBoxVisible)( const float *mins, const float *maxs ); int (*R_CullBox)( const float *mins, const float *maxs ); - int (*R_AddEntity)( struct cl_entity_s *pEnt, int ed_type, HSPRITE customShader ); + int (*R_AddEntity)( struct cl_entity_s *pEnt, int entityType, HSPRITE customShader ); void (*R_EnvShot)( const float *vieworg, const char *name, int skyshot ); } efxapi_t; diff --git a/common/svgame_api.h b/common/eiface.h similarity index 70% rename from common/svgame_api.h rename to common/eiface.h index e5ddd6af..83ba317c 100644 --- a/common/svgame_api.h +++ b/common/eiface.h @@ -1,59 +1,64 @@ //======================================================================= // Copyright XashXT Group 2008 © -// svgame_api.h - entity interface between engine and svgame +// eiface.h - interface between engine and dlls //======================================================================= -#ifndef SVGAME_API_H -#define SVGAME_API_H +#ifndef EIFACE_H +#define EIFACE_H -#include "trace_def.h" -#include "pm_shared.h" +#include "cvardef.h" -#define SV_INTERFACE_VERSION 140 +#define INTERFACE_VERSION 140 // GetEntityAPI, GetEntityAPI2 +#define NEW_DLL_FUNCTIONS_VERSION 2 // GetNewDLLFunctions (Xash3D uses version 2) -typedef struct globalvars_s -{ - float time; - float frametime; - int force_retouch; +typedef unsigned long CRC32_t; - string_t mapname; - string_t startspot; +// filter console messages +typedef enum +{ + at_notice, + at_console, // format: [msg] + at_warning, // format: Warning: [msg] + at_error, // format: Error: [msg] + at_aiconsole, // same as at_console, but only shown if developer level is 5! + at_logged // server print to console ( only in multiplayer games ). (NOT IMPLEMENTED) +} ALERT_TYPE; - BOOL deathmatch; - BOOL coop; - BOOL teamplay; +// filter client messages +typedef enum +{ + print_console, // dev. console messages + print_center, // at center of the screen + print_chat, // level high +} PRINT_TYPE; - int serverflags; - int found_secrets; +// for integrity checking of content on clients +typedef enum +{ + force_exactfile, // file on client must exactly match server's file + force_model_samebounds, // for model files only, the geometry must fit in the same bbox + force_model_specifybounds, // for model files only, the geometry must fit in the specified bbox +} FORCE_TYPE; - // MakeVectors info - vec3_t v_forward; - vec3_t v_up; - vec3_t v_right; +typedef enum +{ + AREA_SOLID, // find any solid edicts + AREA_TRIGGERS, // find all SOLID_TRIGGER edicts + AREA_CUSTOM, // find all edicts with custom contents - water, lava, fog, laders etc +} AREA_TYPE; - // global TraceResult - BOOL trace_allsolid; - BOOL trace_startsolid; - float trace_fraction; - vec3_t trace_endpos; - vec3_t trace_plane_normal; - float trace_plane_dist; - edict_t *trace_ent; - BOOL trace_inopen; - BOOL trace_inwater; - int trace_hitgroup; - int trace_flags; - - int changelevel; // transition in progress when true (was msg_entity) - int numEntities; // actual ents count (was cdAudioTrack) - int maxClients; - int maxEntities; - - const char *pStringBase; // actual only when sys_sharedstrings is 1 - - void *pSaveData; // (SAVERESTOREDATA *) pointer - vec3_t vecLandmarkOffset; -} globalvars_t; +typedef struct +{ + int fAllSolid; // if true, plane is not valid + int fStartSolid; // if true, the initial point was in a solid area + int fInOpen; // if true trace is open + int fInWater; // if true trace is in water + float flFraction; // time completed, 1.0 = didn't hit anything + vec3_t vecEndPos; // final position + float flPlaneDist; // planes distance + vec3_t vecPlaneNormal; // surface normal at impact + edict_t *pHit; // entity the surface is on + int iHitgroup; // 0 == generic, non zero is specific body part +} TraceResult; // engine hands this to DLLs for functionality callbacks typedef struct enginefuncs_s @@ -65,8 +70,8 @@ typedef struct enginefuncs_s int (*pfnModelFrames)( int modelIndex ); void (*pfnSetSize)( edict_t *e, const float *rgflMin, const float *rgflMax ); void (*pfnChangeLevel)( const char* s1, const char* s2 ); - edict_t* (*pfnFindClientInPHS)( edict_t *pEdict ); // was pfnGetSpawnParms - edict_t* (*pfnEntitiesInPHS)( edict_t *pplayer ); // was pfnSaveSpawnParms + edict_t* (*pfnFindClientInPHS)( edict_t *pEdict ); + edict_t* (*pfnEntitiesInPHS)( edict_t *pplayer ); float (*pfnVecToYaw)( const float *rgflVector ); void (*pfnVecToAngles)( const float *rgflVectorIn, float *rgflVectorOut ); void (*pfnMoveToOrigin)( edict_t *ent, const float *pflGoal, float dist, int iMoveType ); @@ -83,7 +88,7 @@ typedef struct enginefuncs_s void (*pfnRemoveEntity)( edict_t* e ); edict_t* (*pfnCreateNamedEntity)( string_t className ); void (*pfnMakeStatic)( edict_t *ent ); - void (*pfnLinkEdict)( edict_t *e, int touch_triggers ); // was pfnEntIsOnFloor + void (*pfnLinkEdict)( edict_t *e, int touch_triggers ); // a part of CustomPhysics implementation int (*pfnDropToFloor)( edict_t* e ); int (*pfnWalkMove)( edict_t *ent, float yaw, float dist, int iMode ); void (*pfnSetOrigin)( edict_t *e, const float *rgflOrigin ); @@ -93,9 +98,9 @@ typedef struct enginefuncs_s void (*pfnTraceToss)( edict_t* pent, edict_t* pentToIgnore, TraceResult *ptr ); int (*pfnTraceMonsterHull)( edict_t *pEdict, const float *v1, const float *v2, int fNoMonsters, edict_t *pentToSkip, TraceResult *ptr ); void (*pfnTraceHull)( const float *v1, const float *v2, int fNoMonsters, int hullNumber, edict_t *pentToSkip, TraceResult *ptr ); - void (*pfnTraceModel)( const float *v1, const float *v2, edict_t *pent, TraceResult *ptr ); + void (*pfnTraceModel)( const float *v1, const float *v2, int hullNumber, edict_t *pent, TraceResult *ptr ); const char *(*pfnTraceTexture)( edict_t *pTextureEntity, const float *v1, const float *v2 ); - int (*pfnTestEntityPosition)( edict_t *pTestEdict ); // was pfnTraceSphere + int (*pfnAreaEdicts)( const float *mins, const float *maxs, edict_t **list, int maxcount, AREA_TYPE areatype ); // a part of CustomPhysics implementation void (*pfnGetAimVector)( edict_t* ent, float speed, float *rgflReturn ); void (*pfnServerCommand)( const char* str ); void (*pfnServerExecute)( void ); @@ -120,7 +125,7 @@ typedef struct enginefuncs_s void (*pfnCVarSetFloat)( const char *szVarName, float flValue ); void (*pfnCVarSetString)( const char *szVarName, const char *szValue ); void (*pfnAlertMessage)( ALERT_TYPE level, char *szFmt, ... ); - void* (*pfnGetProcAddress)( void *hInstance, const char *name ); // was pfnEngineFprintf + void (*pfnDropClient)( int clientIndex ); void* (*pfnPvAllocEntPrivateData)( edict_t *pEdict, long cb ); void* (*pfnPvEntPrivateData)( edict_t *pEdict ); void (*pfnFreeEntPrivateData)( edict_t *pEdict ); @@ -144,26 +149,26 @@ typedef struct enginefuncs_s const char *(*pfnCmd_Argv)( int argc ); int (*pfnCmd_Argc)( void ); void (*pfnGetAttachment)( const edict_t *pEdict, int iAttachment, float *rgflOrigin, float *rgflAngles ); - void (*pfnCRC_Init)( CRC32_t *pulCRC ); - void (*pfnCRC_ProcessBuffer)( CRC32_t *pulCRC, void *p, int len ); - void (*pfnCRC_ProcessByte)( CRC32_t *pulCRC, byte ch ); - CRC32_t (*pfnCRC_Final)( CRC32_t pulCRC ); + void (*pfnCRC32_Init)( CRC32_t *pulCRC ); + void (*pfnCRC32_ProcessBuffer)( CRC32_t *pulCRC, void *p, int len ); + void (*pfnCRC32_ProcessByte)( CRC32_t *pulCRC, byte ch ); + CRC32_t (*pfnCRC32_Final)( CRC32_t pulCRC ); long (*pfnRandomLong)( long lLow, long lHigh ); float (*pfnRandomFloat)( float flLow, float flHigh ); void (*pfnSetView)( const edict_t *pClient, const edict_t *pViewent ); float (*pfnTime)( void ); // host.realtime, not sv.time void (*pfnCrosshairAngle)( const edict_t *pClient, float pitch, float yaw ); - byte* (*pfnLoadFile)( const char *filename, int *pLength ); + byte* (*pfnLoadFileForMe)( const char *filename, int *pLength ); void (*pfnFreeFile)( void *buffer ); void (*pfnEndSection)( const char *pszSectionName ); int (*pfnCompareFileTime)( const char *filename1, const char *filename2, int *iCompare ); void (*pfnGetGameDir)( char *szGetGameDir ); - void (*pfnClassifyEdict)( edict_t *pEdict, int ed_type ); // was pfnCvar_RegisterVariable + void (*pfnHostError)( const char *szFmt, ... ); void (*pfnFadeClientVolume)( const edict_t *pEdict, float fadePercent, float fadeOutSeconds, float holdTime, float fadeInSeconds ); void (*pfnSetClientMaxspeed)( const edict_t *pEdict, float fNewMaxspeed ); edict_t *(*pfnCreateFakeClient)( const char *netname ); // returns NULL if fake client can't be created void (*pfnRunPlayerMove)( edict_t *fakeclient, const float *viewangles, float forwardmove, float sidemove, float upmove, word buttons, byte impulse, byte msec ); - int (*pfnFileExists)( const char *filename ); // was pfnNumberOfEntities - see gpGlobals->numEntities + int (*pfnNumberOfEntities)( void ); char* (*pfnGetInfoKeyBuffer)( edict_t *e ); // passing in NULL gets the serverinfo char* (*pfnInfoKeyValue)( char *infobuffer, char *key ); void (*pfnSetKeyValue)( char *infobuffer, char *key, char *value ); @@ -172,18 +177,18 @@ typedef struct enginefuncs_s void (*pfnStaticDecal)( const float *origin, int decalIndex, int entityIndex, int modelIndex ); int (*pfnPrecacheGeneric)( const char* s ); int (*pfnGetPlayerUserId)(edict_t *e ); // returns the server assigned userid for this player. useful for logging frags, etc. - void (*pfnPlayMusic)( const char *trackname, int flags ); // was pfnBuildSoundMsg + void (*pfnSoundTrack)( const char *trackname, int flags ); // playing looped soundtrack int (*pfnIsDedicatedServer)( void ); // is this a dedicated server? - void* (*pfnMemAlloc)(size_t cb, const char *file, const int line);// was pfnCVarGetPointer - void (*pfnMemFree)(void *mem, const char *file, const int line); // was pfnGetPlayerWONId + cvar_t *(*pfnCVarGetPointer)( const char *szVarName ); + uint (*pfnGetPlayerWONId)( edict_t *e ); // returns the server assigned WONid for this player. useful for logging frags, etc. returns -1 if the edict couldn't be found in the list of clients void (*pfnInfo_RemoveKey)( char *s, char *key ); const char *(*pfnGetPhysicsKeyValue)( const edict_t *pClient, const char *key ); void (*pfnSetPhysicsKeyValue)( const edict_t *pClient, const char *key, const char *value ); const char *(*pfnGetPhysicsInfoString)( const edict_t *pClient ); word (*pfnPrecacheEvent)( int type, const char *psz ); void (*pfnPlaybackEvent)( int flags, const edict_t *pInvoker, word eventindex, float delay, float *origin, float *angles, float fparam1, float fparam2, int iparam1, int iparam2, int bparam1, int bparam2 ); - byte *(*pfnSetFatPVS)( const float *org, int portal ); - byte *(*pfnSetFatPAS)( const float *org, int portal ); + byte *(*pfnSetFatPVS)( const float *org ); + byte *(*pfnSetFatPAS)( const float *org ); int (*pfnCheckVisibility)( const edict_t *entity, byte *pset ); void (*pfnDeltaSetField) ( struct delta_s *pFields, const char *fieldname ); void (*pfnDeltaUnsetField)( struct delta_s *pFields, const char *fieldname ); @@ -194,15 +199,13 @@ typedef struct enginefuncs_s void (*pfnDeltaSetFieldByIndex)( struct delta_s *pFields, int fieldNumber ); void (*pfnDeltaUnsetFieldByIndex)( struct delta_s *pFields, int fieldNumber ); void (*pfnSetGroupMask)( int mask, int op ); - void (*pfnDropClient)( int clientIndex ); // was pfnCreateInstancedBaseline - void (*pfnHostError)( const char *szFmt, ... ); // was pfnCvar_DirectSet - char *(*pfnParseToken)( const char **data_p ); // was pfnForceUnmodified + int (*pfnCreateInstancedBaseline)( int classname, struct entity_state_s *baseline ); + void (*pfnCvar_DirectSet)( cvar_t *var, char *value ); + void (*pfnForceUnmodified)( FORCE_TYPE type, float *mins, float *maxs, const char *filename ); void (*pfnGetPlayerStats)( const edict_t *pClient, int *ping, int *packet_loss ); void (*pfnAddServerCommand)( const char *cmd_name, void (*function)(void), const char *cmd_desc ); - void* (*pfnLoadLibrary)( const char *name ); // was pfnVoice_GetClientListening - void (*pfnFreeLibrary)( void *hInstance ); // was pfnVoice_SetClientListening const char *(*pfnGetPlayerAuthId)( edict_t *e ); - // ONLY ADD NEW FUNCTIONS TO THE END OF THIS STRUCT. INTERFACE VERSION IS FROZEN AT 140 + // ONLY ADD NEW FUNCTIONS TO THE END OF THIS STRUCT. INTERFACE VERSION IS FROZEN AT 138 } enginefuncs_t; // passed to pfnKeyValue @@ -224,8 +227,8 @@ typedef struct typedef struct { - int id; // ENG ordinal ID of this entity (used for entity <--> pointer conversions) - edict_t *pent; // ENG pointer to the in-game entity + int id; // ordinal ID of this entity (used for entity <--> pointer conversions) + edict_t *pent; // pointer to the in-game entity int location; // offset from the base data of this entity int size; // byte size of this entity's data @@ -243,24 +246,24 @@ typedef struct typedef struct saverestore_s { - char *pBaseData; // ENG start of all entity save data + char *pBaseData; // start of all entity save data char *pCurrentData; // current buffer pointer for sequential access int size; // current data size - int bufferSize; // ENG total space for data (Valve used 512 kb's) + int bufferSize; // total space for data (Valve used 512 kb's) int tokenSize; // always equal 0 (probably not used) - int tokenCount; // ENG number of elements in the pTokens table (Valve used 4096 tokens) - char **pTokens; // ENG hash table of entity strings (sparse) - int currentIndex; // ENG holds a global entity table ID - int tableCount; // ENG number of elements in the entity table (numEntities) + int tokenCount; // number of elements in the pTokens table (Valve used 4096 tokens) + char **pTokens; // hash table of entity strings (sparse) + int currentIndex; // holds a global entity table ID + int tableCount; // number of elements in the entity table (numEntities) int connectionCount; // number of elements in the levelList[] - ENTITYTABLE *pTable; // ENG array of ENTITYTABLE elements (1 for each entity) + ENTITYTABLE *pTable; // array of ENTITYTABLE elements (1 for each entity) LEVELLIST levelList[MAX_LEVEL_CONNECTIONS]; // list of connections from this level // smooth transition - int fUseLandmark; // ENG + int fUseLandmark; char szLandmarkName[20]; // landmark we'll spawn near in next level vec3_t vecLandmarkOffset; // for landmark transitions - float time; // ENG current sv.time + float time; // current sv.time char szCurrentMapName[32];// To check global entities } SAVERESTOREDATA; @@ -284,10 +287,7 @@ typedef enum _fieldtypes FIELD_TIME, // a floating point time (these are fixed up automatically too!) FIELD_MODELNAME, // engine string that is a model name (needs precache) FIELD_SOUNDNAME, // engine string that is a sound name (needs precache) - FIELD_RANGE, // Min and Max range for generate random value - FIELD_INTEGER64, // long integer - FIELD_DOUBLE, // float with double precision - + FIELD_TYPECOUNT, // MUST BE LAST } FIELDTYPE; @@ -343,40 +343,53 @@ typedef struct void (*pfnPlayerPostThink)( edict_t *pEntity ); void (*pfnStartFrame)( void ); - int (*pfnCreate)( edict_t *pent, const char *szName ); // was pfnParmsNewLevel + void (*pfnParmsNewLevel)( void ); void (*pfnParmsChangeLevel)( void ); // returns string describing current .dll. E.g., TeamFotrress 2, Half-Life const char *(*pfnGetGameDescription)( void ); - int (*pfnPhysicsEntity)( edict_t *pEntity ); // was pfnPlayerCustomization + + // Notify dll about a player customization. (completely ignored in Xash3D) + void (*pfnPlayerCustomization)( edict_t *pEntity, void *pUnused ); // Spectator funcs void (*pfnSpectatorConnect)( edict_t *pEntity ); void (*pfnSpectatorDisconnect)( edict_t *pEntity ); void (*pfnSpectatorThink)( edict_t *pEntity ); - int (*pfnClassifyEdict)( edict_t *pentToClassify ); // was pfnSys_Error + // Notify game .dll that engine is going to shut down. Allows mod authors to set a breakpoint. + void (*pfnSys_Error)( const char *error_string ); void (*pfnPM_Move)( struct playermove_s *ppmove, int server ); void (*pfnPM_Init)( struct playermove_s *ppmove ); - char (*pfnPM_FindTextureType)( const char *name ); - void (*pfnSetupVisibility)( edict_t *pViewEntity, edict_t *pClient, byte **pvs, byte **pas, int portal ); + char (*pfnPM_FindTextureType)( char *name ); + void (*pfnSetupVisibility)( edict_t *pViewEntity, edict_t *pClient, byte **pvs, byte **pas ); void (*pfnUpdateClientData)( const edict_t *ent, int sendweapons, struct clientdata_s *cd ); - int (*pfnAddToFullPack)( struct entity_state_s *state, edict_t *pView, edict_t *pHost, edict_t *pEdict, int hostflags, byte *pSet ); - void (*pfnCreateBaseline)( struct entity_state_s *baseline, edict_t *entity, int playermodelindex ); + + int (*pfnAddToFullPack)( struct entity_state_s *state, int e, edict_t *ent, edict_t *host, int hostflags, int player, byte *pSet ); + void (*pfnCreateBaseline)( int player, int eindex, struct entity_state_s *baseline, edict_t *entity, int playermodelindex, vec3_t player_mins, vec3_t player_maxs ); void (*pfnRegisterEncoders)( void ); int (*pfnGetWeaponData)( edict_t *player, struct weapon_data_s *info ); - void (*pfnCmdStart)( const edict_t *player, const usercmd_t *cmd, unsigned int random_seed ); + void (*pfnCmdStart)( const edict_t *player, const struct usercmd_s *cmd, unsigned int random_seed ); void (*pfnCmdEnd)( const edict_t *player ); - // these funcs come from GetEntityAPI2 and replace some funcs from GetEntityAPI - void (*pfnOnFreeEntPrivateData)( edict_t *pEnt ); // was pfnConnectionlessPacket + int (*pfnConnectionlessPacket)( const struct netadr_s *net_from, const char *args, char *response_buffer, int *response_buffer_size ); int (*pfnGetHullBounds)( int hullnumber, float *mins, float *maxs ); - int (*pfnShouldCollide)( edict_t *pTouch, edict_t *pOther ); // was pfnCreateInstancedBaselines + void (*pfnCreateInstancedBaselines)( void ); + int (*pfnInconsistentFile)( const edict_t *player, const char *filename, char *disconnect_message ); + int (*pfnAllowLagCompensation)( void ); } DLL_FUNCTIONS; -typedef void (*GIVEFNPTRSTODLL)( enginefuncs_t* engfuncs, globalvars_t *pGlobals ); -typedef int (*APIFUNCTION)( DLL_FUNCTIONS *pFunctionTable, int interfaceVersion ); -typedef void (*LINK_ENTITY_FUNC)( entvars_t *pev ); +typedef struct +{ + void (*pfnOnFreeEntPrivateData)( edict_t *pEnt ); // ñalled right before the object's memory is freed. + void (*pfnGameShutdown)( void ); + int (*pfnShouldCollide)( edict_t *pentTouched, edict_t *pentOther ); + int (*pfnCreate)( edict_t *pent, const char *szName ); // passed through pfnCreate (0 is attempt to create, -1 is reject) + int (*pfnPhysicsEntity)( edict_t *pEntity ); // run custom physics for each entity (return 0 to use engine physic) +} NEW_DLL_FUNCTIONS; -#endif//SVGAME_API_H \ No newline at end of file +typedef int (*APIFUNCTION)( DLL_FUNCTIONS *pFunctionTable, int interfaceVersion ); +typedef int (*APIFUNCTION2)( DLL_FUNCTIONS *pFunctionTable, int *interfaceVersion ); + +#endif//EIFACE_H \ No newline at end of file diff --git a/common/entity_state.h b/common/entity_state.h index c56eddde..08052848 100644 --- a/common/entity_state.h +++ b/common/entity_state.h @@ -5,41 +5,15 @@ #ifndef ENTITY_STATE_H #define ENTITY_STATE_H -// engine edict types that shared across network -typedef enum -{ - ED_SPAWNED = 0, // this entity requris to set own type with SV_ClassifyEdict - ED_WORLDSPAWN, // this is a worldspawn - ED_STATIC, // this is a logic without model or entity with static model - ED_AMBIENT, // this is entity emitted ambient sounds only - ED_NORMAL, // normal entity with model (and\or) sound - ED_BSPBRUSH, // brush entity (a part of level) - ED_CLIENT, // this is a client entity - ED_MONSTER, // monster or bot (generic npc with AI) - ED_TEMPENTITY, // this edict will be removed on server when "lifetime" exceeds - ED_BEAM, // laser beam (needs to recalculate pvs and frustum) - ED_MOVER, // func_train, func_door and another bsp or mdl movers - ED_VIEWMODEL, // client or bot viewmodel (for spectating) - ED_RIGIDBODY, // simulated physic - ED_TRIGGER, // just for sorting on a server - ED_PORTAL, // realtime portal or mirror brush or model - ED_SKYPORTAL, // realtime 3D-sky camera - ED_SCREEN, // realtime monitor (like portal but without perspective) - ED_MAXTYPES, -} edtype_t; - -// entity_state_t->ed_flags -#define ESF_LINKEDICT BIT( 0 ) // needs to relink edict on client -#define ESF_NODELTA BIT( 1 ) // force no delta frame -#define ESF_NO_PREDICTION BIT( 2 ) // e.g. teleport time - typedef struct entity_state_s { - // engine specific + // Fields which are filled in by routines outside of delta compression + int entityType; // hint for engine int number; // edict index - edtype_t ed_type; // edict type - string_t classname; // edict classname - int ed_flags; // engine clearing this at end of server frame + float msg_time; + + // Message number last time the player/entity state was updated. + int messagenum; // Fields which can be transitted and reconstructed over the network stream vec3_t origin; @@ -113,10 +87,15 @@ typedef struct entity_state_s vec3_t vuser3; vec3_t vuser4; - // FIXME: old xash variables needs to be removed - int flags; + // xash shared strings + char classname[32]; + char targetname[32]; + char target[32]; + char netname[32]; } entity_state_t; +#include "pm_info.h" + typedef struct clientdata_s { vec3_t origin; @@ -153,7 +132,7 @@ typedef struct clientdata_s int tfstate; int pushmsec; int deadflag; - char physinfo[512]; // MAX_PHYSINFO_STRING + char physinfo[MAX_PHYSINFO_STRING]; // for mods int iuser1; @@ -171,4 +150,13 @@ typedef struct clientdata_s } clientdata_t; +#include "weaponinfo.h" + +typedef struct local_state_s +{ + entity_state_t playerstate; + clientdata_t client; + weapon_data_t weapondata[32]; // WEAPON_BACKUP +} local_state_t; + #endif//ENTITY_STATE_H \ No newline at end of file diff --git a/common/entity_types.h b/common/entity_types.h new file mode 100644 index 00000000..a570f93f --- /dev/null +++ b/common/entity_types.h @@ -0,0 +1,17 @@ +//======================================================================= +// Copyright XashXT Group 2010 © +// entity_types.h - shared entity types +//======================================================================= +#ifndef ENTITY_TYPES_H +#define ENTITY_TYPES_H + +#define ET_NORMAL 0 +#define ET_PLAYER 1 +#define ET_TEMPENTITY 2 +#define ET_BEAM 3 +#define ET_FRAGMENTED 4 // BMODEL or SPRITE that was split across BSP nodes +#define ET_VIEWENTITY 5 // special case for firstperson viewmodel +#define ET_PORTAL 6 // this is portal surface +#define ET_SKYPORTAL 7 // env_sky use this + +#endif//ENTITY_TYPES_H \ No newline at end of file diff --git a/common/game_shared.h b/common/game_shared.h deleted file mode 100644 index a5877dce..00000000 --- a/common/game_shared.h +++ /dev/null @@ -1,30 +0,0 @@ -//======================================================================= -// Copyright XashXT Group 2009 © -// game_shared.h - user constants -//======================================================================= -#ifndef GAME_SHARED_H -#define GAME_SHARED_H - -#define HUD_PRINTNOTIFY 1 -#define HUD_PRINTCONSOLE 2 -#define HUD_PRINTTALK 3 -#define HUD_PRINTCENTER 4 - -#define MAX_WEAPONS 32 -#define WEAPON_ALLWEAPONS (~(1<cTasks ) + { + return TRUE; + } + + return FALSE; +} + +//========================================================= +// ChangeSchedule - replaces the monster's schedule pointer +// with the passed pointer, and sets the ScheduleIndex back +// to 0 +//========================================================= +void CBaseMonster :: ChangeSchedule ( Schedule_t *pNewSchedule ) +{ + ASSERT( pNewSchedule != NULL ); + + m_pSchedule = pNewSchedule; + m_iScheduleIndex = 0; + m_iTaskStatus = TASKSTATUS_NEW; + m_afConditions = 0;// clear all of the conditions + m_failSchedule = SCHED_NONE; + + if ( m_pSchedule->iInterruptMask & bits_COND_HEAR_SOUND && !(m_pSchedule->iSoundMask) ) + { + ALERT ( at_aiconsole, "COND_HEAR_SOUND with no sound mask!\n" ); + } + else if ( m_pSchedule->iSoundMask && !(m_pSchedule->iInterruptMask & bits_COND_HEAR_SOUND) ) + { + ALERT ( at_aiconsole, "Sound mask without COND_HEAR_SOUND!\n" ); + } + +#if _DEBUG + if ( !ScheduleFromName( pNewSchedule->pName ) ) + { + ALERT( at_console, "Schedule %s not in table!!!\n", pNewSchedule->pName ); + } +#endif + +// this is very useful code if you can isolate a test case in a level with a single monster. It will notify +// you of every schedule selection the monster makes. +#if 0 + if ( FClassnameIs( pev, "monster_human_grunt" ) ) + { + Task_t *pTask = GetTask(); + + if ( pTask ) + { + const char *pName = NULL; + + if ( m_pSchedule ) + { + pName = m_pSchedule->pName; + } + else + { + pName = "No Schedule"; + } + + if ( !pName ) + { + pName = "Unknown"; + } + + ALERT( at_aiconsole, "%s: picked schedule %s\n", STRING( pev->classname ), pName ); + } + } +#endif// 0 + +} + +//========================================================= +// NextScheduledTask - increments the ScheduleIndex +//========================================================= +void CBaseMonster :: NextScheduledTask ( void ) +{ + ASSERT( m_pSchedule != NULL ); + + m_iTaskStatus = TASKSTATUS_NEW; + m_iScheduleIndex++; + + if ( FScheduleDone() ) + { + // just completed last task in schedule, so make it invalid by clearing it. + SetConditions( bits_COND_SCHEDULE_DONE ); + //ClearSchedule(); + } +} + +//========================================================= +// IScheduleFlags - returns an integer with all Conditions +// bits that are currently set and also set in the current +// schedule's Interrupt mask. +//========================================================= +int CBaseMonster :: IScheduleFlags ( void ) +{ + if( !m_pSchedule ) + { + return 0; + } + + // strip off all bits excepts the ones capable of breaking this schedule. + return m_afConditions & m_pSchedule->iInterruptMask; +} + +//========================================================= +// FScheduleValid - returns TRUE as long as the current +// schedule is still the proper schedule to be executing, +// taking into account all conditions +//========================================================= +BOOL CBaseMonster :: FScheduleValid ( void ) +{ + if ( m_pSchedule == NULL ) + { + // schedule is empty, and therefore not valid. + return FALSE; + } + + if ( HasConditions( m_pSchedule->iInterruptMask | bits_COND_SCHEDULE_DONE | bits_COND_TASK_FAILED ) ) + { +#ifdef DEBUG + if ( HasConditions ( bits_COND_TASK_FAILED ) && m_failSchedule == SCHED_NONE ) + { + // fail! Send a visual indicator. + ALERT ( at_aiconsole, "Schedule: %s Failed\n", m_pSchedule->pName ); + + Vector tmp = pev->origin; + tmp.z = pev->absmax.z + 16; + UTIL_Sparks( tmp ); + } +#endif // DEBUG + + // some condition has interrupted the schedule, or the schedule is done + return FALSE; + } + + return TRUE; +} + +//========================================================= +// MaintainSchedule - does all the per-think schedule maintenance. +// ensures that the monster leaves this function with a valid +// schedule! +//========================================================= +void CBaseMonster :: MaintainSchedule ( void ) +{ + Schedule_t *pNewSchedule; + int i; + + // UNDONE: Tune/fix this 10... This is just here so infinite loops are impossible + for ( i = 0; i < 10; i++ ) + { + if ( m_pSchedule != NULL && TaskIsComplete() ) + { + NextScheduledTask(); + } + + // validate existing schedule + if ( !FScheduleValid() || m_MonsterState != m_IdealMonsterState ) + { + // if we come into this block of code, the schedule is going to have to be changed. + // if the previous schedule was interrupted by a condition, GetIdealState will be + // called. Else, a schedule finished normally. + + // Notify the monster that his schedule is changing + ScheduleChange(); + + // Call GetIdealState if we're not dead and one or more of the following... + // - in COMBAT state with no enemy (it died?) + // - conditions bits (excluding SCHEDULE_DONE) indicate interruption, + // - schedule is done but schedule indicates it wants GetIdealState called + // after successful completion (by setting bits_COND_SCHEDULE_DONE in iInterruptMask) + // DEAD & SCRIPT are not suggestions, they are commands! + if ( m_IdealMonsterState != MONSTERSTATE_DEAD && + (m_IdealMonsterState != MONSTERSTATE_SCRIPT || m_IdealMonsterState == m_MonsterState) ) + { + if ( (m_afConditions && !HasConditions(bits_COND_SCHEDULE_DONE)) || + (m_pSchedule && (m_pSchedule->iInterruptMask & bits_COND_SCHEDULE_DONE)) || + ((m_MonsterState == MONSTERSTATE_COMBAT) && (m_hEnemy == NULL)) ) + { + GetIdealState(); + } + } + if ( HasConditions( bits_COND_TASK_FAILED ) && m_MonsterState == m_IdealMonsterState ) + { + if ( m_failSchedule != SCHED_NONE ) + pNewSchedule = GetScheduleOfType( m_failSchedule ); + else + pNewSchedule = GetScheduleOfType( SCHED_FAIL ); + // schedule was invalid because the current task failed to start or complete + ALERT ( at_aiconsole, "Schedule Failed at %d!\n", m_iScheduleIndex ); + ChangeSchedule( pNewSchedule ); + } + else + { + SetState( m_IdealMonsterState ); + if ( m_MonsterState == MONSTERSTATE_SCRIPT || m_MonsterState == MONSTERSTATE_DEAD ) + pNewSchedule = CBaseMonster::GetSchedule(); + else + pNewSchedule = GetSchedule(); + ChangeSchedule( pNewSchedule ); + } + } + + if ( m_iTaskStatus == TASKSTATUS_NEW ) + { + Task_t *pTask = GetTask(); + ASSERT( pTask != NULL ); + TaskBegin(); + StartTask( pTask ); + } + + // UNDONE: Twice?!!! + if ( m_Activity != m_IdealActivity ) + { + SetActivity ( m_IdealActivity ); + } + + if ( !TaskIsComplete() && m_iTaskStatus != TASKSTATUS_NEW ) + break; + } + + if ( TaskIsRunning() ) + { + Task_t *pTask = GetTask(); + ASSERT( pTask != NULL ); + RunTask( pTask ); + } + + // UNDONE: We have to do this so that we have an animation set to blend to if RunTask changes the animation + // RunTask() will always change animations at the end of a script! + // Don't do this twice + if ( m_Activity != m_IdealActivity ) + { + SetActivity ( m_IdealActivity ); + } +} + +//========================================================= +// RunTask +//========================================================= +void CBaseMonster :: RunTask ( Task_t *pTask ) +{ + switch ( pTask->iTask ) + { + case TASK_TURN_RIGHT: + case TASK_TURN_LEFT: + { + ChangeYaw( pev->yaw_speed ); + + if ( FacingIdeal() ) + { + TaskComplete(); + } + break; + } + + case TASK_PLAY_SEQUENCE_FACE_ENEMY: + case TASK_PLAY_SEQUENCE_FACE_TARGET: + { + CBaseEntity *pTarget; + + if ( pTask->iTask == TASK_PLAY_SEQUENCE_FACE_TARGET ) + pTarget = m_hTargetEnt; + else + pTarget = m_hEnemy; + if ( pTarget ) + { + pev->ideal_yaw = UTIL_VecToYaw( pTarget->pev->origin - pev->origin ); + ChangeYaw( pev->yaw_speed ); + } + if ( m_fSequenceFinished ) + TaskComplete(); + } + break; + + case TASK_PLAY_SEQUENCE: + case TASK_PLAY_ACTIVE_IDLE: + { + if ( m_fSequenceFinished ) + { + TaskComplete(); + } + break; + } + + + case TASK_FACE_ENEMY: + { + MakeIdealYaw( m_vecEnemyLKP ); + + ChangeYaw( pev->yaw_speed ); + + if ( FacingIdeal() ) + { + TaskComplete(); + } + break; + } + case TASK_FACE_HINTNODE: + case TASK_FACE_LASTPOSITION: + case TASK_FACE_TARGET: + case TASK_FACE_IDEAL: + case TASK_FACE_ROUTE: + { + ChangeYaw( pev->yaw_speed ); + + if ( FacingIdeal() ) + { + TaskComplete(); + } + break; + } + case TASK_WAIT_PVS: + { + if ( !FNullEnt(FIND_CLIENT_IN_PVS(edict())) ) + { + TaskComplete(); + } + break; + } + case TASK_WAIT_INDEFINITE: + { + // don't do anything. + break; + } + case TASK_WAIT: + case TASK_WAIT_RANDOM: + { + if ( gpGlobals->time >= m_flWaitFinished ) + { + TaskComplete(); + } + break; + } + case TASK_WAIT_FACE_ENEMY: + { + MakeIdealYaw ( m_vecEnemyLKP ); + ChangeYaw( pev->yaw_speed ); + + if ( gpGlobals->time >= m_flWaitFinished ) + { + TaskComplete(); + } + break; + } + case TASK_MOVE_TO_TARGET_RANGE: + { + float distance; + + if ( m_hTargetEnt == NULL ) + TaskFail(); + else + { + distance = ( m_vecMoveGoal - pev->origin ).Length2D(); + // Re-evaluate when you think your finished, or the target has moved too far + if ( (distance < pTask->flData) || (m_vecMoveGoal - m_hTargetEnt->pev->origin).Length() > pTask->flData * 0.5 ) + { + m_vecMoveGoal = m_hTargetEnt->pev->origin; + distance = ( m_vecMoveGoal - pev->origin ).Length2D(); + FRefreshRoute(); + } + + // Set the appropriate activity based on an overlapping range + // overlap the range to prevent oscillation + if ( distance < pTask->flData ) + { + TaskComplete(); + RouteClear(); // Stop moving + } + else if ( distance < 190 && m_movementActivity != ACT_WALK ) + m_movementActivity = ACT_WALK; + else if ( distance >= 270 && m_movementActivity != ACT_RUN ) + m_movementActivity = ACT_RUN; + } + + break; + } + case TASK_WAIT_FOR_MOVEMENT: + { + if (MovementIsComplete()) + { + TaskComplete(); + RouteClear(); // Stop moving + } + break; + } + case TASK_DIE: + { + if ( m_fSequenceFinished && pev->frame >= 255 ) + { + pev->deadflag = DEAD_DEAD; + + SetThink ( NULL ); + StopAnimation(); + + if ( !BBoxFlat() ) + { + // a bit of a hack. If a corpses' bbox is positioned such that being left solid so that it can be attacked will + // block the player on a slope or stairs, the corpse is made nonsolid. +// pev->solid = SOLID_NOT; + UTIL_SetSize ( pev, Vector ( -4, -4, 0 ), Vector ( 4, 4, 1 ) ); + } + else // !!!HACKHACK - put monster in a thin, wide bounding box until we fix the solid type/bounding volume problem + UTIL_SetSize ( pev, Vector ( pev->mins.x, pev->mins.y, pev->mins.z ), Vector ( pev->maxs.x, pev->maxs.y, pev->mins.z + 1 ) ); + + if ( ShouldFadeOnDeath() ) + { + // this monster was created by a monstermaker... fade the corpse out. + SUB_StartFadeOut(); + } + else + { + // body is gonna be around for a while, so have it stink for a bit. + CSoundEnt::InsertSound ( bits_SOUND_CARCASS, pev->origin, 384, 30 ); + } + } + break; + } + case TASK_RANGE_ATTACK1_NOTURN: + case TASK_MELEE_ATTACK1_NOTURN: + case TASK_MELEE_ATTACK2_NOTURN: + case TASK_RANGE_ATTACK2_NOTURN: + case TASK_RELOAD_NOTURN: + { + if ( m_fSequenceFinished ) + { + m_Activity = ACT_RESET; + TaskComplete(); + } + break; + } + case TASK_RANGE_ATTACK1: + case TASK_MELEE_ATTACK1: + case TASK_MELEE_ATTACK2: + case TASK_RANGE_ATTACK2: + case TASK_SPECIAL_ATTACK1: + case TASK_SPECIAL_ATTACK2: + case TASK_RELOAD: + { + MakeIdealYaw ( m_vecEnemyLKP ); + ChangeYaw ( pev->yaw_speed ); + + if ( m_fSequenceFinished ) + { + m_Activity = ACT_RESET; + TaskComplete(); + } + break; + } + case TASK_SMALL_FLINCH: + { + if ( m_fSequenceFinished ) + { + TaskComplete(); + } + } + break; + case TASK_WAIT_FOR_SCRIPT: + { + if ( m_pCine->m_iDelay <= 0 && gpGlobals->time >= m_pCine->m_startTime ) + { + TaskComplete(); + m_pCine->StartSequence( (CBaseMonster *)this, m_pCine->m_iszPlay, TRUE ); + if ( m_fSequenceFinished ) + ClearSchedule(); + pev->framerate = 1.0; + //ALERT( at_aiconsole, "Script %s has begun for %s\n", STRING( m_pCine->m_iszPlay ), STRING(pev->classname) ); + } + break; + } + case TASK_PLAY_SCRIPT: + { + if (m_fSequenceFinished) + { + m_pCine->SequenceDone( this ); + } + break; + } + } +} + +//========================================================= +// SetTurnActivity - measures the difference between the way +// the monster is facing and determines whether or not to +// select one of the 180 turn animations. +//========================================================= +void CBaseMonster :: SetTurnActivity ( void ) +{ + float flYD; + flYD = FlYawDiff(); + + if ( flYD <= -45 && LookupActivity ( ACT_TURN_RIGHT ) != ACTIVITY_NOT_AVAILABLE ) + {// big right turn + m_IdealActivity = ACT_TURN_RIGHT; + } + else if ( flYD > 45 && LookupActivity ( ACT_TURN_LEFT ) != ACTIVITY_NOT_AVAILABLE ) + {// big left turn + m_IdealActivity = ACT_TURN_LEFT; + } +} + +//========================================================= +// Start task - selects the correct activity and performs +// any necessary calculations to start the next task on the +// schedule. +//========================================================= +void CBaseMonster :: StartTask ( Task_t *pTask ) +{ + switch ( pTask->iTask ) + { + case TASK_TURN_RIGHT: + { + float flCurrentYaw; + + flCurrentYaw = UTIL_AngleMod( pev->angles.y ); + pev->ideal_yaw = UTIL_AngleMod( flCurrentYaw - pTask->flData ); + SetTurnActivity(); + break; + } + case TASK_TURN_LEFT: + { + float flCurrentYaw; + + flCurrentYaw = UTIL_AngleMod( pev->angles.y ); + pev->ideal_yaw = UTIL_AngleMod( flCurrentYaw + pTask->flData ); + SetTurnActivity(); + break; + } + case TASK_REMEMBER: + { + Remember ( (int)pTask->flData ); + TaskComplete(); + break; + } + case TASK_FORGET: + { + Forget ( (int)pTask->flData ); + TaskComplete(); + break; + } + case TASK_FIND_HINTNODE: + { + m_iHintNode = FindHintNode(); + + if ( m_iHintNode != NO_NODE ) + { + TaskComplete(); + } + else + { + TaskFail(); + } + break; + } + case TASK_STORE_LASTPOSITION: + { + m_vecLastPosition = pev->origin; + TaskComplete(); + break; + } + case TASK_CLEAR_LASTPOSITION: + { + m_vecLastPosition = g_vecZero; + TaskComplete(); + break; + } + case TASK_CLEAR_HINTNODE: + { + m_iHintNode = NO_NODE; + TaskComplete(); + break; + } + case TASK_STOP_MOVING: + { + if ( m_IdealActivity == m_movementActivity ) + { + m_IdealActivity = GetStoppedActivity(); + } + + RouteClear(); + TaskComplete(); + break; + } + case TASK_PLAY_SEQUENCE_FACE_ENEMY: + case TASK_PLAY_SEQUENCE_FACE_TARGET: + case TASK_PLAY_SEQUENCE: + { + m_IdealActivity = ( Activity )( int )pTask->flData; + break; + } + case TASK_PLAY_ACTIVE_IDLE: + { + // monsters verify that they have a sequence for the node's activity BEFORE + // moving towards the node, so it's ok to just set the activity without checking here. + m_IdealActivity = ( Activity )WorldGraph.m_pNodes[ m_iHintNode ].m_sHintActivity; + break; + } + case TASK_SET_SCHEDULE: + { + Schedule_t *pNewSchedule; + + pNewSchedule = GetScheduleOfType( (int)pTask->flData ); + + if ( pNewSchedule ) + { + ChangeSchedule( pNewSchedule ); + } + else + { + TaskFail(); + } + + break; + } + case TASK_FIND_NEAR_NODE_COVER_FROM_ENEMY: + { + if ( m_hEnemy == NULL ) + { + TaskFail(); + return; + } + + if ( FindCover( m_hEnemy->pev->origin, m_hEnemy->pev->view_ofs, 0, pTask->flData ) ) + { + // try for cover farther than the FLData from the schedule. + TaskComplete(); + } + else + { + // no coverwhatsoever. + TaskFail(); + } + break; + } + case TASK_FIND_FAR_NODE_COVER_FROM_ENEMY: + { + if ( m_hEnemy == NULL ) + { + TaskFail(); + return; + } + + if ( FindCover( m_hEnemy->pev->origin, m_hEnemy->pev->view_ofs, pTask->flData, CoverRadius() ) ) + { + // try for cover farther than the FLData from the schedule. + TaskComplete(); + } + else + { + // no coverwhatsoever. + TaskFail(); + } + break; + } + case TASK_FIND_NODE_COVER_FROM_ENEMY: + { + if ( m_hEnemy == NULL ) + { + TaskFail(); + return; + } + + if ( FindCover( m_hEnemy->pev->origin, m_hEnemy->pev->view_ofs, 0, CoverRadius() ) ) + { + // try for cover farther than the FLData from the schedule. + TaskComplete(); + } + else + { + // no coverwhatsoever. + TaskFail(); + } + break; + } + case TASK_FIND_COVER_FROM_ENEMY: + { + entvars_t *pevCover; + + if ( m_hEnemy == NULL ) + { + // Find cover from self if no enemy available + pevCover = pev; +// TaskFail(); +// return; + } + else + pevCover = m_hEnemy->pev; + + if ( FindLateralCover( pevCover->origin, pevCover->view_ofs ) ) + { + // try lateral first + m_flMoveWaitFinished = gpGlobals->time + pTask->flData; + TaskComplete(); + } + else if ( FindCover( pevCover->origin, pevCover->view_ofs, 0, CoverRadius() ) ) + { + // then try for plain ole cover + m_flMoveWaitFinished = gpGlobals->time + pTask->flData; + TaskComplete(); + } + else + { + // no coverwhatsoever. + TaskFail(); + } + break; + } + case TASK_FIND_COVER_FROM_ORIGIN: + { + if ( FindCover( pev->origin, pev->view_ofs, 0, CoverRadius() ) ) + { + // then try for plain ole cover + m_flMoveWaitFinished = gpGlobals->time + pTask->flData; + TaskComplete(); + } + else + { + // no cover! + TaskFail(); + } + } + break; + case TASK_FIND_COVER_FROM_BEST_SOUND: + { + CSound *pBestSound; + + pBestSound = PBestSound(); + + ASSERT( pBestSound != NULL ); + /* + if ( pBestSound && FindLateralCover( pBestSound->m_vecOrigin, g_vecZero ) ) + { + // try lateral first + m_flMoveWaitFinished = gpGlobals->time + pTask->flData; + TaskComplete(); + } + */ + + if ( pBestSound && FindCover( pBestSound->m_vecOrigin, g_vecZero, pBestSound->m_iVolume, CoverRadius() ) ) + { + // then try for plain ole cover + m_flMoveWaitFinished = gpGlobals->time + pTask->flData; + TaskComplete(); + } + else + { + // no coverwhatsoever. or no sound in list + TaskFail(); + } + break; + } + case TASK_FACE_HINTNODE: + { + pev->ideal_yaw = WorldGraph.m_pNodes[ m_iHintNode ].m_flHintYaw; + SetTurnActivity(); + break; + } + + case TASK_FACE_LASTPOSITION: + MakeIdealYaw ( m_vecLastPosition ); + SetTurnActivity(); + break; + + case TASK_FACE_TARGET: + if ( m_hTargetEnt != NULL ) + { + MakeIdealYaw ( m_hTargetEnt->pev->origin ); + SetTurnActivity(); + } + else + TaskFail(); + break; + case TASK_FACE_ENEMY: + { + MakeIdealYaw ( m_vecEnemyLKP ); + SetTurnActivity(); + break; + } + case TASK_FACE_IDEAL: + { + SetTurnActivity(); + break; + } + case TASK_FACE_ROUTE: + { + if (FRouteClear()) + { + ALERT(at_aiconsole, "No route to face!\n"); + TaskFail(); + } + else + { + MakeIdealYaw(m_Route[m_iRouteIndex].vecLocation); + SetTurnActivity(); + } + break; + } + case TASK_WAIT_PVS: + case TASK_WAIT_INDEFINITE: + { + // don't do anything. + break; + } + case TASK_WAIT: + case TASK_WAIT_FACE_ENEMY: + {// set a future time that tells us when the wait is over. + m_flWaitFinished = gpGlobals->time + pTask->flData; + break; + } + case TASK_WAIT_RANDOM: + {// set a future time that tells us when the wait is over. + m_flWaitFinished = gpGlobals->time + RANDOM_FLOAT( 0.1, pTask->flData ); + break; + } + case TASK_MOVE_TO_TARGET_RANGE: + { + if ( (m_hTargetEnt->pev->origin - pev->origin).Length() < 1 ) + TaskComplete(); + else + { + m_vecMoveGoal = m_hTargetEnt->pev->origin; + if ( !MoveToTarget( ACT_WALK, 2 ) ) + TaskFail(); + } + break; + } + case TASK_RUN_TO_TARGET: + case TASK_WALK_TO_TARGET: + { + Activity newActivity; + + if ( (m_hTargetEnt->pev->origin - pev->origin).Length() < 1 ) + TaskComplete(); + else + { + if ( pTask->iTask == TASK_WALK_TO_TARGET ) + newActivity = ACT_WALK; + else + newActivity = ACT_RUN; + // This monster can't do this! + if ( LookupActivity( newActivity ) == ACTIVITY_NOT_AVAILABLE ) + TaskComplete(); + else + { + if ( m_hTargetEnt == NULL || !MoveToTarget( newActivity, 2 ) ) + { + TaskFail(); + ALERT( at_aiconsole, "%s Failed to reach target!!!\n", STRING(pev->classname) ); + RouteClear(); + } + } + } + TaskComplete(); + break; + } + case TASK_CLEAR_MOVE_WAIT: + { + m_flMoveWaitFinished = gpGlobals->time; + TaskComplete(); + break; + } + case TASK_MELEE_ATTACK1_NOTURN: + case TASK_MELEE_ATTACK1: + { + m_IdealActivity = ACT_MELEE_ATTACK1; + break; + } + case TASK_MELEE_ATTACK2_NOTURN: + case TASK_MELEE_ATTACK2: + { + m_IdealActivity = ACT_MELEE_ATTACK2; + break; + } + case TASK_RANGE_ATTACK1_NOTURN: + case TASK_RANGE_ATTACK1: + { + m_IdealActivity = ACT_RANGE_ATTACK1; + break; + } + case TASK_RANGE_ATTACK2_NOTURN: + case TASK_RANGE_ATTACK2: + { + m_IdealActivity = ACT_RANGE_ATTACK2; + break; + } + case TASK_RELOAD_NOTURN: + case TASK_RELOAD: + { + m_IdealActivity = ACT_RELOAD; + break; + } + case TASK_SPECIAL_ATTACK1: + { + m_IdealActivity = ACT_SPECIAL_ATTACK1; + break; + } + case TASK_SPECIAL_ATTACK2: + { + m_IdealActivity = ACT_SPECIAL_ATTACK2; + break; + } + case TASK_SET_ACTIVITY: + { + m_IdealActivity = (Activity)(int)pTask->flData; + TaskComplete(); + break; + } + case TASK_GET_PATH_TO_ENEMY_LKP: + { + if ( BuildRoute ( m_vecEnemyLKP, bits_MF_TO_LOCATION, NULL ) ) + { + TaskComplete(); + } + else if (BuildNearestRoute( m_vecEnemyLKP, pev->view_ofs, 0, (m_vecEnemyLKP - pev->origin).Length() )) + { + TaskComplete(); + } + else + { + // no way to get there =( + ALERT ( at_aiconsole, "GetPathToEnemyLKP failed!!\n" ); + TaskFail(); + } + break; + } + case TASK_GET_PATH_TO_ENEMY: + { + CBaseEntity *pEnemy = m_hEnemy; + + if ( pEnemy == NULL ) + { + TaskFail(); + return; + } + + if ( BuildRoute ( pEnemy->pev->origin, bits_MF_TO_ENEMY, pEnemy ) ) + { + TaskComplete(); + } + else if (BuildNearestRoute( pEnemy->pev->origin, pEnemy->pev->view_ofs, 0, (pEnemy->pev->origin - pev->origin).Length() )) + { + TaskComplete(); + } + else + { + // no way to get there =( + ALERT ( at_aiconsole, "GetPathToEnemy failed!!\n" ); + TaskFail(); + } + break; + } + case TASK_GET_PATH_TO_ENEMY_CORPSE: + { + UTIL_MakeVectors( pev->angles ); + if ( BuildRoute ( m_vecEnemyLKP - gpGlobals->v_forward * 64, bits_MF_TO_LOCATION, NULL ) ) + { + TaskComplete(); + } + else + { + ALERT ( at_aiconsole, "GetPathToEnemyCorpse failed!!\n" ); + TaskFail(); + } + } + break; + case TASK_GET_PATH_TO_SPOT: + { + CBaseEntity *pPlayer = CBaseEntity::Instance( FIND_ENTITY_BY_CLASSNAME( NULL, "player" ) ); + if ( BuildRoute ( m_vecMoveGoal, bits_MF_TO_LOCATION, pPlayer ) ) + { + TaskComplete(); + } + else + { + // no way to get there =( + ALERT ( at_aiconsole, "GetPathToSpot failed!!\n" ); + TaskFail(); + } + break; + } + + case TASK_GET_PATH_TO_TARGET: + { + RouteClear(); + if ( m_hTargetEnt != NULL && MoveToTarget( m_movementActivity, 1 ) ) + { + TaskComplete(); + } + else + { + // no way to get there =( + ALERT ( at_aiconsole, "GetPathToSpot failed!!\n" ); + TaskFail(); + } + break; + } + case TASK_GET_PATH_TO_HINTNODE:// for active idles! + { + if ( MoveToLocation( m_movementActivity, 2, WorldGraph.m_pNodes[ m_iHintNode ].m_vecOrigin ) ) + { + TaskComplete(); + } + else + { + // no way to get there =( + ALERT ( at_aiconsole, "GetPathToHintNode failed!!\n" ); + TaskFail(); + } + break; + } + case TASK_GET_PATH_TO_LASTPOSITION: + { + m_vecMoveGoal = m_vecLastPosition; + + if ( MoveToLocation( m_movementActivity, 2, m_vecMoveGoal ) ) + { + TaskComplete(); + } + else + { + // no way to get there =( + ALERT ( at_aiconsole, "GetPathToLastPosition failed!!\n" ); + TaskFail(); + } + break; + } + case TASK_GET_PATH_TO_BESTSOUND: + { + CSound *pSound; + + pSound = PBestSound(); + + if ( pSound && MoveToLocation( m_movementActivity, 2, pSound->m_vecOrigin ) ) + { + TaskComplete(); + } + else + { + // no way to get there =( + ALERT ( at_aiconsole, "GetPathToBestSound failed!!\n" ); + TaskFail(); + } + break; + } +case TASK_GET_PATH_TO_BESTSCENT: + { + CSound *pScent; + + pScent = PBestScent(); + + if ( pScent && MoveToLocation( m_movementActivity, 2, pScent->m_vecOrigin ) ) + { + TaskComplete(); + } + else + { + // no way to get there =( + ALERT ( at_aiconsole, "GetPathToBestScent failed!!\n" ); + + TaskFail(); + } + break; + } + case TASK_RUN_PATH: + { + // UNDONE: This is in some default AI and some monsters can't run? -- walk instead? + if ( LookupActivity( ACT_RUN ) != ACTIVITY_NOT_AVAILABLE ) + { + m_movementActivity = ACT_RUN; + } + else + { + m_movementActivity = ACT_WALK; + } + TaskComplete(); + break; + } + case TASK_WALK_PATH: + { + if ( pev->movetype == MOVETYPE_FLY ) + { + m_movementActivity = ACT_FLY; + } + if ( LookupActivity( ACT_WALK ) != ACTIVITY_NOT_AVAILABLE ) + { + m_movementActivity = ACT_WALK; + } + else + { + m_movementActivity = ACT_RUN; + } + TaskComplete(); + break; + } + case TASK_STRAFE_PATH: + { + Vector2D vec2DirToPoint; + Vector2D vec2RightSide; + + // to start strafing, we have to first figure out if the target is on the left side or right side + UTIL_MakeVectors ( pev->angles ); + + vec2DirToPoint = ( m_Route[ 0 ].vecLocation - pev->origin ).Make2D().Normalize(); + vec2RightSide = gpGlobals->v_right.Make2D().Normalize(); + + if ( DotProduct ( vec2DirToPoint, vec2RightSide ) > 0 ) + { + // strafe right + m_movementActivity = ACT_STRAFE_RIGHT; + } + else + { + // strafe left + m_movementActivity = ACT_STRAFE_LEFT; + } + TaskComplete(); + break; + } + + + case TASK_WAIT_FOR_MOVEMENT: + { + if (FRouteClear()) + { + TaskComplete(); + } + break; + } + + case TASK_EAT: + { + Eat( pTask->flData ); + TaskComplete(); + break; + } + case TASK_SMALL_FLINCH: + { + m_IdealActivity = GetSmallFlinchActivity(); + break; + } + case TASK_DIE: + { + RouteClear(); + + m_IdealActivity = GetDeathActivity(); + + pev->deadflag = DEAD_DYING; + break; + } + case TASK_SOUND_WAKE: + { + AlertSound(); + TaskComplete(); + break; + } + case TASK_SOUND_DIE: + { + DeathSound(); + TaskComplete(); + break; + } + case TASK_SOUND_IDLE: + { + IdleSound(); + TaskComplete(); + break; + } + case TASK_SOUND_PAIN: + { + PainSound(); + TaskComplete(); + break; + } + case TASK_SOUND_DEATH: + { + DeathSound(); + TaskComplete(); + break; + } + case TASK_SOUND_ANGRY: + { + // sounds are complete as soon as we get here, cause we've already played them. + ALERT ( at_aiconsole, "SOUND\n" ); + TaskComplete(); + break; + } + case TASK_WAIT_FOR_SCRIPT: + { + if (m_pCine->m_iszIdle) + { + m_pCine->StartSequence( (CBaseMonster *)this, m_pCine->m_iszIdle, FALSE ); + if (FStrEq( STRING(m_pCine->m_iszIdle), STRING(m_pCine->m_iszPlay))) + { + pev->framerate = 0; + } + } + else + m_IdealActivity = ACT_IDLE; + + break; + } + case TASK_PLAY_SCRIPT: + { + pev->movetype = MOVETYPE_FLY; + ClearBits(pev->flags, FL_ONGROUND); + m_scriptState = SCRIPT_PLAYING; + break; + } + case TASK_ENABLE_SCRIPT: + { + m_pCine->DelayStart( 0 ); + TaskComplete(); + break; + } + case TASK_PLANT_ON_SCRIPT: + { + if ( m_hTargetEnt != NULL ) + { + pev->origin = m_hTargetEnt->pev->origin; // Plant on target + } + + TaskComplete(); + break; + } + case TASK_FACE_SCRIPT: + { + if ( m_hTargetEnt != NULL ) + { + pev->ideal_yaw = UTIL_AngleMod( m_hTargetEnt->pev->angles.y ); + } + + TaskComplete(); + m_IdealActivity = ACT_IDLE; + RouteClear(); + break; + } + + case TASK_SUGGEST_STATE: + { + m_IdealMonsterState = (MONSTERSTATE)(int)pTask->flData; + TaskComplete(); + break; + } + + case TASK_SET_FAIL_SCHEDULE: + m_failSchedule = (int)pTask->flData; + TaskComplete(); + break; + + case TASK_CLEAR_FAIL_SCHEDULE: + m_failSchedule = SCHED_NONE; + TaskComplete(); + break; + + default: + { + ALERT ( at_aiconsole, "No StartTask entry for %d\n", (SHARED_TASKS)pTask->iTask ); + break; + } + } +} + +//========================================================= +// GetTask - returns a pointer to the current +// scheduled task. NULL if there's a problem. +//========================================================= +Task_t *CBaseMonster :: GetTask ( void ) +{ + if ( m_iScheduleIndex < 0 || m_iScheduleIndex >= m_pSchedule->cTasks ) + { + // m_iScheduleIndex is not within valid range for the monster's current schedule. + return NULL; + } + else + { + return &m_pSchedule->pTasklist[ m_iScheduleIndex ]; + } +} + +//========================================================= +// GetSchedule - Decides which type of schedule best suits +// the monster's current state and conditions. Then calls +// monster's member function to get a pointer to a schedule +// of the proper type. +//========================================================= +Schedule_t *CBaseMonster :: GetSchedule ( void ) +{ + switch ( m_MonsterState ) + { + case MONSTERSTATE_PRONE: + { + return GetScheduleOfType( SCHED_BARNACLE_VICTIM_GRAB ); + break; + } + case MONSTERSTATE_NONE: + { + ALERT ( at_aiconsole, "MONSTERSTATE IS NONE!\n" ); + break; + } + case MONSTERSTATE_IDLE: + { + if ( HasConditions ( bits_COND_HEAR_SOUND ) ) + { + return GetScheduleOfType( SCHED_ALERT_FACE ); + } + else if ( FRouteClear() ) + { + // no valid route! + return GetScheduleOfType( SCHED_IDLE_STAND ); + } + else + { + // valid route. Get moving + return GetScheduleOfType( SCHED_IDLE_WALK ); + } + break; + } + case MONSTERSTATE_ALERT: + { + if ( HasConditions( bits_COND_ENEMY_DEAD ) && LookupActivity( ACT_VICTORY_DANCE ) != ACTIVITY_NOT_AVAILABLE ) + { + return GetScheduleOfType ( SCHED_VICTORY_DANCE ); + } + + if ( HasConditions(bits_COND_LIGHT_DAMAGE | bits_COND_HEAVY_DAMAGE) ) + { + if ( fabs( FlYawDiff() ) < (1.0 - m_flFieldOfView) * 60 ) // roughly in the correct direction + { + return GetScheduleOfType( SCHED_TAKE_COVER_FROM_ORIGIN ); + } + else + { + return GetScheduleOfType( SCHED_ALERT_SMALL_FLINCH ); + } + } + + else if ( HasConditions ( bits_COND_HEAR_SOUND ) ) + { + return GetScheduleOfType( SCHED_ALERT_FACE ); + } + else + { + return GetScheduleOfType( SCHED_ALERT_STAND ); + } + break; + } + case MONSTERSTATE_COMBAT: + { + if ( HasConditions( bits_COND_ENEMY_DEAD ) ) + { + // clear the current (dead) enemy and try to find another. + m_hEnemy = NULL; + + if ( GetEnemy() ) + { + ClearConditions( bits_COND_ENEMY_DEAD ); + return GetSchedule(); + } + else + { + SetState( MONSTERSTATE_ALERT ); + return GetSchedule(); + } + } + + if ( HasConditions(bits_COND_NEW_ENEMY) ) + { + return GetScheduleOfType ( SCHED_WAKE_ANGRY ); + } + else if (HasConditions(bits_COND_LIGHT_DAMAGE) && !HasMemory( bits_MEMORY_FLINCHED) ) + { + return GetScheduleOfType( SCHED_SMALL_FLINCH ); + } + else if ( !HasConditions(bits_COND_SEE_ENEMY) ) + { + // we can't see the enemy + if ( !HasConditions(bits_COND_ENEMY_OCCLUDED) ) + { + // enemy is unseen, but not occluded! + // turn to face enemy + return GetScheduleOfType( SCHED_COMBAT_FACE ); + } + else + { + // chase! + return GetScheduleOfType( SCHED_CHASE_ENEMY ); + } + } + else + { + // we can see the enemy + if ( HasConditions(bits_COND_CAN_RANGE_ATTACK1) ) + { + return GetScheduleOfType( SCHED_RANGE_ATTACK1 ); + } + if ( HasConditions(bits_COND_CAN_RANGE_ATTACK2) ) + { + return GetScheduleOfType( SCHED_RANGE_ATTACK2 ); + } + if ( HasConditions(bits_COND_CAN_MELEE_ATTACK1) ) + { + return GetScheduleOfType( SCHED_MELEE_ATTACK1 ); + } + if ( HasConditions(bits_COND_CAN_MELEE_ATTACK2) ) + { + return GetScheduleOfType( SCHED_MELEE_ATTACK2 ); + } + if ( !HasConditions(bits_COND_CAN_RANGE_ATTACK1 | bits_COND_CAN_MELEE_ATTACK1) ) + { + // if we can see enemy but can't use either attack type, we must need to get closer to enemy + return GetScheduleOfType( SCHED_CHASE_ENEMY ); + } + else if ( !FacingIdeal() ) + { + //turn + return GetScheduleOfType( SCHED_COMBAT_FACE ); + } + else + { + ALERT ( at_aiconsole, "No suitable combat schedule!\n" ); + } + } + break; + } + case MONSTERSTATE_DEAD: + { + return GetScheduleOfType( SCHED_DIE ); + break; + } + case MONSTERSTATE_SCRIPT: + { + ASSERT( m_pCine != NULL ); + if ( !m_pCine ) + { + ALERT( at_aiconsole, "Script failed for %s\n", STRING(pev->classname) ); + CineCleanup(); + return GetScheduleOfType( SCHED_IDLE_STAND ); + } + + return GetScheduleOfType( SCHED_AISCRIPT ); + } + default: + { + ALERT ( at_aiconsole, "Invalid State for GetSchedule!\n" ); + break; + } + } + + return &slError[ 0 ]; +} diff --git a/dlls/Makefile b/dlls/Makefile new file mode 100644 index 00000000..374be650 --- /dev/null +++ b/dlls/Makefile @@ -0,0 +1,186 @@ +# +# Half-Life Full SDK 2.3 hl_i386.so Makefile for x86 Linux +# +# October 2002 by Leon Hartwig (hartwig@valvesoftware.com) +# + +DLLNAME=hl + +ARCH=i386 + +#make sure this is the correct compiler for your system +CC=gcc + +DLL_SRCDIR=. +ENGINE_SRCDIR=../engine +COMMON_SRCDIR=../common +WPN_SHARED_SRCDIR=./wpn_shared +PM_SHARED_SRCDIR=../pm_shared +GAME_SHARED_SRCDIR=../game_shared + +DLL_OBJDIR=$(DLL_SRCDIR)/obj +WPN_SHARED_OBJDIR=$(WPN_SHARED_SRCDIR)/obj +PM_SHARED_OBJDIR=$(PM_SHARED_SRCDIR)/obj +GAME_SHARED_OBJDIR=$(GAME_SHARED_SRCDIR)/obj + +BASE_CFLAGS= -Dstricmp=strcasecmp -D_strnicmp=strncasecmp -Dstrnicmp=strncasecmp \ + -DCLIENT_WEAPONS + +#safe optimization +CFLAGS=$(BASE_CFLAGS) -w -m486 -O1 + +#full optimization +#CFLAGS=$(BASE_CFLAGS) -w -O1 -m486 -ffast-math -funroll-loops \ + -fomit-frame-pointer -fexpensive-optimizations \ + -malign-loops=2 -malign-jumps=2 -malign-functions=2 + +#use these when debugging +#CFLAGS=$(BASE_CFLAGS) -g + +INCLUDEDIRS=-I. -I$(ENGINE_SRCDIR) -I$(COMMON_SRCDIR) -I$(PM_SHARED_SRCDIR) -I$(GAME_SHARED_SRCDIR) + +LDFLAGS= + +SHLIBEXT=so +SHLIBCFLAGS=-fPIC +SHLIBLDFLAGS=-shared + +DO_CC=$(CC) $(CFLAGS) $(SHLIBCFLAGS) $(INCLUDEDIRS) -o $@ -c $< + +############################################################################# +# SETUP AND BUILD +# GAME +############################################################################# + +$(DLL_OBJDIR)/%.o: $(DLL_SRCDIR)/%.cpp + $(DO_CC) + +$(WPN_SHARED_OBJDIR)/%.o: $(WPN_SHARED_SRCDIR)/%.cpp + $(DO_CC) + +$(GAME_SHARED_OBJDIR)/%.o: $(GAME_SHARED_SRCDIR)/%.cpp + $(DO_CC) + +$(PM_SHARED_OBJDIR)/%.o: $(PM_SHARED_SRCDIR)/%.c + $(DO_CC) + +OBJ = \ + $(DLL_OBJDIR)/aflock.o \ + $(DLL_OBJDIR)/agrunt.o \ + $(DLL_OBJDIR)/airtank.o \ + $(DLL_OBJDIR)/animating.o \ + $(DLL_OBJDIR)/animation.o \ + $(DLL_OBJDIR)/apache.o \ + $(DLL_OBJDIR)/barnacle.o \ + $(DLL_OBJDIR)/barney.o \ + $(DLL_OBJDIR)/bigmomma.o \ + $(DLL_OBJDIR)/bloater.o \ + $(DLL_OBJDIR)/bmodels.o \ + $(DLL_OBJDIR)/bullsquid.o \ + $(DLL_OBJDIR)/buttons.o \ + $(DLL_OBJDIR)/cbase.o \ + $(DLL_OBJDIR)/client.o \ + $(DLL_OBJDIR)/combat.o \ + $(DLL_OBJDIR)/controller.o \ + $(DLL_OBJDIR)/crossbow.o \ + $(DLL_OBJDIR)/crowbar.o \ + $(DLL_OBJDIR)/defaultai.o \ + $(DLL_OBJDIR)/doors.o \ + $(DLL_OBJDIR)/effects.o \ + $(DLL_OBJDIR)/egon.o \ + $(DLL_OBJDIR)/explode.o \ + $(DLL_OBJDIR)/flyingmonster.o \ + $(DLL_OBJDIR)/func_break.o \ + $(DLL_OBJDIR)/func_tank.o \ + $(DLL_OBJDIR)/game.o \ + $(DLL_OBJDIR)/gamerules.o \ + $(DLL_OBJDIR)/gargantua.o \ + $(DLL_OBJDIR)/gauss.o \ + $(DLL_OBJDIR)/genericmonster.o \ + $(DLL_OBJDIR)/ggrenade.o \ + $(DLL_OBJDIR)/globals.o \ + $(DLL_OBJDIR)/gman.o \ + $(DLL_OBJDIR)/h_ai.o \ + $(DLL_OBJDIR)/h_battery.o \ + $(DLL_OBJDIR)/h_cine.o \ + $(DLL_OBJDIR)/h_cycler.o \ + $(DLL_OBJDIR)/h_export.o \ + $(DLL_OBJDIR)/handgrenade.o \ + $(DLL_OBJDIR)/hassassin.o \ + $(DLL_OBJDIR)/headcrab.o \ + $(DLL_OBJDIR)/healthkit.o \ + $(DLL_OBJDIR)/hgrunt.o \ + $(DLL_OBJDIR)/hornet.o \ + $(DLL_OBJDIR)/hornetgun.o \ + $(DLL_OBJDIR)/houndeye.o \ + $(DLL_OBJDIR)/ichthyosaur.o \ + $(DLL_OBJDIR)/islave.o \ + $(DLL_OBJDIR)/items.o \ + $(DLL_OBJDIR)/leech.o \ + $(DLL_OBJDIR)/lights.o \ + $(DLL_OBJDIR)/maprules.o \ + $(DLL_OBJDIR)/monstermaker.o \ + $(DLL_OBJDIR)/monsters.o \ + $(DLL_OBJDIR)/monsterstate.o \ + $(DLL_OBJDIR)/mortar.o \ + $(DLL_OBJDIR)/mp5.o \ + $(DLL_OBJDIR)/multiplay_gamerules.o \ + $(DLL_OBJDIR)/nihilanth.o \ + $(DLL_OBJDIR)/nodes.o \ + $(DLL_OBJDIR)/osprey.o \ + $(DLL_OBJDIR)/pathcorner.o \ + $(DLL_OBJDIR)/plane.o \ + $(DLL_OBJDIR)/plats.o \ + $(DLL_OBJDIR)/player.o \ + $(DLL_OBJDIR)/python.o \ + $(DLL_OBJDIR)/rat.o \ + $(DLL_OBJDIR)/roach.o \ + $(DLL_OBJDIR)/rpg.o \ + $(DLL_OBJDIR)/satchel.o \ + $(DLL_OBJDIR)/schedule.o \ + $(DLL_OBJDIR)/scientist.o \ + $(DLL_OBJDIR)/scripted.o \ + $(DLL_OBJDIR)/shotgun.o \ + $(DLL_OBJDIR)/singleplay_gamerules.o \ + $(DLL_OBJDIR)/skill.o \ + $(DLL_OBJDIR)/sound.o \ + $(DLL_OBJDIR)/soundent.o \ + $(DLL_OBJDIR)/spectator.o \ + $(DLL_OBJDIR)/squadmonster.o \ + $(DLL_OBJDIR)/squeakgrenade.o \ + $(DLL_OBJDIR)/subs.o \ + $(DLL_OBJDIR)/talkmonster.o \ + $(DLL_OBJDIR)/teamplay_gamerules.o \ + $(DLL_OBJDIR)/tempmonster.o \ + $(DLL_OBJDIR)/tentacle.o \ + $(DLL_OBJDIR)/triggers.o \ + $(DLL_OBJDIR)/tripmine.o \ + $(DLL_OBJDIR)/turret.o \ + $(DLL_OBJDIR)/util.o \ + $(DLL_OBJDIR)/weapons.o \ + $(DLL_OBJDIR)/world.o \ + $(DLL_OBJDIR)/xen.o \ + $(DLL_OBJDIR)/zombie.o \ + $(WPN_SHARED_OBJDIR)/hl_wpn_glock.o \ + $(GAME_SHARED_OBJDIR)/voice_gamemgr.o \ + $(PM_SHARED_OBJDIR)/pm_debug.o \ + $(PM_SHARED_OBJDIR)/pm_math.o \ + $(PM_SHARED_OBJDIR)/pm_shared.o + +$(DLLNAME)_$(ARCH).$(SHLIBEXT) : neat $(OBJ) + $(CC) $(CFLAGS) $(SHLIBLDFLAGS) $(LDFLAGS) -o $@ $(OBJ) + +neat: + -mkdir $(DLL_OBJDIR) + -mkdir $(WPN_SHARED_OBJDIR) + -mkdir $(GAME_SHARED_OBJDIR) + -mkdir $(PM_SHARED_OBJDIR) +clean: + -rm -f $(OBJ) + -rm -f $(DLLNAME)_$(ARCH).$(SHLIBEXT) +spotless: clean + -rm -r $(DLL_OBJDIR) + -rm -r $(WPN_SHARED_OBJDIR) + -rm -r $(GAME_SHARED_OBJDIR) + -rm -r $(PM_SHARED_OBJDIR) + diff --git a/dlls/Wxdebug.cpp b/dlls/Wxdebug.cpp new file mode 100644 index 00000000..d3902d6c --- /dev/null +++ b/dlls/Wxdebug.cpp @@ -0,0 +1,395 @@ +//==========================================================================; +// +// THIS CODE AND INFORMATION IS PROVIDED "AS IS" WITHOUT WARRANTY OF ANY +// KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE +// IMPLIED WARRANTIES OF MERCHANTABILITY AND/OR FITNESS FOR A PARTICULAR +// PURPOSE. +// +// Copyright (c) 1992 - 1997 Microsoft Corporation. All Rights Reserved. +// +//--------------------------------------------------------------------------; + + +// For every module and executable we store a debugging level and flags +// for the types of output that are desired. Constants for the types are +// defined in WXDEBUG.H and more can be added. +// The keys are stored in the registry under the +// HKEY_LOCAL_MACHINE\SOFTWARE\Debug\\Type and +// HKEY_LOCAL_MACHINE\SOFTWARE\Debug\\Level key values +// +// There are also global values under SOFTWARE\Debug\Global which are loaded +// after the module-specific values. The Types specified there are OR'ed with +// the module specific types and m_dwLevel is set to the greater of the global +// and the module specific settings. + +#include +#include + +#include "extdll.h" +#include "util.h" +#include "wxdebug.h" + +#include + +#ifdef _DEBUG + +void WINAPI DbgInitModuleName(void); +void WINAPI DbgInitModuleSettings(void); +void WINAPI DbgInitGlobalSettings(void); +void WINAPI DbgInitLogTo(HKEY hKey); +void WINAPI DbgInitKeyLevels(HKEY hKey, DWORD *pdwTypes, DWORD *pdwLevel); + + + +const INT iDEBUGINFO = 512; // Used to format strings + +HINSTANCE m_hInst; // Module instance handle +TCHAR m_ModuleName[iDEBUGINFO]; // Cut down module name +//CRITICAL_SECTION m_CSDebug; // Controls access to list +BOOL m_bInit = FALSE; // Have we been initialised +HANDLE m_hOutput = INVALID_HANDLE_VALUE; // Optional output written here +DWORD m_dwTypes = 0; +DWORD m_dwLevel = 0; + +const TCHAR *m_pBaseKey = TEXT("SOFTWARE\\Debug"); +const TCHAR *m_pGlobalKey = TEXT("GLOBAL"); +TCHAR *pKeyNames[] = +{ + TEXT("Types"), + TEXT("Level") +}; + + +// DbgInitialize +// This sets the instance handle that the debug library uses to find +// the module's file name from the Win32 GetModuleFileName function +void WINAPI DbgInitialise(HINSTANCE hInst) +{ + if (!m_bInit) + { + //InitializeCriticalSection(&m_CSDebug); + m_bInit = TRUE; + m_hInst = hInst; + DbgInitModuleName(); + DbgInitModuleSettings(); + DbgInitGlobalSettings(); + } +} + + +// DbgTerminate +// This is called to clear up any resources the debug library uses - at the +// moment we delete our critical section and the handle of the output file. +void WINAPI DbgTerminate() +{ + if (m_bInit) + { + if (m_hOutput != INVALID_HANDLE_VALUE) + { + DBGASSERTEXECUTE(CloseHandle(m_hOutput)); + m_hOutput = INVALID_HANDLE_VALUE; + } + //DeleteCriticalSection(&m_CSDebug); + m_bInit = FALSE; + } +} + + +// DbgInitModuleName +// Initialise the module file name +void WINAPI DbgInitModuleName() +{ + TCHAR FullName[iDEBUGINFO]; // Load the full path and module name + TCHAR *pName; // Searches from the end for a backslash + + GetModuleFileName(m_hInst,FullName,iDEBUGINFO); + pName = _tcsrchr(FullName,'\\'); + if (pName == NULL) + { + pName = FullName; + } + else + { + pName++; + } + lstrcpy(m_ModuleName,pName); +} + + +// DbgInitModuleSettings +// Retrieve the module-specific settings +void WINAPI DbgInitModuleSettings() +{ + LONG lReturn; // Create key return value + TCHAR szInfo[iDEBUGINFO]; // Constructs key names + HKEY hModuleKey; // Module key handle + + // Construct the base key name + wsprintf(szInfo,TEXT("%s\\%s"),m_pBaseKey,m_ModuleName); + + // Create or open the key for this module + lReturn = RegCreateKeyEx(HKEY_LOCAL_MACHINE, // Handle of an open key + szInfo, // Address of subkey name + (DWORD)0, // Reserved value + NULL, // Address of class name + (DWORD)0, // Special options flags + KEY_ALL_ACCESS, // Desired security access + NULL, // Key security descriptor + &hModuleKey, // Opened handle buffer + NULL); // What really happened + + if (lReturn != ERROR_SUCCESS) + { + DbgLogInfo(LOG_ERROR, 0, TEXT("Could not access module key")); + return; + } + + DbgInitLogTo(hModuleKey); + DbgInitKeyLevels(hModuleKey, &m_dwTypes, &m_dwLevel); + RegCloseKey(hModuleKey); +} + + +// DbgInitGlobalSettings +// This is called by DbgInitialize to read the global debug settings for +// Level and Type from the registry. The Types are OR'ed together and m_dwLevel +// is set to the greater of the global and module-specific values. +void WINAPI DbgInitGlobalSettings() +{ + LONG lReturn; // Create key return value + TCHAR szInfo[iDEBUGINFO]; // Constructs key names + HKEY hGlobalKey; // Global override key + DWORD dwTypes = 0; + DWORD dwLevel = 0; + + // Construct the global base key name + wsprintf(szInfo,TEXT("%s\\%s"),m_pBaseKey,m_pGlobalKey); + + // Create or open the key for this module + lReturn = RegCreateKeyEx(HKEY_LOCAL_MACHINE, // Handle of an open key + szInfo, // Address of subkey name + (DWORD)0, // Reserved value + NULL, // Address of class name + (DWORD)0, // Special options flags + KEY_ALL_ACCESS, // Desired security access + NULL, // Key security descriptor + &hGlobalKey, // Opened handle buffer + NULL); // What really happened + + if (lReturn != ERROR_SUCCESS) + { + DbgLogInfo(LOG_ERROR, 0, TEXT("Could not access GLOBAL module key")); + return; + } + + DbgInitKeyLevels(hGlobalKey, &dwTypes, &dwLevel); + RegCloseKey(hGlobalKey); + + m_dwTypes |= dwTypes; + if (dwLevel > m_dwLevel) + m_dwLevel = dwLevel; +} + + +// DbgInitLogTo +// Called by DbgInitModuleSettings to setup alternate logging destinations +void WINAPI DbgInitLogTo(HKEY hKey) +{ + LONG lReturn; + DWORD dwKeyType; + DWORD dwKeySize; + TCHAR szFile[MAX_PATH] = {0}; + static const TCHAR cszKey[] = TEXT("LogToFile"); + + dwKeySize = MAX_PATH; + lReturn = RegQueryValueEx( + hKey, // Handle to an open key + cszKey, // Subkey name derivation + NULL, // Reserved field + &dwKeyType, // Returns the field type + (LPBYTE) szFile, // Returns the field's value + &dwKeySize); // Number of bytes transferred + + // create an empty key if it does not already exist + if (lReturn != ERROR_SUCCESS || dwKeyType != REG_SZ) + { + dwKeySize = 1; + lReturn = RegSetValueEx( + hKey, // Handle of an open key + cszKey, // Address of subkey name + (DWORD) 0, // Reserved field + REG_SZ, // Type of the key field + (PBYTE)szFile, // Value for the field + dwKeySize); // Size of the field buffer + } + + // if an output-to was specified. try to open it. + if (m_hOutput != INVALID_HANDLE_VALUE) + { + DBGASSERTEXECUTE(CloseHandle(m_hOutput)); + m_hOutput = INVALID_HANDLE_VALUE; + } + if (szFile[0] != 0) + { + if (!lstrcmpi(szFile, TEXT("Console"))) + { + m_hOutput = GetStdHandle(STD_OUTPUT_HANDLE); + if (m_hOutput == INVALID_HANDLE_VALUE) + { + AllocConsole(); + m_hOutput = GetStdHandle(STD_OUTPUT_HANDLE); + } + SetConsoleTitle (TEXT("Valve Debug Output")); + } else if (szFile[0] && + lstrcmpi(szFile, TEXT("Debug")) && + lstrcmpi(szFile, TEXT("Debugger")) && + lstrcmpi(szFile, TEXT("Deb"))) + { + m_hOutput = CreateFile(szFile, GENERIC_WRITE, FILE_SHARE_READ, NULL, OPEN_ALWAYS, FILE_ATTRIBUTE_NORMAL, NULL); + if (INVALID_HANDLE_VALUE != m_hOutput) + { + static const TCHAR cszBar[] = TEXT("\r\n\r\n=====DbgInitialize()=====\r\n\r\n"); + SetFilePointer (m_hOutput, 0, NULL, FILE_END); + DbgOutString (cszBar); + } + } + } +} + + +// DbgInitKeyLevels +// This is called by DbgInitModuleSettings and DbgInitGlobalSettings to read +// settings for Types and Level from the registry +void WINAPI DbgInitKeyLevels(HKEY hKey, DWORD *pdwTypes, DWORD *pdwLevel) +{ + LONG lReturn; // Create key return value + DWORD dwKeySize; // Size of the key value + DWORD dwKeyType; // Receives it's type + + // Get the Types value + dwKeySize = sizeof(DWORD); + lReturn = RegQueryValueEx( + hKey, // Handle to an open key + pKeyNames[0], // Subkey name derivation + NULL, // Reserved field + &dwKeyType, // Returns the field type + (LPBYTE)pdwTypes, // Returns the field's value + &dwKeySize ); // Number of bytes transferred + + // If either the key was not available or it was not a DWORD value + // then we ensure only the high priority debug logging is output + // but we try and update the field to a zero filled DWORD value + + if (lReturn != ERROR_SUCCESS || dwKeyType != REG_DWORD) + { + *pdwTypes = 0; + lReturn = RegSetValueEx( + hKey, // Handle of an open key + pKeyNames[0], // Address of subkey name + (DWORD)0, // Reserved field + REG_DWORD, // Type of the key field + (PBYTE)pdwTypes, // Value for the field + sizeof(DWORD)); // Size of the field buffer + + if (lReturn != ERROR_SUCCESS) + { + DbgLogInfo(LOG_ERROR, 0, TEXT("Could not create subkey %s"),pKeyNames[0]); + *pdwTypes = 0; + } + } + + // Get the Level value + dwKeySize = sizeof(DWORD); + lReturn = RegQueryValueEx( + hKey, // Handle to an open key + pKeyNames[1], // Subkey name derivation + NULL, // Reserved field + &dwKeyType, // Returns the field type + (LPBYTE)pdwLevel, // Returns the field's value + &dwKeySize ); // Number of bytes transferred + + // If either the key was not available or it was not a DWORD value + // then we ensure only the high priority debug logging is output + // but we try and update the field to a zero filled DWORD value + + if (lReturn != ERROR_SUCCESS || dwKeyType != REG_DWORD) + { + *pdwLevel = 0; + lReturn = RegSetValueEx( + hKey, // Handle of an open key + pKeyNames[1], // Address of subkey name + (DWORD)0, // Reserved field + REG_DWORD, // Type of the key field + (PBYTE)pdwLevel, // Value for the field + sizeof(DWORD)); // Size of the field buffer + + if (lReturn != ERROR_SUCCESS) + { + DbgLogInfo(LOG_ERROR, 0, TEXT("Could not create subkey %s"),pKeyNames[1]); + *pdwLevel = 0; + } + } +} + + +// DbgOutString +void WINAPI DbgOutString(LPCTSTR psz) +{ + if (!m_bInit) + return; + if (m_hOutput != INVALID_HANDLE_VALUE) { + UINT cb = lstrlen(psz); + DWORD dw; + WriteFile (m_hOutput, psz, cb, &dw, NULL); + } else { + OutputDebugString (psz); + } +} + + +// DbgLogInfo +// Print a formatted string to the debugger prefixed with this module's name +// Because the debug code is linked statically every module loaded will +// have its own copy of this code. It therefore helps if the module name is +// included on the output so that the offending code can be easily found +void WINAPI DbgLogInfo(DWORD Type, DWORD Level, const TCHAR *pFormat,...) +{ + if (!m_bInit) + return; + // Check the current level for this type combination */ + if (((Type & m_dwTypes) == 0) || (m_dwLevel < Level)) + return; + + TCHAR szInfo[2000]; + + // Format the variable length parameter list + + va_list va; + va_start(va, pFormat); + + //lstrcpy(szInfo, m_ModuleName); + //lstrcat(szInfo, TEXT(": ")); + wvsprintf(szInfo /* + lstrlen(szInfo) */, pFormat, va); + //lstrcat(szInfo, TEXT("\r\n")); + DbgOutString(szInfo); + + va_end(va); +} + + +// DbgKernelAssert +// If we are executing as a pure kernel filter we cannot display message +// boxes to the user, this provides an alternative which puts the error +// condition on the debugger output with a suitable eye catching message +void WINAPI DbgKernelAssert(const TCHAR *pCondition, const TCHAR *pFileName, INT iLine) +{ + if (!m_bInit) + return; + DbgLogInfo(LOG_ERROR, 0, TEXT(m_ModuleName)); + DbgLogInfo(LOG_ERROR, 0, TEXT(": Assertion FAILED (%s) at line %d in file %s\r\n"), pCondition, iLine, pFileName); + DebugBreak(); +} + +#endif // _DEBUG + + diff --git a/spirit/activity.h b/dlls/activity.h similarity index 97% rename from spirit/activity.h rename to dlls/activity.h index 6fd3a188..37c82b6e 100644 --- a/spirit/activity.h +++ b/dlls/activity.h @@ -1,109 +1,109 @@ -/*** -* -* Copyright (c) 1996-2002, 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. -* -****/ - -#ifndef ACTIVITY_H -#define ACTIVITY_H - - -typedef enum { - ACT_RESET = 0, // Set m_Activity to this invalid value to force a reset to m_IdealActivity - ACT_IDLE = 1, - ACT_GUARD, - ACT_WALK, - ACT_RUN, - ACT_FLY, // Fly (and flap if appropriate) - ACT_SWIM, - ACT_HOP, // vertical jump - ACT_LEAP, // long forward jump - ACT_FALL, - ACT_LAND, - ACT_STRAFE_LEFT, - ACT_STRAFE_RIGHT, - ACT_ROLL_LEFT, // tuck and roll, left - ACT_ROLL_RIGHT, // tuck and roll, right - ACT_TURN_LEFT, // turn quickly left (stationary) - ACT_TURN_RIGHT, // turn quickly right (stationary) - ACT_CROUCH, // the act of crouching down from a standing position - ACT_CROUCHIDLE, // holding body in crouched position (loops) - ACT_STAND, // the act of standing from a crouched position - ACT_USE, - ACT_SIGNAL1, - ACT_SIGNAL2, - ACT_SIGNAL3, - ACT_TWITCH, - ACT_COWER, - ACT_SMALL_FLINCH, - ACT_BIG_FLINCH, - ACT_RANGE_ATTACK1, - ACT_RANGE_ATTACK2, - ACT_MELEE_ATTACK1, - ACT_MELEE_ATTACK2, - ACT_RELOAD, - ACT_ARM, // pull out gun, for instance - ACT_DISARM, // reholster gun - ACT_EAT, // monster chowing on a large food item (loop) - ACT_DIESIMPLE, - ACT_DIEBACKWARD, - ACT_DIEFORWARD, - ACT_DIEVIOLENT, - ACT_BARNACLE_HIT, // barnacle tongue hits a monster - ACT_BARNACLE_PULL, // barnacle is lifting the monster ( loop ) - ACT_BARNACLE_CHOMP, // barnacle latches on to the monster - ACT_BARNACLE_CHEW, // barnacle is holding the monster in its mouth ( loop ) - ACT_SLEEP, - ACT_INSPECT_FLOOR, // for active idles, look at something on or near the floor - ACT_INSPECT_WALL, // for active idles, look at something directly ahead of you ( doesn't HAVE to be a wall or on a wall ) - ACT_IDLE_ANGRY, // alternate idle animation in which the monster is clearly agitated. (loop) - ACT_WALK_HURT, // limp (loop) - ACT_RUN_HURT, // limp (loop) - ACT_HOVER, // Idle while in flight - ACT_GLIDE, // Fly (don't flap) - ACT_FLY_LEFT, // Turn left in flight - ACT_FLY_RIGHT, // Turn right in flight - ACT_DETECT_SCENT, // this means the monster smells a scent carried by the air - ACT_SNIFF, // this is the act of actually sniffing an item in front of the monster - ACT_BITE, // some large monsters can eat small things in one bite. This plays one time, EAT loops. - ACT_THREAT_DISPLAY, // without attacking, monster demonstrates that it is angry. (Yell, stick out chest, etc ) - ACT_FEAR_DISPLAY, // monster just saw something that it is afraid of - ACT_EXCITED, // for some reason, monster is excited. Sees something he really likes to eat, or whatever. - ACT_SPECIAL_ATTACK1, // very monster specific special attacks. - ACT_SPECIAL_ATTACK2, - ACT_COMBAT_IDLE, // agitated idle. - ACT_WALK_SCARED, - ACT_RUN_SCARED, - ACT_VICTORY_DANCE, // killed a player, do a victory dance. - ACT_DIE_HEADSHOT, // die, hit in head. - ACT_DIE_CHESTSHOT, // die, hit in chest - ACT_DIE_GUTSHOT, // die, hit in gut - ACT_DIE_BACKSHOT, // die, hit in back - ACT_FLINCH_HEAD, - ACT_FLINCH_CHEST, - ACT_FLINCH_STOMACH, - ACT_FLINCH_LEFTARM, - ACT_FLINCH_RIGHTARM, - ACT_FLINCH_LEFTLEG, - ACT_FLINCH_RIGHTLEG, -} Activity; - - -typedef struct { - int type; - char *name; -} activity_map_t; - -extern activity_map_t activity_map[]; - - -#endif //ACTIVITY_H +/*** +* +* Copyright (c) 1996-2002, 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. +* +****/ + +#ifndef ACTIVITY_H +#define ACTIVITY_H + + +typedef enum { + ACT_RESET = 0, // Set m_Activity to this invalid value to force a reset to m_IdealActivity + ACT_IDLE = 1, + ACT_GUARD, + ACT_WALK, + ACT_RUN, + ACT_FLY, // Fly (and flap if appropriate) + ACT_SWIM, + ACT_HOP, // vertical jump + ACT_LEAP, // long forward jump + ACT_FALL, + ACT_LAND, + ACT_STRAFE_LEFT, + ACT_STRAFE_RIGHT, + ACT_ROLL_LEFT, // tuck and roll, left + ACT_ROLL_RIGHT, // tuck and roll, right + ACT_TURN_LEFT, // turn quickly left (stationary) + ACT_TURN_RIGHT, // turn quickly right (stationary) + ACT_CROUCH, // the act of crouching down from a standing position + ACT_CROUCHIDLE, // holding body in crouched position (loops) + ACT_STAND, // the act of standing from a crouched position + ACT_USE, + ACT_SIGNAL1, + ACT_SIGNAL2, + ACT_SIGNAL3, + ACT_TWITCH, + ACT_COWER, + ACT_SMALL_FLINCH, + ACT_BIG_FLINCH, + ACT_RANGE_ATTACK1, + ACT_RANGE_ATTACK2, + ACT_MELEE_ATTACK1, + ACT_MELEE_ATTACK2, + ACT_RELOAD, + ACT_ARM, // pull out gun, for instance + ACT_DISARM, // reholster gun + ACT_EAT, // monster chowing on a large food item (loop) + ACT_DIESIMPLE, + ACT_DIEBACKWARD, + ACT_DIEFORWARD, + ACT_DIEVIOLENT, + ACT_BARNACLE_HIT, // barnacle tongue hits a monster + ACT_BARNACLE_PULL, // barnacle is lifting the monster ( loop ) + ACT_BARNACLE_CHOMP, // barnacle latches on to the monster + ACT_BARNACLE_CHEW, // barnacle is holding the monster in its mouth ( loop ) + ACT_SLEEP, + ACT_INSPECT_FLOOR, // for active idles, look at something on or near the floor + ACT_INSPECT_WALL, // for active idles, look at something directly ahead of you ( doesn't HAVE to be a wall or on a wall ) + ACT_IDLE_ANGRY, // alternate idle animation in which the monster is clearly agitated. (loop) + ACT_WALK_HURT, // limp (loop) + ACT_RUN_HURT, // limp (loop) + ACT_HOVER, // Idle while in flight + ACT_GLIDE, // Fly (don't flap) + ACT_FLY_LEFT, // Turn left in flight + ACT_FLY_RIGHT, // Turn right in flight + ACT_DETECT_SCENT, // this means the monster smells a scent carried by the air + ACT_SNIFF, // this is the act of actually sniffing an item in front of the monster + ACT_BITE, // some large monsters can eat small things in one bite. This plays one time, EAT loops. + ACT_THREAT_DISPLAY, // without attacking, monster demonstrates that it is angry. (Yell, stick out chest, etc ) + ACT_FEAR_DISPLAY, // monster just saw something that it is afraid of + ACT_EXCITED, // for some reason, monster is excited. Sees something he really likes to eat, or whatever. + ACT_SPECIAL_ATTACK1, // very monster specific special attacks. + ACT_SPECIAL_ATTACK2, + ACT_COMBAT_IDLE, // agitated idle. + ACT_WALK_SCARED, + ACT_RUN_SCARED, + ACT_VICTORY_DANCE, // killed a player, do a victory dance. + ACT_DIE_HEADSHOT, // die, hit in head. + ACT_DIE_CHESTSHOT, // die, hit in chest + ACT_DIE_GUTSHOT, // die, hit in gut + ACT_DIE_BACKSHOT, // die, hit in back + ACT_FLINCH_HEAD, + ACT_FLINCH_CHEST, + ACT_FLINCH_STOMACH, + ACT_FLINCH_LEFTARM, + ACT_FLINCH_RIGHTARM, + ACT_FLINCH_LEFTLEG, + ACT_FLINCH_RIGHTLEG, +} Activity; + + +typedef struct { + int type; + char *name; +} activity_map_t; + +extern activity_map_t activity_map[]; + + +#endif //ACTIVITY_H diff --git a/spirit/activitymap.h b/dlls/activitymap.h similarity index 95% rename from spirit/activitymap.h rename to dlls/activitymap.h index 92cadae7..b72c4e4d 100644 --- a/spirit/activitymap.h +++ b/dlls/activitymap.h @@ -1,97 +1,97 @@ -/*** -* -* Copyright (c) 1996-2002, 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. -* -****/ - -#define _A( a ) { a, #a } - -activity_map_t activity_map[] = -{ -_A( ACT_IDLE ), -_A( ACT_GUARD ), -_A( ACT_WALK ), -_A( ACT_RUN ), -_A( ACT_FLY ), -_A( ACT_SWIM ), -_A( ACT_HOP ), -_A( ACT_LEAP ), -_A( ACT_FALL ), -_A( ACT_LAND ), -_A( ACT_STRAFE_LEFT ), -_A( ACT_STRAFE_RIGHT ), -_A( ACT_ROLL_LEFT ), -_A( ACT_ROLL_RIGHT ), -_A( ACT_TURN_LEFT ), -_A( ACT_TURN_RIGHT ), -_A( ACT_CROUCH ), -_A( ACT_CROUCHIDLE ), -_A( ACT_STAND ), -_A( ACT_USE ), -_A( ACT_SIGNAL1 ), -_A( ACT_SIGNAL2 ), -_A( ACT_SIGNAL3 ), -_A( ACT_TWITCH ), -_A( ACT_COWER ), -_A( ACT_SMALL_FLINCH ), -_A( ACT_BIG_FLINCH ), -_A( ACT_RANGE_ATTACK1 ), -_A( ACT_RANGE_ATTACK2 ), -_A( ACT_MELEE_ATTACK1 ), -_A( ACT_MELEE_ATTACK2 ), -_A( ACT_RELOAD ), -_A( ACT_ARM ), -_A( ACT_DISARM ), -_A( ACT_EAT ), -_A( ACT_DIESIMPLE ), -_A( ACT_DIEBACKWARD ), -_A( ACT_DIEFORWARD ), -_A( ACT_DIEVIOLENT ), -_A( ACT_BARNACLE_HIT ), -_A( ACT_BARNACLE_PULL ), -_A( ACT_BARNACLE_CHOMP ), -_A( ACT_BARNACLE_CHEW ), -_A( ACT_SLEEP ), -_A( ACT_INSPECT_FLOOR ), -_A( ACT_INSPECT_WALL ), -_A( ACT_IDLE_ANGRY ), -_A( ACT_WALK_HURT ), -_A( ACT_RUN_HURT ), -_A( ACT_HOVER ), -_A( ACT_GLIDE ), -_A( ACT_FLY_LEFT ), -_A( ACT_FLY_RIGHT ), -_A( ACT_DETECT_SCENT ), -_A( ACT_SNIFF ), -_A( ACT_BITE ), -_A( ACT_THREAT_DISPLAY ), -_A( ACT_FEAR_DISPLAY ), -_A( ACT_EXCITED ), -_A( ACT_SPECIAL_ATTACK1 ), -_A( ACT_SPECIAL_ATTACK2 ), -_A( ACT_COMBAT_IDLE ), -_A( ACT_WALK_SCARED ), -_A( ACT_RUN_SCARED ), -_A( ACT_VICTORY_DANCE ), -_A( ACT_DIE_HEADSHOT ), -_A( ACT_DIE_CHESTSHOT ), -_A( ACT_DIE_GUTSHOT ), -_A( ACT_DIE_BACKSHOT ), -_A( ACT_FLINCH_HEAD ), -_A( ACT_FLINCH_CHEST ), -_A( ACT_FLINCH_STOMACH ), -_A( ACT_FLINCH_LEFTARM ), -_A( ACT_FLINCH_RIGHTARM ), -_A( ACT_FLINCH_LEFTLEG ), -_A( ACT_FLINCH_RIGHTLEG ), -0, NULL -}; +/*** +* +* Copyright (c) 1996-2002, 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. +* +****/ + +#define _A( a ) { a, #a } + +activity_map_t activity_map[] = +{ +_A( ACT_IDLE ), +_A( ACT_GUARD ), +_A( ACT_WALK ), +_A( ACT_RUN ), +_A( ACT_FLY ), +_A( ACT_SWIM ), +_A( ACT_HOP ), +_A( ACT_LEAP ), +_A( ACT_FALL ), +_A( ACT_LAND ), +_A( ACT_STRAFE_LEFT ), +_A( ACT_STRAFE_RIGHT ), +_A( ACT_ROLL_LEFT ), +_A( ACT_ROLL_RIGHT ), +_A( ACT_TURN_LEFT ), +_A( ACT_TURN_RIGHT ), +_A( ACT_CROUCH ), +_A( ACT_CROUCHIDLE ), +_A( ACT_STAND ), +_A( ACT_USE ), +_A( ACT_SIGNAL1 ), +_A( ACT_SIGNAL2 ), +_A( ACT_SIGNAL3 ), +_A( ACT_TWITCH ), +_A( ACT_COWER ), +_A( ACT_SMALL_FLINCH ), +_A( ACT_BIG_FLINCH ), +_A( ACT_RANGE_ATTACK1 ), +_A( ACT_RANGE_ATTACK2 ), +_A( ACT_MELEE_ATTACK1 ), +_A( ACT_MELEE_ATTACK2 ), +_A( ACT_RELOAD ), +_A( ACT_ARM ), +_A( ACT_DISARM ), +_A( ACT_EAT ), +_A( ACT_DIESIMPLE ), +_A( ACT_DIEBACKWARD ), +_A( ACT_DIEFORWARD ), +_A( ACT_DIEVIOLENT ), +_A( ACT_BARNACLE_HIT ), +_A( ACT_BARNACLE_PULL ), +_A( ACT_BARNACLE_CHOMP ), +_A( ACT_BARNACLE_CHEW ), +_A( ACT_SLEEP ), +_A( ACT_INSPECT_FLOOR ), +_A( ACT_INSPECT_WALL ), +_A( ACT_IDLE_ANGRY ), +_A( ACT_WALK_HURT ), +_A( ACT_RUN_HURT ), +_A( ACT_HOVER ), +_A( ACT_GLIDE ), +_A( ACT_FLY_LEFT ), +_A( ACT_FLY_RIGHT ), +_A( ACT_DETECT_SCENT ), +_A( ACT_SNIFF ), +_A( ACT_BITE ), +_A( ACT_THREAT_DISPLAY ), +_A( ACT_FEAR_DISPLAY ), +_A( ACT_EXCITED ), +_A( ACT_SPECIAL_ATTACK1 ), +_A( ACT_SPECIAL_ATTACK2 ), +_A( ACT_COMBAT_IDLE ), +_A( ACT_WALK_SCARED ), +_A( ACT_RUN_SCARED ), +_A( ACT_VICTORY_DANCE ), +_A( ACT_DIE_HEADSHOT ), +_A( ACT_DIE_CHESTSHOT ), +_A( ACT_DIE_GUTSHOT ), +_A( ACT_DIE_BACKSHOT ), +_A( ACT_FLINCH_HEAD ), +_A( ACT_FLINCH_CHEST ), +_A( ACT_FLINCH_STOMACH ), +_A( ACT_FLINCH_LEFTARM ), +_A( ACT_FLINCH_RIGHTARM ), +_A( ACT_FLINCH_LEFTLEG ), +_A( ACT_FLINCH_RIGHTLEG ), +0, NULL +}; diff --git a/spirit/aflock.cpp b/dlls/aflock.cpp similarity index 92% rename from spirit/aflock.cpp rename to dlls/aflock.cpp index 563e09d2..ab6139ce 100644 --- a/spirit/aflock.cpp +++ b/dlls/aflock.cpp @@ -202,7 +202,7 @@ void CFlockingFlyerFlock :: SpawnFlock( void ) vecSpot.z = RANDOM_FLOAT( 0, 16 ); vecSpot = pev->origin + vecSpot; - UTIL_SetOrigin(pBoid, vecSpot); + UTIL_SetOrigin(pBoid->pev, vecSpot); pBoid->pev->movetype = MOVETYPE_FLY; pBoid->SpawnCommonCode(); pBoid->pev->flags &= ~FL_ONGROUND; @@ -210,8 +210,8 @@ void CFlockingFlyerFlock :: SpawnFlock( void ) pBoid->pev->angles = pev->angles; pBoid->pev->frame = 0; - pBoid->SetNextThink( 0.2 ); - pBoid->SetThink(& CFlockingFlyer :: IdleThink ); + pBoid->pev->nextthink = gpGlobals->time + 0.2; + pBoid->SetThink( CFlockingFlyer :: IdleThink ); if ( pBoid != pLeader ) { @@ -228,8 +228,8 @@ void CFlockingFlyer :: Spawn( ) SpawnCommonCode(); pev->frame = 0; - SetNextThink( 0.1 ); - SetThink(&CFlockingFlyer :: IdleThink ); + pev->nextthink = gpGlobals->time + 0.1; + SetThink( IdleThink ); } //========================================================= @@ -292,8 +292,8 @@ void CFlockingFlyer :: Killed( entvars_t *pevAttacker, int iGib ) UTIL_SetSize( pev, Vector(0,0,0), Vector(0,0,0) ); pev->movetype = MOVETYPE_TOSS; - SetThink(&CFlockingFlyer :: FallHack ); - SetNextThink( 0.1 ); + SetThink ( FallHack ); + pev->nextthink = gpGlobals->time + 0.1; } void CFlockingFlyer :: FallHack( void ) @@ -303,7 +303,7 @@ void CFlockingFlyer :: FallHack( void ) if ( !FClassnameIs ( pev->groundentity, "worldspawn" ) ) { pev->flags &= ~FL_ONGROUND; - SetNextThink( 0.1 ); + pev->nextthink = gpGlobals->time + 0.1; } else { @@ -361,14 +361,13 @@ void CFlockingFlyer :: BoidAdvanceFrame ( ) //========================================================= void CFlockingFlyer :: IdleThink( void ) { - SetNextThink( 0.2 ); + pev->nextthink = gpGlobals->time + 0.2; // see if there's a client in the same pvs as the monster -// if ( !FNullEnt(FIND_CLIENT_IN_PVS(edict()))) - if ( !FNullEnt(FIND_CLIENT_IN_PVS(edict())) || HaveCamerasInPVS( edict() )) + if ( !FNullEnt( FIND_CLIENT_IN_PVS( edict() ) ) ) { - SetThink(&CFlockingFlyer :: Start ); - SetNextThink( 0.1 ); + SetThink( Start ); + pev->nextthink = gpGlobals->time + 0.1; } } @@ -377,15 +376,15 @@ void CFlockingFlyer :: IdleThink( void ) //========================================================= void CFlockingFlyer :: Start( void ) { - SetNextThink( 0.1 ); + pev->nextthink = gpGlobals->time + 0.1; if ( IsLeader() ) { - SetThink(&CFlockingFlyer :: FlockLeaderThink ); + SetThink( FlockLeaderThink ); } else { - SetThink(&CFlockingFlyer :: FlockFollowerThink ); + SetThink( FlockFollowerThink ); } /* @@ -439,8 +438,8 @@ void CFlockingFlyer :: FormFlock( void ) } } - SetThink(&CFlockingFlyer :: IdleThink );// now that flock is formed, go to idle and wait for a player to come along. - SetNextThink( 0 ); + SetThink( IdleThink );// now that flock is formed, go to idle and wait for a player to come along. + pev->nextthink = gpGlobals->time; } //========================================================= @@ -564,7 +563,7 @@ void CFlockingFlyer :: FlockLeaderThink( void ) float flRightSide; - SetNextThink( 0.1 ); + pev->nextthink = gpGlobals->time + 0.1; UTIL_MakeVectors ( pev->angles ); @@ -643,7 +642,7 @@ void CFlockingFlyer :: FlockLeaderThink( void ) // maybe it did, though. if ( FBitSet (pev->flags, FL_ONGROUND) ) { - UTIL_SetOrigin (this, pev->origin + Vector ( 0 , 0 , 1 ) ); + UTIL_SetOrigin (pev, pev->origin + Vector ( 0 , 0 , 1 ) ); pev->velocity.z = 0; } @@ -669,12 +668,12 @@ void CFlockingFlyer :: FlockFollowerThink( void ) Vector vecDirToLeader; float flDistToLeader; - SetNextThink( 0.1 ); + pev->nextthink = gpGlobals->time + 0.1; if ( IsLeader() || !InSquad() ) { // the leader has been killed and this flyer suddenly finds himself the leader. - SetThink(&CFlockingFlyer :: FlockLeaderThink ); + SetThink ( FlockLeaderThink ); return; } diff --git a/spirit/agrunt.cpp b/dlls/agrunt.cpp similarity index 94% rename from spirit/agrunt.cpp rename to dlls/agrunt.cpp index d846e6fe..ff31e8ce 100644 --- a/spirit/agrunt.cpp +++ b/dlls/agrunt.cpp @@ -1,1218 +1,1186 @@ -/*** -* -* Copyright (c) 1999, 2000 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. -* -* This source code contains proprietary and confidential information of -* Valve LLC and its suppliers. Access to this code is restricted to -* persons who have executed a written SDK license with Valve. Any access, -* use or distribution of this code by or to any unlicensed person is illegal. -* -****/ -//========================================================= -// Agrunt - Dominant, warlike alien grunt monster -//========================================================= - -#include "extdll.h" -#include "util.h" -#include "cbase.h" -#include "monsters.h" -#include "schedule.h" -#include "squadmonster.h" -#include "weapons.h" -#include "soundent.h" -#include "hornet.h" -#include "scripted.h" - -//========================================================= -// monster-specific schedule types -//========================================================= -enum -{ - SCHED_AGRUNT_SUPPRESS = LAST_COMMON_SCHEDULE + 1, - SCHED_AGRUNT_THREAT_DISPLAY, -}; - -//========================================================= -// monster-specific tasks -//========================================================= -enum -{ - TASK_AGRUNT_SETUP_HIDE_ATTACK = LAST_COMMON_TASK + 1, - TASK_AGRUNT_GET_PATH_TO_ENEMY_CORPSE, -}; - -int iAgruntMuzzleFlash; - -//========================================================= -// Monster's Anim Events Go Here -//========================================================= -#define AGRUNT_AE_HORNET1 ( 1 ) -#define AGRUNT_AE_HORNET2 ( 2 ) -#define AGRUNT_AE_HORNET3 ( 3 ) -#define AGRUNT_AE_HORNET4 ( 4 ) -#define AGRUNT_AE_HORNET5 ( 5 ) -// some events are set up in the QC file that aren't recognized by the code yet. -#define AGRUNT_AE_PUNCH ( 6 ) -#define AGRUNT_AE_BITE ( 7 ) - -#define AGRUNT_AE_LEFT_FOOT ( 10 ) -#define AGRUNT_AE_RIGHT_FOOT ( 11 ) - -#define AGRUNT_AE_LEFT_PUNCH ( 12 ) -#define AGRUNT_AE_RIGHT_PUNCH ( 13 ) - - - -#define AGRUNT_MELEE_DIST 100 - -//LRC - body definitions for the Agrunt model -#define AGRUNT_BODY_HASGUN 0 -#define AGRUNT_BODY_NOGUN 1 - -class CAGrunt : public CSquadMonster -{ -public: - void Spawn( void ); - void Precache( void ); - void SetYawSpeed ( void ); - int Classify ( void ); - int ISoundMask ( void ); - void HandleAnimEvent( MonsterEvent_t *pEvent ); - void SetObjectCollisionBox( void ) - { - pev->absmin = pev->origin + Vector( -32, -32, 0 ); - pev->absmax = pev->origin + Vector( 32, 32, 85 ); - } - - Schedule_t* GetSchedule ( void ); - Schedule_t* GetScheduleOfType ( int Type ); - BOOL FCanCheckAttacks ( void ); - BOOL CheckMeleeAttack1 ( float flDot, float flDist ); - BOOL CheckRangeAttack1 ( float flDot, float flDist ); - void StartTask ( Task_t *pTask ); - void AlertSound( void ); - void DeathSound ( void ); - void PainSound ( void ); - void AttackSound ( void ); - void PrescheduleThink ( void ); - void TraceAttack( entvars_t *pevAttacker, float flDamage, Vector vecDir, TraceResult *ptr, int bitsDamageType); - int IRelationship( CBaseEntity *pTarget ); - void StopTalking ( void ); - BOOL ShouldSpeak( void ); - virtual void Killed( entvars_t *pevAttacker, int iGib ); - - - CUSTOM_SCHEDULES; - - virtual int Save( CSave &save ); - virtual int Restore( CRestore &restore ); - static TYPEDESCRIPTION m_SaveData[]; - - static const char *pAttackHitSounds[]; - static const char *pAttackMissSounds[]; - static const char *pAttackSounds[]; - static const char *pDieSounds[]; - static const char *pPainSounds[]; - static const char *pIdleSounds[]; - static const char *pAlertSounds[]; - - BOOL m_fCanHornetAttack; - float m_flNextHornetAttackCheck; - - float m_flNextPainTime; - - // three hacky fields for speech stuff. These don't really need to be saved. - float m_flNextSpeakTime; - float m_flNextWordTime; - int m_iLastWord; -}; -LINK_ENTITY_TO_CLASS( monster_alien_grunt, CAGrunt ); - -TYPEDESCRIPTION CAGrunt::m_SaveData[] = -{ - DEFINE_FIELD( CAGrunt, m_fCanHornetAttack, FIELD_BOOLEAN ), - DEFINE_FIELD( CAGrunt, m_flNextHornetAttackCheck, FIELD_TIME ), - DEFINE_FIELD( CAGrunt, m_flNextPainTime, FIELD_TIME ), - DEFINE_FIELD( CAGrunt, m_flNextSpeakTime, FIELD_TIME ), - DEFINE_FIELD( CAGrunt, m_flNextWordTime, FIELD_TIME ), - DEFINE_FIELD( CAGrunt, m_iLastWord, FIELD_INTEGER ), -}; - -IMPLEMENT_SAVERESTORE( CAGrunt, CSquadMonster ); - -const char *CAGrunt::pAttackHitSounds[] = -{ - "zombie/claw_strike1.wav", - "zombie/claw_strike2.wav", - "zombie/claw_strike3.wav", -}; - -const char *CAGrunt::pAttackMissSounds[] = -{ - "zombie/claw_miss1.wav", - "zombie/claw_miss2.wav", -}; - -const char *CAGrunt::pAttackSounds[] = -{ - "agrunt/ag_attack1.wav", - "agrunt/ag_attack2.wav", - "agrunt/ag_attack3.wav", -}; - -const char *CAGrunt::pDieSounds[] = -{ - "agrunt/ag_die1.wav", - "agrunt/ag_die4.wav", - "agrunt/ag_die5.wav", -}; - -const char *CAGrunt::pPainSounds[] = -{ - "agrunt/ag_pain1.wav", - "agrunt/ag_pain2.wav", - "agrunt/ag_pain3.wav", - "agrunt/ag_pain4.wav", - "agrunt/ag_pain5.wav", -}; - -const char *CAGrunt::pIdleSounds[] = -{ - "agrunt/ag_idle1.wav", - "agrunt/ag_idle2.wav", - "agrunt/ag_idle3.wav", - "agrunt/ag_idle4.wav", -}; - -const char *CAGrunt::pAlertSounds[] = -{ - "agrunt/ag_alert1.wav", - "agrunt/ag_alert3.wav", - "agrunt/ag_alert4.wav", - "agrunt/ag_alert5.wav", -}; - -//========================================================= -// IRelationship - overridden because Human Grunts are -// Alien Grunt's nemesis. -//========================================================= -int CAGrunt::IRelationship ( CBaseEntity *pTarget ) -{ - if ( FClassnameIs( pTarget->pev, "monster_human_grunt" ) ) - { - return R_NM; - } - - return CSquadMonster :: IRelationship( pTarget ); -} - -//========================================================= -// ISoundMask -//========================================================= -int CAGrunt :: ISoundMask ( void ) -{ - return bits_SOUND_WORLD | - bits_SOUND_COMBAT | - bits_SOUND_PLAYER | - bits_SOUND_DANGER; -} - -//========================================================= -// TraceAttack -//========================================================= -void CAGrunt :: TraceAttack( entvars_t *pevAttacker, float flDamage, Vector vecDir, TraceResult *ptr, int bitsDamageType) -{ - if ( ptr->iHitgroup == 10 && (bitsDamageType & (DMG_BULLET | DMG_SLASH | DMG_CLUB))) - { - // hit armor - if ( pev->dmgtime != gpGlobals->time || (RANDOM_LONG(0,10) < 1) ) - { - UTIL_Ricochet( ptr->vecEndPos, RANDOM_FLOAT( 1, 2) ); - pev->dmgtime = gpGlobals->time; - } - - if ( RANDOM_LONG( 0, 1 ) == 0 ) - { - Vector vecTracerDir = vecDir; - - vecTracerDir.x += RANDOM_FLOAT( -0.3, 0.3 ); - vecTracerDir.y += RANDOM_FLOAT( -0.3, 0.3 ); - vecTracerDir.z += RANDOM_FLOAT( -0.3, 0.3 ); - - vecTracerDir = vecTracerDir * -512; - - MESSAGE_BEGIN( MSG_PVS, gmsgTempEntity, ptr->vecEndPos ); - WRITE_BYTE( TE_TRACER ); - WRITE_COORD( ptr->vecEndPos.x ); - WRITE_COORD( ptr->vecEndPos.y ); - WRITE_COORD( ptr->vecEndPos.z ); - - WRITE_COORD( vecTracerDir.x ); - WRITE_COORD( vecTracerDir.y ); - WRITE_COORD( vecTracerDir.z ); - MESSAGE_END(); - } - - flDamage -= 20; - if (flDamage <= 0) - flDamage = 0.1;// don't hurt the monster much, but allow bits_COND_LIGHT_DAMAGE to be generated - } - else - { - SpawnBlood(ptr->vecEndPos, BloodColor(), flDamage);// a little surface blood. - TraceBleed( flDamage, vecDir, ptr, bitsDamageType ); - } - - AddMultiDamage( pevAttacker, this, flDamage, bitsDamageType ); -} - -//========================================================= -// StopTalking - won't speak again for 10-20 seconds. -//========================================================= -void CAGrunt::StopTalking( void ) -{ - m_flNextWordTime = m_flNextSpeakTime = gpGlobals->time + 10 + RANDOM_LONG(0, 10); -} - -//========================================================= -// ShouldSpeak - Should this agrunt be talking? -//========================================================= -BOOL CAGrunt::ShouldSpeak( void ) -{ - if ( m_flNextSpeakTime > gpGlobals->time ) - { - // my time to talk is still in the future. - return FALSE; - } - - if ( pev->spawnflags & SF_MONSTER_GAG ) - { - if ( m_MonsterState != MONSTERSTATE_COMBAT ) - { - // if gagged, don't talk outside of combat. - // if not going to talk because of this, put the talk time - // into the future a bit, so we don't talk immediately after - // going into combat - m_flNextSpeakTime = gpGlobals->time + 3; - return FALSE; - } - } - - return TRUE; -} - -//========================================================= -// PrescheduleThink -//========================================================= -void CAGrunt :: PrescheduleThink ( void ) -{ - if ( ShouldSpeak() ) - { - if ( m_flNextWordTime < gpGlobals->time ) - { - int num = -1; - - do - { - num = RANDOM_LONG(0,ARRAYSIZE(pIdleSounds)-1); - } while( num == m_iLastWord ); - - m_iLastWord = num; - - // play a new sound - EMIT_SOUND ( ENT(pev), CHAN_VOICE, pIdleSounds[ num ], 1.0, ATTN_NORM ); - - // is this word our last? - if ( RANDOM_LONG( 1, 10 ) <= 1 ) - { - // stop talking. - StopTalking(); - } - else - { - m_flNextWordTime = gpGlobals->time + RANDOM_FLOAT( 0.5, 1 ); - } - } - } -} - -//========================================================= -// DieSound -//========================================================= -void CAGrunt :: DeathSound ( void ) -{ - StopTalking(); - - EMIT_SOUND ( ENT(pev), CHAN_VOICE, pDieSounds[RANDOM_LONG(0,ARRAYSIZE(pDieSounds)-1)], 1.0, ATTN_NORM ); -} - -//========================================================= -// AlertSound -//========================================================= -void CAGrunt :: AlertSound ( void ) -{ - StopTalking(); - - EMIT_SOUND ( ENT(pev), CHAN_VOICE, pAlertSounds[RANDOM_LONG(0,ARRAYSIZE(pAlertSounds)-1)], 1.0, ATTN_NORM ); -} - -//========================================================= -// AttackSound -//========================================================= -void CAGrunt :: AttackSound ( void ) -{ - StopTalking(); - - EMIT_SOUND ( ENT(pev), CHAN_VOICE, pAttackSounds[RANDOM_LONG(0,ARRAYSIZE(pAttackSounds)-1)], 1.0, ATTN_NORM ); -} - -//========================================================= -// PainSound -//========================================================= -void CAGrunt :: PainSound ( void ) -{ - if ( m_flNextPainTime > gpGlobals->time ) - { - return; - } - - m_flNextPainTime = gpGlobals->time + 0.6; - - StopTalking(); - - EMIT_SOUND ( ENT(pev), CHAN_VOICE, pPainSounds[RANDOM_LONG(0,ARRAYSIZE(pPainSounds)-1)], 1.0, ATTN_NORM ); -} - -//========================================================= -// Classify - indicates this monster's place in the -// relationship table. -//========================================================= -int CAGrunt :: Classify ( void ) -{ - return m_iClass?m_iClass:CLASS_ALIEN_MILITARY; -} - -//========================================================= -// SetYawSpeed - allows each sequence to have a different -// turn rate associated with it. -//========================================================= -void CAGrunt :: SetYawSpeed ( void ) -{ - int ys; - - switch ( m_Activity ) - { - case ACT_TURN_LEFT: - case ACT_TURN_RIGHT: - ys = 110; - break; - default: ys = 100; - } - - pev->yaw_speed = ys; -} - -//========================================================= -// HandleAnimEvent - catches the monster-specific messages -// that occur when tagged animation frames are played. -// -// Returns number of events handled, 0 if none. -//========================================================= -void CAGrunt :: HandleAnimEvent( MonsterEvent_t *pEvent ) -{ - switch( pEvent->event ) - { - case AGRUNT_AE_HORNET1: - case AGRUNT_AE_HORNET2: - case AGRUNT_AE_HORNET3: - case AGRUNT_AE_HORNET4: - case AGRUNT_AE_HORNET5: - { - // m_vecEnemyLKP should be center of enemy body - Vector vecArmPos, vecArmDir; - Vector vecDirToEnemy; - Vector angDir; - - if (HasConditions( bits_COND_SEE_ENEMY)) - { - vecDirToEnemy = ( ( m_vecEnemyLKP ) - pev->origin ); - angDir = UTIL_VecToAngles( vecDirToEnemy ); - vecDirToEnemy = vecDirToEnemy.Normalize(); - } - else - { - angDir = pev->angles; - UTIL_MakeAimVectors( angDir ); - vecDirToEnemy = gpGlobals->v_forward; - } - - pev->effects = EF_MUZZLEFLASH; - - // make angles +-180 - if (angDir.x > 180) - { - angDir.x = angDir.x - 360; - } - - SetBlending( 0, angDir.x ); - GetAttachment( 0, vecArmPos, vecArmDir ); - - vecArmPos = vecArmPos + vecDirToEnemy * 32; - MESSAGE_BEGIN( MSG_PVS, gmsgTempEntity, vecArmPos ); - WRITE_BYTE( TE_SPRITE ); - WRITE_COORD( vecArmPos.x ); // pos - WRITE_COORD( vecArmPos.y ); - WRITE_COORD( vecArmPos.z ); - WRITE_SHORT( iAgruntMuzzleFlash ); // model - WRITE_BYTE( 6 ); // size * 10 - WRITE_BYTE( 128 ); // brightness - MESSAGE_END(); - - CBaseEntity *pHornet = CBaseEntity::Create( "hornet", vecArmPos, UTIL_VecToAngles( vecDirToEnemy ), edict() ); - UTIL_MakeVectors ( pHornet->pev->angles ); - pHornet->pev->velocity = gpGlobals->v_forward * 300; - - CBaseMonster *pHornetMonster = pHornet->MyMonsterPointer(); - - //LRC - hornets have the same allegiance as their creators - pHornetMonster->m_iPlayerReact = m_iPlayerReact; - pHornetMonster->m_iClass = m_iClass; - if (m_afMemory & bits_MEMORY_PROVOKED) // if I'm mad at the player, so are my hornets - pHornetMonster->Remember(bits_MEMORY_PROVOKED); - - if ( pHornetMonster ) - { - if (m_pCine && m_pCine->PreciseAttack()) //LRC- are we doing a scripted action? - pHornetMonster->m_hEnemy = m_hTargetEnt; - else - pHornetMonster->m_hEnemy = m_hEnemy; - } - } - break; - - case AGRUNT_AE_LEFT_FOOT: - switch (RANDOM_LONG(0,1)) - { - // left foot - case 0: EMIT_SOUND_DYN ( ENT(pev), CHAN_BODY, "player/pl_ladder2.wav", 1, ATTN_NORM, 0, 70 ); break; - case 1: EMIT_SOUND_DYN ( ENT(pev), CHAN_BODY, "player/pl_ladder4.wav", 1, ATTN_NORM, 0, 70 ); break; - } - break; - case AGRUNT_AE_RIGHT_FOOT: - // right foot - switch (RANDOM_LONG(0,1)) - { - case 0: EMIT_SOUND_DYN ( ENT(pev), CHAN_BODY, "player/pl_ladder1.wav", 1, ATTN_NORM, 0, 70 ); break; - case 1: EMIT_SOUND_DYN ( ENT(pev), CHAN_BODY, "player/pl_ladder3.wav", 1, ATTN_NORM, 0 ,70); break; - } - break; - - case AGRUNT_AE_LEFT_PUNCH: - { - CBaseEntity *pHurt = CheckTraceHullAttack( AGRUNT_MELEE_DIST, gSkillData.agruntDmgPunch, DMG_CLUB ); - - if ( pHurt ) - { - pHurt->pev->punchangle.y = -25; - pHurt->pev->punchangle.x = 8; - - // OK to use gpGlobals without calling MakeVectors, cause CheckTraceHullAttack called it above. - if ( pHurt->IsPlayer() ) - { - // this is a player. Knock him around. - pHurt->pev->velocity = pHurt->pev->velocity + gpGlobals->v_right * 250; - } - - EMIT_SOUND_DYN ( ENT(pev), CHAN_WEAPON, pAttackHitSounds[ RANDOM_LONG(0,ARRAYSIZE(pAttackHitSounds)-1) ], 1.0, ATTN_NORM, 0, 100 + RANDOM_LONG(-5,5) ); - - Vector vecArmPos, vecArmAng; - GetAttachment( 0, vecArmPos, vecArmAng ); - SpawnBlood(vecArmPos, pHurt->BloodColor(), 25);// a little surface blood. - } - else - { - // Play a random attack miss sound - EMIT_SOUND_DYN ( ENT(pev), CHAN_WEAPON, pAttackMissSounds[ RANDOM_LONG(0,ARRAYSIZE(pAttackMissSounds)-1) ], 1.0, ATTN_NORM, 0, 100 + RANDOM_LONG(-5,5) ); - } - } - break; - - case AGRUNT_AE_RIGHT_PUNCH: - { - CBaseEntity *pHurt = CheckTraceHullAttack( AGRUNT_MELEE_DIST, gSkillData.agruntDmgPunch, DMG_CLUB ); - - if ( pHurt ) - { - pHurt->pev->punchangle.y = 25; - pHurt->pev->punchangle.x = 8; - - // OK to use gpGlobals without calling MakeVectors, cause CheckTraceHullAttack called it above. - if ( pHurt->IsPlayer() ) - { - // this is a player. Knock him around. - pHurt->pev->velocity = pHurt->pev->velocity + gpGlobals->v_right * -250; - } - - EMIT_SOUND_DYN ( ENT(pev), CHAN_WEAPON, pAttackHitSounds[ RANDOM_LONG(0,ARRAYSIZE(pAttackHitSounds)-1) ], 1.0, ATTN_NORM, 0, 100 + RANDOM_LONG(-5,5) ); - - Vector vecArmPos, vecArmAng; - GetAttachment( 0, vecArmPos, vecArmAng ); - SpawnBlood(vecArmPos, pHurt->BloodColor(), 25);// a little surface blood. - } - else - { - // Play a random attack miss sound - EMIT_SOUND_DYN ( ENT(pev), CHAN_WEAPON, pAttackMissSounds[ RANDOM_LONG(0,ARRAYSIZE(pAttackMissSounds)-1) ], 1.0, ATTN_NORM, 0, 100 + RANDOM_LONG(-5,5) ); - } - } - break; - - default: - CSquadMonster::HandleAnimEvent( pEvent ); - break; - } -} - -//========================================================= -// Spawn -//========================================================= -void CAGrunt :: Spawn() -{ - Precache( ); - - if (pev->model) - SET_MODEL(ENT(pev), STRING(pev->model)); //LRC - else - SET_MODEL(ENT(pev), "models/agrunt.mdl"); - UTIL_SetSize(pev, Vector(-32, -32, 0), Vector(32, 32, 64)); - - pev->solid = SOLID_SLIDEBOX; - pev->movetype = MOVETYPE_STEP; - m_bloodColor = BLOOD_COLOR_GREEN; - pev->effects = 0; - if (pev->health == 0) - pev->health = gSkillData.agruntHealth; - m_flFieldOfView = 0.2;// indicates the width of this monster's forward view cone ( as a dotproduct result ) - m_MonsterState = MONSTERSTATE_NONE; - m_afCapability = 0; - m_afCapability |= bits_CAP_SQUAD; - - m_HackedGunPos = Vector( 24, 64, 48 ); - - m_flNextSpeakTime = m_flNextWordTime = gpGlobals->time + 10 + RANDOM_LONG(0, 10); - - - MonsterInit(); -} - -//========================================================= -// Precache - precaches all resources this monster needs -//========================================================= -void CAGrunt :: Precache() -{ - int i; - - if (pev->model) - PRECACHE_MODEL((char*)STRING(pev->model)); //LRC - else - PRECACHE_MODEL("models/agrunt.mdl"); - - for ( i = 0; i < ARRAYSIZE( pAttackHitSounds ); i++ ) - PRECACHE_SOUND((char *)pAttackHitSounds[i]); - - for ( i = 0; i < ARRAYSIZE( pAttackMissSounds ); i++ ) - PRECACHE_SOUND((char *)pAttackMissSounds[i]); - - for ( i = 0; i < ARRAYSIZE( pIdleSounds ); i++ ) - PRECACHE_SOUND((char *)pIdleSounds[i]); - - for ( i = 0; i < ARRAYSIZE( pDieSounds ); i++ ) - PRECACHE_SOUND((char *)pDieSounds[i]); - - for ( i = 0; i < ARRAYSIZE( pPainSounds ); i++ ) - PRECACHE_SOUND((char *)pPainSounds[i]); - - for ( i = 0; i < ARRAYSIZE( pAttackSounds ); i++ ) - PRECACHE_SOUND((char *)pAttackSounds[i]); - - for ( i = 0; i < ARRAYSIZE( pAlertSounds ); i++ ) - PRECACHE_SOUND((char *)pAlertSounds[i]); - - - PRECACHE_SOUND( "hassault/hw_shoot1.wav" ); - - iAgruntMuzzleFlash = PRECACHE_MODEL( "sprites/muz4.spr" ); - - UTIL_PrecacheOther( "hornet" ); -} - -//========================================================= -// AI Schedules Specific to this monster -//========================================================= - -//========================================================= -// Fail Schedule -//========================================================= -Task_t tlAGruntFail[] = -{ - { TASK_STOP_MOVING, 0 }, - { TASK_SET_ACTIVITY, (float)ACT_IDLE }, - { TASK_WAIT, (float)2 }, - { TASK_WAIT_PVS, (float)0 }, -}; - -Schedule_t slAGruntFail[] = -{ - { - tlAGruntFail, - ARRAYSIZE ( tlAGruntFail ), - bits_COND_CAN_RANGE_ATTACK1 | - bits_COND_CAN_MELEE_ATTACK1, - 0, - "AGrunt Fail" - }, -}; - -//========================================================= -// Combat Fail Schedule -//========================================================= -Task_t tlAGruntCombatFail[] = -{ - { TASK_STOP_MOVING, 0 }, - { TASK_SET_ACTIVITY, (float)ACT_IDLE }, - { TASK_WAIT_FACE_ENEMY, (float)2 }, - { TASK_WAIT_PVS, (float)0 }, -}; - -Schedule_t slAGruntCombatFail[] = -{ - { - tlAGruntCombatFail, - ARRAYSIZE ( tlAGruntCombatFail ), - bits_COND_CAN_RANGE_ATTACK1 | - bits_COND_CAN_MELEE_ATTACK1, - 0, - "AGrunt Combat Fail" - }, -}; - -//========================================================= -// Standoff schedule. Used in combat when a monster is -// hiding in cover or the enemy has moved out of sight. -// Should we look around in this schedule? -//========================================================= -Task_t tlAGruntStandoff[] = -{ - { TASK_STOP_MOVING, (float)0 }, - { TASK_SET_ACTIVITY, (float)ACT_IDLE }, - { TASK_WAIT_FACE_ENEMY, (float)2 }, -}; - -Schedule_t slAGruntStandoff[] = -{ - { - tlAGruntStandoff, - ARRAYSIZE ( tlAGruntStandoff ), - bits_COND_CAN_RANGE_ATTACK1 | - bits_COND_CAN_MELEE_ATTACK1 | - bits_COND_SEE_ENEMY | - bits_COND_NEW_ENEMY | - bits_COND_HEAR_SOUND, - - bits_SOUND_DANGER, - "Agrunt Standoff" - } -}; - -//========================================================= -// Suppress -//========================================================= -Task_t tlAGruntSuppressHornet[] = -{ - { TASK_STOP_MOVING, (float)0 }, - { TASK_RANGE_ATTACK1, (float)0 }, -}; - -Schedule_t slAGruntSuppress[] = -{ - { - tlAGruntSuppressHornet, - ARRAYSIZE ( tlAGruntSuppressHornet ), - 0, - 0, - "AGrunt Suppress Hornet", - }, -}; - -//========================================================= -// primary range attacks -//========================================================= -Task_t tlAGruntRangeAttack1[] = -{ - { TASK_STOP_MOVING, (float)0 }, - { TASK_FACE_ENEMY, (float)0 }, - { TASK_RANGE_ATTACK1, (float)0 }, -}; - -Schedule_t slAGruntRangeAttack1[] = -{ - { - tlAGruntRangeAttack1, - ARRAYSIZE ( tlAGruntRangeAttack1 ), - bits_COND_NEW_ENEMY | - bits_COND_ENEMY_DEAD | - bits_COND_HEAVY_DAMAGE, - - 0, - "AGrunt Range Attack1" - }, -}; - - -Task_t tlAGruntHiddenRangeAttack1[] = -{ - { TASK_SET_FAIL_SCHEDULE, (float)SCHED_STANDOFF }, - { TASK_AGRUNT_SETUP_HIDE_ATTACK, 0 }, - { TASK_STOP_MOVING, 0 }, - { TASK_FACE_IDEAL, 0 }, - { TASK_RANGE_ATTACK1_NOTURN, (float)0 }, -}; - -Schedule_t slAGruntHiddenRangeAttack[] = -{ - { - tlAGruntHiddenRangeAttack1, - ARRAYSIZE ( tlAGruntHiddenRangeAttack1 ), - bits_COND_NEW_ENEMY | - bits_COND_HEAVY_DAMAGE | - bits_COND_HEAR_SOUND, - - bits_SOUND_DANGER, - "AGrunt Hidden Range Attack1" - }, -}; - -//========================================================= -// Take cover from enemy! Tries lateral cover before node -// cover! -//========================================================= -Task_t tlAGruntTakeCoverFromEnemy[] = -{ - { TASK_STOP_MOVING, (float)0 }, - { TASK_WAIT, (float)0.2 }, - { TASK_FIND_COVER_FROM_ENEMY, (float)0 }, - { TASK_RUN_PATH, (float)0 }, - { TASK_WAIT_FOR_MOVEMENT, (float)0 }, - { TASK_REMEMBER, (float)bits_MEMORY_INCOVER }, - { TASK_FACE_ENEMY, (float)0 }, -}; - -Schedule_t slAGruntTakeCoverFromEnemy[] = -{ - { - tlAGruntTakeCoverFromEnemy, - ARRAYSIZE ( tlAGruntTakeCoverFromEnemy ), - bits_COND_NEW_ENEMY, - 0, - "AGruntTakeCoverFromEnemy" - }, -}; - -//========================================================= -// Victory dance! -//========================================================= -Task_t tlAGruntVictoryDance[] = -{ - { TASK_STOP_MOVING, (float)0 }, - { TASK_SET_FAIL_SCHEDULE, (float)SCHED_AGRUNT_THREAT_DISPLAY }, - { TASK_WAIT, (float)0.2 }, - { TASK_AGRUNT_GET_PATH_TO_ENEMY_CORPSE, (float)0 }, - { TASK_WALK_PATH, (float)0 }, - { TASK_WAIT_FOR_MOVEMENT, (float)0 }, - { TASK_FACE_ENEMY, (float)0 }, - { TASK_PLAY_SEQUENCE, (float)ACT_CROUCH }, - { TASK_PLAY_SEQUENCE, (float)ACT_VICTORY_DANCE }, - { TASK_PLAY_SEQUENCE, (float)ACT_VICTORY_DANCE }, - { TASK_PLAY_SEQUENCE, (float)ACT_STAND }, - { TASK_PLAY_SEQUENCE, (float)ACT_THREAT_DISPLAY }, - { TASK_PLAY_SEQUENCE, (float)ACT_CROUCH }, - { TASK_PLAY_SEQUENCE, (float)ACT_VICTORY_DANCE }, - { TASK_PLAY_SEQUENCE, (float)ACT_VICTORY_DANCE }, - { TASK_PLAY_SEQUENCE, (float)ACT_VICTORY_DANCE }, - { TASK_PLAY_SEQUENCE, (float)ACT_VICTORY_DANCE }, - { TASK_PLAY_SEQUENCE, (float)ACT_VICTORY_DANCE }, - { TASK_PLAY_SEQUENCE, (float)ACT_STAND }, -}; - -Schedule_t slAGruntVictoryDance[] = -{ - { - tlAGruntVictoryDance, - ARRAYSIZE ( tlAGruntVictoryDance ), - bits_COND_NEW_ENEMY | - bits_COND_LIGHT_DAMAGE | - bits_COND_HEAVY_DAMAGE, - 0, - "AGruntVictoryDance" - }, -}; - -//========================================================= -//========================================================= -Task_t tlAGruntThreatDisplay[] = -{ - { TASK_STOP_MOVING, (float)0 }, - { TASK_FACE_ENEMY, (float)0 }, - { TASK_PLAY_SEQUENCE, (float)ACT_THREAT_DISPLAY }, -}; - -Schedule_t slAGruntThreatDisplay[] = -{ - { - tlAGruntThreatDisplay, - ARRAYSIZE ( tlAGruntThreatDisplay ), - bits_COND_NEW_ENEMY | - bits_COND_LIGHT_DAMAGE | - bits_COND_HEAVY_DAMAGE, - - bits_SOUND_PLAYER | - bits_SOUND_COMBAT | - bits_SOUND_WORLD, - "AGruntThreatDisplay" - }, -}; - -DEFINE_CUSTOM_SCHEDULES( CAGrunt ) -{ - slAGruntFail, - slAGruntCombatFail, - slAGruntStandoff, - slAGruntSuppress, - slAGruntRangeAttack1, - slAGruntHiddenRangeAttack, - slAGruntTakeCoverFromEnemy, - slAGruntVictoryDance, - slAGruntThreatDisplay, -}; - -IMPLEMENT_CUSTOM_SCHEDULES( CAGrunt, CSquadMonster ); - -//========================================================= -// FCanCheckAttacks - this is overridden for alien grunts -// because they can use their smart weapons against unseen -// enemies. Base class doesn't attack anyone it can't see. -//========================================================= -BOOL CAGrunt :: FCanCheckAttacks ( void ) -{ - if ( !HasConditions( bits_COND_ENEMY_TOOFAR ) ) - { - return TRUE; - } - else - { - return FALSE; - } -} - -//========================================================= -// CheckMeleeAttack1 - alien grunts zap the crap out of -// any enemy that gets too close. -//========================================================= -BOOL CAGrunt :: CheckMeleeAttack1 ( float flDot, float flDist ) -{ - if ( HasConditions ( bits_COND_SEE_ENEMY ) && flDist <= AGRUNT_MELEE_DIST && flDot >= 0.6 && m_hEnemy != NULL ) - { - return TRUE; - } - return FALSE; -} - -//========================================================= -// CheckRangeAttack1 -// -// !!!LATER - we may want to load balance this. Several -// tracelines are done, so we may not want to do this every -// server frame. Definitely not while firing. -//========================================================= -BOOL CAGrunt :: CheckRangeAttack1 ( float flDot, float flDist ) -{ - if ( gpGlobals->time < m_flNextHornetAttackCheck ) - { - return m_fCanHornetAttack; - } - - if ( HasConditions( bits_COND_SEE_ENEMY ) && flDist >= AGRUNT_MELEE_DIST && flDist <= 1024 && flDot >= 0.5 && NoFriendlyFire() ) - { - TraceResult tr; - Vector vecArmPos, vecArmDir; - - // verify that a shot fired from the gun will hit the enemy before the world. - // !!!LATER - we may wish to do something different for projectile weapons as opposed to instant-hit - UTIL_MakeVectors( pev->angles ); - GetAttachment( 0, vecArmPos, vecArmDir ); -// UTIL_TraceLine( vecArmPos, vecArmPos + gpGlobals->v_forward * 256, ignore_monsters, ENT(pev), &tr); - UTIL_TraceLine( vecArmPos, m_hEnemy->BodyTarget(vecArmPos), dont_ignore_monsters, ENT(pev), &tr); - - if ( tr.flFraction == 1.0 || tr.pHit == m_hEnemy->edict() ) - { - m_flNextHornetAttackCheck = gpGlobals->time + RANDOM_FLOAT( 2, 5 ); - m_fCanHornetAttack = TRUE; - return m_fCanHornetAttack; - } - } - - m_flNextHornetAttackCheck = gpGlobals->time + 0.2;// don't check for half second if this check wasn't successful - m_fCanHornetAttack = FALSE; - return m_fCanHornetAttack; -} - -//========================================================= -// StartTask -//========================================================= -void CAGrunt :: StartTask ( Task_t *pTask ) -{ - switch ( pTask->iTask ) - { - case TASK_AGRUNT_GET_PATH_TO_ENEMY_CORPSE: - { - UTIL_MakeVectors( pev->angles ); - if ( BuildRoute ( m_vecEnemyLKP - gpGlobals->v_forward * 50, bits_MF_TO_LOCATION, NULL ) ) - { - TaskComplete(); - } - else - { - ALERT ( at_aiconsole, "AGruntGetPathToEnemyCorpse failed!!\n" ); - TaskFail(); - } - } - break; - - case TASK_AGRUNT_SETUP_HIDE_ATTACK: - // alien grunt shoots hornets back out into the open from a concealed location. - // try to find a spot to throw that gives the smart weapon a good chance of finding the enemy. - // ideally, this spot is along a line that is perpendicular to a line drawn from the agrunt to the enemy. - - CBaseMonster *pEnemyMonsterPtr; - - pEnemyMonsterPtr = m_hEnemy->MyMonsterPointer(); - - if ( pEnemyMonsterPtr ) - { - Vector vecCenter; - TraceResult tr; - BOOL fSkip; - - fSkip = FALSE; - vecCenter = Center(); - - UTIL_VecToAngles( m_vecEnemyLKP - pev->origin ); - - UTIL_TraceLine( Center() + gpGlobals->v_forward * 128, m_vecEnemyLKP, ignore_monsters, ENT(pev), &tr); - if ( tr.flFraction == 1.0 ) - { - MakeIdealYaw ( pev->origin + gpGlobals->v_right * 128 ); - fSkip = TRUE; - TaskComplete(); - } - - if ( !fSkip ) - { - UTIL_TraceLine( Center() - gpGlobals->v_forward * 128, m_vecEnemyLKP, ignore_monsters, ENT(pev), &tr); - if ( tr.flFraction == 1.0 ) - { - MakeIdealYaw ( pev->origin - gpGlobals->v_right * 128 ); - fSkip = TRUE; - TaskComplete(); - } - } - - if ( !fSkip ) - { - UTIL_TraceLine( Center() + gpGlobals->v_forward * 256, m_vecEnemyLKP, ignore_monsters, ENT(pev), &tr); - if ( tr.flFraction == 1.0 ) - { - MakeIdealYaw ( pev->origin + gpGlobals->v_right * 256 ); - fSkip = TRUE; - TaskComplete(); - } - } - - if ( !fSkip ) - { - UTIL_TraceLine( Center() - gpGlobals->v_forward * 256, m_vecEnemyLKP, ignore_monsters, ENT(pev), &tr); - if ( tr.flFraction == 1.0 ) - { - MakeIdealYaw ( pev->origin - gpGlobals->v_right * 256 ); - fSkip = TRUE; - TaskComplete(); - } - } - - if ( !fSkip ) - { - TaskFail(); - } - } - else - { - ALERT ( at_aiconsole, "AGRunt - no enemy monster ptr!!!\n" ); - TaskFail(); - } - break; - - default: - CSquadMonster :: StartTask ( pTask ); - break; - } -} - -//========================================================= -// GetSchedule - Decides which type of schedule best suits -// the monster's current state and conditions. Then calls -// monster's member function to get a pointer to a schedule -// of the proper type. -//========================================================= -Schedule_t *CAGrunt :: GetSchedule ( void ) -{ - if ( HasConditions(bits_COND_HEAR_SOUND) ) - { - CSound *pSound; - pSound = PBestSound(); - - ASSERT( pSound != NULL ); - if ( pSound && (pSound->m_iType & bits_SOUND_DANGER) ) - { - // dangerous sound nearby! - return GetScheduleOfType( SCHED_TAKE_COVER_FROM_BEST_SOUND ); - } - } - - switch ( m_MonsterState ) - { - case MONSTERSTATE_COMBAT: - { -// dead enemy - if ( HasConditions( bits_COND_ENEMY_DEAD ) ) - { - // call base class, all code to handle dead enemies is centralized there. - return CBaseMonster :: GetSchedule(); - } - - if ( HasConditions(bits_COND_NEW_ENEMY) ) - { - return GetScheduleOfType( SCHED_WAKE_ANGRY ); - } - - // zap player! - if ( HasConditions ( bits_COND_CAN_MELEE_ATTACK1 ) ) - { - AttackSound();// this is a total hack. Should be parto f the schedule - return GetScheduleOfType ( SCHED_MELEE_ATTACK1 ); - } - - if ( HasConditions ( bits_COND_HEAVY_DAMAGE ) ) - { - return GetScheduleOfType( SCHED_SMALL_FLINCH ); - } - - // can attack - if ( HasConditions ( bits_COND_CAN_RANGE_ATTACK1 ) && OccupySlot ( bits_SLOTS_AGRUNT_HORNET ) ) - { - return GetScheduleOfType ( SCHED_RANGE_ATTACK1 ); - } - - if ( OccupySlot ( bits_SLOT_AGRUNT_CHASE ) ) - { - return GetScheduleOfType ( SCHED_CHASE_ENEMY ); - } - - return GetScheduleOfType ( SCHED_STANDOFF ); - } - } - - return CSquadMonster :: GetSchedule(); -} - -//========================================================= -//========================================================= -Schedule_t* CAGrunt :: GetScheduleOfType ( int Type ) -{ - switch ( Type ) - { - case SCHED_TAKE_COVER_FROM_ENEMY: - return &slAGruntTakeCoverFromEnemy[ 0 ]; - break; - - case SCHED_RANGE_ATTACK1: - if ( HasConditions( bits_COND_SEE_ENEMY ) ) - { - //normal attack - return &slAGruntRangeAttack1[ 0 ]; - } - else - { - // attack an unseen enemy - // return &slAGruntHiddenRangeAttack[ 0 ]; - return &slAGruntRangeAttack1[ 0 ]; - } - break; - - case SCHED_AGRUNT_THREAT_DISPLAY: - return &slAGruntThreatDisplay[ 0 ]; - break; - - case SCHED_AGRUNT_SUPPRESS: - return &slAGruntSuppress[ 0 ]; - break; - - case SCHED_STANDOFF: - return &slAGruntStandoff[ 0 ]; - break; - - case SCHED_VICTORY_DANCE: - return &slAGruntVictoryDance[ 0 ]; - break; - - case SCHED_FAIL: - // no fail schedule specified, so pick a good generic one. - { - if ( m_hEnemy != NULL ) - { - // I have an enemy - // !!!LATER - what if this enemy is really far away and i'm chasing him? - // this schedule will make me stop, face his last known position for 2 - // seconds, and then try to move again - return &slAGruntCombatFail[ 0 ]; - } - - return &slAGruntFail[ 0 ]; - } - break; - - } - - return CSquadMonster :: GetScheduleOfType( Type ); -} - - -void CAGrunt::Killed( entvars_t *pevAttacker, int iGib ) -{ - if ( pev->spawnflags & SF_MONSTER_NO_WPN_DROP ) - {// drop the hornetgun! - Vector vecGunPos; - Vector vecGunAngles; - - pev->body = AGRUNT_BODY_NOGUN; - - GetAttachment( 0, vecGunPos, vecGunAngles ); - - DropItem( "weapon_hornetgun", vecGunPos, vecGunAngles ); - } - - CBaseMonster::Killed( pevAttacker, iGib ); -} +/*** +* +* Copyright (c) 1996-2002, 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. +* +* This source code contains proprietary and confidential information of +* Valve LLC and its suppliers. Access to this code is restricted to +* persons who have executed a written SDK license with Valve. Any access, +* use or distribution of this code by or to any unlicensed person is illegal. +* +****/ +//========================================================= +// Agrunt - Dominant, warlike alien grunt monster +//========================================================= + +#include "extdll.h" +#include "util.h" +#include "cbase.h" +#include "monsters.h" +#include "schedule.h" +#include "squadmonster.h" +#include "weapons.h" +#include "soundent.h" +#include "hornet.h" + +//========================================================= +// monster-specific schedule types +//========================================================= +enum +{ + SCHED_AGRUNT_SUPPRESS = LAST_COMMON_SCHEDULE + 1, + SCHED_AGRUNT_THREAT_DISPLAY, +}; + +//========================================================= +// monster-specific tasks +//========================================================= +enum +{ + TASK_AGRUNT_SETUP_HIDE_ATTACK = LAST_COMMON_TASK + 1, + TASK_AGRUNT_GET_PATH_TO_ENEMY_CORPSE, +}; + +int iAgruntMuzzleFlash; + +//========================================================= +// Monster's Anim Events Go Here +//========================================================= +#define AGRUNT_AE_HORNET1 ( 1 ) +#define AGRUNT_AE_HORNET2 ( 2 ) +#define AGRUNT_AE_HORNET3 ( 3 ) +#define AGRUNT_AE_HORNET4 ( 4 ) +#define AGRUNT_AE_HORNET5 ( 5 ) +// some events are set up in the QC file that aren't recognized by the code yet. +#define AGRUNT_AE_PUNCH ( 6 ) +#define AGRUNT_AE_BITE ( 7 ) + +#define AGRUNT_AE_LEFT_FOOT ( 10 ) +#define AGRUNT_AE_RIGHT_FOOT ( 11 ) + +#define AGRUNT_AE_LEFT_PUNCH ( 12 ) +#define AGRUNT_AE_RIGHT_PUNCH ( 13 ) + + + +#define AGRUNT_MELEE_DIST 100 + +class CAGrunt : public CSquadMonster +{ +public: + void Spawn( void ); + void Precache( void ); + void SetYawSpeed ( void ); + int Classify ( void ); + int ISoundMask ( void ); + void HandleAnimEvent( MonsterEvent_t *pEvent ); + void SetObjectCollisionBox( void ) + { + pev->absmin = pev->origin + Vector( -32, -32, 0 ); + pev->absmax = pev->origin + Vector( 32, 32, 85 ); + } + + Schedule_t* GetSchedule ( void ); + Schedule_t* GetScheduleOfType ( int Type ); + BOOL FCanCheckAttacks ( void ); + BOOL CheckMeleeAttack1 ( float flDot, float flDist ); + BOOL CheckRangeAttack1 ( float flDot, float flDist ); + void StartTask ( Task_t *pTask ); + void AlertSound( void ); + void DeathSound ( void ); + void PainSound ( void ); + void AttackSound ( void ); + void PrescheduleThink ( void ); + void TraceAttack( entvars_t *pevAttacker, float flDamage, Vector vecDir, TraceResult *ptr, int bitsDamageType); + int IRelationship( CBaseEntity *pTarget ); + void StopTalking ( void ); + BOOL ShouldSpeak( void ); + CUSTOM_SCHEDULES; + + virtual int Save( CSave &save ); + virtual int Restore( CRestore &restore ); + static TYPEDESCRIPTION m_SaveData[]; + + static const char *pAttackHitSounds[]; + static const char *pAttackMissSounds[]; + static const char *pAttackSounds[]; + static const char *pDieSounds[]; + static const char *pPainSounds[]; + static const char *pIdleSounds[]; + static const char *pAlertSounds[]; + + BOOL m_fCanHornetAttack; + float m_flNextHornetAttackCheck; + + float m_flNextPainTime; + + // three hacky fields for speech stuff. These don't really need to be saved. + float m_flNextSpeakTime; + float m_flNextWordTime; + int m_iLastWord; +}; +LINK_ENTITY_TO_CLASS( monster_alien_grunt, CAGrunt ); + +TYPEDESCRIPTION CAGrunt::m_SaveData[] = +{ + DEFINE_FIELD( CAGrunt, m_fCanHornetAttack, FIELD_BOOLEAN ), + DEFINE_FIELD( CAGrunt, m_flNextHornetAttackCheck, FIELD_TIME ), + DEFINE_FIELD( CAGrunt, m_flNextPainTime, FIELD_TIME ), + DEFINE_FIELD( CAGrunt, m_flNextSpeakTime, FIELD_TIME ), + DEFINE_FIELD( CAGrunt, m_flNextWordTime, FIELD_TIME ), + DEFINE_FIELD( CAGrunt, m_iLastWord, FIELD_INTEGER ), +}; + +IMPLEMENT_SAVERESTORE( CAGrunt, CSquadMonster ); + +const char *CAGrunt::pAttackHitSounds[] = +{ + "zombie/claw_strike1.wav", + "zombie/claw_strike2.wav", + "zombie/claw_strike3.wav", +}; + +const char *CAGrunt::pAttackMissSounds[] = +{ + "zombie/claw_miss1.wav", + "zombie/claw_miss2.wav", +}; + +const char *CAGrunt::pAttackSounds[] = +{ + "agrunt/ag_attack1.wav", + "agrunt/ag_attack2.wav", + "agrunt/ag_attack3.wav", +}; + +const char *CAGrunt::pDieSounds[] = +{ + "agrunt/ag_die1.wav", + "agrunt/ag_die4.wav", + "agrunt/ag_die5.wav", +}; + +const char *CAGrunt::pPainSounds[] = +{ + "agrunt/ag_pain1.wav", + "agrunt/ag_pain2.wav", + "agrunt/ag_pain3.wav", + "agrunt/ag_pain4.wav", + "agrunt/ag_pain5.wav", +}; + +const char *CAGrunt::pIdleSounds[] = +{ + "agrunt/ag_idle1.wav", + "agrunt/ag_idle2.wav", + "agrunt/ag_idle3.wav", + "agrunt/ag_idle4.wav", +}; + +const char *CAGrunt::pAlertSounds[] = +{ + "agrunt/ag_alert1.wav", + "agrunt/ag_alert3.wav", + "agrunt/ag_alert4.wav", + "agrunt/ag_alert5.wav", +}; + +//========================================================= +// IRelationship - overridden because Human Grunts are +// Alien Grunt's nemesis. +//========================================================= +int CAGrunt::IRelationship ( CBaseEntity *pTarget ) +{ + if ( FClassnameIs( pTarget->pev, "monster_human_grunt" ) ) + { + return R_NM; + } + + return CSquadMonster :: IRelationship( pTarget ); +} + +//========================================================= +// ISoundMask +//========================================================= +int CAGrunt :: ISoundMask ( void ) +{ + return bits_SOUND_WORLD | + bits_SOUND_COMBAT | + bits_SOUND_PLAYER | + bits_SOUND_DANGER; +} + +//========================================================= +// TraceAttack +//========================================================= +void CAGrunt :: TraceAttack( entvars_t *pevAttacker, float flDamage, Vector vecDir, TraceResult *ptr, int bitsDamageType) +{ + if ( ptr->iHitgroup == 10 && (bitsDamageType & (DMG_BULLET | DMG_SLASH | DMG_CLUB))) + { + // hit armor + if ( pev->dmgtime != gpGlobals->time || (RANDOM_LONG(0,10) < 1) ) + { + UTIL_Ricochet( ptr->vecEndPos, RANDOM_FLOAT( 1, 2) ); + pev->dmgtime = gpGlobals->time; + } + + if ( RANDOM_LONG( 0, 1 ) == 0 ) + { + Vector vecTracerDir = vecDir; + + vecTracerDir.x += RANDOM_FLOAT( -0.3, 0.3 ); + vecTracerDir.y += RANDOM_FLOAT( -0.3, 0.3 ); + vecTracerDir.z += RANDOM_FLOAT( -0.3, 0.3 ); + + vecTracerDir = vecTracerDir * -512; + + MESSAGE_BEGIN( MSG_PVS, SVC_TEMPENTITY, ptr->vecEndPos ); + WRITE_BYTE( TE_TRACER ); + WRITE_COORD( ptr->vecEndPos.x ); + WRITE_COORD( ptr->vecEndPos.y ); + WRITE_COORD( ptr->vecEndPos.z ); + + WRITE_COORD( vecTracerDir.x ); + WRITE_COORD( vecTracerDir.y ); + WRITE_COORD( vecTracerDir.z ); + MESSAGE_END(); + } + + flDamage -= 20; + if (flDamage <= 0) + flDamage = 0.1;// don't hurt the monster much, but allow bits_COND_LIGHT_DAMAGE to be generated + } + else + { + SpawnBlood(ptr->vecEndPos, BloodColor(), flDamage);// a little surface blood. + TraceBleed( flDamage, vecDir, ptr, bitsDamageType ); + } + + AddMultiDamage( pevAttacker, this, flDamage, bitsDamageType ); +} + +//========================================================= +// StopTalking - won't speak again for 10-20 seconds. +//========================================================= +void CAGrunt::StopTalking( void ) +{ + m_flNextWordTime = m_flNextSpeakTime = gpGlobals->time + 10 + RANDOM_LONG(0, 10); +} + +//========================================================= +// ShouldSpeak - Should this agrunt be talking? +//========================================================= +BOOL CAGrunt::ShouldSpeak( void ) +{ + if ( m_flNextSpeakTime > gpGlobals->time ) + { + // my time to talk is still in the future. + return FALSE; + } + + if ( pev->spawnflags & SF_MONSTER_GAG ) + { + if ( m_MonsterState != MONSTERSTATE_COMBAT ) + { + // if gagged, don't talk outside of combat. + // if not going to talk because of this, put the talk time + // into the future a bit, so we don't talk immediately after + // going into combat + m_flNextSpeakTime = gpGlobals->time + 3; + return FALSE; + } + } + + return TRUE; +} + +//========================================================= +// PrescheduleThink +//========================================================= +void CAGrunt :: PrescheduleThink ( void ) +{ + if ( ShouldSpeak() ) + { + if ( m_flNextWordTime < gpGlobals->time ) + { + int num = -1; + + do + { + num = RANDOM_LONG(0,ARRAYSIZE(pIdleSounds)-1); + } while( num == m_iLastWord ); + + m_iLastWord = num; + + // play a new sound + EMIT_SOUND ( ENT(pev), CHAN_VOICE, pIdleSounds[ num ], 1.0, ATTN_NORM ); + + // is this word our last? + if ( RANDOM_LONG( 1, 10 ) <= 1 ) + { + // stop talking. + StopTalking(); + } + else + { + m_flNextWordTime = gpGlobals->time + RANDOM_FLOAT( 0.5, 1 ); + } + } + } +} + +//========================================================= +// DieSound +//========================================================= +void CAGrunt :: DeathSound ( void ) +{ + StopTalking(); + + EMIT_SOUND ( ENT(pev), CHAN_VOICE, pDieSounds[RANDOM_LONG(0,ARRAYSIZE(pDieSounds)-1)], 1.0, ATTN_NORM ); +} + +//========================================================= +// AlertSound +//========================================================= +void CAGrunt :: AlertSound ( void ) +{ + StopTalking(); + + EMIT_SOUND ( ENT(pev), CHAN_VOICE, pAlertSounds[RANDOM_LONG(0,ARRAYSIZE(pAlertSounds)-1)], 1.0, ATTN_NORM ); +} + +//========================================================= +// AttackSound +//========================================================= +void CAGrunt :: AttackSound ( void ) +{ + StopTalking(); + + EMIT_SOUND ( ENT(pev), CHAN_VOICE, pAttackSounds[RANDOM_LONG(0,ARRAYSIZE(pAttackSounds)-1)], 1.0, ATTN_NORM ); +} + +//========================================================= +// PainSound +//========================================================= +void CAGrunt :: PainSound ( void ) +{ + if ( m_flNextPainTime > gpGlobals->time ) + { + return; + } + + m_flNextPainTime = gpGlobals->time + 0.6; + + StopTalking(); + + EMIT_SOUND ( ENT(pev), CHAN_VOICE, pPainSounds[RANDOM_LONG(0,ARRAYSIZE(pPainSounds)-1)], 1.0, ATTN_NORM ); +} + +//========================================================= +// Classify - indicates this monster's place in the +// relationship table. +//========================================================= +int CAGrunt :: Classify ( void ) +{ + return CLASS_ALIEN_MILITARY; +} + +//========================================================= +// SetYawSpeed - allows each sequence to have a different +// turn rate associated with it. +//========================================================= +void CAGrunt :: SetYawSpeed ( void ) +{ + int ys; + + switch ( m_Activity ) + { + case ACT_TURN_LEFT: + case ACT_TURN_RIGHT: + ys = 110; + break; + default: ys = 100; + } + + pev->yaw_speed = ys; +} + +//========================================================= +// HandleAnimEvent - catches the monster-specific messages +// that occur when tagged animation frames are played. +// +// Returns number of events handled, 0 if none. +//========================================================= +void CAGrunt :: HandleAnimEvent( MonsterEvent_t *pEvent ) +{ + switch( pEvent->event ) + { + case AGRUNT_AE_HORNET1: + case AGRUNT_AE_HORNET2: + case AGRUNT_AE_HORNET3: + case AGRUNT_AE_HORNET4: + case AGRUNT_AE_HORNET5: + { + // m_vecEnemyLKP should be center of enemy body + Vector vecArmPos, vecArmDir; + Vector vecDirToEnemy; + Vector angDir; + + if (HasConditions( bits_COND_SEE_ENEMY)) + { + vecDirToEnemy = ( ( m_vecEnemyLKP ) - pev->origin ); + angDir = UTIL_VecToAngles( vecDirToEnemy ); + vecDirToEnemy = vecDirToEnemy.Normalize(); + } + else + { + angDir = pev->angles; + UTIL_MakeAimVectors( angDir ); + vecDirToEnemy = gpGlobals->v_forward; + } + + pev->effects = EF_MUZZLEFLASH; + + // make angles +-180 + if (angDir.x > 180) + { + angDir.x = angDir.x - 360; + } + + SetBlending( 0, angDir.x ); + GetAttachment( 0, vecArmPos, vecArmDir ); + + vecArmPos = vecArmPos + vecDirToEnemy * 32; + MESSAGE_BEGIN( MSG_PVS, SVC_TEMPENTITY, vecArmPos ); + WRITE_BYTE( TE_SPRITE ); + WRITE_COORD( vecArmPos.x ); // pos + WRITE_COORD( vecArmPos.y ); + WRITE_COORD( vecArmPos.z ); + WRITE_SHORT( iAgruntMuzzleFlash ); // model + WRITE_BYTE( 6 ); // size * 10 + WRITE_BYTE( 128 ); // brightness + MESSAGE_END(); + + CBaseEntity *pHornet = CBaseEntity::Create( "hornet", vecArmPos, UTIL_VecToAngles( vecDirToEnemy ), edict() ); + UTIL_MakeVectors ( pHornet->pev->angles ); + pHornet->pev->velocity = gpGlobals->v_forward * 300; + + + + switch ( RANDOM_LONG ( 0 , 2 ) ) + { + case 0: EMIT_SOUND_DYN ( ENT(pev), CHAN_WEAPON, "agrunt/ag_fire1.wav", 1.0, ATTN_NORM, 0, 100 ); break; + case 1: EMIT_SOUND_DYN ( ENT(pev), CHAN_WEAPON, "agrunt/ag_fire2.wav", 1.0, ATTN_NORM, 0, 100 ); break; + case 2: EMIT_SOUND_DYN ( ENT(pev), CHAN_WEAPON, "agrunt/ag_fire3.wav", 1.0, ATTN_NORM, 0, 100 ); break; + } + + CBaseMonster *pHornetMonster = pHornet->MyMonsterPointer(); + + if ( pHornetMonster ) + { + pHornetMonster->m_hEnemy = m_hEnemy; + } + } + break; + + case AGRUNT_AE_LEFT_FOOT: + switch (RANDOM_LONG(0,1)) + { + // left foot + case 0: EMIT_SOUND_DYN ( ENT(pev), CHAN_BODY, "player/pl_ladder2.wav", 1, ATTN_NORM, 0, 70 ); break; + case 1: EMIT_SOUND_DYN ( ENT(pev), CHAN_BODY, "player/pl_ladder4.wav", 1, ATTN_NORM, 0, 70 ); break; + } + break; + case AGRUNT_AE_RIGHT_FOOT: + // right foot + switch (RANDOM_LONG(0,1)) + { + case 0: EMIT_SOUND_DYN ( ENT(pev), CHAN_BODY, "player/pl_ladder1.wav", 1, ATTN_NORM, 0, 70 ); break; + case 1: EMIT_SOUND_DYN ( ENT(pev), CHAN_BODY, "player/pl_ladder3.wav", 1, ATTN_NORM, 0 ,70); break; + } + break; + + case AGRUNT_AE_LEFT_PUNCH: + { + CBaseEntity *pHurt = CheckTraceHullAttack( AGRUNT_MELEE_DIST, gSkillData.agruntDmgPunch, DMG_CLUB ); + + if ( pHurt ) + { + pHurt->pev->punchangle.y = -25; + pHurt->pev->punchangle.x = 8; + + // OK to use gpGlobals without calling MakeVectors, cause CheckTraceHullAttack called it above. + if ( pHurt->IsPlayer() ) + { + // this is a player. Knock him around. + pHurt->pev->velocity = pHurt->pev->velocity + gpGlobals->v_right * 250; + } + + EMIT_SOUND_DYN ( ENT(pev), CHAN_WEAPON, pAttackHitSounds[ RANDOM_LONG(0,ARRAYSIZE(pAttackHitSounds)-1) ], 1.0, ATTN_NORM, 0, 100 + RANDOM_LONG(-5,5) ); + + Vector vecArmPos, vecArmAng; + GetAttachment( 0, vecArmPos, vecArmAng ); + SpawnBlood(vecArmPos, pHurt->BloodColor(), 25);// a little surface blood. + } + else + { + // Play a random attack miss sound + EMIT_SOUND_DYN ( ENT(pev), CHAN_WEAPON, pAttackMissSounds[ RANDOM_LONG(0,ARRAYSIZE(pAttackMissSounds)-1) ], 1.0, ATTN_NORM, 0, 100 + RANDOM_LONG(-5,5) ); + } + } + break; + + case AGRUNT_AE_RIGHT_PUNCH: + { + CBaseEntity *pHurt = CheckTraceHullAttack( AGRUNT_MELEE_DIST, gSkillData.agruntDmgPunch, DMG_CLUB ); + + if ( pHurt ) + { + pHurt->pev->punchangle.y = 25; + pHurt->pev->punchangle.x = 8; + + // OK to use gpGlobals without calling MakeVectors, cause CheckTraceHullAttack called it above. + if ( pHurt->IsPlayer() ) + { + // this is a player. Knock him around. + pHurt->pev->velocity = pHurt->pev->velocity + gpGlobals->v_right * -250; + } + + EMIT_SOUND_DYN ( ENT(pev), CHAN_WEAPON, pAttackHitSounds[ RANDOM_LONG(0,ARRAYSIZE(pAttackHitSounds)-1) ], 1.0, ATTN_NORM, 0, 100 + RANDOM_LONG(-5,5) ); + + Vector vecArmPos, vecArmAng; + GetAttachment( 0, vecArmPos, vecArmAng ); + SpawnBlood(vecArmPos, pHurt->BloodColor(), 25);// a little surface blood. + } + else + { + // Play a random attack miss sound + EMIT_SOUND_DYN ( ENT(pev), CHAN_WEAPON, pAttackMissSounds[ RANDOM_LONG(0,ARRAYSIZE(pAttackMissSounds)-1) ], 1.0, ATTN_NORM, 0, 100 + RANDOM_LONG(-5,5) ); + } + } + break; + + default: + CSquadMonster::HandleAnimEvent( pEvent ); + break; + } +} + +//========================================================= +// Spawn +//========================================================= +void CAGrunt :: Spawn() +{ + Precache( ); + + SET_MODEL(ENT(pev), "models/agrunt.mdl"); + UTIL_SetSize(pev, Vector(-32, -32, 0), Vector(32, 32, 64)); + + pev->solid = SOLID_SLIDEBOX; + pev->movetype = MOVETYPE_STEP; + m_bloodColor = BLOOD_COLOR_GREEN; + pev->effects = 0; + pev->health = gSkillData.agruntHealth; + m_flFieldOfView = 0.2;// indicates the width of this monster's forward view cone ( as a dotproduct result ) + m_MonsterState = MONSTERSTATE_NONE; + m_afCapability = 0; + m_afCapability |= bits_CAP_SQUAD; + + m_HackedGunPos = Vector( 24, 64, 48 ); + + m_flNextSpeakTime = m_flNextWordTime = gpGlobals->time + 10 + RANDOM_LONG(0, 10); + + + MonsterInit(); +} + +//========================================================= +// Precache - precaches all resources this monster needs +//========================================================= +void CAGrunt :: Precache() +{ + int i; + + PRECACHE_MODEL("models/agrunt.mdl"); + + for ( i = 0; i < ARRAYSIZE( pAttackHitSounds ); i++ ) + PRECACHE_SOUND((char *)pAttackHitSounds[i]); + + for ( i = 0; i < ARRAYSIZE( pAttackMissSounds ); i++ ) + PRECACHE_SOUND((char *)pAttackMissSounds[i]); + + for ( i = 0; i < ARRAYSIZE( pIdleSounds ); i++ ) + PRECACHE_SOUND((char *)pIdleSounds[i]); + + for ( i = 0; i < ARRAYSIZE( pDieSounds ); i++ ) + PRECACHE_SOUND((char *)pDieSounds[i]); + + for ( i = 0; i < ARRAYSIZE( pPainSounds ); i++ ) + PRECACHE_SOUND((char *)pPainSounds[i]); + + for ( i = 0; i < ARRAYSIZE( pAttackSounds ); i++ ) + PRECACHE_SOUND((char *)pAttackSounds[i]); + + for ( i = 0; i < ARRAYSIZE( pAlertSounds ); i++ ) + PRECACHE_SOUND((char *)pAlertSounds[i]); + + + PRECACHE_SOUND( "hassault/hw_shoot1.wav" ); + + iAgruntMuzzleFlash = PRECACHE_MODEL( "sprites/muz4.spr" ); + + UTIL_PrecacheOther( "hornet" ); +} + +//========================================================= +// AI Schedules Specific to this monster +//========================================================= + +//========================================================= +// Fail Schedule +//========================================================= +Task_t tlAGruntFail[] = +{ + { TASK_STOP_MOVING, 0 }, + { TASK_SET_ACTIVITY, (float)ACT_IDLE }, + { TASK_WAIT, (float)2 }, + { TASK_WAIT_PVS, (float)0 }, +}; + +Schedule_t slAGruntFail[] = +{ + { + tlAGruntFail, + ARRAYSIZE ( tlAGruntFail ), + bits_COND_CAN_RANGE_ATTACK1 | + bits_COND_CAN_MELEE_ATTACK1, + 0, + "AGrunt Fail" + }, +}; + +//========================================================= +// Combat Fail Schedule +//========================================================= +Task_t tlAGruntCombatFail[] = +{ + { TASK_STOP_MOVING, 0 }, + { TASK_SET_ACTIVITY, (float)ACT_IDLE }, + { TASK_WAIT_FACE_ENEMY, (float)2 }, + { TASK_WAIT_PVS, (float)0 }, +}; + +Schedule_t slAGruntCombatFail[] = +{ + { + tlAGruntCombatFail, + ARRAYSIZE ( tlAGruntCombatFail ), + bits_COND_CAN_RANGE_ATTACK1 | + bits_COND_CAN_MELEE_ATTACK1, + 0, + "AGrunt Combat Fail" + }, +}; + +//========================================================= +// Standoff schedule. Used in combat when a monster is +// hiding in cover or the enemy has moved out of sight. +// Should we look around in this schedule? +//========================================================= +Task_t tlAGruntStandoff[] = +{ + { TASK_STOP_MOVING, (float)0 }, + { TASK_SET_ACTIVITY, (float)ACT_IDLE }, + { TASK_WAIT_FACE_ENEMY, (float)2 }, +}; + +Schedule_t slAGruntStandoff[] = +{ + { + tlAGruntStandoff, + ARRAYSIZE ( tlAGruntStandoff ), + bits_COND_CAN_RANGE_ATTACK1 | + bits_COND_CAN_MELEE_ATTACK1 | + bits_COND_SEE_ENEMY | + bits_COND_NEW_ENEMY | + bits_COND_HEAR_SOUND, + + bits_SOUND_DANGER, + "Agrunt Standoff" + } +}; + +//========================================================= +// Suppress +//========================================================= +Task_t tlAGruntSuppressHornet[] = +{ + { TASK_STOP_MOVING, (float)0 }, + { TASK_RANGE_ATTACK1, (float)0 }, +}; + +Schedule_t slAGruntSuppress[] = +{ + { + tlAGruntSuppressHornet, + ARRAYSIZE ( tlAGruntSuppressHornet ), + 0, + 0, + "AGrunt Suppress Hornet", + }, +}; + +//========================================================= +// primary range attacks +//========================================================= +Task_t tlAGruntRangeAttack1[] = +{ + { TASK_STOP_MOVING, (float)0 }, + { TASK_FACE_ENEMY, (float)0 }, + { TASK_RANGE_ATTACK1, (float)0 }, +}; + +Schedule_t slAGruntRangeAttack1[] = +{ + { + tlAGruntRangeAttack1, + ARRAYSIZE ( tlAGruntRangeAttack1 ), + bits_COND_NEW_ENEMY | + bits_COND_ENEMY_DEAD | + bits_COND_HEAVY_DAMAGE, + + 0, + "AGrunt Range Attack1" + }, +}; + + +Task_t tlAGruntHiddenRangeAttack1[] = +{ + { TASK_SET_FAIL_SCHEDULE, (float)SCHED_STANDOFF }, + { TASK_AGRUNT_SETUP_HIDE_ATTACK, 0 }, + { TASK_STOP_MOVING, 0 }, + { TASK_FACE_IDEAL, 0 }, + { TASK_RANGE_ATTACK1_NOTURN, (float)0 }, +}; + +Schedule_t slAGruntHiddenRangeAttack[] = +{ + { + tlAGruntHiddenRangeAttack1, + ARRAYSIZE ( tlAGruntHiddenRangeAttack1 ), + bits_COND_NEW_ENEMY | + bits_COND_HEAVY_DAMAGE | + bits_COND_HEAR_SOUND, + + bits_SOUND_DANGER, + "AGrunt Hidden Range Attack1" + }, +}; + +//========================================================= +// Take cover from enemy! Tries lateral cover before node +// cover! +//========================================================= +Task_t tlAGruntTakeCoverFromEnemy[] = +{ + { TASK_STOP_MOVING, (float)0 }, + { TASK_WAIT, (float)0.2 }, + { TASK_FIND_COVER_FROM_ENEMY, (float)0 }, + { TASK_RUN_PATH, (float)0 }, + { TASK_WAIT_FOR_MOVEMENT, (float)0 }, + { TASK_REMEMBER, (float)bits_MEMORY_INCOVER }, + { TASK_FACE_ENEMY, (float)0 }, +}; + +Schedule_t slAGruntTakeCoverFromEnemy[] = +{ + { + tlAGruntTakeCoverFromEnemy, + ARRAYSIZE ( tlAGruntTakeCoverFromEnemy ), + bits_COND_NEW_ENEMY, + 0, + "AGruntTakeCoverFromEnemy" + }, +}; + +//========================================================= +// Victory dance! +//========================================================= +Task_t tlAGruntVictoryDance[] = +{ + { TASK_STOP_MOVING, (float)0 }, + { TASK_SET_FAIL_SCHEDULE, (float)SCHED_AGRUNT_THREAT_DISPLAY }, + { TASK_WAIT, (float)0.2 }, + { TASK_AGRUNT_GET_PATH_TO_ENEMY_CORPSE, (float)0 }, + { TASK_WALK_PATH, (float)0 }, + { TASK_WAIT_FOR_MOVEMENT, (float)0 }, + { TASK_FACE_ENEMY, (float)0 }, + { TASK_PLAY_SEQUENCE, (float)ACT_CROUCH }, + { TASK_PLAY_SEQUENCE, (float)ACT_VICTORY_DANCE }, + { TASK_PLAY_SEQUENCE, (float)ACT_VICTORY_DANCE }, + { TASK_PLAY_SEQUENCE, (float)ACT_STAND }, + { TASK_PLAY_SEQUENCE, (float)ACT_THREAT_DISPLAY }, + { TASK_PLAY_SEQUENCE, (float)ACT_CROUCH }, + { TASK_PLAY_SEQUENCE, (float)ACT_VICTORY_DANCE }, + { TASK_PLAY_SEQUENCE, (float)ACT_VICTORY_DANCE }, + { TASK_PLAY_SEQUENCE, (float)ACT_VICTORY_DANCE }, + { TASK_PLAY_SEQUENCE, (float)ACT_VICTORY_DANCE }, + { TASK_PLAY_SEQUENCE, (float)ACT_VICTORY_DANCE }, + { TASK_PLAY_SEQUENCE, (float)ACT_STAND }, +}; + +Schedule_t slAGruntVictoryDance[] = +{ + { + tlAGruntVictoryDance, + ARRAYSIZE ( tlAGruntVictoryDance ), + bits_COND_NEW_ENEMY | + bits_COND_LIGHT_DAMAGE | + bits_COND_HEAVY_DAMAGE, + 0, + "AGruntVictoryDance" + }, +}; + +//========================================================= +//========================================================= +Task_t tlAGruntThreatDisplay[] = +{ + { TASK_STOP_MOVING, (float)0 }, + { TASK_FACE_ENEMY, (float)0 }, + { TASK_PLAY_SEQUENCE, (float)ACT_THREAT_DISPLAY }, +}; + +Schedule_t slAGruntThreatDisplay[] = +{ + { + tlAGruntThreatDisplay, + ARRAYSIZE ( tlAGruntThreatDisplay ), + bits_COND_NEW_ENEMY | + bits_COND_LIGHT_DAMAGE | + bits_COND_HEAVY_DAMAGE, + + bits_SOUND_PLAYER | + bits_SOUND_COMBAT | + bits_SOUND_WORLD, + "AGruntThreatDisplay" + }, +}; + +DEFINE_CUSTOM_SCHEDULES( CAGrunt ) +{ + slAGruntFail, + slAGruntCombatFail, + slAGruntStandoff, + slAGruntSuppress, + slAGruntRangeAttack1, + slAGruntHiddenRangeAttack, + slAGruntTakeCoverFromEnemy, + slAGruntVictoryDance, + slAGruntThreatDisplay, +}; + +IMPLEMENT_CUSTOM_SCHEDULES( CAGrunt, CSquadMonster ); + +//========================================================= +// FCanCheckAttacks - this is overridden for alien grunts +// because they can use their smart weapons against unseen +// enemies. Base class doesn't attack anyone it can't see. +//========================================================= +BOOL CAGrunt :: FCanCheckAttacks ( void ) +{ + if ( !HasConditions( bits_COND_ENEMY_TOOFAR ) ) + { + return TRUE; + } + else + { + return FALSE; + } +} + +//========================================================= +// CheckMeleeAttack1 - alien grunts zap the crap out of +// any enemy that gets too close. +//========================================================= +BOOL CAGrunt :: CheckMeleeAttack1 ( float flDot, float flDist ) +{ + if ( HasConditions ( bits_COND_SEE_ENEMY ) && flDist <= AGRUNT_MELEE_DIST && flDot >= 0.6 && m_hEnemy != NULL ) + { + return TRUE; + } + return FALSE; +} + +//========================================================= +// CheckRangeAttack1 +// +// !!!LATER - we may want to load balance this. Several +// tracelines are done, so we may not want to do this every +// server frame. Definitely not while firing. +//========================================================= +BOOL CAGrunt :: CheckRangeAttack1 ( float flDot, float flDist ) +{ + if ( gpGlobals->time < m_flNextHornetAttackCheck ) + { + return m_fCanHornetAttack; + } + + if ( HasConditions( bits_COND_SEE_ENEMY ) && flDist >= AGRUNT_MELEE_DIST && flDist <= 1024 && flDot >= 0.5 && NoFriendlyFire() ) + { + TraceResult tr; + Vector vecArmPos, vecArmDir; + + // verify that a shot fired from the gun will hit the enemy before the world. + // !!!LATER - we may wish to do something different for projectile weapons as opposed to instant-hit + UTIL_MakeVectors( pev->angles ); + GetAttachment( 0, vecArmPos, vecArmDir ); +// UTIL_TraceLine( vecArmPos, vecArmPos + gpGlobals->v_forward * 256, ignore_monsters, ENT(pev), &tr); + UTIL_TraceLine( vecArmPos, m_hEnemy->BodyTarget(vecArmPos), dont_ignore_monsters, ENT(pev), &tr); + + if ( tr.flFraction == 1.0 || tr.pHit == m_hEnemy->edict() ) + { + m_flNextHornetAttackCheck = gpGlobals->time + RANDOM_FLOAT( 2, 5 ); + m_fCanHornetAttack = TRUE; + return m_fCanHornetAttack; + } + } + + m_flNextHornetAttackCheck = gpGlobals->time + 0.2;// don't check for half second if this check wasn't successful + m_fCanHornetAttack = FALSE; + return m_fCanHornetAttack; +} + +//========================================================= +// StartTask +//========================================================= +void CAGrunt :: StartTask ( Task_t *pTask ) +{ + switch ( pTask->iTask ) + { + case TASK_AGRUNT_GET_PATH_TO_ENEMY_CORPSE: + { + UTIL_MakeVectors( pev->angles ); + if ( BuildRoute ( m_vecEnemyLKP - gpGlobals->v_forward * 50, bits_MF_TO_LOCATION, NULL ) ) + { + TaskComplete(); + } + else + { + ALERT ( at_aiconsole, "AGruntGetPathToEnemyCorpse failed!!\n" ); + TaskFail(); + } + } + break; + + case TASK_AGRUNT_SETUP_HIDE_ATTACK: + // alien grunt shoots hornets back out into the open from a concealed location. + // try to find a spot to throw that gives the smart weapon a good chance of finding the enemy. + // ideally, this spot is along a line that is perpendicular to a line drawn from the agrunt to the enemy. + + CBaseMonster *pEnemyMonsterPtr; + + pEnemyMonsterPtr = m_hEnemy->MyMonsterPointer(); + + if ( pEnemyMonsterPtr ) + { + Vector vecCenter; + TraceResult tr; + BOOL fSkip; + + fSkip = FALSE; + vecCenter = Center(); + + UTIL_VecToAngles( m_vecEnemyLKP - pev->origin ); + + UTIL_TraceLine( Center() + gpGlobals->v_forward * 128, m_vecEnemyLKP, ignore_monsters, ENT(pev), &tr); + if ( tr.flFraction == 1.0 ) + { + MakeIdealYaw ( pev->origin + gpGlobals->v_right * 128 ); + fSkip = TRUE; + TaskComplete(); + } + + if ( !fSkip ) + { + UTIL_TraceLine( Center() - gpGlobals->v_forward * 128, m_vecEnemyLKP, ignore_monsters, ENT(pev), &tr); + if ( tr.flFraction == 1.0 ) + { + MakeIdealYaw ( pev->origin - gpGlobals->v_right * 128 ); + fSkip = TRUE; + TaskComplete(); + } + } + + if ( !fSkip ) + { + UTIL_TraceLine( Center() + gpGlobals->v_forward * 256, m_vecEnemyLKP, ignore_monsters, ENT(pev), &tr); + if ( tr.flFraction == 1.0 ) + { + MakeIdealYaw ( pev->origin + gpGlobals->v_right * 256 ); + fSkip = TRUE; + TaskComplete(); + } + } + + if ( !fSkip ) + { + UTIL_TraceLine( Center() - gpGlobals->v_forward * 256, m_vecEnemyLKP, ignore_monsters, ENT(pev), &tr); + if ( tr.flFraction == 1.0 ) + { + MakeIdealYaw ( pev->origin - gpGlobals->v_right * 256 ); + fSkip = TRUE; + TaskComplete(); + } + } + + if ( !fSkip ) + { + TaskFail(); + } + } + else + { + ALERT ( at_aiconsole, "AGRunt - no enemy monster ptr!!!\n" ); + TaskFail(); + } + break; + + default: + CSquadMonster :: StartTask ( pTask ); + break; + } +} + +//========================================================= +// GetSchedule - Decides which type of schedule best suits +// the monster's current state and conditions. Then calls +// monster's member function to get a pointer to a schedule +// of the proper type. +//========================================================= +Schedule_t *CAGrunt :: GetSchedule ( void ) +{ + if ( HasConditions(bits_COND_HEAR_SOUND) ) + { + CSound *pSound; + pSound = PBestSound(); + + ASSERT( pSound != NULL ); + if ( pSound && (pSound->m_iType & bits_SOUND_DANGER) ) + { + // dangerous sound nearby! + return GetScheduleOfType( SCHED_TAKE_COVER_FROM_BEST_SOUND ); + } + } + + switch ( m_MonsterState ) + { + case MONSTERSTATE_COMBAT: + { +// dead enemy + if ( HasConditions( bits_COND_ENEMY_DEAD ) ) + { + // call base class, all code to handle dead enemies is centralized there. + return CBaseMonster :: GetSchedule(); + } + + if ( HasConditions(bits_COND_NEW_ENEMY) ) + { + return GetScheduleOfType( SCHED_WAKE_ANGRY ); + } + + // zap player! + if ( HasConditions ( bits_COND_CAN_MELEE_ATTACK1 ) ) + { + AttackSound();// this is a total hack. Should be parto f the schedule + return GetScheduleOfType ( SCHED_MELEE_ATTACK1 ); + } + + if ( HasConditions ( bits_COND_HEAVY_DAMAGE ) ) + { + return GetScheduleOfType( SCHED_SMALL_FLINCH ); + } + + // can attack + if ( HasConditions ( bits_COND_CAN_RANGE_ATTACK1 ) && OccupySlot ( bits_SLOTS_AGRUNT_HORNET ) ) + { + return GetScheduleOfType ( SCHED_RANGE_ATTACK1 ); + } + + if ( OccupySlot ( bits_SLOT_AGRUNT_CHASE ) ) + { + return GetScheduleOfType ( SCHED_CHASE_ENEMY ); + } + + return GetScheduleOfType ( SCHED_STANDOFF ); + } + } + + return CSquadMonster :: GetSchedule(); +} + +//========================================================= +//========================================================= +Schedule_t* CAGrunt :: GetScheduleOfType ( int Type ) +{ + switch ( Type ) + { + case SCHED_TAKE_COVER_FROM_ENEMY: + return &slAGruntTakeCoverFromEnemy[ 0 ]; + break; + + case SCHED_RANGE_ATTACK1: + if ( HasConditions( bits_COND_SEE_ENEMY ) ) + { + //normal attack + return &slAGruntRangeAttack1[ 0 ]; + } + else + { + // attack an unseen enemy + // return &slAGruntHiddenRangeAttack[ 0 ]; + return &slAGruntRangeAttack1[ 0 ]; + } + break; + + case SCHED_AGRUNT_THREAT_DISPLAY: + return &slAGruntThreatDisplay[ 0 ]; + break; + + case SCHED_AGRUNT_SUPPRESS: + return &slAGruntSuppress[ 0 ]; + break; + + case SCHED_STANDOFF: + return &slAGruntStandoff[ 0 ]; + break; + + case SCHED_VICTORY_DANCE: + return &slAGruntVictoryDance[ 0 ]; + break; + + case SCHED_FAIL: + // no fail schedule specified, so pick a good generic one. + { + if ( m_hEnemy != NULL ) + { + // I have an enemy + // !!!LATER - what if this enemy is really far away and i'm chasing him? + // this schedule will make me stop, face his last known position for 2 + // seconds, and then try to move again + return &slAGruntCombatFail[ 0 ]; + } + + return &slAGruntFail[ 0 ]; + } + break; + + } + + return CSquadMonster :: GetScheduleOfType( Type ); +} + diff --git a/spirit/airtank.cpp b/dlls/airtank.cpp similarity index 91% rename from spirit/airtank.cpp rename to dlls/airtank.cpp index bf6240d0..a356744b 100644 --- a/spirit/airtank.cpp +++ b/dlls/airtank.cpp @@ -56,10 +56,10 @@ void CAirtank :: Spawn( void ) SET_MODEL(ENT(pev), "models/w_oxygen.mdl"); UTIL_SetSize(pev, Vector( -16, -16, 0), Vector(16, 16, 36)); - UTIL_SetOrigin( this, pev->origin ); + UTIL_SetOrigin( pev, pev->origin ); - SetTouch(&CAirtank :: TankTouch ); - SetThink(&CAirtank :: TankThink ); + SetTouch( TankTouch ); + SetThink( TankThink ); pev->flags |= FL_MONSTER; pev->takedamage = DAMAGE_YES; @@ -104,7 +104,7 @@ void CAirtank::TankTouch( CBaseEntity *pOther ) EMIT_SOUND( ENT(pev), CHAN_BODY, "player/pl_swim2.wav", 1.0, ATTN_NORM ); return; } - + // give player 12 more seconds of air pOther->pev->air_finished = gpGlobals->time + 12; @@ -112,7 +112,7 @@ void CAirtank::TankTouch( CBaseEntity *pOther ) EMIT_SOUND( ENT(pev), CHAN_VOICE, "doors/aliendoor3.wav", 1.0, ATTN_NORM ); // recharge airtank in 30 seconds - SetNextThink( 30 ); + pev->nextthink = gpGlobals->time + 30; m_state = 0; SUB_UseTargets( this, USE_TOGGLE, 1 ); } diff --git a/spirit/animating.cpp b/dlls/animating.cpp similarity index 94% rename from spirit/animating.cpp rename to dlls/animating.cpp index c9e9c6f2..1a6595eb 100644 --- a/spirit/animating.cpp +++ b/dlls/animating.cpp @@ -246,15 +246,6 @@ int CBaseAnimating :: GetBodygroup( int iGroup ) return ::GetBodygroup( GET_MODEL_PTR( ENT(pev) ), pev, iGroup ); } -int CBaseAnimating :: GetBoneCount( void ) -{ - return ::GetBoneCount( GET_MODEL_PTR(ENT(pev)) ); -} - -void CBaseAnimating :: SetBones( float (*data)[3], int datasize ) -{ - ::SetBones( GET_MODEL_PTR( ENT(pev) ), data, datasize ); -} int CBaseAnimating :: ExtractBbox( int sequence, Vector &mins, Vector &maxs ) { diff --git a/spirit/animation.cpp b/dlls/animation.cpp similarity index 87% rename from spirit/animation.cpp rename to dlls/animation.cpp index ff5b5e33..6db230d1 100644 --- a/spirit/animation.cpp +++ b/dlls/animation.cpp @@ -17,10 +17,14 @@ #include #include "extdll.h" -#include "const.h" #include "studio.h" #include "util.h" + +#ifndef ACTIVITY_H +#include "activity.h" +#endif + #include "activitymap.h" #ifndef ANIMATION_H @@ -120,7 +124,7 @@ void GetEyePosition ( void *pmodel, Vector &vecEyePosition ) if ( !pstudiohdr ) { - ALERT ( at_debug, "GetEyePosition() Can't get pstudiohdr ptr!\n" ); + ALERT ( at_console, "GetEyePosition() Can't get pstudiohdr ptr!\n" ); return; } @@ -189,7 +193,10 @@ void SequencePrecache( void *pmodel, const char *pSequenceName ) ALERT( at_error, "Bad sound event %d in sequence %s :: %s (sound is \"%s\")\n", pevent[i].event, pstudiohdr->name, pSequenceName, pevent[i].options ); } + // g-cont. old code crash server when using StringTable + // new code working fine in both modes PRECACHE_SOUND( (char *)STRING( ALLOC_STRING( pevent[i].options )) ); +// PRECACHE_SOUND( (char *)(gpGlobals->pStringBase + ALLOC_STRING(pevent[i].options) ) ); } } } @@ -303,8 +310,7 @@ float SetController( void *pmodel, entvars_t *pev, int iController, float flValu mstudiobonecontroller_t *pbonecontroller = (mstudiobonecontroller_t *)((byte *)pstudiohdr + pstudiohdr->bonecontrollerindex); // find first controller that matches the index - int i = 0; - for (i = 0; i < pstudiohdr->numbonecontrollers; i++, pbonecontroller++) + for (int i = 0; i < pstudiohdr->numbonecontrollers; i++, pbonecontroller++) { if (pbonecontroller->index == iController) break; @@ -454,7 +460,7 @@ int FindTransition( void *pmodel, int iEndingAnim, int iGoalAnim, int *piDir ) } } - ALERT( at_debug, "error in transition graph" ); + ALERT( at_console, "error in transition graph" ); return iGoalAnim; } @@ -499,50 +505,4 @@ int GetBodygroup( void *pmodel, entvars_t *pev, int iGroup ) int iCurrent = (pev->body / pbodypart->base) % pbodypart->nummodels; return iCurrent; -} - -//LRC -int GetBoneCount( void *pmodel ) -{ - studiohdr_t *pstudiohdr; - - pstudiohdr = (studiohdr_t *)pmodel; - if (!pstudiohdr) - { - ALERT(at_error, "Bad header in SetBones!\n"); - return 0; - } - - return pstudiohdr->numbones; -} - -#ifndef min -#define min(a,b) (((a) < (b)) ? (a) : (b)) -#endif - -//LRC -void SetBones( void *pmodel, float (*data)[3], int datasize) -{ - studiohdr_t *pstudiohdr; - - pstudiohdr = (studiohdr_t *)pmodel; - if (!pstudiohdr) - { - ALERT(at_error, "Bad header in SetBones!\n"); - return; - } - - mstudiobone_t *pbone = (mstudiobone_t *)((byte *)pstudiohdr + pstudiohdr->boneindex); - -// ALERT(at_console, "List begins:\n"); - int j; - int limit = min(pstudiohdr->numbones, datasize); - // go through the bones - for (int i = 0; i < limit; i++, pbone++) - { -// ALERT(at_console, " %s\n", pbone->name); - for (j = 0; j < 3; j++) - pbone->value[j] = data[i][j]; - } -// ALERT(at_console, "List ends.\n"); -} +} \ No newline at end of file diff --git a/spirit/animation.h b/dlls/animation.h similarity index 88% rename from spirit/animation.h rename to dlls/animation.h index 232e3218..e835fcac 100644 --- a/spirit/animation.h +++ b/dlls/animation.h @@ -17,7 +17,9 @@ #define ACTIVITY_NOT_AVAILABLE -1 +#ifndef MONSTEREVENT_H #include "monsterevent.h" +#endif extern int IsSoundEvent( int eventNumber ); @@ -35,15 +37,10 @@ int FindTransition( void *pmodel, int iEndingAnim, int iGoalAnim, int *piDir ); void SetBodygroup( void *pmodel, entvars_t *pev, int iGroup, int iValue ); int GetBodygroup( void *pmodel, entvars_t *pev, int iGroup ); -//LRC -void SetBones( void *pmodel, float (*data)[3], int datasize ); -int GetBoneCount( void *pmodel ); -int GetSequenceFrames( void *pmodel, entvars_t *pev ); //LRC - int GetAnimationEvent( void *pmodel, entvars_t *pev, MonsterEvent_t *pMonsterEvent, float flStart, float flEnd, int index ); int ExtractBbox( void *pmodel, int sequence, Vector &mins, Vector &maxs ); -// From /common/ref_studio.h +// From /engine/studio.h #define STUDIO_LOOPING 0x0001 diff --git a/spirit/apache.cpp b/dlls/apache.cpp similarity index 90% rename from spirit/apache.cpp rename to dlls/apache.cpp index 885a3afe..2b8d4edd 100644 --- a/spirit/apache.cpp +++ b/dlls/apache.cpp @@ -121,17 +121,13 @@ void CApache :: Spawn( void ) pev->movetype = MOVETYPE_FLY; pev->solid = SOLID_BBOX; - if (pev->model) - SET_MODEL(ENT(pev), STRING(pev->model)); //LRC - else - SET_MODEL(ENT(pev), "models/apache.mdl"); + SET_MODEL(ENT(pev), "models/apache.mdl"); UTIL_SetSize( pev, Vector( -32, -32, -64 ), Vector( 32, 32, 0 ) ); - UTIL_SetOrigin( this, pev->origin ); + UTIL_SetOrigin( pev, pev->origin ); pev->flags |= FL_MONSTER; pev->takedamage = DAMAGE_AIM; - if (pev->health == 0) - pev->health = gSkillData.apacheHealth; + pev->health = gSkillData.apacheHealth; m_flFieldOfView = -0.707; // 270 degrees @@ -143,13 +139,13 @@ void CApache :: Spawn( void ) if (pev->spawnflags & SF_WAITFORTRIGGER) { - SetUse(&CApache :: StartupUse ); + SetUse( StartupUse ); } else { - SetThink(&CApache :: HuntThink ); - SetTouch(&CApache :: FlyTouch ); - SetNextThink( 1.0 ); + SetThink( HuntThink ); + SetTouch( FlyTouch ); + pev->nextthink = gpGlobals->time + 1.0; } m_iRockets = 10; @@ -158,10 +154,7 @@ void CApache :: Spawn( void ) void CApache::Precache( void ) { - if (pev->model) - PRECACHE_MODEL((char*)STRING(pev->model)); //LRC - else - PRECACHE_MODEL("models/apache.mdl"); + PRECACHE_MODEL("models/apache.mdl"); PRECACHE_SOUND("apache/ap_rotor1.wav"); PRECACHE_SOUND("apache/ap_rotor2.wav"); @@ -187,15 +180,15 @@ void CApache::Precache( void ) void CApache::NullThink( void ) { StudioFrameAdvance( ); - SetNextThink( 0.5 ); + pev->nextthink = gpGlobals->time + 0.5; } void CApache::StartupUse( CBaseEntity *pActivator, CBaseEntity *pCaller, USE_TYPE useType, float value ) { - SetThink(&CApache:: HuntThink ); - SetTouch(&CApache:: FlyTouch ); - SetNextThink( 0.1 ); + SetThink( HuntThink ); + SetTouch( FlyTouch ); + pev->nextthink = gpGlobals->time + 0.1; SetUse( NULL ); } @@ -207,9 +200,9 @@ void CApache :: Killed( entvars_t *pevAttacker, int iGib ) STOP_SOUND( ENT(pev), CHAN_STATIC, "apache/ap_rotor2.wav" ); UTIL_SetSize( pev, Vector( -32, -32, -64), Vector( 32, 32, 0) ); - SetThink(&CApache :: DyingThink ); - SetTouch(&CApache :: CrashTouch ); - SetNextThink( 0.1 ); + SetThink( DyingThink ); + SetTouch( CrashTouch ); + pev->nextthink = gpGlobals->time + 0.1; pev->health = 0; pev->takedamage = DAMAGE_NO; @@ -226,7 +219,7 @@ void CApache :: Killed( entvars_t *pevAttacker, int iGib ) void CApache :: DyingThink( void ) { StudioFrameAdvance( ); - SetNextThink( 0.1 ); + pev->nextthink = gpGlobals->time + 0.1; pev->avelocity = pev->avelocity * 1.02; @@ -234,7 +227,7 @@ void CApache :: DyingThink( void ) if (m_flNextRocket > gpGlobals->time ) { // random explosions - MESSAGE_BEGIN( MSG_PVS, gmsgTempEntity, pev->origin ); + MESSAGE_BEGIN( MSG_PVS, SVC_TEMPENTITY, pev->origin ); WRITE_BYTE( TE_EXPLOSION); // This just makes a dynamic light now WRITE_COORD( pev->origin.x + RANDOM_FLOAT( -150, 150 )); WRITE_COORD( pev->origin.y + RANDOM_FLOAT( -150, 150 )); @@ -246,7 +239,7 @@ void CApache :: DyingThink( void ) MESSAGE_END(); // lots of smoke - MESSAGE_BEGIN( MSG_PVS, gmsgTempEntity, pev->origin ); + MESSAGE_BEGIN( MSG_PVS, SVC_TEMPENTITY, pev->origin ); WRITE_BYTE( TE_SMOKE ); WRITE_COORD( pev->origin.x + RANDOM_FLOAT( -150, 150 )); WRITE_COORD( pev->origin.y + RANDOM_FLOAT( -150, 150 )); @@ -257,7 +250,7 @@ void CApache :: DyingThink( void ) MESSAGE_END(); Vector vecSpot = pev->origin + (pev->mins + pev->maxs) * 0.5; - MESSAGE_BEGIN( MSG_PVS, gmsgTempEntity, vecSpot ); + MESSAGE_BEGIN( MSG_PVS, SVC_TEMPENTITY, vecSpot ); WRITE_BYTE( TE_BREAKMODEL); // position @@ -294,7 +287,7 @@ void CApache :: DyingThink( void ) // don't stop it we touch a entity pev->flags &= ~FL_ONGROUND; - SetNextThink( 0.2 ); + pev->nextthink = gpGlobals->time + 0.2; return; } else @@ -302,7 +295,7 @@ void CApache :: DyingThink( void ) Vector vecSpot = pev->origin + (pev->mins + pev->maxs) * 0.5; /* - MESSAGE_BEGIN( MSG_BROADCAST, gmsgTempEntity ); + MESSAGE_BEGIN( MSG_BROADCAST, SVC_TEMPENTITY ); WRITE_BYTE( TE_EXPLOSION); // This just makes a dynamic light now WRITE_COORD( vecSpot.x ); WRITE_COORD( vecSpot.y ); @@ -314,7 +307,7 @@ void CApache :: DyingThink( void ) */ // fireball - MESSAGE_BEGIN( MSG_PVS, gmsgTempEntity, vecSpot ); + MESSAGE_BEGIN( MSG_PVS, SVC_TEMPENTITY, vecSpot ); WRITE_BYTE( TE_SPRITE ); WRITE_COORD( vecSpot.x ); WRITE_COORD( vecSpot.y ); @@ -325,7 +318,7 @@ void CApache :: DyingThink( void ) MESSAGE_END(); // big smoke - MESSAGE_BEGIN( MSG_PVS, gmsgTempEntity, vecSpot ); + MESSAGE_BEGIN( MSG_PVS, SVC_TEMPENTITY, vecSpot ); WRITE_BYTE( TE_SMOKE ); WRITE_COORD( vecSpot.x ); WRITE_COORD( vecSpot.y ); @@ -336,7 +329,7 @@ void CApache :: DyingThink( void ) MESSAGE_END(); // blast circle - MESSAGE_BEGIN( MSG_PVS, gmsgTempEntity, pev->origin ); + MESSAGE_BEGIN( MSG_PVS, SVC_TEMPENTITY, pev->origin ); WRITE_BYTE( TE_BEAMCYLINDER ); WRITE_COORD( pev->origin.x); WRITE_COORD( pev->origin.y); @@ -374,7 +367,7 @@ void CApache :: DyingThink( void ) // gibs vecSpot = pev->origin + (pev->mins + pev->maxs) * 0.5; - MESSAGE_BEGIN( MSG_PVS, gmsgTempEntity, vecSpot ); + MESSAGE_BEGIN( MSG_PVS, SVC_TEMPENTITY, vecSpot ); WRITE_BYTE( TE_BREAKMODEL); // position @@ -409,8 +402,8 @@ void CApache :: DyingThink( void ) WRITE_BYTE( BREAK_METAL ); MESSAGE_END(); - SetThink(&CApache :: SUB_Remove ); - SetNextThink( 0.1 ); + SetThink( SUB_Remove ); + pev->nextthink = gpGlobals->time + 0.1; } } @@ -435,7 +428,7 @@ void CApache::CrashTouch( CBaseEntity *pOther ) { SetTouch( NULL ); m_flNextRocket = gpGlobals->time; - SetNextThink( 0 ); + pev->nextthink = gpGlobals->time; } } @@ -450,7 +443,7 @@ void CApache :: GibMonster( void ) void CApache :: HuntThink( void ) { StudioFrameAdvance( ); - SetNextThink( 0.1 ); + pev->nextthink = gpGlobals->time + 0.1; ShowDamage( ); @@ -770,7 +763,7 @@ void CApache :: FireRocket( void ) case 4: break; } - MESSAGE_BEGIN( MSG_PVS, gmsgTempEntity, vecSrc ); + MESSAGE_BEGIN( MSG_PVS, SVC_TEMPENTITY, vecSrc ); WRITE_BYTE( TE_SMOKE ); WRITE_COORD( vecSrc.x ); WRITE_COORD( vecSrc.y ); @@ -878,7 +871,7 @@ void CApache :: ShowDamage( void ) { if (m_iDoSmokePuff > 0 || RANDOM_LONG(0,99) > pev->health) { - MESSAGE_BEGIN( MSG_PVS, gmsgTempEntity, pev->origin ); + MESSAGE_BEGIN( MSG_PVS, SVC_TEMPENTITY, pev->origin ); WRITE_BYTE( TE_SMOKE ); WRITE_COORD( pev->origin.x ); WRITE_COORD( pev->origin.y ); @@ -977,16 +970,16 @@ void CApacheHVR :: Spawn( void ) SET_MODEL(ENT(pev), "models/HVR.mdl"); UTIL_SetSize(pev, Vector( 0, 0, 0), Vector(0, 0, 0)); - UTIL_SetOrigin( this, pev->origin ); + UTIL_SetOrigin( pev, pev->origin ); - SetThink(&CApacheHVR :: IgniteThink ); - SetTouch(&CApacheHVR :: ExplodeTouch ); + SetThink( IgniteThink ); + SetTouch( ExplodeTouch ); UTIL_MakeAimVectors( pev->angles ); m_vecForward = gpGlobals->v_forward; pev->gravity = 0.5; - SetNextThink( 0.1 ); + pev->nextthink = gpGlobals->time + 0.1; pev->dmg = 150; } @@ -1011,7 +1004,7 @@ void CApacheHVR :: IgniteThink( void ) EMIT_SOUND( ENT(pev), CHAN_VOICE, "weapons/rocket1.wav", 1, 0.5 ); // rocket trail - MESSAGE_BEGIN( MSG_BROADCAST, gmsgTempEntity ); + MESSAGE_BEGIN( MSG_BROADCAST, SVC_TEMPENTITY ); WRITE_BYTE( TE_BEAMFOLLOW ); WRITE_SHORT(entindex()); // entity @@ -1026,8 +1019,8 @@ void CApacheHVR :: IgniteThink( void ) MESSAGE_END(); // move PHS/PVS data sending into here (SEND_ALL, SEND_PVS, SEND_PHS) // set to accelerate - SetThink(&CApacheHVR :: AccelerateThink ); - SetNextThink( 0.1 ); + SetThink( AccelerateThink ); + pev->nextthink = gpGlobals->time + 0.1; } @@ -1050,8 +1043,8 @@ void CApacheHVR :: AccelerateThink( void ) // re-aim pev->angles = UTIL_VecToAngles( pev->velocity ); - SetNextThink( 0.1 ); + pev->nextthink = gpGlobals->time + 0.1; } -#endif +#endif \ No newline at end of file diff --git a/spirit/barnacle.cpp b/dlls/barnacle.cpp similarity index 88% rename from spirit/barnacle.cpp rename to dlls/barnacle.cpp index 8ef46c3b..67b1b943 100644 --- a/spirit/barnacle.cpp +++ b/dlls/barnacle.cpp @@ -75,7 +75,7 @@ IMPLEMENT_SAVERESTORE( CBarnacle, CBaseMonster ); //========================================================= int CBarnacle :: Classify ( void ) { - return m_iClass?m_iClass:CLASS_ALIEN_MONSTER; + return CLASS_ALIEN_MONSTER; } //========================================================= @@ -104,10 +104,7 @@ void CBarnacle :: Spawn() { Precache( ); - if (pev->model) - SET_MODEL(ENT(pev), STRING(pev->model)); //LRC - else - SET_MODEL(ENT(pev), "models/barnacle.mdl"); + SET_MODEL(ENT(pev), "models/barnacle.mdl"); UTIL_SetSize( pev, Vector(-16, -16, -32), Vector(16, 16, 0) ); pev->solid = SOLID_SLIDEBOX; @@ -127,10 +124,10 @@ void CBarnacle :: Spawn() SetActivity ( ACT_IDLE ); - SetThink(&CBarnacle :: BarnacleThink ); - SetNextThink( 0.5 ); + SetThink ( BarnacleThink ); + pev->nextthink = gpGlobals->time + 0.5; - UTIL_SetOrigin ( this, pev->origin ); + UTIL_SetOrigin ( pev, pev->origin ); } int CBarnacle::TakeDamage( entvars_t *pevInflictor, entvars_t *pevAttacker, float flDamage, int bitsDamageType ) @@ -151,7 +148,7 @@ void CBarnacle :: BarnacleThink ( void ) CBaseMonster *pVictim; float flLength; - SetNextThink( 0.1 ); + pev->nextthink = gpGlobals->time + 0.1; if ( m_hEnemy != NULL ) { @@ -189,8 +186,7 @@ void CBarnacle :: BarnacleThink ( void ) if ( fabs( pev->origin.z - ( vecNewEnemyOrigin.z + m_hEnemy->pev->view_ofs.z - 8 ) ) < BARNACLE_BODY_HEIGHT ) { - // prey has just been lifted into position ( if the victim origin + eye height + 8 is higher - // than the bottom of the barnacle, it is assumed that the head is within barnacle's body ) + // prey has just been lifted into position ( if the victim origin + eye height + 8 is higher than the bottom of the barnacle, it is assumed that the head is within barnacle's body ) m_fLiftingPrey = FALSE; EMIT_SOUND( ENT(pev), CHAN_WEAPON, "barnacle/bcl_bite3.wav", 1, ATTN_NORM ); @@ -206,7 +202,7 @@ void CBarnacle :: BarnacleThink ( void ) } } - UTIL_SetOrigin ( m_hEnemy, vecNewEnemyOrigin ); + UTIL_SetOrigin ( m_hEnemy->pev, vecNewEnemyOrigin ); } else { @@ -246,8 +242,8 @@ void CBarnacle :: BarnacleThink ( void ) // barnacle has no prey right now, so just idle and check to see if anything is touching the tongue. // If idle and no nearby client, don't think so often - if ( FNullEnt( FIND_CLIENT_IN_PVS( edict() ) ) && !HaveCamerasInPVS( edict() ) ) - SetNextThink( RANDOM_FLOAT(1,1.5) ); // Stagger a bit to keep barnacles from thinking on the same frame + if ( FNullEnt( FIND_CLIENT_IN_PVS( edict() ) ) ) + pev->nextthink = gpGlobals->time + RANDOM_FLOAT(1,1.5); // Stagger a bit to keep barnacles from thinking on the same frame if ( m_fSequenceFinished ) {// this is done so barnacle will fidget. @@ -284,8 +280,8 @@ void CBarnacle :: BarnacleThink ( void ) m_hEnemy = pTouchEnt; pTouchEnt->pev->movetype = MOVETYPE_FLY; - pTouchEnt->pev->velocity = pev->velocity; //LRC- make him come _with_ me - pTouchEnt->pev->basevelocity = pev->velocity; //LRC + pTouchEnt->pev->velocity = g_vecZero; + pTouchEnt->pev->basevelocity = g_vecZero; pTouchEnt->pev->origin.x = pev->origin.x; pTouchEnt->pev->origin.y = pev->origin.y; @@ -352,15 +348,15 @@ void CBarnacle :: Killed( entvars_t *pevAttacker, int iGib ) StudioFrameAdvance( 0.1 ); - SetNextThink( 0.1 ); - SetThink(&CBarnacle :: WaitTillDead ); + pev->nextthink = gpGlobals->time + 0.1; + SetThink ( WaitTillDead ); } //========================================================= //========================================================= void CBarnacle :: WaitTillDead ( void ) { - SetNextThink( 0.1 ); + pev->nextthink = gpGlobals->time + 0.1; float flInterval = StudioFrameAdvance( 0.1 ); DispatchAnimEvents ( flInterval ); @@ -378,10 +374,7 @@ void CBarnacle :: WaitTillDead ( void ) //========================================================= void CBarnacle :: Precache() { - if (pev->model) - PRECACHE_MODEL((char*)STRING(pev->model)); //LRC - else - PRECACHE_MODEL("models/barnacle.mdl"); + PRECACHE_MODEL("models/barnacle.mdl"); PRECACHE_SOUND("barnacle/bcl_alert2.wav");//happy, lifting food up PRECACHE_SOUND("barnacle/bcl_bite3.wav");//just got food to mouth diff --git a/spirit/barney.cpp b/dlls/barney.cpp similarity index 79% rename from spirit/barney.cpp rename to dlls/barney.cpp index f4c45e83..76eba8dd 100644 --- a/spirit/barney.cpp +++ b/dlls/barney.cpp @@ -77,7 +77,6 @@ public: virtual int Restore( CRestore &restore ); static TYPEDESCRIPTION m_SaveData[]; - int m_iBaseBody; //LRC - for barneys with different bodies BOOL m_fGunDrawn; float m_painTime; float m_checkAttackTime; @@ -93,7 +92,6 @@ LINK_ENTITY_TO_CLASS( monster_barney, CBarney ); TYPEDESCRIPTION CBarney::m_SaveData[] = { - DEFINE_FIELD( CBarney, m_iBaseBody, FIELD_INTEGER ), //LRC DEFINE_FIELD( CBarney, m_fGunDrawn, FIELD_BOOLEAN ), DEFINE_FIELD( CBarney, m_painTime, FIELD_TIME ), DEFINE_FIELD( CBarney, m_checkAttackTime, FIELD_TIME ), @@ -263,7 +261,7 @@ int CBarney :: ISoundMask ( void) //========================================================= int CBarney :: Classify ( void ) { - return m_iClass?m_iClass:CLASS_PLAYER_ALLY; + return CLASS_PLAYER_ALLY; } //========================================================= @@ -275,17 +273,7 @@ void CBarney :: AlertSound( void ) { if ( FOkToSpeak() ) { - if (m_iszSpeakAs) - { - char szBuf[32]; - strcpy(szBuf,STRING(m_iszSpeakAs)); - strcat(szBuf,"_ATTACK"); - PlaySentence( szBuf, RANDOM_FLOAT(2.8, 3.2), VOL_NORM, ATTN_IDLE ); - } - else - { - PlaySentence( "BA_ATTACK", RANDOM_FLOAT(2.8, 3.2), VOL_NORM, ATTN_IDLE ); - } + PlaySentence( "BA_ATTACK", RANDOM_FLOAT(2.8, 3.2), VOL_NORM, ATTN_IDLE ); } } @@ -364,28 +352,16 @@ void CBarney :: BarneyFirePistol ( void ) SetBlending( 0, angDir.x ); pev->effects = EF_MUZZLEFLASH; - if (pev->frags) - { - FireBullets(1, vecShootOrigin, vecShootDir, VECTOR_CONE_2DEGREES, 1024, BULLET_PLAYER_357); - if (RANDOM_LONG(0, 1)) - EMIT_SOUND_DYN( ENT(pev), CHAN_WEAPON, "weapons/357_shot1.wav", 1, ATTN_NORM, 0, 100 ); - else - EMIT_SOUND_DYN( ENT(pev), CHAN_WEAPON, "weapons/357_shot2.wav", 1, ATTN_NORM, 0, 100 ); - } - else - { - FireBullets(1, vecShootOrigin, vecShootDir, VECTOR_CONE_2DEGREES, 1024, BULLET_MONSTER_9MM ); - - int pitchShift = RANDOM_LONG( 0, 20 ); + FireBullets(1, vecShootOrigin, vecShootDir, VECTOR_CONE_2DEGREES, 1024, BULLET_MONSTER_9MM ); - // Only shift about half the time - if ( pitchShift > 10 ) - pitchShift = 0; - else - pitchShift -= 5; - - EMIT_SOUND_DYN( ENT(pev), CHAN_WEAPON, "barney/ba_attack2.wav", 1, ATTN_NORM, 0, 100 + pitchShift ); - } + int pitchShift = RANDOM_LONG( 0, 20 ); + + // Only shift about half the time + if ( pitchShift > 10 ) + pitchShift = 0; + else + pitchShift -= 5; + EMIT_SOUND_DYN( ENT(pev), CHAN_WEAPON, "barney/ba_attack2.wav", 1, ATTN_NORM, 0, 100 + pitchShift ); CSoundEnt::InsertSound ( bits_SOUND_COMBAT, pev->origin, 384, 0.3 ); @@ -409,13 +385,13 @@ void CBarney :: HandleAnimEvent( MonsterEvent_t *pEvent ) case BARNEY_AE_DRAW: // barney's bodygroup switches here so he can pull gun from holster - pev->body = m_iBaseBody + BARNEY_BODY_GUNDRAWN; + pev->body = BARNEY_BODY_GUNDRAWN; m_fGunDrawn = TRUE; break; case BARNEY_AE_HOLSTER: // change bodygroup to replace gun in holster - pev->body = m_iBaseBody + BARNEY_BODY_GUNHOLSTERED; + pev->body = BARNEY_BODY_GUNHOLSTERED; m_fGunDrawn = FALSE; break; @@ -431,29 +407,24 @@ void CBarney :: Spawn() { Precache( ); - if (pev->model) - SET_MODEL(ENT(pev), STRING(pev->model)); //LRC - else - SET_MODEL(ENT(pev), "models/barney.mdl"); + SET_MODEL(ENT(pev), "models/barney.mdl"); UTIL_SetSize(pev, VEC_HUMAN_HULL_MIN, VEC_HUMAN_HULL_MAX); pev->solid = SOLID_SLIDEBOX; pev->movetype = MOVETYPE_STEP; m_bloodColor = BLOOD_COLOR_RED; - if (pev->health == 0) //LRC - pev->health = gSkillData.barneyHealth; + pev->health = gSkillData.barneyHealth; pev->view_ofs = Vector ( 0, 0, 50 );// position of the eyes relative to monster's origin. m_flFieldOfView = VIEW_FIELD_WIDE; // NOTE: we need a wide field of view so npc will notice player and say hello m_MonsterState = MONSTERSTATE_NONE; - m_iBaseBody = pev->body; //LRC - pev->body = m_iBaseBody + BARNEY_BODY_GUNHOLSTERED; // gun in holster + pev->body = 0; // gun in holster m_fGunDrawn = FALSE; m_afCapability = bits_CAP_HEAR | bits_CAP_TURN_HEAD | bits_CAP_DOORS_GROUP; MonsterInit(); - SetUse(&CBarney :: FollowerUse ); + SetUse( FollowerUse ); } //========================================================= @@ -461,10 +432,7 @@ void CBarney :: Spawn() //========================================================= void CBarney :: Precache() { - if (pev->model) - PRECACHE_MODEL((char*)STRING(pev->model)); //LRC - else - PRECACHE_MODEL("models/barney.mdl"); + PRECACHE_MODEL("models/barney.mdl"); PRECACHE_SOUND("barney/ba_attack1.wav" ); PRECACHE_SOUND("barney/ba_attack2.wav" ); @@ -489,44 +457,31 @@ void CBarney :: TalkInit() CTalkMonster::TalkInit(); - // barney speech group names (group names are in sentences.txt) + // scientists speach group names (group names are in sentences.txt) - if (!m_iszSpeakAs) - { - m_szGrp[TLK_ANSWER] = "BA_ANSWER"; - m_szGrp[TLK_QUESTION] = "BA_QUESTION"; - m_szGrp[TLK_IDLE] = "BA_IDLE"; - m_szGrp[TLK_STARE] = "BA_STARE"; - if (pev->spawnflags & SF_MONSTER_PREDISASTER) //LRC - m_szGrp[TLK_USE] = "BA_PFOLLOW"; - else - m_szGrp[TLK_USE] = "BA_OK"; - if (pev->spawnflags & SF_MONSTER_PREDISASTER) - m_szGrp[TLK_UNUSE] = "BA_PWAIT"; - else - m_szGrp[TLK_UNUSE] = "BA_WAIT"; - if (pev->spawnflags & SF_MONSTER_PREDISASTER) - m_szGrp[TLK_DECLINE] = "BA_POK"; - else - m_szGrp[TLK_DECLINE] = "BA_NOTOK"; - m_szGrp[TLK_STOP] = "BA_STOP"; + m_szGrp[TLK_ANSWER] = "BA_ANSWER"; + m_szGrp[TLK_QUESTION] = "BA_QUESTION"; + m_szGrp[TLK_IDLE] = "BA_IDLE"; + m_szGrp[TLK_STARE] = "BA_STARE"; + m_szGrp[TLK_USE] = "BA_OK"; + m_szGrp[TLK_UNUSE] = "BA_WAIT"; + m_szGrp[TLK_STOP] = "BA_STOP"; - m_szGrp[TLK_NOSHOOT] = "BA_SCARED"; - m_szGrp[TLK_HELLO] = "BA_HELLO"; + m_szGrp[TLK_NOSHOOT] = "BA_SCARED"; + m_szGrp[TLK_HELLO] = "BA_HELLO"; - m_szGrp[TLK_PLHURT1] = "!BA_CUREA"; - m_szGrp[TLK_PLHURT2] = "!BA_CUREB"; - m_szGrp[TLK_PLHURT3] = "!BA_CUREC"; + m_szGrp[TLK_PLHURT1] = "!BA_CUREA"; + m_szGrp[TLK_PLHURT2] = "!BA_CUREB"; + m_szGrp[TLK_PLHURT3] = "!BA_CUREC"; - m_szGrp[TLK_PHELLO] = NULL; //"BA_PHELLO"; // UNDONE - m_szGrp[TLK_PIDLE] = NULL; //"BA_PIDLE"; // UNDONE - m_szGrp[TLK_PQUESTION] = "BA_PQUEST"; // UNDONE + m_szGrp[TLK_PHELLO] = NULL; //"BA_PHELLO"; // UNDONE + m_szGrp[TLK_PIDLE] = NULL; //"BA_PIDLE"; // UNDONE + m_szGrp[TLK_PQUESTION] = "BA_PQUEST"; // UNDONE - m_szGrp[TLK_SMELL] = "BA_SMELL"; + m_szGrp[TLK_SMELL] = "BA_SMELL"; - m_szGrp[TLK_WOUND] = "BA_WOUND"; - m_szGrp[TLK_MORTAL] = "BA_MORTAL"; - } + m_szGrp[TLK_WOUND] = "BA_WOUND"; + m_szGrp[TLK_MORTAL] = "BA_MORTAL"; // get voice for head - just one barney voice for now m_voicePitch = 100; @@ -558,9 +513,6 @@ int CBarney :: TakeDamage( entvars_t* pevInflictor, entvars_t* pevAttacker, floa if ( !IsAlive() || pev->deadflag == DEAD_DYING ) return ret; - // LRC - if my reaction to the player has been overridden, don't do this stuff - if (m_iPlayerReact) return ret; - if ( m_MonsterState != MONSTERSTATE_PRONE && (pevAttacker->flags & FL_CLIENT) ) { m_flPlayerDamage += flDamage; @@ -573,17 +525,7 @@ int CBarney :: TakeDamage( entvars_t* pevInflictor, entvars_t* pevAttacker, floa if ( (m_afMemory & bits_MEMORY_SUSPICIOUS) || IsFacing( pevAttacker, pev->origin ) ) { // Alright, now I'm pissed! - if (m_iszSpeakAs) - { - char szBuf[32]; - strcpy(szBuf,STRING(m_iszSpeakAs)); - strcat(szBuf,"_MAD"); - PlaySentence( szBuf, 4, VOL_NORM, ATTN_NORM ); - } - else - { - PlaySentence( "BA_MAD", 4, VOL_NORM, ATTN_NORM ); - } + PlaySentence( "BA_MAD", 4, VOL_NORM, ATTN_NORM ); Remember( bits_MEMORY_PROVOKED ); StopFollowing( TRUE ); @@ -591,33 +533,13 @@ int CBarney :: TakeDamage( entvars_t* pevInflictor, entvars_t* pevAttacker, floa else { // Hey, be careful with that - if (m_iszSpeakAs) - { - char szBuf[32]; - strcpy(szBuf,STRING(m_iszSpeakAs)); - strcat(szBuf,"_SHOT"); - PlaySentence( szBuf, 4, VOL_NORM, ATTN_NORM ); - } - else - { - PlaySentence( "BA_SHOT", 4, VOL_NORM, ATTN_NORM ); - } + PlaySentence( "BA_SHOT", 4, VOL_NORM, ATTN_NORM ); Remember( bits_MEMORY_SUSPICIOUS ); } } else if ( !(m_hEnemy->IsPlayer()) && pev->deadflag == DEAD_NO ) { - if (m_iszSpeakAs) - { - char szBuf[32]; - strcpy(szBuf,STRING(m_iszSpeakAs)); - strcat(szBuf,"_SHOT"); - PlaySentence( szBuf, 4, VOL_NORM, ATTN_NORM ); - } - else - { - PlaySentence( "BA_SHOT", 4, VOL_NORM, ATTN_NORM ); - } + PlaySentence( "BA_SHOT", 4, VOL_NORM, ATTN_NORM ); } } @@ -689,20 +611,16 @@ void CBarney::TraceAttack( entvars_t *pevAttacker, float flDamage, Vector vecDir void CBarney::Killed( entvars_t *pevAttacker, int iGib ) { - if ( pev->body < m_iBaseBody + BARNEY_BODY_GUNGONE && !(pev->spawnflags & SF_MONSTER_NO_WPN_DROP)) + if ( pev->body < BARNEY_BODY_GUNGONE ) {// drop the gun! Vector vecGunPos; Vector vecGunAngles; - pev->body = m_iBaseBody + BARNEY_BODY_GUNGONE; + pev->body = BARNEY_BODY_GUNGONE; GetAttachment( 0, vecGunPos, vecGunAngles ); - CBaseEntity *pGun; - if (pev->frags) - pGun = DropItem( "weapon_357", vecGunPos, vecGunAngles ); - else - pGun = DropItem( "weapon_9mmhandgun", vecGunPos, vecGunAngles ); + CBaseEntity *pGun = DropItem( "weapon_9mmhandgun", vecGunPos, vecGunAngles ); } SetUse( NULL ); @@ -777,18 +695,7 @@ Schedule_t *CBarney :: GetSchedule ( void ) } if ( HasConditions( bits_COND_ENEMY_DEAD ) && FOkToSpeak() ) { - // Hey, be careful with that - if (m_iszSpeakAs) - { - char szBuf[32]; - strcpy(szBuf,STRING(m_iszSpeakAs)); - strcat(szBuf,"_KILL"); - PlaySentence( szBuf, 4, VOL_NORM, ATTN_NORM ); - } - else - { - PlaySentence( "BA_KILL", 4, VOL_NORM, ATTN_NORM ); - } + PlaySentence( "BA_KILL", 4, VOL_NORM, ATTN_NORM ); } switch( m_MonsterState ) @@ -863,7 +770,7 @@ MONSTERSTATE CBarney :: GetIdealState ( void ) void CBarney::DeclineFollowing( void ) { - PlaySentence( m_szGrp[TLK_DECLINE], 2, VOL_NORM, ATTN_NORM ); //LRC + PlaySentence( "BA_POK", 2, VOL_NORM, ATTN_NORM ); } @@ -923,7 +830,7 @@ void CDeadBarney :: Spawn( ) pev->sequence = LookupSequence( m_szPoses[m_iPose] ); if (pev->sequence == -1) { - ALERT ( at_debug, "Dead barney with bad pose\n" ); + ALERT ( at_console, "Dead barney with bad pose\n" ); } // Corpses have less health pev->health = 8;//gSkillData.barneyHealth; diff --git a/spirit/basemonster.h b/dlls/basemonster.h similarity index 92% rename from spirit/basemonster.h rename to dlls/basemonster.h index babab73c..04fbce91 100644 --- a/spirit/basemonster.h +++ b/dlls/basemonster.h @@ -110,7 +110,7 @@ public: virtual int Save( CSave &save ); virtual int Restore( CRestore &restore ); - + static TYPEDESCRIPTION m_SaveData[]; void KeyValue( KeyValueData *pkvd ); @@ -121,11 +121,6 @@ public: // overrideable Monster member functions - // LRC- to allow level-designers to change monster allegiances - int m_iClass; - int m_iPlayerReact; - virtual int Classify( void ) { return m_iClass?m_iClass:CLASS_NONE; } - virtual int BloodColor( void ) { return m_bloodColor; } virtual CBaseMonster *MyMonsterPointer( void ) { return this; } @@ -193,9 +188,8 @@ public: virtual Schedule_t *GetScheduleOfType( int Type ); virtual Schedule_t *GetSchedule( void ); virtual void ScheduleChange( void ) {} -// virtual int CanPlaySequence( void ) { return ((m_pCine == NULL) && (m_MonsterState == MONSTERSTATE_NONE || m_MonsterState == MONSTERSTATE_IDLE || m_IdealMonsterState == MONSTERSTATE_IDLE)); } - virtual int CanPlaySequence( int interruptFlags ); -// virtual int CanPlaySequence( BOOL fDisregardState, int interruptLevel ); + // virtual int CanPlaySequence( void ) { return ((m_pCine == NULL) && (m_MonsterState == MONSTERSTATE_NONE || m_MonsterState == MONSTERSTATE_IDLE || m_IdealMonsterState == MONSTERSTATE_IDLE)); } + virtual int CanPlaySequence( BOOL fDisregardState, int interruptLevel ); virtual int CanPlaySentence( BOOL fDisregardState ) { return IsAlive(); } virtual void PlaySentence( const char *pszSentence, float duration, float volume, float attenuation ); virtual void PlayScriptedSentence( const char *pszSentence, float duration, float volume, float attenuation, BOOL bConcurrent, CBaseEntity *pListener ); @@ -302,7 +296,6 @@ public: virtual void GibMonster( void ); BOOL ShouldGibMonster( int iGib ); void CallGibMonster( void ); - virtual int HasCustomGibs( void ) { return FALSE; } //LRC virtual BOOL HasHumanGibs( void ); virtual BOOL HasAlienGibs( void ); virtual void FadeMonster( void ); // Called instead of GibMonster() when gibs are disabled @@ -313,7 +306,6 @@ public: virtual Vector GetGunPosition( void ); virtual int TakeHealth( float flHealth, int bitsDamageType ); - virtual int TakeArmor( float flArmor ); virtual int TakeDamage( entvars_t* pevInflictor, entvars_t* pevAttacker, float flDamage, int bitsDamageType); int DeadTakeDamage( entvars_t *pevInflictor, entvars_t *pevAttacker, float flDamage, int bitsDamageType ); @@ -339,16 +331,7 @@ public: BOOL ExitScriptedSequence( ); BOOL CineCleanup( ); - void StartPatrol( CBaseEntity *path ); - CBaseEntity* DropItem ( char *pszItemName, const Vector &vecPos, const Vector &vecAng );// drop an item. - - //LRC - float CalcRatio( CBaseEntity *pLocus ) - { - /*ALERT(at_console, "monster CR: %f/%f = %f\n", pev->health, pev->max_health, pev->health / pev->max_health);*/ - return pev->health / pev->max_health; - } }; diff --git a/spirit/bigmomma.cpp b/dlls/bigmomma.cpp similarity index 92% rename from spirit/bigmomma.cpp rename to dlls/bigmomma.cpp index 59948cc7..ca7a1bf8 100644 --- a/spirit/bigmomma.cpp +++ b/dlls/bigmomma.cpp @@ -1,1320 +1,1251 @@ -/*** -* -* Copyright (c) 1996-2002, 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. -* -* This source code contains proprietary and confidential information of -* Valve LLC and its suppliers. Access to this code is restricted to -* persons who have executed a written SDK license with Valve. Any access, -* use or distribution of this code by or to any unlicensed person is illegal. -* -****/ -#if !defined( OEM_BUILD ) && !defined( HLDEMO_BUILD ) - -//========================================================= -// monster template -//========================================================= -#include "extdll.h" -#include "util.h" -#include "cbase.h" -#include "monsters.h" -#include "schedule.h" -#include "decals.h" -#include "weapons.h" -#include "game.h" - -//LRC brought in from animation.h -#define ACTIVITY_NOT_AVAILABLE -1 - -#define SF_INFOBM_RUN 0x0001 -#define SF_INFOBM_WAIT 0x0002 - -// AI Nodes for Big Momma -class CInfoBM : public CPointEntity -{ -public: - void Spawn( void ); - void KeyValue( KeyValueData* pkvd ); - - // name in pev->targetname - // next in pev->target - // radius in pev->scale - // health in pev->health - // Reach target in pev->message - // Reach delay in pev->speed - // Reach sequence in pev->netname - - virtual int Save( CSave &save ); - virtual int Restore( CRestore &restore ); - static TYPEDESCRIPTION m_SaveData[]; - - int m_preSequence; -}; - -LINK_ENTITY_TO_CLASS( info_bigmomma, CInfoBM ); - -TYPEDESCRIPTION CInfoBM::m_SaveData[] = -{ - DEFINE_FIELD( CInfoBM, m_preSequence, FIELD_STRING ), -}; - -IMPLEMENT_SAVERESTORE( CInfoBM, CPointEntity ); - -void CInfoBM::Spawn( void ) -{ -} - - -void CInfoBM::KeyValue( KeyValueData* pkvd ) -{ - if (FStrEq(pkvd->szKeyName, "radius")) - { - pev->scale = atof(pkvd->szValue); - pkvd->fHandled = TRUE; - } - else if (FStrEq(pkvd->szKeyName, "reachdelay")) - { - pev->speed = atof(pkvd->szValue); - pkvd->fHandled = TRUE; - } - else if (FStrEq(pkvd->szKeyName, "reachtarget")) - { - pev->message = ALLOC_STRING(pkvd->szValue); - pkvd->fHandled = TRUE; - } - else if (FStrEq(pkvd->szKeyName, "reachsequence")) - { - pev->netname = ALLOC_STRING(pkvd->szValue); - pkvd->fHandled = TRUE; - } - else if (FStrEq(pkvd->szKeyName, "presequence")) - { - m_preSequence = ALLOC_STRING(pkvd->szValue); - pkvd->fHandled = TRUE; - } - else - CPointEntity::KeyValue( pkvd ); -} - -//========================================================= -// Mortar shot entity -//========================================================= -class CBMortar : public CBaseEntity -{ -public: - void Spawn( void ); - - static CBMortar *Shoot( edict_t *pOwner, Vector vecStart, Vector vecVelocity ); - void Touch( CBaseEntity *pOther ); - void EXPORT Animate( void ); - - virtual int Save( CSave &save ); - virtual int Restore( CRestore &restore ); - static TYPEDESCRIPTION m_SaveData[]; - - int m_maxFrame; -}; - -LINK_ENTITY_TO_CLASS( bmortar, CBMortar ); - -TYPEDESCRIPTION CBMortar::m_SaveData[] = -{ - DEFINE_FIELD( CBMortar, m_maxFrame, FIELD_INTEGER ), -}; - -IMPLEMENT_SAVERESTORE( CBMortar, CBaseEntity ); - - -//========================================================= -// Monster's Anim Events Go Here -//========================================================= -#define BIG_AE_STEP1 1 // Footstep left -#define BIG_AE_STEP2 2 // Footstep right -#define BIG_AE_STEP3 3 // Footstep back left -#define BIG_AE_STEP4 4 // Footstep back right -#define BIG_AE_SACK 5 // Sack slosh -#define BIG_AE_DEATHSOUND 6 // Death sound - -#define BIG_AE_MELEE_ATTACKBR 8 // Leg attack -#define BIG_AE_MELEE_ATTACKBL 9 // Leg attack -#define BIG_AE_MELEE_ATTACK1 10 // Leg attack -#define BIG_AE_MORTAR_ATTACK1 11 // Launch a mortar -#define BIG_AE_LAY_CRAB 12 // Lay a headcrab -#define BIG_AE_JUMP_FORWARD 13 // Jump up and forward -#define BIG_AE_SCREAM 14 // alert sound -#define BIG_AE_PAIN_SOUND 15 // pain sound -#define BIG_AE_ATTACK_SOUND 16 // attack sound -#define BIG_AE_BIRTH_SOUND 17 // birth sound -#define BIG_AE_EARLY_TARGET 50 // Fire target early - - - -// User defined conditions -#define bits_COND_NODE_SEQUENCE ( bits_COND_SPECIAL1 ) // pev->netname contains the name of a sequence to play - -// Attack distance constants -#define BIG_ATTACKDIST 170 -#define BIG_MORTARDIST 800 -#define BIG_MAXCHILDREN 20 // Max # of live headcrab children - - -#define bits_MEMORY_CHILDPAIR (bits_MEMORY_CUSTOM1) -#define bits_MEMORY_ADVANCE_NODE (bits_MEMORY_CUSTOM2) -#define bits_MEMORY_COMPLETED_NODE (bits_MEMORY_CUSTOM3) -#define bits_MEMORY_FIRED_NODE (bits_MEMORY_CUSTOM4) - -int gSpitSprite, gSpitDebrisSprite; -Vector VecCheckSplatToss( entvars_t *pev, const Vector &vecSpot1, Vector vecSpot2, float maxHeight ); -void MortarSpray( const Vector &position, const Vector &direction, int spriteModel, int count ); - - -// UNDONE: -// -#define BIG_CHILDCLASS "monster_babycrab" - -class CBigMomma : public CBaseMonster -{ -public: - void Spawn( void ); - void Precache( void ); - void KeyValue( KeyValueData *pkvd ); - void Activate( void ); - int TakeDamage( entvars_t *pevInflictor, entvars_t *pevAttacker, float flDamage, int bitsDamageType ); - - void RunTask( Task_t *pTask ); - void StartTask( Task_t *pTask ); - Schedule_t *GetSchedule( void ); - Schedule_t *GetScheduleOfType( int Type ); - void TraceAttack( entvars_t *pevAttacker, float flDamage, Vector vecDir, TraceResult *ptr, int bitsDamageType ); - void SetActivity ( Activity NewActivity ); - - void NodeStart( int iszNextNode ); - void NodeReach( void ); - BOOL ShouldGoToNode( void ); - - void SetYawSpeed( void ); - int Classify ( void ); - void HandleAnimEvent( MonsterEvent_t *pEvent ); - void LayHeadcrab( void ); - - int GetNodeSequence( void ) - { - CBaseEntity *pTarget = m_hTargetEnt; - if ( pTarget ) - { - return pTarget->pev->netname; // netname holds node sequence - } - return 0; - } - - - int GetNodePresequence( void ) - { - CInfoBM *pTarget = (CInfoBM *)(CBaseEntity *)m_hTargetEnt; - if ( pTarget ) - { - return pTarget->m_preSequence; - } - return 0; - } - - float GetNodeDelay( void ) - { - CBaseEntity *pTarget = m_hTargetEnt; - if ( pTarget ) - { - return pTarget->pev->speed; // Speed holds node delay - } - return 0; - } - - float GetNodeRange( void ) - { - CBaseEntity *pTarget = m_hTargetEnt; - if ( pTarget ) - { - return pTarget->pev->scale; // Scale holds node delay - } - return 1e6; - } - - float GetNodeYaw( void ) - { - CBaseEntity *pTarget = m_hTargetEnt; - if ( pTarget ) - { - if ( pTarget->pev->angles.y != 0 ) - return pTarget->pev->angles.y; - } - return pev->angles.y; - } - - // Restart the crab count on each new level - void OverrideReset( void ) - { - m_crabCount = 0; - } - - void DeathNotice( entvars_t *pevChild ); - - BOOL CanLayCrab( void ) - { - if ( m_crabTime < gpGlobals->time && m_crabCount < BIG_MAXCHILDREN ) - { - // Don't spawn crabs inside each other - Vector mins = pev->origin - Vector( 32, 32, 0 ); - Vector maxs = pev->origin + Vector( 32, 32, 0 ); - - CBaseEntity *pList[2]; - int count = UTIL_EntitiesInBox( pList, 2, mins, maxs, FL_MONSTER ); - for ( int i = 0; i < count; i++ ) - { - if ( pList[i] != this ) // Don't hurt yourself! - return FALSE; - } - return TRUE; - } - - return FALSE; - } - - void LaunchMortar( void ); - - void SetObjectCollisionBox( void ) - { - pev->absmin = pev->origin + Vector( -95, -95, 0 ); - pev->absmax = pev->origin + Vector( 95, 95, 190 ); - } - - BOOL CheckMeleeAttack1( float flDot, float flDist ); // Slash - BOOL CheckMeleeAttack2( float flDot, float flDist ); // Lay a crab - BOOL CheckRangeAttack1( float flDot, float flDist ); // Mortar launch - - virtual int Save( CSave &save ); - virtual int Restore( CRestore &restore ); - static TYPEDESCRIPTION m_SaveData[]; - - static const char *pChildDieSounds[]; - static const char *pSackSounds[]; - static const char *pDeathSounds[]; - static const char *pAttackSounds[]; - static const char *pAttackHitSounds[]; - static const char *pBirthSounds[]; - static const char *pAlertSounds[]; - static const char *pPainSounds[]; - static const char *pFootSounds[]; - - CUSTOM_SCHEDULES; - -private: - float m_nodeTime; - float m_crabTime; - float m_mortarTime; - float m_painSoundTime; - int m_crabCount; -}; -LINK_ENTITY_TO_CLASS( monster_bigmomma, CBigMomma ); - -TYPEDESCRIPTION CBigMomma::m_SaveData[] = -{ - DEFINE_FIELD( CBigMomma, m_nodeTime, FIELD_TIME ), - DEFINE_FIELD( CBigMomma, m_crabTime, FIELD_TIME ), - DEFINE_FIELD( CBigMomma, m_mortarTime, FIELD_TIME ), - DEFINE_FIELD( CBigMomma, m_painSoundTime, FIELD_TIME ), - DEFINE_FIELD( CBigMomma, m_crabCount, FIELD_INTEGER ), -}; - -IMPLEMENT_SAVERESTORE( CBigMomma, CBaseMonster ); - -const char *CBigMomma::pChildDieSounds[] = -{ - "gonarch/gon_childdie1.wav", - "gonarch/gon_childdie2.wav", - "gonarch/gon_childdie3.wav", -}; - -const char *CBigMomma::pSackSounds[] = -{ - "gonarch/gon_sack1.wav", - "gonarch/gon_sack2.wav", - "gonarch/gon_sack3.wav", -}; - -const char *CBigMomma::pDeathSounds[] = -{ - "gonarch/gon_die1.wav", -}; - -const char *CBigMomma::pAttackSounds[] = -{ - "gonarch/gon_attack1.wav", - "gonarch/gon_attack2.wav", - "gonarch/gon_attack3.wav", -}; -const char *CBigMomma::pAttackHitSounds[] = -{ - "zombie/claw_strike1.wav", - "zombie/claw_strike2.wav", - "zombie/claw_strike3.wav", -}; - -const char *CBigMomma::pBirthSounds[] = -{ - "gonarch/gon_birth1.wav", - "gonarch/gon_birth2.wav", - "gonarch/gon_birth3.wav", -}; - -const char *CBigMomma::pAlertSounds[] = -{ - "gonarch/gon_alert1.wav", - "gonarch/gon_alert2.wav", - "gonarch/gon_alert3.wav", -}; - -const char *CBigMomma::pPainSounds[] = -{ - "gonarch/gon_pain2.wav", - "gonarch/gon_pain4.wav", - "gonarch/gon_pain5.wav", -}; - -const char *CBigMomma::pFootSounds[] = -{ - "gonarch/gon_step1.wav", - "gonarch/gon_step2.wav", - "gonarch/gon_step3.wav", -}; - - - -void CBigMomma :: KeyValue( KeyValueData *pkvd ) -{ -#if 0 - if (FStrEq(pkvd->szKeyName, "volume")) - { - m_volume = atof(pkvd->szValue); - pkvd->fHandled = TRUE; - } - else -#endif - CBaseMonster::KeyValue( pkvd ); -} - -//========================================================= -// Classify - indicates this monster's place in the -// relationship table. -//========================================================= -int CBigMomma :: Classify ( void ) -{ - return m_iClass?m_iClass:CLASS_ALIEN_MONSTER; -} - -//========================================================= -// SetYawSpeed - allows each sequence to have a different -// turn rate associated with it. -//========================================================= -void CBigMomma :: SetYawSpeed ( void ) -{ - int ys; - - switch ( m_Activity ) - { - case ACT_IDLE: - ys = 100; - break; - default: - ys = 90; - } - pev->yaw_speed = ys; -} - -//========================================================= -// HandleAnimEvent - catches the monster-specific messages -// that occur when tagged animation frames are played. -// -// Returns number of events handled, 0 if none. -//========================================================= -void CBigMomma :: HandleAnimEvent( MonsterEvent_t *pEvent ) -{ - switch( pEvent->event ) - { - case BIG_AE_MELEE_ATTACKBR: - case BIG_AE_MELEE_ATTACKBL: - case BIG_AE_MELEE_ATTACK1: - { - Vector forward, right; - - UTIL_MakeVectorsPrivate( pev->angles, forward, right, NULL ); - - Vector center = pev->origin + forward * 128; - Vector mins = center - Vector( 64, 64, 0 ); - Vector maxs = center + Vector( 64, 64, 64 ); - - CBaseEntity *pList[8]; - int count = UTIL_EntitiesInBox( pList, 8, mins, maxs, FL_MONSTER|FL_CLIENT ); - CBaseEntity *pHurt = NULL; - - for ( int i = 0; i < count && !pHurt; i++ ) - { - if ( pList[i] != this ) - { - if ( pList[i]->pev->owner != edict() ) - pHurt = pList[i]; - } - } - - if ( pHurt ) - { - pHurt->TakeDamage( pev, pev, gSkillData.bigmommaDmgSlash, DMG_CRUSH | DMG_SLASH ); - pHurt->pev->punchangle.x = 15; - switch( pEvent->event ) - { - case BIG_AE_MELEE_ATTACKBR: - pHurt->pev->velocity = pHurt->pev->velocity + (forward * 150) + Vector(0,0,250) - (right * 200); - break; - - case BIG_AE_MELEE_ATTACKBL: - pHurt->pev->velocity = pHurt->pev->velocity + (forward * 150) + Vector(0,0,250) + (right * 200); - break; - - case BIG_AE_MELEE_ATTACK1: - pHurt->pev->velocity = pHurt->pev->velocity + (forward * 220) + Vector(0,0,200); - break; - } - - pHurt->pev->flags &= ~FL_ONGROUND; - EMIT_SOUND_DYN( edict(), CHAN_WEAPON, RANDOM_SOUND_ARRAY(pAttackHitSounds), 1.0, ATTN_NORM, 0, 100 + RANDOM_LONG(-5,5) ); - } - } - break; - - case BIG_AE_SCREAM: - EMIT_SOUND_ARRAY_DYN( CHAN_VOICE, pAlertSounds ); - break; - - case BIG_AE_PAIN_SOUND: - EMIT_SOUND_ARRAY_DYN( CHAN_VOICE, pPainSounds ); - break; - - case BIG_AE_ATTACK_SOUND: - EMIT_SOUND_ARRAY_DYN( CHAN_WEAPON, pAttackSounds ); - break; - - case BIG_AE_BIRTH_SOUND: - EMIT_SOUND_ARRAY_DYN( CHAN_BODY, pBirthSounds ); - break; - - case BIG_AE_SACK: - if ( RANDOM_LONG(0,100) < 30 ) - EMIT_SOUND_ARRAY_DYN( CHAN_BODY, pSackSounds ); - break; - - case BIG_AE_DEATHSOUND: - EMIT_SOUND_ARRAY_DYN( CHAN_VOICE, pDeathSounds ); - break; - - case BIG_AE_STEP1: // Footstep left - case BIG_AE_STEP3: // Footstep back left - EMIT_SOUND_ARRAY_DYN( CHAN_ITEM, pFootSounds ); - break; - - case BIG_AE_STEP4: // Footstep back right - case BIG_AE_STEP2: // Footstep right - EMIT_SOUND_ARRAY_DYN( CHAN_BODY, pFootSounds ); - break; - - case BIG_AE_MORTAR_ATTACK1: - LaunchMortar(); - break; - - case BIG_AE_LAY_CRAB: - LayHeadcrab(); - break; - - case BIG_AE_JUMP_FORWARD: - ClearBits( pev->flags, FL_ONGROUND ); - - UTIL_SetOrigin (this, pev->origin + Vector ( 0 , 0 , 1) );// take her off ground so engine doesn't instantly reset onground - UTIL_MakeVectors ( pev->angles ); - - pev->velocity = (gpGlobals->v_forward * 200) + gpGlobals->v_up * 500; - break; - - case BIG_AE_EARLY_TARGET: - { - CBaseEntity *pTarget = m_hTargetEnt; - if ( pTarget && pTarget->pev->message ) - FireTargets( STRING(pTarget->pev->message), this, this, USE_TOGGLE, 0 ); - Remember( bits_MEMORY_FIRED_NODE ); - } - break; - - default: - CBaseMonster::HandleAnimEvent( pEvent ); - break; - } -} - -void CBigMomma :: TraceAttack( entvars_t *pevAttacker, float flDamage, Vector vecDir, TraceResult *ptr, int bitsDamageType ) -{ - if ( ptr->iHitgroup != 1 ) - { - // didn't hit the sack? - - if ( pev->dmgtime != gpGlobals->time || (RANDOM_LONG(0,10) < 1) ) - { - UTIL_Ricochet( ptr->vecEndPos, RANDOM_FLOAT( 1, 2) ); - pev->dmgtime = gpGlobals->time; - } - - flDamage = 0.1;// don't hurt the monster much, but allow bits_COND_LIGHT_DAMAGE to be generated - } - else if ( gpGlobals->time > m_painSoundTime ) - { - m_painSoundTime = gpGlobals->time + RANDOM_LONG(1, 3); - EMIT_SOUND_ARRAY_DYN( CHAN_VOICE, pPainSounds ); - } - - - CBaseMonster::TraceAttack( pevAttacker, flDamage, vecDir, ptr, bitsDamageType ); -} - - -int CBigMomma :: TakeDamage( entvars_t *pevInflictor, entvars_t *pevAttacker, float flDamage, int bitsDamageType ) -{ - // Don't take any acid damage -- BigMomma's mortar is acid - if ( bitsDamageType & DMG_ACID ) - flDamage = 0; - - if ( !HasMemory(bits_MEMORY_PATH_FINISHED) ) - { - if ( pev->health <= flDamage ) - { - pev->health = flDamage + 1; - Remember( bits_MEMORY_ADVANCE_NODE | bits_MEMORY_COMPLETED_NODE ); - ALERT( at_aiconsole, "BM: Finished node health!!!\n" ); - } - } - - return CBaseMonster::TakeDamage( pevInflictor, pevAttacker, flDamage, bitsDamageType ); -} - -void CBigMomma :: LayHeadcrab( void ) -{ - CBaseEntity *pChild = CBaseEntity::Create( BIG_CHILDCLASS, pev->origin, pev->angles, edict() ); - - pChild->pev->spawnflags |= SF_MONSTER_FALL_TO_GROUND; - - // Is this the second crab in a pair? - if ( HasMemory( bits_MEMORY_CHILDPAIR ) ) - { - m_crabTime = gpGlobals->time + RANDOM_FLOAT( 5, 10 ); - Forget( bits_MEMORY_CHILDPAIR ); - } - else - { - m_crabTime = gpGlobals->time + RANDOM_FLOAT( 0.5, 2.5 ); - Remember( bits_MEMORY_CHILDPAIR ); - } - - TraceResult tr; - UTIL_TraceLine( pev->origin, pev->origin - Vector(0,0,100), ignore_monsters, edict(), &tr); - UTIL_DecalTrace( &tr, DECAL_MOMMABIRTH ); - - EMIT_SOUND_DYN( edict(), CHAN_WEAPON, RANDOM_SOUND_ARRAY(pBirthSounds), 1.0, ATTN_NORM, 0, 100 + RANDOM_LONG(-5,5) ); - m_crabCount++; -} - - - -void CBigMomma::DeathNotice( entvars_t *pevChild ) -{ - if ( m_crabCount > 0 ) // Some babies may cross a transition, but we reset the count then - m_crabCount--; - if ( IsAlive() ) - { - // Make the "my baby's dead" noise! - EMIT_SOUND_ARRAY_DYN( CHAN_WEAPON, pChildDieSounds ); - } -} - - -void CBigMomma::LaunchMortar( void ) -{ - m_mortarTime = gpGlobals->time + RANDOM_FLOAT( 2, 15 ); - - Vector startPos = pev->origin; - startPos.z += 180; - Vector vecLaunch = g_vecZero; - - if (m_pCine) // is a scripted_action making me shoot? - { - if (m_hTargetEnt != NULL) // don't check m_fTurnType- bigmomma can fire in any direction. - { - vecLaunch = VecCheckSplatToss( pev, startPos, m_hTargetEnt->pev->origin, RANDOM_FLOAT( 150, 500 ) ); - } - if (vecLaunch == g_vecZero) - { - vecLaunch = pev->movedir; - } - } - else - { - vecLaunch = pev->movedir; - } - - EMIT_SOUND_DYN( edict(), CHAN_WEAPON, RANDOM_SOUND_ARRAY(pSackSounds), 1.0, ATTN_NORM, 0, 100 + RANDOM_LONG(-5,5) ); - CBMortar *pBomb = CBMortar::Shoot( edict(), startPos, vecLaunch ); - pBomb->pev->gravity = 1.0; - MortarSpray( startPos, Vector(0,0,1), gSpitSprite, 24 ); -} - -//========================================================= -// Spawn -//========================================================= -void CBigMomma :: Spawn() -{ - Precache( ); - - if (pev->model) - SET_MODEL(ENT(pev), STRING(pev->model)); //LRC - else - SET_MODEL(ENT(pev), "models/big_mom.mdl"); - UTIL_SetSize( pev, Vector( -32, -32, 0 ), Vector( 32, 32, 64 ) ); - - pev->solid = SOLID_SLIDEBOX; - pev->movetype = MOVETYPE_STEP; - m_bloodColor = BLOOD_COLOR_GREEN; - if (pev->health == 0) - pev->health = 150 * gSkillData.bigmommaHealthFactor; - pev->view_ofs = Vector ( 0, 0, 128 );// position of the eyes relative to monster's origin. - m_flFieldOfView = 0.3;// indicates the width of this monster's forward view cone ( as a dotproduct result ) - m_MonsterState = MONSTERSTATE_NONE; - - MonsterInit(); -} - -//========================================================= -// Precache - precaches all resources this monster needs -//========================================================= -void CBigMomma :: Precache() -{ - if (pev->model) - PRECACHE_MODEL((char*)STRING(pev->model)); //LRC - else - PRECACHE_MODEL("models/big_mom.mdl"); - - PRECACHE_SOUND_ARRAY( pChildDieSounds ); - PRECACHE_SOUND_ARRAY( pSackSounds ); - PRECACHE_SOUND_ARRAY( pDeathSounds ); - PRECACHE_SOUND_ARRAY( pAttackSounds ); - PRECACHE_SOUND_ARRAY( pAttackHitSounds ); - PRECACHE_SOUND_ARRAY( pBirthSounds ); - PRECACHE_SOUND_ARRAY( pAlertSounds ); - PRECACHE_SOUND_ARRAY( pPainSounds ); - PRECACHE_SOUND_ARRAY( pFootSounds ); - - UTIL_PrecacheOther( BIG_CHILDCLASS ); - - // TEMP: Squid - PRECACHE_MODEL("sprites/mommaspit.spr");// spit projectile. - gSpitSprite = PRECACHE_MODEL("sprites/mommaspout.spr");// client side spittle. - gSpitDebrisSprite = PRECACHE_MODEL("sprites/mommablob.spr" ); - - PRECACHE_SOUND( "bullchicken/bc_acid1.wav" ); - PRECACHE_SOUND( "bullchicken/bc_spithit1.wav" ); - PRECACHE_SOUND( "bullchicken/bc_spithit2.wav" ); -} - - -void CBigMomma::Activate( void ) -{ - if ( m_hTargetEnt == NULL ) - Remember( bits_MEMORY_ADVANCE_NODE ); // Start 'er up - - CBaseMonster::Activate(); -} - - -void CBigMomma::NodeStart( int iszNextNode ) -{ - pev->netname = iszNextNode; - - CBaseEntity *pTarget = NULL; - - if ( pev->netname ) - { - edict_t *pentTarget = FIND_ENTITY_BY_TARGETNAME ( NULL, STRING(pev->netname) ); - - if ( !FNullEnt(pentTarget) ) - pTarget = Instance( pentTarget ); - } - - - if ( !pTarget ) - { - ALERT( at_aiconsole, "BM: Finished the path!!\n" ); - Remember( bits_MEMORY_PATH_FINISHED ); - return; - } - Remember( bits_MEMORY_ON_PATH ); - m_hTargetEnt = pTarget; -} - - -void CBigMomma::NodeReach( void ) -{ - CBaseEntity *pTarget = m_hTargetEnt; - - Forget( bits_MEMORY_ADVANCE_NODE ); - - if ( !pTarget ) - return; - - if ( pTarget->pev->health ) - pev->max_health = pev->health = pTarget->pev->health * gSkillData.bigmommaHealthFactor; - - if ( !HasMemory( bits_MEMORY_FIRED_NODE ) ) - { - if ( pTarget->pev->message ) - FireTargets( STRING(pTarget->pev->message), this, this, USE_TOGGLE, 0 ); - } - Forget( bits_MEMORY_FIRED_NODE ); - - pev->netname = pTarget->pev->target; - if ( pTarget->pev->health == 0 ) - Remember( bits_MEMORY_ADVANCE_NODE ); // Move on if no health at this node -} - - - // Slash -BOOL CBigMomma::CheckMeleeAttack1( float flDot, float flDist ) -{ - if (flDot >= 0.7) - { - if ( flDist <= BIG_ATTACKDIST ) - return TRUE; - } - return FALSE; -} - - -// Lay a crab -BOOL CBigMomma::CheckMeleeAttack2( float flDot, float flDist ) -{ - return CanLayCrab(); -} - - -// Mortar launch -BOOL CBigMomma::CheckRangeAttack1( float flDot, float flDist ) -{ - if ( flDist <= BIG_MORTARDIST && m_mortarTime < gpGlobals->time ) - { - CBaseEntity *pEnemy = m_hEnemy; - - if ( pEnemy ) - { - Vector startPos = pev->origin; - startPos.z += 180; - pev->movedir = VecCheckSplatToss( pev, startPos, pEnemy->BodyTarget( pev->origin ), RANDOM_FLOAT( 150, 500 ) ); - if ( pev->movedir != g_vecZero ) - return TRUE; - } - } - return FALSE; -} - -//========================================================= -// AI Schedules Specific to this monster -//========================================================= - -enum -{ - SCHED_BIG_NODE = LAST_COMMON_SCHEDULE + 1, - SCHED_NODE_FAIL, -}; - -enum -{ - TASK_MOVE_TO_NODE_RANGE = LAST_COMMON_TASK + 1, // Move within node range - TASK_FIND_NODE, // Find my next node - TASK_PLAY_NODE_PRESEQUENCE, // Play node pre-script - TASK_PLAY_NODE_SEQUENCE, // Play node script - TASK_PROCESS_NODE, // Fire targets, etc. - TASK_WAIT_NODE, // Wait at the node - TASK_NODE_DELAY, // Delay walking toward node for a bit. You've failed to get there - TASK_NODE_YAW, // Get the best facing direction for this node -}; - - -Task_t tlBigNode[] = -{ - { TASK_SET_FAIL_SCHEDULE, (float)SCHED_NODE_FAIL }, - { TASK_STOP_MOVING, (float)0 }, - { TASK_FIND_NODE, (float)0 }, // Find my next node - { TASK_PLAY_NODE_PRESEQUENCE,(float)0 }, // Play the pre-approach sequence if any - { TASK_MOVE_TO_NODE_RANGE, (float)0 }, // Move within node range - { TASK_STOP_MOVING, (float)0 }, - { TASK_NODE_YAW, (float)0 }, - { TASK_FACE_IDEAL, (float)0 }, - { TASK_WAIT_NODE, (float)0 }, // Wait for node delay - { TASK_PLAY_NODE_SEQUENCE, (float)0 }, // Play the sequence if one exists - { TASK_PROCESS_NODE, (float)0 }, // Fire targets, etc. - { TASK_SET_ACTIVITY, (float)ACT_IDLE }, -}; - -Schedule_t slBigNode[] = -{ - { - tlBigNode, - ARRAYSIZE ( tlBigNode ), - 0, - 0, - "Big Node" - }, -}; - - -Task_t tlNodeFail[] = -{ - { TASK_NODE_DELAY, (float)10 }, // Try to do something else for 10 seconds - { TASK_SET_ACTIVITY, (float)ACT_IDLE }, -}; - -Schedule_t slNodeFail[] = -{ - { - tlNodeFail, - ARRAYSIZE ( tlNodeFail ), - 0, - 0, - "NodeFail" - }, -}; - -DEFINE_CUSTOM_SCHEDULES( CBigMomma ) -{ - slBigNode, - slNodeFail, -}; - -IMPLEMENT_CUSTOM_SCHEDULES( CBigMomma, CBaseMonster ); - - - - -Schedule_t *CBigMomma::GetScheduleOfType( int Type ) -{ - switch( Type ) - { - case SCHED_BIG_NODE: - return slBigNode; - break; - - case SCHED_NODE_FAIL: - return slNodeFail; - break; - } - - return CBaseMonster::GetScheduleOfType( Type ); -} - - -BOOL CBigMomma::ShouldGoToNode( void ) -{ - if ( HasMemory( bits_MEMORY_ADVANCE_NODE ) ) - { - if ( m_nodeTime < gpGlobals->time ) - return TRUE; - } - return FALSE; -} - -// Overridden to make BigMomma jump on command; the model doesn't support it otherwise. -void CBigMomma :: SetActivity ( Activity NewActivity ) -{ - int iSequence; - - if (NewActivity == ACT_HOP) - { - iSequence = LookupSequence( "jump" ); - } - else - { - iSequence = LookupActivity ( NewActivity ); - } - - // Set to the desired anim, or default anim if the desired is not present - if ( iSequence > ACTIVITY_NOT_AVAILABLE ) - { - if ( pev->sequence != iSequence || !m_fSequenceLoops ) - { - // don't reset frame between walk and run - if ( !(m_Activity == ACT_WALK || m_Activity == ACT_RUN) || !(NewActivity == ACT_WALK || NewActivity == ACT_RUN)) - pev->frame = 0; - } - - pev->sequence = iSequence; // Set to the reset anim (if it's there) - ResetSequenceInfo( ); - SetYawSpeed(); - } - else - { - // Not available try to get default anim - ALERT ( at_aiconsole, "%s has no sequence for act:%d\n", STRING(pev->classname), NewActivity ); - pev->sequence = 0; // Set to the reset anim (if it's there) - } - - m_Activity = NewActivity; // Go ahead and set this so it doesn't keep trying when the anim is not present - - // In case someone calls this with something other than the ideal activity - m_IdealActivity = m_Activity; -} - -Schedule_t *CBigMomma::GetSchedule( void ) -{ - if ( ShouldGoToNode() ) - { - return GetScheduleOfType( SCHED_BIG_NODE ); - } - - return CBaseMonster::GetSchedule(); -} - - -void CBigMomma::StartTask( Task_t *pTask ) -{ - switch ( pTask->iTask ) - { - case TASK_FIND_NODE: - { - CBaseEntity *pTarget = m_hTargetEnt; - if ( !HasMemory( bits_MEMORY_ADVANCE_NODE ) ) - { - if ( pTarget ) - pev->netname = m_hTargetEnt->pev->target; - } - NodeStart( pev->netname ); - TaskComplete(); - ALERT( at_aiconsole, "BM: Found node %s\n", STRING(pev->netname) ); - } - break; - - case TASK_NODE_DELAY: - m_nodeTime = gpGlobals->time + pTask->flData; - TaskComplete(); - ALERT( at_aiconsole, "BM: FAIL! Delay %.2f\n", pTask->flData ); - break; - - case TASK_PROCESS_NODE: - ALERT( at_aiconsole, "BM: Reached node %s\n", STRING(pev->netname) ); - NodeReach(); - TaskComplete(); - break; - - case TASK_PLAY_NODE_PRESEQUENCE: - case TASK_PLAY_NODE_SEQUENCE: - { - int sequence; - if ( pTask->iTask == TASK_PLAY_NODE_SEQUENCE ) - sequence = GetNodeSequence(); - else - sequence = GetNodePresequence(); - - ALERT( at_aiconsole, "BM: Playing node sequence %s\n", STRING(sequence) ); - if ( sequence ) - { - sequence = LookupSequence( STRING( sequence ) ); - if ( sequence != -1 ) - { - pev->sequence = sequence; - pev->frame = 0; - ResetSequenceInfo( ); - ALERT( at_aiconsole, "BM: Sequence %s\n", STRING(GetNodeSequence()) ); - return; - } - } - TaskComplete(); - } - break; - - case TASK_NODE_YAW: - pev->ideal_yaw = GetNodeYaw(); - TaskComplete(); - break; - - case TASK_WAIT_NODE: - m_flWait = gpGlobals->time + GetNodeDelay(); - if ( m_hTargetEnt->pev->spawnflags & SF_INFOBM_WAIT ) - ALERT( at_aiconsole, "BM: Wait at node %s forever\n", STRING(pev->netname) ); - else - ALERT( at_aiconsole, "BM: Wait at node %s for %.2f\n", STRING(pev->netname), GetNodeDelay() ); - break; - - - case TASK_MOVE_TO_NODE_RANGE: - { - CBaseEntity *pTarget = m_hTargetEnt; - if ( !pTarget ) - TaskFail(); - else - { - if ( (pTarget->pev->origin - pev->origin).Length() < GetNodeRange() ) - TaskComplete(); - else - { - Activity act = ACT_WALK; - if ( pTarget->pev->spawnflags & SF_INFOBM_RUN ) - act = ACT_RUN; - - m_vecMoveGoal = pTarget->pev->origin; - if ( !MoveToTarget( act, 2 ) ) - { - TaskFail(); - } - } - } - } - ALERT( at_aiconsole, "BM: Moving to node %s\n", STRING(pev->netname) ); - - break; - - case TASK_MELEE_ATTACK1: - // Play an attack sound here - EMIT_SOUND_DYN( ENT(pev), CHAN_VOICE, RANDOM_SOUND_ARRAY(pAttackSounds), 1.0, ATTN_NORM, 0, PITCH_NORM ); - CBaseMonster::StartTask( pTask ); - break; - - default: - CBaseMonster::StartTask( pTask ); - break; - } -} - -//========================================================= -// RunTask -//========================================================= -void CBigMomma::RunTask( Task_t *pTask ) -{ - switch ( pTask->iTask ) - { - case TASK_MOVE_TO_NODE_RANGE: - { - float distance; - - if ( m_hTargetEnt == NULL ) - TaskFail(); - else - { - distance = ( m_vecMoveGoal - pev->origin ).Length2D(); - // Set the appropriate activity based on an overlapping range - // overlap the range to prevent oscillation - if ( (distance < GetNodeRange()) || MovementIsComplete() ) - { - ALERT( at_aiconsole, "BM: Reached node!\n" ); - TaskComplete(); - RouteClear(); // Stop moving - } - } - } - - break; - - case TASK_WAIT_NODE: - if ( m_hTargetEnt != NULL && (m_hTargetEnt->pev->spawnflags & SF_INFOBM_WAIT) ) - return; - - if ( gpGlobals->time > m_flWaitFinished ) - TaskComplete(); - ALERT( at_aiconsole, "BM: The WAIT is over!\n" ); - break; - - case TASK_PLAY_NODE_PRESEQUENCE: - case TASK_PLAY_NODE_SEQUENCE: - if ( m_fSequenceFinished ) - { - m_Activity = ACT_RESET; - TaskComplete(); - } - break; - - default: - CBaseMonster::RunTask( pTask ); - break; - } -} - - - -Vector VecCheckSplatToss( entvars_t *pev, const Vector &vecSpot1, Vector vecSpot2, float maxHeight ) -{ - TraceResult tr; - Vector vecMidPoint;// halfway point between Spot1 and Spot2 - Vector vecApex;// highest point - Vector vecScale; - Vector vecGrenadeVel; - Vector vecTemp; - float flGravity = g_psv_gravity->value; - - // calculate the midpoint and apex of the 'triangle' - vecMidPoint = vecSpot1 + (vecSpot2 - vecSpot1) * 0.5; - UTIL_TraceLine(vecMidPoint, vecMidPoint + Vector(0,0,maxHeight), ignore_monsters, ENT(pev), &tr); - vecApex = tr.vecEndPos; - - UTIL_TraceLine(vecSpot1, vecApex, dont_ignore_monsters, ENT(pev), &tr); - if (tr.flFraction != 1.0) - { - // fail! - return g_vecZero; - } - - // Don't worry about actually hitting the target, this won't hurt us! - - // How high should the grenade travel (subtract 15 so the grenade doesn't hit the ceiling)? - float height = (vecApex.z - vecSpot1.z) - 15; - // How fast does the grenade need to travel to reach that height given gravity? - float speed = sqrt( 2 * flGravity * height ); - - // How much time does it take to get there? - float time = speed / flGravity; - vecGrenadeVel = (vecSpot2 - vecSpot1); - vecGrenadeVel.z = 0; - float distance = vecGrenadeVel.Length(); - - // Travel half the distance to the target in that time (apex is at the midpoint) - vecGrenadeVel = vecGrenadeVel * ( 0.5 / time ); - // Speed to offset gravity at the desired height - vecGrenadeVel.z = speed; - - return vecGrenadeVel; -} - - - - -// --------------------------------- -// -// Mortar -// -// --------------------------------- -void MortarSpray( const Vector &position, const Vector &direction, int spriteModel, int count ) -{ - MESSAGE_BEGIN( MSG_PVS, gmsgTempEntity, position ); - WRITE_BYTE( TE_SPRITE_SPRAY ); - WRITE_COORD( position.x); // pos - WRITE_COORD( position.y); - WRITE_COORD( position.z); - WRITE_COORD( direction.x); // dir - WRITE_COORD( direction.y); - WRITE_COORD( direction.z); - WRITE_SHORT( spriteModel ); // model - WRITE_BYTE ( count ); // count - WRITE_BYTE ( 130 ); // speed - WRITE_BYTE ( 80 ); // noise ( client will divide by 100 ) - MESSAGE_END(); -} - - -// UNDONE: right now this is pretty much a copy of the squid spit with minor changes to the way it does damage -void CBMortar:: Spawn( void ) -{ - pev->movetype = MOVETYPE_TOSS; - pev->classname = MAKE_STRING( "bmortar" ); - - pev->solid = SOLID_BBOX; - pev->rendermode = kRenderTransAlpha; - pev->renderamt = 255; - - SET_MODEL(ENT(pev), "sprites/mommaspit.spr"); - pev->frame = 0; - pev->scale = 0.5; - - UTIL_SetSize( pev, Vector( 0, 0, 0), Vector(0, 0, 0) ); - - m_maxFrame = (float) MODEL_FRAMES( pev->modelindex ) - 1; - pev->dmgtime = gpGlobals->time + 0.4; -} - -void CBMortar::Animate( void ) -{ - SetNextThink( 0.1 ); - - if ( gpGlobals->time > pev->dmgtime ) - { - pev->dmgtime = gpGlobals->time + 0.2; - MortarSpray( pev->origin, -pev->velocity.Normalize(), gSpitSprite, 3 ); - } - if ( pev->frame++ ) - { - if ( pev->frame > m_maxFrame ) - { - pev->frame = 0; - } - } -} - -CBMortar *CBMortar::Shoot( edict_t *pOwner, Vector vecStart, Vector vecVelocity ) -{ - CBMortar *pSpit = GetClassPtr( (CBMortar *)NULL ); - pSpit->Spawn(); - - UTIL_SetOrigin( pSpit, vecStart ); - pSpit->pev->velocity = vecVelocity; - pSpit->pev->owner = pOwner; - pSpit->pev->scale = 2.5; - pSpit->SetThink(&CBMortar:: Animate ); - pSpit->SetNextThink( 0.1 ); - - return pSpit; -} - - -void CBMortar::Touch( CBaseEntity *pOther ) -{ - TraceResult tr; - int iPitch; - - // splat sound - iPitch = RANDOM_FLOAT( 90, 110 ); - - EMIT_SOUND_DYN( ENT(pev), CHAN_VOICE, "bullchicken/bc_acid1.wav", 1, ATTN_NORM, 0, iPitch ); - - switch ( RANDOM_LONG( 0, 1 ) ) - { - case 0: - EMIT_SOUND_DYN( ENT(pev), CHAN_WEAPON, "bullchicken/bc_spithit1.wav", 1, ATTN_NORM, 0, iPitch ); - break; - case 1: - EMIT_SOUND_DYN( ENT(pev), CHAN_WEAPON, "bullchicken/bc_spithit2.wav", 1, ATTN_NORM, 0, iPitch ); - break; - } - - if ( pOther->IsBSPModel() ) - { - - // make a splat on the wall - UTIL_TraceLine( pev->origin, pev->origin + pev->velocity * 10, dont_ignore_monsters, ENT( pev ), &tr ); - UTIL_DecalTrace(&tr, DECAL_MOMMASPLAT); - } - else - { - tr.vecEndPos = pev->origin; - tr.vecPlaneNormal = -1 * pev->velocity.Normalize(); - } - // make some flecks - MortarSpray( tr.vecEndPos, tr.vecPlaneNormal, gSpitSprite, 24 ); - - entvars_t *pevOwner = NULL; - if ( pev->owner ) - pevOwner = VARS(pev->owner); - - RadiusDamage( pev->origin, pev, pevOwner, gSkillData.bigmommaDmgBlast, gSkillData.bigmommaRadiusBlast, CLASS_NONE, DMG_ACID ); - UTIL_Remove( this ); -} - -#endif +/*** +* +* Copyright (c) 1996-2002, 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. +* +* This source code contains proprietary and confidential information of +* Valve LLC and its suppliers. Access to this code is restricted to +* persons who have executed a written SDK license with Valve. Any access, +* use or distribution of this code by or to any unlicensed person is illegal. +* +****/ +#if !defined( OEM_BUILD ) && !defined( HLDEMO_BUILD ) + +//========================================================= +// monster template +//========================================================= +#include "extdll.h" +#include "util.h" +#include "cbase.h" +#include "monsters.h" +#include "schedule.h" +#include "decals.h" +#include "weapons.h" +#include "game.h" + +#define SF_INFOBM_RUN 0x0001 +#define SF_INFOBM_WAIT 0x0002 + +// AI Nodes for Big Momma +class CInfoBM : public CPointEntity +{ +public: + void Spawn( void ); + void KeyValue( KeyValueData* pkvd ); + + // name in pev->targetname + // next in pev->target + // radius in pev->scale + // health in pev->health + // Reach target in pev->message + // Reach delay in pev->speed + // Reach sequence in pev->netname + + virtual int Save( CSave &save ); + virtual int Restore( CRestore &restore ); + static TYPEDESCRIPTION m_SaveData[]; + + int m_preSequence; +}; + +LINK_ENTITY_TO_CLASS( info_bigmomma, CInfoBM ); + +TYPEDESCRIPTION CInfoBM::m_SaveData[] = +{ + DEFINE_FIELD( CInfoBM, m_preSequence, FIELD_STRING ), +}; + +IMPLEMENT_SAVERESTORE( CInfoBM, CPointEntity ); + +void CInfoBM::Spawn( void ) +{ +} + + +void CInfoBM::KeyValue( KeyValueData* pkvd ) +{ + if (FStrEq(pkvd->szKeyName, "radius")) + { + pev->scale = atof(pkvd->szValue); + pkvd->fHandled = TRUE; + } + else if (FStrEq(pkvd->szKeyName, "reachdelay")) + { + pev->speed = atof(pkvd->szValue); + pkvd->fHandled = TRUE; + } + else if (FStrEq(pkvd->szKeyName, "reachtarget")) + { + pev->message = ALLOC_STRING(pkvd->szValue); + pkvd->fHandled = TRUE; + } + else if (FStrEq(pkvd->szKeyName, "reachsequence")) + { + pev->netname = ALLOC_STRING(pkvd->szValue); + pkvd->fHandled = TRUE; + } + else if (FStrEq(pkvd->szKeyName, "presequence")) + { + m_preSequence = ALLOC_STRING(pkvd->szValue); + pkvd->fHandled = TRUE; + } + else + CPointEntity::KeyValue( pkvd ); +} + +//========================================================= +// Mortar shot entity +//========================================================= +class CBMortar : public CBaseEntity +{ +public: + void Spawn( void ); + + static CBMortar *Shoot( edict_t *pOwner, Vector vecStart, Vector vecVelocity ); + void Touch( CBaseEntity *pOther ); + void EXPORT Animate( void ); + + virtual int Save( CSave &save ); + virtual int Restore( CRestore &restore ); + static TYPEDESCRIPTION m_SaveData[]; + + int m_maxFrame; +}; + +LINK_ENTITY_TO_CLASS( bmortar, CBMortar ); + +TYPEDESCRIPTION CBMortar::m_SaveData[] = +{ + DEFINE_FIELD( CBMortar, m_maxFrame, FIELD_INTEGER ), +}; + +IMPLEMENT_SAVERESTORE( CBMortar, CBaseEntity ); + + +//========================================================= +// Monster's Anim Events Go Here +//========================================================= +#define BIG_AE_STEP1 1 // Footstep left +#define BIG_AE_STEP2 2 // Footstep right +#define BIG_AE_STEP3 3 // Footstep back left +#define BIG_AE_STEP4 4 // Footstep back right +#define BIG_AE_SACK 5 // Sack slosh +#define BIG_AE_DEATHSOUND 6 // Death sound + +#define BIG_AE_MELEE_ATTACKBR 8 // Leg attack +#define BIG_AE_MELEE_ATTACKBL 9 // Leg attack +#define BIG_AE_MELEE_ATTACK1 10 // Leg attack +#define BIG_AE_MORTAR_ATTACK1 11 // Launch a mortar +#define BIG_AE_LAY_CRAB 12 // Lay a headcrab +#define BIG_AE_JUMP_FORWARD 13 // Jump up and forward +#define BIG_AE_SCREAM 14 // alert sound +#define BIG_AE_PAIN_SOUND 15 // pain sound +#define BIG_AE_ATTACK_SOUND 16 // attack sound +#define BIG_AE_BIRTH_SOUND 17 // birth sound +#define BIG_AE_EARLY_TARGET 50 // Fire target early + + + +// User defined conditions +#define bits_COND_NODE_SEQUENCE ( bits_COND_SPECIAL1 ) // pev->netname contains the name of a sequence to play + +// Attack distance constants +#define BIG_ATTACKDIST 170 +#define BIG_MORTARDIST 800 +#define BIG_MAXCHILDREN 20 // Max # of live headcrab children + + +#define bits_MEMORY_CHILDPAIR (bits_MEMORY_CUSTOM1) +#define bits_MEMORY_ADVANCE_NODE (bits_MEMORY_CUSTOM2) +#define bits_MEMORY_COMPLETED_NODE (bits_MEMORY_CUSTOM3) +#define bits_MEMORY_FIRED_NODE (bits_MEMORY_CUSTOM4) + +int gSpitSprite, gSpitDebrisSprite; +Vector VecCheckSplatToss( entvars_t *pev, const Vector &vecSpot1, Vector vecSpot2, float maxHeight ); +void MortarSpray( const Vector &position, const Vector &direction, int spriteModel, int count ); + + +// UNDONE: +// +#define BIG_CHILDCLASS "monster_babycrab" + +class CBigMomma : public CBaseMonster +{ +public: + void Spawn( void ); + void Precache( void ); + void KeyValue( KeyValueData *pkvd ); + void Activate( void ); + int TakeDamage( entvars_t *pevInflictor, entvars_t *pevAttacker, float flDamage, int bitsDamageType ); + + void RunTask( Task_t *pTask ); + void StartTask( Task_t *pTask ); + Schedule_t *GetSchedule( void ); + Schedule_t *GetScheduleOfType( int Type ); + void TraceAttack( entvars_t *pevAttacker, float flDamage, Vector vecDir, TraceResult *ptr, int bitsDamageType ); + + void NodeStart( int iszNextNode ); + void NodeReach( void ); + BOOL ShouldGoToNode( void ); + + void SetYawSpeed( void ); + int Classify ( void ); + void HandleAnimEvent( MonsterEvent_t *pEvent ); + void LayHeadcrab( void ); + + int GetNodeSequence( void ) + { + CBaseEntity *pTarget = m_hTargetEnt; + if ( pTarget ) + { + return pTarget->pev->netname; // netname holds node sequence + } + return 0; + } + + + int GetNodePresequence( void ) + { + CInfoBM *pTarget = (CInfoBM *)(CBaseEntity *)m_hTargetEnt; + if ( pTarget ) + { + return pTarget->m_preSequence; + } + return 0; + } + + float GetNodeDelay( void ) + { + CBaseEntity *pTarget = m_hTargetEnt; + if ( pTarget ) + { + return pTarget->pev->speed; // Speed holds node delay + } + return 0; + } + + float GetNodeRange( void ) + { + CBaseEntity *pTarget = m_hTargetEnt; + if ( pTarget ) + { + return pTarget->pev->scale; // Scale holds node delay + } + return 1e6; + } + + float GetNodeYaw( void ) + { + CBaseEntity *pTarget = m_hTargetEnt; + if ( pTarget ) + { + if ( pTarget->pev->angles.y != 0 ) + return pTarget->pev->angles.y; + } + return pev->angles.y; + } + + // Restart the crab count on each new level + void OverrideReset( void ) + { + m_crabCount = 0; + } + + void DeathNotice( entvars_t *pevChild ); + + BOOL CanLayCrab( void ) + { + if ( m_crabTime < gpGlobals->time && m_crabCount < BIG_MAXCHILDREN ) + { + // Don't spawn crabs inside each other + Vector mins = pev->origin - Vector( 32, 32, 0 ); + Vector maxs = pev->origin + Vector( 32, 32, 0 ); + + CBaseEntity *pList[2]; + int count = UTIL_EntitiesInBox( pList, 2, mins, maxs, FL_MONSTER ); + for ( int i = 0; i < count; i++ ) + { + if ( pList[i] != this ) // Don't hurt yourself! + return FALSE; + } + return TRUE; + } + + return FALSE; + } + + void LaunchMortar( void ); + + void SetObjectCollisionBox( void ) + { + pev->absmin = pev->origin + Vector( -95, -95, 0 ); + pev->absmax = pev->origin + Vector( 95, 95, 190 ); + } + + BOOL CheckMeleeAttack1( float flDot, float flDist ); // Slash + BOOL CheckMeleeAttack2( float flDot, float flDist ); // Lay a crab + BOOL CheckRangeAttack1( float flDot, float flDist ); // Mortar launch + + virtual int Save( CSave &save ); + virtual int Restore( CRestore &restore ); + static TYPEDESCRIPTION m_SaveData[]; + + static const char *pChildDieSounds[]; + static const char *pSackSounds[]; + static const char *pDeathSounds[]; + static const char *pAttackSounds[]; + static const char *pAttackHitSounds[]; + static const char *pBirthSounds[]; + static const char *pAlertSounds[]; + static const char *pPainSounds[]; + static const char *pFootSounds[]; + + CUSTOM_SCHEDULES; + +private: + float m_nodeTime; + float m_crabTime; + float m_mortarTime; + float m_painSoundTime; + int m_crabCount; +}; +LINK_ENTITY_TO_CLASS( monster_bigmomma, CBigMomma ); + +TYPEDESCRIPTION CBigMomma::m_SaveData[] = +{ + DEFINE_FIELD( CBigMomma, m_nodeTime, FIELD_TIME ), + DEFINE_FIELD( CBigMomma, m_crabTime, FIELD_TIME ), + DEFINE_FIELD( CBigMomma, m_mortarTime, FIELD_TIME ), + DEFINE_FIELD( CBigMomma, m_painSoundTime, FIELD_TIME ), + DEFINE_FIELD( CBigMomma, m_crabCount, FIELD_INTEGER ), +}; + +IMPLEMENT_SAVERESTORE( CBigMomma, CBaseMonster ); + +const char *CBigMomma::pChildDieSounds[] = +{ + "gonarch/gon_childdie1.wav", + "gonarch/gon_childdie2.wav", + "gonarch/gon_childdie3.wav", +}; + +const char *CBigMomma::pSackSounds[] = +{ + "gonarch/gon_sack1.wav", + "gonarch/gon_sack2.wav", + "gonarch/gon_sack3.wav", +}; + +const char *CBigMomma::pDeathSounds[] = +{ + "gonarch/gon_die1.wav", +}; + +const char *CBigMomma::pAttackSounds[] = +{ + "gonarch/gon_attack1.wav", + "gonarch/gon_attack2.wav", + "gonarch/gon_attack3.wav", +}; +const char *CBigMomma::pAttackHitSounds[] = +{ + "zombie/claw_strike1.wav", + "zombie/claw_strike2.wav", + "zombie/claw_strike3.wav", +}; + +const char *CBigMomma::pBirthSounds[] = +{ + "gonarch/gon_birth1.wav", + "gonarch/gon_birth2.wav", + "gonarch/gon_birth3.wav", +}; + +const char *CBigMomma::pAlertSounds[] = +{ + "gonarch/gon_alert1.wav", + "gonarch/gon_alert2.wav", + "gonarch/gon_alert3.wav", +}; + +const char *CBigMomma::pPainSounds[] = +{ + "gonarch/gon_pain2.wav", + "gonarch/gon_pain4.wav", + "gonarch/gon_pain5.wav", +}; + +const char *CBigMomma::pFootSounds[] = +{ + "gonarch/gon_step1.wav", + "gonarch/gon_step2.wav", + "gonarch/gon_step3.wav", +}; + + + +void CBigMomma :: KeyValue( KeyValueData *pkvd ) +{ +#if 0 + if (FStrEq(pkvd->szKeyName, "volume")) + { + m_volume = atof(pkvd->szValue); + pkvd->fHandled = TRUE; + } + else +#endif + CBaseMonster::KeyValue( pkvd ); +} + +//========================================================= +// Classify - indicates this monster's place in the +// relationship table. +//========================================================= +int CBigMomma :: Classify ( void ) +{ + return CLASS_ALIEN_MONSTER; +} + +//========================================================= +// SetYawSpeed - allows each sequence to have a different +// turn rate associated with it. +//========================================================= +void CBigMomma :: SetYawSpeed ( void ) +{ + int ys; + + switch ( m_Activity ) + { + case ACT_IDLE: + ys = 100; + break; + default: + ys = 90; + } + pev->yaw_speed = ys; +} + +//========================================================= +// HandleAnimEvent - catches the monster-specific messages +// that occur when tagged animation frames are played. +// +// Returns number of events handled, 0 if none. +//========================================================= +void CBigMomma :: HandleAnimEvent( MonsterEvent_t *pEvent ) +{ + switch( pEvent->event ) + { + case BIG_AE_MELEE_ATTACKBR: + case BIG_AE_MELEE_ATTACKBL: + case BIG_AE_MELEE_ATTACK1: + { + Vector forward, right; + + UTIL_MakeVectorsPrivate( pev->angles, forward, right, NULL ); + + Vector center = pev->origin + forward * 128; + Vector mins = center - Vector( 64, 64, 0 ); + Vector maxs = center + Vector( 64, 64, 64 ); + + CBaseEntity *pList[8]; + int count = UTIL_EntitiesInBox( pList, 8, mins, maxs, FL_MONSTER|FL_CLIENT ); + CBaseEntity *pHurt = NULL; + + for ( int i = 0; i < count && !pHurt; i++ ) + { + if ( pList[i] != this ) + { + if ( pList[i]->pev->owner != edict() ) + pHurt = pList[i]; + } + } + + if ( pHurt ) + { + pHurt->TakeDamage( pev, pev, gSkillData.bigmommaDmgSlash, DMG_CRUSH | DMG_SLASH ); + pHurt->pev->punchangle.x = 15; + switch( pEvent->event ) + { + case BIG_AE_MELEE_ATTACKBR: + pHurt->pev->velocity = pHurt->pev->velocity + (forward * 150) + Vector(0,0,250) - (right * 200); + break; + + case BIG_AE_MELEE_ATTACKBL: + pHurt->pev->velocity = pHurt->pev->velocity + (forward * 150) + Vector(0,0,250) + (right * 200); + break; + + case BIG_AE_MELEE_ATTACK1: + pHurt->pev->velocity = pHurt->pev->velocity + (forward * 220) + Vector(0,0,200); + break; + } + + pHurt->pev->flags &= ~FL_ONGROUND; + EMIT_SOUND_DYN( edict(), CHAN_WEAPON, RANDOM_SOUND_ARRAY(pAttackHitSounds), 1.0, ATTN_NORM, 0, 100 + RANDOM_LONG(-5,5) ); + } + } + break; + + case BIG_AE_SCREAM: + EMIT_SOUND_ARRAY_DYN( CHAN_VOICE, pAlertSounds ); + break; + + case BIG_AE_PAIN_SOUND: + EMIT_SOUND_ARRAY_DYN( CHAN_VOICE, pPainSounds ); + break; + + case BIG_AE_ATTACK_SOUND: + EMIT_SOUND_ARRAY_DYN( CHAN_WEAPON, pAttackSounds ); + break; + + case BIG_AE_BIRTH_SOUND: + EMIT_SOUND_ARRAY_DYN( CHAN_BODY, pBirthSounds ); + break; + + case BIG_AE_SACK: + if ( RANDOM_LONG(0,100) < 30 ) + EMIT_SOUND_ARRAY_DYN( CHAN_BODY, pSackSounds ); + break; + + case BIG_AE_DEATHSOUND: + EMIT_SOUND_ARRAY_DYN( CHAN_VOICE, pDeathSounds ); + break; + + case BIG_AE_STEP1: // Footstep left + case BIG_AE_STEP3: // Footstep back left + EMIT_SOUND_ARRAY_DYN( CHAN_ITEM, pFootSounds ); + break; + + case BIG_AE_STEP4: // Footstep back right + case BIG_AE_STEP2: // Footstep right + EMIT_SOUND_ARRAY_DYN( CHAN_BODY, pFootSounds ); + break; + + case BIG_AE_MORTAR_ATTACK1: + LaunchMortar(); + break; + + case BIG_AE_LAY_CRAB: + LayHeadcrab(); + break; + + case BIG_AE_JUMP_FORWARD: + ClearBits( pev->flags, FL_ONGROUND ); + + UTIL_SetOrigin (pev, pev->origin + Vector ( 0 , 0 , 1) );// take him off ground so engine doesn't instantly reset onground + UTIL_MakeVectors ( pev->angles ); + + pev->velocity = (gpGlobals->v_forward * 200) + gpGlobals->v_up * 500; + break; + + case BIG_AE_EARLY_TARGET: + { + CBaseEntity *pTarget = m_hTargetEnt; + if ( pTarget && pTarget->pev->message ) + FireTargets( STRING(pTarget->pev->message), this, this, USE_TOGGLE, 0 ); + Remember( bits_MEMORY_FIRED_NODE ); + } + break; + + default: + CBaseMonster::HandleAnimEvent( pEvent ); + break; + } +} + +void CBigMomma :: TraceAttack( entvars_t *pevAttacker, float flDamage, Vector vecDir, TraceResult *ptr, int bitsDamageType ) +{ + if ( ptr->iHitgroup != 1 ) + { + // didn't hit the sack? + + if ( pev->dmgtime != gpGlobals->time || (RANDOM_LONG(0,10) < 1) ) + { + UTIL_Ricochet( ptr->vecEndPos, RANDOM_FLOAT( 1, 2) ); + pev->dmgtime = gpGlobals->time; + } + + flDamage = 0.1;// don't hurt the monster much, but allow bits_COND_LIGHT_DAMAGE to be generated + } + else if ( gpGlobals->time > m_painSoundTime ) + { + m_painSoundTime = gpGlobals->time + RANDOM_LONG(1, 3); + EMIT_SOUND_ARRAY_DYN( CHAN_VOICE, pPainSounds ); + } + + + CBaseMonster::TraceAttack( pevAttacker, flDamage, vecDir, ptr, bitsDamageType ); +} + + +int CBigMomma :: TakeDamage( entvars_t *pevInflictor, entvars_t *pevAttacker, float flDamage, int bitsDamageType ) +{ + // Don't take any acid damage -- BigMomma's mortar is acid + if ( bitsDamageType & DMG_ACID ) + flDamage = 0; + + if ( !HasMemory(bits_MEMORY_PATH_FINISHED) ) + { + if ( pev->health <= flDamage ) + { + pev->health = flDamage + 1; + Remember( bits_MEMORY_ADVANCE_NODE | bits_MEMORY_COMPLETED_NODE ); + ALERT( at_aiconsole, "BM: Finished node health!!!\n" ); + } + } + + return CBaseMonster::TakeDamage( pevInflictor, pevAttacker, flDamage, bitsDamageType ); +} + +void CBigMomma :: LayHeadcrab( void ) +{ + CBaseEntity *pChild = CBaseEntity::Create( BIG_CHILDCLASS, pev->origin, pev->angles, edict() ); + + pChild->pev->spawnflags |= SF_MONSTER_FALL_TO_GROUND; + + // Is this the second crab in a pair? + if ( HasMemory( bits_MEMORY_CHILDPAIR ) ) + { + m_crabTime = gpGlobals->time + RANDOM_FLOAT( 5, 10 ); + Forget( bits_MEMORY_CHILDPAIR ); + } + else + { + m_crabTime = gpGlobals->time + RANDOM_FLOAT( 0.5, 2.5 ); + Remember( bits_MEMORY_CHILDPAIR ); + } + + TraceResult tr; + UTIL_TraceLine( pev->origin, pev->origin - Vector(0,0,100), ignore_monsters, edict(), &tr); + UTIL_DecalTrace( &tr, DECAL_MOMMABIRTH ); + + EMIT_SOUND_DYN( edict(), CHAN_WEAPON, RANDOM_SOUND_ARRAY(pBirthSounds), 1.0, ATTN_NORM, 0, 100 + RANDOM_LONG(-5,5) ); + m_crabCount++; +} + + + +void CBigMomma::DeathNotice( entvars_t *pevChild ) +{ + if ( m_crabCount > 0 ) // Some babies may cross a transition, but we reset the count then + m_crabCount--; + if ( IsAlive() ) + { + // Make the "my baby's dead" noise! + EMIT_SOUND_ARRAY_DYN( CHAN_WEAPON, pChildDieSounds ); + } +} + + +void CBigMomma::LaunchMortar( void ) +{ + m_mortarTime = gpGlobals->time + RANDOM_FLOAT( 2, 15 ); + + Vector startPos = pev->origin; + startPos.z += 180; + + EMIT_SOUND_DYN( edict(), CHAN_WEAPON, RANDOM_SOUND_ARRAY(pSackSounds), 1.0, ATTN_NORM, 0, 100 + RANDOM_LONG(-5,5) ); + CBMortar *pBomb = CBMortar::Shoot( edict(), startPos, pev->movedir ); + pBomb->pev->gravity = 1.0; + MortarSpray( startPos, Vector(0,0,1), gSpitSprite, 24 ); +} + +//========================================================= +// Spawn +//========================================================= +void CBigMomma :: Spawn() +{ + Precache( ); + + SET_MODEL(ENT(pev), "models/big_mom.mdl"); + UTIL_SetSize( pev, Vector( -32, -32, 0 ), Vector( 32, 32, 64 ) ); + + pev->solid = SOLID_SLIDEBOX; + pev->movetype = MOVETYPE_STEP; + m_bloodColor = BLOOD_COLOR_GREEN; + pev->health = 150 * gSkillData.bigmommaHealthFactor; + pev->view_ofs = Vector ( 0, 0, 128 );// position of the eyes relative to monster's origin. + m_flFieldOfView = 0.3;// indicates the width of this monster's forward view cone ( as a dotproduct result ) + m_MonsterState = MONSTERSTATE_NONE; + + MonsterInit(); +} + +//========================================================= +// Precache - precaches all resources this monster needs +//========================================================= +void CBigMomma :: Precache() +{ + PRECACHE_MODEL("models/big_mom.mdl"); + + PRECACHE_SOUND_ARRAY( pChildDieSounds ); + PRECACHE_SOUND_ARRAY( pSackSounds ); + PRECACHE_SOUND_ARRAY( pDeathSounds ); + PRECACHE_SOUND_ARRAY( pAttackSounds ); + PRECACHE_SOUND_ARRAY( pAttackHitSounds ); + PRECACHE_SOUND_ARRAY( pBirthSounds ); + PRECACHE_SOUND_ARRAY( pAlertSounds ); + PRECACHE_SOUND_ARRAY( pPainSounds ); + PRECACHE_SOUND_ARRAY( pFootSounds ); + + UTIL_PrecacheOther( BIG_CHILDCLASS ); + + // TEMP: Squid + PRECACHE_MODEL("sprites/mommaspit.spr");// spit projectile. + gSpitSprite = PRECACHE_MODEL("sprites/mommaspout.spr");// client side spittle. + gSpitDebrisSprite = PRECACHE_MODEL("sprites/mommablob.spr" ); + + PRECACHE_SOUND( "bullchicken/bc_acid1.wav" ); + PRECACHE_SOUND( "bullchicken/bc_spithit1.wav" ); + PRECACHE_SOUND( "bullchicken/bc_spithit2.wav" ); +} + + +void CBigMomma::Activate( void ) +{ + if ( m_hTargetEnt == NULL ) + Remember( bits_MEMORY_ADVANCE_NODE ); // Start 'er up +} + + +void CBigMomma::NodeStart( int iszNextNode ) +{ + pev->netname = iszNextNode; + + CBaseEntity *pTarget = NULL; + + if ( pev->netname ) + { + edict_t *pentTarget = FIND_ENTITY_BY_TARGETNAME ( NULL, STRING(pev->netname) ); + + if ( !FNullEnt(pentTarget) ) + pTarget = Instance( pentTarget ); + } + + + if ( !pTarget ) + { + ALERT( at_aiconsole, "BM: Finished the path!!\n" ); + Remember( bits_MEMORY_PATH_FINISHED ); + return; + } + Remember( bits_MEMORY_ON_PATH ); + m_hTargetEnt = pTarget; +} + + +void CBigMomma::NodeReach( void ) +{ + CBaseEntity *pTarget = m_hTargetEnt; + + Forget( bits_MEMORY_ADVANCE_NODE ); + + if ( !pTarget ) + return; + + if ( pTarget->pev->health ) + pev->max_health = pev->health = pTarget->pev->health * gSkillData.bigmommaHealthFactor; + + if ( !HasMemory( bits_MEMORY_FIRED_NODE ) ) + { + if ( pTarget->pev->message ) + FireTargets( STRING(pTarget->pev->message), this, this, USE_TOGGLE, 0 ); + } + Forget( bits_MEMORY_FIRED_NODE ); + + pev->netname = pTarget->pev->target; + if ( pTarget->pev->health == 0 ) + Remember( bits_MEMORY_ADVANCE_NODE ); // Move on if no health at this node +} + + + // Slash +BOOL CBigMomma::CheckMeleeAttack1( float flDot, float flDist ) +{ + if (flDot >= 0.7) + { + if ( flDist <= BIG_ATTACKDIST ) + return TRUE; + } + return FALSE; +} + + +// Lay a crab +BOOL CBigMomma::CheckMeleeAttack2( float flDot, float flDist ) +{ + return CanLayCrab(); +} + + +// Mortar launch +BOOL CBigMomma::CheckRangeAttack1( float flDot, float flDist ) +{ + if ( flDist <= BIG_MORTARDIST && m_mortarTime < gpGlobals->time ) + { + CBaseEntity *pEnemy = m_hEnemy; + + if ( pEnemy ) + { + Vector startPos = pev->origin; + startPos.z += 180; + pev->movedir = VecCheckSplatToss( pev, startPos, pEnemy->BodyTarget( pev->origin ), RANDOM_FLOAT( 150, 500 ) ); + if ( pev->movedir != g_vecZero ) + return TRUE; + } + } + return FALSE; +} + +//========================================================= +// AI Schedules Specific to this monster +//========================================================= + +enum +{ + SCHED_BIG_NODE = LAST_COMMON_SCHEDULE + 1, + SCHED_NODE_FAIL, +}; + +enum +{ + TASK_MOVE_TO_NODE_RANGE = LAST_COMMON_TASK + 1, // Move within node range + TASK_FIND_NODE, // Find my next node + TASK_PLAY_NODE_PRESEQUENCE, // Play node pre-script + TASK_PLAY_NODE_SEQUENCE, // Play node script + TASK_PROCESS_NODE, // Fire targets, etc. + TASK_WAIT_NODE, // Wait at the node + TASK_NODE_DELAY, // Delay walking toward node for a bit. You've failed to get there + TASK_NODE_YAW, // Get the best facing direction for this node +}; + + +Task_t tlBigNode[] = +{ + { TASK_SET_FAIL_SCHEDULE, (float)SCHED_NODE_FAIL }, + { TASK_STOP_MOVING, (float)0 }, + { TASK_FIND_NODE, (float)0 }, // Find my next node + { TASK_PLAY_NODE_PRESEQUENCE,(float)0 }, // Play the pre-approach sequence if any + { TASK_MOVE_TO_NODE_RANGE, (float)0 }, // Move within node range + { TASK_STOP_MOVING, (float)0 }, + { TASK_NODE_YAW, (float)0 }, + { TASK_FACE_IDEAL, (float)0 }, + { TASK_WAIT_NODE, (float)0 }, // Wait for node delay + { TASK_PLAY_NODE_SEQUENCE, (float)0 }, // Play the sequence if one exists + { TASK_PROCESS_NODE, (float)0 }, // Fire targets, etc. + { TASK_SET_ACTIVITY, (float)ACT_IDLE }, +}; + +Schedule_t slBigNode[] = +{ + { + tlBigNode, + ARRAYSIZE ( tlBigNode ), + 0, + 0, + "Big Node" + }, +}; + + +Task_t tlNodeFail[] = +{ + { TASK_NODE_DELAY, (float)10 }, // Try to do something else for 10 seconds + { TASK_SET_ACTIVITY, (float)ACT_IDLE }, +}; + +Schedule_t slNodeFail[] = +{ + { + tlNodeFail, + ARRAYSIZE ( tlNodeFail ), + 0, + 0, + "NodeFail" + }, +}; + +DEFINE_CUSTOM_SCHEDULES( CBigMomma ) +{ + slBigNode, + slNodeFail, +}; + +IMPLEMENT_CUSTOM_SCHEDULES( CBigMomma, CBaseMonster ); + + + + +Schedule_t *CBigMomma::GetScheduleOfType( int Type ) +{ + switch( Type ) + { + case SCHED_BIG_NODE: + return slBigNode; + break; + + case SCHED_NODE_FAIL: + return slNodeFail; + break; + } + + return CBaseMonster::GetScheduleOfType( Type ); +} + + +BOOL CBigMomma::ShouldGoToNode( void ) +{ + if ( HasMemory( bits_MEMORY_ADVANCE_NODE ) ) + { + if ( m_nodeTime < gpGlobals->time ) + return TRUE; + } + return FALSE; +} + + + +Schedule_t *CBigMomma::GetSchedule( void ) +{ + if ( ShouldGoToNode() ) + { + return GetScheduleOfType( SCHED_BIG_NODE ); + } + + return CBaseMonster::GetSchedule(); +} + + +void CBigMomma::StartTask( Task_t *pTask ) +{ + switch ( pTask->iTask ) + { + case TASK_FIND_NODE: + { + CBaseEntity *pTarget = m_hTargetEnt; + if ( !HasMemory( bits_MEMORY_ADVANCE_NODE ) ) + { + if ( pTarget ) + pev->netname = m_hTargetEnt->pev->target; + } + NodeStart( pev->netname ); + TaskComplete(); + ALERT( at_aiconsole, "BM: Found node %s\n", STRING(pev->netname) ); + } + break; + + case TASK_NODE_DELAY: + m_nodeTime = gpGlobals->time + pTask->flData; + TaskComplete(); + ALERT( at_aiconsole, "BM: FAIL! Delay %.2f\n", pTask->flData ); + break; + + case TASK_PROCESS_NODE: + ALERT( at_aiconsole, "BM: Reached node %s\n", STRING(pev->netname) ); + NodeReach(); + TaskComplete(); + break; + + case TASK_PLAY_NODE_PRESEQUENCE: + case TASK_PLAY_NODE_SEQUENCE: + { + int sequence; + if ( pTask->iTask == TASK_PLAY_NODE_SEQUENCE ) + sequence = GetNodeSequence(); + else + sequence = GetNodePresequence(); + + ALERT( at_aiconsole, "BM: Playing node sequence %s\n", STRING(sequence) ); + if ( sequence ) + { + sequence = LookupSequence( STRING( sequence ) ); + if ( sequence != -1 ) + { + pev->sequence = sequence; + pev->frame = 0; + ResetSequenceInfo( ); + ALERT( at_aiconsole, "BM: Sequence %s\n", STRING(GetNodeSequence()) ); + return; + } + } + TaskComplete(); + } + break; + + case TASK_NODE_YAW: + pev->ideal_yaw = GetNodeYaw(); + TaskComplete(); + break; + + case TASK_WAIT_NODE: + m_flWait = gpGlobals->time + GetNodeDelay(); + if ( m_hTargetEnt->pev->spawnflags & SF_INFOBM_WAIT ) + ALERT( at_aiconsole, "BM: Wait at node %s forever\n", STRING(pev->netname) ); + else + ALERT( at_aiconsole, "BM: Wait at node %s for %.2f\n", STRING(pev->netname), GetNodeDelay() ); + break; + + + case TASK_MOVE_TO_NODE_RANGE: + { + CBaseEntity *pTarget = m_hTargetEnt; + if ( !pTarget ) + TaskFail(); + else + { + if ( (pTarget->pev->origin - pev->origin).Length() < GetNodeRange() ) + TaskComplete(); + else + { + Activity act = ACT_WALK; + if ( pTarget->pev->spawnflags & SF_INFOBM_RUN ) + act = ACT_RUN; + + m_vecMoveGoal = pTarget->pev->origin; + if ( !MoveToTarget( act, 2 ) ) + { + TaskFail(); + } + } + } + } + ALERT( at_aiconsole, "BM: Moving to node %s\n", STRING(pev->netname) ); + + break; + + case TASK_MELEE_ATTACK1: + // Play an attack sound here + EMIT_SOUND_DYN( ENT(pev), CHAN_VOICE, RANDOM_SOUND_ARRAY(pAttackSounds), 1.0, ATTN_NORM, 0, PITCH_NORM ); + CBaseMonster::StartTask( pTask ); + break; + + default: + CBaseMonster::StartTask( pTask ); + break; + } +} + +//========================================================= +// RunTask +//========================================================= +void CBigMomma::RunTask( Task_t *pTask ) +{ + switch ( pTask->iTask ) + { + case TASK_MOVE_TO_NODE_RANGE: + { + float distance; + + if ( m_hTargetEnt == NULL ) + TaskFail(); + else + { + distance = ( m_vecMoveGoal - pev->origin ).Length2D(); + // Set the appropriate activity based on an overlapping range + // overlap the range to prevent oscillation + if ( (distance < GetNodeRange()) || MovementIsComplete() ) + { + ALERT( at_aiconsole, "BM: Reached node!\n" ); + TaskComplete(); + RouteClear(); // Stop moving + } + } + } + + break; + + case TASK_WAIT_NODE: + if ( m_hTargetEnt != NULL && (m_hTargetEnt->pev->spawnflags & SF_INFOBM_WAIT) ) + return; + + if ( gpGlobals->time > m_flWaitFinished ) + TaskComplete(); + ALERT( at_aiconsole, "BM: The WAIT is over!\n" ); + break; + + case TASK_PLAY_NODE_PRESEQUENCE: + case TASK_PLAY_NODE_SEQUENCE: + if ( m_fSequenceFinished ) + { + m_Activity = ACT_RESET; + TaskComplete(); + } + break; + + default: + CBaseMonster::RunTask( pTask ); + break; + } +} + + + +Vector VecCheckSplatToss( entvars_t *pev, const Vector &vecSpot1, Vector vecSpot2, float maxHeight ) +{ + TraceResult tr; + Vector vecMidPoint;// halfway point between Spot1 and Spot2 + Vector vecApex;// highest point + Vector vecScale; + Vector vecGrenadeVel; + Vector vecTemp; + float flGravity = g_psv_gravity->value; + + // calculate the midpoint and apex of the 'triangle' + vecMidPoint = vecSpot1 + (vecSpot2 - vecSpot1) * 0.5; + UTIL_TraceLine(vecMidPoint, vecMidPoint + Vector(0,0,maxHeight), ignore_monsters, ENT(pev), &tr); + vecApex = tr.vecEndPos; + + UTIL_TraceLine(vecSpot1, vecApex, dont_ignore_monsters, ENT(pev), &tr); + if (tr.flFraction != 1.0) + { + // fail! + return g_vecZero; + } + + // Don't worry about actually hitting the target, this won't hurt us! + + // How high should the grenade travel (subtract 15 so the grenade doesn't hit the ceiling)? + float height = (vecApex.z - vecSpot1.z) - 15; + // How fast does the grenade need to travel to reach that height given gravity? + float speed = sqrt( 2 * flGravity * height ); + + // How much time does it take to get there? + float time = speed / flGravity; + vecGrenadeVel = (vecSpot2 - vecSpot1); + vecGrenadeVel.z = 0; + float distance = vecGrenadeVel.Length(); + + // Travel half the distance to the target in that time (apex is at the midpoint) + vecGrenadeVel = vecGrenadeVel * ( 0.5 / time ); + // Speed to offset gravity at the desired height + vecGrenadeVel.z = speed; + + return vecGrenadeVel; +} + + + + +// --------------------------------- +// +// Mortar +// +// --------------------------------- +void MortarSpray( const Vector &position, const Vector &direction, int spriteModel, int count ) +{ + MESSAGE_BEGIN( MSG_PVS, SVC_TEMPENTITY, position ); + WRITE_BYTE( TE_SPRITE_SPRAY ); + WRITE_COORD( position.x); // pos + WRITE_COORD( position.y); + WRITE_COORD( position.z); + WRITE_COORD( direction.x); // dir + WRITE_COORD( direction.y); + WRITE_COORD( direction.z); + WRITE_SHORT( spriteModel ); // model + WRITE_BYTE ( count ); // count + WRITE_BYTE ( 130 ); // speed + WRITE_BYTE ( 80 ); // noise ( client will divide by 100 ) + MESSAGE_END(); +} + + +// UNDONE: right now this is pretty much a copy of the squid spit with minor changes to the way it does damage +void CBMortar:: Spawn( void ) +{ + pev->movetype = MOVETYPE_TOSS; + pev->classname = MAKE_STRING( "bmortar" ); + + pev->solid = SOLID_BBOX; + pev->rendermode = kRenderTransAlpha; + pev->renderamt = 255; + + SET_MODEL(ENT(pev), "sprites/mommaspit.spr"); + pev->frame = 0; + pev->scale = 0.5; + + UTIL_SetSize( pev, Vector( 0, 0, 0), Vector(0, 0, 0) ); + + m_maxFrame = (float) MODEL_FRAMES( pev->modelindex ) - 1; + pev->dmgtime = gpGlobals->time + 0.4; +} + +void CBMortar::Animate( void ) +{ + pev->nextthink = gpGlobals->time + 0.1; + + if ( gpGlobals->time > pev->dmgtime ) + { + pev->dmgtime = gpGlobals->time + 0.2; + MortarSpray( pev->origin, -pev->velocity.Normalize(), gSpitSprite, 3 ); + } + if ( pev->frame++ ) + { + if ( pev->frame > m_maxFrame ) + { + pev->frame = 0; + } + } +} + +CBMortar *CBMortar::Shoot( edict_t *pOwner, Vector vecStart, Vector vecVelocity ) +{ + CBMortar *pSpit = GetClassPtr( (CBMortar *)NULL ); + pSpit->Spawn(); + + UTIL_SetOrigin( pSpit->pev, vecStart ); + pSpit->pev->velocity = vecVelocity; + pSpit->pev->owner = pOwner; + pSpit->pev->scale = 2.5; + pSpit->SetThink ( Animate ); + pSpit->pev->nextthink = gpGlobals->time + 0.1; + + return pSpit; +} + + +void CBMortar::Touch( CBaseEntity *pOther ) +{ + TraceResult tr; + int iPitch; + + // splat sound + iPitch = RANDOM_FLOAT( 90, 110 ); + + EMIT_SOUND_DYN( ENT(pev), CHAN_VOICE, "bullchicken/bc_acid1.wav", 1, ATTN_NORM, 0, iPitch ); + + switch ( RANDOM_LONG( 0, 1 ) ) + { + case 0: + EMIT_SOUND_DYN( ENT(pev), CHAN_WEAPON, "bullchicken/bc_spithit1.wav", 1, ATTN_NORM, 0, iPitch ); + break; + case 1: + EMIT_SOUND_DYN( ENT(pev), CHAN_WEAPON, "bullchicken/bc_spithit2.wav", 1, ATTN_NORM, 0, iPitch ); + break; + } + + if ( pOther->IsBSPModel() ) + { + + // make a splat on the wall + UTIL_TraceLine( pev->origin, pev->origin + pev->velocity * 10, dont_ignore_monsters, ENT( pev ), &tr ); + UTIL_DecalTrace(&tr, DECAL_MOMMASPLAT); + } + else + { + tr.vecEndPos = pev->origin; + tr.vecPlaneNormal = -1 * pev->velocity.Normalize(); + } + // make some flecks + MortarSpray( tr.vecEndPos, tr.vecPlaneNormal, gSpitSprite, 24 ); + + entvars_t *pevOwner = NULL; + if ( pev->owner ) + pevOwner = VARS(pev->owner); + + RadiusDamage( pev->origin, pev, pevOwner, gSkillData.bigmommaDmgBlast, gSkillData.bigmommaRadiusBlast, CLASS_NONE, DMG_ACID ); + UTIL_Remove( this ); +} + +#endif diff --git a/spirit/bloater.cpp b/dlls/bloater.cpp similarity index 94% rename from spirit/bloater.cpp rename to dlls/bloater.cpp index 31bcbe7f..889b866e 100644 --- a/spirit/bloater.cpp +++ b/dlls/bloater.cpp @@ -1,225 +1,219 @@ -/*** -* -* Copyright (c) 1996-2002, 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. -* -* This source code contains proprietary and confidential information of -* Valve LLC and its suppliers. Access to this code is restricted to -* persons who have executed a written SDK license with Valve. Any access, -* use or distribution of this code by or to any unlicensed person is illegal. -* -****/ -//========================================================= -// Bloater -//========================================================= - -#include "extdll.h" -#include "util.h" -#include "cbase.h" -#include "monsters.h" -#include "schedule.h" - - -//========================================================= -// Monster's Anim Events Go Here -//========================================================= -#define BLOATER_AE_ATTACK_MELEE1 0x01 - - -class CBloater : public CBaseMonster -{ -public: - void Spawn( void ); - void Precache( void ); - void SetYawSpeed( void ); - int Classify ( void ); - void HandleAnimEvent( MonsterEvent_t *pEvent ); - - void PainSound( void ); - void AlertSound( void ); - void IdleSound( void ); - void AttackSnd( void ); - - // No range attacks - BOOL CheckRangeAttack1 ( float flDot, float flDist ) { return FALSE; } - BOOL CheckRangeAttack2 ( float flDot, float flDist ) { return FALSE; } - int TakeDamage( entvars_t *pevInflictor, entvars_t *pevAttacker, float flDamage, int bitsDamageType ); -}; - -LINK_ENTITY_TO_CLASS( monster_bloater, CBloater ); - -//========================================================= -// Classify - indicates this monster's place in the -// relationship table. -//========================================================= -int CBloater :: Classify ( void ) -{ - return m_iClass?m_iClass:CLASS_ALIEN_MONSTER; -} - -//========================================================= -// SetYawSpeed - allows each sequence to have a different -// turn rate associated with it. -//========================================================= -void CBloater :: SetYawSpeed ( void ) -{ - int ys; - - ys = 120; - -#if 0 - switch ( m_Activity ) - { - } -#endif - - pev->yaw_speed = ys; -} - -int CBloater :: TakeDamage( entvars_t *pevInflictor, entvars_t *pevAttacker, float flDamage, int bitsDamageType ) -{ - PainSound(); - return CBaseMonster::TakeDamage( pevInflictor, pevAttacker, flDamage, bitsDamageType ); -} - -void CBloater :: PainSound( void ) -{ -#if 0 - int pitch = 95 + RANDOM_LONG(0,9); - - switch (RANDOM_LONG(0,5)) - { - case 0: - EMIT_SOUND_DYN(ENT(pev), CHAN_VOICE, "zombie/zo_pain1.wav", 1.0, ATTN_NORM, 0, pitch); - break; - case 1: - EMIT_SOUND_DYN(ENT(pev), CHAN_VOICE, "zombie/zo_pain2.wav", 1.0, ATTN_NORM, 0, pitch); - break; - default: - break; - } -#endif -} - -void CBloater :: AlertSound( void ) -{ -#if 0 - int pitch = 95 + RANDOM_LONG(0,9); - - switch (RANDOM_LONG(0,2)) - { - case 0: - EMIT_SOUND_DYN(ENT(pev), CHAN_VOICE, "zombie/zo_alert10.wav", 1.0, ATTN_NORM, 0, pitch); - break; - case 1: - EMIT_SOUND_DYN(ENT(pev), CHAN_VOICE, "zombie/zo_alert20.wav", 1.0, ATTN_NORM, 0, pitch); - break; - case 2: - EMIT_SOUND_DYN(ENT(pev), CHAN_VOICE, "zombie/zo_alert30.wav", 1.0, ATTN_NORM, 0, pitch); - break; - } -#endif -} - -void CBloater :: IdleSound( void ) -{ -#if 0 - int pitch = 95 + RANDOM_LONG(0,9); - - switch (RANDOM_LONG(0,2)) - { - case 0: - EMIT_SOUND_DYN(ENT(pev), CHAN_VOICE, "zombie/zo_idle1.wav", 1.0, ATTN_NORM, 0, pitch); - break; - case 1: - EMIT_SOUND_DYN(ENT(pev), CHAN_VOICE, "zombie/zo_idle2.wav", 1.0, ATTN_NORM, 0, pitch); - break; - case 2: - EMIT_SOUND_DYN(ENT(pev), CHAN_VOICE, "zombie/zo_idle3.wav", 1.0, ATTN_NORM, 0, pitch); - break; - } -#endif -} - -void CBloater :: AttackSnd( void ) -{ -#if 0 - int pitch = 95 + RANDOM_LONG(0,9); - - switch (RANDOM_LONG(0,1)) - { - case 0: - EMIT_SOUND_DYN(ENT(pev), CHAN_VOICE, "zombie/zo_attack1.wav", 1.0, ATTN_NORM, 0, pitch); - break; - case 1: - EMIT_SOUND_DYN(ENT(pev), CHAN_VOICE, "zombie/zo_attack2.wav", 1.0, ATTN_NORM, 0, pitch); - break; - } -#endif -} - - -//========================================================= -// HandleAnimEvent - catches the monster-specific messages -// that occur when tagged animation frames are played. -//========================================================= -void CBloater :: HandleAnimEvent( MonsterEvent_t *pEvent ) -{ - switch( pEvent->event ) - { - case BLOATER_AE_ATTACK_MELEE1: - { - // do stuff for this event. - AttackSnd(); - } - break; - - default: - CBaseMonster::HandleAnimEvent( pEvent ); - break; - } -} - -//========================================================= -// Spawn -//========================================================= -void CBloater :: Spawn() -{ - Precache( ); - - if (pev->model) - SET_MODEL(ENT(pev), STRING(pev->model)); //LRC - else - SET_MODEL(ENT(pev), "models/floater.mdl"); - UTIL_SetSize( pev, VEC_HUMAN_HULL_MIN, VEC_HUMAN_HULL_MAX ); - - pev->solid = SOLID_SLIDEBOX; - pev->movetype = MOVETYPE_FLY; - pev->spawnflags |= FL_FLY; - m_bloodColor = BLOOD_COLOR_GREEN; - pev->health = 40; - pev->view_ofs = VEC_VIEW;// position of the eyes relative to monster's origin. - m_flFieldOfView = 0.5;// indicates the width of this monster's forward view cone ( as a dotproduct result ) - m_MonsterState = MONSTERSTATE_NONE; - - MonsterInit(); -} - -//========================================================= -// Precache - precaches all resources this monster needs -//========================================================= -void CBloater :: Precache() -{ - if (pev->model) - PRECACHE_MODEL((char*)STRING(pev->model)); //LRC - else - PRECACHE_MODEL("models/floater.mdl"); -} - -//========================================================= -// AI Schedules Specific to this monster -//========================================================= - +/*** +* +* Copyright (c) 1996-2002, 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. +* +* This source code contains proprietary and confidential information of +* Valve LLC and its suppliers. Access to this code is restricted to +* persons who have executed a written SDK license with Valve. Any access, +* use or distribution of this code by or to any unlicensed person is illegal. +* +****/ +//========================================================= +// Bloater +//========================================================= + +#include "extdll.h" +#include "util.h" +#include "cbase.h" +#include "monsters.h" +#include "schedule.h" + + +//========================================================= +// Monster's Anim Events Go Here +//========================================================= +#define BLOATER_AE_ATTACK_MELEE1 0x01 + + +class CBloater : public CBaseMonster +{ +public: + void Spawn( void ); + void Precache( void ); + void SetYawSpeed( void ); + int Classify ( void ); + void HandleAnimEvent( MonsterEvent_t *pEvent ); + + void PainSound( void ); + void AlertSound( void ); + void IdleSound( void ); + void AttackSnd( void ); + + // No range attacks + BOOL CheckRangeAttack1 ( float flDot, float flDist ) { return FALSE; } + BOOL CheckRangeAttack2 ( float flDot, float flDist ) { return FALSE; } + int TakeDamage( entvars_t *pevInflictor, entvars_t *pevAttacker, float flDamage, int bitsDamageType ); +}; + +LINK_ENTITY_TO_CLASS( monster_bloater, CBloater ); + +//========================================================= +// Classify - indicates this monster's place in the +// relationship table. +//========================================================= +int CBloater :: Classify ( void ) +{ + return CLASS_ALIEN_MONSTER; +} + +//========================================================= +// SetYawSpeed - allows each sequence to have a different +// turn rate associated with it. +//========================================================= +void CBloater :: SetYawSpeed ( void ) +{ + int ys; + + ys = 120; + +#if 0 + switch ( m_Activity ) + { + } +#endif + + pev->yaw_speed = ys; +} + +int CBloater :: TakeDamage( entvars_t *pevInflictor, entvars_t *pevAttacker, float flDamage, int bitsDamageType ) +{ + PainSound(); + return CBaseMonster::TakeDamage( pevInflictor, pevAttacker, flDamage, bitsDamageType ); +} + +void CBloater :: PainSound( void ) +{ +#if 0 + int pitch = 95 + RANDOM_LONG(0,9); + + switch (RANDOM_LONG(0,5)) + { + case 0: + EMIT_SOUND_DYN(ENT(pev), CHAN_VOICE, "zombie/zo_pain1.wav", 1.0, ATTN_NORM, 0, pitch); + break; + case 1: + EMIT_SOUND_DYN(ENT(pev), CHAN_VOICE, "zombie/zo_pain2.wav", 1.0, ATTN_NORM, 0, pitch); + break; + default: + break; + } +#endif +} + +void CBloater :: AlertSound( void ) +{ +#if 0 + int pitch = 95 + RANDOM_LONG(0,9); + + switch (RANDOM_LONG(0,2)) + { + case 0: + EMIT_SOUND_DYN(ENT(pev), CHAN_VOICE, "zombie/zo_alert10.wav", 1.0, ATTN_NORM, 0, pitch); + break; + case 1: + EMIT_SOUND_DYN(ENT(pev), CHAN_VOICE, "zombie/zo_alert20.wav", 1.0, ATTN_NORM, 0, pitch); + break; + case 2: + EMIT_SOUND_DYN(ENT(pev), CHAN_VOICE, "zombie/zo_alert30.wav", 1.0, ATTN_NORM, 0, pitch); + break; + } +#endif +} + +void CBloater :: IdleSound( void ) +{ +#if 0 + int pitch = 95 + RANDOM_LONG(0,9); + + switch (RANDOM_LONG(0,2)) + { + case 0: + EMIT_SOUND_DYN(ENT(pev), CHAN_VOICE, "zombie/zo_idle1.wav", 1.0, ATTN_NORM, 0, pitch); + break; + case 1: + EMIT_SOUND_DYN(ENT(pev), CHAN_VOICE, "zombie/zo_idle2.wav", 1.0, ATTN_NORM, 0, pitch); + break; + case 2: + EMIT_SOUND_DYN(ENT(pev), CHAN_VOICE, "zombie/zo_idle3.wav", 1.0, ATTN_NORM, 0, pitch); + break; + } +#endif +} + +void CBloater :: AttackSnd( void ) +{ +#if 0 + int pitch = 95 + RANDOM_LONG(0,9); + + switch (RANDOM_LONG(0,1)) + { + case 0: + EMIT_SOUND_DYN(ENT(pev), CHAN_VOICE, "zombie/zo_attack1.wav", 1.0, ATTN_NORM, 0, pitch); + break; + case 1: + EMIT_SOUND_DYN(ENT(pev), CHAN_VOICE, "zombie/zo_attack2.wav", 1.0, ATTN_NORM, 0, pitch); + break; + } +#endif +} + + +//========================================================= +// HandleAnimEvent - catches the monster-specific messages +// that occur when tagged animation frames are played. +//========================================================= +void CBloater :: HandleAnimEvent( MonsterEvent_t *pEvent ) +{ + switch( pEvent->event ) + { + case BLOATER_AE_ATTACK_MELEE1: + { + // do stuff for this event. + AttackSnd(); + } + break; + + default: + CBaseMonster::HandleAnimEvent( pEvent ); + break; + } +} + +//========================================================= +// Spawn +//========================================================= +void CBloater :: Spawn() +{ + Precache( ); + + SET_MODEL(ENT(pev), "models/floater.mdl"); + UTIL_SetSize( pev, VEC_HUMAN_HULL_MIN, VEC_HUMAN_HULL_MAX ); + + pev->solid = SOLID_SLIDEBOX; + pev->movetype = MOVETYPE_FLY; + pev->spawnflags |= FL_FLY; + m_bloodColor = BLOOD_COLOR_GREEN; + pev->health = 40; + pev->view_ofs = VEC_VIEW;// position of the eyes relative to monster's origin. + m_flFieldOfView = 0.5;// indicates the width of this monster's forward view cone ( as a dotproduct result ) + m_MonsterState = MONSTERSTATE_NONE; + + MonsterInit(); +} + +//========================================================= +// Precache - precaches all resources this monster needs +//========================================================= +void CBloater :: Precache() +{ + PRECACHE_MODEL("models/floater.mdl"); +} + +//========================================================= +// AI Schedules Specific to this monster +//========================================================= + diff --git a/spirit/bmodels.cpp b/dlls/bmodels.cpp similarity index 64% rename from spirit/bmodels.cpp rename to dlls/bmodels.cpp index c97909ed..cfb41bb1 100644 --- a/spirit/bmodels.cpp +++ b/dlls/bmodels.cpp @@ -24,7 +24,6 @@ #include "util.h" #include "cbase.h" #include "doors.h" -#include "movewith.h" extern DLL_GLOBAL Vector g_vecAttackDir; @@ -43,8 +42,7 @@ extern DLL_GLOBAL Vector g_vecAttackDir; // Vector VecBModelOrigin( entvars_t* pevBModel ) { - return (pevBModel->absmin + pevBModel->absmax) * 0.5; //LRC - bug fix for rotating ents -// return pevBModel->absmin + ( pevBModel->size * 0.5 ); + return pevBModel->absmin + ( pevBModel->size * 0.5 ); } // =================== FUNC_WALL ============================================== @@ -58,53 +56,28 @@ public: void Spawn( void ); void Use( CBaseEntity *pActivator, CBaseEntity *pCaller, USE_TYPE useType, float value ); - virtual STATE GetState( void ) { return pev->frame?STATE_ON:STATE_OFF; }; - // Bmodels don't go across transitions virtual int ObjectCaps( void ) { return CBaseEntity :: ObjectCaps() & ~FCAP_ACROSS_TRANSITION; } - - int m_iStyle; }; LINK_ENTITY_TO_CLASS( func_wall, CFuncWall ); void CFuncWall :: Spawn( void ) -{ +{ pev->angles = g_vecZero; pev->movetype = MOVETYPE_PUSH; // so it doesn't get pushed by anything pev->solid = SOLID_BSP; SET_MODEL( ENT(pev), STRING(pev->model) ); // If it can't move/go away, it's really part of the world - if (!m_pMoveWith) //LRC - pev->flags |= FL_WORLDBRUSH; - - //LRC - if (m_iStyle >= 32) LIGHT_STYLE(m_iStyle, "a"); - else if (m_iStyle <= -32) LIGHT_STYLE(-m_iStyle, "z"); - + pev->flags |= FL_WORLDBRUSH; } + void CFuncWall :: Use( CBaseEntity *pActivator, CBaseEntity *pCaller, USE_TYPE useType, float value ) { if ( ShouldToggle( useType, (int)(pev->frame)) ) - { pev->frame = 1 - pev->frame; - if (m_iStyle >= 32) - { - if (pev->frame) - LIGHT_STYLE(m_iStyle, "z"); - else - LIGHT_STYLE(m_iStyle, "a"); - } - else if (m_iStyle <= -32) - { - if (pev->frame) - LIGHT_STYLE(-m_iStyle, "a"); - else - LIGHT_STYLE(-m_iStyle, "z"); - } - } } @@ -118,7 +91,6 @@ public: void TurnOff( void ); void TurnOn( void ); BOOL IsOn( void ); - virtual STATE GetState( void ) { return (pev->solid == SOLID_NOT)?STATE_OFF:STATE_ON; }; }; LINK_ENTITY_TO_CLASS( func_wall_toggle, CFuncWallToggle ); @@ -135,7 +107,7 @@ void CFuncWallToggle :: TurnOff( void ) { pev->solid = SOLID_NOT; pev->effects |= EF_NODRAW; - UTIL_SetOrigin( this, pev->origin ); + UTIL_SetOrigin( pev, pev->origin ); } @@ -143,7 +115,7 @@ void CFuncWallToggle :: TurnOn( void ) { pev->solid = SOLID_BSP; pev->effects &= ~EF_NODRAW; - UTIL_SetOrigin( this, pev->origin ); + UTIL_SetOrigin( pev, pev->origin ); } @@ -157,8 +129,7 @@ BOOL CFuncWallToggle :: IsOn( void ) void CFuncWallToggle :: Use( CBaseEntity *pActivator, CBaseEntity *pCaller, USE_TYPE useType, float value ) { -// int status = IsOn(); - BOOL status = (GetState() == STATE_ON); + int status = IsOn(); if ( ShouldToggle( useType, status ) ) { @@ -247,7 +218,6 @@ LINK_ENTITY_TO_CLASS( func_illusionary, CFuncIllusionary ); void CFuncIllusionary :: KeyValue( KeyValueData *pkvd ) { - // LRC- surely it just parses this automatically? pev values are handled by the engine. if (FStrEq(pkvd->szKeyName, "skin"))//skin is used for content type { pev->skin = atof(pkvd->szValue); @@ -270,65 +240,6 @@ void CFuncIllusionary :: Spawn( void ) // MAKE_STATIC(ENT(pev)); } -// =================== FUNC_SHINE ============================================== - -//LRC - shiny surfaces -class CFuncShine : public CBaseEntity -{ -public: - void Spawn( void ); - void Activate( void ); - virtual int ObjectCaps( void ) { return CBaseEntity :: ObjectCaps() & ~FCAP_ACROSS_TRANSITION; } - - void DesiredAction( void ); - void EXPORT Think( void ); -}; - -LINK_ENTITY_TO_CLASS( func_shine, CFuncShine ); - -extern int gmsgAddShine; -void CFuncShine :: Spawn( void ) -{ - pev->solid = SOLID_NOT;// always solid_not - SET_MODEL( ENT(pev), STRING(pev->model) ); - pev->effects |= EF_NODRAW; - - // not that we actually need to precache it here, but we do need to make sure it exists - PRECACHE_MODEL( (char*)STRING(pev->message) ); -} - -void CFuncShine :: Activate( void ) -{ -// ALERT(at_console, "Activate shine\n"); - - CBaseEntity::Activate(); - UTIL_DesiredAction(this); -} - -void CFuncShine :: DesiredAction( void ) -{ - if (pev->message && pev->renderamt) - { -// ALERT(at_console, "Prepare think\n"); - pev->nextthink = gpGlobals->time + 1.5; - } -} - -void CFuncShine :: Think( void ) -{ -// ALERT(at_console, "Think shine\n"); - MESSAGE_BEGIN(MSG_BROADCAST, gmsgAddShine, NULL); - WRITE_BYTE(pev->scale); - WRITE_BYTE(pev->renderamt); - WRITE_COORD(pev->absmin.x + 2); // take off 2: mins values are padded, but we just want to hug the surface - WRITE_COORD(pev->absmax.x - 2); - WRITE_COORD(pev->absmin.y + 2); - WRITE_COORD(pev->absmax.y - 2); - WRITE_COORD(pev->absmin.z + 2); - WRITE_STRING(STRING(pev->message)); - MESSAGE_END(); -} - // ------------------------------------------------------------------------------- // @@ -371,7 +282,6 @@ public: void KeyValue( KeyValueData* pkvd); void EXPORT HurtTouch ( CBaseEntity *pOther ); void EXPORT RotatingUse( CBaseEntity *pActivator, CBaseEntity *pCaller, USE_TYPE useType, float value ); - void EXPORT WaitForStart (); //LRC - get round 1.1.0.8's bizarre behaviour on startup void EXPORT Rotate( void ); void RampPitchVol (int fUp ); void Blocked( CBaseEntity *pOther ); @@ -386,16 +296,6 @@ public: float m_flVolume; float m_pitch; int m_sounds; - - EHANDLE m_hActivator; //AJH - - float m_fCurSpeed; //LRC - during spin-up and spin-down, this is - // the current speed factor (between 0 and 1). - // storing this here lets us avoid the hassle of deriving it - // from pev->avelocity. - - STATE m_iState; //LRC - virtual STATE GetState( void ) { return m_iState; }; //LRC }; TYPEDESCRIPTION CFuncRotating::m_SaveData[] = @@ -404,8 +304,7 @@ TYPEDESCRIPTION CFuncRotating::m_SaveData[] = DEFINE_FIELD( CFuncRotating, m_flAttenuation, FIELD_FLOAT ), DEFINE_FIELD( CFuncRotating, m_flVolume, FIELD_FLOAT ), DEFINE_FIELD( CFuncRotating, m_pitch, FIELD_FLOAT ), - DEFINE_FIELD( CFuncRotating, m_sounds, FIELD_INTEGER ), - DEFINE_FIELD( CFuncRotating, m_fCurSpeed, FIELD_FLOAT ), + DEFINE_FIELD( CFuncRotating, m_sounds, FIELD_INTEGER ) }; IMPLEMENT_SAVERESTORE( CFuncRotating, CBaseEntity ); @@ -442,11 +341,6 @@ void CFuncRotating :: KeyValue( KeyValueData* pkvd) m_sounds = atoi(pkvd->szValue); pkvd->fHandled = TRUE; } - else if (FStrEq(pkvd->szKeyName, "axes")) - { - UTIL_StringToVector( (float *)(pev->movedir), pkvd->szValue); - pkvd->fHandled = TRUE; - } else CBaseEntity::KeyValue( pkvd ); } @@ -467,10 +361,6 @@ REVERSE will cause the it to rotate in the opposite direction. void CFuncRotating :: Spawn( ) { - m_iState = STATE_OFF; - - m_fCurSpeed = 0; //LRC - // set final pitch. Must not be PITCH_NORM, since we // plan on pitch shifting later. @@ -497,20 +387,17 @@ void CFuncRotating :: Spawn( ) } // prevent divide by zero if level designer forgets friction! - if ( m_flFanFriction <= 0 ) //LRC - ensure it's not negative + if ( m_flFanFriction == 0 ) { m_flFanFriction = 1; } - if (pev->movedir == g_vecZero) - { - if ( FBitSet(pev->spawnflags, SF_BRUSH_ROTATE_Z_AXIS) ) - pev->movedir = Vector(0,0,1); - else if ( FBitSet(pev->spawnflags, SF_BRUSH_ROTATE_X_AXIS) ) - pev->movedir = Vector(1,0,0); - else - pev->movedir = Vector(0,1,0); // y-axis - } + if ( FBitSet(pev->spawnflags, SF_BRUSH_ROTATE_Z_AXIS) ) + pev->movedir = Vector(0,0,1); + else if ( FBitSet(pev->spawnflags, SF_BRUSH_ROTATE_X_AXIS) ) + pev->movedir = Vector(1,0,0); + else + pev->movedir = Vector(0,1,0); // y-axis // check for reverse rotation if ( FBitSet(pev->spawnflags, SF_BRUSH_ROTATE_BACKWARDS) ) @@ -529,10 +416,10 @@ void CFuncRotating :: Spawn( ) pev->movetype = MOVETYPE_PUSH; } - UTIL_SetOrigin(this, pev->origin); + UTIL_SetOrigin(pev, pev->origin); SET_MODEL( ENT(pev), STRING(pev->model) ); - SetUse(&CFuncRotating :: RotatingUse ); + SetUse( RotatingUse ); // did level designer forget to assign speed? if (pev->speed <= 0) pev->speed = 0; @@ -541,15 +428,16 @@ void CFuncRotating :: Spawn( ) // if (pev->dmg == 0) // pev->dmg = 2; - if ( FBitSet( pev->spawnflags, SF_BRUSH_ROTATE_INSTANT)) + // instant-use brush? + if ( FBitSet( pev->spawnflags, SF_BRUSH_ROTATE_INSTANT) ) { - SetThink(&CFuncRotating :: WaitForStart ); - SetNextThink( 1.5 ); // leave a magic delay for client to start up + SetThink( SUB_CallUseToggle ); + pev->nextthink = pev->ltime + 1.5; // leave a magic delay for client to start up } // can this brush inflict pain? if ( FBitSet (pev->spawnflags, SF_BRUSH_HURT) ) { - SetTouch(&CFuncRotating :: HurtTouch ); + SetTouch( HurtTouch ); } Precache( ); @@ -576,23 +464,23 @@ void CFuncRotating :: Precache( void ) { case 1: PRECACHE_SOUND ("fans/fan1.wav"); - pev->noiseRunning = MAKE_STRING("fans/fan1.wav"); + pev->noiseRunning = ALLOC_STRING("fans/fan1.wav"); break; case 2: PRECACHE_SOUND ("fans/fan2.wav"); - pev->noiseRunning = MAKE_STRING("fans/fan2.wav"); + pev->noiseRunning = ALLOC_STRING("fans/fan2.wav"); break; case 3: PRECACHE_SOUND ("fans/fan3.wav"); - pev->noiseRunning = MAKE_STRING("fans/fan3.wav"); + pev->noiseRunning = ALLOC_STRING("fans/fan3.wav"); break; case 4: PRECACHE_SOUND ("fans/fan4.wav"); - pev->noiseRunning = MAKE_STRING("fans/fan4.wav"); + pev->noiseRunning = ALLOC_STRING("fans/fan4.wav"); break; case 5: PRECACHE_SOUND ("fans/fan5.wav"); - pev->noiseRunning = MAKE_STRING("fans/fan5.wav"); + pev->noiseRunning = ALLOC_STRING("fans/fan5.wav"); break; case 0: @@ -605,34 +493,23 @@ void CFuncRotating :: Precache( void ) break; } else { - pev->noiseRunning = MAKE_STRING("common/null.wav"); + pev->noiseRunning = ALLOC_STRING("common/null.wav"); break; } } } - if (m_fCurSpeed != 0 ) + if (pev->avelocity != g_vecZero ) { // if fan was spinning, and we went through transition or save/restore, // make sure we restart the sound. 1.5 sec delay is magic number. KDB - SetThink(&CFuncRotating :: SpinUp ); - SetNextThink( 1.5 ); + SetThink ( SpinUp ); + pev->nextthink = pev->ltime + 1.5; } } -void CFuncRotating :: WaitForStart() -{ - if (gpGlobals->time > 1) // has the client started yet? - { - SUB_CallUseToggle(); - } - else - { - SetNextThink( 0.1 ); - } -} // // Touch - will hurt others based on how fast the brush is spinning @@ -646,13 +523,9 @@ void CFuncRotating :: HurtTouch ( CBaseEntity *pOther ) return; // calculate damage based on rotation speed - pev->dmg = m_fCurSpeed / 10; //LRC -// pev->dmg = pev->avelocity.Length() / 10; + pev->dmg = pev->avelocity.Length() / 10; - if (m_hActivator) - pOther->TakeDamage( pev, m_hActivator->pev, pev->dmg, DMG_CRUSH ); - else - pOther->TakeDamage( pev, pev, pev->dmg, DMG_CRUSH ); + pOther->TakeDamage( pev, pev, pev->dmg, DMG_CRUSH); pevOther->velocity = (pevOther->origin - VecBModelOrigin(pev) ).Normalize() * pev->dmg; } @@ -666,14 +539,34 @@ void CFuncRotating :: HurtTouch ( CBaseEntity *pOther ) void CFuncRotating :: RampPitchVol (int fUp) { + + Vector vecAVel = pev->avelocity; + vec_t vecCur; + vec_t vecFinal; + float fpct; float fvol; float fpitch; int pitch; - float speedfactor = m_fCurSpeed/pev->speed; - fvol = m_flVolume * speedfactor; // slowdown volume ramps down to 0 + // get current angular velocity - fpitch = FANPITCHMIN + (FANPITCHMAX - FANPITCHMIN) * speedfactor; + vecCur = abs(vecAVel.x != 0 ? vecAVel.x : (vecAVel.y != 0 ? vecAVel.y : vecAVel.z)); + + // get target angular velocity + + vecFinal = (pev->movedir.x != 0 ? pev->movedir.x : (pev->movedir.y != 0 ? pev->movedir.y : pev->movedir.z)); + vecFinal *= pev->speed; + vecFinal = abs(vecFinal); + + // calc volume and pitch as % of final vol and pitch + + fpct = vecCur / vecFinal; +// if (fUp) +// fvol = m_flVolume * (0.5 + fpct/2.0); // spinup volume ramps up from 50% max vol +// else + fvol = m_flVolume * fpct; // slowdown volume ramps down to 0 + + fpitch = FANPITCHMIN + (FANPITCHMAX - FANPITCHMIN) * fpct; pitch = (int) fpitch; if (pitch == PITCH_NORM) @@ -691,26 +584,23 @@ void CFuncRotating :: RampPitchVol (int fUp) // void CFuncRotating :: SpinUp( void ) { - //Vector vecAVel;//rotational velocity + Vector vecAVel;//rotational velocity - SetNextThink( 0.1 ); - m_fCurSpeed = m_fCurSpeed + ( pev->speed * m_flFanFriction ); - UTIL_SetAvelocity(this, pev->movedir * m_fCurSpeed); - //pev->avelocity = pev->avelocity + ( pev->movedir * ( pev->speed * m_flFanFriction ) ); + pev->nextthink = pev->ltime + 0.1; + pev->avelocity = pev->avelocity + ( pev->movedir * ( pev->speed * m_flFanFriction ) ); - //vecAVel = pev->avelocity;// cache entity's rotational velocity + vecAVel = pev->avelocity;// cache entity's rotational velocity // if we've met or exceeded target speed, set target speed and stop thinking - if ( m_fCurSpeed >= pev->speed ) + if ( abs(vecAVel.x) >= abs(pev->movedir.x * pev->speed) && + abs(vecAVel.y) >= abs(pev->movedir.y * pev->speed) && + abs(vecAVel.z) >= abs(pev->movedir.z * pev->speed) ) { - m_iState = STATE_ON; - m_fCurSpeed = pev->speed; - UTIL_SetAvelocity(this, pev->movedir * pev->speed); - //pev->avelocity = pev->movedir * pev->speed;// set speed in case we overshot + pev->avelocity = pev->movedir * pev->speed;// set speed in case we overshot EMIT_SOUND_DYN(ENT(pev), CHAN_STATIC, (char *)STRING(pev->noiseRunning), m_flVolume, m_flAttenuation, SND_CHANGE_PITCH | SND_CHANGE_VOL, FANPITCHMAX); - SetThink(&CFuncRotating :: Rotate ); + SetThink( Rotate ); Rotate(); } else @@ -724,25 +614,34 @@ void CFuncRotating :: SpinUp( void ) // void CFuncRotating :: SpinDown( void ) { - SetNextThink( 0.1 ); + Vector vecAVel;//rotational velocity + vec_t vecdir; - m_fCurSpeed = m_fCurSpeed - ( pev->speed * m_flFanFriction ); - UTIL_SetAvelocity(this, pev->movedir * m_fCurSpeed); - //pev->avelocity = pev->avelocity - ( pev->movedir * ( pev->speed * m_flFanFriction ) );//spin down slower than spinup + pev->nextthink = pev->ltime + 0.1; + + pev->avelocity = pev->avelocity - ( pev->movedir * ( pev->speed * m_flFanFriction ) );//spin down slower than spinup + + vecAVel = pev->avelocity;// cache entity's rotational velocity + + if (pev->movedir.x != 0) + vecdir = pev->movedir.x; + else if (pev->movedir.y != 0) + vecdir = pev->movedir.y; + else + vecdir = pev->movedir.z; // if we've met or exceeded target speed, set target speed and stop thinking - if (m_fCurSpeed <= 0) + // (note: must check for movedir > 0 or < 0) + if (((vecdir > 0) && (vecAVel.x <= 0 && vecAVel.y <= 0 && vecAVel.z <= 0)) || + ((vecdir < 0) && (vecAVel.x >= 0 && vecAVel.y >= 0 && vecAVel.z >= 0))) { - m_iState = STATE_OFF; - m_fCurSpeed = 0; - UTIL_SetAvelocity(this, g_vecZero); - //pev->avelocity = g_vecZero;// set speed in case we overshot + pev->avelocity = g_vecZero;// set speed in case we overshot // stop sound, we're done EMIT_SOUND_DYN(ENT(pev), CHAN_STATIC, (char *)STRING(pev->noiseRunning /* Stop */), 0, 0, SND_STOP, m_pitch); - SetThink(&CFuncRotating :: Rotate ); + SetThink( Rotate ); Rotate(); } else @@ -753,7 +652,7 @@ void CFuncRotating :: SpinDown( void ) void CFuncRotating :: Rotate( void ) { - SetNextThink( 10 ); + pev->nextthink = pev->ltime + 10; } //========================================================= @@ -761,61 +660,47 @@ void CFuncRotating :: Rotate( void ) //========================================================= void CFuncRotating :: RotatingUse( CBaseEntity *pActivator, CBaseEntity *pCaller, USE_TYPE useType, float value ) { - m_hActivator = pActivator; //AJH - - if (!ShouldToggle(useType)) return; - // is this a brush that should accelerate and decelerate when turned on/off (fan)? if ( FBitSet ( pev->spawnflags, SF_BRUSH_ACCDCC ) ) { // fan is spinning, so stop it. - if ( m_fCurSpeed != 0 ) -// if ( pev->avelocity != g_vecZero ) + if ( pev->avelocity != g_vecZero ) { - m_iState = STATE_TURN_OFF; - SetThink(&CFuncRotating :: SpinDown ); + SetThink ( SpinDown ); //EMIT_SOUND_DYN(ENT(pev), CHAN_WEAPON, (char *)STRING(pev->noiseStop), // m_flVolume, m_flAttenuation, 0, m_pitch); - SetNextThink( 0.1 ); + pev->nextthink = pev->ltime + 0.1; } else// fan is not moving, so start it { - m_iState = STATE_TURN_ON; - SetThink(&CFuncRotating :: SpinUp ); + SetThink ( SpinUp ); EMIT_SOUND_DYN(ENT(pev), CHAN_STATIC, (char *)STRING(pev->noiseRunning), 0.01, m_flAttenuation, 0, FANPITCHMIN); - SetNextThink( 0.1 ); + pev->nextthink = pev->ltime + 0.1; } } - else // if ( !FBitSet ( pev->spawnflags, SF_BRUSH_ACCDCC ) )//this is a normal start/stop brush. + else if ( !FBitSet ( pev->spawnflags, SF_BRUSH_ACCDCC ) )//this is a normal start/stop brush. { - if ( m_fCurSpeed != 0 ) //LRC -// if ( pev->avelocity != g_vecZero ) + if ( pev->avelocity != g_vecZero ) { - m_iState = STATE_OFF; // play stopping sound here - SetThink(&CFuncRotating :: SpinDown ); + SetThink ( SpinDown ); // EMIT_SOUND_DYN(ENT(pev), CHAN_WEAPON, (char *)STRING(pev->noiseStop), // m_flVolume, m_flAttenuation, 0, m_pitch); - SetNextThink( 0.1 ); + pev->nextthink = pev->ltime + 0.1; // pev->avelocity = g_vecZero; } else { - m_iState = STATE_ON; EMIT_SOUND_DYN(ENT(pev), CHAN_STATIC, (char *)STRING(pev->noiseRunning), m_flVolume, m_flAttenuation, 0, FANPITCHMAX); + pev->avelocity = pev->movedir * pev->speed; - //LRC - m_fCurSpeed = pev->speed; - UTIL_SetAvelocity(this, pev->movedir * pev->speed); -// pev->avelocity = pev->movedir * pev->speed; - - SetThink(&CFuncRotating :: Rotate ); + SetThink( Rotate ); Rotate(); } } @@ -826,21 +711,9 @@ void CFuncRotating :: RotatingUse( CBaseEntity *pActivator, CBaseEntity *pCaller // RotatingBlocked - An entity has blocked the brush // void CFuncRotating :: Blocked( CBaseEntity *pOther ) + { - //g-cont. simple recursive anouncer for parent system - //tell parent who blocked his - if(!FNullEnt(m_pMoveWith) && m_iLFlags & LF_PARENTMOVE) m_pMoveWith->Blocked( this ); - if(!FNullEnt(m_pChildMoveWith)) - { - if(m_pChildMoveWith == pOther) - { - //ALERT(at_console, "I'am blocked by my child!\n"); - Use( NULL, NULL, USE_OFF, 0 ); - } - } - - if (m_hActivator) pOther->TakeDamage( pev, m_hActivator->pev, pev->dmg, DMG_CRUSH ); //AJH Attribute damage to he who switched me. - else pOther->TakeDamage( pev, pev, pev->dmg, DMG_CRUSH ); + pOther->TakeDamage( pev, pev, pev->dmg, DMG_CRUSH); } @@ -856,16 +729,15 @@ class CPendulum : public CBaseEntity public: void Spawn ( void ); void KeyValue( KeyValueData *pkvd ); - void EXPORT SwingThink( void ); + void EXPORT Swing( void ); void EXPORT PendulumUse( CBaseEntity *pActivator, CBaseEntity *pCaller, USE_TYPE useType, float value ); - void EXPORT StopThink( void ); + void EXPORT Stop( void ); void Touch( CBaseEntity *pOther ); void EXPORT RopeTouch ( CBaseEntity *pOther );// this touch func makes the pendulum a rope virtual int ObjectCaps( void ) { return CBaseEntity :: ObjectCaps() & ~FCAP_ACROSS_TRANSITION; } virtual int Save( CSave &save ); virtual int Restore( CRestore &restore ); void Blocked( CBaseEntity *pOther ); - virtual STATE GetState( void ) { return (pev->speed)?STATE_ON:STATE_OFF; } static TYPEDESCRIPTION m_SaveData[]; @@ -877,8 +749,6 @@ public: float m_dampSpeed; vec3_t m_center; vec3_t m_start; - - EHANDLE m_hActivator; //AJH (give frags to this entity) }; LINK_ENTITY_TO_CLASS( func_pendulum, CPendulum ); @@ -906,11 +776,6 @@ void CPendulum :: KeyValue( KeyValueData *pkvd ) m_distance = atof(pkvd->szValue); pkvd->fHandled = TRUE; } - else if (FStrEq(pkvd->szKeyName, "axes")) - { - UTIL_StringToVector( (float*)(pev->movedir), pkvd->szValue ); - pkvd->fHandled = TRUE; - } else if (FStrEq(pkvd->szKeyName, "damp")) { m_damp = atof(pkvd->szValue) * 0.001; @@ -931,7 +796,7 @@ void CPendulum :: Spawn( void ) else pev->solid = SOLID_BSP; pev->movetype = MOVETYPE_PUSH; - UTIL_SetOrigin(this, pev->origin); + UTIL_SetOrigin(pev, pev->origin); SET_MODEL(ENT(pev), STRING(pev->model) ); if ( m_distance == 0 ) @@ -947,25 +812,21 @@ void CPendulum :: Spawn( void ) if ( FBitSet( pev->spawnflags, SF_BRUSH_ROTATE_INSTANT) ) { - SetThink(&CPendulum :: SUB_CallUseToggle ); - SetNextThink( 0.1 ); + SetThink( SUB_CallUseToggle ); + pev->nextthink = gpGlobals->time + 0.1; } pev->speed = 0; - SetUse(&CPendulum :: PendulumUse ); + SetUse( PendulumUse ); if ( FBitSet( pev->spawnflags, SF_PENDULUM_SWING ) ) { - SetTouch(&CPendulum :: RopeTouch ); + SetTouch ( RopeTouch ); } } void CPendulum :: PendulumUse( CBaseEntity *pActivator, CBaseEntity *pCaller, USE_TYPE useType, float value ) { - if (!ShouldToggle(useType)) return; - - m_hActivator = pActivator; //AJH - if ( pev->speed ) // Pendulum is moving, stop it and auto-return if necessary { if ( FBitSet( pev->spawnflags, SF_PENDULUM_AUTO_RETURN ) ) @@ -974,56 +835,43 @@ void CPendulum :: PendulumUse( CBaseEntity *pActivator, CBaseEntity *pCaller, US delta = CBaseToggle :: AxisDelta( pev->spawnflags, pev->angles, m_start ); - UTIL_SetAvelocity(this, m_maxSpeed * pev->movedir); //LRC - //pev->avelocity = m_maxSpeed * pev->movedir; - SetNextThink(delta / m_maxSpeed); - SetThink(&CPendulum ::StopThink); + pev->avelocity = m_maxSpeed * pev->movedir; + pev->nextthink = pev->ltime + (delta / m_maxSpeed); + SetThink( Stop ); } else { pev->speed = 0; // Dead stop - DontThink(); - UTIL_SetAvelocity(this, g_vecZero); //LRC - //pev->avelocity = g_vecZero; + SetThink( NULL ); + pev->avelocity = g_vecZero; } } else { - SetNextThink(0.1); // start the pendulum moving - SetThink(&CPendulum ::SwingThink); + pev->nextthink = pev->ltime + 0.1; // Start the pendulum moving m_time = gpGlobals->time; // Save time to calculate dt + SetThink( Swing ); m_dampSpeed = m_maxSpeed; } } -void CPendulum :: StopThink( void ) + +void CPendulum :: Stop( void ) { - UTIL_AssignAngles(this, m_start); //LRC - //pev->angles = m_start; + pev->angles = m_start; pev->speed = 0; - DontThink(); - UTIL_SetAvelocity(this, g_vecZero); //LRC - //pev->avelocity = g_vecZero; + SetThink( NULL ); + pev->avelocity = g_vecZero; } void CPendulum::Blocked( CBaseEntity *pOther ) { - //g-cont. simple recursive anouncer for parent system - //tell parent who blocked his - if(!FNullEnt(m_pMoveWith) && m_iLFlags & LF_PARENTMOVE) m_pMoveWith->Blocked( this ); - if(!FNullEnt(m_pChildMoveWith)) - { - if(m_pChildMoveWith == pOther) - { - //ALERT(at_console, "I'am blocked by my child!\n"); - Use( NULL, NULL, USE_OFF, 0 ); - } - } m_time = gpGlobals->time; } -void CPendulum :: SwingThink( void ) + +void CPendulum :: Swing( void ) { float delta, dt; @@ -1040,33 +888,21 @@ void CPendulum :: SwingThink( void ) pev->speed = m_maxSpeed; else if ( pev->speed < -m_maxSpeed ) pev->speed = -m_maxSpeed; - // scale the destdelta vector by the time spent traveling to get velocity - UTIL_SetAvelocity(this, pev->speed * pev->movedir); //LRC - //pev->avelocity = pev->speed * pev->movedir; - -// ALERT(at_console, "m_damp %f, m_dampSpeed %f\n", m_damp, m_dampSpeed); -// ALERT(at_console, "SwingThink: delta %f, dt %f, speed %f, avel %f %f %f\n", delta, dt, pev->speed, pev->avelocity.x, pev->avelocity.y, pev->avelocity.z); + pev->avelocity = pev->speed * pev->movedir; // Call this again - SetNextThink(0.1); - SetThink(&CPendulum ::SwingThink); - -// if (m_pMoveWith) // correct MoveWith problems associated with fast-thinking entities -// UTIL_AssignOrigin(this, m_vecOffsetOrigin + m_pMoveWith->pev->origin); + pev->nextthink = pev->ltime + 0.1; if ( m_damp ) { m_dampSpeed -= m_damp * m_dampSpeed * dt; if ( m_dampSpeed < 30.0 ) { - UTIL_AssignAngles(this, m_center); //LRC - //pev->angles = m_center; + pev->angles = m_center; pev->speed = 0; - ALERT(at_debug, "**CANCELLING pendulum think!\n"); - DontThink(); - UTIL_SetAvelocity(this, g_vecZero); //LRC - //pev->avelocity = g_vecZero; + SetThink( NULL ); + pev->avelocity = g_vecZero; } else if ( pev->speed > m_dampSpeed ) pev->speed = m_dampSpeed; @@ -1094,10 +930,7 @@ void CPendulum :: Touch ( CBaseEntity *pOther ) if ( damage < 0 ) damage = -damage; - if (m_hActivator) - pOther->TakeDamage( pev, m_hActivator->pev, damage, DMG_CRUSH ); - else - pOther->TakeDamage( pev, pev, damage, DMG_CRUSH ); + pOther->TakeDamage( pev, pev, damage, DMG_CRUSH ); pevOther->velocity = (pevOther->origin - VecBModelOrigin(pev) ).Normalize() * damage; } @@ -1122,3 +955,4 @@ void CPendulum :: RopeTouch ( CBaseEntity *pOther ) pevOther->movetype = MOVETYPE_NONE; } + diff --git a/spirit/bullsquid.cpp b/dlls/bullsquid.cpp similarity index 92% rename from spirit/bullsquid.cpp rename to dlls/bullsquid.cpp index 1420726e..c8518b62 100644 --- a/spirit/bullsquid.cpp +++ b/dlls/bullsquid.cpp @@ -25,9 +25,7 @@ #include "effects.h" #include "decals.h" #include "soundent.h" -#include "scripted.h" #include "game.h" -#include "weapons.h" #define SQUID_SPRINT_DIST 256 // how close the squid has to get before starting to sprint and refusing to swerve @@ -103,7 +101,7 @@ void CSquidSpit:: Spawn( void ) void CSquidSpit::Animate( void ) { - SetNextThink( 0.1 ); + pev->nextthink = gpGlobals->time + 0.1; if ( pev->frame++ ) { @@ -119,12 +117,12 @@ void CSquidSpit::Shoot( entvars_t *pevOwner, Vector vecStart, Vector vecVelocity CSquidSpit *pSpit = GetClassPtr( (CSquidSpit *)NULL ); pSpit->Spawn(); - UTIL_SetOrigin( pSpit, vecStart ); + UTIL_SetOrigin( pSpit->pev, vecStart ); pSpit->pev->velocity = vecVelocity; pSpit->pev->owner = ENT(pevOwner); - pSpit->SetThink(&CSquidSpit:: Animate ); - pSpit->SetNextThink( 0.1 ); + pSpit->SetThink ( Animate ); + pSpit->pev->nextthink = gpGlobals->time + 0.1; } void CSquidSpit :: Touch ( CBaseEntity *pOther ) @@ -152,14 +150,10 @@ void CSquidSpit :: Touch ( CBaseEntity *pOther ) // make a splat on the wall UTIL_TraceLine( pev->origin, pev->origin + pev->velocity * 10, dont_ignore_monsters, ENT( pev ), &tr ); - - CBaseEntity *pHit = CBaseEntity::Instance( tr.pHit ); - PLAYBACK_EVENT_FULL( FEV_RELIABLE|FEV_GLOBAL, edict(), m_usDecals, 0.0, (float *)&tr.vecEndPos, (float *)&g_vecZero, 0.0, 0.0, pHit->entindex(), 4, 0, 0 ); - - //UTIL_DecalTrace(&tr, DECAL_SPIT1 + RANDOM_LONG(0,1)); + UTIL_DecalTrace(&tr, DECAL_SPIT1 + RANDOM_LONG(0,1)); // make some flecks - MESSAGE_BEGIN( MSG_PVS, gmsgTempEntity, tr.vecEndPos ); + MESSAGE_BEGIN( MSG_PVS, SVC_TEMPENTITY, tr.vecEndPos ); WRITE_BYTE( TE_SPRITE_SPRAY ); WRITE_COORD( tr.vecEndPos.x); // pos WRITE_COORD( tr.vecEndPos.y); @@ -178,8 +172,8 @@ void CSquidSpit :: Touch ( CBaseEntity *pOther ) pOther->TakeDamage ( pev, pev, gSkillData.bullsquidDmgSpit, DMG_GENERIC ); } - SetThink(&CSquidSpit :: SUB_Remove ); - SetNextThink( 0 ); + SetThink ( SUB_Remove ); + pev->nextthink = gpGlobals->time; } //========================================================= @@ -232,7 +226,6 @@ public: float m_flNextSpitTime;// last time the bullsquid used the spit attack. }; LINK_ENTITY_TO_CLASS( monster_bullchicken, CBullsquid ); -LINK_ENTITY_TO_CLASS( monster_bullsquid, CBullsquid ); //LRC - let's get the right name... TYPEDESCRIPTION CBullsquid::m_SaveData[] = { @@ -253,7 +246,6 @@ int CBullsquid::IgnoreConditions ( void ) if ( gpGlobals->time - m_flLastHurtTime <= 20 ) { // haven't been hurt in 20 seconds, so let the squid care about stink. - // Er, more like, we HAVE been hurt in the last 20 seconds, so DON'T let it care about food. --LRC iIgnore = bits_COND_SMELL | bits_COND_SMELL_FOOD; } @@ -262,7 +254,6 @@ int CBullsquid::IgnoreConditions ( void ) if ( FClassnameIs( m_hEnemy->pev, "monster_headcrab" ) ) { // (Unless after a tasty headcrab) - // i.e. when chasing a headcrab, don't worry about other food. --LRC iIgnore = bits_COND_SMELL | bits_COND_SMELL_FOOD; } } @@ -434,7 +425,7 @@ int CBullsquid :: ISoundMask ( void ) //========================================================= int CBullsquid :: Classify ( void ) { - return m_iClass?m_iClass:CLASS_ALIEN_PREDATOR; + return CLASS_ALIEN_PREDATOR; } //========================================================= @@ -548,14 +539,6 @@ void CBullsquid :: HandleAnimEvent( MonsterEvent_t *pEvent ) // we should be able to read the position of bones at runtime for this info. vecSpitOffset = ( gpGlobals->v_right * 8 + gpGlobals->v_forward * 37 + gpGlobals->v_up * 23 ); vecSpitOffset = ( pev->origin + vecSpitOffset ); - if (m_pCine) // LRC- are we being told to do this by a scripted_action? - { - if (m_hTargetEnt != NULL && m_pCine->PreciseAttack()) - vecSpitDir = ( ( m_hTargetEnt->pev->origin ) - vecSpitOffset ).Normalize(); - else - vecSpitDir = gpGlobals->v_forward; - } - else vecSpitDir = ( ( m_hEnemy->pev->origin + m_hEnemy->pev->view_ofs ) - vecSpitOffset ).Normalize(); vecSpitDir.x += RANDOM_FLOAT( -0.05, 0.05 ); @@ -567,7 +550,7 @@ void CBullsquid :: HandleAnimEvent( MonsterEvent_t *pEvent ) AttackSound(); // spew the spittle temporary ents. - MESSAGE_BEGIN( MSG_PVS, gmsgTempEntity, vecSpitOffset ); + MESSAGE_BEGIN( MSG_PVS, SVC_TEMPENTITY, vecSpitOffset ); WRITE_BYTE( TE_SPRITE_SPRAY ); WRITE_COORD( vecSpitOffset.x); // pos WRITE_COORD( vecSpitOffset.y); @@ -687,18 +670,14 @@ void CBullsquid :: Spawn() { Precache( ); - if (pev->model) - SET_MODEL(ENT(pev), STRING(pev->model)); //LRC - else - SET_MODEL(ENT(pev), "models/bullsquid.mdl"); + SET_MODEL(ENT(pev), "models/bullsquid.mdl"); UTIL_SetSize( pev, Vector( -32, -32, 0 ), Vector( 32, 32, 64 ) ); pev->solid = SOLID_SLIDEBOX; pev->movetype = MOVETYPE_STEP; m_bloodColor = BLOOD_COLOR_GREEN; pev->effects = 0; - if (pev->health == 0) - pev->health = gSkillData.bullsquidHealth; + pev->health = gSkillData.bullsquidHealth; m_flFieldOfView = 0.2;// indicates the width of this monster's forward view cone ( as a dotproduct result ) m_MonsterState = MONSTERSTATE_NONE; @@ -713,10 +692,7 @@ void CBullsquid :: Spawn() //========================================================= void CBullsquid :: Precache() { - if (pev->model) - PRECACHE_MODEL((char*)STRING(pev->model)); //LRC - else - PRECACHE_MODEL("models/bullsquid.mdl"); + PRECACHE_MODEL("models/bullsquid.mdl"); PRECACHE_MODEL("sprites/bigspit.spr");// spit projectile. diff --git a/spirit/buttons.cpp b/dlls/buttons.cpp similarity index 63% rename from spirit/buttons.cpp rename to dlls/buttons.cpp index c619cbe7..e4932673 100644 --- a/spirit/buttons.cpp +++ b/dlls/buttons.cpp @@ -29,13 +29,9 @@ #define SF_BUTTON_DONTMOVE 1 #define SF_ROTBUTTON_NOTSOLID 1 -#define SF_BUTTON_ONLYDIRECT 16 //LRC - button can't be used through walls. #define SF_BUTTON_TOGGLE 32 // button stays pushed until reactivated #define SF_BUTTON_SPARK_IF_OFF 64 // button sparks in OFF state -#define SF_BUTTON_NOT_SOLID 128 // button isn't solid -#define SF_BUTTON_TOUCH_ONLY 256 // button must be touched to be used. -#define SF_BUTTON_USEKEY 512 // change the reaction of the button to the USE key. - // (i.e. if it's meant to be ignored, don't ignore it; otherwise ignore it.) +#define SF_BUTTON_TOUCH_ONLY 256 // button only fires as a result of USE key. #define SF_GLOBAL_SET 1 // Set global state to initial state on spawn @@ -101,39 +97,28 @@ void CEnvGlobal::Use( CBaseEntity *pActivator, CBaseEntity *pCaller, USE_TYPE us GLOBALESTATE oldState = gGlobalState.EntityGetState( m_globalstate ); GLOBALESTATE newState; - if (useType==USE_ON)//this support USE_TYPE global states. G-Cont. - { - newState = GLOBAL_ON; - } - else if(useType==USE_OFF) - { - newState = GLOBAL_OFF; - } - else + switch( m_triggermode ) { - switch( m_triggermode ) - { - case 0: + case 0: + newState = GLOBAL_OFF; + break; + + case 1: + newState = GLOBAL_ON; + break; + + case 2: + newState = GLOBAL_DEAD; + break; + + default: + case 3: + if ( oldState == GLOBAL_ON ) newState = GLOBAL_OFF; - break; - - case 1: - newState = GLOBAL_ON; - break; - - case 2: - newState = GLOBAL_DEAD; - break; - - default: - case 3: - if ( oldState == GLOBAL_ON ) - newState = GLOBAL_OFF; - else if ( oldState == GLOBAL_OFF ) - newState = GLOBAL_ON; - else - newState = oldState; - } + else if ( oldState == GLOBAL_OFF ) + newState = GLOBAL_ON; + else + newState = oldState; } if ( gGlobalState.EntityInTable( m_globalstate ) ) @@ -143,202 +128,6 @@ void CEnvGlobal::Use( CBaseEntity *pActivator, CBaseEntity *pCaller, USE_TYPE us } -//================================================== -//LRC- a simple entity, just maintains a state -//================================================== - -#define SF_ENVSTATE_START_ON 1 -#define SF_ENVSTATE_DEBUG 2 - -class CEnvState : public CPointEntity -{ -public: - void Spawn( void ); - void Think( void ); - void KeyValue( KeyValueData *pkvd ); - void Use( CBaseEntity *pActivator, CBaseEntity *pCaller, USE_TYPE useType, float value ); - - BOOL IsLockedByMaster( void ) { return !UTIL_IsMasterTriggered(m_sMaster, NULL); }; - - virtual int Save( CSave &save ); - virtual int Restore( CRestore &restore ); - - virtual STATE GetState() { return m_iState; } - - static TYPEDESCRIPTION m_SaveData[]; - - STATE m_iState; - float m_fTurnOnTime; - float m_fTurnOffTime; - int m_sMaster; -}; - -void CEnvState::Spawn( void ) -{ - if (pev->spawnflags & SF_ENVSTATE_START_ON) - m_iState = STATE_ON; - else - m_iState = STATE_OFF; -} - -TYPEDESCRIPTION CEnvState::m_SaveData[] = -{ - DEFINE_FIELD( CEnvState, m_iState, FIELD_INTEGER ), - DEFINE_FIELD( CEnvState, m_fTurnOnTime, FIELD_INTEGER ), - DEFINE_FIELD( CEnvState, m_fTurnOffTime, FIELD_INTEGER ), - DEFINE_FIELD( CEnvState, m_sMaster, FIELD_STRING ), -}; - -IMPLEMENT_SAVERESTORE( CEnvState, CPointEntity ); - -LINK_ENTITY_TO_CLASS( env_state, CEnvState ); - -void CEnvState::KeyValue( KeyValueData *pkvd ) -{ - pkvd->fHandled = TRUE; - - if ( FStrEq(pkvd->szKeyName, "turnontime") ) - m_fTurnOnTime = atof( pkvd->szValue ); - else if ( FStrEq(pkvd->szKeyName, "turnofftime") ) - m_fTurnOffTime = atof( pkvd->szValue ); - else if ( FStrEq(pkvd->szKeyName, "master") ) - m_sMaster = ALLOC_STRING( pkvd->szValue ); - else - CPointEntity::KeyValue( pkvd ); -} - -void CEnvState::Use( CBaseEntity *pActivator, CBaseEntity *pCaller, USE_TYPE useType, float value ) -{ - if (!ShouldToggle(useType) || IsLockedByMaster()) - { - if (pev->spawnflags & SF_ENVSTATE_DEBUG) - { - ALERT(at_debug,"DEBUG: env_state \"%s\" ",STRING(pev->targetname)); - if (IsLockedByMaster()) - ALERT(at_debug,"ignored trigger %s; locked by master \"%s\".\n",GetStringForUseType(useType),STRING(m_sMaster)); - else if (useType == USE_ON) - ALERT(at_debug,"ignored trigger USE_ON; already on\n"); - else if (useType == USE_OFF) - ALERT(at_debug,"ignored trigger USE_OFF; already off\n"); - else - ALERT(at_debug,"ignored trigger %s.\n",GetStringForUseType(useType)); - } - return; - } - - switch (GetState()) - { - case STATE_ON: - case STATE_TURN_ON: - if (m_fTurnOffTime) - { - m_iState = STATE_TURN_OFF; - if (pev->spawnflags & SF_ENVSTATE_DEBUG) - { - ALERT(at_debug,"DEBUG: env_state \"%s\" triggered; will turn off in %f seconds.\n", STRING(pev->targetname), m_fTurnOffTime); - } - SetNextThink( m_fTurnOffTime ); - } - else - { - m_iState = STATE_OFF; - if (pev->spawnflags & SF_ENVSTATE_DEBUG) - { - ALERT(at_debug,"DEBUG: env_state \"%s\" triggered, turned off", STRING(pev->targetname)); - if (pev->target) - { - ALERT(at_debug,": firing \"%s\"",STRING(pev->target)); - if (pev->noise2) - ALERT(at_debug," and \"%s\"",STRING(pev->noise2)); - } - else if (pev->noise2) - ALERT(at_debug,": firing \"%s\"",STRING(pev->noise2)); - ALERT(at_debug,".\n"); - } - FireTargets(STRING(pev->target),pActivator,this,USE_OFF,0); - FireTargets(STRING(pev->noise2),pActivator,this,USE_TOGGLE,0); - DontThink(); - } - break; - case STATE_OFF: - case STATE_TURN_OFF: - if (m_fTurnOnTime) - { - m_iState = STATE_TURN_ON; - if (pev->spawnflags & SF_ENVSTATE_DEBUG) - { - ALERT(at_debug,"DEBUG: env_state \"%s\" triggered; will turn on in %f seconds.\n", STRING(pev->targetname), m_fTurnOnTime); - } - SetNextThink( m_fTurnOnTime ); - } - else - { - m_iState = STATE_ON; - if (pev->spawnflags & SF_ENVSTATE_DEBUG) - { - ALERT(at_debug,"DEBUG: env_state \"%s\" triggered, turned on",STRING(pev->targetname)); - if (pev->target) - { - ALERT(at_debug,": firing \"%s\"",STRING(pev->target)); - if (pev->noise1) - ALERT(at_debug," and \"%s\"",STRING(pev->noise1)); - } - else if (pev->noise1) - ALERT(at_debug,": firing \"%s\"", STRING(pev->noise1)); - ALERT(at_debug,".\n"); - } - FireTargets(STRING(pev->target),pActivator,this,USE_ON,0); - FireTargets(STRING(pev->noise1),pActivator,this,USE_TOGGLE,0); - DontThink(); - } - break; - } -} - -void CEnvState::Think( void ) -{ - if (m_iState == STATE_TURN_ON) - { - m_iState = STATE_ON; - if (pev->spawnflags & SF_ENVSTATE_DEBUG) - { - ALERT(at_debug,"DEBUG: env_state \"%s\" turned itself on",STRING(pev->targetname)); - if (pev->target) - { - ALERT(at_debug,": firing %s",STRING(pev->target)); - if (pev->noise1) - ALERT(at_debug," and %s",STRING(pev->noise1)); - } - else if (pev->noise1) - ALERT(at_debug,": firing %s",STRING(pev->noise1)); - ALERT(at_debug,".\n"); - } - FireTargets(STRING(pev->target),this,this,USE_ON,0); - FireTargets(STRING(pev->noise1),this,this,USE_TOGGLE,0); - } - else if (m_iState == STATE_TURN_OFF) - { - m_iState = STATE_OFF; - if (pev->spawnflags & SF_ENVSTATE_DEBUG) - { - ALERT(at_debug,"DEBUG: env_state \"%s\" turned itself off",STRING(pev->targetname)); - if (pev->target) - ALERT(at_debug,": firing %s",STRING(pev->target)); - if (pev->noise2) - ALERT(at_debug," and %s",STRING(pev->noise2)); - else if (pev->noise2) - ALERT(at_debug,": firing %s",STRING(pev->noise2)); - ALERT(at_debug,".\n"); - } - FireTargets(STRING(pev->target),this,this,USE_OFF,0); - FireTargets(STRING(pev->noise2),this,this,USE_TOGGLE,0); - } -} - - -//=========================== -//LRC- the evil multisource... -//=========================== TYPEDESCRIPTION CMultiSource::m_SaveData[] = { @@ -374,8 +163,7 @@ void CMultiSource::KeyValue( KeyValueData *pkvd ) CPointEntity::KeyValue( pkvd ); } -#define SF_MULTI_FIREONCLOSE 1 -#define SF_MULTI_INIT 2 +#define SF_MULTI_INIT 1 void CMultiSource::Spawn() { @@ -383,9 +171,9 @@ void CMultiSource::Spawn() pev->solid = SOLID_NOT; pev->movetype = MOVETYPE_NONE; - SetNextThink( 0.1 ); + pev->nextthink = gpGlobals->time + 0.1; pev->spawnflags |= SF_MULTI_INIT; // Until it's initialized - SetThink(&CMultiSource::Register); + SetThink(Register); } void CMultiSource::Use( CBaseEntity *pActivator, CBaseEntity *pCaller, USE_TYPE useType, float value ) @@ -400,40 +188,17 @@ void CMultiSource::Use( CBaseEntity *pActivator, CBaseEntity *pCaller, USE_TYPE // if we didn't find it, report error and leave if (i > m_iTotal) { - if (pCaller->pev->targetname) - ALERT(at_debug, "multisource \"%s\": Used by non-member %s \"%s\"\n", STRING(pev->targetname), STRING(pCaller->pev->classname), STRING(pCaller->pev->targetname)); - else - ALERT(at_debug, "multisource \"%s\": Used by non-member %s\n", STRING(pev->targetname), STRING(pCaller->pev->classname)); + ALERT(at_console, "MultiSrc:Used by non member %s.\n", STRING(pCaller->pev->classname)); return; } // CONSIDER: a Use input to the multisource always toggles. Could check useType for ON/OFF/TOGGLE - // LRC- On could be meaningful. Off, sadly, can't work in the obvious manner. - // LRC (09/06/01)- er... why not? - // LRC (28/04/02)- that depends what the "obvious" manner is. - // store the state before the change, so we can compare it to the new state - STATE s = GetState(); - - // do the change m_rgTriggered[i-1] ^= 1; - // did we change state? - if ( s == GetState() ) - return; - - if ( s == STATE_ON && pev->netname) + // + if ( IsTriggered( pActivator ) ) { - // the change disabled me and I have a "fire on disable" field - ALERT( at_aiconsole, "Multisource %s deactivated (%d inputs)\n", STRING(pev->targetname), m_iTotal ); - if ( m_globalstate ) - FireTargets( STRING(pev->netname), NULL, this, USE_OFF, 0 ); - else - FireTargets( STRING(pev->netname), NULL, this, USE_TOGGLE, 0 ); - } - else if ( s == STATE_OFF ) - { - // the change activated me ALERT( at_aiconsole, "Multisource %s enabled (%d inputs)\n", STRING(pev->targetname), m_iTotal ); USE_TYPE useType = USE_TOGGLE; if ( m_globalstate ) @@ -443,15 +208,14 @@ void CMultiSource::Use( CBaseEntity *pActivator, CBaseEntity *pCaller, USE_TYPE } -//LRC- while we're in STATE_OFF, mastered entities can't do anything. -STATE CMultiSource::GetState( void ) +BOOL CMultiSource::IsTriggered( CBaseEntity * ) { // Is everything triggered? int i = 0; // Still initializing? if ( pev->spawnflags & SF_MULTI_INIT ) - return STATE_OFF; + return 0; while (i < m_iTotal) { @@ -463,12 +227,12 @@ STATE CMultiSource::GetState( void ) if (i == m_iTotal) { if ( !m_globalstate || gGlobalState.EntityGetState( m_globalstate ) == GLOBAL_ON ) - return STATE_ON; + return 1; } - return STATE_OFF; + return 0; } -/* + void CMultiSource::Register(void) { edict_t *pentTarget = NULL; @@ -476,7 +240,7 @@ void CMultiSource::Register(void) m_iTotal = 0; memset( m_rgEntities, 0, MS_MAX_TARGETS * sizeof(EHANDLE) ); - SetThink(&CMultiSource::SUB_DoNothing); + SetThink(SUB_DoNothing); // search for all entities which target this multisource (pev->targetname) @@ -503,54 +267,8 @@ void CMultiSource::Register(void) pev->spawnflags &= ~SF_MULTI_INIT; } -*/ -void CMultiSource::Register(void) -{ - m_iTotal = 0; - memset( m_rgEntities, 0, MS_MAX_TARGETS * sizeof(EHANDLE) ); - SetThink(&CMultiSource::SUB_DoNothing); - - // search for all entities which target this multisource (pev->targetname) - - CBaseEntity *pTarget = UTIL_FindEntityByTarget( NULL, STRING(pev->targetname) ); - while (pTarget && (m_iTotal < MS_MAX_TARGETS)) - { - m_rgEntities[m_iTotal++] = pTarget; - - pTarget = UTIL_FindEntityByTarget( pTarget, STRING(pev->targetname)); - } - - pTarget = UTIL_FindEntityByClassname(NULL, "multi_manager"); - while (pTarget && (m_iTotal < MS_MAX_TARGETS)) - { - if ( pTarget->HasTarget(pev->targetname) ) - m_rgEntities[m_iTotal++] = pTarget; - - pTarget = UTIL_FindEntityByClassname( pTarget, "multi_manager" ); - } - - if (m_iTotal >= MS_MAX_TARGETS) - { - ALERT(at_debug,"WARNING: There are too many entities targetting multisource \"%s\". (limit is %d)\n", STRING(pev->targetname), MS_MAX_TARGETS); - } - - pev->spawnflags &= ~SF_MULTI_INIT; -} - -//=================================== -// func_button (= CBaseButton) -//=================================== - -//LRC - moved here from cbase.h to use the spawnflags defined in this file -// Buttons that don't take damage can be IMPULSE used -int CBaseButton::ObjectCaps( void ) -{ - return (CBaseToggle:: ObjectCaps() & ~FCAP_ACROSS_TRANSITION) | - (pev->takedamage?0:FCAP_IMPULSE_USE) | - (pev->spawnflags & SF_BUTTON_ONLYDIRECT?FCAP_ONLYDIRECT_USE:0); -} - +// CBaseButton TYPEDESCRIPTION CBaseButton::m_SaveData[] = { DEFINE_FIELD( CBaseButton, m_fStayPushed, FIELD_BOOLEAN ), @@ -737,27 +455,15 @@ void CBaseButton::Spawn( ) if ( FBitSet ( pev->spawnflags, SF_BUTTON_SPARK_IF_OFF ) )// this button should spark in OFF state { - SetThink(&CBaseButton:: ButtonSpark ); - SetNextThink( 0.5 );// no hurry, make sure everything else spawns + SetThink ( ButtonSpark ); + pev->nextthink = gpGlobals->time + 0.5;// no hurry, make sure everything else spawns } SetMovedir(pev); pev->movetype = MOVETYPE_PUSH; - if ( FBitSet ( pev->spawnflags, SF_BUTTON_NOT_SOLID ) ) - { - pev->solid = SOLID_NOT; - pev->skin = CONTENTS_EMPTY; - } - else - { - pev->solid = SOLID_BSP; - } + pev->solid = SOLID_BSP; SET_MODEL(ENT(pev), STRING(pev->model)); - - //LRC - if (m_iStyle >= 32) LIGHT_STYLE(m_iStyle, "z"); - else if (m_iStyle <= -32) LIGHT_STYLE(-m_iStyle, "a"); if (pev->speed == 0) pev->speed = 40; @@ -789,36 +495,15 @@ void CBaseButton::Spawn( ) if ( FBitSet ( pev->spawnflags, SF_BUTTON_TOUCH_ONLY ) ) // touchable button { - SetTouch(&CBaseButton:: ButtonTouch ); - if ( !FBitSet ( pev->spawnflags, SF_BUTTON_USEKEY ) ) - SetUse(&CBaseButton:: ButtonUse_IgnorePlayer ); - else - SetUse(&CBaseButton:: ButtonUse ); + SetTouch( ButtonTouch ); } else { SetTouch ( NULL ); - if ( FBitSet ( pev->spawnflags, SF_BUTTON_USEKEY ) ) - SetUse(&CBaseButton:: ButtonUse_IgnorePlayer ); - else - SetUse(&CBaseButton:: ButtonUse ); + SetUse ( ButtonUse ); } } -//LRC -void CBaseButton :: PostSpawn( void ) -{ - if (m_pMoveWith) - m_vecPosition1 = pev->origin - m_pMoveWith->pev->origin; - else - m_vecPosition1 = pev->origin; - // Subtract 2 from size because the engine expands bboxes by 1 in all directions - m_vecPosition2 = m_vecPosition1 + (pev->movedir * (fabs( pev->movedir.x * (pev->size.x-2) ) + fabs( pev->movedir.y * (pev->size.y-2) ) + fabs( pev->movedir.z * (pev->size.z-2) ) - m_flLip)); - - // Is this a non-moving button? - if ( ((m_vecPosition2 - m_vecPosition1).Length() < 1) || (pev->spawnflags & SF_BUTTON_DONTMOVE) ) - m_vecPosition2 = m_vecPosition1; -} // Button sound table. // Also used by CBaseDoor to get 'touched' door lock/unlock sounds @@ -868,7 +553,7 @@ void DoSpark(entvars_t *pev, const Vector &location ) Vector tmp = location + pev->size * 0.5; UTIL_Sparks( tmp ); - float flVolume = RANDOM_FLOAT ( 0.1 , 0.25 ) * 0.4;//random volume range + float flVolume = RANDOM_FLOAT ( 0.25 , 0.75 ) * 0.4;//random volume range switch ( (int)(RANDOM_FLOAT(0,1) * 6) ) { case 0: EMIT_SOUND(ENT(pev), CHAN_VOICE, "buttons/spark1.wav", flVolume, ATTN_NORM); break; @@ -882,8 +567,8 @@ void DoSpark(entvars_t *pev, const Vector &location ) void CBaseButton::ButtonSpark ( void ) { - SetThink(&CBaseButton:: ButtonSpark ); - SetNextThink( 0.1 + RANDOM_FLOAT ( 0, 1.5 ) );// spark again at random interval + SetThink ( ButtonSpark ); + pev->nextthink = gpGlobals->time + ( 0.1 + RANDOM_FLOAT ( 0, 1.5 ) );// spark again at random interval DoSpark( pev, pev->mins ); } @@ -914,12 +599,6 @@ void CBaseButton::ButtonUse ( CBaseEntity *pActivator, CBaseEntity *pCaller, USE ButtonActivate( ); } -//LRC - they had it set up so that a touch-only button couldn't even be triggered!? -void CBaseButton::ButtonUse_IgnorePlayer ( CBaseEntity *pActivator, CBaseEntity *pCaller, USE_TYPE useType, float value ) -{ - if ( !pCaller || !pCaller->IsPlayer() ) - ButtonUse( pActivator, pCaller, useType, value ); -} CBaseButton::BUTTON_CODE CBaseButton::ButtonResponseToTouch( void ) { @@ -1001,21 +680,11 @@ void CBaseButton::ButtonActivate( ) ASSERT(m_toggle_state == TS_AT_BOTTOM); m_toggle_state = TS_GOING_UP; - //LRC - unhelpfully, SF_BUTTON_DONTMOVE is the same value as - // SF_ROTBUTTON_NOTSOLID, so we have to assume that a rotbutton will - // never be DONTMOVE. - if (pev->spawnflags & SF_BUTTON_DONTMOVE && !m_fRotating) - { - TriggerAndWait(); - } + SetMoveDone( TriggerAndWait ); + if (!m_fRotating) + LinearMove( m_vecPosition2, pev->speed); else - { - SetMoveDone(&CBaseButton:: TriggerAndWait ); - if (!m_fRotating) - LinearMove( m_vecPosition2, pev->speed); - else - AngularMove( m_vecAngle2, pev->speed); - } + AngularMove( m_vecAngle2, pev->speed); } // @@ -1030,13 +699,6 @@ void CBaseButton::TriggerAndWait( void ) m_toggle_state = TS_AT_TOP; - pev->frame = 1; // use alternate textures - //LRC - if (m_iStyle >= 32) LIGHT_STYLE(m_iStyle, "a"); - else if (m_iStyle <= -32) LIGHT_STYLE(-m_iStyle, "z"); - - SUB_UseTargets( m_hActivator, USE_TOGGLE, 0 ); - // If button automatically comes back out, start it moving out. // Else re-instate touch method if (m_fStayPushed || FBitSet ( pev->spawnflags, SF_BUTTON_TOGGLE ) ) @@ -1047,20 +709,18 @@ void CBaseButton::TriggerAndWait( void ) SetTouch ( NULL ); } else - SetTouch(&CBaseButton:: ButtonTouch ); + SetTouch( ButtonTouch ); } else { - SetThink(&CBaseButton:: ButtonReturn ); - if ( m_flWait ) - { - SetNextThink( m_flWait ); - } - else - { - ButtonReturn(); - } + pev->nextthink = pev->ltime + m_flWait; + SetThink( ButtonReturn ); } + + pev->frame = 1; // use alternate textures + + + SUB_UseTargets( m_hActivator, USE_TOGGLE, 0 ); } @@ -1072,24 +732,13 @@ void CBaseButton::ButtonReturn( void ) ASSERT(m_toggle_state == TS_AT_TOP); m_toggle_state = TS_GOING_DOWN; - pev->frame = 0; // use normal textures - - //LRC - if (m_iStyle >= 32) LIGHT_STYLE(m_iStyle, "z"); - else if (m_iStyle <= -32) LIGHT_STYLE(-m_iStyle, "a"); - - if (pev->spawnflags & SF_BUTTON_DONTMOVE) - { - ButtonBackHome(); - } + SetMoveDone( ButtonBackHome ); + if (!m_fRotating) + LinearMove( m_vecPosition1, pev->speed); else - { - SetMoveDone(&CBaseButton:: ButtonBackHome ); - if (!m_fRotating) - LinearMove( m_vecPosition1, pev->speed); - else - AngularMove( m_vecAngle1, pev->speed); - } + AngularMove( m_vecAngle1, pev->speed); + + pev->frame = 0; // use normal textures } @@ -1111,19 +760,20 @@ void CBaseButton::ButtonBackHome( void ) if (!FStringNull(pev->target)) { - CBaseEntity *pTarget = NULL; + edict_t* pentTarget = NULL; for (;;) { - pTarget = UTIL_FindEntityByTargetname(pTarget, STRING(pev->target), m_hActivator); + pentTarget = FIND_ENTITY_BY_TARGETNAME(pentTarget, STRING(pev->target)); - if (FNullEnt(pTarget)) + if (FNullEnt(pentTarget)) break; - if (!FClassnameIs(pTarget->pev, "multisource")) - // LRC- hmm... I see. On returning, a button will only turn off multisources. + if (!FClassnameIs(pentTarget, "multisource")) continue; + CBaseEntity *pTarget = CBaseEntity::Instance( pentTarget ); - pTarget->Use( m_hActivator, this, USE_TOGGLE, 0 ); + if ( pTarget ) + pTarget->Use( m_hActivator, this, USE_TOGGLE, 0 ); } } @@ -1134,17 +784,13 @@ void CBaseButton::ButtonBackHome( void ) SetTouch ( NULL ); } else - SetTouch(&CBaseButton:: ButtonTouch ); + SetTouch( ButtonTouch ); // reset think for a sparking button if ( FBitSet ( pev->spawnflags, SF_BUTTON_SPARK_IF_OFF ) ) { - SetThink(&CBaseButton:: ButtonSpark ); - SetNextThink( 0.5 );// no hurry. - } - else - { - DontThink(); + SetThink ( ButtonSpark ); + pev->nextthink = gpGlobals->time + 0.5;// no hurry. } } @@ -1157,23 +803,10 @@ class CRotButton : public CBaseButton { public: void Spawn( void ); - void PostSpawn( void ) {} // don't use the moveWith fix from CBaseButton - virtual void KeyValue( KeyValueData* pkvd); }; LINK_ENTITY_TO_CLASS( func_rot_button, CRotButton ); -void CRotButton::KeyValue( KeyValueData *pkvd ) -{ - if (FStrEq(pkvd->szKeyName, "axes")) - { - UTIL_StringToVector( (float*)(pev->movedir), pkvd->szValue ); - pkvd->fHandled = TRUE; - } - else - CBaseButton::KeyValue( pkvd ); -} - void CRotButton::Spawn( void ) { char *pszSound; @@ -1224,19 +857,10 @@ void CRotButton::Spawn( void ) if ( !FBitSet ( pev->spawnflags, SF_BUTTON_TOUCH_ONLY ) ) { SetTouch ( NULL ); - if ( FBitSet ( pev->spawnflags, SF_BUTTON_USEKEY ) ) - SetUse(&CRotButton:: ButtonUse_IgnorePlayer ); - else - SetUse(&CRotButton:: ButtonUse ); + SetUse ( ButtonUse ); } else // touchable button - { - SetTouch(&CRotButton:: ButtonTouch ); - if ( !FBitSet ( pev->spawnflags, SF_BUTTON_USEKEY ) ) - SetUse(&CRotButton:: ButtonUse_IgnorePlayer ); - else - SetUse(&CRotButton:: ButtonUse ); - } + SetTouch( ButtonTouch ); //SetTouch( ButtonTouch ); } @@ -1324,7 +948,7 @@ void CMomentaryRotButton::Spawn( void ) pev->solid = SOLID_NOT; pev->movetype = MOVETYPE_PUSH; - UTIL_SetOrigin(this, pev->origin); + UTIL_SetOrigin(pev, pev->origin); SET_MODEL(ENT(pev), STRING(pev->model) ); char *pszSound = ButtonSound( m_sounds ); @@ -1345,11 +969,6 @@ void CMomentaryRotButton::KeyValue( KeyValueData *pkvd ) m_sounds = atoi(pkvd->szValue); pkvd->fHandled = TRUE; } - else if (FStrEq(pkvd->szKeyName, "axes")) - { - UTIL_StringToVector((float*)(pev->movedir), pkvd->szValue); - pkvd->fHandled = TRUE; - } else CBaseToggle::KeyValue( pkvd ); } @@ -1364,36 +983,33 @@ void CMomentaryRotButton::PlaySound( void ) // current, not future position. void CMomentaryRotButton::Use( CBaseEntity *pActivator, CBaseEntity *pCaller, USE_TYPE useType, float value ) { - if (IsLockedByMaster()) return; //LRC - // the distance between the current angle and the "base" angle. pev->ideal_yaw = CBaseToggle::AxisDelta( pev->spawnflags, pev->angles, m_start ) / m_flMoveDistance; UpdateAllButtons( pev->ideal_yaw, 1 ); - - float f = m_fNextThink - pev->ltime; - f = CBaseToggle::AxisDelta( pev->spawnflags, pev->angles + pev->avelocity*f, m_start ) / m_flMoveDistance; -// ALERT(at_console,"sending update = %f\n", f); - UpdateTarget( f ); + UpdateTarget( pev->ideal_yaw ); } void CMomentaryRotButton::UpdateAllButtons( float value, int start ) { - // Update all rot buttons attached to my target - // (this includes myself) - CBaseEntity *pTarget = NULL; + // Update all rot buttons attached to the same target + edict_t *pentTarget = NULL; for (;;) { - pTarget = UTIL_FindEntityByTarget(pTarget, STRING(pev->target)); - if (FNullEnt(pTarget)) + + pentTarget = FIND_ENTITY_BY_STRING(pentTarget, "target", STRING(pev->target)); + if (FNullEnt(pentTarget)) break; - if ( FClassnameIs( pTarget->pev, "momentary_rot_button" ) ) + if ( FClassnameIs( VARS(pentTarget), "momentary_rot_button" ) ) { - CMomentaryRotButton *pEntity = (CMomentaryRotButton*)pTarget; - if ( start ) - pEntity->UpdateSelf( value ); - else - pEntity->UpdateSelfReturn( value ); + CMomentaryRotButton *pEntity = CMomentaryRotButton::Instance(pentTarget); + if ( pEntity ) + { + if ( start ) + pEntity->UpdateSelf( value ); + else + pEntity->UpdateSelfReturn( value ); + } } } } @@ -1409,17 +1025,14 @@ void CMomentaryRotButton::UpdateSelf( float value ) } m_lastUsed = 1; - SetNextThink( 0.1 ); - - //LRC check if we're outside the boundaries + pev->nextthink = pev->ltime + 0.1; if ( m_direction > 0 && value >= 1.0 ) { pev->avelocity = g_vecZero; pev->angles = m_end; return; } - - if ( m_direction < 0 && value <= 0 ) + else if ( m_direction < 0 && value <= 0 ) { pev->avelocity = g_vecZero; pev->angles = m_start; @@ -1429,32 +1042,31 @@ void CMomentaryRotButton::UpdateSelf( float value ) if (fplaysound) PlaySound(); - // HACKHACK -- If we're going slow, we'll get multiple player packets per frame; - // bump nexthink on each one to avoid stalling - //LRC- that is to say: our avelocity will get us to the target point in 0.1 secs. - // If we're being told to move further than that, wait that much longer. - if ( m_fNextThink < pev->ltime ) - SetNextThink( 0.1 ); + // HACKHACK -- If we're going slow, we'll get multiple player packets per frame, bump nexthink on each one to avoid stalling + if ( pev->nextthink < pev->ltime ) + pev->nextthink = pev->ltime + 0.1; else - { - AbsoluteNextThink( m_fNextThink + 0.1 ); - } + pev->nextthink += 0.1; pev->avelocity = (m_direction * pev->speed) * pev->movedir; - SetThink(&CMomentaryRotButton:: Off ); + SetThink( Off ); } void CMomentaryRotButton::UpdateTarget( float value ) { if (!FStringNull(pev->target)) { - CBaseEntity* pTarget = NULL; + edict_t* pentTarget = NULL; for (;;) { - pTarget = UTIL_FindEntityByTargetname(pTarget, STRING(pev->target)); - if ( !pTarget ) + pentTarget = FIND_ENTITY_BY_TARGETNAME(pentTarget, STRING(pev->target)); + if (FNullEnt(pentTarget)) break; - pTarget->Use( this, this, USE_SET, value ); + CBaseEntity *pEntity = CBaseEntity::Instance(pentTarget); + if ( pEntity ) + { + pEntity->Use( this, this, USE_SET, value ); + } } } } @@ -1465,8 +1077,8 @@ void CMomentaryRotButton::Off( void ) m_lastUsed = 0; if ( FBitSet( pev->spawnflags, SF_PENDULUM_AUTO_RETURN ) && m_returnSpeed > 0 ) { - SetThink(&CMomentaryRotButton:: Return ); - SetNextThink( 0.1 ); + SetThink( Return ); + pev->nextthink = pev->ltime + 0.1; m_direction = -1; } else @@ -1489,13 +1101,13 @@ void CMomentaryRotButton::UpdateSelfReturn( float value ) { pev->avelocity = g_vecZero; pev->angles = m_start; - DontThink(); + pev->nextthink = -1; SetThink( NULL ); } else { pev->avelocity = -m_returnSpeed * pev->movedir; - SetNextThink( 0.1 ); + pev->nextthink = pev->ltime + 0.1; } } @@ -1520,15 +1132,12 @@ public: static TYPEDESCRIPTION m_SaveData[]; float m_flDelay; - STATE m_iState; //LRC - virtual STATE GetState( void ) { return m_iState; }; }; TYPEDESCRIPTION CEnvSpark::m_SaveData[] = { DEFINE_FIELD( CEnvSpark, m_flDelay, FIELD_FLOAT), - DEFINE_FIELD( CEnvSpark, m_iState, FIELD_INTEGER), //LRC }; IMPLEMENT_SAVERESTORE( CEnvSpark, CBaseEntity ); @@ -1545,21 +1154,21 @@ void CEnvSpark::Spawn(void) { if (FBitSet(pev->spawnflags, 64)) // Start on { - SetThink(&CEnvSpark::SparkThink); // start sparking - SetUse(&CEnvSpark::SparkStop); // set up +USE to stop sparking + SetThink(SparkThink); // start sparking + SetUse(SparkStop); // set up +USE to stop sparking } else - SetUse(&CEnvSpark::SparkStart); + SetUse(SparkStart); } else - SetThink(&CEnvSpark::SparkThink); + SetThink(SparkThink); - SetNextThink( 0.1 + RANDOM_FLOAT (0, 1.5) ); - + pev->nextthink = gpGlobals->time + ( 0.1 + RANDOM_FLOAT ( 0, 1.5 ) ); + if (m_flDelay <= 0) m_flDelay = 1.5; - Precache( ); + Precache( ); } @@ -1593,25 +1202,22 @@ void CEnvSpark::KeyValue( KeyValueData *pkvd ) void EXPORT CEnvSpark::SparkThink(void) { - SetNextThink( 0.1 + RANDOM_FLOAT (0, m_flDelay) ); + pev->nextthink = gpGlobals->time + 0.1 + RANDOM_FLOAT (0, m_flDelay); DoSpark( pev, pev->origin ); } void EXPORT CEnvSpark::SparkStart(CBaseEntity *pActivator, CBaseEntity *pCaller, USE_TYPE useType, float value ) { - SetUse(&CEnvSpark::SparkStop); - SetThink(&CEnvSpark::SparkThink); - m_iState = STATE_ON; //LRC - SetNextThink( 0.1 + RANDOM_FLOAT ( 0, m_flDelay) ); + SetUse(SparkStop); + SetThink(SparkThink); + pev->nextthink = gpGlobals->time + (0.1 + RANDOM_FLOAT ( 0, m_flDelay)); } void EXPORT CEnvSpark::SparkStop(CBaseEntity *pActivator, CBaseEntity *pCaller, USE_TYPE useType, float value ) { - SetUse(&CEnvSpark::SparkStart); + SetUse(SparkStart); SetThink(NULL); - m_iState = STATE_OFF; //LRC } -//G-Cont. flag 16 is removed - we don't need this #define SF_BTARGET_USE 0x0001 #define SF_BTARGET_ON 0x0002 diff --git a/dlls/cbase.cpp b/dlls/cbase.cpp new file mode 100644 index 00000000..52fb7188 --- /dev/null +++ b/dlls/cbase.cpp @@ -0,0 +1,774 @@ +/*** +* +* Copyright (c) 1996-2002, 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. +* +****/ +#include "extdll.h" +#include "util.h" +#include "cbase.h" +#include "saverestore.h" +#include "client.h" +#include "decals.h" +#include "gamerules.h" +#include "game.h" + +void EntvarsKeyvalue( entvars_t *pev, KeyValueData *pkvd ); + +extern void PM_Move ( struct playermove_s *ppmove, int server ); +extern void PM_Init ( struct playermove_s *ppmove ); +extern char PM_FindTextureType( char *name ); + +extern Vector VecBModelOrigin( entvars_t* pevBModel ); +extern DLL_GLOBAL Vector g_vecAttackDir; +extern DLL_GLOBAL int g_iSkillLevel; + +static DLL_FUNCTIONS gFunctionTable = +{ + GameDLLInit, //pfnGameInit + DispatchSpawn, //pfnSpawn + DispatchThink, //pfnThink + DispatchUse, //pfnUse + DispatchTouch, //pfnTouch + DispatchBlocked, //pfnBlocked + DispatchKeyValue, //pfnKeyValue + DispatchSave, //pfnSave + DispatchRestore, //pfnRestore + DispatchObjectCollsionBox, //pfnAbsBox + + SaveWriteFields, //pfnSaveWriteFields + SaveReadFields, //pfnSaveReadFields + + SaveGlobalState, //pfnSaveGlobalState + RestoreGlobalState, //pfnRestoreGlobalState + ResetGlobalState, //pfnResetGlobalState + + ClientConnect, //pfnClientConnect + ClientDisconnect, //pfnClientDisconnect + ClientKill, //pfnClientKill + ClientPutInServer, //pfnClientPutInServer + ClientCommand, //pfnClientCommand + ClientUserInfoChanged, //pfnClientUserInfoChanged + ServerActivate, //pfnServerActivate + ServerDeactivate, //pfnServerDeactivate + + PlayerPreThink, //pfnPlayerPreThink + PlayerPostThink, //pfnPlayerPostThink + + StartFrame, //pfnStartFrame + ParmsNewLevel, //pfnParmsNewLevel + ParmsChangeLevel, //pfnParmsChangeLevel + + GetGameDescription, //pfnGetGameDescription Returns string describing current .dll game. + PlayerCustomization, //pfnPlayerCustomization Notifies .dll of new customization for player. + + SpectatorConnect, //pfnSpectatorConnect Called when spectator joins server + SpectatorDisconnect, //pfnSpectatorDisconnect Called when spectator leaves the server + SpectatorThink, //pfnSpectatorThink Called when spectator sends a command packet (usercmd_t) + + Sys_Error, //pfnSys_Error Called when engine has encountered an error + + PM_Move, //pfnPM_Move + PM_Init, //pfnPM_Init Server version of player movement initialization + PM_FindTextureType, //pfnPM_FindTextureType + + SetupVisibility, //pfnSetupVisibility Set up PVS and PAS for networking for this client + UpdateClientData, //pfnUpdateClientData Set up data sent only to specific client + AddToFullPack, //pfnAddToFullPack + CreateBaseline, //pfnCreateBaseline Tweak entity baseline for network encoding, allows setup of player baselines, too. + RegisterEncoders, //pfnRegisterEncoders Callbacks for network encoding + GetWeaponData, //pfnGetWeaponData + CmdStart, //pfnCmdStart + CmdEnd, //pfnCmdEnd + ConnectionlessPacket, //pfnConnectionlessPacket + GetHullBounds, //pfnGetHullBounds + CreateInstancedBaselines, //pfnCreateInstancedBaselines + InconsistentFile, //pfnInconsistentFile + AllowLagCompensation, //pfnAllowLagCompensation +}; + +static void SetObjectCollisionBox( entvars_t *pev ); + +#ifndef _WIN32 +extern "C" { +#endif +int GetEntityAPI( DLL_FUNCTIONS *pFunctionTable, int interfaceVersion ) +{ + if ( !pFunctionTable || interfaceVersion != INTERFACE_VERSION ) + { + return FALSE; + } + + memcpy( pFunctionTable, &gFunctionTable, sizeof( DLL_FUNCTIONS ) ); + return TRUE; +} + +int GetEntityAPI2( DLL_FUNCTIONS *pFunctionTable, int *interfaceVersion ) +{ + if ( !pFunctionTable || *interfaceVersion != INTERFACE_VERSION ) + { + // Tell engine what version we had, so it can figure out who is out of date. + *interfaceVersion = INTERFACE_VERSION; + return FALSE; + } + + memcpy( pFunctionTable, &gFunctionTable, sizeof( DLL_FUNCTIONS ) ); + return TRUE; +} + +#ifndef _WIN32 +} +#endif + + +int DispatchSpawn( edict_t *pent ) +{ + CBaseEntity *pEntity = (CBaseEntity *)GET_PRIVATE(pent); + + if (pEntity) + { + // Initialize these or entities who don't link to the world won't have anything in here + pEntity->pev->absmin = pEntity->pev->origin - Vector(1,1,1); + pEntity->pev->absmax = pEntity->pev->origin + Vector(1,1,1); + + pEntity->Spawn(); + + // Try to get the pointer again, in case the spawn function deleted the entity. + // UNDONE: Spawn() should really return a code to ask that the entity be deleted, but + // that would touch too much code for me to do that right now. + pEntity = (CBaseEntity *)GET_PRIVATE(pent); + + if ( pEntity ) + { + if ( g_pGameRules && !g_pGameRules->IsAllowedToSpawn( pEntity ) ) + return -1; // return that this entity should be deleted + if ( pEntity->pev->flags & FL_KILLME ) + return -1; + } + + + // Handle global stuff here + if ( pEntity && pEntity->pev->globalname ) + { + const globalentity_t *pGlobal = gGlobalState.EntityFromTable( pEntity->pev->globalname ); + if ( pGlobal ) + { + // Already dead? delete + if ( pGlobal->state == GLOBAL_DEAD ) + return -1; + else if ( !FStrEq( STRING(gpGlobals->mapname), pGlobal->levelName ) ) + pEntity->MakeDormant(); // Hasn't been moved to this level yet, wait but stay alive + // In this level & not dead, continue on as normal + } + else + { + // Spawned entities default to 'On' + gGlobalState.EntityAdd( pEntity->pev->globalname, gpGlobals->mapname, GLOBAL_ON ); +// ALERT( at_console, "Added global entity %s (%s)\n", STRING(pEntity->pev->classname), STRING(pEntity->pev->globalname) ); + } + } + + } + + return 0; +} + +void DispatchKeyValue( edict_t *pentKeyvalue, KeyValueData *pkvd ) +{ + if ( !pkvd || !pentKeyvalue ) + return; + + EntvarsKeyvalue( VARS(pentKeyvalue), pkvd ); + + // If the key was an entity variable, or there's no class set yet, don't look for the object, it may + // not exist yet. + if ( pkvd->fHandled || pkvd->szClassName == NULL ) + return; + + // Get the actualy entity object + CBaseEntity *pEntity = (CBaseEntity *)GET_PRIVATE(pentKeyvalue); + + if ( !pEntity ) + return; + + pEntity->KeyValue( pkvd ); +} + + +// HACKHACK -- this is a hack to keep the node graph entity from "touching" things (like triggers) +// while it builds the graph +BOOL gTouchDisabled = FALSE; +void DispatchTouch( edict_t *pentTouched, edict_t *pentOther ) +{ + if ( gTouchDisabled ) + return; + + CBaseEntity *pEntity = (CBaseEntity *)GET_PRIVATE(pentTouched); + CBaseEntity *pOther = (CBaseEntity *)GET_PRIVATE( pentOther ); + + if ( pEntity && pOther && ! ((pEntity->pev->flags | pOther->pev->flags) & FL_KILLME) ) + pEntity->Touch( pOther ); +} + + +void DispatchUse( edict_t *pentUsed, edict_t *pentOther ) +{ + CBaseEntity *pEntity = (CBaseEntity *)GET_PRIVATE(pentUsed); + CBaseEntity *pOther = (CBaseEntity *)GET_PRIVATE(pentOther); + + if (pEntity && !(pEntity->pev->flags & FL_KILLME) ) + pEntity->Use( pOther, pOther, USE_TOGGLE, 0 ); +} + +void DispatchThink( edict_t *pent ) +{ + CBaseEntity *pEntity = (CBaseEntity *)GET_PRIVATE(pent); + if (pEntity) + { + if ( FBitSet( pEntity->pev->flags, FL_DORMANT ) ) + ALERT( at_error, "Dormant entity %s is thinking!!\n", STRING(pEntity->pev->classname) ); + + pEntity->Think(); + } +} + +void DispatchBlocked( edict_t *pentBlocked, edict_t *pentOther ) +{ + CBaseEntity *pEntity = (CBaseEntity *)GET_PRIVATE( pentBlocked ); + CBaseEntity *pOther = (CBaseEntity *)GET_PRIVATE( pentOther ); + + if (pEntity) + pEntity->Blocked( pOther ); +} + +void DispatchSave( edict_t *pent, SAVERESTOREDATA *pSaveData ) +{ + CBaseEntity *pEntity = (CBaseEntity *)GET_PRIVATE(pent); + + if ( pEntity && pSaveData ) + { + ENTITYTABLE *pTable = &pSaveData->pTable[ pSaveData->currentIndex ]; + + if ( pTable->pent != pent ) + ALERT( at_error, "ENTITY TABLE OR INDEX IS WRONG!!!!\n" ); + + if ( pEntity->ObjectCaps() & FCAP_DONT_SAVE ) + return; + + // These don't use ltime & nextthink as times really, but we'll fudge around it. + if ( pEntity->pev->movetype == MOVETYPE_PUSH ) + { + float delta = pEntity->pev->nextthink - pEntity->pev->ltime; + pEntity->pev->ltime = gpGlobals->time; + pEntity->pev->nextthink = pEntity->pev->ltime + delta; + } + + pTable->location = pSaveData->size; // Remember entity position for file I/O + pTable->classname = pEntity->pev->classname; // Remember entity class for respawn + + CSave saveHelper( pSaveData ); + pEntity->Save( saveHelper ); + + pTable->size = pSaveData->size - pTable->location; // Size of entity block is data size written to block + } +} + + +// Find the matching global entity. Spit out an error if the designer made entities of +// different classes with the same global name +CBaseEntity *FindGlobalEntity( string_t classname, string_t globalname ) +{ + edict_t *pent = FIND_ENTITY_BY_STRING( NULL, "globalname", STRING(globalname) ); + CBaseEntity *pReturn = CBaseEntity::Instance( pent ); + if ( pReturn ) + { + if ( !FClassnameIs( pReturn->pev, STRING(classname) ) ) + { + ALERT( at_console, "Global entity found %s, wrong class %s\n", STRING(globalname), STRING(pReturn->pev->classname) ); + pReturn = NULL; + } + } + + return pReturn; +} + + +int DispatchRestore( edict_t *pent, SAVERESTOREDATA *pSaveData, int globalEntity ) +{ + CBaseEntity *pEntity = (CBaseEntity *)GET_PRIVATE(pent); + + if ( pEntity && pSaveData ) + { + entvars_t tmpVars; + Vector oldOffset; + + CRestore restoreHelper( pSaveData ); + if ( globalEntity ) + { + CRestore tmpRestore( pSaveData ); + tmpRestore.PrecacheMode( 0 ); + tmpRestore.ReadEntVars( "ENTVARS", &tmpVars ); + + // HACKHACK - reset the save pointers, we're going to restore for real this time + pSaveData->size = pSaveData->pTable[pSaveData->currentIndex].location; + pSaveData->pCurrentData = pSaveData->pBaseData + pSaveData->size; + // ------------------- + + + const globalentity_t *pGlobal = gGlobalState.EntityFromTable( tmpVars.globalname ); + + // Don't overlay any instance of the global that isn't the latest + // pSaveData->szCurrentMapName is the level this entity is coming from + // pGlobla->levelName is the last level the global entity was active in. + // If they aren't the same, then this global update is out of date. + if ( !FStrEq( pSaveData->szCurrentMapName, pGlobal->levelName ) ) + return 0; + + // Compute the new global offset + oldOffset = pSaveData->vecLandmarkOffset; + CBaseEntity *pNewEntity = FindGlobalEntity( tmpVars.classname, tmpVars.globalname ); + if ( pNewEntity ) + { +// ALERT( at_console, "Overlay %s with %s\n", STRING(pNewEntity->pev->classname), STRING(tmpVars.classname) ); + // Tell the restore code we're overlaying a global entity from another level + restoreHelper.SetGlobalMode( 1 ); // Don't overwrite global fields + pSaveData->vecLandmarkOffset = (pSaveData->vecLandmarkOffset - pNewEntity->pev->mins) + tmpVars.mins; + pEntity = pNewEntity;// we're going to restore this data OVER the old entity + pent = ENT( pEntity->pev ); + // Update the global table to say that the global definition of this entity should come from this level + gGlobalState.EntityUpdate( pEntity->pev->globalname, gpGlobals->mapname ); + } + else + { + // This entity will be freed automatically by the engine. If we don't do a restore on a matching entity (below) + // or call EntityUpdate() to move it to this level, we haven't changed global state at all. + return 0; + } + + } + + if ( pEntity->ObjectCaps() & FCAP_MUST_SPAWN ) + { + pEntity->Restore( restoreHelper ); + pEntity->Spawn(); + } + else + { + pEntity->Restore( restoreHelper ); + pEntity->Precache( ); + } + + // Again, could be deleted, get the pointer again. + pEntity = (CBaseEntity *)GET_PRIVATE(pent); + +#if 0 + if ( pEntity && pEntity->pev->globalname && globalEntity ) + { + ALERT( at_console, "Global %s is %s\n", STRING(pEntity->pev->globalname), STRING(pEntity->pev->model) ); + } +#endif + + // Is this an overriding global entity (coming over the transition), or one restoring in a level + if ( globalEntity ) + { +// ALERT( at_console, "After: %f %f %f %s\n", pEntity->pev->origin.x, pEntity->pev->origin.y, pEntity->pev->origin.z, STRING(pEntity->pev->model) ); + pSaveData->vecLandmarkOffset = oldOffset; + if ( pEntity ) + { + UTIL_SetOrigin( pEntity->pev, pEntity->pev->origin ); + pEntity->OverrideReset(); + } + } + else if ( pEntity && pEntity->pev->globalname ) + { + const globalentity_t *pGlobal = gGlobalState.EntityFromTable( pEntity->pev->globalname ); + if ( pGlobal ) + { + // Already dead? delete + if ( pGlobal->state == GLOBAL_DEAD ) + return -1; + else if ( !FStrEq( STRING(gpGlobals->mapname), pGlobal->levelName ) ) + { + pEntity->MakeDormant(); // Hasn't been moved to this level yet, wait but stay alive + } + // In this level & not dead, continue on as normal + } + else + { + ALERT( at_error, "Global Entity %s (%s) not in table!!!\n", STRING(pEntity->pev->globalname), STRING(pEntity->pev->classname) ); + // Spawned entities default to 'On' + gGlobalState.EntityAdd( pEntity->pev->globalname, gpGlobals->mapname, GLOBAL_ON ); + } + } + } + return 0; +} + + +void DispatchObjectCollsionBox( edict_t *pent ) +{ + CBaseEntity *pEntity = (CBaseEntity *)GET_PRIVATE(pent); + if (pEntity) + { + pEntity->SetObjectCollisionBox(); + } + else + SetObjectCollisionBox( &pent->v ); +} + + +void SaveWriteFields( SAVERESTOREDATA *pSaveData, const char *pname, void *pBaseData, TYPEDESCRIPTION *pFields, int fieldCount ) +{ + CSave saveHelper( pSaveData ); + saveHelper.WriteFields( pname, pBaseData, pFields, fieldCount ); +} + + +void SaveReadFields( SAVERESTOREDATA *pSaveData, const char *pname, void *pBaseData, TYPEDESCRIPTION *pFields, int fieldCount ) +{ + CRestore restoreHelper( pSaveData ); + restoreHelper.ReadFields( pname, pBaseData, pFields, fieldCount ); +} + + +edict_t * EHANDLE::Get( void ) +{ + if (m_pent) + { + if (m_pent->serialnumber == m_serialnumber) + return m_pent; + else + return NULL; + } + return NULL; +}; + +edict_t * EHANDLE::Set( edict_t *pent ) +{ + m_pent = pent; + if (pent) + m_serialnumber = m_pent->serialnumber; + return pent; +}; + + +EHANDLE :: operator CBaseEntity *() +{ + return (CBaseEntity *)GET_PRIVATE( Get( ) ); +}; + + +CBaseEntity * EHANDLE :: operator = (CBaseEntity *pEntity) +{ + if (pEntity) + { + m_pent = ENT( pEntity->pev ); + if (m_pent) + m_serialnumber = m_pent->serialnumber; + } + else + { + m_pent = NULL; + m_serialnumber = 0; + } + return pEntity; +} + +EHANDLE :: operator int () +{ + return Get() != NULL; +} + +CBaseEntity * EHANDLE :: operator -> () +{ + return (CBaseEntity *)GET_PRIVATE( Get( ) ); +} + + +// give health +int CBaseEntity :: TakeHealth( float flHealth, int bitsDamageType ) +{ + if (!pev->takedamage) + return 0; + +// heal + if ( pev->health >= pev->max_health ) + return 0; + + pev->health += flHealth; + + if (pev->health > pev->max_health) + pev->health = pev->max_health; + + return 1; +} + +// inflict damage on this entity. bitsDamageType indicates type of damage inflicted, ie: DMG_CRUSH + +int CBaseEntity :: TakeDamage( entvars_t* pevInflictor, entvars_t* pevAttacker, float flDamage, int bitsDamageType ) +{ + Vector vecTemp; + + if (!pev->takedamage) + return 0; + + // UNDONE: some entity types may be immune or resistant to some bitsDamageType + + // if Attacker == Inflictor, the attack was a melee or other instant-hit attack. + // (that is, no actual entity projectile was involved in the attack so use the shooter's origin). + if ( pevAttacker == pevInflictor ) + { + vecTemp = pevInflictor->origin - ( VecBModelOrigin(pev) ); + } + else + // an actual missile was involved. + { + vecTemp = pevInflictor->origin - ( VecBModelOrigin(pev) ); + } + +// this global is still used for glass and other non-monster killables, along with decals. + g_vecAttackDir = vecTemp.Normalize(); + +// save damage based on the target's armor level + +// figure momentum add (don't let hurt brushes or other triggers move player) + if ((!FNullEnt(pevInflictor)) && (pev->movetype == MOVETYPE_WALK || pev->movetype == MOVETYPE_STEP) && (pevAttacker->solid != SOLID_TRIGGER) ) + { + Vector vecDir = pev->origin - (pevInflictor->absmin + pevInflictor->absmax) * 0.5; + vecDir = vecDir.Normalize(); + + float flForce = flDamage * ((32 * 32 * 72.0) / (pev->size.x * pev->size.y * pev->size.z)) * 5; + + if (flForce > 1000.0) + flForce = 1000.0; + pev->velocity = pev->velocity + vecDir * flForce; + } + +// do the damage + pev->health -= flDamage; + if (pev->health <= 0) + { + Killed( pevAttacker, GIB_NORMAL ); + return 0; + } + + return 1; +} + + +void CBaseEntity :: Killed( entvars_t *pevAttacker, int iGib ) +{ + pev->takedamage = DAMAGE_NO; + pev->deadflag = DEAD_DEAD; + UTIL_Remove( this ); +} + + +CBaseEntity *CBaseEntity::GetNextTarget( void ) +{ + if ( FStringNull( pev->target ) ) + return NULL; + edict_t *pTarget = FIND_ENTITY_BY_TARGETNAME ( NULL, STRING(pev->target) ); + if ( FNullEnt(pTarget) ) + return NULL; + + return Instance( pTarget ); +} + +// Global Savedata for Delay +TYPEDESCRIPTION CBaseEntity::m_SaveData[] = +{ + DEFINE_FIELD( CBaseEntity, m_pGoalEnt, FIELD_CLASSPTR ), + + DEFINE_FIELD( CBaseEntity, m_pfnThink, FIELD_FUNCTION ), // UNDONE: Build table of these!!! + DEFINE_FIELD( CBaseEntity, m_pfnTouch, FIELD_FUNCTION ), + DEFINE_FIELD( CBaseEntity, m_pfnUse, FIELD_FUNCTION ), + DEFINE_FIELD( CBaseEntity, m_pfnBlocked, FIELD_FUNCTION ), +}; + + +int CBaseEntity::Save( CSave &save ) +{ + if ( save.WriteEntVars( "ENTVARS", pev ) ) + return save.WriteFields( "BASE", this, m_SaveData, ARRAYSIZE(m_SaveData) ); + + return 0; +} + +int CBaseEntity::Restore( CRestore &restore ) +{ + int status; + + status = restore.ReadEntVars( "ENTVARS", pev ); + if ( status ) + status = restore.ReadFields( "BASE", this, m_SaveData, ARRAYSIZE(m_SaveData) ); + + if ( pev->modelindex != 0 && !FStringNull(pev->model) ) + { + Vector mins, maxs; + mins = pev->mins; // Set model is about to destroy these + maxs = pev->maxs; + + + PRECACHE_MODEL( (char *)STRING(pev->model) ); + SET_MODEL(ENT(pev), STRING(pev->model)); + UTIL_SetSize(pev, mins, maxs); // Reset them + } + + return status; +} + + +// Initialize absmin & absmax to the appropriate box +void SetObjectCollisionBox( entvars_t *pev ) +{ + if ( (pev->solid == SOLID_BSP) && + (pev->angles.x || pev->angles.y|| pev->angles.z) ) + { // expand for rotation + float max, v; + int i; + + max = 0; + for (i=0 ; i<3 ; i++) + { + v = fabs( ((float *)pev->mins)[i]); + if (v > max) + max = v; + v = fabs( ((float *)pev->maxs)[i]); + if (v > max) + max = v; + } + for (i=0 ; i<3 ; i++) + { + ((float *)pev->absmin)[i] = ((float *)pev->origin)[i] - max; + ((float *)pev->absmax)[i] = ((float *)pev->origin)[i] + max; + } + } + else + { + pev->absmin = pev->origin + pev->mins; + pev->absmax = pev->origin + pev->maxs; + } + + pev->absmin.x -= 1; + pev->absmin.y -= 1; + pev->absmin.z -= 1; + pev->absmax.x += 1; + pev->absmax.y += 1; + pev->absmax.z += 1; +} + + +void CBaseEntity::SetObjectCollisionBox( void ) +{ + ::SetObjectCollisionBox( pev ); +} + + +int CBaseEntity :: Intersects( CBaseEntity *pOther ) +{ + if ( pOther->pev->absmin.x > pev->absmax.x || + pOther->pev->absmin.y > pev->absmax.y || + pOther->pev->absmin.z > pev->absmax.z || + pOther->pev->absmax.x < pev->absmin.x || + pOther->pev->absmax.y < pev->absmin.y || + pOther->pev->absmax.z < pev->absmin.z ) + return 0; + return 1; +} + +void CBaseEntity :: MakeDormant( void ) +{ + SetBits( pev->flags, FL_DORMANT ); + + // Don't touch + pev->solid = SOLID_NOT; + // Don't move + pev->movetype = MOVETYPE_NONE; + // Don't draw + SetBits( pev->effects, EF_NODRAW ); + // Don't think + pev->nextthink = 0; + // Relink + UTIL_SetOrigin( pev, pev->origin ); +} + +int CBaseEntity :: IsDormant( void ) +{ + return FBitSet( pev->flags, FL_DORMANT ); +} + +BOOL CBaseEntity :: IsInWorld( void ) +{ + // position + if (pev->origin.x >= 4096) return FALSE; + if (pev->origin.y >= 4096) return FALSE; + if (pev->origin.z >= 4096) return FALSE; + if (pev->origin.x <= -4096) return FALSE; + if (pev->origin.y <= -4096) return FALSE; + if (pev->origin.z <= -4096) return FALSE; + // speed + if (pev->velocity.x >= 2000) return FALSE; + if (pev->velocity.y >= 2000) return FALSE; + if (pev->velocity.z >= 2000) return FALSE; + if (pev->velocity.x <= -2000) return FALSE; + if (pev->velocity.y <= -2000) return FALSE; + if (pev->velocity.z <= -2000) return FALSE; + + return TRUE; +} + +int CBaseEntity::ShouldToggle( USE_TYPE useType, BOOL currentState ) +{ + if ( useType != USE_TOGGLE && useType != USE_SET ) + { + if ( (currentState && useType == USE_ON) || (!currentState && useType == USE_OFF) ) + return 0; + } + return 1; +} + + +int CBaseEntity :: DamageDecal( int bitsDamageType ) +{ + if ( pev->rendermode == kRenderTransAlpha ) + return -1; + + if ( pev->rendermode != kRenderNormal ) + return DECAL_BPROOF1; + + return DECAL_GUNSHOT1 + RANDOM_LONG(0,4); +} + + + +// NOTE: szName must be a pointer to constant memory, e.g. "monster_class" because the entity +// will keep a pointer to it after this call. +CBaseEntity * CBaseEntity::Create( char *szName, const Vector &vecOrigin, const Vector &vecAngles, edict_t *pentOwner ) +{ + edict_t *pent; + CBaseEntity *pEntity; + + pent = CREATE_NAMED_ENTITY( MAKE_STRING( szName )); + if ( FNullEnt( pent ) ) + { + ALERT ( at_console, "NULL Ent in Create!\n" ); + return NULL; + } + pEntity = Instance( pent ); + pEntity->pev->owner = pentOwner; + pEntity->pev->origin = vecOrigin; + pEntity->pev->angles = vecAngles; + DispatchSpawn( pEntity->edict() ); + return pEntity; +} + + diff --git a/spirit/cbase.h b/dlls/cbase.h similarity index 70% rename from spirit/cbase.h rename to dlls/cbase.h index d0a7f4cb..cbee60d7 100644 --- a/spirit/cbase.h +++ b/dlls/cbase.h @@ -1,9 +1,9 @@ /*** * * Copyright (c) 1996-2002, Valve LLC. All rights reserved. -* -* This product contains software technology licensed from Id -* Software, Inc. ("Id Technology"). Id Technology (c) 1996 Id Software, Inc. +* +* 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 @@ -17,25 +17,15 @@ Class Hierachy CBaseEntity - CPointEntity - CBasePlayerAmmo CBaseDelay - CBaseAnimating - CBasePlayerItem - CBasePlayerWeapon CBaseToggle - CBaseButton - CBaseDoor - CBaseTrigger - CBasePlatTrain + CBaseItem CBaseMonster - CCycler - CBasePlayer - CCineMonster + CBaseCycler + CBasePlayer + CBaseGroup */ -#include "entity_state.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) @@ -48,15 +38,12 @@ CBaseEntity #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) - // LRC: no longer used -#define FCAP_ONLYDIRECT_USE 0x00000100 //LRC - can't use this entity through a wall. // UNDONE: This will ignore transition volumes (trigger_transition), but not the PVS!!! #define FCAP_FORCE_TRANSITION 0x00000080 // ALWAYS goes across transitions #include "saverestore.h" #include "schedule.h" -#include "studio.h" #ifndef MONSTEREVENT_H #include "monsterevent.h" @@ -74,28 +61,21 @@ extern "C" EXPORT int GetEntityAPI( DLL_FUNCTIONS *pFunctionTable, int interface extern "C" EXPORT int GetEntityAPI2( DLL_FUNCTIONS *pFunctionTable, int *interfaceVersion ); extern int DispatchSpawn( edict_t *pent ); -extern int DispatchCreate( edict_t *pent, const char *szName ); extern void DispatchKeyValue( edict_t *pentKeyvalue, KeyValueData *pkvd ); extern void DispatchTouch( edict_t *pentTouched, edict_t *pentOther ); extern void DispatchUse( edict_t *pentUsed, edict_t *pentOther ); extern void DispatchThink( edict_t *pent ); -extern int DispatchFrame( edict_t *pent ); extern void DispatchBlocked( edict_t *pentBlocked, edict_t *pentOther ); extern void DispatchSave( edict_t *pent, SAVERESTOREDATA *pSaveData ); extern int DispatchRestore( edict_t *pent, SAVERESTOREDATA *pSaveData, int globalEntity ); -extern void DispatchObjectCollsionBox( edict_t *pent ); +extern void DispatchObjectCollsionBox( edict_t *pent ); extern void SaveWriteFields( SAVERESTOREDATA *pSaveData, const char *pname, void *pBaseData, TYPEDESCRIPTION *pFields, int fieldCount ); extern void SaveReadFields( SAVERESTOREDATA *pSaveData, const char *pname, void *pBaseData, TYPEDESCRIPTION *pFields, int fieldCount ); extern void SaveGlobalState( SAVERESTOREDATA *pSaveData ); extern void RestoreGlobalState( SAVERESTOREDATA *pSaveData ); extern void ResetGlobalState( void ); -extern int ServerClassifyEdict( edict_t *pentToClassify ); -extern void OnFreeEntPrivateData( edict_s *pEdict ); -extern int ShouldCollide( edict_t *pentTouched, edict_t *pentOther ); -//extern CBaseEntity *g_pDesiredList; //LRC- handles DesiredVel, for movewith - -extern char* GetStringForUseType( USE_TYPE useType ); +typedef enum { USE_OFF = 0, USE_ON = 1, USE_SET = 2, USE_TOGGLE = 3 } USE_TYPE; extern void FireTargets( const char *targetName, CBaseEntity *pActivator, CBaseEntity *pCaller, USE_TYPE useType, float value ); @@ -118,16 +98,13 @@ typedef void (CBaseEntity::*USEPTR)( CBaseEntity *pActivator, CBaseEntity *pCall #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_FACTION_A 14 //LRC - very simple new classes, for use with Behaves As -#define CLASS_FACTION_B 15 -#define CLASS_FACTION_C 16 #define CLASS_BARNACLE 99 // special because no one pays attention to it, and it eats a wide cross-section of creatures. class CBaseEntity; class CBaseMonster; class CBasePlayerItem; class CSquadMonster; -class CThinker; + #define SF_NORESPAWN ( 1 << 30 )// !!!set this bit on guns and stuff that should never respawn. @@ -155,7 +132,7 @@ public: // // Base Entity. All entity types derive from this // -class CBaseEntity +class CBaseEntity { public: // Constructor. Set engine to use C/C++ callback functions @@ -164,130 +141,35 @@ public: // path corners CBaseEntity *m_pGoalEnt;// path corner we are heading towards - CBaseEntity *m_pLink;// used for temporary link-list operations. - - CBaseEntity *m_pMoveWith; // LRC- the entity I move with. - int m_MoveWith; //LRC- Name of that entity - CBaseEntity *m_pChildMoveWith; //LRC- one of the entities that's moving with me. - CBaseEntity *m_pSiblingMoveWith; //LRC- another entity that's Moving With the same ent as me. (linked list.) - CBaseEntity *m_pAssistLink; // LRC- link to the next entity which needs to be Assisted before physics are applied. - Vector m_vecPostAssistVel; // LRC - Vector m_vecPostAssistAVel; // LRC - Vector m_vecPostAssistOrg; //g-cont. child postorigin - Vector m_vecPostAssistAng; //g-cont. child postangles - - Vector m_vecOffsetOrigin; //spawn offset origin - Vector m_vecOffsetAngles; //spawn offset angles - Vector m_vecParentAngles; //temp container - Vector m_vecParentOrigin; //temp container - - float m_fNextThink; // LRC - for SetNextThink and SetPhysThink. Marks the time when a think will be performed - not necessarily the same as pev->nextthink! - float m_fPevNextThink; // LRC - always set equal to pev->nextthink, so that we can tell when the latter gets changed by the @#$^¬! engine. - int m_iLFlags; // LRC- a new set of flags. (pev->spawnflags and pev->flags are full...) - virtual void DesiredAction( void ) {}; // LRC - for postponing stuff until PostThink time, not as a think. - int m_iStyle; // LRC - almost anything can have a lightstyle these days... - int m_iClassType; // edict classtype - Vector m_vecSpawnOffset; // LRC- To fix things which (for example) MoveWith a door which Starts Open. - BOOL m_activated; // LRC- moved here from func_train. Signifies that an entity has already been - // activated. (and hence doesn't need reactivating.) - - //LRC - decent mechanisms for setting think times! - // this should have been done a long time ago, but MoveWith finally forced me. - virtual void SetNextThink( float delay ) { SetNextThink(delay, FALSE); } - virtual void SetNextThink( float delay, BOOL correctSpeed ); - virtual void AbsoluteNextThink( float time ) { AbsoluteNextThink(time, FALSE); } - virtual void AbsoluteNextThink( float time, BOOL correctSpeed ); - void SetEternalThink( ); - - void DontThink( void ); - virtual void ThinkCorrection( void ); - - //LRC - loci - virtual Vector CalcPosition( CBaseEntity *pLocus ) { return pev->origin; } - virtual Vector CalcVelocity( CBaseEntity *pLocus ) { return pev->velocity; } - virtual float CalcRatio( CBaseEntity *pLocus ) { return 0; } - - virtual void SetObjectClass( int iClassType = ED_SPAWNED ) - { - m_iClassType = iClassType; - } - - //LRC - aliases - virtual BOOL IsAlias( void ) { return FALSE; } + CBaseEntity *m_pLink;// used for temporary link-list operations. // initialization functions virtual void Spawn( void ) { return; } virtual void Precache( void ) { return; } - virtual void KeyValue( KeyValueData* pkvd) - { - //LRC - MoveWith for all! - if (FStrEq(pkvd->szKeyName, "movewith")) - { - m_MoveWith = ALLOC_STRING(pkvd->szValue); - pkvd->fHandled = TRUE; - } - else if (FStrEq(pkvd->szKeyName, "style")) - { - m_iStyle = atoi(pkvd->szValue); - pkvd->fHandled = TRUE; - } - else pkvd->fHandled = FALSE; - } + virtual void KeyValue( KeyValueData* pkvd) { pkvd->fHandled = FALSE; } virtual int Save( CSave &save ); virtual int Restore( CRestore &restore ); - //LRC - if I MoveWith something, then only cross transitions if the MoveWith entity does too. - virtual int ObjectCaps( void ) { return m_pMoveWith?m_pMoveWith->ObjectCaps()&FCAP_ACROSS_TRANSITION:FCAP_ACROSS_TRANSITION; } - virtual void Activate( void ); //LRC - void InitMoveWith( void ); //LRC - called by Activate() to set up moveWith values - void SetParent( int m_iNewParent, int m_iAttachment = 0);//g-cont. two version of SetParent. from xash 0.4 - void SetParent( CBaseEntity *pParent, int m_iAttachment = 0 );//g-cont. dynamiclly link parents - void ResetParent( void ); - void ClearPointers( void ); //g-cont. directly clear all movewith pointer before changelevel - virtual void PostSpawn( void ) {} //LRC - called by Activate() to handle entity-specific initialisation. - // (mostly setting positions, for MoveWith support) - + 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 ); - void UTIL_AutoSetSize( void )//automatically set collision box - { - studiohdr_t *pstudiohdr; - pstudiohdr = (studiohdr_t*)GET_MODEL_PTR( ENT(pev) ); - - if (pstudiohdr == NULL) - { - ALERT(at_console,"Unable to fetch model pointer!\n"); - return; - } - mstudioseqdesc_t *pseqdesc; - pseqdesc = (mstudioseqdesc_t *)((byte *)pstudiohdr + pstudiohdr->seqindex); - UTIL_SetSize(pev,pseqdesc[ pev->sequence ].bbmin,pseqdesc[ pev->sequence ].bbmax); - } - -// Classify - returns the type of group (e.g., "alien monster", or "human military" so that monsters -// on the same side won't attack each other, even if they have different classnames. +// 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. -// LRC- this supports a global concept of "entities with states", so that state_watchers and -// mastership (mastery? masterhood?) can work universally. - virtual STATE GetState ( void ) { return STATE_OFF; }; - -// For team-specific doors in multiplayer, etc: a master's state depends on who wants to know. - virtual STATE GetState ( CBaseEntity* pEnt ) { return GetState(); }; - static TYPEDESCRIPTION m_SaveData[]; 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 int TakeArmor( float flArmor ); 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 ); -//LRC- superceded by GetState ( pActivator ). -// virtual BOOL IsTriggered( CBaseEntity *pActivator ) {return TRUE;} + virtual BOOL IsTriggered( CBaseEntity *pActivator ) {return TRUE;} virtual CBaseMonster *MyMonsterPointer( void ) { return NULL;} virtual CSquadMonster *MySquadMonsterPointer( void ) { return NULL;} virtual int GetToggleState( void ) { return TS_AT_TOP; } @@ -318,7 +200,7 @@ public: // virtual void SetActivator( CBaseEntity *pActivator ) {} virtual CBaseEntity *GetNextTarget( void ); - + // fundamental callbacks void (CBaseEntity ::*m_pfnThink)(void); void (CBaseEntity ::*m_pfnTouch)( CBaseEntity *pOther ); @@ -327,9 +209,9 @@ public: virtual void Think( void ) { if (m_pfnThink) (this->*m_pfnThink)(); }; virtual void Touch( CBaseEntity *pOther ) { if (m_pfnTouch) (this->*m_pfnTouch)( pOther ); }; - virtual void Use( CBaseEntity *pActivator, CBaseEntity *pCaller, USE_TYPE useType, float value ) - { - if (m_pfnUse) + virtual void Use( CBaseEntity *pActivator, CBaseEntity *pCaller, USE_TYPE useType, float value ) + { + if (m_pfnUse) (this->*m_pfnUse)( pActivator, pCaller, useType, value ); } virtual void Blocked( CBaseEntity *pOther ) { if (m_pfnBlocked) (this->*m_pfnBlocked)( pOther ); }; @@ -348,17 +230,15 @@ public: }; #endif - virtual void UpdateOnRemove( void ); + 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 ) // a think function used at spawn time. Don't apply the moveWith fix to it. - { this->Use( this, this, USE_TOGGLE, 0 ); } + void EXPORT SUB_CallUseToggle( void ) { this->Use( this, this, USE_TOGGLE, 0 ); } int ShouldToggle( USE_TYPE useType, BOOL currentState ); - int ShouldToggle( USE_TYPE useType ); //LRC this version uses GetState() 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 ); @@ -371,16 +251,6 @@ public: int IsDormant( void ); BOOL IsLockedByMaster( void ) { return FALSE; } -#ifdef _DEBUG - static CBaseEntity *Instance( edict_t *pent ) - { - if ( !pent ) - pent = ENT(0); - CBaseEntity *pEnt = (CBaseEntity *)GET_PRIVATE(pent); - ASSERT(pEnt!=NULL); - return pEnt; - } -#else static CBaseEntity *Instance( edict_t *pent ) { if ( !pent ) @@ -388,20 +258,19 @@ public: CBaseEntity *pEnt = (CBaseEntity *)GET_PRIVATE(pent); return pEnt; } -#endif static CBaseEntity *Instance( entvars_t *pev ) { return Instance( ENT( pev ) ); } static CBaseEntity *Instance( int eoffset) { return Instance( ENT( eoffset) ); } - CBaseMonster *GetMonsterPointer( entvars_t *pevMonster ) - { + CBaseMonster *GetMonsterPointer( entvars_t *pevMonster ) + { CBaseEntity *pEntity = Instance( pevMonster ); if ( pEntity ) return pEntity->MyMonsterPointer(); return NULL; } - CBaseMonster *GetMonsterPointer( edict_t *pentMonster ) - { + CBaseMonster *GetMonsterPointer( edict_t *pentMonster ) + { CBaseEntity *pEntity = Instance( pentMonster ); if ( pEntity ) return pEntity->MyMonsterPointer(); @@ -411,34 +280,34 @@ public: // Ugly code to lookup all functions to make sure they are exported when set. #ifdef _DEBUG - void FunctionCheck( void *pFunction, char *name ) - { + void FunctionCheck( void *pFunction, char *name ) + { if (pFunction && !NAME_FOR_FUNCTION((unsigned long)(pFunction)) ) ALERT( at_error, "No EXPORT: %s:%s (%08lx)\n", STRING(pev->classname), name, (unsigned long)pFunction ); } - BASEPTR ThinkSet( BASEPTR func, char *name ) - { - m_pfnThink = func; - FunctionCheck( (void *)*((int *)((char *)this + ( offsetof(CBaseEntity,m_pfnThink)))), name ); + BASEPTR ThinkSet( BASEPTR func, char *name ) + { + m_pfnThink = func; + FunctionCheck( (void *)*((int *)((char *)this + ( offsetof(CBaseEntity,m_pfnThink)))), name ); return func; } - ENTITYFUNCPTR TouchSet( ENTITYFUNCPTR func, char *name ) - { - m_pfnTouch = func; - FunctionCheck( (void *)*((int *)((char *)this + ( offsetof(CBaseEntity,m_pfnTouch)))), name ); + ENTITYFUNCPTR TouchSet( ENTITYFUNCPTR func, char *name ) + { + m_pfnTouch = func; + FunctionCheck( (void *)*((int *)((char *)this + ( offsetof(CBaseEntity,m_pfnTouch)))), name ); return func; } - USEPTR UseSet( USEPTR func, char *name ) - { - m_pfnUse = func; - FunctionCheck( (void *)*((int *)((char *)this + ( offsetof(CBaseEntity,m_pfnUse)))), name ); + USEPTR UseSet( USEPTR func, char *name ) + { + m_pfnUse = func; + FunctionCheck( (void *)*((int *)((char *)this + ( offsetof(CBaseEntity,m_pfnUse)))), name ); return func; } - ENTITYFUNCPTR BlockedSet( ENTITYFUNCPTR func, char *name ) - { - m_pfnBlocked = func; - FunctionCheck( (void *)*((int *)((char *)this + ( offsetof(CBaseEntity,m_pfnBlocked)))), name ); + ENTITYFUNCPTR BlockedSet( ENTITYFUNCPTR func, char *name ) + { + m_pfnBlocked = func; + FunctionCheck( (void *)*((int *)((char *)this + ( offsetof(CBaseEntity,m_pfnBlocked)))), name ); return func; } @@ -446,7 +315,7 @@ public: // virtual functions used by a few classes - + // used by monsters that are created by the MonsterMaker virtual void UpdateOwner( void ) { return; }; @@ -488,11 +357,10 @@ public: int m_fireState; }; -//LRC- moved here from player.cpp. I'd put it in util.h with its friends, but it needs CBaseEntity to be declared. -inline BOOL FNullEnt( CBaseEntity *ent ) { return ent == NULL || FNullEnt( ent->edict() ); } + // 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 +// 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. #ifdef _DEBUG @@ -552,7 +420,8 @@ public: void Spawn( ); void KeyValue( KeyValueData *pkvd ); void Use( CBaseEntity *pActivator, CBaseEntity *pCaller, USE_TYPE useType, float value ); - STATE GetState( void ); + int ObjectCaps( void ) { return (CPointEntity::ObjectCaps() | FCAP_MASTER); } + BOOL IsTriggered( CBaseEntity *pActivator ); void EXPORT Register( void ); virtual int Save( CSave &save ); virtual int Restore( CRestore &restore ); @@ -575,12 +444,11 @@ class CBaseDelay : public CBaseEntity public: float m_flDelay; int m_iszKillTarget; - EHANDLE m_hActivator; //LRC - moved here from CBaseToggle virtual void KeyValue( KeyValueData* pkvd); virtual int Save( CSave &save ); virtual int Restore( CRestore &restore ); - + static TYPEDESCRIPTION m_SaveData[]; // common member functions void SUB_UseTargets( CBaseEntity *pActivator, USE_TYPE useType, float value ); @@ -614,11 +482,6 @@ public: void GetAttachment ( int iAttachment, Vector &origin, Vector &angles ); void SetBodygroup( int iGroup, int iValue ); int GetBodygroup( int iGroup ); - - //LRC - int GetBoneCount( void ); - void SetBones( float (*data)[3], int datasize ); - int ExtractBbox( int sequence, Vector &mins, Vector &maxs ); void SetSequenceBox( void ); @@ -634,7 +497,7 @@ public: // // generic Toggle entity. // -#define SF_ITEM_USE_ONLY 256 // ITEM_USE_ONLY = BUTTON_USE_ONLY = DOOR_USE_ONLY!!! +#define SF_ITEM_USE_ONLY 256 // ITEM_USE_ONLY = BUTTON_USE_ONLY = DOOR_USE_ONLY!!! class CBaseToggle : public CBaseAnimating { @@ -656,11 +519,9 @@ public: int m_cTriggersLeft; // trigger_counter only, # of activations remaining float m_flHeight; + EHANDLE m_hActivator; void (CBaseToggle::*m_pfnCallWhenMoveDone)(void); Vector m_vecFinalDest; - float m_flLinearMoveSpeed; // LRC- allows a LinearMove to be delayed until a think. - float m_flAngularMoveSpeed; // LRC - Vector m_vecFinalAngle; int m_bitsDamageInflict; // DMG_ damage type that the door or tigger does @@ -671,22 +532,13 @@ public: static TYPEDESCRIPTION m_SaveData[]; virtual int GetToggleState( void ) { return m_toggle_state; } - - // LRC- overridden because toggling entities have general rules governing their states. - virtual STATE GetState( void ); - virtual float GetDelay( void ) { return m_flWait; } // common member functions - void LinearMove( Vector vecInput, float flSpeed ); - void EXPORT LinearMoveNow( void ); //LRC- think function that lets us guarantee a LinearMove gets done as a think. + void LinearMove( Vector vecDest, float flSpeed ); void EXPORT LinearMoveDone( void ); - void EXPORT LinearMoveDoneNow( void ); //LRC -// void EXPORT LinearMoveFinalDone( void ); void AngularMove( Vector vecDestAngle, float flSpeed ); - void EXPORT AngularMoveNow( void ); //LRC- think function that lets us guarantee an AngularMove gets done as a think. void EXPORT AngularMoveDone( void ); - void EXPORT AngularMoveDoneNow( void ); BOOL IsLockedByMaster( void ); static float AxisValue( int flags, const Vector &angles ); @@ -694,7 +546,7 @@ public: 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 + // 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. @@ -744,7 +596,7 @@ public: #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_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 @@ -791,7 +643,7 @@ public: #define SLOWFREEZE_DAMAGE 1.0 -#define itbd_Paralyze 0 +#define itbd_Paralyze 0 #define itbd_NerveGas 1 #define itbd_Poison 2 #define itbd_Radiation 3 @@ -801,7 +653,7 @@ public: #define itbd_SlowFreeze 7 #define CDMG_TIMEBASED 8 -// when calling KILLED(), a value that governs gib behavior is expected to be +// 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 ) @@ -824,7 +676,6 @@ class CBaseButton : public CBaseToggle { public: void Spawn( void ); - virtual void PostSpawn( void ); //LRC virtual void Precache( void ); void RotSpawn( void ); virtual void KeyValue( KeyValueData* pkvd); @@ -838,17 +689,17 @@ public: void EXPORT TriggerAndWait( void ); void EXPORT ButtonReturn( void ); void EXPORT ButtonBackHome( void ); - void EXPORT ButtonUse_IgnorePlayer( CBaseEntity *pActivator, CBaseEntity *pCaller, USE_TYPE useType, float value ); void EXPORT ButtonUse ( CBaseEntity *pActivator, CBaseEntity *pCaller, USE_TYPE useType, float value ); virtual int TakeDamage( entvars_t* pevInflictor, entvars_t* pevAttacker, float flDamage, int bitsDamageType ); virtual int Save( CSave &save ); virtual int Restore( CRestore &restore ); - + enum BUTTON_CODE { BUTTON_NOTHING, BUTTON_ACTIVATE, BUTTON_RETURN }; BUTTON_CODE ButtonResponseToTouch( void ); - + static TYPEDESCRIPTION m_SaveData[]; - virtual int ObjectCaps( void ); + // Buttons that don't take damage can be IMPULSE used + virtual int ObjectCaps( void ) { return (CBaseToggle:: ObjectCaps() & ~FCAP_ACROSS_TRANSITION) | (pev->takedamage?0:FCAP_IMPULSE_USE); } BOOL m_fStayPushed; // button stays pushed in until touched again? BOOL m_fRotating; // a rotating button? default is a sliding button. @@ -858,16 +709,16 @@ public: // to the button's ChangeTarget. This allows you to make a func_train switch paths, etc. locksound_t m_ls; // door lock sounds - + BYTE m_bLockedSound; // ordinals from entity selection - BYTE m_bLockedSentence; - BYTE m_bUnlockedSound; + BYTE m_bLockedSentence; + BYTE m_bUnlockedSound; BYTE m_bUnlockedSentence; int m_sounds; }; // -// Weapons +// Weapons // #define BAD_WEAPON 0x00007FFF @@ -887,9 +738,9 @@ template T * GetClassPtr( T *a ) // get the private data a = (T *)GET_PRIVATE(ENT(pev)); - if (a == NULL) + if (a == NULL) { - // allocate private data + // allocate private data a = new(pev) T; a->pev = pev; } @@ -930,63 +781,6 @@ typedef struct _SelAmmo BYTE Ammo2; } SelAmmo; -//LRC- much as I hate to add new globals, I can't see how to read data from the World entity. -extern BOOL g_startSuit; - -//LRC- moved here from alias.cpp so that util functions can use these defs. -class CBaseAlias : public CPointEntity -{ -public: - BOOL IsAlias( void ) { return TRUE; }; - virtual CBaseEntity *FollowAlias( CBaseEntity *pFrom ) { return NULL; }; - virtual void ChangeValue( int iszValue ) { ALERT(at_error, "%s entities cannot change value!", STRING(pev->classname)); } - virtual void ChangeValue( CBaseEntity *pValue ) { ChangeValue(pValue->pev->targetname); } - virtual void FlushChanges( void ) {}; - - virtual int Save( CSave &save ); - virtual int Restore( CRestore &restore ); - static TYPEDESCRIPTION m_SaveData[]; - - CBaseAlias *m_pNextAlias; -}; - -class CInfoGroup : public CPointEntity -{ -public: - void KeyValue( KeyValueData *pkvd ); - void Use(CBaseEntity* pActivator, CBaseEntity* pCaller, USE_TYPE useType, float value); - int GetMember( const char* szMemberName ); - - virtual int Save( CSave &save ); - virtual int Restore( CRestore &restore ); - - static TYPEDESCRIPTION m_SaveData[]; - - int m_cMembers; - int m_iszMemberName [ MAX_MULTI_TARGETS ]; - int m_iszMemberValue [ MAX_MULTI_TARGETS ]; - int m_iszDefaultMember; -}; - -class CMultiAlias : public CBaseAlias -{ -public: - void KeyValue( KeyValueData *pkvd ); - - virtual int Save( CSave &save ); - virtual int Restore( CRestore &restore ); - - static TYPEDESCRIPTION m_SaveData[]; - - CBaseEntity *FollowAlias( CBaseEntity *pFrom ); - - int m_cTargets; - int m_iszTargets [ MAX_MULTI_TARGETS ]; - int m_iTotalValue; - int m_iValues [ MAX_MULTI_TARGETS ]; - int m_iMode; -}; - // this moved here from world.cpp, to allow classes to be derived from it //======================= @@ -1000,8 +794,4 @@ public: void Spawn( void ); void Precache( void ); void KeyValue( KeyValueData *pkvd ); - - CBaseAlias *m_pFirstAlias; }; - -extern CWorld *g_pWorld; diff --git a/spirit/cdll_dll.h b/dlls/cdll_dll.h similarity index 64% rename from spirit/cdll_dll.h rename to dlls/cdll_dll.h index 79dfbd4f..2293a27e 100644 --- a/spirit/cdll_dll.h +++ b/dlls/cdll_dll.h @@ -20,10 +20,27 @@ #ifndef CDLL_DLL_H #define CDLL_DLL_H +#define MAX_WEAPONS 32 // ??? + #define MAX_WEAPON_SLOTS 5 // hud item selection slots #define MAX_ITEM_TYPES 6 // hud item selection slots + #define MAX_ITEMS 5 // hard coded item types -#define HIDEHUD_CROSSHAIR ( 1<<4 ) //LRC - probably not the right way to do this, but it's just an experiment. +#define HIDEHUD_WEAPONS ( 1<<0 ) +#define HIDEHUD_FLASHLIGHT ( 1<<1 ) +#define HIDEHUD_ALL ( 1<<2 ) +#define HIDEHUD_HEALTH ( 1<<3 ) -#endif +#define MAX_AMMO_TYPES 32 // ??? +#define MAX_AMMO_SLOTS 32 // not really slots + +#define HUD_PRINTNOTIFY 1 +#define HUD_PRINTCONSOLE 2 +#define HUD_PRINTTALK 3 +#define HUD_PRINTCENTER 4 + + +#define WEAPON_SUIT 31 + +#endif \ No newline at end of file diff --git a/spirit/client.cpp b/dlls/client.cpp similarity index 62% rename from spirit/client.cpp rename to dlls/client.cpp index e7d967ae..77eb3d86 100644 --- a/spirit/client.cpp +++ b/dlls/client.cpp @@ -1,9 +1,9 @@ /*** * * Copyright (c) 1996-2002, Valve LLC. All rights reserved. -* -* This product contains software technology licensed from Id -* Software, Inc. ("Id Technology"). Id Technology (c) 1996 Id Software, Inc. +* +* 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 @@ -12,7 +12,7 @@ * without written permission from Valve LLC. * ****/ -// Robin, 4-22-98: Moved set_suicide_frame() here from player.cpp to allow us to +// Robin, 4-22-98: Moved set_suicide_frame() here from player.cpp to allow us to // have one without a hardcoded player.mdl in tf_client.cpp /* @@ -23,8 +23,6 @@ */ -#include - #include "extdll.h" #include "util.h" #include "cbase.h" @@ -35,10 +33,11 @@ #include "soundent.h" #include "gamerules.h" #include "game.h" +#include "customentity.h" #include "weapons.h" #include "weaponinfo.h" #include "usercmd.h" -#include "movewith.h" +#include "netadr.h" extern DLL_GLOBAL ULONG g_ulModelIndexPlayer; extern DLL_GLOBAL BOOL g_fGameOver; @@ -48,20 +47,17 @@ extern DLL_GLOBAL ULONG g_ulFrameCount; extern void CopyToBodyQue(entvars_t* pev); extern int giPrecacheGrunt; extern int gmsgSayText; -extern int gmsgHUDColor; -extern int gmsgCamData; // for trigger_viewset extern int g_teamplay; -DLL_GLOBAL int g_serveractive = 0; + void LinkUserMessages( void ); -char text[256]; /* * used by kill command and disconnect command * ROBIN: Moved here from player.cpp, to allow multiple player models */ void set_suicide_frame(entvars_t* pev) -{ +{ if (!FStrEq(STRING(pev->model), "models/player.mdl")) return; // allready gibbed @@ -80,8 +76,8 @@ ClientConnect called when a player connects to a server ============ */ -BOOL ClientConnect( edict_t *pEntity, const char *pszName, const char *pszAddress, char szRejectReason[128] ) -{ +BOOL ClientConnect( edict_t *pEntity, const char *pszName, const char *pszAddress, char szRejectReason[ 128 ] ) +{ return g_pGameRules->ClientConnected( pEntity, pszName, pszAddress, szRejectReason ); // a client connecting during an intermission can cause problems @@ -115,7 +111,7 @@ void ClientDisconnect( edict_t *pEntity ) CSound *pSound; pSound = CSoundEnt::SoundPointerForIndex( CSoundEnt::ClientSoundIndex( pEntity ) ); { - // since this client isn't around to think anymore, reset their sound. + // since this client isn't around to think anymore, reset their sound. if ( pSound ) { pSound->Reset(); @@ -125,7 +121,7 @@ void ClientDisconnect( edict_t *pEntity ) // since the edict doesn't get deleted, fix it so it doesn't interfere. pEntity->v.takedamage = DAMAGE_NO;// don't attract autoaim pEntity->v.solid = SOLID_NOT;// nonsolid - UTIL_SetEdictOrigin ( pEntity, pEntity->v.origin ); + UTIL_SetOrigin ( &pEntity->v, pEntity->v.origin ); g_pGameRules->ClientDisconnected( pEntity ); } @@ -134,7 +130,7 @@ void ClientDisconnect( edict_t *pEntity ) // called by ClientKill and DeadThink void respawn(entvars_t* pev, BOOL fCopyCorpse) { - if (gpGlobals->teamplay || gpGlobals->coop || gpGlobals->deathmatch) + if (gpGlobals->coop || gpGlobals->deathmatch) { if ( fCopyCorpse ) { @@ -201,7 +197,6 @@ void ClientPutInServer( edict_t *pEntity ) // Reset interpolation during first frame pPlayer->pev->effects |= EF_NOINTERP; - pPlayer->m_flViewHeight = pPlayer->pev->view_ofs.z; // keep viewheight an actual } //// HOST_SAY @@ -266,8 +261,7 @@ void Host_Say( edict_t *pEntity, int teamonly ) } // make sure the text has content - char *pc; - for ( pc = p; pc != NULL && *pc != 0; pc++ ) + for ( char *pc = p; pc != NULL && *pc != 0; pc++ ) { if ( isprint( *pc ) && !isspace( *pc ) ) { @@ -300,11 +294,11 @@ void Host_Say( edict_t *pEntity, int teamonly ) // so check it, or it will infinite loop client = NULL; - while ( ((client = (CBasePlayer*)UTIL_FindEntityByClassname( client, "player" )) != NULL) && (!FNullEnt(client->edict())) ) + while ( ((client = (CBasePlayer*)UTIL_FindEntityByClassname( client, "player" )) != NULL) && (!FNullEnt(client->edict())) ) { if ( !client->pev ) continue; - + if ( client->edict() == pEntity ) continue; @@ -335,12 +329,12 @@ void Host_Say( edict_t *pEntity, int teamonly ) temp = "say_team"; else temp = "say"; - + // team match? if ( g_teamplay ) { - UTIL_LogPrintf( "\"%s<%i><%s><%s>\" %s \"%s\"\n", - STRING( pEntity->v.netname ), + UTIL_LogPrintf( "\"%s<%i><%s><%s>\" %s \"%s\"\n", + STRING( pEntity->v.netname ), GETPLAYERUSERID( pEntity ), GETPLAYERAUTHID( pEntity ), g_engfuncs.pfnInfoKeyValue( g_engfuncs.pfnGetInfoKeyBuffer( pEntity ), "model" ), @@ -349,8 +343,8 @@ void Host_Say( edict_t *pEntity, int teamonly ) } else { - UTIL_LogPrintf( "\"%s<%i><%s><%i>\" %s \"%s\"\n", - STRING( pEntity->v.netname ), + UTIL_LogPrintf( "\"%s<%i><%s><%i>\" %s \"%s\"\n", + STRING( pEntity->v.netname ), GETPLAYERUSERID( pEntity ), GETPLAYERAUTHID( pEntity ), GETPLAYERUSERID( pEntity ), @@ -367,7 +361,6 @@ called each time a player uses a "cmd" command ============ */ extern float g_flWeaponCheat; -extern int gmsgPlayMP3; //AJH - Killars MP3player // Use CMD_ARGV, CMD_ARGV, and CMD_ARGC to get pointers the character string command. void ClientCommand( edict_t *pEntity ) @@ -381,11 +374,31 @@ void ClientCommand( edict_t *pEntity ) entvars_t *pev = &pEntity->v; - if ( FStrEq( pcmd, "noclip" )) + if ( FStrEq(pcmd, "say" ) ) { - if (g_flWeaponCheat) + Host_Say( pEntity, 0 ); + } + else if ( FStrEq(pcmd, "say_team" ) ) + { + Host_Say( pEntity, 1 ); + } + else if ( FStrEq(pcmd, "fullupdate" ) ) + { + GetClassPtr((CBasePlayer *)pev)->ForceClientDllUpdate(); + } + else if ( FStrEq(pcmd, "give" ) ) + { + if ( g_flWeaponCheat != 0.0) { - if( pEntity->v.movetype == MOVETYPE_WALK ) + int iszItem = ALLOC_STRING( CMD_ARGV(1) ); // Make a copy of the classname + GetClassPtr((CBasePlayer *)pev)->GiveNamedItem( STRING(iszItem) ); + } + } + else if ( FStrEq( pcmd, "noclip" )) + { + if ( g_flWeaponCheat != 0.0) + { + if( pEntity->v.movetype != MOVETYPE_NOCLIP ) { pEntity->v.movetype = MOVETYPE_NOCLIP; ClientPrint( &pEntity->v, HUD_PRINTCONSOLE, "noclip on\n" ); @@ -399,7 +412,7 @@ void ClientCommand( edict_t *pEntity ) } else if ( FStrEq( pcmd, "god" )) { - if (g_flWeaponCheat) + if ( g_flWeaponCheat != 0.0) { pEntity->v.flags = pEntity->v.flags ^ FL_GODMODE; @@ -408,9 +421,9 @@ void ClientCommand( edict_t *pEntity ) else ClientPrint( &pEntity->v, HUD_PRINTCONSOLE, "godmode ON\n" ); } } - else if( FStrEq( pcmd, "notarget" )) + else if ( FStrEq( pcmd, "notarget" )) { - if (g_flWeaponCheat) + if ( g_flWeaponCheat != 0.0) { pEntity->v.flags = pEntity->v.flags ^ FL_NOTARGET; @@ -419,25 +432,9 @@ void ClientCommand( edict_t *pEntity ) else ClientPrint( &pEntity->v, HUD_PRINTCONSOLE, "notarget ON\n" ); } } - else if ( FStrEq(pcmd, "hud_color") ) //LRC + else if ( FStrEq(pcmd, "fire" )) { - if (CMD_ARGC() == 4) - { - int col = (atoi(CMD_ARGV(1)) & 255) << 16; - col += (atoi(CMD_ARGV(2)) & 255) << 8; - col += (atoi(CMD_ARGV(3)) & 255); - MESSAGE_BEGIN( MSG_ONE, gmsgHUDColor, NULL, &pEntity->v ); - WRITE_LONG(col); - MESSAGE_END(); - } - else - { - ALERT(at_console, "Syntax: hud_color RRR GGG BBB\n"); - } - } - else if ( FStrEq(pcmd, "fire") ) //LRC - trigger entities manually - { - if (g_flWeaponCheat) + if ( g_flWeaponCheat != 0.0) { CBaseEntity *pPlayer = CBaseEntity::Instance(pEntity); if (CMD_ARGC() > 1) @@ -450,7 +447,7 @@ void ClientCommand( edict_t *pEntity ) UTIL_MakeVectors(pev->v_angle); UTIL_TraceLine( pev->origin + pev->view_ofs, - pev->origin + pev->view_ofs + gpGlobals->v_forward * 1000, + pev->origin + pev->view_ofs + gpGlobals->v_forward * 1024, dont_ignore_monsters, pEntity, &tr ); @@ -466,64 +463,27 @@ void ClientCommand( edict_t *pEntity ) } } } - else if ( FStrEq(pcmd, "say" ) ) - { - Host_Say( pEntity, 0 ); - } - else if ( FStrEq(pcmd, "say_team" ) ) - { - Host_Say( pEntity, 1 ); - } - else if ( FStrEq(pcmd, "game_over" ) ) - { - if( IsMultiplayer( )) - g_pGameRules->EndMultiplayerGame(); // loading next map - else g_engfuncs.pfnEndSection( CMD_ARGV( 1 ) ); - } - else if( FStrEq( pcmd, "gametitle" )) - { - MESSAGE_BEGIN( MSG_ONE, gmsgShowGameTitle, NULL, ENT( pev )); - MESSAGE_END(); - } - else if( FStrEq( pcmd, "intermission" )) - { - MESSAGE_BEGIN( MSG_ONE, gmsgIntermission, NULL, ENT( pev )); - MESSAGE_END(); - } - else if ( FStrEq(pcmd, "fullupdate" ) ) - { - GetClassPtr((CBasePlayer *)pev)->ForceClientDllUpdate(); - } - else if ( FStrEq(pcmd, "give" ) ) - { - if ( g_flWeaponCheat != 0.0) - { - int iszItem = ALLOC_STRING( CMD_ARGV(1) ); // Make a copy of the classname - GetClassPtr((CBasePlayer *)pev)->GiveNamedItem( STRING(iszItem) ); - } - } - else if ( FStrEq(pcmd, "drop" ) ) { - // player is dropping an item. + // player is dropping an item. GetClassPtr((CBasePlayer *)pev)->DropPlayerItem((char *)CMD_ARGV(1)); } else if ( FStrEq(pcmd, "fov" ) ) { if ( g_flWeaponCheat && CMD_ARGC() > 1) { - GetClassPtr((CBasePlayer *)pev)->pev->fov = atof( CMD_ARGV(1) ); + GetClassPtr((CBasePlayer *)pev)->m_iFOV = atoi( CMD_ARGV(1) ); } else { - CLIENT_PRINTF( pEntity, print_console, UTIL_VarArgs( "\"fov\" is \"%d\"\n", (int)GetClassPtr((CBasePlayer *)pev)->pev->fov ) ); + CLIENT_PRINTF( pEntity, print_console, UTIL_VarArgs( "\"fov\" is \"%d\"\n", (int)GetClassPtr((CBasePlayer *)pev)->m_iFOV ) ); } } else if ( FStrEq(pcmd, "use" ) ) { GetClassPtr((CBasePlayer *)pev)->SelectItem((char *)CMD_ARGV(1)); } - else if (((pstr = strstr(pcmd, "weapon_")) != NULL) && (pstr == pcmd)) + else if (((pstr = strstr(pcmd, "weapon_")) != NULL) && (pstr == pcmd)) { GetClassPtr((CBasePlayer *)pev)->SelectItem(pcmd); } @@ -602,20 +562,20 @@ void ClientUserInfoChanged( edict_t *pEntity, char *infobuffer ) // team match? if ( g_teamplay ) { - UTIL_LogPrintf( "\"%s<%i><%s><%s>\" changed name to \"%s\"\n", - STRING( pEntity->v.netname ), - GETPLAYERUSERID( pEntity ), + UTIL_LogPrintf( "\"%s<%i><%s><%s>\" changed name to \"%s\"\n", + STRING( pEntity->v.netname ), + GETPLAYERUSERID( pEntity ), GETPLAYERAUTHID( pEntity ), - g_engfuncs.pfnInfoKeyValue( infobuffer, "model" ), + g_engfuncs.pfnInfoKeyValue( infobuffer, "model" ), g_engfuncs.pfnInfoKeyValue( infobuffer, "name" ) ); } else { - UTIL_LogPrintf( "\"%s<%i><%s><%i>\" changed name to \"%s\"\n", - STRING( pEntity->v.netname ), - GETPLAYERUSERID( pEntity ), + UTIL_LogPrintf( "\"%s<%i><%s><%i>\" changed name to \"%s\"\n", + STRING( pEntity->v.netname ), + GETPLAYERUSERID( pEntity ), GETPLAYERAUTHID( pEntity ), - GETPLAYERUSERID( pEntity ), + GETPLAYERUSERID( pEntity ), g_engfuncs.pfnInfoKeyValue( infobuffer, "name" ) ); } } @@ -623,13 +583,12 @@ void ClientUserInfoChanged( edict_t *pEntity, char *infobuffer ) g_pGameRules->ClientUserInfoChanged( GetClassPtr((CBasePlayer *)&pEntity->v), infobuffer ); } +static int g_serveractive = 0; + void ServerDeactivate( void ) { - // make sure they reinitialise the World in the next server - g_pWorld = NULL; - // It's possible that the engine will call this function more times than is necessary - // Therefore, only run it one time for each call to ServerActivate + // Therefore, only run it one time for each call to ServerActivate if ( g_serveractive != 1 ) { return; @@ -654,7 +613,7 @@ void ServerActivate( edict_t *pEdictList, int edictCount, int clientMax ) { if ( pEdictList[i].free ) continue; - + // Clients aren't necessarily initialized until ClientPutInServer() if ( i < clientMax || !pEdictList[i].pvPrivateData ) continue; @@ -667,7 +626,7 @@ void ServerActivate( edict_t *pEdictList, int edictCount, int clientMax ) } else { - ALERT( at_debug, "Can't instance %s\n", STRING(pEdictList[i].v.classname) ); + ALERT( at_console, "Can't instance %s\n", STRING(pEdictList[i].v.classname) ); } } @@ -675,9 +634,6 @@ void ServerActivate( edict_t *pEdictList, int edictCount, int clientMax ) LinkUserMessages(); } -// a cached version of gpGlobals->frametime. The engine sets frametime to 0 if the player is frozen... so we just cache it in prethink, -// allowing it to be restored later and used by CheckDesiredList. -float cached_frametime = 0.0f; /* ================ @@ -693,8 +649,6 @@ void PlayerPreThink( edict_t *pEntity ) if (pPlayer) pPlayer->PreThink( ); - - cached_frametime = gpGlobals->frametime; } /* @@ -711,16 +665,16 @@ void PlayerPostThink( edict_t *pEntity ) if (pPlayer) pPlayer->PostThink( ); - - // use the old frametime, even if the engine has reset it - gpGlobals->frametime = cached_frametime; - - //LRC - moved to here from CBasePlayer::PostThink, so that - // things don't stop when the player dies - CheckDesiredList( ); } -void BuildLevelList( void ) + + +void ParmsNewLevel( void ) +{ +} + + +void ParmsChangeLevel( void ) { // retrieve the pointer to the save data SAVERESTOREDATA *pSaveData = (SAVERESTOREDATA *)gpGlobals->pSaveData; @@ -743,46 +697,19 @@ void StartFrame( void ) gpGlobals->teamplay = teamplay->value; g_ulFrameCount++; - - if( g_psv_maxspeed->modified ) - { - char msg[64]; - - // maxspeed is modified, refresh maxspeed for each client - for( int i = 0; i < gpGlobals->maxClients; i++ ) - { - edict_t *pClientEdict = INDEXENT( i + 1 ); - - if( pClientEdict == NULL || pClientEdict->free ) - continue; - - // can update even if client it's not active - g_engfuncs.pfnSetClientMaxspeed( pClientEdict, g_psv_maxspeed->value ); - } - - sprintf( msg, "sv_maxspeed is changed to %g\n", g_psv_maxspeed->value ); - g_engfuncs.pfnServerPrint( msg ); - g_psv_maxspeed->modified = false; - } - -// CheckDesiredList(); //LRC - CheckAssistList(); //LRC } -void EndFrame( void ) -{ -} void ClientPrecache( void ) { // setup precaches always needed PRECACHE_SOUND("player/sprayer.wav"); // spray paint sound for PreAlpha - + // PRECACHE_SOUND("player/pl_jumpland2.wav"); // UNDONE: play 2x step sound - - PRECACHE_SOUND("player/pl_fallpain2.wav"); - PRECACHE_SOUND("player/pl_fallpain3.wav"); - + + PRECACHE_SOUND("player/pl_fallpain2.wav"); + PRECACHE_SOUND("player/pl_fallpain3.wav"); + PRECACHE_SOUND("player/pl_step1.wav"); // walk on concrete PRECACHE_SOUND("player/pl_step2.wav"); PRECACHE_SOUND("player/pl_step3.wav"); @@ -855,7 +782,7 @@ void ClientPrecache( void ) PRECACHE_SOUND( SOUND_FLASHLIGHT_OFF ); // player gib sounds - PRECACHE_SOUND("common/bodysplat.wav"); + PRECACHE_SOUND("common/bodysplat.wav"); // player pain sounds PRECACHE_SOUND("player/pl_pain2.wav"); @@ -874,6 +801,7 @@ void ClientPrecache( void ) PRECACHE_SOUND("common/wpn_select.wav"); PRECACHE_SOUND("common/wpn_denyselect.wav"); + // geiger sounds PRECACHE_SOUND("player/geiger6.wav"); @@ -896,45 +824,33 @@ Returns the descriptive name of this .dll. E.g., Half-Life, or Team Fortress 2 */ const char *GetGameDescription() { -#if 0 if ( g_pGameRules ) // this function may be called before the world has spawned, and the game rules initialized return g_pGameRules->GetGameDescription(); else - return GAME_NAME; -#else - // alternative handle come from Xash 0.4 - char *token = NULL; - char szbuffer[128]; - char *afile = (char *)LOAD_FILE_FOR_ME( "gameinfo.txt", NULL ); - const char *pfile = afile; + return "Half-Life"; +} - memset( text, 0, sizeof( text )); +/* +================ +Sys_Error - if( pfile ) - { - token = COM_ParseToken( &pfile ); - - while( token ) - { - if( !stricmp( token, "title" )) - { - token = COM_ParseToken( &pfile ); - sprintf( szbuffer, "%s ", token ); - strncat( text, szbuffer, sizeof( text )); - } - else if( !stricmp( token, "version" )) - { - token = COM_ParseToken( &pfile ); - strncat( text, token, sizeof( text )); - } - token = COM_ParseToken( &pfile ); - } +Engine is going to shut down, allows setting a breakpoint in game .dll to catch that occasion +================ +*/ +void Sys_Error( const char *error_string ) +{ + // Default case, do nothing. MOD AUTHORS: Add code ( e.g., _asm { int 3 }; here to cause a breakpoint for debugging your game .dlls +} - FREE_FILE( afile ); - return text; - } - return GAME_NAME; -#endif +/* +================ +PlayerCustomization + +Completely ignored in Xash3D +================ +*/ +void PlayerCustomization( edict_t *pEntity, void *pCust ) +{ } /* @@ -985,104 +901,6 @@ void SpectatorThink( edict_t *pEntity ) pPlayer->SpectatorThink( ); } -// FIXME: implement VirtualClass GetClass instead -int AutoClassify( edict_t *pentToClassify ) -{ - CBaseEntity *pClass; - - pClass = CBaseEntity::Instance( pentToClassify ); - if( !pClass ) return ED_SPAWNED; - - const char *classname = STRING( pClass->pev->classname ); - - if ( !strnicmp( "worldspawn", classname, 10 )) - { - return ED_WORLDSPAWN; - } - - if ( !strnicmp( "bodyque", classname, 7 )) - { - return ED_NORMAL; - } - - // first pass: determine type by explicit parms - if ( pClass->pev->solid == SOLID_TRIGGER ) - { - if ( FClassnameIs( pClass->pev, "ambient_generic" ) || FClassnameIs( pClass->pev, "target_speaker" )) // FIXME - { - pClass->pev->flags |= FL_PHS_FILTER; - return ED_AMBIENT; - } - else if( pClass->pev->movetype == MOVETYPE_TOSS ) - return ED_NORMAL; // it's item or weapon - if ( FBitSet( pClass->pev->effects, EF_NODRAW )) - return ED_TRIGGER; // never sending to client - return ED_NORMAL; // e.g. friction modifier - } - else if ( pClass->pev->movetype == MOVETYPE_PHYSIC ) - return ED_RIGIDBODY; - else if ( pClass->pev->solid == SOLID_BSP ) - { - if ( pClass->pev->flags & FL_CONVEYOR ) - return ED_MOVER; - else if ( pClass->pev->flags & FL_WORLDBRUSH ) - return ED_BSPBRUSH; - else if ( pClass->pev->movetype == MOVETYPE_PUSH ) - return ED_MOVER; - else if ( pClass->pev->movetype == MOVETYPE_NONE ) - return ED_BSPBRUSH; - } - else if ( pClass->pev->flags & FL_MONSTER ) - return ED_MONSTER; - else if ( pClass->pev->flags & FL_CLIENT ) - return ED_CLIENT; - else if ( pClass->pev->flags & FL_CONVEYOR ) - return ED_MOVER; - else if ( !pClass->pev->modelindex && !pClass->pev->aiment ) - { - if ( pClass->pev->noise || pClass->pev->noise1 || pClass->pev->noise2 || pClass->pev->noise3 ) - { - pClass->pev->flags |= FL_PHS_FILTER; - return ED_AMBIENT; - } - return ED_STATIC; // never sending to client - } - - // mark as normal - if ( pClass->pev->modelindex || pClass->pev->noise ) - return ED_NORMAL; - - // fail to classify :-( - return ED_SPAWNED; -} - -int ServerClassifyEdict( edict_t *pentToClassify ) -{ - // NOTE: we can't use FNullEnt here to handle 'worldspawn' properly - // but must skip clients because they not spawned at this point - if( !pentToClassify || pentToClassify->free || !pentToClassify->pvPrivateData ) - return ED_SPAWNED; - - CBaseEntity *pClass; - - pClass = CBaseEntity::Instance( pentToClassify ); - if( !pClass ) return ED_SPAWNED; - - // user-defined - if( pClass->m_iClassType != ED_SPAWNED ) - return pClass->m_iClassType; - - int m_iNewClass = AutoClassify( pentToClassify ); - - if( m_iNewClass != ED_SPAWNED ) - { - // tell server.dll about new class - pClass->SetObjectClass( m_iNewClass ); - } - - return m_iNewClass; -} - //////////////////////////////////////////////////////// // PAS and PVS routines for client messaging // @@ -1101,74 +919,48 @@ From the eye position, we set up the PAS and PVS to use for filtering network me NOTE: Do not cache the values of pas and pvs, as they depend on reusable memory in the engine, they are only good for this one frame ================ */ -void SetupVisibility( edict_t *pViewEntity, edict_t *pClient, byte **pvs, byte **pas, int portal ) +void SetupVisibility( edict_t *pViewEntity, edict_t *pClient, unsigned char **pvs, unsigned char **pas ) { - Vector org = g_vecZero; - edict_t *pView = pClient; + Vector org; + edict_t *pView = pClient; - if( portal ) + // Find the client's PVS + if ( pViewEntity ) { - // Entity's added from portal camera PVS - if( FNullEnt( pViewEntity )) return; // broken portal ? + pView = pViewEntity; + } - CBaseEntity *pCamera = (CBaseEntity *)CBaseEntity::Instance( pViewEntity ); + if ( pClient->v.flags & FL_PROXY ) + { + *pvs = NULL; // the spectator proxy sees + *pas = NULL; // and hears everything + return; + } - if( !pCamera ) return; - - // determine visible point - if( pCamera->m_iClassType == ED_PORTAL ) + if( pView->v.effects & EF_MERGE_VISIBILITY ) + { + if( FClassnameIs( pView, "env_sky" )) { - // don't build visibility for mirrors - if( pCamera->pev->origin == pCamera->pev->oldorigin ) - return; - else org = pCamera->pev->oldorigin; + org = pView->v.origin; } - else if( pCamera->m_iClassType == ED_SKYPORTAL ) - { - org = pCamera->pev->origin; - } - else return; // other edicts can't merge pvs + else return; // don't merge pvs } else { - // calc point view from client eyes or client camera's - if( FNullEnt( pClient )) HOST_ERROR( "SetupVisibility: client == NULL\n" ); - if( !FNullEnt( pViewEntity )) pView = pViewEntity; - - if( pClient->v.flags & FL_PROXY ) - { - *pvs = NULL; // the spectator proxy sees - *pas = NULL; // and hears everything - return; - } - - CBasePlayer *pPlayer = (CBasePlayer *)CBaseEntity::Instance( pClient ); - - // for trigger_viewset - if( pPlayer && pPlayer->viewFlags & 1 ) - { - - CBaseEntity *pViewEnt = UTIL_FindEntityByString( NULL, "targetname", STRING(pPlayer->viewEntity) ); - - if (!FNullEnt(pViewEnt)) pView = pViewEnt->edict(); - else - { - // try to find entity by classname - CBaseEntity *pViewEnt = UTIL_FindEntityByString( NULL, "classname", STRING(pPlayer->viewEntity) ); - - if ( !FNullEnt ( pViewEnt )) - pView = pViewEnt->edict(); - else pPlayer->viewFlags = 0; - } - } - // NOTE: view offset always contains actual viewheight (set it in PM_Move) org = pView->v.origin + pView->v.view_ofs; + if ( pView->v.flags & FL_DUCKING ) + { + org = org + ( VEC_HULL_MIN - VEC_DUCK_HULL_MIN ); + } } - *pvs = ENGINE_SET_PVS( (float *)&org, portal ); - *pas = ENGINE_SET_PAS( (float *)&org, portal ); + *pvs = ENGINE_SET_PVS ( (float *)&org ); + *pas = ENGINE_SET_PAS ( (float *)&org ); } +#include "entity_state.h" +#include "entity_types.h" + /* AddToFullPack @@ -1182,242 +974,188 @@ player is 1 if the ent/e is a player and 0 otherwise pSet is either the PAS or PVS that we previous set up. We can use it to ask the engine to filter the entity against the PAS or PVS. we could also use the pas/ pvs that we set in SetupVisibility, if we wanted to. Caching the value is valid in that case, but still only for the current frame */ -int AddToFullPack( entity_state_t *state, edict_t *pView, edict_t *pHost, edict_t *pEdict, int hostflags, byte *pSet ) +int AddToFullPack( struct entity_state_s *state, int e, edict_t *ent, edict_t *host, int hostflags, int player, unsigned char *pSet ) { - CBaseEntity *pEntity; - Vector delta; // for ambient sounds + int i; - if( FNullEnt( pEdict )) - return 0; // never adding invalid entities - - if( FNullEnt( pView )) pView = pHost; // pfnCustomView not set - - CBaseEntity *pViewEntity = (CBaseEntity *)CBaseEntity::Instance( pView ); - - BOOL bIsPortalPass = FALSE; - - if( pViewEntity && pViewEntity->m_iClassType == ED_PORTAL ) - bIsPortalPass = TRUE; // view from portal camera - - pEntity = (CBaseEntity *)CBaseEntity::Instance( pEdict ); - - if( !pEntity ) return 0; - - // NOTE: always add himslef to list - if( !bIsPortalPass && ( pHost == pEdict )) - goto addEntity; - - // completely ignore dormant entity - if( pEdict->v.flags & FL_DORMANT ) + // don't send if flagged for NODRAW and it's not the host getting the message + if (( ent->v.effects == EF_NODRAW ) && ( ent != host )) return 0; - - // don't send spectators to other players - if(( pEdict->v.flags & FL_SPECTATOR ) && ( pEdict != pHost )) + + // Ignore ents without valid / visible models + if ( !ent->v.modelindex || !STRING( ent->v.model )) + return 0; + + // Don't send spectators to other players + if ( ( ent->v.flags & FL_SPECTATOR ) && ( ent != host ) ) { return 0; } - // quick reject by type - switch( pEntity->m_iClassType ) + // Ignore if not the host and not touching a PVS/PAS leaf + // If pSet is NULL, then the test will always succeed and the entity will be added to the update + if ( ent != host ) { - case ED_SKYPORTAL: - goto addEntity; // no additional check requires - case ED_BEAM: - case ED_MOVER: - case ED_NORMAL: - case ED_PORTAL: - case ED_CLIENT: - case ED_MONSTER: - case ED_AMBIENT: - case ED_BSPBRUSH: - case ED_RIGIDBODY: break; - default: return 0; // skipped - } - - // check for ambients distance - if( pEntity->m_iClassType == ED_AMBIENT ) - { - Vector entorigin; - - // don't send sounds if they will be attenuated away - if( pEntity->pev->origin == g_vecZero ) - entorigin = (pEntity->pev->mins + pEntity->pev->maxs) * 0.5f; - else entorigin = pEntity->pev->origin; - - // precalc delta distance for sounds - delta = pView->v.origin - entorigin; - } - else delta = g_vecZero; - - // check visibility - if ( !ENGINE_CHECK_PVS( pEdict, pSet )) - { - float m_flRadius = 1024; // g-cont: tune distance by taste :) - - if ( pEntity->pev->flags & FL_PHS_FILTER ) + if ( !ENGINE_CHECK_VISIBILITY( ent, pSet )) { - if( pEntity->pev->armorvalue > 0 ) - m_flRadius = pEntity->pev->armorvalue; - - if( delta.Length() > m_flRadius ) + // env_sky is visible always + if( !FClassnameIs( ent, "env_sky" )) + { return 0; - } - else //if( pEntity->m_iClassType != ED_BEAM ) - { - return 0; + } } } - if( FNullEnt( pHost )) HOST_ERROR( "pHost == NULL\n" ); - // don't send entity to local client if the client says it's predicting the entity itself. - if( pEntity->pev->flags & FL_SKIPLOCALHOST ) + // Don't send entity to local client if the client says it's predicting the entity itself. + if ( ent->v.flags & FL_SKIPLOCALHOST ) { - if( bIsPortalPass ) return 0; - if(( hostflags & 1 ) && ( pEntity->pev->owner == pHost )) + if ( hostflags & 4 ) return 0; // it's a portal pass + if (( hostflags & 1 ) && ( ent->v.owner == host )) return 0; } - - if( !pEntity->pev->modelindex || FStringNull( pEntity->pev->model )) + + if ( host->v.groupinfo ) { - // can't ignore ents withouts models, because portals and mirrors can't working otherwise - // and null.mdl it's no more needs to be set: ED_CLASS rejection is working fine - // so we wan't reject this entities here. - } + UTIL_SetGroupTrace( host->v.groupinfo, GROUP_OP_AND ); - if( pHost->v.groupinfo ) - { - UTIL_SetGroupTrace( pHost->v.groupinfo, GROUP_OP_AND ); - - // should always be set, of course - if( pEdict->v.groupinfo ) + // Should always be set, of course + if ( ent->v.groupinfo ) { - if( g_groupop == GROUP_OP_AND ) + if ( g_groupop == GROUP_OP_AND ) { - if(!( pEdict->v.groupinfo & pHost->v.groupinfo )) + if ( !(ent->v.groupinfo & host->v.groupinfo ) ) return 0; } - else if( g_groupop == GROUP_OP_NAND ) + else if ( g_groupop == GROUP_OP_NAND ) { - if( pEdict->v.groupinfo & pHost->v.groupinfo ) + if ( ent->v.groupinfo & host->v.groupinfo ) return 0; } } + UTIL_UnsetGroupTrace(); } -addEntity: + memset( state, 0, sizeof( *state ) ); - // setup some special edict flags (engine will clearing them after the current frame) - if( state->modelindex != pEntity->pev->modelindex || ( pEntity->pev->effects & EF_NOINTERP )) - state->ed_flags |= ESF_NODELTA; + // Assign index so we can track this entity from frame to frame and + // delta from it. + state->number = e; - // always keep an actual - state->number = pEdict->serialnumber; + // set entity type + if ( ent->v.flags & FL_CLIENT ) + state->entityType = ET_PLAYER; + else if ( ent->v.flags & FL_CUSTOMENTITY ) + state->entityType = ET_BEAM; + else state->entityType = ET_NORMAL; - // copy progs values to state - state->solid = (solid_t)pEntity->pev->solid; + // + // Copy state data + // - state->modelindex = pEntity->pev->modelindex; - state->origin = pEntity->pev->origin; - state->angles = pEntity->pev->angles; - state->health = pEntity->pev->health; - state->skin = pEntity->pev->skin; // studio model skin - state->body = pEntity->pev->body; // studio model submodel - state->effects = pEntity->pev->effects; // shared client and render flags - state->renderfx = pEntity->pev->renderfx; // renderer flags - state->rendermode = pEntity->pev->rendermode; // rendering mode - state->renderamt = pEntity->pev->renderamt; // alpha value - state->animtime = (int)(1000.0 * pEntity->pev->animtime) * 0.001; // sequence time - state->scale = pEntity->pev->scale; // shared client and render flags - state->movetype = (movetype_t)pEntity->pev->movetype; - state->frame = pEntity->pev->frame; // any model current frame - state->framerate = pEntity->pev->framerate; - state->mins = pEntity->pev->mins; - state->maxs = pEntity->pev->maxs; - state->flags = pEntity->pev->flags; - state->vuser1 = pEntity->pev->oldorigin; // used for portals and skyportals - state->colormap = pEntity->pev->colormap; // attachments + // Round animtime to nearest millisecond + state->animtime = (int)(1000.0 * ent->v.animtime ) / 1000.0; - state->rendercolor.r = (byte)pEntity->pev->rendercolor.x; - state->rendercolor.g = (byte)pEntity->pev->rendercolor.y; - state->rendercolor.b = (byte)pEntity->pev->rendercolor.z; + memcpy( state->origin, ent->v.origin, 3 * sizeof( float ) ); + memcpy( state->angles, ent->v.angles, 3 * sizeof( float ) ); + memcpy( state->mins, ent->v.mins, 3 * sizeof( float ) ); + memcpy( state->maxs, ent->v.maxs, 3 * sizeof( float ) ); + + memcpy( state->startpos, ent->v.startpos, 3 * sizeof( float ) ); + memcpy( state->endpos, ent->v.endpos, 3 * sizeof( float ) ); + + state->impacttime = ent->v.impacttime; + state->starttime = ent->v.starttime; + + state->modelindex = ent->v.modelindex; - if( pEntity->pev->groundentity ) - state->onground = ENTINDEX( pEntity->pev->groundentity ); - else state->onground = 0; + state->frame = ent->v.frame; - // translate attached entity - if( pEntity->pev->aiment ) - state->aiment = ENTINDEX( pEntity->pev->aiment ); - else state->aiment = 0; + state->skin = ent->v.skin; + state->effects = ent->v.effects; - // studio model sequence - if( pEntity->pev->sequence != -1 ) - state->sequence = pEntity->pev->sequence; - - for( int i = 0; i < 16; i++ ) + // This non-player entity is being moved by the game .dll and not the physics simulation system + // make sure that we interpolate it's position on the client if it moves + if ( !player && ent->v.animtime && ent->v.velocity == g_vecZero ) { - // copy blendings and bone ctrlrs - state->blending[i] = pEntity->pev->blending[i]; - state->controller[i] = pEntity->pev->controller[i]; + state->eflags |= EFLAG_SLERP; } - if( state->ed_type == ED_CLIENT ) + state->scale = ent->v.scale; + state->solid = ent->v.solid; + state->colormap = ent->v.colormap; + + state->movetype = ent->v.movetype; + state->sequence = ent->v.sequence; + state->framerate = ent->v.framerate; + state->body = ent->v.body; + + for (i = 0; i < 4; i++) { - if( pEntity->pev->teleport_time ) + state->controller[i] = ent->v.controller[i]; + } + + for (i = 0; i < 2; i++) + { + state->blending[i] = ent->v.blending[i]; + } + + state->rendermode = ent->v.rendermode; + state->renderamt = ent->v.renderamt; + state->renderfx = ent->v.renderfx; + state->rendercolor.r = ent->v.rendercolor.x; + state->rendercolor.g = ent->v.rendercolor.y; + state->rendercolor.b = ent->v.rendercolor.z; + + state->aiment = 0; + if ( ent->v.aiment ) + { + state->aiment = ENTINDEX( ent->v.aiment ); + } + + state->owner = 0; + if ( ent->v.owner ) + { + int owner = ENTINDEX( ent->v.owner ); + + // Only care if owned by a player + if ( owner >= 1 && owner <= gpGlobals->maxClients ) { - state->ed_flags |= ESF_NO_PREDICTION; - state->ed_flags |= ESF_NODELTA; - pEntity->pev->teleport_time = 0.0f; + state->owner = owner; } - - if( pEntity->pev->aiment ) - state->aiment = ENTINDEX( pEntity->pev->aiment ); - else state->aiment = 0; - - state->velocity = pEntity->pev->velocity; - state->basevelocity = pEntity->pev->clbasevelocity; - state->iStepLeft = pEntity->pev->iStepLeft; - state->flFallVelocity = pEntity->pev->flFallVelocity; - - // playermodel sequence, that will be playing on a client - if( pEntity->pev->gaitsequence != -1 ) - state->gaitsequence = pEntity->pev->gaitsequence; - if( pEntity->pev->weaponmodel != iStringNull ) - state->weaponmodel = MODEL_INDEX( STRING( pEntity->pev->weaponmodel )); - else state->weaponmodel = 0; - - // clamp fov - if( pEntity->pev->fov < 0.0 ) pEntity->pev->fov = 0.0; - if( pEntity->pev->fov > 160 ) pEntity->pev->fov = 160; - state->fov = pEntity->pev->fov; } - else if( state->ed_type == ED_AMBIENT ) - { - // add here specified fields e.g for trigger_teleport wind sound etc - } - else if( state->ed_type == ED_MOVER || state->ed_type == ED_BSPBRUSH || state->ed_type == ED_PORTAL ) - { - state->body = DirToBits( pEntity->pev->movedir ); - state->velocity = pEntity->pev->velocity; - // this is conveyor - send speed to render for right texture scrolling - state->framerate = pEntity->pev->speed; - } - else if( state->ed_type == ED_BEAM ) + // HACK: Somewhat... + // Class is overridden for non-players to signify a breakable glass object ( sort of a class? ) + if ( !player ) { - state->gaitsequence = pEntity->pev->frags; // beam type + state->playerclass = ent->v.playerclass; + } - // translate StartBeamEntity - if( pEntity->pev->owner ) - state->owner = ENTINDEX( pEntity->pev->owner ); - else state->owner = 0; + // Special stuff for players only + if ( player ) + { + memcpy( state->basevelocity, ent->v.basevelocity, 3 * sizeof( float ) ); + + state->weaponmodel = MODEL_INDEX( STRING( ent->v.weaponmodel ) ); + state->gaitsequence = ent->v.gaitsequence; + state->spectator = ent->v.flags & FL_SPECTATOR; + state->friction = ent->v.friction; + + state->gravity = ent->v.gravity; +// state->team = ent->v.team; +// + state->usehull = ( ent->v.flags & FL_DUCKING ) ? 1 : 0; + state->health = ent->v.health; } return 1; } +// defaults for clientinfo messages +#define DEFAULT_VIEWHEIGHT 28 + /* =================== CreateBaseline @@ -1425,31 +1163,28 @@ CreateBaseline Creates baselines used for network encoding, especially for player data since players are not spawned until connect time. =================== */ -void CreateBaseline( entity_state_t *baseline, edict_t *entity, int playermodelindex ) +void CreateBaseline( int player, int eindex, struct entity_state_s *baseline, struct edict_s *entity, int playermodelindex, vec3_t player_mins, vec3_t player_maxs ) { - // always set nodelta's for baseline - baseline->ed_flags |= ESF_NODELTA; - - baseline->origin = entity->v.origin; - baseline->angles = entity->v.angles; - baseline->frame = entity->v.frame; - baseline->skin = (short)entity->v.skin; + baseline->origin = entity->v.origin; + baseline->angles = entity->v.angles; + baseline->frame = entity->v.frame; + baseline->skin = (short)entity->v.skin; // render information - baseline->rendermode = (byte)entity->v.rendermode; - baseline->renderamt = (byte)entity->v.renderamt; - baseline->rendercolor.r = (byte)entity->v.rendercolor.x; - baseline->rendercolor.g = (byte)entity->v.rendercolor.y; - baseline->rendercolor.b = (byte)entity->v.rendercolor.z; - baseline->renderfx = (byte)entity->v.renderfx; + baseline->rendermode = (byte)entity->v.rendermode; + baseline->renderamt = (byte)entity->v.renderamt; + baseline->rendercolor.r = (byte)entity->v.rendercolor.x; + baseline->rendercolor.g = (byte)entity->v.rendercolor.y; + baseline->rendercolor.b = (byte)entity->v.rendercolor.z; + baseline->renderfx = (byte)entity->v.renderfx; - baseline->mins = entity->v.mins; - baseline->maxs = entity->v.maxs; - - if( baseline->ed_type == ED_CLIENT ) + if ( player ) { - baseline->colormap = entity->serialnumber; - baseline->modelindex = playermodelindex; // "model" field from userinfo + baseline->mins = player_mins; + baseline->maxs = player_maxs; + + baseline->colormap = eindex; + baseline->modelindex = playermodelindex; baseline->friction = 1.0; baseline->movetype = MOVETYPE_WALK; @@ -1461,12 +1196,15 @@ void CreateBaseline( entity_state_t *baseline, edict_t *entity, int playermodeli } else { + baseline->mins = entity->v.mins; + baseline->maxs = entity->v.maxs; + baseline->colormap = 0; - baseline->modelindex = entity->v.modelindex; - baseline->movetype = (movetype_t)entity->v.movetype; + baseline->modelindex = entity->v.modelindex;//SV_ModelIndex(pr_strings + entity->v.model); + baseline->movetype = entity->v.movetype; baseline->scale = entity->v.scale; - baseline->solid = (solid_t)entity->v.solid; + baseline->solid = entity->v.solid; baseline->framerate = entity->v.framerate; baseline->gravity = entity->v.gravity; } @@ -1537,6 +1275,17 @@ void Entity_Encode( struct delta_s *pFields, const unsigned char *from, const un DELTA_UNSETBYINDEX( pFields, entity_field_alias[ FIELD_ORIGIN2 ].field ); } + if ( ( t->impacttime != 0 ) && ( t->starttime != 0 ) ) + { + DELTA_UNSETBYINDEX( pFields, entity_field_alias[ FIELD_ORIGIN0 ].field ); + DELTA_UNSETBYINDEX( pFields, entity_field_alias[ FIELD_ORIGIN1 ].field ); + DELTA_UNSETBYINDEX( pFields, entity_field_alias[ FIELD_ORIGIN2 ].field ); + + DELTA_UNSETBYINDEX( pFields, entity_field_alias[ FIELD_ANGLES0 ].field ); + DELTA_UNSETBYINDEX( pFields, entity_field_alias[ FIELD_ANGLES1 ].field ); + DELTA_UNSETBYINDEX( pFields, entity_field_alias[ FIELD_ANGLES2 ].field ); + } + if ( ( t->movetype == MOVETYPE_FOLLOW ) && ( t->aiment != 0 ) ) { @@ -1592,23 +1341,23 @@ void Player_Encode( struct delta_s *pFields, const unsigned char *from, const un localplayer = ( t->number - 1 ) == ENGINE_CURRENT_PLAYER(); if ( localplayer ) { - DELTA_UNSETBYINDEX( pFields, entity_field_alias[ FIELD_ORIGIN0 ].field ); - DELTA_UNSETBYINDEX( pFields, entity_field_alias[ FIELD_ORIGIN1 ].field ); - DELTA_UNSETBYINDEX( pFields, entity_field_alias[ FIELD_ORIGIN2 ].field ); + DELTA_UNSETBYINDEX( pFields, player_field_alias[ FIELD_ORIGIN0 ].field ); + DELTA_UNSETBYINDEX( pFields, player_field_alias[ FIELD_ORIGIN1 ].field ); + DELTA_UNSETBYINDEX( pFields, player_field_alias[ FIELD_ORIGIN2 ].field ); } if ( ( t->movetype == MOVETYPE_FOLLOW ) && ( t->aiment != 0 ) ) { - DELTA_UNSETBYINDEX( pFields, entity_field_alias[ FIELD_ORIGIN0 ].field ); - DELTA_UNSETBYINDEX( pFields, entity_field_alias[ FIELD_ORIGIN1 ].field ); - DELTA_UNSETBYINDEX( pFields, entity_field_alias[ FIELD_ORIGIN2 ].field ); + DELTA_UNSETBYINDEX( pFields, player_field_alias[ FIELD_ORIGIN0 ].field ); + DELTA_UNSETBYINDEX( pFields, player_field_alias[ FIELD_ORIGIN1 ].field ); + DELTA_UNSETBYINDEX( pFields, player_field_alias[ FIELD_ORIGIN2 ].field ); } else if ( t->aiment != f->aiment ) { - DELTA_SETBYINDEX( pFields, entity_field_alias[ FIELD_ORIGIN0 ].field ); - DELTA_SETBYINDEX( pFields, entity_field_alias[ FIELD_ORIGIN1 ].field ); - DELTA_SETBYINDEX( pFields, entity_field_alias[ FIELD_ORIGIN2 ].field ); + DELTA_SETBYINDEX( pFields, player_field_alias[ FIELD_ORIGIN0 ].field ); + DELTA_SETBYINDEX( pFields, player_field_alias[ FIELD_ORIGIN1 ].field ); + DELTA_SETBYINDEX( pFields, player_field_alias[ FIELD_ORIGIN2 ].field ); } } @@ -1671,7 +1420,7 @@ void Custom_Encode( struct delta_s *pFields, const unsigned char *from, const un f = (entity_state_t *)from; t = (entity_state_t *)to; - beamType = t->rendermode; + beamType = t->rendermode & 0x0f; if ( beamType != BEAM_POINTS && beamType != BEAM_ENTPOINT ) { @@ -1706,8 +1455,6 @@ void Custom_Encode( struct delta_s *pFields, const unsigned char *from, const un RegisterEncoders Allows game .dll to override network encoding of certain types of entities and tweak values, etc. - -disabled for now, wait for Xash 0.8 ================= */ void RegisterEncoders( void ) @@ -1717,18 +1464,69 @@ void RegisterEncoders( void ) DELTA_ADDENCODER( "Player_Encode", Player_Encode ); } -/* -================= -GetWeaponData - -Part of weapon predict system - -disabled for now, wait for Xash 0.8 -================= -*/ -int GetWeaponData( struct edict_s *player, weapon_data_t *info ) +int GetWeaponData( struct edict_s *player, struct weapon_data_s *info ) { - memset( info, 0, 32 * sizeof( weapon_data_t )); +#if defined( CLIENT_WEAPONS ) + int i; + weapon_data_t *item; + entvars_t *pev = &player->v; + CBasePlayer *pl = ( CBasePlayer *) CBasePlayer::Instance( pev ); + CBasePlayerWeapon *gun; + + ItemInfo II; + + memset( info, 0, 32 * sizeof( weapon_data_t ) ); + + if ( !pl ) + return 1; + + // go through all of the weapons and make a list of the ones to pack + for ( i = 0 ; i < MAX_ITEM_TYPES ; i++ ) + { + if ( pl->m_rgpPlayerItems[ i ] ) + { + // there's a weapon here. Should I pack it? + CBasePlayerItem *pPlayerItem = pl->m_rgpPlayerItems[ i ]; + + while ( pPlayerItem ) + { + gun = (CBasePlayerWeapon *)pPlayerItem->GetWeaponPtr(); + if ( gun && gun->UseDecrement() ) + { + // Get The ID. + memset( &II, 0, sizeof( II ) ); + gun->GetItemInfo( &II ); + + if ( II.iId >= 0 && II.iId < 32 ) + { + item = &info[ II.iId ]; + + item->m_iId = II.iId; + item->m_iClip = gun->m_iClip; + + item->m_flTimeWeaponIdle = max( gun->m_flTimeWeaponIdle, -0.001 ); + item->m_flNextPrimaryAttack = max( gun->m_flNextPrimaryAttack, -0.001 ); + item->m_flNextSecondaryAttack = max( gun->m_flNextSecondaryAttack, -0.001 ); + item->m_fInReload = gun->m_fInReload; + item->m_fInSpecialReload = gun->m_fInSpecialReload; + item->fuser1 = max( gun->pev->fuser1, -0.001 ); + item->fuser2 = gun->m_flStartThrow; + item->fuser3 = gun->m_flReleaseThrow; + item->iuser1 = gun->m_chargeReady; + item->iuser2 = gun->m_fInAttack; + item->iuser3 = gun->m_fireState; + + +// item->m_flPumpTime = max( gun->m_flPumpTime, -0.001 ); + } + } + pPlayerItem = pPlayerItem->m_pNext; + } + } + } +#else + memset( info, 0, 32 * sizeof( weapon_data_t ) ); +#endif return 1; } @@ -1740,7 +1538,7 @@ Data sent to current client only engine sets cd to 0 before calling. ================= */ -void UpdateClientData( const edict_t *ent, int sendweapons, clientdata_t *cd ) +void UpdateClientData ( const struct edict_s *ent, int sendweapons, struct clientdata_s *cd ) { cd->flags = ent->v.flags; cd->health = ent->v.health; @@ -1770,6 +1568,55 @@ void UpdateClientData( const edict_t *ent, int sendweapons, clientdata_t *cd ) cd->weaponanim = ent->v.weaponanim; cd->pushmsec = ent->v.pushmsec; + +#if defined( CLIENT_WEAPONS ) + if ( sendweapons ) + { + entvars_t *pev = (entvars_t *)&ent->v; + CBasePlayer *pl = ( CBasePlayer *) CBasePlayer::Instance( pev ); + + if ( pl ) + { + cd->m_flNextAttack = pl->m_flNextAttack; + cd->fuser2 = pl->m_flNextAmmoBurn; + cd->fuser3 = pl->m_flAmmoStartCharge; + cd->vuser1.x = pl->ammo_9mm; + cd->vuser1.y = pl->ammo_357; + cd->vuser1.z = pl->ammo_argrens; + cd->ammo_nails = pl->ammo_bolts; + cd->ammo_shells = pl->ammo_buckshot; + cd->ammo_rockets = pl->ammo_rockets; + cd->ammo_cells = pl->ammo_uranium; + cd->vuser2.x = pl->ammo_hornets; + + + if ( pl->m_pActiveItem ) + { + CBasePlayerWeapon *gun; + gun = (CBasePlayerWeapon *)pl->m_pActiveItem->GetWeaponPtr(); + if ( gun && gun->UseDecrement() ) + { + ItemInfo II; + memset( &II, 0, sizeof( II ) ); + gun->GetItemInfo( &II ); + + cd->m_iId = II.iId; + + cd->vuser3.z = gun->m_iSecondaryAmmoType; + cd->vuser4.x = gun->m_iPrimaryAmmoType; + cd->vuser4.y = pl->m_rgAmmo[gun->m_iPrimaryAmmoType]; + cd->vuser4.z = pl->m_rgAmmo[gun->m_iSecondaryAmmoType]; + + if ( pl->m_pActiveItem->m_iId == WEAPON_RPG ) + { + cd->vuser2.y = ( ( CRpg * )pl->m_pActiveItem)->m_fSpotActive; + cd->vuser2.z = ( ( CRpg * )pl->m_pActiveItem)->m_cActiveRockets; + } + } + } + } + } +#endif } /* @@ -1785,9 +1632,7 @@ void CmdStart( const edict_t *player, const struct usercmd_s *cmd, unsigned int entvars_t *pev = (entvars_t *)&player->v; CBasePlayer *pl = ( CBasePlayer *) CBasePlayer::Instance( pev ); - if( !pl ) - return; - + if ( !pl ) return; if ( pl->pev->groupinfo != 0 ) { UTIL_SetGroupTrace( pl->pev->groupinfo, GROUP_OP_AND ); @@ -1803,13 +1648,13 @@ CmdEnd Each cmdstart is exactly matched with a cmd end, clean up any group trace flags, etc. here ================= */ -void CmdEnd ( const edict_t *player ) +void CmdEnd( const edict_t *player ) { entvars_t *pev = (entvars_t *)&player->v; CBasePlayer *pl = ( CBasePlayer *) CBasePlayer::Instance( pev ); - if( !pl ) - return; + if ( !pl ) return; + if ( pl->pev->groupinfo != 0 ) { UTIL_UnsetGroupTrace(); @@ -1818,14 +1663,114 @@ void CmdEnd ( const edict_t *player ) /* ================================ -ShouldCollide +ConnectionlessPacket - Called when the engine believes two entities are about to collide. Return 0 if you - want the two entities to just pass through each other without colliding or calling the - touch function. + Return 1 if the packet is valid. Set response_buffer_size if you want to send a response packet. Incoming, it holds the max + size of the response_buffer, so you must zero it out if you choose not to respond. ================================ */ -int ShouldCollide( edict_t *pentTouched, edict_t *pentOther ) +int ConnectionlessPacket( const struct netadr_s *net_from, const char *args, char *response_buffer, int *response_buffer_size ) +{ + // Parse stuff from args + int max_buffer_size = *response_buffer_size; + + // Zero it out since we aren't going to respond. + // If we wanted to response, we'd write data into response_buffer + *response_buffer_size = 0; + + // Since we don't listen for anything here, just respond that it's a bogus message + // If we didn't reject the message, we'd return 1 for success instead. + return 0; +} + +/* +================================ +GetHullBounds + + Engine calls this to enumerate player collision hulls, for prediction. Return 0 if the hullnumber doesn't exist. +================================ +*/ +int GetHullBounds( int hullnumber, float *mins, float *maxs ) +{ + int iret = 0; + + switch ( hullnumber ) + { + case 0: // Normal player + VEC_HULL_MIN.CopyToArray( mins ); + VEC_HULL_MAX.CopyToArray( maxs ); + iret = 1; + break; + case 1: // Crouched player + VEC_DUCK_HULL_MIN.CopyToArray( mins ); + VEC_DUCK_HULL_MAX.CopyToArray( maxs ); + iret = 1; + break; + case 2: // Point based hull + g_vecZero.CopyToArray( mins ); + g_vecZero.CopyToArray( maxs ); + iret = 1; + break; + } + + return iret; +} + +/* +================================ +CreateInstancedBaselines + +Create pseudo-baselines for items that aren't placed in the map at spawn time, but which are likely +to be created during play ( e.g., grenades, ammo packs, projectiles, corpses, etc. ) +================================ +*/ +void CreateInstancedBaselines ( void ) +{ + int iret = 0; + entity_state_t state; + + memset( &state, 0, sizeof( state ) ); + + // Create any additional baselines here for things like grendates, etc. + // iret = ENGINE_INSTANCE_BASELINE( pc->pev->classname, &state ); + + // Destroy objects. + //UTIL_Remove( pc ); +} + +/* +================================ +InconsistentFile + +One of the ENGINE_FORCE_UNMODIFIED files failed the consistency check for the specified player + Return 0 to allow the client to continue, 1 to force immediate disconnection ( with an optional disconnect message of up to 256 characters ) +================================ +*/ +int InconsistentFile( const edict_t *player, const char *filename, char *disconnect_message ) +{ + // Server doesn't care? + if ( CVAR_GET_FLOAT( "mp_consistency" ) != 1 ) + return 0; + + // Default behavior is to kick the player + sprintf( disconnect_message, "Server is enforcing file consistency for %s\n", filename ); + + // Kick now with specified disconnect message. + return 1; +} + +/* +================================ +AllowLagCompensation + + The game .dll should return 1 if lag compensation should be allowed ( could also just set + the sv_unlag cvar. + Most games right now should return 0, until client-side weapon prediction code is written + and tested for them ( note you can predict weapons, but not do lag compensation, too, + if you want. +================================ +*/ +int AllowLagCompensation( void ) { return 1; -} \ No newline at end of file +} diff --git a/spirit/client.h b/dlls/client.h similarity index 60% rename from spirit/client.h rename to dlls/client.h index b98de1d4..9c279c68 100644 --- a/spirit/client.h +++ b/dlls/client.h @@ -15,12 +15,8 @@ #ifndef CLIENT_H #define CLIENT_H -extern void PhysicsPreFrame( void ); -extern void PhysicsFrame( void ); -extern void PhysicsPostFrame( void ); - extern void respawn( entvars_t* pev, BOOL fCopyCorpse ); -extern BOOL ClientConnect( edict_t *pEntity, const char *pszName, const char *pszAddress, char szRejectReason[128] ); +extern BOOL ClientConnect( edict_t *pEntity, const char *pszName, const char *pszAddress, char szRejectReason[ 128 ] ); extern void ClientDisconnect( edict_t *pEntity ); extern void ClientKill( edict_t *pEntity ); extern void ClientPutInServer( edict_t *pEntity ); @@ -31,13 +27,13 @@ extern void ServerDeactivate( void ); extern void StartFrame( void ); extern void PlayerPostThink( edict_t *pEntity ); extern void PlayerPreThink( edict_t *pEntity ); -extern void BuildLevelList( void ); +extern void ParmsNewLevel( void ); extern void ParmsChangeLevel( void ); -extern void RegisterEncoders( void ); + extern void ClientPrecache( void ); extern const char *GetGameDescription( void ); -extern int GetWeaponData( edict_t *player, struct weapon_data_s *info ); +extern void PlayerCustomization( edict_t *pEntity, void *pUnused ); extern void SpectatorConnect ( edict_t *pEntity ); extern void SpectatorDisconnect ( edict_t *pEntity ); @@ -45,14 +41,25 @@ extern void SpectatorThink ( edict_t *pEntity ); extern void Sys_Error( const char *error_string ); -extern void SetupVisibility( edict_t *pViewEntity, edict_t *pClient, byte **pvs, byte **pas, int portal ); -extern void UpdateClientData( const struct edict_s *ent, int sendweapons, struct clientdata_s *cd ); -extern int AddToFullPack( entity_state_t *state, edict_t *pView, edict_t *pHost, edict_t *pEdict, int hostflags, byte *pSet ); -extern void CreateBaseline( entity_state_t *baseline, edict_t *entity, int playermodelindex ); +extern void SetupVisibility( edict_t *pViewEntity, edict_t *pClient, unsigned char **pvs, unsigned char **pas ); +extern void UpdateClientData ( const struct edict_s *ent, int sendweapons, struct clientdata_s *cd ); +extern int AddToFullPack( struct entity_state_s *state, int e, edict_t *ent, edict_t *host, int hostflags, int player, unsigned char *pSet ); +extern void CreateBaseline( int player, int eindex, struct entity_state_s *baseline, struct edict_s *entity, int playermodelindex, vec3_t player_mins, vec3_t player_maxs ); +extern void RegisterEncoders( void ); + +extern int GetWeaponData( struct edict_s *player, struct weapon_data_s *info ); extern void CmdStart( const edict_t *player, const struct usercmd_s *cmd, unsigned int random_seed ); extern void CmdEnd ( const edict_t *player ); -extern int g_serveractive; +extern int ConnectionlessPacket( const struct netadr_s *net_from, const char *args, char *response_buffer, int *response_buffer_size ); + +extern int GetHullBounds( int hullnumber, float *mins, float *maxs ); + +extern void CreateInstancedBaselines ( void ); + +extern int InconsistentFile( const edict_t *player, const char *filename, char *disconnect_message ); + +extern int AllowLagCompensation( void ); #endif // CLIENT_H diff --git a/spirit/combat.cpp b/dlls/combat.cpp similarity index 80% rename from spirit/combat.cpp rename to dlls/combat.cpp index 482dccde..5f57b2c5 100644 --- a/spirit/combat.cpp +++ b/dlls/combat.cpp @@ -29,7 +29,6 @@ #include "animation.h" #include "weapons.h" #include "func_break.h" -#include "studio.h" //LRC extern DLL_GLOBAL Vector g_vecAttackDir; extern DLL_GLOBAL int g_iSkillLevel; @@ -116,7 +115,7 @@ void CGib :: SpawnStickyGibs( entvars_t *pevVictim, Vector vecOrigin, int cGibs pGib->pev->movetype = MOVETYPE_TOSS; pGib->pev->solid = SOLID_BBOX; UTIL_SetSize ( pGib->pev, Vector ( 0, 0 ,0 ), Vector ( 0, 0, 0 ) ); - pGib->SetTouch(&CGib:: StickyGibTouch ); + pGib->SetTouch ( StickyGibTouch ); pGib->SetThink (NULL); } pGib->LimitVelocity(); @@ -124,19 +123,19 @@ void CGib :: SpawnStickyGibs( entvars_t *pevVictim, Vector vecOrigin, int cGibs } void CGib :: SpawnHeadGib( entvars_t *pevVictim ) -{ - if ( g_Language == LANGUAGE_GERMAN ) - SpawnHeadGib(pevVictim, "models/germangibs.mdl" );// throw one head - else - SpawnHeadGib(pevVictim, "models/hgibs.mdl" ); -} - -void CGib :: SpawnHeadGib( entvars_t *pevVictim, const char* szGibModel ) { CGib *pGib = GetClassPtr( (CGib *)NULL ); - pGib->Spawn( szGibModel );// throw one head - pGib->pev->body = 0; + if ( g_Language == LANGUAGE_GERMAN ) + { + pGib->Spawn( "models/germangibs.mdl" );// throw one head + pGib->pev->body = 0; + } + else + { + pGib->Spawn( "models/hgibs.mdl" );// throw one head + pGib->pev->body = 0; + } if ( pevVictim ) { @@ -183,42 +182,32 @@ void CGib :: SpawnHeadGib( entvars_t *pevVictim, const char* szGibModel ) void CGib :: SpawnRandomGibs( entvars_t *pevVictim, int cGibs, int human ) { - if ( g_Language == LANGUAGE_GERMAN ) - SpawnRandomGibs(pevVictim, cGibs, 1, "models/germangibs.mdl"); - else if (human) - SpawnRandomGibs(pevVictim, cGibs, 1, "models/hgibs.mdl"); - else - SpawnRandomGibs(pevVictim, cGibs, 0, "models/agibs.mdl"); -} + int cSplat; -//LRC - changed signature, to support custom gib models -void CGib :: SpawnRandomGibs( entvars_t *pevVictim, int cGibs, int notfirst, const char *szGibModel ) -{ - if (cGibs == 0) return; // spawn nothing! - - CGib *pGib = GetClassPtr( (CGib *)NULL ); - pGib->Spawn( szGibModel ); - - //LRC - check the model itself to find out how many gibs are available - studiohdr_t *pstudiohdr = (studiohdr_t *)(GET_MODEL_PTR( ENT(pGib->pev) )); - if (! pstudiohdr) - return; - - mstudiobodyparts_t *pbodypart = (mstudiobodyparts_t *)((byte *)pstudiohdr + pstudiohdr->bodypartindex); - //ALERT(at_console, "read %d bodyparts, canonical is %d\n", pbodypart->nummodels, HUMAN_GIB_COUNT); - - for (int cSplat = 0 ; cSplat < cGibs ; cSplat++ ) + for ( cSplat = 0 ; cSplat < cGibs ; cSplat++ ) { - if (pGib == NULL) // first time through, we set pGib before the loop started - { - pGib = GetClassPtr( (CGib *)NULL ); - pGib->Spawn( szGibModel ); - } + CGib *pGib = GetClassPtr( (CGib *)NULL ); - if (notfirst) - pGib->pev->body = RANDOM_LONG(1, pbodypart->nummodels - 1);// start at one to avoid throwing random amounts of skulls (0th gib) + if ( g_Language == LANGUAGE_GERMAN ) + { + pGib->Spawn( "models/germangibs.mdl" ); + pGib->pev->body = RANDOM_LONG(0,GERMAN_GIB_COUNT-1); + } else - pGib->pev->body = RANDOM_LONG(0, pbodypart->nummodels - 1); + { + if ( human ) + { + // human pieces + pGib->Spawn( "models/hgibs.mdl" ); + pGib->pev->body = RANDOM_LONG(1,HUMAN_GIB_COUNT-1);// start at one to avoid throwing random amounts of skulls (0th gib) + } + else + { + // aliens + pGib->Spawn( "models/agibs.mdl" ); + pGib->pev->body = RANDOM_LONG(0,ALIEN_GIB_COUNT-1); + } + } if ( pevVictim ) { @@ -260,66 +249,39 @@ void CGib :: SpawnRandomGibs( entvars_t *pevVictim, int cGibs, int notfirst, con UTIL_SetSize ( pGib->pev, Vector( 0 , 0 , 0 ), Vector ( 0, 0, 0 ) ); } pGib->LimitVelocity(); - pGib = NULL; //LRC } } -//LRC - work out gibs from blood colour, instead of from class. BOOL CBaseMonster :: HasHumanGibs( void ) { int myClass = Classify(); - // these types of monster don't use gibs - if ( myClass == CLASS_NONE || myClass == CLASS_MACHINE || - myClass == CLASS_PLAYER_BIOWEAPON && myClass == CLASS_ALIEN_BIOWEAPON) - { - return FALSE; - } - else - { - return (this->m_bloodColor == BLOOD_COLOR_RED); - } + if ( myClass == CLASS_HUMAN_MILITARY || + myClass == CLASS_PLAYER_ALLY || + myClass == CLASS_HUMAN_PASSIVE || + myClass == CLASS_PLAYER ) -// if ( myClass == CLASS_HUMAN_MILITARY || -// myClass == CLASS_PLAYER_ALLY || -// myClass == CLASS_HUMAN_PASSIVE || -// myClass == CLASS_PLAYER ) -// -// return TRUE; -// -// return FALSE; + return TRUE; + + return FALSE; } -//LRC - work out gibs from blood colour, instead. BOOL CBaseMonster :: HasAlienGibs( void ) { int myClass = Classify(); - // these types of monster don't use gibs - if ( myClass == CLASS_NONE || myClass == CLASS_MACHINE || - myClass == CLASS_PLAYER_BIOWEAPON && myClass == CLASS_ALIEN_BIOWEAPON) - { - return FALSE; - } - else - { - return (this->m_bloodColor == BLOOD_COLOR_GREEN); - } + if ( myClass == CLASS_ALIEN_MILITARY || + myClass == CLASS_ALIEN_MONSTER || + myClass == CLASS_ALIEN_PASSIVE || + myClass == CLASS_INSECT || + myClass == CLASS_ALIEN_PREDATOR || + myClass == CLASS_ALIEN_PREY ) -// int myClass = Classify(); -// -// if ( myClass == CLASS_ALIEN_MILITARY || -// myClass == CLASS_ALIEN_MONSTER || -// myClass == CLASS_ALIEN_PASSIVE || -// myClass == CLASS_INSECT || -// myClass == CLASS_ALIEN_PREDATOR || -// myClass == CLASS_ALIEN_PREY ) -// -// return TRUE; -// -// return FALSE; + return TRUE; + + return FALSE; } @@ -342,23 +304,13 @@ void CBaseMonster :: GibMonster( void ) { TraceResult tr; BOOL gibbed = FALSE; - int iszCustomGibs; EMIT_SOUND(ENT(pev), CHAN_WEAPON, "common/bodysplat.wav", 1, ATTN_NORM); - if ( iszCustomGibs = HasCustomGibs() ) //LRC - monster_generic can have a custom gibset - { - if ( CVAR_GET_FLOAT("violence_hgibs") != 0 ) - { - CGib::SpawnHeadGib( pev, STRING(iszCustomGibs) ); - CGib::SpawnRandomGibs( pev, 4, 1, STRING(iszCustomGibs) ); - } - gibbed = TRUE; - } // only humans throw skulls !!!UNDONE - eventually monsters will have their own sets of gibs - else if ( HasHumanGibs() ) + if ( HasHumanGibs() ) { - if ( CVAR_GET_FLOAT("violence_hgibs") != 0 )// Only the player will ever fail this test + if ( CVAR_GET_FLOAT("violence_hgibs") != 0 ) // Only the player will ever get here { CGib::SpawnHeadGib( pev ); CGib::SpawnRandomGibs( pev, 4, 1 ); // throw some human gibs. @@ -367,7 +319,7 @@ void CBaseMonster :: GibMonster( void ) } else if ( HasAlienGibs() ) { - if ( CVAR_GET_FLOAT("violence_agibs") != 0 )// Should never fail this test, but someone might call it directly + if ( CVAR_GET_FLOAT("violence_agibs") != 0 ) // Should never get here, but someone might call it directly { CGib::SpawnRandomGibs( pev, 4, 0 ); // Throw alien gibs } @@ -379,8 +331,8 @@ void CBaseMonster :: GibMonster( void ) if ( gibbed ) { // don't remove players! - SetThink(&CBaseMonster :: SUB_Remove ); - SetNextThink( 0 ); + SetThink ( SUB_Remove ); + pev->nextthink = gpGlobals->time; } else { @@ -701,8 +653,8 @@ void CBaseEntity :: SUB_StartFadeOut ( void ) pev->solid = SOLID_NOT; pev->avelocity = g_vecZero; - SetNextThink( 0.1 ); - SetThink(&CBaseEntity :: SUB_FadeOut ); + pev->nextthink = gpGlobals->time + 0.1; + SetThink ( SUB_FadeOut ); } void CBaseEntity :: SUB_FadeOut ( void ) @@ -710,13 +662,13 @@ void CBaseEntity :: SUB_FadeOut ( void ) if ( pev->renderamt > 7 ) { pev->renderamt -= 7; - SetNextThink( 0.1 ); + pev->nextthink = gpGlobals->time + 0.1; } else { pev->renderamt = 0; - SetNextThink( 0.2 ); - SetThink(&CBaseEntity :: SUB_Remove ); + pev->nextthink = gpGlobals->time + 0.2; + SetThink ( SUB_Remove ); } } @@ -736,8 +688,8 @@ void CGib :: WaitTillLand ( void ) if ( pev->velocity == g_vecZero ) { - SetThink(&CGib ::SUB_StartFadeOut); - SetNextThink( m_lifeTime ); + SetThink (SUB_StartFadeOut); + pev->nextthink = gpGlobals->time + m_lifeTime; // If you bleed, you stink! if ( m_bloodColor != DONT_BLEED ) @@ -749,7 +701,7 @@ void CGib :: WaitTillLand ( void ) else { // wait and check again in another half second. - SetNextThink( 0.5 ); + pev->nextthink = gpGlobals->time + 0.5; } } @@ -779,13 +731,7 @@ void CGib :: BounceGibTouch ( CBaseEntity *pOther ) vecSpot = pev->origin + Vector ( 0 , 0 , 8 );//move up a bit, and trace down. UTIL_TraceLine ( vecSpot, vecSpot + Vector ( 0, 0, -24 ), ignore_monsters, ENT(pev), & tr); - //UTIL_BloodDecalTrace( &tr, m_bloodColor ); - - int blood; - if(m_bloodColor == BLOOD_COLOR_RED)blood = 1; - else if(m_bloodColor == BLOOD_COLOR_YELLOW)blood = 2; - CBaseEntity *pHit = CBaseEntity::Instance( tr.pHit ); - PLAYBACK_EVENT_FULL( FEV_RELIABLE|FEV_GLOBAL, edict(), m_usDecals, 0.0, (float *)&tr.vecEndPos, (float *)&g_vecZero, 0.0, 0.0, pHit->entindex(), blood, 0, 0 ); + UTIL_BloodDecalTrace( &tr, m_bloodColor ); m_cBloodDecals--; } @@ -810,24 +756,18 @@ void CGib :: StickyGibTouch ( CBaseEntity *pOther ) Vector vecSpot; TraceResult tr; - SetThink(&CGib :: SUB_Remove ); - SetNextThink( 10 ); + SetThink ( SUB_Remove ); + pev->nextthink = gpGlobals->time + 10; if ( !FClassnameIs( pOther->pev, "worldspawn" ) ) { - SetNextThink( 0 ); + pev->nextthink = gpGlobals->time; return; } UTIL_TraceLine ( pev->origin, pev->origin + pev->velocity * 32, ignore_monsters, ENT(pev), & tr); - //UTIL_BloodDecalTrace( &tr, m_bloodColor ); - - int blood; - if(m_bloodColor == BLOOD_COLOR_RED)blood = 1; - else if(m_bloodColor == BLOOD_COLOR_YELLOW)blood = 2; - CBaseEntity *pHit = CBaseEntity::Instance( tr.pHit ); - PLAYBACK_EVENT_FULL( FEV_RELIABLE|FEV_GLOBAL, edict(), m_usDecals, 0.0, (float *)&tr.vecEndPos, (float *)&g_vecZero, 0.0, 0.0, pHit->entindex(), blood, 0, 0 ); + UTIL_BloodDecalTrace( &tr, m_bloodColor ); pev->velocity = tr.vecPlaneNormal * -1; pev->angles = UTIL_VecToAngles ( pev->velocity ); @@ -849,18 +789,16 @@ void CGib :: Spawn( const char *szGibModel ) pev->renderamt = 255; pev->rendermode = kRenderNormal; pev->renderfx = kRenderFxNone; - pev->solid = SOLID_TRIGGER; //LRC - so that they don't get in each other's way when we fire lots -// pev->solid = SOLID_SLIDEBOX;/// hopefully this will fix the VELOCITY TOO LOW crap + pev->solid = SOLID_SLIDEBOX;/// hopefully this will fix the VELOCITY TOO LOW crap pev->classname = MAKE_STRING("gib"); - SetObjectClass( ED_NORMAL ); // AutoClassify can't determine gibs properly SET_MODEL(ENT(pev), szGibModel); UTIL_SetSize(pev, Vector( 0, 0, 0), Vector(0, 0, 0)); - SetNextThink( 4 ); + pev->nextthink = gpGlobals->time + 4; m_lifeTime = 25; - SetThink(&CGib :: WaitTillLand ); - SetTouch(&CGib :: BounceGibTouch ); + SetThink ( WaitTillLand ); + SetTouch ( BounceGibTouch ); m_material = matNone; m_cBloodDecals = 5;// how many blood decals this gib can place (1 per bounce until none remain). @@ -881,12 +819,6 @@ int CBaseMonster :: TakeHealth (float flHealth, int bitsDamageType) return CBaseEntity::TakeHealth(flHealth, bitsDamageType); } -int CBaseMonster :: TakeArmor (float flArmor ) -{ - if (!pev->takedamage) return 0; - return CBaseEntity::TakeArmor( flArmor ); -} - /* ============ TakeDamage @@ -999,27 +931,6 @@ int CBaseMonster :: TakeDamage( entvars_t *pevInflictor, entvars_t *pevAttacker, // react to the damage (get mad) if ( (pev->flags & FL_MONSTER) && !FNullEnt(pevAttacker) ) { - //LRC - new behaviours, for m_iPlayerReact. - if (pevAttacker->flags & FL_CLIENT) - { - if (m_iPlayerReact == 2) - { - // just get angry. - Remember( bits_MEMORY_PROVOKED ); - } - else if (m_iPlayerReact == 3) - { - // try to decide whether it was deliberate... if I have an enemy, assume it was just crossfire. - if ( m_hEnemy == NULL ) - { - if ( (m_afMemory & bits_MEMORY_SUSPICIOUS) || UTIL_IsFacing( pevAttacker, pev->origin ) ) - Remember( bits_MEMORY_PROVOKED ); - else - Remember( bits_MEMORY_SUSPICIOUS ); - } - } - } - if ( pevAttacker->flags & (FL_MONSTER | FL_CLIENT) ) {// only if the attack was a monster or client! @@ -1327,14 +1238,12 @@ BOOL CBaseEntity :: FVisible ( CBaseEntity *pEntity ) UTIL_TraceLine(vecLookerOrigin, vecTargetOrigin, ignore_monsters, ignore_glass, ENT(pev)/*pentIgnore*/, &tr); - if (tr.flFraction != 1.0 && tr.pHit != ENT(pEntity->pev)) //LRC - added so that monsters can "see" some bsp objects + if (tr.flFraction != 1.0) { -// ALERT(at_console, "can't see \"%s\"\n", STRING(pEntity->pev->classname)); return FALSE;// Line of sight is not established } else { -// ALERT(at_console, "Seen ok\n"); return TRUE;// line of sight is valid. } } @@ -1514,7 +1423,7 @@ void CBaseEntity::FireBullets(ULONG cShots, Vector vecSrc, Vector vecDirShooting case BULLET_MONSTER_9MM: case BULLET_MONSTER_12MM: default: - MESSAGE_BEGIN( MSG_PAS, gmsgTempEntity, vecTracerSrc ); + MESSAGE_BEGIN( MSG_PAS, SVC_TEMPENTITY, vecTracerSrc ); WRITE_BYTE( TE_TRACER ); WRITE_COORD( vecTracerSrc.x ); WRITE_COORD( vecTracerSrc.y ); @@ -1536,8 +1445,7 @@ void CBaseEntity::FireBullets(ULONG cShots, Vector vecSrc, Vector vecDirShooting pEntity->TraceAttack(pevAttacker, iDamage, vecDir, &tr, DMG_BULLET | ((iDamage > 16) ? DMG_ALWAYSGIB : DMG_NEVERGIB) ); TEXTURETYPE_PlaySound(&tr, vecSrc, vecEnd, iBulletType); - PLAYBACK_EVENT_FULL( FEV_RELIABLE|FEV_GLOBAL, edict(), m_usDecals, 0.0, (float *)&tr.vecEndPos, (float *)&g_vecZero, 0.0, 0.0, pEntity->entindex(), 6, 0, 0 ); - //DecalGunshot( &tr, iBulletType ); + DecalGunshot( &tr, iBulletType ); } else switch(iBulletType) { @@ -1546,8 +1454,7 @@ void CBaseEntity::FireBullets(ULONG cShots, Vector vecSrc, Vector vecDirShooting pEntity->TraceAttack(pevAttacker, gSkillData.monDmg9MM, vecDir, &tr, DMG_BULLET); TEXTURETYPE_PlaySound(&tr, vecSrc, vecEnd, iBulletType); - PLAYBACK_EVENT_FULL( FEV_RELIABLE|FEV_GLOBAL, edict(), m_usDecals, 0.0, (float *)&tr.vecEndPos, (float *)&g_vecZero, 0.0, 0.0, pEntity->entindex(), 6, 0, 0 ); - //DecalGunshot( &tr, iBulletType ); + DecalGunshot( &tr, iBulletType ); break; @@ -1555,8 +1462,7 @@ void CBaseEntity::FireBullets(ULONG cShots, Vector vecSrc, Vector vecDirShooting pEntity->TraceAttack(pevAttacker, gSkillData.monDmgMP5, vecDir, &tr, DMG_BULLET); TEXTURETYPE_PlaySound(&tr, vecSrc, vecEnd, iBulletType); - PLAYBACK_EVENT_FULL( FEV_RELIABLE|FEV_GLOBAL, edict(), m_usDecals, 0.0, (float *)&tr.vecEndPos, (float *)&g_vecZero, 0.0, 0.0, pEntity->entindex(), 6, 0, 0 ); - //DecalGunshot( &tr, iBulletType ); + DecalGunshot( &tr, iBulletType ); break; @@ -1565,21 +1471,10 @@ void CBaseEntity::FireBullets(ULONG cShots, Vector vecSrc, Vector vecDirShooting if ( !tracer ) { TEXTURETYPE_PlaySound(&tr, vecSrc, vecEnd, iBulletType); - PLAYBACK_EVENT_FULL( FEV_RELIABLE|FEV_GLOBAL, edict(), m_usDecals, 0.0, (float *)&tr.vecEndPos, (float *)&g_vecZero, 0.0, 0.0, pEntity->entindex(), 6, 0, 0 ); - //DecalGunshot( &tr, iBulletType ); + DecalGunshot( &tr, iBulletType ); } break; - case BULLET_PLAYER_357: - pEntity->TraceAttack(pevAttacker, gSkillData.plrDmg357, vecDir, &tr, DMG_BULLET); - if ( !tracer ) - { - TEXTURETYPE_PlaySound(&tr, vecSrc, vecEnd, iBulletType); - PLAYBACK_EVENT_FULL( FEV_RELIABLE|FEV_GLOBAL, edict(), m_usDecals, 0.0, (float *)&tr.vecEndPos, (float *)&g_vecZero, 0.0, 0.0, pEntity->entindex(), 6, 0, 0 ); - //DecalGunshot( &tr, iBulletType ); - } - break; - case BULLET_NONE: // FIX pEntity->TraceAttack(pevAttacker, 50, vecDir, &tr, DMG_CLUB); TEXTURETYPE_PlaySound(&tr, vecSrc, vecEnd, iBulletType); @@ -1752,13 +1647,7 @@ void CBaseEntity :: TraceBleed( float flDamage, Vector vecDir, TraceResult *ptr, if ( Bloodtr.flFraction != 1.0 ) { - //UTIL_BloodDecalTrace( &Bloodtr, BloodColor() ); - - int blood; - if(BloodColor() == BLOOD_COLOR_RED)blood = 1; - else if(BloodColor() == BLOOD_COLOR_YELLOW)blood = 2; - CBaseEntity *pHit = CBaseEntity::Instance( Bloodtr.pHit ); - PLAYBACK_EVENT_FULL( FEV_RELIABLE|FEV_GLOBAL, edict(), m_usDecals, 0.0, (float *)&Bloodtr.vecEndPos, (float *)&g_vecZero, 0.0, 0.0, pHit->entindex(), blood, 0, 0 ); + UTIL_BloodDecalTrace( &Bloodtr, BloodColor() ); } } } @@ -1797,7 +1686,7 @@ void CBaseMonster :: MakeDamageBloodDecal ( int cCount, float flNoise, TraceResu UTIL_TraceLine( ptr->vecEndPos, ptr->vecEndPos + vecTraceDir * 172, ignore_monsters, ENT(pev), &Bloodtr); /* - MESSAGE_BEGIN( MSG_BROADCAST, gmsgTempEntity ); + MESSAGE_BEGIN( MSG_BROADCAST, SVC_TEMPENTITY ); WRITE_BYTE( TE_SHOWLINE); WRITE_COORD( ptr->vecEndPos.x ); WRITE_COORD( ptr->vecEndPos.y ); @@ -1811,11 +1700,7 @@ void CBaseMonster :: MakeDamageBloodDecal ( int cCount, float flNoise, TraceResu if ( Bloodtr.flFraction != 1.0 ) { - int blood; - if(BloodColor() == BLOOD_COLOR_RED)blood = 1; - else if(BloodColor() == BLOOD_COLOR_YELLOW)blood = 2; - CBaseEntity *pHit = CBaseEntity::Instance( Bloodtr.pHit ); - PLAYBACK_EVENT_FULL( FEV_RELIABLE|FEV_GLOBAL, edict(), m_usDecals, 0.0, (float *)&Bloodtr.vecEndPos, (float *)&g_vecZero, 0.0, 0.0, pHit->entindex(), blood, 0, 0 ); + UTIL_BloodDecalTrace( &Bloodtr, BloodColor() ); } } } diff --git a/spirit/controller.cpp b/dlls/controller.cpp similarity index 89% rename from spirit/controller.cpp rename to dlls/controller.cpp index 7eff4f89..8cf0272a 100644 --- a/spirit/controller.cpp +++ b/dlls/controller.cpp @@ -26,7 +26,6 @@ #include "schedule.h" #include "weapons.h" #include "squadmonster.h" -#include "scripted.h" //========================================================= // Monster's Anim Events Go Here @@ -158,7 +157,7 @@ const char *CController::pDeathSounds[] = //========================================================= int CController :: Classify ( void ) { - return m_iClass?m_iClass:CLASS_ALIEN_MILITARY; + return CLASS_ALIEN_MILITARY; } //========================================================= @@ -270,12 +269,11 @@ void CController :: HandleAnimEvent( MonsterEvent_t *pEvent ) { case CONTROLLER_AE_HEAD_OPEN: { - //ALERT(at_console,"Controller Head Open\n"); Vector vecStart, angleGun; GetAttachment( 0, vecStart, angleGun ); - MESSAGE_BEGIN( MSG_BROADCAST, gmsgTempEntity ); + MESSAGE_BEGIN( MSG_BROADCAST, SVC_TEMPENTITY ); WRITE_BYTE( TE_ELIGHT ); WRITE_SHORT( entindex( ) + 0x1000 ); // entity, attachment WRITE_COORD( vecStart.x ); // origin @@ -299,12 +297,11 @@ void CController :: HandleAnimEvent( MonsterEvent_t *pEvent ) case CONTROLLER_AE_BALL_SHOOT: { - //ALERT(at_console,"Controller Ball Shoot\n"); Vector vecStart, angleGun; GetAttachment( 0, vecStart, angleGun ); - MESSAGE_BEGIN( MSG_BROADCAST, gmsgTempEntity ); + MESSAGE_BEGIN( MSG_BROADCAST, SVC_TEMPENTITY ); WRITE_BYTE( TE_ELIGHT ); WRITE_SHORT( entindex( ) + 0x1000 ); // entity, attachment WRITE_COORD( 0 ); // origin @@ -321,14 +318,7 @@ void CController :: HandleAnimEvent( MonsterEvent_t *pEvent ) CBaseMonster *pBall = (CBaseMonster*)Create( "controller_head_ball", vecStart, pev->angles, edict() ); pBall->pev->velocity = Vector( 0, 0, 32 ); - if (m_pCine) - { - pBall->m_hEnemy = m_hTargetEnt; - } - else - { - pBall->m_hEnemy = m_hEnemy; - } + pBall->m_hEnemy = m_hEnemy; m_iBall[0] = 0; m_iBall[1] = 0; @@ -337,7 +327,6 @@ void CController :: HandleAnimEvent( MonsterEvent_t *pEvent ) case CONTROLLER_AE_SMALL_SHOOT: { - //ALERT(at_console,"Controller Small Shoot\n"); AttackSound( ); m_flShootTime = gpGlobals->time; m_flShootEnd = m_flShootTime + atoi( pEvent->options ) / 15.0; @@ -345,7 +334,6 @@ void CController :: HandleAnimEvent( MonsterEvent_t *pEvent ) break; case CONTROLLER_AE_POWERUP_FULL: { - //ALERT(at_console,"Controller Powerup Full\n"); m_iBall[0] = 255; m_iBallTime[0] = gpGlobals->time + atoi( pEvent->options ) / 15.0; m_iBall[1] = 255; @@ -354,7 +342,6 @@ void CController :: HandleAnimEvent( MonsterEvent_t *pEvent ) break; case CONTROLLER_AE_POWERUP_HALF: { - //ALERT(at_console,"Controller Powerup Half\n"); m_iBall[0] = 192; m_iBallTime[0] = gpGlobals->time + atoi( pEvent->options ) / 15.0; m_iBall[1] = 192; @@ -374,18 +361,14 @@ void CController :: Spawn() { Precache( ); - if (pev->model) - SET_MODEL(ENT(pev), STRING(pev->model)); //LRC - else - SET_MODEL(ENT(pev), "models/controller.mdl"); + SET_MODEL(ENT(pev), "models/controller.mdl"); UTIL_SetSize( pev, Vector( -32, -32, 0 ), Vector( 32, 32, 64 )); pev->solid = SOLID_SLIDEBOX; pev->movetype = MOVETYPE_FLY; pev->flags |= FL_FLY; m_bloodColor = BLOOD_COLOR_GREEN; - if (pev->health == 0) - pev->health = gSkillData.controllerHealth; + pev->health = gSkillData.controllerHealth; pev->view_ofs = Vector( 0, 0, -2 );// position of the eyes relative to monster's origin. m_flFieldOfView = VIEW_FIELD_FULL;// indicates the width of this monster's forward view cone ( as a dotproduct result ) m_MonsterState = MONSTERSTATE_NONE; @@ -398,10 +381,7 @@ void CController :: Spawn() //========================================================= void CController :: Precache() { - if (pev->model) - PRECACHE_MODEL((char*)STRING(pev->model)); //LRC - else - PRECACHE_MODEL("models/controller.mdl"); + PRECACHE_MODEL("models/controller.mdl"); PRECACHE_SOUND_ARRAY( pAttackSounds ); PRECACHE_SOUND_ARRAY( pIdleSounds ); @@ -659,33 +639,17 @@ void CController :: RunTask ( Task_t *pTask ) Vector vecSrc = vecHand + pev->velocity * (m_flShootTime - gpGlobals->time); Vector vecDir; - if (m_pCine != NULL || m_hEnemy != NULL) + if (m_hEnemy != NULL) { - if (m_pCine != NULL) // LRC- is this a script that's telling it to fire? + if (HasConditions( bits_COND_SEE_ENEMY )) { - if (m_hTargetEnt != NULL && m_pCine->PreciseAttack()) - { - vecDir = (m_hTargetEnt->pev->origin - pev->origin).Normalize() * gSkillData.controllerSpeedBall; - } - else - { - UTIL_MakeVectors(pev->angles); - vecDir = gpGlobals->v_forward * gSkillData.controllerSpeedBall; - } + m_vecEstVelocity = m_vecEstVelocity * 0.5 + m_hEnemy->pev->velocity * 0.5; } - else if (m_hEnemy != NULL) + else { - if (HasConditions( bits_COND_SEE_ENEMY )) - { - m_vecEstVelocity = m_vecEstVelocity * 0.5 + m_hEnemy->pev->velocity * 0.5; - } - else - { - m_vecEstVelocity = m_vecEstVelocity * 0.8; - } - vecDir = Intersect( vecSrc, m_hEnemy->BodyTarget( pev->origin ), m_vecEstVelocity, gSkillData.controllerSpeedBall ); + m_vecEstVelocity = m_vecEstVelocity * 0.8; } - + vecDir = Intersect( vecSrc, m_hEnemy->BodyTarget( pev->origin ), m_vecEstVelocity, gSkillData.controllerSpeedBall ); float delta = 0.03490; // +-2 degree vecDir = vecDir + Vector( RANDOM_FLOAT( -delta, delta ), RANDOM_FLOAT( -delta, delta ), RANDOM_FLOAT( -delta, delta ) ) * gSkillData.controllerSpeedBall; @@ -901,9 +865,9 @@ void CController :: RunAI( void ) m_pBall[i]->SetBrightness( m_iBallCurrent[i] ); GetAttachment( i + 2, vecStart, angleGun ); - UTIL_SetOrigin( m_pBall[i], vecStart ); + UTIL_SetOrigin( m_pBall[i]->pev, vecStart ); - MESSAGE_BEGIN( MSG_BROADCAST, gmsgTempEntity ); + MESSAGE_BEGIN( MSG_BROADCAST, SVC_TEMPENTITY ); WRITE_BYTE( TE_ELIGHT ); WRITE_SHORT( entindex( ) + 0x1000 * (i + 3) ); // entity, attachment WRITE_COORD( vecStart.x ); // origin @@ -1155,12 +1119,13 @@ void CController::MoveExecute( CBaseEntity *pTargetEnt, const Vector &vecDir, fl // ALERT( at_console, "move %.4f %.4f %.4f : %f\n", vecDir.x, vecDir.y, vecDir.z, flInterval ); - //float flTotal = m_flGroundSpeed * pev->framerate * flInterval; - //UTIL_MoveToOrigin ( ENT(pev), m_Route[ m_iRouteIndex ].vecLocation, flTotal, MOVE_STRAFE ); + // float flTotal = m_flGroundSpeed * pev->framerate * flInterval; + // UTIL_MoveToOrigin ( ENT(pev), m_Route[ m_iRouteIndex ].vecLocation, flTotal, MOVE_STRAFE ); m_velocity = m_velocity * 0.8 + m_flGroundSpeed * vecDir * 0.2; UTIL_MoveToOrigin ( ENT(pev), pev->origin + m_velocity, m_velocity.Length() * flInterval, MOVE_STRAFE ); + } @@ -1203,14 +1168,14 @@ void CControllerHeadBall :: Spawn( void ) pev->scale = 2.0; UTIL_SetSize(pev, Vector( 0, 0, 0), Vector(0, 0, 0)); - UTIL_SetOrigin( this, pev->origin ); + UTIL_SetOrigin( pev, pev->origin ); - SetThink(&CControllerHeadBall :: HuntThink ); - SetTouch(&CControllerHeadBall :: BounceTouch ); + SetThink( HuntThink ); + SetTouch( BounceTouch ); m_vecIdeal = Vector( 0, 0, 0 ); - SetNextThink( 0.1 ); + pev->nextthink = gpGlobals->time + 0.1; m_hOwner = Instance( pev->owner ); pev->dmgtime = gpGlobals->time; @@ -1227,11 +1192,11 @@ void CControllerHeadBall :: Precache( void ) void CControllerHeadBall :: HuntThink( void ) { - SetNextThink( 0.1 ); + pev->nextthink = gpGlobals->time + 0.1; pev->renderamt -= 5; - MESSAGE_BEGIN( MSG_BROADCAST, gmsgTempEntity ); + MESSAGE_BEGIN( MSG_BROADCAST, SVC_TEMPENTITY ); WRITE_BYTE( TE_ELIGHT ); WRITE_SHORT( entindex( ) ); // entity, attachment WRITE_COORD( pev->origin.x ); // origin @@ -1269,7 +1234,7 @@ void CControllerHeadBall :: HuntThink( void ) ApplyMultiDamage( pev, m_hOwner->pev ); } - MESSAGE_BEGIN( MSG_BROADCAST, gmsgTempEntity ); + MESSAGE_BEGIN( MSG_BROADCAST, SVC_TEMPENTITY ); WRITE_BYTE( TE_BEAMENTPOINT ); WRITE_SHORT( entindex() ); WRITE_COORD( tr.vecEndPos.x ); @@ -1292,8 +1257,8 @@ void CControllerHeadBall :: HuntThink( void ) m_flNextAttack = gpGlobals->time + 3.0; - SetThink(&CControllerHeadBall :: DieThink ); - SetNextThink( 0.3 ); + SetThink( DieThink ); + pev->nextthink = gpGlobals->time + 0.3; } // Crawl( ); @@ -1332,7 +1297,7 @@ void CControllerHeadBall :: Crawl( void ) Vector vecAim = Vector( RANDOM_FLOAT( -1, 1 ), RANDOM_FLOAT( -1, 1 ), RANDOM_FLOAT( -1, 1 ) ).Normalize( ); Vector vecPnt = pev->origin + pev->velocity * 0.3 + vecAim * 64; - MESSAGE_BEGIN( MSG_BROADCAST, gmsgTempEntity ); + MESSAGE_BEGIN( MSG_BROADCAST, SVC_TEMPENTITY ); WRITE_BYTE( TE_BEAMENTPOINT ); WRITE_SHORT( entindex() ); WRITE_COORD( vecPnt.x); @@ -1397,14 +1362,14 @@ void CControllerZapBall :: Spawn( void ) pev->scale = 0.5; UTIL_SetSize(pev, Vector( 0, 0, 0), Vector(0, 0, 0)); - UTIL_SetOrigin( this, pev->origin ); + UTIL_SetOrigin( pev, pev->origin ); - SetThink(&CControllerZapBall :: AnimateThink ); - SetTouch(&CControllerZapBall :: ExplodeTouch ); + SetThink( AnimateThink ); + SetTouch( ExplodeTouch ); m_hOwner = Instance( pev->owner ); pev->dmgtime = gpGlobals->time; // keep track of when ball spawned - SetNextThink( 0.1 ); + pev->nextthink = gpGlobals->time + 0.1; } @@ -1418,7 +1383,7 @@ void CControllerZapBall :: Precache( void ) void CControllerZapBall :: AnimateThink( void ) { - SetNextThink( 0.1 ); + pev->nextthink = gpGlobals->time + 0.1; pev->frame = ((int)pev->frame + 1) % 11; diff --git a/spirit/crossbow.cpp b/dlls/crossbow.cpp similarity index 61% rename from spirit/crossbow.cpp rename to dlls/crossbow.cpp index 34052731..5fe0a788 100644 --- a/spirit/crossbow.cpp +++ b/dlls/crossbow.cpp @@ -12,6 +12,7 @@ * without written permission from Valve LLC. * ****/ +#if !defined( OEM_BUILD ) && !defined( HLDEMO_BUILD ) #include "extdll.h" #include "util.h" @@ -22,9 +23,15 @@ #include "player.h" #include "gamerules.h" +#ifndef CLIENT_DLL #define BOLT_AIR_VELOCITY 2000 #define BOLT_WATER_VELOCITY 1000 +// UNDONE: Save/restore this? Don't forget to set classname and LINK_ENTITY_TO_CLASS() +// +// OVERLOADS SOME ENTVARS: +// +// speed - the ideal magnitude of my velocity class CCrossbowBolt : public CBaseEntity { void Spawn( void ); @@ -61,12 +68,12 @@ void CCrossbowBolt::Spawn( ) SET_MODEL(ENT(pev), "models/crossbow_bolt.mdl"); - UTIL_SetOrigin( this, pev->origin ); + UTIL_SetOrigin( pev, pev->origin ); UTIL_SetSize(pev, Vector(0, 0, 0), Vector(0, 0, 0)); - SetTouch(&CCrossbowBolt:: BoltTouch ); - SetThink(&CCrossbowBolt:: BubbleThink ); - SetNextThink( 0.2 ); + SetTouch( BoltTouch ); + SetThink( BubbleThink ); + pev->nextthink = gpGlobals->time + 0.2; } @@ -123,7 +130,7 @@ void CCrossbowBolt::BoltTouch( CBaseEntity *pOther ) EMIT_SOUND(ENT(pev), CHAN_BODY, "weapons/xbow_hitbod2.wav", 1, ATTN_NORM); break; } - if ( !IsMultiplayer() ) + if ( !g_pGameRules->IsMultiplayer() ) { Killed( pev, GIB_NEVER ); } @@ -132,28 +139,22 @@ void CCrossbowBolt::BoltTouch( CBaseEntity *pOther ) { EMIT_SOUND_DYN(ENT(pev), CHAN_BODY, "weapons/xbow_hit1.wav", RANDOM_FLOAT(0.95, 1.0), ATTN_NORM, 0, 98 + RANDOM_LONG(0,7)); - SetThink(&CCrossbowBolt:: SUB_Remove ); - SetNextThink( 10 );// this will get changed below if the bolt is allowed to stick in what it hit. + SetThink( SUB_Remove ); + pev->nextthink = gpGlobals->time;// this will get changed below if the bolt is allowed to stick in what it hit. - // if what we hit is static architecture, can stay around for a while. - Vector vecDir = pev->velocity.Normalize( ); - UTIL_SetOrigin( this, pev->origin - vecDir * 12 ); - pev->angles = UTIL_VecToAngles( vecDir ); - pev->solid = SOLID_NOT; - pev->movetype = MOVETYPE_FLY; - pev->velocity = Vector( 0, 0, 0 ); - pev->avelocity.z = 0; - pev->angles.z = RANDOM_LONG(0,360); - - if( pOther->IsBSPModel()) - { - // TEST compound - pev->movetype = MOVETYPE_COMPOUND; - pev->aiment = pOther->edict(); - pev->effects |= EF_NOINTERP; - -// SetParent( pOther );//glue bolt with parent system - } + if ( FClassnameIs( pOther->pev, "worldspawn" ) ) + { + // if what we hit is static architecture, can stay around for a while. + Vector vecDir = pev->velocity.Normalize( ); + UTIL_SetOrigin( pev, pev->origin - vecDir * 12 ); + pev->angles = UTIL_VecToAngles( vecDir ); + pev->solid = SOLID_NOT; + pev->movetype = MOVETYPE_FLY; + pev->velocity = Vector( 0, 0, 0 ); + pev->avelocity.z = 0; + pev->angles.z = RANDOM_LONG(0,360); + pev->nextthink = gpGlobals->time + 10.0; + } if (UTIL_PointContents(pev->origin) != CONTENTS_WATER) { @@ -161,18 +162,18 @@ void CCrossbowBolt::BoltTouch( CBaseEntity *pOther ) } } - if ( IsMultiplayer() ) + if ( g_pGameRules->IsMultiplayer() ) { - SetThink(&CCrossbowBolt:: ExplodeThink ); - SetNextThink( 0.1 ); + SetThink( ExplodeThink ); + pev->nextthink = gpGlobals->time + 0.1; } } void CCrossbowBolt::BubbleThink( void ) { - SetNextThink( 0.1 ); + pev->nextthink = gpGlobals->time + 0.1; - if (pev->waterlevel == 0 || pev->watertype <= CONTENTS_FLYFIELD) + if (pev->waterlevel == 0) return; UTIL_BubbleTrail( pev->origin - pev->velocity * 0.1, pev->origin, 1 ); @@ -186,7 +187,7 @@ void CCrossbowBolt::ExplodeThink( void ) pev->dmg = 40; iScale = 10; - MESSAGE_BEGIN( MSG_PVS, gmsgTempEntity, pev->origin ); + MESSAGE_BEGIN( MSG_PVS, SVC_TEMPENTITY, pev->origin ); WRITE_BYTE( TE_EXPLOSION); WRITE_COORD( pev->origin.x ); WRITE_COORD( pev->origin.y ); @@ -217,39 +218,23 @@ void CCrossbowBolt::ExplodeThink( void ) UTIL_Remove(this); } +#endif enum crossbow_e { CROSSBOW_IDLE1 = 0, // full - CROSSBOW_IDLE2, // empty + CROSSBOW_IDLE2, // empty CROSSBOW_FIDGET1, // full - CROSSBOW_FIRE, // full - CROSSBOW_FIRE_LAST, // to empty + CROSSBOW_FIDGET2, // empty + CROSSBOW_FIRE1, // full + CROSSBOW_FIRE2, // reload + CROSSBOW_FIRE3, // empty CROSSBOW_RELOAD, // from empty - CROSSBOW_DRAW1, // full - CROSSBOW_DRAW2, // empty + CROSSBOW_DRAW1, // full + CROSSBOW_DRAW2, // empty CROSSBOW_HOLSTER1, // full CROSSBOW_HOLSTER2, // empty }; -class CCrossbow : public CBasePlayerWeapon -{ -public: - void Spawn( void ); - void Precache( void ); - int GetItemInfo(ItemInfo *p); - void FireBolt( void ); - void FireSniperBolt( void ); - void PrimaryAttack( void ); - void SecondaryAttack( void ) { m_flNextSecondaryAttack = UTIL_WeaponTimeBase() + 1E6; };//just stub - BOOL Deploy( ); - void Holster( ); - BOOL ShouldWeaponIdle( void ) { return TRUE; }; - void Reload( void ); - void WeaponIdle( void ); - void ZoomUpdate( void ); - void ZoomReset( void ); - BOOL b_setup; -}; LINK_ENTITY_TO_CLASS( weapon_crossbow, CCrossbow ); void CCrossbow::Spawn( ) @@ -263,6 +248,18 @@ void CCrossbow::Spawn( ) FallInit();// get ready to fall down. } +int CCrossbow::AddToPlayer( CBasePlayer *pPlayer ) +{ + if ( CBasePlayerWeapon::AddToPlayer( pPlayer ) ) + { + MESSAGE_BEGIN( MSG_ONE, gmsgWeapPickup, NULL, pPlayer->pev ); + WRITE_BYTE( m_iId ); + MESSAGE_END(); + return TRUE; + } + return FALSE; +} + void CCrossbow::Precache( void ) { PRECACHE_MODEL("models/w_crossbow.mdl"); @@ -273,6 +270,9 @@ void CCrossbow::Precache( void ) PRECACHE_SOUND("weapons/xbow_reload1.wav"); UTIL_PrecacheOther( "crossbow_bolt" ); + + m_usCrossbow = PRECACHE_EVENT( 1, "events/crossbow1.sc" ); + m_usCrossbow2 = PRECACHE_EVENT( 1, "events/crossbow2.sc" ); } @@ -300,19 +300,30 @@ BOOL CCrossbow::Deploy( ) return DefaultDeploy( "models/v_crossbow.mdl", "models/p_crossbow.mdl", CROSSBOW_DRAW2, "bow" ); } -void CCrossbow::Holster( ) +void CCrossbow::Holster( int skiplocal /* = 0 */ ) { m_fInReload = FALSE;// cancel any reload in progress. - m_pPlayer->m_flNextAttack = UTIL_WeaponTimeBase() + 0.5; - ZoomReset(); - if (m_iClip) SendWeaponAnim( CROSSBOW_HOLSTER1 ); - else SendWeaponAnim( CROSSBOW_HOLSTER2 ); + if ( m_fInZoom ) + { + SecondaryAttack( ); + } + + m_pPlayer->m_flNextAttack = UTIL_WeaponTimeBase() + 0.5; + if (m_iClip) + SendWeaponAnim( CROSSBOW_HOLSTER1 ); + else + SendWeaponAnim( CROSSBOW_HOLSTER2 ); } void CCrossbow::PrimaryAttack( void ) { - if ( m_iChargeLevel && IsMultiplayer() ) + +#ifdef CLIENT_DLL + if ( m_fInZoom && bIsMultiplayer() ) +#else + if ( m_fInZoom && g_pGameRules->IsMultiplayer() ) +#endif { FireSniperBolt(); return; @@ -337,15 +348,14 @@ void CCrossbow::FireSniperBolt() m_pPlayer->m_iWeaponVolume = QUIET_GUN_VOLUME; m_iClip--; - // make twang sound - EMIT_SOUND_DYN(ENT(m_pPlayer->pev), CHAN_WEAPON, "weapons/xbow_fire1.wav", RANDOM_FLOAT(0.95, 1.0), ATTN_NORM, 0, 93 + RANDOM_LONG(0,0xF)); + int flags; +#if defined( CLIENT_WEAPONS ) + flags = FEV_NOTHOST; +#else + flags = 0; +#endif - if (m_iClip) - { - SendWeaponAnim( CROSSBOW_FIRE ); - m_iBody++; - } - else SendWeaponAnim( CROSSBOW_FIRE_LAST ); + PLAYBACK_EVENT_FULL( flags, m_pPlayer->edict(), m_usCrossbow2, 0.0, (float *)&g_vecZero, (float *)&g_vecZero, 0, 0, m_iClip, m_pPlayer->m_rgAmmo[m_iPrimaryAmmoType], 0, 0 ); // player "shoot" animation m_pPlayer->SetAnimation( PLAYER_ATTACK1 ); @@ -357,45 +367,14 @@ void CCrossbow::FireSniperBolt() UTIL_TraceLine(vecSrc, vecSrc + vecDir * 8192, dont_ignore_monsters, m_pPlayer->edict(), &tr); +#ifndef CLIENT_DLL if ( tr.pHit->v.takedamage ) { - switch( RANDOM_LONG(0,1) ) - { - case 0: EMIT_SOUND( tr.pHit, CHAN_BODY, "weapons/xbow_hitbod1.wav", 1, ATTN_NORM); break; - case 1: EMIT_SOUND( tr.pHit, CHAN_BODY, "weapons/xbow_hitbod2.wav", 1, ATTN_NORM); break; - } - ClearMultiDamage( ); CBaseEntity::Instance(tr.pHit)->TraceAttack(m_pPlayer->pev, 120, vecDir, &tr, DMG_BULLET | DMG_NEVERGIB ); ApplyMultiDamage( pev, m_pPlayer->pev ); } - else - { - // create a bolt - CCrossbowBolt *pBolt = CCrossbowBolt::BoltCreate(); - pBolt->pev->origin = tr.vecEndPos - vecDir * 10; - pBolt->pev->angles = UTIL_VecToAngles( vecDir ); - pBolt->pev->solid = SOLID_NOT; - pBolt->SetTouch( NULL ); - pBolt->SetThink( SUB_Remove ); - - EMIT_SOUND( pBolt->edict(), CHAN_WEAPON, "weapons/xbow_hit1.wav", RANDOM_FLOAT(0.95, 1.0), ATTN_NORM ); - - if (UTIL_PointContents(tr.vecEndPos) != CONTENTS_WATER) - { - UTIL_Sparks( tr.vecEndPos ); - } - - if ( FClassnameIs( tr.pHit, "worldspawn" ) ) - { - // let the bolt sit around for a while if it hit static architecture - pBolt->pev->nextthink = gpGlobals->time + 5.0; - } - else - { - pBolt->pev->nextthink = gpGlobals->time; - } - } +#endif } void CCrossbow::FireBolt() @@ -412,15 +391,14 @@ void CCrossbow::FireBolt() m_iClip--; - // make twang sound - EMIT_SOUND_DYN(ENT(m_pPlayer->pev), CHAN_WEAPON, "weapons/xbow_fire1.wav", RANDOM_FLOAT(0.95, 1.0), ATTN_NORM, 0, 93 + RANDOM_LONG(0,0xF)); + int flags; +#if defined( CLIENT_WEAPONS ) + flags = FEV_NOTHOST; +#else + flags = 0; +#endif - if (m_iClip) - { - SendWeaponAnim( CROSSBOW_FIRE ); - m_iBody++; - } - else SendWeaponAnim( CROSSBOW_FIRE_LAST ); + PLAYBACK_EVENT_FULL( flags, m_pPlayer->edict(), m_usCrossbow, 0.0, (float *)&g_vecZero, (float *)&g_vecZero, 0, 0, m_iClip, m_pPlayer->m_rgAmmo[m_iPrimaryAmmoType], 0, 0 ); // player "shoot" animation m_pPlayer->SetAnimation( PLAYER_ATTACK1 ); @@ -432,12 +410,13 @@ void CCrossbow::FireBolt() Vector vecSrc = m_pPlayer->GetGunPosition( ) - gpGlobals->v_up * 2; Vector vecDir = gpGlobals->v_forward; +#ifndef CLIENT_DLL CCrossbowBolt *pBolt = CCrossbowBolt::BoltCreate(); pBolt->pev->origin = vecSrc; pBolt->pev->angles = anglesAim; pBolt->pev->owner = m_pPlayer->edict(); - if (m_pPlayer->pev->waterlevel == 3 && m_pPlayer->pev->watertype > CONTENTS_FLYFIELD) + if (m_pPlayer->pev->waterlevel == 3) { pBolt->pev->velocity = vecDir * BOLT_WATER_VELOCITY; pBolt->pev->speed = BOLT_WATER_VELOCITY; @@ -448,71 +427,67 @@ void CCrossbow::FireBolt() pBolt->pev->speed = BOLT_AIR_VELOCITY; } pBolt->pev->avelocity.z = 10; +#endif if (!m_iClip && m_pPlayer->m_rgAmmo[m_iPrimaryAmmoType] <= 0) + // HEV suit - indicate out of ammo condition m_pPlayer->SetSuitUpdate("!HEV_AMO0", FALSE, 0); - m_pPlayer->m_flNextAttack = UTIL_WeaponTimeBase() + 0.75; + m_flNextPrimaryAttack = UTIL_WeaponTimeBase() + 0.75; + + m_flNextSecondaryAttack = UTIL_WeaponTimeBase() + 0.75; if (m_iClip != 0) m_flTimeWeaponIdle = UTIL_WeaponTimeBase() + 5.0; - else m_flTimeWeaponIdle = UTIL_WeaponTimeBase() + 0.75; + else + m_flTimeWeaponIdle = UTIL_WeaponTimeBase() + 0.75; +} + + +void CCrossbow::SecondaryAttack() +{ + if ( m_pPlayer->pev->fov != 0 ) + { + m_pPlayer->pev->fov = m_pPlayer->m_iFOV = 0; // 0 means reset to default fov + m_fInZoom = 0; + } + else if ( m_pPlayer->pev->fov != 20 ) + { + m_pPlayer->pev->fov = m_pPlayer->m_iFOV = 20; + m_fInZoom = 1; + } + + pev->nextthink = UTIL_WeaponTimeBase() + 0.1; + m_flNextSecondaryAttack = UTIL_WeaponTimeBase() + 1.0; } void CCrossbow::Reload( void ) { - if (m_iClip) return; - if ( m_iChargeLevel ) ZoomReset(); - m_iBody = 0;//show full - if ( DefaultReload( 5, CROSSBOW_RELOAD, 4.7 ) ) + if ( m_pPlayer->ammo_bolts <= 0 ) + return; + + if ( m_pPlayer->pev->fov != 0 ) + { + SecondaryAttack(); + } + + if ( DefaultReload( 5, CROSSBOW_RELOAD, 4.5 ) ) { EMIT_SOUND_DYN(ENT(m_pPlayer->pev), CHAN_ITEM, "weapons/xbow_reload1.wav", RANDOM_FLOAT(0.95, 1.0), ATTN_NORM, 0, 93 + RANDOM_LONG(0,0xF)); } } -void CCrossbow :: ZoomUpdate( void ) -{ - if (m_pPlayer->pev->button & IN_ATTACK2) - { - if(m_iChargeLevel == 0) - { - if (m_flShockTime > UTIL_WeaponTimeBase()) return; - m_iChargeLevel = 1; - m_flTimeUpdate = UTIL_WeaponTimeBase() + 0.5; - } - if(m_iChargeLevel == 1) - { - m_pPlayer->pev->fov = 50; - m_iChargeLevel = 2;//ready to zooming, wait for 0.5 secs - } - - if (m_flTimeUpdate > UTIL_WeaponTimeBase()) return; - if (m_iChargeLevel == 2 && m_pPlayer->pev->fov > 20) - { - m_pPlayer->pev->fov--; - m_flTimeUpdate = UTIL_WeaponTimeBase() + 0.02; - } - if(m_iChargeLevel == 3) ZoomReset(); - } - else if(m_iChargeLevel > 1) m_iChargeLevel = 3; -} - -void CCrossbow::ZoomReset( void ) -{ - m_flShockTime = UTIL_WeaponTimeBase() + 0.5; - m_pPlayer->pev->fov = 90; - m_iChargeLevel = 0;//clear zoom -} void CCrossbow::WeaponIdle( void ) { m_pPlayer->GetAutoaimVector( AUTOAIM_2DEGREES ); // get the autoaim vector but ignore it; used for autoaim crosshair in DM - ZoomUpdate(); - if (m_flTimeWeaponIdle < UTIL_WeaponTimeBase()) + ResetEmptySound( ); + + if ( m_flTimeWeaponIdle < UTIL_WeaponTimeBase() ) { - float flRand = RANDOM_FLOAT(0, 1); + float flRand = UTIL_SharedRandomFloat( m_pPlayer->random_seed, 0, 1 ); if (flRand <= 0.75) { if (m_iClip) @@ -523,12 +498,20 @@ void CCrossbow::WeaponIdle( void ) { SendWeaponAnim( CROSSBOW_IDLE2 ); } - m_flTimeWeaponIdle = UTIL_WeaponTimeBase() + RANDOM_FLOAT ( 10, 15 ); + m_flTimeWeaponIdle = UTIL_WeaponTimeBase() + UTIL_SharedRandomFloat( m_pPlayer->random_seed, 10, 15 ); } - else if (m_iClip) + else { - SendWeaponAnim( CROSSBOW_FIDGET1 ); - m_flTimeWeaponIdle = UTIL_WeaponTimeBase() + 90.0 / 30.0; + if (m_iClip) + { + SendWeaponAnim( CROSSBOW_FIDGET1 ); + m_flTimeWeaponIdle = UTIL_WeaponTimeBase() + 90.0 / 30.0; + } + else + { + SendWeaponAnim( CROSSBOW_FIDGET2 ); + m_flTimeWeaponIdle = UTIL_WeaponTimeBase() + 80.0 / 30.0; + } } } } @@ -559,3 +542,7 @@ class CCrossbowAmmo : public CBasePlayerAmmo } }; LINK_ENTITY_TO_CLASS( ammo_crossbow, CCrossbowAmmo ); + + + +#endif \ No newline at end of file diff --git a/spirit/crowbar.cpp b/dlls/crowbar.cpp similarity index 60% rename from spirit/crowbar.cpp rename to dlls/crowbar.cpp index 6251b1ff..f301de7f 100644 --- a/spirit/crowbar.cpp +++ b/dlls/crowbar.cpp @@ -26,28 +26,11 @@ #define CROWBAR_BODYHIT_VOLUME 128 #define CROWBAR_WALLHIT_VOLUME 512 -class CCrowbar : public CBasePlayerWeapon -{ -public: - void Spawn( void ); - void Precache( void ); - int GetItemInfo(ItemInfo *p); - - void PrimaryAttack( void ); - void SecondaryAttack( void ); - int Swing( int fFirst ); - BOOL Deploy( void ); - void Holster( ); - void WeaponIdle( void ); - BOOL ShouldWeaponIdle( void ) { return TRUE; }; - int m_iSwing; - BOOL bHit; -private: - unsigned int m_usCrowbar; -}; LINK_ENTITY_TO_CLASS( weapon_crowbar, CCrowbar ); -enum crowbar_e { + + +enum gauss_e { CROWBAR_IDLE = 0, CROWBAR_DRAW, CROWBAR_HOLSTER, @@ -66,6 +49,7 @@ void CCrowbar::Spawn( ) m_iId = WEAPON_CROWBAR; SET_MODEL(ENT(pev), "models/w_crowbar.mdl"); m_iClip = -1; + FallInit();// get ready to fall down. } @@ -82,7 +66,7 @@ void CCrowbar::Precache( void ) PRECACHE_SOUND("weapons/cbar_hitbod3.wav"); PRECACHE_SOUND("weapons/cbar_miss1.wav"); - m_usCrowbar = PRECACHE_EVENT ( 1, "evCrowbar" ); + m_usCrowbar = PRECACHE_EVENT ( 1, "events/crowbar.sc" ); } int CCrowbar::GetItemInfo(ItemInfo *p) @@ -107,7 +91,7 @@ BOOL CCrowbar::Deploy( ) return DefaultDeploy( "models/v_crowbar.mdl", "models/p_crowbar.mdl", CROWBAR_DRAW, "crowbar" ); } -void CCrowbar::Holster( ) +void CCrowbar::Holster( int skiplocal /* = 0 */ ) { m_pPlayer->m_flNextAttack = UTIL_WeaponTimeBase() + 0.5; SendWeaponAnim( CROWBAR_HOLSTER ); @@ -161,32 +145,39 @@ void FindHullIntersection( const Vector &vecSrc, TraceResult &tr, float *mins, f void CCrowbar::PrimaryAttack() { - if (!Swing(TRUE)) Swing (FALSE); - m_flTimeUpdate = UTIL_WeaponTimeBase() + 0.2; + if (! Swing( 1 )) + { + SetThink( SwingAgain ); + pev->nextthink = gpGlobals->time + 0.1; + } } -void CCrowbar::SecondaryAttack() + +void CCrowbar::Smack( ) { - if (!Swing(TRUE)) Swing (FALSE); - m_flTimeUpdate = UTIL_WeaponTimeBase() + 0.2; + DecalGunshot( &m_trHit, BULLET_PLAYER_CROWBAR ); } + +void CCrowbar::SwingAgain( void ) +{ + Swing( 0 ); +} + + int CCrowbar::Swing( int fFirst ) { int fDidHit = FALSE; - bHit = FALSE; + TraceResult tr; - if ( m_flTimeUpdate > UTIL_WeaponTimeBase() ) return fDidHit; - - m_flTimeWeaponIdle = UTIL_WeaponTimeBase() + 3; - UTIL_MakeVectors (m_pPlayer->pev->v_angle); Vector vecSrc = m_pPlayer->GetGunPosition( ); Vector vecEnd = vecSrc + gpGlobals->v_forward * 32; UTIL_TraceLine( vecSrc, vecEnd, dont_ignore_monsters, ENT( m_pPlayer->pev ), &tr ); +#ifndef CLIENT_DLL if ( tr.flFraction >= 1.0 ) { UTIL_TraceHull( vecSrc, vecEnd, dont_ignore_monsters, head_hull, ENT( m_pPlayer->pev ), &tr ); @@ -200,21 +191,57 @@ int CCrowbar::Swing( int fFirst ) vecEnd = tr.vecEndPos; // This is the point on the actual surface (the hull could have hit space) } } +#endif - if ( tr.flFraction >= 1.0 && fFirst) m_pPlayer->SetAnimation( PLAYER_ATTACK1 ); + PLAYBACK_EVENT_FULL( FEV_NOTHOST, m_pPlayer->edict(), m_usCrowbar, + 0.0, (float *)&g_vecZero, (float *)&g_vecZero, 0, 0, 0, + 0.0, 0, 0.0 ); + + + if ( tr.flFraction >= 1.0 ) + { + if (fFirst) + { + // miss + m_flNextPrimaryAttack = UTIL_WeaponTimeBase() + 0.5; + + // player "shoot" animation + m_pPlayer->SetAnimation( PLAYER_ATTACK1 ); + } + } else { + switch( ((m_iSwing++) % 2) + 1 ) + { + case 0: + SendWeaponAnim( CROWBAR_ATTACK1HIT ); break; + case 1: + SendWeaponAnim( CROWBAR_ATTACK2HIT ); break; + case 2: + SendWeaponAnim( CROWBAR_ATTACK3HIT ); break; + } + // player "shoot" animation m_pPlayer->SetAnimation( PLAYER_ATTACK1 ); +#ifndef CLIENT_DLL + // hit fDidHit = TRUE; CBaseEntity *pEntity = CBaseEntity::Instance(tr.pHit); ClearMultiDamage( ); - pEntity->TraceAttack(m_pPlayer->pev, gSkillData.plrDmgCrowbar, gpGlobals->v_forward, &tr, DMG_CLUB ); - + if ( (m_flNextPrimaryAttack + 1 < UTIL_WeaponTimeBase() ) || g_pGameRules->IsMultiplayer() ) + { + // first swing does full damage + pEntity->TraceAttack(m_pPlayer->pev, gSkillData.plrDmgCrowbar, gpGlobals->v_forward, &tr, DMG_CLUB ); + } + else + { + // subsequent swings do half + pEntity->TraceAttack(m_pPlayer->pev, gSkillData.plrDmgCrowbar / 2, gpGlobals->v_forward, &tr, DMG_CLUB ); + } ApplyMultiDamage( m_pPlayer->pev, m_pPlayer->pev ); // play thwack, smack, or dong sound @@ -225,33 +252,67 @@ int CCrowbar::Swing( int fFirst ) { if ( pEntity->Classify() != CLASS_NONE && pEntity->Classify() != CLASS_MACHINE ) { - bHit = TRUE;//play hitbody sound on client + // play thwack or smack sound + switch( RANDOM_LONG(0,2) ) + { + case 0: + EMIT_SOUND(ENT(m_pPlayer->pev), CHAN_ITEM, "weapons/cbar_hitbod1.wav", 1, ATTN_NORM); break; + case 1: + EMIT_SOUND(ENT(m_pPlayer->pev), CHAN_ITEM, "weapons/cbar_hitbod2.wav", 1, ATTN_NORM); break; + case 2: + EMIT_SOUND(ENT(m_pPlayer->pev), CHAN_ITEM, "weapons/cbar_hitbod3.wav", 1, ATTN_NORM); break; + } m_pPlayer->m_iWeaponVolume = CROWBAR_BODYHIT_VOLUME; if ( !pEntity->IsAlive() ) - m_pPlayer->m_flNextAttack = UTIL_WeaponTimeBase() + 0.5; - else flVol = 0.1; + return TRUE; + else + flVol = 0.1; + fHitWorld = FALSE; } } - m_pPlayer->m_iWeaponVolume = flVol * CROWBAR_WALLHIT_VOLUME; - m_flNextPrimaryAttack = m_flNextSecondaryAttack = UTIL_WeaponTimeBase() + 0.25; - m_flTimeUpdate = UTIL_WeaponTimeBase() + 0.2; - } - PLAYBACK_EVENT_FULL( 0, m_pPlayer->edict(), m_usCrowbar, 0.0, (float *)&g_vecZero, (float *)&g_vecZero, 0, 0, pev->body, fFirst, bHit, 0 ); + // play texture hit sound + // UNDONE: Calculate the correct point of intersection when we hit with the hull instead of the line + + if (fHitWorld) + { + float fvolbar = TEXTURETYPE_PlaySound(&tr, vecSrc, vecSrc + (vecEnd-vecSrc)*2, BULLET_PLAYER_CROWBAR); + + if ( g_pGameRules->IsMultiplayer() ) + { + // override the volume here, cause we don't play texture sounds in multiplayer, + // and fvolbar is going to be 0 from the above call. + + fvolbar = 1; + } + + // also play crowbar strike + switch( RANDOM_LONG(0,1) ) + { + case 0: + EMIT_SOUND_DYN(ENT(m_pPlayer->pev), CHAN_ITEM, "weapons/cbar_hit1.wav", fvolbar, ATTN_NORM, 0, 98 + RANDOM_LONG(0,3)); + break; + case 1: + EMIT_SOUND_DYN(ENT(m_pPlayer->pev), CHAN_ITEM, "weapons/cbar_hit2.wav", fvolbar, ATTN_NORM, 0, 98 + RANDOM_LONG(0,3)); + break; + } + + // delay the decal a bit + m_trHit = tr; + } + + m_pPlayer->m_iWeaponVolume = flVol * CROWBAR_WALLHIT_VOLUME; +#endif + m_flNextPrimaryAttack = UTIL_WeaponTimeBase() + 0.25; + + SetThink( Smack ); + pev->nextthink = UTIL_WeaponTimeBase() + 0.2; + + + } return fDidHit; } -void CCrowbar:: WeaponIdle( void ) -{ - if ( m_flTimeWeaponIdle > UTIL_WeaponTimeBase() ) return; - float flRand = RANDOM_FLOAT(0, 1); - if ( flRand <= 0.5 ) - { - SendWeaponAnim( CROWBAR_IDLE ); - m_flTimeWeaponIdle = UTIL_WeaponTimeBase() + RANDOM_FLOAT ( 10, 15 ); - } -} - diff --git a/spirit/decals.h b/dlls/decals.h similarity index 95% rename from spirit/decals.h rename to dlls/decals.h index 95fa44f5..0f8ff4ed 100644 --- a/spirit/decals.h +++ b/dlls/decals.h @@ -1,75 +1,75 @@ -/*** -* -* Copyright (c) 1996-2002, 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. -* -****/ -#ifndef DECALS_H -#define DECALS_H - -// -// Dynamic Decals -// -enum decal_e -{ - DECAL_GUNSHOT1 = 0, - DECAL_GUNSHOT2, - DECAL_GUNSHOT3, - DECAL_GUNSHOT4, - DECAL_GUNSHOT5, - DECAL_LAMBDA1, - DECAL_LAMBDA2, - DECAL_LAMBDA3, - DECAL_LAMBDA4, - DECAL_LAMBDA5, - DECAL_LAMBDA6, - DECAL_SCORCH1, - DECAL_SCORCH2, - DECAL_BLOOD1, - DECAL_BLOOD2, - DECAL_BLOOD3, - DECAL_BLOOD4, - DECAL_BLOOD5, - DECAL_BLOOD6, - DECAL_YBLOOD1, - DECAL_YBLOOD2, - DECAL_YBLOOD3, - DECAL_YBLOOD4, - DECAL_YBLOOD5, - DECAL_YBLOOD6, - DECAL_GLASSBREAK1, - DECAL_GLASSBREAK2, - DECAL_GLASSBREAK3, - DECAL_BIGSHOT1, - DECAL_BIGSHOT2, - DECAL_BIGSHOT3, - DECAL_BIGSHOT4, - DECAL_BIGSHOT5, - DECAL_SPIT1, - DECAL_SPIT2, - DECAL_BPROOF1, // Bulletproof glass decal - DECAL_GARGSTOMP1, // Gargantua stomp crack - DECAL_SMALLSCORCH1, // Small scorch mark - DECAL_SMALLSCORCH2, // Small scorch mark - DECAL_SMALLSCORCH3, // Small scorch mark - DECAL_MOMMABIRTH, // Big momma birth splatter - DECAL_MOMMASPLAT, -}; - -typedef struct -{ - char *name; - int index; -} DLL_DECALLIST; - -extern DLL_DECALLIST gDecals[]; - -#endif // DECALS_H +/*** +* +* Copyright (c) 1996-2002, 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. +* +****/ +#ifndef DECALS_H +#define DECALS_H + +// +// Dynamic Decals +// +enum decal_e +{ + DECAL_GUNSHOT1 = 0, + DECAL_GUNSHOT2, + DECAL_GUNSHOT3, + DECAL_GUNSHOT4, + DECAL_GUNSHOT5, + DECAL_LAMBDA1, + DECAL_LAMBDA2, + DECAL_LAMBDA3, + DECAL_LAMBDA4, + DECAL_LAMBDA5, + DECAL_LAMBDA6, + DECAL_SCORCH1, + DECAL_SCORCH2, + DECAL_BLOOD1, + DECAL_BLOOD2, + DECAL_BLOOD3, + DECAL_BLOOD4, + DECAL_BLOOD5, + DECAL_BLOOD6, + DECAL_YBLOOD1, + DECAL_YBLOOD2, + DECAL_YBLOOD3, + DECAL_YBLOOD4, + DECAL_YBLOOD5, + DECAL_YBLOOD6, + DECAL_GLASSBREAK1, + DECAL_GLASSBREAK2, + DECAL_GLASSBREAK3, + DECAL_BIGSHOT1, + DECAL_BIGSHOT2, + DECAL_BIGSHOT3, + DECAL_BIGSHOT4, + DECAL_BIGSHOT5, + DECAL_SPIT1, + DECAL_SPIT2, + DECAL_BPROOF1, // Bulletproof glass decal + DECAL_GARGSTOMP1, // Gargantua stomp crack + DECAL_SMALLSCORCH1, // Small scorch mark + DECAL_SMALLSCORCH2, // Small scorch mark + DECAL_SMALLSCORCH3, // Small scorch mark + DECAL_MOMMABIRTH, // Big momma birth splatter + DECAL_MOMMASPLAT, +}; + +typedef struct +{ + char *name; + int index; +} DLL_DECALLIST; + +extern DLL_DECALLIST gDecals[]; + +#endif // DECALS_H diff --git a/spirit/defaultai.cpp b/dlls/defaultai.cpp similarity index 91% rename from spirit/defaultai.cpp rename to dlls/defaultai.cpp index 2558c978..efc0087e 100644 --- a/spirit/defaultai.cpp +++ b/dlls/defaultai.cpp @@ -796,30 +796,9 @@ Schedule_t slError[] = }, }; -//LRC -Task_t tlScriptedTeleport[] = -{ - { TASK_PLANT_ON_SCRIPT, (float)0 }, - { TASK_WAIT_FOR_SCRIPT, (float)0 }, - { TASK_PLAY_SCRIPT, (float)0 }, - { TASK_END_SCRIPT, (float)0 }, -}; - -//LRC -Schedule_t slTeleportToScript[] = -{ - { - tlScriptedTeleport, - ARRAYSIZE ( tlScriptedTeleport ), - SCRIPT_BREAK_CONDITIONS, - 0, - "TeleportToScript" - }, -}; - Task_t tlScriptedWalk[] = { - { TASK_WALK_TO_SCRIPT, (float)TARGET_MOVE_SCRIPTED }, + { TASK_WALK_TO_TARGET, (float)TARGET_MOVE_SCRIPTED }, { TASK_WAIT_FOR_MOVEMENT, (float)0 }, { TASK_PLANT_ON_SCRIPT, (float)0 }, { TASK_FACE_SCRIPT, (float)0 }, @@ -827,7 +806,6 @@ Task_t tlScriptedWalk[] = { TASK_ENABLE_SCRIPT, (float)0 }, { TASK_WAIT_FOR_SCRIPT, (float)0 }, { TASK_PLAY_SCRIPT, (float)0 }, - { TASK_END_SCRIPT, (float)0 }, }; Schedule_t slWalkToScript[] = @@ -844,7 +822,7 @@ Schedule_t slWalkToScript[] = Task_t tlScriptedRun[] = { - { TASK_RUN_TO_SCRIPT, (float)TARGET_MOVE_SCRIPTED }, + { TASK_RUN_TO_TARGET, (float)TARGET_MOVE_SCRIPTED }, { TASK_WAIT_FOR_MOVEMENT, (float)0 }, { TASK_PLANT_ON_SCRIPT, (float)0 }, { TASK_FACE_SCRIPT, (float)0 }, @@ -852,7 +830,6 @@ Task_t tlScriptedRun[] = { TASK_ENABLE_SCRIPT, (float)0 }, { TASK_WAIT_FOR_SCRIPT, (float)0 }, { TASK_PLAY_SCRIPT, (float)0 }, - { TASK_END_SCRIPT, (float)0 }, }; Schedule_t slRunToScript[] = @@ -869,10 +846,8 @@ Schedule_t slRunToScript[] = Task_t tlScriptedWait[] = { { TASK_STOP_MOVING, 0 }, -// { TASK_ENABLE_SCRIPT, (float)0 }, { TASK_WAIT_FOR_SCRIPT, (float)0 }, { TASK_PLAY_SCRIPT, (float)0 }, - { TASK_END_SCRIPT, (float)0 }, }; Schedule_t slWaitScript[] = @@ -893,7 +868,6 @@ Task_t tlScriptedFace[] = { TASK_FACE_IDEAL, (float)0 }, { TASK_WAIT_FOR_SCRIPT, (float)0 }, { TASK_PLAY_SCRIPT, (float)0 }, - { TASK_END_SCRIPT, (float)0 }, }; Schedule_t slFaceScript[] = @@ -1058,7 +1032,7 @@ Schedule_t *CBaseMonster :: ScheduleInList( const char *pName, Schedule_t **pLis if ( !pName ) { - ALERT( at_debug, "%s set to unnamed schedule!\n", STRING(pev->classname) ); + ALERT( at_console, "%s set to unnamed schedule!\n", STRING(pev->classname) ); return NULL; } @@ -1067,7 +1041,7 @@ Schedule_t *CBaseMonster :: ScheduleInList( const char *pName, Schedule_t **pLis { if ( !pList[i]->pName ) { - ALERT( at_debug, "Unnamed schedule!\n" ); + ALERT( at_console, "Unnamed schedule!\n" ); continue; } if ( stricmp( pName, pList[i]->pName ) == 0 ) @@ -1085,10 +1059,9 @@ Schedule_t* CBaseMonster :: GetScheduleOfType ( int Type ) // ALERT ( at_console, "Sched Type:%d\n", Type ); switch ( Type ) { - // This is the schedule for scripted sequences AND scripted AI. // LRC- And scripted actions, too. + // This is the schedule for scripted sequences AND scripted AI case SCHED_AISCRIPT: { -// ALERT(at_console, "Doing AISCRIPT\n"); ASSERT( m_pCine != NULL ); if ( !m_pCine ) { @@ -1101,16 +1074,15 @@ Schedule_t* CBaseMonster :: GetScheduleOfType ( int Type ) switch ( m_pCine->m_fMoveTo ) { - case 0: - return slWaitScript; - case 4: case 6: - return slTeleportToScript; - case 1: - return slWalkToScript; - case 2: - return slRunToScript; - case 5: - return slFaceScript; + case 0: + case 4: + return slWaitScript; + case 1: + return slWalkToScript; + case 2: + return slRunToScript; + case 5: + return slFaceScript; } break; } @@ -1249,7 +1221,7 @@ Schedule_t* CBaseMonster :: GetScheduleOfType ( int Type ) } default: { - ALERT ( at_debug, "GetScheduleOfType()\nNo CASE for Schedule Type %d!\n", Type ); + ALERT ( at_console, "GetScheduleOfType()\nNo CASE for Schedule Type %d!\n", Type ); return &slIdleStand[ 0 ]; break; diff --git a/spirit/defaultai.h b/dlls/defaultai.h similarity index 97% rename from spirit/defaultai.h rename to dlls/defaultai.h index 652d1085..271ac7aa 100644 --- a/spirit/defaultai.h +++ b/dlls/defaultai.h @@ -1,98 +1,98 @@ -/*** -* -* Copyright (c) 1996-2002, 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. -* -* This source code contains proprietary and confidential information of -* Valve LLC and its suppliers. Access to this code is restricted to -* persons who have executed a written SDK license with Valve. Any access, -* use or distribution of this code by or to any unlicensed person is illegal. -* -****/ -#ifndef DEFAULTAI_H -#define DEFAULTAI_H - -//========================================================= -// Failed -//========================================================= -extern Schedule_t slFail[]; - -//========================================================= -// Idle Schedules -//========================================================= -extern Schedule_t slIdleStand[]; -extern Schedule_t slIdleTrigger[]; -extern Schedule_t slIdleWalk[]; - -//========================================================= -// Wake Schedules -//========================================================= -extern Schedule_t slWakeAngry[]; - -//========================================================= -// AlertTurn Schedules -//========================================================= -extern Schedule_t slAlertFace[]; - -//========================================================= -// AlertIdle Schedules -//========================================================= -extern Schedule_t slAlertStand[]; - -//========================================================= -// CombatIdle Schedule -//========================================================= -extern Schedule_t slCombatStand[]; - -//========================================================= -// CombatFace Schedule -//========================================================= -extern Schedule_t slCombatFace[]; - -//========================================================= -// reload schedule -//========================================================= -extern Schedule_t slReload[]; - -//========================================================= -// Attack Schedules -//========================================================= - -extern Schedule_t slRangeAttack1[]; -extern Schedule_t slRangeAttack2[]; - -extern Schedule_t slTakeCoverFromBestSound[]; - -// primary melee attack -extern Schedule_t slMeleeAttack[]; - -// Chase enemy schedule -extern Schedule_t slChaseEnemy[]; - -//========================================================= -// small flinch, used when a relatively minor bit of damage -// is inflicted. -//========================================================= -extern Schedule_t slSmallFlinch[]; - -//========================================================= -// Die! -//========================================================= -extern Schedule_t slDie[]; - -//========================================================= -// Universal Error Schedule -//========================================================= -extern Schedule_t slError[]; - -//========================================================= -// Scripted sequences -//========================================================= -extern Schedule_t slWalkToScript[]; -extern Schedule_t slRunToScript[]; -extern Schedule_t slWaitScript[]; - -#endif // DEFAULTAI_H +/*** +* +* Copyright (c) 1996-2002, 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. +* +* This source code contains proprietary and confidential information of +* Valve LLC and its suppliers. Access to this code is restricted to +* persons who have executed a written SDK license with Valve. Any access, +* use or distribution of this code by or to any unlicensed person is illegal. +* +****/ +#ifndef DEFAULTAI_H +#define DEFAULTAI_H + +//========================================================= +// Failed +//========================================================= +extern Schedule_t slFail[]; + +//========================================================= +// Idle Schedules +//========================================================= +extern Schedule_t slIdleStand[]; +extern Schedule_t slIdleTrigger[]; +extern Schedule_t slIdleWalk[]; + +//========================================================= +// Wake Schedules +//========================================================= +extern Schedule_t slWakeAngry[]; + +//========================================================= +// AlertTurn Schedules +//========================================================= +extern Schedule_t slAlertFace[]; + +//========================================================= +// AlertIdle Schedules +//========================================================= +extern Schedule_t slAlertStand[]; + +//========================================================= +// CombatIdle Schedule +//========================================================= +extern Schedule_t slCombatStand[]; + +//========================================================= +// CombatFace Schedule +//========================================================= +extern Schedule_t slCombatFace[]; + +//========================================================= +// reload schedule +//========================================================= +extern Schedule_t slReload[]; + +//========================================================= +// Attack Schedules +//========================================================= + +extern Schedule_t slRangeAttack1[]; +extern Schedule_t slRangeAttack2[]; + +extern Schedule_t slTakeCoverFromBestSound[]; + +// primary melee attack +extern Schedule_t slMeleeAttack[]; + +// Chase enemy schedule +extern Schedule_t slChaseEnemy[]; + +//========================================================= +// small flinch, used when a relatively minor bit of damage +// is inflicted. +//========================================================= +extern Schedule_t slSmallFlinch[]; + +//========================================================= +// Die! +//========================================================= +extern Schedule_t slDie[]; + +//========================================================= +// Universal Error Schedule +//========================================================= +extern Schedule_t slError[]; + +//========================================================= +// Scripted sequences +//========================================================= +extern Schedule_t slWalkToScript[]; +extern Schedule_t slRunToScript[]; +extern Schedule_t slWaitScript[]; + +#endif // DEFAULTAI_H diff --git a/spirit/doors.cpp b/dlls/doors.cpp similarity index 59% rename from spirit/doors.cpp rename to dlls/doors.cpp index a86a296e..08511d7d 100644 --- a/spirit/doors.cpp +++ b/dlls/doors.cpp @@ -22,7 +22,7 @@ #include "util.h" #include "cbase.h" #include "doors.h" -#include "movewith.h" + extern void SetMovedir(entvars_t* ev); @@ -34,7 +34,6 @@ class CBaseDoor : public CBaseToggle public: void Spawn( void ); void Precache( void ); - virtual void PostSpawn( void ); virtual void KeyValue( KeyValueData *pkvd ); virtual void Use( CBaseEntity *pActivator, CBaseEntity *pCaller, USE_TYPE useType, float value ); virtual void Blocked( CBaseEntity *pOther ); @@ -43,10 +42,7 @@ public: virtual int ObjectCaps( void ) { if (pev->spawnflags & SF_ITEM_USE_ONLY) - { - return (CBaseToggle::ObjectCaps() & ~FCAP_ACROSS_TRANSITION) | FCAP_IMPULSE_USE | - (m_iDirectUse ? FCAP_ONLYDIRECT_USE : 0); - } + return (CBaseToggle::ObjectCaps() & ~FCAP_ACROSS_TRANSITION) | FCAP_IMPULSE_USE; else return (CBaseToggle::ObjectCaps() & ~FCAP_ACROSS_TRANSITION); }; @@ -78,16 +74,6 @@ public: BYTE m_bLockedSentence; BYTE m_bUnlockedSound; BYTE m_bUnlockedSentence; - - BOOL m_iOnOffMode; - BOOL m_iImmediateMode; - - BOOL m_iDirectUse; - - float m_fAcceleration; - float m_fDeceleration; - float m_flBlockedTime; - BOOL m_iSpeedMode;//AJH for changing door speeds }; @@ -102,10 +88,6 @@ TYPEDESCRIPTION CBaseDoor::m_SaveData[] = DEFINE_FIELD( CBaseDoor, m_bUnlockedSound, FIELD_CHARACTER ), DEFINE_FIELD( CBaseDoor, m_bUnlockedSentence, FIELD_CHARACTER ), - DEFINE_FIELD( CBaseDoor, m_iOnOffMode, FIELD_BOOLEAN ), - DEFINE_FIELD( CBaseDoor, m_iImmediateMode, FIELD_BOOLEAN ), - - DEFINE_FIELD( CBaseDoor, m_iDirectUse, FIELD_BOOLEAN ), }; IMPLEMENT_SAVERESTORE( CBaseDoor, CBaseToggle ); @@ -253,21 +235,6 @@ void CBaseDoor::KeyValue( KeyValueData *pkvd ) m_bUnlockedSentence = atof(pkvd->szValue); pkvd->fHandled = TRUE; } - else if (FStrEq(pkvd->szKeyName, "immediatemode")) - { - m_iImmediateMode = atoi(pkvd->szValue); - pkvd->fHandled = TRUE; - } - else if (FStrEq(pkvd->szKeyName, "onoffmode")) - { - m_iOnOffMode = atoi(pkvd->szValue); - pkvd->fHandled = TRUE; - } - else if (FStrEq(pkvd->szKeyName, "directuse")) - { - m_iDirectUse = atoi(pkvd->szValue); - pkvd->fHandled = TRUE; - } else if (FStrEq(pkvd->szKeyName, "WaveHeight")) { pev->scale = atof(pkvd->szValue) * (1.0/8.0); @@ -308,9 +275,7 @@ LINK_ENTITY_TO_CLASS( func_door, CBaseDoor ); // LINK_ENTITY_TO_CLASS( func_water, CBaseDoor ); -//MH this new spawn function messes up SF_DOOR_START_OPEN -// so replace it with the old one (below) -/* + void CBaseDoor::Spawn( ) { Precache(); @@ -330,8 +295,8 @@ void CBaseDoor::Spawn( ) } pev->movetype = MOVETYPE_PUSH; + UTIL_SetOrigin(pev, pev->origin); SET_MODEL( ENT(pev), STRING(pev->model) ); - UTIL_SetOrigin(this, pev->origin); if (pev->speed == 0) pev->speed = 100; @@ -342,7 +307,7 @@ void CBaseDoor::Spawn( ) ASSERTSZ(m_vecPosition1 != m_vecPosition2, "door start/end positions are equal"); if ( FBitSet (pev->spawnflags, SF_DOOR_START_OPEN) ) { // swap pos1 and pos2, put door at pos2 - UTIL_SetOrigin(this, m_vecPosition2); + UTIL_SetOrigin(pev, m_vecPosition2); m_vecPosition2 = m_vecPosition1; m_vecPosition1 = pev->origin; } @@ -350,112 +315,21 @@ void CBaseDoor::Spawn( ) m_toggle_state = TS_AT_BOTTOM; // if the door is flagged for USE button activation only, use NULL touch function - // (unless it's overridden, of course- LRC) - if ( FBitSet ( pev->spawnflags, SF_DOOR_USE_ONLY ) && - !FBitSet ( pev->spawnflags, SF_DOOR_FORCETOUCHABLE )) - { - SetTouch ( NULL ); - } - else // touchable button - SetTouch(&CBaseDoor:: DoorTouch ); -} -*/ - -//standard Spirit 1.0 spawn function -void CBaseDoor::Spawn( ) -{ - Precache(); - SetMovedir (pev); - - if ( pev->skin == 0 ) - {//normal door - if ( FBitSet (pev->spawnflags, SF_DOOR_PASSABLE) ) - pev->solid = SOLID_NOT; - else - pev->solid = SOLID_BSP; - } - else - {// special contents - pev->solid = SOLID_NOT; - SetBits( pev->spawnflags, SF_DOOR_SILENT ); // water is silent for now - } - - pev->movetype = MOVETYPE_PUSH; - SET_MODEL( ENT(pev), STRING(pev->model) ); - UTIL_SetOrigin(this, pev->origin); - - if (pev->speed == 0) - pev->speed = 100; - - m_toggle_state = TS_AT_BOTTOM; - - // if the door is flagged for USE button activation only, use NULL touch function - // (unless it's overridden, of course- LRC) - if ( FBitSet ( pev->spawnflags, SF_DOOR_USE_ONLY ) && - !FBitSet ( pev->spawnflags, SF_DOOR_FORCETOUCHABLE )) + if ( FBitSet ( pev->spawnflags, SF_DOOR_USE_ONLY ) ) { SetTouch ( NULL ); } else // touchable button SetTouch( DoorTouch ); } -//END -//LRC -void CBaseDoor :: PostSpawn( void ) -{ - if (m_pMoveWith) - m_vecPosition1 = pev->origin - m_pMoveWith->pev->origin; - else - m_vecPosition1 = pev->origin; - - // Subtract 2 from size because the engine expands bboxes by 1 in all directions - m_vecPosition2 = m_vecPosition1 + (pev->movedir * (fabs( pev->movedir.x * (pev->size.x-2) ) + fabs( pev->movedir.y * (pev->size.y-2) ) + fabs( pev->movedir.z * (pev->size.z-2) ) - m_flLip)); - - ASSERTSZ(m_vecPosition1 != m_vecPosition2, "door start/end positions are equal"); - if ( FBitSet (pev->spawnflags, SF_DOOR_START_OPEN) ) - { // swap pos1 and pos2, put door at pos2 - if (m_pMoveWith) - { - m_vecSpawnOffset = m_vecSpawnOffset + (m_vecPosition2 + m_pMoveWith->pev->origin) - pev->origin; - UTIL_AssignOrigin(this, m_vecPosition2 + m_pMoveWith->pev->origin); - } - else - { - m_vecSpawnOffset = m_vecSpawnOffset + m_vecPosition2 - pev->origin; - UTIL_AssignOrigin(this, m_vecPosition2); - } - Vector vecTemp = m_vecPosition2; - m_vecPosition2 = m_vecPosition1; - m_vecPosition1 = vecTemp; -// ALERT(at_console, "func_door postspawn: origin %f %f %f\n", pev->origin.x, pev->origin.y, pev->origin.z); - } -} - -//void CBaseDoor :: PostMoveWith( void ) -//{ -// Vector vecTemp = m_vecPosition1 - m_pMoveWith->m_vecSpawnOffset; -// ALERT(at_console, "door %s pmw: pos1 changes from (%f %f %f) to (%f %f %f)\n", STRING(pev->targetname), m_vecPosition1.x, m_vecPosition1.y, m_vecPosition1.z, vecTemp.x, vecTemp.y, vecTemp.z); -// m_vecPosition1 = m_vecPosition1 - m_pMoveWith->m_vecSpawnOffset; -// m_vecPosition2 = m_vecPosition2 - m_pMoveWith->m_vecSpawnOffset; -//} void CBaseDoor :: SetToggleState( int state ) { if ( state == TS_AT_TOP ) - { - if (m_pMoveWith) - UTIL_AssignOrigin( this, m_vecPosition2 + m_pMoveWith->pev->origin); - else - UTIL_AssignOrigin( this, m_vecPosition2 ); - } - else - { - if (m_pMoveWith) - UTIL_AssignOrigin( this, m_vecPosition1 + m_pMoveWith->pev->origin); - else - UTIL_AssignOrigin( this, m_vecPosition1 ); - } + UTIL_SetOrigin( pev, m_vecPosition2 ); + else + UTIL_SetOrigin( pev, m_vecPosition1 ); } @@ -467,50 +341,50 @@ void CBaseDoor::Precache( void ) switch (m_bMoveSnd) { case 0: - pev->noiseMoving = MAKE_STRING("common/null.wav"); + pev->noiseMoving = ALLOC_STRING("common/null.wav"); break; case 1: PRECACHE_SOUND ("doors/doormove1.wav"); - pev->noiseMoving = MAKE_STRING("doors/doormove1.wav"); + pev->noiseMoving = ALLOC_STRING("doors/doormove1.wav"); break; case 2: PRECACHE_SOUND ("doors/doormove2.wav"); - pev->noiseMoving = MAKE_STRING("doors/doormove2.wav"); + pev->noiseMoving = ALLOC_STRING("doors/doormove2.wav"); break; case 3: PRECACHE_SOUND ("doors/doormove3.wav"); - pev->noiseMoving = MAKE_STRING("doors/doormove3.wav"); + pev->noiseMoving = ALLOC_STRING("doors/doormove3.wav"); break; case 4: PRECACHE_SOUND ("doors/doormove4.wav"); - pev->noiseMoving = MAKE_STRING("doors/doormove4.wav"); + pev->noiseMoving = ALLOC_STRING("doors/doormove4.wav"); break; case 5: PRECACHE_SOUND ("doors/doormove5.wav"); - pev->noiseMoving = MAKE_STRING("doors/doormove5.wav"); + pev->noiseMoving = ALLOC_STRING("doors/doormove5.wav"); break; case 6: PRECACHE_SOUND ("doors/doormove6.wav"); - pev->noiseMoving = MAKE_STRING("doors/doormove6.wav"); + pev->noiseMoving = ALLOC_STRING("doors/doormove6.wav"); break; case 7: PRECACHE_SOUND ("doors/doormove7.wav"); - pev->noiseMoving = MAKE_STRING("doors/doormove7.wav"); + pev->noiseMoving = ALLOC_STRING("doors/doormove7.wav"); break; case 8: PRECACHE_SOUND ("doors/doormove8.wav"); - pev->noiseMoving = MAKE_STRING("doors/doormove8.wav"); + pev->noiseMoving = ALLOC_STRING("doors/doormove8.wav"); break; case 9: PRECACHE_SOUND ("doors/doormove9.wav"); - pev->noiseMoving = MAKE_STRING("doors/doormove9.wav"); + pev->noiseMoving = ALLOC_STRING("doors/doormove9.wav"); break; case 10: PRECACHE_SOUND ("doors/doormove10.wav"); - pev->noiseMoving = MAKE_STRING("doors/doormove10.wav"); + pev->noiseMoving = ALLOC_STRING("doors/doormove10.wav"); break; default: - pev->noiseMoving = MAKE_STRING("common/null.wav"); + pev->noiseMoving = ALLOC_STRING("common/null.wav"); break; } @@ -518,42 +392,42 @@ void CBaseDoor::Precache( void ) switch (m_bStopSnd) { case 0: - pev->noiseArrived = MAKE_STRING("common/null.wav"); + pev->noiseArrived = ALLOC_STRING("common/null.wav"); break; case 1: PRECACHE_SOUND ("doors/doorstop1.wav"); - pev->noiseArrived = MAKE_STRING("doors/doorstop1.wav"); + pev->noiseArrived = ALLOC_STRING("doors/doorstop1.wav"); break; case 2: PRECACHE_SOUND ("doors/doorstop2.wav"); - pev->noiseArrived = MAKE_STRING("doors/doorstop2.wav"); + pev->noiseArrived = ALLOC_STRING("doors/doorstop2.wav"); break; case 3: PRECACHE_SOUND ("doors/doorstop3.wav"); - pev->noiseArrived = MAKE_STRING("doors/doorstop3.wav"); + pev->noiseArrived = ALLOC_STRING("doors/doorstop3.wav"); break; case 4: PRECACHE_SOUND ("doors/doorstop4.wav"); - pev->noiseArrived = MAKE_STRING("doors/doorstop4.wav"); + pev->noiseArrived = ALLOC_STRING("doors/doorstop4.wav"); break; case 5: PRECACHE_SOUND ("doors/doorstop5.wav"); - pev->noiseArrived = MAKE_STRING("doors/doorstop5.wav"); + pev->noiseArrived = ALLOC_STRING("doors/doorstop5.wav"); break; case 6: PRECACHE_SOUND ("doors/doorstop6.wav"); - pev->noiseArrived = MAKE_STRING("doors/doorstop6.wav"); + pev->noiseArrived = ALLOC_STRING("doors/doorstop6.wav"); break; case 7: PRECACHE_SOUND ("doors/doorstop7.wav"); - pev->noiseArrived = MAKE_STRING("doors/doorstop7.wav"); + pev->noiseArrived = ALLOC_STRING("doors/doorstop7.wav"); break; case 8: PRECACHE_SOUND ("doors/doorstop8.wav"); - pev->noiseArrived = MAKE_STRING("doors/doorstop8.wav"); + pev->noiseArrived = ALLOC_STRING("doors/doorstop8.wav"); break; default: - pev->noiseArrived = MAKE_STRING("common/null.wav"); + pev->noiseArrived = ALLOC_STRING("common/null.wav"); break; } @@ -577,29 +451,29 @@ void CBaseDoor::Precache( void ) switch (m_bLockedSentence) { - case 1: m_ls.sLockedSentence = MAKE_STRING("NA"); break; // access denied - case 2: m_ls.sLockedSentence = MAKE_STRING("ND"); break; // security lockout - case 3: m_ls.sLockedSentence = MAKE_STRING("NF"); break; // blast door - case 4: m_ls.sLockedSentence = MAKE_STRING("NFIRE"); break; // fire door - case 5: m_ls.sLockedSentence = MAKE_STRING("NCHEM"); break; // chemical door - case 6: m_ls.sLockedSentence = MAKE_STRING("NRAD"); break; // radiation door - case 7: m_ls.sLockedSentence = MAKE_STRING("NCON"); break; // gen containment - case 8: m_ls.sLockedSentence = MAKE_STRING("NH"); break; // maintenance door - case 9: m_ls.sLockedSentence = MAKE_STRING("NG"); break; // broken door + case 1: m_ls.sLockedSentence = ALLOC_STRING("NA"); break; // access denied + case 2: m_ls.sLockedSentence = ALLOC_STRING("ND"); break; // security lockout + case 3: m_ls.sLockedSentence = ALLOC_STRING("NF"); break; // blast door + case 4: m_ls.sLockedSentence = ALLOC_STRING("NFIRE"); break; // fire door + case 5: m_ls.sLockedSentence = ALLOC_STRING("NCHEM"); break; // chemical door + case 6: m_ls.sLockedSentence = ALLOC_STRING("NRAD"); break; // radiation door + case 7: m_ls.sLockedSentence = ALLOC_STRING("NCON"); break; // gen containment + case 8: m_ls.sLockedSentence = ALLOC_STRING("NH"); break; // maintenance door + case 9: m_ls.sLockedSentence = ALLOC_STRING("NG"); break; // broken door default: m_ls.sLockedSentence = 0; break; } switch (m_bUnlockedSentence) { - case 1: m_ls.sUnlockedSentence = MAKE_STRING("EA"); break; // access granted - case 2: m_ls.sUnlockedSentence = MAKE_STRING("ED"); break; // security door - case 3: m_ls.sUnlockedSentence = MAKE_STRING("EF"); break; // blast door - case 4: m_ls.sUnlockedSentence = MAKE_STRING("EFIRE"); break; // fire door - case 5: m_ls.sUnlockedSentence = MAKE_STRING("ECHEM"); break; // chemical door - case 6: m_ls.sUnlockedSentence = MAKE_STRING("ERAD"); break; // radiation door - case 7: m_ls.sUnlockedSentence = MAKE_STRING("ECON"); break; // gen containment - case 8: m_ls.sUnlockedSentence = MAKE_STRING("EH"); break; // maintenance door + case 1: m_ls.sUnlockedSentence = ALLOC_STRING("EA"); break; // access granted + case 2: m_ls.sUnlockedSentence = ALLOC_STRING("ED"); break; // security door + case 3: m_ls.sUnlockedSentence = ALLOC_STRING("EF"); break; // blast door + case 4: m_ls.sUnlockedSentence = ALLOC_STRING("EFIRE"); break; // fire door + case 5: m_ls.sUnlockedSentence = ALLOC_STRING("ECHEM"); break; // chemical door + case 6: m_ls.sUnlockedSentence = ALLOC_STRING("ERAD"); break; // radiation door + case 7: m_ls.sUnlockedSentence = ALLOC_STRING("ECON"); break; // gen containment + case 8: m_ls.sUnlockedSentence = ALLOC_STRING("EH"); break; // maintenance door default: m_ls.sUnlockedSentence = 0; break; } @@ -624,8 +498,8 @@ void CBaseDoor::DoorTouch( CBaseEntity *pOther ) // If door is somebody's target, then touching does nothing. // You have to activate the owner (e.g. button). - //LRC- allow flags to override this - if (!FStringNull(pev->targetname) && !FBitSet(pev->spawnflags,SF_DOOR_FORCETOUCHABLE)) + + if (!FStringNull(pev->targetname)) { // play locked sound PlayLockSounds(pev, &m_ls, TRUE, FALSE); @@ -645,41 +519,8 @@ void CBaseDoor::DoorTouch( CBaseEntity *pOther ) void CBaseDoor::Use( CBaseEntity *pActivator, CBaseEntity *pCaller, USE_TYPE useType, float value ) { m_hActivator = pActivator; - - if (!UTIL_IsMasterTriggered(m_sMaster, pActivator)) - return; - - if (m_iOnOffMode) - { - if (useType == USE_ON) - { - if (m_toggle_state == TS_AT_BOTTOM) - { - PlayLockSounds(pev, &m_ls, FALSE, FALSE); - DoorGoUp(); - } - return; - } - else if (useType == USE_OFF) - { - if (m_toggle_state == TS_AT_TOP) - { - DoorGoDown(); - } - return; - } - } - - // if not ready to be used, ignore "use" command. - if (m_toggle_state == TS_AT_TOP) - { - if (!FBitSet(pev->spawnflags, SF_DOOR_NO_AUTO_RETURN)) - return; - } - else if (m_toggle_state != TS_AT_BOTTOM) - return; - + if (m_toggle_state == TS_AT_BOTTOM || FBitSet(pev->spawnflags, SF_DOOR_NO_AUTO_RETURN) && m_toggle_state == TS_AT_TOP) DoorActivate(); } @@ -732,20 +573,9 @@ void CBaseDoor::DoorGoUp( void ) if ( !FBitSet( pev->spawnflags, SF_DOOR_SILENT ) ) EMIT_SOUND(ENT(pev), CHAN_STATIC, (char*)STRING(pev->noiseMoving), 1, ATTN_NORM); -// ALERT(at_debug, "%s go up (was %d)\n", STRING(pev->targetname), m_toggle_state); m_toggle_state = TS_GOING_UP; - - SetMoveDone(&CBaseDoor:: DoorHitTop ); - - // LRC- if synched, we fire as soon as we start to go up - if (m_iImmediateMode) - { - if (m_iOnOffMode) - SUB_UseTargets( m_hActivator, USE_ON, 0 ); - else - SUB_UseTargets( m_hActivator, USE_TOGGLE, 0 ); - } - + + SetMoveDone( DoorHitTop ); if ( FClassnameIs(pev, "func_door_rotating")) // !!! BUGBUG Triggered doors don't work with this yet { float sign = 1.0; @@ -786,7 +616,6 @@ void CBaseDoor::DoorHitTop( void ) EMIT_SOUND(ENT(pev), CHAN_STATIC, (char*)STRING(pev->noiseArrived), 1, ATTN_NORM); } -// ALERT(at_debug, "%s hit top\n", STRING(pev->targetname)); ASSERT(m_toggle_state == TS_GOING_UP); m_toggle_state = TS_AT_TOP; @@ -794,42 +623,26 @@ void CBaseDoor::DoorHitTop( void ) if (FBitSet(pev->spawnflags, SF_DOOR_NO_AUTO_RETURN)) { // Re-instate touch method, movement is complete - if ( !FBitSet ( pev->spawnflags, SF_DOOR_USE_ONLY ) || - FBitSet ( pev->spawnflags, SF_DOOR_FORCETOUCHABLE ) ) - SetTouch(&CBaseDoor:: DoorTouch ); + if ( !FBitSet ( pev->spawnflags, SF_DOOR_USE_ONLY ) ) + SetTouch( DoorTouch ); } else { // In flWait seconds, DoorGoDown will fire, unless wait is -1, then door stays open - SetNextThink( m_flWait ); - SetThink(&CBaseDoor:: DoorGoDown ); + pev->nextthink = pev->ltime + m_flWait; + SetThink( DoorGoDown ); if ( m_flWait == -1 ) { - DontThink(); + pev->nextthink = -1; } } // Fire the close target (if startopen is set, then "top" is closed) - netname is the close target - if (pev->spawnflags & SF_DOOR_START_OPEN) - { - if (pev->netname) - FireTargets( STRING(pev->netname), m_hActivator, this, USE_TOGGLE, 0 ); - } - else - { - if (pev->message) - FireTargets( STRING(pev->message), m_hActivator, this, USE_TOGGLE, 0 ); - } + if ( pev->netname && (pev->spawnflags & SF_DOOR_START_OPEN) ) + FireTargets( STRING(pev->netname), m_hActivator, this, USE_TOGGLE, 0 ); - // LRC - if (!m_iImmediateMode) - { - if (m_iOnOffMode) - SUB_UseTargets( m_hActivator, USE_OFF, 0 ); - else - SUB_UseTargets( m_hActivator, USE_TOGGLE, 0 ); - } + SUB_UseTargets( m_hActivator, USE_TOGGLE, 0 ); // this isn't finished } @@ -841,36 +654,16 @@ void CBaseDoor::DoorGoDown( void ) if ( !FBitSet( pev->spawnflags, SF_DOOR_SILENT ) ) EMIT_SOUND(ENT(pev), CHAN_STATIC, (char*)STRING(pev->noiseMoving), 1, ATTN_NORM); -// ALERT(at_debug, "%s go down (was %d)\n", STRING(pev->targetname), m_toggle_state); - -//FYI: not defined, so this doesn't happen. --LRC #ifdef DOOR_ASSERT ASSERT(m_toggle_state == TS_AT_TOP); #endif // DOOR_ASSERT m_toggle_state = TS_GOING_DOWN; - SetMoveDone(&CBaseDoor:: DoorHitBottom ); + SetMoveDone( DoorHitBottom ); if ( FClassnameIs(pev, "func_door_rotating"))//rotating door - { - // LRC- if synched, we fire as soon as we start to go down - if (m_iImmediateMode) - { - if (m_iOnOffMode) - SUB_UseTargets( m_hActivator, USE_OFF, 0 ); - else - SUB_UseTargets( m_hActivator, USE_TOGGLE, 0 ); - } AngularMove( m_vecAngle1, pev->speed); - } else - { - // LRC- if synched, we fire as soon as we start to go down - if (m_iImmediateMode) - { - SUB_UseTargets( m_hActivator, USE_OFF, 0 ); - } LinearMove( m_vecPosition1, pev->speed); - } } // @@ -884,123 +677,88 @@ void CBaseDoor::DoorHitBottom( void ) EMIT_SOUND(ENT(pev), CHAN_STATIC, (char*)STRING(pev->noiseArrived), 1, ATTN_NORM); } -// ALERT(at_debug, "%s hit bottom\n", STRING(pev->targetname)); ASSERT(m_toggle_state == TS_GOING_DOWN); m_toggle_state = TS_AT_BOTTOM; // Re-instate touch method, cycle is complete - if ( FBitSet ( pev->spawnflags, SF_DOOR_USE_ONLY ) && - !FBitSet ( pev->spawnflags, SF_DOOR_FORCETOUCHABLE ) ) + if ( FBitSet ( pev->spawnflags, SF_DOOR_USE_ONLY ) ) {// use only door SetTouch ( NULL ); } else // touchable door - SetTouch(&CBaseDoor:: DoorTouch ); + SetTouch( DoorTouch ); + + SUB_UseTargets( m_hActivator, USE_TOGGLE, 0 ); // this isn't finished // Fire the close target (if startopen is set, then "top" is closed) - netname is the close target - // LRC- 'message' is the open target - if (pev->spawnflags & SF_DOOR_START_OPEN) - { - if (pev->message) - FireTargets( STRING(pev->message), m_hActivator, this, USE_TOGGLE, 0 ); - } - else - { - if (pev->netname) - FireTargets( STRING(pev->netname), m_hActivator, this, USE_TOGGLE, 0 ); - } -// else -// { -// ALERT(at_console,"didn't fire closetarget because "); -// if (!(pev->netname)) -// ALERT(at_console,"no netname\n"); -// else if (pev->spawnflags & SF_DOOR_START_OPEN) -// ALERT(at_console,"startopen\n"); -// else -// ALERT(at_console,"!!?!\n"); -// } - - // LRC- if synched, don't fire now - if (!m_iImmediateMode) - { - if (m_iOnOffMode) - SUB_UseTargets( m_hActivator, USE_ON, 0 ); - else - SUB_UseTargets( m_hActivator, USE_TOGGLE, 0 ); - } + if ( pev->netname && !(pev->spawnflags & SF_DOOR_START_OPEN) ) + FireTargets( STRING(pev->netname), m_hActivator, this, USE_TOGGLE, 0 ); } void CBaseDoor::Blocked( CBaseEntity *pOther ) { - //g-cont. simple recursive anouncer for parent system - //tell parent who blocked his - if(!FNullEnt(m_pMoveWith) && m_iLFlags & LF_PARENTMOVE) m_pMoveWith->Blocked( this ); - if(!FNullEnt(m_pChildMoveWith)) - { - if(m_pChildMoveWith == pOther) - { - //ALERT(at_console, "I'am blocked by my child!\n"); - Use( NULL, NULL, USE_OFF, 0 ); - } - } - - UTIL_AssignOrigin(this, pev->origin); - //make delay before retouching - if ( gpGlobals->time < m_flBlockedTime) return; - m_flBlockedTime = gpGlobals->time + 0.5; + edict_t *pentTarget = NULL; + CBaseDoor *pDoor = NULL; - if ( pev->dmg ) pOther->TakeDamage( pev, pev, pev->dmg, DMG_CRUSH ); + + // Hurt the blocker a little. + if ( pev->dmg ) + pOther->TakeDamage( pev, pev, pev->dmg, DMG_CRUSH ); + + // if a door has a negative wait, it would never come back if blocked, + // so let it just squash the object to death real fast if (m_flWait >= 0) { - if ( !FBitSet( pev->spawnflags, SF_DOOR_SILENT ) ) - STOP_SOUND(ENT(pev), CHAN_STATIC, (char*)STRING(pev->noiseMoving) ); - if (m_toggle_state == TS_GOING_DOWN) DoorGoUp(); - else DoorGoDown(); + if (m_toggle_state == TS_GOING_DOWN) + { + DoorGoUp(); + } + else + { + DoorGoDown(); + } } - - //what the hell does this ? - //UTIL_SynchDoors( this ); - SetNextThink( 0 ); - - CBaseEntity *pTarget = NULL; - CBaseDoor *pDoor = NULL; // Block all door pieces with the same targetname here. - //LRC - in immediate mode don't do this, doors are expected to do it themselves. - if ( !m_iImmediateMode && !FStringNull ( pev->targetname ) ) + if ( !FStringNull ( pev->targetname ) ) { for (;;) { - pTarget = UTIL_FindEntityByTargetname(pTarget, STRING(pev->targetname)); + pentTarget = FIND_ENTITY_BY_TARGETNAME(pentTarget, STRING(pev->targetname)); - if ( !pTarget ) - break; - - if ( pTarget != this ) + if ( VARS( pentTarget ) != pev ) { - pDoor = GetClassPtr( (CBaseDoor *) VARS(pTarget->pev) ); - if ( pDoor->m_flWait >= 0) + if (FNullEnt(pentTarget)) + break; + + if ( FClassnameIs ( pentTarget, "func_door" ) || FClassnameIs ( pentTarget, "func_door_rotating" ) ) { - // avelocity == velocity!? LRC - //g-cont. rewrote this hack. now is working correctly - if (pDoor->pev->velocity == pev->velocity && FClassnameIs ( pTarget->pev, "func_door" )) + + pDoor = GetClassPtr( (CBaseDoor *) VARS(pentTarget) ); + + if ( pDoor->m_flWait >= 0) { - // this is the most hacked, evil, bastardized thing I've ever seen. kjb - pDoor->pev->origin = pev->origin; - UTIL_SetVelocity(pDoor, g_vecZero);// stop! + if (pDoor->pev->velocity == pev->velocity && pDoor->pev->avelocity == pev->velocity) + { + // this is the most hacked, evil, bastardized thing I've ever seen. kjb + if ( FClassnameIs ( pentTarget, "func_door" ) ) + {// set origin to realign normal doors + pDoor->pev->origin = pev->origin; + pDoor->pev->velocity = g_vecZero;// stop! + } + else + {// set angles to realign rotating doors + pDoor->pev->angles = pev->angles; + pDoor->pev->avelocity = g_vecZero; + } + } + + if ( pDoor->m_toggle_state == TS_GOING_DOWN) + pDoor->DoorGoUp(); + else + pDoor->DoorGoDown(); } - if (pDoor->pev->avelocity == pev->avelocity && FClassnameIs ( pTarget->pev, "func_door_rotating" )) - { - pDoor->pev->angles = pev->angles; - UTIL_SetAvelocity(pDoor, g_vecZero); - } - - if ( !FBitSet( pDoor->pev->spawnflags, SF_DOOR_SILENT ) ) - STOP_SOUND(ENT(pDoor->pev), CHAN_STATIC, (char*)STRING(pDoor->pev->noiseMoving) ); - if ( pDoor->m_toggle_state == TS_GOING_DOWN) - pDoor->DoorGoUp(); - else pDoor->DoorGoDown(); } } } @@ -1050,8 +808,6 @@ class CRotDoor : public CBaseDoor { public: void Spawn( void ); - void KeyValue( KeyValueData *pkvd ); - virtual void PostSpawn( void ) {} // don't use the moveWith fix from CBaseDoor virtual void SetToggleState( int state ); }; @@ -1071,9 +827,7 @@ void CRotDoor::Spawn( void ) //m_flWait = 2; who the hell did this? (sjb) m_vecAngle1 = pev->angles; m_vecAngle2 = pev->angles + pev->movedir * m_flMoveDistance; - - SetBits( m_iLFlags, LF_ANGULAR ); - + ASSERTSZ(m_vecAngle1 != m_vecAngle2, "rotating door start/end positions are equal"); if ( FBitSet (pev->spawnflags, SF_DOOR_PASSABLE) ) @@ -1082,7 +836,7 @@ void CRotDoor::Spawn( void ) pev->solid = SOLID_BSP; pev->movetype = MOVETYPE_PUSH; - UTIL_SetOrigin(this, pev->origin); + UTIL_SetOrigin(pev, pev->origin); SET_MODEL(ENT(pev), STRING(pev->model) ); if (pev->speed == 0) @@ -1101,24 +855,14 @@ void CRotDoor::Spawn( void ) m_toggle_state = TS_AT_BOTTOM; - if ( FBitSet ( pev->spawnflags, SF_DOOR_USE_ONLY ) && !FBitSet(pev->spawnflags, SF_DOOR_FORCETOUCHABLE) ) + if ( FBitSet ( pev->spawnflags, SF_DOOR_USE_ONLY ) ) { SetTouch ( NULL ); } else // touchable button - SetTouch(&CRotDoor:: DoorTouch ); + SetTouch( DoorTouch ); } -void CRotDoor::KeyValue( KeyValueData *pkvd ) -{ - if (FStrEq(pkvd->szKeyName, "axes")) - { - UTIL_StringToVector( (float*)(pev->movedir), pkvd->szValue ); - pkvd->fHandled = TRUE; - } - else - CBaseDoor::KeyValue( pkvd ); -} void CRotDoor :: SetToggleState( int state ) { @@ -1127,7 +871,7 @@ void CRotDoor :: SetToggleState( int state ) else pev->angles = m_vecAngle1; - UTIL_SetOrigin( this, pev->origin ); + UTIL_SetOrigin( pev, pev->origin ); } @@ -1136,7 +880,6 @@ class CMomentaryDoor : public CBaseToggle public: void Spawn( void ); void Precache( void ); - void EXPORT MomentaryMoveDone( void ); void KeyValue( KeyValueData *pkvd ); void Use( CBaseEntity *pActivator, CBaseEntity *pCaller, USE_TYPE useType, float value ); @@ -1146,14 +889,7 @@ public: virtual int Restore( CRestore &restore ); static TYPEDESCRIPTION m_SaveData[]; - BYTE m_bMoveSnd; // sound a door makes while moving - BYTE m_bStopSnd; // sound a door makes while moving - - STATE m_iState; - float m_fLastPos; - - STATE GetState( void ) { return m_iState; } - float CalcRatio( CBaseEntity *pLocus ) { return m_fLastPos; } + BYTE m_bMoveSnd; // sound a door makes while moving }; LINK_ENTITY_TO_CLASS( momentary_door, CMomentaryDoor ); @@ -1161,9 +897,6 @@ LINK_ENTITY_TO_CLASS( momentary_door, CMomentaryDoor ); TYPEDESCRIPTION CMomentaryDoor::m_SaveData[] = { DEFINE_FIELD( CMomentaryDoor, m_bMoveSnd, FIELD_CHARACTER ), - DEFINE_FIELD( CMomentaryDoor, m_bStopSnd, FIELD_CHARACTER ), - DEFINE_FIELD( CMomentaryDoor, m_iState, FIELD_INTEGER ), - DEFINE_FIELD( CMomentaryDoor, m_fLastPos, FIELD_FLOAT ), }; IMPLEMENT_SAVERESTORE( CMomentaryDoor, CBaseToggle ); @@ -1175,41 +908,30 @@ void CMomentaryDoor::Spawn( void ) pev->solid = SOLID_BSP; pev->movetype = MOVETYPE_PUSH; - UTIL_SetOrigin(this, pev->origin); + UTIL_SetOrigin(pev, pev->origin); SET_MODEL( ENT(pev), STRING(pev->model) ); -// if (pev->speed == 0) -// pev->speed = 100; + if (pev->speed == 0) + pev->speed = 100; if (pev->dmg == 0) pev->dmg = 2; - - m_iState = STATE_OFF; m_vecPosition1 = pev->origin; // Subtract 2 from size because the engine expands bboxes by 1 in all directions making the size too big m_vecPosition2 = m_vecPosition1 + (pev->movedir * (fabs( pev->movedir.x * (pev->size.x-2) ) + fabs( pev->movedir.y * (pev->size.y-2) ) + fabs( pev->movedir.z * (pev->size.z-2) ) - m_flLip)); ASSERTSZ(m_vecPosition1 != m_vecPosition2, "door start/end positions are equal"); - //LRC: FIXME, move to PostSpawn if ( FBitSet (pev->spawnflags, SF_DOOR_START_OPEN) ) { // swap pos1 and pos2, put door at pos2 - UTIL_AssignOrigin(this, m_vecPosition2); - Vector vecTemp = m_vecPosition2; + UTIL_SetOrigin(pev, m_vecPosition2); m_vecPosition2 = m_vecPosition1; - m_vecPosition1 = vecTemp; + m_vecPosition1 = pev->origin; } - - if (m_pMoveWith) - { - m_vecPosition1 = m_vecPosition1 - m_pMoveWith->pev->origin; - m_vecPosition2 = m_vecPosition2 - m_pMoveWith->pev->origin; - } - - Precache(); SetTouch( NULL ); + Precache(); } - + void CMomentaryDoor::Precache( void ) { @@ -1255,50 +977,6 @@ void CMomentaryDoor::Precache( void ) pev->noiseMoving = ALLOC_STRING("common/null.wav"); break; } - - - // set the door's "stop" sound - switch (m_bStopSnd) - { - case 0: - pev->noiseArrived = ALLOC_STRING("common/null.wav"); - break; - case 1: - PRECACHE_SOUND ("doors/doorstop1.wav"); - pev->noiseArrived = ALLOC_STRING("doors/doorstop1.wav"); - break; - case 2: - PRECACHE_SOUND ("doors/doorstop2.wav"); - pev->noiseArrived = ALLOC_STRING("doors/doorstop2.wav"); - break; - case 3: - PRECACHE_SOUND ("doors/doorstop3.wav"); - pev->noiseArrived = ALLOC_STRING("doors/doorstop3.wav"); - break; - case 4: - PRECACHE_SOUND ("doors/doorstop4.wav"); - pev->noiseArrived = ALLOC_STRING("doors/doorstop4.wav"); - break; - case 5: - PRECACHE_SOUND ("doors/doorstop5.wav"); - pev->noiseArrived = ALLOC_STRING("doors/doorstop5.wav"); - break; - case 6: - PRECACHE_SOUND ("doors/doorstop6.wav"); - pev->noiseArrived = ALLOC_STRING("doors/doorstop6.wav"); - break; - case 7: - PRECACHE_SOUND ("doors/doorstop7.wav"); - pev->noiseArrived = ALLOC_STRING("doors/doorstop7.wav"); - break; - case 8: - PRECACHE_SOUND ("doors/doorstop8.wav"); - pev->noiseArrived = ALLOC_STRING("doors/doorstop8.wav"); - break; - default: - pev->noiseArrived = ALLOC_STRING("common/null.wav"); - break; - } } void CMomentaryDoor::KeyValue( KeyValueData *pkvd ) @@ -1311,7 +989,7 @@ void CMomentaryDoor::KeyValue( KeyValueData *pkvd ) } else if (FStrEq(pkvd->szKeyName, "stopsnd")) { - m_bStopSnd = atof(pkvd->szValue); +// m_bStopSnd = atof(pkvd->szValue); pkvd->fHandled = TRUE; } else if (FStrEq(pkvd->szKeyName, "healthvalue")) @@ -1330,55 +1008,19 @@ void CMomentaryDoor::Use( CBaseEntity *pActivator, CBaseEntity *pCaller, USE_TYP if ( value > 1.0 ) value = 1.0; - - if (IsLockedByMaster()) return; - Vector move = m_vecPosition1 + (value * (m_vecPosition2 - m_vecPosition1)); + + Vector delta = move - pev->origin; + float speed = delta.Length() * 10; - float speed = 0; - Vector delta; - -/* if ((value == 1) || (value == 0)) - { - STOP_SOUND(ENT(pev), CHAN_STATIC, (char*)STRING(pev->noiseMoving));//G-Cont. fix sound bug (original HL). - return; - } -*/ - if (pev->speed) - { - //LRC- move at the given speed, if any. - speed = pev->speed; - } - else - { - // default: get there in 0.1 secs - delta = move - pev->origin; - - speed = delta.Length() * 10; - } - - //FIXME: allow for it being told to move at the same speed in the _opposite_ direction! if ( speed != 0 ) { - // This entity only thinks when it moves - //LRC- nope, in a MoveWith world you can't rely on that. Check the state instead. - if ( m_iState == STATE_OFF ) - { - //ALERT(at_console,"USE: start moving to %f %f %f.\n", move.x, move.y, move.z); - m_iState = STATE_ON; + // This entity only thinks when it moves, so if it's thinking, it's in the process of moving + // play the sound when it starts moving + if ( pev->nextthink < pev->ltime || pev->nextthink == 0 ) EMIT_SOUND(ENT(pev), CHAN_STATIC, (char*)STRING(pev->noiseMoving), 1, ATTN_NORM); - } - m_fLastPos = value; LinearMove( move, speed ); - //LinearMove( m_vecPosition1, pev->speed, m_fAcceleration, m_fDeceleration); - SetMoveDone(&CMomentaryDoor:: MomentaryMoveDone ); } -} -void CMomentaryDoor::MomentaryMoveDone( void ) -{ - m_iState = STATE_OFF; - STOP_SOUND(ENT(pev), CHAN_STATIC, (char*)STRING(pev->noiseMoving)); - EMIT_SOUND(ENT(pev), CHAN_STATIC, (char*)STRING(pev->noiseArrived), 1, ATTN_NORM); -} +} \ No newline at end of file diff --git a/spirit/doors.h b/dlls/doors.h similarity index 70% rename from spirit/doors.h rename to dlls/doors.h index 611cbba7..55a853fc 100644 --- a/spirit/doors.h +++ b/dlls/doors.h @@ -1,38 +1,33 @@ -/*** -* -* Copyright (c) 1996-2002, 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. -* -****/ -#ifndef DOORS_H -#define DOORS_H - -// doors -#define SF_DOOR_ROTATE_Y 0 -#define SF_DOOR_START_OPEN 1 -#define SF_DOOR_ROTATE_BACKWARDS 2 -#define SF_DOOR_PASSABLE 8 -#define SF_DOOR_ONEWAY 16 -#define SF_DOOR_NO_AUTO_RETURN 32 -#define SF_DOOR_ROTATE_Z 64 -#define SF_DOOR_ROTATE_X 128 -#define SF_DOOR_USE_ONLY 256 // door must be opened by player's use button. -#define SF_DOOR_NOMONSTERS 512 // Monster can't open -#define SF_DOOR_FORCETOUCHABLE 1024 //LRC- Opens when touched, even though it's named and/or "use only" -//LRC - clashes with 'not in deathmatch'. Replaced with 'Target mode' and 'On/Off Mode' fields. -//#define SF_DOOR_SYNCHED 2048 //LRC- sends USE_ON/OFF when it starts to open/close (instead of sending - // USE_TOGGLE when fully open/closed); also responds to USE_ON and USE_OFF - // 'correctly'. -#define SF_DOOR_SILENT 0x80000000 - - - -#endif //DOORS_H +/*** +* +* Copyright (c) 1996-2002, 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. +* +****/ +#ifndef DOORS_H +#define DOORS_H + +// doors +#define SF_DOOR_ROTATE_Y 0 +#define SF_DOOR_START_OPEN 1 +#define SF_DOOR_ROTATE_BACKWARDS 2 +#define SF_DOOR_PASSABLE 8 +#define SF_DOOR_ONEWAY 16 +#define SF_DOOR_NO_AUTO_RETURN 32 +#define SF_DOOR_ROTATE_Z 64 +#define SF_DOOR_ROTATE_X 128 +#define SF_DOOR_USE_ONLY 256 // door must be opened by player's use button. +#define SF_DOOR_NOMONSTERS 512 // Monster can't open +#define SF_DOOR_SILENT 0x80000000 + + + +#endif //DOORS_H diff --git a/dlls/effects.cpp b/dlls/effects.cpp new file mode 100644 index 00000000..5d7a1b38 --- /dev/null +++ b/dlls/effects.cpp @@ -0,0 +1,2268 @@ +/*** +* +* Copyright (c) 1996-2002, 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. +* +****/ +#include "extdll.h" +#include "util.h" +#include "cbase.h" +#include "monsters.h" +#include "customentity.h" +#include "effects.h" +#include "weapons.h" +#include "decals.h" +#include "func_break.h" +#include "shake.h" + +#define SF_GIBSHOOTER_REPEATABLE 1 // allows a gibshooter to be refired + +#define SF_FUNNEL_REVERSE 1 // funnel effect repels particles instead of attracting them. + + +// Lightning target, just alias landmark +LINK_ENTITY_TO_CLASS( info_target, CPointEntity ); + + +class CBubbling : public CBaseEntity +{ +public: + void Spawn( void ); + void Precache( void ); + void KeyValue( KeyValueData *pkvd ); + + void EXPORT FizzThink( void ); + void Use( CBaseEntity *pActivator, CBaseEntity *pCaller, USE_TYPE useType, float value ); + + virtual int Save( CSave &save ); + virtual int Restore( CRestore &restore ); + virtual int ObjectCaps( void ) { return CBaseEntity::ObjectCaps() & ~FCAP_ACROSS_TRANSITION; } + static TYPEDESCRIPTION m_SaveData[]; + + int m_density; + int m_frequency; + int m_bubbleModel; + int m_state; +}; + +LINK_ENTITY_TO_CLASS( env_bubbles, CBubbling ); + +TYPEDESCRIPTION CBubbling::m_SaveData[] = +{ + DEFINE_FIELD( CBubbling, m_density, FIELD_INTEGER ), + DEFINE_FIELD( CBubbling, m_frequency, FIELD_INTEGER ), + DEFINE_FIELD( CBubbling, m_state, FIELD_INTEGER ), + // Let spawn restore this! + // DEFINE_FIELD( CBubbling, m_bubbleModel, FIELD_INTEGER ), +}; + +IMPLEMENT_SAVERESTORE( CBubbling, CBaseEntity ); + + +#define SF_BUBBLES_STARTOFF 0x0001 + +void CBubbling::Spawn( void ) +{ + Precache( ); + SET_MODEL( ENT(pev), STRING(pev->model) ); // Set size + + pev->solid = SOLID_NOT; // Remove model & collisions + pev->renderamt = 0; // The engine won't draw this model if this is set to 0 and blending is on + pev->rendermode = kRenderTransTexture; + int speed = pev->speed > 0 ? pev->speed : -pev->speed; + + // HACKHACK!!! - Speed in rendercolor + pev->rendercolor.x = speed >> 8; + pev->rendercolor.y = speed & 255; + pev->rendercolor.z = (pev->speed < 0) ? 1 : 0; + + + if ( !(pev->spawnflags & SF_BUBBLES_STARTOFF) ) + { + SetThink( FizzThink ); + pev->nextthink = gpGlobals->time + 2.0; + m_state = 1; + } + else + m_state = 0; +} + +void CBubbling::Precache( void ) +{ + m_bubbleModel = PRECACHE_MODEL("sprites/bubble.spr"); // Precache bubble sprite +} + + +void CBubbling::Use( CBaseEntity *pActivator, CBaseEntity *pCaller, USE_TYPE useType, float value ) +{ + if ( ShouldToggle( useType, m_state ) ) + m_state = !m_state; + + if ( m_state ) + { + SetThink( FizzThink ); + pev->nextthink = gpGlobals->time + 0.1; + } + else + { + SetThink( NULL ); + pev->nextthink = 0; + } +} + + +void CBubbling::KeyValue( KeyValueData *pkvd ) +{ + if (FStrEq(pkvd->szKeyName, "density")) + { + m_density = atoi(pkvd->szValue); + pkvd->fHandled = TRUE; + } + else if (FStrEq(pkvd->szKeyName, "frequency")) + { + m_frequency = atoi(pkvd->szValue); + pkvd->fHandled = TRUE; + } + else if (FStrEq(pkvd->szKeyName, "current")) + { + pev->speed = atoi(pkvd->szValue); + pkvd->fHandled = TRUE; + } + else + CBaseEntity::KeyValue( pkvd ); +} + + +void CBubbling::FizzThink( void ) +{ + MESSAGE_BEGIN( MSG_PAS, SVC_TEMPENTITY, VecBModelOrigin(pev) ); + WRITE_BYTE( TE_FIZZ ); + WRITE_SHORT( (short)ENTINDEX( edict() ) ); + WRITE_SHORT( (short)m_bubbleModel ); + WRITE_BYTE( m_density ); + MESSAGE_END(); + + if ( m_frequency > 19 ) + pev->nextthink = gpGlobals->time + 0.5; + else + pev->nextthink = gpGlobals->time + 2.5 - (0.1 * m_frequency); +} + +// -------------------------------------------------- +// +// Beams +// +// -------------------------------------------------- + +LINK_ENTITY_TO_CLASS( beam, CBeam ); + +void CBeam::Spawn( void ) +{ + pev->solid = SOLID_NOT; // Remove model & collisions + Precache( ); +} + +void CBeam::Precache( void ) +{ + if ( pev->owner ) + SetStartEntity( ENTINDEX( pev->owner ) ); + if ( pev->aiment ) + SetEndEntity( ENTINDEX( pev->aiment ) ); +} + +void CBeam::SetStartEntity( int entityIndex ) +{ + pev->sequence = (entityIndex & 0x0FFF) | ((pev->sequence&0xF000)<<12); + pev->owner = g_engfuncs.pfnPEntityOfEntIndex( entityIndex ); +} + +void CBeam::SetEndEntity( int entityIndex ) +{ + pev->skin = (entityIndex & 0x0FFF) | ((pev->skin&0xF000)<<12); + pev->aiment = g_engfuncs.pfnPEntityOfEntIndex( entityIndex ); +} + + +// These don't take attachments into account +const Vector &CBeam::GetStartPos( void ) +{ + if ( GetType() == BEAM_ENTS ) + { + edict_t *pent = g_engfuncs.pfnPEntityOfEntIndex( GetStartEntity() ); + return pent->v.origin; + } + return pev->origin; +} + + +const Vector &CBeam::GetEndPos( void ) +{ + int type = GetType(); + if ( type == BEAM_POINTS || type == BEAM_HOSE ) + { + return pev->angles; + } + + edict_t *pent = g_engfuncs.pfnPEntityOfEntIndex( GetEndEntity() ); + if ( pent ) + return pent->v.origin; + return pev->angles; +} + + +CBeam *CBeam::BeamCreate( const char *pSpriteName, int width ) +{ + // Create a new entity with CBeam private data + CBeam *pBeam = GetClassPtr( (CBeam *)NULL ); + pBeam->pev->classname = MAKE_STRING("beam"); + + pBeam->BeamInit( pSpriteName, width ); + + return pBeam; +} + + +void CBeam::BeamInit( const char *pSpriteName, int width ) +{ + pev->flags |= FL_CUSTOMENTITY; + SetColor( 255, 255, 255 ); + SetBrightness( 255 ); + SetNoise( 0 ); + SetFrame( 0 ); + SetScrollRate( 0 ); + pev->model = MAKE_STRING( pSpriteName ); + SetTexture( PRECACHE_MODEL( (char *)pSpriteName ) ); + SetWidth( width ); + pev->skin = 0; + pev->sequence = 0; + pev->rendermode = 0; +} + + +void CBeam::PointsInit( const Vector &start, const Vector &end ) +{ + SetType( BEAM_POINTS ); + SetStartPos( start ); + SetEndPos( end ); + SetStartAttachment( 0 ); + SetEndAttachment( 0 ); + RelinkBeam(); +} + + +void CBeam::HoseInit( const Vector &start, const Vector &direction ) +{ + SetType( BEAM_HOSE ); + SetStartPos( start ); + SetEndPos( direction ); + SetStartAttachment( 0 ); + SetEndAttachment( 0 ); + RelinkBeam(); +} + + +void CBeam::PointEntInit( const Vector &start, int endIndex ) +{ + SetType( BEAM_ENTPOINT ); + SetStartPos( start ); + SetEndEntity( endIndex ); + SetStartAttachment( 0 ); + SetEndAttachment( 0 ); + RelinkBeam(); +} + +void CBeam::EntsInit( int startIndex, int endIndex ) +{ + SetType( BEAM_ENTS ); + SetStartEntity( startIndex ); + SetEndEntity( endIndex ); + SetStartAttachment( 0 ); + SetEndAttachment( 0 ); + RelinkBeam(); +} + + +void CBeam::RelinkBeam( void ) +{ + const Vector &startPos = GetStartPos(), &endPos = GetEndPos(); + + pev->mins.x = min( startPos.x, endPos.x ); + pev->mins.y = min( startPos.y, endPos.y ); + pev->mins.z = min( startPos.z, endPos.z ); + pev->maxs.x = max( startPos.x, endPos.x ); + pev->maxs.y = max( startPos.y, endPos.y ); + pev->maxs.z = max( startPos.z, endPos.z ); + pev->mins = pev->mins - pev->origin; + pev->maxs = pev->maxs - pev->origin; + + UTIL_SetSize( pev, pev->mins, pev->maxs ); + UTIL_SetOrigin( pev, pev->origin ); +} + +#if 0 +void CBeam::SetObjectCollisionBox( void ) +{ + const Vector &startPos = GetStartPos(), &endPos = GetEndPos(); + + pev->absmin.x = min( startPos.x, endPos.x ); + pev->absmin.y = min( startPos.y, endPos.y ); + pev->absmin.z = min( startPos.z, endPos.z ); + pev->absmax.x = max( startPos.x, endPos.x ); + pev->absmax.y = max( startPos.y, endPos.y ); + pev->absmax.z = max( startPos.z, endPos.z ); +} +#endif + + +void CBeam::TriggerTouch( CBaseEntity *pOther ) +{ + if ( pOther->pev->flags & (FL_CLIENT | FL_MONSTER) ) + { + if ( pev->owner ) + { + CBaseEntity *pOwner = CBaseEntity::Instance(pev->owner); + pOwner->Use( pOther, this, USE_TOGGLE, 0 ); + } + ALERT( at_console, "Firing targets!!!\n" ); + } +} + + +CBaseEntity *CBeam::RandomTargetname( const char *szName ) +{ + int total = 0; + + CBaseEntity *pEntity = NULL; + CBaseEntity *pNewEntity = NULL; + while ((pNewEntity = UTIL_FindEntityByTargetname( pNewEntity, szName )) != NULL) + { + total++; + if (RANDOM_LONG(0,total-1) < 1) + pEntity = pNewEntity; + } + return pEntity; +} + + +void CBeam::DoSparks( const Vector &start, const Vector &end ) +{ + if ( pev->spawnflags & (SF_BEAM_SPARKSTART|SF_BEAM_SPARKEND) ) + { + if ( pev->spawnflags & SF_BEAM_SPARKSTART ) + { + UTIL_Sparks( start ); + } + if ( pev->spawnflags & SF_BEAM_SPARKEND ) + { + UTIL_Sparks( end ); + } + } +} + + +class CLightning : public CBeam +{ +public: + void Spawn( void ); + void Precache( void ); + void KeyValue( KeyValueData *pkvd ); + void Activate( void ); + + void EXPORT StrikeThink( void ); + void EXPORT DamageThink( void ); + void RandomArea( void ); + void RandomPoint( Vector &vecSrc ); + void Zap( const Vector &vecSrc, const Vector &vecDest ); + void EXPORT StrikeUse( CBaseEntity *pActivator, CBaseEntity *pCaller, USE_TYPE useType, float value ); + void EXPORT ToggleUse( CBaseEntity *pActivator, CBaseEntity *pCaller, USE_TYPE useType, float value ); + + inline BOOL ServerSide( void ) + { + if ( m_life == 0 && !(pev->spawnflags & SF_BEAM_RING) ) + return TRUE; + return FALSE; + } + + virtual int Save( CSave &save ); + virtual int Restore( CRestore &restore ); + static TYPEDESCRIPTION m_SaveData[]; + + void BeamUpdateVars( void ); + + int m_active; + int m_iszStartEntity; + int m_iszEndEntity; + float m_life; + int m_boltWidth; + int m_noiseAmplitude; + int m_brightness; + int m_speed; + float m_restrike; + int m_spriteTexture; + int m_iszSpriteName; + int m_frameStart; + + float m_radius; +}; + +LINK_ENTITY_TO_CLASS( env_lightning, CLightning ); +LINK_ENTITY_TO_CLASS( env_beam, CLightning ); + +// UNDONE: Jay -- This is only a test +#if _DEBUG +class CTripBeam : public CLightning +{ + void Spawn( void ); +}; +LINK_ENTITY_TO_CLASS( trip_beam, CTripBeam ); + +void CTripBeam::Spawn( void ) +{ + CLightning::Spawn(); + SetTouch( TriggerTouch ); + pev->solid = SOLID_TRIGGER; + RelinkBeam(); +} +#endif + + + +TYPEDESCRIPTION CLightning::m_SaveData[] = +{ + DEFINE_FIELD( CLightning, m_active, FIELD_INTEGER ), + DEFINE_FIELD( CLightning, m_iszStartEntity, FIELD_STRING ), + DEFINE_FIELD( CLightning, m_iszEndEntity, FIELD_STRING ), + DEFINE_FIELD( CLightning, m_life, FIELD_FLOAT ), + DEFINE_FIELD( CLightning, m_boltWidth, FIELD_INTEGER ), + DEFINE_FIELD( CLightning, m_noiseAmplitude, FIELD_INTEGER ), + DEFINE_FIELD( CLightning, m_brightness, FIELD_INTEGER ), + DEFINE_FIELD( CLightning, m_speed, FIELD_INTEGER ), + DEFINE_FIELD( CLightning, m_restrike, FIELD_FLOAT ), + DEFINE_FIELD( CLightning, m_spriteTexture, FIELD_INTEGER ), + DEFINE_FIELD( CLightning, m_iszSpriteName, FIELD_STRING ), + DEFINE_FIELD( CLightning, m_frameStart, FIELD_INTEGER ), + DEFINE_FIELD( CLightning, m_radius, FIELD_FLOAT ), +}; + +IMPLEMENT_SAVERESTORE( CLightning, CBeam ); + + +void CLightning::Spawn( void ) +{ + if ( FStringNull( m_iszSpriteName ) ) + { + SetThink( SUB_Remove ); + return; + } + pev->solid = SOLID_NOT; // Remove model & collisions + Precache( ); + + pev->dmgtime = gpGlobals->time; + + if ( ServerSide() ) + { + SetThink( NULL ); + if ( pev->dmg > 0 ) + { + SetThink( DamageThink ); + pev->nextthink = gpGlobals->time + 0.1; + } + if ( pev->targetname ) + { + if ( !(pev->spawnflags & SF_BEAM_STARTON) ) + { + pev->effects = EF_NODRAW; + m_active = 0; + pev->nextthink = 0; + } + else + m_active = 1; + + SetUse( ToggleUse ); + } + } + else + { + m_active = 0; + if ( !FStringNull(pev->targetname) ) + { + SetUse( StrikeUse ); + } + if ( FStringNull(pev->targetname) || FBitSet(pev->spawnflags, SF_BEAM_STARTON) ) + { + SetThink( StrikeThink ); + pev->nextthink = gpGlobals->time + 1.0; + } + } +} + +void CLightning::Precache( void ) +{ + m_spriteTexture = PRECACHE_MODEL( (char *)STRING(m_iszSpriteName) ); + CBeam::Precache(); +} + + +void CLightning::Activate( void ) +{ + if ( ServerSide() ) + BeamUpdateVars(); +} + + +void CLightning::KeyValue( KeyValueData *pkvd ) +{ + if (FStrEq(pkvd->szKeyName, "LightningStart")) + { + m_iszStartEntity = ALLOC_STRING( pkvd->szValue ); + pkvd->fHandled = TRUE; + } + else if (FStrEq(pkvd->szKeyName, "LightningEnd")) + { + m_iszEndEntity = ALLOC_STRING( pkvd->szValue ); + pkvd->fHandled = TRUE; + } + else if (FStrEq(pkvd->szKeyName, "life")) + { + m_life = atof(pkvd->szValue); + pkvd->fHandled = TRUE; + } + else if (FStrEq(pkvd->szKeyName, "BoltWidth")) + { + m_boltWidth = atoi(pkvd->szValue); + pkvd->fHandled = TRUE; + } + else if (FStrEq(pkvd->szKeyName, "NoiseAmplitude")) + { + m_noiseAmplitude = atoi(pkvd->szValue); + pkvd->fHandled = TRUE; + } + else if (FStrEq(pkvd->szKeyName, "TextureScroll")) + { + m_speed = atoi(pkvd->szValue); + pkvd->fHandled = TRUE; + } + else if (FStrEq(pkvd->szKeyName, "StrikeTime")) + { + m_restrike = atof(pkvd->szValue); + pkvd->fHandled = TRUE; + } + else if (FStrEq(pkvd->szKeyName, "texture")) + { + m_iszSpriteName = ALLOC_STRING(pkvd->szValue); + pkvd->fHandled = TRUE; + } + else if (FStrEq(pkvd->szKeyName, "framestart")) + { + m_frameStart = atoi(pkvd->szValue); + pkvd->fHandled = TRUE; + } + else if (FStrEq(pkvd->szKeyName, "Radius")) + { + m_radius = atof( pkvd->szValue ); + pkvd->fHandled = TRUE; + } + else if (FStrEq(pkvd->szKeyName, "damage")) + { + pev->dmg = atof(pkvd->szValue); + pkvd->fHandled = TRUE; + } + else + CBeam::KeyValue( pkvd ); +} + + +void CLightning::ToggleUse( CBaseEntity *pActivator, CBaseEntity *pCaller, USE_TYPE useType, float value ) +{ + if ( !ShouldToggle( useType, m_active ) ) + return; + if ( m_active ) + { + m_active = 0; + pev->effects |= EF_NODRAW; + pev->nextthink = 0; + } + else + { + m_active = 1; + pev->effects &= ~EF_NODRAW; + DoSparks( GetStartPos(), GetEndPos() ); + if ( pev->dmg > 0 ) + { + pev->nextthink = gpGlobals->time; + pev->dmgtime = gpGlobals->time; + } + } +} + + +void CLightning::StrikeUse( CBaseEntity *pActivator, CBaseEntity *pCaller, USE_TYPE useType, float value ) +{ + if ( !ShouldToggle( useType, m_active ) ) + return; + + if ( m_active ) + { + m_active = 0; + SetThink( NULL ); + } + else + { + SetThink( StrikeThink ); + pev->nextthink = gpGlobals->time + 0.1; + } + + if ( !FBitSet( pev->spawnflags, SF_BEAM_TOGGLE ) ) + SetUse( NULL ); +} + + +int IsPointEntity( CBaseEntity *pEnt ) +{ + if ( !pEnt->pev->modelindex ) + return 1; + if ( FClassnameIs( pEnt->pev, "info_target" ) || FClassnameIs( pEnt->pev, "info_landmark" ) || FClassnameIs( pEnt->pev, "path_corner" ) ) + return 1; + + return 0; +} + + +void CLightning::StrikeThink( void ) +{ + if ( m_life != 0 ) + { + if ( pev->spawnflags & SF_BEAM_RANDOM ) + pev->nextthink = gpGlobals->time + m_life + RANDOM_FLOAT( 0, m_restrike ); + else + pev->nextthink = gpGlobals->time + m_life + m_restrike; + } + m_active = 1; + + if (FStringNull(m_iszEndEntity)) + { + if (FStringNull(m_iszStartEntity)) + { + RandomArea( ); + } + else + { + CBaseEntity *pStart = RandomTargetname( STRING(m_iszStartEntity) ); + if (pStart != NULL) + RandomPoint( pStart->pev->origin ); + else + ALERT( at_console, "env_beam: unknown entity \"%s\"\n", STRING(m_iszStartEntity) ); + } + return; + } + + CBaseEntity *pStart = RandomTargetname( STRING(m_iszStartEntity) ); + CBaseEntity *pEnd = RandomTargetname( STRING(m_iszEndEntity) ); + + if ( pStart != NULL && pEnd != NULL ) + { + if ( IsPointEntity( pStart ) || IsPointEntity( pEnd ) ) + { + if ( pev->spawnflags & SF_BEAM_RING) + { + // don't work + return; + } + } + + MESSAGE_BEGIN( MSG_BROADCAST, SVC_TEMPENTITY ); + if ( IsPointEntity( pStart ) || IsPointEntity( pEnd ) ) + { + if ( !IsPointEntity( pEnd ) ) // One point entity must be in pEnd + { + CBaseEntity *pTemp; + pTemp = pStart; + pStart = pEnd; + pEnd = pTemp; + } + if ( !IsPointEntity( pStart ) ) // One sided + { + WRITE_BYTE( TE_BEAMENTPOINT ); + WRITE_SHORT( pStart->entindex() ); + WRITE_COORD( pEnd->pev->origin.x); + WRITE_COORD( pEnd->pev->origin.y); + WRITE_COORD( pEnd->pev->origin.z); + } + else + { + WRITE_BYTE( TE_BEAMPOINTS); + WRITE_COORD( pStart->pev->origin.x); + WRITE_COORD( pStart->pev->origin.y); + WRITE_COORD( pStart->pev->origin.z); + WRITE_COORD( pEnd->pev->origin.x); + WRITE_COORD( pEnd->pev->origin.y); + WRITE_COORD( pEnd->pev->origin.z); + } + + + } + else + { + if ( pev->spawnflags & SF_BEAM_RING) + WRITE_BYTE( TE_BEAMRING ); + else + WRITE_BYTE( TE_BEAMENTS ); + WRITE_SHORT( pStart->entindex() ); + WRITE_SHORT( pEnd->entindex() ); + } + + WRITE_SHORT( m_spriteTexture ); + WRITE_BYTE( m_frameStart ); // framestart + WRITE_BYTE( (int)pev->framerate); // framerate + WRITE_BYTE( (int)(m_life*10.0) ); // life + WRITE_BYTE( m_boltWidth ); // width + WRITE_BYTE( m_noiseAmplitude ); // noise + WRITE_BYTE( (int)pev->rendercolor.x ); // r, g, b + WRITE_BYTE( (int)pev->rendercolor.y ); // r, g, b + WRITE_BYTE( (int)pev->rendercolor.z ); // r, g, b + WRITE_BYTE( pev->renderamt ); // brightness + WRITE_BYTE( m_speed ); // speed + MESSAGE_END(); + DoSparks( pStart->pev->origin, pEnd->pev->origin ); + if ( pev->dmg > 0 ) + { + TraceResult tr; + UTIL_TraceLine( pStart->pev->origin, pEnd->pev->origin, dont_ignore_monsters, NULL, &tr ); + BeamDamageInstant( &tr, pev->dmg ); + } + } +} + + +void CBeam::BeamDamage( TraceResult *ptr ) +{ + RelinkBeam(); + if ( ptr->flFraction != 1.0 && ptr->pHit != NULL ) + { + CBaseEntity *pHit = CBaseEntity::Instance(ptr->pHit); + if ( pHit ) + { + ClearMultiDamage(); + pHit->TraceAttack( pev, pev->dmg * (gpGlobals->time - pev->dmgtime), (ptr->vecEndPos - pev->origin).Normalize(), ptr, DMG_ENERGYBEAM ); + ApplyMultiDamage( pev, pev ); + if ( pev->spawnflags & SF_BEAM_DECALS ) + { + if ( pHit->IsBSPModel() ) + UTIL_DecalTrace( ptr, DECAL_BIGSHOT1 + RANDOM_LONG(0,4) ); + } + } + } + pev->dmgtime = gpGlobals->time; +} + + +void CLightning::DamageThink( void ) +{ + pev->nextthink = gpGlobals->time + 0.1; + TraceResult tr; + UTIL_TraceLine( GetStartPos(), GetEndPos(), dont_ignore_monsters, NULL, &tr ); + BeamDamage( &tr ); +} + + + +void CLightning::Zap( const Vector &vecSrc, const Vector &vecDest ) +{ +#if 1 + MESSAGE_BEGIN( MSG_BROADCAST, SVC_TEMPENTITY ); + WRITE_BYTE( TE_BEAMPOINTS); + WRITE_COORD(vecSrc.x); + WRITE_COORD(vecSrc.y); + WRITE_COORD(vecSrc.z); + WRITE_COORD(vecDest.x); + WRITE_COORD(vecDest.y); + WRITE_COORD(vecDest.z); + WRITE_SHORT( m_spriteTexture ); + WRITE_BYTE( m_frameStart ); // framestart + WRITE_BYTE( (int)pev->framerate); // framerate + WRITE_BYTE( (int)(m_life*10.0) ); // life + WRITE_BYTE( m_boltWidth ); // width + WRITE_BYTE( m_noiseAmplitude ); // noise + WRITE_BYTE( (int)pev->rendercolor.x ); // r, g, b + WRITE_BYTE( (int)pev->rendercolor.y ); // r, g, b + WRITE_BYTE( (int)pev->rendercolor.z ); // r, g, b + WRITE_BYTE( pev->renderamt ); // brightness + WRITE_BYTE( m_speed ); // speed + MESSAGE_END(); +#else + MESSAGE_BEGIN( MSG_BROADCAST, SVC_TEMPENTITY ); + WRITE_BYTE(TE_LIGHTNING); + WRITE_COORD(vecSrc.x); + WRITE_COORD(vecSrc.y); + WRITE_COORD(vecSrc.z); + WRITE_COORD(vecDest.x); + WRITE_COORD(vecDest.y); + WRITE_COORD(vecDest.z); + WRITE_BYTE(10); + WRITE_BYTE(50); + WRITE_BYTE(40); + WRITE_SHORT(m_spriteTexture); + MESSAGE_END(); +#endif + DoSparks( vecSrc, vecDest ); +} + +void CLightning::RandomArea( void ) +{ + int iLoops = 0; + + for (iLoops = 0; iLoops < 10; iLoops++) + { + Vector vecSrc = pev->origin; + + Vector vecDir1 = Vector( RANDOM_FLOAT( -1.0, 1.0 ), RANDOM_FLOAT( -1.0, 1.0 ),RANDOM_FLOAT( -1.0, 1.0 ) ); + vecDir1 = vecDir1.Normalize(); + TraceResult tr1; + UTIL_TraceLine( vecSrc, vecSrc + vecDir1 * m_radius, ignore_monsters, ENT(pev), &tr1 ); + + if (tr1.flFraction == 1.0) + continue; + + Vector vecDir2; + do { + vecDir2 = Vector( RANDOM_FLOAT( -1.0, 1.0 ), RANDOM_FLOAT( -1.0, 1.0 ),RANDOM_FLOAT( -1.0, 1.0 ) ); + } while (DotProduct(vecDir1, vecDir2 ) > 0); + vecDir2 = vecDir2.Normalize(); + TraceResult tr2; + UTIL_TraceLine( vecSrc, vecSrc + vecDir2 * m_radius, ignore_monsters, ENT(pev), &tr2 ); + + if (tr2.flFraction == 1.0) + continue; + + if ((tr1.vecEndPos - tr2.vecEndPos).Length() < m_radius * 0.1) + continue; + + UTIL_TraceLine( tr1.vecEndPos, tr2.vecEndPos, ignore_monsters, ENT(pev), &tr2 ); + + if (tr2.flFraction != 1.0) + continue; + + Zap( tr1.vecEndPos, tr2.vecEndPos ); + + break; + } +} + + +void CLightning::RandomPoint( Vector &vecSrc ) +{ + int iLoops = 0; + + for (iLoops = 0; iLoops < 10; iLoops++) + { + Vector vecDir1 = Vector( RANDOM_FLOAT( -1.0, 1.0 ), RANDOM_FLOAT( -1.0, 1.0 ),RANDOM_FLOAT( -1.0, 1.0 ) ); + vecDir1 = vecDir1.Normalize(); + TraceResult tr1; + UTIL_TraceLine( vecSrc, vecSrc + vecDir1 * m_radius, ignore_monsters, ENT(pev), &tr1 ); + + if ((tr1.vecEndPos - vecSrc).Length() < m_radius * 0.1) + continue; + + if (tr1.flFraction == 1.0) + continue; + + Zap( vecSrc, tr1.vecEndPos ); + break; + } +} + + + +void CLightning::BeamUpdateVars( void ) +{ + int beamType; + int pointStart, pointEnd; + + edict_t *pStart = FIND_ENTITY_BY_TARGETNAME ( NULL, STRING(m_iszStartEntity) ); + edict_t *pEnd = FIND_ENTITY_BY_TARGETNAME ( NULL, STRING(m_iszEndEntity) ); + pointStart = IsPointEntity( CBaseEntity::Instance(pStart) ); + pointEnd = IsPointEntity( CBaseEntity::Instance(pEnd) ); + + pev->skin = 0; + pev->sequence = 0; + pev->rendermode = 0; + pev->flags |= FL_CUSTOMENTITY; + pev->model = m_iszSpriteName; + SetTexture( m_spriteTexture ); + + beamType = BEAM_ENTS; + if ( pointStart || pointEnd ) + { + if ( !pointStart ) // One point entity must be in pStart + { + edict_t *pTemp; + // Swap start & end + pTemp = pStart; + pStart = pEnd; + pEnd = pTemp; + int swap = pointStart; + pointStart = pointEnd; + pointEnd = swap; + } + if ( !pointEnd ) + beamType = BEAM_ENTPOINT; + else + beamType = BEAM_POINTS; + } + + SetType( beamType ); + if ( beamType == BEAM_POINTS || beamType == BEAM_ENTPOINT || beamType == BEAM_HOSE ) + { + SetStartPos( pStart->v.origin ); + if ( beamType == BEAM_POINTS || beamType == BEAM_HOSE ) + SetEndPos( pEnd->v.origin ); + else + SetEndEntity( ENTINDEX(pEnd) ); + } + else + { + SetStartEntity( ENTINDEX(pStart) ); + SetEndEntity( ENTINDEX(pEnd) ); + } + + RelinkBeam(); + + SetWidth( m_boltWidth ); + SetNoise( m_noiseAmplitude ); + SetFrame( m_frameStart ); + SetScrollRate( m_speed ); + if ( pev->spawnflags & SF_BEAM_SHADEIN ) + SetFlags( BEAM_FSHADEIN ); + else if ( pev->spawnflags & SF_BEAM_SHADEOUT ) + SetFlags( BEAM_FSHADEOUT ); +} + + +LINK_ENTITY_TO_CLASS( env_laser, CLaser ); + +TYPEDESCRIPTION CLaser::m_SaveData[] = +{ + DEFINE_FIELD( CLaser, m_pSprite, FIELD_CLASSPTR ), + DEFINE_FIELD( CLaser, m_iszSpriteName, FIELD_STRING ), + DEFINE_FIELD( CLaser, m_firePosition, FIELD_POSITION_VECTOR ), +}; + +IMPLEMENT_SAVERESTORE( CLaser, CBeam ); + +void CLaser::Spawn( void ) +{ + if ( FStringNull( pev->model ) ) + { + SetThink( SUB_Remove ); + return; + } + pev->solid = SOLID_NOT; // Remove model & collisions + Precache( ); + + SetThink( StrikeThink ); + pev->flags |= FL_CUSTOMENTITY; + + PointsInit( pev->origin, pev->origin ); + + if ( !m_pSprite && m_iszSpriteName ) + m_pSprite = CSprite::SpriteCreate( STRING(m_iszSpriteName), pev->origin, TRUE ); + else + m_pSprite = NULL; + + if ( m_pSprite ) + m_pSprite->SetTransparency( kRenderGlow, pev->rendercolor.x, pev->rendercolor.y, pev->rendercolor.z, pev->renderamt, pev->renderfx ); + + if ( pev->targetname && !(pev->spawnflags & SF_BEAM_STARTON) ) + TurnOff(); + else + TurnOn(); +} + +void CLaser::Precache( void ) +{ + pev->modelindex = PRECACHE_MODEL( (char *)STRING(pev->model) ); + if ( m_iszSpriteName ) + PRECACHE_MODEL( (char *)STRING(m_iszSpriteName) ); +} + + +void CLaser::KeyValue( KeyValueData *pkvd ) +{ + if (FStrEq(pkvd->szKeyName, "LaserTarget")) + { + pev->message = ALLOC_STRING( pkvd->szValue ); + pkvd->fHandled = TRUE; + } + else if (FStrEq(pkvd->szKeyName, "width")) + { + SetWidth( atof(pkvd->szValue) ); + pkvd->fHandled = TRUE; + } + else if (FStrEq(pkvd->szKeyName, "NoiseAmplitude")) + { + SetNoise( atoi(pkvd->szValue) ); + pkvd->fHandled = TRUE; + } + else if (FStrEq(pkvd->szKeyName, "TextureScroll")) + { + SetScrollRate( atoi(pkvd->szValue) ); + pkvd->fHandled = TRUE; + } + else if (FStrEq(pkvd->szKeyName, "texture")) + { + pev->model = ALLOC_STRING(pkvd->szValue); + pkvd->fHandled = TRUE; + } + else if (FStrEq(pkvd->szKeyName, "EndSprite")) + { + m_iszSpriteName = ALLOC_STRING(pkvd->szValue); + pkvd->fHandled = TRUE; + } + else if (FStrEq(pkvd->szKeyName, "framestart")) + { + pev->frame = atoi(pkvd->szValue); + pkvd->fHandled = TRUE; + } + else if (FStrEq(pkvd->szKeyName, "damage")) + { + pev->dmg = atof(pkvd->szValue); + pkvd->fHandled = TRUE; + } + else + CBeam::KeyValue( pkvd ); +} + + +int CLaser::IsOn( void ) +{ + if (pev->effects & EF_NODRAW) + return 0; + return 1; +} + + +void CLaser::TurnOff( void ) +{ + pev->effects |= EF_NODRAW; + pev->nextthink = 0; + if ( m_pSprite ) + m_pSprite->TurnOff(); +} + + +void CLaser::TurnOn( void ) +{ + pev->effects &= ~EF_NODRAW; + if ( m_pSprite ) + m_pSprite->TurnOn(); + pev->dmgtime = gpGlobals->time; + pev->nextthink = gpGlobals->time; +} + + +void CLaser::Use( CBaseEntity *pActivator, CBaseEntity *pCaller, USE_TYPE useType, float value ) +{ + int active = IsOn(); + + if ( !ShouldToggle( useType, active ) ) + return; + if ( active ) + { + TurnOff(); + } + else + { + TurnOn(); + } +} + + +void CLaser::FireAtPoint( TraceResult &tr ) +{ + SetEndPos( tr.vecEndPos ); + if ( m_pSprite ) + UTIL_SetOrigin( m_pSprite->pev, tr.vecEndPos ); + + BeamDamage( &tr ); + DoSparks( GetStartPos(), tr.vecEndPos ); +} + +void CLaser::StrikeThink( void ) +{ + CBaseEntity *pEnd = RandomTargetname( STRING(pev->message) ); + + if ( pEnd ) + m_firePosition = pEnd->pev->origin; + + TraceResult tr; + + UTIL_TraceLine( pev->origin, m_firePosition, dont_ignore_monsters, NULL, &tr ); + FireAtPoint( tr ); + pev->nextthink = gpGlobals->time + 0.1; +} + + + +class CGlow : public CPointEntity +{ +public: + void Spawn( void ); + void Think( void ); + void Animate( float frames ); + virtual int Save( CSave &save ); + virtual int Restore( CRestore &restore ); + static TYPEDESCRIPTION m_SaveData[]; + + float m_lastTime; + float m_maxFrame; +}; + +LINK_ENTITY_TO_CLASS( env_glow, CGlow ); + +TYPEDESCRIPTION CGlow::m_SaveData[] = +{ + DEFINE_FIELD( CGlow, m_lastTime, FIELD_TIME ), + DEFINE_FIELD( CGlow, m_maxFrame, FIELD_FLOAT ), +}; + +IMPLEMENT_SAVERESTORE( CGlow, CPointEntity ); + +void CGlow::Spawn( void ) +{ + pev->solid = SOLID_NOT; + pev->movetype = MOVETYPE_NONE; + pev->effects = 0; + pev->frame = 0; + + PRECACHE_MODEL( (char *)STRING(pev->model) ); + SET_MODEL( ENT(pev), STRING(pev->model) ); + + m_maxFrame = (float) MODEL_FRAMES( pev->modelindex ) - 1; + if ( m_maxFrame > 1.0 && pev->framerate != 0 ) + pev->nextthink = gpGlobals->time + 0.1; + + m_lastTime = gpGlobals->time; +} + + +void CGlow::Think( void ) +{ + Animate( pev->framerate * (gpGlobals->time - m_lastTime) ); + + pev->nextthink = gpGlobals->time + 0.1; + m_lastTime = gpGlobals->time; +} + + +void CGlow::Animate( float frames ) +{ + if ( m_maxFrame > 0 ) + pev->frame = fmod( pev->frame + frames, m_maxFrame ); +} + + +LINK_ENTITY_TO_CLASS( env_sprite, CSprite ); + +TYPEDESCRIPTION CSprite::m_SaveData[] = +{ + DEFINE_FIELD( CSprite, m_lastTime, FIELD_TIME ), + DEFINE_FIELD( CSprite, m_maxFrame, FIELD_FLOAT ), +}; + +IMPLEMENT_SAVERESTORE( CSprite, CPointEntity ); + +void CSprite::Spawn( void ) +{ + pev->solid = SOLID_NOT; + pev->movetype = MOVETYPE_NONE; + pev->effects = 0; + pev->frame = 0; + + Precache(); + SET_MODEL( ENT(pev), STRING(pev->model) ); + + m_maxFrame = (float) MODEL_FRAMES( pev->modelindex ) - 1; + if ( pev->targetname && !(pev->spawnflags & SF_SPRITE_STARTON) ) + TurnOff(); + else + TurnOn(); + + // Worldcraft only sets y rotation, copy to Z + if ( pev->angles.y != 0 && pev->angles.z == 0 ) + { + pev->angles.z = pev->angles.y; + pev->angles.y = 0; + } +} + + +void CSprite::Precache( void ) +{ + PRECACHE_MODEL( (char *)STRING(pev->model) ); + + // Reset attachment after save/restore + if ( pev->aiment ) + SetAttachment( pev->aiment, pev->body ); + else + { + // Clear attachment + pev->skin = 0; + pev->body = 0; + } +} + + +void CSprite::SpriteInit( const char *pSpriteName, const Vector &origin ) +{ + pev->model = MAKE_STRING(pSpriteName); + pev->origin = origin; + Spawn(); +} + +CSprite *CSprite::SpriteCreate( const char *pSpriteName, const Vector &origin, BOOL animate ) +{ + CSprite *pSprite = GetClassPtr( (CSprite *)NULL ); + pSprite->SpriteInit( pSpriteName, origin ); + pSprite->pev->classname = MAKE_STRING("env_sprite"); + pSprite->pev->solid = SOLID_NOT; + pSprite->pev->movetype = MOVETYPE_NOCLIP; + if ( animate ) + pSprite->TurnOn(); + + return pSprite; +} + + +void CSprite::AnimateThink( void ) +{ + Animate( pev->framerate * (gpGlobals->time - m_lastTime) ); + + pev->nextthink = gpGlobals->time + 0.1; + m_lastTime = gpGlobals->time; +} + +void CSprite::AnimateUntilDead( void ) +{ + if ( gpGlobals->time > pev->dmgtime ) + UTIL_Remove(this); + else + { + AnimateThink(); + pev->nextthink = gpGlobals->time; + } +} + +void CSprite::Expand( float scaleSpeed, float fadeSpeed ) +{ + pev->speed = scaleSpeed; + pev->health = fadeSpeed; + SetThink( ExpandThink ); + + pev->nextthink = gpGlobals->time; + m_lastTime = gpGlobals->time; +} + + +void CSprite::ExpandThink( void ) +{ + float frametime = gpGlobals->time - m_lastTime; + pev->scale += pev->speed * frametime; + pev->renderamt -= pev->health * frametime; + if ( pev->renderamt <= 0 ) + { + pev->renderamt = 0; + UTIL_Remove( this ); + } + else + { + pev->nextthink = gpGlobals->time + 0.1; + m_lastTime = gpGlobals->time; + } +} + + +void CSprite::Animate( float frames ) +{ + pev->frame += frames; + if ( pev->frame > m_maxFrame ) + { + if ( pev->spawnflags & SF_SPRITE_ONCE ) + { + TurnOff(); + } + else + { + if ( m_maxFrame > 0 ) + pev->frame = fmod( pev->frame, m_maxFrame ); + } + } +} + + +void CSprite::TurnOff( void ) +{ + pev->effects = EF_NODRAW; + pev->nextthink = 0; +} + + +void CSprite::TurnOn( void ) +{ + pev->effects = 0; + if ( (pev->framerate && m_maxFrame > 1.0) || (pev->spawnflags & SF_SPRITE_ONCE) ) + { + SetThink( AnimateThink ); + pev->nextthink = gpGlobals->time; + m_lastTime = gpGlobals->time; + } + pev->frame = 0; +} + + +void CSprite::Use( CBaseEntity *pActivator, CBaseEntity *pCaller, USE_TYPE useType, float value ) +{ + int on = pev->effects != EF_NODRAW; + if ( ShouldToggle( useType, on ) ) + { + if ( on ) + { + TurnOff(); + } + else + { + TurnOn(); + } + } +} + + +class CGibShooter : public CBaseDelay +{ +public: + void Spawn( void ); + void Precache( void ); + void KeyValue( KeyValueData *pkvd ); + void EXPORT ShootThink( void ); + void Use( CBaseEntity *pActivator, CBaseEntity *pCaller, USE_TYPE useType, float value ); + + virtual CGib *CreateGib( void ); + + virtual int Save( CSave &save ); + virtual int Restore( CRestore &restore ); + static TYPEDESCRIPTION m_SaveData[]; + + int m_iGibs; + int m_iGibCapacity; + int m_iGibMaterial; + int m_iGibModelIndex; + float m_flGibVelocity; + float m_flVariance; + float m_flGibLife; +}; + +TYPEDESCRIPTION CGibShooter::m_SaveData[] = +{ + DEFINE_FIELD( CGibShooter, m_iGibs, FIELD_INTEGER ), + DEFINE_FIELD( CGibShooter, m_iGibCapacity, FIELD_INTEGER ), + DEFINE_FIELD( CGibShooter, m_iGibMaterial, FIELD_INTEGER ), + DEFINE_FIELD( CGibShooter, m_iGibModelIndex, FIELD_INTEGER ), + DEFINE_FIELD( CGibShooter, m_flGibVelocity, FIELD_FLOAT ), + DEFINE_FIELD( CGibShooter, m_flVariance, FIELD_FLOAT ), + DEFINE_FIELD( CGibShooter, m_flGibLife, FIELD_FLOAT ), +}; + +IMPLEMENT_SAVERESTORE( CGibShooter, CBaseDelay ); +LINK_ENTITY_TO_CLASS( gibshooter, CGibShooter ); + + +void CGibShooter :: Precache ( void ) +{ + if ( g_Language == LANGUAGE_GERMAN ) + { + m_iGibModelIndex = PRECACHE_MODEL ("models/germanygibs.mdl"); + } + else + { + m_iGibModelIndex = PRECACHE_MODEL ("models/hgibs.mdl"); + } +} + + +void CGibShooter::KeyValue( KeyValueData *pkvd ) +{ + if (FStrEq(pkvd->szKeyName, "m_iGibs")) + { + m_iGibs = m_iGibCapacity = atoi(pkvd->szValue); + pkvd->fHandled = TRUE; + } + else if (FStrEq(pkvd->szKeyName, "m_flVelocity")) + { + m_flGibVelocity = atof(pkvd->szValue); + pkvd->fHandled = TRUE; + } + else if (FStrEq(pkvd->szKeyName, "m_flVariance")) + { + m_flVariance = atof(pkvd->szValue); + pkvd->fHandled = TRUE; + } + else if (FStrEq(pkvd->szKeyName, "m_flGibLife")) + { + m_flGibLife = atof(pkvd->szValue); + pkvd->fHandled = TRUE; + } + else + { + CBaseDelay::KeyValue( pkvd ); + } +} + +void CGibShooter::Use( CBaseEntity *pActivator, CBaseEntity *pCaller, USE_TYPE useType, float value ) +{ + SetThink( ShootThink ); + pev->nextthink = gpGlobals->time; +} + +void CGibShooter::Spawn( void ) +{ + Precache(); + + pev->solid = SOLID_NOT; + pev->effects = EF_NODRAW; + + if ( m_flDelay == 0 ) + { + m_flDelay = 0.1; + } + + if ( m_flGibLife == 0 ) + { + m_flGibLife = 25; + } + + SetMovedir ( pev ); + pev->body = MODEL_FRAMES( m_iGibModelIndex ); +} + + +CGib *CGibShooter :: CreateGib ( void ) +{ + if ( CVAR_GET_FLOAT("violence_hgibs") == 0 ) + return NULL; + + CGib *pGib = GetClassPtr( (CGib *)NULL ); + pGib->Spawn( "models/hgibs.mdl" ); + pGib->m_bloodColor = BLOOD_COLOR_RED; + + if ( pev->body <= 1 ) + { + ALERT ( at_aiconsole, "GibShooter Body is <= 1!\n" ); + } + + pGib->pev->body = RANDOM_LONG ( 1, pev->body - 1 );// avoid throwing random amounts of the 0th gib. (skull). + + return pGib; +} + + +void CGibShooter :: ShootThink ( void ) +{ + pev->nextthink = gpGlobals->time + m_flDelay; + + Vector vecShootDir; + + vecShootDir = pev->movedir; + + vecShootDir = vecShootDir + gpGlobals->v_right * RANDOM_FLOAT( -1, 1) * m_flVariance;; + vecShootDir = vecShootDir + gpGlobals->v_forward * RANDOM_FLOAT( -1, 1) * m_flVariance;; + vecShootDir = vecShootDir + gpGlobals->v_up * RANDOM_FLOAT( -1, 1) * m_flVariance;; + + vecShootDir = vecShootDir.Normalize(); + CGib *pGib = CreateGib(); + + if ( pGib ) + { + pGib->pev->origin = pev->origin; + pGib->pev->velocity = vecShootDir * m_flGibVelocity; + + pGib->pev->avelocity.x = RANDOM_FLOAT ( 100, 200 ); + pGib->pev->avelocity.y = RANDOM_FLOAT ( 100, 300 ); + + float thinkTime = pGib->pev->nextthink - gpGlobals->time; + + pGib->m_lifeTime = (m_flGibLife * RANDOM_FLOAT( 0.95, 1.05 )); // +/- 5% + if ( pGib->m_lifeTime < thinkTime ) + { + pGib->pev->nextthink = gpGlobals->time + pGib->m_lifeTime; + pGib->m_lifeTime = 0; + } + + } + + if ( --m_iGibs <= 0 ) + { + if ( pev->spawnflags & SF_GIBSHOOTER_REPEATABLE ) + { + m_iGibs = m_iGibCapacity; + SetThink ( NULL ); + pev->nextthink = gpGlobals->time; + } + else + { + SetThink ( SUB_Remove ); + pev->nextthink = gpGlobals->time; + } + } +} + + +class CEnvShooter : public CGibShooter +{ + void Precache( void ); + void KeyValue( KeyValueData *pkvd ); + + CGib *CreateGib( void ); +}; + +LINK_ENTITY_TO_CLASS( env_shooter, CEnvShooter ); + +void CEnvShooter :: KeyValue( KeyValueData *pkvd ) +{ + if (FStrEq(pkvd->szKeyName, "shootmodel")) + { + pev->model = ALLOC_STRING(pkvd->szValue); + pkvd->fHandled = TRUE; + } + else if (FStrEq(pkvd->szKeyName, "shootsounds")) + { + int iNoise = atoi(pkvd->szValue); + pkvd->fHandled = TRUE; + switch( iNoise ) + { + case 0: + m_iGibMaterial = matGlass; + break; + case 1: + m_iGibMaterial = matWood; + break; + case 2: + m_iGibMaterial = matMetal; + break; + case 3: + m_iGibMaterial = matFlesh; + break; + case 4: + m_iGibMaterial = matRocks; + break; + + default: + case -1: + m_iGibMaterial = matNone; + break; + } + } + else + { + CGibShooter::KeyValue( pkvd ); + } +} + + +void CEnvShooter :: Precache ( void ) +{ + m_iGibModelIndex = PRECACHE_MODEL( (char *)STRING(pev->model) ); + CBreakable::MaterialSoundPrecache( (Materials)m_iGibMaterial ); +} + + +CGib *CEnvShooter :: CreateGib ( void ) +{ + CGib *pGib = GetClassPtr( (CGib *)NULL ); + + pGib->Spawn( STRING(pev->model) ); + + int bodyPart = 0; + + if ( pev->body > 1 ) + bodyPart = RANDOM_LONG( 0, pev->body-1 ); + + pGib->pev->body = bodyPart; + pGib->m_bloodColor = DONT_BLEED; + pGib->m_material = m_iGibMaterial; + + pGib->pev->rendermode = pev->rendermode; + pGib->pev->renderamt = pev->renderamt; + pGib->pev->rendercolor = pev->rendercolor; + pGib->pev->renderfx = pev->renderfx; + pGib->pev->scale = pev->scale; + pGib->pev->skin = pev->skin; + + return pGib; +} + + + + +class CTestEffect : public CBaseDelay +{ +public: + void Spawn( void ); + void Precache( void ); + // void KeyValue( KeyValueData *pkvd ); + void EXPORT TestThink( void ); + void Use( CBaseEntity *pActivator, CBaseEntity *pCaller, USE_TYPE useType, float value ); + + int m_iLoop; + int m_iBeam; + CBeam *m_pBeam[24]; + float m_flBeamTime[24]; + float m_flStartTime; +}; + + +LINK_ENTITY_TO_CLASS( test_effect, CTestEffect ); + +void CTestEffect::Spawn( void ) +{ + Precache( ); +} + +void CTestEffect::Precache( void ) +{ + PRECACHE_MODEL( "sprites/lgtning.spr" ); +} + +void CTestEffect::TestThink( void ) +{ + int i; + float t = (gpGlobals->time - m_flStartTime); + + if (m_iBeam < 24) + { + CBeam *pbeam = CBeam::BeamCreate( "sprites/lgtning.spr", 100 ); + + TraceResult tr; + + Vector vecSrc = pev->origin; + Vector vecDir = Vector( RANDOM_FLOAT( -1.0, 1.0 ), RANDOM_FLOAT( -1.0, 1.0 ),RANDOM_FLOAT( -1.0, 1.0 ) ); + vecDir = vecDir.Normalize(); + UTIL_TraceLine( vecSrc, vecSrc + vecDir * 128, ignore_monsters, ENT(pev), &tr); + + pbeam->PointsInit( vecSrc, tr.vecEndPos ); + // pbeam->SetColor( 80, 100, 255 ); + pbeam->SetColor( 255, 180, 100 ); + pbeam->SetWidth( 100 ); + pbeam->SetScrollRate( 12 ); + + m_flBeamTime[m_iBeam] = gpGlobals->time; + m_pBeam[m_iBeam] = pbeam; + m_iBeam++; + +#if 0 + Vector vecMid = (vecSrc + tr.vecEndPos) * 0.5; + MESSAGE_BEGIN( MSG_BROADCAST, SVC_TEMPENTITY ); + WRITE_BYTE(TE_DLIGHT); + WRITE_COORD(vecMid.x); // X + WRITE_COORD(vecMid.y); // Y + WRITE_COORD(vecMid.z); // Z + WRITE_BYTE( 20 ); // radius * 0.1 + WRITE_BYTE( 255 ); // r + WRITE_BYTE( 180 ); // g + WRITE_BYTE( 100 ); // b + WRITE_BYTE( 20 ); // time * 10 + WRITE_BYTE( 0 ); // decay * 0.1 + MESSAGE_END( ); +#endif + } + + if (t < 3.0) + { + for (i = 0; i < m_iBeam; i++) + { + t = (gpGlobals->time - m_flBeamTime[i]) / ( 3 + m_flStartTime - m_flBeamTime[i]); + m_pBeam[i]->SetBrightness( 255 * t ); + // m_pBeam[i]->SetScrollRate( 20 * t ); + } + pev->nextthink = gpGlobals->time + 0.1; + } + else + { + for (i = 0; i < m_iBeam; i++) + { + UTIL_Remove( m_pBeam[i] ); + } + m_flStartTime = gpGlobals->time; + m_iBeam = 0; + // pev->nextthink = gpGlobals->time; + SetThink( NULL ); + } +} + + +void CTestEffect::Use( CBaseEntity *pActivator, CBaseEntity *pCaller, USE_TYPE useType, float value ) +{ + SetThink( TestThink ); + pev->nextthink = gpGlobals->time + 0.1; + m_flStartTime = gpGlobals->time; +} + + + +// Blood effects +class CBlood : public CPointEntity +{ +public: + void Spawn( void ); + void Use( CBaseEntity *pActivator, CBaseEntity *pCaller, USE_TYPE useType, float value ); + void KeyValue( KeyValueData *pkvd ); + + inline int Color( void ) { return pev->impulse; } + inline float BloodAmount( void ) { return pev->dmg; } + + inline void SetColor( int color ) { pev->impulse = color; } + inline void SetBloodAmount( float amount ) { pev->dmg = amount; } + + Vector Direction( void ); + Vector BloodPosition( CBaseEntity *pActivator ); + +private: +}; + +LINK_ENTITY_TO_CLASS( env_blood, CBlood ); + + + +#define SF_BLOOD_RANDOM 0x0001 +#define SF_BLOOD_STREAM 0x0002 +#define SF_BLOOD_PLAYER 0x0004 +#define SF_BLOOD_DECAL 0x0008 + +void CBlood::Spawn( void ) +{ + pev->solid = SOLID_NOT; + pev->movetype = MOVETYPE_NONE; + pev->effects = 0; + pev->frame = 0; + SetMovedir( pev ); +} + + +void CBlood::KeyValue( KeyValueData *pkvd ) +{ + if (FStrEq(pkvd->szKeyName, "color")) + { + int color = atoi(pkvd->szValue); + switch( color ) + { + case 1: + SetColor( BLOOD_COLOR_YELLOW ); + break; + default: + SetColor( BLOOD_COLOR_RED ); + break; + } + + pkvd->fHandled = TRUE; + } + else if (FStrEq(pkvd->szKeyName, "amount")) + { + SetBloodAmount( atof(pkvd->szValue) ); + pkvd->fHandled = TRUE; + } + else + CPointEntity::KeyValue( pkvd ); +} + + +Vector CBlood::Direction( void ) +{ + if ( pev->spawnflags & SF_BLOOD_RANDOM ) + return UTIL_RandomBloodVector(); + + return pev->movedir; +} + + +Vector CBlood::BloodPosition( CBaseEntity *pActivator ) +{ + if ( pev->spawnflags & SF_BLOOD_PLAYER ) + { + edict_t *pPlayer; + + if ( pActivator && pActivator->IsPlayer() ) + { + pPlayer = pActivator->edict(); + } + else + pPlayer = g_engfuncs.pfnPEntityOfEntIndex( 1 ); + if ( pPlayer ) + return (pPlayer->v.origin + pPlayer->v.view_ofs) + Vector( RANDOM_FLOAT(-10,10), RANDOM_FLOAT(-10,10), RANDOM_FLOAT(-10,10) ); + } + + return pev->origin; +} + + +void CBlood::Use( CBaseEntity *pActivator, CBaseEntity *pCaller, USE_TYPE useType, float value ) +{ + if ( pev->spawnflags & SF_BLOOD_STREAM ) + UTIL_BloodStream( BloodPosition(pActivator), Direction(), (Color() == BLOOD_COLOR_RED) ? 70 : Color(), BloodAmount() ); + else + UTIL_BloodDrips( BloodPosition(pActivator), Direction(), Color(), BloodAmount() ); + + if ( pev->spawnflags & SF_BLOOD_DECAL ) + { + Vector forward = Direction(); + Vector start = BloodPosition( pActivator ); + TraceResult tr; + + UTIL_TraceLine( start, start + forward * BloodAmount() * 2, ignore_monsters, NULL, &tr ); + if ( tr.flFraction != 1.0 ) + UTIL_BloodDecalTrace( &tr, Color() ); + } +} + + + +// Screen shake +class CShake : public CPointEntity +{ +public: + void Spawn( void ); + void Use( CBaseEntity *pActivator, CBaseEntity *pCaller, USE_TYPE useType, float value ); + void KeyValue( KeyValueData *pkvd ); + + inline float Amplitude( void ) { return pev->scale; } + inline float Frequency( void ) { return pev->dmg_save; } + inline float Duration( void ) { return pev->dmg_take; } + inline float Radius( void ) { return pev->dmg; } + + inline void SetAmplitude( float amplitude ) { pev->scale = amplitude; } + inline void SetFrequency( float frequency ) { pev->dmg_save = frequency; } + inline void SetDuration( float duration ) { pev->dmg_take = duration; } + inline void SetRadius( float radius ) { pev->dmg = radius; } +private: +}; + +LINK_ENTITY_TO_CLASS( env_shake, CShake ); + +// pev->scale is amplitude +// pev->dmg_save is frequency +// pev->dmg_take is duration +// pev->dmg is radius +// radius of 0 means all players +// NOTE: UTIL_ScreenShake() will only shake players who are on the ground + +#define SF_SHAKE_EVERYONE 0x0001 // Don't check radius +// UNDONE: These don't work yet +#define SF_SHAKE_DISRUPT 0x0002 // Disrupt controls +#define SF_SHAKE_INAIR 0x0004 // Shake players in air + +void CShake::Spawn( void ) +{ + pev->solid = SOLID_NOT; + pev->movetype = MOVETYPE_NONE; + pev->effects = 0; + pev->frame = 0; + + if ( pev->spawnflags & SF_SHAKE_EVERYONE ) + pev->dmg = 0; +} + + +void CShake::KeyValue( KeyValueData *pkvd ) +{ + if (FStrEq(pkvd->szKeyName, "amplitude")) + { + SetAmplitude( atof(pkvd->szValue) ); + pkvd->fHandled = TRUE; + } + else if (FStrEq(pkvd->szKeyName, "frequency")) + { + SetFrequency( atof(pkvd->szValue) ); + pkvd->fHandled = TRUE; + } + else if (FStrEq(pkvd->szKeyName, "duration")) + { + SetDuration( atof(pkvd->szValue) ); + pkvd->fHandled = TRUE; + } + else if (FStrEq(pkvd->szKeyName, "radius")) + { + SetRadius( atof(pkvd->szValue) ); + pkvd->fHandled = TRUE; + } + else + CPointEntity::KeyValue( pkvd ); +} + + +void CShake::Use( CBaseEntity *pActivator, CBaseEntity *pCaller, USE_TYPE useType, float value ) +{ + UTIL_ScreenShake( pev->origin, Amplitude(), Frequency(), Duration(), Radius() ); +} + + +class CFade : public CPointEntity +{ +public: + void Spawn( void ); + void Use( CBaseEntity *pActivator, CBaseEntity *pCaller, USE_TYPE useType, float value ); + void KeyValue( KeyValueData *pkvd ); + + inline float Duration( void ) { return pev->dmg_take; } + inline float HoldTime( void ) { return pev->dmg_save; } + + inline void SetDuration( float duration ) { pev->dmg_take = duration; } + inline void SetHoldTime( float hold ) { pev->dmg_save = hold; } +private: +}; + +LINK_ENTITY_TO_CLASS( env_fade, CFade ); + +// pev->dmg_take is duration +// pev->dmg_save is hold duration +#define SF_FADE_IN 0x0001 // Fade in, not out +#define SF_FADE_MODULATE 0x0002 // Modulate, don't blend +#define SF_FADE_ONLYONE 0x0004 + +void CFade::Spawn( void ) +{ + pev->solid = SOLID_NOT; + pev->movetype = MOVETYPE_NONE; + pev->effects = 0; + pev->frame = 0; +} + + +void CFade::KeyValue( KeyValueData *pkvd ) +{ + if (FStrEq(pkvd->szKeyName, "duration")) + { + SetDuration( atof(pkvd->szValue) ); + pkvd->fHandled = TRUE; + } + else if (FStrEq(pkvd->szKeyName, "holdtime")) + { + SetHoldTime( atof(pkvd->szValue) ); + pkvd->fHandled = TRUE; + } + else + CPointEntity::KeyValue( pkvd ); +} + + +void CFade::Use( CBaseEntity *pActivator, CBaseEntity *pCaller, USE_TYPE useType, float value ) +{ + int fadeFlags = 0; + + if ( !(pev->spawnflags & SF_FADE_IN) ) + fadeFlags |= FFADE_OUT; + + if ( pev->spawnflags & SF_FADE_MODULATE ) + fadeFlags |= FFADE_MODULATE; + + if ( pev->spawnflags & SF_FADE_ONLYONE ) + { + if ( pActivator->IsNetClient() ) + { + UTIL_ScreenFade( pActivator, pev->rendercolor, Duration(), HoldTime(), pev->renderamt, fadeFlags ); + } + } + else + { + UTIL_ScreenFadeAll( pev->rendercolor, Duration(), HoldTime(), pev->renderamt, fadeFlags ); + } + SUB_UseTargets( this, USE_TOGGLE, 0 ); +} + + +class CMessage : public CPointEntity +{ +public: + void Spawn( void ); + void Precache( void ); + void Use( CBaseEntity *pActivator, CBaseEntity *pCaller, USE_TYPE useType, float value ); + void KeyValue( KeyValueData *pkvd ); +private: +}; + +LINK_ENTITY_TO_CLASS( env_message, CMessage ); + + +void CMessage::Spawn( void ) +{ + Precache(); + + pev->solid = SOLID_NOT; + pev->movetype = MOVETYPE_NONE; + + switch( pev->impulse ) + { + case 1: // Medium radius + pev->speed = ATTN_STATIC; + break; + + case 2: // Large radius + pev->speed = ATTN_NORM; + break; + + case 3: //EVERYWHERE + pev->speed = ATTN_NONE; + break; + + default: + case 0: // Small radius + pev->speed = ATTN_IDLE; + break; + } + pev->impulse = 0; + + // No volume, use normal + if ( pev->scale <= 0 ) + pev->scale = 1.0; +} + + +void CMessage::Precache( void ) +{ + if ( pev->noise ) + PRECACHE_SOUND( (char *)STRING(pev->noise) ); +} + +void CMessage::KeyValue( KeyValueData *pkvd ) +{ + if (FStrEq(pkvd->szKeyName, "messagesound")) + { + pev->noise = ALLOC_STRING(pkvd->szValue); + pkvd->fHandled = TRUE; + } + else if (FStrEq(pkvd->szKeyName, "messagevolume")) + { + pev->scale = atof(pkvd->szValue) * 0.1; + pkvd->fHandled = TRUE; + } + else if (FStrEq(pkvd->szKeyName, "messageattenuation")) + { + pev->impulse = atoi(pkvd->szValue); + pkvd->fHandled = TRUE; + } + else + CPointEntity::KeyValue( pkvd ); +} + + +void CMessage::Use( CBaseEntity *pActivator, CBaseEntity *pCaller, USE_TYPE useType, float value ) +{ + CBaseEntity *pPlayer = NULL; + + if ( pev->spawnflags & SF_MESSAGE_ALL ) + UTIL_ShowMessageAll( STRING(pev->message) ); + else + { + if ( pActivator && pActivator->IsPlayer() ) + pPlayer = pActivator; + else + { + pPlayer = CBaseEntity::Instance( g_engfuncs.pfnPEntityOfEntIndex( 1 ) ); + } + if ( pPlayer ) + UTIL_ShowMessage( STRING(pev->message), pPlayer ); + } + if ( pev->noise ) + { + EMIT_SOUND( edict(), CHAN_BODY, STRING(pev->noise), pev->scale, pev->speed ); + } + if ( pev->spawnflags & SF_MESSAGE_ONCE ) + UTIL_Remove( this ); + + SUB_UseTargets( this, USE_TOGGLE, 0 ); +} + + + +//========================================================= +// FunnelEffect +//========================================================= +class CEnvFunnel : public CBaseDelay +{ +public: + void Spawn( void ); + void Precache( void ); + void Use( CBaseEntity *pActivator, CBaseEntity *pCaller, USE_TYPE useType, float value ); + + int m_iSprite; // Don't save, precache +}; + +void CEnvFunnel :: Precache ( void ) +{ + m_iSprite = PRECACHE_MODEL ( "sprites/flare6.spr" ); +} + +LINK_ENTITY_TO_CLASS( env_funnel, CEnvFunnel ); + +void CEnvFunnel::Use( CBaseEntity *pActivator, CBaseEntity *pCaller, USE_TYPE useType, float value ) +{ + MESSAGE_BEGIN( MSG_BROADCAST, SVC_TEMPENTITY ); + WRITE_BYTE( TE_LARGEFUNNEL ); + WRITE_COORD( pev->origin.x ); + WRITE_COORD( pev->origin.y ); + WRITE_COORD( pev->origin.z ); + WRITE_SHORT( m_iSprite ); + + if ( pev->spawnflags & SF_FUNNEL_REVERSE )// funnel flows in reverse? + { + WRITE_SHORT( 1 ); + } + else + { + WRITE_SHORT( 0 ); + } + + + MESSAGE_END(); + + SetThink( SUB_Remove ); + pev->nextthink = gpGlobals->time; +} + +void CEnvFunnel::Spawn( void ) +{ + Precache(); + pev->solid = SOLID_NOT; + pev->effects = EF_NODRAW; +} + +//========================================================= +// Beverage Dispenser +// overloaded pev->frags, is now a flag for whether or not a can is stuck in the dispenser. +// overloaded pev->health, is now how many cans remain in the machine. +//========================================================= +class CEnvBeverage : public CBaseDelay +{ +public: + void Spawn( void ); + void Precache( void ); + void Use( CBaseEntity *pActivator, CBaseEntity *pCaller, USE_TYPE useType, float value ); +}; + +void CEnvBeverage :: Precache ( void ) +{ + PRECACHE_MODEL( "models/can.mdl" ); + PRECACHE_SOUND( "weapons/g_bounce3.wav" ); +} + +LINK_ENTITY_TO_CLASS( env_beverage, CEnvBeverage ); + +void CEnvBeverage::Use( CBaseEntity *pActivator, CBaseEntity *pCaller, USE_TYPE useType, float value ) +{ + if ( pev->frags != 0 || pev->health <= 0 ) + { + // no more cans while one is waiting in the dispenser, or if I'm out of cans. + return; + } + + CBaseEntity *pCan = CBaseEntity::Create( "item_sodacan", pev->origin, pev->angles, edict() ); + + if ( pev->skin == 6 ) + { + // random + pCan->pev->skin = RANDOM_LONG( 0, 5 ); + } + else + { + pCan->pev->skin = pev->skin; + } + + pev->frags = 1; + pev->health--; + + //SetThink (SUB_Remove); + //pev->nextthink = gpGlobals->time; +} + +void CEnvBeverage::Spawn( void ) +{ + Precache(); + pev->solid = SOLID_NOT; + pev->effects = EF_NODRAW; + pev->frags = 0; + + if ( pev->health == 0 ) + { + pev->health = 10; + } +} + +//========================================================= +// Soda can +//========================================================= +class CItemSoda : public CBaseEntity +{ +public: + void Spawn( void ); + void Precache( void ); + void EXPORT CanThink ( void ); + void EXPORT CanTouch ( CBaseEntity *pOther ); +}; + +void CItemSoda :: Precache ( void ) +{ +} + +LINK_ENTITY_TO_CLASS( item_sodacan, CItemSoda ); + +void CItemSoda::Spawn( void ) +{ + Precache(); + pev->solid = SOLID_NOT; + pev->movetype = MOVETYPE_TOSS; + + SET_MODEL ( ENT(pev), "models/can.mdl" ); + UTIL_SetSize ( pev, Vector ( 0, 0, 0 ), Vector ( 0, 0, 0 ) ); + + SetThink (CanThink); + pev->nextthink = gpGlobals->time + 0.5; +} + +void CItemSoda::CanThink ( void ) +{ + EMIT_SOUND (ENT(pev), CHAN_WEAPON, "weapons/g_bounce3.wav", 1, ATTN_NORM ); + + pev->solid = SOLID_TRIGGER; + UTIL_SetSize ( pev, Vector ( -8, -8, 0 ), Vector ( 8, 8, 8 ) ); + SetThink ( NULL ); + SetTouch ( CanTouch ); +} + +void CItemSoda::CanTouch ( CBaseEntity *pOther ) +{ + if ( !pOther->IsPlayer() ) + { + return; + } + + // spoit sound here + + pOther->TakeHealth( 1, DMG_GENERIC );// a bit of health. + + if ( !FNullEnt( pev->owner ) ) + { + // tell the machine the can was taken + pev->owner->v.frags = 0; + } + + pev->solid = SOLID_NOT; + pev->movetype = MOVETYPE_NONE; + pev->effects = EF_NODRAW; + SetTouch ( NULL ); + SetThink ( SUB_Remove ); + pev->nextthink = gpGlobals->time; +} \ No newline at end of file diff --git a/spirit/effects.h b/dlls/effects.h similarity index 66% rename from spirit/effects.h rename to dlls/effects.h index dbdd8fe3..1464d6a6 100644 --- a/spirit/effects.h +++ b/dlls/effects.h @@ -15,8 +15,6 @@ #ifndef EFFECTS_H #define EFFECTS_H -#include "beam_def.h" - #define SF_BEAM_STARTON 0x0001 #define SF_BEAM_TOGGLE 0x0002 #define SF_BEAM_RANDOM 0x0004 @@ -26,12 +24,7 @@ #define SF_BEAM_DECALS 0x0040 #define SF_BEAM_SHADEIN 0x0080 #define SF_BEAM_SHADEOUT 0x0100 -#define SF_BEAM_SOLID 0x0200 #define SF_BEAM_TEMPORARY 0x8000 -//LRC - tripbeams -#define SF_BEAM_TRIPPED 0x80000 -//LRC - smoother lasers -#define SF_LASER_INTERPOLATE 0x0400 #define SF_SPRITE_STARTON 0x0001 #define SF_SPRITE_ONCE 0x0002 @@ -57,14 +50,13 @@ public: void Expand( float scaleSpeed, float fadeSpeed ); void SpriteInit( const char *pSpriteName, const Vector &origin ); - virtual STATE GetState( void ) { return (pev->effects & EF_NODRAW)?STATE_OFF:STATE_ON; }; - inline void SetAttachment( edict_t *pEntity, int attachment ) { if ( pEntity ) { - pev->colormap = (pev->colormap & 0xFF00)>>8 | attachment; - pev->aiment = pEntity; // send across network + pev->skin = ENTINDEX(pEntity); + pev->body = attachment; + pev->aiment = pEntity; pev->movetype = MOVETYPE_FOLLOW; } } @@ -87,10 +79,10 @@ public: inline void AnimateAndDie( float framerate ) { - SetThink(&CSprite ::AnimateUntilDead); + SetThink(AnimateUntilDead); pev->framerate = framerate; pev->dmgtime = gpGlobals->time + (m_maxFrame / framerate); - SetNextThink( 0 ); + pev->nextthink = gpGlobals->time; } void EXPORT AnimateUntilDead( void ); @@ -100,7 +92,7 @@ public: static TYPEDESCRIPTION m_SaveData[]; static CSprite *SpriteCreate( const char *pSpriteName, const Vector &origin, BOOL animate ); -//private: +private: float m_lastTime; float m_maxFrame; @@ -111,7 +103,8 @@ class CBeam : public CBaseEntity { public: void Spawn( void ); - int ObjectCaps( void ) + void Precache( void ); + int ObjectCaps( void ) { int flags = 0; if ( pev->spawnflags & SF_BEAM_TEMPORARY ) @@ -123,15 +116,15 @@ public: // These functions are here to show the way beams are encoded as entities. // Encoding beams as entities simplifies their management in the client/server architecture - inline void SetType( int type ) { pev->rendermode = type; } - inline void SetFlags( int flags ) { pev->renderfx |= flags; } + inline void SetType( int type ) { pev->rendermode = (pev->rendermode & 0xF0) | (type&0x0F); } + inline void SetFlags( int flags ) { pev->rendermode = (pev->rendermode & 0x0F) | (flags&0xF0); } inline void SetStartPos( const Vector& pos ) { pev->origin = pos; } inline void SetEndPos( const Vector& pos ) { pev->angles = pos; } - inline void SetStartEntity( edict_t *pEnt ) { pev->aiment = pEnt; } - inline void SetEndEntity( edict_t *pEnt ) { pev->owner = pEnt; } + void SetStartEntity( int entityIndex ); + void SetEndEntity( int entityIndex ); - inline void SetStartAttachment( int attachment ) { pev->colormap = (pev->colormap & 0xFF00)>>8 | attachment; } - inline void SetEndAttachment( int attachment ) { pev->colormap = (pev->colormap & 0xFF) | (attachment<<8); } + inline void SetStartAttachment( int attachment ) { pev->sequence = (pev->sequence & 0x0FFF) | ((attachment&0xF)<<12); } + inline void SetEndAttachment( int attachment ) { pev->skin = (pev->skin & 0x0FFF) | ((attachment&0xF)<<12); } inline void SetTexture( int spriteIndex ) { pev->modelindex = spriteIndex; } inline void SetWidth( int width ) { pev->scale = width; } @@ -141,10 +134,10 @@ public: inline void SetFrame( float frame ) { pev->frame = frame; } inline void SetScrollRate( int speed ) { pev->animtime = speed; } - inline int GetType( void ) { return pev->rendermode; } - inline int GetFlags( void ) { return pev->renderfx; } - inline edict_t *GetStartEntity( void ) { return pev->owner; } - inline edict_t *GetEndEntity( void ) { return pev->aiment; } + inline int GetType( void ) { return pev->rendermode & 0x0F; } + inline int GetFlags( void ) { return pev->rendermode & 0xF0; } + inline int GetStartEntity( void ) { return pev->sequence & 0xFFF; } + inline int GetEndEntity( void ) { return pev->skin & 0xFFF; } const Vector &GetStartPos( void ); const Vector &GetEndPos( void ); @@ -159,8 +152,6 @@ public: inline int GetFrame( void ) { return pev->frame; } inline int GetScrollRate( void ) { return pev->animtime; } - CBaseEntity* GetTripEntity( TraceResult *ptr ); //LRC - // Call after you change start/end positions void RelinkBeam( void ); // void SetObjectCollisionBox( void ); @@ -171,13 +162,13 @@ public: // Init after BeamCreate() void BeamInit( const char *pSpriteName, int width ); void PointsInit( const Vector &start, const Vector &end ); - void PointEntInit( const Vector &start, edict_t *pEnt ); - void EntsInit( edict_t *pStart, edict_t *pEnd ); + void PointEntInit( const Vector &start, int endIndex ); + void EntsInit( int startIndex, int endIndex ); void HoseInit( const Vector &start, const Vector &direction ); static CBeam *BeamCreate( const char *pSpriteName, int width ); - inline void LiveForTime( float time ) { SetThink(&CBeam::SUB_Remove); SetNextThink( time ); } + inline void LiveForTime( float time ) { SetThink(SUB_Remove); pev->nextthink = gpGlobals->time + time; } inline void BeamDamageInstant( TraceResult *ptr, float damage ) { pev->dmg = damage; @@ -195,15 +186,14 @@ class CLaser : public CBeam { public: void Spawn( void ); - void PostSpawn( void ); void Precache( void ); void KeyValue( KeyValueData *pkvd ); void TurnOn( void ); void TurnOff( void ); - virtual STATE GetState( void ) { return (pev->effects & EF_NODRAW)?STATE_OFF:STATE_ON; }; + int IsOn( void ); - void FireAtPoint( Vector startpos, TraceResult &point ); + void FireAtPoint( TraceResult &point ); void EXPORT StrikeThink( void ); void Use( CBaseEntity *pActivator, CBaseEntity *pCaller, USE_TYPE useType, float value ); @@ -211,53 +201,9 @@ public: virtual int Restore( CRestore &restore ); static TYPEDESCRIPTION m_SaveData[]; - EHANDLE m_hActivator; //AJH allow *locus start/end positions - - CSprite *m_pStartSprite; - CSprite *m_pEndSprite; - int m_iszStartSpriteName; - int m_iszEndSpriteName; + CSprite *m_pSprite; + int m_iszSpriteName; Vector m_firePosition; - int m_iProjection; - int m_iStoppedBy; - int m_iszStartPosition; - int m_iTowardsMode; -}; - -class CRainSettings : public CBaseEntity -{ -public: - void Spawn( void ); - void Precache( void ); - void KeyValue( KeyValueData *pkvd ); - - int ObjectCaps( void ) { return (CBaseEntity :: ObjectCaps() & ~FCAP_ACROSS_TRANSITION); } - - virtual int Save( CSave &save ); - virtual int Restore( CRestore &restore ); - static TYPEDESCRIPTION m_SaveData[]; - - float Rain_Distance; - int Rain_Mode; -}; - -class CRainModify : public CBaseEntity -{ -public: - void Spawn( void ); - void KeyValue( KeyValueData *pkvd ); - void Use( CBaseEntity *pActivator, CBaseEntity *pCaller, USE_TYPE useType, float value ); - - int ObjectCaps( void ) { return (CBaseEntity :: ObjectCaps() & ~FCAP_ACROSS_TRANSITION); } - - virtual int Save( CSave &save ); - virtual int Restore( CRestore &restore ); - static TYPEDESCRIPTION m_SaveData[]; - - int Rain_Drips; - float Rain_windX, Rain_windY; - float Rain_randX, Rain_randY; - float fadeTime; }; #endif //EFFECTS_H diff --git a/dlls/egon.cpp b/dlls/egon.cpp new file mode 100644 index 00000000..43cde79b --- /dev/null +++ b/dlls/egon.cpp @@ -0,0 +1,568 @@ +/*** +* +* Copyright (c) 1996-2002, 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. +* +****/ +#if !defined( OEM_BUILD ) && !defined( HLDEMO_BUILD ) + +#include "extdll.h" +#include "util.h" +#include "cbase.h" +#include "player.h" +#include "monsters.h" +#include "weapons.h" +#include "nodes.h" +#include "effects.h" +#include "customentity.h" +#include "gamerules.h" + +#define EGON_PRIMARY_VOLUME 450 +#define EGON_BEAM_SPRITE "sprites/xbeam1.spr" +#define EGON_FLARE_SPRITE "sprites/XSpark1.spr" +#define EGON_SOUND_OFF "weapons/egon_off1.wav" +#define EGON_SOUND_RUN "weapons/egon_run3.wav" +#define EGON_SOUND_STARTUP "weapons/egon_windup2.wav" + +#define EGON_SWITCH_NARROW_TIME 0.75 // Time it takes to switch fire modes +#define EGON_SWITCH_WIDE_TIME 1.5 + +enum egon_e { + EGON_IDLE1 = 0, + EGON_FIDGET1, + EGON_ALTFIREON, + EGON_ALTFIRECYCLE, + EGON_ALTFIREOFF, + EGON_FIRE1, + EGON_FIRE2, + EGON_FIRE3, + EGON_FIRE4, + EGON_DRAW, + EGON_HOLSTER +}; + +LINK_ENTITY_TO_CLASS( weapon_egon, CEgon ); + +void CEgon::Spawn( ) +{ + Precache( ); + m_iId = WEAPON_EGON; + SET_MODEL(ENT(pev), "models/w_egon.mdl"); + + m_iDefaultAmmo = EGON_DEFAULT_GIVE; + + FallInit();// get ready to fall down. +} + + +void CEgon::Precache( void ) +{ + PRECACHE_MODEL("models/w_egon.mdl"); + PRECACHE_MODEL("models/v_egon.mdl"); + PRECACHE_MODEL("models/p_egon.mdl"); + + PRECACHE_MODEL("models/w_9mmclip.mdl"); + PRECACHE_SOUND("items/9mmclip1.wav"); + + PRECACHE_SOUND( EGON_SOUND_OFF ); + PRECACHE_SOUND( EGON_SOUND_RUN ); + PRECACHE_SOUND( EGON_SOUND_STARTUP ); + + PRECACHE_MODEL( EGON_BEAM_SPRITE ); + PRECACHE_MODEL( EGON_FLARE_SPRITE ); + + PRECACHE_SOUND ("weapons/357_cock1.wav"); + + m_usEgonFire = PRECACHE_EVENT ( 1, "events/egon_fire.sc" ); + m_usEgonStop = PRECACHE_EVENT ( 1, "events/egon_stop.sc" ); +} + + +BOOL CEgon::Deploy( void ) +{ + m_deployed = FALSE; + m_fireState = FIRE_OFF; + return DefaultDeploy( "models/v_egon.mdl", "models/p_egon.mdl", EGON_DRAW, "egon" ); +} + +int CEgon::AddToPlayer( CBasePlayer *pPlayer ) +{ + if ( CBasePlayerWeapon::AddToPlayer( pPlayer ) ) + { + MESSAGE_BEGIN( MSG_ONE, gmsgWeapPickup, NULL, pPlayer->pev ); + WRITE_BYTE( m_iId ); + MESSAGE_END(); + return TRUE; + } + return FALSE; +} + + + +void CEgon::Holster( int skiplocal /* = 0 */ ) +{ + m_pPlayer->m_flNextAttack = UTIL_WeaponTimeBase() + 0.5; + SendWeaponAnim( EGON_HOLSTER ); + + EndAttack(); +} + +int CEgon::GetItemInfo(ItemInfo *p) +{ + p->pszName = STRING(pev->classname); + p->pszAmmo1 = "uranium"; + p->iMaxAmmo1 = URANIUM_MAX_CARRY; + p->pszAmmo2 = NULL; + p->iMaxAmmo2 = -1; + p->iMaxClip = WEAPON_NOCLIP; + p->iSlot = 3; + p->iPosition = 2; + p->iId = m_iId = WEAPON_EGON; + p->iFlags = 0; + p->iWeight = EGON_WEIGHT; + + return 1; +} + +#define EGON_PULSE_INTERVAL 0.1 +#define EGON_DISCHARGE_INTERVAL 0.1 + +float CEgon::GetPulseInterval( void ) +{ + return EGON_PULSE_INTERVAL; +} + +float CEgon::GetDischargeInterval( void ) +{ + return EGON_DISCHARGE_INTERVAL; +} + +BOOL CEgon::HasAmmo( void ) +{ + if ( m_pPlayer->ammo_uranium <= 0 ) + return FALSE; + + return TRUE; +} + +void CEgon::UseAmmo( int count ) +{ + if ( m_pPlayer->m_rgAmmo[m_iPrimaryAmmoType] >= count ) + m_pPlayer->m_rgAmmo[m_iPrimaryAmmoType] -= count; + else + m_pPlayer->m_rgAmmo[m_iPrimaryAmmoType] = 0; +} + +void CEgon::Attack( void ) +{ + // don't fire underwater + if ( m_pPlayer->pev->waterlevel == 3 ) + { + + if ( m_fireState != FIRE_OFF || m_pBeam ) + { + EndAttack(); + } + else + { + PlayEmptySound( ); + } + return; + } + + UTIL_MakeVectors( m_pPlayer->pev->v_angle + m_pPlayer->pev->punchangle ); + Vector vecAiming = gpGlobals->v_forward; + Vector vecSrc = m_pPlayer->GetGunPosition( ); + + int flags; +#if defined( CLIENT_WEAPONS ) + flags = FEV_NOTHOST; +#else + flags = 0; +#endif + + switch( m_fireState ) + { + case FIRE_OFF: + { + if ( !HasAmmo() ) + { + m_flNextPrimaryAttack = m_flNextSecondaryAttack = UTIL_WeaponTimeBase() + 0.25; + PlayEmptySound( ); + return; + } + + m_flAmmoUseTime = gpGlobals->time;// start using ammo ASAP. + + PLAYBACK_EVENT_FULL( flags, m_pPlayer->edict(), m_usEgonFire, 0.0, (float *)&g_vecZero, (float *)&g_vecZero, 0.0, 0.0, m_fireState, m_fireMode, 1, 0 ); + + m_shakeTime = 0; + + m_pPlayer->m_iWeaponVolume = EGON_PRIMARY_VOLUME; + m_flTimeWeaponIdle = UTIL_WeaponTimeBase() + 0.1; + pev->fuser1 = UTIL_WeaponTimeBase() + 2; + + pev->dmgtime = gpGlobals->time + GetPulseInterval(); + m_fireState = FIRE_CHARGE; + } + break; + + case FIRE_CHARGE: + { + Fire( vecSrc, vecAiming ); + m_pPlayer->m_iWeaponVolume = EGON_PRIMARY_VOLUME; + + if ( pev->fuser1 <= UTIL_WeaponTimeBase() ) + { + PLAYBACK_EVENT_FULL( flags, m_pPlayer->edict(), m_usEgonFire, 0, (float *)&g_vecZero, (float *)&g_vecZero, 0.0, 0.0, m_fireState, m_fireMode, 0, 0 ); + pev->fuser1 = 1000; + } + + if ( !HasAmmo() ) + { + EndAttack(); + m_flNextPrimaryAttack = m_flNextSecondaryAttack = UTIL_WeaponTimeBase() + 1.0; + } + + } + break; + } +} + +void CEgon::PrimaryAttack( void ) +{ + m_fireMode = FIRE_WIDE; + Attack(); + +} + +void CEgon::Fire( const Vector &vecOrigSrc, const Vector &vecDir ) +{ + Vector vecDest = vecOrigSrc + vecDir * 2048; + edict_t *pentIgnore; + TraceResult tr; + + pentIgnore = m_pPlayer->edict(); + Vector tmpSrc = vecOrigSrc + gpGlobals->v_up * -8 + gpGlobals->v_right * 3; + + // ALERT( at_console, "." ); + + UTIL_TraceLine( vecOrigSrc, vecDest, dont_ignore_monsters, pentIgnore, &tr ); + + if (tr.fAllSolid) + return; + +#ifndef CLIENT_DLL + CBaseEntity *pEntity = CBaseEntity::Instance(tr.pHit); + + if (pEntity == NULL) + return; + + if ( g_pGameRules->IsMultiplayer() ) + { + if ( m_pSprite && pEntity->pev->takedamage ) + { + m_pSprite->pev->effects &= ~EF_NODRAW; + } + else if ( m_pSprite ) + { + m_pSprite->pev->effects |= EF_NODRAW; + } + } + + +#endif + + float timedist; + + switch ( m_fireMode ) + { + case FIRE_NARROW: +#ifndef CLIENT_DLL + if ( pev->dmgtime < gpGlobals->time ) + { + // Narrow mode only does damage to the entity it hits + ClearMultiDamage(); + if (pEntity->pev->takedamage) + { + pEntity->TraceAttack( m_pPlayer->pev, gSkillData.plrDmgEgonNarrow, vecDir, &tr, DMG_ENERGYBEAM ); + } + ApplyMultiDamage(m_pPlayer->pev, m_pPlayer->pev); + + if ( g_pGameRules->IsMultiplayer() ) + { + // multiplayer uses 1 ammo every 1/10th second + if ( gpGlobals->time >= m_flAmmoUseTime ) + { + UseAmmo( 1 ); + m_flAmmoUseTime = gpGlobals->time + 0.1; + } + } + else + { + // single player, use 3 ammo/second + if ( gpGlobals->time >= m_flAmmoUseTime ) + { + UseAmmo( 1 ); + m_flAmmoUseTime = gpGlobals->time + 0.166; + } + } + + pev->dmgtime = gpGlobals->time + GetPulseInterval(); + } +#endif + timedist = ( pev->dmgtime - gpGlobals->time ) / GetPulseInterval(); + break; + + case FIRE_WIDE: +#ifndef CLIENT_DLL + if ( pev->dmgtime < gpGlobals->time ) + { + // wide mode does damage to the ent, and radius damage + ClearMultiDamage(); + if (pEntity->pev->takedamage) + { + pEntity->TraceAttack( m_pPlayer->pev, gSkillData.plrDmgEgonWide, vecDir, &tr, DMG_ENERGYBEAM | DMG_ALWAYSGIB); + } + ApplyMultiDamage(m_pPlayer->pev, m_pPlayer->pev); + + if ( g_pGameRules->IsMultiplayer() ) + { + // radius damage a little more potent in multiplayer. + ::RadiusDamage( tr.vecEndPos, pev, m_pPlayer->pev, gSkillData.plrDmgEgonWide/4, 128, CLASS_NONE, DMG_ENERGYBEAM | DMG_BLAST | DMG_ALWAYSGIB ); + } + + if ( !m_pPlayer->IsAlive() ) + return; + + if ( g_pGameRules->IsMultiplayer() ) + { + //multiplayer uses 5 ammo/second + if ( gpGlobals->time >= m_flAmmoUseTime ) + { + UseAmmo( 1 ); + m_flAmmoUseTime = gpGlobals->time + 0.2; + } + } + else + { + // Wide mode uses 10 charges per second in single player + if ( gpGlobals->time >= m_flAmmoUseTime ) + { + UseAmmo( 1 ); + m_flAmmoUseTime = gpGlobals->time + 0.1; + } + } + + pev->dmgtime = gpGlobals->time + GetDischargeInterval(); + if ( m_shakeTime < gpGlobals->time ) + { + UTIL_ScreenShake( tr.vecEndPos, 5.0, 150.0, 0.75, 250.0 ); + m_shakeTime = gpGlobals->time + 1.5; + } + } +#endif + timedist = ( pev->dmgtime - gpGlobals->time ) / GetDischargeInterval(); + break; + } + + if ( timedist < 0 ) + timedist = 0; + else if ( timedist > 1 ) + timedist = 1; + timedist = 1-timedist; + + UpdateEffect( tmpSrc, tr.vecEndPos, timedist ); +} + + +void CEgon::UpdateEffect( const Vector &startPoint, const Vector &endPoint, float timeBlend ) +{ +#ifndef CLIENT_DLL + if ( !m_pBeam ) + { + CreateEffect(); + } + + m_pBeam->SetStartPos( endPoint ); + m_pBeam->SetBrightness( 255 - (timeBlend*180) ); + m_pBeam->SetWidth( 40 - (timeBlend*20) ); + + if ( m_fireMode == FIRE_WIDE ) + m_pBeam->SetColor( 30 + (25*timeBlend), 30 + (30*timeBlend), 64 + 80*fabs(sin(gpGlobals->time*10)) ); + else + m_pBeam->SetColor( 60 + (25*timeBlend), 120 + (30*timeBlend), 64 + 80*fabs(sin(gpGlobals->time*10)) ); + + + UTIL_SetOrigin( m_pSprite->pev, endPoint ); + m_pSprite->pev->frame += 8 * gpGlobals->frametime; + if ( m_pSprite->pev->frame > m_pSprite->Frames() ) + m_pSprite->pev->frame = 0; + + m_pNoise->SetStartPos( endPoint ); + +#endif + +} + +void CEgon::CreateEffect( void ) +{ + +#ifndef CLIENT_DLL + DestroyEffect(); + + m_pBeam = CBeam::BeamCreate( EGON_BEAM_SPRITE, 40 ); + m_pBeam->PointEntInit( pev->origin, m_pPlayer->entindex() ); + m_pBeam->SetFlags( BEAM_FSINE ); + m_pBeam->SetEndAttachment( 1 ); + m_pBeam->pev->spawnflags |= SF_BEAM_TEMPORARY; // Flag these to be destroyed on save/restore or level transition + m_pBeam->pev->flags |= FL_SKIPLOCALHOST; + m_pBeam->pev->owner = m_pPlayer->edict(); + + m_pNoise = CBeam::BeamCreate( EGON_BEAM_SPRITE, 55 ); + m_pNoise->PointEntInit( pev->origin, m_pPlayer->entindex() ); + m_pNoise->SetScrollRate( 25 ); + m_pNoise->SetBrightness( 100 ); + m_pNoise->SetEndAttachment( 1 ); + m_pNoise->pev->spawnflags |= SF_BEAM_TEMPORARY; + m_pNoise->pev->flags |= FL_SKIPLOCALHOST; + m_pNoise->pev->owner = m_pPlayer->edict(); + + m_pSprite = CSprite::SpriteCreate( EGON_FLARE_SPRITE, pev->origin, FALSE ); + m_pSprite->pev->scale = 1.0; + m_pSprite->SetTransparency( kRenderGlow, 255, 255, 255, 255, kRenderFxNoDissipation ); + m_pSprite->pev->spawnflags |= SF_SPRITE_TEMPORARY; + m_pSprite->pev->flags |= FL_SKIPLOCALHOST; + m_pSprite->pev->owner = m_pPlayer->edict(); + + if ( m_fireMode == FIRE_WIDE ) + { + m_pBeam->SetScrollRate( 50 ); + m_pBeam->SetNoise( 20 ); + m_pNoise->SetColor( 50, 50, 255 ); + m_pNoise->SetNoise( 8 ); + } + else + { + m_pBeam->SetScrollRate( 110 ); + m_pBeam->SetNoise( 5 ); + m_pNoise->SetColor( 80, 120, 255 ); + m_pNoise->SetNoise( 2 ); + } +#endif + +} + + +void CEgon::DestroyEffect( void ) +{ + +#ifndef CLIENT_DLL + if ( m_pBeam ) + { + UTIL_Remove( m_pBeam ); + m_pBeam = NULL; + } + if ( m_pNoise ) + { + UTIL_Remove( m_pNoise ); + m_pNoise = NULL; + } + if ( m_pSprite ) + { + if ( m_fireMode == FIRE_WIDE ) + m_pSprite->Expand( 10, 500 ); + else + UTIL_Remove( m_pSprite ); + m_pSprite = NULL; + } +#endif + +} + + + +void CEgon::WeaponIdle( void ) +{ + ResetEmptySound( ); + + if ( m_flTimeWeaponIdle > gpGlobals->time ) + return; + + if ( m_fireState != FIRE_OFF ) + EndAttack(); + + int iAnim; + + float flRand = RANDOM_FLOAT(0,1); + + if ( flRand <= 0.5 ) + { + iAnim = EGON_IDLE1; + m_flTimeWeaponIdle = UTIL_WeaponTimeBase() + UTIL_SharedRandomFloat( m_pPlayer->random_seed, 10, 15 ); + } + else + { + iAnim = EGON_FIDGET1; + m_flTimeWeaponIdle = UTIL_WeaponTimeBase() + 3; + } + + SendWeaponAnim( iAnim ); + m_deployed = TRUE; +} + + + +void CEgon::EndAttack( void ) +{ + bool bMakeNoise = false; + + if ( m_fireState != FIRE_OFF ) //Checking the button just in case!. + bMakeNoise = true; + + PLAYBACK_EVENT_FULL( FEV_GLOBAL | FEV_RELIABLE, m_pPlayer->edict(), m_usEgonStop, 0, (float *)&m_pPlayer->pev->origin, (float *)&m_pPlayer->pev->angles, 0.0, 0.0, bMakeNoise, 0, 0, 0 ); + + m_flTimeWeaponIdle = UTIL_WeaponTimeBase() + 2.0; + m_flNextPrimaryAttack = m_flNextSecondaryAttack = UTIL_WeaponTimeBase() + 0.5; + + m_fireState = FIRE_OFF; + + DestroyEffect(); +} + + + +class CEgonAmmo : public CBasePlayerAmmo +{ + void Spawn( void ) + { + Precache( ); + SET_MODEL(ENT(pev), "models/w_chainammo.mdl"); + CBasePlayerAmmo::Spawn( ); + } + void Precache( void ) + { + PRECACHE_MODEL ("models/w_chainammo.mdl"); + PRECACHE_SOUND("items/9mmclip1.wav"); + } + BOOL AddAmmo( CBaseEntity *pOther ) + { + if (pOther->GiveAmmo( AMMO_URANIUMBOX_GIVE, "uranium", URANIUM_MAX_CARRY ) != -1) + { + EMIT_SOUND(ENT(pev), CHAN_ITEM, "items/9mmclip1.wav", 1, ATTN_NORM); + return TRUE; + } + return FALSE; + } +}; +LINK_ENTITY_TO_CLASS( ammo_egonclip, CEgonAmmo ); + +#endif \ No newline at end of file diff --git a/dlls/enginecallback.h b/dlls/enginecallback.h new file mode 100644 index 00000000..aad3724d --- /dev/null +++ b/dlls/enginecallback.h @@ -0,0 +1,158 @@ +/*** +* +* Copyright (c) 1996-2002, 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. +* +****/ +#ifndef ENGINECALLBACK_H +#define ENGINECALLBACK_H +#pragma once + +#include "event_flags.h" + +// Must be provided by user of this code +extern enginefuncs_t g_engfuncs; + +// The actual engine callbacks +#define GETPLAYERUSERID (*g_engfuncs.pfnGetPlayerUserId) +#define PRECACHE_MODEL (*g_engfuncs.pfnPrecacheModel) +#define PRECACHE_SOUND (*g_engfuncs.pfnPrecacheSound) +#define PRECACHE_GENERIC (*g_engfuncs.pfnPrecacheGeneric) +#define SET_MODEL (*g_engfuncs.pfnSetModel) +#define MODEL_INDEX (*g_engfuncs.pfnModelIndex) +#define MODEL_FRAMES (*g_engfuncs.pfnModelFrames) +#define SET_SIZE (*g_engfuncs.pfnSetSize) +#define CHANGE_LEVEL (*g_engfuncs.pfnChangeLevel) +#define GET_SPAWN_PARMS (*g_engfuncs.pfnGetSpawnParms) +#define SAVE_SPAWN_PARMS (*g_engfuncs.pfnSaveSpawnParms) +#define VEC_TO_YAW (*g_engfuncs.pfnVecToYaw) +#define VEC_TO_ANGLES (*g_engfuncs.pfnVecToAngles) +#define MOVE_TO_ORIGIN (*g_engfuncs.pfnMoveToOrigin) +#define oldCHANGE_YAW (*g_engfuncs.pfnChangeYaw) +#define CHANGE_PITCH (*g_engfuncs.pfnChangePitch) +#define MAKE_VECTORS (*g_engfuncs.pfnMakeVectors) +#define CREATE_ENTITY (*g_engfuncs.pfnCreateEntity) +#define REMOVE_ENTITY (*g_engfuncs.pfnRemoveEntity) +#define CREATE_NAMED_ENTITY (*g_engfuncs.pfnCreateNamedEntity) +#define MAKE_STATIC (*g_engfuncs.pfnMakeStatic) +#define ENT_IS_ON_FLOOR (*g_engfuncs.pfnEntIsOnFloor) +#define DROP_TO_FLOOR (*g_engfuncs.pfnDropToFloor) +#define WALK_MOVE (*g_engfuncs.pfnWalkMove) +#define SET_ORIGIN (*g_engfuncs.pfnSetOrigin) +#define EMIT_SOUND_DYN2 (*g_engfuncs.pfnEmitSound) +#define BUILD_SOUND_MSG (*g_engfuncs.pfnBuildSoundMsg) +#define TRACE_LINE (*g_engfuncs.pfnTraceLine) +#define TRACE_TOSS (*g_engfuncs.pfnTraceToss) +#define TRACE_MONSTER_HULL (*g_engfuncs.pfnTraceMonsterHull) +#define TRACE_HULL (*g_engfuncs.pfnTraceHull) +#define GET_AIM_VECTOR (*g_engfuncs.pfnGetAimVector) +#define SERVER_COMMAND (*g_engfuncs.pfnServerCommand) +#define SERVER_EXECUTE (*g_engfuncs.pfnServerExecute) +#define CLIENT_COMMAND (*g_engfuncs.pfnClientCommand) +#define PARTICLE_EFFECT (*g_engfuncs.pfnParticleEffect) +#define LIGHT_STYLE (*g_engfuncs.pfnLightStyle) +#define DECAL_INDEX (*g_engfuncs.pfnDecalIndex) +#define POINT_CONTENTS (*g_engfuncs.pfnPointContents) +#define CRC32_INIT (*g_engfuncs.pfnCRC32_Init) +#define CRC32_PROCESS_BUFFER (*g_engfuncs.pfnCRC32_ProcessBuffer) +#define CRC32_PROCESS_BYTE (*g_engfuncs.pfnCRC32_ProcessByte) +#define CRC32_FINAL (*g_engfuncs.pfnCRC32_Final) +#define RANDOM_LONG (*g_engfuncs.pfnRandomLong) +#define RANDOM_FLOAT (*g_engfuncs.pfnRandomFloat) +#define GETPLAYERAUTHID (*g_engfuncs.pfnGetPlayerAuthId) + +inline void MESSAGE_BEGIN( int msg_dest, int msg_type, const float *pOrigin = NULL, edict_t *ed = NULL ) { + (*g_engfuncs.pfnMessageBegin)(msg_dest, msg_type, pOrigin, ed); +} +#define MESSAGE_END (*g_engfuncs.pfnMessageEnd) +#define WRITE_BYTE (*g_engfuncs.pfnWriteByte) +#define WRITE_CHAR (*g_engfuncs.pfnWriteChar) +#define WRITE_SHORT (*g_engfuncs.pfnWriteShort) +#define WRITE_LONG (*g_engfuncs.pfnWriteLong) +#define WRITE_ANGLE (*g_engfuncs.pfnWriteAngle) +#define WRITE_COORD (*g_engfuncs.pfnWriteCoord) +#define WRITE_STRING (*g_engfuncs.pfnWriteString) +#define WRITE_ENTITY (*g_engfuncs.pfnWriteEntity) +#define CVAR_REGISTER (*g_engfuncs.pfnCVarRegister) +#define CVAR_GET_FLOAT (*g_engfuncs.pfnCVarGetFloat) +#define CVAR_GET_STRING (*g_engfuncs.pfnCVarGetString) +#define CVAR_SET_FLOAT (*g_engfuncs.pfnCVarSetFloat) +#define CVAR_SET_STRING (*g_engfuncs.pfnCVarSetString) +#define CVAR_GET_POINTER (*g_engfuncs.pfnCVarGetPointer) +#define ALERT (*g_engfuncs.pfnAlertMessage) +#define ENGINE_FPRINTF (*g_engfuncs.pfnEngineFprintf) +#define ALLOC_PRIVATE (*g_engfuncs.pfnPvAllocEntPrivateData) +inline void *GET_PRIVATE( edict_t *pent ) +{ + if ( pent ) + return pent->pvPrivateData; + return NULL; +} + +#define FREE_PRIVATE (*g_engfuncs.pfnFreeEntPrivateData) +//#define STRING (*g_engfuncs.pfnSzFromIndex) +#define ALLOC_STRING (*g_engfuncs.pfnAllocString) +#define FIND_ENTITY_BY_STRING (*g_engfuncs.pfnFindEntityByString) +#define GETENTITYILLUM (*g_engfuncs.pfnGetEntityIllum) +#define FIND_ENTITY_IN_SPHERE (*g_engfuncs.pfnFindEntityInSphere) +#define FIND_CLIENT_IN_PVS (*g_engfuncs.pfnFindClientInPVS) +#define EMIT_AMBIENT_SOUND (*g_engfuncs.pfnEmitAmbientSound) +#define GET_MODEL_PTR (*g_engfuncs.pfnGetModelPtr) +#define REG_USER_MSG (*g_engfuncs.pfnRegUserMsg) +#define GET_BONE_POSITION (*g_engfuncs.pfnGetBonePosition) +#define FUNCTION_FROM_NAME (*g_engfuncs.pfnFunctionFromName) +#define NAME_FOR_FUNCTION (*g_engfuncs.pfnNameForFunction) +#define TRACE_TEXTURE (*g_engfuncs.pfnTraceTexture) +#define CLIENT_PRINTF (*g_engfuncs.pfnClientPrintf) +#define CMD_ARGS (*g_engfuncs.pfnCmd_Args) +#define CMD_ARGC (*g_engfuncs.pfnCmd_Argc) +#define CMD_ARGV (*g_engfuncs.pfnCmd_Argv) +#define GET_ATTACHMENT (*g_engfuncs.pfnGetAttachment) +#define SET_VIEW (*g_engfuncs.pfnSetView) +#define SET_CROSSHAIRANGLE (*g_engfuncs.pfnCrosshairAngle) +#define LOAD_FILE_FOR_ME (*g_engfuncs.pfnLoadFileForMe) +#define FREE_FILE (*g_engfuncs.pfnFreeFile) +#define COMPARE_FILE_TIME (*g_engfuncs.pfnCompareFileTime) +#define GET_GAME_DIR (*g_engfuncs.pfnGetGameDir) +#define IS_MAP_VALID (*g_engfuncs.pfnIsMapValid) +#define NUMBER_OF_ENTITIES (*g_engfuncs.pfnNumberOfEntities) +#define IS_DEDICATED_SERVER (*g_engfuncs.pfnIsDedicatedServer) + +#define PRECACHE_EVENT (*g_engfuncs.pfnPrecacheEvent) +#define PLAYBACK_EVENT_FULL (*g_engfuncs.pfnPlaybackEvent) + +#define ENGINE_SET_PVS (*g_engfuncs.pfnSetFatPVS) +#define ENGINE_SET_PAS (*g_engfuncs.pfnSetFatPAS) + +#define ENGINE_CHECK_VISIBILITY (*g_engfuncs.pfnCheckVisibility) + +#define DELTA_SET ( *g_engfuncs.pfnDeltaSetField ) +#define DELTA_UNSET ( *g_engfuncs.pfnDeltaUnsetField ) +#define DELTA_ADDENCODER ( *g_engfuncs.pfnDeltaAddEncoder ) +#define ENGINE_CURRENT_PLAYER ( *g_engfuncs.pfnGetCurrentPlayer ) + +#define ENGINE_CANSKIP ( *g_engfuncs.pfnCanSkipPlayer ) + +#define DELTA_FINDFIELD ( *g_engfuncs.pfnDeltaFindField ) +#define DELTA_SETBYINDEX ( *g_engfuncs.pfnDeltaSetFieldByIndex ) +#define DELTA_UNSETBYINDEX ( *g_engfuncs.pfnDeltaUnsetFieldByIndex ) + +#define ENGINE_GETPHYSINFO ( *g_engfuncs.pfnGetPhysicsInfoString ) + +#define ENGINE_SETGROUPMASK ( *g_engfuncs.pfnSetGroupMask ) + +#define ENGINE_INSTANCE_BASELINE ( *g_engfuncs.pfnCreateInstancedBaseline ) + +#define ENGINE_FORCE_UNMODIFIED ( *g_engfuncs.pfnForceUnmodified ) + +#define PLAYER_CNX_STATS ( *g_engfuncs.pfnGetPlayerStats ) + +#endif //ENGINECALLBACK_H \ No newline at end of file diff --git a/spirit/explode.cpp b/dlls/explode.cpp similarity index 84% rename from spirit/explode.cpp rename to dlls/explode.cpp index 3826918c..55dcb1c9 100644 --- a/spirit/explode.cpp +++ b/dlls/explode.cpp @@ -24,8 +24,6 @@ #include "cbase.h" #include "decals.h" #include "explode.h" -#include "locus.h" -#include "weapons.h" // Spark Shower class CShower : public CBaseEntity @@ -49,7 +47,7 @@ void CShower::Spawn( void ) pev->velocity.z -= 200; pev->movetype = MOVETYPE_BOUNCE; pev->gravity = 0.5; - SetNextThink( 0.1 ); + pev->nextthink = gpGlobals->time + 0.1; pev->solid = SOLID_NOT; SET_MODEL( edict(), "models/grenade.mdl"); // Need a model, just use the grenade, we don't draw it anyway UTIL_SetSize(pev, g_vecZero, g_vecZero ); @@ -66,7 +64,7 @@ void CShower::Think( void ) pev->speed -= 0.1; if ( pev->speed > 0 ) - SetNextThink( 0.1 ); + pev->nextthink = gpGlobals->time + 0.1; else UTIL_Remove( this ); pev->flags &= ~FL_ONGROUND; @@ -158,17 +156,9 @@ void CEnvExplosion::Use( CBaseEntity *pActivator, CBaseEntity *pCaller, USE_TYPE Vector vecSpot;// trace starts here! - //LRC - if (FStringNull(pev->target)) - { - vecSpot = pev->origin; - } - else - { - vecSpot = CalcLocus_Position(this, pActivator, STRING(pev->target)); - } - - UTIL_TraceLine ( vecSpot + Vector( 0, 0, 8 ), vecSpot + Vector ( 0, 0, -32 ), ignore_monsters, ENT(pev), & tr); + vecSpot = pev->origin + Vector ( 0 , 0 , 8 ); + + UTIL_TraceLine ( vecSpot, vecSpot + Vector ( 0, 0, -40 ), ignore_monsters, ENT(pev), & tr); // Pull out of the wall a bit if ( tr.flFraction != 1.0 ) @@ -177,20 +167,26 @@ void CEnvExplosion::Use( CBaseEntity *pActivator, CBaseEntity *pCaller, USE_TYPE } else { - pev->origin = vecSpot; //LRC + pev->origin = pev->origin; } // draw decal if (! ( pev->spawnflags & SF_ENVEXPLOSION_NODECAL)) { - CBaseEntity *pHit = CBaseEntity::Instance( tr.pHit ); - PLAYBACK_EVENT_FULL( FEV_RELIABLE|FEV_GLOBAL, edict(), m_usDecals, 0.0, (float *)&tr.vecEndPos, (float *)&g_vecZero, 0.0, 0.0, pHit->entindex(), 0, 0, 0 ); + if ( RANDOM_FLOAT( 0 , 1 ) < 0.5 ) + { + UTIL_DecalTrace( &tr, DECAL_SCORCH1 ); + } + else + { + UTIL_DecalTrace( &tr, DECAL_SCORCH2 ); + } } // draw fireball if ( !( pev->spawnflags & SF_ENVEXPLOSION_NOFIREBALL ) ) { - MESSAGE_BEGIN( MSG_PAS, gmsgTempEntity, pev->origin ); + MESSAGE_BEGIN( MSG_PAS, SVC_TEMPENTITY, pev->origin ); WRITE_BYTE( TE_EXPLOSION); WRITE_COORD( pev->origin.x ); WRITE_COORD( pev->origin.y ); @@ -203,7 +199,7 @@ void CEnvExplosion::Use( CBaseEntity *pActivator, CBaseEntity *pCaller, USE_TYPE } else { - MESSAGE_BEGIN( MSG_PAS, gmsgTempEntity, pev->origin ); + MESSAGE_BEGIN( MSG_PAS, SVC_TEMPENTITY, pev->origin ); WRITE_BYTE( TE_EXPLOSION); WRITE_COORD( pev->origin.x ); WRITE_COORD( pev->origin.y ); @@ -221,8 +217,8 @@ void CEnvExplosion::Use( CBaseEntity *pActivator, CBaseEntity *pCaller, USE_TYPE RadiusDamage ( pev, pev, m_iMagnitude, CLASS_NONE, DMG_BLAST ); } - SetThink(&CEnvExplosion:: Smoke ); - SetNextThink( 0.3 ); + SetThink( Smoke ); + pev->nextthink = gpGlobals->time + 0.3; // draw sparks if ( !( pev->spawnflags & SF_ENVEXPLOSION_NOSPARKS ) ) @@ -240,7 +236,7 @@ void CEnvExplosion::Smoke( void ) { if ( !( pev->spawnflags & SF_ENVEXPLOSION_NOSMOKE ) ) { - MESSAGE_BEGIN( MSG_PAS, gmsgTempEntity, pev->origin ); + MESSAGE_BEGIN( MSG_PAS, SVC_TEMPENTITY, pev->origin ); WRITE_BYTE( TE_SMOKE ); WRITE_COORD( pev->origin.x ); WRITE_COORD( pev->origin.y ); diff --git a/spirit/explode.h b/dlls/explode.h similarity index 97% rename from spirit/explode.h rename to dlls/explode.h index 3feb011c..3d8c4107 100644 --- a/spirit/explode.h +++ b/dlls/explode.h @@ -1,32 +1,32 @@ -/*** -* -* Copyright (c) 1996-2002, 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. -* -****/ -#ifndef EXPLODE_H -#define EXPLODE_H - - -#define SF_ENVEXPLOSION_NODAMAGE ( 1 << 0 ) // when set, ENV_EXPLOSION will not actually inflict damage -#define SF_ENVEXPLOSION_REPEATABLE ( 1 << 1 ) // can this entity be refired? -#define SF_ENVEXPLOSION_NOFIREBALL ( 1 << 2 ) // don't draw the fireball -#define SF_ENVEXPLOSION_NOSMOKE ( 1 << 3 ) // don't draw the smoke -#define SF_ENVEXPLOSION_NODECAL ( 1 << 4 ) // don't make a scorch mark -#define SF_ENVEXPLOSION_NOSPARKS ( 1 << 5 ) // don't make a scorch mark - -extern DLL_GLOBAL short g_sModelIndexFireball; -extern DLL_GLOBAL short g_sModelIndexSmoke; - - -extern void ExplosionCreate( const Vector ¢er, const Vector &angles, edict_t *pOwner, int magnitude, BOOL doDamage ); - -#endif //EXPLODE_H +/*** +* +* Copyright (c) 1996-2002, 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. +* +****/ +#ifndef EXPLODE_H +#define EXPLODE_H + + +#define SF_ENVEXPLOSION_NODAMAGE ( 1 << 0 ) // when set, ENV_EXPLOSION will not actually inflict damage +#define SF_ENVEXPLOSION_REPEATABLE ( 1 << 1 ) // can this entity be refired? +#define SF_ENVEXPLOSION_NOFIREBALL ( 1 << 2 ) // don't draw the fireball +#define SF_ENVEXPLOSION_NOSMOKE ( 1 << 3 ) // don't draw the smoke +#define SF_ENVEXPLOSION_NODECAL ( 1 << 4 ) // don't make a scorch mark +#define SF_ENVEXPLOSION_NOSPARKS ( 1 << 5 ) // don't make a scorch mark + +extern DLL_GLOBAL short g_sModelIndexFireball; +extern DLL_GLOBAL short g_sModelIndexSmoke; + + +extern void ExplosionCreate( const Vector ¢er, const Vector &angles, edict_t *pOwner, int magnitude, BOOL doDamage ); + +#endif //EXPLODE_H diff --git a/spirit/extdll.h b/dlls/extdll.h similarity index 69% rename from spirit/extdll.h rename to dlls/extdll.h index ecc60263..845ab2cd 100644 --- a/spirit/extdll.h +++ b/dlls/extdll.h @@ -32,43 +32,42 @@ #pragma warning(disable : 4514) // unreferenced inline function removed #pragma warning(disable : 4100) // unreferenced formal parameter +// Prevent tons of unused windows definitions +#define WIN32_LEAN_AND_MEAN +#define NOWINRES +#define NOSERVICE +#define NOMCX +#define NOIME #include "windows.h" -#include "basetypes.h" - -#define FALSE 0 -#define TRUE 1 - -typedef unsigned long ULONG; - -#include -#include - -#ifndef min -#define min(a,b) (((a) < (b)) ? (a) : (b)) -#endif #ifndef max #define max(a,b) (((a) > (b)) ? (a) : (b)) -#endif - #define _vsnprintf(a,b,c,d) vsnprintf(a,b,c,d) - -#define at_debug at_console +#endif // Misc C-runtime library headers #include "stdio.h" #include "stdlib.h" #include "math.h" -// Shared engine/DLL constants -#include "const.h" +// Header file containing definition of globalvars_t and entvars_t +typedef int func_t; // +typedef int string_t; // from engine's pr_comp.h; +typedef float vec_t; // needed before including progdefs.h // Vector class #include "vector.h" - // Shared header describing protocol between engine and DLLs -#include "entity_def.h" -#include "svgame_api.h" +// Defining it as a (bogus) struct helps enforce type-checking +#define vec3_t Vector + +// Shared engine/DLL constants +#include "const.h" +#include "progdefs.h" +#include "edict.h" + +// Shared header describing protocol between engine and DLLs +#include "eiface.h" // Shared header between the client DLL and the game DLLs #include "cdll_dll.h" diff --git a/spirit/flyingmonster.cpp b/dlls/flyingmonster.cpp similarity index 96% rename from spirit/flyingmonster.cpp rename to dlls/flyingmonster.cpp index 0b361ba6..9a0774f8 100644 --- a/spirit/flyingmonster.cpp +++ b/dlls/flyingmonster.cpp @@ -1,281 +1,281 @@ -/*** -* -* Copyright (c) 1996-2002, 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. -* -* This source code contains proprietary and confidential information of -* Valve LLC and its suppliers. Access to this code is restricted to -* persons who have executed a written SDK license with Valve. Any access, -* use or distribution of this code by or to any unlicensed person is illegal. -* -****/ -#include "extdll.h" -#include "util.h" -#include "cbase.h" -#include "monsters.h" -#include "schedule.h" -#include "flyingmonster.h" - -#define FLYING_AE_FLAP (8) -#define FLYING_AE_FLAPSOUND (9) - - -extern DLL_GLOBAL edict_t *g_pBodyQueueHead; - -int CFlyingMonster :: CheckLocalMove ( const Vector &vecStart, const Vector &vecEnd, CBaseEntity *pTarget, float *pflDist ) -{ - // UNDONE: need to check more than the endpoint - if (FBitSet(pev->flags, FL_SWIM) && (UTIL_PointContents(vecEnd) != CONTENTS_WATER)) - { - // ALERT(at_aiconsole, "can't swim out of water\n"); - return FALSE; - } - - TraceResult tr; - - UTIL_TraceHull( vecStart + Vector( 0, 0, 32 ), vecEnd + Vector( 0, 0, 32 ), dont_ignore_monsters, large_hull, edict(), &tr ); - - // ALERT( at_console, "%.0f %.0f %.0f : ", vecStart.x, vecStart.y, vecStart.z ); - // ALERT( at_console, "%.0f %.0f %.0f\n", vecEnd.x, vecEnd.y, vecEnd.z ); - - if (pflDist) - { - *pflDist = ( (tr.vecEndPos - Vector( 0, 0, 32 )) - vecStart ).Length();// get the distance. - } - - // ALERT( at_console, "check %d %d %f\n", tr.fStartSolid, tr.fAllSolid, tr.flFraction ); - if (tr.fStartSolid || tr.flFraction < 1.0) - { - if ( pTarget && pTarget->edict() == gpGlobals->trace_ent ) - return LOCALMOVE_VALID; - return LOCALMOVE_INVALID; - } - - return LOCALMOVE_VALID; -} - - -BOOL CFlyingMonster :: FTriangulate ( const Vector &vecStart , const Vector &vecEnd, float flDist, CBaseEntity *pTargetEnt, Vector *pApex ) -{ - return CBaseMonster::FTriangulate( vecStart, vecEnd, flDist, pTargetEnt, pApex ); -} - - -Activity CFlyingMonster :: GetStoppedActivity( void ) -{ - if ( pev->movetype != MOVETYPE_FLY ) // UNDONE: Ground idle here, IDLE may be something else - return ACT_IDLE; - - return ACT_HOVER; -} - - -void CFlyingMonster :: Stop( void ) -{ - Activity stopped = GetStoppedActivity(); - if ( m_IdealActivity != stopped ) - { - m_flightSpeed = 0; - m_IdealActivity = stopped; - } - pev->angles.z = 0; - pev->angles.x = 0; - m_vecTravel = g_vecZero; -} - - -float CFlyingMonster :: ChangeYaw( int speed ) -{ - if ( pev->movetype == MOVETYPE_FLY ) - { - float diff = FlYawDiff(); - float target = 0; - - if ( m_IdealActivity != GetStoppedActivity() ) - { - if ( diff < -20 ) - target = 90; - else if ( diff > 20 ) - target = -90; - } - pev->angles.z = UTIL_Approach( target, pev->angles.z, 220.0 * gpGlobals->frametime ); - } - return CBaseMonster::ChangeYaw( speed ); -} - - -void CFlyingMonster :: Killed( entvars_t *pevAttacker, int iGib ) -{ - pev->movetype = MOVETYPE_STEP; - ClearBits( pev->flags, FL_ONGROUND ); - pev->angles.z = 0; - pev->angles.x = 0; - CBaseMonster::Killed( pevAttacker, iGib ); -} - - -void CFlyingMonster :: HandleAnimEvent( MonsterEvent_t *pEvent ) -{ - switch( pEvent->event ) - { - case FLYING_AE_FLAP: - m_flightSpeed = 400; - break; - - case FLYING_AE_FLAPSOUND: - if ( m_pFlapSound ) - EMIT_SOUND( edict(), CHAN_BODY, m_pFlapSound, 1, ATTN_NORM ); - break; - - default: - CBaseMonster::HandleAnimEvent( pEvent ); - break; - } -} - - -void CFlyingMonster :: Move( float flInterval ) -{ - if ( pev->movetype == MOVETYPE_FLY ) - m_flGroundSpeed = m_flightSpeed; - CBaseMonster::Move( flInterval ); -} - - -BOOL CFlyingMonster:: ShouldAdvanceRoute( float flWaypointDist ) -{ - // Get true 3D distance to the goal so we actually reach the correct height - if ( m_Route[ m_iRouteIndex ].iType & bits_MF_IS_GOAL ) - flWaypointDist = ( m_Route[ m_iRouteIndex ].vecLocation - pev->origin ).Length(); - - if ( flWaypointDist <= 64 + (m_flGroundSpeed * gpGlobals->frametime) ) - return TRUE; - - return FALSE; -} - - -void CFlyingMonster::MoveExecute( CBaseEntity *pTargetEnt, const Vector &vecDir, float flInterval ) -{ - if ( pev->movetype == MOVETYPE_FLY ) - { - if ( gpGlobals->time - m_stopTime > 1.0 ) - { - if ( m_IdealActivity != m_movementActivity ) - { - m_IdealActivity = m_movementActivity; - m_flGroundSpeed = m_flightSpeed = 200; - } - } - Vector vecMove = pev->origin + (( vecDir + (m_vecTravel * m_momentum) ).Normalize() * (m_flGroundSpeed * flInterval)); - - if ( m_IdealActivity != m_movementActivity ) - { - m_flightSpeed = UTIL_Approach( 100, m_flightSpeed, 75 * gpGlobals->frametime ); - if ( m_flightSpeed < 100 ) - m_stopTime = gpGlobals->time; - } - else - m_flightSpeed = UTIL_Approach( 20, m_flightSpeed, 300 * gpGlobals->frametime ); - - if ( CheckLocalMove ( pev->origin, vecMove, pTargetEnt, NULL ) ) - { - m_vecTravel = (vecMove - pev->origin); - m_vecTravel = m_vecTravel.Normalize(); - UTIL_MoveToOrigin(ENT(pev), vecMove, (m_flGroundSpeed * flInterval), MOVE_STRAFE); - } - else - { - m_IdealActivity = GetStoppedActivity(); - m_stopTime = gpGlobals->time; - m_vecTravel = g_vecZero; - } - } - else - CBaseMonster::MoveExecute( pTargetEnt, vecDir, flInterval ); -} - - -float CFlyingMonster::CeilingZ( const Vector &position ) -{ - TraceResult tr; - - Vector minUp = position; - Vector maxUp = position; - maxUp.z += 4096.0; - - UTIL_TraceLine(position, maxUp, ignore_monsters, NULL, &tr); - if (tr.flFraction != 1.0) - maxUp.z = tr.vecEndPos.z; - - if ((pev->flags) & FL_SWIM) - { - return UTIL_WaterLevel( position, minUp.z, maxUp.z ); - } - return maxUp.z; -} - -BOOL CFlyingMonster::ProbeZ( const Vector &position, const Vector &probe, float *pFraction) -{ - int conPosition = UTIL_PointContents(position); - if ( (((pev->flags) & FL_SWIM) == FL_SWIM) ^ (conPosition == CONTENTS_WATER)) - { - // SWIMING & !WATER - // or FLYING & WATER - // - *pFraction = 0.0; - return TRUE; // We hit a water boundary because we are where we don't belong. - } - int conProbe = UTIL_PointContents(probe); - if (conProbe == conPosition) - { - // The probe is either entirely inside the water (for fish) or entirely - // outside the water (for birds). - // - *pFraction = 1.0; - return FALSE; - } - - Vector ProbeUnit = (probe-position).Normalize(); - float ProbeLength = (probe-position).Length(); - float maxProbeLength = ProbeLength; - float minProbeLength = 0; - - float diff = maxProbeLength - minProbeLength; - while (diff > 1.0) - { - float midProbeLength = minProbeLength + diff/2.0; - Vector midProbeVec = midProbeLength * ProbeUnit; - if (UTIL_PointContents(position+midProbeVec) == conPosition) - { - minProbeLength = midProbeLength; - } - else - { - maxProbeLength = midProbeLength; - } - diff = maxProbeLength - minProbeLength; - } - *pFraction = minProbeLength/ProbeLength; - - return TRUE; -} - -float CFlyingMonster::FloorZ( const Vector &position ) -{ - TraceResult tr; - - Vector down = position; - down.z -= 2048; - - UTIL_TraceLine( position, down, ignore_monsters, NULL, &tr ); - - if ( tr.flFraction != 1.0 ) - return tr.vecEndPos.z; - - return down.z; -} - +/*** +* +* Copyright (c) 1996-2002, 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. +* +* This source code contains proprietary and confidential information of +* Valve LLC and its suppliers. Access to this code is restricted to +* persons who have executed a written SDK license with Valve. Any access, +* use or distribution of this code by or to any unlicensed person is illegal. +* +****/ +#include "extdll.h" +#include "util.h" +#include "cbase.h" +#include "monsters.h" +#include "schedule.h" +#include "flyingmonster.h" + +#define FLYING_AE_FLAP (8) +#define FLYING_AE_FLAPSOUND (9) + + +extern DLL_GLOBAL edict_t *g_pBodyQueueHead; + +int CFlyingMonster :: CheckLocalMove ( const Vector &vecStart, const Vector &vecEnd, CBaseEntity *pTarget, float *pflDist ) +{ + // UNDONE: need to check more than the endpoint + if (FBitSet(pev->flags, FL_SWIM) && (UTIL_PointContents(vecEnd) != CONTENTS_WATER)) + { + // ALERT(at_aiconsole, "can't swim out of water\n"); + return FALSE; + } + + TraceResult tr; + + UTIL_TraceHull( vecStart + Vector( 0, 0, 32 ), vecEnd + Vector( 0, 0, 32 ), dont_ignore_monsters, large_hull, edict(), &tr ); + + // ALERT( at_console, "%.0f %.0f %.0f : ", vecStart.x, vecStart.y, vecStart.z ); + // ALERT( at_console, "%.0f %.0f %.0f\n", vecEnd.x, vecEnd.y, vecEnd.z ); + + if (pflDist) + { + *pflDist = ( (tr.vecEndPos - Vector( 0, 0, 32 )) - vecStart ).Length();// get the distance. + } + + // ALERT( at_console, "check %d %d %f\n", tr.fStartSolid, tr.fAllSolid, tr.flFraction ); + if (tr.fStartSolid || tr.flFraction < 1.0) + { + if ( pTarget && pTarget->edict() == gpGlobals->trace_ent ) + return LOCALMOVE_VALID; + return LOCALMOVE_INVALID; + } + + return LOCALMOVE_VALID; +} + + +BOOL CFlyingMonster :: FTriangulate ( const Vector &vecStart , const Vector &vecEnd, float flDist, CBaseEntity *pTargetEnt, Vector *pApex ) +{ + return CBaseMonster::FTriangulate( vecStart, vecEnd, flDist, pTargetEnt, pApex ); +} + + +Activity CFlyingMonster :: GetStoppedActivity( void ) +{ + if ( pev->movetype != MOVETYPE_FLY ) // UNDONE: Ground idle here, IDLE may be something else + return ACT_IDLE; + + return ACT_HOVER; +} + + +void CFlyingMonster :: Stop( void ) +{ + Activity stopped = GetStoppedActivity(); + if ( m_IdealActivity != stopped ) + { + m_flightSpeed = 0; + m_IdealActivity = stopped; + } + pev->angles.z = 0; + pev->angles.x = 0; + m_vecTravel = g_vecZero; +} + + +float CFlyingMonster :: ChangeYaw( int speed ) +{ + if ( pev->movetype == MOVETYPE_FLY ) + { + float diff = FlYawDiff(); + float target = 0; + + if ( m_IdealActivity != GetStoppedActivity() ) + { + if ( diff < -20 ) + target = 90; + else if ( diff > 20 ) + target = -90; + } + pev->angles.z = UTIL_Approach( target, pev->angles.z, 220.0 * gpGlobals->frametime ); + } + return CBaseMonster::ChangeYaw( speed ); +} + + +void CFlyingMonster :: Killed( entvars_t *pevAttacker, int iGib ) +{ + pev->movetype = MOVETYPE_STEP; + ClearBits( pev->flags, FL_ONGROUND ); + pev->angles.z = 0; + pev->angles.x = 0; + CBaseMonster::Killed( pevAttacker, iGib ); +} + + +void CFlyingMonster :: HandleAnimEvent( MonsterEvent_t *pEvent ) +{ + switch( pEvent->event ) + { + case FLYING_AE_FLAP: + m_flightSpeed = 400; + break; + + case FLYING_AE_FLAPSOUND: + if ( m_pFlapSound ) + EMIT_SOUND( edict(), CHAN_BODY, m_pFlapSound, 1, ATTN_NORM ); + break; + + default: + CBaseMonster::HandleAnimEvent( pEvent ); + break; + } +} + + +void CFlyingMonster :: Move( float flInterval ) +{ + if ( pev->movetype == MOVETYPE_FLY ) + m_flGroundSpeed = m_flightSpeed; + CBaseMonster::Move( flInterval ); +} + + +BOOL CFlyingMonster:: ShouldAdvanceRoute( float flWaypointDist ) +{ + // Get true 3D distance to the goal so we actually reach the correct height + if ( m_Route[ m_iRouteIndex ].iType & bits_MF_IS_GOAL ) + flWaypointDist = ( m_Route[ m_iRouteIndex ].vecLocation - pev->origin ).Length(); + + if ( flWaypointDist <= 64 + (m_flGroundSpeed * gpGlobals->frametime) ) + return TRUE; + + return FALSE; +} + + +void CFlyingMonster::MoveExecute( CBaseEntity *pTargetEnt, const Vector &vecDir, float flInterval ) +{ + if ( pev->movetype == MOVETYPE_FLY ) + { + if ( gpGlobals->time - m_stopTime > 1.0 ) + { + if ( m_IdealActivity != m_movementActivity ) + { + m_IdealActivity = m_movementActivity; + m_flGroundSpeed = m_flightSpeed = 200; + } + } + Vector vecMove = pev->origin + (( vecDir + (m_vecTravel * m_momentum) ).Normalize() * (m_flGroundSpeed * flInterval)); + + if ( m_IdealActivity != m_movementActivity ) + { + m_flightSpeed = UTIL_Approach( 100, m_flightSpeed, 75 * gpGlobals->frametime ); + if ( m_flightSpeed < 100 ) + m_stopTime = gpGlobals->time; + } + else + m_flightSpeed = UTIL_Approach( 20, m_flightSpeed, 300 * gpGlobals->frametime ); + + if ( CheckLocalMove ( pev->origin, vecMove, pTargetEnt, NULL ) ) + { + m_vecTravel = (vecMove - pev->origin); + m_vecTravel = m_vecTravel.Normalize(); + UTIL_MoveToOrigin(ENT(pev), vecMove, (m_flGroundSpeed * flInterval), MOVE_STRAFE); + } + else + { + m_IdealActivity = GetStoppedActivity(); + m_stopTime = gpGlobals->time; + m_vecTravel = g_vecZero; + } + } + else + CBaseMonster::MoveExecute( pTargetEnt, vecDir, flInterval ); +} + + +float CFlyingMonster::CeilingZ( const Vector &position ) +{ + TraceResult tr; + + Vector minUp = position; + Vector maxUp = position; + maxUp.z += 4096.0; + + UTIL_TraceLine(position, maxUp, ignore_monsters, NULL, &tr); + if (tr.flFraction != 1.0) + maxUp.z = tr.vecEndPos.z; + + if ((pev->flags) & FL_SWIM) + { + return UTIL_WaterLevel( position, minUp.z, maxUp.z ); + } + return maxUp.z; +} + +BOOL CFlyingMonster::ProbeZ( const Vector &position, const Vector &probe, float *pFraction) +{ + int conPosition = UTIL_PointContents(position); + if ( (((pev->flags) & FL_SWIM) == FL_SWIM) ^ (conPosition == CONTENTS_WATER)) + { + // SWIMING & !WATER + // or FLYING & WATER + // + *pFraction = 0.0; + return TRUE; // We hit a water boundary because we are where we don't belong. + } + int conProbe = UTIL_PointContents(probe); + if (conProbe == conPosition) + { + // The probe is either entirely inside the water (for fish) or entirely + // outside the water (for birds). + // + *pFraction = 1.0; + return FALSE; + } + + Vector ProbeUnit = (probe-position).Normalize(); + float ProbeLength = (probe-position).Length(); + float maxProbeLength = ProbeLength; + float minProbeLength = 0; + + float diff = maxProbeLength - minProbeLength; + while (diff > 1.0) + { + float midProbeLength = minProbeLength + diff/2.0; + Vector midProbeVec = midProbeLength * ProbeUnit; + if (UTIL_PointContents(position+midProbeVec) == conPosition) + { + minProbeLength = midProbeLength; + } + else + { + maxProbeLength = midProbeLength; + } + diff = maxProbeLength - minProbeLength; + } + *pFraction = minProbeLength/ProbeLength; + + return TRUE; +} + +float CFlyingMonster::FloorZ( const Vector &position ) +{ + TraceResult tr; + + Vector down = position; + down.z -= 2048; + + UTIL_TraceLine( position, down, ignore_monsters, NULL, &tr ); + + if ( tr.flFraction != 1.0 ) + return tr.vecEndPos.z; + + return down.z; +} + diff --git a/spirit/flyingmonster.h b/dlls/flyingmonster.h similarity index 97% rename from spirit/flyingmonster.h rename to dlls/flyingmonster.h index 616194db..752f56ec 100644 --- a/spirit/flyingmonster.h +++ b/dlls/flyingmonster.h @@ -1,53 +1,53 @@ -/*** -* -* Copyright (c) 1996-2002, 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. -* -* This source code contains proprietary and confidential information of -* Valve LLC and its suppliers. Access to this code is restricted to -* persons who have executed a written SDK license with Valve. Any access, -* use or distribution of this code by or to any unlicensed person is illegal. -* -****/ -// Base class for flying monsters. This overrides the movement test & execution code from CBaseMonster - -#ifndef FLYINGMONSTER_H -#define FLYINGMONSTER_H - -class CFlyingMonster : public CBaseMonster -{ -public: - int CheckLocalMove ( const Vector &vecStart, const Vector &vecEnd, CBaseEntity *pTarget, float *pflDist );// check validity of a straight move through space - BOOL FTriangulate ( const Vector &vecStart , const Vector &vecEnd, float flDist, CBaseEntity *pTargetEnt, Vector *pApex ); - Activity GetStoppedActivity( void ); - void Killed( entvars_t *pevAttacker, int iGib ); - void Stop( void ); - float ChangeYaw( int speed ); - void HandleAnimEvent( MonsterEvent_t *pEvent ); - void MoveExecute( CBaseEntity *pTargetEnt, const Vector &vecDir, float flInterval ); - void Move( float flInterval = 0.1 ); - BOOL ShouldAdvanceRoute( float flWaypointDist ); - - inline void SetFlyingMomentum( float momentum ) { m_momentum = momentum; } - inline void SetFlyingFlapSound( const char *pFlapSound ) { m_pFlapSound = pFlapSound; } - inline void SetFlyingSpeed( float speed ) { m_flightSpeed = speed; } - float CeilingZ( const Vector &position ); - float FloorZ( const Vector &position ); - BOOL ProbeZ( const Vector &position, const Vector &probe, float *pFraction ); - - - // UNDONE: Save/restore this stuff!!! -protected: - Vector m_vecTravel; // Current direction - float m_flightSpeed; // Current flight speed (decays when not flapping or gliding) - float m_stopTime; // Last time we stopped (to avoid switching states too soon) - float m_momentum; // Weight for desired vs. momentum velocity - const char *m_pFlapSound; -}; - - -#endif //FLYINGMONSTER_H - +/*** +* +* Copyright (c) 1996-2002, 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. +* +* This source code contains proprietary and confidential information of +* Valve LLC and its suppliers. Access to this code is restricted to +* persons who have executed a written SDK license with Valve. Any access, +* use or distribution of this code by or to any unlicensed person is illegal. +* +****/ +// Base class for flying monsters. This overrides the movement test & execution code from CBaseMonster + +#ifndef FLYINGMONSTER_H +#define FLYINGMONSTER_H + +class CFlyingMonster : public CBaseMonster +{ +public: + int CheckLocalMove ( const Vector &vecStart, const Vector &vecEnd, CBaseEntity *pTarget, float *pflDist );// check validity of a straight move through space + BOOL FTriangulate ( const Vector &vecStart , const Vector &vecEnd, float flDist, CBaseEntity *pTargetEnt, Vector *pApex ); + Activity GetStoppedActivity( void ); + void Killed( entvars_t *pevAttacker, int iGib ); + void Stop( void ); + float ChangeYaw( int speed ); + void HandleAnimEvent( MonsterEvent_t *pEvent ); + void MoveExecute( CBaseEntity *pTargetEnt, const Vector &vecDir, float flInterval ); + void Move( float flInterval = 0.1 ); + BOOL ShouldAdvanceRoute( float flWaypointDist ); + + inline void SetFlyingMomentum( float momentum ) { m_momentum = momentum; } + inline void SetFlyingFlapSound( const char *pFlapSound ) { m_pFlapSound = pFlapSound; } + inline void SetFlyingSpeed( float speed ) { m_flightSpeed = speed; } + float CeilingZ( const Vector &position ); + float FloorZ( const Vector &position ); + BOOL ProbeZ( const Vector &position, const Vector &probe, float *pFraction ); + + + // UNDONE: Save/restore this stuff!!! +protected: + Vector m_vecTravel; // Current direction + float m_flightSpeed; // Current flight speed (decays when not flapping or gliding) + float m_stopTime; // Last time we stopped (to avoid switching states too soon) + float m_momentum; // Weight for desired vs. momentum velocity + const char *m_pFlapSound; +}; + + +#endif //FLYINGMONSTER_H + diff --git a/spirit/func_break.cpp b/dlls/func_break.cpp similarity index 70% rename from spirit/func_break.cpp rename to dlls/func_break.cpp index 2b3f70e1..096bde79 100644 --- a/spirit/func_break.cpp +++ b/dlls/func_break.cpp @@ -1,9 +1,9 @@ /*** * * Copyright (c) 1996-2002, Valve LLC. All rights reserved. -* -* This product contains software technology licensed from Id -* Software, Inc. ("Id Technology"). Id Technology (c) 1996 Id Software, Inc. +* +* 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 @@ -115,21 +115,6 @@ void CBreakable::KeyValue( KeyValueData* pkvd ) } else if (FStrEq(pkvd->szKeyName, "lip") ) pkvd->fHandled = TRUE; - else if (FStrEq(pkvd->szKeyName, "respawn") ) //LRC - { - m_iRespawnTime = atoi( pkvd->szValue ); - pkvd->fHandled = TRUE; - } - else if (FStrEq(pkvd->szKeyName, "whenhit") ) //LRC - { - m_iszWhenHit = ALLOC_STRING(pkvd->szValue); - pkvd->fHandled = TRUE; - } - else if (FStrEq(pkvd->szKeyName, "m_iClass") ) - { - m_iClass = atoi( pkvd->szValue ); - pkvd->fHandled = TRUE; - } else CBaseDelay::KeyValue( pkvd ); } @@ -152,84 +137,51 @@ TYPEDESCRIPTION CBreakable::m_SaveData[] = DEFINE_FIELD( CBreakable, m_iszSpawnObject, FIELD_STRING ), // Explosion magnitude is stored in pev->impulse - - //LRC- time until respawn - DEFINE_FIELD( CBreakable, m_iRespawnTime, FIELD_INTEGER ), - //LRC- health to set on respawn - DEFINE_FIELD( CBreakable, m_iInitialHealth, FIELD_INTEGER ), - DEFINE_FIELD( CBreakable, m_iInitialRenderAmt, FIELD_INTEGER ), - DEFINE_FIELD( CBreakable, m_iInitialRenderMode, FIELD_INTEGER ), - DEFINE_FIELD( CBreakable, m_iszWhenHit, FIELD_STRING ), - DEFINE_FIELD( CBreakable, m_pHitProxy, FIELD_CLASSPTR ), }; IMPLEMENT_SAVERESTORE( CBreakable, CBaseEntity ); void CBreakable::Spawn( void ) { - Precache( ); + Precache( ); if ( FBitSet( pev->spawnflags, SF_BREAK_TRIGGER_ONLY ) ) pev->takedamage = DAMAGE_NO; else pev->takedamage = DAMAGE_YES; - - if (m_iClass) //LRC - might these additions cause problems? - { - pev->flags |= FL_MONSTER; - pev->view_ofs = (pev->maxs + pev->mins) / 2; - } - - if (m_iszWhenHit) //LRC - locus trigger - { - m_pHitProxy = GetClassPtr( (CPointEntity*)NULL ); - } - + pev->solid = SOLID_BSP; pev->movetype = MOVETYPE_PUSH; m_angle = pev->angles.y; pev->angles.y = 0; - m_iInitialHealth = pev->health; - m_iInitialRenderAmt = pev->renderamt; - m_iInitialRenderMode = pev->rendermode; + + // HACK: matGlass can receive decals, we need the client to know about this + // so use class to store the material flag + if ( m_Material == matGlass ) + { + pev->playerclass = 1; + } SET_MODEL(ENT(pev), STRING(pev->model) );//set size and link into world. - SetTouch(&CBreakable:: BreakTouch ); - SetUse(&CBreakable:: BreakUse ); + SetTouch( BreakTouch ); if ( FBitSet( pev->spawnflags, SF_BREAK_TRIGGER_ONLY ) ) // Only break on trigger SetTouch( NULL ); // Flag unbreakable glass as "worldbrush" so it will block ALL tracelines if ( !IsBreakable() && pev->rendermode != kRenderNormal ) pev->flags |= FL_WORLDBRUSH; - - if (m_iStyle >= 32) - LIGHT_STYLE(m_iStyle, "z"); - else if (m_iStyle <= -32) - LIGHT_STYLE(-m_iStyle, "a"); } -STATE CBreakable::GetState( void ) -{ - if (m_iRespawnTime) - { - if (pev->effects & EF_NODRAW) - return STATE_OFF; - else - return STATE_ON; - } - else return STATE_OFF; -} -const char *CBreakable::pSoundsWood[] = +const char *CBreakable::pSoundsWood[] = { "debris/wood1.wav", "debris/wood2.wav", "debris/wood3.wav", }; -const char *CBreakable::pSoundsFlesh[] = +const char *CBreakable::pSoundsFlesh[] = { "debris/flesh1.wav", "debris/flesh2.wav", @@ -239,14 +191,14 @@ const char *CBreakable::pSoundsFlesh[] = "debris/flesh7.wav", }; -const char *CBreakable::pSoundsMetal[] = +const char *CBreakable::pSoundsMetal[] = { "debris/metal1.wav", "debris/metal2.wav", "debris/metal3.wav", }; -const char *CBreakable::pSoundsConcrete[] = +const char *CBreakable::pSoundsConcrete[] = { "debris/concrete1.wav", "debris/concrete2.wav", @@ -254,7 +206,7 @@ const char *CBreakable::pSoundsConcrete[] = }; -const char *CBreakable::pSoundsGlass[] = +const char *CBreakable::pSoundsGlass[] = { "debris/glass1.wav", "debris/glass2.wav", @@ -265,7 +217,7 @@ const char **CBreakable::MaterialSoundList( Materials precacheMaterial, int &sou { const char **pSoundList = NULL; - switch ( precacheMaterial ) + switch ( precacheMaterial ) { case matWood: pSoundList = pSoundsWood; @@ -292,8 +244,8 @@ const char **CBreakable::MaterialSoundList( Materials precacheMaterial, int &sou pSoundList = pSoundsConcrete; soundCount = ARRAYSIZE(pSoundsConcrete); break; - - + + case matCeilingTile: case matNone: default: @@ -333,17 +285,17 @@ void CBreakable::Precache( void ) { const char *pGibName; - switch (m_Material) + switch (m_Material) { case matWood: pGibName = "models/woodgibs.mdl"; - + PRECACHE_SOUND("debris/bustcrate1.wav"); PRECACHE_SOUND("debris/bustcrate2.wav"); break; case matFlesh: pGibName = "models/fleshgibs.mdl"; - + PRECACHE_SOUND("debris/bustflesh1.wav"); PRECACHE_SOUND("debris/bustflesh2.wav"); break; @@ -351,7 +303,7 @@ void CBreakable::Precache( void ) PRECACHE_SOUND("buttons/spark5.wav"); PRECACHE_SOUND("buttons/spark6.wav"); pGibName = "models/computergibs.mdl"; - + PRECACHE_SOUND("debris/bustmetal1.wav"); PRECACHE_SOUND("debris/bustmetal2.wav"); break; @@ -359,32 +311,32 @@ void CBreakable::Precache( void ) case matUnbreakableGlass: case matGlass: pGibName = "models/glassgibs.mdl"; - + PRECACHE_SOUND("debris/bustglass1.wav"); PRECACHE_SOUND("debris/bustglass2.wav"); break; case matMetal: pGibName = "models/metalplategibs.mdl"; - + PRECACHE_SOUND("debris/bustmetal1.wav"); PRECACHE_SOUND("debris/bustmetal2.wav"); break; case matCinderBlock: pGibName = "models/cindergibs.mdl"; - + PRECACHE_SOUND("debris/bustconcrete1.wav"); PRECACHE_SOUND("debris/bustconcrete2.wav"); break; case matRocks: pGibName = "models/rockgibs.mdl"; - + PRECACHE_SOUND("debris/bustconcrete1.wav"); PRECACHE_SOUND("debris/bustconcrete2.wav"); break; case matCeilingTile: pGibName = "models/ceilinggibs.mdl"; - - PRECACHE_SOUND ("debris/bustceiling.wav"); + + PRECACHE_SOUND ("debris/bustceiling.wav"); break; } MaterialSoundPrecache( m_Material ); @@ -392,7 +344,6 @@ void CBreakable::Precache( void ) pGibName = STRING(m_iszGibModel); m_idShard = PRECACHE_MODEL( (char *)pGibName ); - //ALERT(at_debug,"Breakable: Shard is %d\n",m_idShard); // Precache the spawn item's data if ( m_iszSpawnObject ) @@ -402,10 +353,6 @@ void CBreakable::Precache( void ) // play shard sound when func_breakable takes damage. // the more damage, the louder the shard sound. - float CBreakable::CalcRatio(CBaseEntity* plocus, int mode ){//AJH added 'mode' = ratio to return - return pev->health/m_iInitialHealth; -} - void CBreakable::DamageSound( void ) { @@ -485,7 +432,7 @@ void CBreakable::BreakTouch( CBaseEntity *pOther ) { float flDamage; entvars_t* pevToucher = pOther->pev; - + // only players can break these right now if ( !pOther->IsPlayer() || !IsBreakable() ) { @@ -493,7 +440,7 @@ void CBreakable::BreakTouch( CBaseEntity *pOther ) } if ( FBitSet ( pev->spawnflags, SF_BREAK_TOUCH ) ) - {// can be broken when run into + {// can be broken when run into flDamage = pevToucher->velocity.Length() * 0.01; if (flDamage >= pev->health) @@ -508,19 +455,19 @@ void CBreakable::BreakTouch( CBaseEntity *pOther ) if ( FBitSet ( pev->spawnflags, SF_BREAK_PRESSURE ) && pevToucher->absmin.z >= pev->maxs.z - 2 ) {// can be broken when stood upon - + // play creaking sound here. DamageSound(); - SetThink(&CBreakable:: Die ); + SetThink ( Die ); SetTouch( NULL ); - + if ( m_flDelay == 0 ) {// !!!BUGBUG - why doesn't zero delay work? m_flDelay = 0.1; } - SetNextThink( m_flDelay ); + pev->nextthink = pev->ltime + m_flDelay; } @@ -532,11 +479,8 @@ void CBreakable::BreakTouch( CBaseEntity *pOther ) // // Break when triggered -void CBreakable::BreakUse( CBaseEntity *pActivator, CBaseEntity *pCaller, USE_TYPE useType, float value ) +void CBreakable::Use( CBaseEntity *pActivator, CBaseEntity *pCaller, USE_TYPE useType, float value ) { - // for a respawnable entity, ON means someone wants it to respawn- but this one's solid already. - if (m_iRespawnTime && useType == USE_ON) - return; if ( IsBreakable() ) { pev->angles.y = m_angle; @@ -547,93 +491,6 @@ void CBreakable::BreakUse( CBaseEntity *pActivator, CBaseEntity *pCaller, USE_TY } } -//LRC -void CBreakable::RespawnUse( CBaseEntity *pActivator, CBaseEntity *pCaller, USE_TYPE useType, float value ) -{ - // OFF means someone wants it to break, but this one's broken already. - if (useType == USE_OFF) - { - if (m_iRespawnTime > 0) - { - // reset my respawn time - SetThink(&CBreakable:: RespawnThink ); - SetNextThink( m_iRespawnTime ); - } - return; - } -// ALERT(at_debug,"Respawn trigger received\n"); - SetThink(&CBreakable:: RespawnThink ); - SetNextThink( 0.1 ); -} - -//LRC -void CBreakable::RespawnThink( void ) -{ -// ALERT(at_debug,"RespawnThink: "); - CBaseEntity *pList[2]; - int count = UTIL_EntitiesInBox( pList, 2, pev->mins, pev->maxs, FL_MONSTER | FL_CLIENT ); - if ( count ) - { - // Can't respawn right now, a monster or player is in the way. Wait a bit. -// ALERT(at_debug,"Respawn failed, count is %d\n",count); - SetThink(&CBreakable:: RespawnThink ); - SetNextThink( 2 ); // CONSIDER: change this number? - } - else - { - // fade in, don't just appear(?) - if (pev->spawnflags & SF_BREAK_FADE_RESPAWN) - { - SetThink(&CBreakable:: RespawnFadeThink ); - SetNextThink( 0.1 ); - pev->renderamt = 0; - if (m_iInitialRenderMode == kRenderNormal) - { - pev->rendermode = kRenderTransTexture; - m_iInitialRenderAmt = 255; - } - } -// ALERT(at_debug,"Respawn OK\n"); - /* if (FStrEq("func_pushable",STRING(pev->classname))){ //AJH Fix for respawnable breakable pushables - pev->solid = SOLID_BBOX; //For some reason this code must be executed outside of - pev->origin.z+=1; //the RespawnThink function. Uses DoRespawn() - UTIL_SetOrigin(this,pev->origin); - }else{ - pev->solid = SOLID_BSP; - } - */ - DoRespawn(); //AJH Fix for respawnable breakable pushables (BY HAWK777) - SetUse(&CBreakable:: BreakUse ); - pev->effects &= ~EF_NODRAW; - pev->health = m_iInitialHealth; - - if ( !FBitSet( pev->spawnflags, SF_BREAK_TRIGGER_ONLY ) ) - pev->takedamage = DAMAGE_YES; - - // trigger the "fire on respawn" target - FireTargets( STRING(pev->netname), this, this, USE_TOGGLE, 0); - } -} - -void CBreakable::DoRespawn( void ) //AJH Fix for respawnable breakable pushables (BY HAWK777) -{ -pev->solid = SOLID_BSP; -} - -void CBreakable::RespawnFadeThink ( void ) -{ - int newamt = min( pev->renderamt + 50, m_iInitialRenderAmt); -// ALERT(at_debug, "FadeThink: %d changed to %d\n",pev->renderamt,newamt); - pev->renderamt = newamt; - if (pev->renderamt < m_iInitialRenderAmt) - { - SetNextThink( 0.1 ); - } - else - { - pev->rendermode = m_iInitialRenderMode; - } -} void CBreakable::TraceAttack( entvars_t *pevAttacker, float flDamage, Vector vecDir, TraceResult *ptr, int bitsDamageType ) { @@ -654,34 +511,13 @@ void CBreakable::TraceAttack( entvars_t *pevAttacker, float flDamage, Vector vec } } break; - + case matUnbreakableGlass: UTIL_Ricochet( ptr->vecEndPos, RANDOM_FLOAT(0.5,1.5) ); break; } } - //LRC - if (m_iszWhenHit) - { - if(m_pHitProxy==NULL){ //AJH may need to reset this as it's null after save/load - m_pHitProxy = GetClassPtr( (CPointEntity*)NULL ); - } - - m_pHitProxy->pev->origin = ptr->vecEndPos; - if (pev->spawnflags&SF_BREAKABLE_INVERT){ //AJH - vecDir.y=-vecDir.y; - //vecDir.z=-vecDir.z; - vecDir.x=-vecDir.x; - //ALERT(at_debug,"INVERTING Breakables 'hit' vector (x&y components only) \n"); - } - m_pHitProxy->pev->velocity = vecDir; - m_pHitProxy->pev->angles = UTIL_VecToAngles(vecDir); //AJH - - //ALERT(at_debug,"Func_breakable fires %s \n",STRING(m_iszWhenHit)); - FireTargets( STRING(m_iszWhenHit), m_pHitProxy, this, USE_TOGGLE, 0 ); - } - CBaseDelay::TraceAttack( pevAttacker, flDamage, vecDir, ptr, bitsDamageType ); } @@ -695,11 +531,11 @@ int CBreakable :: TakeDamage( entvars_t* pevInflictor, entvars_t* pevAttacker, f Vector vecTemp; // if Attacker == Inflictor, the attack was a melee or other instant-hit attack. - // (that is, no actual entity projectile was involved in the attack so use the shooter's origin). - if ( pevAttacker == pevInflictor ) + // (that is, no actual entity projectile was involved in the attack so use the shooter's origin). + if ( pevAttacker == pevInflictor ) { vecTemp = pevInflictor->origin - ( pev->absmin + ( pev->size * 0.5 ) ); - + // if a client hit the breakable with a crowbar, and breakable is crowbar-sensitive, break it now. if ( FBitSet ( pevAttacker->flags, FL_CLIENT ) && FBitSet ( pev->spawnflags, SF_BREAK_CROWBAR ) && (bitsDamageType & DMG_CLUB)) @@ -710,7 +546,7 @@ int CBreakable :: TakeDamage( entvars_t* pevInflictor, entvars_t* pevAttacker, f { vecTemp = pevInflictor->origin - ( pev->absmin + ( pev->size * 0.5 ) ); } - + if (!IsBreakable()) return 0; @@ -724,16 +560,12 @@ int CBreakable :: TakeDamage( entvars_t* pevInflictor, entvars_t* pevAttacker, f // this global is still used for glass and other non-monster killables, along with decals. g_vecAttackDir = vecTemp.Normalize(); - + // do the damage pev->health -= flDamage; if (pev->health <= 0) { -// LRC - Die() does everything necessary -// if (!m_iRespawnTime) -// { -// Killed( pevAttacker, GIB_NORMAL ); -// } + Killed( pevAttacker, GIB_NORMAL ); Die(); return 0; } @@ -755,7 +587,7 @@ void CBreakable::Die( void ) char cFlag = 0; int pitch; float fvol; - + pitch = 95 + RANDOM_LONG(0,29); if (pitch > 97 && pitch < 103) @@ -775,9 +607,9 @@ void CBreakable::Die( void ) case matGlass: switch ( RANDOM_LONG(0,1) ) { - case 0: EMIT_SOUND_DYN(ENT(pev), CHAN_VOICE, "debris/bustglass1.wav", fvol, ATTN_NORM, 0, pitch); + case 0: EMIT_SOUND_DYN(ENT(pev), CHAN_VOICE, "debris/bustglass1.wav", fvol, ATTN_NORM, 0, pitch); break; - case 1: EMIT_SOUND_DYN(ENT(pev), CHAN_VOICE, "debris/bustglass2.wav", fvol, ATTN_NORM, 0, pitch); + case 1: EMIT_SOUND_DYN(ENT(pev), CHAN_VOICE, "debris/bustglass2.wav", fvol, ATTN_NORM, 0, pitch); break; } cFlag = BREAK_GLASS; @@ -786,9 +618,9 @@ void CBreakable::Die( void ) case matWood: switch ( RANDOM_LONG(0,1) ) { - case 0: EMIT_SOUND_DYN(ENT(pev), CHAN_VOICE, "debris/bustcrate1.wav", fvol, ATTN_NORM, 0, pitch); + case 0: EMIT_SOUND_DYN(ENT(pev), CHAN_VOICE, "debris/bustcrate1.wav", fvol, ATTN_NORM, 0, pitch); break; - case 1: EMIT_SOUND_DYN(ENT(pev), CHAN_VOICE, "debris/bustcrate2.wav", fvol, ATTN_NORM, 0, pitch); + case 1: EMIT_SOUND_DYN(ENT(pev), CHAN_VOICE, "debris/bustcrate2.wav", fvol, ATTN_NORM, 0, pitch); break; } cFlag = BREAK_WOOD; @@ -798,9 +630,9 @@ void CBreakable::Die( void ) case matMetal: switch ( RANDOM_LONG(0,1) ) { - case 0: EMIT_SOUND_DYN(ENT(pev), CHAN_VOICE, "debris/bustmetal1.wav", fvol, ATTN_NORM, 0, pitch); + case 0: EMIT_SOUND_DYN(ENT(pev), CHAN_VOICE, "debris/bustmetal1.wav", fvol, ATTN_NORM, 0, pitch); break; - case 1: EMIT_SOUND_DYN(ENT(pev), CHAN_VOICE, "debris/bustmetal2.wav", fvol, ATTN_NORM, 0, pitch); + case 1: EMIT_SOUND_DYN(ENT(pev), CHAN_VOICE, "debris/bustmetal2.wav", fvol, ATTN_NORM, 0, pitch); break; } cFlag = BREAK_METAL; @@ -809,9 +641,9 @@ void CBreakable::Die( void ) case matFlesh: switch ( RANDOM_LONG(0,1) ) { - case 0: EMIT_SOUND_DYN(ENT(pev), CHAN_VOICE, "debris/bustflesh1.wav", fvol, ATTN_NORM, 0, pitch); + case 0: EMIT_SOUND_DYN(ENT(pev), CHAN_VOICE, "debris/bustflesh1.wav", fvol, ATTN_NORM, 0, pitch); break; - case 1: EMIT_SOUND_DYN(ENT(pev), CHAN_VOICE, "debris/bustflesh2.wav", fvol, ATTN_NORM, 0, pitch); + case 1: EMIT_SOUND_DYN(ENT(pev), CHAN_VOICE, "debris/bustflesh2.wav", fvol, ATTN_NORM, 0, pitch); break; } cFlag = BREAK_FLESH; @@ -821,9 +653,9 @@ void CBreakable::Die( void ) case matCinderBlock: switch ( RANDOM_LONG(0,1) ) { - case 0: EMIT_SOUND_DYN(ENT(pev), CHAN_VOICE, "debris/bustconcrete1.wav", fvol, ATTN_NORM, 0, pitch); + case 0: EMIT_SOUND_DYN(ENT(pev), CHAN_VOICE, "debris/bustconcrete1.wav", fvol, ATTN_NORM, 0, pitch); break; - case 1: EMIT_SOUND_DYN(ENT(pev), CHAN_VOICE, "debris/bustconcrete2.wav", fvol, ATTN_NORM, 0, pitch); + case 1: EMIT_SOUND_DYN(ENT(pev), CHAN_VOICE, "debris/bustconcrete2.wav", fvol, ATTN_NORM, 0, pitch); break; } cFlag = BREAK_CONCRETE; @@ -833,8 +665,8 @@ void CBreakable::Die( void ) EMIT_SOUND_DYN(ENT(pev), CHAN_VOICE, "debris/bustceiling.wav", fvol, ATTN_NORM, 0, pitch); break; } - - + + if (m_Explosion == expDirected) vecVelocity = g_vecAttackDir * 200; else @@ -845,7 +677,7 @@ void CBreakable::Die( void ) } vecSpot = pev->origin + (pev->mins + pev->maxs) * 0.5; - MESSAGE_BEGIN( MSG_PVS, gmsgTempEntity, vecSpot ); + MESSAGE_BEGIN( MSG_PVS, SVC_TEMPENTITY, vecSpot ); WRITE_BYTE( TE_BREAKMODEL); // position @@ -859,12 +691,12 @@ void CBreakable::Die( void ) WRITE_COORD( pev->size.z); // velocity - WRITE_COORD( vecVelocity.x ); + WRITE_COORD( vecVelocity.x ); WRITE_COORD( vecVelocity.y ); WRITE_COORD( vecVelocity.z ); // randomization - WRITE_BYTE( 10 ); + WRITE_BYTE( 10 ); // Model WRITE_SHORT( m_idShard ); //model id# @@ -904,49 +736,15 @@ void CBreakable::Die( void ) } } - // If I'm getting removed, don't fire something that could fire myself - if (!m_iRespawnTime) - pev->targetname = 0; + // Don't fire something that could fire myself + pev->targetname = 0; pev->solid = SOLID_NOT; - pev->effects |= EF_NODRAW; - pev->takedamage = DAMAGE_NO; - - if (m_iStyle >= 32) - LIGHT_STYLE(m_iStyle, "a"); - else if (m_iStyle <= -32) - LIGHT_STYLE(-m_iStyle, "z"); - // Fire targets on break SUB_UseTargets( NULL, USE_TOGGLE, 0 ); - if (m_iRespawnTime == -1) - { -// ALERT(at_debug,"Waiting for respawn trigger\n"); - SetUse(&CBreakable:: RespawnUse ); } - else if (m_iRespawnTime) - { -// ALERT(at_debug,"Respawning in %d secs\n",m_iRespawnTime); - SetThink(&CBreakable:: RespawnThink ); - SetNextThink( m_iRespawnTime ); - } - else - { -// ALERT(at_debug,"No respawn\n"); - - //tidy up - if (m_pHitProxy) - { - m_pHitProxy->SetThink(&CBreakable::SUB_Remove ); - m_pHitProxy->SetNextThink( 0.1 ); - m_pHitProxy = NULL; - } - - SetThink(&CBreakable::SUB_Remove ); - SetNextThink( 0.1 ); -// ALERT(at_console, "Set SUB_Remove\n"); - } - + SetThink( SUB_Remove ); + pev->nextthink = pev->ltime + 0.1; if ( m_iszSpawnObject ) CBaseEntity::Create( (char *)STRING(m_iszSpawnObject), VecBModelOrigin(pev), pev->angles, edict() ); @@ -959,8 +757,8 @@ void CBreakable::Die( void ) -BOOL CBreakable :: IsBreakable( void ) -{ +BOOL CBreakable :: IsBreakable( void ) +{ return m_Material != matUnbreakableGlass; } @@ -986,9 +784,8 @@ public: void Move( CBaseEntity *pMover, int push ); void KeyValue( KeyValueData *pkvd ); void Use( CBaseEntity *pActivator, CBaseEntity *pCaller, USE_TYPE useType, float value ); - void DoRespawn(void); //AJH Fix for respawnable breakable pushables (BY HAWK777) void EXPORT StopSound( void ); - // virtual void SetActivator( CBaseEntity *pActivator ) { m_pPusher = pActivator; } +// virtual void SetActivator( CBaseEntity *pActivator ) { m_pPusher = pActivator; } virtual int ObjectCaps( void ) { return (CBaseEntity :: ObjectCaps() & ~FCAP_ACROSS_TRANSITION) | FCAP_CONTINUOUS_USE; } virtual int Save( CSave &save ); @@ -998,7 +795,7 @@ public: // breakables use an overridden takedamage virtual int TakeDamage( entvars_t* pevInflictor, entvars_t* pevAttacker, float flDamage, int bitsDamageType ); - + static TYPEDESCRIPTION m_SaveData[]; static char *m_soundNames[3]; @@ -1007,7 +804,7 @@ public: float m_soundTime; }; -TYPEDESCRIPTION CPushable::m_SaveData[] = +TYPEDESCRIPTION CPushable::m_SaveData[] = { DEFINE_FIELD( CPushable, m_maxSpeed, FIELD_FLOAT ), DEFINE_FIELD( CPushable, m_soundTime, FIELD_TIME ), @@ -1022,9 +819,6 @@ char *CPushable :: m_soundNames[3] = { "debris/pushbox1.wav", "debris/pushbox2.w void CPushable :: Spawn( void ) { - Vector vecMins = pev->mins; - Vector vecMaxs = pev->maxs; - if ( pev->spawnflags & SF_PUSH_BREAKABLE ) CBreakable::Spawn(); else @@ -1034,17 +828,15 @@ void CPushable :: Spawn( void ) pev->solid = SOLID_BBOX; SET_MODEL( ENT(pev), STRING(pev->model) ); -// UTIL_SetSize( pev, vecMins, vecMaxs ); - if ( pev->friction > 399 ) pev->friction = 399; m_maxSpeed = 400 - pev->friction; SetBits( pev->flags, FL_FLOAT ); pev->friction = 0; - + pev->origin.z += 1; // Pick up off of the floor - UTIL_SetOrigin( this, pev->origin ); + UTIL_SetOrigin( pev, pev->origin ); // Multiply by area of the box's cross-section (assume 1000 units^3 standard volume) pev->skin = ( pev->skin * (pev->maxs.x - pev->mins.x) * (pev->maxs.y - pev->mins.y) ) * 0.0005; @@ -1110,8 +902,6 @@ void CPushable :: Use( CBaseEntity *pActivator, CBaseEntity *pCaller, USE_TYPE u return; } - if ( pev->spawnflags & SF_PUSH_NOPULL ) return; //LRC: a non-pullable pushable. - if ( pActivator->pev->velocity != g_vecZero ) Move( pActivator, 0 ); } @@ -1135,7 +925,7 @@ void CPushable :: Move( CBaseEntity *pOther, int push ) if ( FBitSet(pevToucher->flags,FL_ONGROUND) && pevToucher->groundentity && VARS(pevToucher->groundentity) == pev ) { // Only push if floating - if ( pev->waterlevel > 0 && pev->watertype > CONTENTS_FLYFIELD) + if ( pev->waterlevel > 0 ) pev->velocity.z += pevToucher->velocity.z * 0.1; return; @@ -1144,9 +934,7 @@ void CPushable :: Move( CBaseEntity *pOther, int push ) if ( pOther->IsPlayer() ) { - if ( push && !(pevToucher->button & (IN_FORWARD|IN_MOVERIGHT|IN_MOVELEFT|IN_BACK)) ) - return; - if ( !push && !(pevToucher->button & IN_BACK) ) + if ( push && !(pevToucher->button & (IN_FORWARD|IN_USE)) ) // Don't push unless the player is pushing forward and NOT use (pull) return; playerTouch = 1; } @@ -1157,22 +945,20 @@ void CPushable :: Move( CBaseEntity *pOther, int push ) { if ( !(pevToucher->flags & FL_ONGROUND) ) // Don't push away from jumping/falling players unless in water { - if ( pev->waterlevel < 1 || pev->watertype <= CONTENTS_FLYFIELD) + if ( pev->waterlevel < 1 ) return; - else + else factor = 0.1; } else factor = 1; } - else + else factor = 0.25; - if (!push) factor = factor*0.5; - pev->velocity.x += pevToucher->velocity.x * factor; pev->velocity.y += pevToucher->velocity.y * factor; - + float length = sqrt( pev->velocity.x * pev->velocity.x + pev->velocity.y * pev->velocity.y ); if ( push && (length > MaxSpeed()) ) { @@ -1191,7 +977,7 @@ void CPushable :: Move( CBaseEntity *pOther, int push ) m_lastSound = RANDOM_LONG(0,2); EMIT_SOUND(ENT(pev), CHAN_WEAPON, m_soundNames[m_lastSound], 0.5, ATTN_NORM); // SetThink( StopSound ); - // SetNextThink( 0.1 ); + // pev->nextthink = pev->ltime + 0.1; } else STOP_SOUND( ENT(pev), CHAN_WEAPON, m_soundNames[m_lastSound] ); @@ -1216,8 +1002,3 @@ int CPushable::TakeDamage( entvars_t* pevInflictor, entvars_t* pevAttacker, floa return 1; } -void CPushable::DoRespawn(void){ //AJH Fix for respawnable breakable pushables (BY HAWK777) - pev->solid = SOLID_BBOX; - pev->origin.z += 1; - UTIL_SetOrigin( this, pev->origin ); -} \ No newline at end of file diff --git a/spirit/func_break.h b/dlls/func_break.h similarity index 76% rename from spirit/func_break.h rename to dlls/func_break.h index e304f6d0..2441f756 100644 --- a/spirit/func_break.h +++ b/dlls/func_break.h @@ -1,91 +1,74 @@ -/*** -* -* Copyright (c) 1996-2002, 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. -* -****/ -#ifndef FUNC_BREAK_H -#define FUNC_BREAK_H - -typedef enum { expRandom, expDirected} Explosions; -typedef enum { matGlass = 0, matWood, matMetal, matFlesh, matCinderBlock, matCeilingTile, matComputer, matUnbreakableGlass, matRocks, matNone, matLastMaterial } Materials; - +/*** +* +* Copyright (c) 1996-2002, 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. +* +****/ +#ifndef FUNC_BREAK_H +#define FUNC_BREAK_H + +typedef enum { expRandom, expDirected} Explosions; +typedef enum { matGlass = 0, matWood, matMetal, matFlesh, matCinderBlock, matCeilingTile, matComputer, matUnbreakableGlass, matRocks, matNone, matLastMaterial } Materials; + #define NUM_SHARDS 6 // this many shards spawned when breakable objects break; -#define SF_BREAKABLE_INVERT 16 - -class CBreakable : public CBaseDelay -{ -public: - // basic functions - void Spawn( void ); - void Precache( void ); + +class CBreakable : public CBaseDelay +{ +public: + // basic functions + void Spawn( void ); + void Precache( void ); void KeyValue( KeyValueData* pkvd); - virtual float CalcRatio(CBaseEntity *pLocus,int mode);//AJH added 'mode' = ratio to return); - void EXPORT BreakTouch( CBaseEntity *pOther ); - void EXPORT BreakUse( CBaseEntity *pActivator, CBaseEntity *pCaller, USE_TYPE useType, float value ); - void EXPORT RespawnUse( CBaseEntity *pActivator, CBaseEntity *pCaller, USE_TYPE useType, float value ); - void EXPORT RespawnThink( void ); - void EXPORT RespawnFadeThink( void ); + void EXPORT BreakTouch( CBaseEntity *pOther ); + void Use( CBaseEntity *pActivator, CBaseEntity *pCaller, USE_TYPE useType, float value ); void DamageSound( void ); - virtual void DoRespawn( void ); //AJH Fix for respawnable breakable pushables - virtual int Classify ( void ) { return m_iClass; } - - // breakables use an overridden takedamage - virtual int TakeDamage( entvars_t* pevInflictor, entvars_t* pevAttacker, float flDamage, int bitsDamageType ); - // To spark when hit - void TraceAttack( entvars_t *pevAttacker, float flDamage, Vector vecDir, TraceResult *ptr, int bitsDamageType ); - - BOOL IsBreakable( void ); - BOOL SparkWhenHit( void ); - - STATE GetState( void ); - - int DamageDecal( int bitsDamageType ); - - void EXPORT Die( void ); - virtual int ObjectCaps( void ) { return (CBaseEntity :: ObjectCaps() & ~FCAP_ACROSS_TRANSITION); } - virtual int Save( CSave &save ); - virtual int Restore( CRestore &restore ); - - inline BOOL Explodable( void ) { return ExplosionMagnitude() > 0; } - inline int ExplosionMagnitude( void ) { return pev->impulse; } - inline void ExplosionSetMagnitude( int magnitude ) { pev->impulse = magnitude; } - - static void MaterialSoundPrecache( Materials precacheMaterial ); - static void MaterialSoundRandom( edict_t *pEdict, Materials soundMaterial, float volume ); - static const char **MaterialSoundList( Materials precacheMaterial, int &soundCount ); - - static const char *pSoundsWood[]; - static const char *pSoundsFlesh[]; - static const char *pSoundsGlass[]; - static const char *pSoundsMetal[]; - static const char *pSoundsConcrete[]; - static const char *pSpawnObjects[]; - - static TYPEDESCRIPTION m_SaveData[]; - - Materials m_Material; - Explosions m_Explosion; - int m_idShard; - float m_angle; - int m_iszGibModel; - int m_iszSpawnObject; - //LRC - int m_iRespawnTime; - int m_iInitialHealth; - int m_iInitialRenderAmt; - int m_iInitialRenderMode; - int m_iClass; //so that monsters will attack it - int m_iszWhenHit; // locus trigger - CPointEntity *m_pHitProxy; -}; - -#endif // FUNC_BREAK_H + + // breakables use an overridden takedamage + virtual int TakeDamage( entvars_t* pevInflictor, entvars_t* pevAttacker, float flDamage, int bitsDamageType ); + // To spark when hit + void TraceAttack( entvars_t *pevAttacker, float flDamage, Vector vecDir, TraceResult *ptr, int bitsDamageType ); + + BOOL IsBreakable( void ); + BOOL SparkWhenHit( void ); + + int DamageDecal( int bitsDamageType ); + + void EXPORT Die( void ); + virtual int ObjectCaps( void ) { return (CBaseEntity :: ObjectCaps() & ~FCAP_ACROSS_TRANSITION); } + virtual int Save( CSave &save ); + virtual int Restore( CRestore &restore ); + + inline BOOL Explodable( void ) { return ExplosionMagnitude() > 0; } + inline int ExplosionMagnitude( void ) { return pev->impulse; } + inline void ExplosionSetMagnitude( int magnitude ) { pev->impulse = magnitude; } + + static void MaterialSoundPrecache( Materials precacheMaterial ); + static void MaterialSoundRandom( edict_t *pEdict, Materials soundMaterial, float volume ); + static const char **MaterialSoundList( Materials precacheMaterial, int &soundCount ); + + static const char *pSoundsWood[]; + static const char *pSoundsFlesh[]; + static const char *pSoundsGlass[]; + static const char *pSoundsMetal[]; + static const char *pSoundsConcrete[]; + static const char *pSpawnObjects[]; + + static TYPEDESCRIPTION m_SaveData[]; + + Materials m_Material; + Explosions m_Explosion; + int m_idShard; + float m_angle; + int m_iszGibModel; + int m_iszSpawnObject; +}; + +#endif // FUNC_BREAK_H diff --git a/dlls/func_tank.cpp b/dlls/func_tank.cpp new file mode 100644 index 00000000..459d21af --- /dev/null +++ b/dlls/func_tank.cpp @@ -0,0 +1,1039 @@ +/*** +* +* Copyright (c) 1996-2002, 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. +* +****/ +#include "extdll.h" +#include "util.h" +#include "cbase.h" +#include "effects.h" +#include "weapons.h" +#include "explode.h" + +#include "player.h" + + +#define SF_TANK_ACTIVE 0x0001 +#define SF_TANK_PLAYER 0x0002 +#define SF_TANK_HUMANS 0x0004 +#define SF_TANK_ALIENS 0x0008 +#define SF_TANK_LINEOFSIGHT 0x0010 +#define SF_TANK_CANCONTROL 0x0020 +#define SF_TANK_SOUNDON 0x8000 + +enum TANKBULLET +{ + TANK_BULLET_NONE = 0, + TANK_BULLET_9MM = 1, + TANK_BULLET_MP5 = 2, + TANK_BULLET_12MM = 3, +}; + +// Custom damage +// env_laser (duration is 0.5 rate of fire) +// rockets +// explosion? + +class CFuncTank : public CBaseEntity +{ +public: + void Spawn( void ); + void Precache( void ); + void KeyValue( KeyValueData *pkvd ); + void Use( CBaseEntity *pActivator, CBaseEntity *pCaller, USE_TYPE useType, float value ); + void Think( void ); + void TrackTarget( void ); + + virtual void Fire( const Vector &barrelEnd, const Vector &forward, entvars_t *pevAttacker ); + virtual Vector UpdateTargetPosition( CBaseEntity *pTarget ) + { + return pTarget->BodyTarget( pev->origin ); + } + + void StartRotSound( void ); + void StopRotSound( void ); + + // Bmodels don't go across transitions + virtual int ObjectCaps( void ) { return CBaseEntity :: ObjectCaps() & ~FCAP_ACROSS_TRANSITION; } + + inline BOOL IsActive( void ) { return (pev->spawnflags & SF_TANK_ACTIVE)?TRUE:FALSE; } + inline void TankActivate( void ) { pev->spawnflags |= SF_TANK_ACTIVE; pev->nextthink = pev->ltime + 0.1; m_fireLast = 0; } + inline void TankDeactivate( void ) { pev->spawnflags &= ~SF_TANK_ACTIVE; m_fireLast = 0; StopRotSound(); } + inline BOOL CanFire( void ) { return (gpGlobals->time - m_lastSightTime) < m_persist; } + BOOL InRange( float range ); + + // Acquire a target. pPlayer is a player in the PVS + edict_t *FindTarget( edict_t *pPlayer ); + + void TankTrace( const Vector &vecStart, const Vector &vecForward, const Vector &vecSpread, TraceResult &tr ); + + Vector BarrelPosition( void ) + { + Vector forward, right, up; + UTIL_MakeVectorsPrivate( pev->angles, forward, right, up ); + return pev->origin + (forward * m_barrelPos.x) + (right * m_barrelPos.y) + (up * m_barrelPos.z); + } + + void AdjustAnglesForBarrel( Vector &angles, float distance ); + + virtual int Save( CSave &save ); + virtual int Restore( CRestore &restore ); + static TYPEDESCRIPTION m_SaveData[]; + + BOOL OnControls( entvars_t *pevTest ); + BOOL StartControl( CBasePlayer* pController ); + void StopControl( void ); + void ControllerPostFrame( void ); + + +protected: + CBasePlayer* m_pController; + float m_flNextAttack; + Vector m_vecControllerUsePos; + + float m_yawCenter; // "Center" yaw + float m_yawRate; // Max turn rate to track targets + float m_yawRange; // Range of turning motion (one-sided: 30 is +/- 30 degress from center) + // Zero is full rotation + float m_yawTolerance; // Tolerance angle + + float m_pitchCenter; // "Center" pitch + float m_pitchRate; // Max turn rate on pitch + float m_pitchRange; // Range of pitch motion as above + float m_pitchTolerance; // Tolerance angle + + float m_fireLast; // Last time I fired + float m_fireRate; // How many rounds/second + float m_lastSightTime;// Last time I saw target + float m_persist; // Persistence of firing (how long do I shoot when I can't see) + float m_minRange; // Minimum range to aim/track + float m_maxRange; // Max range to aim/track + + Vector m_barrelPos; // Length of the freakin barrel + float m_spriteScale; // Scale of any sprites we shoot + int m_iszSpriteSmoke; + int m_iszSpriteFlash; + TANKBULLET m_bulletType; // Bullet type + int m_iBulletDamage; // 0 means use Bullet type's default damage + + Vector m_sightOrigin; // Last sight of target + int m_spread; // firing spread + int m_iszMaster; // Master entity (game_team_master or multisource) +}; + + +TYPEDESCRIPTION CFuncTank::m_SaveData[] = +{ + DEFINE_FIELD( CFuncTank, m_yawCenter, FIELD_FLOAT ), + DEFINE_FIELD( CFuncTank, m_yawRate, FIELD_FLOAT ), + DEFINE_FIELD( CFuncTank, m_yawRange, FIELD_FLOAT ), + DEFINE_FIELD( CFuncTank, m_yawTolerance, FIELD_FLOAT ), + DEFINE_FIELD( CFuncTank, m_pitchCenter, FIELD_FLOAT ), + DEFINE_FIELD( CFuncTank, m_pitchRate, FIELD_FLOAT ), + DEFINE_FIELD( CFuncTank, m_pitchRange, FIELD_FLOAT ), + DEFINE_FIELD( CFuncTank, m_pitchTolerance, FIELD_FLOAT ), + DEFINE_FIELD( CFuncTank, m_fireLast, FIELD_TIME ), + DEFINE_FIELD( CFuncTank, m_fireRate, FIELD_FLOAT ), + DEFINE_FIELD( CFuncTank, m_lastSightTime, FIELD_TIME ), + DEFINE_FIELD( CFuncTank, m_persist, FIELD_FLOAT ), + DEFINE_FIELD( CFuncTank, m_minRange, FIELD_FLOAT ), + DEFINE_FIELD( CFuncTank, m_maxRange, FIELD_FLOAT ), + DEFINE_FIELD( CFuncTank, m_barrelPos, FIELD_VECTOR ), + DEFINE_FIELD( CFuncTank, m_spriteScale, FIELD_FLOAT ), + DEFINE_FIELD( CFuncTank, m_iszSpriteSmoke, FIELD_STRING ), + DEFINE_FIELD( CFuncTank, m_iszSpriteFlash, FIELD_STRING ), + DEFINE_FIELD( CFuncTank, m_bulletType, FIELD_INTEGER ), + DEFINE_FIELD( CFuncTank, m_sightOrigin, FIELD_VECTOR ), + DEFINE_FIELD( CFuncTank, m_spread, FIELD_INTEGER ), + DEFINE_FIELD( CFuncTank, m_pController, FIELD_CLASSPTR ), + DEFINE_FIELD( CFuncTank, m_vecControllerUsePos, FIELD_VECTOR ), + DEFINE_FIELD( CFuncTank, m_flNextAttack, FIELD_TIME ), + DEFINE_FIELD( CFuncTank, m_iBulletDamage, FIELD_INTEGER ), + DEFINE_FIELD( CFuncTank, m_iszMaster, FIELD_STRING ), +}; + +IMPLEMENT_SAVERESTORE( CFuncTank, CBaseEntity ); + +static Vector gTankSpread[] = +{ + Vector( 0, 0, 0 ), // perfect + Vector( 0.025, 0.025, 0.025 ), // small cone + Vector( 0.05, 0.05, 0.05 ), // medium cone + Vector( 0.1, 0.1, 0.1 ), // large cone + Vector( 0.25, 0.25, 0.25 ), // extra-large cone +}; +#define MAX_FIRING_SPREADS ARRAYSIZE(gTankSpread) + + +void CFuncTank :: Spawn( void ) +{ + Precache(); + + pev->movetype = MOVETYPE_PUSH; // so it doesn't get pushed by anything + pev->solid = SOLID_BSP; + SET_MODEL( ENT(pev), STRING(pev->model) ); + + m_yawCenter = pev->angles.y; + m_pitchCenter = pev->angles.x; + + if ( IsActive() ) + pev->nextthink = pev->ltime + 1.0; + + m_sightOrigin = BarrelPosition(); // Point at the end of the barrel + + if ( m_fireRate <= 0 ) + m_fireRate = 1; + if ( m_spread > MAX_FIRING_SPREADS ) + m_spread = 0; + + pev->oldorigin = pev->origin; +} + + +void CFuncTank :: Precache( void ) +{ + if ( m_iszSpriteSmoke ) + PRECACHE_MODEL( (char *)STRING(m_iszSpriteSmoke) ); + if ( m_iszSpriteFlash ) + PRECACHE_MODEL( (char *)STRING(m_iszSpriteFlash) ); + + if ( pev->noise ) + PRECACHE_SOUND( (char *)STRING(pev->noise) ); +} + + +void CFuncTank :: KeyValue( KeyValueData *pkvd ) +{ + if (FStrEq(pkvd->szKeyName, "yawrate")) + { + m_yawRate = atof(pkvd->szValue); + pkvd->fHandled = TRUE; + } + else if (FStrEq(pkvd->szKeyName, "yawrange")) + { + m_yawRange = atof(pkvd->szValue); + pkvd->fHandled = TRUE; + } + else if (FStrEq(pkvd->szKeyName, "yawtolerance")) + { + m_yawTolerance = atof(pkvd->szValue); + pkvd->fHandled = TRUE; + } + else if (FStrEq(pkvd->szKeyName, "pitchrange")) + { + m_pitchRange = atof(pkvd->szValue); + pkvd->fHandled = TRUE; + } + else if (FStrEq(pkvd->szKeyName, "pitchrate")) + { + m_pitchRate = atof(pkvd->szValue); + pkvd->fHandled = TRUE; + } + else if (FStrEq(pkvd->szKeyName, "pitchtolerance")) + { + m_pitchTolerance = atof(pkvd->szValue); + pkvd->fHandled = TRUE; + } + else if (FStrEq(pkvd->szKeyName, "firerate")) + { + m_fireRate = atof(pkvd->szValue); + pkvd->fHandled = TRUE; + } + else if (FStrEq(pkvd->szKeyName, "barrel")) + { + m_barrelPos.x = atof(pkvd->szValue); + pkvd->fHandled = TRUE; + } + else if (FStrEq(pkvd->szKeyName, "barrely")) + { + m_barrelPos.y = atof(pkvd->szValue); + pkvd->fHandled = TRUE; + } + else if (FStrEq(pkvd->szKeyName, "barrelz")) + { + m_barrelPos.z = atof(pkvd->szValue); + pkvd->fHandled = TRUE; + } + else if (FStrEq(pkvd->szKeyName, "spritescale")) + { + m_spriteScale = atof(pkvd->szValue); + pkvd->fHandled = TRUE; + } + else if (FStrEq(pkvd->szKeyName, "spritesmoke")) + { + m_iszSpriteSmoke = ALLOC_STRING(pkvd->szValue); + pkvd->fHandled = TRUE; + } + else if (FStrEq(pkvd->szKeyName, "spriteflash")) + { + m_iszSpriteFlash = ALLOC_STRING(pkvd->szValue); + pkvd->fHandled = TRUE; + } + else if (FStrEq(pkvd->szKeyName, "rotatesound")) + { + pev->noise = ALLOC_STRING(pkvd->szValue); + pkvd->fHandled = TRUE; + } + else if (FStrEq(pkvd->szKeyName, "persistence")) + { + m_persist = atof(pkvd->szValue); + pkvd->fHandled = TRUE; + } + else if (FStrEq(pkvd->szKeyName, "bullet")) + { + m_bulletType = (TANKBULLET)atoi(pkvd->szValue); + pkvd->fHandled = TRUE; + } + else if ( FStrEq(pkvd->szKeyName, "bullet_damage" )) + { + m_iBulletDamage = atoi(pkvd->szValue); + pkvd->fHandled = TRUE; + } + else if (FStrEq(pkvd->szKeyName, "firespread")) + { + m_spread = atoi(pkvd->szValue); + pkvd->fHandled = TRUE; + } + else if (FStrEq(pkvd->szKeyName, "minRange")) + { + m_minRange = atof(pkvd->szValue); + pkvd->fHandled = TRUE; + } + else if (FStrEq(pkvd->szKeyName, "maxRange")) + { + m_maxRange = atof(pkvd->szValue); + pkvd->fHandled = TRUE; + } + else if (FStrEq(pkvd->szKeyName, "master")) + { + m_iszMaster = ALLOC_STRING(pkvd->szValue); + pkvd->fHandled = TRUE; + } + else + CBaseEntity::KeyValue( pkvd ); +} + +////////////// START NEW STUFF ////////////// + +//================================================================================== +// TANK CONTROLLING +BOOL CFuncTank :: OnControls( entvars_t *pevTest ) +{ + if ( !(pev->spawnflags & SF_TANK_CANCONTROL) ) + return FALSE; + + Vector offset = pevTest->origin - pev->origin; + + if ( (m_vecControllerUsePos - pevTest->origin).Length() < 30 ) + return TRUE; + + return FALSE; +} + +BOOL CFuncTank :: StartControl( CBasePlayer *pController ) +{ + if ( m_pController != NULL ) + return FALSE; + + // Team only or disabled? + if ( m_iszMaster ) + { + if ( !UTIL_IsMasterTriggered( m_iszMaster, pController ) ) + return FALSE; + } + + ALERT( at_console, "using TANK!\n"); + + m_pController = pController; + if ( m_pController->m_pActiveItem ) + { + m_pController->m_pActiveItem->Holster(); + m_pController->pev->weaponmodel = 0; + m_pController->pev->viewmodel = 0; + + } + + m_pController->m_iHideHUD |= HIDEHUD_WEAPONS; + m_vecControllerUsePos = m_pController->pev->origin; + + pev->nextthink = pev->ltime + 0.1; + + return TRUE; +} + +void CFuncTank :: StopControl() +{ + // TODO: bring back the controllers current weapon + if ( !m_pController ) + return; + + if ( m_pController->m_pActiveItem ) + m_pController->m_pActiveItem->Deploy(); + + ALERT( at_console, "stopped using TANK\n"); + + m_pController->m_iHideHUD &= ~HIDEHUD_WEAPONS; + + pev->nextthink = 0; + m_pController = NULL; + + if ( IsActive() ) + pev->nextthink = pev->ltime + 1.0; +} + +// Called each frame by the player's ItemPostFrame +void CFuncTank :: ControllerPostFrame( void ) +{ + ASSERT(m_pController != NULL); + + if ( gpGlobals->time < m_flNextAttack ) + return; + + if ( m_pController->pev->button & IN_ATTACK ) + { + Vector vecForward; + UTIL_MakeVectorsPrivate( pev->angles, vecForward, NULL, NULL ); + + m_fireLast = gpGlobals->time - (1/m_fireRate) - 0.01; // to make sure the gun doesn't fire too many bullets + + Fire( BarrelPosition(), vecForward, m_pController->pev ); + + // HACKHACK -- make some noise (that the AI can hear) + if ( m_pController && m_pController->IsPlayer() ) + ((CBasePlayer *)m_pController)->m_iWeaponVolume = LOUD_GUN_VOLUME; + + m_flNextAttack = gpGlobals->time + (1/m_fireRate); + } +} +////////////// END NEW STUFF ////////////// + + +void CFuncTank :: Use( CBaseEntity *pActivator, CBaseEntity *pCaller, USE_TYPE useType, float value ) +{ + if ( pev->spawnflags & SF_TANK_CANCONTROL ) + { // player controlled turret + + if ( pActivator->Classify() != CLASS_PLAYER ) + return; + + if ( value == 2 && useType == USE_SET ) + { + ControllerPostFrame(); + } + else if ( !m_pController && useType != USE_OFF ) + { + ((CBasePlayer*)pActivator)->m_pTank = this; + StartControl( (CBasePlayer*)pActivator ); + } + else + { + StopControl(); + } + } + else + { + if ( !ShouldToggle( useType, IsActive() ) ) + return; + + if ( IsActive() ) + TankDeactivate(); + else + TankActivate(); + } +} + + +edict_t *CFuncTank :: FindTarget( edict_t *pPlayer ) +{ + return pPlayer; +} + + + +BOOL CFuncTank :: InRange( float range ) +{ + if ( range < m_minRange ) + return FALSE; + if ( m_maxRange > 0 && range > m_maxRange ) + return FALSE; + + return TRUE; +} + + +void CFuncTank :: Think( void ) +{ + pev->avelocity = g_vecZero; + TrackTarget(); + + if ( fabs(pev->avelocity.x) > 1 || fabs(pev->avelocity.y) > 1 ) + StartRotSound(); + else + StopRotSound(); +} + +void CFuncTank::TrackTarget( void ) +{ + TraceResult tr; + edict_t *pPlayer = FIND_CLIENT_IN_PVS( edict() ); + BOOL updateTime = FALSE, lineOfSight; + Vector angles, direction, targetPosition, barrelEnd; + edict_t *pTarget; + + // Get a position to aim for + if (m_pController) + { + // Tanks attempt to mirror the player's angles + angles = m_pController->pev->v_angle; + angles[0] = 0 - angles[0]; + pev->nextthink = pev->ltime + 0.05; + } + else + { + if ( IsActive() ) + pev->nextthink = pev->ltime + 0.1; + else + return; + + if ( FNullEnt( pPlayer ) ) + { + if ( IsActive() ) + pev->nextthink = pev->ltime + 2; // Wait 2 secs + return; + } + pTarget = FindTarget( pPlayer ); + if ( !pTarget ) + return; + + // Calculate angle needed to aim at target + barrelEnd = BarrelPosition(); + targetPosition = pTarget->v.origin + pTarget->v.view_ofs; + float range = (targetPosition - barrelEnd).Length(); + + if ( !InRange( range ) ) + return; + + UTIL_TraceLine( barrelEnd, targetPosition, dont_ignore_monsters, edict(), &tr ); + + lineOfSight = FALSE; + // No line of sight, don't track + if ( tr.flFraction == 1.0 || tr.pHit == pTarget ) + { + lineOfSight = TRUE; + + CBaseEntity *pInstance = CBaseEntity::Instance(pTarget); + if ( InRange( range ) && pInstance && pInstance->IsAlive() ) + { + updateTime = TRUE; + m_sightOrigin = UpdateTargetPosition( pInstance ); + } + } + + // Track sight origin + +// !!! I'm not sure what i changed + direction = m_sightOrigin - pev->origin; +// direction = m_sightOrigin - barrelEnd; + angles = UTIL_VecToAngles( direction ); + + // Calculate the additional rotation to point the end of the barrel at the target (not the gun's center) + AdjustAnglesForBarrel( angles, direction.Length() ); + } + + angles.x = -angles.x; + + // Force the angles to be relative to the center position + angles.y = m_yawCenter + UTIL_AngleDistance( angles.y, m_yawCenter ); + angles.x = m_pitchCenter + UTIL_AngleDistance( angles.x, m_pitchCenter ); + + // Limit against range in y + if ( angles.y > m_yawCenter + m_yawRange ) + { + angles.y = m_yawCenter + m_yawRange; + updateTime = FALSE; // Don't update if you saw the player, but out of range + } + else if ( angles.y < (m_yawCenter - m_yawRange) ) + { + angles.y = (m_yawCenter - m_yawRange); + updateTime = FALSE; // Don't update if you saw the player, but out of range + } + + if ( updateTime ) + m_lastSightTime = gpGlobals->time; + + // Move toward target at rate or less + float distY = UTIL_AngleDistance( angles.y, pev->angles.y ); + pev->avelocity.y = distY * 10; + if ( pev->avelocity.y > m_yawRate ) + pev->avelocity.y = m_yawRate; + else if ( pev->avelocity.y < -m_yawRate ) + pev->avelocity.y = -m_yawRate; + + // Limit against range in x + if ( angles.x > m_pitchCenter + m_pitchRange ) + angles.x = m_pitchCenter + m_pitchRange; + else if ( angles.x < m_pitchCenter - m_pitchRange ) + angles.x = m_pitchCenter - m_pitchRange; + + // Move toward target at rate or less + float distX = UTIL_AngleDistance( angles.x, pev->angles.x ); + pev->avelocity.x = distX * 10; + + if ( pev->avelocity.x > m_pitchRate ) + pev->avelocity.x = m_pitchRate; + else if ( pev->avelocity.x < -m_pitchRate ) + pev->avelocity.x = -m_pitchRate; + + if ( m_pController ) + return; + + if ( CanFire() && ( (fabs(distX) < m_pitchTolerance && fabs(distY) < m_yawTolerance) || (pev->spawnflags & SF_TANK_LINEOFSIGHT) ) ) + { + BOOL fire = FALSE; + Vector forward; + UTIL_MakeVectorsPrivate( pev->angles, forward, NULL, NULL ); + + if ( pev->spawnflags & SF_TANK_LINEOFSIGHT ) + { + float length = direction.Length(); + UTIL_TraceLine( barrelEnd, barrelEnd + forward * length, dont_ignore_monsters, edict(), &tr ); + if ( tr.pHit == pTarget ) + fire = TRUE; + } + else + fire = TRUE; + + if ( fire ) + { + Fire( BarrelPosition(), forward, pev ); + } + else + m_fireLast = 0; + } + else + m_fireLast = 0; +} + + +// If barrel is offset, add in additional rotation +void CFuncTank::AdjustAnglesForBarrel( Vector &angles, float distance ) +{ + float r2, d2; + + + if ( m_barrelPos.y != 0 || m_barrelPos.z != 0 ) + { + distance -= m_barrelPos.z; + d2 = distance * distance; + if ( m_barrelPos.y ) + { + r2 = m_barrelPos.y * m_barrelPos.y; + angles.y += (180.0 / M_PI) * atan2( m_barrelPos.y, sqrt( d2 - r2 ) ); + } + if ( m_barrelPos.z ) + { + r2 = m_barrelPos.z * m_barrelPos.z; + angles.x += (180.0 / M_PI) * atan2( -m_barrelPos.z, sqrt( d2 - r2 ) ); + } + } +} + + +// Fire targets and spawn sprites +void CFuncTank::Fire( const Vector &barrelEnd, const Vector &forward, entvars_t *pevAttacker ) +{ + if ( m_fireLast != 0 ) + { + if ( m_iszSpriteSmoke ) + { + CSprite *pSprite = CSprite::SpriteCreate( STRING(m_iszSpriteSmoke), barrelEnd, TRUE ); + pSprite->AnimateAndDie( RANDOM_FLOAT( 15.0, 20.0 ) ); + pSprite->SetTransparency( kRenderTransAlpha, pev->rendercolor.x, pev->rendercolor.y, pev->rendercolor.z, 255, kRenderFxNone ); + pSprite->pev->velocity.z = RANDOM_FLOAT(40, 80); + pSprite->SetScale( m_spriteScale ); + } + if ( m_iszSpriteFlash ) + { + CSprite *pSprite = CSprite::SpriteCreate( STRING(m_iszSpriteFlash), barrelEnd, TRUE ); + pSprite->AnimateAndDie( 60 ); + pSprite->SetTransparency( kRenderTransAdd, 255, 255, 255, 255, kRenderFxNoDissipation ); + pSprite->SetScale( m_spriteScale ); + + // Hack Hack, make it stick around for at least 100 ms. + pSprite->pev->nextthink += 0.1; + } + SUB_UseTargets( this, USE_TOGGLE, 0 ); + } + m_fireLast = gpGlobals->time; +} + + +void CFuncTank::TankTrace( const Vector &vecStart, const Vector &vecForward, const Vector &vecSpread, TraceResult &tr ) +{ + // get circular gaussian spread + float x, y, z; + do { + x = RANDOM_FLOAT(-0.5,0.5) + RANDOM_FLOAT(-0.5,0.5); + y = RANDOM_FLOAT(-0.5,0.5) + RANDOM_FLOAT(-0.5,0.5); + z = x*x+y*y; + } while (z > 1); + Vector vecDir = vecForward + + x * vecSpread.x * gpGlobals->v_right + + y * vecSpread.y * gpGlobals->v_up; + Vector vecEnd; + + vecEnd = vecStart + vecDir * 4096; + UTIL_TraceLine( vecStart, vecEnd, dont_ignore_monsters, edict(), &tr ); +} + + +void CFuncTank::StartRotSound( void ) +{ + if ( !pev->noise || (pev->spawnflags & SF_TANK_SOUNDON) ) + return; + pev->spawnflags |= SF_TANK_SOUNDON; + EMIT_SOUND( edict(), CHAN_STATIC, (char*)STRING(pev->noise), 0.85, ATTN_NORM); +} + + +void CFuncTank::StopRotSound( void ) +{ + if ( pev->spawnflags & SF_TANK_SOUNDON ) + STOP_SOUND( edict(), CHAN_STATIC, (char*)STRING(pev->noise) ); + pev->spawnflags &= ~SF_TANK_SOUNDON; +} + +class CFuncTankGun : public CFuncTank +{ +public: + void Fire( const Vector &barrelEnd, const Vector &forward, entvars_t *pevAttacker ); +}; +LINK_ENTITY_TO_CLASS( func_tank, CFuncTankGun ); + +void CFuncTankGun::Fire( const Vector &barrelEnd, const Vector &forward, entvars_t *pevAttacker ) +{ + int i; + + if ( m_fireLast != 0 ) + { + // FireBullets needs gpGlobals->v_up, etc. + UTIL_MakeAimVectors(pev->angles); + + int bulletCount = (gpGlobals->time - m_fireLast) * m_fireRate; + if ( bulletCount > 0 ) + { + for ( i = 0; i < bulletCount; i++ ) + { + switch( m_bulletType ) + { + case TANK_BULLET_9MM: + FireBullets( 1, barrelEnd, forward, gTankSpread[m_spread], 4096, BULLET_MONSTER_9MM, 1, m_iBulletDamage, pevAttacker ); + break; + + case TANK_BULLET_MP5: + FireBullets( 1, barrelEnd, forward, gTankSpread[m_spread], 4096, BULLET_MONSTER_MP5, 1, m_iBulletDamage, pevAttacker ); + break; + + case TANK_BULLET_12MM: + FireBullets( 1, barrelEnd, forward, gTankSpread[m_spread], 4096, BULLET_MONSTER_12MM, 1, m_iBulletDamage, pevAttacker ); + break; + + default: + case TANK_BULLET_NONE: + break; + } + } + CFuncTank::Fire( barrelEnd, forward, pevAttacker ); + } + } + else + CFuncTank::Fire( barrelEnd, forward, pevAttacker ); +} + + + +class CFuncTankLaser : public CFuncTank +{ +public: + void Activate( void ); + void KeyValue( KeyValueData *pkvd ); + void Fire( const Vector &barrelEnd, const Vector &forward, entvars_t *pevAttacker ); + void Think( void ); + CLaser *GetLaser( void ); + + virtual int Save( CSave &save ); + virtual int Restore( CRestore &restore ); + static TYPEDESCRIPTION m_SaveData[]; + +private: + CLaser *m_pLaser; + float m_laserTime; +}; +LINK_ENTITY_TO_CLASS( func_tanklaser, CFuncTankLaser ); + +TYPEDESCRIPTION CFuncTankLaser::m_SaveData[] = +{ + DEFINE_FIELD( CFuncTankLaser, m_pLaser, FIELD_CLASSPTR ), + DEFINE_FIELD( CFuncTankLaser, m_laserTime, FIELD_TIME ), +}; + +IMPLEMENT_SAVERESTORE( CFuncTankLaser, CFuncTank ); + +void CFuncTankLaser::Activate( void ) +{ + if ( !GetLaser() ) + { + UTIL_Remove(this); + ALERT( at_error, "Laser tank with no env_laser!\n" ); + } + else + { + m_pLaser->TurnOff(); + } +} + + +void CFuncTankLaser::KeyValue( KeyValueData *pkvd ) +{ + if (FStrEq(pkvd->szKeyName, "laserentity")) + { + pev->message = ALLOC_STRING(pkvd->szValue); + pkvd->fHandled = TRUE; + } + else + CFuncTank::KeyValue( pkvd ); +} + + +CLaser *CFuncTankLaser::GetLaser( void ) +{ + if ( m_pLaser ) + return m_pLaser; + + edict_t *pentLaser; + + pentLaser = FIND_ENTITY_BY_TARGETNAME( NULL, STRING(pev->message) ); + while ( !FNullEnt( pentLaser ) ) + { + // Found the landmark + if ( FClassnameIs( pentLaser, "env_laser" ) ) + { + m_pLaser = (CLaser *)CBaseEntity::Instance(pentLaser); + break; + } + else + pentLaser = FIND_ENTITY_BY_TARGETNAME( pentLaser, STRING(pev->message) ); + } + + return m_pLaser; +} + + +void CFuncTankLaser::Think( void ) +{ + if ( m_pLaser && (gpGlobals->time > m_laserTime) ) + m_pLaser->TurnOff(); + + CFuncTank::Think(); +} + + +void CFuncTankLaser::Fire( const Vector &barrelEnd, const Vector &forward, entvars_t *pevAttacker ) +{ + int i; + TraceResult tr; + + if ( m_fireLast != 0 && GetLaser() ) + { + // TankTrace needs gpGlobals->v_up, etc. + UTIL_MakeAimVectors(pev->angles); + + int bulletCount = (gpGlobals->time - m_fireLast) * m_fireRate; + if ( bulletCount ) + { + for ( i = 0; i < bulletCount; i++ ) + { + m_pLaser->pev->origin = barrelEnd; + TankTrace( barrelEnd, forward, gTankSpread[m_spread], tr ); + + m_laserTime = gpGlobals->time; + m_pLaser->TurnOn(); + m_pLaser->pev->dmgtime = gpGlobals->time - 1.0; + m_pLaser->FireAtPoint( tr ); + m_pLaser->pev->nextthink = 0; + } + CFuncTank::Fire( barrelEnd, forward, pev ); + } + } + else + { + CFuncTank::Fire( barrelEnd, forward, pev ); + } +} + +class CFuncTankRocket : public CFuncTank +{ +public: + void Precache( void ); + void Fire( const Vector &barrelEnd, const Vector &forward, entvars_t *pevAttacker ); +}; +LINK_ENTITY_TO_CLASS( func_tankrocket, CFuncTankRocket ); + +void CFuncTankRocket::Precache( void ) +{ + UTIL_PrecacheOther( "rpg_rocket" ); + CFuncTank::Precache(); +} + + + +void CFuncTankRocket::Fire( const Vector &barrelEnd, const Vector &forward, entvars_t *pevAttacker ) +{ + int i; + + if ( m_fireLast != 0 ) + { + int bulletCount = (gpGlobals->time - m_fireLast) * m_fireRate; + if ( bulletCount > 0 ) + { + for ( i = 0; i < bulletCount; i++ ) + { + CBaseEntity *pRocket = CBaseEntity::Create( "rpg_rocket", barrelEnd, pev->angles, edict() ); + } + CFuncTank::Fire( barrelEnd, forward, pev ); + } + } + else + CFuncTank::Fire( barrelEnd, forward, pev ); +} + + +class CFuncTankMortar : public CFuncTank +{ +public: + void KeyValue( KeyValueData *pkvd ); + void Fire( const Vector &barrelEnd, const Vector &forward, entvars_t *pevAttacker ); +}; +LINK_ENTITY_TO_CLASS( func_tankmortar, CFuncTankMortar ); + + +void CFuncTankMortar::KeyValue( KeyValueData *pkvd ) +{ + if (FStrEq(pkvd->szKeyName, "iMagnitude")) + { + pev->impulse = atoi( pkvd->szValue ); + pkvd->fHandled = TRUE; + } + else + CFuncTank::KeyValue( pkvd ); +} + + +void CFuncTankMortar::Fire( const Vector &barrelEnd, const Vector &forward, entvars_t *pevAttacker ) +{ + if ( m_fireLast != 0 ) + { + int bulletCount = (gpGlobals->time - m_fireLast) * m_fireRate; + // Only create 1 explosion + if ( bulletCount > 0 ) + { + TraceResult tr; + + // TankTrace needs gpGlobals->v_up, etc. + UTIL_MakeAimVectors(pev->angles); + + TankTrace( barrelEnd, forward, gTankSpread[m_spread], tr ); + + ExplosionCreate( tr.vecEndPos, pev->angles, edict(), pev->impulse, TRUE ); + + CFuncTank::Fire( barrelEnd, forward, pev ); + } + } + else + CFuncTank::Fire( barrelEnd, forward, pev ); +} + + + +//============================================================================ +// FUNC TANK CONTROLS +//============================================================================ +class CFuncTankControls : public CBaseEntity +{ +public: + virtual int ObjectCaps( void ); + void Spawn( void ); + void Use( CBaseEntity *pActivator, CBaseEntity *pCaller, USE_TYPE useType, float value ); + void Think( void ); + + virtual int Save( CSave &save ); + virtual int Restore( CRestore &restore ); + static TYPEDESCRIPTION m_SaveData[]; + + CFuncTank *m_pTank; +}; +LINK_ENTITY_TO_CLASS( func_tankcontrols, CFuncTankControls ); + +TYPEDESCRIPTION CFuncTankControls::m_SaveData[] = +{ + DEFINE_FIELD( CFuncTankControls, m_pTank, FIELD_CLASSPTR ), +}; + +IMPLEMENT_SAVERESTORE( CFuncTankControls, CBaseEntity ); + +int CFuncTankControls :: ObjectCaps( void ) +{ + return (CBaseEntity::ObjectCaps() & ~FCAP_ACROSS_TRANSITION) | FCAP_IMPULSE_USE; +} + + +void CFuncTankControls :: Use( CBaseEntity *pActivator, CBaseEntity *pCaller, USE_TYPE useType, float value ) +{ // pass the Use command onto the controls + if ( m_pTank ) + m_pTank->Use( pActivator, pCaller, useType, value ); + + ASSERT( m_pTank != NULL ); // if this fails, most likely means save/restore hasn't worked properly +} + + +void CFuncTankControls :: Think( void ) +{ + edict_t *pTarget = NULL; + + do + { + pTarget = FIND_ENTITY_BY_TARGETNAME( pTarget, STRING(pev->target) ); + } while ( !FNullEnt(pTarget) && strncmp( STRING(pTarget->v.classname), "func_tank", 9 ) ); + + if ( FNullEnt( pTarget ) ) + { + ALERT( at_console, "No tank %s\n", STRING(pev->target) ); + return; + } + + m_pTank = (CFuncTank*)Instance(pTarget); +} + +void CFuncTankControls::Spawn( void ) +{ + pev->solid = SOLID_TRIGGER; + pev->movetype = MOVETYPE_NONE; + pev->effects |= EF_NODRAW; + SET_MODEL( ENT(pev), STRING(pev->model) ); + + UTIL_SetSize( pev, pev->mins, pev->maxs ); + UTIL_SetOrigin( pev, pev->origin ); + + pev->nextthink = gpGlobals->time + 0.3; // After all the func_tank's have spawned + + CBaseEntity::Spawn(); +} diff --git a/spirit/game.cpp b/dlls/game.cpp similarity index 88% rename from spirit/game.cpp rename to dlls/game.cpp index 22c4cde7..3b4adbdf 100644 --- a/spirit/game.cpp +++ b/dlls/game.cpp @@ -13,20 +13,19 @@ * ****/ #include "extdll.h" +#include "eiface.h" #include "util.h" #include "game.h" // special macros for handle skill data #define CVAR_REGISTER_SKILL( x ) (*g_engfuncs.pfnCVarRegister)( #x, "0", 0, "skill config cvar" ) -// Engine Cvars -cvar_t *g_psv_gravity = NULL; -cvar_t *g_psv_aim = NULL; -cvar_t *g_psv_maxspeed = NULL; -cvar_t *g_footsteps = NULL; - cvar_t *displaysoundlist; +// multiplayer server rules +cvar_t *fragsleft; // Don't spam console/log files/users with this changing +cvar_t *timeleft; // " " + // multiplayer server rules cvar_t *teamplay; cvar_t *fraglimit; @@ -39,48 +38,53 @@ cvar_t *flashlight; cvar_t *aimcrosshair; cvar_t *decalfrequency; cvar_t *teamlist; -cvar_t *timeleft; cvar_t *teamoverride; cvar_t *defaultteam; cvar_t *allowmonsters; + cvar_t *mp_chattime; +// Engine Cvars +cvar_t *g_psv_gravity = NULL; +cvar_t *g_psv_aim = NULL; +cvar_t *g_footsteps = NULL; + // Register your console variables here // This gets called one time when the game is initialied void GameDLLInit( void ) { - // Register cvars here: - ALERT( at_aiconsole, "GameDLLInit();\n" ); - g_psv_maxspeed = CVAR_REGISTER( "sv_maxspeed", "320", 0, "maximum speed a player can accelerate to when on ground" ); - g_psv_gravity = CVAR_REGISTER( "sv_gravity", "800", 0, "world gravity" ); - g_psv_aim = CVAR_REGISTER( "sv_aim", "1", 0, "enable auto-aiming" ); - g_footsteps = CVAR_REGISTER( "mp_footsteps", "0", FCVAR_SERVERINFO|FCVAR_PHYSICINFO, "can hear footsteps from other players" ); +#ifndef SYS_SHAREDSTRINGS + // tell engine what we want use StringTable system instead of shared strings + gpGlobals->pStringBase = NULL; +#endif + // Register cvars here: + g_psv_gravity = CVAR_GET_POINTER( "sv_gravity" ); + g_psv_aim = CVAR_GET_POINTER( "sv_aim" ); + g_footsteps = CVAR_GET_POINTER( "mp_footsteps" ); displaysoundlist = CVAR_REGISTER( "displaysoundlist", "0", 0, "show monster sounds that actually playing" ); - teamplay = CVAR_REGISTER( "mp_teamplay", "0", FCVAR_SERVERINFO, "sets to 1 to indicate teamplay" ); - fraglimit = CVAR_REGISTER( "mp_fraglimit", "0", FCVAR_SERVERINFO, "limit of frags for current server" ); - timelimit = CVAR_REGISTER( "mp_timelimit", "0", FCVAR_SERVERINFO, "server timelimit" ); - CVAR_REGISTER( "mp_footsteps", "0", FCVAR_SERVERINFO, "can hear footsteps from other players" ); + teamplay = CVAR_REGISTER( "mp_teamplay", "0", FCVAR_SERVER, "sets to 1 to indicate teamplay" ); + fraglimit = CVAR_REGISTER( "mp_fraglimit", "0", FCVAR_SERVER, "limit of frags for current server" ); + timelimit = CVAR_REGISTER( "mp_timelimit", "0", FCVAR_SERVER, "server timelimit" ); - CVAR_REGISTER( "mp_fragsleft", "0", FCVAR_SERVERINFO, "counter that indicated how many frags remaining" ); - timeleft = CVAR_REGISTER( "mp_timeleft", "0" , FCVAR_SERVERINFO, "counter that indicated how many time remaining" ); + fragsleft = CVAR_REGISTER( "mp_fragsleft", "0", FCVAR_SERVER | FCVAR_UNLOGGED, "how many frags remaining" ); + timeleft = CVAR_REGISTER( "mp_timeleft", "0" , FCVAR_SERVER | FCVAR_UNLOGGED, "how many time remaining" ); - friendlyfire = CVAR_REGISTER( "mp_friendlyfire", "0", FCVAR_SERVERINFO, "enables firedlyfire for teamplay" ); - falldamage = CVAR_REGISTER( "mp_falldamage", "0", FCVAR_SERVERINFO, "falldamage multiplier" ); - weaponstay = CVAR_REGISTER( "mp_weaponstay", "0", FCVAR_SERVERINFO, "weapon leave stays on ground" ); - forcerespawn = CVAR_REGISTER( "mp_forcerespawn", "1", FCVAR_SERVERINFO, "force client respawn after his death" ); - flashlight = CVAR_REGISTER( "mp_flashlight", "0", FCVAR_SERVERINFO, "attempt to use flashlight in multiplayer" ); - aimcrosshair = CVAR_REGISTER( "mp_autocrosshair", "1", FCVAR_SERVERINFO, "enables auto-aim in multiplayer" ); - decalfrequency = CVAR_REGISTER( "decalfrequency", "30", FCVAR_SERVERINFO, "how many decals can be spawned" ); - teamlist = CVAR_REGISTER( "mp_teamlist", "hgrunt,scientist", FCVAR_SERVERINFO, "names of default teams" ); + friendlyfire = CVAR_REGISTER( "mp_friendlyfire", "0", FCVAR_SERVER, "enables firedlyfire for teamplay" ); + falldamage = CVAR_REGISTER( "mp_falldamage", "0", FCVAR_SERVER, "falldamage multiplier" ); + weaponstay = CVAR_REGISTER( "mp_weaponstay", "0", FCVAR_SERVER, "weapon leave stays on ground" ); + forcerespawn = CVAR_REGISTER( "mp_forcerespawn", "1", FCVAR_SERVER, "force client respawn after his death" ); + flashlight = CVAR_REGISTER( "mp_flashlight", "0", FCVAR_SERVER, "attempt to use flashlight in multiplayer" ); + aimcrosshair = CVAR_REGISTER( "mp_autocrosshair", "1", FCVAR_SERVER, "enables auto-aim in multiplayer" ); + decalfrequency = CVAR_REGISTER( "decalfrequency", "30", FCVAR_SERVER, "how many decals can be spawned" ); + teamlist = CVAR_REGISTER( "mp_teamlist", "hgrunt,scientist", FCVAR_SERVER, "names of default teams" ); teamoverride = CVAR_REGISTER( "mp_teamoverride", "1", 0, "can ovveride teams from map settings ?" ); defaultteam = CVAR_REGISTER( "mp_defaultteam", "0", 0, "use default team instead ?" ); - allowmonsters = CVAR_REGISTER( "mp_allowmonsters", "0", FCVAR_SERVERINFO, "allow monsters in multiplayer" ); - CVAR_REGISTER( "sohl_impulsetarget", "0", FCVAR_SERVERINFO, "trigger ents manually" ); //LRC - CVAR_REGISTER( "sohl_mwdebug", "0", FCVAR_SERVERINFO, "debug info. for MoveWith" ); //LRC - mp_chattime = CVAR_REGISTER( "mp_chattime", "10", FCVAR_SERVERINFO, "time beetween messages" ); + allowmonsters = CVAR_REGISTER( "mp_allowmonsters", "0", FCVAR_SERVER, "allow monsters in multiplayer" ); + + mp_chattime = CVAR_REGISTER( "mp_chattime", "10", FCVAR_SERVER, "time beetween messages" ); // REGISTER CVARS FOR SKILL LEVEL STUFF // Agrunt @@ -439,10 +443,7 @@ void GameDLLInit( void ) CVAR_REGISTER_SKILL ( sk_scientist_heal1 ); CVAR_REGISTER_SKILL ( sk_scientist_heal2 ); CVAR_REGISTER_SKILL ( sk_scientist_heal3 ); - - CVAR_REGISTER_SKILL ( sk_flashcharge1 ); - CVAR_REGISTER_SKILL ( sk_flashcharge2 ); - CVAR_REGISTER_SKILL ( sk_flashcharge3 ); + // monster damage adjusters CVAR_REGISTER_SKILL ( sk_monster_head1 ); diff --git a/spirit/game.h b/dlls/game.h similarity index 89% rename from spirit/game.h rename to dlls/game.h index 440f7e02..9fd17faf 100644 --- a/spirit/game.h +++ b/dlls/game.h @@ -17,9 +17,7 @@ #define GAME_H extern void GameDLLInit( void ); -extern void GameDLLShutdown( void ); -#include "cvardef.h" extern cvar_t *displaysoundlist; @@ -43,6 +41,5 @@ extern cvar_t *allowmonsters; extern cvar_t *g_psv_gravity; extern cvar_t *g_psv_aim; extern cvar_t *g_footsteps; -extern cvar_t *g_psv_maxspeed; #endif // GAME_H diff --git a/spirit/gamerules.cpp b/dlls/gamerules.cpp similarity index 94% rename from spirit/gamerules.cpp rename to dlls/gamerules.cpp index 17db0fa3..53c496b5 100644 --- a/spirit/gamerules.cpp +++ b/dlls/gamerules.cpp @@ -71,12 +71,6 @@ edict_t *CGameRules :: GetPlayerSpawnSpot( CBasePlayer *pPlayer ) pPlayer->pev->punchangle = g_vecZero; pPlayer->pev->fixangle = TRUE; - //LRC - if (pentSpawnSpot->v.spawnflags & 1) // the START WITH SUIT flag - { - g_startSuit = TRUE; - } - return pentSpawnSpot; } @@ -134,7 +128,7 @@ void CGameRules::RefreshSkillData ( void ) gSkillData.iSkillLevel = iSkill; - ALERT ( at_debug, "\nGAME SKILL LEVEL:%d\n",iSkill ); + ALERT ( at_console, "\nGAME SKILL LEVEL:%d\n",iSkill ); //Agrunt gSkillData.agruntHealth = GetSkillCvar( "sk_agrunt_health" ); @@ -287,14 +281,14 @@ void CGameRules::RefreshSkillData ( void ) // via SKILLS.CFG. Any player hivehand tuning must take place in the code. (sjb) gSkillData.plrDmgHornet = 7; + // HEALTH/CHARGE gSkillData.suitchargerCapacity = GetSkillCvar( "sk_suitcharger" ); gSkillData.batteryCapacity = GetSkillCvar( "sk_battery" ); gSkillData.healthchargerCapacity = GetSkillCvar ( "sk_healthcharger" ); gSkillData.healthkitCapacity = GetSkillCvar ( "sk_healthkit" ); gSkillData.scientistHeal = GetSkillCvar ( "sk_scientist_heal" ); - gSkillData.flashlightCharge = GetSkillCvar ( "sk_flashcharge" ); - + // monster damage adj gSkillData.monHead = GetSkillCvar( "sk_monster_head" ); gSkillData.monChest = GetSkillCvar( "sk_monster_chest" ); @@ -317,7 +311,7 @@ void CGameRules::RefreshSkillData ( void ) CGameRules *InstallGameRules( void ) { SERVER_COMMAND( "exec game.cfg\n" ); - g_engfuncs.pfnServerExecute(); // stupid fucking winspool.h AGRHHH!!!! + SERVER_EXECUTE( ); if ( !gpGlobals->deathmatch ) { diff --git a/spirit/gamerules.h b/dlls/gamerules.h similarity index 95% rename from spirit/gamerules.h rename to dlls/gamerules.h index c542d21d..2e853045 100644 --- a/spirit/gamerules.h +++ b/dlls/gamerules.h @@ -16,9 +16,6 @@ // GameRules //========================================================= -//LRC -#define GAME_NAME "SoHL:CB 1.6 final" - //#include "weapons.h" //#include "items.h" class CBasePlayerItem; @@ -75,10 +72,10 @@ public: virtual BOOL IsDeathmatch( void ) = 0;//is this a deathmatch game? virtual BOOL IsTeamplay( void ) { return FALSE; };// is this deathmatch game being played with team rules? virtual BOOL IsCoOp( void ) = 0;// is this a coop game? - virtual const char *GetGameDescription( void ) { return GAME_NAME; } // this is the game name that gets seen in the server browser + virtual const char *GetGameDescription( void ) { return "Half-Life"; } // this is the game name that gets seen in the server browser // Client connection/disconnection - virtual BOOL ClientConnected( edict_t *pEntity, const char *pszName, const char *pszAddress, char szRejectReason[128] ) = 0;// a client just connected to the server (player hasn't spawned yet) + virtual BOOL ClientConnected( edict_t *pEntity, const char *pszName, const char *pszAddress, char szRejectReason[ 128 ] ) = 0;// a client just connected to the server (player hasn't spawned yet) virtual void InitHUD( CBasePlayer *pl ) = 0; // the client dll is ready for updating virtual void ClientDisconnected( edict_t *pClient ) = 0;// a client just disconnected from the server virtual void UpdateGameMode( CBasePlayer *pPlayer ) {} // the client needs to be informed of the current game mode @@ -188,7 +185,7 @@ public: virtual BOOL IsCoOp( void ); // Client connection/disconnection - virtual BOOL ClientConnected( edict_t *pEntity, const char *pszName, const char *pszAddress, char szRejectReason[128] ); + virtual BOOL ClientConnected( edict_t *pEntity, const char *pszName, const char *pszAddress, char szRejectReason[ 128 ] ); virtual void InitHUD( CBasePlayer *pl ); // the client dll is ready for updating virtual void ClientDisconnected( edict_t *pClient ); @@ -278,7 +275,7 @@ public: // If ClientConnected returns FALSE, the connection is rejected and the user is provided the reason specified in // svRejectReason // Only the client's name and remote address are provided to the dll for verification. - virtual BOOL ClientConnected( edict_t *pEntity, const char *pszName, const char *pszAddress, char szRejectReason[128] ); + virtual BOOL ClientConnected( edict_t *pEntity, const char *pszName, const char *pszAddress, char szRejectReason[ 128 ] ); virtual void InitHUD( CBasePlayer *pl ); // the client dll is ready for updating virtual void ClientDisconnected( edict_t *pClient ); virtual void UpdateGameMode( CBasePlayer *pPlayer ); // the client needs to be informed of the current game mode diff --git a/spirit/gargantua.cpp b/dlls/gargantua.cpp similarity index 89% rename from spirit/gargantua.cpp rename to dlls/gargantua.cpp index 5fc9d9c5..1c23d438 100644 --- a/spirit/gargantua.cpp +++ b/dlls/gargantua.cpp @@ -23,13 +23,13 @@ #include "nodes.h" #include "monsters.h" #include "schedule.h" +#include "customentity.h" #include "weapons.h" #include "effects.h" #include "soundent.h" #include "decals.h" #include "explode.h" #include "func_break.h" -#include "scripted.h" //========================================================= // Gargantua Monster @@ -105,7 +105,7 @@ CStomp *CStomp::StompCreate( const Vector &origin, const Vector &end, float spee void CStomp::Spawn( void ) { - SetNextThink( 0 ); + pev->nextthink = gpGlobals->time; pev->classname = MAKE_STRING("garg_stomp"); pev->dmgtime = gpGlobals->time; @@ -123,7 +123,7 @@ void CStomp::Think( void ) { TraceResult tr; - SetNextThink( 0.1 ); + pev->nextthink = gpGlobals->time + 0.1; // Do damage for this frame Vector vecStart = pev->origin; @@ -160,9 +160,9 @@ void CStomp::Think( void ) pSprite->pev->origin = tr.vecEndPos; pSprite->pev->velocity = Vector(RANDOM_FLOAT(-200,200),RANDOM_FLOAT(-200,200),175); // pSprite->AnimateAndDie( RANDOM_FLOAT( 8.0, 12.0 ) ); - pSprite->SetNextThink( 0.3 ); - pSprite->SetThink(&CSprite:: SUB_Remove ); - pSprite->SetTransparency( kRenderTransAdd, 255, 255, 255, 255, kRenderFxNone ); // ehm. Xash3D doesn't have kRenderFxFadeFast + pSprite->pev->nextthink = gpGlobals->time + 0.3; + pSprite->SetThink( SUB_Remove ); + pSprite->SetTransparency( kRenderTransAdd, 255, 255, 255, 255, kRenderFxFadeFast ); } } pev->dmgtime += STOMP_INTERVAL; @@ -181,7 +181,7 @@ void CStomp::Think( void ) void StreakSplash( const Vector &origin, const Vector &direction, int color, int count, int speed, int velocityRange ) { - MESSAGE_BEGIN( MSG_PVS, gmsgTempEntity, origin ); + MESSAGE_BEGIN( MSG_PVS, SVC_TEMPENTITY, origin ); WRITE_BYTE( TE_STREAK_SPLASH ); WRITE_COORD( origin.x ); // origin WRITE_COORD( origin.y ); @@ -464,7 +464,7 @@ void CGargantua::EyeUpdate( void ) m_pEyeGlow->pev->effects |= EF_NODRAW; else m_pEyeGlow->pev->effects &= ~EF_NODRAW; - UTIL_SetOrigin( m_pEyeGlow, pev->origin ); + UTIL_SetOrigin( m_pEyeGlow->pev, pev->origin ); } } @@ -512,13 +512,13 @@ void CGargantua :: FlameCreate( void ) Vector vecEnd = (gpGlobals->v_forward * GARG_FLAME_LENGTH) + posGun; UTIL_TraceLine( posGun, vecEnd, dont_ignore_monsters, edict(), &trace ); - m_pFlame[i]->PointEntInit( trace.vecEndPos, edict() ); + m_pFlame[i]->PointEntInit( trace.vecEndPos, entindex() ); if ( i < 2 ) m_pFlame[i]->SetColor( 255, 130, 90 ); else m_pFlame[i]->SetColor( 0, 120, 255 ); m_pFlame[i]->SetBrightness( 190 ); - m_pFlame[i]->SetFlags( FBEAM_SHADEIN ); + m_pFlame[i]->SetFlags( BEAM_FSHADEIN ); m_pFlame[i]->SetScrollRate( 20 ); // attachment is 1 based in SetEndAttachment m_pFlame[i]->SetEndAttachment( attach + 2 ); @@ -579,15 +579,12 @@ void CGargantua :: FlameUpdate( void ) { StreakSplash( trace.vecEndPos, trace.vecPlaneNormal, 6, 20, 50, 400 ); streaks = TRUE; - - CBaseEntity *pHit = CBaseEntity::Instance( trace.pHit ); - PLAYBACK_EVENT_FULL( FEV_RELIABLE|FEV_GLOBAL, edict(), m_usDecals, 0.0, (float *)&trace.vecEndPos, (float *)&g_vecZero, 0.0, 0.0, pHit->entindex(), 5, 0, 0 ); - //UTIL_DecalTrace( &trace, DECAL_SMALLSCORCH1 + RANDOM_LONG(0,2) ); + UTIL_DecalTrace( &trace, DECAL_SMALLSCORCH1 + RANDOM_LONG(0,2) ); } // RadiusDamage( trace.vecEndPos, pev, pev, gSkillData.gargantuaDmgFire, CLASS_ALIEN_MONSTER, DMG_BURN ); FlameDamage( vecStart, trace.vecEndPos, pev, pev, gSkillData.gargantuaDmgFire, CLASS_ALIEN_MONSTER, DMG_BURN ); - MESSAGE_BEGIN( MSG_BROADCAST, gmsgTempEntity ); + MESSAGE_BEGIN( MSG_BROADCAST, SVC_TEMPENTITY ); WRITE_BYTE( TE_ELIGHT ); WRITE_SHORT( entindex( ) + 0x1000 * (i + 2) ); // entity, attachment WRITE_COORD( vecStart.x ); // origin @@ -713,7 +710,7 @@ void CGargantua :: PrescheduleThink( void ) //========================================================= int CGargantua :: Classify ( void ) { - return m_iClass?m_iClass:CLASS_ALIEN_MONSTER; + return CLASS_ALIEN_MONSTER; } //========================================================= @@ -754,17 +751,13 @@ void CGargantua :: Spawn() { Precache( ); - if (pev->model) - SET_MODEL(ENT(pev), STRING(pev->model)); //LRC - else - SET_MODEL(ENT(pev), "models/garg.mdl"); + SET_MODEL(ENT(pev), "models/garg.mdl"); UTIL_SetSize( pev, Vector( -32, -32, 0 ), Vector( 32, 32, 64 ) ); pev->solid = SOLID_SLIDEBOX; pev->movetype = MOVETYPE_STEP; m_bloodColor = BLOOD_COLOR_GREEN; - if (pev->health == 0) - pev->health = gSkillData.gargantuaHealth; + pev->health = gSkillData.gargantuaHealth; //pev->view_ofs = Vector ( 0, 0, 96 );// taken from mdl file m_flFieldOfView = -0.2;// width of forward view cone ( as a dotproduct result ) m_MonsterState = MONSTERSTATE_NONE; @@ -787,10 +780,7 @@ void CGargantua :: Precache() { int i; - if (pev->model) - PRECACHE_MODEL((char*)STRING(pev->model)); //LRC - else - PRECACHE_MODEL("models/garg.mdl"); + PRECACHE_MODEL("models/garg.mdl"); PRECACHE_MODEL( GARG_EYE_SPRITE_NAME ); PRECACHE_MODEL( GARG_BEAM_SPRITE_NAME ); PRECACHE_MODEL( GARG_BEAM_SPRITE2 ); @@ -910,7 +900,7 @@ void CGargantua::DeathEffect( void ) pSmoker->pev->health = 1; // 1 smoke balls pSmoker->pev->scale = 46; // 4.6X normal size pSmoker->pev->dmg = 0; // 0 radial distribution - pSmoker->SetNextThink( 2.5 ); // Start in 2.5 seconds + pSmoker->pev->nextthink = gpGlobals->time + 2.5; // Start in 2.5 seconds } @@ -1025,8 +1015,7 @@ void CGargantua::HandleAnimEvent(MonsterEvent_t *pEvent) break; case GARG_AE_BREATHE: - if ( !(pev->spawnflags & SF_MONSTER_GAG) || m_MonsterState != MONSTERSTATE_IDLE) - EMIT_SOUND_DYN ( edict(), CHAN_VOICE, pBreatheSounds[ RANDOM_LONG(0,ARRAYSIZE(pBreatheSounds)-1) ], 1.0, ATTN_GARG, 0, PITCH_NORM + RANDOM_LONG(-10,10) ); + EMIT_SOUND_DYN ( edict(), CHAN_VOICE, pBreatheSounds[ RANDOM_LONG(0,ARRAYSIZE(pBreatheSounds)-1) ], 1.0, ATTN_GARG, 0, PITCH_NORM + RANDOM_LONG(-10,10) ); break; default: @@ -1111,20 +1100,6 @@ void CGargantua::StartTask( Task_t *pTask ) TaskComplete(); break; - // allow a scripted_action to make gargantua shoot flames. - case TASK_PLAY_SCRIPT: - if ( m_pCine->IsAction() && m_pCine->m_fAction == 3) - { - FlameCreate(); - m_flWaitFinished = gpGlobals->time + 4.5; - m_flameTime = gpGlobals->time + 6; - m_flameX = 0; - m_flameY = 0; - } - else - CBaseMonster::StartTask( pTask ); - break; - case TASK_DIE: m_flWaitFinished = gpGlobals->time + 1.6; DeathEffect(); @@ -1150,8 +1125,8 @@ void CGargantua::RunTask( Task_t *pTask ) pev->rendercolor.y = 0; pev->rendercolor.z = 0; StopAnimation(); - SetNextThink( 0.15 ); - SetThink(&CGargantua:: SUB_Remove ); + pev->nextthink = gpGlobals->time + 0.15; + SetThink( SUB_Remove ); int i; int parts = MODEL_FRAMES( gGargGibModel ); for ( i = 0; i < 10; i++ ) @@ -1169,10 +1144,10 @@ void CGargantua::RunTask( Task_t *pTask ) pGib->m_material = matNone; pGib->pev->origin = pev->origin; pGib->pev->velocity = UTIL_RandomBloodVector() * RANDOM_FLOAT( 300, 500 ); - pGib->SetNextThink( 1.25 ); - pGib->SetThink(&CGib:: SUB_FadeOut ); + pGib->pev->nextthink = gpGlobals->time + 1.25; + pGib->SetThink( SUB_FadeOut ); } - MESSAGE_BEGIN( MSG_PVS, gmsgTempEntity, pev->origin ); + MESSAGE_BEGIN( MSG_PVS, SVC_TEMPENTITY, pev->origin ); WRITE_BYTE( TE_BREAKMODEL); // position @@ -1213,24 +1188,6 @@ void CGargantua::RunTask( Task_t *pTask ) CBaseMonster::RunTask(pTask); break; - case TASK_PLAY_SCRIPT: - if (m_pCine->IsAction() && m_pCine->m_fAction == 3) - { - if (m_fSequenceFinished) - { - FlameDestroy(); - FlameControls( 0, 0 ); - SetBoneController( 0, 0 ); - SetBoneController( 1, 0 ); - m_pCine->SequenceDone( this ); - break; - } - } - else - { - CBaseMonster::RunTask( pTask ); - break; - } case TASK_FLAME_SWEEP: if ( gpGlobals->time > m_flWaitFinished ) { @@ -1247,11 +1204,7 @@ void CGargantua::RunTask( Task_t *pTask ) Vector angles = g_vecZero; FlameUpdate(); - CBaseEntity *pEnemy; - if (m_pCine) // LRC- are we obeying a scripted_action? - pEnemy = m_hTargetEnt; - else - pEnemy = m_hEnemy; + CBaseEntity *pEnemy = m_hEnemy; if ( pEnemy ) { Vector org = pev->origin; @@ -1295,7 +1248,7 @@ LINK_ENTITY_TO_CLASS( env_smoker, CSmoker ); void CSmoker::Spawn( void ) { pev->movetype = MOVETYPE_NONE; - SetNextThink( 0 ); + pev->nextthink = gpGlobals->time; pev->solid = SOLID_NOT; UTIL_SetSize(pev, g_vecZero, g_vecZero ); pev->effects |= EF_NODRAW; @@ -1306,7 +1259,7 @@ void CSmoker::Spawn( void ) void CSmoker::Think( void ) { // lots of smoke - MESSAGE_BEGIN( MSG_PVS, gmsgTempEntity, pev->origin ); + MESSAGE_BEGIN( MSG_PVS, SVC_TEMPENTITY, pev->origin ); WRITE_BYTE( TE_SMOKE ); WRITE_COORD( pev->origin.x + RANDOM_FLOAT( -pev->dmg, pev->dmg )); WRITE_COORD( pev->origin.y + RANDOM_FLOAT( -pev->dmg, pev->dmg )); @@ -1318,7 +1271,7 @@ void CSmoker::Think( void ) pev->health--; if ( pev->health > 0 ) - SetNextThink( RANDOM_FLOAT(0.1, 0.2) ); + pev->nextthink = gpGlobals->time + RANDOM_FLOAT(0.1, 0.2); else UTIL_Remove( this ); } @@ -1327,7 +1280,7 @@ void CSmoker::Think( void ) void CSpiral::Spawn( void ) { pev->movetype = MOVETYPE_NONE; - SetNextThink( 0 ); + pev->nextthink = gpGlobals->time; pev->solid = SOLID_NOT; UTIL_SetSize(pev, g_vecZero, g_vecZero ); pev->effects |= EF_NODRAW; @@ -1342,7 +1295,7 @@ CSpiral *CSpiral::Create( const Vector &origin, float height, float radius, floa CSpiral *pSpiral = GetClassPtr( (CSpiral *)NULL ); pSpiral->Spawn(); - pSpiral->pev->dmgtime = pSpiral->m_fNextThink; + pSpiral->pev->dmgtime = pSpiral->pev->nextthink; pSpiral->pev->origin = origin; pSpiral->pev->scale = radius; pSpiral->pev->dmg = height; @@ -1382,7 +1335,7 @@ void CSpiral::Think( void ) time -= SPIRAL_INTERVAL; } - SetNextThink( 0 ); + pev->nextthink = gpGlobals->time; if ( pev->health >= pev->speed ) UTIL_Remove( this ); @@ -1406,10 +1359,10 @@ void SpawnExplosion( Vector center, float randomRange, float time, int magnitude pExplosion->pev->spawnflags |= SF_ENVEXPLOSION_NODAMAGE; pExplosion->Spawn(); - pExplosion->SetThink(& CBaseEntity::SUB_CallUseToggle ); - pExplosion->SetNextThink( time ); + pExplosion->SetThink( CBaseEntity::SUB_CallUseToggle ); + pExplosion->pev->nextthink = gpGlobals->time + time; } -#endif +#endif \ No newline at end of file diff --git a/spirit/gauss.cpp b/dlls/gauss.cpp similarity index 50% rename from spirit/gauss.cpp rename to dlls/gauss.cpp index d33fee48..d89a41cf 100644 --- a/spirit/gauss.cpp +++ b/dlls/gauss.cpp @@ -12,6 +12,7 @@ * without written permission from Valve LLC. * ****/ +#if !defined( OEM_BUILD ) && !defined( HLDEMO_BUILD ) #include "extdll.h" #include "util.h" @@ -25,8 +26,8 @@ #include "gamerules.h" -#define PRIMARY_CHARGE_VOLUME 256// how loud gauss is while charging -#define PRIMARY_FIRE_VOLUME 450// how loud gauss is when discharged +#define GAUSS_PRIMARY_CHARGE_VOLUME 256// how loud gauss is while charging +#define GAUSS_PRIMARY_FIRE_VOLUME 450// how loud gauss is when discharged enum gauss_e { GAUSS_IDLE = 0, @@ -40,58 +41,70 @@ enum gauss_e { GAUSS_DRAW }; -class CGauss : public CBasePlayerWeapon -{ -public: - void Spawn( void ); - void Precache( void ); - int GetItemInfo(ItemInfo *p); - - BOOL Deploy( void ); - void Holster( ); - - void PrimaryAttack( void ); - void SecondaryAttack( void ); - void WeaponIdle( void ); - - void StartFire( void ); - void Fire( Vector vecOrigSrc, Vector vecDirShooting, float flDamage ); - int m_iSoundState; -private: - unsigned short m_usGaussFire; - unsigned short m_usGaussSpin; -}; LINK_ENTITY_TO_CLASS( weapon_gauss, CGauss ); +float CGauss::GetFullChargeTime( void ) +{ +#ifdef CLIENT_DLL + if ( bIsMultiplayer() ) +#else + if ( g_pGameRules->IsMultiplayer() ) +#endif + { + return 1.5; + } + + return 4; +} + +#ifdef CLIENT_DLL +extern int g_irunninggausspred; +#endif void CGauss::Spawn( ) { Precache( ); m_iId = WEAPON_GAUSS; SET_MODEL(ENT(pev), "models/w_gauss.mdl"); + m_iDefaultAmmo = GAUSS_DEFAULT_GIVE; FallInit();// get ready to fall down. } + void CGauss::Precache( void ) { PRECACHE_MODEL("models/w_gauss.mdl"); PRECACHE_MODEL("models/v_gauss.mdl"); PRECACHE_MODEL("models/p_gauss.mdl"); + PRECACHE_SOUND("items/9mmclip1.wav"); + + PRECACHE_SOUND("weapons/gauss2.wav"); PRECACHE_SOUND("weapons/electro4.wav"); PRECACHE_SOUND("weapons/electro5.wav"); PRECACHE_SOUND("weapons/electro6.wav"); + PRECACHE_SOUND("ambience/pulsemachine.wav"); - //precached client resourses - int m_iGlow = PRECACHE_MODEL( "sprites/hotglow.spr" ); - int m_iBalls = PRECACHE_MODEL( "sprites/hotglow.spr" ); - int m_iBeam = PRECACHE_MODEL( "sprites/smoke.spr" ); + m_iGlow = PRECACHE_MODEL( "sprites/hotglow.spr" ); + m_iBalls = PRECACHE_MODEL( "sprites/hotglow.spr" ); + m_iBeam = PRECACHE_MODEL( "sprites/smoke.spr" ); - m_usGaussFire = PRECACHE_EVENT( 1, "evGauss" ); - m_usGaussSpin = PRECACHE_EVENT( 1, "evGaussSpin" ); - m_flChargeTime = UTIL_WeaponTimeBase() - 10;//instant zap after save\load + m_usGaussFire = PRECACHE_EVENT( 1, "events/gauss.sc" ); + m_usGaussSpin = PRECACHE_EVENT( 1, "events/gaussspin.sc" ); +} + +int CGauss::AddToPlayer( CBasePlayer *pPlayer ) +{ + if ( CBasePlayerWeapon::AddToPlayer( pPlayer ) ) + { + MESSAGE_BEGIN( MSG_ONE, gmsgWeapPickup, NULL, pPlayer->pev ); + WRITE_BYTE( m_iId ); + MESSAGE_END(); + return TRUE; + } + return FALSE; } int CGauss::GetItemInfo(ItemInfo *p) @@ -113,41 +126,47 @@ int CGauss::GetItemInfo(ItemInfo *p) BOOL CGauss::Deploy( ) { - AnimRestore = TRUE; - m_flShockTime = 0; - return DefaultDeploy( "models/v_gauss.mdl", "models/p_gauss.mdl", GAUSS_DRAW, "gauss", 0.6 ); + m_pPlayer->m_flPlayAftershock = 0.0; + return DefaultDeploy( "models/v_gauss.mdl", "models/p_gauss.mdl", GAUSS_DRAW, "gauss" ); } -void CGauss::Holster( ) +void CGauss::Holster( int skiplocal /* = 0 */ ) { + PLAYBACK_EVENT_FULL( FEV_RELIABLE | FEV_GLOBAL, m_pPlayer->edict(), m_usGaussFire, 0.01, (float *)&m_pPlayer->pev->origin, (float *)&m_pPlayer->pev->angles, 0.0, 0.0, 0, 0, 0, 1 ); + m_pPlayer->m_flNextAttack = UTIL_WeaponTimeBase() + 0.5; + SendWeaponAnim( GAUSS_HOLSTER ); m_fInAttack = 0; - m_iChargeLevel = 0; - EMIT_SOUND(ENT(m_pPlayer->pev), CHAN_WEAPON, "common/null.wav", 1.0, ATTN_NORM); } void CGauss::PrimaryAttack() { // don't fire underwater - if ( m_pPlayer->pev->waterlevel != 3 && m_pPlayer->m_rgAmmo[ m_iPrimaryAmmoType ] > 2) + if ( m_pPlayer->pev->waterlevel == 3 ) { - m_pPlayer->m_iWeaponVolume = PRIMARY_FIRE_VOLUME; - pev->frags = TRUE;//set primary fire - - m_pPlayer->m_rgAmmo[m_iPrimaryAmmoType] -= 2; - - StartFire(); - m_flTimeWeaponIdle = UTIL_WeaponTimeBase() + 1.0; - m_pPlayer->m_flNextAttack = UTIL_WeaponTimeBase() + 0.2; + PlayEmptySound( ); + m_flNextSecondaryAttack = m_flNextPrimaryAttack = UTIL_WeaponTimeBase() + 0.15; + return; } - else + + if ( m_pPlayer->m_rgAmmo[ m_iPrimaryAmmoType ] < 2 ) { - PlayEmptySound( 1 ); + PlayEmptySound( ); m_pPlayer->m_flNextAttack = UTIL_WeaponTimeBase() + 0.5; return; } + + m_pPlayer->m_iWeaponVolume = GAUSS_PRIMARY_FIRE_VOLUME; + m_fPrimaryFire = TRUE; + + m_pPlayer->m_rgAmmo[m_iPrimaryAmmoType] -= 2; + + StartFire(); + m_fInAttack = 0; + m_flTimeWeaponIdle = UTIL_WeaponTimeBase() + 1.0; + m_pPlayer->m_flNextAttack = UTIL_WeaponTimeBase() + 0.2; } void CGauss::SecondaryAttack() @@ -160,9 +179,11 @@ void CGauss::SecondaryAttack() EMIT_SOUND_DYN(ENT(m_pPlayer->pev), CHAN_WEAPON, "weapons/electro4.wav", 1.0, ATTN_NORM, 0, 80 + RANDOM_LONG(0,0x3f)); SendWeaponAnim( GAUSS_IDLE ); m_fInAttack = 0; - m_iChargeLevel = 0; } - else PlayEmptySound( 1 ); + else + { + PlayEmptySound( ); + } m_flNextSecondaryAttack = m_flNextPrimaryAttack = UTIL_WeaponTimeBase() + 0.5; return; @@ -172,23 +193,27 @@ void CGauss::SecondaryAttack() { if ( m_pPlayer->m_rgAmmo[m_iPrimaryAmmoType] <= 0 ) { - PlayEmptySound( 1 ); + EMIT_SOUND(ENT(m_pPlayer->pev), CHAN_WEAPON, "weapons/357_cock1.wav", 0.8, ATTN_NORM); m_pPlayer->m_flNextAttack = UTIL_WeaponTimeBase() + 0.5; return; } - pev->frags = FALSE;//set secondary attack + m_fPrimaryFire = FALSE; + m_pPlayer->m_rgAmmo[m_iPrimaryAmmoType]--;// take one ammo just to start the spin - m_iChargeLevel++; + m_pPlayer->m_flNextAmmoBurn = UTIL_WeaponTimeBase(); // spin up - m_pPlayer->m_iWeaponVolume = PRIMARY_CHARGE_VOLUME; + m_pPlayer->m_iWeaponVolume = GAUSS_PRIMARY_CHARGE_VOLUME; SendWeaponAnim( GAUSS_SPINUP ); m_fInAttack = 1; m_flTimeWeaponIdle = UTIL_WeaponTimeBase() + 0.5; - m_flChargeTime = UTIL_WeaponTimeBase(); - PLAYBACK_EVENT_FULL( 0, m_pPlayer->edict(), m_usGaussSpin, 0.0, (float *)&g_vecZero, (float *)&g_vecZero, 0.0, 0.0, 110, 0, 0, 0 ); + m_pPlayer->m_flStartCharge = gpGlobals->time; + m_pPlayer->m_flAmmoStartCharge = UTIL_WeaponTimeBase() + GetFullChargeTime(); + + PLAYBACK_EVENT_FULL( FEV_NOTHOST, m_pPlayer->edict(), m_usGaussSpin, 0.0, (float *)&g_vecZero, (float *)&g_vecZero, 0.0, 0.0, 110, 0, 0, 0 ); + m_iSoundState = SND_CHANGE_PITCH; } else if (m_fInAttack == 1) @@ -199,67 +224,79 @@ void CGauss::SecondaryAttack() m_fInAttack = 2; } } - else if (m_fInAttack == 2) + else { + // during the charging process, eat one bit of ammo every once in a while + if ( UTIL_WeaponTimeBase() >= m_pPlayer->m_flNextAmmoBurn && m_pPlayer->m_flNextAmmoBurn != 1000 ) + { +#ifdef CLIENT_DLL + if ( bIsMultiplayer() ) +#else + if ( g_pGameRules->IsMultiplayer() ) +#endif + { + m_pPlayer->m_rgAmmo[m_iPrimaryAmmoType]--; + m_pPlayer->m_flNextAmmoBurn = UTIL_WeaponTimeBase() + 0.1; + } + else + { + m_pPlayer->m_rgAmmo[m_iPrimaryAmmoType]--; + m_pPlayer->m_flNextAmmoBurn = UTIL_WeaponTimeBase() + 0.3; + } + } + if ( m_pPlayer->m_rgAmmo[m_iPrimaryAmmoType] <= 0 ) { // out of ammo! force the gun to fire StartFire(); + m_fInAttack = 0; m_flTimeWeaponIdle = UTIL_WeaponTimeBase() + 1.0; m_pPlayer->m_flNextAttack = UTIL_WeaponTimeBase() + 1; return; } - - if ( m_flTimeUpdate < UTIL_WeaponTimeBase()) + + if ( UTIL_WeaponTimeBase() >= m_pPlayer->m_flAmmoStartCharge ) { - if(m_iChargeLevel != 16) - { - m_iChargeLevel++; - m_pPlayer->m_rgAmmo[m_iPrimaryAmmoType]--; - if ( IsMultiplayer() ) - { - m_flTimeUpdate = UTIL_WeaponTimeBase() + 0.05; - } - else - { - m_flTimeUpdate = UTIL_WeaponTimeBase() + 0.15; - } - int pitch = 95 + 10 * m_iChargeLevel; - PLAYBACK_EVENT_FULL( 0, m_pPlayer->edict(), m_usGaussSpin, 0.0, (float *)&g_vecZero, (float *)&g_vecZero, 0.0, 0.0, pitch, 0, ( m_iSoundState == SND_CHANGE_PITCH ) ? 1 : 0, 0 ); - } - if(m_iChargeLevel == 16)//end of charge - { - m_fInAttack = 3;//unused value - } + // don't eat any more ammo after gun is fully charged. + m_pPlayer->m_flNextAmmoBurn = 1000; + } + + int pitch = ( gpGlobals->time - m_pPlayer->m_flStartCharge ) * ( 150 / GetFullChargeTime() ) + 100; + if ( pitch > 250 ) + pitch = 250; + + // ALERT( at_console, "%d %d %d\n", m_fInAttack, m_iSoundState, pitch ); + + if ( m_iSoundState == 0 ) + ALERT( at_console, "sound state %d\n", m_iSoundState ); + + PLAYBACK_EVENT_FULL( FEV_NOTHOST, m_pPlayer->edict(), m_usGaussSpin, 0.0, (float *)&g_vecZero, (float *)&g_vecZero, 0.0, 0.0, pitch, 0, ( m_iSoundState == SND_CHANGE_PITCH ) ? 1 : 0, 0 ); + + m_iSoundState = SND_CHANGE_PITCH; // hack for going through level transitions + + m_pPlayer->m_iWeaponVolume = GAUSS_PRIMARY_CHARGE_VOLUME; + + // m_flTimeWeaponIdle = UTIL_WeaponTimeBase() + 0.1; + if ( m_pPlayer->m_flStartCharge < gpGlobals->time - 10 ) + { + // Player charged up too long. Zap him. + EMIT_SOUND_DYN(ENT(m_pPlayer->pev), CHAN_WEAPON, "weapons/electro4.wav", 1.0, ATTN_NORM, 0, 80 + RANDOM_LONG(0,0x3f)); + EMIT_SOUND_DYN(ENT(m_pPlayer->pev), CHAN_ITEM, "weapons/electro6.wav", 1.0, ATTN_NORM, 0, 75 + RANDOM_LONG(0,0x3f)); + + m_fInAttack = 0; + m_flTimeWeaponIdle = UTIL_WeaponTimeBase() + 1.0; + m_pPlayer->m_flNextAttack = UTIL_WeaponTimeBase() + 1.0; + +#ifndef CLIENT_DLL + m_pPlayer->TakeDamage( VARS(eoNullEntity), VARS(eoNullEntity), 50, DMG_SHOCK ); + UTIL_ScreenFade( m_pPlayer, Vector(255,128,0), 2, 0.5, 128, FFADE_IN ); +#endif + SendWeaponAnim( GAUSS_IDLE ); + + // Player may have been killed and this weapon dropped, don't execute any more code after this! + return; } } - if(!AnimRestore) - { - if( m_fInAttack == 1 ) SendWeaponAnim( GAUSS_SPINUP ); - if( m_fInAttack >= 2 ) SendWeaponAnim( GAUSS_SPIN ); - AnimRestore = TRUE; - } - - if ( m_flChargeTime < UTIL_WeaponTimeBase() - 10 ) - { - // Player charged up too long. Zap him. - EMIT_SOUND_DYN(ENT(m_pPlayer->pev), CHAN_WEAPON, "weapons/electro4.wav", 1.0, ATTN_NORM, 0, 80 + RANDOM_LONG(0,0x3f)); - EMIT_SOUND_DYN(ENT(m_pPlayer->pev), CHAN_ITEM, "weapons/electro6.wav", 1.0, ATTN_NORM, 0, 75 + RANDOM_LONG(0,0x3f)); - - m_fInAttack = 0; - m_iChargeLevel = 0; - m_flTimeWeaponIdle = UTIL_WeaponTimeBase() + 1.0; - m_pPlayer->m_flNextAttack = UTIL_WeaponTimeBase() + 1.0; - - m_pPlayer->TakeDamage( VARS(eoNullEntity), VARS(eoNullEntity), 50, DMG_SHOCK ); - UTIL_ScreenFade( m_pPlayer, Vector(255,128,0), 2, 0.5, 128, FFADE_IN ); - - SendWeaponAnim( GAUSS_IDLE ); - return; - } - - m_iSoundState = SND_CHANGE_PITCH; // hack for going through level transitions - m_pPlayer->m_iWeaponVolume = PRIMARY_CHARGE_VOLUME; } //========================================================= @@ -274,32 +311,59 @@ void CGauss::StartFire( void ) UTIL_MakeVectors( m_pPlayer->pev->v_angle + m_pPlayer->pev->punchangle ); Vector vecAiming = gpGlobals->v_forward; - Vector vecSrc = m_pPlayer->GetGunPosition( ); + Vector vecSrc = m_pPlayer->GetGunPosition( ); // + gpGlobals->v_up * -8 + gpGlobals->v_right * 8; - if ( pev->frags ) flDamage = gSkillData.plrDmgGauss; - if ( m_iChargeLevel >= 1) flDamage = 12.5 * m_iChargeLevel; - - if (m_fInAttack >= 1) + if ( gpGlobals->time - m_pPlayer->m_flStartCharge > GetFullChargeTime() ) { + flDamage = 200; + } + else + { + flDamage = 200 * (( gpGlobals->time - m_pPlayer->m_flStartCharge) / GetFullChargeTime() ); + } + + if ( m_fPrimaryFire ) + { + // fixed damage on primary attack +#ifdef CLIENT_DLL + flDamage = 20; +#else + flDamage = gSkillData.plrDmgGauss; +#endif + } + + if (m_fInAttack != 3) + { + //ALERT ( at_console, "Time:%f Damage:%f\n", gpGlobals->time - m_pPlayer->m_flStartCharge, flDamage ); + +#ifndef CLIENT_DLL float flZVel = m_pPlayer->pev->velocity.z; - if ( !pev->frags ) m_pPlayer->pev->velocity = m_pPlayer->pev->velocity - gpGlobals->v_forward * flDamage * 10; - if ( !IsMultiplayer() ) m_pPlayer->pev->velocity.z = flZVel; + if ( !m_fPrimaryFire ) + { + m_pPlayer->pev->velocity = m_pPlayer->pev->velocity - gpGlobals->v_forward * flDamage * 5; + } + + if ( !g_pGameRules->IsMultiplayer() ) + + { + // in deathmatch, gauss can pop you up into the air. Not in single play. + m_pPlayer->pev->velocity.z = flZVel; + } +#endif + // player "shoot" animation + m_pPlayer->SetAnimation( PLAYER_ATTACK1 ); } - // player "shoot" animation - m_pPlayer->SetAnimation( PLAYER_ATTACK1 ); // time until aftershock 'static discharge' sound - m_flShockTime = UTIL_WeaponTimeBase() + RANDOM_FLOAT(0.3, 0.8); + m_pPlayer->m_flPlayAftershock = gpGlobals->time + UTIL_SharedRandomFloat( m_pPlayer->random_seed, 0.3, 0.8 ); Fire( vecSrc, vecAiming, flDamage ); - m_fInAttack = 0; - m_iChargeLevel = 0; } void CGauss::Fire( Vector vecOrigSrc, Vector vecDir, float flDamage ) { - m_pPlayer->m_iWeaponVolume = PRIMARY_FIRE_VOLUME; + m_pPlayer->m_iWeaponVolume = GAUSS_PRIMARY_FIRE_VOLUME; Vector vecSrc = vecOrigSrc; Vector vecDest = vecSrc + vecDir * 8192; @@ -312,23 +376,35 @@ void CGauss::Fire( Vector vecOrigSrc, Vector vecDir, float flDamage ) int nMaxHits = 10; pentIgnore = ENT( m_pPlayer->pev ); + +#ifdef CLIENT_DLL + if ( m_fPrimaryFire == false ) + g_irunninggausspred = true; +#endif // The main firing event is sent unreliably so it won't be delayed. - PLAYBACK_EVENT_FULL( 0, m_pPlayer->edict(), m_usGaussFire, 0.0, (float *)&m_pPlayer->pev->origin, (float *)&m_pPlayer->pev->angles, flDamage, 0.0, pev->body, 0, pev->frags, 0 ); + PLAYBACK_EVENT_FULL( FEV_NOTHOST, m_pPlayer->edict(), m_usGaussFire, 0.0, (float *)&m_pPlayer->pev->origin, (float *)&m_pPlayer->pev->angles, flDamage, 0.0, 0, 0, m_fPrimaryFire ? 1 : 0, 0 ); // This reliable event is used to stop the spinning sound // It's delayed by a fraction of second to make sure it is delayed by 1 frame on the client // It's sent reliably anyway, which could lead to other delays - PLAYBACK_EVENT_FULL( FEV_RELIABLE, m_pPlayer->edict(), m_usGaussFire, 0.01, (float *)&m_pPlayer->pev->origin, (float *)&m_pPlayer->pev->angles, 0.0, 0.0, pev->body, 0, 0, 1 ); + PLAYBACK_EVENT_FULL( FEV_NOTHOST | FEV_RELIABLE, m_pPlayer->edict(), m_usGaussFire, 0.01, (float *)&m_pPlayer->pev->origin, (float *)&m_pPlayer->pev->angles, 0.0, 0.0, 0, 0, 0, 1 ); + -// ALERT( at_console, "%f %f %f\n%f %f %f\n", vecSrc.x, vecSrc.y, vecSrc.z, vecDest.x, vecDest.y, vecDest.z ); + /*ALERT( at_console, "%f %f %f\n%f %f %f\n", + vecSrc.x, vecSrc.y, vecSrc.z, + vecDest.x, vecDest.y, vecDest.z );*/ + + // ALERT( at_console, "%f %f\n", tr.flFraction, flMaxFrac ); +#ifndef CLIENT_DLL while (flDamage > 10 && nMaxHits > 0) { nMaxHits--; + // ALERT( at_console, "." ); UTIL_TraceLine(vecSrc, vecDest, dont_ignore_monsters, pentIgnore, &tr); if (tr.fAllSolid) @@ -336,7 +412,8 @@ void CGauss::Fire( Vector vecOrigSrc, Vector vecDir, float flDamage ) CBaseEntity *pEntity = CBaseEntity::Instance(tr.pHit); - if (pEntity == NULL) break; + if (pEntity == NULL) + break; if ( fFirstBeam ) { @@ -364,6 +441,7 @@ void CGauss::Fire( Vector vecOrigSrc, Vector vecDir, float flDamage ) if (n < 0.5) // 60 degrees { // ALERT( at_console, "reflect %f\n", n ); + // reflect Vector r; r = 2.0 * tr.vecPlaneNormal * n + vecDir; @@ -391,7 +469,7 @@ void CGauss::Fire( Vector vecOrigSrc, Vector vecDir, float flDamage ) fHasPunched = 1; // try punching through wall if secondary attack (primary is incapable of breaking through) - if ( !pev->frags ) + if ( !m_fPrimaryFire ) { UTIL_TraceLine( tr.vecEndPos + vecDir * 8, vecDest, dont_ignore_monsters, pentIgnore, &beam_tr); if (!beam_tr.fAllSolid) @@ -414,7 +492,7 @@ void CGauss::Fire( Vector vecOrigSrc, Vector vecDir, float flDamage ) float damage_radius; - if ( IsMultiplayer() ) + if ( g_pGameRules->IsMultiplayer() ) { damage_radius = flDamage * 1.75; // Old code == 2.5 } @@ -432,9 +510,19 @@ void CGauss::Fire( Vector vecOrigSrc, Vector vecDir, float flDamage ) vecSrc = beam_tr.vecEndPos + vecDir; } } - else flDamage = 0; + else + { + //ALERT( at_console, "blocked %f\n", n ); + flDamage = 0; + } } - else flDamage = 0; + else + { + //ALERT( at_console, "blocked solid\n" ); + + flDamage = 0; + } + } } else @@ -443,6 +531,8 @@ void CGauss::Fire( Vector vecOrigSrc, Vector vecDir, float flDamage ) pentIgnore = ENT( pEntity->pev ); } } +#endif + // ALERT( at_console, "%d bytes\n", nTotal ); } @@ -450,48 +540,53 @@ void CGauss::Fire( Vector vecOrigSrc, Vector vecDir, float flDamage ) void CGauss::WeaponIdle( void ) { + ResetEmptySound( ); + // play aftershock static discharge - if ( m_flShockTime && m_flShockTime < gpGlobals->time ) + if ( m_pPlayer->m_flPlayAftershock && m_pPlayer->m_flPlayAftershock < gpGlobals->time ) { switch (RANDOM_LONG(0,3)) { - case 0: EMIT_SOUND(ENT(m_pPlayer->pev), CHAN_WEAPON, "weapons/electro4.wav", RANDOM_FLOAT(0.7, 0.8), ATTN_NORM); break; - case 1: EMIT_SOUND(ENT(m_pPlayer->pev), CHAN_WEAPON, "weapons/electro5.wav", RANDOM_FLOAT(0.7, 0.8), ATTN_NORM); break; - case 2: EMIT_SOUND(ENT(m_pPlayer->pev), CHAN_WEAPON, "weapons/electro6.wav", RANDOM_FLOAT(0.7, 0.8), ATTN_NORM); break; - case 3: break; // no sound + case 0: EMIT_SOUND(ENT(m_pPlayer->pev), CHAN_WEAPON, "weapons/electro4.wav", RANDOM_FLOAT(0.7, 0.8), ATTN_NORM); break; + case 1: EMIT_SOUND(ENT(m_pPlayer->pev), CHAN_WEAPON, "weapons/electro5.wav", RANDOM_FLOAT(0.7, 0.8), ATTN_NORM); break; + case 2: EMIT_SOUND(ENT(m_pPlayer->pev), CHAN_WEAPON, "weapons/electro6.wav", RANDOM_FLOAT(0.7, 0.8), ATTN_NORM); break; + case 3: break; // no sound } - m_flShockTime = 0; + m_pPlayer->m_flPlayAftershock = 0.0; } - if (m_flTimeWeaponIdle > UTIL_WeaponTimeBase()) return; + if (m_flTimeWeaponIdle > UTIL_WeaponTimeBase()) + return; if (m_fInAttack != 0) { StartFire(); + m_fInAttack = 0; m_flTimeWeaponIdle = UTIL_WeaponTimeBase() + 2.0; - return; } - - if (m_pPlayer->m_rgAmmo[m_iPrimaryAmmoType] > 0) + else { int iAnim; - float flRand = RANDOM_FLOAT(0,1); - if ( flRand <= 0.3 ) + float flRand = RANDOM_FLOAT(0, 1); + if (flRand <= 0.5) { iAnim = GAUSS_IDLE; - m_flTimeWeaponIdle = UTIL_WeaponTimeBase() + 61 / 15; + m_flTimeWeaponIdle = UTIL_WeaponTimeBase() + UTIL_SharedRandomFloat( m_pPlayer->random_seed, 10, 15 ); } - if ( flRand >= 0.5 ) - { - iAnim = GAUSS_FIDGET; - m_flTimeWeaponIdle = UTIL_WeaponTimeBase() + 91 / 30; - } - else + else if (flRand <= 0.75) { iAnim = GAUSS_IDLE2; - m_flTimeWeaponIdle = UTIL_WeaponTimeBase() + 61 / 15; + m_flTimeWeaponIdle = UTIL_WeaponTimeBase() + UTIL_SharedRandomFloat( m_pPlayer->random_seed, 10, 15 ); } + else + { + iAnim = GAUSS_FIDGET; + m_flTimeWeaponIdle = UTIL_WeaponTimeBase() + 3; + } + + return; SendWeaponAnim( iAnim ); + } } @@ -524,3 +619,5 @@ class CGaussAmmo : public CBasePlayerAmmo } }; LINK_ENTITY_TO_CLASS( ammo_gaussclip, CGaussAmmo ); + +#endif diff --git a/dlls/genericmonster.cpp b/dlls/genericmonster.cpp new file mode 100644 index 00000000..8f40be61 --- /dev/null +++ b/dlls/genericmonster.cpp @@ -0,0 +1,140 @@ +/*** +* +* Copyright (c) 1996-2002, 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. +* +* This source code contains proprietary and confidential information of +* Valve LLC and its suppliers. Access to this code is restricted to +* persons who have executed a written SDK license with Valve. Any access, +* use or distribution of this code by or to any unlicensed person is illegal. +* +****/ +//========================================================= +// Generic Monster - purely for scripted sequence work. +//========================================================= +#include "extdll.h" +#include "util.h" +#include "cbase.h" +#include "monsters.h" +#include "schedule.h" + +// For holograms, make them not solid so the player can walk through them +#define SF_GENERICMONSTER_NOTSOLID 4 + +//========================================================= +// Monster's Anim Events Go Here +//========================================================= + +class CGenericMonster : public CBaseMonster +{ +public: + void Spawn( void ); + void Precache( void ); + void SetYawSpeed( void ); + int Classify ( void ); + void HandleAnimEvent( MonsterEvent_t *pEvent ); + int ISoundMask ( void ); +}; +LINK_ENTITY_TO_CLASS( monster_generic, CGenericMonster ); + +//========================================================= +// Classify - indicates this monster's place in the +// relationship table. +//========================================================= +int CGenericMonster :: Classify ( void ) +{ + return CLASS_PLAYER_ALLY; +} + +//========================================================= +// SetYawSpeed - allows each sequence to have a different +// turn rate associated with it. +//========================================================= +void CGenericMonster :: SetYawSpeed ( void ) +{ + int ys; + + switch ( m_Activity ) + { + case ACT_IDLE: + default: + ys = 90; + } + + pev->yaw_speed = ys; +} + +//========================================================= +// HandleAnimEvent - catches the monster-specific messages +// that occur when tagged animation frames are played. +//========================================================= +void CGenericMonster :: HandleAnimEvent( MonsterEvent_t *pEvent ) +{ + switch( pEvent->event ) + { + case 0: + default: + CBaseMonster::HandleAnimEvent( pEvent ); + break; + } +} + +//========================================================= +// ISoundMask - generic monster can't hear. +//========================================================= +int CGenericMonster :: ISoundMask ( void ) +{ + return NULL; +} + +//========================================================= +// Spawn +//========================================================= +void CGenericMonster :: Spawn() +{ + Precache(); + + SET_MODEL( ENT(pev), STRING(pev->model) ); + +/* + if ( FStrEq( STRING(pev->model), "models/player.mdl" ) ) + UTIL_SetSize(pev, VEC_HUMAN_HULL_MIN, VEC_HUMAN_HULL_MAX); + else + UTIL_SetSize(pev, VEC_HULL_MIN, VEC_HULL_MAX); +*/ + + if ( FStrEq( STRING(pev->model), "models/player.mdl" ) || FStrEq( STRING(pev->model), "models/holo.mdl" ) ) + UTIL_SetSize(pev, VEC_HULL_MIN, VEC_HULL_MAX); + else + UTIL_SetSize(pev, VEC_HUMAN_HULL_MIN, VEC_HUMAN_HULL_MAX); + + pev->solid = SOLID_SLIDEBOX; + pev->movetype = MOVETYPE_STEP; + m_bloodColor = BLOOD_COLOR_RED; + pev->health = 8; + m_flFieldOfView = 0.5;// indicates the width of this monster's forward view cone ( as a dotproduct result ) + m_MonsterState = MONSTERSTATE_NONE; + + MonsterInit(); + + if ( pev->spawnflags & SF_GENERICMONSTER_NOTSOLID ) + { + pev->solid = SOLID_NOT; + pev->takedamage = DAMAGE_NO; + } +} + +//========================================================= +// Precache - precaches all resources this monster needs +//========================================================= +void CGenericMonster :: Precache() +{ + PRECACHE_MODEL( (char *)STRING(pev->model) ); +} + +//========================================================= +// AI Schedules Specific to this monster +//========================================================= diff --git a/spirit/ggrenade.cpp b/dlls/ggrenade.cpp similarity index 84% rename from spirit/ggrenade.cpp rename to dlls/ggrenade.cpp index 224c7436..bed91293 100644 --- a/spirit/ggrenade.cpp +++ b/dlls/ggrenade.cpp @@ -64,8 +64,8 @@ void CGrenade::Explode( TraceResult *pTrace, int bitsDamageType ) } int iContents = UTIL_PointContents ( pev->origin ); - - MESSAGE_BEGIN( MSG_PAS, gmsgTempEntity, pev->origin ); + + MESSAGE_BEGIN( MSG_PAS, SVC_TEMPENTITY, pev->origin ); WRITE_BYTE( TE_EXPLOSION ); // This makes a dynamic light and the explosion sprites/sound WRITE_COORD( pev->origin.x ); // Send to PAS because of the sound WRITE_COORD( pev->origin.y ); @@ -93,10 +93,15 @@ void CGrenade::Explode( TraceResult *pTrace, int bitsDamageType ) pev->owner = NULL; // can't traceline attack owner if this is set RadiusDamage ( pev, pevOwner, pev->dmg, CLASS_NONE, bitsDamageType ); - - CBaseEntity *pHit = CBaseEntity::Instance( pTrace->pHit ); - - UTIL_DecalTrace( pTrace, DECAL_SCORCH2 ); + + if ( RANDOM_FLOAT( 0 , 1 ) < 0.5 ) + { + UTIL_DecalTrace( pTrace, DECAL_SCORCH1 ); + } + else + { + UTIL_DecalTrace( pTrace, DECAL_SCORCH2 ); + } flRndSound = RANDOM_FLOAT( 0 , 1 ); @@ -108,14 +113,13 @@ void CGrenade::Explode( TraceResult *pTrace, int bitsDamageType ) } pev->effects |= EF_NODRAW; - SetThink(&CGrenade:: Smoke ); + SetThink( Smoke ); pev->velocity = g_vecZero; - SetNextThink( 0.3 ); + pev->nextthink = gpGlobals->time + 0.3; if (iContents != CONTENTS_WATER) { int sparkCount = RANDOM_LONG(0,3); - for ( int i = 0; i < sparkCount; i++ ) Create( "spark_shower", pev->origin, pTrace->vecPlaneNormal, NULL ); } @@ -124,13 +128,13 @@ void CGrenade::Explode( TraceResult *pTrace, int bitsDamageType ) void CGrenade::Smoke( void ) { - if ( UTIL_PointContents ( pev->origin ) == CONTENTS_WATER ) + if (UTIL_PointContents ( pev->origin ) == CONTENTS_WATER) { UTIL_Bubbles( pev->origin - Vector( 64, 64, 64 ), pev->origin + Vector( 64, 64, 64 ), 100 ); } else { - MESSAGE_BEGIN( MSG_PVS, gmsgTempEntity, pev->origin ); + MESSAGE_BEGIN( MSG_PVS, SVC_TEMPENTITY, pev->origin ); WRITE_BYTE( TE_SMOKE ); WRITE_COORD( pev->origin.x ); WRITE_COORD( pev->origin.y ); @@ -152,16 +156,16 @@ void CGrenade::Killed( entvars_t *pevAttacker, int iGib ) // Timed grenade, this think is called when time runs out. void CGrenade::DetonateUse( CBaseEntity *pActivator, CBaseEntity *pCaller, USE_TYPE useType, float value ) { - SetThink(&CGrenade:: Detonate ); - SetNextThink( 0 ); + SetThink( Detonate ); + pev->nextthink = gpGlobals->time; } void CGrenade::PreDetonate( void ) { CSoundEnt::InsertSound ( bits_SOUND_DANGER, pev->origin, 400, 0.3 ); - SetThink(&CGrenade:: Detonate ); - SetNextThink( 1 ); + SetThink( Detonate ); + pev->nextthink = gpGlobals->time + 1; } @@ -203,9 +207,9 @@ void CGrenade::DangerSoundThink( void ) } CSoundEnt::InsertSound ( bits_SOUND_DANGER, pev->origin + pev->velocity * 0.5, pev->velocity.Length( ), 0.2 ); - SetNextThink( 0.2 ); + pev->nextthink = gpGlobals->time + 0.2; - if (pev->waterlevel != 0 && pev->watertype > CONTENTS_FLYFIELD) + if (pev->waterlevel != 0) { pev->velocity = pev->velocity * 0.5; } @@ -318,7 +322,7 @@ void CGrenade :: TumbleThink( void ) } StudioFrameAdvance( ); - SetNextThink( 0.1 ); + pev->nextthink = gpGlobals->time + 0.1; if (pev->dmgtime - 1 < gpGlobals->time) { @@ -327,9 +331,9 @@ void CGrenade :: TumbleThink( void ) if (pev->dmgtime <= gpGlobals->time) { - SetThink(&CGrenade :: Detonate ); + SetThink( Detonate ); } - if (pev->waterlevel != 0 && pev->watertype > CONTENTS_FLYFIELD) + if (pev->waterlevel != 0) { pev->velocity = pev->velocity * 0.5; pev->framerate = 0.2; @@ -358,20 +362,20 @@ CGrenade *CGrenade::ShootContact( entvars_t *pevOwner, Vector vecStart, Vector v pGrenade->Spawn(); // contact grenades arc lower pGrenade->pev->gravity = 0.5;// lower gravity since grenade is aerodynamic and engine doesn't know it. - UTIL_SetOrigin( pGrenade, vecStart ); + UTIL_SetOrigin( pGrenade->pev, vecStart ); pGrenade->pev->velocity = vecVelocity; pGrenade->pev->angles = UTIL_VecToAngles (pGrenade->pev->velocity); pGrenade->pev->owner = ENT(pevOwner); // make monsters afaid of it while in the air - pGrenade->SetThink(&CGrenade:: DangerSoundThink ); - pGrenade->SetNextThink( 0 ); + pGrenade->SetThink( DangerSoundThink ); + pGrenade->pev->nextthink = gpGlobals->time; // Tumble in air pGrenade->pev->avelocity.x = RANDOM_FLOAT ( -100, -500 ); // Explode on contact - pGrenade->SetTouch(&CGrenade:: ExplodeTouch ); + pGrenade->SetTouch( ExplodeTouch ); pGrenade->pev->dmg = gSkillData.plrDmgM203Grenade; @@ -383,23 +387,23 @@ CGrenade * CGrenade:: ShootTimed( entvars_t *pevOwner, Vector vecStart, Vector v { CGrenade *pGrenade = GetClassPtr( (CGrenade *)NULL ); pGrenade->Spawn(); - UTIL_SetOrigin( pGrenade, vecStart ); + UTIL_SetOrigin( pGrenade->pev, vecStart ); pGrenade->pev->velocity = vecVelocity; pGrenade->pev->angles = UTIL_VecToAngles(pGrenade->pev->velocity); pGrenade->pev->owner = ENT(pevOwner); - pGrenade->SetTouch(&CGrenade:: BounceTouch ); // Bounce if touched + pGrenade->SetTouch( BounceTouch ); // Bounce if touched // Take one second off of the desired detonation time and set the think to PreDetonate. PreDetonate // will insert a DANGER sound into the world sound list and delay detonation for one second so that // the grenade explodes after the exact amount of time specified in the call to ShootTimed(). pGrenade->pev->dmgtime = gpGlobals->time + time; - pGrenade->SetThink(&CGrenade:: TumbleThink ); - pGrenade->SetNextThink( 0.1 ); + pGrenade->SetThink( TumbleThink ); + pGrenade->pev->nextthink = gpGlobals->time + 0.1; if (time < 0.1) { - pGrenade->SetNextThink( 0 ); + pGrenade->pev->nextthink = gpGlobals->time; pGrenade->pev->velocity = Vector( 0, 0, 0 ); } @@ -412,7 +416,7 @@ CGrenade * CGrenade:: ShootTimed( entvars_t *pevOwner, Vector vecStart, Vector v pGrenade->pev->gravity = 0.5; pGrenade->pev->friction = 0.8; - SET_MODEL(ENT(pGrenade->pev), "models/hgrenade.mdl"); + SET_MODEL(ENT(pGrenade->pev), "models/w_grenade.mdl"); pGrenade->pev->dmg = 100; return pGrenade; @@ -432,15 +436,15 @@ CGrenade * CGrenade :: ShootSatchelCharge( entvars_t *pevOwner, Vector vecStart, UTIL_SetSize(pGrenade->pev, Vector( 0, 0, 0), Vector(0, 0, 0)); pGrenade->pev->dmg = 200; - UTIL_SetOrigin( pGrenade, vecStart ); + UTIL_SetOrigin( pGrenade->pev, vecStart ); pGrenade->pev->velocity = vecVelocity; pGrenade->pev->angles = g_vecZero; pGrenade->pev->owner = ENT(pevOwner); // Detonate in "time" seconds - pGrenade->SetThink(&CGrenade:: SUB_DoNothing ); - pGrenade->SetUse(&CGrenade:: DetonateUse ); - pGrenade->SetTouch(&CGrenade:: SlideTouch ); + pGrenade->SetThink( SUB_DoNothing ); + pGrenade->SetUse( DetonateUse ); + pGrenade->SetTouch( SlideTouch ); pGrenade->pev->spawnflags = SF_DETONATE; pGrenade->pev->friction = 0.9; @@ -452,6 +456,7 @@ CGrenade * CGrenade :: ShootSatchelCharge( entvars_t *pevOwner, Vector vecStart, void CGrenade :: UseSatchelCharges( entvars_t *pevOwner, SATCHELCODE code ) { + edict_t *pentFind; edict_t *pentOwner; if ( !pevOwner ) @@ -461,8 +466,11 @@ void CGrenade :: UseSatchelCharges( entvars_t *pevOwner, SATCHELCODE code ) pentOwner = pOwner->edict(); - CBaseEntity *pEnt = UTIL_FindEntityByClassname( NULL, "grenade" ); - while ( pEnt ) + pentFind = FIND_ENTITY_BY_CLASSNAME( NULL, "grenade" ); + while ( !FNullEnt( pentFind ) ) + { + CBaseEntity *pEnt = Instance( pentFind ); + if ( pEnt ) { if ( FBitSet( pEnt->pev->spawnflags, SF_DETONATE ) && pEnt->pev->owner == pentOwner ) { @@ -471,7 +479,8 @@ void CGrenade :: UseSatchelCharges( entvars_t *pevOwner, SATCHELCODE code ) else // SATCHEL_RELEASE pEnt->pev->owner = NULL; } - pEnt = UTIL_FindEntityByClassname( pEnt, "grenade" ); + } + pentFind = FIND_ENTITY_BY_CLASSNAME( pentFind, "grenade" ); } } diff --git a/spirit/globals.cpp b/dlls/globals.cpp similarity index 96% rename from spirit/globals.cpp rename to dlls/globals.cpp index ef657c2d..be31ef9f 100644 --- a/spirit/globals.cpp +++ b/dlls/globals.cpp @@ -1,39 +1,39 @@ -/*** -* -* Copyright (c) 1996-2002, 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. -* -****/ -/* - -===== globals.cpp ======================================================== - - DLL-wide global variable definitions. - They're all defined here, for convenient centralization. - Source files that need them should "extern ..." declare each - variable, to better document what globals they care about. - -*/ - -#include "extdll.h" -#include "util.h" -#include "cbase.h" -#include "soundent.h" - -DLL_GLOBAL ULONG g_ulFrameCount; -DLL_GLOBAL ULONG g_ulModelIndexEyes; -DLL_GLOBAL ULONG g_ulModelIndexPlayer; -DLL_GLOBAL Vector g_vecAttackDir; -DLL_GLOBAL int g_iSkillLevel; -DLL_GLOBAL int gDisplayTitle; -DLL_GLOBAL BOOL g_fGameOver; -DLL_GLOBAL const Vector g_vecZero = Vector(0,0,0); -DLL_GLOBAL int g_Language; +/*** +* +* Copyright (c) 1996-2002, 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. +* +****/ +/* + +===== globals.cpp ======================================================== + + DLL-wide global variable definitions. + They're all defined here, for convenient centralization. + Source files that need them should "extern ..." declare each + variable, to better document what globals they care about. + +*/ + +#include "extdll.h" +#include "util.h" +#include "cbase.h" +#include "soundent.h" + +DLL_GLOBAL ULONG g_ulFrameCount; +DLL_GLOBAL ULONG g_ulModelIndexEyes; +DLL_GLOBAL ULONG g_ulModelIndexPlayer; +DLL_GLOBAL Vector g_vecAttackDir; +DLL_GLOBAL int g_iSkillLevel; +DLL_GLOBAL int gDisplayTitle; +DLL_GLOBAL BOOL g_fGameOver; +DLL_GLOBAL const Vector g_vecZero = Vector(0,0,0); +DLL_GLOBAL int g_Language; diff --git a/dlls/glock.cpp b/dlls/glock.cpp new file mode 100644 index 00000000..769ab4ac --- /dev/null +++ b/dlls/glock.cpp @@ -0,0 +1,274 @@ +/*** +* +* Copyright (c) 1996-2002, 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. +* +****/ + +#include "extdll.h" +#include "util.h" +#include "cbase.h" +#include "monsters.h" +#include "weapons.h" +#include "nodes.h" +#include "player.h" + +enum glock_e { + GLOCK_IDLE1 = 0, + GLOCK_IDLE2, + GLOCK_IDLE3, + GLOCK_SHOOT, + GLOCK_SHOOT_EMPTY, + GLOCK_RELOAD, + GLOCK_RELOAD_NOT_EMPTY, + GLOCK_DRAW, + GLOCK_HOLSTER, + GLOCK_ADD_SILENCER +}; + +LINK_ENTITY_TO_CLASS( weapon_glock, CGlock ); +LINK_ENTITY_TO_CLASS( weapon_9mmhandgun, CGlock ); + + +void CGlock::Spawn( ) +{ + pev->classname = MAKE_STRING("weapon_9mmhandgun"); // hack to allow for old names + Precache( ); + m_iId = WEAPON_GLOCK; + SET_MODEL(ENT(pev), "models/w_9mmhandgun.mdl"); + + m_iDefaultAmmo = GLOCK_DEFAULT_GIVE; + + FallInit();// get ready to fall down. +} + + +void CGlock::Precache( void ) +{ + PRECACHE_MODEL("models/v_9mmhandgun.mdl"); + PRECACHE_MODEL("models/w_9mmhandgun.mdl"); + PRECACHE_MODEL("models/p_9mmhandgun.mdl"); + + m_iShell = PRECACHE_MODEL ("models/shell.mdl");// brass shell + + PRECACHE_SOUND("items/9mmclip1.wav"); + PRECACHE_SOUND("items/9mmclip2.wav"); + + PRECACHE_SOUND ("weapons/pl_gun1.wav");//silenced handgun + PRECACHE_SOUND ("weapons/pl_gun2.wav");//silenced handgun + PRECACHE_SOUND ("weapons/pl_gun3.wav");//handgun + + m_usFireGlock1 = PRECACHE_EVENT( 1, "events/glock1.sc" ); + m_usFireGlock2 = PRECACHE_EVENT( 1, "events/glock2.sc" ); +} + +int CGlock::GetItemInfo(ItemInfo *p) +{ + p->pszName = STRING(pev->classname); + p->pszAmmo1 = "9mm"; + p->iMaxAmmo1 = _9MM_MAX_CARRY; + p->pszAmmo2 = NULL; + p->iMaxAmmo2 = -1; + p->iMaxClip = GLOCK_MAX_CLIP; + p->iSlot = 1; + p->iPosition = 0; + p->iFlags = 0; + p->iId = m_iId = WEAPON_GLOCK; + p->iWeight = GLOCK_WEIGHT; + + return 1; +} + +BOOL CGlock::Deploy( ) +{ + // pev->body = 1; + return DefaultDeploy( "models/v_9mmhandgun.mdl", "models/p_9mmhandgun.mdl", GLOCK_DRAW, "onehanded", /*UseDecrement() ? 1 : 0*/ 0 ); +} + +void CGlock::SecondaryAttack( void ) +{ + GlockFire( 0.1, 0.2, FALSE ); +} + +void CGlock::PrimaryAttack( void ) +{ + GlockFire( 0.01, 0.3, TRUE ); +} + +void CGlock::GlockFire( float flSpread , float flCycleTime, BOOL fUseAutoAim ) +{ + if (m_iClip <= 0) + { + if (m_fFireOnEmpty) + { + PlayEmptySound(); + m_flNextPrimaryAttack = UTIL_WeaponTimeBase() + 0.2; + } + + return; + } + + m_iClip--; + + m_pPlayer->pev->effects = (int)(m_pPlayer->pev->effects) | EF_MUZZLEFLASH; + + int flags; + +#if defined( CLIENT_WEAPONS ) + flags = FEV_NOTHOST; +#else + flags = 0; +#endif + + // player "shoot" animation + m_pPlayer->SetAnimation( PLAYER_ATTACK1 ); + + // silenced + if (pev->body == 1) + { + m_pPlayer->m_iWeaponVolume = QUIET_GUN_VOLUME; + m_pPlayer->m_iWeaponFlash = DIM_GUN_FLASH; + } + else + { + // non-silenced + m_pPlayer->m_iWeaponVolume = NORMAL_GUN_VOLUME; + m_pPlayer->m_iWeaponFlash = NORMAL_GUN_FLASH; + } + + Vector vecSrc = m_pPlayer->GetGunPosition( ); + Vector vecAiming; + + if ( fUseAutoAim ) + { + vecAiming = m_pPlayer->GetAutoaimVector( AUTOAIM_10DEGREES ); + } + else + { + vecAiming = gpGlobals->v_forward; + } + + Vector vecDir; + vecDir = m_pPlayer->FireBulletsPlayer( 1, vecSrc, vecAiming, Vector( flSpread, flSpread, flSpread ), 8192, BULLET_PLAYER_9MM, 0, 0, m_pPlayer->pev, m_pPlayer->random_seed ); + + PLAYBACK_EVENT_FULL( flags, m_pPlayer->edict(), fUseAutoAim ? m_usFireGlock1 : m_usFireGlock2, 0.0, (float *)&g_vecZero, (float *)&g_vecZero, vecDir.x, vecDir.y, 0, 0, ( m_iClip == 0 ) ? 1 : 0, 0 ); + + m_flNextPrimaryAttack = m_flNextSecondaryAttack = UTIL_WeaponTimeBase() + flCycleTime; + + if (!m_iClip && m_pPlayer->m_rgAmmo[m_iPrimaryAmmoType] <= 0) + // HEV suit - indicate out of ammo condition + m_pPlayer->SetSuitUpdate("!HEV_AMO0", FALSE, 0); + + m_flTimeWeaponIdle = UTIL_WeaponTimeBase() + UTIL_SharedRandomFloat( m_pPlayer->random_seed, 10, 15 ); +} + + +void CGlock::Reload( void ) +{ + if ( m_pPlayer->ammo_9mm <= 0 ) + return; + + int iResult; + + if (m_iClip == 0) + iResult = DefaultReload( 17, GLOCK_RELOAD, 1.5 ); + else + iResult = DefaultReload( 17, GLOCK_RELOAD_NOT_EMPTY, 1.5 ); + + if (iResult) + { + m_flTimeWeaponIdle = UTIL_WeaponTimeBase() + UTIL_SharedRandomFloat( m_pPlayer->random_seed, 10, 15 ); + } +} + + + +void CGlock::WeaponIdle( void ) +{ + ResetEmptySound( ); + + m_pPlayer->GetAutoaimVector( AUTOAIM_10DEGREES ); + + if ( m_flTimeWeaponIdle > UTIL_WeaponTimeBase() ) + return; + + // only idle if the slid isn't back + if (m_iClip != 0) + { + int iAnim; + float flRand = UTIL_SharedRandomFloat( m_pPlayer->random_seed, 0.0, 1.0 ); + + if (flRand <= 0.3 + 0 * 0.75) + { + iAnim = GLOCK_IDLE3; + m_flTimeWeaponIdle = UTIL_WeaponTimeBase() + 49.0 / 16; + } + else if (flRand <= 0.6 + 0 * 0.875) + { + iAnim = GLOCK_IDLE1; + m_flTimeWeaponIdle = UTIL_WeaponTimeBase() + 60.0 / 16.0; + } + else + { + iAnim = GLOCK_IDLE2; + m_flTimeWeaponIdle = UTIL_WeaponTimeBase() + 40.0 / 16.0; + } + SendWeaponAnim( iAnim, 1 ); + } +} + + + + + + + + +class CGlockAmmo : public CBasePlayerAmmo +{ + void Spawn( void ) + { + Precache( ); + SET_MODEL(ENT(pev), "models/w_9mmclip.mdl"); + CBasePlayerAmmo::Spawn( ); + } + void Precache( void ) + { + PRECACHE_MODEL ("models/w_9mmclip.mdl"); + PRECACHE_SOUND("items/9mmclip1.wav"); + } + BOOL AddAmmo( CBaseEntity *pOther ) + { + if (pOther->GiveAmmo( AMMO_GLOCKCLIP_GIVE, "9mm", _9MM_MAX_CARRY ) != -1) + { + EMIT_SOUND(ENT(pev), CHAN_ITEM, "items/9mmclip1.wav", 1, ATTN_NORM); + return TRUE; + } + return FALSE; + } +}; +LINK_ENTITY_TO_CLASS( ammo_glockclip, CGlockAmmo ); +LINK_ENTITY_TO_CLASS( ammo_9mmclip, CGlockAmmo ); + + + + + + + + + + + + + + + diff --git a/spirit/gman.cpp b/dlls/gman.cpp similarity index 94% rename from spirit/gman.cpp rename to dlls/gman.cpp index e71d5ced..77a1bd8a 100644 --- a/spirit/gman.cpp +++ b/dlls/gman.cpp @@ -1,243 +1,237 @@ -/*** -* -* Copyright (c) 1996-2002, 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. -* -* This source code contains proprietary and confidential information of -* Valve LLC and its suppliers. Access to this code is restricted to -* persons who have executed a written SDK license with Valve. Any access, -* use or distribution of this code by or to any unlicensed person is illegal. -* -****/ -//========================================================= -// GMan - misunderstood servant of the people -//========================================================= -#include "extdll.h" -#include "util.h" -#include "cbase.h" -#include "monsters.h" -#include "schedule.h" -#include "weapons.h" - -//========================================================= -// Monster's Anim Events Go Here -//========================================================= - -class CGMan : public CBaseMonster -{ -public: - void Spawn( void ); - void Precache( void ); - void SetYawSpeed( void ); - int Classify ( void ); - void HandleAnimEvent( MonsterEvent_t *pEvent ); - int ISoundMask ( void ); - - int Save( CSave &save ); - int Restore( CRestore &restore ); - static TYPEDESCRIPTION m_SaveData[]; - - void StartTask( Task_t *pTask ); - void RunTask( Task_t *pTask ); - int TakeDamage( entvars_t* pevInflictor, entvars_t* pevAttacker, float flDamage, int bitsDamageType ); - void TraceAttack( entvars_t *pevAttacker, float flDamage, Vector vecDir, TraceResult *ptr, int bitsDamageType); - - void PlayScriptedSentence( const char *pszSentence, float duration, float volume, float attenuation, BOOL bConcurrent, CBaseEntity *pListener ); - - EHANDLE m_hPlayer; - EHANDLE m_hTalkTarget; - float m_flTalkTime; -}; -LINK_ENTITY_TO_CLASS( monster_gman, CGMan ); - - -TYPEDESCRIPTION CGMan::m_SaveData[] = -{ - DEFINE_FIELD( CGMan, m_hTalkTarget, FIELD_EHANDLE ), - DEFINE_FIELD( CGMan, m_flTalkTime, FIELD_TIME ), -}; -IMPLEMENT_SAVERESTORE( CGMan, CBaseMonster ); - - -//========================================================= -// Classify - indicates this monster's place in the -// relationship table. -//========================================================= -int CGMan :: Classify ( void ) -{ - return m_iClass?m_iClass:CLASS_NONE; -} - -//========================================================= -// SetYawSpeed - allows each sequence to have a different -// turn rate associated with it. -//========================================================= -void CGMan :: SetYawSpeed ( void ) -{ - int ys; - - switch ( m_Activity ) - { - case ACT_IDLE: - default: - ys = 90; - } - - pev->yaw_speed = ys; -} - -//========================================================= -// HandleAnimEvent - catches the monster-specific messages -// that occur when tagged animation frames are played. -//========================================================= -void CGMan :: HandleAnimEvent( MonsterEvent_t *pEvent ) -{ - switch( pEvent->event ) - { - case 0: - default: - CBaseMonster::HandleAnimEvent( pEvent ); - break; - } -} - -//========================================================= -// ISoundMask - generic monster can't hear. -//========================================================= -int CGMan :: ISoundMask ( void ) -{ - return NULL; -} - -//========================================================= -// Spawn -//========================================================= -void CGMan :: Spawn() -{ - Precache(); - - if (pev->model) - SET_MODEL(ENT(pev), STRING(pev->model)); //LRC - else - SET_MODEL( ENT(pev), "models/gman.mdl" ); - UTIL_SetSize(pev, VEC_HUMAN_HULL_MIN, VEC_HUMAN_HULL_MAX); - - pev->solid = SOLID_SLIDEBOX; - pev->movetype = MOVETYPE_STEP; - m_bloodColor = DONT_BLEED; - pev->health = 100; - m_flFieldOfView = 0.5;// indicates the width of this monster's forward view cone ( as a dotproduct result ) - m_MonsterState = MONSTERSTATE_NONE; - - MonsterInit(); -} - -//========================================================= -// Precache - precaches all resources this monster needs -//========================================================= -void CGMan :: Precache() -{ - if (pev->model) - PRECACHE_MODEL((char*)STRING(pev->model)); //LRC - else - PRECACHE_MODEL( "models/gman.mdl" ); -} - - -//========================================================= -// AI Schedules Specific to this monster -//========================================================= - - -void CGMan :: StartTask( Task_t *pTask ) -{ - switch( pTask->iTask ) - { - case TASK_WAIT: - if (m_hPlayer == NULL) - { - m_hPlayer = UTIL_FindEntityByClassname( NULL, "player" ); - } - break; - } - CBaseMonster::StartTask( pTask ); -} - -void CGMan :: RunTask( Task_t *pTask ) -{ - switch( pTask->iTask ) - { - case TASK_WAIT: - // look at who I'm talking to - if (m_flTalkTime > gpGlobals->time && m_hTalkTarget != NULL) - { - float yaw = VecToYaw(m_hTalkTarget->pev->origin - pev->origin) - pev->angles.y; - - if (yaw > 180) yaw -= 360; - if (yaw < -180) yaw += 360; - - // turn towards vector - SetBoneController( 0, yaw ); - } - // look at player, but only if playing a "safe" idle animation - else if (m_hPlayer != NULL && pev->sequence == 0) - { - float yaw = VecToYaw(m_hPlayer->pev->origin - pev->origin) - pev->angles.y; - - if (yaw > 180) yaw -= 360; - if (yaw < -180) yaw += 360; - - // turn towards vector - SetBoneController( 0, yaw ); - } - else - { - SetBoneController( 0, 0 ); - } - CBaseMonster::RunTask( pTask ); - break; - default: - SetBoneController( 0, 0 ); - CBaseMonster::RunTask( pTask ); - break; - } -} - - -//========================================================= -// Override all damage -//========================================================= -int CGMan :: TakeDamage( entvars_t* pevInflictor, entvars_t* pevAttacker, float flDamage, int bitsDamageType ) -{ - pev->health = pev->max_health / 2; // always trigger the 50% damage aitrigger - - if ( flDamage > 0 ) - { - SetConditions(bits_COND_LIGHT_DAMAGE); - } - - if ( flDamage >= 20 ) - { - SetConditions(bits_COND_HEAVY_DAMAGE); - } - return TRUE; -} - - -void CGMan::TraceAttack( entvars_t *pevAttacker, float flDamage, Vector vecDir, TraceResult *ptr, int bitsDamageType) -{ - UTIL_Ricochet( ptr->vecEndPos, 1.0 ); - AddMultiDamage( pevAttacker, this, flDamage, bitsDamageType ); -} - - -void CGMan::PlayScriptedSentence( const char *pszSentence, float duration, float volume, float attenuation, BOOL bConcurrent, CBaseEntity *pListener ) -{ - CBaseMonster::PlayScriptedSentence( pszSentence, duration, volume, attenuation, bConcurrent, pListener ); - - m_flTalkTime = gpGlobals->time + duration; - m_hTalkTarget = pListener; -} +/*** +* +* Copyright (c) 1996-2002, 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. +* +* This source code contains proprietary and confidential information of +* Valve LLC and its suppliers. Access to this code is restricted to +* persons who have executed a written SDK license with Valve. Any access, +* use or distribution of this code by or to any unlicensed person is illegal. +* +****/ +//========================================================= +// GMan - misunderstood servant of the people +//========================================================= +#include "extdll.h" +#include "util.h" +#include "cbase.h" +#include "monsters.h" +#include "schedule.h" +#include "weapons.h" + +//========================================================= +// Monster's Anim Events Go Here +//========================================================= + +class CGMan : public CBaseMonster +{ +public: + void Spawn( void ); + void Precache( void ); + void SetYawSpeed( void ); + int Classify ( void ); + void HandleAnimEvent( MonsterEvent_t *pEvent ); + int ISoundMask ( void ); + + int Save( CSave &save ); + int Restore( CRestore &restore ); + static TYPEDESCRIPTION m_SaveData[]; + + void StartTask( Task_t *pTask ); + void RunTask( Task_t *pTask ); + int TakeDamage( entvars_t* pevInflictor, entvars_t* pevAttacker, float flDamage, int bitsDamageType ); + void TraceAttack( entvars_t *pevAttacker, float flDamage, Vector vecDir, TraceResult *ptr, int bitsDamageType); + + void PlayScriptedSentence( const char *pszSentence, float duration, float volume, float attenuation, BOOL bConcurrent, CBaseEntity *pListener ); + + EHANDLE m_hPlayer; + EHANDLE m_hTalkTarget; + float m_flTalkTime; +}; +LINK_ENTITY_TO_CLASS( monster_gman, CGMan ); + + +TYPEDESCRIPTION CGMan::m_SaveData[] = +{ + DEFINE_FIELD( CGMan, m_hTalkTarget, FIELD_EHANDLE ), + DEFINE_FIELD( CGMan, m_flTalkTime, FIELD_TIME ), +}; +IMPLEMENT_SAVERESTORE( CGMan, CBaseMonster ); + + +//========================================================= +// Classify - indicates this monster's place in the +// relationship table. +//========================================================= +int CGMan :: Classify ( void ) +{ + return CLASS_NONE; +} + +//========================================================= +// SetYawSpeed - allows each sequence to have a different +// turn rate associated with it. +//========================================================= +void CGMan :: SetYawSpeed ( void ) +{ + int ys; + + switch ( m_Activity ) + { + case ACT_IDLE: + default: + ys = 90; + } + + pev->yaw_speed = ys; +} + +//========================================================= +// HandleAnimEvent - catches the monster-specific messages +// that occur when tagged animation frames are played. +//========================================================= +void CGMan :: HandleAnimEvent( MonsterEvent_t *pEvent ) +{ + switch( pEvent->event ) + { + case 0: + default: + CBaseMonster::HandleAnimEvent( pEvent ); + break; + } +} + +//========================================================= +// ISoundMask - generic monster can't hear. +//========================================================= +int CGMan :: ISoundMask ( void ) +{ + return NULL; +} + +//========================================================= +// Spawn +//========================================================= +void CGMan :: Spawn() +{ + Precache(); + + SET_MODEL( ENT(pev), "models/gman.mdl" ); + UTIL_SetSize(pev, VEC_HUMAN_HULL_MIN, VEC_HUMAN_HULL_MAX); + + pev->solid = SOLID_SLIDEBOX; + pev->movetype = MOVETYPE_STEP; + m_bloodColor = DONT_BLEED; + pev->health = 100; + m_flFieldOfView = 0.5;// indicates the width of this monster's forward view cone ( as a dotproduct result ) + m_MonsterState = MONSTERSTATE_NONE; + + MonsterInit(); +} + +//========================================================= +// Precache - precaches all resources this monster needs +//========================================================= +void CGMan :: Precache() +{ + PRECACHE_MODEL( "models/gman.mdl" ); +} + + +//========================================================= +// AI Schedules Specific to this monster +//========================================================= + + +void CGMan :: StartTask( Task_t *pTask ) +{ + switch( pTask->iTask ) + { + case TASK_WAIT: + if (m_hPlayer == NULL) + { + m_hPlayer = UTIL_FindEntityByClassname( NULL, "player" ); + } + break; + } + CBaseMonster::StartTask( pTask ); +} + +void CGMan :: RunTask( Task_t *pTask ) +{ + switch( pTask->iTask ) + { + case TASK_WAIT: + // look at who I'm talking to + if (m_flTalkTime > gpGlobals->time && m_hTalkTarget != NULL) + { + float yaw = VecToYaw(m_hTalkTarget->pev->origin - pev->origin) - pev->angles.y; + + if (yaw > 180) yaw -= 360; + if (yaw < -180) yaw += 360; + + // turn towards vector + SetBoneController( 0, yaw ); + } + // look at player, but only if playing a "safe" idle animation + else if (m_hPlayer != NULL && pev->sequence == 0) + { + float yaw = VecToYaw(m_hPlayer->pev->origin - pev->origin) - pev->angles.y; + + if (yaw > 180) yaw -= 360; + if (yaw < -180) yaw += 360; + + // turn towards vector + SetBoneController( 0, yaw ); + } + else + { + SetBoneController( 0, 0 ); + } + CBaseMonster::RunTask( pTask ); + break; + default: + SetBoneController( 0, 0 ); + CBaseMonster::RunTask( pTask ); + break; + } +} + + +//========================================================= +// Override all damage +//========================================================= +int CGMan :: TakeDamage( entvars_t* pevInflictor, entvars_t* pevAttacker, float flDamage, int bitsDamageType ) +{ + pev->health = pev->max_health / 2; // always trigger the 50% damage aitrigger + + if ( flDamage > 0 ) + { + SetConditions(bits_COND_LIGHT_DAMAGE); + } + + if ( flDamage >= 20 ) + { + SetConditions(bits_COND_HEAVY_DAMAGE); + } + return TRUE; +} + + +void CGMan::TraceAttack( entvars_t *pevAttacker, float flDamage, Vector vecDir, TraceResult *ptr, int bitsDamageType) +{ + UTIL_Ricochet( ptr->vecEndPos, 1.0 ); + AddMultiDamage( pevAttacker, this, flDamage, bitsDamageType ); +} + + +void CGMan::PlayScriptedSentence( const char *pszSentence, float duration, float volume, float attenuation, BOOL bConcurrent, CBaseEntity *pListener ) +{ + CBaseMonster::PlayScriptedSentence( pszSentence, duration, volume, attenuation, bConcurrent, pListener ); + + m_flTalkTime = gpGlobals->time + duration; + m_hTalkTarget = pListener; +} diff --git a/spirit/h_ai.cpp b/dlls/h_ai.cpp similarity index 96% rename from spirit/h_ai.cpp rename to dlls/h_ai.cpp index 73a5064a..f24a988e 100644 --- a/spirit/h_ai.cpp +++ b/dlls/h_ai.cpp @@ -1,199 +1,199 @@ -/*** -* -* Copyright (c) 1996-2002, 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. -* -****/ -/* - - h_ai.cpp - halflife specific ai code - -*/ - - -#include "extdll.h" -#include "util.h" -#include "cbase.h" -#include "monsters.h" -#include "game.h" - -#define NUM_LATERAL_CHECKS 13 // how many checks are made on each side of a monster looking for lateral cover -#define NUM_LATERAL_LOS_CHECKS 6 // how many checks are made on each side of a monster looking for lateral cover - -//float flRandom = RANDOM_FLOAT(0,1); - -DLL_GLOBAL BOOL g_fDrawLines = FALSE; - -//========================================================= -// -// AI UTILITY FUNCTIONS -// -// !!!UNDONE - move CBaseMonster functions to monsters.cpp -//========================================================= - -//========================================================= -// FBoxVisible - a more accurate ( and slower ) version -// of FVisible. -// -// !!!UNDONE - make this CBaseMonster? -//========================================================= -BOOL FBoxVisible ( entvars_t *pevLooker, entvars_t *pevTarget, Vector &vecTargetOrigin, float flSize ) -{ - // don't look through water - if ((pevLooker->waterlevel != 3 && pevTarget->waterlevel == 3) - || (pevLooker->waterlevel == 3 && pevTarget->waterlevel == 0)) - return FALSE; - - TraceResult tr; - Vector vecLookerOrigin = pevLooker->origin + pevLooker->view_ofs;//look through the monster's 'eyes' - for (int i = 0; i < 5; i++) - { - Vector vecTarget = pevTarget->origin; - vecTarget.x += RANDOM_FLOAT( pevTarget->mins.x + flSize, pevTarget->maxs.x - flSize); - vecTarget.y += RANDOM_FLOAT( pevTarget->mins.y + flSize, pevTarget->maxs.y - flSize); - vecTarget.z += RANDOM_FLOAT( pevTarget->mins.z + flSize, pevTarget->maxs.z - flSize); - - UTIL_TraceLine(vecLookerOrigin, vecTarget, ignore_monsters, ignore_glass, ENT(pevLooker)/*pentIgnore*/, &tr); - - if (tr.flFraction == 1.0) - { - vecTargetOrigin = vecTarget; - return TRUE;// line of sight is valid. - } - } - return FALSE;// Line of sight is not established -} - -// -// VecCheckToss - returns the velocity at which an object should be lobbed from vecspot1 to land near vecspot2. -// returns g_vecZero if toss is not feasible. -// -Vector VecCheckToss ( entvars_t *pev, const Vector &vecSpot1, Vector vecSpot2, float flGravityAdj ) -{ - TraceResult tr; - Vector vecMidPoint;// halfway point between Spot1 and Spot2 - Vector vecApex;// highest point - Vector vecScale; - Vector vecGrenadeVel; - Vector vecTemp; - float flGravity = g_psv_gravity->value * flGravityAdj; - - if (vecSpot2.z - vecSpot1.z > 500) - { - // to high, fail - return g_vecZero; - } - - UTIL_MakeVectors (pev->angles); - - // toss a little bit to the left or right, not right down on the enemy's bean (head). - vecSpot2 = vecSpot2 + gpGlobals->v_right * ( RANDOM_FLOAT(-8,8) + RANDOM_FLOAT(-16,16) ); - vecSpot2 = vecSpot2 + gpGlobals->v_forward * ( RANDOM_FLOAT(-8,8) + RANDOM_FLOAT(-16,16) ); - - // calculate the midpoint and apex of the 'triangle' - // UNDONE: normalize any Z position differences between spot1 and spot2 so that triangle is always RIGHT - - // How much time does it take to get there? - - // get a rough idea of how high it can be thrown - vecMidPoint = vecSpot1 + (vecSpot2 - vecSpot1) * 0.5; - UTIL_TraceLine(vecMidPoint, vecMidPoint + Vector(0,0,500), ignore_monsters, ENT(pev), &tr); - vecMidPoint = tr.vecEndPos; - // (subtract 15 so the grenade doesn't hit the ceiling) - vecMidPoint.z -= 15; - - if (vecMidPoint.z < vecSpot1.z || vecMidPoint.z < vecSpot2.z) - { - // to not enough space, fail - return g_vecZero; - } - - // How high should the grenade travel to reach the apex - float distance1 = (vecMidPoint.z - vecSpot1.z); - float distance2 = (vecMidPoint.z - vecSpot2.z); - - // How long will it take for the grenade to travel this distance - float time1 = sqrt( distance1 / (0.5 * flGravity) ); - float time2 = sqrt( distance2 / (0.5 * flGravity) ); - - if (time1 < 0.1) - { - // too close - return g_vecZero; - } - - // how hard to throw sideways to get there in time. - vecGrenadeVel = (vecSpot2 - vecSpot1) / (time1 + time2); - // how hard upwards to reach the apex at the right time. - vecGrenadeVel.z = flGravity * time1; - - // find the apex - vecApex = vecSpot1 + vecGrenadeVel * time1; - vecApex.z = vecMidPoint.z; - - UTIL_TraceLine(vecSpot1, vecApex, dont_ignore_monsters, ENT(pev), &tr); - if (tr.flFraction != 1.0) - { - // fail! - return g_vecZero; - } - - // UNDONE: either ignore monsters or change it to not care if we hit our enemy - UTIL_TraceLine(vecSpot2, vecApex, ignore_monsters, ENT(pev), &tr); - if (tr.flFraction != 1.0) - { - // fail! - return g_vecZero; - } - - return vecGrenadeVel; -} - - -// -// VecCheckThrow - returns the velocity vector at which an object should be thrown from vecspot1 to hit vecspot2. -// returns g_vecZero if throw is not feasible. -// -Vector VecCheckThrow ( entvars_t *pev, const Vector &vecSpot1, Vector vecSpot2, float flSpeed, float flGravityAdj ) -{ - float flGravity = g_psv_gravity->value * flGravityAdj; - - Vector vecGrenadeVel = (vecSpot2 - vecSpot1); - - // throw at a constant time - float time = vecGrenadeVel.Length( ) / flSpeed; - vecGrenadeVel = vecGrenadeVel * (1.0 / time); - - // adjust upward toss to compensate for gravity loss - vecGrenadeVel.z += flGravity * time * 0.5; - - Vector vecApex = vecSpot1 + (vecSpot2 - vecSpot1) * 0.5; - vecApex.z += 0.5 * flGravity * (time * 0.5) * (time * 0.5); - - TraceResult tr; - UTIL_TraceLine(vecSpot1, vecApex, dont_ignore_monsters, ENT(pev), &tr); - if (tr.flFraction != 1.0) - { - // fail! - return g_vecZero; - } - - UTIL_TraceLine(vecSpot2, vecApex, ignore_monsters, ENT(pev), &tr); - if (tr.flFraction != 1.0) - { - // fail! - return g_vecZero; - } - - return vecGrenadeVel; -} - - +/*** +* +* Copyright (c) 1996-2002, 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. +* +****/ +/* + + h_ai.cpp - halflife specific ai code + +*/ + + +#include "extdll.h" +#include "util.h" +#include "cbase.h" +#include "monsters.h" +#include "game.h" + +#define NUM_LATERAL_CHECKS 13 // how many checks are made on each side of a monster looking for lateral cover +#define NUM_LATERAL_LOS_CHECKS 6 // how many checks are made on each side of a monster looking for lateral cover + +//float flRandom = RANDOM_FLOAT(0,1); + +DLL_GLOBAL BOOL g_fDrawLines = FALSE; + +//========================================================= +// +// AI UTILITY FUNCTIONS +// +// !!!UNDONE - move CBaseMonster functions to monsters.cpp +//========================================================= + +//========================================================= +// FBoxVisible - a more accurate ( and slower ) version +// of FVisible. +// +// !!!UNDONE - make this CBaseMonster? +//========================================================= +BOOL FBoxVisible ( entvars_t *pevLooker, entvars_t *pevTarget, Vector &vecTargetOrigin, float flSize ) +{ + // don't look through water + if ((pevLooker->waterlevel != 3 && pevTarget->waterlevel == 3) + || (pevLooker->waterlevel == 3 && pevTarget->waterlevel == 0)) + return FALSE; + + TraceResult tr; + Vector vecLookerOrigin = pevLooker->origin + pevLooker->view_ofs;//look through the monster's 'eyes' + for (int i = 0; i < 5; i++) + { + Vector vecTarget = pevTarget->origin; + vecTarget.x += RANDOM_FLOAT( pevTarget->mins.x + flSize, pevTarget->maxs.x - flSize); + vecTarget.y += RANDOM_FLOAT( pevTarget->mins.y + flSize, pevTarget->maxs.y - flSize); + vecTarget.z += RANDOM_FLOAT( pevTarget->mins.z + flSize, pevTarget->maxs.z - flSize); + + UTIL_TraceLine(vecLookerOrigin, vecTarget, ignore_monsters, ignore_glass, ENT(pevLooker)/*pentIgnore*/, &tr); + + if (tr.flFraction == 1.0) + { + vecTargetOrigin = vecTarget; + return TRUE;// line of sight is valid. + } + } + return FALSE;// Line of sight is not established +} + +// +// VecCheckToss - returns the velocity at which an object should be lobbed from vecspot1 to land near vecspot2. +// returns g_vecZero if toss is not feasible. +// +Vector VecCheckToss ( entvars_t *pev, const Vector &vecSpot1, Vector vecSpot2, float flGravityAdj ) +{ + TraceResult tr; + Vector vecMidPoint;// halfway point between Spot1 and Spot2 + Vector vecApex;// highest point + Vector vecScale; + Vector vecGrenadeVel; + Vector vecTemp; + float flGravity = g_psv_gravity->value * flGravityAdj; + + if (vecSpot2.z - vecSpot1.z > 500) + { + // to high, fail + return g_vecZero; + } + + UTIL_MakeVectors (pev->angles); + + // toss a little bit to the left or right, not right down on the enemy's bean (head). + vecSpot2 = vecSpot2 + gpGlobals->v_right * ( RANDOM_FLOAT(-8,8) + RANDOM_FLOAT(-16,16) ); + vecSpot2 = vecSpot2 + gpGlobals->v_forward * ( RANDOM_FLOAT(-8,8) + RANDOM_FLOAT(-16,16) ); + + // calculate the midpoint and apex of the 'triangle' + // UNDONE: normalize any Z position differences between spot1 and spot2 so that triangle is always RIGHT + + // How much time does it take to get there? + + // get a rough idea of how high it can be thrown + vecMidPoint = vecSpot1 + (vecSpot2 - vecSpot1) * 0.5; + UTIL_TraceLine(vecMidPoint, vecMidPoint + Vector(0,0,500), ignore_monsters, ENT(pev), &tr); + vecMidPoint = tr.vecEndPos; + // (subtract 15 so the grenade doesn't hit the ceiling) + vecMidPoint.z -= 15; + + if (vecMidPoint.z < vecSpot1.z || vecMidPoint.z < vecSpot2.z) + { + // to not enough space, fail + return g_vecZero; + } + + // How high should the grenade travel to reach the apex + float distance1 = (vecMidPoint.z - vecSpot1.z); + float distance2 = (vecMidPoint.z - vecSpot2.z); + + // How long will it take for the grenade to travel this distance + float time1 = sqrt( distance1 / (0.5 * flGravity) ); + float time2 = sqrt( distance2 / (0.5 * flGravity) ); + + if (time1 < 0.1) + { + // too close + return g_vecZero; + } + + // how hard to throw sideways to get there in time. + vecGrenadeVel = (vecSpot2 - vecSpot1) / (time1 + time2); + // how hard upwards to reach the apex at the right time. + vecGrenadeVel.z = flGravity * time1; + + // find the apex + vecApex = vecSpot1 + vecGrenadeVel * time1; + vecApex.z = vecMidPoint.z; + + UTIL_TraceLine(vecSpot1, vecApex, dont_ignore_monsters, ENT(pev), &tr); + if (tr.flFraction != 1.0) + { + // fail! + return g_vecZero; + } + + // UNDONE: either ignore monsters or change it to not care if we hit our enemy + UTIL_TraceLine(vecSpot2, vecApex, ignore_monsters, ENT(pev), &tr); + if (tr.flFraction != 1.0) + { + // fail! + return g_vecZero; + } + + return vecGrenadeVel; +} + + +// +// VecCheckThrow - returns the velocity vector at which an object should be thrown from vecspot1 to hit vecspot2. +// returns g_vecZero if throw is not feasible. +// +Vector VecCheckThrow ( entvars_t *pev, const Vector &vecSpot1, Vector vecSpot2, float flSpeed, float flGravityAdj ) +{ + float flGravity = g_psv_gravity->value * flGravityAdj; + + Vector vecGrenadeVel = (vecSpot2 - vecSpot1); + + // throw at a constant time + float time = vecGrenadeVel.Length( ) / flSpeed; + vecGrenadeVel = vecGrenadeVel * (1.0 / time); + + // adjust upward toss to compensate for gravity loss + vecGrenadeVel.z += flGravity * time * 0.5; + + Vector vecApex = vecSpot1 + (vecSpot2 - vecSpot1) * 0.5; + vecApex.z += 0.5 * flGravity * (time * 0.5) * (time * 0.5); + + TraceResult tr; + UTIL_TraceLine(vecSpot1, vecApex, dont_ignore_monsters, ENT(pev), &tr); + if (tr.flFraction != 1.0) + { + // fail! + return g_vecZero; + } + + UTIL_TraceLine(vecSpot2, vecApex, ignore_monsters, ENT(pev), &tr); + if (tr.flFraction != 1.0) + { + // fail! + return g_vecZero; + } + + return vecGrenadeVel; +} + + diff --git a/spirit/h_battery.cpp b/dlls/h_battery.cpp similarity index 78% rename from spirit/h_battery.cpp rename to dlls/h_battery.cpp index 8f006f49..3f2a9a29 100644 --- a/spirit/h_battery.cpp +++ b/dlls/h_battery.cpp @@ -26,7 +26,6 @@ #include "saverestore.h" #include "skill.h" #include "gamerules.h" -#include "player.h" class CRecharge : public CBaseToggle { @@ -40,7 +39,6 @@ public: virtual int ObjectCaps( void ) { return (CBaseToggle :: ObjectCaps() | FCAP_CONTINUOUS_USE) & ~FCAP_ACROSS_TRANSITION; } virtual int Save( CSave &save ); virtual int Restore( CRestore &restore ); - virtual STATE GetState( void ); static TYPEDESCRIPTION m_SaveData[]; @@ -91,14 +89,11 @@ void CRecharge::Spawn() pev->solid = SOLID_BSP; pev->movetype = MOVETYPE_PUSH; - UTIL_SetOrigin(this, pev->origin); // set size and link into world + UTIL_SetOrigin(pev, pev->origin); // set size and link into world UTIL_SetSize(pev, pev->mins, pev->maxs); SET_MODEL(ENT(pev), STRING(pev->model) ); m_iJuice = gSkillData.suitchargerCapacity; pev->frame = 0; - //LRC - if (m_iStyle >= 32) LIGHT_STYLE(m_iStyle, "a"); - else if (m_iStyle <= -32) LIGHT_STYLE(-m_iStyle, "z"); } void CRecharge::Precache() @@ -119,16 +114,11 @@ void CRecharge::Use( CBaseEntity *pActivator, CBaseEntity *pCaller, USE_TYPE use if (m_iJuice <= 0) { pev->frame = 1; - //LRC - if (m_iStyle >= 32) LIGHT_STYLE(m_iStyle, "z"); - else if (m_iStyle <= -32) LIGHT_STYLE(-m_iStyle, "a"); Off(); } - - CBasePlayer *pPlayer = (CBasePlayer *)pActivator; - + // if the player doesn't have the suit, or there is no juice left, make the deny noise - if ((m_iJuice <= 0) || (!(pPlayer->pev->weapons & ITEM_SUIT))) + if ((m_iJuice <= 0) || (!(pActivator->pev->weapons & (1<time) { @@ -138,8 +128,8 @@ void CRecharge::Use( CBaseEntity *pActivator, CBaseEntity *pCaller, USE_TYPE use return; } - SetNextThink( 0.25 ); - SetThink(&CRecharge::Off); + pev->nextthink = pev->ltime + 0.25; + SetThink(Off); // Time to recharge yet? @@ -172,9 +162,13 @@ void CRecharge::Use( CBaseEntity *pActivator, CBaseEntity *pCaller, USE_TYPE use // charge the player - if (m_hActivator->TakeArmor( 1 )) + if (m_hActivator->pev->armorvalue < 100) { m_iJuice--; + m_hActivator->pev->armorvalue += 1; + + if (m_hActivator->pev->armorvalue > 100) + m_hActivator->pev->armorvalue = 100; } // govern the rate of charge @@ -185,10 +179,7 @@ void CRecharge::Recharge(void) { m_iJuice = gSkillData.suitchargerCapacity; pev->frame = 0; - //LRC - if (m_iStyle >= 32) LIGHT_STYLE(m_iStyle, "a"); - else if (m_iStyle <= -32) LIGHT_STYLE(-m_iStyle, "z"); - SetThink(&CRecharge:: SUB_DoNothing ); + SetThink( SUB_DoNothing ); } void CRecharge::Off(void) @@ -201,19 +192,9 @@ void CRecharge::Off(void) if ((!m_iJuice) && ( ( m_iReactivate = g_pGameRules->FlHEVChargerRechargeTime() ) > 0) ) { - SetNextThink( m_iReactivate ); - SetThink(&CRecharge::Recharge); + pev->nextthink = pev->ltime + m_iReactivate; + SetThink(Recharge); } else - SetThink(&CRecharge:: SUB_DoNothing ); -} - -STATE CRecharge::GetState( void ) -{ - if (m_iOn == 2) - return STATE_IN_USE; - else if (m_iJuice) - return STATE_ON; - else - return STATE_OFF; -} + SetThink( SUB_DoNothing ); +} \ No newline at end of file diff --git a/spirit/h_cine.cpp b/dlls/h_cine.cpp similarity index 86% rename from spirit/h_cine.cpp rename to dlls/h_cine.cpp index 6389eddf..7f681922 100644 --- a/spirit/h_cine.cpp +++ b/dlls/h_cine.cpp @@ -27,7 +27,7 @@ #include "cbase.h" #include "monsters.h" #include "decals.h" -#include "weapons.h" + class CLegacyCineMonster : public CBaseMonster { @@ -124,8 +124,8 @@ void CLegacyCineMonster :: CineSpawn( char *szModel ) // if no targetname, start now if ( FStringNull(pev->targetname) ) { - SetThink(&CLegacyCineMonster :: CineThink ); - AbsoluteNextThink( m_fNextThink + 0.1 ); + SetThink( CineThink ); + pev->nextthink += 1.0; } } @@ -136,8 +136,8 @@ void CLegacyCineMonster :: CineSpawn( char *szModel ) void CLegacyCineMonster :: Use( CBaseEntity *pActivator, CBaseEntity *pCaller, USE_TYPE useType, float value ) { pev->animtime = 0; // reset the sequence - SetThink(&CLegacyCineMonster :: CineThink ); - SetNextThink( 0 ); + SetThink( CineThink ); + pev->nextthink = gpGlobals->time; } // @@ -145,7 +145,7 @@ void CLegacyCineMonster :: Use( CBaseEntity *pActivator, CBaseEntity *pCaller, U // void CLegacyCineMonster :: Die( void ) { - SetThink(&CLegacyCineMonster :: SUB_Remove ); + SetThink( SUB_Remove ); } // @@ -167,7 +167,7 @@ void CLegacyCineMonster :: CineThink( void ) if (!pev->animtime) ResetSequenceInfo( ); - SetNextThink( 1.0 ); + pev->nextthink = gpGlobals->time + 1.0; if (pev->spawnflags != 0 && m_fSequenceFinished) { @@ -197,7 +197,7 @@ void CCineBlood :: BloodGush ( void ) { Vector vecSplatDir; TraceResult tr; - SetNextThink( 0.1 ); + pev->nextthink = gpGlobals->time + 0.1; UTIL_MakeVectors(pev->angles); if ( pev->health-- < 0 ) @@ -221,22 +221,21 @@ void CCineBlood :: BloodGush ( void ) if ( tr.flFraction != 1.0 ) { // Decal with a bloodsplat - CBaseEntity *pHit = CBaseEntity::Instance( tr.pHit ); - PLAYBACK_EVENT_FULL( FEV_RELIABLE|FEV_GLOBAL, edict(), m_usDecals, 0.0, (float *)&tr.vecEndPos, (float *)&g_vecZero, 0.0, 0.0, pHit->entindex(), 1, 0, 0 ); + UTIL_BloodDecalTrace( &tr, BLOOD_COLOR_RED ); } } } void CCineBlood :: BloodStart ( CBaseEntity *pActivator, CBaseEntity *pCaller, USE_TYPE useType, float value ) { - SetThink(&CCineBlood :: BloodGush ); - SetNextThink( 0 );// now! + SetThink( BloodGush ); + pev->nextthink = gpGlobals->time;// now! } void CCineBlood :: Spawn ( void ) { pev->solid = SOLID_NOT; - SetUse(&CCineBlood :: BloodStart ); + SetUse ( BloodStart ); pev->health = 20;//hacked health to count iterations } diff --git a/spirit/h_cycler.cpp b/dlls/h_cycler.cpp similarity index 90% rename from spirit/h_cycler.cpp rename to dlls/h_cycler.cpp index 909231c5..a306359f 100644 --- a/spirit/h_cycler.cpp +++ b/dlls/h_cycler.cpp @@ -127,7 +127,7 @@ void CCycler :: Spawn( ) m_flFrameRate = 75; m_flGroundSpeed = 0; - AbsoluteNextThink( m_fNextThink + 1.0 ); + pev->nextthink += 1.0; ResetSequenceInfo( ); @@ -150,7 +150,7 @@ void CCycler :: Spawn( ) // void CCycler :: Think( void ) { - SetNextThink( 0.1 ); + pev->nextthink = gpGlobals->time + 0.1; if (m_animate) { @@ -206,7 +206,7 @@ int CCycler :: TakeDamage( entvars_t* pevInflictor, entvars_t* pevAttacker, floa pev->framerate = 1.0; StudioFrameAdvance ( 0.1 ); pev->framerate = 0; - ALERT( at_debug, "sequence: %d, frame %.0f\n", pev->sequence, pev->frame ); + ALERT( at_console, "sequence: %d, frame %.0f\n", pev->sequence, pev->frame ); } return 0; @@ -249,13 +249,13 @@ IMPLEMENT_SAVERESTORE( CCyclerSprite, CBaseEntity ); void CCyclerSprite::Spawn( void ) { - pev->solid = SOLID_SLIDEBOX; + pev->solid = SOLID_SLIDEBOX; pev->movetype = MOVETYPE_NONE; pev->takedamage = DAMAGE_YES; pev->effects = 0; pev->frame = 0; - SetNextThink( 0.1 ); + pev->nextthink = gpGlobals->time + 0.1; m_animate = 1; m_lastTime = gpGlobals->time; @@ -271,7 +271,7 @@ void CCyclerSprite::Think( void ) if ( ShouldAnimate() ) Animate( pev->framerate * (gpGlobals->time - m_lastTime) ); - SetNextThink( 0.1 ); + pev->nextthink = gpGlobals->time + 0.1; m_lastTime = gpGlobals->time; } @@ -279,7 +279,7 @@ void CCyclerSprite::Think( void ) void CCyclerSprite::Use( CBaseEntity *pActivator, CBaseEntity *pCaller, USE_TYPE useType, float value ) { m_animate = !m_animate; - ALERT( at_debug, "Sprite: %s\n", STRING(pev->model) ); + ALERT( at_console, "Sprite: %s\n", STRING(pev->model) ); } @@ -301,7 +301,10 @@ void CCyclerSprite::Animate( float frames ) -//Weapon Cycler + + + + class CWeaponCycler : public CBasePlayerWeapon { public: @@ -329,9 +332,9 @@ void CWeaponCycler::Spawn( ) m_iszModel = pev->model; m_iModel = pev->modelindex; - UTIL_SetOrigin( this, pev->origin ); + UTIL_SetOrigin( pev, pev->origin ); UTIL_SetSize(pev, Vector(-16, -16, 0), Vector(16, 16, 16)); - SetTouch(&CWeaponCycler:: DefaultTouch ); + SetTouch( DefaultTouch ); } @@ -381,6 +384,9 @@ void CWeaponCycler::SecondaryAttack( void ) m_flNextSecondaryAttack = gpGlobals->time + 0.3; } + + + // Flaming Wreakage class CWreckage : public CBaseMonster { @@ -411,7 +417,7 @@ void CWreckage::Spawn( void ) pev->effects = 0; pev->frame = 0; - SetNextThink( 0.1 ); + pev->nextthink = gpGlobals->time + 0.1; if (pev->model) { @@ -432,7 +438,7 @@ void CWreckage::Precache( ) void CWreckage::Think( void ) { StudioFrameAdvance( ); - SetNextThink( 0.2 ); + pev->nextthink = gpGlobals->time + 0.2; if (pev->dmgtime) { @@ -453,7 +459,7 @@ void CWreckage::Think( void ) VecSrc.y = RANDOM_FLOAT( pev->absmin.y, pev->absmax.y ); VecSrc.z = RANDOM_FLOAT( pev->absmin.z, pev->absmax.z ); - MESSAGE_BEGIN( MSG_PVS, gmsgTempEntity, VecSrc ); + MESSAGE_BEGIN( MSG_PVS, SVC_TEMPENTITY, VecSrc ); WRITE_BYTE( TE_SMOKE ); WRITE_COORD( VecSrc.x ); WRITE_COORD( VecSrc.y ); diff --git a/spirit/h_export.cpp b/dlls/h_export.cpp similarity index 72% rename from spirit/h_export.cpp rename to dlls/h_export.cpp index a16a5d41..4d3914de 100644 --- a/spirit/h_export.cpp +++ b/dlls/h_export.cpp @@ -21,23 +21,31 @@ */ #include "extdll.h" +#include "util.h" + +#include "cbase.h" // Holds engine functionality callbacks -BOOL WINAPI DllMain( HINSTANCE hinstDLL, DWORD fdwReason, LPVOID lpvReserved ) -{ - return TRUE; -} - enginefuncs_t g_engfuncs; globalvars_t *gpGlobals; -#ifdef _WIN32 -void DLLEXPORT GiveFnptrsToDll( enginefuncs_t* pengfuncsFromEngine, globalvars_t *pGlobals ) -#else -extern "C" void DLLEXPORT GiveFnptrsToDll( enginefuncs_t* pengfuncsFromEngine, globalvars_t *pGlobals ) -#endif +// Required DLL entry point +BOOL WINAPI DllMain( HINSTANCE hinstDLL, DWORD fdwReason, LPVOID lpvReserved ) { - memcpy(&g_engfuncs, pengfuncsFromEngine, sizeof(enginefuncs_t)); + if ( fdwReason == DLL_PROCESS_ATTACH ) + { + + } + else if ( fdwReason == DLL_PROCESS_DETACH ) + { + + } + return TRUE; +} + +void __stdcall GiveFnptrsToDll( enginefuncs_t* pengfuncsFromEngine, globalvars_t *pGlobals ) +{ + memcpy( &g_engfuncs, pengfuncsFromEngine, sizeof( enginefuncs_t )); gpGlobals = pGlobals; -} \ No newline at end of file +} diff --git a/spirit/handgrenade.cpp b/dlls/handgrenade.cpp similarity index 72% rename from spirit/handgrenade.cpp rename to dlls/handgrenade.cpp index b5c9001b..007ad662 100644 --- a/spirit/handgrenade.cpp +++ b/dlls/handgrenade.cpp @@ -34,19 +34,6 @@ enum handgrenade_e { HANDGRENADE_DRAW }; -class CHandGrenade : public CBasePlayerWeapon -{ -public: - void Spawn( void ); - void Precache( void ); - int GetItemInfo(ItemInfo *p); - - void PrimaryAttack( void ); - BOOL Deploy( void ); - BOOL CanHolster( void ); - void Holster( ); - void WeaponIdle( void ); -}; LINK_ENTITY_TO_CLASS( weapon_handgrenade, CHandGrenade ); @@ -57,8 +44,12 @@ void CHandGrenade::Spawn( ) m_iId = WEAPON_HANDGRENADE; SET_MODEL(ENT(pev), "models/w_grenade.mdl"); +#ifndef CLIENT_DLL pev->dmg = gSkillData.plrDmgHandGrenade; +#endif + m_iDefaultAmmo = HANDGRENADE_DEFAULT_GIVE; + FallInit();// get ready to fall down. } @@ -68,7 +59,6 @@ void CHandGrenade::Precache( void ) PRECACHE_MODEL("models/w_grenade.mdl"); PRECACHE_MODEL("models/v_grenade.mdl"); PRECACHE_MODEL("models/p_grenade.mdl"); - PRECACHE_MODEL("models/hgrenade.mdl"); // physic model. Xash3D requires it } int CHandGrenade::GetItemInfo(ItemInfo *p) @@ -91,8 +81,8 @@ int CHandGrenade::GetItemInfo(ItemInfo *p) BOOL CHandGrenade::Deploy( ) { - m_iChargeLevel = 0; - return DefaultDeploy( "models/v_grenade.mdl", "models/p_grenade.mdl", HANDGRENADE_DRAW, "crowbar", 0.6 ); + m_flReleaseThrow = -1; + return DefaultDeploy( "models/v_grenade.mdl", "models/p_grenade.mdl", HANDGRENADE_DRAW, "crowbar" ); } BOOL CHandGrenade::CanHolster( void ) @@ -101,19 +91,22 @@ BOOL CHandGrenade::CanHolster( void ) return ( m_flStartThrow == 0 ); } -void CHandGrenade::Holster( ) +void CHandGrenade::Holster( int skiplocal /* = 0 */ ) { m_pPlayer->m_flNextAttack = UTIL_WeaponTimeBase() + 0.5; if ( m_pPlayer->m_rgAmmo[ m_iPrimaryAmmoType ] ) + { SendWeaponAnim( HANDGRENADE_HOLSTER ); + } else { // no more grenades! m_pPlayer->pev->weapons &= ~(1<nextthink = gpGlobals->time + 0.1; } + EMIT_SOUND(ENT(m_pPlayer->pev), CHAN_WEAPON, "common/null.wav", 1.0, ATTN_NORM); } @@ -121,7 +114,7 @@ void CHandGrenade::PrimaryAttack() { if ( !m_flStartThrow && m_pPlayer->m_rgAmmo[ m_iPrimaryAmmoType ] > 0 ) { - m_flStartThrow = UTIL_WeaponTimeBase(); + m_flStartThrow = gpGlobals->time; m_flReleaseThrow = 0; SendWeaponAnim( HANDGRENADE_PINPULL ); @@ -132,54 +125,56 @@ void CHandGrenade::PrimaryAttack() void CHandGrenade::WeaponIdle( void ) { - if ( m_flTimeUpdate < UTIL_WeaponTimeBase() && m_iChargeLevel ) - { - // we've finished the throw, restart. - m_flStartThrow = 0; - m_iChargeLevel = 0; - if ( m_pPlayer->m_rgAmmo[ m_iPrimaryAmmoType ] ) - SendWeaponAnim( HANDGRENADE_DRAW ); - else - { - RetireWeapon(); - return; - } + if ( m_flReleaseThrow == 0 && m_flStartThrow ) + m_flReleaseThrow = gpGlobals->time; - m_flTimeWeaponIdle = UTIL_WeaponTimeBase() + RANDOM_LONG( 10, 15 ); + if ( m_flTimeWeaponIdle > UTIL_WeaponTimeBase() ) return; - } - if ( m_flTimeWeaponIdle > UTIL_WeaponTimeBase() ) return; if ( m_flStartThrow ) { Vector angThrow = m_pPlayer->pev->v_angle + m_pPlayer->pev->punchangle; if ( angThrow.x < 0 ) angThrow.x = -10 + angThrow.x * ( ( 90 - 10 ) / 90.0 ); - else angThrow.x = -10 + angThrow.x * ( ( 90 + 10 ) / 90.0 ); + else + angThrow.x = -10 + angThrow.x * ( ( 90 + 10 ) / 90.0 ); float flVel = ( 90 - angThrow.x ) * 4; - if ( flVel > 500 ) flVel = 500; + if ( flVel > 500 ) + flVel = 500; UTIL_MakeVectors( angThrow ); + Vector vecSrc = m_pPlayer->pev->origin + m_pPlayer->pev->view_ofs + gpGlobals->v_forward * 16; + Vector vecThrow = gpGlobals->v_forward * flVel + m_pPlayer->pev->velocity; // alway explode 3 seconds after the pin was pulled - float time = m_flStartThrow - UTIL_WeaponTimeBase() + 3.0; - if (time < 0) time = 0; + float time = m_flStartThrow - gpGlobals->time + 3.0; + if (time < 0) + time = 0; CGrenade::ShootTimed( m_pPlayer->pev, vecSrc, vecThrow, time ); - if ( flVel < 500 ) SendWeaponAnim( HANDGRENADE_THROW1 ); - else if ( flVel < 1000 ) SendWeaponAnim( HANDGRENADE_THROW2 ); - else SendWeaponAnim( HANDGRENADE_THROW3 ); + if ( flVel < 500 ) + { + SendWeaponAnim( HANDGRENADE_THROW1 ); + } + else if ( flVel < 1000 ) + { + SendWeaponAnim( HANDGRENADE_THROW2 ); + } + else + { + SendWeaponAnim( HANDGRENADE_THROW3 ); + } // player "shoot" animation m_pPlayer->SetAnimation( PLAYER_ATTACK1 ); + m_flReleaseThrow = 0; m_flStartThrow = 0; - m_iChargeLevel = 1; m_flNextPrimaryAttack = UTIL_WeaponTimeBase() + 0.5; m_flTimeWeaponIdle = UTIL_WeaponTimeBase() + 0.5; @@ -192,24 +187,43 @@ void CHandGrenade::WeaponIdle( void ) // animation, weapon idle will automatically retire the weapon for us. m_flTimeWeaponIdle = m_flNextSecondaryAttack = m_flNextPrimaryAttack = UTIL_WeaponTimeBase() + 0.5;// ensure that the animation can finish playing } - m_flTimeUpdate = m_flNextSecondaryAttack = m_flNextPrimaryAttack = UTIL_WeaponTimeBase() + 1.0; - m_flTimeWeaponIdle = UTIL_WeaponTimeBase() + 3.0; return; } + else if ( m_flReleaseThrow > 0 ) + { + // we've finished the throw, restart. + m_flStartThrow = 0; + + if ( m_pPlayer->m_rgAmmo[ m_iPrimaryAmmoType ] ) + { + SendWeaponAnim( HANDGRENADE_DRAW ); + } + else + { + RetireWeapon(); + return; + } + + m_flTimeWeaponIdle = UTIL_WeaponTimeBase() + UTIL_SharedRandomFloat( m_pPlayer->random_seed, 10, 15 ); + m_flReleaseThrow = -1; + return; + } + if ( m_pPlayer->m_rgAmmo[m_iPrimaryAmmoType] ) { int iAnim; - float flRand = RANDOM_FLOAT( 0, 1 ); + float flRand = UTIL_SharedRandomFloat( m_pPlayer->random_seed, 0, 1 ); if (flRand <= 0.75) { iAnim = HANDGRENADE_IDLE; - m_flTimeWeaponIdle = UTIL_WeaponTimeBase() + RANDOM_LONG(10, 15 );// how long till we do this again. + m_flTimeWeaponIdle = UTIL_WeaponTimeBase() + UTIL_SharedRandomFloat( m_pPlayer->random_seed, 10, 15 );// how long till we do this again. } else { iAnim = HANDGRENADE_FIDGET; m_flTimeWeaponIdle = UTIL_WeaponTimeBase() + 75.0 / 30.0; } + SendWeaponAnim( iAnim ); } } diff --git a/spirit/hassassin.cpp b/dlls/hassassin.cpp similarity index 91% rename from spirit/hassassin.cpp rename to dlls/hassassin.cpp index b15c7982..da8f677a 100644 --- a/spirit/hassassin.cpp +++ b/dlls/hassassin.cpp @@ -1,1069 +1,1015 @@ -/*** -* -* Copyright (c) 1996-2002, 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. -* -* This source code contains proprietary and confidential information of -* Valve LLC and its suppliers. Access to this code is restricted to -* persons who have executed a written SDK license with Valve. Any access, -* use or distribution of this code by or to any unlicensed person is illegal. -* -****/ -#if !defined( OEM_BUILD ) && !defined( HLDEMO_BUILD ) - -//========================================================= -// hassassin - Human assassin, fast and stealthy -//========================================================= - -#include "extdll.h" -#include "util.h" -#include "cbase.h" -#include "monsters.h" -#include "schedule.h" -#include "squadmonster.h" -#include "weapons.h" -#include "soundent.h" -#include "scripted.h" -#include "game.h" - -extern DLL_GLOBAL int g_iSkillLevel; - -//========================================================= -// monster-specific schedule types -//========================================================= -enum -{ - SCHED_ASSASSIN_EXPOSED = LAST_COMMON_SCHEDULE + 1,// cover was blown. - SCHED_ASSASSIN_JUMP, // fly through the air - SCHED_ASSASSIN_JUMP_ATTACK, // fly through the air and shoot - SCHED_ASSASSIN_JUMP_LAND, // hit and run away -}; - -//========================================================= -// monster-specific tasks -//========================================================= - -enum -{ - TASK_ASSASSIN_FALL_TO_GROUND = LAST_COMMON_TASK + 1, // falling and waiting to hit ground -}; - - -//========================================================= -// Monster's Anim Events Go Here -//========================================================= -#define ASSASSIN_AE_SHOOT1 1 -#define ASSASSIN_AE_TOSS1 2 -#define ASSASSIN_AE_JUMP 3 - - -#define bits_MEMORY_BADJUMP (bits_MEMORY_CUSTOM1) - -class CHAssassin : public CBaseMonster -{ -public: - void Spawn( void ); - void Precache( void ); - void SetYawSpeed ( void ); - int Classify ( void ); - int ISoundMask ( void); - void Shoot( void ); - void HandleAnimEvent( MonsterEvent_t *pEvent ); - Schedule_t* GetSchedule ( void ); - Schedule_t* GetScheduleOfType ( int Type ); - BOOL CheckMeleeAttack1 ( float flDot, float flDist ); // jump - // BOOL CheckMeleeAttack2 ( float flDot, float flDist ); - BOOL CheckRangeAttack1 ( float flDot, float flDist ); // shoot - BOOL CheckRangeAttack2 ( float flDot, float flDist ); // throw grenade - void StartTask ( Task_t *pTask ); - void RunAI( void ); - void RunTask ( Task_t *pTask ); - void DeathSound ( void ); - void IdleSound ( void ); - CUSTOM_SCHEDULES; - - int Save( CSave &save ); - int Restore( CRestore &restore ); - static TYPEDESCRIPTION m_SaveData[]; - - float m_flLastShot; - float m_flDiviation; - - float m_flNextJump; - Vector m_vecJumpVelocity; - - float m_flNextGrenadeCheck; - Vector m_vecTossVelocity; - BOOL m_fThrowGrenade; - - int m_iTargetRanderamt; - - int m_iFrustration; - - int m_iShell; -}; -LINK_ENTITY_TO_CLASS( monster_human_assassin, CHAssassin ); - - -TYPEDESCRIPTION CHAssassin::m_SaveData[] = -{ - DEFINE_FIELD( CHAssassin, m_flLastShot, FIELD_TIME ), - DEFINE_FIELD( CHAssassin, m_flDiviation, FIELD_FLOAT ), - - DEFINE_FIELD( CHAssassin, m_flNextJump, FIELD_TIME ), - DEFINE_FIELD( CHAssassin, m_vecJumpVelocity, FIELD_VECTOR ), - - DEFINE_FIELD( CHAssassin, m_flNextGrenadeCheck, FIELD_TIME ), - DEFINE_FIELD( CHAssassin, m_vecTossVelocity, FIELD_VECTOR ), - DEFINE_FIELD( CHAssassin, m_fThrowGrenade, FIELD_BOOLEAN ), - - DEFINE_FIELD( CHAssassin, m_iTargetRanderamt, FIELD_INTEGER ), - DEFINE_FIELD( CHAssassin, m_iFrustration, FIELD_INTEGER ), -}; - -IMPLEMENT_SAVERESTORE( CHAssassin, CBaseMonster ); - - -//========================================================= -// DieSound -//========================================================= -void CHAssassin :: DeathSound ( void ) -{ -} - -//========================================================= -// IdleSound -//========================================================= -void CHAssassin :: IdleSound ( void ) -{ -} - -//========================================================= -// ISoundMask - returns a bit mask indicating which types -// of sounds this monster regards. -//========================================================= -int CHAssassin :: ISoundMask ( void) -{ - return bits_SOUND_WORLD | - bits_SOUND_COMBAT | - bits_SOUND_DANGER | - bits_SOUND_PLAYER; -} - - -//========================================================= -// Classify - indicates this monster's place in the -// relationship table. -//========================================================= -int CHAssassin :: Classify ( void ) -{ - return m_iClass?m_iClass:CLASS_HUMAN_MILITARY; -} - -//========================================================= -// SetYawSpeed - allows each sequence to have a different -// turn rate associated with it. -//========================================================= -void CHAssassin :: SetYawSpeed ( void ) -{ - int ys; - - switch ( m_Activity ) - { - case ACT_TURN_LEFT: - case ACT_TURN_RIGHT: - ys = 360; - break; - default: - ys = 360; - break; - } - - pev->yaw_speed = ys; -} - - -//========================================================= -// Shoot -//========================================================= -void CHAssassin :: Shoot ( void ) -{ - if (m_hEnemy == NULL && !m_pCine) //LRC - { - return; - } - - Vector vecShootOrigin = GetGunPosition(); - Vector vecShootDir = ShootAtEnemy( vecShootOrigin ); - - if (m_flLastShot + 2 < gpGlobals->time) - { - m_flDiviation = 0.10; - } - else - { - m_flDiviation -= 0.01; - if (m_flDiviation < 0.02) - m_flDiviation = 0.02; - } - m_flLastShot = gpGlobals->time; - - UTIL_MakeVectors ( pev->angles ); - - Vector vecShellVelocity = gpGlobals->v_right * RANDOM_FLOAT(40,90) + gpGlobals->v_up * RANDOM_FLOAT(75,200) + gpGlobals->v_forward * RANDOM_FLOAT(-40, 40); - EjectBrass ( pev->origin + gpGlobals->v_up * 32 + gpGlobals->v_forward * 12, vecShellVelocity, pev->angles.y, m_iShell, TE_BOUNCE_SHELL); - FireBullets(1, vecShootOrigin, vecShootDir, Vector( m_flDiviation, m_flDiviation, m_flDiviation ), 2048, BULLET_MONSTER_9MM ); // shoot +-8 degrees - - switch(RANDOM_LONG(0,1)) - { - case 0: - EMIT_SOUND(ENT(pev), CHAN_WEAPON, "weapons/pl_gun1.wav", RANDOM_FLOAT(0.6, 0.8), ATTN_NORM); - break; - case 1: - EMIT_SOUND(ENT(pev), CHAN_WEAPON, "weapons/pl_gun2.wav", RANDOM_FLOAT(0.6, 0.8), ATTN_NORM); - break; - } - - pev->effects |= EF_MUZZLEFLASH; - - Vector angDir = UTIL_VecToAngles( vecShootDir ); - SetBlending( 0, angDir.x ); - - m_cAmmoLoaded--; -} - - -//========================================================= -// HandleAnimEvent - catches the monster-specific messages -// that occur when tagged animation frames are played. -// -// Returns number of events handled, 0 if none. -//========================================================= -void CHAssassin :: HandleAnimEvent( MonsterEvent_t *pEvent ) -{ - switch( pEvent->event ) - { - case ASSASSIN_AE_SHOOT1: - Shoot( ); - break; - case ASSASSIN_AE_TOSS1: - { - Vector vecGunPosition = pev->origin + gpGlobals->v_forward * 34 + Vector (0, 0, 32); - UTIL_MakeVectors( pev->angles ); - //LRC - if (m_pCine && m_pCine->IsAction()) - { - Vector vecToss; - if (m_pCine->PreciseAttack() && m_hTargetEnt != NULL) - { - vecToss = VecCheckToss( pev, vecGunPosition, m_hTargetEnt->pev->origin, 0.5 ); - //if (vecToss != g_vecZero) - // ALERT(at_console,"Assassin %s throws precise grenade\n",STRING(pev->targetname)); - } - else - { - //ALERT(at_console,"Assassin %s throws nonprecise grenade\n",STRING(pev->targetname)); - // what speed would be best to use, here? Borrowing the hgrunt grenade speed seems silly... - vecToss = ((gpGlobals->v_forward*0.5)+(gpGlobals->v_up*0.5)).Normalize()*gSkillData.hgruntGrenadeSpeed; - } - CGrenade::ShootTimed( pev, vecGunPosition, vecToss, 2.0 ); - } - else - CGrenade::ShootTimed( pev, vecGunPosition, m_vecTossVelocity, 2.0 ); - - m_flNextGrenadeCheck = gpGlobals->time + 6;// wait six seconds before even looking again to see if a grenade can be thrown. - m_fThrowGrenade = FALSE; - // !!!LATER - when in a group, only try to throw grenade if ordered. - } - break; - case ASSASSIN_AE_JUMP: - { - // ALERT( at_console, "jumping"); - UTIL_MakeAimVectors( pev->angles ); - pev->movetype = MOVETYPE_TOSS; - pev->flags &= ~FL_ONGROUND; - if (m_pCine) //LRC... - { - pev->velocity = g_vecZero; - if (m_pCine->PreciseAttack() && m_hTargetEnt != NULL) - { - Vector vecTemp = m_hTargetEnt->pev->origin; - vecTemp.y = vecTemp.y + 50; // put her feet on the target. - pev->velocity = VecCheckToss( pev, pev->origin, vecTemp, 0.5 ); - //if (pev->velocity != g_vecZero) - // ALERT(at_console,"Precise jump for assassin %s\n",STRING(pev->targetname)); - //else - // ALERT(at_console,"Precise jump failed. "); - } - if (pev->velocity == g_vecZero) - { // just jump, it doesn't matter where to. - //ALERT(at_console,"Nonprecise jump for assassin %s\n",STRING(pev->targetname)); - float flGravity = g_psv_gravity->value; - float time = sqrt( 160 / (0.5 * flGravity)); - float speed = flGravity * time / 160; - UTIL_MakeVectors(pev->angles); - Vector vecDest = pev->origin + (gpGlobals->v_forward * 32); - vecDest.z += 160; // don't forget to jump into the air, now... - pev->velocity= (vecDest - pev->origin) * speed; - } - } - else - pev->velocity = m_vecJumpVelocity; - m_flNextJump = gpGlobals->time + 3.0; - } - return; - default: - CBaseMonster::HandleAnimEvent( pEvent ); - break; - } -} - -//========================================================= -// Spawn -//========================================================= -void CHAssassin :: Spawn() -{ - Precache( ); - - if (pev->model) - SET_MODEL(ENT(pev), STRING(pev->model)); //LRC - else - SET_MODEL(ENT(pev), "models/hassassin.mdl"); - UTIL_SetSize(pev, VEC_HUMAN_HULL_MIN, VEC_HUMAN_HULL_MAX); - - pev->solid = SOLID_SLIDEBOX; - pev->movetype = MOVETYPE_STEP; - m_bloodColor = BLOOD_COLOR_RED; - pev->effects = 0; - if (pev->health == 0) - pev->health = gSkillData.hassassinHealth; - m_flFieldOfView = VIEW_FIELD_WIDE; // indicates the width of this monster's forward view cone ( as a dotproduct result ) - m_MonsterState = MONSTERSTATE_NONE; - m_afCapability = bits_CAP_MELEE_ATTACK1 | bits_CAP_DOORS_GROUP; - pev->friction = 1; - - m_HackedGunPos = Vector( 0, 24, 48 ); - - m_iTargetRanderamt = 20; - pev->renderamt = 20; - pev->rendermode = kRenderTransTexture; - - MonsterInit(); -} - -//========================================================= -// Precache - precaches all resources this monster needs -//========================================================= -void CHAssassin :: Precache() -{ - if (pev->model) - PRECACHE_MODEL((char*)STRING(pev->model)); //LRC - else - PRECACHE_MODEL("models/hassassin.mdl"); - - PRECACHE_SOUND("weapons/pl_gun1.wav"); - PRECACHE_SOUND("weapons/pl_gun2.wav"); - - PRECACHE_SOUND("debris/beamstart1.wav"); - - m_iShell = PRECACHE_MODEL ("models/shell.mdl");// brass shell -} - - - -//========================================================= -// AI Schedules Specific to this monster -//========================================================= - -//========================================================= -// Fail Schedule -//========================================================= -Task_t tlAssassinFail[] = -{ - { TASK_STOP_MOVING, 0 }, - { TASK_SET_ACTIVITY, (float)ACT_IDLE }, - { TASK_WAIT_FACE_ENEMY, (float)2 }, - // { TASK_WAIT_PVS, (float)0 }, - { TASK_SET_SCHEDULE, (float)SCHED_CHASE_ENEMY }, -}; - -Schedule_t slAssassinFail[] = -{ - { - tlAssassinFail, - ARRAYSIZE ( tlAssassinFail ), - bits_COND_LIGHT_DAMAGE | - bits_COND_HEAVY_DAMAGE | - bits_COND_PROVOKED | - bits_COND_CAN_RANGE_ATTACK1 | - bits_COND_CAN_RANGE_ATTACK2 | - bits_COND_CAN_MELEE_ATTACK1 | - bits_COND_HEAR_SOUND, - - bits_SOUND_DANGER | - bits_SOUND_PLAYER, - "AssassinFail" - }, -}; - - -//========================================================= -// Enemy exposed Agrunt's cover -//========================================================= -Task_t tlAssassinExposed[] = -{ - { TASK_STOP_MOVING, (float)0 }, - { TASK_RANGE_ATTACK1, (float)0 }, - { TASK_SET_FAIL_SCHEDULE, (float)SCHED_ASSASSIN_JUMP }, - { TASK_SET_SCHEDULE, (float)SCHED_TAKE_COVER_FROM_ENEMY }, -}; - -Schedule_t slAssassinExposed[] = -{ - { - tlAssassinExposed, - ARRAYSIZE ( tlAssassinExposed ), - bits_COND_CAN_MELEE_ATTACK1, - 0, - "AssassinExposed", - }, -}; - - -//========================================================= -// Take cover from enemy! Tries lateral cover before node -// cover! -//========================================================= -Task_t tlAssassinTakeCoverFromEnemy[] = -{ - { TASK_STOP_MOVING, (float)0 }, - { TASK_WAIT, (float)0.2 }, - { TASK_SET_FAIL_SCHEDULE, (float)SCHED_RANGE_ATTACK1 }, - { TASK_FIND_COVER_FROM_ENEMY, (float)0 }, - { TASK_RUN_PATH, (float)0 }, - { TASK_WAIT_FOR_MOVEMENT, (float)0 }, - { TASK_REMEMBER, (float)bits_MEMORY_INCOVER }, - { TASK_FACE_ENEMY, (float)0 }, -}; - -Schedule_t slAssassinTakeCoverFromEnemy[] = -{ - { - tlAssassinTakeCoverFromEnemy, - ARRAYSIZE ( tlAssassinTakeCoverFromEnemy ), - bits_COND_NEW_ENEMY | - bits_COND_CAN_MELEE_ATTACK1 | - bits_COND_HEAR_SOUND, - - bits_SOUND_DANGER, - "AssassinTakeCoverFromEnemy" - }, -}; - - -//========================================================= -// Take cover from enemy! Tries lateral cover before node -// cover! -//========================================================= -Task_t tlAssassinTakeCoverFromEnemy2[] = -{ - { TASK_STOP_MOVING, (float)0 }, - { TASK_WAIT, (float)0.2 }, - { TASK_FACE_ENEMY, (float)0 }, - { TASK_RANGE_ATTACK1, (float)0 }, - { TASK_SET_FAIL_SCHEDULE, (float)SCHED_RANGE_ATTACK2 }, - { TASK_FIND_FAR_NODE_COVER_FROM_ENEMY, (float)384 }, - { TASK_RUN_PATH, (float)0 }, - { TASK_WAIT_FOR_MOVEMENT, (float)0 }, - { TASK_REMEMBER, (float)bits_MEMORY_INCOVER }, - { TASK_FACE_ENEMY, (float)0 }, -}; - -Schedule_t slAssassinTakeCoverFromEnemy2[] = -{ - { - tlAssassinTakeCoverFromEnemy2, - ARRAYSIZE ( tlAssassinTakeCoverFromEnemy2 ), - bits_COND_NEW_ENEMY | - bits_COND_CAN_MELEE_ATTACK2 | - bits_COND_HEAR_SOUND, - - bits_SOUND_DANGER, - "AssassinTakeCoverFromEnemy2" - }, -}; - - -//========================================================= -// hide from the loudest sound source -//========================================================= -Task_t tlAssassinTakeCoverFromBestSound[] = -{ - { TASK_SET_FAIL_SCHEDULE, (float)SCHED_MELEE_ATTACK1 }, - { TASK_STOP_MOVING, (float)0 }, - { TASK_FIND_COVER_FROM_BEST_SOUND, (float)0 }, - { TASK_RUN_PATH, (float)0 }, - { TASK_WAIT_FOR_MOVEMENT, (float)0 }, - { TASK_REMEMBER, (float)bits_MEMORY_INCOVER }, - { TASK_TURN_LEFT, (float)179 }, -}; - -Schedule_t slAssassinTakeCoverFromBestSound[] = -{ - { - tlAssassinTakeCoverFromBestSound, - ARRAYSIZE ( tlAssassinTakeCoverFromBestSound ), - bits_COND_NEW_ENEMY, - 0, - "AssassinTakeCoverFromBestSound" - }, -}; - - - - - -//========================================================= -// AlertIdle Schedules -//========================================================= -Task_t tlAssassinHide[] = -{ - { TASK_STOP_MOVING, 0 }, - { TASK_SET_ACTIVITY, (float)ACT_IDLE }, - { TASK_WAIT, (float)2 }, - { TASK_SET_SCHEDULE, (float)SCHED_CHASE_ENEMY }, -}; - -Schedule_t slAssassinHide[] = -{ - { - tlAssassinHide, - ARRAYSIZE ( tlAssassinHide ), - bits_COND_NEW_ENEMY | - bits_COND_SEE_ENEMY | - bits_COND_SEE_FEAR | - bits_COND_LIGHT_DAMAGE | - bits_COND_HEAVY_DAMAGE | - bits_COND_PROVOKED | - bits_COND_HEAR_SOUND, - - bits_SOUND_DANGER, - "AssassinHide" - }, -}; - - - -//========================================================= -// HUNT Schedules -//========================================================= -Task_t tlAssassinHunt[] = -{ - { TASK_GET_PATH_TO_ENEMY, (float)0 }, - { TASK_RUN_PATH, (float)0 }, - { TASK_WAIT_FOR_MOVEMENT, (float)0 }, -}; - -Schedule_t slAssassinHunt[] = -{ - { - tlAssassinHunt, - ARRAYSIZE ( tlAssassinHunt ), - bits_COND_NEW_ENEMY | - // bits_COND_SEE_ENEMY | - bits_COND_CAN_RANGE_ATTACK1 | - bits_COND_HEAR_SOUND, - - bits_SOUND_DANGER, - "AssassinHunt" - }, -}; - - -//========================================================= -// Jumping Schedules -//========================================================= -Task_t tlAssassinJump[] = -{ - { TASK_STOP_MOVING, (float)0 }, - { TASK_PLAY_SEQUENCE, (float)ACT_HOP }, - { TASK_SET_SCHEDULE, (float)SCHED_ASSASSIN_JUMP_ATTACK }, -}; - -Schedule_t slAssassinJump[] = -{ - { - tlAssassinJump, - ARRAYSIZE ( tlAssassinJump ), - 0, - 0, - "AssassinJump" - }, -}; - - -//========================================================= -// repel -//========================================================= -Task_t tlAssassinJumpAttack[] = -{ - { TASK_SET_FAIL_SCHEDULE, (float)SCHED_ASSASSIN_JUMP_LAND }, - // { TASK_SET_ACTIVITY, (float)ACT_FLY }, - { TASK_ASSASSIN_FALL_TO_GROUND, (float)0 }, -}; - - -Schedule_t slAssassinJumpAttack[] = -{ - { - tlAssassinJumpAttack, - ARRAYSIZE ( tlAssassinJumpAttack ), - 0, - 0, - "AssassinJumpAttack" - }, -}; - - -//========================================================= -// repel -//========================================================= -Task_t tlAssassinJumpLand[] = -{ - { TASK_SET_FAIL_SCHEDULE, (float)SCHED_ASSASSIN_EXPOSED }, - // { TASK_SET_FAIL_SCHEDULE, (float)SCHED_MELEE_ATTACK1 }, - { TASK_SET_ACTIVITY, (float)ACT_IDLE }, - { TASK_REMEMBER, (float)bits_MEMORY_BADJUMP }, - { TASK_FIND_NODE_COVER_FROM_ENEMY, (float)0 }, - { TASK_RUN_PATH, (float)0 }, - { TASK_FORGET, (float)bits_MEMORY_BADJUMP }, - { TASK_WAIT_FOR_MOVEMENT, (float)0 }, - { TASK_REMEMBER, (float)bits_MEMORY_INCOVER }, - { TASK_FACE_ENEMY, (float)0 }, - { TASK_SET_FAIL_SCHEDULE, (float)SCHED_RANGE_ATTACK1 }, -}; - -Schedule_t slAssassinJumpLand[] = -{ - { - tlAssassinJumpLand, - ARRAYSIZE ( tlAssassinJumpLand ), - 0, - 0, - "AssassinJumpLand" - }, -}; - -DEFINE_CUSTOM_SCHEDULES( CHAssassin ) -{ - slAssassinFail, - slAssassinExposed, - slAssassinTakeCoverFromEnemy, - slAssassinTakeCoverFromEnemy2, - slAssassinTakeCoverFromBestSound, - slAssassinHide, - slAssassinHunt, - slAssassinJump, - slAssassinJumpAttack, - slAssassinJumpLand, -}; - -IMPLEMENT_CUSTOM_SCHEDULES( CHAssassin, CBaseMonster ); - - -//========================================================= -// CheckMeleeAttack1 - jump like crazy if the enemy gets too close. -//========================================================= -BOOL CHAssassin :: CheckMeleeAttack1 ( float flDot, float flDist ) -{ - if ( m_flNextJump < gpGlobals->time && (flDist <= 128 || HasMemory( bits_MEMORY_BADJUMP )) && m_hEnemy != NULL ) - { - TraceResult tr; - - Vector vecDest = pev->origin + Vector( RANDOM_FLOAT( -64, 64), RANDOM_FLOAT( -64, 64 ), 160 ); - - UTIL_TraceHull( pev->origin + Vector( 0, 0, 36 ), vecDest + Vector( 0, 0, 36 ), dont_ignore_monsters, human_hull, ENT(pev), &tr); - - if ( tr.fStartSolid || tr.flFraction < 1.0) - { - return FALSE; - } - - float flGravity = g_psv_gravity->value; - - float time = sqrt( 160 / (0.5 * flGravity)); - float speed = flGravity * time / 160; - m_vecJumpVelocity = (vecDest - pev->origin) * speed; - - return TRUE; - } - return FALSE; -} - -//========================================================= -// CheckRangeAttack1 - drop a cap in their ass -// -//========================================================= -BOOL CHAssassin :: CheckRangeAttack1 ( float flDot, float flDist ) -{ - if ( !HasConditions( bits_COND_ENEMY_OCCLUDED ) && flDist > 64 && flDist <= 2048 /* && flDot >= 0.5 */ /* && NoFriendlyFire() */ ) - { - TraceResult tr; - - Vector vecSrc = GetGunPosition(); - - // verify that a bullet fired from the gun will hit the enemy before the world. - UTIL_TraceLine( vecSrc, m_hEnemy->BodyTarget(vecSrc), dont_ignore_monsters, ENT(pev), &tr); - - if ( tr.flFraction == 1 || tr.pHit == m_hEnemy->edict() ) - { - return TRUE; - } - } - return FALSE; -} - -//========================================================= -// CheckRangeAttack2 - toss grenade is enemy gets in the way and is too close. -//========================================================= -BOOL CHAssassin :: CheckRangeAttack2 ( float flDot, float flDist ) -{ - m_fThrowGrenade = FALSE; - if ( !FBitSet ( m_hEnemy->pev->flags, FL_ONGROUND ) ) - { - // don't throw grenades at anything that isn't on the ground! - return FALSE; - } - - // don't get grenade happy unless the player starts to piss you off - if ( m_iFrustration <= 2) - return FALSE; - - if ( m_flNextGrenadeCheck < gpGlobals->time && !HasConditions( bits_COND_ENEMY_OCCLUDED ) && flDist <= 512 /* && flDot >= 0.5 */ /* && NoFriendlyFire() */ ) - { - Vector vecToss = VecCheckThrow( pev, GetGunPosition( ), m_hEnemy->Center(), flDist, 0.5 ); // use dist as speed to get there in 1 second - - if ( vecToss != g_vecZero ) - { - m_vecTossVelocity = vecToss; - - // throw a hand grenade - m_fThrowGrenade = TRUE; - - return TRUE; - } - } - - return FALSE; -} - - -//========================================================= -// RunAI -//========================================================= -void CHAssassin :: RunAI( void ) -{ - CBaseMonster :: RunAI(); - - // always visible if moving - // always visible is not on hard - if (g_iSkillLevel != SKILL_HARD || m_hEnemy == NULL || pev->deadflag != DEAD_NO || m_Activity == ACT_RUN || m_Activity == ACT_WALK || !(pev->flags & FL_ONGROUND)) - m_iTargetRanderamt = 255; - else - m_iTargetRanderamt = 20; - - if (pev->renderamt > m_iTargetRanderamt) - { - if (pev->renderamt == 255) - { - EMIT_SOUND (ENT(pev), CHAN_BODY, "debris/beamstart1.wav", 0.2, ATTN_NORM ); - } - - pev->renderamt = max( pev->renderamt - 50, m_iTargetRanderamt ); - pev->rendermode = kRenderTransTexture; - } - else if (pev->renderamt < m_iTargetRanderamt) - { - pev->renderamt = min( pev->renderamt + 50, m_iTargetRanderamt ); - if (pev->renderamt == 255) - pev->rendermode = kRenderNormal; - } - - if (m_Activity == ACT_RUN || m_Activity == ACT_WALK) - { - static int iStep = 0; - iStep = ! iStep; - if (iStep) - { - switch( RANDOM_LONG( 0, 3 ) ) - { - case 0: EMIT_SOUND( ENT(pev), CHAN_BODY, "player/pl_step1.wav", 0.5, ATTN_NORM); break; - case 1: EMIT_SOUND( ENT(pev), CHAN_BODY, "player/pl_step3.wav", 0.5, ATTN_NORM); break; - case 2: EMIT_SOUND( ENT(pev), CHAN_BODY, "player/pl_step2.wav", 0.5, ATTN_NORM); break; - case 3: EMIT_SOUND( ENT(pev), CHAN_BODY, "player/pl_step4.wav", 0.5, ATTN_NORM); break; - } - } - } -} - - -//========================================================= -// StartTask -//========================================================= -void CHAssassin :: StartTask ( Task_t *pTask ) -{ - switch ( pTask->iTask ) - { - case TASK_RANGE_ATTACK2: - if (!m_fThrowGrenade) - { - TaskComplete( ); - } - else - { - CBaseMonster :: StartTask ( pTask ); - } - break; - case TASK_ASSASSIN_FALL_TO_GROUND: - break; - default: - CBaseMonster :: StartTask ( pTask ); - break; - } -} - - -//========================================================= -// RunTask -//========================================================= -void CHAssassin :: RunTask ( Task_t *pTask ) -{ - switch ( pTask->iTask ) - { - case TASK_ASSASSIN_FALL_TO_GROUND: - MakeIdealYaw( m_vecEnemyLKP ); - ChangeYaw( pev->yaw_speed ); - - if (m_fSequenceFinished) - { - if (pev->velocity.z > 0) - { - pev->sequence = LookupSequence( "fly_up" ); - } - else if (HasConditions ( bits_COND_SEE_ENEMY )) - { - pev->sequence = LookupSequence( "fly_attack" ); - pev->frame = 0; - } - else - { - pev->sequence = LookupSequence( "fly_down" ); - pev->frame = 0; - } - - ResetSequenceInfo( ); - SetYawSpeed(); - } - if (pev->flags & FL_ONGROUND) - { - // ALERT( at_console, "on ground\n"); - TaskComplete( ); - } - break; - default: - CBaseMonster :: RunTask ( pTask ); - break; - } -} - -//========================================================= -// GetSchedule - Decides which type of schedule best suits -// the monster's current state and conditions. Then calls -// monster's member function to get a pointer to a schedule -// of the proper type. -//========================================================= -Schedule_t *CHAssassin :: GetSchedule ( void ) -{ - switch ( m_MonsterState ) - { - case MONSTERSTATE_IDLE: - case MONSTERSTATE_ALERT: - { - if ( HasConditions ( bits_COND_HEAR_SOUND )) - { - CSound *pSound; - pSound = PBestSound(); - - ASSERT( pSound != NULL ); - if ( pSound && (pSound->m_iType & bits_SOUND_DANGER) ) - { - return GetScheduleOfType( SCHED_TAKE_COVER_FROM_BEST_SOUND ); - } - if ( pSound && (pSound->m_iType & bits_SOUND_COMBAT) ) - { - return GetScheduleOfType( SCHED_INVESTIGATE_SOUND ); - } - } - } - break; - - case MONSTERSTATE_COMBAT: - { -// dead enemy - if ( HasConditions( bits_COND_ENEMY_DEAD ) ) - { - // call base class, all code to handle dead enemies is centralized there. - return CBaseMonster :: GetSchedule(); - } - - // flying? - if ( pev->movetype == MOVETYPE_TOSS) - { - if (pev->flags & FL_ONGROUND) - { - // ALERT( at_console, "landed\n"); - // just landed - pev->movetype = MOVETYPE_STEP; - return GetScheduleOfType ( SCHED_ASSASSIN_JUMP_LAND ); - } - else - { - // ALERT( at_console, "jump\n"); - // jump or jump/shoot - if ( m_MonsterState == MONSTERSTATE_COMBAT ) - return GetScheduleOfType ( SCHED_ASSASSIN_JUMP ); - else - return GetScheduleOfType ( SCHED_ASSASSIN_JUMP_ATTACK ); - } - } - - if ( HasConditions ( bits_COND_HEAR_SOUND )) - { - CSound *pSound; - pSound = PBestSound(); - - ASSERT( pSound != NULL ); - if ( pSound && (pSound->m_iType & bits_SOUND_DANGER) ) - { - return GetScheduleOfType( SCHED_TAKE_COVER_FROM_BEST_SOUND ); - } - } - - if ( HasConditions ( bits_COND_LIGHT_DAMAGE ) ) - { - m_iFrustration++; - } - if ( HasConditions ( bits_COND_HEAVY_DAMAGE ) ) - { - m_iFrustration++; - } - - // jump player! - if ( HasConditions ( bits_COND_CAN_MELEE_ATTACK1 ) ) - { - // ALERT( at_console, "melee attack 1\n"); - return GetScheduleOfType ( SCHED_MELEE_ATTACK1 ); - } - - // throw grenade - if ( HasConditions ( bits_COND_CAN_RANGE_ATTACK2 ) ) - { - // ALERT( at_console, "range attack 2\n"); - return GetScheduleOfType ( SCHED_RANGE_ATTACK2 ); - } - - // spotted - if ( HasConditions ( bits_COND_SEE_ENEMY ) && HasConditions ( bits_COND_ENEMY_FACING_ME ) ) - { - // ALERT( at_console, "exposed\n"); - m_iFrustration++; - return GetScheduleOfType ( SCHED_ASSASSIN_EXPOSED ); - } - - // can attack - if ( HasConditions ( bits_COND_CAN_RANGE_ATTACK1 ) ) - { - // ALERT( at_console, "range attack 1\n"); - m_iFrustration = 0; - return GetScheduleOfType ( SCHED_RANGE_ATTACK1 ); - } - - if ( HasConditions ( bits_COND_SEE_ENEMY ) ) - { - // ALERT( at_console, "face\n"); - return GetScheduleOfType ( SCHED_COMBAT_FACE ); - } - - // new enemy - if ( HasConditions ( bits_COND_NEW_ENEMY ) ) - { - // ALERT( at_console, "take cover\n"); - return GetScheduleOfType ( SCHED_TAKE_COVER_FROM_ENEMY ); - } - - // ALERT( at_console, "stand\n"); - return GetScheduleOfType ( SCHED_ALERT_STAND ); - } - break; - } - - return CBaseMonster :: GetSchedule(); -} - -//========================================================= -//========================================================= -Schedule_t* CHAssassin :: GetScheduleOfType ( int Type ) -{ - // ALERT( at_console, "%d\n", m_iFrustration ); - switch ( Type ) - { - case SCHED_TAKE_COVER_FROM_ENEMY: - if (pev->health > 30) - return slAssassinTakeCoverFromEnemy; - else - return slAssassinTakeCoverFromEnemy2; - case SCHED_TAKE_COVER_FROM_BEST_SOUND: - return slAssassinTakeCoverFromBestSound; - case SCHED_ASSASSIN_EXPOSED: - return slAssassinExposed; - case SCHED_FAIL: - if (m_MonsterState == MONSTERSTATE_COMBAT) - return slAssassinFail; - break; - case SCHED_ALERT_STAND: - if (m_MonsterState == MONSTERSTATE_COMBAT) - return slAssassinHide; - break; - case SCHED_CHASE_ENEMY: - return slAssassinHunt; - case SCHED_MELEE_ATTACK1: - if (pev->flags & FL_ONGROUND) - { - if (m_flNextJump > gpGlobals->time) - { - // can't jump yet, go ahead and fail - return slAssassinFail; - } - else - { - return slAssassinJump; - } - } - else - { - return slAssassinJumpAttack; - } - case SCHED_ASSASSIN_JUMP: - case SCHED_ASSASSIN_JUMP_ATTACK: - return slAssassinJumpAttack; - case SCHED_ASSASSIN_JUMP_LAND: - return slAssassinJumpLand; - } - - return CBaseMonster :: GetScheduleOfType( Type ); -} - -#endif +/*** +* +* Copyright (c) 1996-2002, 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. +* +* This source code contains proprietary and confidential information of +* Valve LLC and its suppliers. Access to this code is restricted to +* persons who have executed a written SDK license with Valve. Any access, +* use or distribution of this code by or to any unlicensed person is illegal. +* +****/ +#if !defined( OEM_BUILD ) && !defined( HLDEMO_BUILD ) + +//========================================================= +// hassassin - Human assassin, fast and stealthy +//========================================================= + +#include "extdll.h" +#include "util.h" +#include "cbase.h" +#include "monsters.h" +#include "schedule.h" +#include "squadmonster.h" +#include "weapons.h" +#include "soundent.h" +#include "game.h" + +extern DLL_GLOBAL int g_iSkillLevel; + +//========================================================= +// monster-specific schedule types +//========================================================= +enum +{ + SCHED_ASSASSIN_EXPOSED = LAST_COMMON_SCHEDULE + 1,// cover was blown. + SCHED_ASSASSIN_JUMP, // fly through the air + SCHED_ASSASSIN_JUMP_ATTACK, // fly through the air and shoot + SCHED_ASSASSIN_JUMP_LAND, // hit and run away +}; + +//========================================================= +// monster-specific tasks +//========================================================= + +enum +{ + TASK_ASSASSIN_FALL_TO_GROUND = LAST_COMMON_TASK + 1, // falling and waiting to hit ground +}; + + +//========================================================= +// Monster's Anim Events Go Here +//========================================================= +#define ASSASSIN_AE_SHOOT1 1 +#define ASSASSIN_AE_TOSS1 2 +#define ASSASSIN_AE_JUMP 3 + + +#define bits_MEMORY_BADJUMP (bits_MEMORY_CUSTOM1) + +class CHAssassin : public CBaseMonster +{ +public: + void Spawn( void ); + void Precache( void ); + void SetYawSpeed ( void ); + int Classify ( void ); + int ISoundMask ( void); + void Shoot( void ); + void HandleAnimEvent( MonsterEvent_t *pEvent ); + Schedule_t* GetSchedule ( void ); + Schedule_t* GetScheduleOfType ( int Type ); + BOOL CheckMeleeAttack1 ( float flDot, float flDist ); // jump + // BOOL CheckMeleeAttack2 ( float flDot, float flDist ); + BOOL CheckRangeAttack1 ( float flDot, float flDist ); // shoot + BOOL CheckRangeAttack2 ( float flDot, float flDist ); // throw grenade + void StartTask ( Task_t *pTask ); + void RunAI( void ); + void RunTask ( Task_t *pTask ); + void DeathSound ( void ); + void IdleSound ( void ); + CUSTOM_SCHEDULES; + + int Save( CSave &save ); + int Restore( CRestore &restore ); + static TYPEDESCRIPTION m_SaveData[]; + + float m_flLastShot; + float m_flDiviation; + + float m_flNextJump; + Vector m_vecJumpVelocity; + + float m_flNextGrenadeCheck; + Vector m_vecTossVelocity; + BOOL m_fThrowGrenade; + + int m_iTargetRanderamt; + + int m_iFrustration; + + int m_iShell; +}; +LINK_ENTITY_TO_CLASS( monster_human_assassin, CHAssassin ); + + +TYPEDESCRIPTION CHAssassin::m_SaveData[] = +{ + DEFINE_FIELD( CHAssassin, m_flLastShot, FIELD_TIME ), + DEFINE_FIELD( CHAssassin, m_flDiviation, FIELD_FLOAT ), + + DEFINE_FIELD( CHAssassin, m_flNextJump, FIELD_TIME ), + DEFINE_FIELD( CHAssassin, m_vecJumpVelocity, FIELD_VECTOR ), + + DEFINE_FIELD( CHAssassin, m_flNextGrenadeCheck, FIELD_TIME ), + DEFINE_FIELD( CHAssassin, m_vecTossVelocity, FIELD_VECTOR ), + DEFINE_FIELD( CHAssassin, m_fThrowGrenade, FIELD_BOOLEAN ), + + DEFINE_FIELD( CHAssassin, m_iTargetRanderamt, FIELD_INTEGER ), + DEFINE_FIELD( CHAssassin, m_iFrustration, FIELD_INTEGER ), +}; + +IMPLEMENT_SAVERESTORE( CHAssassin, CBaseMonster ); + + +//========================================================= +// DieSound +//========================================================= +void CHAssassin :: DeathSound ( void ) +{ +} + +//========================================================= +// IdleSound +//========================================================= +void CHAssassin :: IdleSound ( void ) +{ +} + +//========================================================= +// ISoundMask - returns a bit mask indicating which types +// of sounds this monster regards. +//========================================================= +int CHAssassin :: ISoundMask ( void) +{ + return bits_SOUND_WORLD | + bits_SOUND_COMBAT | + bits_SOUND_DANGER | + bits_SOUND_PLAYER; +} + + +//========================================================= +// Classify - indicates this monster's place in the +// relationship table. +//========================================================= +int CHAssassin :: Classify ( void ) +{ + return CLASS_HUMAN_MILITARY; +} + +//========================================================= +// SetYawSpeed - allows each sequence to have a different +// turn rate associated with it. +//========================================================= +void CHAssassin :: SetYawSpeed ( void ) +{ + int ys; + + switch ( m_Activity ) + { + case ACT_TURN_LEFT: + case ACT_TURN_RIGHT: + ys = 360; + break; + default: + ys = 360; + break; + } + + pev->yaw_speed = ys; +} + + +//========================================================= +// Shoot +//========================================================= +void CHAssassin :: Shoot ( void ) +{ + if (m_hEnemy == NULL) + { + return; + } + + Vector vecShootOrigin = GetGunPosition(); + Vector vecShootDir = ShootAtEnemy( vecShootOrigin ); + + if (m_flLastShot + 2 < gpGlobals->time) + { + m_flDiviation = 0.10; + } + else + { + m_flDiviation -= 0.01; + if (m_flDiviation < 0.02) + m_flDiviation = 0.02; + } + m_flLastShot = gpGlobals->time; + + UTIL_MakeVectors ( pev->angles ); + + Vector vecShellVelocity = gpGlobals->v_right * RANDOM_FLOAT(40,90) + gpGlobals->v_up * RANDOM_FLOAT(75,200) + gpGlobals->v_forward * RANDOM_FLOAT(-40, 40); + EjectBrass ( pev->origin + gpGlobals->v_up * 32 + gpGlobals->v_forward * 12, vecShellVelocity, pev->angles.y, m_iShell, TE_BOUNCE_SHELL); + FireBullets(1, vecShootOrigin, vecShootDir, Vector( m_flDiviation, m_flDiviation, m_flDiviation ), 2048, BULLET_MONSTER_9MM ); // shoot +-8 degrees + + switch(RANDOM_LONG(0,1)) + { + case 0: + EMIT_SOUND(ENT(pev), CHAN_WEAPON, "weapons/pl_gun1.wav", RANDOM_FLOAT(0.6, 0.8), ATTN_NORM); + break; + case 1: + EMIT_SOUND(ENT(pev), CHAN_WEAPON, "weapons/pl_gun2.wav", RANDOM_FLOAT(0.6, 0.8), ATTN_NORM); + break; + } + + pev->effects |= EF_MUZZLEFLASH; + + Vector angDir = UTIL_VecToAngles( vecShootDir ); + SetBlending( 0, angDir.x ); + + m_cAmmoLoaded--; +} + + +//========================================================= +// HandleAnimEvent - catches the monster-specific messages +// that occur when tagged animation frames are played. +// +// Returns number of events handled, 0 if none. +//========================================================= +void CHAssassin :: HandleAnimEvent( MonsterEvent_t *pEvent ) +{ + switch( pEvent->event ) + { + case ASSASSIN_AE_SHOOT1: + Shoot( ); + break; + case ASSASSIN_AE_TOSS1: + { + UTIL_MakeVectors( pev->angles ); + CGrenade::ShootTimed( pev, pev->origin + gpGlobals->v_forward * 34 + Vector (0, 0, 32), m_vecTossVelocity, 2.0 ); + + m_flNextGrenadeCheck = gpGlobals->time + 6;// wait six seconds before even looking again to see if a grenade can be thrown. + m_fThrowGrenade = FALSE; + // !!!LATER - when in a group, only try to throw grenade if ordered. + } + break; + case ASSASSIN_AE_JUMP: + { + // ALERT( at_console, "jumping"); + UTIL_MakeAimVectors( pev->angles ); + pev->movetype = MOVETYPE_TOSS; + pev->flags &= ~FL_ONGROUND; + pev->velocity = m_vecJumpVelocity; + m_flNextJump = gpGlobals->time + 3.0; + } + return; + default: + CBaseMonster::HandleAnimEvent( pEvent ); + break; + } +} + +//========================================================= +// Spawn +//========================================================= +void CHAssassin :: Spawn() +{ + Precache( ); + + SET_MODEL(ENT(pev), "models/hassassin.mdl"); + UTIL_SetSize(pev, VEC_HUMAN_HULL_MIN, VEC_HUMAN_HULL_MAX); + + pev->solid = SOLID_SLIDEBOX; + pev->movetype = MOVETYPE_STEP; + m_bloodColor = BLOOD_COLOR_RED; + pev->effects = 0; + pev->health = gSkillData.hassassinHealth; + m_flFieldOfView = VIEW_FIELD_WIDE; // indicates the width of this monster's forward view cone ( as a dotproduct result ) + m_MonsterState = MONSTERSTATE_NONE; + m_afCapability = bits_CAP_MELEE_ATTACK1 | bits_CAP_DOORS_GROUP; + pev->friction = 1; + + m_HackedGunPos = Vector( 0, 24, 48 ); + + m_iTargetRanderamt = 20; + pev->renderamt = 20; + pev->rendermode = kRenderTransTexture; + + MonsterInit(); +} + +//========================================================= +// Precache - precaches all resources this monster needs +//========================================================= +void CHAssassin :: Precache() +{ + PRECACHE_MODEL("models/hassassin.mdl"); + + PRECACHE_SOUND("weapons/pl_gun1.wav"); + PRECACHE_SOUND("weapons/pl_gun2.wav"); + + PRECACHE_SOUND("debris/beamstart1.wav"); + + m_iShell = PRECACHE_MODEL ("models/shell.mdl");// brass shell +} + + + +//========================================================= +// AI Schedules Specific to this monster +//========================================================= + +//========================================================= +// Fail Schedule +//========================================================= +Task_t tlAssassinFail[] = +{ + { TASK_STOP_MOVING, 0 }, + { TASK_SET_ACTIVITY, (float)ACT_IDLE }, + { TASK_WAIT_FACE_ENEMY, (float)2 }, + // { TASK_WAIT_PVS, (float)0 }, + { TASK_SET_SCHEDULE, (float)SCHED_CHASE_ENEMY }, +}; + +Schedule_t slAssassinFail[] = +{ + { + tlAssassinFail, + ARRAYSIZE ( tlAssassinFail ), + bits_COND_LIGHT_DAMAGE | + bits_COND_HEAVY_DAMAGE | + bits_COND_PROVOKED | + bits_COND_CAN_RANGE_ATTACK1 | + bits_COND_CAN_RANGE_ATTACK2 | + bits_COND_CAN_MELEE_ATTACK1 | + bits_COND_HEAR_SOUND, + + bits_SOUND_DANGER | + bits_SOUND_PLAYER, + "AssassinFail" + }, +}; + + +//========================================================= +// Enemy exposed Agrunt's cover +//========================================================= +Task_t tlAssassinExposed[] = +{ + { TASK_STOP_MOVING, (float)0 }, + { TASK_RANGE_ATTACK1, (float)0 }, + { TASK_SET_FAIL_SCHEDULE, (float)SCHED_ASSASSIN_JUMP }, + { TASK_SET_SCHEDULE, (float)SCHED_TAKE_COVER_FROM_ENEMY }, +}; + +Schedule_t slAssassinExposed[] = +{ + { + tlAssassinExposed, + ARRAYSIZE ( tlAssassinExposed ), + bits_COND_CAN_MELEE_ATTACK1, + 0, + "AssassinExposed", + }, +}; + + +//========================================================= +// Take cover from enemy! Tries lateral cover before node +// cover! +//========================================================= +Task_t tlAssassinTakeCoverFromEnemy[] = +{ + { TASK_STOP_MOVING, (float)0 }, + { TASK_WAIT, (float)0.2 }, + { TASK_SET_FAIL_SCHEDULE, (float)SCHED_RANGE_ATTACK1 }, + { TASK_FIND_COVER_FROM_ENEMY, (float)0 }, + { TASK_RUN_PATH, (float)0 }, + { TASK_WAIT_FOR_MOVEMENT, (float)0 }, + { TASK_REMEMBER, (float)bits_MEMORY_INCOVER }, + { TASK_FACE_ENEMY, (float)0 }, +}; + +Schedule_t slAssassinTakeCoverFromEnemy[] = +{ + { + tlAssassinTakeCoverFromEnemy, + ARRAYSIZE ( tlAssassinTakeCoverFromEnemy ), + bits_COND_NEW_ENEMY | + bits_COND_CAN_MELEE_ATTACK1 | + bits_COND_HEAR_SOUND, + + bits_SOUND_DANGER, + "AssassinTakeCoverFromEnemy" + }, +}; + + +//========================================================= +// Take cover from enemy! Tries lateral cover before node +// cover! +//========================================================= +Task_t tlAssassinTakeCoverFromEnemy2[] = +{ + { TASK_STOP_MOVING, (float)0 }, + { TASK_WAIT, (float)0.2 }, + { TASK_FACE_ENEMY, (float)0 }, + { TASK_RANGE_ATTACK1, (float)0 }, + { TASK_SET_FAIL_SCHEDULE, (float)SCHED_RANGE_ATTACK2 }, + { TASK_FIND_FAR_NODE_COVER_FROM_ENEMY, (float)384 }, + { TASK_RUN_PATH, (float)0 }, + { TASK_WAIT_FOR_MOVEMENT, (float)0 }, + { TASK_REMEMBER, (float)bits_MEMORY_INCOVER }, + { TASK_FACE_ENEMY, (float)0 }, +}; + +Schedule_t slAssassinTakeCoverFromEnemy2[] = +{ + { + tlAssassinTakeCoverFromEnemy2, + ARRAYSIZE ( tlAssassinTakeCoverFromEnemy2 ), + bits_COND_NEW_ENEMY | + bits_COND_CAN_MELEE_ATTACK2 | + bits_COND_HEAR_SOUND, + + bits_SOUND_DANGER, + "AssassinTakeCoverFromEnemy2" + }, +}; + + +//========================================================= +// hide from the loudest sound source +//========================================================= +Task_t tlAssassinTakeCoverFromBestSound[] = +{ + { TASK_SET_FAIL_SCHEDULE, (float)SCHED_MELEE_ATTACK1 }, + { TASK_STOP_MOVING, (float)0 }, + { TASK_FIND_COVER_FROM_BEST_SOUND, (float)0 }, + { TASK_RUN_PATH, (float)0 }, + { TASK_WAIT_FOR_MOVEMENT, (float)0 }, + { TASK_REMEMBER, (float)bits_MEMORY_INCOVER }, + { TASK_TURN_LEFT, (float)179 }, +}; + +Schedule_t slAssassinTakeCoverFromBestSound[] = +{ + { + tlAssassinTakeCoverFromBestSound, + ARRAYSIZE ( tlAssassinTakeCoverFromBestSound ), + bits_COND_NEW_ENEMY, + 0, + "AssassinTakeCoverFromBestSound" + }, +}; + + + + + +//========================================================= +// AlertIdle Schedules +//========================================================= +Task_t tlAssassinHide[] = +{ + { TASK_STOP_MOVING, 0 }, + { TASK_SET_ACTIVITY, (float)ACT_IDLE }, + { TASK_WAIT, (float)2 }, + { TASK_SET_SCHEDULE, (float)SCHED_CHASE_ENEMY }, +}; + +Schedule_t slAssassinHide[] = +{ + { + tlAssassinHide, + ARRAYSIZE ( tlAssassinHide ), + bits_COND_NEW_ENEMY | + bits_COND_SEE_ENEMY | + bits_COND_SEE_FEAR | + bits_COND_LIGHT_DAMAGE | + bits_COND_HEAVY_DAMAGE | + bits_COND_PROVOKED | + bits_COND_HEAR_SOUND, + + bits_SOUND_DANGER, + "AssassinHide" + }, +}; + + + +//========================================================= +// HUNT Schedules +//========================================================= +Task_t tlAssassinHunt[] = +{ + { TASK_GET_PATH_TO_ENEMY, (float)0 }, + { TASK_RUN_PATH, (float)0 }, + { TASK_WAIT_FOR_MOVEMENT, (float)0 }, +}; + +Schedule_t slAssassinHunt[] = +{ + { + tlAssassinHunt, + ARRAYSIZE ( tlAssassinHunt ), + bits_COND_NEW_ENEMY | + // bits_COND_SEE_ENEMY | + bits_COND_CAN_RANGE_ATTACK1 | + bits_COND_HEAR_SOUND, + + bits_SOUND_DANGER, + "AssassinHunt" + }, +}; + + +//========================================================= +// Jumping Schedules +//========================================================= +Task_t tlAssassinJump[] = +{ + { TASK_STOP_MOVING, (float)0 }, + { TASK_PLAY_SEQUENCE, (float)ACT_HOP }, + { TASK_SET_SCHEDULE, (float)SCHED_ASSASSIN_JUMP_ATTACK }, +}; + +Schedule_t slAssassinJump[] = +{ + { + tlAssassinJump, + ARRAYSIZE ( tlAssassinJump ), + 0, + 0, + "AssassinJump" + }, +}; + + +//========================================================= +// repel +//========================================================= +Task_t tlAssassinJumpAttack[] = +{ + { TASK_SET_FAIL_SCHEDULE, (float)SCHED_ASSASSIN_JUMP_LAND }, + // { TASK_SET_ACTIVITY, (float)ACT_FLY }, + { TASK_ASSASSIN_FALL_TO_GROUND, (float)0 }, +}; + + +Schedule_t slAssassinJumpAttack[] = +{ + { + tlAssassinJumpAttack, + ARRAYSIZE ( tlAssassinJumpAttack ), + 0, + 0, + "AssassinJumpAttack" + }, +}; + + +//========================================================= +// repel +//========================================================= +Task_t tlAssassinJumpLand[] = +{ + { TASK_SET_FAIL_SCHEDULE, (float)SCHED_ASSASSIN_EXPOSED }, + // { TASK_SET_FAIL_SCHEDULE, (float)SCHED_MELEE_ATTACK1 }, + { TASK_SET_ACTIVITY, (float)ACT_IDLE }, + { TASK_REMEMBER, (float)bits_MEMORY_BADJUMP }, + { TASK_FIND_NODE_COVER_FROM_ENEMY, (float)0 }, + { TASK_RUN_PATH, (float)0 }, + { TASK_FORGET, (float)bits_MEMORY_BADJUMP }, + { TASK_WAIT_FOR_MOVEMENT, (float)0 }, + { TASK_REMEMBER, (float)bits_MEMORY_INCOVER }, + { TASK_FACE_ENEMY, (float)0 }, + { TASK_SET_FAIL_SCHEDULE, (float)SCHED_RANGE_ATTACK1 }, +}; + +Schedule_t slAssassinJumpLand[] = +{ + { + tlAssassinJumpLand, + ARRAYSIZE ( tlAssassinJumpLand ), + 0, + 0, + "AssassinJumpLand" + }, +}; + +DEFINE_CUSTOM_SCHEDULES( CHAssassin ) +{ + slAssassinFail, + slAssassinExposed, + slAssassinTakeCoverFromEnemy, + slAssassinTakeCoverFromEnemy2, + slAssassinTakeCoverFromBestSound, + slAssassinHide, + slAssassinHunt, + slAssassinJump, + slAssassinJumpAttack, + slAssassinJumpLand, +}; + +IMPLEMENT_CUSTOM_SCHEDULES( CHAssassin, CBaseMonster ); + + +//========================================================= +// CheckMeleeAttack1 - jump like crazy if the enemy gets too close. +//========================================================= +BOOL CHAssassin :: CheckMeleeAttack1 ( float flDot, float flDist ) +{ + if ( m_flNextJump < gpGlobals->time && (flDist <= 128 || HasMemory( bits_MEMORY_BADJUMP )) && m_hEnemy != NULL ) + { + TraceResult tr; + + Vector vecDest = pev->origin + Vector( RANDOM_FLOAT( -64, 64), RANDOM_FLOAT( -64, 64 ), 160 ); + + UTIL_TraceHull( pev->origin + Vector( 0, 0, 36 ), vecDest + Vector( 0, 0, 36 ), dont_ignore_monsters, human_hull, ENT(pev), &tr); + + if ( tr.fStartSolid || tr.flFraction < 1.0) + { + return FALSE; + } + + float flGravity = g_psv_gravity->value; + + float time = sqrt( 160 / (0.5 * flGravity)); + float speed = flGravity * time / 160; + m_vecJumpVelocity = (vecDest - pev->origin) * speed; + + return TRUE; + } + return FALSE; +} + +//========================================================= +// CheckRangeAttack1 - drop a cap in their ass +// +//========================================================= +BOOL CHAssassin :: CheckRangeAttack1 ( float flDot, float flDist ) +{ + if ( !HasConditions( bits_COND_ENEMY_OCCLUDED ) && flDist > 64 && flDist <= 2048 /* && flDot >= 0.5 */ /* && NoFriendlyFire() */ ) + { + TraceResult tr; + + Vector vecSrc = GetGunPosition(); + + // verify that a bullet fired from the gun will hit the enemy before the world. + UTIL_TraceLine( vecSrc, m_hEnemy->BodyTarget(vecSrc), dont_ignore_monsters, ENT(pev), &tr); + + if ( tr.flFraction == 1 || tr.pHit == m_hEnemy->edict() ) + { + return TRUE; + } + } + return FALSE; +} + +//========================================================= +// CheckRangeAttack2 - toss grenade is enemy gets in the way and is too close. +//========================================================= +BOOL CHAssassin :: CheckRangeAttack2 ( float flDot, float flDist ) +{ + m_fThrowGrenade = FALSE; + if ( !FBitSet ( m_hEnemy->pev->flags, FL_ONGROUND ) ) + { + // don't throw grenades at anything that isn't on the ground! + return FALSE; + } + + // don't get grenade happy unless the player starts to piss you off + if ( m_iFrustration <= 2) + return FALSE; + + if ( m_flNextGrenadeCheck < gpGlobals->time && !HasConditions( bits_COND_ENEMY_OCCLUDED ) && flDist <= 512 /* && flDot >= 0.5 */ /* && NoFriendlyFire() */ ) + { + Vector vecToss = VecCheckThrow( pev, GetGunPosition( ), m_hEnemy->Center(), flDist, 0.5 ); // use dist as speed to get there in 1 second + + if ( vecToss != g_vecZero ) + { + m_vecTossVelocity = vecToss; + + // throw a hand grenade + m_fThrowGrenade = TRUE; + + return TRUE; + } + } + + return FALSE; +} + + +//========================================================= +// RunAI +//========================================================= +void CHAssassin :: RunAI( void ) +{ + CBaseMonster :: RunAI(); + + // always visible if moving + // always visible is not on hard + if (g_iSkillLevel != SKILL_HARD || m_hEnemy == NULL || pev->deadflag != DEAD_NO || m_Activity == ACT_RUN || m_Activity == ACT_WALK || !(pev->flags & FL_ONGROUND)) + m_iTargetRanderamt = 255; + else + m_iTargetRanderamt = 20; + + if (pev->renderamt > m_iTargetRanderamt) + { + if (pev->renderamt == 255) + { + EMIT_SOUND (ENT(pev), CHAN_BODY, "debris/beamstart1.wav", 0.2, ATTN_NORM ); + } + + pev->renderamt = max( pev->renderamt - 50, m_iTargetRanderamt ); + pev->rendermode = kRenderTransTexture; + } + else if (pev->renderamt < m_iTargetRanderamt) + { + pev->renderamt = min( pev->renderamt + 50, m_iTargetRanderamt ); + if (pev->renderamt == 255) + pev->rendermode = kRenderNormal; + } + + if (m_Activity == ACT_RUN || m_Activity == ACT_WALK) + { + static int iStep = 0; + iStep = ! iStep; + if (iStep) + { + switch( RANDOM_LONG( 0, 3 ) ) + { + case 0: EMIT_SOUND( ENT(pev), CHAN_BODY, "player/pl_step1.wav", 0.5, ATTN_NORM); break; + case 1: EMIT_SOUND( ENT(pev), CHAN_BODY, "player/pl_step3.wav", 0.5, ATTN_NORM); break; + case 2: EMIT_SOUND( ENT(pev), CHAN_BODY, "player/pl_step2.wav", 0.5, ATTN_NORM); break; + case 3: EMIT_SOUND( ENT(pev), CHAN_BODY, "player/pl_step4.wav", 0.5, ATTN_NORM); break; + } + } + } +} + + +//========================================================= +// StartTask +//========================================================= +void CHAssassin :: StartTask ( Task_t *pTask ) +{ + switch ( pTask->iTask ) + { + case TASK_RANGE_ATTACK2: + if (!m_fThrowGrenade) + { + TaskComplete( ); + } + else + { + CBaseMonster :: StartTask ( pTask ); + } + break; + case TASK_ASSASSIN_FALL_TO_GROUND: + break; + default: + CBaseMonster :: StartTask ( pTask ); + break; + } +} + + +//========================================================= +// RunTask +//========================================================= +void CHAssassin :: RunTask ( Task_t *pTask ) +{ + switch ( pTask->iTask ) + { + case TASK_ASSASSIN_FALL_TO_GROUND: + MakeIdealYaw( m_vecEnemyLKP ); + ChangeYaw( pev->yaw_speed ); + + if (m_fSequenceFinished) + { + if (pev->velocity.z > 0) + { + pev->sequence = LookupSequence( "fly_up" ); + } + else if (HasConditions ( bits_COND_SEE_ENEMY )) + { + pev->sequence = LookupSequence( "fly_attack" ); + pev->frame = 0; + } + else + { + pev->sequence = LookupSequence( "fly_down" ); + pev->frame = 0; + } + + ResetSequenceInfo( ); + SetYawSpeed(); + } + if (pev->flags & FL_ONGROUND) + { + // ALERT( at_console, "on ground\n"); + TaskComplete( ); + } + break; + default: + CBaseMonster :: RunTask ( pTask ); + break; + } +} + +//========================================================= +// GetSchedule - Decides which type of schedule best suits +// the monster's current state and conditions. Then calls +// monster's member function to get a pointer to a schedule +// of the proper type. +//========================================================= +Schedule_t *CHAssassin :: GetSchedule ( void ) +{ + switch ( m_MonsterState ) + { + case MONSTERSTATE_IDLE: + case MONSTERSTATE_ALERT: + { + if ( HasConditions ( bits_COND_HEAR_SOUND )) + { + CSound *pSound; + pSound = PBestSound(); + + ASSERT( pSound != NULL ); + if ( pSound && (pSound->m_iType & bits_SOUND_DANGER) ) + { + return GetScheduleOfType( SCHED_TAKE_COVER_FROM_BEST_SOUND ); + } + if ( pSound && (pSound->m_iType & bits_SOUND_COMBAT) ) + { + return GetScheduleOfType( SCHED_INVESTIGATE_SOUND ); + } + } + } + break; + + case MONSTERSTATE_COMBAT: + { +// dead enemy + if ( HasConditions( bits_COND_ENEMY_DEAD ) ) + { + // call base class, all code to handle dead enemies is centralized there. + return CBaseMonster :: GetSchedule(); + } + + // flying? + if ( pev->movetype == MOVETYPE_TOSS) + { + if (pev->flags & FL_ONGROUND) + { + // ALERT( at_console, "landed\n"); + // just landed + pev->movetype = MOVETYPE_STEP; + return GetScheduleOfType ( SCHED_ASSASSIN_JUMP_LAND ); + } + else + { + // ALERT( at_console, "jump\n"); + // jump or jump/shoot + if ( m_MonsterState == MONSTERSTATE_COMBAT ) + return GetScheduleOfType ( SCHED_ASSASSIN_JUMP ); + else + return GetScheduleOfType ( SCHED_ASSASSIN_JUMP_ATTACK ); + } + } + + if ( HasConditions ( bits_COND_HEAR_SOUND )) + { + CSound *pSound; + pSound = PBestSound(); + + ASSERT( pSound != NULL ); + if ( pSound && (pSound->m_iType & bits_SOUND_DANGER) ) + { + return GetScheduleOfType( SCHED_TAKE_COVER_FROM_BEST_SOUND ); + } + } + + if ( HasConditions ( bits_COND_LIGHT_DAMAGE ) ) + { + m_iFrustration++; + } + if ( HasConditions ( bits_COND_HEAVY_DAMAGE ) ) + { + m_iFrustration++; + } + + // jump player! + if ( HasConditions ( bits_COND_CAN_MELEE_ATTACK1 ) ) + { + // ALERT( at_console, "melee attack 1\n"); + return GetScheduleOfType ( SCHED_MELEE_ATTACK1 ); + } + + // throw grenade + if ( HasConditions ( bits_COND_CAN_RANGE_ATTACK2 ) ) + { + // ALERT( at_console, "range attack 2\n"); + return GetScheduleOfType ( SCHED_RANGE_ATTACK2 ); + } + + // spotted + if ( HasConditions ( bits_COND_SEE_ENEMY ) && HasConditions ( bits_COND_ENEMY_FACING_ME ) ) + { + // ALERT( at_console, "exposed\n"); + m_iFrustration++; + return GetScheduleOfType ( SCHED_ASSASSIN_EXPOSED ); + } + + // can attack + if ( HasConditions ( bits_COND_CAN_RANGE_ATTACK1 ) ) + { + // ALERT( at_console, "range attack 1\n"); + m_iFrustration = 0; + return GetScheduleOfType ( SCHED_RANGE_ATTACK1 ); + } + + if ( HasConditions ( bits_COND_SEE_ENEMY ) ) + { + // ALERT( at_console, "face\n"); + return GetScheduleOfType ( SCHED_COMBAT_FACE ); + } + + // new enemy + if ( HasConditions ( bits_COND_NEW_ENEMY ) ) + { + // ALERT( at_console, "take cover\n"); + return GetScheduleOfType ( SCHED_TAKE_COVER_FROM_ENEMY ); + } + + // ALERT( at_console, "stand\n"); + return GetScheduleOfType ( SCHED_ALERT_STAND ); + } + break; + } + + return CBaseMonster :: GetSchedule(); +} + +//========================================================= +//========================================================= +Schedule_t* CHAssassin :: GetScheduleOfType ( int Type ) +{ + // ALERT( at_console, "%d\n", m_iFrustration ); + switch ( Type ) + { + case SCHED_TAKE_COVER_FROM_ENEMY: + if (pev->health > 30) + return slAssassinTakeCoverFromEnemy; + else + return slAssassinTakeCoverFromEnemy2; + case SCHED_TAKE_COVER_FROM_BEST_SOUND: + return slAssassinTakeCoverFromBestSound; + case SCHED_ASSASSIN_EXPOSED: + return slAssassinExposed; + case SCHED_FAIL: + if (m_MonsterState == MONSTERSTATE_COMBAT) + return slAssassinFail; + break; + case SCHED_ALERT_STAND: + if (m_MonsterState == MONSTERSTATE_COMBAT) + return slAssassinHide; + break; + case SCHED_CHASE_ENEMY: + return slAssassinHunt; + case SCHED_MELEE_ATTACK1: + if (pev->flags & FL_ONGROUND) + { + if (m_flNextJump > gpGlobals->time) + { + // can't jump yet, go ahead and fail + return slAssassinFail; + } + else + { + return slAssassinJump; + } + } + else + { + return slAssassinJumpAttack; + } + case SCHED_ASSASSIN_JUMP: + case SCHED_ASSASSIN_JUMP_ATTACK: + return slAssassinJumpAttack; + case SCHED_ASSASSIN_JUMP_LAND: + return slAssassinJumpLand; + } + + return CBaseMonster :: GetScheduleOfType( Type ); +} + +#endif \ No newline at end of file diff --git a/spirit/headcrab.cpp b/dlls/headcrab.cpp similarity index 93% rename from spirit/headcrab.cpp rename to dlls/headcrab.cpp index 48323976..9adc8772 100644 --- a/spirit/headcrab.cpp +++ b/dlls/headcrab.cpp @@ -1,568 +1,555 @@ -/*** -* -* Copyright (c) 1996-2002, 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. -* -* This source code contains proprietary and confidential information of -* Valve LLC and its suppliers. Access to this code is restricted to -* persons who have executed a written SDK license with Valve. Any access, -* use or distribution of this code by or to any unlicensed person is illegal. -* -****/ -//========================================================= -// headcrab.cpp - tiny, jumpy alien parasite -//========================================================= - -#include "extdll.h" -#include "util.h" -#include "cbase.h" -#include "monsters.h" -#include "schedule.h" -#include "game.h" - -//========================================================= -// Monster's Anim Events Go Here -//========================================================= -#define HC_AE_JUMPATTACK ( 2 ) - -Task_t tlHCRangeAttack1[] = -{ - { TASK_STOP_MOVING, (float)0 }, - { TASK_FACE_IDEAL, (float)0 }, - { TASK_RANGE_ATTACK1, (float)0 }, - { TASK_SET_ACTIVITY, (float)ACT_IDLE }, - { TASK_FACE_IDEAL, (float)0 }, - { TASK_WAIT_RANDOM, (float)0.5 }, -}; - -Schedule_t slHCRangeAttack1[] = -{ - { - tlHCRangeAttack1, - ARRAYSIZE ( tlHCRangeAttack1 ), - bits_COND_ENEMY_OCCLUDED | - bits_COND_NO_AMMO_LOADED, - 0, - "HCRangeAttack1" - }, -}; - -Task_t tlHCRangeAttack1Fast[] = -{ - { TASK_STOP_MOVING, (float)0 }, - { TASK_FACE_IDEAL, (float)0 }, - { TASK_RANGE_ATTACK1, (float)0 }, - { TASK_SET_ACTIVITY, (float)ACT_IDLE }, -}; - -Schedule_t slHCRangeAttack1Fast[] = -{ - { - tlHCRangeAttack1Fast, - ARRAYSIZE ( tlHCRangeAttack1Fast ), - bits_COND_ENEMY_OCCLUDED | - bits_COND_NO_AMMO_LOADED, - 0, - "HCRAFast" - }, -}; - -class CHeadCrab : public CBaseMonster -{ -public: - void Spawn( void ); - void Precache( void ); - void RunTask ( Task_t *pTask ); - void StartTask ( Task_t *pTask ); - void SetYawSpeed ( void ); - void EXPORT LeapTouch ( CBaseEntity *pOther ); - Vector Center( void ); - Vector BodyTarget( const Vector &posSrc ); - void PainSound( void ); - void DeathSound( void ); - void IdleSound( void ); - void AlertSound( void ); - void PrescheduleThink( void ); - int Classify ( void ); - void HandleAnimEvent( MonsterEvent_t *pEvent ); - BOOL CheckRangeAttack1 ( float flDot, float flDist ); - BOOL CheckRangeAttack2 ( float flDot, float flDist ); - int TakeDamage( entvars_t *pevInflictor, entvars_t *pevAttacker, float flDamage, int bitsDamageType ); - - virtual float GetDamageAmount( void ) { return gSkillData.headcrabDmgBite; } - virtual int GetVoicePitch( void ) { return 100; } - virtual float GetSoundVolue( void ) { return 1.0; } - Schedule_t* GetScheduleOfType ( int Type ); - - CUSTOM_SCHEDULES; - - static const char *pIdleSounds[]; - static const char *pAlertSounds[]; - static const char *pPainSounds[]; - static const char *pAttackSounds[]; - static const char *pDeathSounds[]; - static const char *pBiteSounds[]; -}; -LINK_ENTITY_TO_CLASS( monster_headcrab, CHeadCrab ); - -DEFINE_CUSTOM_SCHEDULES( CHeadCrab ) -{ - slHCRangeAttack1, - slHCRangeAttack1Fast, -}; - -IMPLEMENT_CUSTOM_SCHEDULES( CHeadCrab, CBaseMonster ); - -const char *CHeadCrab::pIdleSounds[] = -{ - "headcrab/hc_idle1.wav", - "headcrab/hc_idle2.wav", - "headcrab/hc_idle3.wav", -}; -const char *CHeadCrab::pAlertSounds[] = -{ - "headcrab/hc_alert1.wav", -}; -const char *CHeadCrab::pPainSounds[] = -{ - "headcrab/hc_pain1.wav", - "headcrab/hc_pain2.wav", - "headcrab/hc_pain3.wav", -}; -const char *CHeadCrab::pAttackSounds[] = -{ - "headcrab/hc_attack1.wav", - "headcrab/hc_attack2.wav", - "headcrab/hc_attack3.wav", -}; - -const char *CHeadCrab::pDeathSounds[] = -{ - "headcrab/hc_die1.wav", - "headcrab/hc_die2.wav", -}; - -const char *CHeadCrab::pBiteSounds[] = -{ - "headcrab/hc_headbite.wav", -}; - -//========================================================= -// Classify - indicates this monster's place in the -// relationship table. -//========================================================= -int CHeadCrab :: Classify ( void ) -{ - return m_iClass?m_iClass:CLASS_ALIEN_PREY; -} - -//========================================================= -// Center - returns the real center of the headcrab. The -// bounding box is much larger than the actual creature so -// this is needed for targeting -//========================================================= -Vector CHeadCrab :: Center ( void ) -{ - return Vector( pev->origin.x, pev->origin.y, pev->origin.z + 6 ); -} - - -Vector CHeadCrab :: BodyTarget( const Vector &posSrc ) -{ - return Center( ); -} - -//========================================================= -// SetYawSpeed - allows each sequence to have a different -// turn rate associated with it. -//========================================================= -void CHeadCrab :: SetYawSpeed ( void ) -{ - int ys; - - switch ( m_Activity ) - { - case ACT_IDLE: - ys = 30; - break; - case ACT_RUN: - case ACT_WALK: - ys = 20; - break; - case ACT_TURN_LEFT: - case ACT_TURN_RIGHT: - ys = 60; - break; - case ACT_RANGE_ATTACK1: - ys = 30; - break; - default: - ys = 30; - break; - } - - pev->yaw_speed = ys; -} - -//========================================================= -// HandleAnimEvent - catches the monster-specific messages -// that occur when tagged animation frames are played. -//========================================================= -void CHeadCrab :: HandleAnimEvent( MonsterEvent_t *pEvent ) -{ - switch( pEvent->event ) - { - case HC_AE_JUMPATTACK: - { - ClearBits( pev->flags, FL_ONGROUND ); - - UTIL_SetOrigin (this, pev->origin + Vector ( 0 , 0 , 1) );// take him off ground so engine doesn't instantly reset onground - UTIL_MakeVectors ( pev->angles ); - - Vector vecJumpDir; - if (m_hEnemy != NULL) - { - float gravity = g_psv_gravity->value; - if (gravity <= 1) - gravity = 1; - - // How fast does the headcrab need to travel to reach that height given gravity? - float height = (m_hEnemy->pev->origin.z + m_hEnemy->pev->view_ofs.z - pev->origin.z); - if (height < 16) - height = 16; - float speed = sqrt( 2 * gravity * height ); - float time = speed / gravity; - - // Scale the sideways velocity to get there at the right time - vecJumpDir = (m_hEnemy->pev->origin + m_hEnemy->pev->view_ofs - pev->origin); - vecJumpDir = vecJumpDir * ( 1.0 / time ); - - // Speed to offset gravity at the desired height - vecJumpDir.z = speed; - - // Don't jump too far/fast - float distance = vecJumpDir.Length(); - - if (distance > 650) - { - vecJumpDir = vecJumpDir * ( 650.0 / distance ); - } - } - else - { - // jump hop, don't care where - vecJumpDir = Vector( gpGlobals->v_forward.x, gpGlobals->v_forward.y, gpGlobals->v_up.z ) * 350; - } - - int iSound = RANDOM_LONG(0,2); - if ( iSound != 0 ) - EMIT_SOUND_DYN( edict(), CHAN_VOICE, pAttackSounds[iSound], GetSoundVolue(), ATTN_IDLE, 0, GetVoicePitch() ); - - pev->velocity = vecJumpDir; - m_flNextAttack = gpGlobals->time + 2; - } - break; - - default: - CBaseMonster::HandleAnimEvent( pEvent ); - break; - } -} - -//========================================================= -// Spawn -//========================================================= -void CHeadCrab :: Spawn() -{ - Precache( ); - - if (pev->model) - SET_MODEL(ENT(pev), STRING(pev->model)); //LRC - else - SET_MODEL(ENT(pev), "models/headcrab.mdl"); - UTIL_SetSize(pev, Vector(-12, -12, 0), Vector(12, 12, 24)); - - pev->solid = SOLID_SLIDEBOX; - pev->movetype = MOVETYPE_STEP; - m_bloodColor = BLOOD_COLOR_GREEN; - pev->effects = 0; - if (pev->health == 0) - pev->health = gSkillData.headcrabHealth; - pev->view_ofs = Vector ( 0, 0, 20 );// position of the eyes relative to monster's origin. - pev->yaw_speed = 5;//!!! should we put this in the monster's changeanim function since turn rates may vary with state/anim? - m_flFieldOfView = 0.5;// indicates the width of this monster's forward view cone ( as a dotproduct result ) - m_MonsterState = MONSTERSTATE_NONE; - - MonsterInit(); -} - -//========================================================= -// Precache - precaches all resources this monster needs -//========================================================= -void CHeadCrab :: Precache() -{ - PRECACHE_SOUND_ARRAY(pIdleSounds); - PRECACHE_SOUND_ARRAY(pAlertSounds); - PRECACHE_SOUND_ARRAY(pPainSounds); - PRECACHE_SOUND_ARRAY(pAttackSounds); - PRECACHE_SOUND_ARRAY(pDeathSounds); - PRECACHE_SOUND_ARRAY(pBiteSounds); - - if (pev->model) - PRECACHE_MODEL((char*)STRING(pev->model)); //LRC - else - PRECACHE_MODEL("models/headcrab.mdl"); -} - - -//========================================================= -// RunTask -//========================================================= -void CHeadCrab :: RunTask ( Task_t *pTask ) -{ - switch ( pTask->iTask ) - { - case TASK_RANGE_ATTACK1: - case TASK_RANGE_ATTACK2: - { - if ( m_fSequenceFinished ) - { - TaskComplete(); - SetTouch( NULL ); - m_IdealActivity = ACT_IDLE; - } - break; - } - default: - { - CBaseMonster :: RunTask(pTask); - } - } -} - -//========================================================= -// LeapTouch - this is the headcrab's touch function when it -// is in the air -//========================================================= -void CHeadCrab :: LeapTouch ( CBaseEntity *pOther ) -{ - if ( !pOther->pev->takedamage ) - { - return; - } - - if ( pOther->Classify() == Classify() ) - { - return; - } - - // Don't hit if back on ground - if ( !FBitSet( pev->flags, FL_ONGROUND ) ) - { - EMIT_SOUND_DYN( edict(), CHAN_WEAPON, RANDOM_SOUND_ARRAY(pBiteSounds), GetSoundVolue(), ATTN_IDLE, 0, GetVoicePitch() ); - - pOther->TakeDamage( pev, pev, GetDamageAmount(), DMG_SLASH ); - } - - SetTouch( NULL ); -} - -//========================================================= -// PrescheduleThink -//========================================================= -void CHeadCrab :: PrescheduleThink ( void ) -{ - // make the crab coo a little bit in combat state - if ( m_MonsterState == MONSTERSTATE_COMBAT && RANDOM_FLOAT( 0, 5 ) < 0.1 ) - { - IdleSound(); - } -} - -void CHeadCrab :: StartTask ( Task_t *pTask ) -{ - m_iTaskStatus = TASKSTATUS_RUNNING; - - switch ( pTask->iTask ) - { - case TASK_RANGE_ATTACK1: - { - EMIT_SOUND_DYN( edict(), CHAN_WEAPON, pAttackSounds[0], GetSoundVolue(), ATTN_IDLE, 0, GetVoicePitch() ); - m_IdealActivity = ACT_RANGE_ATTACK1; - SetTouch(&CHeadCrab :: LeapTouch ); - break; - } - default: - { - CBaseMonster :: StartTask( pTask ); - } - } -} - - -//========================================================= -// CheckRangeAttack1 -//========================================================= -BOOL CHeadCrab :: CheckRangeAttack1 ( float flDot, float flDist ) -{ - if ( FBitSet( pev->flags, FL_ONGROUND ) && flDist <= 256 && flDot >= 0.65 ) - { - return TRUE; - } - return FALSE; -} - -//========================================================= -// CheckRangeAttack2 -//========================================================= -BOOL CHeadCrab :: CheckRangeAttack2 ( float flDot, float flDist ) -{ - return FALSE; - // BUGBUG: Why is this code here? There is no ACT_RANGE_ATTACK2 animation. I've disabled it for now. -#if 0 - if ( FBitSet( pev->flags, FL_ONGROUND ) && flDist > 64 && flDist <= 256 && flDot >= 0.5 ) - { - return TRUE; - } - return FALSE; -#endif -} - -int CHeadCrab :: TakeDamage( entvars_t *pevInflictor, entvars_t *pevAttacker, float flDamage, int bitsDamageType ) -{ - // Don't take any acid damage -- BigMomma's mortar is acid - if ( bitsDamageType & DMG_ACID ) - flDamage = 0; - - return CBaseMonster::TakeDamage( pevInflictor, pevAttacker, flDamage, bitsDamageType ); -} - -//========================================================= -// IdleSound -//========================================================= -#define CRAB_ATTN_IDLE (float)1.5 -void CHeadCrab :: IdleSound ( void ) -{ - EMIT_SOUND_DYN( edict(), CHAN_VOICE, RANDOM_SOUND_ARRAY(pIdleSounds), GetSoundVolue(), ATTN_IDLE, 0, GetVoicePitch() ); -} - -//========================================================= -// AlertSound -//========================================================= -void CHeadCrab :: AlertSound ( void ) -{ - EMIT_SOUND_DYN( edict(), CHAN_VOICE, RANDOM_SOUND_ARRAY(pAlertSounds), GetSoundVolue(), ATTN_IDLE, 0, GetVoicePitch() ); -} - -//========================================================= -// AlertSound -//========================================================= -void CHeadCrab :: PainSound ( void ) -{ - EMIT_SOUND_DYN( edict(), CHAN_VOICE, RANDOM_SOUND_ARRAY(pPainSounds), GetSoundVolue(), ATTN_IDLE, 0, GetVoicePitch() ); -} - -//========================================================= -// DeathSound -//========================================================= -void CHeadCrab :: DeathSound ( void ) -{ - EMIT_SOUND_DYN( edict(), CHAN_VOICE, RANDOM_SOUND_ARRAY(pDeathSounds), GetSoundVolue(), ATTN_IDLE, 0, GetVoicePitch() ); -} - -Schedule_t* CHeadCrab :: GetScheduleOfType ( int Type ) -{ - switch ( Type ) - { - case SCHED_RANGE_ATTACK1: - { - return &slHCRangeAttack1[ 0 ]; - } - break; - } - - return CBaseMonster::GetScheduleOfType( Type ); -} - - -class CBabyCrab : public CHeadCrab -{ -public: - void Spawn( void ); - void Precache( void ); - void SetYawSpeed ( void ); - float GetDamageAmount( void ) { return gSkillData.headcrabDmgBite * 0.3; } - BOOL CheckRangeAttack1 ( float flDot, float flDist ); - Schedule_t* GetScheduleOfType ( int Type ); - virtual int GetVoicePitch( void ) { return PITCH_NORM + RANDOM_LONG(40,50); } - virtual float GetSoundVolue( void ) { return 0.8; } -}; -LINK_ENTITY_TO_CLASS( monster_babycrab, CBabyCrab ); - -void CBabyCrab :: Spawn( void ) -{ - CHeadCrab::Spawn(); - if (pev->model) - SET_MODEL(ENT(pev), STRING(pev->model)); //LRC - else - SET_MODEL(ENT(pev), "models/baby_headcrab.mdl"); - pev->rendermode = kRenderTransTexture; - pev->renderamt = 192; - UTIL_SetSize(pev, Vector(-12, -12, 0), Vector(12, 12, 24)); - - pev->health = gSkillData.headcrabHealth * 0.25; // less health than full grown -} - -void CBabyCrab :: Precache( void ) -{ - if (pev->model) - PRECACHE_MODEL((char*)STRING(pev->model)); //LRC - else - PRECACHE_MODEL( "models/baby_headcrab.mdl" ); - CHeadCrab::Precache(); -} - - -void CBabyCrab :: SetYawSpeed ( void ) -{ - pev->yaw_speed = 120; -} - - -BOOL CBabyCrab :: CheckRangeAttack1( float flDot, float flDist ) -{ - if ( pev->flags & FL_ONGROUND ) - { - if ( pev->groundentity && (pev->groundentity->v.flags & (FL_CLIENT|FL_MONSTER)) ) - return TRUE; - - // A little less accurate, but jump from closer - if ( flDist <= 180 && flDot >= 0.55 ) - return TRUE; - } - - return FALSE; -} - - -Schedule_t* CBabyCrab :: GetScheduleOfType ( int Type ) -{ - switch( Type ) - { - case SCHED_FAIL: // If you fail, try to jump! - if ( m_hEnemy != NULL ) - return slHCRangeAttack1Fast; - break; - - case SCHED_RANGE_ATTACK1: - { - return slHCRangeAttack1Fast; - } - break; - } - - return CHeadCrab::GetScheduleOfType( Type ); -} +/*** +* +* Copyright (c) 1996-2002, 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. +* +* This source code contains proprietary and confidential information of +* Valve LLC and its suppliers. Access to this code is restricted to +* persons who have executed a written SDK license with Valve. Any access, +* use or distribution of this code by or to any unlicensed person is illegal. +* +****/ +//========================================================= +// headcrab.cpp - tiny, jumpy alien parasite +//========================================================= + +#include "extdll.h" +#include "util.h" +#include "cbase.h" +#include "monsters.h" +#include "schedule.h" +#include "game.h" + +//========================================================= +// Monster's Anim Events Go Here +//========================================================= +#define HC_AE_JUMPATTACK ( 2 ) + +Task_t tlHCRangeAttack1[] = +{ + { TASK_STOP_MOVING, (float)0 }, + { TASK_FACE_IDEAL, (float)0 }, + { TASK_RANGE_ATTACK1, (float)0 }, + { TASK_SET_ACTIVITY, (float)ACT_IDLE }, + { TASK_FACE_IDEAL, (float)0 }, + { TASK_WAIT_RANDOM, (float)0.5 }, +}; + +Schedule_t slHCRangeAttack1[] = +{ + { + tlHCRangeAttack1, + ARRAYSIZE ( tlHCRangeAttack1 ), + bits_COND_ENEMY_OCCLUDED | + bits_COND_NO_AMMO_LOADED, + 0, + "HCRangeAttack1" + }, +}; + +Task_t tlHCRangeAttack1Fast[] = +{ + { TASK_STOP_MOVING, (float)0 }, + { TASK_FACE_IDEAL, (float)0 }, + { TASK_RANGE_ATTACK1, (float)0 }, + { TASK_SET_ACTIVITY, (float)ACT_IDLE }, +}; + +Schedule_t slHCRangeAttack1Fast[] = +{ + { + tlHCRangeAttack1Fast, + ARRAYSIZE ( tlHCRangeAttack1Fast ), + bits_COND_ENEMY_OCCLUDED | + bits_COND_NO_AMMO_LOADED, + 0, + "HCRAFast" + }, +}; + +class CHeadCrab : public CBaseMonster +{ +public: + void Spawn( void ); + void Precache( void ); + void RunTask ( Task_t *pTask ); + void StartTask ( Task_t *pTask ); + void SetYawSpeed ( void ); + void EXPORT LeapTouch ( CBaseEntity *pOther ); + Vector Center( void ); + Vector BodyTarget( const Vector &posSrc ); + void PainSound( void ); + void DeathSound( void ); + void IdleSound( void ); + void AlertSound( void ); + void PrescheduleThink( void ); + int Classify ( void ); + void HandleAnimEvent( MonsterEvent_t *pEvent ); + BOOL CheckRangeAttack1 ( float flDot, float flDist ); + BOOL CheckRangeAttack2 ( float flDot, float flDist ); + int TakeDamage( entvars_t *pevInflictor, entvars_t *pevAttacker, float flDamage, int bitsDamageType ); + + virtual float GetDamageAmount( void ) { return gSkillData.headcrabDmgBite; } + virtual int GetVoicePitch( void ) { return 100; } + virtual float GetSoundVolue( void ) { return 1.0; } + Schedule_t* GetScheduleOfType ( int Type ); + + CUSTOM_SCHEDULES; + + static const char *pIdleSounds[]; + static const char *pAlertSounds[]; + static const char *pPainSounds[]; + static const char *pAttackSounds[]; + static const char *pDeathSounds[]; + static const char *pBiteSounds[]; +}; +LINK_ENTITY_TO_CLASS( monster_headcrab, CHeadCrab ); + +DEFINE_CUSTOM_SCHEDULES( CHeadCrab ) +{ + slHCRangeAttack1, + slHCRangeAttack1Fast, +}; + +IMPLEMENT_CUSTOM_SCHEDULES( CHeadCrab, CBaseMonster ); + +const char *CHeadCrab::pIdleSounds[] = +{ + "headcrab/hc_idle1.wav", + "headcrab/hc_idle2.wav", + "headcrab/hc_idle3.wav", +}; +const char *CHeadCrab::pAlertSounds[] = +{ + "headcrab/hc_alert1.wav", +}; +const char *CHeadCrab::pPainSounds[] = +{ + "headcrab/hc_pain1.wav", + "headcrab/hc_pain2.wav", + "headcrab/hc_pain3.wav", +}; +const char *CHeadCrab::pAttackSounds[] = +{ + "headcrab/hc_attack1.wav", + "headcrab/hc_attack2.wav", + "headcrab/hc_attack3.wav", +}; + +const char *CHeadCrab::pDeathSounds[] = +{ + "headcrab/hc_die1.wav", + "headcrab/hc_die2.wav", +}; + +const char *CHeadCrab::pBiteSounds[] = +{ + "headcrab/hc_headbite.wav", +}; + +//========================================================= +// Classify - indicates this monster's place in the +// relationship table. +//========================================================= +int CHeadCrab :: Classify ( void ) +{ + return CLASS_ALIEN_PREY; +} + +//========================================================= +// Center - returns the real center of the headcrab. The +// bounding box is much larger than the actual creature so +// this is needed for targeting +//========================================================= +Vector CHeadCrab :: Center ( void ) +{ + return Vector( pev->origin.x, pev->origin.y, pev->origin.z + 6 ); +} + + +Vector CHeadCrab :: BodyTarget( const Vector &posSrc ) +{ + return Center( ); +} + +//========================================================= +// SetYawSpeed - allows each sequence to have a different +// turn rate associated with it. +//========================================================= +void CHeadCrab :: SetYawSpeed ( void ) +{ + int ys; + + switch ( m_Activity ) + { + case ACT_IDLE: + ys = 30; + break; + case ACT_RUN: + case ACT_WALK: + ys = 20; + break; + case ACT_TURN_LEFT: + case ACT_TURN_RIGHT: + ys = 60; + break; + case ACT_RANGE_ATTACK1: + ys = 30; + break; + default: + ys = 30; + break; + } + + pev->yaw_speed = ys; +} + +//========================================================= +// HandleAnimEvent - catches the monster-specific messages +// that occur when tagged animation frames are played. +//========================================================= +void CHeadCrab :: HandleAnimEvent( MonsterEvent_t *pEvent ) +{ + switch( pEvent->event ) + { + case HC_AE_JUMPATTACK: + { + ClearBits( pev->flags, FL_ONGROUND ); + + UTIL_SetOrigin (pev, pev->origin + Vector ( 0 , 0 , 1) );// take him off ground so engine doesn't instantly reset onground + UTIL_MakeVectors ( pev->angles ); + + Vector vecJumpDir; + if (m_hEnemy != NULL) + { + float gravity = g_psv_gravity->value; + if (gravity <= 1) + gravity = 1; + + // How fast does the headcrab need to travel to reach that height given gravity? + float height = (m_hEnemy->pev->origin.z + m_hEnemy->pev->view_ofs.z - pev->origin.z); + if (height < 16) + height = 16; + float speed = sqrt( 2 * gravity * height ); + float time = speed / gravity; + + // Scale the sideways velocity to get there at the right time + vecJumpDir = (m_hEnemy->pev->origin + m_hEnemy->pev->view_ofs - pev->origin); + vecJumpDir = vecJumpDir * ( 1.0 / time ); + + // Speed to offset gravity at the desired height + vecJumpDir.z = speed; + + // Don't jump too far/fast + float distance = vecJumpDir.Length(); + + if (distance > 650) + { + vecJumpDir = vecJumpDir * ( 650.0 / distance ); + } + } + else + { + // jump hop, don't care where + vecJumpDir = Vector( gpGlobals->v_forward.x, gpGlobals->v_forward.y, gpGlobals->v_up.z ) * 350; + } + + int iSound = RANDOM_LONG(0,2); + if ( iSound != 0 ) + EMIT_SOUND_DYN( edict(), CHAN_VOICE, pAttackSounds[iSound], GetSoundVolue(), ATTN_IDLE, 0, GetVoicePitch() ); + + pev->velocity = vecJumpDir; + m_flNextAttack = gpGlobals->time + 2; + } + break; + + default: + CBaseMonster::HandleAnimEvent( pEvent ); + break; + } +} + +//========================================================= +// Spawn +//========================================================= +void CHeadCrab :: Spawn() +{ + Precache( ); + + SET_MODEL(ENT(pev), "models/headcrab.mdl"); + UTIL_SetSize(pev, Vector(-12, -12, 0), Vector(12, 12, 24)); + + pev->solid = SOLID_SLIDEBOX; + pev->movetype = MOVETYPE_STEP; + m_bloodColor = BLOOD_COLOR_GREEN; + pev->effects = 0; + pev->health = gSkillData.headcrabHealth; + pev->view_ofs = Vector ( 0, 0, 20 );// position of the eyes relative to monster's origin. + pev->yaw_speed = 5;//!!! should we put this in the monster's changeanim function since turn rates may vary with state/anim? + m_flFieldOfView = 0.5;// indicates the width of this monster's forward view cone ( as a dotproduct result ) + m_MonsterState = MONSTERSTATE_NONE; + + MonsterInit(); +} + +//========================================================= +// Precache - precaches all resources this monster needs +//========================================================= +void CHeadCrab :: Precache() +{ + PRECACHE_SOUND_ARRAY(pIdleSounds); + PRECACHE_SOUND_ARRAY(pAlertSounds); + PRECACHE_SOUND_ARRAY(pPainSounds); + PRECACHE_SOUND_ARRAY(pAttackSounds); + PRECACHE_SOUND_ARRAY(pDeathSounds); + PRECACHE_SOUND_ARRAY(pBiteSounds); + + PRECACHE_MODEL("models/headcrab.mdl"); +} + + +//========================================================= +// RunTask +//========================================================= +void CHeadCrab :: RunTask ( Task_t *pTask ) +{ + switch ( pTask->iTask ) + { + case TASK_RANGE_ATTACK1: + case TASK_RANGE_ATTACK2: + { + if ( m_fSequenceFinished ) + { + TaskComplete(); + SetTouch( NULL ); + m_IdealActivity = ACT_IDLE; + } + break; + } + default: + { + CBaseMonster :: RunTask(pTask); + } + } +} + +//========================================================= +// LeapTouch - this is the headcrab's touch function when it +// is in the air +//========================================================= +void CHeadCrab :: LeapTouch ( CBaseEntity *pOther ) +{ + if ( !pOther->pev->takedamage ) + { + return; + } + + if ( pOther->Classify() == Classify() ) + { + return; + } + + // Don't hit if back on ground + if ( !FBitSet( pev->flags, FL_ONGROUND ) ) + { + EMIT_SOUND_DYN( edict(), CHAN_WEAPON, RANDOM_SOUND_ARRAY(pBiteSounds), GetSoundVolue(), ATTN_IDLE, 0, GetVoicePitch() ); + + pOther->TakeDamage( pev, pev, GetDamageAmount(), DMG_SLASH ); + } + + SetTouch( NULL ); +} + +//========================================================= +// PrescheduleThink +//========================================================= +void CHeadCrab :: PrescheduleThink ( void ) +{ + // make the crab coo a little bit in combat state + if ( m_MonsterState == MONSTERSTATE_COMBAT && RANDOM_FLOAT( 0, 5 ) < 0.1 ) + { + IdleSound(); + } +} + +void CHeadCrab :: StartTask ( Task_t *pTask ) +{ + m_iTaskStatus = TASKSTATUS_RUNNING; + + switch ( pTask->iTask ) + { + case TASK_RANGE_ATTACK1: + { + EMIT_SOUND_DYN( edict(), CHAN_WEAPON, pAttackSounds[0], GetSoundVolue(), ATTN_IDLE, 0, GetVoicePitch() ); + m_IdealActivity = ACT_RANGE_ATTACK1; + SetTouch ( LeapTouch ); + break; + } + default: + { + CBaseMonster :: StartTask( pTask ); + } + } +} + + +//========================================================= +// CheckRangeAttack1 +//========================================================= +BOOL CHeadCrab :: CheckRangeAttack1 ( float flDot, float flDist ) +{ + if ( FBitSet( pev->flags, FL_ONGROUND ) && flDist <= 256 && flDot >= 0.65 ) + { + return TRUE; + } + return FALSE; +} + +//========================================================= +// CheckRangeAttack2 +//========================================================= +BOOL CHeadCrab :: CheckRangeAttack2 ( float flDot, float flDist ) +{ + return FALSE; + // BUGBUG: Why is this code here? There is no ACT_RANGE_ATTACK2 animation. I've disabled it for now. +#if 0 + if ( FBitSet( pev->flags, FL_ONGROUND ) && flDist > 64 && flDist <= 256 && flDot >= 0.5 ) + { + return TRUE; + } + return FALSE; +#endif +} + +int CHeadCrab :: TakeDamage( entvars_t *pevInflictor, entvars_t *pevAttacker, float flDamage, int bitsDamageType ) +{ + // Don't take any acid damage -- BigMomma's mortar is acid + if ( bitsDamageType & DMG_ACID ) + flDamage = 0; + + return CBaseMonster::TakeDamage( pevInflictor, pevAttacker, flDamage, bitsDamageType ); +} + +//========================================================= +// IdleSound +//========================================================= +#define CRAB_ATTN_IDLE (float)1.5 +void CHeadCrab :: IdleSound ( void ) +{ + EMIT_SOUND_DYN( edict(), CHAN_VOICE, RANDOM_SOUND_ARRAY(pIdleSounds), GetSoundVolue(), ATTN_IDLE, 0, GetVoicePitch() ); +} + +//========================================================= +// AlertSound +//========================================================= +void CHeadCrab :: AlertSound ( void ) +{ + EMIT_SOUND_DYN( edict(), CHAN_VOICE, RANDOM_SOUND_ARRAY(pAlertSounds), GetSoundVolue(), ATTN_IDLE, 0, GetVoicePitch() ); +} + +//========================================================= +// AlertSound +//========================================================= +void CHeadCrab :: PainSound ( void ) +{ + EMIT_SOUND_DYN( edict(), CHAN_VOICE, RANDOM_SOUND_ARRAY(pPainSounds), GetSoundVolue(), ATTN_IDLE, 0, GetVoicePitch() ); +} + +//========================================================= +// DeathSound +//========================================================= +void CHeadCrab :: DeathSound ( void ) +{ + EMIT_SOUND_DYN( edict(), CHAN_VOICE, RANDOM_SOUND_ARRAY(pDeathSounds), GetSoundVolue(), ATTN_IDLE, 0, GetVoicePitch() ); +} + +Schedule_t* CHeadCrab :: GetScheduleOfType ( int Type ) +{ + switch ( Type ) + { + case SCHED_RANGE_ATTACK1: + { + return &slHCRangeAttack1[ 0 ]; + } + break; + } + + return CBaseMonster::GetScheduleOfType( Type ); +} + + +class CBabyCrab : public CHeadCrab +{ +public: + void Spawn( void ); + void Precache( void ); + void SetYawSpeed ( void ); + float GetDamageAmount( void ) { return gSkillData.headcrabDmgBite * 0.3; } + BOOL CheckRangeAttack1 ( float flDot, float flDist ); + Schedule_t* GetScheduleOfType ( int Type ); + virtual int GetVoicePitch( void ) { return PITCH_NORM + RANDOM_LONG(40,50); } + virtual float GetSoundVolue( void ) { return 0.8; } +}; +LINK_ENTITY_TO_CLASS( monster_babycrab, CBabyCrab ); + +void CBabyCrab :: Spawn( void ) +{ + CHeadCrab::Spawn(); + SET_MODEL(ENT(pev), "models/baby_headcrab.mdl"); + pev->rendermode = kRenderTransTexture; + pev->renderamt = 192; + UTIL_SetSize(pev, Vector(-12, -12, 0), Vector(12, 12, 24)); + + pev->health = gSkillData.headcrabHealth * 0.25; // less health than full grown +} + +void CBabyCrab :: Precache( void ) +{ + PRECACHE_MODEL( "models/baby_headcrab.mdl" ); + CHeadCrab::Precache(); +} + + +void CBabyCrab :: SetYawSpeed ( void ) +{ + pev->yaw_speed = 120; +} + + +BOOL CBabyCrab :: CheckRangeAttack1( float flDot, float flDist ) +{ + if ( pev->flags & FL_ONGROUND ) + { + if ( pev->groundentity && (pev->groundentity->v.flags & (FL_CLIENT|FL_MONSTER)) ) + return TRUE; + + // A little less accurate, but jump from closer + if ( flDist <= 180 && flDot >= 0.55 ) + return TRUE; + } + + return FALSE; +} + + +Schedule_t* CBabyCrab :: GetScheduleOfType ( int Type ) +{ + switch( Type ) + { + case SCHED_FAIL: // If you fail, try to jump! + if ( m_hEnemy != NULL ) + return slHCRangeAttack1Fast; + break; + + case SCHED_RANGE_ATTACK1: + { + return slHCRangeAttack1Fast; + } + break; + } + + return CHeadCrab::GetScheduleOfType( Type ); +} diff --git a/spirit/healthkit.cpp b/dlls/healthkit.cpp similarity index 81% rename from spirit/healthkit.cpp rename to dlls/healthkit.cpp index 594ead0f..73931f28 100644 --- a/spirit/healthkit.cpp +++ b/dlls/healthkit.cpp @@ -113,12 +113,11 @@ public: virtual int ObjectCaps( void ) { return (CBaseToggle :: ObjectCaps() | FCAP_CONTINUOUS_USE) & ~FCAP_ACROSS_TRANSITION; } virtual int Save( CSave &save ); virtual int Restore( CRestore &restore ); - virtual STATE GetState( void ); static TYPEDESCRIPTION m_SaveData[]; float m_flNextCharge; - int m_iReactivate ; // DeathMatch Delay until reactivated + int m_iReactivate ; // DeathMatch Delay until reactvated int m_iJuice; int m_iOn; // 0 = off, 1 = startup, 2 = going float m_flSoundTime; @@ -164,14 +163,12 @@ void CWallHealth::Spawn() pev->solid = SOLID_BSP; pev->movetype = MOVETYPE_PUSH; - UTIL_SetOrigin(this, pev->origin); // set size and link into world + UTIL_SetOrigin(pev, pev->origin); // set size and link into world UTIL_SetSize(pev, pev->mins, pev->maxs); SET_MODEL(ENT(pev), STRING(pev->model) ); m_iJuice = gSkillData.healthchargerCapacity; pev->frame = 0; - //LRC - if (m_iStyle >= 32) LIGHT_STYLE(m_iStyle, "a"); - else if (m_iStyle <= -32) LIGHT_STYLE(-m_iStyle, "z"); + } void CWallHealth::Precache() @@ -195,16 +192,11 @@ void CWallHealth::Use( CBaseEntity *pActivator, CBaseEntity *pCaller, USE_TYPE u if (m_iJuice <= 0) { pev->frame = 1; - //LRC - if (m_iStyle >= 32) LIGHT_STYLE(m_iStyle, "z"); - else if (m_iStyle <= -32) LIGHT_STYLE(-m_iStyle, "a"); Off(); } - - CBasePlayer *pPlayer = (CBasePlayer *)pActivator; - + // if the player doesn't have the suit, or there is no juice left, make the deny noise - if ((m_iJuice <= 0) || (!(pPlayer->pev->weapons & ITEM_SUIT))) + if ((m_iJuice <= 0) || (!(pActivator->pev->weapons & (1<time) { @@ -214,8 +206,8 @@ void CWallHealth::Use( CBaseEntity *pActivator, CBaseEntity *pCaller, USE_TYPE u return; } - SetNextThink( 0.25 ); - SetThink(&CWallHealth::Off); + pev->nextthink = pev->ltime + 0.25; + SetThink(Off); // Time to recharge yet? @@ -251,10 +243,7 @@ void CWallHealth::Recharge(void) EMIT_SOUND(ENT(pev), CHAN_ITEM, "items/medshot4.wav", 1.0, ATTN_NORM ); m_iJuice = gSkillData.healthchargerCapacity; pev->frame = 0; - //LRC - if (m_iStyle >= 32) LIGHT_STYLE(m_iStyle, "a"); - else if (m_iStyle <= -32) LIGHT_STYLE(-m_iStyle, "z"); - SetThink(&CWallHealth:: SUB_DoNothing ); + SetThink( SUB_DoNothing ); } void CWallHealth::Off(void) @@ -267,19 +256,9 @@ void CWallHealth::Off(void) if ((!m_iJuice) && ( ( m_iReactivate = g_pGameRules->FlHealthChargerRechargeTime() ) > 0) ) { - SetNextThink( m_iReactivate ); - SetThink(&CWallHealth::Recharge); + pev->nextthink = pev->ltime + m_iReactivate; + SetThink(Recharge); } else - SetThink(&CWallHealth:: SUB_DoNothing ); -} - -STATE CWallHealth::GetState( void ) -{ - if (m_iOn == 2) - return STATE_IN_USE; - else if (m_iJuice) - return STATE_ON; - else - return STATE_OFF; -} + SetThink( SUB_DoNothing ); +} \ No newline at end of file diff --git a/spirit/hgrunt.cpp b/dlls/hgrunt.cpp similarity index 91% rename from spirit/hgrunt.cpp rename to dlls/hgrunt.cpp index f8e8f6c0..0d2ca6c9 100644 --- a/spirit/hgrunt.cpp +++ b/dlls/hgrunt.cpp @@ -1,2623 +1,2517 @@ -/*** -* -* Copyright (c) 1996-2002, 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. -* -* This source code contains proprietary and confidential information of -* Valve LLC and its suppliers. Access to this code is restricted to -* persons who have executed a written SDK license with Valve. Any access, -* use or distribution of this code by or to any unlicensed person is illegal. -* -****/ -//========================================================= -// hgrunt -//========================================================= - -//========================================================= -// Hit groups! -//========================================================= -/* - - 1 - Head - 2 - Stomach - 3 - Gun - -*/ - - -#include "extdll.h" -#include "plane.h" -#include "util.h" -#include "cbase.h" -#include "monsters.h" -#include "schedule.h" -#include "animation.h" -#include "squadmonster.h" -#include "weapons.h" -#include "talkmonster.h" -#include "soundent.h" -#include "effects.h" -#include "scripted.h" //LRC - -int g_fGruntQuestion; // true if an idle grunt asked a question. Cleared when someone answers. - -extern DLL_GLOBAL int g_iSkillLevel; - -//========================================================= -// monster-specific DEFINE's -//========================================================= -#define GRUNT_CLIP_SIZE 36 // how many bullets in a clip? - NOTE: 3 round burst sound, so keep as 3 * x! -#define GRUNT_VOL 0.35 // volume of grunt sounds -#define GRUNT_ATTN ATTN_NORM // attenutation of grunt sentences -#define HGRUNT_LIMP_HEALTH 20 -#define HGRUNT_DMG_HEADSHOT ( DMG_BULLET | DMG_CLUB ) // damage types that can kill a grunt with a single headshot. -#define HGRUNT_NUM_HEADS 2 // how many grunt heads are there? -#define HGRUNT_MINIMUM_HEADSHOT_DAMAGE 15 // must do at least this much damage in one shot to head to score a headshot kill -#define HGRUNT_SENTENCE_VOLUME (float)0.35 // volume of grunt sentences - -#define HGRUNT_9MMAR ( 1 << 0) -#define HGRUNT_HANDGRENADE ( 1 << 1) -#define HGRUNT_GRENADELAUNCHER ( 1 << 2) -#define HGRUNT_SHOTGUN ( 1 << 3) - -#define HEAD_GROUP 1 -#define HEAD_GRUNT 0 -#define HEAD_COMMANDER 1 -#define HEAD_SHOTGUN 2 -#define HEAD_M203 3 - -#define GUN_GROUP 2 -#define GUN_MP5 0 -#define GUN_SHOTGUN 1 -#define GUN_NONE 2 - -//========================================================= -// Monster's Anim Events Go Here -//========================================================= -#define HGRUNT_AE_RELOAD ( 2 ) -#define HGRUNT_AE_KICK ( 3 ) -#define HGRUNT_AE_BURST1 ( 4 ) -#define HGRUNT_AE_BURST2 ( 5 ) -#define HGRUNT_AE_BURST3 ( 6 ) -#define HGRUNT_AE_GREN_TOSS ( 7 ) -#define HGRUNT_AE_GREN_LAUNCH ( 8 ) -#define HGRUNT_AE_GREN_DROP ( 9 ) -#define HGRUNT_AE_CAUGHT_ENEMY ( 10) // grunt established sight with an enemy (player only) that had previously eluded the squad. -#define HGRUNT_AE_DROP_GUN ( 11) // grunt (probably dead) is dropping his mp5. - -//========================================================= -// monster-specific schedule types -//========================================================= -enum -{ - SCHED_GRUNT_SUPPRESS = LAST_COMMON_SCHEDULE + 1, - SCHED_GRUNT_ESTABLISH_LINE_OF_FIRE,// move to a location to set up an attack against the enemy. (usually when a friendly is in the way). - SCHED_GRUNT_COVER_AND_RELOAD, - SCHED_GRUNT_SWEEP, - SCHED_GRUNT_FOUND_ENEMY, - SCHED_GRUNT_REPEL, - SCHED_GRUNT_REPEL_ATTACK, - SCHED_GRUNT_REPEL_LAND, - SCHED_GRUNT_WAIT_FACE_ENEMY, - SCHED_GRUNT_TAKECOVER_FAILED,// special schedule type that forces analysis of conditions and picks the best possible schedule to recover from this type of failure. - SCHED_GRUNT_ELOF_FAIL, -}; - -//========================================================= -// monster-specific tasks -//========================================================= -enum -{ - TASK_GRUNT_FACE_TOSS_DIR = LAST_COMMON_TASK + 1, - TASK_GRUNT_SPEAK_SENTENCE, - TASK_GRUNT_CHECK_FIRE, -}; - -//========================================================= -// monster-specific conditions -//========================================================= -#define bits_COND_GRUNT_NOFIRE ( bits_COND_SPECIAL1 ) - -class CHGrunt : public CSquadMonster -{ -public: - void Spawn( void ); - void Precache( void ); - void SetYawSpeed ( void ); - int Classify ( void ); - int ISoundMask ( void ); - void HandleAnimEvent( MonsterEvent_t *pEvent ); - BOOL FCanCheckAttacks ( void ); - BOOL CheckMeleeAttack1 ( float flDot, float flDist ); - BOOL CheckRangeAttack1 ( float flDot, float flDist ); - BOOL CheckRangeAttack2 ( float flDot, float flDist ); - void CheckAmmo ( void ); - void SetActivity ( Activity NewActivity ); - void StartTask ( Task_t *pTask ); - void RunTask ( Task_t *pTask ); - void DeathSound( void ); - void PainSound( void ); - void IdleSound ( void ); - Vector GetGunPosition( void ); - void Shoot ( void ); - void Shotgun ( void ); - void PrescheduleThink ( void ); - void GibMonster( void ); - void SpeakSentence( void ); - - int Save( CSave &save ); - int Restore( CRestore &restore ); - - CBaseEntity *Kick( void ); - Schedule_t *GetSchedule( void ); - Schedule_t *GetScheduleOfType ( int Type ); - void TraceAttack( entvars_t *pevAttacker, float flDamage, Vector vecDir, TraceResult *ptr, int bitsDamageType); - int TakeDamage( entvars_t *pevInflictor, entvars_t *pevAttacker, float flDamage, int bitsDamageType ); - - int IRelationship ( CBaseEntity *pTarget ); - - BOOL FOkToSpeak( void ); - void JustSpoke( void ); - - CUSTOM_SCHEDULES; - static TYPEDESCRIPTION m_SaveData[]; - - // checking the feasibility of a grenade toss is kind of costly, so we do it every couple of seconds, - // not every server frame. - float m_flNextGrenadeCheck; - float m_flNextPainTime; - float m_flLastEnemySightTime; - - Vector m_vecTossVelocity; - - BOOL m_fThrowGrenade; - BOOL m_fStanding; - BOOL m_fFirstEncounter;// only put on the handsign show in the squad's first encounter. - int m_cClipSize; - - int m_voicePitch; - - int m_iBrassShell; - int m_iShotgunShell; - - int m_iSentence; - - static const char *pGruntSentences[]; -}; - -LINK_ENTITY_TO_CLASS( monster_human_grunt, CHGrunt ); - -TYPEDESCRIPTION CHGrunt::m_SaveData[] = -{ - DEFINE_FIELD( CHGrunt, m_flNextGrenadeCheck, FIELD_TIME ), - DEFINE_FIELD( CHGrunt, m_flNextPainTime, FIELD_TIME ), -// DEFINE_FIELD( CHGrunt, m_flLastEnemySightTime, FIELD_TIME ), // don't save, go to zero - DEFINE_FIELD( CHGrunt, m_vecTossVelocity, FIELD_VECTOR ), - DEFINE_FIELD( CHGrunt, m_fThrowGrenade, FIELD_BOOLEAN ), - DEFINE_FIELD( CHGrunt, m_fStanding, FIELD_BOOLEAN ), - DEFINE_FIELD( CHGrunt, m_fFirstEncounter, FIELD_BOOLEAN ), - DEFINE_FIELD( CHGrunt, m_cClipSize, FIELD_INTEGER ), - DEFINE_FIELD( CHGrunt, m_voicePitch, FIELD_INTEGER ), -// DEFINE_FIELD( CShotgun, m_iBrassShell, FIELD_INTEGER ), -// DEFINE_FIELD( CShotgun, m_iShotgunShell, FIELD_INTEGER ), - DEFINE_FIELD( CHGrunt, m_iSentence, FIELD_INTEGER ), -}; - -IMPLEMENT_SAVERESTORE( CHGrunt, CSquadMonster ); - -const char *CHGrunt::pGruntSentences[] = -{ - "HG_GREN", // grenade scared grunt - "HG_ALERT", // sees player - "HG_MONSTER", // sees monster - "HG_COVER", // running to cover - "HG_THROW", // about to throw grenade - "HG_CHARGE", // running out to get the enemy - "HG_TAUNT", // say rude things -}; - -enum -{ - HGRUNT_SENT_NONE = -1, - HGRUNT_SENT_GREN = 0, - HGRUNT_SENT_ALERT, - HGRUNT_SENT_MONSTER, - HGRUNT_SENT_COVER, - HGRUNT_SENT_THROW, - HGRUNT_SENT_CHARGE, - HGRUNT_SENT_TAUNT, -} HGRUNT_SENTENCE_TYPES; - -//========================================================= -// Speak Sentence - say your cued up sentence. -// -// Some grunt sentences (take cover and charge) rely on actually -// being able to execute the intended action. It's really lame -// when a grunt says 'COVER ME' and then doesn't move. The problem -// is that the sentences were played when the decision to TRY -// to move to cover was made. Now the sentence is played after -// we know for sure that there is a valid path. The schedule -// may still fail but in most cases, well after the grunt has -// started moving. -//========================================================= -void CHGrunt :: SpeakSentence( void ) -{ - if ( m_iSentence == HGRUNT_SENT_NONE ) - { - // no sentence cued up. - return; - } - - if (FOkToSpeak()) - { - SENTENCEG_PlayRndSz( ENT(pev), pGruntSentences[ m_iSentence ], HGRUNT_SENTENCE_VOLUME, GRUNT_ATTN, 0, m_voicePitch); - JustSpoke(); - } -} - -//========================================================= -// IRelationship - overridden because Alien Grunts are -// Human Grunt's nemesis. -//========================================================= -int CHGrunt::IRelationship ( CBaseEntity *pTarget ) -{ - //LRC- only hate alien grunts if my behaviour hasn't been overridden - if (!m_iClass && FClassnameIs( pTarget->pev, "monster_alien_grunt" ) || ( FClassnameIs( pTarget->pev, "monster_gargantua" ) ) ) - { - return R_NM; - } - - return CSquadMonster::IRelationship( pTarget ); -} - -//========================================================= -// GibMonster - make gun fly through the air. -//========================================================= -void CHGrunt :: GibMonster ( void ) -{ - Vector vecGunPos; - Vector vecGunAngles; - - if ( GetBodygroup( 2 ) != 2 && !(pev->spawnflags & SF_MONSTER_NO_WPN_DROP)) - {// throw a gun if the grunt has one - GetAttachment( 0, vecGunPos, vecGunAngles ); - - CBaseEntity *pGun; - if (FBitSet( pev->weapons, HGRUNT_SHOTGUN )) - { - pGun = DropItem( "weapon_shotgun", vecGunPos, vecGunAngles ); - } - else - { - pGun = DropItem( "weapon_9mmAR", vecGunPos, vecGunAngles ); - } - if ( pGun ) - { - pGun->pev->velocity = Vector (RANDOM_FLOAT(-100,100), RANDOM_FLOAT(-100,100), RANDOM_FLOAT(200,300)); - pGun->pev->avelocity = Vector ( 0, RANDOM_FLOAT( 200, 400 ), 0 ); - } - - if (FBitSet( pev->weapons, HGRUNT_GRENADELAUNCHER )) - { - pGun = DropItem( "ammo_ARgrenades", vecGunPos, vecGunAngles ); - if ( pGun ) - { - pGun->pev->velocity = Vector (RANDOM_FLOAT(-100,100), RANDOM_FLOAT(-100,100), RANDOM_FLOAT(200,300)); - pGun->pev->avelocity = Vector ( 0, RANDOM_FLOAT( 200, 400 ), 0 ); - } - } - } - - CBaseMonster :: GibMonster(); -} - -//========================================================= -// ISoundMask - Overidden for human grunts because they -// hear the DANGER sound that is made by hand grenades and -// other dangerous items. -//========================================================= -int CHGrunt :: ISoundMask ( void ) -{ - return bits_SOUND_WORLD | - bits_SOUND_COMBAT | - bits_SOUND_PLAYER | - bits_SOUND_DANGER; -} - -//========================================================= -// someone else is talking - don't speak -//========================================================= -BOOL CHGrunt :: FOkToSpeak( void ) -{ -// if someone else is talking, don't speak - if (gpGlobals->time <= CTalkMonster::g_talkWaitTime) - return FALSE; - - if ( pev->spawnflags & SF_MONSTER_GAG ) - { - if ( m_MonsterState != MONSTERSTATE_COMBAT ) - { - // no talking outside of combat if gagged. - return FALSE; - } - } - - // if player is not in pvs, don't speak -// if (FNullEnt(FIND_CLIENT_IN_PVS(edict()))) -// return FALSE; - - return TRUE; -} - -//========================================================= -//========================================================= -void CHGrunt :: JustSpoke( void ) -{ - CTalkMonster::g_talkWaitTime = gpGlobals->time + RANDOM_FLOAT(1.5, 2.0); - m_iSentence = HGRUNT_SENT_NONE; -} - -//========================================================= -// PrescheduleThink - this function runs after conditions -// are collected and before scheduling code is run. -//========================================================= -void CHGrunt :: PrescheduleThink ( void ) -{ - if ( InSquad() && m_hEnemy != NULL ) - { - if ( HasConditions ( bits_COND_SEE_ENEMY ) ) - { - // update the squad's last enemy sighting time. - MySquadLeader()->m_flLastEnemySightTime = gpGlobals->time; - } - else - { - if ( gpGlobals->time - MySquadLeader()->m_flLastEnemySightTime > 5 ) - { - // been a while since we've seen the enemy - MySquadLeader()->m_fEnemyEluded = TRUE; - } - } - } -} - -//========================================================= -// FCanCheckAttacks - this is overridden for human grunts -// because they can throw/shoot grenades when they can't see their -// target and the base class doesn't check attacks if the monster -// cannot see its enemy. -// -// !!!BUGBUG - this gets called before a 3-round burst is fired -// which means that a friendly can still be hit with up to 2 rounds. -// ALSO, grenades will not be tossed if there is a friendly in front, -// this is a bad bug. Friendly machine gun fire avoidance -// will unecessarily prevent the throwing of a grenade as well. -//========================================================= -BOOL CHGrunt :: FCanCheckAttacks ( void ) -{ - if ( !HasConditions( bits_COND_ENEMY_TOOFAR ) ) - { - return TRUE; - } - else - { - return FALSE; - } -} - - -//========================================================= -// CheckMeleeAttack1 -//========================================================= -BOOL CHGrunt :: CheckMeleeAttack1 ( float flDot, float flDist ) -{ - CBaseMonster *pEnemy; - - if ( m_hEnemy != NULL ) - { - pEnemy = m_hEnemy->MyMonsterPointer(); - - if ( !pEnemy ) - { - return FALSE; - } - } - - if ( flDist <= 64 && flDot >= 0.7 && - pEnemy->Classify() != CLASS_ALIEN_BIOWEAPON && - pEnemy->Classify() != CLASS_PLAYER_BIOWEAPON ) - { - return TRUE; - } - return FALSE; -} - -//========================================================= -// CheckRangeAttack1 - overridden for HGrunt, cause -// FCanCheckAttacks() doesn't disqualify all attacks based -// on whether or not the enemy is occluded because unlike -// the base class, the HGrunt can attack when the enemy is -// occluded (throw grenade over wall, etc). We must -// disqualify the machine gun attack if the enemy is occluded. -//========================================================= -BOOL CHGrunt :: CheckRangeAttack1 ( float flDot, float flDist ) -{ - if ( !HasConditions( bits_COND_ENEMY_OCCLUDED ) && flDist <= 2048 && flDot >= 0.5 && NoFriendlyFire() ) - { - TraceResult tr; - - if ( !m_hEnemy->IsPlayer() && flDist <= 64 ) - { - // kick nonclients who are close enough, but don't shoot at them. - return FALSE; - } - - Vector vecSrc = GetGunPosition(); - - // verify that a bullet fired from the gun will hit the enemy before the world. - UTIL_TraceLine( vecSrc, m_hEnemy->BodyTarget(vecSrc), ignore_monsters, ignore_glass, ENT(pev), &tr); - - if ( tr.flFraction == 1.0 ) - { - return TRUE; - } - } - - return FALSE; -} - -//========================================================= -// CheckRangeAttack2 - this checks the Grunt's grenade -// attack. -//========================================================= -BOOL CHGrunt :: CheckRangeAttack2 ( float flDot, float flDist ) -{ - if (! FBitSet(pev->weapons, (HGRUNT_HANDGRENADE | HGRUNT_GRENADELAUNCHER))) - { - return FALSE; - } - - // if the grunt isn't moving, it's ok to check. - if ( m_flGroundSpeed != 0 ) - { - m_fThrowGrenade = FALSE; - return m_fThrowGrenade; - } - - // assume things haven't changed too much since last time - if (gpGlobals->time < m_flNextGrenadeCheck ) - { - return m_fThrowGrenade; - } - - if ( !FBitSet ( m_hEnemy->pev->flags, FL_ONGROUND ) && (m_hEnemy->pev->waterlevel == 0 || m_hEnemy->pev->watertype==CONTENTS_FOG) && m_vecEnemyLKP.z > pev->absmax.z ) - { - //!!!BUGBUG - we should make this check movetype and make sure it isn't FLY? Players who jump a lot are unlikely to - // be grenaded. - // don't throw grenades at anything that isn't on the ground! - m_fThrowGrenade = FALSE; - return m_fThrowGrenade; - } - - Vector vecTarget; - - if (FBitSet( pev->weapons, HGRUNT_HANDGRENADE)) - { - // find feet - if (RANDOM_LONG(0,1)) - { - // magically know where they are - vecTarget = Vector( m_hEnemy->pev->origin.x, m_hEnemy->pev->origin.y, m_hEnemy->pev->absmin.z ); - } - else - { - // toss it to where you last saw them - vecTarget = m_vecEnemyLKP; - } - // vecTarget = m_vecEnemyLKP + (m_hEnemy->BodyTarget( pev->origin ) - m_hEnemy->pev->origin); - // estimate position - // vecTarget = vecTarget + m_hEnemy->pev->velocity * 2; - } - else - { - // find target - // vecTarget = m_hEnemy->BodyTarget( pev->origin ); - vecTarget = m_vecEnemyLKP + (m_hEnemy->BodyTarget( pev->origin ) - m_hEnemy->pev->origin); - // estimate position - if (HasConditions( bits_COND_SEE_ENEMY)) - vecTarget = vecTarget + ((vecTarget - pev->origin).Length() / gSkillData.hgruntGrenadeSpeed) * m_hEnemy->pev->velocity; - } - - // are any of my squad members near the intended grenade impact area? - if ( InSquad() ) - { - if (SquadMemberInRange( vecTarget, 256 )) - { - // crap, I might blow my own guy up. Don't throw a grenade and don't check again for a while. - m_flNextGrenadeCheck = gpGlobals->time + 1; // one full second. - m_fThrowGrenade = FALSE; - return m_fThrowGrenade; //AJH need this or it is overridden later. - } - } - - if ( ( vecTarget - pev->origin ).Length2D() <= 256 ) - { - // crap, I don't want to blow myself up - m_flNextGrenadeCheck = gpGlobals->time + 1; // one full second. - m_fThrowGrenade = FALSE; - return m_fThrowGrenade; - } - - - if (FBitSet( pev->weapons, HGRUNT_HANDGRENADE)) - { - Vector vecToss = VecCheckToss( pev, GetGunPosition(), vecTarget, 0.5 ); - - if ( vecToss != g_vecZero ) - { - m_vecTossVelocity = vecToss; - - // throw a hand grenade - m_fThrowGrenade = TRUE; - // don't check again for a while. - m_flNextGrenadeCheck = gpGlobals->time; // 1/3 second. - } - else - { - // don't throw - m_fThrowGrenade = FALSE; - // don't check again for a while. - m_flNextGrenadeCheck = gpGlobals->time + 1; // one full second. - } - } - else - { - Vector vecToss = VecCheckThrow( pev, GetGunPosition(), vecTarget, gSkillData.hgruntGrenadeSpeed, 0.5 ); - - if ( vecToss != g_vecZero ) - { - m_vecTossVelocity = vecToss; - - // throw a hand grenade - m_fThrowGrenade = TRUE; - // don't check again for a while. - m_flNextGrenadeCheck = gpGlobals->time + 0.3; // 1/3 second. - } - else - { - // don't throw - m_fThrowGrenade = FALSE; - // don't check again for a while. - m_flNextGrenadeCheck = gpGlobals->time + 1; // one full second. - } - } - - - - return m_fThrowGrenade; -} - - -//========================================================= -// TraceAttack - make sure we're not taking it in the helmet -//========================================================= -void CHGrunt :: TraceAttack( entvars_t *pevAttacker, float flDamage, Vector vecDir, TraceResult *ptr, int bitsDamageType) -{ - // check for helmet shot - if (ptr->iHitgroup == 11) - { - // make sure we're wearing one - if (GetBodygroup( 1 ) == HEAD_GRUNT && (bitsDamageType & (DMG_BULLET | DMG_SLASH | DMG_BLAST | DMG_CLUB))) - { - // absorb damage - flDamage -= 20; - if (flDamage <= 0) - { - UTIL_Ricochet( ptr->vecEndPos, 1.0 ); - flDamage = 0.01; - } - } - // it's head shot anyways - ptr->iHitgroup = HITGROUP_HEAD; - } - CSquadMonster::TraceAttack( pevAttacker, flDamage, vecDir, ptr, bitsDamageType ); -} - - -//========================================================= -// TakeDamage - overridden for the grunt because the grunt -// needs to forget that he is in cover if he's hurt. (Obviously -// not in a safe place anymore). -//========================================================= -int CHGrunt :: TakeDamage( entvars_t *pevInflictor, entvars_t *pevAttacker, float flDamage, int bitsDamageType ) -{ - Forget( bits_MEMORY_INCOVER ); - - return CSquadMonster :: TakeDamage ( pevInflictor, pevAttacker, flDamage, bitsDamageType ); -} - -//========================================================= -// SetYawSpeed - allows each sequence to have a different -// turn rate associated with it. -//========================================================= -void CHGrunt :: SetYawSpeed ( void ) -{ - int ys; - - switch ( m_Activity ) - { - case ACT_IDLE: - ys = 150; - break; - case ACT_RUN: - ys = 150; - break; - case ACT_WALK: - ys = 180; - break; - case ACT_RANGE_ATTACK1: - ys = 120; - break; - case ACT_RANGE_ATTACK2: - ys = 120; - break; - case ACT_MELEE_ATTACK1: - ys = 120; - break; - case ACT_MELEE_ATTACK2: - ys = 120; - break; - case ACT_TURN_LEFT: - case ACT_TURN_RIGHT: - ys = 180; - break; - case ACT_GLIDE: - case ACT_FLY: - ys = 30; - break; - default: - ys = 90; - break; - } - - pev->yaw_speed = ys; -} - -void CHGrunt :: IdleSound( void ) -{ - if (FOkToSpeak() && (g_fGruntQuestion || RANDOM_LONG(0,1))) - { - if (!g_fGruntQuestion) - { - // ask question or make statement - switch (RANDOM_LONG(0,2)) - { - case 0: // check in - SENTENCEG_PlayRndSz(ENT(pev), "HG_CHECK", HGRUNT_SENTENCE_VOLUME, ATTN_NORM, 0, m_voicePitch); - g_fGruntQuestion = 1; - break; - case 1: // question - SENTENCEG_PlayRndSz(ENT(pev), "HG_QUEST", HGRUNT_SENTENCE_VOLUME, ATTN_NORM, 0, m_voicePitch); - g_fGruntQuestion = 2; - break; - case 2: // statement - SENTENCEG_PlayRndSz(ENT(pev), "HG_IDLE", HGRUNT_SENTENCE_VOLUME, ATTN_NORM, 0, m_voicePitch); - break; - } - } - else - { - switch (g_fGruntQuestion) - { - case 1: // check in - SENTENCEG_PlayRndSz(ENT(pev), "HG_CLEAR", HGRUNT_SENTENCE_VOLUME, ATTN_NORM, 0, m_voicePitch); - break; - case 2: // question - SENTENCEG_PlayRndSz(ENT(pev), "HG_ANSWER", HGRUNT_SENTENCE_VOLUME, ATTN_NORM, 0, m_voicePitch); - break; - } - g_fGruntQuestion = 0; - } - JustSpoke(); - } -} - -//========================================================= -// CheckAmmo - overridden for the grunt because he actually -// uses ammo! (base class doesn't) -//========================================================= -void CHGrunt :: CheckAmmo ( void ) -{ - if ( m_cAmmoLoaded <= 0 ) - { - SetConditions(bits_COND_NO_AMMO_LOADED); - } -} - -//========================================================= -// Classify - indicates this monster's place in the -// relationship table. -//========================================================= -int CHGrunt :: Classify ( void ) -{ - return m_iClass?m_iClass:CLASS_HUMAN_MILITARY; -} - -//========================================================= -//========================================================= -CBaseEntity *CHGrunt :: Kick( void ) -{ - TraceResult tr; - - UTIL_MakeVectors( pev->angles ); - Vector vecStart = pev->origin; - vecStart.z += pev->size.z * 0.5; - Vector vecEnd = vecStart + (gpGlobals->v_forward * 70); - - UTIL_TraceHull( vecStart, vecEnd, dont_ignore_monsters, head_hull, ENT(pev), &tr ); - - if ( tr.pHit ) - { - CBaseEntity *pEntity = CBaseEntity::Instance( tr.pHit ); - return pEntity; - } - - return NULL; -} - -//========================================================= -// GetGunPosition return the end of the barrel -//========================================================= - -Vector CHGrunt :: GetGunPosition( ) -{ - if (m_fStanding ) - { - return pev->origin + Vector( 0, 0, 60 ); - } - else - { - return pev->origin + Vector( 0, 0, 48 ); - } -} - -//========================================================= -// Shoot -//========================================================= -void CHGrunt :: Shoot ( void ) -{ - if (m_hEnemy == NULL && m_pCine == NULL) //LRC - scripts may fire when you have no enemy - { - return; - } - - Vector vecShootOrigin = GetGunPosition(); - Vector vecShootDir = ShootAtEnemy( vecShootOrigin ); - - if (m_cAmmoLoaded > 0) - { - UTIL_MakeVectors ( pev->angles ); - - Vector vecShellVelocity = gpGlobals->v_right * RANDOM_FLOAT(40,90) + gpGlobals->v_up * RANDOM_FLOAT(75,200) + gpGlobals->v_forward * RANDOM_FLOAT(-40, 40); - EjectBrass ( vecShootOrigin - vecShootDir * 24, vecShellVelocity, pev->angles.y, m_iBrassShell, TE_BOUNCE_SHELL); - FireBullets(1, vecShootOrigin, vecShootDir, VECTOR_CONE_10DEGREES, 2048, BULLET_MONSTER_MP5 ); // shoot +-5 degrees - - pev->effects |= EF_MUZZLEFLASH; - - m_cAmmoLoaded--;// take away a bullet! - } - - Vector angDir = UTIL_VecToAngles( vecShootDir ); - SetBlending( 0, angDir.x ); - - // Teh_Freak: World Lighting! - MESSAGE_BEGIN( MSG_BROADCAST, gmsgTempEntity ); - WRITE_BYTE( TE_DLIGHT ); - WRITE_COORD( vecShootOrigin.x ); // origin - WRITE_COORD( vecShootOrigin.y ); - WRITE_COORD( vecShootOrigin.z ); - WRITE_BYTE( 16 ); // radius - WRITE_BYTE( 255 ); // R - WRITE_BYTE( 255 ); // G - WRITE_BYTE( 128 ); // B - WRITE_BYTE( 0 ); // life * 10 - WRITE_BYTE( 0 ); // decay - MESSAGE_END(); - // Teh_Freak: World Lighting! - -} - -//========================================================= -// Shoot -//========================================================= -void CHGrunt :: Shotgun ( void ) -{ - if (m_hEnemy == NULL && m_pCine == NULL) - { - return; - } - - Vector vecShootOrigin = GetGunPosition(); - Vector vecShootDir = ShootAtEnemy( vecShootOrigin ); - - UTIL_MakeVectors ( pev->angles ); - - Vector vecShellVelocity = gpGlobals->v_right * RANDOM_FLOAT(40,90) + gpGlobals->v_up * RANDOM_FLOAT(75,200) + gpGlobals->v_forward * RANDOM_FLOAT(-40, 40); - EjectBrass ( vecShootOrigin - vecShootDir * 24, vecShellVelocity, pev->angles.y, m_iShotgunShell, TE_BOUNCE_SHOTSHELL); - FireBullets(gSkillData.hgruntShotgunPellets, vecShootOrigin, vecShootDir, VECTOR_CONE_15DEGREES, 2048, BULLET_PLAYER_BUCKSHOT, 0 ); // shoot +-7.5 degrees - - pev->effects |= EF_MUZZLEFLASH; - - m_cAmmoLoaded--;// take away a bullet! - - Vector angDir = UTIL_VecToAngles( vecShootDir ); - SetBlending( 0, angDir.x ); +/*** +* +* Copyright (c) 1996-2002, 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. +* +* This source code contains proprietary and confidential information of +* Valve LLC and its suppliers. Access to this code is restricted to +* persons who have executed a written SDK license with Valve. Any access, +* use or distribution of this code by or to any unlicensed person is illegal. +* +****/ +//========================================================= +// hgrunt +//========================================================= - // Teh_Freak: World Lighting! - MESSAGE_BEGIN( MSG_BROADCAST, gmsgTempEntity ); - WRITE_BYTE( TE_DLIGHT ); - WRITE_COORD( vecShootOrigin.x ); // origin - WRITE_COORD( vecShootOrigin.y ); - WRITE_COORD( vecShootOrigin.z ); - WRITE_BYTE( 16 ); // radius - WRITE_BYTE( 255 ); // R - WRITE_BYTE( 255 ); // G - WRITE_BYTE( 128 ); // B - WRITE_BYTE( 0 ); // life * 10 - WRITE_BYTE( 0 ); // decay - MESSAGE_END(); - // Teh_Freak: World Lighting! - -} - -//========================================================= -// HandleAnimEvent - catches the monster-specific messages -// that occur when tagged animation frames are played. -//========================================================= -void CHGrunt :: HandleAnimEvent( MonsterEvent_t *pEvent ) -{ - Vector vecShootDir; - Vector vecShootOrigin; - - switch( pEvent->event ) - { - case HGRUNT_AE_DROP_GUN: - { - if (pev->spawnflags & SF_MONSTER_NO_WPN_DROP) break; //LRC - - Vector vecGunPos; - Vector vecGunAngles; - - GetAttachment( 0, vecGunPos, vecGunAngles ); - - // switch to body group with no gun. - SetBodygroup( GUN_GROUP, GUN_NONE ); - - // now spawn a gun. - if (FBitSet( pev->weapons, HGRUNT_SHOTGUN )) - { - DropItem( "weapon_shotgun", vecGunPos, vecGunAngles ); - } - else - { - DropItem( "weapon_9mmAR", vecGunPos, vecGunAngles ); - } - if (FBitSet( pev->weapons, HGRUNT_GRENADELAUNCHER )) - { - DropItem( "ammo_ARgrenades", BodyTarget( pev->origin ), vecGunAngles ); - } - - } - break; - - case HGRUNT_AE_RELOAD: - EMIT_SOUND( ENT(pev), CHAN_WEAPON, "hgrunt/gr_reload1.wav", 1, ATTN_NORM ); - m_cAmmoLoaded = m_cClipSize; - ClearConditions(bits_COND_NO_AMMO_LOADED); - break; - - case HGRUNT_AE_GREN_TOSS: - { - UTIL_MakeVectors( pev->angles ); - // CGrenade::ShootTimed( pev, pev->origin + gpGlobals->v_forward * 34 + Vector (0, 0, 32), m_vecTossVelocity, 3.5 ); - //LRC - a bit of a hack. Ideally the grunts would work out in advance whether it's ok to throw. - if (m_pCine) - { - Vector vecToss = g_vecZero; - if (m_hTargetEnt != NULL && m_pCine->PreciseAttack()) - { - vecToss = VecCheckToss( pev, GetGunPosition(), m_hTargetEnt->pev->origin, 0.5 ); - } - if (vecToss == g_vecZero) - { - vecToss = (gpGlobals->v_forward*0.5+gpGlobals->v_up*0.5).Normalize()*gSkillData.hgruntGrenadeSpeed; - } - CGrenade::ShootTimed( pev, GetGunPosition(), vecToss, 3.5 ); - } - else - CGrenade::ShootTimed( pev, GetGunPosition(), m_vecTossVelocity, 3.5 ); - - m_fThrowGrenade = FALSE; - m_flNextGrenadeCheck = gpGlobals->time + 6;// wait six seconds before even looking again to see if a grenade can be thrown. - // !!!LATER - when in a group, only try to throw grenade if ordered. - } - break; - - case HGRUNT_AE_GREN_LAUNCH: - { - EMIT_SOUND(ENT(pev), CHAN_WEAPON, "weapons/glauncher.wav", 0.8, ATTN_NORM); - //LRC: firing due to a script? - if (m_pCine) - { - Vector vecToss; - if (m_hTargetEnt != NULL && m_pCine->PreciseAttack()) - vecToss = VecCheckThrow( pev, GetGunPosition(), m_hTargetEnt->pev->origin, gSkillData.hgruntGrenadeSpeed, 0.5 ); - else - { - // just shoot diagonally up+forwards - UTIL_MakeVectors(pev->angles); - vecToss = (gpGlobals->v_forward*0.5 + gpGlobals->v_up*0.5).Normalize() * gSkillData.hgruntGrenadeSpeed; - } - CGrenade::ShootContact( pev, GetGunPosition(), vecToss ); - } - else - CGrenade::ShootContact( pev, GetGunPosition(), m_vecTossVelocity ); - m_fThrowGrenade = FALSE; - if (g_iSkillLevel == SKILL_HARD) - m_flNextGrenadeCheck = gpGlobals->time + RANDOM_FLOAT( 2, 5 );// wait a random amount of time before shooting again - else - m_flNextGrenadeCheck = gpGlobals->time + 6;// wait six seconds before even looking again to see if a grenade can be thrown. - } - break; - - case HGRUNT_AE_GREN_DROP: - { - UTIL_MakeVectors( pev->angles ); - CGrenade::ShootTimed( pev, pev->origin + gpGlobals->v_forward * 17 - gpGlobals->v_right * 27 + gpGlobals->v_up * 6, g_vecZero, 3 ); - } - break; - - case HGRUNT_AE_BURST1: - { - if ( FBitSet( pev->weapons, HGRUNT_9MMAR )) - { - // the first round of the three round burst plays the sound and puts a sound in the world sound list. - if (m_cAmmoLoaded > 0) - { - if ( RANDOM_LONG(0,1) ) - { - EMIT_SOUND( ENT(pev), CHAN_WEAPON, "hgrunt/gr_mgun1.wav", 1, ATTN_NORM ); - } - else - { - EMIT_SOUND( ENT(pev), CHAN_WEAPON, "hgrunt/gr_mgun2.wav", 1, ATTN_NORM ); - } - } - else - { - EMIT_SOUND( ENT(pev), CHAN_WEAPON, "weapons/dryfire1.wav", 1, ATTN_NORM ); - } - - Shoot(); - } - else - { - Shotgun( ); - - EMIT_SOUND(ENT(pev), CHAN_WEAPON, "weapons/sbarrel1.wav", 1, ATTN_NORM ); - } - - CSoundEnt::InsertSound ( bits_SOUND_COMBAT, pev->origin, 384, 0.3 ); - } - break; - - case HGRUNT_AE_BURST2: - case HGRUNT_AE_BURST3: - Shoot(); - break; - - case HGRUNT_AE_KICK: - { - CBaseEntity *pHurt = Kick(); - - if ( pHurt ) - { - // SOUND HERE! - UTIL_MakeVectors( pev->angles ); - pHurt->pev->punchangle.x = 15; - pHurt->pev->velocity = pHurt->pev->velocity + gpGlobals->v_forward * 100 + gpGlobals->v_up * 50; - pHurt->TakeDamage( pev, pev, gSkillData.hgruntDmgKick, DMG_CLUB ); - } - } - break; - - case HGRUNT_AE_CAUGHT_ENEMY: - { - if ( FOkToSpeak() ) - { - SENTENCEG_PlayRndSz(ENT(pev), "HG_ALERT", HGRUNT_SENTENCE_VOLUME, GRUNT_ATTN, 0, m_voicePitch); - JustSpoke(); - } - - } - - default: - CSquadMonster::HandleAnimEvent( pEvent ); - break; - } -} - -//========================================================= -// Spawn -//========================================================= -void CHGrunt :: Spawn() -{ - Precache( ); - - if (pev->model) - SET_MODEL(ENT(pev), STRING(pev->model)); //LRC - else - SET_MODEL(ENT(pev), "models/hgrunt.mdl"); - UTIL_SetSize(pev, VEC_HUMAN_HULL_MIN, VEC_HUMAN_HULL_MAX); - - pev->solid = SOLID_SLIDEBOX; - pev->movetype = MOVETYPE_STEP; - m_bloodColor = BLOOD_COLOR_RED; - pev->effects = 0; - if (pev->health == 0) - pev->health = gSkillData.hgruntHealth; - m_flFieldOfView = 0.2;// indicates the width of this monster's forward view cone ( as a dotproduct result ) - m_MonsterState = MONSTERSTATE_NONE; - m_flNextGrenadeCheck = gpGlobals->time + 1; - m_flNextPainTime = gpGlobals->time; - m_iSentence = HGRUNT_SENT_NONE; - - m_afCapability = bits_CAP_SQUAD | bits_CAP_TURN_HEAD | bits_CAP_DOORS_GROUP; - - m_fEnemyEluded = FALSE; - m_fFirstEncounter = TRUE;// this is true when the grunt spawns, because he hasn't encountered an enemy yet. - - m_HackedGunPos = Vector ( 0, 0, 55 ); - - if (pev->weapons == 0) - { - // initialize to original values - pev->weapons = HGRUNT_9MMAR | HGRUNT_HANDGRENADE; - // pev->weapons = HGRUNT_SHOTGUN; - // pev->weapons = HGRUNT_9MMAR | HGRUNT_GRENADELAUNCHER; - } - - if (FBitSet( pev->weapons, HGRUNT_SHOTGUN )) - { - SetBodygroup( GUN_GROUP, GUN_SHOTGUN ); - m_cClipSize = 8; - } - else - { - m_cClipSize = GRUNT_CLIP_SIZE; - } - m_cAmmoLoaded = m_cClipSize; - - if (RANDOM_LONG( 0, 99 ) < 80) - pev->skin = 0; // light skin - else - pev->skin = 1; // dark skin - - if (FBitSet( pev->weapons, HGRUNT_SHOTGUN )) - { - SetBodygroup( HEAD_GROUP, HEAD_SHOTGUN); - } - else if (FBitSet( pev->weapons, HGRUNT_GRENADELAUNCHER )) - { - SetBodygroup( HEAD_GROUP, HEAD_M203 ); - pev->skin = 1; // alway dark skin - } - - CTalkMonster::g_talkWaitTime = 0; - - MonsterInit(); -} - -//========================================================= -// Precache - precaches all resources this monster needs -//========================================================= -void CHGrunt :: Precache() -{ - if (pev->model) - PRECACHE_MODEL((char*)STRING(pev->model)); //LRC - else - PRECACHE_MODEL("models/hgrunt.mdl"); - - PRECACHE_SOUND( "weapons/dryfire1.wav" ); //LRC - - PRECACHE_SOUND( "hgrunt/gr_mgun1.wav" ); - PRECACHE_SOUND( "hgrunt/gr_mgun2.wav" ); - - PRECACHE_SOUND( "hgrunt/gr_die1.wav" ); - PRECACHE_SOUND( "hgrunt/gr_die2.wav" ); - PRECACHE_SOUND( "hgrunt/gr_die3.wav" ); - - PRECACHE_SOUND( "hgrunt/gr_pain1.wav" ); - PRECACHE_SOUND( "hgrunt/gr_pain2.wav" ); - PRECACHE_SOUND( "hgrunt/gr_pain3.wav" ); - PRECACHE_SOUND( "hgrunt/gr_pain4.wav" ); - PRECACHE_SOUND( "hgrunt/gr_pain5.wav" ); - - PRECACHE_SOUND( "hgrunt/gr_reload1.wav" ); - - PRECACHE_SOUND( "weapons/glauncher.wav" ); - - PRECACHE_SOUND( "weapons/sbarrel1.wav" ); - - PRECACHE_SOUND("zombie/claw_miss2.wav");// because we use the basemonster SWIPE animation event - - // get voice pitch - if (RANDOM_LONG(0,1)) - m_voicePitch = 109 + RANDOM_LONG(0,7); - else - m_voicePitch = 100; - - m_iBrassShell = PRECACHE_MODEL ("models/shell.mdl");// brass shell - m_iShotgunShell = PRECACHE_MODEL ("models/shotgunshell.mdl"); -} - -//========================================================= -// start task -//========================================================= -void CHGrunt :: StartTask ( Task_t *pTask ) -{ - m_iTaskStatus = TASKSTATUS_RUNNING; - - switch ( pTask->iTask ) - { - case TASK_GRUNT_CHECK_FIRE: - if ( !NoFriendlyFire() ) - { - SetConditions( bits_COND_GRUNT_NOFIRE ); - } - TaskComplete(); - break; - - case TASK_GRUNT_SPEAK_SENTENCE: - SpeakSentence(); - TaskComplete(); - break; - - case TASK_WALK_PATH: - case TASK_RUN_PATH: - // grunt no longer assumes he is covered if he moves - Forget( bits_MEMORY_INCOVER ); - CSquadMonster ::StartTask( pTask ); - break; - - case TASK_RELOAD: - m_IdealActivity = ACT_RELOAD; - break; - - case TASK_GRUNT_FACE_TOSS_DIR: - break; - - case TASK_FACE_IDEAL: - case TASK_FACE_ENEMY: - CSquadMonster :: StartTask( pTask ); - if (pev->movetype == MOVETYPE_FLY) - { - m_IdealActivity = ACT_GLIDE; - } - break; - - default: - CSquadMonster :: StartTask( pTask ); - break; - } -} - -//========================================================= -// RunTask -//========================================================= -void CHGrunt :: RunTask ( Task_t *pTask ) -{ - switch ( pTask->iTask ) - { - case TASK_GRUNT_FACE_TOSS_DIR: - { - // project a point along the toss vector and turn to face that point. - MakeIdealYaw( pev->origin + m_vecTossVelocity * 64 ); - ChangeYaw( pev->yaw_speed ); - - if ( FacingIdeal() ) - { - m_iTaskStatus = TASKSTATUS_COMPLETE; - } - break; - } - default: - { - CSquadMonster :: RunTask( pTask ); - break; - } - } -} - -//========================================================= -// PainSound -//========================================================= -void CHGrunt :: PainSound ( void ) -{ - if ( gpGlobals->time > m_flNextPainTime ) - { -#if 0 - if ( RANDOM_LONG(0,99) < 5 ) - { - // pain sentences are rare - if (FOkToSpeak()) - { - SENTENCEG_PlayRndSz(ENT(pev), "HG_PAIN", HGRUNT_SENTENCE_VOLUME, ATTN_NORM, 0, PITCH_NORM); - JustSpoke(); - return; - } - } -#endif - switch ( RANDOM_LONG(0,6) ) - { - case 0: - EMIT_SOUND( ENT(pev), CHAN_VOICE, "hgrunt/gr_pain3.wav", 1, ATTN_NORM ); - break; - case 1: - EMIT_SOUND( ENT(pev), CHAN_VOICE, "hgrunt/gr_pain4.wav", 1, ATTN_NORM ); - break; - case 2: - EMIT_SOUND( ENT(pev), CHAN_VOICE, "hgrunt/gr_pain5.wav", 1, ATTN_NORM ); - break; - case 3: - EMIT_SOUND( ENT(pev), CHAN_VOICE, "hgrunt/gr_pain1.wav", 1, ATTN_NORM ); - break; - case 4: - EMIT_SOUND( ENT(pev), CHAN_VOICE, "hgrunt/gr_pain2.wav", 1, ATTN_NORM ); - break; - } - - m_flNextPainTime = gpGlobals->time + 1; - } -} - -//========================================================= -// DeathSound -//========================================================= -void CHGrunt :: DeathSound ( void ) -{ - switch ( RANDOM_LONG(0,2) ) - { - case 0: - EMIT_SOUND( ENT(pev), CHAN_VOICE, "hgrunt/gr_die1.wav", 1, ATTN_IDLE ); - break; - case 1: - EMIT_SOUND( ENT(pev), CHAN_VOICE, "hgrunt/gr_die2.wav", 1, ATTN_IDLE ); - break; - case 2: - EMIT_SOUND( ENT(pev), CHAN_VOICE, "hgrunt/gr_die3.wav", 1, ATTN_IDLE ); - break; - } -} - -//========================================================= -// AI Schedules Specific to this monster -//========================================================= - -//========================================================= -// GruntFail -//========================================================= -Task_t tlGruntFail[] = -{ - { TASK_STOP_MOVING, 0 }, - { TASK_SET_ACTIVITY, (float)ACT_IDLE }, - { TASK_WAIT, (float)2 }, - { TASK_WAIT_PVS, (float)0 }, -}; - -Schedule_t slGruntFail[] = -{ - { - tlGruntFail, - ARRAYSIZE ( tlGruntFail ), - bits_COND_CAN_RANGE_ATTACK1 | - bits_COND_CAN_RANGE_ATTACK2 | - bits_COND_CAN_MELEE_ATTACK1 | - bits_COND_CAN_MELEE_ATTACK2, - 0, - "Grunt Fail" - }, -}; - -//========================================================= -// Grunt Combat Fail -//========================================================= -Task_t tlGruntCombatFail[] = -{ - { TASK_STOP_MOVING, 0 }, - { TASK_SET_ACTIVITY, (float)ACT_IDLE }, - { TASK_WAIT_FACE_ENEMY, (float)2 }, - { TASK_WAIT_PVS, (float)0 }, -}; - -Schedule_t slGruntCombatFail[] = -{ - { - tlGruntCombatFail, - ARRAYSIZE ( tlGruntCombatFail ), - bits_COND_CAN_RANGE_ATTACK1 | - bits_COND_CAN_RANGE_ATTACK2, - 0, - "Grunt Combat Fail" - }, -}; - -//========================================================= -// Victory dance! -//========================================================= -Task_t tlGruntVictoryDance[] = -{ - { TASK_STOP_MOVING, (float)0 }, - { TASK_FACE_ENEMY, (float)0 }, - { TASK_WAIT, (float)1.5 }, - { TASK_GET_PATH_TO_ENEMY_CORPSE, (float)0 }, - { TASK_WALK_PATH, (float)0 }, - { TASK_WAIT_FOR_MOVEMENT, (float)0 }, - { TASK_FACE_ENEMY, (float)0 }, - { TASK_PLAY_SEQUENCE, (float)ACT_VICTORY_DANCE }, -}; - -Schedule_t slGruntVictoryDance[] = -{ - { - tlGruntVictoryDance, - ARRAYSIZE ( tlGruntVictoryDance ), - bits_COND_NEW_ENEMY | - bits_COND_LIGHT_DAMAGE | - bits_COND_HEAVY_DAMAGE, - 0, - "GruntVictoryDance" - }, -}; - -//========================================================= -// Establish line of fire - move to a position that allows -// the grunt to attack. -//========================================================= -Task_t tlGruntEstablishLineOfFire[] = -{ - { TASK_SET_FAIL_SCHEDULE, (float)SCHED_GRUNT_ELOF_FAIL }, - { TASK_GET_PATH_TO_ENEMY, (float)0 }, - { TASK_GRUNT_SPEAK_SENTENCE,(float)0 }, - { TASK_RUN_PATH, (float)0 }, - { TASK_WAIT_FOR_MOVEMENT, (float)0 }, -}; - -Schedule_t slGruntEstablishLineOfFire[] = -{ - { - tlGruntEstablishLineOfFire, - ARRAYSIZE ( tlGruntEstablishLineOfFire ), - bits_COND_NEW_ENEMY | - bits_COND_ENEMY_DEAD | - bits_COND_CAN_RANGE_ATTACK1 | - bits_COND_CAN_MELEE_ATTACK1 | - bits_COND_CAN_RANGE_ATTACK2 | - bits_COND_CAN_MELEE_ATTACK2 | - bits_COND_HEAR_SOUND, - - bits_SOUND_DANGER, - "GruntEstablishLineOfFire" - }, -}; - -//========================================================= -// GruntFoundEnemy - grunt established sight with an enemy -// that was hiding from the squad. -//========================================================= -Task_t tlGruntFoundEnemy[] = -{ - { TASK_STOP_MOVING, 0 }, - { TASK_FACE_ENEMY, (float)0 }, - { TASK_PLAY_SEQUENCE_FACE_ENEMY,(float)ACT_SIGNAL1 }, -}; - -Schedule_t slGruntFoundEnemy[] = -{ - { - tlGruntFoundEnemy, - ARRAYSIZE ( tlGruntFoundEnemy ), - bits_COND_HEAR_SOUND, - - bits_SOUND_DANGER, - "GruntFoundEnemy" - }, -}; - -//========================================================= -// GruntCombatFace Schedule -//========================================================= -Task_t tlGruntCombatFace1[] = -{ - { TASK_STOP_MOVING, 0 }, - { TASK_SET_ACTIVITY, (float)ACT_IDLE }, - { TASK_FACE_ENEMY, (float)0 }, - { TASK_WAIT, (float)1.5 }, - { TASK_SET_SCHEDULE, (float)SCHED_GRUNT_SWEEP }, -}; - -Schedule_t slGruntCombatFace[] = -{ - { - tlGruntCombatFace1, - ARRAYSIZE ( tlGruntCombatFace1 ), - bits_COND_NEW_ENEMY | - bits_COND_ENEMY_DEAD | - bits_COND_CAN_RANGE_ATTACK1 | - bits_COND_CAN_RANGE_ATTACK2, - 0, - "Combat Face" - }, -}; - -//========================================================= -// Suppressing fire - don't stop shooting until the clip is -// empty or grunt gets hurt. -//========================================================= -Task_t tlGruntSignalSuppress[] = -{ - { TASK_STOP_MOVING, 0 }, - { TASK_FACE_IDEAL, (float)0 }, - { TASK_PLAY_SEQUENCE_FACE_ENEMY, (float)ACT_SIGNAL2 }, - { TASK_FACE_ENEMY, (float)0 }, - { TASK_GRUNT_CHECK_FIRE, (float)0 }, - { TASK_RANGE_ATTACK1, (float)0 }, - { TASK_FACE_ENEMY, (float)0 }, - { TASK_GRUNT_CHECK_FIRE, (float)0 }, - { TASK_RANGE_ATTACK1, (float)0 }, - { TASK_FACE_ENEMY, (float)0 }, - { TASK_GRUNT_CHECK_FIRE, (float)0 }, - { TASK_RANGE_ATTACK1, (float)0 }, - { TASK_FACE_ENEMY, (float)0 }, - { TASK_GRUNT_CHECK_FIRE, (float)0 }, - { TASK_RANGE_ATTACK1, (float)0 }, - { TASK_FACE_ENEMY, (float)0 }, - { TASK_GRUNT_CHECK_FIRE, (float)0 }, - { TASK_RANGE_ATTACK1, (float)0 }, -}; - -Schedule_t slGruntSignalSuppress[] = -{ - { - tlGruntSignalSuppress, - ARRAYSIZE ( tlGruntSignalSuppress ), - bits_COND_ENEMY_DEAD | - bits_COND_LIGHT_DAMAGE | - bits_COND_HEAVY_DAMAGE | - bits_COND_HEAR_SOUND | - bits_COND_GRUNT_NOFIRE | - bits_COND_NO_AMMO_LOADED, - - bits_SOUND_DANGER, - "SignalSuppress" - }, -}; - -Task_t tlGruntSuppress[] = -{ - { TASK_STOP_MOVING, 0 }, - { TASK_FACE_ENEMY, (float)0 }, - { TASK_GRUNT_CHECK_FIRE, (float)0 }, - { TASK_RANGE_ATTACK1, (float)0 }, - { TASK_FACE_ENEMY, (float)0 }, - { TASK_GRUNT_CHECK_FIRE, (float)0 }, - { TASK_RANGE_ATTACK1, (float)0 }, - { TASK_FACE_ENEMY, (float)0 }, - { TASK_GRUNT_CHECK_FIRE, (float)0 }, - { TASK_RANGE_ATTACK1, (float)0 }, - { TASK_FACE_ENEMY, (float)0 }, - { TASK_GRUNT_CHECK_FIRE, (float)0 }, - { TASK_RANGE_ATTACK1, (float)0 }, - { TASK_FACE_ENEMY, (float)0 }, - { TASK_GRUNT_CHECK_FIRE, (float)0 }, - { TASK_RANGE_ATTACK1, (float)0 }, -}; - -Schedule_t slGruntSuppress[] = -{ - { - tlGruntSuppress, - ARRAYSIZE ( tlGruntSuppress ), - bits_COND_ENEMY_DEAD | - bits_COND_LIGHT_DAMAGE | - bits_COND_HEAVY_DAMAGE | - bits_COND_HEAR_SOUND | - bits_COND_GRUNT_NOFIRE | - bits_COND_NO_AMMO_LOADED, - - bits_SOUND_DANGER, - "Suppress" - }, -}; - - -//========================================================= -// grunt wait in cover - we don't allow danger or the ability -// to attack to break a grunt's run to cover schedule, but -// when a grunt is in cover, we do want them to attack if they can. -//========================================================= -Task_t tlGruntWaitInCover[] = -{ - { TASK_STOP_MOVING, (float)0 }, - { TASK_SET_ACTIVITY, (float)ACT_IDLE }, - { TASK_WAIT_FACE_ENEMY, (float)1 }, -}; - -Schedule_t slGruntWaitInCover[] = -{ - { - tlGruntWaitInCover, - ARRAYSIZE ( tlGruntWaitInCover ), - bits_COND_NEW_ENEMY | - bits_COND_HEAR_SOUND | - bits_COND_CAN_RANGE_ATTACK1 | - bits_COND_CAN_RANGE_ATTACK2 | - bits_COND_CAN_MELEE_ATTACK1 | - bits_COND_CAN_MELEE_ATTACK2, - - bits_SOUND_DANGER, - "GruntWaitInCover" - }, -}; - -//========================================================= -// run to cover. -// !!!BUGBUG - set a decent fail schedule here. -//========================================================= -Task_t tlGruntTakeCover1[] = -{ - { TASK_STOP_MOVING, (float)0 }, - { TASK_SET_FAIL_SCHEDULE, (float)SCHED_GRUNT_TAKECOVER_FAILED }, - { TASK_WAIT, (float)0.2 }, - { TASK_FIND_COVER_FROM_ENEMY, (float)0 }, - { TASK_GRUNT_SPEAK_SENTENCE, (float)0 }, - { TASK_RUN_PATH, (float)0 }, - { TASK_WAIT_FOR_MOVEMENT, (float)0 }, - { TASK_REMEMBER, (float)bits_MEMORY_INCOVER }, - { TASK_SET_SCHEDULE, (float)SCHED_GRUNT_WAIT_FACE_ENEMY }, -}; - -Schedule_t slGruntTakeCover[] = -{ - { - tlGruntTakeCover1, - ARRAYSIZE ( tlGruntTakeCover1 ), - 0, - 0, - "TakeCover" - }, -}; - -//========================================================= -// drop grenade then run to cover. -//========================================================= -Task_t tlGruntGrenadeCover1[] = -{ - { TASK_STOP_MOVING, (float)0 }, - { TASK_FIND_COVER_FROM_ENEMY, (float)99 }, - { TASK_FIND_FAR_NODE_COVER_FROM_ENEMY, (float)384 }, - { TASK_PLAY_SEQUENCE, (float)ACT_SPECIAL_ATTACK1 }, - { TASK_CLEAR_MOVE_WAIT, (float)0 }, - { TASK_RUN_PATH, (float)0 }, - { TASK_WAIT_FOR_MOVEMENT, (float)0 }, - { TASK_SET_SCHEDULE, (float)SCHED_GRUNT_WAIT_FACE_ENEMY }, -}; - -Schedule_t slGruntGrenadeCover[] = -{ - { - tlGruntGrenadeCover1, - ARRAYSIZE ( tlGruntGrenadeCover1 ), - 0, - 0, - "GrenadeCover" - }, -}; - - -//========================================================= -// drop grenade then run to cover. -//========================================================= -Task_t tlGruntTossGrenadeCover1[] = -{ - { TASK_FACE_ENEMY, (float)0 }, - { TASK_RANGE_ATTACK2, (float)0 }, - { TASK_SET_SCHEDULE, (float)SCHED_TAKE_COVER_FROM_ENEMY }, -}; - -Schedule_t slGruntTossGrenadeCover[] = -{ - { - tlGruntTossGrenadeCover1, - ARRAYSIZE ( tlGruntTossGrenadeCover1 ), - 0, - 0, - "TossGrenadeCover" - }, -}; - -//========================================================= -// hide from the loudest sound source (to run from grenade) -//========================================================= -Task_t tlGruntTakeCoverFromBestSound[] = -{ - { TASK_SET_FAIL_SCHEDULE, (float)SCHED_COWER },// duck and cover if cannot move from explosion - { TASK_STOP_MOVING, (float)0 }, - { TASK_FIND_COVER_FROM_BEST_SOUND, (float)0 }, - { TASK_RUN_PATH, (float)0 }, - { TASK_WAIT_FOR_MOVEMENT, (float)0 }, - { TASK_REMEMBER, (float)bits_MEMORY_INCOVER }, - { TASK_TURN_LEFT, (float)179 }, -}; - -Schedule_t slGruntTakeCoverFromBestSound[] = -{ - { - tlGruntTakeCoverFromBestSound, - ARRAYSIZE ( tlGruntTakeCoverFromBestSound ), - 0, - 0, - "GruntTakeCoverFromBestSound" - }, -}; - -//========================================================= -// Grunt reload schedule -//========================================================= -Task_t tlGruntHideReload[] = -{ - { TASK_STOP_MOVING, (float)0 }, - { TASK_SET_FAIL_SCHEDULE, (float)SCHED_RELOAD }, - { TASK_FIND_COVER_FROM_ENEMY, (float)0 }, - { TASK_RUN_PATH, (float)0 }, - { TASK_WAIT_FOR_MOVEMENT, (float)0 }, - { TASK_REMEMBER, (float)bits_MEMORY_INCOVER }, - { TASK_FACE_ENEMY, (float)0 }, - { TASK_PLAY_SEQUENCE, (float)ACT_RELOAD }, -}; - -Schedule_t slGruntHideReload[] = -{ - { - tlGruntHideReload, - ARRAYSIZE ( tlGruntHideReload ), - bits_COND_HEAVY_DAMAGE | - bits_COND_HEAR_SOUND, - - bits_SOUND_DANGER, - "GruntHideReload" - } -}; - -//========================================================= -// Do a turning sweep of the area -//========================================================= -Task_t tlGruntSweep[] = -{ - { TASK_TURN_LEFT, (float)179 }, - { TASK_WAIT, (float)1 }, - { TASK_TURN_LEFT, (float)179 }, - { TASK_WAIT, (float)1 }, -}; - -Schedule_t slGruntSweep[] = -{ - { - tlGruntSweep, - ARRAYSIZE ( tlGruntSweep ), - - bits_COND_NEW_ENEMY | - bits_COND_LIGHT_DAMAGE | - bits_COND_HEAVY_DAMAGE | - bits_COND_CAN_RANGE_ATTACK1 | - bits_COND_CAN_RANGE_ATTACK2 | - bits_COND_HEAR_SOUND, - - bits_SOUND_WORLD |// sound flags - bits_SOUND_DANGER | - bits_SOUND_PLAYER, - - "Grunt Sweep" - }, -}; - -//========================================================= -// primary range attack. Overriden because base class stops attacking when the enemy is occluded. -// grunt's grenade toss requires the enemy be occluded. -//========================================================= -Task_t tlGruntRangeAttack1A[] = -{ - { TASK_STOP_MOVING, (float)0 }, - { TASK_PLAY_SEQUENCE_FACE_ENEMY, (float)ACT_CROUCH }, - { TASK_GRUNT_CHECK_FIRE, (float)0 }, - { TASK_RANGE_ATTACK1, (float)0 }, - { TASK_FACE_ENEMY, (float)0 }, - { TASK_GRUNT_CHECK_FIRE, (float)0 }, - { TASK_RANGE_ATTACK1, (float)0 }, - { TASK_FACE_ENEMY, (float)0 }, - { TASK_GRUNT_CHECK_FIRE, (float)0 }, - { TASK_RANGE_ATTACK1, (float)0 }, - { TASK_FACE_ENEMY, (float)0 }, - { TASK_GRUNT_CHECK_FIRE, (float)0 }, - { TASK_RANGE_ATTACK1, (float)0 }, -}; - -Schedule_t slGruntRangeAttack1A[] = -{ - { - tlGruntRangeAttack1A, - ARRAYSIZE ( tlGruntRangeAttack1A ), - bits_COND_NEW_ENEMY | - bits_COND_ENEMY_DEAD | - bits_COND_HEAVY_DAMAGE | - bits_COND_ENEMY_OCCLUDED | - bits_COND_HEAR_SOUND | - bits_COND_GRUNT_NOFIRE | - bits_COND_NO_AMMO_LOADED, - - bits_SOUND_DANGER, - "Range Attack1A" - }, -}; - - -//========================================================= -// primary range attack. Overriden because base class stops attacking when the enemy is occluded. -// grunt's grenade toss requires the enemy be occluded. -//========================================================= -Task_t tlGruntRangeAttack1B[] = -{ - { TASK_STOP_MOVING, (float)0 }, - { TASK_PLAY_SEQUENCE_FACE_ENEMY,(float)ACT_IDLE_ANGRY }, - { TASK_GRUNT_CHECK_FIRE, (float)0 }, - { TASK_RANGE_ATTACK1, (float)0 }, - { TASK_FACE_ENEMY, (float)0 }, - { TASK_GRUNT_CHECK_FIRE, (float)0 }, - { TASK_RANGE_ATTACK1, (float)0 }, - { TASK_FACE_ENEMY, (float)0 }, - { TASK_GRUNT_CHECK_FIRE, (float)0 }, - { TASK_RANGE_ATTACK1, (float)0 }, - { TASK_FACE_ENEMY, (float)0 }, - { TASK_GRUNT_CHECK_FIRE, (float)0 }, - { TASK_RANGE_ATTACK1, (float)0 }, -}; - -Schedule_t slGruntRangeAttack1B[] = -{ - { - tlGruntRangeAttack1B, - ARRAYSIZE ( tlGruntRangeAttack1B ), - bits_COND_NEW_ENEMY | - bits_COND_ENEMY_DEAD | - bits_COND_HEAVY_DAMAGE | - bits_COND_ENEMY_OCCLUDED | - bits_COND_NO_AMMO_LOADED | - bits_COND_GRUNT_NOFIRE | - bits_COND_HEAR_SOUND, - - bits_SOUND_DANGER, - "Range Attack1B" - }, -}; - -//========================================================= -// secondary range attack. Overriden because base class stops attacking when the enemy is occluded. -// grunt's grenade toss requires the enemy be occluded. -//========================================================= -Task_t tlGruntRangeAttack2[] = -{ - { TASK_STOP_MOVING, (float)0 }, - { TASK_GRUNT_FACE_TOSS_DIR, (float)0 }, - { TASK_PLAY_SEQUENCE, (float)ACT_RANGE_ATTACK2 }, - { TASK_SET_SCHEDULE, (float)SCHED_GRUNT_WAIT_FACE_ENEMY },// don't run immediately after throwing grenade. -}; - -Schedule_t slGruntRangeAttack2[] = -{ - { - tlGruntRangeAttack2, - ARRAYSIZE ( tlGruntRangeAttack2 ), - 0, - 0, - "RangeAttack2" - }, -}; - - -//========================================================= -// repel -//========================================================= -Task_t tlGruntRepel[] = -{ - { TASK_STOP_MOVING, (float)0 }, - { TASK_FACE_IDEAL, (float)0 }, - { TASK_PLAY_SEQUENCE, (float)ACT_GLIDE }, -}; - -Schedule_t slGruntRepel[] = -{ - { - tlGruntRepel, - ARRAYSIZE ( tlGruntRepel ), - bits_COND_SEE_ENEMY | - bits_COND_NEW_ENEMY | - bits_COND_LIGHT_DAMAGE | - bits_COND_HEAVY_DAMAGE | - bits_COND_HEAR_SOUND, - - bits_SOUND_DANGER | - bits_SOUND_COMBAT | - bits_SOUND_PLAYER, - "Repel" - }, -}; - - -//========================================================= -// repel -//========================================================= -Task_t tlGruntRepelAttack[] = -{ - { TASK_STOP_MOVING, (float)0 }, - { TASK_FACE_ENEMY, (float)0 }, - { TASK_PLAY_SEQUENCE, (float)ACT_FLY }, -}; - -Schedule_t slGruntRepelAttack[] = -{ - { - tlGruntRepelAttack, - ARRAYSIZE ( tlGruntRepelAttack ), - bits_COND_ENEMY_OCCLUDED, - 0, - "Repel Attack" - }, -}; - -//========================================================= -// repel land -//========================================================= -Task_t tlGruntRepelLand[] = -{ - { TASK_STOP_MOVING, (float)0 }, - { TASK_PLAY_SEQUENCE, (float)ACT_LAND }, - { TASK_GET_PATH_TO_LASTPOSITION,(float)0 }, - { TASK_RUN_PATH, (float)0 }, - { TASK_WAIT_FOR_MOVEMENT, (float)0 }, - { TASK_CLEAR_LASTPOSITION, (float)0 }, -}; - -Schedule_t slGruntRepelLand[] = -{ - { - tlGruntRepelLand, - ARRAYSIZE ( tlGruntRepelLand ), - bits_COND_SEE_ENEMY | - bits_COND_NEW_ENEMY | - bits_COND_LIGHT_DAMAGE | - bits_COND_HEAVY_DAMAGE | - bits_COND_HEAR_SOUND, - - bits_SOUND_DANGER | - bits_SOUND_COMBAT | - bits_SOUND_PLAYER, - "Repel Land" - }, -}; - - -DEFINE_CUSTOM_SCHEDULES( CHGrunt ) -{ - slGruntFail, - slGruntCombatFail, - slGruntVictoryDance, - slGruntEstablishLineOfFire, - slGruntFoundEnemy, - slGruntCombatFace, - slGruntSignalSuppress, - slGruntSuppress, - slGruntWaitInCover, - slGruntTakeCover, - slGruntGrenadeCover, - slGruntTossGrenadeCover, - slGruntTakeCoverFromBestSound, - slGruntHideReload, - slGruntSweep, - slGruntRangeAttack1A, - slGruntRangeAttack1B, - slGruntRangeAttack2, - slGruntRepel, - slGruntRepelAttack, - slGruntRepelLand, -}; - -IMPLEMENT_CUSTOM_SCHEDULES( CHGrunt, CSquadMonster ); - -//========================================================= -// SetActivity -//========================================================= -void CHGrunt :: SetActivity ( Activity NewActivity ) -{ - int iSequence = ACTIVITY_NOT_AVAILABLE; - void *pmodel = GET_MODEL_PTR( ENT(pev) ); - - switch ( NewActivity) - { - case ACT_RANGE_ATTACK1: - // grunt is either shooting standing or shooting crouched - if (FBitSet( pev->weapons, HGRUNT_9MMAR)) - { - if ( m_fStanding ) - { - // get aimable sequence - iSequence = LookupSequence( "standing_mp5" ); - } - else - { - // get crouching shoot - iSequence = LookupSequence( "crouching_mp5" ); - } - } - else - { - if ( m_fStanding ) - { - // get aimable sequence - iSequence = LookupSequence( "standing_shotgun" ); - } - else - { - // get crouching shoot - iSequence = LookupSequence( "crouching_shotgun" ); - } - } - break; - case ACT_RANGE_ATTACK2: - // grunt is going to a secondary long range attack. This may be a thrown - // grenade or fired grenade, we must determine which and pick proper sequence - if ( pev->weapons & HGRUNT_HANDGRENADE ) - { - // get toss anim - iSequence = LookupSequence( "throwgrenade" ); - } - // LRC: added a test to stop a marine without a launcher from firing. - else if ( pev->weapons & HGRUNT_GRENADELAUNCHER ) - { - // get launch anim - iSequence = LookupSequence( "launchgrenade" ); - } - else - { - ALERT( at_debug, "No grenades available. "); // flow into the error message we get at the end... - } - break; - case ACT_RUN: - if ( pev->health <= HGRUNT_LIMP_HEALTH ) - { - // limp! - iSequence = LookupActivity ( ACT_RUN_HURT ); - } - else - { - iSequence = LookupActivity ( NewActivity ); - } - break; - case ACT_WALK: - if ( pev->health <= HGRUNT_LIMP_HEALTH ) - { - // limp! - iSequence = LookupActivity ( ACT_WALK_HURT ); - } - else - { - iSequence = LookupActivity ( NewActivity ); - } - break; - case ACT_IDLE: - if ( m_MonsterState == MONSTERSTATE_COMBAT ) - { - NewActivity = ACT_IDLE_ANGRY; - } - iSequence = LookupActivity ( NewActivity ); - break; - default: - iSequence = LookupActivity ( NewActivity ); - break; - } - - m_Activity = NewActivity; // Go ahead and set this so it doesn't keep trying when the anim is not present - - // Set to the desired anim, or default anim if the desired is not present - if ( iSequence > ACTIVITY_NOT_AVAILABLE ) - { - if ( pev->sequence != iSequence || !m_fSequenceLoops ) - { - pev->frame = 0; - } - - pev->sequence = iSequence; // Set to the reset anim (if it's there) - ResetSequenceInfo( ); - SetYawSpeed(); - } - else - { - // Not available try to get default anim - ALERT ( at_debug, "%s has no sequence for act:%d\n", STRING(pev->classname), NewActivity ); - pev->sequence = 0; // Set to the reset anim (if it's there) - } -} - -//========================================================= -// Get Schedule! -//========================================================= -Schedule_t *CHGrunt :: GetSchedule( void ) -{ - - // clear old sentence - m_iSentence = HGRUNT_SENT_NONE; - - // flying? If PRONE, barnacle has me. IF not, it's assumed I am rapelling. - if ( pev->movetype == MOVETYPE_FLY && m_MonsterState != MONSTERSTATE_PRONE ) - { - if (pev->flags & FL_ONGROUND) - { - // just landed - pev->movetype = MOVETYPE_STEP; - return GetScheduleOfType ( SCHED_GRUNT_REPEL_LAND ); - } - else - { - // repel down a rope, - if ( m_MonsterState == MONSTERSTATE_COMBAT ) - return GetScheduleOfType ( SCHED_GRUNT_REPEL_ATTACK ); - else - return GetScheduleOfType ( SCHED_GRUNT_REPEL ); - } - } - - // grunts place HIGH priority on running away from danger sounds. - if ( HasConditions(bits_COND_HEAR_SOUND) ) - { - CSound *pSound; - pSound = PBestSound(); - - ASSERT( pSound != NULL ); - if ( pSound) - { - if (pSound->m_iType & bits_SOUND_DANGER) - { - // dangerous sound nearby! - - //!!!KELLY - currently, this is the grunt's signal that a grenade has landed nearby, - // and the grunt should find cover from the blast - // good place for "SHIT!" or some other colorful verbal indicator of dismay. - // It's not safe to play a verbal order here "Scatter", etc cause - // this may only affect a single individual in a squad. - - if (FOkToSpeak()) - { - SENTENCEG_PlayRndSz( ENT(pev), "HG_GREN", HGRUNT_SENTENCE_VOLUME, GRUNT_ATTN, 0, m_voicePitch); - JustSpoke(); - } - return GetScheduleOfType( SCHED_TAKE_COVER_FROM_BEST_SOUND ); - } - /* - if (!HasConditions( bits_COND_SEE_ENEMY ) && ( pSound->m_iType & (bits_SOUND_PLAYER | bits_SOUND_COMBAT) )) - { - MakeIdealYaw( pSound->m_vecOrigin ); - } - */ - } - } - switch ( m_MonsterState ) - { - case MONSTERSTATE_COMBAT: - { -// dead enemy - if ( HasConditions( bits_COND_ENEMY_DEAD ) ) - { - // call base class, all code to handle dead enemies is centralized there. - return CBaseMonster :: GetSchedule(); - } - -// new enemy - if ( HasConditions(bits_COND_NEW_ENEMY) ) - { - if ( InSquad() ) - { - MySquadLeader()->m_fEnemyEluded = FALSE; - - if ( !IsLeader() ) - { - return GetScheduleOfType ( SCHED_TAKE_COVER_FROM_ENEMY ); - } - else - { - ALERT(at_aiconsole,"leader spotted player!\n"); - //!!!KELLY - the leader of a squad of grunts has just seen the player or a - // monster and has made it the squad's enemy. You - // can check pev->flags for FL_CLIENT to determine whether this is the player - // or a monster. He's going to immediately start - // firing, though. If you'd like, we can make an alternate "first sight" - // schedule where the leader plays a handsign anim - // that gives us enough time to hear a short sentence or spoken command - // before he starts pluggin away. - if (FOkToSpeak())// && RANDOM_LONG(0,1)) - { - if ((m_hEnemy != NULL) && m_hEnemy->IsPlayer()) - // player - SENTENCEG_PlayRndSz( ENT(pev), "HG_ALERT", HGRUNT_SENTENCE_VOLUME, GRUNT_ATTN, 0, m_voicePitch); - else if ((m_hEnemy != NULL) && - (m_hEnemy->Classify() != CLASS_PLAYER_ALLY) && - (m_hEnemy->Classify() != CLASS_HUMAN_PASSIVE) && - (m_hEnemy->Classify() != CLASS_MACHINE)) - // monster - SENTENCEG_PlayRndSz( ENT(pev), "HG_MONST", HGRUNT_SENTENCE_VOLUME, GRUNT_ATTN, 0, m_voicePitch); - - JustSpoke(); - } - - if ( HasConditions ( bits_COND_CAN_RANGE_ATTACK1 ) ) - { - return GetScheduleOfType ( SCHED_GRUNT_SUPPRESS ); - } - else - { - return GetScheduleOfType ( SCHED_GRUNT_ESTABLISH_LINE_OF_FIRE ); - } - } - } - } -// no ammo - else if ( HasConditions ( bits_COND_NO_AMMO_LOADED ) ) - { - //!!!KELLY - this individual just realized he's out of bullet ammo. - // He's going to try to find cover to run to and reload, but rarely, if - // none is available, he'll drop and reload in the open here. - return GetScheduleOfType ( SCHED_GRUNT_COVER_AND_RELOAD ); - } - -// damaged just a little - else if ( HasConditions( bits_COND_LIGHT_DAMAGE ) ) - { - // if hurt: - // 90% chance of taking cover - // 10% chance of flinch. - int iPercent = RANDOM_LONG(0,99); - - if ( iPercent <= 90 && m_hEnemy != NULL ) - { - // only try to take cover if we actually have an enemy! - - //!!!KELLY - this grunt was hit and is going to run to cover. - if (FOkToSpeak()) // && RANDOM_LONG(0,1)) - { - //SENTENCEG_PlayRndSz( ENT(pev), "HG_COVER", HGRUNT_SENTENCE_VOLUME, GRUNT_ATTN, 0, m_voicePitch); - m_iSentence = HGRUNT_SENT_COVER; - //JustSpoke(); - } - return GetScheduleOfType( SCHED_TAKE_COVER_FROM_ENEMY ); - } - else - { - return GetScheduleOfType( SCHED_SMALL_FLINCH ); - } - } -// can kick - else if ( HasConditions ( bits_COND_CAN_MELEE_ATTACK1 ) ) - { - return GetScheduleOfType ( SCHED_MELEE_ATTACK1 ); - } -// can grenade launch - - else if ( FBitSet( pev->weapons, HGRUNT_GRENADELAUNCHER) && HasConditions ( bits_COND_CAN_RANGE_ATTACK2 ) && OccupySlot( bits_SLOTS_HGRUNT_GRENADE ) ) - { - // shoot a grenade if you can - return GetScheduleOfType( SCHED_RANGE_ATTACK2 ); - } -// can shoot - else if ( HasConditions ( bits_COND_CAN_RANGE_ATTACK1 ) ) - { - if ( InSquad() ) - { - // if the enemy has eluded the squad and a squad member has just located the enemy - // and the enemy does not see the squad member, issue a call to the squad to waste a - // little time and give the player a chance to turn. - if ( MySquadLeader()->m_fEnemyEluded && !HasConditions ( bits_COND_ENEMY_FACING_ME ) ) - { - MySquadLeader()->m_fEnemyEluded = FALSE; - return GetScheduleOfType ( SCHED_GRUNT_FOUND_ENEMY ); - } - } - - if ( OccupySlot ( bits_SLOTS_HGRUNT_ENGAGE ) ) - { - // try to take an available ENGAGE slot - return GetScheduleOfType( SCHED_RANGE_ATTACK1 ); - } - else if ( HasConditions ( bits_COND_CAN_RANGE_ATTACK2 ) && OccupySlot( bits_SLOTS_HGRUNT_GRENADE ) ) - { - // throw a grenade if can and no engage slots are available - return GetScheduleOfType( SCHED_RANGE_ATTACK2 ); - } - else - { - // hide! - return GetScheduleOfType( SCHED_TAKE_COVER_FROM_ENEMY ); - } - } -// can't see enemy - else if ( HasConditions( bits_COND_ENEMY_OCCLUDED ) ) - { - if ( HasConditions( bits_COND_CAN_RANGE_ATTACK2 ) && OccupySlot( bits_SLOTS_HGRUNT_GRENADE ) ) - { - //!!!KELLY - this grunt is about to throw or fire a grenade at the player. Great place for "fire in the hole" "frag out" etc - if (FOkToSpeak()) - { - SENTENCEG_PlayRndSz( ENT(pev), "HG_THROW", HGRUNT_SENTENCE_VOLUME, GRUNT_ATTN, 0, m_voicePitch); - JustSpoke(); - } - return GetScheduleOfType( SCHED_RANGE_ATTACK2 ); - } - else if ( OccupySlot( bits_SLOTS_HGRUNT_ENGAGE ) ) - { - //!!!KELLY - grunt cannot see the enemy and has just decided to - // charge the enemy's position. - if (FOkToSpeak())// && RANDOM_LONG(0,1)) - { - //SENTENCEG_PlayRndSz( ENT(pev), "HG_CHARGE", HGRUNT_SENTENCE_VOLUME, GRUNT_ATTN, 0, m_voicePitch); - m_iSentence = HGRUNT_SENT_CHARGE; - //JustSpoke(); - } - - return GetScheduleOfType( SCHED_GRUNT_ESTABLISH_LINE_OF_FIRE ); - } - else - { - //!!!KELLY - grunt is going to stay put for a couple seconds to see if - // the enemy wanders back out into the open, or approaches the - // grunt's covered position. Good place for a taunt, I guess? - if (FOkToSpeak() && RANDOM_LONG(0,1)) - { - SENTENCEG_PlayRndSz( ENT(pev), "HG_TAUNT", HGRUNT_SENTENCE_VOLUME, GRUNT_ATTN, 0, m_voicePitch); - JustSpoke(); - } - return GetScheduleOfType( SCHED_STANDOFF ); - } - } - - if ( HasConditions( bits_COND_SEE_ENEMY ) && !HasConditions ( bits_COND_CAN_RANGE_ATTACK1 ) ) - { - return GetScheduleOfType ( SCHED_GRUNT_ESTABLISH_LINE_OF_FIRE ); - } - } - } - - // no special cases here, call the base class - return CSquadMonster :: GetSchedule(); -} - -//========================================================= -//========================================================= -Schedule_t* CHGrunt :: GetScheduleOfType ( int Type ) -{ - switch ( Type ) - { - case SCHED_TAKE_COVER_FROM_ENEMY: - { - if ( InSquad() ) - { - if ( g_iSkillLevel == SKILL_HARD && HasConditions( bits_COND_CAN_RANGE_ATTACK2 ) && OccupySlot( bits_SLOTS_HGRUNT_GRENADE ) ) - { - if (FOkToSpeak()) - { - SENTENCEG_PlayRndSz( ENT(pev), "HG_THROW", HGRUNT_SENTENCE_VOLUME, GRUNT_ATTN, 0, m_voicePitch); - JustSpoke(); - } - return slGruntTossGrenadeCover; - } - else - { - return &slGruntTakeCover[ 0 ]; - } - } - else - { - if ( OccupySlot( bits_SLOTS_HGRUNT_GRENADE ) && RANDOM_LONG(0,1) ) - { - return &slGruntGrenadeCover[ 0 ]; - } - else - { - return &slGruntTakeCover[ 0 ]; - } - } - } - case SCHED_TAKE_COVER_FROM_BEST_SOUND: - { - return &slGruntTakeCoverFromBestSound[ 0 ]; - } - case SCHED_GRUNT_TAKECOVER_FAILED: - { - if ( HasConditions( bits_COND_CAN_RANGE_ATTACK1 ) && OccupySlot( bits_SLOTS_HGRUNT_ENGAGE ) ) - { - return GetScheduleOfType( SCHED_RANGE_ATTACK1 ); - } - - return GetScheduleOfType ( SCHED_FAIL ); - } - break; - case SCHED_GRUNT_ELOF_FAIL: - { - // human grunt is unable to move to a position that allows him to attack the enemy. - return GetScheduleOfType ( SCHED_TAKE_COVER_FROM_ENEMY ); - } - break; - case SCHED_GRUNT_ESTABLISH_LINE_OF_FIRE: - { - return &slGruntEstablishLineOfFire[ 0 ]; - } - break; - case SCHED_RANGE_ATTACK1: - { - // randomly stand or crouch - if (RANDOM_LONG(0,9) == 0) - m_fStanding = RANDOM_LONG(0,1); - - if (m_fStanding) - return &slGruntRangeAttack1B[ 0 ]; - else - return &slGruntRangeAttack1A[ 0 ]; - } - case SCHED_RANGE_ATTACK2: - { - return &slGruntRangeAttack2[ 0 ]; - } - case SCHED_COMBAT_FACE: - { - return &slGruntCombatFace[ 0 ]; - } - case SCHED_GRUNT_WAIT_FACE_ENEMY: - { - return &slGruntWaitInCover[ 0 ]; - } - case SCHED_GRUNT_SWEEP: - { - return &slGruntSweep[ 0 ]; - } - case SCHED_GRUNT_COVER_AND_RELOAD: - { - return &slGruntHideReload[ 0 ]; - } - case SCHED_GRUNT_FOUND_ENEMY: - { - return &slGruntFoundEnemy[ 0 ]; - } - case SCHED_VICTORY_DANCE: - { - if ( InSquad() ) - { - if ( !IsLeader() ) - { - return &slGruntFail[ 0 ]; - } - } - - return &slGruntVictoryDance[ 0 ]; - } - case SCHED_GRUNT_SUPPRESS: - { - if ( m_hEnemy->IsPlayer() && m_fFirstEncounter ) - { - m_fFirstEncounter = FALSE;// after first encounter, leader won't issue handsigns anymore when he has a new enemy - return &slGruntSignalSuppress[ 0 ]; - } - else - { - return &slGruntSuppress[ 0 ]; - } - } - case SCHED_FAIL: - { - if ( m_hEnemy != NULL ) - { - // grunt has an enemy, so pick a different default fail schedule most likely to help recover. - return &slGruntCombatFail[ 0 ]; - } - - return &slGruntFail[ 0 ]; - } - case SCHED_GRUNT_REPEL: - { - if (pev->velocity.z > -128) - pev->velocity.z -= 32; - return &slGruntRepel[ 0 ]; - } - case SCHED_GRUNT_REPEL_ATTACK: - { - if (pev->velocity.z > -128) - pev->velocity.z -= 32; - return &slGruntRepelAttack[ 0 ]; - } - case SCHED_GRUNT_REPEL_LAND: - { - return &slGruntRepelLand[ 0 ]; - } - default: - { - return CSquadMonster :: GetScheduleOfType ( Type ); - } - } -} - - -//========================================================= -// CHGruntRepel - when triggered, spawns a monster_human_grunt -// repelling down a line. -//========================================================= - -class CHGruntRepel : public CBaseMonster -{ -public: - void Spawn( void ); - void Precache( void ); - void EXPORT RepelUse ( CBaseEntity *pActivator, CBaseEntity *pCaller, USE_TYPE useType, float value ); - int m_iSpriteTexture; // Don't save, precache -}; - -LINK_ENTITY_TO_CLASS( monster_grunt_repel, CHGruntRepel ); - -void CHGruntRepel::Spawn( void ) -{ - Precache( ); - pev->solid = SOLID_NOT; - - SetUse(&CHGruntRepel:: RepelUse ); -} - -void CHGruntRepel::Precache( void ) -{ - UTIL_PrecacheOther( "monster_human_grunt" ); - m_iSpriteTexture = PRECACHE_MODEL( "sprites/rope.spr" ); -} - -void CHGruntRepel::RepelUse ( CBaseEntity *pActivator, CBaseEntity *pCaller, USE_TYPE useType, float value ) -{ - TraceResult tr; - UTIL_TraceLine( pev->origin, pev->origin + Vector( 0, 0, -4096.0), dont_ignore_monsters, ENT(pev), &tr); - /* - if ( tr.pHit && Instance( tr.pHit )->pev->solid != SOLID_BSP) - return NULL; - */ - - CBaseEntity *pEntity = Create( "monster_human_grunt", pev->origin, pev->angles ); - CBaseMonster *pGrunt = pEntity->MyMonsterPointer( ); - pGrunt->pev->movetype = MOVETYPE_FLY; - pGrunt->pev->velocity = Vector( 0, 0, RANDOM_FLOAT( -196, -128 ) ); - pGrunt->SetActivity( ACT_GLIDE ); - // UNDONE: position? - pGrunt->m_vecLastPosition = tr.vecEndPos; - - CBeam *pBeam = CBeam::BeamCreate( "sprites/rope.spr", 10 ); - pBeam->PointEntInit( pev->origin + Vector(0,0,112), pGrunt->edict() ); - pBeam->SetFlags( FBEAM_SOLID ); - pBeam->SetColor( 255, 255, 255 ); - pBeam->SetThink(&CBeam:: SUB_Remove ); - pBeam->SetNextThink( -4096.0 * tr.flFraction / pGrunt->pev->velocity.z + 0.5 ); - - UTIL_Remove( this ); -} - - - -//========================================================= -// DEAD HGRUNT PROP -//========================================================= -class CDeadHGrunt : public CBaseMonster -{ -public: - void Spawn( void ); - int Classify ( void ) { return CLASS_HUMAN_MILITARY; } - - void KeyValue( KeyValueData *pkvd ); - - int m_iPose;// which sequence to display -- temporary, don't need to save - static char *m_szPoses[3]; -}; - -char *CDeadHGrunt::m_szPoses[] = { "deadstomach", "deadside", "deadsitting" }; - -void CDeadHGrunt::KeyValue( KeyValueData *pkvd ) -{ - if (FStrEq(pkvd->szKeyName, "pose")) - { - m_iPose = atoi(pkvd->szValue); - pkvd->fHandled = TRUE; - } - else - CBaseMonster::KeyValue( pkvd ); -} - -LINK_ENTITY_TO_CLASS( monster_hgrunt_dead, CDeadHGrunt ); - -//========================================================= -// ********** DeadHGrunt SPAWN ********** -//========================================================= -void CDeadHGrunt :: Spawn( void ) -{ - int oldBody; - - PRECACHE_MODEL("models/hgrunt.mdl"); - SET_MODEL(ENT(pev), "models/hgrunt.mdl"); - - pev->effects = 0; - pev->yaw_speed = 8; - pev->sequence = 0; - m_bloodColor = BLOOD_COLOR_RED; - - pev->sequence = LookupSequence( m_szPoses[m_iPose] ); - - if (pev->sequence == -1) - { - ALERT ( at_debug, "Dead hgrunt with bad pose\n" ); - } - - // Corpses have less health - pev->health = 8; - - oldBody = pev->body; - pev->body = 0; - - if (oldBody >= 5 && oldBody <= 7) - pev->skin = 1; - else - pev->skin = 0; - - switch( pev->weapons ) - { - case 0: // MP5 - SetBodygroup( GUN_GROUP, GUN_MP5 ); - break; - case 1: // Shotgun - SetBodygroup( GUN_GROUP, GUN_SHOTGUN ); - break; - case 2: // No gun - SetBodygroup( GUN_GROUP, GUN_NONE ); - break; - } - - switch( oldBody ) - { - case 2: // Gasmask, no gun - SetBodygroup( GUN_GROUP, GUN_NONE ); //fall through - case 0: case 6: // Gasmask (white/black) - SetBodygroup( HEAD_GROUP, HEAD_GRUNT ); - break; - case 3: // Commander, no gun - SetBodygroup( GUN_GROUP, GUN_NONE ); //fall through - case 1: // Commander - SetBodygroup( HEAD_GROUP, HEAD_COMMANDER ); - break; - case 4: case 7: // Skimask (white/black) - SetBodygroup( HEAD_GROUP, HEAD_SHOTGUN ); - break; - case 5: // Commander - SetBodygroup( HEAD_GROUP, HEAD_M203 ); - break; - } - - MonsterInitDead(); -} +//========================================================= +// Hit groups! +//========================================================= +/* + + 1 - Head + 2 - Stomach + 3 - Gun + +*/ + + +#include "extdll.h" +#include "plane.h" +#include "util.h" +#include "cbase.h" +#include "monsters.h" +#include "schedule.h" +#include "animation.h" +#include "squadmonster.h" +#include "weapons.h" +#include "talkmonster.h" +#include "soundent.h" +#include "effects.h" +#include "customentity.h" + +int g_fGruntQuestion; // true if an idle grunt asked a question. Cleared when someone answers. + +extern DLL_GLOBAL int g_iSkillLevel; + +//========================================================= +// monster-specific DEFINE's +//========================================================= +#define GRUNT_CLIP_SIZE 36 // how many bullets in a clip? - NOTE: 3 round burst sound, so keep as 3 * x! +#define GRUNT_VOL 0.35 // volume of grunt sounds +#define GRUNT_ATTN ATTN_NORM // attenutation of grunt sentences +#define HGRUNT_LIMP_HEALTH 20 +#define HGRUNT_DMG_HEADSHOT ( DMG_BULLET | DMG_CLUB ) // damage types that can kill a grunt with a single headshot. +#define HGRUNT_NUM_HEADS 2 // how many grunt heads are there? +#define HGRUNT_MINIMUM_HEADSHOT_DAMAGE 15 // must do at least this much damage in one shot to head to score a headshot kill +#define HGRUNT_SENTENCE_VOLUME (float)0.35 // volume of grunt sentences + +#define HGRUNT_9MMAR ( 1 << 0) +#define HGRUNT_HANDGRENADE ( 1 << 1) +#define HGRUNT_GRENADELAUNCHER ( 1 << 2) +#define HGRUNT_SHOTGUN ( 1 << 3) + +#define HEAD_GROUP 1 +#define HEAD_GRUNT 0 +#define HEAD_COMMANDER 1 +#define HEAD_SHOTGUN 2 +#define HEAD_M203 3 +#define GUN_GROUP 2 +#define GUN_MP5 0 +#define GUN_SHOTGUN 1 +#define GUN_NONE 2 + +//========================================================= +// Monster's Anim Events Go Here +//========================================================= +#define HGRUNT_AE_RELOAD ( 2 ) +#define HGRUNT_AE_KICK ( 3 ) +#define HGRUNT_AE_BURST1 ( 4 ) +#define HGRUNT_AE_BURST2 ( 5 ) +#define HGRUNT_AE_BURST3 ( 6 ) +#define HGRUNT_AE_GREN_TOSS ( 7 ) +#define HGRUNT_AE_GREN_LAUNCH ( 8 ) +#define HGRUNT_AE_GREN_DROP ( 9 ) +#define HGRUNT_AE_CAUGHT_ENEMY ( 10) // grunt established sight with an enemy (player only) that had previously eluded the squad. +#define HGRUNT_AE_DROP_GUN ( 11) // grunt (probably dead) is dropping his mp5. + +//========================================================= +// monster-specific schedule types +//========================================================= +enum +{ + SCHED_GRUNT_SUPPRESS = LAST_COMMON_SCHEDULE + 1, + SCHED_GRUNT_ESTABLISH_LINE_OF_FIRE,// move to a location to set up an attack against the enemy. (usually when a friendly is in the way). + SCHED_GRUNT_COVER_AND_RELOAD, + SCHED_GRUNT_SWEEP, + SCHED_GRUNT_FOUND_ENEMY, + SCHED_GRUNT_REPEL, + SCHED_GRUNT_REPEL_ATTACK, + SCHED_GRUNT_REPEL_LAND, + SCHED_GRUNT_WAIT_FACE_ENEMY, + SCHED_GRUNT_TAKECOVER_FAILED,// special schedule type that forces analysis of conditions and picks the best possible schedule to recover from this type of failure. + SCHED_GRUNT_ELOF_FAIL, +}; + +//========================================================= +// monster-specific tasks +//========================================================= +enum +{ + TASK_GRUNT_FACE_TOSS_DIR = LAST_COMMON_TASK + 1, + TASK_GRUNT_SPEAK_SENTENCE, + TASK_GRUNT_CHECK_FIRE, +}; + +//========================================================= +// monster-specific conditions +//========================================================= +#define bits_COND_GRUNT_NOFIRE ( bits_COND_SPECIAL1 ) + +class CHGrunt : public CSquadMonster +{ +public: + void Spawn( void ); + void Precache( void ); + void SetYawSpeed ( void ); + int Classify ( void ); + int ISoundMask ( void ); + void HandleAnimEvent( MonsterEvent_t *pEvent ); + BOOL FCanCheckAttacks ( void ); + BOOL CheckMeleeAttack1 ( float flDot, float flDist ); + BOOL CheckRangeAttack1 ( float flDot, float flDist ); + BOOL CheckRangeAttack2 ( float flDot, float flDist ); + void CheckAmmo ( void ); + void SetActivity ( Activity NewActivity ); + void StartTask ( Task_t *pTask ); + void RunTask ( Task_t *pTask ); + void DeathSound( void ); + void PainSound( void ); + void IdleSound ( void ); + Vector GetGunPosition( void ); + void Shoot ( void ); + void Shotgun ( void ); + void PrescheduleThink ( void ); + void GibMonster( void ); + void SpeakSentence( void ); + + int Save( CSave &save ); + int Restore( CRestore &restore ); + + CBaseEntity *Kick( void ); + Schedule_t *GetSchedule( void ); + Schedule_t *GetScheduleOfType ( int Type ); + void TraceAttack( entvars_t *pevAttacker, float flDamage, Vector vecDir, TraceResult *ptr, int bitsDamageType); + int TakeDamage( entvars_t *pevInflictor, entvars_t *pevAttacker, float flDamage, int bitsDamageType ); + + int IRelationship ( CBaseEntity *pTarget ); + + BOOL FOkToSpeak( void ); + void JustSpoke( void ); + + CUSTOM_SCHEDULES; + static TYPEDESCRIPTION m_SaveData[]; + + // checking the feasibility of a grenade toss is kind of costly, so we do it every couple of seconds, + // not every server frame. + float m_flNextGrenadeCheck; + float m_flNextPainTime; + float m_flLastEnemySightTime; + + Vector m_vecTossVelocity; + + BOOL m_fThrowGrenade; + BOOL m_fStanding; + BOOL m_fFirstEncounter;// only put on the handsign show in the squad's first encounter. + int m_cClipSize; + + int m_voicePitch; + + int m_iBrassShell; + int m_iShotgunShell; + + int m_iSentence; + + static const char *pGruntSentences[]; +}; + +LINK_ENTITY_TO_CLASS( monster_human_grunt, CHGrunt ); + +TYPEDESCRIPTION CHGrunt::m_SaveData[] = +{ + DEFINE_FIELD( CHGrunt, m_flNextGrenadeCheck, FIELD_TIME ), + DEFINE_FIELD( CHGrunt, m_flNextPainTime, FIELD_TIME ), +// DEFINE_FIELD( CHGrunt, m_flLastEnemySightTime, FIELD_TIME ), // don't save, go to zero + DEFINE_FIELD( CHGrunt, m_vecTossVelocity, FIELD_VECTOR ), + DEFINE_FIELD( CHGrunt, m_fThrowGrenade, FIELD_BOOLEAN ), + DEFINE_FIELD( CHGrunt, m_fStanding, FIELD_BOOLEAN ), + DEFINE_FIELD( CHGrunt, m_fFirstEncounter, FIELD_BOOLEAN ), + DEFINE_FIELD( CHGrunt, m_cClipSize, FIELD_INTEGER ), + DEFINE_FIELD( CHGrunt, m_voicePitch, FIELD_INTEGER ), +// DEFINE_FIELD( CShotgun, m_iBrassShell, FIELD_INTEGER ), +// DEFINE_FIELD( CShotgun, m_iShotgunShell, FIELD_INTEGER ), + DEFINE_FIELD( CHGrunt, m_iSentence, FIELD_INTEGER ), +}; + +IMPLEMENT_SAVERESTORE( CHGrunt, CSquadMonster ); + +const char *CHGrunt::pGruntSentences[] = +{ + "HG_GREN", // grenade scared grunt + "HG_ALERT", // sees player + "HG_MONSTER", // sees monster + "HG_COVER", // running to cover + "HG_THROW", // about to throw grenade + "HG_CHARGE", // running out to get the enemy + "HG_TAUNT", // say rude things +}; + +enum +{ + HGRUNT_SENT_NONE = -1, + HGRUNT_SENT_GREN = 0, + HGRUNT_SENT_ALERT, + HGRUNT_SENT_MONSTER, + HGRUNT_SENT_COVER, + HGRUNT_SENT_THROW, + HGRUNT_SENT_CHARGE, + HGRUNT_SENT_TAUNT, +} HGRUNT_SENTENCE_TYPES; + +//========================================================= +// Speak Sentence - say your cued up sentence. +// +// Some grunt sentences (take cover and charge) rely on actually +// being able to execute the intended action. It's really lame +// when a grunt says 'COVER ME' and then doesn't move. The problem +// is that the sentences were played when the decision to TRY +// to move to cover was made. Now the sentence is played after +// we know for sure that there is a valid path. The schedule +// may still fail but in most cases, well after the grunt has +// started moving. +//========================================================= +void CHGrunt :: SpeakSentence( void ) +{ + if ( m_iSentence == HGRUNT_SENT_NONE ) + { + // no sentence cued up. + return; + } + + if (FOkToSpeak()) + { + SENTENCEG_PlayRndSz( ENT(pev), pGruntSentences[ m_iSentence ], HGRUNT_SENTENCE_VOLUME, GRUNT_ATTN, 0, m_voicePitch); + JustSpoke(); + } +} + +//========================================================= +// IRelationship - overridden because Alien Grunts are +// Human Grunt's nemesis. +//========================================================= +int CHGrunt::IRelationship ( CBaseEntity *pTarget ) +{ + if ( FClassnameIs( pTarget->pev, "monster_alien_grunt" ) || ( FClassnameIs( pTarget->pev, "monster_gargantua" ) ) ) + { + return R_NM; + } + + return CSquadMonster::IRelationship( pTarget ); +} + +//========================================================= +// GibMonster - make gun fly through the air. +//========================================================= +void CHGrunt :: GibMonster ( void ) +{ + Vector vecGunPos; + Vector vecGunAngles; + + if ( GetBodygroup( 2 ) != 2 ) + {// throw a gun if the grunt has one + GetAttachment( 0, vecGunPos, vecGunAngles ); + + CBaseEntity *pGun; + if (FBitSet( pev->weapons, HGRUNT_SHOTGUN )) + { + pGun = DropItem( "weapon_shotgun", vecGunPos, vecGunAngles ); + } + else + { + pGun = DropItem( "weapon_9mmAR", vecGunPos, vecGunAngles ); + } + if ( pGun ) + { + pGun->pev->velocity = Vector (RANDOM_FLOAT(-100,100), RANDOM_FLOAT(-100,100), RANDOM_FLOAT(200,300)); + pGun->pev->avelocity = Vector ( 0, RANDOM_FLOAT( 200, 400 ), 0 ); + } + + if (FBitSet( pev->weapons, HGRUNT_GRENADELAUNCHER )) + { + pGun = DropItem( "ammo_ARgrenades", vecGunPos, vecGunAngles ); + if ( pGun ) + { + pGun->pev->velocity = Vector (RANDOM_FLOAT(-100,100), RANDOM_FLOAT(-100,100), RANDOM_FLOAT(200,300)); + pGun->pev->avelocity = Vector ( 0, RANDOM_FLOAT( 200, 400 ), 0 ); + } + } + } + + CBaseMonster :: GibMonster(); +} + +//========================================================= +// ISoundMask - Overidden for human grunts because they +// hear the DANGER sound that is made by hand grenades and +// other dangerous items. +//========================================================= +int CHGrunt :: ISoundMask ( void ) +{ + return bits_SOUND_WORLD | + bits_SOUND_COMBAT | + bits_SOUND_PLAYER | + bits_SOUND_DANGER; +} + +//========================================================= +// someone else is talking - don't speak +//========================================================= +BOOL CHGrunt :: FOkToSpeak( void ) +{ +// if someone else is talking, don't speak + if (gpGlobals->time <= CTalkMonster::g_talkWaitTime) + return FALSE; + + if ( pev->spawnflags & SF_MONSTER_GAG ) + { + if ( m_MonsterState != MONSTERSTATE_COMBAT ) + { + // no talking outside of combat if gagged. + return FALSE; + } + } + + // if player is not in pvs, don't speak +// if (FNullEnt(FIND_CLIENT_IN_PVS(edict()))) +// return FALSE; + + return TRUE; +} + +//========================================================= +//========================================================= +void CHGrunt :: JustSpoke( void ) +{ + CTalkMonster::g_talkWaitTime = gpGlobals->time + RANDOM_FLOAT(1.5, 2.0); + m_iSentence = HGRUNT_SENT_NONE; +} + +//========================================================= +// PrescheduleThink - this function runs after conditions +// are collected and before scheduling code is run. +//========================================================= +void CHGrunt :: PrescheduleThink ( void ) +{ + if ( InSquad() && m_hEnemy != NULL ) + { + if ( HasConditions ( bits_COND_SEE_ENEMY ) ) + { + // update the squad's last enemy sighting time. + MySquadLeader()->m_flLastEnemySightTime = gpGlobals->time; + } + else + { + if ( gpGlobals->time - MySquadLeader()->m_flLastEnemySightTime > 5 ) + { + // been a while since we've seen the enemy + MySquadLeader()->m_fEnemyEluded = TRUE; + } + } + } +} + +//========================================================= +// FCanCheckAttacks - this is overridden for human grunts +// because they can throw/shoot grenades when they can't see their +// target and the base class doesn't check attacks if the monster +// cannot see its enemy. +// +// !!!BUGBUG - this gets called before a 3-round burst is fired +// which means that a friendly can still be hit with up to 2 rounds. +// ALSO, grenades will not be tossed if there is a friendly in front, +// this is a bad bug. Friendly machine gun fire avoidance +// will unecessarily prevent the throwing of a grenade as well. +//========================================================= +BOOL CHGrunt :: FCanCheckAttacks ( void ) +{ + if ( !HasConditions( bits_COND_ENEMY_TOOFAR ) ) + { + return TRUE; + } + else + { + return FALSE; + } +} + + +//========================================================= +// CheckMeleeAttack1 +//========================================================= +BOOL CHGrunt :: CheckMeleeAttack1 ( float flDot, float flDist ) +{ + CBaseMonster *pEnemy; + + if ( m_hEnemy != NULL ) + { + pEnemy = m_hEnemy->MyMonsterPointer(); + + if ( !pEnemy ) + { + return FALSE; + } + } + + if ( flDist <= 64 && flDot >= 0.7 && + pEnemy->Classify() != CLASS_ALIEN_BIOWEAPON && + pEnemy->Classify() != CLASS_PLAYER_BIOWEAPON ) + { + return TRUE; + } + return FALSE; +} + +//========================================================= +// CheckRangeAttack1 - overridden for HGrunt, cause +// FCanCheckAttacks() doesn't disqualify all attacks based +// on whether or not the enemy is occluded because unlike +// the base class, the HGrunt can attack when the enemy is +// occluded (throw grenade over wall, etc). We must +// disqualify the machine gun attack if the enemy is occluded. +//========================================================= +BOOL CHGrunt :: CheckRangeAttack1 ( float flDot, float flDist ) +{ + if ( !HasConditions( bits_COND_ENEMY_OCCLUDED ) && flDist <= 2048 && flDot >= 0.5 && NoFriendlyFire() ) + { + TraceResult tr; + + if ( !m_hEnemy->IsPlayer() && flDist <= 64 ) + { + // kick nonclients, but don't shoot at them. + return FALSE; + } + + Vector vecSrc = GetGunPosition(); + + // verify that a bullet fired from the gun will hit the enemy before the world. + UTIL_TraceLine( vecSrc, m_hEnemy->BodyTarget(vecSrc), ignore_monsters, ignore_glass, ENT(pev), &tr); + + if ( tr.flFraction == 1.0 ) + { + return TRUE; + } + } + + return FALSE; +} + +//========================================================= +// CheckRangeAttack2 - this checks the Grunt's grenade +// attack. +//========================================================= +BOOL CHGrunt :: CheckRangeAttack2 ( float flDot, float flDist ) +{ + if (! FBitSet(pev->weapons, (HGRUNT_HANDGRENADE | HGRUNT_GRENADELAUNCHER))) + { + return FALSE; + } + + // if the grunt isn't moving, it's ok to check. + if ( m_flGroundSpeed != 0 ) + { + m_fThrowGrenade = FALSE; + return m_fThrowGrenade; + } + + // assume things haven't changed too much since last time + if (gpGlobals->time < m_flNextGrenadeCheck ) + { + return m_fThrowGrenade; + } + + if ( !FBitSet ( m_hEnemy->pev->flags, FL_ONGROUND ) && m_hEnemy->pev->waterlevel == 0 && m_vecEnemyLKP.z > pev->absmax.z ) + { + //!!!BUGBUG - we should make this check movetype and make sure it isn't FLY? Players who jump a lot are unlikely to + // be grenaded. + // don't throw grenades at anything that isn't on the ground! + m_fThrowGrenade = FALSE; + return m_fThrowGrenade; + } + + Vector vecTarget; + + if (FBitSet( pev->weapons, HGRUNT_HANDGRENADE)) + { + // find feet + if (RANDOM_LONG(0,1)) + { + // magically know where they are + vecTarget = Vector( m_hEnemy->pev->origin.x, m_hEnemy->pev->origin.y, m_hEnemy->pev->absmin.z ); + } + else + { + // toss it to where you last saw them + vecTarget = m_vecEnemyLKP; + } + // vecTarget = m_vecEnemyLKP + (m_hEnemy->BodyTarget( pev->origin ) - m_hEnemy->pev->origin); + // estimate position + // vecTarget = vecTarget + m_hEnemy->pev->velocity * 2; + } + else + { + // find target + // vecTarget = m_hEnemy->BodyTarget( pev->origin ); + vecTarget = m_vecEnemyLKP + (m_hEnemy->BodyTarget( pev->origin ) - m_hEnemy->pev->origin); + // estimate position + if (HasConditions( bits_COND_SEE_ENEMY)) + vecTarget = vecTarget + ((vecTarget - pev->origin).Length() / gSkillData.hgruntGrenadeSpeed) * m_hEnemy->pev->velocity; + } + + // are any of my squad members near the intended grenade impact area? + if ( InSquad() ) + { + if (SquadMemberInRange( vecTarget, 256 )) + { + // crap, I might blow my own guy up. Don't throw a grenade and don't check again for a while. + m_flNextGrenadeCheck = gpGlobals->time + 1; // one full second. + m_fThrowGrenade = FALSE; + } + } + + if ( ( vecTarget - pev->origin ).Length2D() <= 256 ) + { + // crap, I don't want to blow myself up + m_flNextGrenadeCheck = gpGlobals->time + 1; // one full second. + m_fThrowGrenade = FALSE; + return m_fThrowGrenade; + } + + + if (FBitSet( pev->weapons, HGRUNT_HANDGRENADE)) + { + Vector vecToss = VecCheckToss( pev, GetGunPosition(), vecTarget, 0.5 ); + + if ( vecToss != g_vecZero ) + { + m_vecTossVelocity = vecToss; + + // throw a hand grenade + m_fThrowGrenade = TRUE; + // don't check again for a while. + m_flNextGrenadeCheck = gpGlobals->time; // 1/3 second. + } + else + { + // don't throw + m_fThrowGrenade = FALSE; + // don't check again for a while. + m_flNextGrenadeCheck = gpGlobals->time + 1; // one full second. + } + } + else + { + Vector vecToss = VecCheckThrow( pev, GetGunPosition(), vecTarget, gSkillData.hgruntGrenadeSpeed, 0.5 ); + + if ( vecToss != g_vecZero ) + { + m_vecTossVelocity = vecToss; + + // throw a hand grenade + m_fThrowGrenade = TRUE; + // don't check again for a while. + m_flNextGrenadeCheck = gpGlobals->time + 0.3; // 1/3 second. + } + else + { + // don't throw + m_fThrowGrenade = FALSE; + // don't check again for a while. + m_flNextGrenadeCheck = gpGlobals->time + 1; // one full second. + } + } + + + + return m_fThrowGrenade; +} + + +//========================================================= +// TraceAttack - make sure we're not taking it in the helmet +//========================================================= +void CHGrunt :: TraceAttack( entvars_t *pevAttacker, float flDamage, Vector vecDir, TraceResult *ptr, int bitsDamageType) +{ + // check for helmet shot + if (ptr->iHitgroup == 11) + { + // make sure we're wearing one + if (GetBodygroup( 1 ) == HEAD_GRUNT && (bitsDamageType & (DMG_BULLET | DMG_SLASH | DMG_BLAST | DMG_CLUB))) + { + // absorb damage + flDamage -= 20; + if (flDamage <= 0) + { + UTIL_Ricochet( ptr->vecEndPos, 1.0 ); + flDamage = 0.01; + } + } + // it's head shot anyways + ptr->iHitgroup = HITGROUP_HEAD; + } + CSquadMonster::TraceAttack( pevAttacker, flDamage, vecDir, ptr, bitsDamageType ); +} + + +//========================================================= +// TakeDamage - overridden for the grunt because the grunt +// needs to forget that he is in cover if he's hurt. (Obviously +// not in a safe place anymore). +//========================================================= +int CHGrunt :: TakeDamage( entvars_t *pevInflictor, entvars_t *pevAttacker, float flDamage, int bitsDamageType ) +{ + Forget( bits_MEMORY_INCOVER ); + + return CSquadMonster :: TakeDamage ( pevInflictor, pevAttacker, flDamage, bitsDamageType ); +} + +//========================================================= +// SetYawSpeed - allows each sequence to have a different +// turn rate associated with it. +//========================================================= +void CHGrunt :: SetYawSpeed ( void ) +{ + int ys; + + switch ( m_Activity ) + { + case ACT_IDLE: + ys = 150; + break; + case ACT_RUN: + ys = 150; + break; + case ACT_WALK: + ys = 180; + break; + case ACT_RANGE_ATTACK1: + ys = 120; + break; + case ACT_RANGE_ATTACK2: + ys = 120; + break; + case ACT_MELEE_ATTACK1: + ys = 120; + break; + case ACT_MELEE_ATTACK2: + ys = 120; + break; + case ACT_TURN_LEFT: + case ACT_TURN_RIGHT: + ys = 180; + break; + case ACT_GLIDE: + case ACT_FLY: + ys = 30; + break; + default: + ys = 90; + break; + } + + pev->yaw_speed = ys; +} + +void CHGrunt :: IdleSound( void ) +{ + if (FOkToSpeak() && (g_fGruntQuestion || RANDOM_LONG(0,1))) + { + if (!g_fGruntQuestion) + { + // ask question or make statement + switch (RANDOM_LONG(0,2)) + { + case 0: // check in + SENTENCEG_PlayRndSz(ENT(pev), "HG_CHECK", HGRUNT_SENTENCE_VOLUME, ATTN_NORM, 0, m_voicePitch); + g_fGruntQuestion = 1; + break; + case 1: // question + SENTENCEG_PlayRndSz(ENT(pev), "HG_QUEST", HGRUNT_SENTENCE_VOLUME, ATTN_NORM, 0, m_voicePitch); + g_fGruntQuestion = 2; + break; + case 2: // statement + SENTENCEG_PlayRndSz(ENT(pev), "HG_IDLE", HGRUNT_SENTENCE_VOLUME, ATTN_NORM, 0, m_voicePitch); + break; + } + } + else + { + switch (g_fGruntQuestion) + { + case 1: // check in + SENTENCEG_PlayRndSz(ENT(pev), "HG_CLEAR", HGRUNT_SENTENCE_VOLUME, ATTN_NORM, 0, m_voicePitch); + break; + case 2: // question + SENTENCEG_PlayRndSz(ENT(pev), "HG_ANSWER", HGRUNT_SENTENCE_VOLUME, ATTN_NORM, 0, m_voicePitch); + break; + } + g_fGruntQuestion = 0; + } + JustSpoke(); + } +} + +//========================================================= +// CheckAmmo - overridden for the grunt because he actually +// uses ammo! (base class doesn't) +//========================================================= +void CHGrunt :: CheckAmmo ( void ) +{ + if ( m_cAmmoLoaded <= 0 ) + { + SetConditions(bits_COND_NO_AMMO_LOADED); + } +} + +//========================================================= +// Classify - indicates this monster's place in the +// relationship table. +//========================================================= +int CHGrunt :: Classify ( void ) +{ + return CLASS_HUMAN_MILITARY; +} + +//========================================================= +//========================================================= +CBaseEntity *CHGrunt :: Kick( void ) +{ + TraceResult tr; + + UTIL_MakeVectors( pev->angles ); + Vector vecStart = pev->origin; + vecStart.z += pev->size.z * 0.5; + Vector vecEnd = vecStart + (gpGlobals->v_forward * 70); + + UTIL_TraceHull( vecStart, vecEnd, dont_ignore_monsters, head_hull, ENT(pev), &tr ); + + if ( tr.pHit ) + { + CBaseEntity *pEntity = CBaseEntity::Instance( tr.pHit ); + return pEntity; + } + + return NULL; +} + +//========================================================= +// GetGunPosition return the end of the barrel +//========================================================= + +Vector CHGrunt :: GetGunPosition( ) +{ + if (m_fStanding ) + { + return pev->origin + Vector( 0, 0, 60 ); + } + else + { + return pev->origin + Vector( 0, 0, 48 ); + } +} + +//========================================================= +// Shoot +//========================================================= +void CHGrunt :: Shoot ( void ) +{ + if (m_hEnemy == NULL) + { + return; + } + + Vector vecShootOrigin = GetGunPosition(); + Vector vecShootDir = ShootAtEnemy( vecShootOrigin ); + + UTIL_MakeVectors ( pev->angles ); + + Vector vecShellVelocity = gpGlobals->v_right * RANDOM_FLOAT(40,90) + gpGlobals->v_up * RANDOM_FLOAT(75,200) + gpGlobals->v_forward * RANDOM_FLOAT(-40, 40); + EjectBrass ( vecShootOrigin - vecShootDir * 24, vecShellVelocity, pev->angles.y, m_iBrassShell, TE_BOUNCE_SHELL); + FireBullets(1, vecShootOrigin, vecShootDir, VECTOR_CONE_10DEGREES, 2048, BULLET_MONSTER_MP5 ); // shoot +-5 degrees + + pev->effects |= EF_MUZZLEFLASH; + + m_cAmmoLoaded--;// take away a bullet! + + Vector angDir = UTIL_VecToAngles( vecShootDir ); + SetBlending( 0, angDir.x ); +} + +//========================================================= +// Shoot +//========================================================= +void CHGrunt :: Shotgun ( void ) +{ + if (m_hEnemy == NULL) + { + return; + } + + Vector vecShootOrigin = GetGunPosition(); + Vector vecShootDir = ShootAtEnemy( vecShootOrigin ); + + UTIL_MakeVectors ( pev->angles ); + + Vector vecShellVelocity = gpGlobals->v_right * RANDOM_FLOAT(40,90) + gpGlobals->v_up * RANDOM_FLOAT(75,200) + gpGlobals->v_forward * RANDOM_FLOAT(-40, 40); + EjectBrass ( vecShootOrigin - vecShootDir * 24, vecShellVelocity, pev->angles.y, m_iShotgunShell, TE_BOUNCE_SHOTSHELL); + FireBullets(gSkillData.hgruntShotgunPellets, vecShootOrigin, vecShootDir, VECTOR_CONE_15DEGREES, 2048, BULLET_PLAYER_BUCKSHOT, 0 ); // shoot +-7.5 degrees + + pev->effects |= EF_MUZZLEFLASH; + + m_cAmmoLoaded--;// take away a bullet! + + Vector angDir = UTIL_VecToAngles( vecShootDir ); + SetBlending( 0, angDir.x ); +} + +//========================================================= +// HandleAnimEvent - catches the monster-specific messages +// that occur when tagged animation frames are played. +//========================================================= +void CHGrunt :: HandleAnimEvent( MonsterEvent_t *pEvent ) +{ + Vector vecShootDir; + Vector vecShootOrigin; + + switch( pEvent->event ) + { + case HGRUNT_AE_DROP_GUN: + { + Vector vecGunPos; + Vector vecGunAngles; + + GetAttachment( 0, vecGunPos, vecGunAngles ); + + // switch to body group with no gun. + SetBodygroup( GUN_GROUP, GUN_NONE ); + + // now spawn a gun. + if (FBitSet( pev->weapons, HGRUNT_SHOTGUN )) + { + DropItem( "weapon_shotgun", vecGunPos, vecGunAngles ); + } + else + { + DropItem( "weapon_9mmAR", vecGunPos, vecGunAngles ); + } + if (FBitSet( pev->weapons, HGRUNT_GRENADELAUNCHER )) + { + DropItem( "ammo_ARgrenades", BodyTarget( pev->origin ), vecGunAngles ); + } + + } + break; + + case HGRUNT_AE_RELOAD: + EMIT_SOUND( ENT(pev), CHAN_WEAPON, "hgrunt/gr_reload1.wav", 1, ATTN_NORM ); + m_cAmmoLoaded = m_cClipSize; + ClearConditions(bits_COND_NO_AMMO_LOADED); + break; + + case HGRUNT_AE_GREN_TOSS: + { + UTIL_MakeVectors( pev->angles ); + // CGrenade::ShootTimed( pev, pev->origin + gpGlobals->v_forward * 34 + Vector (0, 0, 32), m_vecTossVelocity, 3.5 ); + CGrenade::ShootTimed( pev, GetGunPosition(), m_vecTossVelocity, 3.5 ); + + m_fThrowGrenade = FALSE; + m_flNextGrenadeCheck = gpGlobals->time + 6;// wait six seconds before even looking again to see if a grenade can be thrown. + // !!!LATER - when in a group, only try to throw grenade if ordered. + } + break; + + case HGRUNT_AE_GREN_LAUNCH: + { + EMIT_SOUND(ENT(pev), CHAN_WEAPON, "weapons/glauncher.wav", 0.8, ATTN_NORM); + CGrenade::ShootContact( pev, GetGunPosition(), m_vecTossVelocity ); + m_fThrowGrenade = FALSE; + if (g_iSkillLevel == SKILL_HARD) + m_flNextGrenadeCheck = gpGlobals->time + RANDOM_FLOAT( 2, 5 );// wait a random amount of time before shooting again + else + m_flNextGrenadeCheck = gpGlobals->time + 6;// wait six seconds before even looking again to see if a grenade can be thrown. + } + break; + + case HGRUNT_AE_GREN_DROP: + { + UTIL_MakeVectors( pev->angles ); + CGrenade::ShootTimed( pev, pev->origin + gpGlobals->v_forward * 17 - gpGlobals->v_right * 27 + gpGlobals->v_up * 6, g_vecZero, 3 ); + } + break; + + case HGRUNT_AE_BURST1: + { + if ( FBitSet( pev->weapons, HGRUNT_9MMAR )) + { + Shoot(); + + // the first round of the three round burst plays the sound and puts a sound in the world sound list. + if ( RANDOM_LONG(0,1) ) + { + EMIT_SOUND( ENT(pev), CHAN_WEAPON, "hgrunt/gr_mgun1.wav", 1, ATTN_NORM ); + } + else + { + EMIT_SOUND( ENT(pev), CHAN_WEAPON, "hgrunt/gr_mgun2.wav", 1, ATTN_NORM ); + } + } + else + { + Shotgun( ); + + EMIT_SOUND(ENT(pev), CHAN_WEAPON, "weapons/sbarrel1.wav", 1, ATTN_NORM ); + } + + CSoundEnt::InsertSound ( bits_SOUND_COMBAT, pev->origin, 384, 0.3 ); + } + break; + + case HGRUNT_AE_BURST2: + case HGRUNT_AE_BURST3: + Shoot(); + break; + + case HGRUNT_AE_KICK: + { + CBaseEntity *pHurt = Kick(); + + if ( pHurt ) + { + // SOUND HERE! + UTIL_MakeVectors( pev->angles ); + pHurt->pev->punchangle.x = 15; + pHurt->pev->velocity = pHurt->pev->velocity + gpGlobals->v_forward * 100 + gpGlobals->v_up * 50; + pHurt->TakeDamage( pev, pev, gSkillData.hgruntDmgKick, DMG_CLUB ); + } + } + break; + + case HGRUNT_AE_CAUGHT_ENEMY: + { + if ( FOkToSpeak() ) + { + SENTENCEG_PlayRndSz(ENT(pev), "HG_ALERT", HGRUNT_SENTENCE_VOLUME, GRUNT_ATTN, 0, m_voicePitch); + JustSpoke(); + } + + } + + default: + CSquadMonster::HandleAnimEvent( pEvent ); + break; + } +} + +//========================================================= +// Spawn +//========================================================= +void CHGrunt :: Spawn() +{ + Precache( ); + + SET_MODEL(ENT(pev), "models/hgrunt.mdl"); + UTIL_SetSize(pev, VEC_HUMAN_HULL_MIN, VEC_HUMAN_HULL_MAX); + + pev->solid = SOLID_SLIDEBOX; + pev->movetype = MOVETYPE_STEP; + m_bloodColor = BLOOD_COLOR_RED; + pev->effects = 0; + pev->health = gSkillData.hgruntHealth; + m_flFieldOfView = 0.2;// indicates the width of this monster's forward view cone ( as a dotproduct result ) + m_MonsterState = MONSTERSTATE_NONE; + m_flNextGrenadeCheck = gpGlobals->time + 1; + m_flNextPainTime = gpGlobals->time; + m_iSentence = HGRUNT_SENT_NONE; + + m_afCapability = bits_CAP_SQUAD | bits_CAP_TURN_HEAD | bits_CAP_DOORS_GROUP; + + m_fEnemyEluded = FALSE; + m_fFirstEncounter = TRUE;// this is true when the grunt spawns, because he hasn't encountered an enemy yet. + + m_HackedGunPos = Vector ( 0, 0, 55 ); + + if (pev->weapons == 0) + { + // initialize to original values + pev->weapons = HGRUNT_9MMAR | HGRUNT_HANDGRENADE; + // pev->weapons = HGRUNT_SHOTGUN; + // pev->weapons = HGRUNT_9MMAR | HGRUNT_GRENADELAUNCHER; + } + + if (FBitSet( pev->weapons, HGRUNT_SHOTGUN )) + { + SetBodygroup( GUN_GROUP, GUN_SHOTGUN ); + m_cClipSize = 8; + } + else + { + m_cClipSize = GRUNT_CLIP_SIZE; + } + m_cAmmoLoaded = m_cClipSize; + + if (RANDOM_LONG( 0, 99 ) < 80) + pev->skin = 0; // light skin + else + pev->skin = 1; // dark skin + + if (FBitSet( pev->weapons, HGRUNT_SHOTGUN )) + { + SetBodygroup( HEAD_GROUP, HEAD_SHOTGUN); + } + else if (FBitSet( pev->weapons, HGRUNT_GRENADELAUNCHER )) + { + SetBodygroup( HEAD_GROUP, HEAD_M203 ); + pev->skin = 1; // alway dark skin + } + + CTalkMonster::g_talkWaitTime = 0; + + MonsterInit(); +} + +//========================================================= +// Precache - precaches all resources this monster needs +//========================================================= +void CHGrunt :: Precache() +{ + PRECACHE_MODEL("models/hgrunt.mdl"); + + PRECACHE_SOUND( "hgrunt/gr_mgun1.wav" ); + PRECACHE_SOUND( "hgrunt/gr_mgun2.wav" ); + + PRECACHE_SOUND( "hgrunt/gr_die1.wav" ); + PRECACHE_SOUND( "hgrunt/gr_die2.wav" ); + PRECACHE_SOUND( "hgrunt/gr_die3.wav" ); + + PRECACHE_SOUND( "hgrunt/gr_pain1.wav" ); + PRECACHE_SOUND( "hgrunt/gr_pain2.wav" ); + PRECACHE_SOUND( "hgrunt/gr_pain3.wav" ); + PRECACHE_SOUND( "hgrunt/gr_pain4.wav" ); + PRECACHE_SOUND( "hgrunt/gr_pain5.wav" ); + + PRECACHE_SOUND( "hgrunt/gr_reload1.wav" ); + + PRECACHE_SOUND( "weapons/glauncher.wav" ); + + PRECACHE_SOUND( "weapons/sbarrel1.wav" ); + + PRECACHE_SOUND("zombie/claw_miss2.wav");// because we use the basemonster SWIPE animation event + + // get voice pitch + if (RANDOM_LONG(0,1)) + m_voicePitch = 109 + RANDOM_LONG(0,7); + else + m_voicePitch = 100; + + m_iBrassShell = PRECACHE_MODEL ("models/shell.mdl");// brass shell + m_iShotgunShell = PRECACHE_MODEL ("models/shotgunshell.mdl"); +} + +//========================================================= +// start task +//========================================================= +void CHGrunt :: StartTask ( Task_t *pTask ) +{ + m_iTaskStatus = TASKSTATUS_RUNNING; + + switch ( pTask->iTask ) + { + case TASK_GRUNT_CHECK_FIRE: + if ( !NoFriendlyFire() ) + { + SetConditions( bits_COND_GRUNT_NOFIRE ); + } + TaskComplete(); + break; + + case TASK_GRUNT_SPEAK_SENTENCE: + SpeakSentence(); + TaskComplete(); + break; + + case TASK_WALK_PATH: + case TASK_RUN_PATH: + // grunt no longer assumes he is covered if he moves + Forget( bits_MEMORY_INCOVER ); + CSquadMonster ::StartTask( pTask ); + break; + + case TASK_RELOAD: + m_IdealActivity = ACT_RELOAD; + break; + + case TASK_GRUNT_FACE_TOSS_DIR: + break; + + case TASK_FACE_IDEAL: + case TASK_FACE_ENEMY: + CSquadMonster :: StartTask( pTask ); + if (pev->movetype == MOVETYPE_FLY) + { + m_IdealActivity = ACT_GLIDE; + } + break; + + default: + CSquadMonster :: StartTask( pTask ); + break; + } +} + +//========================================================= +// RunTask +//========================================================= +void CHGrunt :: RunTask ( Task_t *pTask ) +{ + switch ( pTask->iTask ) + { + case TASK_GRUNT_FACE_TOSS_DIR: + { + // project a point along the toss vector and turn to face that point. + MakeIdealYaw( pev->origin + m_vecTossVelocity * 64 ); + ChangeYaw( pev->yaw_speed ); + + if ( FacingIdeal() ) + { + m_iTaskStatus = TASKSTATUS_COMPLETE; + } + break; + } + default: + { + CSquadMonster :: RunTask( pTask ); + break; + } + } +} + +//========================================================= +// PainSound +//========================================================= +void CHGrunt :: PainSound ( void ) +{ + if ( gpGlobals->time > m_flNextPainTime ) + { +#if 0 + if ( RANDOM_LONG(0,99) < 5 ) + { + // pain sentences are rare + if (FOkToSpeak()) + { + SENTENCEG_PlayRndSz(ENT(pev), "HG_PAIN", HGRUNT_SENTENCE_VOLUME, ATTN_NORM, 0, PITCH_NORM); + JustSpoke(); + return; + } + } +#endif + switch ( RANDOM_LONG(0,6) ) + { + case 0: + EMIT_SOUND( ENT(pev), CHAN_VOICE, "hgrunt/gr_pain3.wav", 1, ATTN_NORM ); + break; + case 1: + EMIT_SOUND( ENT(pev), CHAN_VOICE, "hgrunt/gr_pain4.wav", 1, ATTN_NORM ); + break; + case 2: + EMIT_SOUND( ENT(pev), CHAN_VOICE, "hgrunt/gr_pain5.wav", 1, ATTN_NORM ); + break; + case 3: + EMIT_SOUND( ENT(pev), CHAN_VOICE, "hgrunt/gr_pain1.wav", 1, ATTN_NORM ); + break; + case 4: + EMIT_SOUND( ENT(pev), CHAN_VOICE, "hgrunt/gr_pain2.wav", 1, ATTN_NORM ); + break; + } + + m_flNextPainTime = gpGlobals->time + 1; + } +} + +//========================================================= +// DeathSound +//========================================================= +void CHGrunt :: DeathSound ( void ) +{ + switch ( RANDOM_LONG(0,2) ) + { + case 0: + EMIT_SOUND( ENT(pev), CHAN_VOICE, "hgrunt/gr_die1.wav", 1, ATTN_IDLE ); + break; + case 1: + EMIT_SOUND( ENT(pev), CHAN_VOICE, "hgrunt/gr_die2.wav", 1, ATTN_IDLE ); + break; + case 2: + EMIT_SOUND( ENT(pev), CHAN_VOICE, "hgrunt/gr_die3.wav", 1, ATTN_IDLE ); + break; + } +} + +//========================================================= +// AI Schedules Specific to this monster +//========================================================= + +//========================================================= +// GruntFail +//========================================================= +Task_t tlGruntFail[] = +{ + { TASK_STOP_MOVING, 0 }, + { TASK_SET_ACTIVITY, (float)ACT_IDLE }, + { TASK_WAIT, (float)2 }, + { TASK_WAIT_PVS, (float)0 }, +}; + +Schedule_t slGruntFail[] = +{ + { + tlGruntFail, + ARRAYSIZE ( tlGruntFail ), + bits_COND_CAN_RANGE_ATTACK1 | + bits_COND_CAN_RANGE_ATTACK2 | + bits_COND_CAN_MELEE_ATTACK1 | + bits_COND_CAN_MELEE_ATTACK2, + 0, + "Grunt Fail" + }, +}; + +//========================================================= +// Grunt Combat Fail +//========================================================= +Task_t tlGruntCombatFail[] = +{ + { TASK_STOP_MOVING, 0 }, + { TASK_SET_ACTIVITY, (float)ACT_IDLE }, + { TASK_WAIT_FACE_ENEMY, (float)2 }, + { TASK_WAIT_PVS, (float)0 }, +}; + +Schedule_t slGruntCombatFail[] = +{ + { + tlGruntCombatFail, + ARRAYSIZE ( tlGruntCombatFail ), + bits_COND_CAN_RANGE_ATTACK1 | + bits_COND_CAN_RANGE_ATTACK2, + 0, + "Grunt Combat Fail" + }, +}; + +//========================================================= +// Victory dance! +//========================================================= +Task_t tlGruntVictoryDance[] = +{ + { TASK_STOP_MOVING, (float)0 }, + { TASK_FACE_ENEMY, (float)0 }, + { TASK_WAIT, (float)1.5 }, + { TASK_GET_PATH_TO_ENEMY_CORPSE, (float)0 }, + { TASK_WALK_PATH, (float)0 }, + { TASK_WAIT_FOR_MOVEMENT, (float)0 }, + { TASK_FACE_ENEMY, (float)0 }, + { TASK_PLAY_SEQUENCE, (float)ACT_VICTORY_DANCE }, +}; + +Schedule_t slGruntVictoryDance[] = +{ + { + tlGruntVictoryDance, + ARRAYSIZE ( tlGruntVictoryDance ), + bits_COND_NEW_ENEMY | + bits_COND_LIGHT_DAMAGE | + bits_COND_HEAVY_DAMAGE, + 0, + "GruntVictoryDance" + }, +}; + +//========================================================= +// Establish line of fire - move to a position that allows +// the grunt to attack. +//========================================================= +Task_t tlGruntEstablishLineOfFire[] = +{ + { TASK_SET_FAIL_SCHEDULE, (float)SCHED_GRUNT_ELOF_FAIL }, + { TASK_GET_PATH_TO_ENEMY, (float)0 }, + { TASK_GRUNT_SPEAK_SENTENCE,(float)0 }, + { TASK_RUN_PATH, (float)0 }, + { TASK_WAIT_FOR_MOVEMENT, (float)0 }, +}; + +Schedule_t slGruntEstablishLineOfFire[] = +{ + { + tlGruntEstablishLineOfFire, + ARRAYSIZE ( tlGruntEstablishLineOfFire ), + bits_COND_NEW_ENEMY | + bits_COND_ENEMY_DEAD | + bits_COND_CAN_RANGE_ATTACK1 | + bits_COND_CAN_MELEE_ATTACK1 | + bits_COND_CAN_RANGE_ATTACK2 | + bits_COND_CAN_MELEE_ATTACK2 | + bits_COND_HEAR_SOUND, + + bits_SOUND_DANGER, + "GruntEstablishLineOfFire" + }, +}; + +//========================================================= +// GruntFoundEnemy - grunt established sight with an enemy +// that was hiding from the squad. +//========================================================= +Task_t tlGruntFoundEnemy[] = +{ + { TASK_STOP_MOVING, 0 }, + { TASK_FACE_ENEMY, (float)0 }, + { TASK_PLAY_SEQUENCE_FACE_ENEMY,(float)ACT_SIGNAL1 }, +}; + +Schedule_t slGruntFoundEnemy[] = +{ + { + tlGruntFoundEnemy, + ARRAYSIZE ( tlGruntFoundEnemy ), + bits_COND_HEAR_SOUND, + + bits_SOUND_DANGER, + "GruntFoundEnemy" + }, +}; + +//========================================================= +// GruntCombatFace Schedule +//========================================================= +Task_t tlGruntCombatFace1[] = +{ + { TASK_STOP_MOVING, 0 }, + { TASK_SET_ACTIVITY, (float)ACT_IDLE }, + { TASK_FACE_ENEMY, (float)0 }, + { TASK_WAIT, (float)1.5 }, + { TASK_SET_SCHEDULE, (float)SCHED_GRUNT_SWEEP }, +}; + +Schedule_t slGruntCombatFace[] = +{ + { + tlGruntCombatFace1, + ARRAYSIZE ( tlGruntCombatFace1 ), + bits_COND_NEW_ENEMY | + bits_COND_ENEMY_DEAD | + bits_COND_CAN_RANGE_ATTACK1 | + bits_COND_CAN_RANGE_ATTACK2, + 0, + "Combat Face" + }, +}; + +//========================================================= +// Suppressing fire - don't stop shooting until the clip is +// empty or grunt gets hurt. +//========================================================= +Task_t tlGruntSignalSuppress[] = +{ + { TASK_STOP_MOVING, 0 }, + { TASK_FACE_IDEAL, (float)0 }, + { TASK_PLAY_SEQUENCE_FACE_ENEMY, (float)ACT_SIGNAL2 }, + { TASK_FACE_ENEMY, (float)0 }, + { TASK_GRUNT_CHECK_FIRE, (float)0 }, + { TASK_RANGE_ATTACK1, (float)0 }, + { TASK_FACE_ENEMY, (float)0 }, + { TASK_GRUNT_CHECK_FIRE, (float)0 }, + { TASK_RANGE_ATTACK1, (float)0 }, + { TASK_FACE_ENEMY, (float)0 }, + { TASK_GRUNT_CHECK_FIRE, (float)0 }, + { TASK_RANGE_ATTACK1, (float)0 }, + { TASK_FACE_ENEMY, (float)0 }, + { TASK_GRUNT_CHECK_FIRE, (float)0 }, + { TASK_RANGE_ATTACK1, (float)0 }, + { TASK_FACE_ENEMY, (float)0 }, + { TASK_GRUNT_CHECK_FIRE, (float)0 }, + { TASK_RANGE_ATTACK1, (float)0 }, +}; + +Schedule_t slGruntSignalSuppress[] = +{ + { + tlGruntSignalSuppress, + ARRAYSIZE ( tlGruntSignalSuppress ), + bits_COND_ENEMY_DEAD | + bits_COND_LIGHT_DAMAGE | + bits_COND_HEAVY_DAMAGE | + bits_COND_HEAR_SOUND | + bits_COND_GRUNT_NOFIRE | + bits_COND_NO_AMMO_LOADED, + + bits_SOUND_DANGER, + "SignalSuppress" + }, +}; + +Task_t tlGruntSuppress[] = +{ + { TASK_STOP_MOVING, 0 }, + { TASK_FACE_ENEMY, (float)0 }, + { TASK_GRUNT_CHECK_FIRE, (float)0 }, + { TASK_RANGE_ATTACK1, (float)0 }, + { TASK_FACE_ENEMY, (float)0 }, + { TASK_GRUNT_CHECK_FIRE, (float)0 }, + { TASK_RANGE_ATTACK1, (float)0 }, + { TASK_FACE_ENEMY, (float)0 }, + { TASK_GRUNT_CHECK_FIRE, (float)0 }, + { TASK_RANGE_ATTACK1, (float)0 }, + { TASK_FACE_ENEMY, (float)0 }, + { TASK_GRUNT_CHECK_FIRE, (float)0 }, + { TASK_RANGE_ATTACK1, (float)0 }, + { TASK_FACE_ENEMY, (float)0 }, + { TASK_GRUNT_CHECK_FIRE, (float)0 }, + { TASK_RANGE_ATTACK1, (float)0 }, +}; + +Schedule_t slGruntSuppress[] = +{ + { + tlGruntSuppress, + ARRAYSIZE ( tlGruntSuppress ), + bits_COND_ENEMY_DEAD | + bits_COND_LIGHT_DAMAGE | + bits_COND_HEAVY_DAMAGE | + bits_COND_HEAR_SOUND | + bits_COND_GRUNT_NOFIRE | + bits_COND_NO_AMMO_LOADED, + + bits_SOUND_DANGER, + "Suppress" + }, +}; + + +//========================================================= +// grunt wait in cover - we don't allow danger or the ability +// to attack to break a grunt's run to cover schedule, but +// when a grunt is in cover, we do want them to attack if they can. +//========================================================= +Task_t tlGruntWaitInCover[] = +{ + { TASK_STOP_MOVING, (float)0 }, + { TASK_SET_ACTIVITY, (float)ACT_IDLE }, + { TASK_WAIT_FACE_ENEMY, (float)1 }, +}; + +Schedule_t slGruntWaitInCover[] = +{ + { + tlGruntWaitInCover, + ARRAYSIZE ( tlGruntWaitInCover ), + bits_COND_NEW_ENEMY | + bits_COND_HEAR_SOUND | + bits_COND_CAN_RANGE_ATTACK1 | + bits_COND_CAN_RANGE_ATTACK2 | + bits_COND_CAN_MELEE_ATTACK1 | + bits_COND_CAN_MELEE_ATTACK2, + + bits_SOUND_DANGER, + "GruntWaitInCover" + }, +}; + +//========================================================= +// run to cover. +// !!!BUGBUG - set a decent fail schedule here. +//========================================================= +Task_t tlGruntTakeCover1[] = +{ + { TASK_STOP_MOVING, (float)0 }, + { TASK_SET_FAIL_SCHEDULE, (float)SCHED_GRUNT_TAKECOVER_FAILED }, + { TASK_WAIT, (float)0.2 }, + { TASK_FIND_COVER_FROM_ENEMY, (float)0 }, + { TASK_GRUNT_SPEAK_SENTENCE, (float)0 }, + { TASK_RUN_PATH, (float)0 }, + { TASK_WAIT_FOR_MOVEMENT, (float)0 }, + { TASK_REMEMBER, (float)bits_MEMORY_INCOVER }, + { TASK_SET_SCHEDULE, (float)SCHED_GRUNT_WAIT_FACE_ENEMY }, +}; + +Schedule_t slGruntTakeCover[] = +{ + { + tlGruntTakeCover1, + ARRAYSIZE ( tlGruntTakeCover1 ), + 0, + 0, + "TakeCover" + }, +}; + +//========================================================= +// drop grenade then run to cover. +//========================================================= +Task_t tlGruntGrenadeCover1[] = +{ + { TASK_STOP_MOVING, (float)0 }, + { TASK_FIND_COVER_FROM_ENEMY, (float)99 }, + { TASK_FIND_FAR_NODE_COVER_FROM_ENEMY, (float)384 }, + { TASK_PLAY_SEQUENCE, (float)ACT_SPECIAL_ATTACK1 }, + { TASK_CLEAR_MOVE_WAIT, (float)0 }, + { TASK_RUN_PATH, (float)0 }, + { TASK_WAIT_FOR_MOVEMENT, (float)0 }, + { TASK_SET_SCHEDULE, (float)SCHED_GRUNT_WAIT_FACE_ENEMY }, +}; + +Schedule_t slGruntGrenadeCover[] = +{ + { + tlGruntGrenadeCover1, + ARRAYSIZE ( tlGruntGrenadeCover1 ), + 0, + 0, + "GrenadeCover" + }, +}; + + +//========================================================= +// drop grenade then run to cover. +//========================================================= +Task_t tlGruntTossGrenadeCover1[] = +{ + { TASK_FACE_ENEMY, (float)0 }, + { TASK_RANGE_ATTACK2, (float)0 }, + { TASK_SET_SCHEDULE, (float)SCHED_TAKE_COVER_FROM_ENEMY }, +}; + +Schedule_t slGruntTossGrenadeCover[] = +{ + { + tlGruntTossGrenadeCover1, + ARRAYSIZE ( tlGruntTossGrenadeCover1 ), + 0, + 0, + "TossGrenadeCover" + }, +}; + +//========================================================= +// hide from the loudest sound source (to run from grenade) +//========================================================= +Task_t tlGruntTakeCoverFromBestSound[] = +{ + { TASK_SET_FAIL_SCHEDULE, (float)SCHED_COWER },// duck and cover if cannot move from explosion + { TASK_STOP_MOVING, (float)0 }, + { TASK_FIND_COVER_FROM_BEST_SOUND, (float)0 }, + { TASK_RUN_PATH, (float)0 }, + { TASK_WAIT_FOR_MOVEMENT, (float)0 }, + { TASK_REMEMBER, (float)bits_MEMORY_INCOVER }, + { TASK_TURN_LEFT, (float)179 }, +}; + +Schedule_t slGruntTakeCoverFromBestSound[] = +{ + { + tlGruntTakeCoverFromBestSound, + ARRAYSIZE ( tlGruntTakeCoverFromBestSound ), + 0, + 0, + "GruntTakeCoverFromBestSound" + }, +}; + +//========================================================= +// Grunt reload schedule +//========================================================= +Task_t tlGruntHideReload[] = +{ + { TASK_STOP_MOVING, (float)0 }, + { TASK_SET_FAIL_SCHEDULE, (float)SCHED_RELOAD }, + { TASK_FIND_COVER_FROM_ENEMY, (float)0 }, + { TASK_RUN_PATH, (float)0 }, + { TASK_WAIT_FOR_MOVEMENT, (float)0 }, + { TASK_REMEMBER, (float)bits_MEMORY_INCOVER }, + { TASK_FACE_ENEMY, (float)0 }, + { TASK_PLAY_SEQUENCE, (float)ACT_RELOAD }, +}; + +Schedule_t slGruntHideReload[] = +{ + { + tlGruntHideReload, + ARRAYSIZE ( tlGruntHideReload ), + bits_COND_HEAVY_DAMAGE | + bits_COND_HEAR_SOUND, + + bits_SOUND_DANGER, + "GruntHideReload" + } +}; + +//========================================================= +// Do a turning sweep of the area +//========================================================= +Task_t tlGruntSweep[] = +{ + { TASK_TURN_LEFT, (float)179 }, + { TASK_WAIT, (float)1 }, + { TASK_TURN_LEFT, (float)179 }, + { TASK_WAIT, (float)1 }, +}; + +Schedule_t slGruntSweep[] = +{ + { + tlGruntSweep, + ARRAYSIZE ( tlGruntSweep ), + + bits_COND_NEW_ENEMY | + bits_COND_LIGHT_DAMAGE | + bits_COND_HEAVY_DAMAGE | + bits_COND_CAN_RANGE_ATTACK1 | + bits_COND_CAN_RANGE_ATTACK2 | + bits_COND_HEAR_SOUND, + + bits_SOUND_WORLD |// sound flags + bits_SOUND_DANGER | + bits_SOUND_PLAYER, + + "Grunt Sweep" + }, +}; + +//========================================================= +// primary range attack. Overriden because base class stops attacking when the enemy is occluded. +// grunt's grenade toss requires the enemy be occluded. +//========================================================= +Task_t tlGruntRangeAttack1A[] = +{ + { TASK_STOP_MOVING, (float)0 }, + { TASK_PLAY_SEQUENCE_FACE_ENEMY, (float)ACT_CROUCH }, + { TASK_GRUNT_CHECK_FIRE, (float)0 }, + { TASK_RANGE_ATTACK1, (float)0 }, + { TASK_FACE_ENEMY, (float)0 }, + { TASK_GRUNT_CHECK_FIRE, (float)0 }, + { TASK_RANGE_ATTACK1, (float)0 }, + { TASK_FACE_ENEMY, (float)0 }, + { TASK_GRUNT_CHECK_FIRE, (float)0 }, + { TASK_RANGE_ATTACK1, (float)0 }, + { TASK_FACE_ENEMY, (float)0 }, + { TASK_GRUNT_CHECK_FIRE, (float)0 }, + { TASK_RANGE_ATTACK1, (float)0 }, +}; + +Schedule_t slGruntRangeAttack1A[] = +{ + { + tlGruntRangeAttack1A, + ARRAYSIZE ( tlGruntRangeAttack1A ), + bits_COND_NEW_ENEMY | + bits_COND_ENEMY_DEAD | + bits_COND_HEAVY_DAMAGE | + bits_COND_ENEMY_OCCLUDED | + bits_COND_HEAR_SOUND | + bits_COND_GRUNT_NOFIRE | + bits_COND_NO_AMMO_LOADED, + + bits_SOUND_DANGER, + "Range Attack1A" + }, +}; + + +//========================================================= +// primary range attack. Overriden because base class stops attacking when the enemy is occluded. +// grunt's grenade toss requires the enemy be occluded. +//========================================================= +Task_t tlGruntRangeAttack1B[] = +{ + { TASK_STOP_MOVING, (float)0 }, + { TASK_PLAY_SEQUENCE_FACE_ENEMY,(float)ACT_IDLE_ANGRY }, + { TASK_GRUNT_CHECK_FIRE, (float)0 }, + { TASK_RANGE_ATTACK1, (float)0 }, + { TASK_FACE_ENEMY, (float)0 }, + { TASK_GRUNT_CHECK_FIRE, (float)0 }, + { TASK_RANGE_ATTACK1, (float)0 }, + { TASK_FACE_ENEMY, (float)0 }, + { TASK_GRUNT_CHECK_FIRE, (float)0 }, + { TASK_RANGE_ATTACK1, (float)0 }, + { TASK_FACE_ENEMY, (float)0 }, + { TASK_GRUNT_CHECK_FIRE, (float)0 }, + { TASK_RANGE_ATTACK1, (float)0 }, +}; + +Schedule_t slGruntRangeAttack1B[] = +{ + { + tlGruntRangeAttack1B, + ARRAYSIZE ( tlGruntRangeAttack1B ), + bits_COND_NEW_ENEMY | + bits_COND_ENEMY_DEAD | + bits_COND_HEAVY_DAMAGE | + bits_COND_ENEMY_OCCLUDED | + bits_COND_NO_AMMO_LOADED | + bits_COND_GRUNT_NOFIRE | + bits_COND_HEAR_SOUND, + + bits_SOUND_DANGER, + "Range Attack1B" + }, +}; + +//========================================================= +// secondary range attack. Overriden because base class stops attacking when the enemy is occluded. +// grunt's grenade toss requires the enemy be occluded. +//========================================================= +Task_t tlGruntRangeAttack2[] = +{ + { TASK_STOP_MOVING, (float)0 }, + { TASK_GRUNT_FACE_TOSS_DIR, (float)0 }, + { TASK_PLAY_SEQUENCE, (float)ACT_RANGE_ATTACK2 }, + { TASK_SET_SCHEDULE, (float)SCHED_GRUNT_WAIT_FACE_ENEMY },// don't run immediately after throwing grenade. +}; + +Schedule_t slGruntRangeAttack2[] = +{ + { + tlGruntRangeAttack2, + ARRAYSIZE ( tlGruntRangeAttack2 ), + 0, + 0, + "RangeAttack2" + }, +}; + + +//========================================================= +// repel +//========================================================= +Task_t tlGruntRepel[] = +{ + { TASK_STOP_MOVING, (float)0 }, + { TASK_FACE_IDEAL, (float)0 }, + { TASK_PLAY_SEQUENCE, (float)ACT_GLIDE }, +}; + +Schedule_t slGruntRepel[] = +{ + { + tlGruntRepel, + ARRAYSIZE ( tlGruntRepel ), + bits_COND_SEE_ENEMY | + bits_COND_NEW_ENEMY | + bits_COND_LIGHT_DAMAGE | + bits_COND_HEAVY_DAMAGE | + bits_COND_HEAR_SOUND, + + bits_SOUND_DANGER | + bits_SOUND_COMBAT | + bits_SOUND_PLAYER, + "Repel" + }, +}; + + +//========================================================= +// repel +//========================================================= +Task_t tlGruntRepelAttack[] = +{ + { TASK_STOP_MOVING, (float)0 }, + { TASK_FACE_ENEMY, (float)0 }, + { TASK_PLAY_SEQUENCE, (float)ACT_FLY }, +}; + +Schedule_t slGruntRepelAttack[] = +{ + { + tlGruntRepelAttack, + ARRAYSIZE ( tlGruntRepelAttack ), + bits_COND_ENEMY_OCCLUDED, + 0, + "Repel Attack" + }, +}; + +//========================================================= +// repel land +//========================================================= +Task_t tlGruntRepelLand[] = +{ + { TASK_STOP_MOVING, (float)0 }, + { TASK_PLAY_SEQUENCE, (float)ACT_LAND }, + { TASK_GET_PATH_TO_LASTPOSITION,(float)0 }, + { TASK_RUN_PATH, (float)0 }, + { TASK_WAIT_FOR_MOVEMENT, (float)0 }, + { TASK_CLEAR_LASTPOSITION, (float)0 }, +}; + +Schedule_t slGruntRepelLand[] = +{ + { + tlGruntRepelLand, + ARRAYSIZE ( tlGruntRepelLand ), + bits_COND_SEE_ENEMY | + bits_COND_NEW_ENEMY | + bits_COND_LIGHT_DAMAGE | + bits_COND_HEAVY_DAMAGE | + bits_COND_HEAR_SOUND, + + bits_SOUND_DANGER | + bits_SOUND_COMBAT | + bits_SOUND_PLAYER, + "Repel Land" + }, +}; + + +DEFINE_CUSTOM_SCHEDULES( CHGrunt ) +{ + slGruntFail, + slGruntCombatFail, + slGruntVictoryDance, + slGruntEstablishLineOfFire, + slGruntFoundEnemy, + slGruntCombatFace, + slGruntSignalSuppress, + slGruntSuppress, + slGruntWaitInCover, + slGruntTakeCover, + slGruntGrenadeCover, + slGruntTossGrenadeCover, + slGruntTakeCoverFromBestSound, + slGruntHideReload, + slGruntSweep, + slGruntRangeAttack1A, + slGruntRangeAttack1B, + slGruntRangeAttack2, + slGruntRepel, + slGruntRepelAttack, + slGruntRepelLand, +}; + +IMPLEMENT_CUSTOM_SCHEDULES( CHGrunt, CSquadMonster ); + +//========================================================= +// SetActivity +//========================================================= +void CHGrunt :: SetActivity ( Activity NewActivity ) +{ + int iSequence = ACTIVITY_NOT_AVAILABLE; + void *pmodel = GET_MODEL_PTR( ENT(pev) ); + + switch ( NewActivity) + { + case ACT_RANGE_ATTACK1: + // grunt is either shooting standing or shooting crouched + if (FBitSet( pev->weapons, HGRUNT_9MMAR)) + { + if ( m_fStanding ) + { + // get aimable sequence + iSequence = LookupSequence( "standing_mp5" ); + } + else + { + // get crouching shoot + iSequence = LookupSequence( "crouching_mp5" ); + } + } + else + { + if ( m_fStanding ) + { + // get aimable sequence + iSequence = LookupSequence( "standing_shotgun" ); + } + else + { + // get crouching shoot + iSequence = LookupSequence( "crouching_shotgun" ); + } + } + break; + case ACT_RANGE_ATTACK2: + // grunt is going to a secondary long range attack. This may be a thrown + // grenade or fired grenade, we must determine which and pick proper sequence + if ( pev->weapons & HGRUNT_HANDGRENADE ) + { + // get toss anim + iSequence = LookupSequence( "throwgrenade" ); + } + else + { + // get launch anim + iSequence = LookupSequence( "launchgrenade" ); + } + break; + case ACT_RUN: + if ( pev->health <= HGRUNT_LIMP_HEALTH ) + { + // limp! + iSequence = LookupActivity ( ACT_RUN_HURT ); + } + else + { + iSequence = LookupActivity ( NewActivity ); + } + break; + case ACT_WALK: + if ( pev->health <= HGRUNT_LIMP_HEALTH ) + { + // limp! + iSequence = LookupActivity ( ACT_WALK_HURT ); + } + else + { + iSequence = LookupActivity ( NewActivity ); + } + break; + case ACT_IDLE: + if ( m_MonsterState == MONSTERSTATE_COMBAT ) + { + NewActivity = ACT_IDLE_ANGRY; + } + iSequence = LookupActivity ( NewActivity ); + break; + default: + iSequence = LookupActivity ( NewActivity ); + break; + } + + m_Activity = NewActivity; // Go ahead and set this so it doesn't keep trying when the anim is not present + + // Set to the desired anim, or default anim if the desired is not present + if ( iSequence > ACTIVITY_NOT_AVAILABLE ) + { + if ( pev->sequence != iSequence || !m_fSequenceLoops ) + { + pev->frame = 0; + } + + pev->sequence = iSequence; // Set to the reset anim (if it's there) + ResetSequenceInfo( ); + SetYawSpeed(); + } + else + { + // Not available try to get default anim + ALERT ( at_console, "%s has no sequence for act:%d\n", STRING(pev->classname), NewActivity ); + pev->sequence = 0; // Set to the reset anim (if it's there) + } +} + +//========================================================= +// Get Schedule! +//========================================================= +Schedule_t *CHGrunt :: GetSchedule( void ) +{ + + // clear old sentence + m_iSentence = HGRUNT_SENT_NONE; + + // flying? If PRONE, barnacle has me. IF not, it's assumed I am rapelling. + if ( pev->movetype == MOVETYPE_FLY && m_MonsterState != MONSTERSTATE_PRONE ) + { + if (pev->flags & FL_ONGROUND) + { + // just landed + pev->movetype = MOVETYPE_STEP; + return GetScheduleOfType ( SCHED_GRUNT_REPEL_LAND ); + } + else + { + // repel down a rope, + if ( m_MonsterState == MONSTERSTATE_COMBAT ) + return GetScheduleOfType ( SCHED_GRUNT_REPEL_ATTACK ); + else + return GetScheduleOfType ( SCHED_GRUNT_REPEL ); + } + } + + // grunts place HIGH priority on running away from danger sounds. + if ( HasConditions(bits_COND_HEAR_SOUND) ) + { + CSound *pSound; + pSound = PBestSound(); + + ASSERT( pSound != NULL ); + if ( pSound) + { + if (pSound->m_iType & bits_SOUND_DANGER) + { + // dangerous sound nearby! + + //!!!KELLY - currently, this is the grunt's signal that a grenade has landed nearby, + // and the grunt should find cover from the blast + // good place for "SHIT!" or some other colorful verbal indicator of dismay. + // It's not safe to play a verbal order here "Scatter", etc cause + // this may only affect a single individual in a squad. + + if (FOkToSpeak()) + { + SENTENCEG_PlayRndSz( ENT(pev), "HG_GREN", HGRUNT_SENTENCE_VOLUME, GRUNT_ATTN, 0, m_voicePitch); + JustSpoke(); + } + return GetScheduleOfType( SCHED_TAKE_COVER_FROM_BEST_SOUND ); + } + /* + if (!HasConditions( bits_COND_SEE_ENEMY ) && ( pSound->m_iType & (bits_SOUND_PLAYER | bits_SOUND_COMBAT) )) + { + MakeIdealYaw( pSound->m_vecOrigin ); + } + */ + } + } + switch ( m_MonsterState ) + { + case MONSTERSTATE_COMBAT: + { +// dead enemy + if ( HasConditions( bits_COND_ENEMY_DEAD ) ) + { + // call base class, all code to handle dead enemies is centralized there. + return CBaseMonster :: GetSchedule(); + } + +// new enemy + if ( HasConditions(bits_COND_NEW_ENEMY) ) + { + if ( InSquad() ) + { + MySquadLeader()->m_fEnemyEluded = FALSE; + + if ( !IsLeader() ) + { + return GetScheduleOfType ( SCHED_TAKE_COVER_FROM_ENEMY ); + } + else + { + //!!!KELLY - the leader of a squad of grunts has just seen the player or a + // monster and has made it the squad's enemy. You + // can check pev->flags for FL_CLIENT to determine whether this is the player + // or a monster. He's going to immediately start + // firing, though. If you'd like, we can make an alternate "first sight" + // schedule where the leader plays a handsign anim + // that gives us enough time to hear a short sentence or spoken command + // before he starts pluggin away. + if (FOkToSpeak())// && RANDOM_LONG(0,1)) + { + if ((m_hEnemy != NULL) && m_hEnemy->IsPlayer()) + // player + SENTENCEG_PlayRndSz( ENT(pev), "HG_ALERT", HGRUNT_SENTENCE_VOLUME, GRUNT_ATTN, 0, m_voicePitch); + else if ((m_hEnemy != NULL) && + (m_hEnemy->Classify() != CLASS_PLAYER_ALLY) && + (m_hEnemy->Classify() != CLASS_HUMAN_PASSIVE) && + (m_hEnemy->Classify() != CLASS_MACHINE)) + // monster + SENTENCEG_PlayRndSz( ENT(pev), "HG_MONST", HGRUNT_SENTENCE_VOLUME, GRUNT_ATTN, 0, m_voicePitch); + + JustSpoke(); + } + + if ( HasConditions ( bits_COND_CAN_RANGE_ATTACK1 ) ) + { + return GetScheduleOfType ( SCHED_GRUNT_SUPPRESS ); + } + else + { + return GetScheduleOfType ( SCHED_GRUNT_ESTABLISH_LINE_OF_FIRE ); + } + } + } + } +// no ammo + else if ( HasConditions ( bits_COND_NO_AMMO_LOADED ) ) + { + //!!!KELLY - this individual just realized he's out of bullet ammo. + // He's going to try to find cover to run to and reload, but rarely, if + // none is available, he'll drop and reload in the open here. + return GetScheduleOfType ( SCHED_GRUNT_COVER_AND_RELOAD ); + } + +// damaged just a little + else if ( HasConditions( bits_COND_LIGHT_DAMAGE ) ) + { + // if hurt: + // 90% chance of taking cover + // 10% chance of flinch. + int iPercent = RANDOM_LONG(0,99); + + if ( iPercent <= 90 && m_hEnemy != NULL ) + { + // only try to take cover if we actually have an enemy! + + //!!!KELLY - this grunt was hit and is going to run to cover. + if (FOkToSpeak()) // && RANDOM_LONG(0,1)) + { + //SENTENCEG_PlayRndSz( ENT(pev), "HG_COVER", HGRUNT_SENTENCE_VOLUME, GRUNT_ATTN, 0, m_voicePitch); + m_iSentence = HGRUNT_SENT_COVER; + //JustSpoke(); + } + return GetScheduleOfType( SCHED_TAKE_COVER_FROM_ENEMY ); + } + else + { + return GetScheduleOfType( SCHED_SMALL_FLINCH ); + } + } +// can kick + else if ( HasConditions ( bits_COND_CAN_MELEE_ATTACK1 ) ) + { + return GetScheduleOfType ( SCHED_MELEE_ATTACK1 ); + } +// can grenade launch + + else if ( FBitSet( pev->weapons, HGRUNT_GRENADELAUNCHER) && HasConditions ( bits_COND_CAN_RANGE_ATTACK2 ) && OccupySlot( bits_SLOTS_HGRUNT_GRENADE ) ) + { + // shoot a grenade if you can + return GetScheduleOfType( SCHED_RANGE_ATTACK2 ); + } +// can shoot + else if ( HasConditions ( bits_COND_CAN_RANGE_ATTACK1 ) ) + { + if ( InSquad() ) + { + // if the enemy has eluded the squad and a squad member has just located the enemy + // and the enemy does not see the squad member, issue a call to the squad to waste a + // little time and give the player a chance to turn. + if ( MySquadLeader()->m_fEnemyEluded && !HasConditions ( bits_COND_ENEMY_FACING_ME ) ) + { + MySquadLeader()->m_fEnemyEluded = FALSE; + return GetScheduleOfType ( SCHED_GRUNT_FOUND_ENEMY ); + } + } + + if ( OccupySlot ( bits_SLOTS_HGRUNT_ENGAGE ) ) + { + // try to take an available ENGAGE slot + return GetScheduleOfType( SCHED_RANGE_ATTACK1 ); + } + else if ( HasConditions ( bits_COND_CAN_RANGE_ATTACK2 ) && OccupySlot( bits_SLOTS_HGRUNT_GRENADE ) ) + { + // throw a grenade if can and no engage slots are available + return GetScheduleOfType( SCHED_RANGE_ATTACK2 ); + } + else + { + // hide! + return GetScheduleOfType( SCHED_TAKE_COVER_FROM_ENEMY ); + } + } +// can't see enemy + else if ( HasConditions( bits_COND_ENEMY_OCCLUDED ) ) + { + if ( HasConditions( bits_COND_CAN_RANGE_ATTACK2 ) && OccupySlot( bits_SLOTS_HGRUNT_GRENADE ) ) + { + //!!!KELLY - this grunt is about to throw or fire a grenade at the player. Great place for "fire in the hole" "frag out" etc + if (FOkToSpeak()) + { + SENTENCEG_PlayRndSz( ENT(pev), "HG_THROW", HGRUNT_SENTENCE_VOLUME, GRUNT_ATTN, 0, m_voicePitch); + JustSpoke(); + } + return GetScheduleOfType( SCHED_RANGE_ATTACK2 ); + } + else if ( OccupySlot( bits_SLOTS_HGRUNT_ENGAGE ) ) + { + //!!!KELLY - grunt cannot see the enemy and has just decided to + // charge the enemy's position. + if (FOkToSpeak())// && RANDOM_LONG(0,1)) + { + //SENTENCEG_PlayRndSz( ENT(pev), "HG_CHARGE", HGRUNT_SENTENCE_VOLUME, GRUNT_ATTN, 0, m_voicePitch); + m_iSentence = HGRUNT_SENT_CHARGE; + //JustSpoke(); + } + + return GetScheduleOfType( SCHED_GRUNT_ESTABLISH_LINE_OF_FIRE ); + } + else + { + //!!!KELLY - grunt is going to stay put for a couple seconds to see if + // the enemy wanders back out into the open, or approaches the + // grunt's covered position. Good place for a taunt, I guess? + if (FOkToSpeak() && RANDOM_LONG(0,1)) + { + SENTENCEG_PlayRndSz( ENT(pev), "HG_TAUNT", HGRUNT_SENTENCE_VOLUME, GRUNT_ATTN, 0, m_voicePitch); + JustSpoke(); + } + return GetScheduleOfType( SCHED_STANDOFF ); + } + } + + if ( HasConditions( bits_COND_SEE_ENEMY ) && !HasConditions ( bits_COND_CAN_RANGE_ATTACK1 ) ) + { + return GetScheduleOfType ( SCHED_GRUNT_ESTABLISH_LINE_OF_FIRE ); + } + } + } + + // no special cases here, call the base class + return CSquadMonster :: GetSchedule(); +} + +//========================================================= +//========================================================= +Schedule_t* CHGrunt :: GetScheduleOfType ( int Type ) +{ + switch ( Type ) + { + case SCHED_TAKE_COVER_FROM_ENEMY: + { + if ( InSquad() ) + { + if ( g_iSkillLevel == SKILL_HARD && HasConditions( bits_COND_CAN_RANGE_ATTACK2 ) && OccupySlot( bits_SLOTS_HGRUNT_GRENADE ) ) + { + if (FOkToSpeak()) + { + SENTENCEG_PlayRndSz( ENT(pev), "HG_THROW", HGRUNT_SENTENCE_VOLUME, GRUNT_ATTN, 0, m_voicePitch); + JustSpoke(); + } + return slGruntTossGrenadeCover; + } + else + { + return &slGruntTakeCover[ 0 ]; + } + } + else + { + if ( RANDOM_LONG(0,1) ) + { + return &slGruntTakeCover[ 0 ]; + } + else + { + return &slGruntGrenadeCover[ 0 ]; + } + } + } + case SCHED_TAKE_COVER_FROM_BEST_SOUND: + { + return &slGruntTakeCoverFromBestSound[ 0 ]; + } + case SCHED_GRUNT_TAKECOVER_FAILED: + { + if ( HasConditions( bits_COND_CAN_RANGE_ATTACK1 ) && OccupySlot( bits_SLOTS_HGRUNT_ENGAGE ) ) + { + return GetScheduleOfType( SCHED_RANGE_ATTACK1 ); + } + + return GetScheduleOfType ( SCHED_FAIL ); + } + break; + case SCHED_GRUNT_ELOF_FAIL: + { + // human grunt is unable to move to a position that allows him to attack the enemy. + return GetScheduleOfType ( SCHED_TAKE_COVER_FROM_ENEMY ); + } + break; + case SCHED_GRUNT_ESTABLISH_LINE_OF_FIRE: + { + return &slGruntEstablishLineOfFire[ 0 ]; + } + break; + case SCHED_RANGE_ATTACK1: + { + // randomly stand or crouch + if (RANDOM_LONG(0,9) == 0) + m_fStanding = RANDOM_LONG(0,1); + + if (m_fStanding) + return &slGruntRangeAttack1B[ 0 ]; + else + return &slGruntRangeAttack1A[ 0 ]; + } + case SCHED_RANGE_ATTACK2: + { + return &slGruntRangeAttack2[ 0 ]; + } + case SCHED_COMBAT_FACE: + { + return &slGruntCombatFace[ 0 ]; + } + case SCHED_GRUNT_WAIT_FACE_ENEMY: + { + return &slGruntWaitInCover[ 0 ]; + } + case SCHED_GRUNT_SWEEP: + { + return &slGruntSweep[ 0 ]; + } + case SCHED_GRUNT_COVER_AND_RELOAD: + { + return &slGruntHideReload[ 0 ]; + } + case SCHED_GRUNT_FOUND_ENEMY: + { + return &slGruntFoundEnemy[ 0 ]; + } + case SCHED_VICTORY_DANCE: + { + if ( InSquad() ) + { + if ( !IsLeader() ) + { + return &slGruntFail[ 0 ]; + } + } + + return &slGruntVictoryDance[ 0 ]; + } + case SCHED_GRUNT_SUPPRESS: + { + if ( m_hEnemy->IsPlayer() && m_fFirstEncounter ) + { + m_fFirstEncounter = FALSE;// after first encounter, leader won't issue handsigns anymore when he has a new enemy + return &slGruntSignalSuppress[ 0 ]; + } + else + { + return &slGruntSuppress[ 0 ]; + } + } + case SCHED_FAIL: + { + if ( m_hEnemy != NULL ) + { + // grunt has an enemy, so pick a different default fail schedule most likely to help recover. + return &slGruntCombatFail[ 0 ]; + } + + return &slGruntFail[ 0 ]; + } + case SCHED_GRUNT_REPEL: + { + if (pev->velocity.z > -128) + pev->velocity.z -= 32; + return &slGruntRepel[ 0 ]; + } + case SCHED_GRUNT_REPEL_ATTACK: + { + if (pev->velocity.z > -128) + pev->velocity.z -= 32; + return &slGruntRepelAttack[ 0 ]; + } + case SCHED_GRUNT_REPEL_LAND: + { + return &slGruntRepelLand[ 0 ]; + } + default: + { + return CSquadMonster :: GetScheduleOfType ( Type ); + } + } +} + + +//========================================================= +// CHGruntRepel - when triggered, spawns a monster_human_grunt +// repelling down a line. +//========================================================= + +class CHGruntRepel : public CBaseMonster +{ +public: + void Spawn( void ); + void Precache( void ); + void EXPORT RepelUse ( CBaseEntity *pActivator, CBaseEntity *pCaller, USE_TYPE useType, float value ); + int m_iSpriteTexture; // Don't save, precache +}; + +LINK_ENTITY_TO_CLASS( monster_grunt_repel, CHGruntRepel ); + +void CHGruntRepel::Spawn( void ) +{ + Precache( ); + pev->solid = SOLID_NOT; + + SetUse( RepelUse ); +} + +void CHGruntRepel::Precache( void ) +{ + UTIL_PrecacheOther( "monster_human_grunt" ); + m_iSpriteTexture = PRECACHE_MODEL( "sprites/rope.spr" ); +} + +void CHGruntRepel::RepelUse ( CBaseEntity *pActivator, CBaseEntity *pCaller, USE_TYPE useType, float value ) +{ + TraceResult tr; + UTIL_TraceLine( pev->origin, pev->origin + Vector( 0, 0, -4096.0), dont_ignore_monsters, ENT(pev), &tr); + /* + if ( tr.pHit && Instance( tr.pHit )->pev->solid != SOLID_BSP) + return NULL; + */ + + CBaseEntity *pEntity = Create( "monster_human_grunt", pev->origin, pev->angles ); + CBaseMonster *pGrunt = pEntity->MyMonsterPointer( ); + pGrunt->pev->movetype = MOVETYPE_FLY; + pGrunt->pev->velocity = Vector( 0, 0, RANDOM_FLOAT( -196, -128 ) ); + pGrunt->SetActivity( ACT_GLIDE ); + // UNDONE: position? + pGrunt->m_vecLastPosition = tr.vecEndPos; + + CBeam *pBeam = CBeam::BeamCreate( "sprites/rope.spr", 10 ); + pBeam->PointEntInit( pev->origin + Vector(0,0,112), pGrunt->entindex() ); + pBeam->SetFlags( BEAM_FSOLID ); + pBeam->SetColor( 255, 255, 255 ); + pBeam->SetThink( SUB_Remove ); + pBeam->pev->nextthink = gpGlobals->time + -4096.0 * tr.flFraction / pGrunt->pev->velocity.z + 0.5; + + UTIL_Remove( this ); +} + + + +//========================================================= +// DEAD HGRUNT PROP +//========================================================= +class CDeadHGrunt : public CBaseMonster +{ +public: + void Spawn( void ); + int Classify ( void ) { return CLASS_HUMAN_MILITARY; } + + void KeyValue( KeyValueData *pkvd ); + + int m_iPose;// which sequence to display -- temporary, don't need to save + static char *m_szPoses[3]; +}; + +char *CDeadHGrunt::m_szPoses[] = { "deadstomach", "deadside", "deadsitting" }; + +void CDeadHGrunt::KeyValue( KeyValueData *pkvd ) +{ + if (FStrEq(pkvd->szKeyName, "pose")) + { + m_iPose = atoi(pkvd->szValue); + pkvd->fHandled = TRUE; + } + else + CBaseMonster::KeyValue( pkvd ); +} + +LINK_ENTITY_TO_CLASS( monster_hgrunt_dead, CDeadHGrunt ); + +//========================================================= +// ********** DeadHGrunt SPAWN ********** +//========================================================= +void CDeadHGrunt :: Spawn( void ) +{ + PRECACHE_MODEL("models/hgrunt.mdl"); + SET_MODEL(ENT(pev), "models/hgrunt.mdl"); + + pev->effects = 0; + pev->yaw_speed = 8; + pev->sequence = 0; + m_bloodColor = BLOOD_COLOR_RED; + + pev->sequence = LookupSequence( m_szPoses[m_iPose] ); + + if (pev->sequence == -1) + { + ALERT ( at_console, "Dead hgrunt with bad pose\n" ); + } + + // Corpses have less health + pev->health = 8; + + // map old bodies onto new bodies + switch( pev->body ) + { + case 0: // Grunt with Gun + pev->body = 0; + pev->skin = 0; + SetBodygroup( HEAD_GROUP, HEAD_GRUNT ); + SetBodygroup( GUN_GROUP, GUN_MP5 ); + break; + case 1: // Commander with Gun + pev->body = 0; + pev->skin = 0; + SetBodygroup( HEAD_GROUP, HEAD_COMMANDER ); + SetBodygroup( GUN_GROUP, GUN_MP5 ); + break; + case 2: // Grunt no Gun + pev->body = 0; + pev->skin = 0; + SetBodygroup( HEAD_GROUP, HEAD_GRUNT ); + SetBodygroup( GUN_GROUP, GUN_NONE ); + break; + case 3: // Commander no Gun + pev->body = 0; + pev->skin = 0; + SetBodygroup( HEAD_GROUP, HEAD_COMMANDER ); + SetBodygroup( GUN_GROUP, GUN_NONE ); + break; + } + + MonsterInitDead(); +} diff --git a/dlls/hl.def b/dlls/hl.def new file mode 100644 index 00000000..c009191a --- /dev/null +++ b/dlls/hl.def @@ -0,0 +1,5 @@ +LIBRARY hl +EXPORTS + GiveFnptrsToDll @1 +SECTIONS + .data READ WRITE diff --git a/spirit/spirit.dsp b/dlls/hl.dsp similarity index 74% rename from spirit/spirit.dsp rename to dlls/hl.dsp index f80f5bc8..c3c14250 100644 --- a/spirit/spirit.dsp +++ b/dlls/hl.dsp @@ -1,35 +1,35 @@ -# Microsoft Developer Studio Project File - Name="spirit" - Package Owner=<4> +# Microsoft Developer Studio Project File - Name="hl" - Package Owner=<4> # Microsoft Developer Studio Generated Build File, Format Version 6.00 # ** DO NOT EDIT ** # TARGTYPE "Win32 (x86) Dynamic-Link Library" 0x0102 -CFG=spirit - Win32 Release +CFG=hl - Win32 Release !MESSAGE This is not a valid makefile. To build this project using NMAKE, !MESSAGE use the Export Makefile command and run !MESSAGE -!MESSAGE NMAKE /f "spirit.mak". +!MESSAGE NMAKE /f "hl.mak". !MESSAGE !MESSAGE You can specify a configuration when running NMAKE !MESSAGE by defining the macro CFG on the command line. For example: !MESSAGE -!MESSAGE NMAKE /f "spirit.mak" CFG="spirit - Win32 Release" +!MESSAGE NMAKE /f "hl.mak" CFG="hl - Win32 Release" !MESSAGE !MESSAGE Possible choices for configuration are: !MESSAGE -!MESSAGE "spirit - Win32 Release" (based on "Win32 (x86) Dynamic-Link Library") -!MESSAGE "spirit - Win32 Debug" (based on "Win32 (x86) Dynamic-Link Library") +!MESSAGE "hl - Win32 Release" (based on "Win32 (x86) Dynamic-Link Library") +!MESSAGE "hl - Win32 Debug" (based on "Win32 (x86) Dynamic-Link Library") !MESSAGE # Begin Project # PROP AllowPerConfigDependencies 0 -# PROP Scc_ProjName ""$/GoldSrc/dlls", ELEBAAAA" -# PROP Scc_LocalPath "." +# PROP Scc_ProjName "" +# PROP Scc_LocalPath "" CPP=cl.exe MTL=midl.exe RSC=rc.exe -!IF "$(CFG)" == "spirit - Win32 Release" +!IF "$(CFG)" == "hl - Win32 Release" # PROP BASE Use_MFC 0 # PROP BASE Use_Debug_Libraries 0 @@ -38,12 +38,12 @@ RSC=rc.exe # PROP BASE Target_Dir "" # PROP Use_MFC 0 # PROP Use_Debug_Libraries 0 -# PROP Output_Dir "..\temp\spirit\!release" -# PROP Intermediate_Dir "..\temp\spirit\!release" +# PROP Output_Dir "..\temp\hl\!release" +# PROP Intermediate_Dir "..\temp\hl\!release" # PROP Ignore_Export_Lib 1 # PROP Target_Dir "" # ADD BASE CPP /nologo /MT /W3 /GX /O2 /D "WIN32" /D "NDEBUG" /D "_WINDOWS" /YX /c -# ADD CPP /nologo /G5 /MD /W3 /GX /O2 /I "..\spirit" /I "..\common" /I "..\game_shared" /D "NDEBUG" /D "WIN32" /D "_WINDOWS" /FD /c +# ADD CPP /nologo /MD /W3 /GX /O2 /I "..\dlls" /I "..\common" /I "..\game_shared" /I "..\\" /D "WIN32" /D "NDEBUG" /D "_WINDOWS" /FD /c # SUBTRACT CPP /Fr /YX # ADD BASE MTL /nologo /D "NDEBUG" /win32 # ADD MTL /nologo /D "NDEBUG" /mktyplib203 /win32 @@ -51,58 +51,55 @@ RSC=rc.exe # ADD RSC /l 0x409 /d "NDEBUG" BSC32=bscmake.exe # ADD BASE BSC32 /nologo -# ADD BSC32 /nologo /o"..\temp\spirit\!release/server.bsc" +# ADD BSC32 /nologo LINK32=link.exe # ADD BASE LINK32 kernel32.lib user32.lib gdi32.lib winspool.lib comdlg32.lib advapi32.lib shell32.lib ole32.lib oleaut32.lib uuid.lib odbc32.lib odbccp32.lib /nologo /subsystem:windows /dll /machine:I386 -# ADD LINK32 msvcrt.lib /nologo /subsystem:windows /dll /pdb:none /machine:I386 /nodefaultlib:"libcmt.lib" /def:".\spirit.def" /out:"..\temp\spirit\!release/server.dll" +# ADD LINK32 msvcrt.lib /nologo /subsystem:windows /dll /pdb:none /machine:I386 /nodefaultlib:"libc.lib" /def:".\hl.def" # SUBTRACT LINK32 /profile /map /debug # Begin Custom Build -TargetDir=\Xash3D\src_main\temp\spirit\!release -InputPath=\Xash3D\src_main\temp\spirit\!release\server.dll +TargetDir=\Xash3D\src_main\temp\hl\!release +InputPath=\Xash3D\src_main\temp\hl\!release\hl.dll SOURCE="$(InputPath)" -"D:\Xash3D\spirit\bin\server.dll" : $(SOURCE) "$(INTDIR)" "$(OUTDIR)" - copy $(TargetDir)\server.dll "D:\Xash3D\spirit\bin\server.dll" +"D:\Xash3D\valve\bin\server.dll" : $(SOURCE) "$(INTDIR)" "$(OUTDIR)" + copy $(TargetDir)\hl.dll "D:\Xash3D\valve\bin\server.dll" # End Custom Build -!ELSEIF "$(CFG)" == "spirit - Win32 Debug" +!ELSEIF "$(CFG)" == "hl - Win32 Debug" # PROP BASE Use_MFC 0 -# PROP BASE Use_Debug_Libraries 0 -# PROP BASE Output_Dir "spirit___Win32_Debug" -# PROP BASE Intermediate_Dir "spirit___Win32_Debug" -# PROP BASE Ignore_Export_Lib 0 +# PROP BASE Use_Debug_Libraries 1 +# PROP BASE Output_Dir ".\hl___Win" +# PROP BASE Intermediate_Dir ".\hl___Win" # PROP BASE Target_Dir "" # PROP Use_MFC 0 -# PROP Use_Debug_Libraries 0 -# PROP Output_Dir "..\temp\spirit\!debug" -# PROP Intermediate_Dir "..\temp\spirit\!debug" +# PROP Use_Debug_Libraries 1 +# PROP Output_Dir "..\temp\hl\!debug" +# PROP Intermediate_Dir "..\temp\hl\!debug" # PROP Ignore_Export_Lib 1 # PROP Target_Dir "" -# ADD BASE CPP /nologo /G5 /MT /W3 /O1 /I "..\dlls" /I "..\engine" /I "..\common" /I "..\game_shared" /D "NDEBUG" /D "WIN32" /D "_WINDOWS" /D "QUIVER" /D "VOXEL" /D "QUAKE2" /D "VALVE_DLL" /YX /FD /c -# SUBTRACT BASE CPP /Fr -# ADD CPP /nologo /MDd /W3 /Gm /GX /ZI /Od /I "..\spirit" /I "..\common" /I "..\game_shared" /D "DEBUG" /D "WIN32" /D "_WINDOWS" /FR /FD /c -# SUBTRACT CPP /WX /YX -# ADD BASE MTL /nologo /D "NDEBUG" /mktyplib203 /win32 -# ADD MTL /nologo /D "NDEBUG" /mktyplib203 /win32 -# ADD BASE RSC /l 0x409 /d "NDEBUG" -# ADD RSC /l 0x409 /d "NDEBUG" +# ADD BASE CPP /nologo /MTd /W3 /Gm /GX /Zi /Od /D "WIN32" /D "_DEBUG" /D "_WINDOWS" /YX /c +# ADD CPP /nologo /MDd /W3 /Gm /GX /ZI /Od /I "..\dlls" /I "..\common" /I "..\game_shared" /I "..\\" /D "WIN32" /D "_DEBUG" /D "_WINDOWS" /FR /FD /c +# SUBTRACT CPP /YX +# ADD BASE MTL /nologo /D "_DEBUG" /win32 +# ADD MTL /nologo /D "_DEBUG" /mktyplib203 /win32 +# ADD BASE RSC /l 0x409 /d "_DEBUG" +# ADD RSC /l 0x409 /d "_DEBUG" BSC32=bscmake.exe -# ADD BASE BSC32 /nologo /o".\Release/server.bsc" -# ADD BSC32 /nologo /o"..\temp\spirit\!debug/server.bsc" +# ADD BASE BSC32 /nologo +# ADD BSC32 /nologo LINK32=link.exe -# ADD BASE LINK32 kernel32.lib user32.lib gdi32.lib winspool.lib comdlg32.lib advapi32.lib shell32.lib ole32.lib oleaut32.lib uuid.lib /nologo /subsystem:windows /dll /machine:I386 /def:".\spirit.def" /out:".\Release/server.dll" -# SUBTRACT BASE LINK32 /profile /map /debug -# ADD LINK32 msvcrtd.lib /nologo /subsystem:windows /dll /incremental:yes /debug /machine:I386 /nodefaultlib:"libcmt.lib" /def:".\spirit.def" /out:"..\temp\spirit\!debug/server.dll" /pdbtype:sept -# SUBTRACT LINK32 /profile /map +# ADD BASE LINK32 kernel32.lib user32.lib gdi32.lib winspool.lib comdlg32.lib advapi32.lib shell32.lib ole32.lib oleaut32.lib uuid.lib odbc32.lib odbccp32.lib /nologo /subsystem:windows /dll /debug /machine:I386 +# ADD LINK32 msvcrtd.lib /nologo /subsystem:windows /dll /debug /machine:I386 /nodefaultlib:"libc.lib" /def:".\hl.def" /pdbtype:sept +# SUBTRACT LINK32 /profile # Begin Custom Build -TargetDir=\Xash3D\src_main\temp\spirit\!debug -InputPath=\Xash3D\src_main\temp\spirit\!debug\server.dll +TargetDir=\Xash3D\src_main\temp\hl\!debug +InputPath=\Xash3D\src_main\temp\hl\!debug\hl.dll SOURCE="$(InputPath)" -"D:\Xash3D\spirit\bin\server.dll" : $(SOURCE) "$(INTDIR)" "$(OUTDIR)" - copy $(TargetDir)\server.dll "D:\Xash3D\spirit\bin\server.dll" +"D:\Xash3D\valve\bin\server.dll" : $(SOURCE) "$(INTDIR)" "$(OUTDIR)" + copy $(TargetDir)\hl.dll "D:\Xash3D\valve\bin\server.dll" # End Custom Build @@ -110,8 +107,8 @@ SOURCE="$(InputPath)" # Begin Target -# Name "spirit - Win32 Release" -# Name "spirit - Win32 Debug" +# Name "hl - Win32 Release" +# Name "hl - Win32 Debug" # Begin Group "Source Files" # PROP Default_Filter "cpp;c;cxx;rc;def;r;odl;idl;hpj;bat;for;f90" @@ -129,10 +126,6 @@ SOURCE=.\airtank.cpp # End Source File # Begin Source File -SOURCE=.\alias.cpp -# End Source File -# Begin Source File - SOURCE=.\animating.cpp # End Source File # Begin Source File @@ -337,10 +330,6 @@ SOURCE=.\lights.cpp # End Source File # Begin Source File -SOURCE=.\locus.cpp -# End Source File -# Begin Source File - SOURCE=.\maprules.cpp # End Source File # Begin Source File @@ -361,10 +350,6 @@ SOURCE=.\mortar.cpp # End Source File # Begin Source File -SOURCE=.\movewith.cpp -# End Source File -# Begin Source File - SOURCE=.\mp5.cpp # End Source File # Begin Source File @@ -401,6 +386,14 @@ SOURCE=.\player.cpp # End Source File # Begin Source File +SOURCE=..\game_shared\pm_debug.cpp +# End Source File +# Begin Source File + +SOURCE=..\game_shared\pm_math.cpp +# End Source File +# Begin Source File + SOURCE=..\game_shared\pm_shared.cpp # End Source File # Begin Source File @@ -605,10 +598,6 @@ SOURCE=.\items.h # End Source File # Begin Source File -SOURCE=.\locus.h -# End Source File -# Begin Source File - SOURCE=.\monsterevent.h # End Source File # Begin Source File @@ -617,10 +606,6 @@ SOURCE=.\monsters.h # End Source File # Begin Source File -SOURCE=.\movewith.h -# End Source File -# Begin Source File - SOURCE=.\nodes.h # End Source File # Begin Source File @@ -705,7 +690,7 @@ SOURCE=.\util.h # End Source File # Begin Source File -SOURCE=..\game_shared\vector.h +SOURCE=.\vector.h # End Source File # Begin Source File diff --git a/dlls/hl.plg b/dlls/hl.plg new file mode 100644 index 00000000..12eccd04 --- /dev/null +++ b/dlls/hl.plg @@ -0,0 +1,25 @@ + + +
+

Build Log

+

+--------------------Configuration: hl - Win32 Debug-------------------- +

+

Command Lines

+Creating temporary file "C:\DOCUME~1\ÌÈØÀ\LOCALS~1\Temp\RSP1491.bat" with contents +[ +@echo off +copy \Xash3D\src_main\temp\hl\!debug\hl.dll "D:\Xash3D\valve\bin\server.dll" +] +Creating command line ""C:\DOCUME~1\ÌÈØÀ\LOCALS~1\Temp\RSP1491.bat"" +

Output Window

+Performing Custom Build Step on \Xash3D\src_main\temp\hl\!debug\hl.dll +‘ª®¯¨à®¢ ­® ä ©«®¢: 1. + + + +

Results

+hl.dll - 0 error(s), 0 warning(s) +
+ + diff --git a/spirit/hornet.cpp b/dlls/hornet.cpp similarity index 88% rename from spirit/hornet.cpp rename to dlls/hornet.cpp index 23f2908b..288a7dbe 100644 --- a/spirit/hornet.cpp +++ b/dlls/hornet.cpp @@ -93,20 +93,13 @@ void CHornet :: Spawn( void ) SET_MODEL(ENT( pev ), "models/hornet.mdl"); UTIL_SetSize( pev, Vector( -4, -4, -4 ), Vector( 4, 4, 4 ) ); - SetTouch(&CHornet :: DieTouch ); - SetThink(&CHornet :: StartTrack ); + SetTouch( DieTouch ); + SetThink( StartTrack ); edict_t *pSoundEnt = pev->owner; if ( !pSoundEnt ) pSoundEnt = edict(); - switch (RANDOM_LONG(0,2)) - { - case 0: EMIT_SOUND( pSoundEnt, CHAN_WEAPON, "agrunt/ag_fire1.wav", 1, ATTN_NORM); break; - case 1: EMIT_SOUND( pSoundEnt, CHAN_WEAPON, "agrunt/ag_fire2.wav", 1, ATTN_NORM); break; - case 2: EMIT_SOUND( pSoundEnt, CHAN_WEAPON, "agrunt/ag_fire3.wav", 1, ATTN_NORM); break; - } - if ( !FNullEnt(pev->owner) && (pev->owner->v.flags & FL_CLIENT) ) { pev->dmg = gSkillData.plrDmgHornet; @@ -117,7 +110,7 @@ void CHornet :: Spawn( void ) pev->dmg = gSkillData.monDmgHornet; } - SetNextThink( 0.1 ); + pev->nextthink = gpGlobals->time + 0.1; ResetSequenceInfo( ); } @@ -160,7 +153,7 @@ int CHornet::IRelationship ( CBaseEntity *pTarget ) //========================================================= int CHornet::Classify ( void ) { - if (m_iClass) return m_iClass; + if ( pev->owner && pev->owner->v.flags & FL_CLIENT) { return CLASS_PLAYER_BIOWEAPON; @@ -176,10 +169,10 @@ void CHornet :: StartTrack ( void ) { IgniteTrail(); - SetTouch(&CHornet :: TrackTouch ); - SetThink(&CHornet :: TrackTarget ); + SetTouch( TrackTouch ); + SetThink( TrackTarget ); - SetNextThink( 0.1 ); + pev->nextthink = gpGlobals->time + 0.1; } //========================================================= @@ -189,10 +182,10 @@ void CHornet :: StartDart ( void ) { IgniteTrail(); - SetTouch(&CHornet :: DartTouch ); + SetTouch( DartTouch ); - SetThink(&CHornet :: SUB_Remove ); - SetNextThink( 4 ); + SetThink( SUB_Remove ); + pev->nextthink = gpGlobals->time + 4; } void CHornet::IgniteTrail( void ) @@ -224,7 +217,7 @@ old colors */ // trail - MESSAGE_BEGIN( MSG_BROADCAST, gmsgTempEntity ); + MESSAGE_BEGIN( MSG_BROADCAST, SVC_TEMPENTITY ); WRITE_BYTE( TE_BEAMFOLLOW ); WRITE_SHORT( entindex() ); // entity WRITE_SHORT( iHornetTrail ); // model @@ -264,8 +257,8 @@ void CHornet :: TrackTarget ( void ) if (gpGlobals->time > m_flStopAttack) { SetTouch( NULL ); - SetThink(&CHornet :: SUB_Remove ); - SetNextThink( 0.1 ); + SetThink( SUB_Remove ); + pev->nextthink = gpGlobals->time + 0.1; return; } @@ -325,11 +318,11 @@ void CHornet :: TrackTarget ( void ) { case HORNET_TYPE_RED: pev->velocity = pev->velocity * ( m_flFlySpeed * flDelta );// scale the dir by the ( speed * width of turn ) - SetNextThink( RANDOM_FLOAT( 0.1, 0.3 ) ); + pev->nextthink = gpGlobals->time + RANDOM_FLOAT( 0.1, 0.3 ); break; case HORNET_TYPE_ORANGE: pev->velocity = pev->velocity * m_flFlySpeed;// do not have to slow down to turn. - SetNextThink( 0.1 );// fixed think time + pev->nextthink = gpGlobals->time + 0.1;// fixed think time break; } @@ -343,7 +336,7 @@ void CHornet :: TrackTarget ( void ) { if ( flDelta >= 0.4 && ( pev->origin - m_vecEnemyLKP ).Length() <= 300 ) { - MESSAGE_BEGIN( MSG_PVS, gmsgTempEntity, pev->origin ); + MESSAGE_BEGIN( MSG_PVS, SVC_TEMPENTITY, pev->origin ); WRITE_BYTE( TE_SPRITE ); WRITE_COORD( pev->origin.x); // pos WRITE_COORD( pev->origin.y); @@ -361,7 +354,7 @@ void CHornet :: TrackTarget ( void ) case 2: EMIT_SOUND( ENT(pev), CHAN_VOICE, "hornet/ag_buzz3.wav", HORNET_BUZZ_VOLUME, ATTN_NORM); break; } pev->velocity = pev->velocity * 2; - SetNextThink( 1.0 ); + pev->nextthink = gpGlobals->time + 1.0; // don't attack again m_flStopAttack = gpGlobals->time; } @@ -420,7 +413,7 @@ void CHornet::DieTouch ( CBaseEntity *pOther ) pev->modelindex = 0;// so will disappear for the 0.1 secs we wait until NEXTTHINK gets rid pev->solid = SOLID_NOT; - SetThink(&CHornet:: SUB_Remove ); - SetNextThink( 1 );// stick around long enough for the sound to finish! + SetThink ( SUB_Remove ); + pev->nextthink = gpGlobals->time + 1;// stick around long enough for the sound to finish! } diff --git a/spirit/hornet.h b/dlls/hornet.h similarity index 97% rename from spirit/hornet.h rename to dlls/hornet.h index 98d1710f..f069c3dd 100644 --- a/spirit/hornet.h +++ b/dlls/hornet.h @@ -1,58 +1,58 @@ -/*** -* -* Copyright (c) 1996-2002, 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. -* -****/ -//========================================================= -// Hornets -//========================================================= - -//========================================================= -// Hornet Defines -//========================================================= -#define HORNET_TYPE_RED 0 -#define HORNET_TYPE_ORANGE 1 -#define HORNET_RED_SPEED (float)600 -#define HORNET_ORANGE_SPEED (float)800 -#define HORNET_BUZZ_VOLUME (float)0.8 - -extern int iHornetPuff; - -//========================================================= -// Hornet - this is the projectile that the Alien Grunt fires. -//========================================================= -class CHornet : public CBaseMonster -{ -public: - void Spawn( void ); - void Precache( void ); - int Classify ( void ); - int IRelationship ( CBaseEntity *pTarget ); - virtual int Save( CSave &save ); - virtual int Restore( CRestore &restore ); - static TYPEDESCRIPTION m_SaveData[]; - - void IgniteTrail( void ); - void EXPORT StartTrack ( void ); - void EXPORT StartDart ( void ); - void EXPORT TrackTarget ( void ); - void EXPORT TrackTouch ( CBaseEntity *pOther ); - void EXPORT DartTouch( CBaseEntity *pOther ); - void EXPORT DieTouch ( CBaseEntity *pOther ); - - int TakeDamage( entvars_t *pevInflictor, entvars_t *pevAttacker, float flDamage, int bitsDamageType ); - - float m_flStopAttack; - int m_iHornetType; - float m_flFlySpeed; -}; - +/*** +* +* Copyright (c) 1996-2002, 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. +* +****/ +//========================================================= +// Hornets +//========================================================= + +//========================================================= +// Hornet Defines +//========================================================= +#define HORNET_TYPE_RED 0 +#define HORNET_TYPE_ORANGE 1 +#define HORNET_RED_SPEED (float)600 +#define HORNET_ORANGE_SPEED (float)800 +#define HORNET_BUZZ_VOLUME (float)0.8 + +extern int iHornetPuff; + +//========================================================= +// Hornet - this is the projectile that the Alien Grunt fires. +//========================================================= +class CHornet : public CBaseMonster +{ +public: + void Spawn( void ); + void Precache( void ); + int Classify ( void ); + int IRelationship ( CBaseEntity *pTarget ); + virtual int Save( CSave &save ); + virtual int Restore( CRestore &restore ); + static TYPEDESCRIPTION m_SaveData[]; + + void IgniteTrail( void ); + void EXPORT StartTrack ( void ); + void EXPORT StartDart ( void ); + void EXPORT TrackTarget ( void ); + void EXPORT TrackTouch ( CBaseEntity *pOther ); + void EXPORT DartTouch( CBaseEntity *pOther ); + void EXPORT DieTouch ( CBaseEntity *pOther ); + + int TakeDamage( entvars_t *pevInflictor, entvars_t *pevAttacker, float flDamage, int bitsDamageType ); + + float m_flStopAttack; + int m_iHornetType; + float m_flFlySpeed; +}; + diff --git a/spirit/hornetgun.cpp b/dlls/hornetgun.cpp similarity index 68% rename from spirit/hornetgun.cpp rename to dlls/hornetgun.cpp index 2303e2d1..76b48894 100644 --- a/spirit/hornetgun.cpp +++ b/dlls/hornetgun.cpp @@ -12,6 +12,7 @@ * without written permission from Valve LLC. * ****/ +#if !defined( OEM_BUILD ) && !defined( HLDEMO_BUILD ) #include "extdll.h" #include "util.h" @@ -39,34 +40,23 @@ enum firemode_e FIREMODE_FAST }; -class CHgun : public CBasePlayerWeapon -{ -public: - void Spawn( void ); - void Precache( void ); - int GetItemInfo(ItemInfo *p); - void PrimaryAttack( void ); - void SecondaryAttack( void ); - BOOL Deploy( void ); - BOOL IsUseable( void ){ return TRUE; }; - void Holster( ); - void Reload( void ); - void WeaponIdle( void ); - float m_flNextAnimTime; - - float m_flRechargeTime; - int m_iFirePhase;// don't save me. -}; LINK_ENTITY_TO_CLASS( weapon_hornetgun, CHgun ); +BOOL CHgun::IsUseable( void ) +{ + return TRUE; +} + void CHgun::Spawn( ) { Precache( ); + m_iId = WEAPON_HORNETGUN; SET_MODEL(ENT(pev), "models/w_hgun.mdl"); m_iDefaultAmmo = HIVEHAND_DEFAULT_GIVE; m_iFirePhase = 0; + FallInit();// get ready to fall down. } @@ -77,9 +67,32 @@ void CHgun::Precache( void ) PRECACHE_MODEL("models/w_hgun.mdl"); PRECACHE_MODEL("models/p_hgun.mdl"); + m_usHornetFire = PRECACHE_EVENT ( 1, "events/firehornet.sc" ); + UTIL_PrecacheOther("hornet"); } +int CHgun::AddToPlayer( CBasePlayer *pPlayer ) +{ + if ( CBasePlayerWeapon::AddToPlayer( pPlayer ) ) + { + +#ifndef CLIENT_DLL + if ( g_pGameRules->IsMultiplayer() ) + { + // in multiplayer, all hivehands come full. + pPlayer->m_rgAmmo[ PrimaryAmmoIndex() ] = HORNET_MAX_CARRY; + } +#endif + + MESSAGE_BEGIN( MSG_ONE, gmsgWeapPickup, NULL, pPlayer->pev ); + WRITE_BYTE( m_iId ); + MESSAGE_END(); + return TRUE; + } + return FALSE; +} + int CHgun::GetItemInfo(ItemInfo *p) { p->pszName = STRING(pev->classname); @@ -100,16 +113,19 @@ int CHgun::GetItemInfo(ItemInfo *p) BOOL CHgun::Deploy( ) { - return DefaultDeploy( "models/v_hgun.mdl", "models/p_hgun.mdl", HGUN_UP, "hive", 0.9 ); + return DefaultDeploy( "models/v_hgun.mdl", "models/p_hgun.mdl", HGUN_UP, "hive" ); } -void CHgun::Holster( ) +void CHgun::Holster( int skiplocal /* = 0 */ ) { - m_pPlayer->m_flNextAttack = UTIL_WeaponTimeBase() + 1.4; + m_pPlayer->m_flNextAttack = UTIL_WeaponTimeBase() + 0.5; SendWeaponAnim( HGUN_DOWN ); + //!!!HACKHACK - can't select hornetgun if it's empty! no way to get ammo for it, either. if ( !m_pPlayer->m_rgAmmo[ PrimaryAmmoIndex() ] ) + { m_pPlayer->m_rgAmmo[ PrimaryAmmoIndex() ] = 1; + } } @@ -117,18 +133,36 @@ void CHgun::PrimaryAttack() { Reload( ); - if (m_pPlayer->m_rgAmmo[m_iPrimaryAmmoType] <= 0) return; + if (m_pPlayer->m_rgAmmo[m_iPrimaryAmmoType] <= 0) + { + return; + } + +#ifndef CLIENT_DLL UTIL_MakeVectors( m_pPlayer->pev->v_angle ); CBaseEntity *pHornet = CBaseEntity::Create( "hornet", m_pPlayer->GetGunPosition( ) + gpGlobals->v_forward * 16 + gpGlobals->v_right * 8 + gpGlobals->v_up * -12, m_pPlayer->pev->v_angle, m_pPlayer->edict() ); pHornet->pev->velocity = gpGlobals->v_forward * 300; m_flRechargeTime = gpGlobals->time + 0.5; +#endif + m_pPlayer->m_rgAmmo[m_iPrimaryAmmoType]--; + + m_pPlayer->m_iWeaponVolume = QUIET_GUN_VOLUME; m_pPlayer->m_iWeaponFlash = DIM_GUN_FLASH; - SendWeaponAnim( HGUN_SHOOT ); + int flags; +#if defined( CLIENT_WEAPONS ) + flags = FEV_NOTHOST; +#else + flags = 0; +#endif + + PLAYBACK_EVENT_FULL( flags, m_pPlayer->edict(), m_usHornetFire, 0.0, (float *)&g_vecZero, (float *)&g_vecZero, 0.0, 0.0, FIREMODE_TRACK, 0, 0, 0 ); + + // player "shoot" animation m_pPlayer->SetAnimation( PLAYER_ATTACK1 ); @@ -136,8 +170,11 @@ void CHgun::PrimaryAttack() m_flNextPrimaryAttack = m_flNextPrimaryAttack + 0.25; if (m_flNextPrimaryAttack < UTIL_WeaponTimeBase() ) + { m_flNextPrimaryAttack = UTIL_WeaponTimeBase() + 0.25; - m_flTimeWeaponIdle = UTIL_WeaponTimeBase() + RANDOM_LONG( 10, 15 ); + } + + m_flTimeWeaponIdle = UTIL_WeaponTimeBase() + UTIL_SharedRandomFloat( m_pPlayer->random_seed, 10, 15 ); } @@ -146,12 +183,18 @@ void CHgun::SecondaryAttack( void ) { Reload(); - if (m_pPlayer->m_rgAmmo[m_iPrimaryAmmoType] <= 0) return; + if (m_pPlayer->m_rgAmmo[m_iPrimaryAmmoType] <= 0) + { + return; + } + //Wouldn't be a bad idea to completely predict these, since they fly so fast... +#ifndef CLIENT_DLL CBaseEntity *pHornet; Vector vecSrc; UTIL_MakeVectors( m_pPlayer->pev->v_angle ); + vecSrc = m_pPlayer->GetGunPosition( ) + gpGlobals->v_forward * 16 + gpGlobals->v_right * 8 + gpGlobals->v_up * -12; m_iFirePhase++; @@ -192,27 +235,38 @@ void CHgun::SecondaryAttack( void ) pHornet->pev->velocity = gpGlobals->v_forward * 1200; pHornet->pev->angles = UTIL_VecToAngles( pHornet->pev->velocity ); - pHornet->SetThink(& CHornet::StartDart ); + pHornet->SetThink( CHornet::StartDart ); + m_flRechargeTime = gpGlobals->time + 0.5; +#endif + + int flags; +#if defined( CLIENT_WEAPONS ) + flags = FEV_NOTHOST; +#else + flags = 0; +#endif + + PLAYBACK_EVENT_FULL( flags, m_pPlayer->edict(), m_usHornetFire, 0.0, (float *)&g_vecZero, (float *)&g_vecZero, 0.0, 0.0, FIREMODE_FAST, 0, 0, 0 ); + m_pPlayer->m_rgAmmo[m_iPrimaryAmmoType]--; m_pPlayer->m_iWeaponVolume = NORMAL_GUN_VOLUME; m_pPlayer->m_iWeaponFlash = DIM_GUN_FLASH; - SendWeaponAnim( HGUN_SHOOT ); - - // player "shoot" animation + // player "shoot" animation m_pPlayer->SetAnimation( PLAYER_ATTACK1 ); m_flNextPrimaryAttack = m_flNextSecondaryAttack = UTIL_WeaponTimeBase() + 0.1; - m_flTimeWeaponIdle = UTIL_WeaponTimeBase() + RANDOM_LONG( 10, 15 ); - m_pPlayer->pev->punchangle.x = RANDOM_FLOAT( 0, 2 ); + m_flTimeWeaponIdle = UTIL_WeaponTimeBase() + UTIL_SharedRandomFloat( m_pPlayer->random_seed, 10, 15 ); } void CHgun::Reload( void ) { - if (m_pPlayer->m_rgAmmo[m_iPrimaryAmmoType] >= HORNET_MAX_CARRY) return; + if (m_pPlayer->m_rgAmmo[m_iPrimaryAmmoType] >= HORNET_MAX_CARRY) + return; + while (m_pPlayer->m_rgAmmo[m_iPrimaryAmmoType] < HORNET_MAX_CARRY && m_flRechargeTime < gpGlobals->time) { m_pPlayer->m_rgAmmo[m_iPrimaryAmmoType]++; @@ -229,7 +283,7 @@ void CHgun::WeaponIdle( void ) return; int iAnim; - float flRand = RANDOM_FLOAT( 0, 1 ); + float flRand = UTIL_SharedRandomFloat( m_pPlayer->random_seed, 0, 1 ); if (flRand <= 0.75) { iAnim = HGUN_IDLE1; @@ -247,3 +301,5 @@ void CHgun::WeaponIdle( void ) } SendWeaponAnim( iAnim ); } + +#endif \ No newline at end of file diff --git a/spirit/houndeye.cpp b/dlls/houndeye.cpp similarity index 94% rename from spirit/houndeye.cpp rename to dlls/houndeye.cpp index 17149663..e8bfacfd 100644 --- a/spirit/houndeye.cpp +++ b/dlls/houndeye.cpp @@ -1,1311 +1,1304 @@ -/*** -* -* Copyright (c) 1996-2002, 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. -* -* This source code contains proprietary and confidential information of -* Valve LLC and its suppliers. Access to this code is restricted to -* persons who have executed a written SDK license with Valve. Any access, -* use or distribution of this code by or to any unlicensed person is illegal. -* -****/ -//========================================================= -// Houndeye - spooky sonic dog. -//========================================================= - -#include "extdll.h" -#include "util.h" -#include "cbase.h" -#include "monsters.h" -#include "schedule.h" -#include "animation.h" -#include "nodes.h" -#include "squadmonster.h" -#include "soundent.h" -#include "game.h" - -extern CGraph WorldGraph; - -// houndeye does 20 points of damage spread over a sphere 384 units in diameter, and each additional -// squad member increases the BASE damage by 110%, per the spec. -#define HOUNDEYE_MAX_SQUAD_SIZE 4 -#define HOUNDEYE_MAX_ATTACK_RADIUS 384 -#define HOUNDEYE_SQUAD_BONUS (float)1.1 - -#define HOUNDEYE_EYE_FRAMES 4 // how many different switchable maps for the eye - -#define HOUNDEYE_SOUND_STARTLE_VOLUME 128 // how loud a sound has to be to badly scare a sleeping houndeye - -//========================================================= -// monster-specific tasks -//========================================================= -enum -{ - TASK_HOUND_CLOSE_EYE = LAST_COMMON_TASK + 1, - TASK_HOUND_OPEN_EYE, - TASK_HOUND_THREAT_DISPLAY, - TASK_HOUND_FALL_ASLEEP, - TASK_HOUND_WAKE_UP, - TASK_HOUND_HOP_BACK -}; - -//========================================================= -// monster-specific schedule types -//========================================================= -enum -{ - SCHED_HOUND_AGITATED = LAST_COMMON_SCHEDULE + 1, - SCHED_HOUND_HOP_RETREAT, - SCHED_HOUND_FAIL, -}; - -//========================================================= -// Monster's Anim Events Go Here -//========================================================= -#define HOUND_AE_WARN 1 -#define HOUND_AE_STARTATTACK 2 -#define HOUND_AE_THUMP 3 -#define HOUND_AE_ANGERSOUND1 4 -#define HOUND_AE_ANGERSOUND2 5 -#define HOUND_AE_HOPBACK 6 -#define HOUND_AE_CLOSE_EYE 7 - -class CHoundeye : public CSquadMonster -{ -public: - void Spawn( void ); - void Precache( void ); - int Classify ( void ); - void HandleAnimEvent( MonsterEvent_t *pEvent ); - void SetYawSpeed ( void ); - void WarmUpSound ( void ); - void AlertSound( void ); - void DeathSound( void ); - void WarnSound( void ); - void PainSound( void ); - void IdleSound( void ); - void StartTask( Task_t *pTask ); - void RunTask ( Task_t *pTask ); - void SonicAttack( void ); - void PrescheduleThink( void ); - void SetActivity ( Activity NewActivity ); - void WriteBeamColor ( void ); - BOOL CheckRangeAttack1 ( float flDot, float flDist ); - BOOL FValidateHintType ( short sHint ); - BOOL FCanActiveIdle ( void ); - Schedule_t *GetScheduleOfType ( int Type ); - Schedule_t *CHoundeye :: GetSchedule( void ); - - int Save( CSave &save ); - int Restore( CRestore &restore ); - - CUSTOM_SCHEDULES; - static TYPEDESCRIPTION m_SaveData[]; - - int m_iSpriteTexture; - BOOL m_fAsleep;// some houndeyes sleep in idle mode if this is set, the houndeye is lying down - BOOL m_fDontBlink;// don't try to open/close eye if this bit is set! - Vector m_vecPackCenter; // the center of the pack. The leader maintains this by averaging the origins of all pack members. -}; -LINK_ENTITY_TO_CLASS( monster_houndeye, CHoundeye ); - -TYPEDESCRIPTION CHoundeye::m_SaveData[] = -{ - DEFINE_FIELD( CHoundeye, m_iSpriteTexture, FIELD_INTEGER ), - DEFINE_FIELD( CHoundeye, m_fAsleep, FIELD_BOOLEAN ), - DEFINE_FIELD( CHoundeye, m_fDontBlink, FIELD_BOOLEAN ), - DEFINE_FIELD( CHoundeye, m_vecPackCenter, FIELD_POSITION_VECTOR ), -}; - -IMPLEMENT_SAVERESTORE( CHoundeye, CSquadMonster ); - -//========================================================= -// Classify - indicates this monster's place in the -// relationship table. -//========================================================= -int CHoundeye :: Classify ( void ) -{ - return m_iClass?m_iClass:CLASS_ALIEN_MONSTER; -} - -//========================================================= -// FValidateHintType -//========================================================= -BOOL CHoundeye :: FValidateHintType ( short sHint ) -{ - int i; - - static short sHoundHints[] = - { - HINT_WORLD_MACHINERY, - HINT_WORLD_BLINKING_LIGHT, - HINT_WORLD_HUMAN_BLOOD, - HINT_WORLD_ALIEN_BLOOD, - }; - - for ( i = 0 ; i < ARRAYSIZE ( sHoundHints ) ; i++ ) - { - if ( sHoundHints[ i ] == sHint ) - { - return TRUE; - } - } - - ALERT ( at_aiconsole, "Couldn't validate hint type" ); - return FALSE; -} - - -//========================================================= -// FCanActiveIdle -//========================================================= -BOOL CHoundeye :: FCanActiveIdle ( void ) -{ - if ( InSquad() ) - { - CSquadMonster *pSquadLeader = MySquadLeader(); - - for (int i = 0; i < MAX_SQUAD_MEMBERS;i++) - { - CSquadMonster *pMember = pSquadLeader->MySquadMember(i); - - if ( pMember != NULL && pMember != this && pMember->m_iHintNode != NO_NODE ) - { - // someone else in the group is active idling right now! - return FALSE; - } - } - - return TRUE; - } - - return TRUE; -} - - -//========================================================= -// CheckRangeAttack1 - overridden for houndeyes so that they -// try to get within half of their max attack radius before -// attacking, so as to increase their chances of doing damage. -//========================================================= -BOOL CHoundeye :: CheckRangeAttack1 ( float flDot, float flDist ) -{ - if ( flDist <= ( HOUNDEYE_MAX_ATTACK_RADIUS * 0.5 ) && flDot >= 0.3 ) - { - return TRUE; - } - return FALSE; -} - -//========================================================= -// SetYawSpeed - allows each sequence to have a different -// turn rate associated with it. -//========================================================= -void CHoundeye :: SetYawSpeed ( void ) -{ - int ys; - - ys = 90; - - switch ( m_Activity ) - { - case ACT_CROUCHIDLE://sleeping! - ys = 0; - break; - case ACT_IDLE: - ys = 60; - break; - case ACT_WALK: - ys = 90; - break; - case ACT_RUN: - ys = 90; - break; - case ACT_TURN_LEFT: - case ACT_TURN_RIGHT: - ys = 90; - break; - } - - pev->yaw_speed = ys; -} - -//========================================================= -// SetActivity -//========================================================= -void CHoundeye :: SetActivity ( Activity NewActivity ) -{ - int iSequence; - - if ( NewActivity == m_Activity ) - return; - - if ( m_MonsterState == MONSTERSTATE_COMBAT && NewActivity == ACT_IDLE && RANDOM_LONG(0,1) ) - { - // play pissed idle. - iSequence = LookupSequence( "madidle" ); - - m_Activity = NewActivity; // Go ahead and set this so it doesn't keep trying when the anim is not present - - // In case someone calls this with something other than the ideal activity - m_IdealActivity = m_Activity; - - // Set to the desired anim, or default anim if the desired is not present - if ( iSequence > ACTIVITY_NOT_AVAILABLE ) - { - pev->sequence = iSequence; // Set to the reset anim (if it's there) - pev->frame = 0; // FIX: frame counter shouldn't be reset when its the same activity as before - ResetSequenceInfo(); - SetYawSpeed(); - } - } - else - { - CSquadMonster :: SetActivity ( NewActivity ); - } -} - -//========================================================= -// HandleAnimEvent - catches the monster-specific messages -// that occur when tagged animation frames are played. -//========================================================= -void CHoundeye :: HandleAnimEvent( MonsterEvent_t *pEvent ) -{ - switch ( pEvent->event ) - { - case HOUND_AE_WARN: - // do stuff for this event. - WarnSound(); - break; - - case HOUND_AE_STARTATTACK: - WarmUpSound(); - break; - - case HOUND_AE_HOPBACK: - { - float flGravity = g_psv_gravity->value; - - pev->flags &= ~FL_ONGROUND; - - pev->velocity = gpGlobals->v_forward * -200; - pev->velocity.z += (0.6 * flGravity) * 0.5; - - break; - } - - case HOUND_AE_THUMP: - // emit the shockwaves - SonicAttack(); - break; - - case HOUND_AE_ANGERSOUND1: - EMIT_SOUND(ENT(pev), CHAN_VOICE, "houndeye/he_pain3.wav", 1, ATTN_NORM); - break; - - case HOUND_AE_ANGERSOUND2: - EMIT_SOUND(ENT(pev), CHAN_VOICE, "houndeye/he_pain1.wav", 1, ATTN_NORM); - break; - - case HOUND_AE_CLOSE_EYE: - if ( !m_fDontBlink ) - { - pev->skin = HOUNDEYE_EYE_FRAMES - 1; - } - break; - - default: - CSquadMonster::HandleAnimEvent( pEvent ); - break; - } -} - -//========================================================= -// Spawn -//========================================================= -void CHoundeye :: Spawn() -{ - Precache( ); - - if (pev->model) - SET_MODEL(ENT(pev), STRING(pev->model)); //LRC - else - SET_MODEL(ENT(pev), "models/houndeye.mdl"); - UTIL_SetSize(pev, Vector ( -16, -16, 0 ), Vector ( 16, 16, 36 ) ); - - pev->solid = SOLID_SLIDEBOX; - pev->movetype = MOVETYPE_STEP; - m_bloodColor = BLOOD_COLOR_YELLOW; - pev->effects = 0; - if (pev->health == 0) - pev->health = gSkillData.houndeyeHealth; - pev->yaw_speed = 5;//!!! should we put this in the monster's changeanim function since turn rates may vary with state/anim? - m_flFieldOfView = 0.5;// indicates the width of this monster's forward view cone ( as a dotproduct result ) - m_MonsterState = MONSTERSTATE_NONE; - m_fAsleep = FALSE; // everyone spawns awake - m_fDontBlink = FALSE; - m_afCapability |= bits_CAP_SQUAD; - - MonsterInit(); -} - -//========================================================= -// Precache - precaches all resources this monster needs -//========================================================= -void CHoundeye :: Precache() -{ - if (pev->model) - PRECACHE_MODEL((char*)STRING(pev->model)); //LRC - else - PRECACHE_MODEL("models/houndeye.mdl"); - - PRECACHE_SOUND("houndeye/he_alert1.wav"); - PRECACHE_SOUND("houndeye/he_alert2.wav"); - PRECACHE_SOUND("houndeye/he_alert3.wav"); - - PRECACHE_SOUND("houndeye/he_die1.wav"); - PRECACHE_SOUND("houndeye/he_die2.wav"); - PRECACHE_SOUND("houndeye/he_die3.wav"); - - PRECACHE_SOUND("houndeye/he_idle1.wav"); - PRECACHE_SOUND("houndeye/he_idle2.wav"); - PRECACHE_SOUND("houndeye/he_idle3.wav"); - - PRECACHE_SOUND("houndeye/he_hunt1.wav"); - PRECACHE_SOUND("houndeye/he_hunt2.wav"); - PRECACHE_SOUND("houndeye/he_hunt3.wav"); - - PRECACHE_SOUND("houndeye/he_pain1.wav"); - PRECACHE_SOUND("houndeye/he_pain3.wav"); - PRECACHE_SOUND("houndeye/he_pain4.wav"); - PRECACHE_SOUND("houndeye/he_pain5.wav"); - - PRECACHE_SOUND("houndeye/he_attack1.wav"); - PRECACHE_SOUND("houndeye/he_attack3.wav"); - - PRECACHE_SOUND("houndeye/he_blast1.wav"); - PRECACHE_SOUND("houndeye/he_blast2.wav"); - PRECACHE_SOUND("houndeye/he_blast3.wav"); - - m_iSpriteTexture = PRECACHE_MODEL( "sprites/shockwave.spr" ); -} - -//========================================================= -// IdleSound -//========================================================= -void CHoundeye :: IdleSound ( void ) -{ - switch ( RANDOM_LONG(0,2) ) - { - case 0: - EMIT_SOUND( ENT(pev), CHAN_VOICE, "houndeye/he_idle1.wav", 1, ATTN_NORM ); - break; - case 1: - EMIT_SOUND( ENT(pev), CHAN_VOICE, "houndeye/he_idle2.wav", 1, ATTN_NORM ); - break; - case 2: - EMIT_SOUND( ENT(pev), CHAN_VOICE, "houndeye/he_idle3.wav", 1, ATTN_NORM ); - break; - } -} - -//========================================================= -// IdleSound -//========================================================= -void CHoundeye :: WarmUpSound ( void ) -{ - switch ( RANDOM_LONG(0,1) ) - { - case 0: - EMIT_SOUND( ENT(pev), CHAN_WEAPON, "houndeye/he_attack1.wav", 0.7, ATTN_NORM ); - break; - case 1: - EMIT_SOUND( ENT(pev), CHAN_WEAPON, "houndeye/he_attack3.wav", 0.7, ATTN_NORM ); - break; - } -} - -//========================================================= -// WarnSound -//========================================================= -void CHoundeye :: WarnSound ( void ) -{ - switch ( RANDOM_LONG(0,2) ) - { - case 0: - EMIT_SOUND( ENT(pev), CHAN_VOICE, "houndeye/he_hunt1.wav", 1, ATTN_NORM ); - break; - case 1: - EMIT_SOUND( ENT(pev), CHAN_VOICE, "houndeye/he_hunt2.wav", 1, ATTN_NORM ); - break; - case 2: - EMIT_SOUND( ENT(pev), CHAN_VOICE, "houndeye/he_hunt3.wav", 1, ATTN_NORM ); - break; - } -} - -//========================================================= -// AlertSound -//========================================================= -void CHoundeye :: AlertSound ( void ) -{ - - if ( InSquad() && !IsLeader() ) - { - return; // only leader makes ALERT sound. - } - - switch ( RANDOM_LONG(0,2) ) - { - case 0: - EMIT_SOUND( ENT(pev), CHAN_VOICE, "houndeye/he_alert1.wav", 1, ATTN_NORM ); - break; - case 1: - EMIT_SOUND( ENT(pev), CHAN_VOICE, "houndeye/he_alert2.wav", 1, ATTN_NORM ); - break; - case 2: - EMIT_SOUND( ENT(pev), CHAN_VOICE, "houndeye/he_alert3.wav", 1, ATTN_NORM ); - break; - } -} - -//========================================================= -// DeathSound -//========================================================= -void CHoundeye :: DeathSound ( void ) -{ - switch ( RANDOM_LONG(0,2) ) - { - case 0: - EMIT_SOUND( ENT(pev), CHAN_VOICE, "houndeye/he_die1.wav", 1, ATTN_NORM ); - break; - case 1: - EMIT_SOUND( ENT(pev), CHAN_VOICE, "houndeye/he_die2.wav", 1, ATTN_NORM ); - break; - case 2: - EMIT_SOUND( ENT(pev), CHAN_VOICE, "houndeye/he_die3.wav", 1, ATTN_NORM ); - break; - } -} - -//========================================================= -// PainSound -//========================================================= -void CHoundeye :: PainSound ( void ) -{ - switch ( RANDOM_LONG(0,2) ) - { - case 0: - EMIT_SOUND( ENT(pev), CHAN_VOICE, "houndeye/he_pain3.wav", 1, ATTN_NORM ); - break; - case 1: - EMIT_SOUND( ENT(pev), CHAN_VOICE, "houndeye/he_pain4.wav", 1, ATTN_NORM ); - break; - case 2: - EMIT_SOUND( ENT(pev), CHAN_VOICE, "houndeye/he_pain5.wav", 1, ATTN_NORM ); - break; - } -} - -//========================================================= -// WriteBeamColor - writes a color vector to the network -// based on the size of the group. -//========================================================= -void CHoundeye :: WriteBeamColor ( void ) -{ - BYTE bRed, bGreen, bBlue; - - if ( InSquad() ) - { - switch ( SquadCount() ) - { - case 2: - // no case for 0 or 1, cause those are impossible for monsters in Squads. - bRed = 101; - bGreen = 133; - bBlue = 221; - break; - case 3: - bRed = 67; - bGreen = 85; - bBlue = 255; - break; - case 4: - bRed = 62; - bGreen = 33; - bBlue = 211; - break; - default: - ALERT ( at_aiconsole, "Unsupported Houndeye SquadSize!\n" ); - bRed = 188; - bGreen = 220; - bBlue = 255; - break; - } - } - else - { - // solo houndeye - weakest beam - bRed = 188; - bGreen = 220; - bBlue = 255; - } - - WRITE_BYTE( bRed ); - WRITE_BYTE( bGreen ); - WRITE_BYTE( bBlue ); -} - - -//========================================================= -// SonicAttack -//========================================================= -void CHoundeye :: SonicAttack ( void ) -{ - float flAdjustedDamage; - float flDist; - - switch ( RANDOM_LONG( 0, 2 ) ) - { - case 0: EMIT_SOUND(ENT(pev), CHAN_WEAPON, "houndeye/he_blast1.wav", 1, ATTN_NORM); break; - case 1: EMIT_SOUND(ENT(pev), CHAN_WEAPON, "houndeye/he_blast2.wav", 1, ATTN_NORM); break; - case 2: EMIT_SOUND(ENT(pev), CHAN_WEAPON, "houndeye/he_blast3.wav", 1, ATTN_NORM); break; - } - - // blast circles - MESSAGE_BEGIN( MSG_PAS, gmsgTempEntity, pev->origin ); - WRITE_BYTE( TE_BEAMCYLINDER ); - WRITE_COORD( pev->origin.x); - WRITE_COORD( pev->origin.y); - WRITE_COORD( pev->origin.z + 16); - WRITE_COORD( pev->origin.x); - WRITE_COORD( pev->origin.y); - WRITE_COORD( pev->origin.z + 16 + HOUNDEYE_MAX_ATTACK_RADIUS / .2); // reach damage radius over .3 seconds - WRITE_SHORT( m_iSpriteTexture ); - WRITE_BYTE( 0 ); // startframe - WRITE_BYTE( 0 ); // framerate - WRITE_BYTE( 2 ); // life - WRITE_BYTE( 16 ); // width - WRITE_BYTE( 0 ); // noise - - WriteBeamColor(); - - WRITE_BYTE( 255 ); //brightness - WRITE_BYTE( 0 ); // speed - MESSAGE_END(); - - MESSAGE_BEGIN( MSG_PAS, gmsgTempEntity, pev->origin ); - WRITE_BYTE( TE_BEAMCYLINDER ); - WRITE_COORD( pev->origin.x); - WRITE_COORD( pev->origin.y); - WRITE_COORD( pev->origin.z + 16); - WRITE_COORD( pev->origin.x); - WRITE_COORD( pev->origin.y); - WRITE_COORD( pev->origin.z + 16 + ( HOUNDEYE_MAX_ATTACK_RADIUS / 2 ) / .2); // reach damage radius over .3 seconds - WRITE_SHORT( m_iSpriteTexture ); - WRITE_BYTE( 0 ); // startframe - WRITE_BYTE( 0 ); // framerate - WRITE_BYTE( 2 ); // life - WRITE_BYTE( 16 ); // width - WRITE_BYTE( 0 ); // noise - - WriteBeamColor(); - - WRITE_BYTE( 255 ); //brightness - WRITE_BYTE( 0 ); // speed - MESSAGE_END(); - - - CBaseEntity *pEntity = NULL; - // iterate on all entities in the vicinity. - while ((pEntity = UTIL_FindEntityInSphere( pEntity, pev->origin, HOUNDEYE_MAX_ATTACK_RADIUS )) != NULL) - { - if ( pEntity->pev->takedamage != DAMAGE_NO ) - { - if ( !FClassnameIs(pEntity->pev, "monster_houndeye") ) - {// houndeyes don't hurt other houndeyes with their attack - - // houndeyes do FULL damage if the ent in question is visible. Half damage otherwise. - // This means that you must get out of the houndeye's attack range entirely to avoid damage. - // Calculate full damage first - - if ( SquadCount() > 1 ) - { - // squad gets attack bonus. - flAdjustedDamage = gSkillData.houndeyeDmgBlast + gSkillData.houndeyeDmgBlast * ( HOUNDEYE_SQUAD_BONUS * ( SquadCount() - 1 ) ); - } - else - { - // solo - flAdjustedDamage = gSkillData.houndeyeDmgBlast; - } - - flDist = (pEntity->Center() - pev->origin).Length(); - - flAdjustedDamage -= ( flDist / HOUNDEYE_MAX_ATTACK_RADIUS ) * flAdjustedDamage; - - if ( !FVisible( pEntity ) ) - { - if ( pEntity->IsPlayer() ) - { - // if this entity is a client, and is not in full view, inflict half damage. We do this so that players still - // take the residual damage if they don't totally leave the houndeye's effective radius. We restrict it to clients - // so that monsters in other parts of the level don't take the damage and get pissed. - flAdjustedDamage *= 0.5; - } - else if ( !FClassnameIs( pEntity->pev, "func_breakable" ) && !FClassnameIs( pEntity->pev, "func_pushable" ) ) - { - // do not hurt nonclients through walls, but allow damage to be done to breakables - flAdjustedDamage = 0; - } - } - - //ALERT ( at_aiconsole, "Damage: %f\n", flAdjustedDamage ); - - if (flAdjustedDamage > 0 ) - { - pEntity->TakeDamage ( pev, pev, flAdjustedDamage, DMG_SONIC | DMG_ALWAYSGIB ); - } - } - } - } -} - -//========================================================= -// start task -//========================================================= -void CHoundeye :: StartTask ( Task_t *pTask ) -{ - m_iTaskStatus = TASKSTATUS_RUNNING; - - switch ( pTask->iTask ) - { - case TASK_HOUND_FALL_ASLEEP: - { - m_fAsleep = TRUE; // signal that hound is lying down (must stand again before doing anything else!) - m_iTaskStatus = TASKSTATUS_COMPLETE; - break; - } - case TASK_HOUND_WAKE_UP: - { - m_fAsleep = FALSE; // signal that hound is standing again - m_iTaskStatus = TASKSTATUS_COMPLETE; - break; - } - case TASK_HOUND_OPEN_EYE: - { - m_fDontBlink = FALSE; // turn blinking back on and that code will automatically open the eye - m_iTaskStatus = TASKSTATUS_COMPLETE; - break; - } - case TASK_HOUND_CLOSE_EYE: - { - pev->skin = 0; - m_fDontBlink = TRUE; // tell blink code to leave the eye alone. - break; - } - case TASK_HOUND_THREAT_DISPLAY: - { - m_IdealActivity = ACT_IDLE_ANGRY; - break; - } - case TASK_HOUND_HOP_BACK: - { - m_IdealActivity = ACT_LEAP; - break; - } - case TASK_RANGE_ATTACK1: - { - m_IdealActivity = ACT_RANGE_ATTACK1; - -/* - if ( InSquad() ) - { - // see if there is a battery to connect to. - CSquadMonster *pSquad = m_pSquadLeader; - - while ( pSquad ) - { - if ( pSquad->m_iMySlot == bits_SLOT_HOUND_BATTERY ) - { - // draw a beam. - MESSAGE_BEGIN( MSG_BROADCAST, gmsgTempEntity ); - WRITE_BYTE( TE_BEAMENTS ); - WRITE_SHORT( ENTINDEX( this->edict() ) ); - WRITE_SHORT( ENTINDEX( pSquad->edict() ) ); - WRITE_SHORT( m_iSpriteTexture ); - WRITE_BYTE( 0 ); // framestart - WRITE_BYTE( 0 ); // framerate - WRITE_BYTE( 10 ); // life - WRITE_BYTE( 40 ); // width - WRITE_BYTE( 10 ); // noise - WRITE_BYTE( 0 ); // r, g, b - WRITE_BYTE( 50 ); // r, g, b - WRITE_BYTE( 250); // r, g, b - WRITE_BYTE( 255 ); // brightness - WRITE_BYTE( 30 ); // speed - MESSAGE_END(); - break; - } - - pSquad = pSquad->m_pSquadNext; - } - } -*/ - - break; - } - case TASK_SPECIAL_ATTACK1: - { - m_IdealActivity = ACT_SPECIAL_ATTACK1; - break; - } - case TASK_GUARD: - { - m_IdealActivity = ACT_GUARD; - break; - } - default: - { - CSquadMonster :: StartTask(pTask); - break; - } - } -} - -//========================================================= -// RunTask -//========================================================= -void CHoundeye :: RunTask ( Task_t *pTask ) -{ - switch ( pTask->iTask ) - { - case TASK_HOUND_THREAT_DISPLAY: - { - MakeIdealYaw ( m_vecEnemyLKP ); - ChangeYaw ( pev->yaw_speed ); - - if ( m_fSequenceFinished ) - { - TaskComplete(); - } - - break; - } - case TASK_HOUND_CLOSE_EYE: - { - if ( pev->skin < HOUNDEYE_EYE_FRAMES - 1 ) - { - pev->skin++; - } - break; - } - case TASK_HOUND_HOP_BACK: - { - if ( m_fSequenceFinished ) - { - TaskComplete(); - } - break; - } - case TASK_SPECIAL_ATTACK1: - { - pev->skin = RANDOM_LONG(0, HOUNDEYE_EYE_FRAMES - 1); - - MakeIdealYaw ( m_vecEnemyLKP ); - ChangeYaw ( pev->yaw_speed ); - - float life; - life = ((255 - pev->frame) / (pev->framerate * m_flFrameRate)); - if (life < 0.1) life = 0.1; - - MESSAGE_BEGIN( MSG_PAS, gmsgTempEntity, pev->origin ); - WRITE_BYTE( TE_IMPLOSION); - WRITE_COORD( pev->origin.x); - WRITE_COORD( pev->origin.y); - WRITE_COORD( pev->origin.z + 16); - WRITE_BYTE( 50 * life + 100); // radius - WRITE_BYTE( pev->frame / 25.0 ); // count - WRITE_BYTE( life * 10 ); // life - MESSAGE_END(); - - if ( m_fSequenceFinished ) - { - SonicAttack(); - TaskComplete(); - } - - break; - } - default: - { - CSquadMonster :: RunTask(pTask); - break; - } - } -} - -//========================================================= -// PrescheduleThink -//========================================================= -void CHoundeye::PrescheduleThink ( void ) -{ - // if the hound is mad and is running, make hunt noises. - if ( m_MonsterState == MONSTERSTATE_COMBAT && m_Activity == ACT_RUN && RANDOM_FLOAT( 0, 1 ) < 0.2 ) - { - WarnSound(); - } - - // at random, initiate a blink if not already blinking or sleeping - if ( !m_fDontBlink ) - { - if ( ( pev->skin == 0 ) && RANDOM_LONG(0,0x7F) == 0 ) - {// start blinking! - pev->skin = HOUNDEYE_EYE_FRAMES - 1; - } - else if ( pev->skin != 0 ) - {// already blinking - pev->skin--; - } - } - - // if you are the leader, average the origins of each pack member to get an approximate center. - if ( IsLeader() ) - { - CSquadMonster *pSquadMember; - int iSquadCount = 0; - - for (int i = 0; i < MAX_SQUAD_MEMBERS; i++) - { - pSquadMember = MySquadMember(i); - - if (pSquadMember) - { - iSquadCount++; - m_vecPackCenter = m_vecPackCenter + pSquadMember->pev->origin; - } - } - - m_vecPackCenter = m_vecPackCenter / iSquadCount; - } -} - -//========================================================= -// AI Schedules Specific to this monster -//========================================================= -Task_t tlHoundGuardPack[] = -{ - { TASK_STOP_MOVING, (float)0 }, - { TASK_GUARD, (float)0 }, -}; - -Schedule_t slHoundGuardPack[] = -{ - { - tlHoundGuardPack, - ARRAYSIZE ( tlHoundGuardPack ), - bits_COND_SEE_HATE | - bits_COND_LIGHT_DAMAGE | - bits_COND_HEAVY_DAMAGE | - bits_COND_PROVOKED | - bits_COND_HEAR_SOUND, - - bits_SOUND_COMBAT |// sound flags - bits_SOUND_WORLD | - bits_SOUND_MEAT | - bits_SOUND_PLAYER, - "GuardPack" - }, -}; - -// primary range attack -Task_t tlHoundYell1[] = -{ - { TASK_STOP_MOVING, (float)0 }, - { TASK_FACE_IDEAL, (float)0 }, - { TASK_RANGE_ATTACK1, (float)0 }, - { TASK_SET_SCHEDULE, (float)SCHED_HOUND_AGITATED }, -}; - -Task_t tlHoundYell2[] = -{ - { TASK_STOP_MOVING, (float)0 }, - { TASK_FACE_IDEAL, (float)0 }, - { TASK_RANGE_ATTACK1, (float)0 }, -}; - -Schedule_t slHoundRangeAttack[] = -{ - { - tlHoundYell1, - ARRAYSIZE ( tlHoundYell1 ), - bits_COND_LIGHT_DAMAGE | - bits_COND_HEAVY_DAMAGE, - 0, - "HoundRangeAttack1" - }, - { - tlHoundYell2, - ARRAYSIZE ( tlHoundYell2 ), - bits_COND_LIGHT_DAMAGE | - bits_COND_HEAVY_DAMAGE, - 0, - "HoundRangeAttack2" - }, -}; - -// lie down and fall asleep -Task_t tlHoundSleep[] = -{ - { TASK_STOP_MOVING, (float)0 }, - { TASK_SET_ACTIVITY, (float)ACT_IDLE }, - { TASK_WAIT_RANDOM, (float)5 }, - { TASK_PLAY_SEQUENCE, (float)ACT_CROUCH }, - { TASK_SET_ACTIVITY, (float)ACT_CROUCHIDLE }, - { TASK_HOUND_FALL_ASLEEP, (float)0 }, - { TASK_WAIT_RANDOM, (float)25 }, - { TASK_HOUND_CLOSE_EYE, (float)0 }, - //{ TASK_WAIT, (float)10 }, - //{ TASK_WAIT_RANDOM, (float)10 }, -}; - -Schedule_t slHoundSleep[] = -{ - { - tlHoundSleep, - ARRAYSIZE ( tlHoundSleep ), - bits_COND_HEAR_SOUND | - bits_COND_LIGHT_DAMAGE | - bits_COND_HEAVY_DAMAGE | - bits_COND_NEW_ENEMY, - - bits_SOUND_COMBAT | - bits_SOUND_PLAYER | - bits_SOUND_WORLD, - "Hound Sleep" - }, -}; - -// wake and stand up lazily -Task_t tlHoundWakeLazy[] = -{ - { TASK_STOP_MOVING, (float)0 }, - { TASK_HOUND_OPEN_EYE, (float)0 }, - { TASK_WAIT_RANDOM, (float)2.5 }, - { TASK_PLAY_SEQUENCE, (float)ACT_STAND }, - { TASK_HOUND_WAKE_UP, (float)0 }, -}; - -Schedule_t slHoundWakeLazy[] = -{ - { - tlHoundWakeLazy, - ARRAYSIZE ( tlHoundWakeLazy ), - 0, - 0, - "WakeLazy" - }, -}; - -// wake and stand up with great urgency! -Task_t tlHoundWakeUrgent[] = -{ - { TASK_HOUND_OPEN_EYE, (float)0 }, - { TASK_PLAY_SEQUENCE, (float)ACT_HOP }, - { TASK_FACE_IDEAL, (float)0 }, - { TASK_HOUND_WAKE_UP, (float)0 }, -}; - -Schedule_t slHoundWakeUrgent[] = -{ - { - tlHoundWakeUrgent, - ARRAYSIZE ( tlHoundWakeUrgent ), - 0, - 0, - "WakeUrgent" - }, -}; - - -Task_t tlHoundSpecialAttack1[] = -{ - { TASK_STOP_MOVING, 0 }, - { TASK_FACE_IDEAL, (float)0 }, - { TASK_SPECIAL_ATTACK1, (float)0 }, - { TASK_PLAY_SEQUENCE, (float)ACT_IDLE_ANGRY }, -}; - -Schedule_t slHoundSpecialAttack1[] = -{ - { - tlHoundSpecialAttack1, - ARRAYSIZE ( tlHoundSpecialAttack1 ), - bits_COND_NEW_ENEMY | - bits_COND_LIGHT_DAMAGE | - bits_COND_HEAVY_DAMAGE | - bits_COND_ENEMY_OCCLUDED, - - 0, - "Hound Special Attack1" - }, -}; - -Task_t tlHoundAgitated[] = -{ - { TASK_STOP_MOVING, 0 }, - { TASK_HOUND_THREAT_DISPLAY, 0 }, -}; - -Schedule_t slHoundAgitated[] = -{ - { - tlHoundAgitated, - ARRAYSIZE ( tlHoundAgitated ), - bits_COND_NEW_ENEMY | - bits_COND_LIGHT_DAMAGE | - bits_COND_HEAVY_DAMAGE, - 0, - "Hound Agitated" - }, -}; - -Task_t tlHoundHopRetreat[] = -{ - { TASK_STOP_MOVING, 0 }, - { TASK_HOUND_HOP_BACK, 0 }, - { TASK_SET_SCHEDULE, (float)SCHED_TAKE_COVER_FROM_ENEMY }, -}; - -Schedule_t slHoundHopRetreat[] = -{ - { - tlHoundHopRetreat, - ARRAYSIZE ( tlHoundHopRetreat ), - 0, - 0, - "Hound Hop Retreat" - }, -}; - -// hound fails in combat with client in the PVS -Task_t tlHoundCombatFailPVS[] = -{ - { TASK_STOP_MOVING, 0 }, - { TASK_HOUND_THREAT_DISPLAY, 0 }, - { TASK_WAIT_FACE_ENEMY, (float)1 }, -}; - -Schedule_t slHoundCombatFailPVS[] = -{ - { - tlHoundCombatFailPVS, - ARRAYSIZE ( tlHoundCombatFailPVS ), - bits_COND_NEW_ENEMY | - bits_COND_LIGHT_DAMAGE | - bits_COND_HEAVY_DAMAGE, - 0, - "HoundCombatFailPVS" - }, -}; - -// hound fails in combat with no client in the PVS. Don't keep peeping! -Task_t tlHoundCombatFailNoPVS[] = -{ - { TASK_STOP_MOVING, 0 }, - { TASK_HOUND_THREAT_DISPLAY, 0 }, - { TASK_WAIT_FACE_ENEMY, (float)2 }, - { TASK_SET_ACTIVITY, (float)ACT_IDLE }, - { TASK_WAIT_PVS, 0 }, -}; - -Schedule_t slHoundCombatFailNoPVS[] = -{ - { - tlHoundCombatFailNoPVS, - ARRAYSIZE ( tlHoundCombatFailNoPVS ), - bits_COND_NEW_ENEMY | - bits_COND_LIGHT_DAMAGE | - bits_COND_HEAVY_DAMAGE, - 0, - "HoundCombatFailNoPVS" - }, -}; - -DEFINE_CUSTOM_SCHEDULES( CHoundeye ) -{ - slHoundGuardPack, - slHoundRangeAttack, - &slHoundRangeAttack[ 1 ], - slHoundSleep, - slHoundWakeLazy, - slHoundWakeUrgent, - slHoundSpecialAttack1, - slHoundAgitated, - slHoundHopRetreat, - slHoundCombatFailPVS, - slHoundCombatFailNoPVS, -}; - -IMPLEMENT_CUSTOM_SCHEDULES( CHoundeye, CSquadMonster ); - -//========================================================= -// GetScheduleOfType -//========================================================= -Schedule_t* CHoundeye :: GetScheduleOfType ( int Type ) -{ - if ( m_fAsleep ) - { - // if the hound is sleeping, must wake and stand! - if ( HasConditions( bits_COND_HEAR_SOUND ) ) - { - CSound *pWakeSound; - - pWakeSound = PBestSound(); - ASSERT( pWakeSound != NULL ); - if ( pWakeSound ) - { - MakeIdealYaw ( pWakeSound->m_vecOrigin ); - - if ( FLSoundVolume ( pWakeSound ) >= HOUNDEYE_SOUND_STARTLE_VOLUME ) - { - // awakened by a loud sound - return &slHoundWakeUrgent[ 0 ]; - } - } - // sound was not loud enough to scare the bejesus out of houndeye - return &slHoundWakeLazy[ 0 ]; - } - else if ( HasConditions( bits_COND_NEW_ENEMY ) ) - { - // get up fast, to fight. - return &slHoundWakeUrgent[ 0 ]; - } - - else - { - // hound is waking up on its own - return &slHoundWakeLazy[ 0 ]; - } - } - switch ( Type ) - { - case SCHED_IDLE_STAND: - { - // we may want to sleep instead of stand! - if ( InSquad() && !IsLeader() && !m_fAsleep && RANDOM_LONG(0,29) < 1 ) - { - return &slHoundSleep[ 0 ]; - } - else - { - return CSquadMonster :: GetScheduleOfType( Type ); - } - } - case SCHED_RANGE_ATTACK1: - { - return &slHoundRangeAttack[ 0 ]; -/* - if ( InSquad() ) - { - return &slHoundRangeAttack[ RANDOM_LONG( 0, 1 ) ]; - } - - return &slHoundRangeAttack[ 1 ]; -*/ - } - case SCHED_SPECIAL_ATTACK1: - { - return &slHoundSpecialAttack1[ 0 ]; - } - case SCHED_GUARD: - { - return &slHoundGuardPack[ 0 ]; - } - case SCHED_HOUND_AGITATED: - { - return &slHoundAgitated[ 0 ]; - } - case SCHED_HOUND_HOP_RETREAT: - { - return &slHoundHopRetreat[ 0 ]; - } - case SCHED_FAIL: - { - if ( m_MonsterState == MONSTERSTATE_COMBAT ) - { - if ( !FNullEnt( FIND_CLIENT_IN_PVS( edict() ) ) ) - { - // client in PVS - return &slHoundCombatFailPVS[ 0 ]; - } - else - { - // client has taken off! - return &slHoundCombatFailNoPVS[ 0 ]; - } - } - else - { - return CSquadMonster :: GetScheduleOfType ( Type ); - } - } - default: - { - return CSquadMonster :: GetScheduleOfType ( Type ); - } - } -} - -//========================================================= -// GetSchedule -//========================================================= -Schedule_t *CHoundeye :: GetSchedule( void ) -{ - switch ( m_MonsterState ) - { - case MONSTERSTATE_COMBAT: - { -// dead enemy - if ( HasConditions( bits_COND_ENEMY_DEAD ) ) - { - // call base class, all code to handle dead enemies is centralized there. - return CBaseMonster :: GetSchedule(); - } - - if ( HasConditions( bits_COND_LIGHT_DAMAGE | bits_COND_HEAVY_DAMAGE ) ) - { - if ( RANDOM_FLOAT( 0 , 1 ) <= 0.4 ) - { - TraceResult tr; - UTIL_MakeVectors( pev->angles ); - UTIL_TraceHull( pev->origin, pev->origin + gpGlobals->v_forward * -128, dont_ignore_monsters, head_hull, ENT( pev ), &tr ); - - if ( tr.flFraction == 1.0 ) - { - // it's clear behind, so the hound will jump - return GetScheduleOfType ( SCHED_HOUND_HOP_RETREAT ); - } - } - - return GetScheduleOfType ( SCHED_TAKE_COVER_FROM_ENEMY ); - } - - if ( HasConditions( bits_COND_CAN_RANGE_ATTACK1 ) ) - { - if ( OccupySlot ( bits_SLOTS_HOUND_ATTACK ) ) - { - return GetScheduleOfType ( SCHED_RANGE_ATTACK1 ); - } - - return GetScheduleOfType ( SCHED_HOUND_AGITATED ); - } - break; - } - } - - return CSquadMonster :: GetSchedule(); -} +/*** +* +* Copyright (c) 1996-2002, 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. +* +* This source code contains proprietary and confidential information of +* Valve LLC and its suppliers. Access to this code is restricted to +* persons who have executed a written SDK license with Valve. Any access, +* use or distribution of this code by or to any unlicensed person is illegal. +* +****/ +//========================================================= +// Houndeye - spooky sonic dog. +//========================================================= + +#include "extdll.h" +#include "util.h" +#include "cbase.h" +#include "monsters.h" +#include "schedule.h" +#include "animation.h" +#include "nodes.h" +#include "squadmonster.h" +#include "soundent.h" +#include "game.h" + +extern CGraph WorldGraph; + +// houndeye does 20 points of damage spread over a sphere 384 units in diameter, and each additional +// squad member increases the BASE damage by 110%, per the spec. +#define HOUNDEYE_MAX_SQUAD_SIZE 4 +#define HOUNDEYE_MAX_ATTACK_RADIUS 384 +#define HOUNDEYE_SQUAD_BONUS (float)1.1 + +#define HOUNDEYE_EYE_FRAMES 4 // how many different switchable maps for the eye + +#define HOUNDEYE_SOUND_STARTLE_VOLUME 128 // how loud a sound has to be to badly scare a sleeping houndeye + +//========================================================= +// monster-specific tasks +//========================================================= +enum +{ + TASK_HOUND_CLOSE_EYE = LAST_COMMON_TASK + 1, + TASK_HOUND_OPEN_EYE, + TASK_HOUND_THREAT_DISPLAY, + TASK_HOUND_FALL_ASLEEP, + TASK_HOUND_WAKE_UP, + TASK_HOUND_HOP_BACK +}; + +//========================================================= +// monster-specific schedule types +//========================================================= +enum +{ + SCHED_HOUND_AGITATED = LAST_COMMON_SCHEDULE + 1, + SCHED_HOUND_HOP_RETREAT, + SCHED_HOUND_FAIL, +}; + +//========================================================= +// Monster's Anim Events Go Here +//========================================================= +#define HOUND_AE_WARN 1 +#define HOUND_AE_STARTATTACK 2 +#define HOUND_AE_THUMP 3 +#define HOUND_AE_ANGERSOUND1 4 +#define HOUND_AE_ANGERSOUND2 5 +#define HOUND_AE_HOPBACK 6 +#define HOUND_AE_CLOSE_EYE 7 + +class CHoundeye : public CSquadMonster +{ +public: + void Spawn( void ); + void Precache( void ); + int Classify ( void ); + void HandleAnimEvent( MonsterEvent_t *pEvent ); + void SetYawSpeed ( void ); + void WarmUpSound ( void ); + void AlertSound( void ); + void DeathSound( void ); + void WarnSound( void ); + void PainSound( void ); + void IdleSound( void ); + void StartTask( Task_t *pTask ); + void RunTask ( Task_t *pTask ); + void SonicAttack( void ); + void PrescheduleThink( void ); + void SetActivity ( Activity NewActivity ); + void WriteBeamColor ( void ); + BOOL CheckRangeAttack1 ( float flDot, float flDist ); + BOOL FValidateHintType ( short sHint ); + BOOL FCanActiveIdle ( void ); + Schedule_t *GetScheduleOfType ( int Type ); + Schedule_t *CHoundeye :: GetSchedule( void ); + + int Save( CSave &save ); + int Restore( CRestore &restore ); + + CUSTOM_SCHEDULES; + static TYPEDESCRIPTION m_SaveData[]; + + int m_iSpriteTexture; + BOOL m_fAsleep;// some houndeyes sleep in idle mode if this is set, the houndeye is lying down + BOOL m_fDontBlink;// don't try to open/close eye if this bit is set! + Vector m_vecPackCenter; // the center of the pack. The leader maintains this by averaging the origins of all pack members. +}; +LINK_ENTITY_TO_CLASS( monster_houndeye, CHoundeye ); + +TYPEDESCRIPTION CHoundeye::m_SaveData[] = +{ + DEFINE_FIELD( CHoundeye, m_iSpriteTexture, FIELD_INTEGER ), + DEFINE_FIELD( CHoundeye, m_fAsleep, FIELD_BOOLEAN ), + DEFINE_FIELD( CHoundeye, m_fDontBlink, FIELD_BOOLEAN ), + DEFINE_FIELD( CHoundeye, m_vecPackCenter, FIELD_POSITION_VECTOR ), +}; + +IMPLEMENT_SAVERESTORE( CHoundeye, CSquadMonster ); + +//========================================================= +// Classify - indicates this monster's place in the +// relationship table. +//========================================================= +int CHoundeye :: Classify ( void ) +{ + return CLASS_ALIEN_MONSTER; +} + +//========================================================= +// FValidateHintType +//========================================================= +BOOL CHoundeye :: FValidateHintType ( short sHint ) +{ + int i; + + static short sHoundHints[] = + { + HINT_WORLD_MACHINERY, + HINT_WORLD_BLINKING_LIGHT, + HINT_WORLD_HUMAN_BLOOD, + HINT_WORLD_ALIEN_BLOOD, + }; + + for ( i = 0 ; i < ARRAYSIZE ( sHoundHints ) ; i++ ) + { + if ( sHoundHints[ i ] == sHint ) + { + return TRUE; + } + } + + ALERT ( at_aiconsole, "Couldn't validate hint type" ); + return FALSE; +} + + +//========================================================= +// FCanActiveIdle +//========================================================= +BOOL CHoundeye :: FCanActiveIdle ( void ) +{ + if ( InSquad() ) + { + CSquadMonster *pSquadLeader = MySquadLeader(); + + for (int i = 0; i < MAX_SQUAD_MEMBERS;i++) + { + CSquadMonster *pMember = pSquadLeader->MySquadMember(i); + + if ( pMember != NULL && pMember != this && pMember->m_iHintNode != NO_NODE ) + { + // someone else in the group is active idling right now! + return FALSE; + } + } + + return TRUE; + } + + return TRUE; +} + + +//========================================================= +// CheckRangeAttack1 - overridden for houndeyes so that they +// try to get within half of their max attack radius before +// attacking, so as to increase their chances of doing damage. +//========================================================= +BOOL CHoundeye :: CheckRangeAttack1 ( float flDot, float flDist ) +{ + if ( flDist <= ( HOUNDEYE_MAX_ATTACK_RADIUS * 0.5 ) && flDot >= 0.3 ) + { + return TRUE; + } + return FALSE; +} + +//========================================================= +// SetYawSpeed - allows each sequence to have a different +// turn rate associated with it. +//========================================================= +void CHoundeye :: SetYawSpeed ( void ) +{ + int ys; + + ys = 90; + + switch ( m_Activity ) + { + case ACT_CROUCHIDLE://sleeping! + ys = 0; + break; + case ACT_IDLE: + ys = 60; + break; + case ACT_WALK: + ys = 90; + break; + case ACT_RUN: + ys = 90; + break; + case ACT_TURN_LEFT: + case ACT_TURN_RIGHT: + ys = 90; + break; + } + + pev->yaw_speed = ys; +} + +//========================================================= +// SetActivity +//========================================================= +void CHoundeye :: SetActivity ( Activity NewActivity ) +{ + int iSequence; + + if ( NewActivity == m_Activity ) + return; + + if ( m_MonsterState == MONSTERSTATE_COMBAT && NewActivity == ACT_IDLE && RANDOM_LONG(0,1) ) + { + // play pissed idle. + iSequence = LookupSequence( "madidle" ); + + m_Activity = NewActivity; // Go ahead and set this so it doesn't keep trying when the anim is not present + + // In case someone calls this with something other than the ideal activity + m_IdealActivity = m_Activity; + + // Set to the desired anim, or default anim if the desired is not present + if ( iSequence > ACTIVITY_NOT_AVAILABLE ) + { + pev->sequence = iSequence; // Set to the reset anim (if it's there) + pev->frame = 0; // FIX: frame counter shouldn't be reset when its the same activity as before + ResetSequenceInfo(); + SetYawSpeed(); + } + } + else + { + CSquadMonster :: SetActivity ( NewActivity ); + } +} + +//========================================================= +// HandleAnimEvent - catches the monster-specific messages +// that occur when tagged animation frames are played. +//========================================================= +void CHoundeye :: HandleAnimEvent( MonsterEvent_t *pEvent ) +{ + switch ( pEvent->event ) + { + case HOUND_AE_WARN: + // do stuff for this event. + WarnSound(); + break; + + case HOUND_AE_STARTATTACK: + WarmUpSound(); + break; + + case HOUND_AE_HOPBACK: + { + float flGravity = g_psv_gravity->value; + + pev->flags &= ~FL_ONGROUND; + + pev->velocity = gpGlobals->v_forward * -200; + pev->velocity.z += (0.6 * flGravity) * 0.5; + + break; + } + + case HOUND_AE_THUMP: + // emit the shockwaves + SonicAttack(); + break; + + case HOUND_AE_ANGERSOUND1: + EMIT_SOUND(ENT(pev), CHAN_VOICE, "houndeye/he_pain3.wav", 1, ATTN_NORM); + break; + + case HOUND_AE_ANGERSOUND2: + EMIT_SOUND(ENT(pev), CHAN_VOICE, "houndeye/he_pain1.wav", 1, ATTN_NORM); + break; + + case HOUND_AE_CLOSE_EYE: + if ( !m_fDontBlink ) + { + pev->skin = HOUNDEYE_EYE_FRAMES - 1; + } + break; + + default: + CSquadMonster::HandleAnimEvent( pEvent ); + break; + } +} + +//========================================================= +// Spawn +//========================================================= +void CHoundeye :: Spawn() +{ + Precache( ); + + SET_MODEL(ENT(pev), "models/houndeye.mdl"); + UTIL_SetSize(pev, Vector ( -16, -16, 0 ), Vector ( 16, 16, 36 ) ); + + pev->solid = SOLID_SLIDEBOX; + pev->movetype = MOVETYPE_STEP; + m_bloodColor = BLOOD_COLOR_YELLOW; + pev->effects = 0; + pev->health = gSkillData.houndeyeHealth; + pev->yaw_speed = 5;//!!! should we put this in the monster's changeanim function since turn rates may vary with state/anim? + m_flFieldOfView = 0.5;// indicates the width of this monster's forward view cone ( as a dotproduct result ) + m_MonsterState = MONSTERSTATE_NONE; + m_fAsleep = FALSE; // everyone spawns awake + m_fDontBlink = FALSE; + m_afCapability |= bits_CAP_SQUAD; + + MonsterInit(); +} + +//========================================================= +// Precache - precaches all resources this monster needs +//========================================================= +void CHoundeye :: Precache() +{ + PRECACHE_MODEL("models/houndeye.mdl"); + + PRECACHE_SOUND("houndeye/he_alert1.wav"); + PRECACHE_SOUND("houndeye/he_alert2.wav"); + PRECACHE_SOUND("houndeye/he_alert3.wav"); + + PRECACHE_SOUND("houndeye/he_die1.wav"); + PRECACHE_SOUND("houndeye/he_die2.wav"); + PRECACHE_SOUND("houndeye/he_die3.wav"); + + PRECACHE_SOUND("houndeye/he_idle1.wav"); + PRECACHE_SOUND("houndeye/he_idle2.wav"); + PRECACHE_SOUND("houndeye/he_idle3.wav"); + + PRECACHE_SOUND("houndeye/he_hunt1.wav"); + PRECACHE_SOUND("houndeye/he_hunt2.wav"); + PRECACHE_SOUND("houndeye/he_hunt3.wav"); + + PRECACHE_SOUND("houndeye/he_pain1.wav"); + PRECACHE_SOUND("houndeye/he_pain3.wav"); + PRECACHE_SOUND("houndeye/he_pain4.wav"); + PRECACHE_SOUND("houndeye/he_pain5.wav"); + + PRECACHE_SOUND("houndeye/he_attack1.wav"); + PRECACHE_SOUND("houndeye/he_attack3.wav"); + + PRECACHE_SOUND("houndeye/he_blast1.wav"); + PRECACHE_SOUND("houndeye/he_blast2.wav"); + PRECACHE_SOUND("houndeye/he_blast3.wav"); + + m_iSpriteTexture = PRECACHE_MODEL( "sprites/shockwave.spr" ); +} + +//========================================================= +// IdleSound +//========================================================= +void CHoundeye :: IdleSound ( void ) +{ + switch ( RANDOM_LONG(0,2) ) + { + case 0: + EMIT_SOUND( ENT(pev), CHAN_VOICE, "houndeye/he_idle1.wav", 1, ATTN_NORM ); + break; + case 1: + EMIT_SOUND( ENT(pev), CHAN_VOICE, "houndeye/he_idle2.wav", 1, ATTN_NORM ); + break; + case 2: + EMIT_SOUND( ENT(pev), CHAN_VOICE, "houndeye/he_idle3.wav", 1, ATTN_NORM ); + break; + } +} + +//========================================================= +// IdleSound +//========================================================= +void CHoundeye :: WarmUpSound ( void ) +{ + switch ( RANDOM_LONG(0,1) ) + { + case 0: + EMIT_SOUND( ENT(pev), CHAN_WEAPON, "houndeye/he_attack1.wav", 0.7, ATTN_NORM ); + break; + case 1: + EMIT_SOUND( ENT(pev), CHAN_WEAPON, "houndeye/he_attack3.wav", 0.7, ATTN_NORM ); + break; + } +} + +//========================================================= +// WarnSound +//========================================================= +void CHoundeye :: WarnSound ( void ) +{ + switch ( RANDOM_LONG(0,2) ) + { + case 0: + EMIT_SOUND( ENT(pev), CHAN_VOICE, "houndeye/he_hunt1.wav", 1, ATTN_NORM ); + break; + case 1: + EMIT_SOUND( ENT(pev), CHAN_VOICE, "houndeye/he_hunt2.wav", 1, ATTN_NORM ); + break; + case 2: + EMIT_SOUND( ENT(pev), CHAN_VOICE, "houndeye/he_hunt3.wav", 1, ATTN_NORM ); + break; + } +} + +//========================================================= +// AlertSound +//========================================================= +void CHoundeye :: AlertSound ( void ) +{ + + if ( InSquad() && !IsLeader() ) + { + return; // only leader makes ALERT sound. + } + + switch ( RANDOM_LONG(0,2) ) + { + case 0: + EMIT_SOUND( ENT(pev), CHAN_VOICE, "houndeye/he_alert1.wav", 1, ATTN_NORM ); + break; + case 1: + EMIT_SOUND( ENT(pev), CHAN_VOICE, "houndeye/he_alert2.wav", 1, ATTN_NORM ); + break; + case 2: + EMIT_SOUND( ENT(pev), CHAN_VOICE, "houndeye/he_alert3.wav", 1, ATTN_NORM ); + break; + } +} + +//========================================================= +// DeathSound +//========================================================= +void CHoundeye :: DeathSound ( void ) +{ + switch ( RANDOM_LONG(0,2) ) + { + case 0: + EMIT_SOUND( ENT(pev), CHAN_VOICE, "houndeye/he_die1.wav", 1, ATTN_NORM ); + break; + case 1: + EMIT_SOUND( ENT(pev), CHAN_VOICE, "houndeye/he_die2.wav", 1, ATTN_NORM ); + break; + case 2: + EMIT_SOUND( ENT(pev), CHAN_VOICE, "houndeye/he_die3.wav", 1, ATTN_NORM ); + break; + } +} + +//========================================================= +// PainSound +//========================================================= +void CHoundeye :: PainSound ( void ) +{ + switch ( RANDOM_LONG(0,2) ) + { + case 0: + EMIT_SOUND( ENT(pev), CHAN_VOICE, "houndeye/he_pain3.wav", 1, ATTN_NORM ); + break; + case 1: + EMIT_SOUND( ENT(pev), CHAN_VOICE, "houndeye/he_pain4.wav", 1, ATTN_NORM ); + break; + case 2: + EMIT_SOUND( ENT(pev), CHAN_VOICE, "houndeye/he_pain5.wav", 1, ATTN_NORM ); + break; + } +} + +//========================================================= +// WriteBeamColor - writes a color vector to the network +// based on the size of the group. +//========================================================= +void CHoundeye :: WriteBeamColor ( void ) +{ + BYTE bRed, bGreen, bBlue; + + if ( InSquad() ) + { + switch ( SquadCount() ) + { + case 2: + // no case for 0 or 1, cause those are impossible for monsters in Squads. + bRed = 101; + bGreen = 133; + bBlue = 221; + break; + case 3: + bRed = 67; + bGreen = 85; + bBlue = 255; + break; + case 4: + bRed = 62; + bGreen = 33; + bBlue = 211; + break; + default: + ALERT ( at_aiconsole, "Unsupported Houndeye SquadSize!\n" ); + bRed = 188; + bGreen = 220; + bBlue = 255; + break; + } + } + else + { + // solo houndeye - weakest beam + bRed = 188; + bGreen = 220; + bBlue = 255; + } + + WRITE_BYTE( bRed ); + WRITE_BYTE( bGreen ); + WRITE_BYTE( bBlue ); +} + + +//========================================================= +// SonicAttack +//========================================================= +void CHoundeye :: SonicAttack ( void ) +{ + float flAdjustedDamage; + float flDist; + + switch ( RANDOM_LONG( 0, 2 ) ) + { + case 0: EMIT_SOUND(ENT(pev), CHAN_WEAPON, "houndeye/he_blast1.wav", 1, ATTN_NORM); break; + case 1: EMIT_SOUND(ENT(pev), CHAN_WEAPON, "houndeye/he_blast2.wav", 1, ATTN_NORM); break; + case 2: EMIT_SOUND(ENT(pev), CHAN_WEAPON, "houndeye/he_blast3.wav", 1, ATTN_NORM); break; + } + + // blast circles + MESSAGE_BEGIN( MSG_PAS, SVC_TEMPENTITY, pev->origin ); + WRITE_BYTE( TE_BEAMCYLINDER ); + WRITE_COORD( pev->origin.x); + WRITE_COORD( pev->origin.y); + WRITE_COORD( pev->origin.z + 16); + WRITE_COORD( pev->origin.x); + WRITE_COORD( pev->origin.y); + WRITE_COORD( pev->origin.z + 16 + HOUNDEYE_MAX_ATTACK_RADIUS / .2); // reach damage radius over .3 seconds + WRITE_SHORT( m_iSpriteTexture ); + WRITE_BYTE( 0 ); // startframe + WRITE_BYTE( 0 ); // framerate + WRITE_BYTE( 2 ); // life + WRITE_BYTE( 16 ); // width + WRITE_BYTE( 0 ); // noise + + WriteBeamColor(); + + WRITE_BYTE( 255 ); //brightness + WRITE_BYTE( 0 ); // speed + MESSAGE_END(); + + MESSAGE_BEGIN( MSG_PAS, SVC_TEMPENTITY, pev->origin ); + WRITE_BYTE( TE_BEAMCYLINDER ); + WRITE_COORD( pev->origin.x); + WRITE_COORD( pev->origin.y); + WRITE_COORD( pev->origin.z + 16); + WRITE_COORD( pev->origin.x); + WRITE_COORD( pev->origin.y); + WRITE_COORD( pev->origin.z + 16 + ( HOUNDEYE_MAX_ATTACK_RADIUS / 2 ) / .2); // reach damage radius over .3 seconds + WRITE_SHORT( m_iSpriteTexture ); + WRITE_BYTE( 0 ); // startframe + WRITE_BYTE( 0 ); // framerate + WRITE_BYTE( 2 ); // life + WRITE_BYTE( 16 ); // width + WRITE_BYTE( 0 ); // noise + + WriteBeamColor(); + + WRITE_BYTE( 255 ); //brightness + WRITE_BYTE( 0 ); // speed + MESSAGE_END(); + + + CBaseEntity *pEntity = NULL; + // iterate on all entities in the vicinity. + while ((pEntity = UTIL_FindEntityInSphere( pEntity, pev->origin, HOUNDEYE_MAX_ATTACK_RADIUS )) != NULL) + { + if ( pEntity->pev->takedamage != DAMAGE_NO ) + { + if ( !FClassnameIs(pEntity->pev, "monster_houndeye") ) + {// houndeyes don't hurt other houndeyes with their attack + + // houndeyes do FULL damage if the ent in question is visible. Half damage otherwise. + // This means that you must get out of the houndeye's attack range entirely to avoid damage. + // Calculate full damage first + + if ( SquadCount() > 1 ) + { + // squad gets attack bonus. + flAdjustedDamage = gSkillData.houndeyeDmgBlast + gSkillData.houndeyeDmgBlast * ( HOUNDEYE_SQUAD_BONUS * ( SquadCount() - 1 ) ); + } + else + { + // solo + flAdjustedDamage = gSkillData.houndeyeDmgBlast; + } + + flDist = (pEntity->Center() - pev->origin).Length(); + + flAdjustedDamage -= ( flDist / HOUNDEYE_MAX_ATTACK_RADIUS ) * flAdjustedDamage; + + if ( !FVisible( pEntity ) ) + { + if ( pEntity->IsPlayer() ) + { + // if this entity is a client, and is not in full view, inflict half damage. We do this so that players still + // take the residual damage if they don't totally leave the houndeye's effective radius. We restrict it to clients + // so that monsters in other parts of the level don't take the damage and get pissed. + flAdjustedDamage *= 0.5; + } + else if ( !FClassnameIs( pEntity->pev, "func_breakable" ) && !FClassnameIs( pEntity->pev, "func_pushable" ) ) + { + // do not hurt nonclients through walls, but allow damage to be done to breakables + flAdjustedDamage = 0; + } + } + + //ALERT ( at_aiconsole, "Damage: %f\n", flAdjustedDamage ); + + if (flAdjustedDamage > 0 ) + { + pEntity->TakeDamage ( pev, pev, flAdjustedDamage, DMG_SONIC | DMG_ALWAYSGIB ); + } + } + } + } +} + +//========================================================= +// start task +//========================================================= +void CHoundeye :: StartTask ( Task_t *pTask ) +{ + m_iTaskStatus = TASKSTATUS_RUNNING; + + switch ( pTask->iTask ) + { + case TASK_HOUND_FALL_ASLEEP: + { + m_fAsleep = TRUE; // signal that hound is lying down (must stand again before doing anything else!) + m_iTaskStatus = TASKSTATUS_COMPLETE; + break; + } + case TASK_HOUND_WAKE_UP: + { + m_fAsleep = FALSE; // signal that hound is standing again + m_iTaskStatus = TASKSTATUS_COMPLETE; + break; + } + case TASK_HOUND_OPEN_EYE: + { + m_fDontBlink = FALSE; // turn blinking back on and that code will automatically open the eye + m_iTaskStatus = TASKSTATUS_COMPLETE; + break; + } + case TASK_HOUND_CLOSE_EYE: + { + pev->skin = 0; + m_fDontBlink = TRUE; // tell blink code to leave the eye alone. + break; + } + case TASK_HOUND_THREAT_DISPLAY: + { + m_IdealActivity = ACT_IDLE_ANGRY; + break; + } + case TASK_HOUND_HOP_BACK: + { + m_IdealActivity = ACT_LEAP; + break; + } + case TASK_RANGE_ATTACK1: + { + m_IdealActivity = ACT_RANGE_ATTACK1; + +/* + if ( InSquad() ) + { + // see if there is a battery to connect to. + CSquadMonster *pSquad = m_pSquadLeader; + + while ( pSquad ) + { + if ( pSquad->m_iMySlot == bits_SLOT_HOUND_BATTERY ) + { + // draw a beam. + MESSAGE_BEGIN( MSG_BROADCAST, SVC_TEMPENTITY ); + WRITE_BYTE( TE_BEAMENTS ); + WRITE_SHORT( ENTINDEX( this->edict() ) ); + WRITE_SHORT( ENTINDEX( pSquad->edict() ) ); + WRITE_SHORT( m_iSpriteTexture ); + WRITE_BYTE( 0 ); // framestart + WRITE_BYTE( 0 ); // framerate + WRITE_BYTE( 10 ); // life + WRITE_BYTE( 40 ); // width + WRITE_BYTE( 10 ); // noise + WRITE_BYTE( 0 ); // r, g, b + WRITE_BYTE( 50 ); // r, g, b + WRITE_BYTE( 250); // r, g, b + WRITE_BYTE( 255 ); // brightness + WRITE_BYTE( 30 ); // speed + MESSAGE_END(); + break; + } + + pSquad = pSquad->m_pSquadNext; + } + } +*/ + + break; + } + case TASK_SPECIAL_ATTACK1: + { + m_IdealActivity = ACT_SPECIAL_ATTACK1; + break; + } + case TASK_GUARD: + { + m_IdealActivity = ACT_GUARD; + break; + } + default: + { + CSquadMonster :: StartTask(pTask); + break; + } + } +} + +//========================================================= +// RunTask +//========================================================= +void CHoundeye :: RunTask ( Task_t *pTask ) +{ + switch ( pTask->iTask ) + { + case TASK_HOUND_THREAT_DISPLAY: + { + MakeIdealYaw ( m_vecEnemyLKP ); + ChangeYaw ( pev->yaw_speed ); + + if ( m_fSequenceFinished ) + { + TaskComplete(); + } + + break; + } + case TASK_HOUND_CLOSE_EYE: + { + if ( pev->skin < HOUNDEYE_EYE_FRAMES - 1 ) + { + pev->skin++; + } + break; + } + case TASK_HOUND_HOP_BACK: + { + if ( m_fSequenceFinished ) + { + TaskComplete(); + } + break; + } + case TASK_SPECIAL_ATTACK1: + { + pev->skin = RANDOM_LONG(0, HOUNDEYE_EYE_FRAMES - 1); + + MakeIdealYaw ( m_vecEnemyLKP ); + ChangeYaw ( pev->yaw_speed ); + + float life; + life = ((255 - pev->frame) / (pev->framerate * m_flFrameRate)); + if (life < 0.1) life = 0.1; + + MESSAGE_BEGIN( MSG_PAS, SVC_TEMPENTITY, pev->origin ); + WRITE_BYTE( TE_IMPLOSION); + WRITE_COORD( pev->origin.x); + WRITE_COORD( pev->origin.y); + WRITE_COORD( pev->origin.z + 16); + WRITE_BYTE( 50 * life + 100); // radius + WRITE_BYTE( pev->frame / 25.0 ); // count + WRITE_BYTE( life * 10 ); // life + MESSAGE_END(); + + if ( m_fSequenceFinished ) + { + SonicAttack(); + TaskComplete(); + } + + break; + } + default: + { + CSquadMonster :: RunTask(pTask); + break; + } + } +} + +//========================================================= +// PrescheduleThink +//========================================================= +void CHoundeye::PrescheduleThink ( void ) +{ + // if the hound is mad and is running, make hunt noises. + if ( m_MonsterState == MONSTERSTATE_COMBAT && m_Activity == ACT_RUN && RANDOM_FLOAT( 0, 1 ) < 0.2 ) + { + WarnSound(); + } + + // at random, initiate a blink if not already blinking or sleeping + if ( !m_fDontBlink ) + { + if ( ( pev->skin == 0 ) && RANDOM_LONG(0,0x7F) == 0 ) + {// start blinking! + pev->skin = HOUNDEYE_EYE_FRAMES - 1; + } + else if ( pev->skin != 0 ) + {// already blinking + pev->skin--; + } + } + + // if you are the leader, average the origins of each pack member to get an approximate center. + if ( IsLeader() ) + { + CSquadMonster *pSquadMember; + int iSquadCount = 0; + + for (int i = 0; i < MAX_SQUAD_MEMBERS; i++) + { + pSquadMember = MySquadMember(i); + + if (pSquadMember) + { + iSquadCount++; + m_vecPackCenter = m_vecPackCenter + pSquadMember->pev->origin; + } + } + + m_vecPackCenter = m_vecPackCenter / iSquadCount; + } +} + +//========================================================= +// AI Schedules Specific to this monster +//========================================================= +Task_t tlHoundGuardPack[] = +{ + { TASK_STOP_MOVING, (float)0 }, + { TASK_GUARD, (float)0 }, +}; + +Schedule_t slHoundGuardPack[] = +{ + { + tlHoundGuardPack, + ARRAYSIZE ( tlHoundGuardPack ), + bits_COND_SEE_HATE | + bits_COND_LIGHT_DAMAGE | + bits_COND_HEAVY_DAMAGE | + bits_COND_PROVOKED | + bits_COND_HEAR_SOUND, + + bits_SOUND_COMBAT |// sound flags + bits_SOUND_WORLD | + bits_SOUND_MEAT | + bits_SOUND_PLAYER, + "GuardPack" + }, +}; + +// primary range attack +Task_t tlHoundYell1[] = +{ + { TASK_STOP_MOVING, (float)0 }, + { TASK_FACE_IDEAL, (float)0 }, + { TASK_RANGE_ATTACK1, (float)0 }, + { TASK_SET_SCHEDULE, (float)SCHED_HOUND_AGITATED }, +}; + +Task_t tlHoundYell2[] = +{ + { TASK_STOP_MOVING, (float)0 }, + { TASK_FACE_IDEAL, (float)0 }, + { TASK_RANGE_ATTACK1, (float)0 }, +}; + +Schedule_t slHoundRangeAttack[] = +{ + { + tlHoundYell1, + ARRAYSIZE ( tlHoundYell1 ), + bits_COND_LIGHT_DAMAGE | + bits_COND_HEAVY_DAMAGE, + 0, + "HoundRangeAttack1" + }, + { + tlHoundYell2, + ARRAYSIZE ( tlHoundYell2 ), + bits_COND_LIGHT_DAMAGE | + bits_COND_HEAVY_DAMAGE, + 0, + "HoundRangeAttack2" + }, +}; + +// lie down and fall asleep +Task_t tlHoundSleep[] = +{ + { TASK_STOP_MOVING, (float)0 }, + { TASK_SET_ACTIVITY, (float)ACT_IDLE }, + { TASK_WAIT_RANDOM, (float)5 }, + { TASK_PLAY_SEQUENCE, (float)ACT_CROUCH }, + { TASK_SET_ACTIVITY, (float)ACT_CROUCHIDLE }, + { TASK_HOUND_FALL_ASLEEP, (float)0 }, + { TASK_WAIT_RANDOM, (float)25 }, + { TASK_HOUND_CLOSE_EYE, (float)0 }, + //{ TASK_WAIT, (float)10 }, + //{ TASK_WAIT_RANDOM, (float)10 }, +}; + +Schedule_t slHoundSleep[] = +{ + { + tlHoundSleep, + ARRAYSIZE ( tlHoundSleep ), + bits_COND_HEAR_SOUND | + bits_COND_LIGHT_DAMAGE | + bits_COND_HEAVY_DAMAGE | + bits_COND_NEW_ENEMY, + + bits_SOUND_COMBAT | + bits_SOUND_PLAYER | + bits_SOUND_WORLD, + "Hound Sleep" + }, +}; + +// wake and stand up lazily +Task_t tlHoundWakeLazy[] = +{ + { TASK_STOP_MOVING, (float)0 }, + { TASK_HOUND_OPEN_EYE, (float)0 }, + { TASK_WAIT_RANDOM, (float)2.5 }, + { TASK_PLAY_SEQUENCE, (float)ACT_STAND }, + { TASK_HOUND_WAKE_UP, (float)0 }, +}; + +Schedule_t slHoundWakeLazy[] = +{ + { + tlHoundWakeLazy, + ARRAYSIZE ( tlHoundWakeLazy ), + 0, + 0, + "WakeLazy" + }, +}; + +// wake and stand up with great urgency! +Task_t tlHoundWakeUrgent[] = +{ + { TASK_HOUND_OPEN_EYE, (float)0 }, + { TASK_PLAY_SEQUENCE, (float)ACT_HOP }, + { TASK_FACE_IDEAL, (float)0 }, + { TASK_HOUND_WAKE_UP, (float)0 }, +}; + +Schedule_t slHoundWakeUrgent[] = +{ + { + tlHoundWakeUrgent, + ARRAYSIZE ( tlHoundWakeUrgent ), + 0, + 0, + "WakeUrgent" + }, +}; + + +Task_t tlHoundSpecialAttack1[] = +{ + { TASK_STOP_MOVING, 0 }, + { TASK_FACE_IDEAL, (float)0 }, + { TASK_SPECIAL_ATTACK1, (float)0 }, + { TASK_PLAY_SEQUENCE, (float)ACT_IDLE_ANGRY }, +}; + +Schedule_t slHoundSpecialAttack1[] = +{ + { + tlHoundSpecialAttack1, + ARRAYSIZE ( tlHoundSpecialAttack1 ), + bits_COND_NEW_ENEMY | + bits_COND_LIGHT_DAMAGE | + bits_COND_HEAVY_DAMAGE | + bits_COND_ENEMY_OCCLUDED, + + 0, + "Hound Special Attack1" + }, +}; + +Task_t tlHoundAgitated[] = +{ + { TASK_STOP_MOVING, 0 }, + { TASK_HOUND_THREAT_DISPLAY, 0 }, +}; + +Schedule_t slHoundAgitated[] = +{ + { + tlHoundAgitated, + ARRAYSIZE ( tlHoundAgitated ), + bits_COND_NEW_ENEMY | + bits_COND_LIGHT_DAMAGE | + bits_COND_HEAVY_DAMAGE, + 0, + "Hound Agitated" + }, +}; + +Task_t tlHoundHopRetreat[] = +{ + { TASK_STOP_MOVING, 0 }, + { TASK_HOUND_HOP_BACK, 0 }, + { TASK_SET_SCHEDULE, (float)SCHED_TAKE_COVER_FROM_ENEMY }, +}; + +Schedule_t slHoundHopRetreat[] = +{ + { + tlHoundHopRetreat, + ARRAYSIZE ( tlHoundHopRetreat ), + 0, + 0, + "Hound Hop Retreat" + }, +}; + +// hound fails in combat with client in the PVS +Task_t tlHoundCombatFailPVS[] = +{ + { TASK_STOP_MOVING, 0 }, + { TASK_HOUND_THREAT_DISPLAY, 0 }, + { TASK_WAIT_FACE_ENEMY, (float)1 }, +}; + +Schedule_t slHoundCombatFailPVS[] = +{ + { + tlHoundCombatFailPVS, + ARRAYSIZE ( tlHoundCombatFailPVS ), + bits_COND_NEW_ENEMY | + bits_COND_LIGHT_DAMAGE | + bits_COND_HEAVY_DAMAGE, + 0, + "HoundCombatFailPVS" + }, +}; + +// hound fails in combat with no client in the PVS. Don't keep peeping! +Task_t tlHoundCombatFailNoPVS[] = +{ + { TASK_STOP_MOVING, 0 }, + { TASK_HOUND_THREAT_DISPLAY, 0 }, + { TASK_WAIT_FACE_ENEMY, (float)2 }, + { TASK_SET_ACTIVITY, (float)ACT_IDLE }, + { TASK_WAIT_PVS, 0 }, +}; + +Schedule_t slHoundCombatFailNoPVS[] = +{ + { + tlHoundCombatFailNoPVS, + ARRAYSIZE ( tlHoundCombatFailNoPVS ), + bits_COND_NEW_ENEMY | + bits_COND_LIGHT_DAMAGE | + bits_COND_HEAVY_DAMAGE, + 0, + "HoundCombatFailNoPVS" + }, +}; + +DEFINE_CUSTOM_SCHEDULES( CHoundeye ) +{ + slHoundGuardPack, + slHoundRangeAttack, + &slHoundRangeAttack[ 1 ], + slHoundSleep, + slHoundWakeLazy, + slHoundWakeUrgent, + slHoundSpecialAttack1, + slHoundAgitated, + slHoundHopRetreat, + slHoundCombatFailPVS, + slHoundCombatFailNoPVS, +}; + +IMPLEMENT_CUSTOM_SCHEDULES( CHoundeye, CSquadMonster ); + +//========================================================= +// GetScheduleOfType +//========================================================= +Schedule_t* CHoundeye :: GetScheduleOfType ( int Type ) +{ + if ( m_fAsleep ) + { + // if the hound is sleeping, must wake and stand! + if ( HasConditions( bits_COND_HEAR_SOUND ) ) + { + CSound *pWakeSound; + + pWakeSound = PBestSound(); + ASSERT( pWakeSound != NULL ); + if ( pWakeSound ) + { + MakeIdealYaw ( pWakeSound->m_vecOrigin ); + + if ( FLSoundVolume ( pWakeSound ) >= HOUNDEYE_SOUND_STARTLE_VOLUME ) + { + // awakened by a loud sound + return &slHoundWakeUrgent[ 0 ]; + } + } + // sound was not loud enough to scare the bejesus out of houndeye + return &slHoundWakeLazy[ 0 ]; + } + else if ( HasConditions( bits_COND_NEW_ENEMY ) ) + { + // get up fast, to fight. + return &slHoundWakeUrgent[ 0 ]; + } + + else + { + // hound is waking up on its own + return &slHoundWakeLazy[ 0 ]; + } + } + switch ( Type ) + { + case SCHED_IDLE_STAND: + { + // we may want to sleep instead of stand! + if ( InSquad() && !IsLeader() && !m_fAsleep && RANDOM_LONG(0,29) < 1 ) + { + return &slHoundSleep[ 0 ]; + } + else + { + return CSquadMonster :: GetScheduleOfType( Type ); + } + } + case SCHED_RANGE_ATTACK1: + { + return &slHoundRangeAttack[ 0 ]; +/* + if ( InSquad() ) + { + return &slHoundRangeAttack[ RANDOM_LONG( 0, 1 ) ]; + } + + return &slHoundRangeAttack[ 1 ]; +*/ + } + case SCHED_SPECIAL_ATTACK1: + { + return &slHoundSpecialAttack1[ 0 ]; + } + case SCHED_GUARD: + { + return &slHoundGuardPack[ 0 ]; + } + case SCHED_HOUND_AGITATED: + { + return &slHoundAgitated[ 0 ]; + } + case SCHED_HOUND_HOP_RETREAT: + { + return &slHoundHopRetreat[ 0 ]; + } + case SCHED_FAIL: + { + if ( m_MonsterState == MONSTERSTATE_COMBAT ) + { + if ( !FNullEnt( FIND_CLIENT_IN_PVS( edict() ) ) ) + { + // client in PVS + return &slHoundCombatFailPVS[ 0 ]; + } + else + { + // client has taken off! + return &slHoundCombatFailNoPVS[ 0 ]; + } + } + else + { + return CSquadMonster :: GetScheduleOfType ( Type ); + } + } + default: + { + return CSquadMonster :: GetScheduleOfType ( Type ); + } + } +} + +//========================================================= +// GetSchedule +//========================================================= +Schedule_t *CHoundeye :: GetSchedule( void ) +{ + switch ( m_MonsterState ) + { + case MONSTERSTATE_COMBAT: + { +// dead enemy + if ( HasConditions( bits_COND_ENEMY_DEAD ) ) + { + // call base class, all code to handle dead enemies is centralized there. + return CBaseMonster :: GetSchedule(); + } + + if ( HasConditions( bits_COND_LIGHT_DAMAGE | bits_COND_HEAVY_DAMAGE ) ) + { + if ( RANDOM_FLOAT( 0 , 1 ) <= 0.4 ) + { + TraceResult tr; + UTIL_MakeVectors( pev->angles ); + UTIL_TraceHull( pev->origin, pev->origin + gpGlobals->v_forward * -128, dont_ignore_monsters, head_hull, ENT( pev ), &tr ); + + if ( tr.flFraction == 1.0 ) + { + // it's clear behind, so the hound will jump + return GetScheduleOfType ( SCHED_HOUND_HOP_RETREAT ); + } + } + + return GetScheduleOfType ( SCHED_TAKE_COVER_FROM_ENEMY ); + } + + if ( HasConditions( bits_COND_CAN_RANGE_ATTACK1 ) ) + { + if ( OccupySlot ( bits_SLOTS_HOUND_ATTACK ) ) + { + return GetScheduleOfType ( SCHED_RANGE_ATTACK1 ); + } + + return GetScheduleOfType ( SCHED_HOUND_AGITATED ); + } + break; + } + } + + return CSquadMonster :: GetSchedule(); +} diff --git a/spirit/ichthyosaur.cpp b/dlls/ichthyosaur.cpp similarity index 93% rename from spirit/ichthyosaur.cpp rename to dlls/ichthyosaur.cpp index cc56aaf5..b88abf15 100644 --- a/spirit/ichthyosaur.cpp +++ b/dlls/ichthyosaur.cpp @@ -326,7 +326,7 @@ IMPLEMENT_CUSTOM_SCHEDULES(CIchthyosaur, CFlyingMonster); //========================================================= int CIchthyosaur :: Classify ( void ) { - return m_iClass?m_iClass:CLASS_ALIEN_MONSTER; + return CLASS_ALIEN_MONSTER; } @@ -476,17 +476,13 @@ void CIchthyosaur :: Spawn() { Precache( ); - if (pev->model) - SET_MODEL(ENT(pev), STRING(pev->model)); //LRC - else - SET_MODEL(ENT(pev), "models/icky.mdl"); + SET_MODEL(ENT(pev), "models/icky.mdl"); UTIL_SetSize( pev, Vector( -32, -32, -32 ), Vector( 32, 32, 32 ) ); pev->solid = SOLID_BBOX; pev->movetype = MOVETYPE_FLY; m_bloodColor = BLOOD_COLOR_GREEN; - if (pev->health == 0) - pev->health = gSkillData.ichthyosaurHealth; + pev->health = gSkillData.ichthyosaurHealth; pev->view_ofs = Vector ( 0, 0, 16 ); m_flFieldOfView = VIEW_FIELD_WIDE; m_MonsterState = MONSTERSTATE_NONE; @@ -498,8 +494,8 @@ void CIchthyosaur :: Spawn() MonsterInit(); - SetTouch(&CIchthyosaur :: BiteTouch ); - SetUse(&CIchthyosaur :: CombatUse ); + SetTouch( BiteTouch ); + SetUse( CombatUse ); m_idealDist = 384; m_flMinSpeed = 80; @@ -517,10 +513,7 @@ void CIchthyosaur :: Spawn() //========================================================= void CIchthyosaur :: Precache() { - if (pev->model) - PRECACHE_MODEL((char*)STRING(pev->model)); //LRC - else - PRECACHE_MODEL("models/icky.mdl"); + PRECACHE_MODEL("models/icky.mdl"); PRECACHE_SOUND_ARRAY( pIdleSounds ); PRECACHE_SOUND_ARRAY( pAlertSounds ); @@ -736,7 +729,7 @@ void CIchthyosaur :: RunTask ( Task_t *pTask ) case TASK_ICHTHYOSAUR_FLOAT: pev->angles.x = UTIL_ApproachAngle( 0, pev->angles.x, 20 ); pev->velocity = pev->velocity * 0.8; - if (pev->waterlevel > 1 && pev->watertype != CONTENTS_FOG && pev->velocity.z < 64) + if (pev->waterlevel > 1 && pev->velocity.z < 64) { pev->velocity.z += 8; } @@ -1112,4 +1105,4 @@ Vector CIchthyosaur::DoProbe(const Vector &Probe) return Vector(0, 0, 0); } -#endif +#endif \ No newline at end of file diff --git a/spirit/islave.cpp b/dlls/islave.cpp similarity index 94% rename from spirit/islave.cpp rename to dlls/islave.cpp index 663e9e35..037001c3 100644 --- a/spirit/islave.cpp +++ b/dlls/islave.cpp @@ -1,876 +1,866 @@ -/*** -* -* Copyright (c) 1996-2002, 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. -* -* This source code contains proprietary and confidential information of -* Valve LLC and its suppliers. Access to this code is restricted to -* persons who have executed a written SDK license with Valve. Any access, -* use or distribution of this code by or to any unlicensed person is illegal. -* -****/ -//========================================================= -// Alien slave monster -//========================================================= - -#include "extdll.h" -#include "util.h" -#include "cbase.h" -#include "monsters.h" -#include "squadmonster.h" -#include "schedule.h" -#include "effects.h" -#include "weapons.h" -#include "soundent.h" - -extern DLL_GLOBAL int g_iSkillLevel; - -//========================================================= -// Monster's Anim Events Go Here -//========================================================= -#define ISLAVE_AE_CLAW ( 1 ) -#define ISLAVE_AE_CLAWRAKE ( 2 ) -#define ISLAVE_AE_ZAP_POWERUP ( 3 ) -#define ISLAVE_AE_ZAP_SHOOT ( 4 ) -#define ISLAVE_AE_ZAP_DONE ( 5 ) - -#define ISLAVE_MAX_BEAMS 8 - -class CISlave : public CSquadMonster -{ -public: - void Spawn( void ); - void Precache( void ); - void SetYawSpeed( void ); - int ISoundMask( void ); - int Classify ( void ); - int IRelationship( CBaseEntity *pTarget ); - void HandleAnimEvent( MonsterEvent_t *pEvent ); - BOOL CheckRangeAttack1 ( float flDot, float flDist ); - BOOL CheckRangeAttack2 ( float flDot, float flDist ); - void CallForHelp( char *szClassname, float flDist, EHANDLE hEnemy, Vector &vecLocation ); - void TraceAttack( entvars_t *pevAttacker, float flDamage, Vector vecDir, TraceResult *ptr, int bitsDamageType); - int TakeDamage( entvars_t* pevInflictor, entvars_t* pevAttacker, float flDamage, int bitsDamageType); - - void DeathSound( void ); - void PainSound( void ); - void AlertSound( void ); - void IdleSound( void ); - - void Killed( entvars_t *pevAttacker, int iGib ); - - void StartTask ( Task_t *pTask ); - Schedule_t *GetSchedule( void ); - Schedule_t *GetScheduleOfType ( int Type ); - CUSTOM_SCHEDULES; - - int Save( CSave &save ); - int Restore( CRestore &restore ); - static TYPEDESCRIPTION m_SaveData[]; - - void ClearBeams( ); - void ArmBeam( int side ); - void WackBeam( int side, CBaseEntity *pEntity ); - void ZapBeam( int side ); - void BeamGlow( void ); - - int m_iBravery; - - CBeam *m_pBeam[ISLAVE_MAX_BEAMS]; - - int m_iBeams; - float m_flNextAttack; - - int m_voicePitch; - - EHANDLE m_hDead; - - static const char *pAttackHitSounds[]; - static const char *pAttackMissSounds[]; - static const char *pPainSounds[]; - static const char *pDeathSounds[]; -}; -LINK_ENTITY_TO_CLASS( monster_alien_slave, CISlave ); -LINK_ENTITY_TO_CLASS( monster_vortigaunt, CISlave ); - - -TYPEDESCRIPTION CISlave::m_SaveData[] = -{ - DEFINE_FIELD( CISlave, m_iBravery, FIELD_INTEGER ), - - DEFINE_ARRAY( CISlave, m_pBeam, FIELD_CLASSPTR, ISLAVE_MAX_BEAMS ), - DEFINE_FIELD( CISlave, m_iBeams, FIELD_INTEGER ), - DEFINE_FIELD( CISlave, m_flNextAttack, FIELD_TIME ), - - DEFINE_FIELD( CISlave, m_voicePitch, FIELD_INTEGER ), - - DEFINE_FIELD( CISlave, m_hDead, FIELD_EHANDLE ), - -}; - -IMPLEMENT_SAVERESTORE( CISlave, CSquadMonster ); - - - - -const char *CISlave::pAttackHitSounds[] = -{ - "zombie/claw_strike1.wav", - "zombie/claw_strike2.wav", - "zombie/claw_strike3.wav", -}; - -const char *CISlave::pAttackMissSounds[] = -{ - "zombie/claw_miss1.wav", - "zombie/claw_miss2.wav", -}; - -const char *CISlave::pPainSounds[] = -{ - "aslave/slv_pain1.wav", - "aslave/slv_pain2.wav", -}; - -const char *CISlave::pDeathSounds[] = -{ - "aslave/slv_die1.wav", - "aslave/slv_die2.wav", -}; - -//========================================================= -// Classify - indicates this monster's place in the -// relationship table. -//========================================================= -int CISlave :: Classify ( void ) -{ - return m_iClass?m_iClass:CLASS_ALIEN_MILITARY; -} - - -int CISlave::IRelationship( CBaseEntity *pTarget ) -{ - if ( (pTarget->IsPlayer()) ) - if ( (pev->spawnflags & SF_MONSTER_WAIT_UNTIL_PROVOKED ) && ! (m_afMemory & bits_MEMORY_PROVOKED )) - return R_NO; - return CBaseMonster::IRelationship( pTarget ); -} - - -void CISlave :: CallForHelp( char *szClassname, float flDist, EHANDLE hEnemy, Vector &vecLocation ) -{ - // ALERT( at_aiconsole, "help " ); - - // skip ones not on my netname - if ( FStringNull( pev->netname )) - return; - - CBaseEntity *pEntity = NULL; - - while ((pEntity = UTIL_FindEntityByString( pEntity, "netname", STRING( pev->netname ))) != NULL) - { - float d = (pev->origin - pEntity->pev->origin).Length(); - if (d < flDist) - { - CBaseMonster *pMonster = pEntity->MyMonsterPointer( ); - if (pMonster) - { - pMonster->m_afMemory |= bits_MEMORY_PROVOKED; - pMonster->PushEnemy( hEnemy, vecLocation ); - } - } - } -} - - -//========================================================= -// ALertSound - scream -//========================================================= -void CISlave :: AlertSound( void ) -{ - if ( m_hEnemy != NULL ) - { - SENTENCEG_PlayRndSz(ENT(pev), "SLV_ALERT", 0.85, ATTN_NORM, 0, m_voicePitch); - - CallForHelp( "monster_alien_slave", 512, m_hEnemy, m_vecEnemyLKP ); - } -} - -//========================================================= -// IdleSound -//========================================================= -void CISlave :: IdleSound( void ) -{ - if (RANDOM_LONG( 0, 2 ) == 0) - { - SENTENCEG_PlayRndSz(ENT(pev), "SLV_IDLE", 0.85, ATTN_NORM, 0, m_voicePitch); - } - -#if 0 - int side = RANDOM_LONG( 0, 1 ) * 2 - 1; - - ClearBeams( ); - ArmBeam( side ); - - UTIL_MakeAimVectors( pev->angles ); - Vector vecSrc = pev->origin + gpGlobals->v_right * 2 * side; - MESSAGE_BEGIN( MSG_PVS, gmsgTempEntity, vecSrc ); - WRITE_BYTE(TE_DLIGHT); - WRITE_COORD(vecSrc.x); // X - WRITE_COORD(vecSrc.y); // Y - WRITE_COORD(vecSrc.z); // Z - WRITE_BYTE( 8 ); // radius * 0.1 - WRITE_BYTE( 255 ); // r - WRITE_BYTE( 180 ); // g - WRITE_BYTE( 96 ); // b - WRITE_BYTE( 10 ); // time * 10 - WRITE_BYTE( 0 ); // decay * 0.1 - MESSAGE_END( ); - - EMIT_SOUND_DYN( ENT(pev), CHAN_WEAPON, "debris/zap1.wav", 1, ATTN_NORM, 0, 100 ); -#endif -} - -//========================================================= -// PainSound -//========================================================= -void CISlave :: PainSound( void ) -{ - if (RANDOM_LONG( 0, 2 ) == 0) - { - EMIT_SOUND_DYN ( ENT(pev), CHAN_WEAPON, pPainSounds[ RANDOM_LONG(0,ARRAYSIZE(pPainSounds)-1) ], 1.0, ATTN_NORM, 0, m_voicePitch ); - } -} - -//========================================================= -// DieSound -//========================================================= - -void CISlave :: DeathSound( void ) -{ - EMIT_SOUND_DYN ( ENT(pev), CHAN_WEAPON, pDeathSounds[ RANDOM_LONG(0,ARRAYSIZE(pDeathSounds)-1) ], 1.0, ATTN_NORM, 0, m_voicePitch ); -} - - -//========================================================= -// ISoundMask - returns a bit mask indicating which types -// of sounds this monster regards. -//========================================================= -int CISlave :: ISoundMask ( void) -{ - return bits_SOUND_WORLD | - bits_SOUND_COMBAT | - bits_SOUND_DANGER | - bits_SOUND_PLAYER; -} - - -void CISlave::Killed( entvars_t *pevAttacker, int iGib ) -{ - ClearBeams( ); - CSquadMonster::Killed( pevAttacker, iGib ); -} - -//========================================================= -// SetYawSpeed - allows each sequence to have a different -// turn rate associated with it. -//========================================================= -void CISlave :: SetYawSpeed ( void ) -{ - int ys; - - switch ( m_Activity ) - { - case ACT_WALK: - ys = 50; - break; - case ACT_RUN: - ys = 70; - break; - case ACT_IDLE: - ys = 50; - break; - default: - ys = 90; - break; - } - - pev->yaw_speed = ys; -} - -//========================================================= -// HandleAnimEvent - catches the monster-specific messages -// that occur when tagged animation frames are played. -// -// Returns number of events handled, 0 if none. -//========================================================= -void CISlave :: HandleAnimEvent( MonsterEvent_t *pEvent ) -{ - // ALERT( at_console, "event %d : %f\n", pEvent->event, pev->frame ); - switch( pEvent->event ) - { - case ISLAVE_AE_CLAW: - { - // SOUND HERE! - CBaseEntity *pHurt = CheckTraceHullAttack( 70, gSkillData.slaveDmgClaw, DMG_SLASH ); - if ( pHurt ) - { - if ( pHurt->pev->flags & (FL_MONSTER|FL_CLIENT) ) - { - pHurt->pev->punchangle.z = -18; - pHurt->pev->punchangle.x = 5; - } - // Play a random attack hit sound - EMIT_SOUND_DYN ( ENT(pev), CHAN_WEAPON, pAttackHitSounds[ RANDOM_LONG(0,ARRAYSIZE(pAttackHitSounds)-1) ], 1.0, ATTN_NORM, 0, m_voicePitch ); - } - else - { - // Play a random attack miss sound - EMIT_SOUND_DYN ( ENT(pev), CHAN_WEAPON, pAttackMissSounds[ RANDOM_LONG(0,ARRAYSIZE(pAttackMissSounds)-1) ], 1.0, ATTN_NORM, 0, m_voicePitch ); - } - } - break; - - case ISLAVE_AE_CLAWRAKE: - { - CBaseEntity *pHurt = CheckTraceHullAttack( 70, gSkillData.slaveDmgClawrake, DMG_SLASH ); - if ( pHurt ) - { - if ( pHurt->pev->flags & (FL_MONSTER|FL_CLIENT) ) - { - pHurt->pev->punchangle.z = -18; - pHurt->pev->punchangle.x = 5; - } - EMIT_SOUND_DYN ( ENT(pev), CHAN_WEAPON, pAttackHitSounds[ RANDOM_LONG(0,ARRAYSIZE(pAttackHitSounds)-1) ], 1.0, ATTN_NORM, 0, m_voicePitch ); - } - else - { - EMIT_SOUND_DYN ( ENT(pev), CHAN_WEAPON, pAttackMissSounds[ RANDOM_LONG(0,ARRAYSIZE(pAttackMissSounds)-1) ], 1.0, ATTN_NORM, 0, m_voicePitch ); - } - } - break; - - case ISLAVE_AE_ZAP_POWERUP: - { - // speed up attack when on hard - if (g_iSkillLevel == SKILL_HARD) - pev->framerate = 1.5; - - UTIL_MakeAimVectors( pev->angles ); - - if (m_iBeams == 0) - { - Vector vecSrc = pev->origin + gpGlobals->v_forward * 2; - MESSAGE_BEGIN( MSG_PVS, gmsgTempEntity, vecSrc ); - WRITE_BYTE(TE_DLIGHT); - WRITE_COORD(vecSrc.x); // X - WRITE_COORD(vecSrc.y); // Y - WRITE_COORD(vecSrc.z); // Z - WRITE_BYTE( 12 ); // radius * 0.1 - WRITE_BYTE( 255 ); // r - WRITE_BYTE( 180 ); // g - WRITE_BYTE( 96 ); // b - WRITE_BYTE( 20 / pev->framerate ); // time * 10 - WRITE_BYTE( 0 ); // decay * 0.1 - MESSAGE_END( ); - - } - if (m_hDead != NULL) - { - WackBeam( -1, m_hDead ); - WackBeam( 1, m_hDead ); - } - else - { - ArmBeam( -1 ); - ArmBeam( 1 ); - BeamGlow( ); - } - - EMIT_SOUND_DYN( ENT(pev), CHAN_WEAPON, "debris/zap4.wav", 1, ATTN_NORM, 0, 100 + m_iBeams * 10 ); - pev->skin = m_iBeams / 2; - } - break; - - case ISLAVE_AE_ZAP_SHOOT: - { - ClearBeams( ); - - if (m_hDead != NULL) - { - Vector vecDest = m_hDead->pev->origin + Vector( 0, 0, 38 ); - TraceResult trace; - UTIL_TraceHull( vecDest, vecDest, dont_ignore_monsters, human_hull, m_hDead->edict(), &trace ); - - if ( !trace.fStartSolid ) - { - CBaseEntity *pNew = Create( "monster_alien_slave", m_hDead->pev->origin, m_hDead->pev->angles ); - CBaseMonster *pNewMonster = pNew->MyMonsterPointer( ); - pNew->pev->spawnflags |= 1; - WackBeam( -1, pNew ); - WackBeam( 1, pNew ); - UTIL_Remove( m_hDead ); - EMIT_SOUND_DYN( ENT(pev), CHAN_WEAPON, "hassault/hw_shoot1.wav", 1, ATTN_NORM, 0, RANDOM_LONG( 130, 160 ) ); - - /* - CBaseEntity *pEffect = Create( "test_effect", pNew->Center(), pev->angles ); - pEffect->Use( this, this, USE_ON, 1 ); - */ - break; - } - } - ClearMultiDamage(); - - UTIL_MakeAimVectors( pev->angles ); - - ZapBeam( -1 ); - ZapBeam( 1 ); - - EMIT_SOUND_DYN( ENT(pev), CHAN_WEAPON, "hassault/hw_shoot1.wav", 1, ATTN_NORM, 0, RANDOM_LONG( 130, 160 ) ); - // STOP_SOUND( ENT(pev), CHAN_WEAPON, "debris/zap4.wav" ); - ApplyMultiDamage(pev, pev); - - m_flNextAttack = gpGlobals->time + RANDOM_FLOAT( 0.5, 4.0 ); - } - break; - - case ISLAVE_AE_ZAP_DONE: - { - ClearBeams( ); - } - break; - - default: - CSquadMonster::HandleAnimEvent( pEvent ); - break; - } -} - -//========================================================= -// CheckRangeAttack1 - normal beam attack -//========================================================= -BOOL CISlave :: CheckRangeAttack1 ( float flDot, float flDist ) -{ - if (m_flNextAttack > gpGlobals->time) - { - return FALSE; - } - - return CSquadMonster::CheckRangeAttack1( flDot, flDist ); -} - -//========================================================= -// CheckRangeAttack2 - check bravery and try to resurect dead comrades -//========================================================= -BOOL CISlave :: CheckRangeAttack2 ( float flDot, float flDist ) -{ - return FALSE; - - if (m_flNextAttack > gpGlobals->time) - { - return FALSE; - } - - m_hDead = NULL; - m_iBravery = 0; - - CBaseEntity *pEntity = NULL; - while ((pEntity = UTIL_FindEntityByClassname( pEntity, "monster_alien_slave" )) != NULL) - { - TraceResult tr; - - UTIL_TraceLine( EyePosition( ), pEntity->EyePosition( ), ignore_monsters, ENT(pev), &tr ); - if (tr.flFraction == 1.0 || tr.pHit == pEntity->edict()) - { - if (pEntity->pev->deadflag == DEAD_DEAD) - { - float d = (pev->origin - pEntity->pev->origin).Length(); - if (d < flDist) - { - m_hDead = pEntity; - flDist = d; - } - m_iBravery--; - } - else - { - m_iBravery++; - } - } - } - if (m_hDead != NULL) - return TRUE; - else - return FALSE; -} - - -//========================================================= -// StartTask -//========================================================= -void CISlave :: StartTask ( Task_t *pTask ) -{ - ClearBeams( ); - - CSquadMonster :: StartTask ( pTask ); -} - - -//========================================================= -// Spawn -//========================================================= -void CISlave :: Spawn() -{ - Precache( ); - - if (pev->model) - SET_MODEL(ENT(pev), STRING(pev->model)); //LRC - else - SET_MODEL(ENT(pev), "models/islave.mdl"); - UTIL_SetSize(pev, VEC_HUMAN_HULL_MIN, VEC_HUMAN_HULL_MAX); - - pev->solid = SOLID_SLIDEBOX; - pev->movetype = MOVETYPE_STEP; - m_bloodColor = BLOOD_COLOR_GREEN; - pev->effects = 0; - if (pev->health == 0) - pev->health = gSkillData.slaveHealth; - pev->view_ofs = Vector ( 0, 0, 64 );// position of the eyes relative to monster's origin. - m_flFieldOfView = VIEW_FIELD_WIDE; // NOTE: we need a wide field of view so npc will notice player and say hello - m_MonsterState = MONSTERSTATE_NONE; - m_afCapability = bits_CAP_HEAR | bits_CAP_TURN_HEAD | bits_CAP_RANGE_ATTACK2 | bits_CAP_DOORS_GROUP; - - m_voicePitch = RANDOM_LONG( 85, 110 ); - - MonsterInit(); -} - -//========================================================= -// Precache - precaches all resources this monster needs -//========================================================= -void CISlave :: Precache() -{ - int i; - - if (pev->model) - PRECACHE_MODEL((char*)STRING(pev->model)); //LRC - else - PRECACHE_MODEL("models/islave.mdl"); - PRECACHE_MODEL("sprites/lgtning.spr"); - PRECACHE_SOUND("debris/zap1.wav"); - PRECACHE_SOUND("debris/zap4.wav"); - PRECACHE_SOUND("weapons/electro4.wav"); - PRECACHE_SOUND("hassault/hw_shoot1.wav"); - PRECACHE_SOUND("zombie/zo_pain2.wav"); - PRECACHE_SOUND("headcrab/hc_headbite.wav"); - PRECACHE_SOUND("weapons/cbar_miss1.wav"); - - for ( i = 0; i < ARRAYSIZE( pAttackHitSounds ); i++ ) - PRECACHE_SOUND((char *)pAttackHitSounds[i]); - - for ( i = 0; i < ARRAYSIZE( pAttackMissSounds ); i++ ) - PRECACHE_SOUND((char *)pAttackMissSounds[i]); - - for ( i = 0; i < ARRAYSIZE( pPainSounds ); i++ ) - PRECACHE_SOUND((char *)pPainSounds[i]); - - for ( i = 0; i < ARRAYSIZE( pDeathSounds ); i++ ) - PRECACHE_SOUND((char *)pDeathSounds[i]); - - UTIL_PrecacheOther( "test_effect" ); -} - - -//========================================================= -// TakeDamage - get provoked when injured -//========================================================= - -int CISlave :: TakeDamage( entvars_t* pevInflictor, entvars_t* pevAttacker, float flDamage, int bitsDamageType) -{ - // don't slash one of your own - if ((bitsDamageType & DMG_SLASH) && pevAttacker && IRelationship( Instance(pevAttacker) ) < R_DL) - return 0; - - //LRC - if my player reaction has been overridden, leave this alone - if (m_iPlayerReact == 0) - m_afMemory |= bits_MEMORY_PROVOKED; - - return CSquadMonster::TakeDamage(pevInflictor, pevAttacker, flDamage, bitsDamageType); -} - - -void CISlave::TraceAttack( entvars_t *pevAttacker, float flDamage, Vector vecDir, TraceResult *ptr, int bitsDamageType) -{ - if (bitsDamageType & DMG_SHOCK) - return; - - CSquadMonster::TraceAttack( pevAttacker, flDamage, vecDir, ptr, bitsDamageType ); -} - - -//========================================================= -// AI Schedules Specific to this monster -//========================================================= - - - -// primary range attack -Task_t tlSlaveAttack1[] = -{ - { TASK_STOP_MOVING, 0 }, - { TASK_FACE_IDEAL, (float)0 }, - { TASK_RANGE_ATTACK1, (float)0 }, -}; - -Schedule_t slSlaveAttack1[] = -{ - { - tlSlaveAttack1, - ARRAYSIZE ( tlSlaveAttack1 ), - bits_COND_CAN_MELEE_ATTACK1 | - bits_COND_HEAR_SOUND | - bits_COND_HEAVY_DAMAGE, - - bits_SOUND_DANGER, - "Slave Range Attack1" - }, -}; - - -DEFINE_CUSTOM_SCHEDULES( CISlave ) -{ - slSlaveAttack1, -}; - -IMPLEMENT_CUSTOM_SCHEDULES( CISlave, CSquadMonster ); - - -//========================================================= -//========================================================= -Schedule_t *CISlave :: GetSchedule( void ) -{ - ClearBeams( ); - -/* - if (pev->spawnflags) - { - pev->spawnflags = 0; - return GetScheduleOfType( SCHED_RELOAD ); - } -*/ - - if ( HasConditions( bits_COND_HEAR_SOUND ) ) - { - CSound *pSound; - pSound = PBestSound(); - - ASSERT( pSound != NULL ); - - if ( pSound && (pSound->m_iType & bits_SOUND_DANGER) ) - return GetScheduleOfType( SCHED_TAKE_COVER_FROM_BEST_SOUND ); - if ( pSound->m_iType & bits_SOUND_COMBAT ) - m_afMemory |= bits_MEMORY_PROVOKED; - } - - switch (m_MonsterState) - { - case MONSTERSTATE_COMBAT: -// dead enemy - if ( HasConditions( bits_COND_ENEMY_DEAD ) ) - { - // call base class, all code to handle dead enemies is centralized there. - return CBaseMonster :: GetSchedule(); - } - - if (pev->health < 20 || m_iBravery < 0) - { - if (!HasConditions( bits_COND_CAN_MELEE_ATTACK1 )) - { - m_failSchedule = SCHED_CHASE_ENEMY; - if (HasConditions( bits_COND_LIGHT_DAMAGE | bits_COND_HEAVY_DAMAGE)) - { - return GetScheduleOfType( SCHED_TAKE_COVER_FROM_ENEMY ); - } - if ( HasConditions ( bits_COND_SEE_ENEMY ) && HasConditions ( bits_COND_ENEMY_FACING_ME ) ) - { - // ALERT( at_console, "exposed\n"); - return GetScheduleOfType( SCHED_TAKE_COVER_FROM_ENEMY ); - } - } - } - break; - } - return CSquadMonster::GetSchedule( ); -} - - -Schedule_t *CISlave :: GetScheduleOfType ( int Type ) -{ - switch ( Type ) - { - case SCHED_FAIL: - if (HasConditions( bits_COND_CAN_MELEE_ATTACK1 )) - { - return CSquadMonster :: GetScheduleOfType( SCHED_MELEE_ATTACK1 ); ; - } - break; - case SCHED_RANGE_ATTACK1: - return slSlaveAttack1; - case SCHED_RANGE_ATTACK2: - return slSlaveAttack1; - } - return CSquadMonster :: GetScheduleOfType( Type ); -} - - -//========================================================= -// ArmBeam - small beam from arm to nearby geometry -//========================================================= - -void CISlave :: ArmBeam( int side ) -{ - TraceResult tr; - float flDist = 1.0; - - if (m_iBeams >= ISLAVE_MAX_BEAMS) - return; - - UTIL_MakeAimVectors( pev->angles ); - Vector vecSrc = pev->origin + gpGlobals->v_up * 36 + gpGlobals->v_right * side * 16 + gpGlobals->v_forward * 32; - - for (int i = 0; i < 3; i++) - { - Vector vecAim = gpGlobals->v_right * side * RANDOM_FLOAT( 0, 1 ) + gpGlobals->v_up * RANDOM_FLOAT( -1, 1 ); - TraceResult tr1; - UTIL_TraceLine ( vecSrc, vecSrc + vecAim * 512, dont_ignore_monsters, ENT( pev ), &tr1); - if (flDist > tr1.flFraction) - { - tr = tr1; - flDist = tr.flFraction; - } - } - - // Couldn't find anything close enough - if ( flDist == 1.0 ) - return; - - DecalGunshot( &tr, BULLET_PLAYER_CROWBAR ); - - m_pBeam[m_iBeams] = CBeam::BeamCreate( "sprites/lgtning.spr", 30 ); - if (!m_pBeam[m_iBeams]) - return; - - m_pBeam[m_iBeams]->PointEntInit( tr.vecEndPos, edict( ) ); - m_pBeam[m_iBeams]->SetEndAttachment( side < 0 ? 2 : 1 ); - // m_pBeam[m_iBeams]->SetColor( 180, 255, 96 ); - m_pBeam[m_iBeams]->SetColor( 96, 128, 16 ); - m_pBeam[m_iBeams]->SetBrightness( 64 ); - m_pBeam[m_iBeams]->SetNoise( 80 ); - m_iBeams++; -} - - -//========================================================= -// BeamGlow - brighten all beams -//========================================================= -void CISlave :: BeamGlow( ) -{ - int b = m_iBeams * 32; - if (b > 255) - b = 255; - - for (int i = 0; i < m_iBeams; i++) - { - if (m_pBeam[i]->GetBrightness() != 255) - { - m_pBeam[i]->SetBrightness( b ); - } - } -} - - -//========================================================= -// WackBeam - regenerate dead colleagues -//========================================================= -void CISlave :: WackBeam( int side, CBaseEntity *pEntity ) -{ - Vector vecDest; - float flDist = 1.0; - - if (m_iBeams >= ISLAVE_MAX_BEAMS) - return; - - if (pEntity == NULL) - return; - - m_pBeam[m_iBeams] = CBeam::BeamCreate( "sprites/lgtning.spr", 30 ); - if (!m_pBeam[m_iBeams]) - return; - - m_pBeam[m_iBeams]->PointEntInit( pEntity->Center(), edict( ) ); - m_pBeam[m_iBeams]->SetEndAttachment( side < 0 ? 2 : 1 ); - m_pBeam[m_iBeams]->SetColor( 180, 255, 96 ); - m_pBeam[m_iBeams]->SetBrightness( 255 ); - m_pBeam[m_iBeams]->SetNoise( 80 ); - m_iBeams++; -} - -//========================================================= -// ZapBeam - heavy damage directly forward -//========================================================= -void CISlave :: ZapBeam( int side ) -{ - Vector vecSrc, vecAim; - TraceResult tr; - CBaseEntity *pEntity; - - if (m_iBeams >= ISLAVE_MAX_BEAMS) - return; - - vecSrc = pev->origin + gpGlobals->v_up * 36; - vecAim = ShootAtEnemy( vecSrc ); - float deflection = 0.01; - vecAim = vecAim + side * gpGlobals->v_right * RANDOM_FLOAT( 0, deflection ) + gpGlobals->v_up * RANDOM_FLOAT( -deflection, deflection ); - UTIL_TraceLine ( vecSrc, vecSrc + vecAim * 1024, dont_ignore_monsters, ENT( pev ), &tr); - - m_pBeam[m_iBeams] = CBeam::BeamCreate( "sprites/lgtning.spr", 50 ); - if (!m_pBeam[m_iBeams]) - return; - - m_pBeam[m_iBeams]->PointEntInit( tr.vecEndPos, edict( ) ); - m_pBeam[m_iBeams]->SetEndAttachment( side < 0 ? 2 : 1 ); - m_pBeam[m_iBeams]->SetColor( 180, 255, 96 ); - m_pBeam[m_iBeams]->SetBrightness( 255 ); - m_pBeam[m_iBeams]->SetNoise( 20 ); - m_iBeams++; - - pEntity = CBaseEntity::Instance(tr.pHit); - if (pEntity != NULL && pEntity->pev->takedamage) - { - pEntity->TraceAttack( pev, gSkillData.slaveDmgZap, vecAim, &tr, DMG_SHOCK ); - } - UTIL_EmitAmbientSound( ENT(pev), tr.vecEndPos, "weapons/electro4.wav", 0.5, ATTN_NORM, 0, RANDOM_LONG( 140, 160 ) ); -} - - -//========================================================= -// ClearBeams - remove all beams -//========================================================= -void CISlave :: ClearBeams( ) -{ - for (int i = 0; i < ISLAVE_MAX_BEAMS; i++) - { - if (m_pBeam[i]) - { - UTIL_Remove( m_pBeam[i] ); - m_pBeam[i] = NULL; - } - } - m_iBeams = 0; - pev->skin = 0; - - STOP_SOUND( ENT(pev), CHAN_WEAPON, "debris/zap4.wav" ); -} +/*** +* +* Copyright (c) 1996-2002, 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. +* +* This source code contains proprietary and confidential information of +* Valve LLC and its suppliers. Access to this code is restricted to +* persons who have executed a written SDK license with Valve. Any access, +* use or distribution of this code by or to any unlicensed person is illegal. +* +****/ +//========================================================= +// Alien slave monster +//========================================================= + +#include "extdll.h" +#include "util.h" +#include "cbase.h" +#include "monsters.h" +#include "squadmonster.h" +#include "schedule.h" +#include "effects.h" +#include "weapons.h" +#include "soundent.h" + +extern DLL_GLOBAL int g_iSkillLevel; + +//========================================================= +// Monster's Anim Events Go Here +//========================================================= +#define ISLAVE_AE_CLAW ( 1 ) +#define ISLAVE_AE_CLAWRAKE ( 2 ) +#define ISLAVE_AE_ZAP_POWERUP ( 3 ) +#define ISLAVE_AE_ZAP_SHOOT ( 4 ) +#define ISLAVE_AE_ZAP_DONE ( 5 ) + +#define ISLAVE_MAX_BEAMS 8 + +class CISlave : public CSquadMonster +{ +public: + void Spawn( void ); + void Precache( void ); + void SetYawSpeed( void ); + int ISoundMask( void ); + int Classify ( void ); + int IRelationship( CBaseEntity *pTarget ); + void HandleAnimEvent( MonsterEvent_t *pEvent ); + BOOL CheckRangeAttack1 ( float flDot, float flDist ); + BOOL CheckRangeAttack2 ( float flDot, float flDist ); + void CallForHelp( char *szClassname, float flDist, EHANDLE hEnemy, Vector &vecLocation ); + void TraceAttack( entvars_t *pevAttacker, float flDamage, Vector vecDir, TraceResult *ptr, int bitsDamageType); + int TakeDamage( entvars_t* pevInflictor, entvars_t* pevAttacker, float flDamage, int bitsDamageType); + + void DeathSound( void ); + void PainSound( void ); + void AlertSound( void ); + void IdleSound( void ); + + void Killed( entvars_t *pevAttacker, int iGib ); + + void StartTask ( Task_t *pTask ); + Schedule_t *GetSchedule( void ); + Schedule_t *GetScheduleOfType ( int Type ); + CUSTOM_SCHEDULES; + + int Save( CSave &save ); + int Restore( CRestore &restore ); + static TYPEDESCRIPTION m_SaveData[]; + + void ClearBeams( ); + void ArmBeam( int side ); + void WackBeam( int side, CBaseEntity *pEntity ); + void ZapBeam( int side ); + void BeamGlow( void ); + + int m_iBravery; + + CBeam *m_pBeam[ISLAVE_MAX_BEAMS]; + + int m_iBeams; + float m_flNextAttack; + + int m_voicePitch; + + EHANDLE m_hDead; + + static const char *pAttackHitSounds[]; + static const char *pAttackMissSounds[]; + static const char *pPainSounds[]; + static const char *pDeathSounds[]; +}; +LINK_ENTITY_TO_CLASS( monster_alien_slave, CISlave ); +LINK_ENTITY_TO_CLASS( monster_vortigaunt, CISlave ); + + +TYPEDESCRIPTION CISlave::m_SaveData[] = +{ + DEFINE_FIELD( CISlave, m_iBravery, FIELD_INTEGER ), + + DEFINE_ARRAY( CISlave, m_pBeam, FIELD_CLASSPTR, ISLAVE_MAX_BEAMS ), + DEFINE_FIELD( CISlave, m_iBeams, FIELD_INTEGER ), + DEFINE_FIELD( CISlave, m_flNextAttack, FIELD_TIME ), + + DEFINE_FIELD( CISlave, m_voicePitch, FIELD_INTEGER ), + + DEFINE_FIELD( CISlave, m_hDead, FIELD_EHANDLE ), + +}; + +IMPLEMENT_SAVERESTORE( CISlave, CSquadMonster ); + + + + +const char *CISlave::pAttackHitSounds[] = +{ + "zombie/claw_strike1.wav", + "zombie/claw_strike2.wav", + "zombie/claw_strike3.wav", +}; + +const char *CISlave::pAttackMissSounds[] = +{ + "zombie/claw_miss1.wav", + "zombie/claw_miss2.wav", +}; + +const char *CISlave::pPainSounds[] = +{ + "aslave/slv_pain1.wav", + "aslave/slv_pain2.wav", +}; + +const char *CISlave::pDeathSounds[] = +{ + "aslave/slv_die1.wav", + "aslave/slv_die2.wav", +}; + +//========================================================= +// Classify - indicates this monster's place in the +// relationship table. +//========================================================= +int CISlave :: Classify ( void ) +{ + return CLASS_ALIEN_MILITARY; +} + + +int CISlave::IRelationship( CBaseEntity *pTarget ) +{ + if ( (pTarget->IsPlayer()) ) + if ( (pev->spawnflags & SF_MONSTER_WAIT_UNTIL_PROVOKED ) && ! (m_afMemory & bits_MEMORY_PROVOKED )) + return R_NO; + return CBaseMonster::IRelationship( pTarget ); +} + + +void CISlave :: CallForHelp( char *szClassname, float flDist, EHANDLE hEnemy, Vector &vecLocation ) +{ + // ALERT( at_aiconsole, "help " ); + + // skip ones not on my netname + if ( FStringNull( pev->netname )) + return; + + CBaseEntity *pEntity = NULL; + + while ((pEntity = UTIL_FindEntityByString( pEntity, "netname", STRING( pev->netname ))) != NULL) + { + float d = (pev->origin - pEntity->pev->origin).Length(); + if (d < flDist) + { + CBaseMonster *pMonster = pEntity->MyMonsterPointer( ); + if (pMonster) + { + pMonster->m_afMemory |= bits_MEMORY_PROVOKED; + pMonster->PushEnemy( hEnemy, vecLocation ); + } + } + } +} + + +//========================================================= +// ALertSound - scream +//========================================================= +void CISlave :: AlertSound( void ) +{ + if ( m_hEnemy != NULL ) + { + SENTENCEG_PlayRndSz(ENT(pev), "SLV_ALERT", 0.85, ATTN_NORM, 0, m_voicePitch); + + CallForHelp( "monster_alien_slave", 512, m_hEnemy, m_vecEnemyLKP ); + } +} + +//========================================================= +// IdleSound +//========================================================= +void CISlave :: IdleSound( void ) +{ + if (RANDOM_LONG( 0, 2 ) == 0) + { + SENTENCEG_PlayRndSz(ENT(pev), "SLV_IDLE", 0.85, ATTN_NORM, 0, m_voicePitch); + } + +#if 0 + int side = RANDOM_LONG( 0, 1 ) * 2 - 1; + + ClearBeams( ); + ArmBeam( side ); + + UTIL_MakeAimVectors( pev->angles ); + Vector vecSrc = pev->origin + gpGlobals->v_right * 2 * side; + MESSAGE_BEGIN( MSG_PVS, SVC_TEMPENTITY, vecSrc ); + WRITE_BYTE(TE_DLIGHT); + WRITE_COORD(vecSrc.x); // X + WRITE_COORD(vecSrc.y); // Y + WRITE_COORD(vecSrc.z); // Z + WRITE_BYTE( 8 ); // radius * 0.1 + WRITE_BYTE( 255 ); // r + WRITE_BYTE( 180 ); // g + WRITE_BYTE( 96 ); // b + WRITE_BYTE( 10 ); // time * 10 + WRITE_BYTE( 0 ); // decay * 0.1 + MESSAGE_END( ); + + EMIT_SOUND_DYN( ENT(pev), CHAN_WEAPON, "debris/zap1.wav", 1, ATTN_NORM, 0, 100 ); +#endif +} + +//========================================================= +// PainSound +//========================================================= +void CISlave :: PainSound( void ) +{ + if (RANDOM_LONG( 0, 2 ) == 0) + { + EMIT_SOUND_DYN ( ENT(pev), CHAN_WEAPON, pPainSounds[ RANDOM_LONG(0,ARRAYSIZE(pPainSounds)-1) ], 1.0, ATTN_NORM, 0, m_voicePitch ); + } +} + +//========================================================= +// DieSound +//========================================================= + +void CISlave :: DeathSound( void ) +{ + EMIT_SOUND_DYN ( ENT(pev), CHAN_WEAPON, pDeathSounds[ RANDOM_LONG(0,ARRAYSIZE(pDeathSounds)-1) ], 1.0, ATTN_NORM, 0, m_voicePitch ); +} + + +//========================================================= +// ISoundMask - returns a bit mask indicating which types +// of sounds this monster regards. +//========================================================= +int CISlave :: ISoundMask ( void) +{ + return bits_SOUND_WORLD | + bits_SOUND_COMBAT | + bits_SOUND_DANGER | + bits_SOUND_PLAYER; +} + + +void CISlave::Killed( entvars_t *pevAttacker, int iGib ) +{ + ClearBeams( ); + CSquadMonster::Killed( pevAttacker, iGib ); +} + +//========================================================= +// SetYawSpeed - allows each sequence to have a different +// turn rate associated with it. +//========================================================= +void CISlave :: SetYawSpeed ( void ) +{ + int ys; + + switch ( m_Activity ) + { + case ACT_WALK: + ys = 50; + break; + case ACT_RUN: + ys = 70; + break; + case ACT_IDLE: + ys = 50; + break; + default: + ys = 90; + break; + } + + pev->yaw_speed = ys; +} + +//========================================================= +// HandleAnimEvent - catches the monster-specific messages +// that occur when tagged animation frames are played. +// +// Returns number of events handled, 0 if none. +//========================================================= +void CISlave :: HandleAnimEvent( MonsterEvent_t *pEvent ) +{ + // ALERT( at_console, "event %d : %f\n", pEvent->event, pev->frame ); + switch( pEvent->event ) + { + case ISLAVE_AE_CLAW: + { + // SOUND HERE! + CBaseEntity *pHurt = CheckTraceHullAttack( 70, gSkillData.slaveDmgClaw, DMG_SLASH ); + if ( pHurt ) + { + if ( pHurt->pev->flags & (FL_MONSTER|FL_CLIENT) ) + { + pHurt->pev->punchangle.z = -18; + pHurt->pev->punchangle.x = 5; + } + // Play a random attack hit sound + EMIT_SOUND_DYN ( ENT(pev), CHAN_WEAPON, pAttackHitSounds[ RANDOM_LONG(0,ARRAYSIZE(pAttackHitSounds)-1) ], 1.0, ATTN_NORM, 0, m_voicePitch ); + } + else + { + // Play a random attack miss sound + EMIT_SOUND_DYN ( ENT(pev), CHAN_WEAPON, pAttackMissSounds[ RANDOM_LONG(0,ARRAYSIZE(pAttackMissSounds)-1) ], 1.0, ATTN_NORM, 0, m_voicePitch ); + } + } + break; + + case ISLAVE_AE_CLAWRAKE: + { + CBaseEntity *pHurt = CheckTraceHullAttack( 70, gSkillData.slaveDmgClawrake, DMG_SLASH ); + if ( pHurt ) + { + if ( pHurt->pev->flags & (FL_MONSTER|FL_CLIENT) ) + { + pHurt->pev->punchangle.z = -18; + pHurt->pev->punchangle.x = 5; + } + EMIT_SOUND_DYN ( ENT(pev), CHAN_WEAPON, pAttackHitSounds[ RANDOM_LONG(0,ARRAYSIZE(pAttackHitSounds)-1) ], 1.0, ATTN_NORM, 0, m_voicePitch ); + } + else + { + EMIT_SOUND_DYN ( ENT(pev), CHAN_WEAPON, pAttackMissSounds[ RANDOM_LONG(0,ARRAYSIZE(pAttackMissSounds)-1) ], 1.0, ATTN_NORM, 0, m_voicePitch ); + } + } + break; + + case ISLAVE_AE_ZAP_POWERUP: + { + // speed up attack when on hard + if (g_iSkillLevel == SKILL_HARD) + pev->framerate = 1.5; + + UTIL_MakeAimVectors( pev->angles ); + + if (m_iBeams == 0) + { + Vector vecSrc = pev->origin + gpGlobals->v_forward * 2; + MESSAGE_BEGIN( MSG_PVS, SVC_TEMPENTITY, vecSrc ); + WRITE_BYTE(TE_DLIGHT); + WRITE_COORD(vecSrc.x); // X + WRITE_COORD(vecSrc.y); // Y + WRITE_COORD(vecSrc.z); // Z + WRITE_BYTE( 12 ); // radius * 0.1 + WRITE_BYTE( 255 ); // r + WRITE_BYTE( 180 ); // g + WRITE_BYTE( 96 ); // b + WRITE_BYTE( 20 / pev->framerate ); // time * 10 + WRITE_BYTE( 0 ); // decay * 0.1 + MESSAGE_END( ); + + } + if (m_hDead != NULL) + { + WackBeam( -1, m_hDead ); + WackBeam( 1, m_hDead ); + } + else + { + ArmBeam( -1 ); + ArmBeam( 1 ); + BeamGlow( ); + } + + EMIT_SOUND_DYN( ENT(pev), CHAN_WEAPON, "debris/zap4.wav", 1, ATTN_NORM, 0, 100 + m_iBeams * 10 ); + pev->skin = m_iBeams / 2; + } + break; + + case ISLAVE_AE_ZAP_SHOOT: + { + ClearBeams( ); + + if (m_hDead != NULL) + { + Vector vecDest = m_hDead->pev->origin + Vector( 0, 0, 38 ); + TraceResult trace; + UTIL_TraceHull( vecDest, vecDest, dont_ignore_monsters, human_hull, m_hDead->edict(), &trace ); + + if ( !trace.fStartSolid ) + { + CBaseEntity *pNew = Create( "monster_alien_slave", m_hDead->pev->origin, m_hDead->pev->angles ); + CBaseMonster *pNewMonster = pNew->MyMonsterPointer( ); + pNew->pev->spawnflags |= 1; + WackBeam( -1, pNew ); + WackBeam( 1, pNew ); + UTIL_Remove( m_hDead ); + EMIT_SOUND_DYN( ENT(pev), CHAN_WEAPON, "hassault/hw_shoot1.wav", 1, ATTN_NORM, 0, RANDOM_LONG( 130, 160 ) ); + + /* + CBaseEntity *pEffect = Create( "test_effect", pNew->Center(), pev->angles ); + pEffect->Use( this, this, USE_ON, 1 ); + */ + break; + } + } + ClearMultiDamage(); + + UTIL_MakeAimVectors( pev->angles ); + + ZapBeam( -1 ); + ZapBeam( 1 ); + + EMIT_SOUND_DYN( ENT(pev), CHAN_WEAPON, "hassault/hw_shoot1.wav", 1, ATTN_NORM, 0, RANDOM_LONG( 130, 160 ) ); + // STOP_SOUND( ENT(pev), CHAN_WEAPON, "debris/zap4.wav" ); + ApplyMultiDamage(pev, pev); + + m_flNextAttack = gpGlobals->time + RANDOM_FLOAT( 0.5, 4.0 ); + } + break; + + case ISLAVE_AE_ZAP_DONE: + { + ClearBeams( ); + } + break; + + default: + CSquadMonster::HandleAnimEvent( pEvent ); + break; + } +} + +//========================================================= +// CheckRangeAttack1 - normal beam attack +//========================================================= +BOOL CISlave :: CheckRangeAttack1 ( float flDot, float flDist ) +{ + if (m_flNextAttack > gpGlobals->time) + { + return FALSE; + } + + return CSquadMonster::CheckRangeAttack1( flDot, flDist ); +} + +//========================================================= +// CheckRangeAttack2 - check bravery and try to resurect dead comrades +//========================================================= +BOOL CISlave :: CheckRangeAttack2 ( float flDot, float flDist ) +{ + return FALSE; + + if (m_flNextAttack > gpGlobals->time) + { + return FALSE; + } + + m_hDead = NULL; + m_iBravery = 0; + + CBaseEntity *pEntity = NULL; + while ((pEntity = UTIL_FindEntityByClassname( pEntity, "monster_alien_slave" )) != NULL) + { + TraceResult tr; + + UTIL_TraceLine( EyePosition( ), pEntity->EyePosition( ), ignore_monsters, ENT(pev), &tr ); + if (tr.flFraction == 1.0 || tr.pHit == pEntity->edict()) + { + if (pEntity->pev->deadflag == DEAD_DEAD) + { + float d = (pev->origin - pEntity->pev->origin).Length(); + if (d < flDist) + { + m_hDead = pEntity; + flDist = d; + } + m_iBravery--; + } + else + { + m_iBravery++; + } + } + } + if (m_hDead != NULL) + return TRUE; + else + return FALSE; +} + + +//========================================================= +// StartTask +//========================================================= +void CISlave :: StartTask ( Task_t *pTask ) +{ + ClearBeams( ); + + CSquadMonster :: StartTask ( pTask ); +} + + +//========================================================= +// Spawn +//========================================================= +void CISlave :: Spawn() +{ + Precache( ); + + SET_MODEL(ENT(pev), "models/islave.mdl"); + UTIL_SetSize(pev, VEC_HUMAN_HULL_MIN, VEC_HUMAN_HULL_MAX); + + pev->solid = SOLID_SLIDEBOX; + pev->movetype = MOVETYPE_STEP; + m_bloodColor = BLOOD_COLOR_GREEN; + pev->effects = 0; + pev->health = gSkillData.slaveHealth; + pev->view_ofs = Vector ( 0, 0, 64 );// position of the eyes relative to monster's origin. + m_flFieldOfView = VIEW_FIELD_WIDE; // NOTE: we need a wide field of view so npc will notice player and say hello + m_MonsterState = MONSTERSTATE_NONE; + m_afCapability = bits_CAP_HEAR | bits_CAP_TURN_HEAD | bits_CAP_RANGE_ATTACK2 | bits_CAP_DOORS_GROUP; + + m_voicePitch = RANDOM_LONG( 85, 110 ); + + MonsterInit(); +} + +//========================================================= +// Precache - precaches all resources this monster needs +//========================================================= +void CISlave :: Precache() +{ + int i; + + PRECACHE_MODEL("models/islave.mdl"); + PRECACHE_MODEL("sprites/lgtning.spr"); + PRECACHE_SOUND("debris/zap1.wav"); + PRECACHE_SOUND("debris/zap4.wav"); + PRECACHE_SOUND("weapons/electro4.wav"); + PRECACHE_SOUND("hassault/hw_shoot1.wav"); + PRECACHE_SOUND("zombie/zo_pain2.wav"); + PRECACHE_SOUND("headcrab/hc_headbite.wav"); + PRECACHE_SOUND("weapons/cbar_miss1.wav"); + + for ( i = 0; i < ARRAYSIZE( pAttackHitSounds ); i++ ) + PRECACHE_SOUND((char *)pAttackHitSounds[i]); + + for ( i = 0; i < ARRAYSIZE( pAttackMissSounds ); i++ ) + PRECACHE_SOUND((char *)pAttackMissSounds[i]); + + for ( i = 0; i < ARRAYSIZE( pPainSounds ); i++ ) + PRECACHE_SOUND((char *)pPainSounds[i]); + + for ( i = 0; i < ARRAYSIZE( pDeathSounds ); i++ ) + PRECACHE_SOUND((char *)pDeathSounds[i]); + + UTIL_PrecacheOther( "test_effect" ); +} + + +//========================================================= +// TakeDamage - get provoked when injured +//========================================================= + +int CISlave :: TakeDamage( entvars_t* pevInflictor, entvars_t* pevAttacker, float flDamage, int bitsDamageType) +{ + // don't slash one of your own + if ((bitsDamageType & DMG_SLASH) && pevAttacker && IRelationship( Instance(pevAttacker) ) < R_DL) + return 0; + + m_afMemory |= bits_MEMORY_PROVOKED; + return CSquadMonster::TakeDamage(pevInflictor, pevAttacker, flDamage, bitsDamageType); +} + + +void CISlave::TraceAttack( entvars_t *pevAttacker, float flDamage, Vector vecDir, TraceResult *ptr, int bitsDamageType) +{ + if (bitsDamageType & DMG_SHOCK) + return; + + CSquadMonster::TraceAttack( pevAttacker, flDamage, vecDir, ptr, bitsDamageType ); +} + + +//========================================================= +// AI Schedules Specific to this monster +//========================================================= + + + +// primary range attack +Task_t tlSlaveAttack1[] = +{ + { TASK_STOP_MOVING, 0 }, + { TASK_FACE_IDEAL, (float)0 }, + { TASK_RANGE_ATTACK1, (float)0 }, +}; + +Schedule_t slSlaveAttack1[] = +{ + { + tlSlaveAttack1, + ARRAYSIZE ( tlSlaveAttack1 ), + bits_COND_CAN_MELEE_ATTACK1 | + bits_COND_HEAR_SOUND | + bits_COND_HEAVY_DAMAGE, + + bits_SOUND_DANGER, + "Slave Range Attack1" + }, +}; + + +DEFINE_CUSTOM_SCHEDULES( CISlave ) +{ + slSlaveAttack1, +}; + +IMPLEMENT_CUSTOM_SCHEDULES( CISlave, CSquadMonster ); + + +//========================================================= +//========================================================= +Schedule_t *CISlave :: GetSchedule( void ) +{ + ClearBeams( ); + +/* + if (pev->spawnflags) + { + pev->spawnflags = 0; + return GetScheduleOfType( SCHED_RELOAD ); + } +*/ + + if ( HasConditions( bits_COND_HEAR_SOUND ) ) + { + CSound *pSound; + pSound = PBestSound(); + + ASSERT( pSound != NULL ); + + if ( pSound && (pSound->m_iType & bits_SOUND_DANGER) ) + return GetScheduleOfType( SCHED_TAKE_COVER_FROM_BEST_SOUND ); + if ( pSound->m_iType & bits_SOUND_COMBAT ) + m_afMemory |= bits_MEMORY_PROVOKED; + } + + switch (m_MonsterState) + { + case MONSTERSTATE_COMBAT: +// dead enemy + if ( HasConditions( bits_COND_ENEMY_DEAD ) ) + { + // call base class, all code to handle dead enemies is centralized there. + return CBaseMonster :: GetSchedule(); + } + + if (pev->health < 20 || m_iBravery < 0) + { + if (!HasConditions( bits_COND_CAN_MELEE_ATTACK1 )) + { + m_failSchedule = SCHED_CHASE_ENEMY; + if (HasConditions( bits_COND_LIGHT_DAMAGE | bits_COND_HEAVY_DAMAGE)) + { + return GetScheduleOfType( SCHED_TAKE_COVER_FROM_ENEMY ); + } + if ( HasConditions ( bits_COND_SEE_ENEMY ) && HasConditions ( bits_COND_ENEMY_FACING_ME ) ) + { + // ALERT( at_console, "exposed\n"); + return GetScheduleOfType( SCHED_TAKE_COVER_FROM_ENEMY ); + } + } + } + break; + } + return CSquadMonster::GetSchedule( ); +} + + +Schedule_t *CISlave :: GetScheduleOfType ( int Type ) +{ + switch ( Type ) + { + case SCHED_FAIL: + if (HasConditions( bits_COND_CAN_MELEE_ATTACK1 )) + { + return CSquadMonster :: GetScheduleOfType( SCHED_MELEE_ATTACK1 ); ; + } + break; + case SCHED_RANGE_ATTACK1: + return slSlaveAttack1; + case SCHED_RANGE_ATTACK2: + return slSlaveAttack1; + } + return CSquadMonster :: GetScheduleOfType( Type ); +} + + +//========================================================= +// ArmBeam - small beam from arm to nearby geometry +//========================================================= + +void CISlave :: ArmBeam( int side ) +{ + TraceResult tr; + float flDist = 1.0; + + if (m_iBeams >= ISLAVE_MAX_BEAMS) + return; + + UTIL_MakeAimVectors( pev->angles ); + Vector vecSrc = pev->origin + gpGlobals->v_up * 36 + gpGlobals->v_right * side * 16 + gpGlobals->v_forward * 32; + + for (int i = 0; i < 3; i++) + { + Vector vecAim = gpGlobals->v_right * side * RANDOM_FLOAT( 0, 1 ) + gpGlobals->v_up * RANDOM_FLOAT( -1, 1 ); + TraceResult tr1; + UTIL_TraceLine ( vecSrc, vecSrc + vecAim * 512, dont_ignore_monsters, ENT( pev ), &tr1); + if (flDist > tr1.flFraction) + { + tr = tr1; + flDist = tr.flFraction; + } + } + + // Couldn't find anything close enough + if ( flDist == 1.0 ) + return; + + DecalGunshot( &tr, BULLET_PLAYER_CROWBAR ); + + m_pBeam[m_iBeams] = CBeam::BeamCreate( "sprites/lgtning.spr", 30 ); + if (!m_pBeam[m_iBeams]) + return; + + m_pBeam[m_iBeams]->PointEntInit( tr.vecEndPos, entindex( ) ); + m_pBeam[m_iBeams]->SetEndAttachment( side < 0 ? 2 : 1 ); + // m_pBeam[m_iBeams]->SetColor( 180, 255, 96 ); + m_pBeam[m_iBeams]->SetColor( 96, 128, 16 ); + m_pBeam[m_iBeams]->SetBrightness( 64 ); + m_pBeam[m_iBeams]->SetNoise( 80 ); + m_iBeams++; +} + + +//========================================================= +// BeamGlow - brighten all beams +//========================================================= +void CISlave :: BeamGlow( ) +{ + int b = m_iBeams * 32; + if (b > 255) + b = 255; + + for (int i = 0; i < m_iBeams; i++) + { + if (m_pBeam[i]->GetBrightness() != 255) + { + m_pBeam[i]->SetBrightness( b ); + } + } +} + + +//========================================================= +// WackBeam - regenerate dead colleagues +//========================================================= +void CISlave :: WackBeam( int side, CBaseEntity *pEntity ) +{ + Vector vecDest; + float flDist = 1.0; + + if (m_iBeams >= ISLAVE_MAX_BEAMS) + return; + + if (pEntity == NULL) + return; + + m_pBeam[m_iBeams] = CBeam::BeamCreate( "sprites/lgtning.spr", 30 ); + if (!m_pBeam[m_iBeams]) + return; + + m_pBeam[m_iBeams]->PointEntInit( pEntity->Center(), entindex( ) ); + m_pBeam[m_iBeams]->SetEndAttachment( side < 0 ? 2 : 1 ); + m_pBeam[m_iBeams]->SetColor( 180, 255, 96 ); + m_pBeam[m_iBeams]->SetBrightness( 255 ); + m_pBeam[m_iBeams]->SetNoise( 80 ); + m_iBeams++; +} + +//========================================================= +// ZapBeam - heavy damage directly forward +//========================================================= +void CISlave :: ZapBeam( int side ) +{ + Vector vecSrc, vecAim; + TraceResult tr; + CBaseEntity *pEntity; + + if (m_iBeams >= ISLAVE_MAX_BEAMS) + return; + + vecSrc = pev->origin + gpGlobals->v_up * 36; + vecAim = ShootAtEnemy( vecSrc ); + float deflection = 0.01; + vecAim = vecAim + side * gpGlobals->v_right * RANDOM_FLOAT( 0, deflection ) + gpGlobals->v_up * RANDOM_FLOAT( -deflection, deflection ); + UTIL_TraceLine ( vecSrc, vecSrc + vecAim * 1024, dont_ignore_monsters, ENT( pev ), &tr); + + m_pBeam[m_iBeams] = CBeam::BeamCreate( "sprites/lgtning.spr", 50 ); + if (!m_pBeam[m_iBeams]) + return; + + m_pBeam[m_iBeams]->PointEntInit( tr.vecEndPos, entindex( ) ); + m_pBeam[m_iBeams]->SetEndAttachment( side < 0 ? 2 : 1 ); + m_pBeam[m_iBeams]->SetColor( 180, 255, 96 ); + m_pBeam[m_iBeams]->SetBrightness( 255 ); + m_pBeam[m_iBeams]->SetNoise( 20 ); + m_iBeams++; + + pEntity = CBaseEntity::Instance(tr.pHit); + if (pEntity != NULL && pEntity->pev->takedamage) + { + pEntity->TraceAttack( pev, gSkillData.slaveDmgZap, vecAim, &tr, DMG_SHOCK ); + } + UTIL_EmitAmbientSound( ENT(pev), tr.vecEndPos, "weapons/electro4.wav", 0.5, ATTN_NORM, 0, RANDOM_LONG( 140, 160 ) ); +} + + +//========================================================= +// ClearBeams - remove all beams +//========================================================= +void CISlave :: ClearBeams( ) +{ + for (int i = 0; i < ISLAVE_MAX_BEAMS; i++) + { + if (m_pBeam[i]) + { + UTIL_Remove( m_pBeam[i] ); + m_pBeam[i] = NULL; + } + } + m_iBeams = 0; + pev->skin = 0; + + STOP_SOUND( ENT(pev), CHAN_WEAPON, "debris/zap4.wav" ); +} diff --git a/spirit/items.cpp b/dlls/items.cpp similarity index 69% rename from spirit/items.cpp rename to dlls/items.cpp index cd4d56e4..449be85f 100644 --- a/spirit/items.cpp +++ b/dlls/items.cpp @@ -74,7 +74,7 @@ void CWorldItem::Spawn( void ) if (!pEntity) { - ALERT( at_debug, "unable to create world_item %d\n", m_iType ); + ALERT( at_console, "unable to create world_item %d\n", m_iType ); } else { @@ -91,9 +91,9 @@ void CItem::Spawn( void ) { pev->movetype = MOVETYPE_TOSS; pev->solid = SOLID_TRIGGER; - UTIL_SetOrigin( this, pev->origin ); + UTIL_SetOrigin( pev, pev->origin ); UTIL_SetSize(pev, Vector(-16, -16, 0), Vector(16, 16, 16)); - SetTouch(&CItem::ItemTouch); + SetTouch(ItemTouch); if (DROP_TO_FLOOR(ENT(pev)) == 0) { @@ -149,10 +149,10 @@ CBaseEntity* CItem::Respawn( void ) SetTouch( NULL ); pev->effects |= EF_NODRAW; - UTIL_SetOrigin( this, g_pGameRules->VecItemRespawnSpot( this ) );// blip to whereever you should respawn. + UTIL_SetOrigin( pev, g_pGameRules->VecItemRespawnSpot( this ) );// blip to whereever you should respawn. - SetThink(&CItem:: Materialize ); - AbsoluteNextThink( g_pGameRules->FlItemRespawnTime( this ) ); + SetThink ( Materialize ); + pev->nextthink = g_pGameRules->FlItemRespawnTime( this ); return this; } @@ -166,7 +166,7 @@ void CItem::Materialize( void ) pev->effects |= EF_MUZZLEFLASH; } - SetTouch(&CItem:: ItemTouch ); + SetTouch( ItemTouch ); } #define SF_SUIT_SHORTLOGON 0x0001 @@ -185,21 +185,15 @@ class CItemSuit : public CItem } BOOL MyTouch( CBasePlayer *pPlayer ) { - if ( pPlayer->pev->deadflag != DEAD_NO ) - { - return FALSE; - } - - if ( pPlayer->pev->weapons & ITEM_SUIT ) + if ( pPlayer->pev->weapons & (1<spawnflags & SF_SUIT_SHORTLOGON ) - EMIT_SOUND_SUIT(pPlayer->edict(), "!HEV_A0"); // short version of suit logon, - else EMIT_SOUND_SUIT(pPlayer->edict(), "!HEV_AAx"); // long version of suit logon - } - pPlayer->pev->weapons |= ITEM_SUIT; + if ( pev->spawnflags & SF_SUIT_SHORTLOGON ) + EMIT_SOUND_SUIT(pPlayer->edict(), "!HEV_A0"); // short version of suit logon, + else + EMIT_SOUND_SUIT(pPlayer->edict(), "!HEV_AAx"); // long version of suit logon + + pPlayer->pev->weapons |= (1<model) - SET_MODEL(ENT(pev), STRING(pev->model)); //LRC - else - SET_MODEL(ENT(pev), "models/w_battery.mdl"); + SET_MODEL(ENT(pev), "models/w_battery.mdl"); CItem::Spawn( ); } void Precache( void ) { - if (pev->model) - PRECACHE_MODEL((char*)STRING(pev->model)); //LRC - else - PRECACHE_MODEL ("models/w_battery.mdl"); - - if (pev->noise) - PRECACHE_SOUND( (char*)STRING(pev->noise) ); //LRC - else - PRECACHE_SOUND( "items/gunpickup2.wav" ); + PRECACHE_MODEL ("models/w_battery.mdl"); + PRECACHE_SOUND( "items/gunpickup2.wav" ); } BOOL MyTouch( CBasePlayer *pPlayer ) { @@ -238,38 +222,33 @@ class CItemBattery : public CItem return FALSE; } - float armor = 0; - if (pev->armorvalue) armor = pev->armorvalue; - else armor = gSkillData.batteryCapacity; - - if (pPlayer->TakeArmor( armor )) + if ((pPlayer->pev->armorvalue < MAX_NORMAL_BATTERY) && + (pPlayer->pev->weapons & (1<pev->armorvalue += gSkillData.batteryCapacity; + pPlayer->pev->armorvalue = min(pPlayer->pev->armorvalue, MAX_NORMAL_BATTERY); - if (pev->noise) EMIT_SOUND( pPlayer->edict(), CHAN_ITEM, STRING(pev->noise), 1, ATTN_NORM ); //LRC - else EMIT_SOUND( pPlayer->edict(), CHAN_ITEM, "items/gunpickup2.wav", 1, ATTN_NORM ); + EMIT_SOUND( pPlayer->edict(), CHAN_ITEM, "items/gunpickup2.wav", 1, ATTN_NORM ); MESSAGE_BEGIN( MSG_ONE, gmsgItemPickup, NULL, pPlayer->pev ); WRITE_STRING( STRING(pev->classname) ); MESSAGE_END(); - if(!gEvilImpulse101) - { - // Suit reports new power level - // For some reason this wasn't working in release build -- round it. - pct = (int)( (float)(pPlayer->pev->armorvalue * 100.0) * (1.0/MAX_NORMAL_BATTERY) + 0.5); - pct = (pct / 5); - if (pct > 0) pct--; + // Suit reports new power level + // For some reason this wasn't working in release build -- round it. + pct = (int)( (float)(pPlayer->pev->armorvalue * 100.0) * (1.0/MAX_NORMAL_BATTERY) + 0.5); + pct = (pct / 5); + if (pct > 0) + pct--; - sprintf( szcharge,"!HEV_%1dP", pct ); - - //EMIT_SOUND_SUIT(ENT(pev), szcharge); - pPlayer->SetSuitUpdate(szcharge, FALSE, SUIT_NEXT_IN_30SEC); - } + sprintf( szcharge,"!HEV_%1dP", pct ); + //EMIT_SOUND_SUIT(ENT(pev), szcharge); + pPlayer->SetSuitUpdate(szcharge, FALSE, SUIT_NEXT_IN_30SEC); return TRUE; } return FALSE; @@ -343,7 +322,7 @@ class CItemLongJump : public CItem return FALSE; } - if (pPlayer->pev->weapons & ITEM_SUIT) + if ( ( pPlayer->pev->weapons & (1<m_fLongJump = TRUE;// player now has longjump module @@ -353,8 +332,7 @@ class CItemLongJump : public CItem WRITE_STRING( STRING(pev->classname) ); MESSAGE_END(); - if(!gEvilImpulse101) // Play the longjump sound UNDONE: Kelly? correct sound? - EMIT_SOUND_SUIT( pPlayer->edict(), "!HEV_A1" ); + EMIT_SOUND_SUIT( pPlayer->edict(), "!HEV_A1" ); // Play the longjump sound UNDONE: Kelly? correct sound? return TRUE; } return FALSE; diff --git a/spirit/items.h b/dlls/items.h similarity index 96% rename from spirit/items.h rename to dlls/items.h index e9852966..04905fc4 100644 --- a/spirit/items.h +++ b/dlls/items.h @@ -1,29 +1,29 @@ -/*** -* -* Copyright (c) 1996-2002, 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. -* -****/ -#ifndef ITEMS_H -#define ITEMS_H - - -class CItem : public CBaseEntity -{ -public: - void Spawn( void ); - CBaseEntity* Respawn( void ); - void EXPORT ItemTouch( CBaseEntity *pOther ); - void EXPORT Materialize( void ); - virtual BOOL MyTouch( CBasePlayer *pPlayer ) { return FALSE; }; -}; - -#endif // ITEMS_H +/*** +* +* Copyright (c) 1996-2002, 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. +* +****/ +#ifndef ITEMS_H +#define ITEMS_H + + +class CItem : public CBaseEntity +{ +public: + void Spawn( void ); + CBaseEntity* Respawn( void ); + void EXPORT ItemTouch( CBaseEntity *pOther ); + void EXPORT Materialize( void ); + virtual BOOL MyTouch( CBasePlayer *pPlayer ) { return FALSE; }; +}; + +#endif // ITEMS_H diff --git a/spirit/leech.cpp b/dlls/leech.cpp similarity index 90% rename from spirit/leech.cpp rename to dlls/leech.cpp index 6d2d40ba..4f1ebc96 100644 --- a/spirit/leech.cpp +++ b/dlls/leech.cpp @@ -181,10 +181,7 @@ const char *CLeech::pAlertSounds[] = void CLeech::Spawn( void ) { Precache(); - if (pev->model) - SET_MODEL(ENT(pev), STRING(pev->model)); //LRC - else - SET_MODEL(ENT(pev), "models/leech.mdl"); + SET_MODEL(ENT(pev), "models/leech.mdl"); // Just for fun // SET_MODEL(ENT(pev), "models/icky.mdl"); @@ -194,13 +191,12 @@ void CLeech::Spawn( void ) pev->solid = SOLID_SLIDEBOX; pev->movetype = MOVETYPE_FLY; SetBits(pev->flags, FL_SWIM); - if (pev->health == 0) - pev->health = gSkillData.leechHealth; + pev->health = gSkillData.leechHealth; m_flFieldOfView = -0.5; // 180 degree FOV m_flDistLook = 750; MonsterInit(); - SetThink(&CLeech:: SwimThink ); + SetThink( SwimThink ); SetUse( NULL ); SetTouch( NULL ); pev->view_ofs = g_vecZero; @@ -216,7 +212,6 @@ void CLeech::Spawn( void ) void CLeech::Activate( void ) { RecalculateWaterlevel(); - CBaseMonster::Activate(); } @@ -259,7 +254,7 @@ void CLeech::SwitchLeechState( void ) { Look( m_flDistLook ); CBaseEntity *pEnemy = BestVisibleEnemy(); - if ( pEnemy && pEnemy->pev->waterlevel != 0 && pEnemy->pev->watertype != CONTENTS_FOG) + if ( pEnemy && pEnemy->pev->waterlevel != 0 ) { m_hEnemy = pEnemy; SetState( MONSTERSTATE_COMBAT ); @@ -300,10 +295,7 @@ void CLeech::Precache( void ) int i; //PRECACHE_MODEL("models/icky.mdl"); - if (pev->model) - PRECACHE_MODEL((char*)STRING(pev->model)); //LRC - else - PRECACHE_MODEL("models/leech.mdl"); + PRECACHE_MODEL("models/leech.mdl"); for ( i = 0; i < ARRAYSIZE( pAttackSounds ); i++ ) PRECACHE_SOUND((char *)pAttackSounds[i]); @@ -321,14 +313,7 @@ int CLeech::TakeDamage( entvars_t *pevInflictor, entvars_t *pevAttacker, float f { pev->velocity = (pev->origin - pevInflictor->origin).Normalize() * 25; } - else if ( pev->movetype == MOVETYPE_TOSS ) - { - ALERT(at_console, "Waterlevel is out\n" ); -// if ( RANDOM_LONG( 0, 99 ) < 1 ) - pev->dmg += 2; - - } return CBaseMonster::TakeDamage( pevInflictor, pevAttacker, flDamage, bitsDamageType ); } @@ -452,7 +437,7 @@ void CLeech::DeadThink( void ) } } StudioFrameAdvance(); - SetNextThink( 0.1 ); + pev->nextthink = gpGlobals->time + 0.1; // Apply damage velocity, but keep out of the walls if ( pev->velocity.x != 0 || pev->velocity.y != 0 ) @@ -518,7 +503,7 @@ void CLeech::UpdateMotion( void ) m_IdealActivity = ACT_MELEE_ATTACK1; // Out of water check - if ( !pev->waterlevel || pev->watertype == CONTENTS_FOG) + if ( !pev->waterlevel ) { pev->movetype = MOVETYPE_TOSS; m_IdealActivity = ACT_TWITCH; @@ -536,12 +521,6 @@ void CLeech::UpdateMotion( void ) pev->movetype = MOVETYPE_FLY; pev->flags &= ~FL_ONGROUND; RecalculateWaterlevel(); - ALERT(at_console, "Waterlevel is out\n" ); - if ( RANDOM_LONG( 0, 99 ) < 1 ) - { - pev->gravity = 0.02; - pev->takedamage += 2; - } m_waterTime = gpGlobals->time + 2; // Recalc again soon, water may be rising } @@ -584,14 +563,14 @@ void CLeech::SwimThink( void ) float targetYaw = 0; CBaseEntity *pTarget; - if ( FNullEnt( FIND_CLIENT_IN_PVS( edict() ) ) && !HaveCamerasInPVS( edict() )) + if ( FNullEnt( FIND_CLIENT_IN_PVS( edict() ) ) ) { - SetNextThink( RANDOM_FLOAT(1,1.5) ); + pev->nextthink = gpGlobals->time + RANDOM_FLOAT(1,1.5); pev->velocity = g_vecZero; return; } else - SetNextThink( 0.1 ); + pev->nextthink = gpGlobals->time + 0.1; targetSpeed = LEECH_SWIM_SPEED; @@ -713,14 +692,14 @@ void CLeech::Killed(entvars_t *pevAttacker, int iGib) Vector vecSplatDir; TraceResult tr; - ALERT(at_aiconsole, "Leech: killed\n"); + //ALERT(at_aiconsole, "Leech: killed\n"); // tell owner ( if any ) that we're dead.This is mostly for MonsterMaker functionality. CBaseEntity *pOwner = CBaseEntity::Instance(pev->owner); if (pOwner) pOwner->DeathNotice(pev); // When we hit the ground, play the "death_end" activity - if ( pev->waterlevel && pev->watertype != CONTENTS_FOG) + if ( pev->waterlevel ) { pev->angles.z = 0; pev->angles.x = 0; @@ -738,7 +717,7 @@ void CLeech::Killed(entvars_t *pevAttacker, int iGib) pev->movetype = MOVETYPE_TOSS; pev->takedamage = DAMAGE_NO; - SetThink(&CLeech:: DeadThink ); + SetThink( DeadThink ); } diff --git a/dlls/lights.cpp b/dlls/lights.cpp new file mode 100644 index 00000000..147dfdf1 --- /dev/null +++ b/dlls/lights.cpp @@ -0,0 +1,199 @@ +/*** +* +* Copyright (c) 1996-2002, 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. +* +****/ +/* + +===== lights.cpp ======================================================== + + spawn and think functions for editor-placed lights + +*/ + +#include "extdll.h" +#include "util.h" +#include "cbase.h" + + + +class CLight : public CPointEntity +{ +public: + virtual void KeyValue( KeyValueData* pkvd ); + virtual void Spawn( void ); + void Use( CBaseEntity *pActivator, CBaseEntity *pCaller, USE_TYPE useType, float value ); + + virtual int Save( CSave &save ); + virtual int Restore( CRestore &restore ); + + static TYPEDESCRIPTION m_SaveData[]; + +private: + int m_iStyle; + int m_iszPattern; +}; +LINK_ENTITY_TO_CLASS( light, CLight ); + +TYPEDESCRIPTION CLight::m_SaveData[] = +{ + DEFINE_FIELD( CLight, m_iStyle, FIELD_INTEGER ), + DEFINE_FIELD( CLight, m_iszPattern, FIELD_STRING ), +}; + +IMPLEMENT_SAVERESTORE( CLight, CPointEntity ); + + +// +// Cache user-entity-field values until spawn is called. +// +void CLight :: KeyValue( KeyValueData* pkvd) +{ + if (FStrEq(pkvd->szKeyName, "style")) + { + m_iStyle = atoi(pkvd->szValue); + pkvd->fHandled = TRUE; + } + else if (FStrEq(pkvd->szKeyName, "pitch")) + { + pev->angles.x = atof(pkvd->szValue); + pkvd->fHandled = TRUE; + } + else if (FStrEq(pkvd->szKeyName, "pattern")) + { + m_iszPattern = ALLOC_STRING( pkvd->szValue ); + pkvd->fHandled = TRUE; + } + else + { + CPointEntity::KeyValue( pkvd ); + } +} + +/*QUAKED light (0 1 0) (-8 -8 -8) (8 8 8) LIGHT_START_OFF +Non-displayed light. +Default light value is 300 +Default style is 0 +If targeted, it will toggle between on or off. +*/ + +void CLight :: Spawn( void ) +{ + if (FStringNull(pev->targetname)) + { // inert light + REMOVE_ENTITY(ENT(pev)); + return; + } + + if (m_iStyle >= 32) + { +// CHANGE_METHOD(ENT(pev), em_use, light_use); + if (FBitSet(pev->spawnflags, SF_LIGHT_START_OFF)) + LIGHT_STYLE(m_iStyle, "a"); + else if (m_iszPattern) + LIGHT_STYLE(m_iStyle, (char *)STRING( m_iszPattern )); + else + LIGHT_STYLE(m_iStyle, "m"); + } +} + + +void CLight :: Use( CBaseEntity *pActivator, CBaseEntity *pCaller, USE_TYPE useType, float value ) +{ + if (m_iStyle >= 32) + { + if ( !ShouldToggle( useType, !FBitSet(pev->spawnflags, SF_LIGHT_START_OFF) ) ) + return; + + if (FBitSet(pev->spawnflags, SF_LIGHT_START_OFF)) + { + if (m_iszPattern) + LIGHT_STYLE(m_iStyle, (char *)STRING( m_iszPattern )); + else + LIGHT_STYLE(m_iStyle, "m"); + ClearBits(pev->spawnflags, SF_LIGHT_START_OFF); + } + else + { + LIGHT_STYLE(m_iStyle, "a"); + SetBits(pev->spawnflags, SF_LIGHT_START_OFF); + } + } +} + +// +// shut up spawn functions for new spotlights +// +LINK_ENTITY_TO_CLASS( light_spot, CLight ); + + +class CEnvLight : public CLight +{ +public: + void KeyValue( KeyValueData* pkvd ); + void Spawn( void ); +}; + +LINK_ENTITY_TO_CLASS( light_environment, CEnvLight ); + +void CEnvLight::KeyValue( KeyValueData* pkvd ) +{ + if (FStrEq(pkvd->szKeyName, "_light")) + { + int r, g, b, v, j; + char szColor[64]; + j = sscanf( pkvd->szValue, "%d %d %d %d\n", &r, &g, &b, &v ); + if (j == 1) + { + g = b = r; + } + else if (j == 4) + { + r = r * (v / 255.0); + g = g * (v / 255.0); + b = b * (v / 255.0); + } + + // simulate qrad direct, ambient,and gamma adjustments, as well as engine scaling + r = pow( r / 114.0, 0.6 ) * 264; + g = pow( g / 114.0, 0.6 ) * 264; + b = pow( b / 114.0, 0.6 ) * 264; + + pkvd->fHandled = TRUE; + sprintf( szColor, "%d", r ); + CVAR_SET_STRING( "sv_skycolor_r", szColor ); + sprintf( szColor, "%d", g ); + CVAR_SET_STRING( "sv_skycolor_g", szColor ); + sprintf( szColor, "%d", b ); + CVAR_SET_STRING( "sv_skycolor_b", szColor ); + } + else + { + CLight::KeyValue( pkvd ); + } +} + + +void CEnvLight :: Spawn( void ) +{ + char szVector[64]; + UTIL_MakeAimVectors( pev->angles ); + + sprintf( szVector, "%f", gpGlobals->v_forward.x ); + CVAR_SET_STRING( "sv_skyvec_x", szVector ); + sprintf( szVector, "%f", gpGlobals->v_forward.y ); + CVAR_SET_STRING( "sv_skyvec_y", szVector ); + sprintf( szVector, "%f", gpGlobals->v_forward.z ); + CVAR_SET_STRING( "sv_skyvec_z", szVector ); + + CLight::Spawn( ); +} diff --git a/spirit/maprules.cpp b/dlls/maprules.cpp similarity index 92% rename from spirit/maprules.cpp rename to dlls/maprules.cpp index abeaccbc..9402f319 100644 --- a/spirit/maprules.cpp +++ b/dlls/maprules.cpp @@ -1,963 +1,918 @@ -/*** -* -* Copyright (c) 1996-2002, 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. -* -****/ - -// ------------------------------------------- -// -// maprules.cpp -// -// This module contains entities for implementing/changing game -// rules dynamically within each map (.BSP) -// -// ------------------------------------------- - -#include "extdll.h" -#include "util.h" -#include "gamerules.h" -#include "maprules.h" -#include "cbase.h" -#include "player.h" - -class CRuleEntity : public CBaseEntity -{ -public: - void Spawn( void ); - void KeyValue( KeyValueData *pkvd ); - virtual int Save( CSave &save ); - virtual int Restore( CRestore &restore ); - static TYPEDESCRIPTION m_SaveData[]; - - void SetMaster( int iszMaster ) { m_iszMaster = iszMaster; } - -protected: - BOOL CanFireForActivator( CBaseEntity *pActivator ); - -private: - string_t m_iszMaster; -}; - -TYPEDESCRIPTION CRuleEntity::m_SaveData[] = -{ - DEFINE_FIELD( CRuleEntity, m_iszMaster, FIELD_STRING), -}; - -IMPLEMENT_SAVERESTORE( CRuleEntity, CBaseEntity ); - - -void CRuleEntity::Spawn( void ) -{ - pev->solid = SOLID_NOT; - pev->movetype = MOVETYPE_NONE; - pev->effects = EF_NODRAW; -} - - -void CRuleEntity::KeyValue( KeyValueData *pkvd ) -{ - if (FStrEq(pkvd->szKeyName, "master")) - { - SetMaster( ALLOC_STRING(pkvd->szValue) ); - pkvd->fHandled = TRUE; - } - else - CBaseEntity::KeyValue( pkvd ); -} - -BOOL CRuleEntity::CanFireForActivator( CBaseEntity *pActivator ) -{ - if (!pActivator) - { - return TRUE; - } - else if ( m_iszMaster ) - { - if ( UTIL_IsMasterTriggered( m_iszMaster, pActivator ) ) - return TRUE; - else - return FALSE; - } - - return TRUE; -} - -// -// CRulePointEntity -- base class for all rule "point" entities (not brushes) -// -class CRulePointEntity : public CRuleEntity -{ -public: - void Spawn( void ); -}; - -void CRulePointEntity::Spawn( void ) -{ - CRuleEntity::Spawn(); - pev->frame = 0; - pev->model = 0; -} - -// -// CRuleBrushEntity -- base class for all rule "brush" entities (not brushes) -// Default behavior is to set up like a trigger, invisible, but keep the model for volume testing -// -class CRuleBrushEntity : public CRuleEntity -{ -public: - void Spawn( void ); - -private: -}; - -void CRuleBrushEntity::Spawn( void ) -{ - SET_MODEL( edict(), STRING(pev->model) ); - CRuleEntity::Spawn(); -} - - -// CGameScore / game_score -- award points to player / team -// Points +/- total -// Flag: Allow negative scores SF_SCORE_NEGATIVE -// Flag: Award points to team in teamplay SF_SCORE_TEAM - -#define SF_SCORE_NEGATIVE 0x0001 -#define SF_SCORE_TEAM 0x0002 - -class CGameScore : public CRulePointEntity -{ -public: - void Spawn( void ); - void Use( CBaseEntity *pActivator, CBaseEntity *pCaller, USE_TYPE useType, float value ); - void KeyValue( KeyValueData *pkvd ); - - inline int Points( void ) { return pev->frags; } - inline BOOL AllowNegativeScore( void ) { return pev->spawnflags & SF_SCORE_NEGATIVE; } - inline BOOL AwardToTeam( void ) { return pev->spawnflags & SF_SCORE_TEAM; } - - inline void SetPoints( int points ) { pev->frags = points; } - -private: -}; - -LINK_ENTITY_TO_CLASS( game_score, CGameScore ); - - -void CGameScore::Spawn( void ) -{ - CRulePointEntity::Spawn(); -} - - -void CGameScore::KeyValue( KeyValueData *pkvd ) -{ - if (FStrEq(pkvd->szKeyName, "points")) - { - SetPoints( atoi(pkvd->szValue) ); - pkvd->fHandled = TRUE; - } - else - CRulePointEntity::KeyValue( pkvd ); -} - - - -void CGameScore::Use( CBaseEntity *pActivator, CBaseEntity *pCaller, USE_TYPE useType, float value ) -{ - if ( !CanFireForActivator( pActivator ) ) - return; - - // Only players can use this - if ( pActivator->IsPlayer() ) - { - if ( AwardToTeam() ) - { - pActivator->AddPointsToTeam( Points(), AllowNegativeScore() ); - } - else - { - pActivator->AddPoints( Points(), AllowNegativeScore() ); - } - } -} - - -// CGameEnd / game_end -- Ends the game in MP - -class CGameEnd : public CRulePointEntity -{ -public: - void Use( CBaseEntity *pActivator, CBaseEntity *pCaller, USE_TYPE useType, float value ); -private: -}; - -LINK_ENTITY_TO_CLASS( game_end, CGameEnd ); - - -void CGameEnd::Use( CBaseEntity *pActivator, CBaseEntity *pCaller, USE_TYPE useType, float value ) -{ - if ( !CanFireForActivator( pActivator ) ) - return; - - g_pGameRules->EndMultiplayerGame(); -} - - -// -// CGameText / game_text -- NON-Localized HUD Message (use env_message to display a titles.txt message) -// Flag: All players SF_ENVTEXT_ALLPLAYERS -// - - -#define SF_ENVTEXT_ALLPLAYERS 0x0001 -#define SF_ENVTEXT_ONLY_ONCE 0x0002 - - -class CGameText : public CRulePointEntity -{ -public: - void Use( CBaseEntity *pActivator, CBaseEntity *pCaller, USE_TYPE useType, float value ); - void KeyValue( KeyValueData *pkvd ); - - virtual int Save( CSave &save ); - virtual int Restore( CRestore &restore ); - static TYPEDESCRIPTION m_SaveData[]; - - inline BOOL MessageToAll( void ) { return (pev->spawnflags & SF_ENVTEXT_ALLPLAYERS); } - inline void MessageSet( const char *pMessage ) { pev->message = ALLOC_STRING(pMessage); } - inline const char *MessageGet( void ) { return STRING(pev->message); } - - void EXPORT TriggerThink( void ); - -private: - - hudtextparms_t m_textParms; - CBaseEntity *m_pActivator; -}; - -LINK_ENTITY_TO_CLASS( game_text, CGameText ); - -// Save parms as a block. Will break save/restore if the structure changes, but this entity didn't ship with Half-Life, so -// it can't impact saved Half-Life games. -TYPEDESCRIPTION CGameText::m_SaveData[] = -{ - DEFINE_ARRAY( CGameText, m_textParms, FIELD_CHARACTER, sizeof(hudtextparms_t) ), - DEFINE_FIELD( CGameText, m_pActivator, FIELD_CLASSPTR ), -}; - -IMPLEMENT_SAVERESTORE( CGameText, CRulePointEntity ); - - -void CGameText::KeyValue( KeyValueData *pkvd ) -{ - if (FStrEq(pkvd->szKeyName, "channel")) - { - m_textParms.channel = atoi( pkvd->szValue ); - pkvd->fHandled = TRUE; - } - else if (FStrEq(pkvd->szKeyName, "x")) - { - m_textParms.x = atof( pkvd->szValue ); - pkvd->fHandled = TRUE; - } - else if (FStrEq(pkvd->szKeyName, "y")) - { - m_textParms.y = atof( pkvd->szValue ); - pkvd->fHandled = TRUE; - } - else if (FStrEq(pkvd->szKeyName, "effect")) - { - m_textParms.effect = atoi( pkvd->szValue ); - pkvd->fHandled = TRUE; - } - else if (FStrEq(pkvd->szKeyName, "color")) - { - int color[4]; - UTIL_StringToIntArray( color, 4, pkvd->szValue ); - m_textParms.r1 = color[0]; - m_textParms.g1 = color[1]; - m_textParms.b1 = color[2]; - m_textParms.a1 = color[3]; - pkvd->fHandled = TRUE; - } - else if (FStrEq(pkvd->szKeyName, "color2")) - { - int color[4]; - UTIL_StringToIntArray( color, 4, pkvd->szValue ); - m_textParms.r2 = color[0]; - m_textParms.g2 = color[1]; - m_textParms.b2 = color[2]; - m_textParms.a2 = color[3]; - pkvd->fHandled = TRUE; - } - else if (FStrEq(pkvd->szKeyName, "fadein")) - { - m_textParms.fadeinTime = atof( pkvd->szValue ); - pkvd->fHandled = TRUE; - } - else if (FStrEq(pkvd->szKeyName, "fadeout")) - { - m_textParms.fadeoutTime = atof( pkvd->szValue ); - pkvd->fHandled = TRUE; - } - else if (FStrEq(pkvd->szKeyName, "holdtime")) - { - m_textParms.holdTime = atof( pkvd->szValue ); - pkvd->fHandled = TRUE; - } - else if (FStrEq(pkvd->szKeyName, "fxtime")) - { - m_textParms.fxTime = atof( pkvd->szValue ); - pkvd->fHandled = TRUE; - } - else - CRulePointEntity::KeyValue( pkvd ); -} - - -void CGameText::Use( CBaseEntity *pActivator, CBaseEntity *pCaller, USE_TYPE useType, float value ) -{ - if ( !CanFireForActivator( pActivator ) ) - return; - - if ( MessageToAll() ) - { - UTIL_HudMessageAll( m_textParms, MessageGet() ); - } - else - { - if ( pActivator && pActivator->IsNetClient() ) - { - UTIL_HudMessage( pActivator, m_textParms, MessageGet() ); - } - } - - if ( pev->target ) - { - m_pActivator = pActivator; - SetThink(&CGameText:: TriggerThink ); - SetNextThink( m_textParms.fadeinTime + m_textParms.holdTime + m_textParms.fadeoutTime ); -// ALERT(at_console, "GameText sets NextThink = %f\n", m_textParms.fadeinTime + m_textParms.holdTime + m_textParms.fadeoutTime); -} - else if ( pev->spawnflags & SF_ENVTEXT_ONLY_ONCE ) - { - SetThink(&CGameText:: SUB_Remove ); - SetNextThink( 0.1 ); - } -} - -//LRC -void CGameText::TriggerThink( void ) -{ -// ALERT(at_console, "GameText uses targets\n"); - SUB_UseTargets( m_pActivator, USE_TOGGLE, 0 ); - - if ( pev->spawnflags & SF_ENVTEXT_ONLY_ONCE ) - { - SetThink(&CGameText:: SUB_Remove ); - SetNextThink( 0.1 ); - } -} - - - -// -// CGameTeamMaster / game_team_master -- "Masters" like multisource, but based on the team of the activator -// Only allows mastered entity to fire if the team matches my team -// -// team index (pulled from server team list "mp_teamlist" -// Flag: Remove on Fire -// Flag: Any team until set? -- Any team can use this until the team is set (otherwise no teams can use it) -// - -#define SF_TEAMMASTER_FIREONCE 0x0001 -#define SF_TEAMMASTER_ANYTEAM 0x0002 - -class CGameTeamMaster : public CRulePointEntity -{ -public: - void KeyValue( KeyValueData *pkvd ); - void Use( CBaseEntity *pActivator, CBaseEntity *pCaller, USE_TYPE useType, float value ); -// int ObjectCaps( void ) { return CRulePointEntity:: ObjectCaps() | FCAP_MASTER; } - - BOOL IsTriggered( CBaseEntity *pActivator ); - const char *TeamID( void ); - inline BOOL RemoveOnFire( void ) { return (pev->spawnflags & SF_TEAMMASTER_FIREONCE) ? TRUE : FALSE; } - inline BOOL AnyTeam( void ) { return (pev->spawnflags & SF_TEAMMASTER_ANYTEAM) ? TRUE : FALSE; } - -private: - BOOL TeamMatch( CBaseEntity *pActivator ); - - int m_teamIndex; - USE_TYPE triggerType; -}; - -LINK_ENTITY_TO_CLASS( game_team_master, CGameTeamMaster ); - -void CGameTeamMaster::KeyValue( KeyValueData *pkvd ) -{ - if (FStrEq(pkvd->szKeyName, "teamindex")) - { - m_teamIndex = atoi( pkvd->szValue ); - pkvd->fHandled = TRUE; - } - else if (FStrEq(pkvd->szKeyName, "triggerstate")) - { - int type = atoi( pkvd->szValue ); - switch( type ) - { - case 0: - triggerType = USE_OFF; - break; - case 2: - triggerType = USE_TOGGLE; - break; - default: - triggerType = USE_ON; - break; - } - pkvd->fHandled = TRUE; - } - else - CRulePointEntity::KeyValue( pkvd ); -} - - -void CGameTeamMaster::Use( CBaseEntity *pActivator, CBaseEntity *pCaller, USE_TYPE useType, float value ) -{ - if ( !CanFireForActivator( pActivator ) ) - return; - - if ( useType == USE_SET ) - { - if ( value < 0 ) - { - m_teamIndex = -1; - } - else - { - m_teamIndex = g_pGameRules->GetTeamIndex( pActivator->TeamID() ); - } - return; - } - - if ( TeamMatch( pActivator ) ) - { - SUB_UseTargets( pActivator, triggerType, value ); - if ( RemoveOnFire() ) - UTIL_Remove( this ); - } -} - - -BOOL CGameTeamMaster::IsTriggered( CBaseEntity *pActivator ) -{ - return TeamMatch( pActivator ); -} - - -const char *CGameTeamMaster::TeamID( void ) -{ - if ( m_teamIndex < 0 ) // Currently set to "no team" - return ""; - - return g_pGameRules->GetIndexedTeamName( m_teamIndex ); // UNDONE: Fill this in with the team from the "teamlist" -} - - -BOOL CGameTeamMaster::TeamMatch( CBaseEntity *pActivator ) -{ - if ( m_teamIndex < 0 && AnyTeam() ) - return TRUE; - - if ( !pActivator ) - return FALSE; - - return UTIL_TeamsMatch( pActivator->TeamID(), TeamID() ); -} - - -// -// CGameTeamSet / game_team_set -- Changes the team of the entity it targets to the activator's team -// Flag: Fire once -// Flag: Clear team -- Sets the team to "NONE" instead of activator - -#define SF_TEAMSET_FIREONCE 0x0001 -#define SF_TEAMSET_CLEARTEAM 0x0002 - -class CGameTeamSet : public CRulePointEntity -{ -public: - void Use( CBaseEntity *pActivator, CBaseEntity *pCaller, USE_TYPE useType, float value ); - inline BOOL RemoveOnFire( void ) { return (pev->spawnflags & SF_TEAMSET_FIREONCE) ? TRUE : FALSE; } - inline BOOL ShouldClearTeam( void ) { return (pev->spawnflags & SF_TEAMSET_CLEARTEAM) ? TRUE : FALSE; } - -private: -}; - -LINK_ENTITY_TO_CLASS( game_team_set, CGameTeamSet ); - - -void CGameTeamSet::Use( CBaseEntity *pActivator, CBaseEntity *pCaller, USE_TYPE useType, float value ) -{ - if ( !CanFireForActivator( pActivator ) ) - return; - - if ( ShouldClearTeam() ) - { - SUB_UseTargets( pActivator, USE_SET, -1 ); - } - else - { - SUB_UseTargets( pActivator, USE_SET, 0 ); - } - - if ( RemoveOnFire() ) - { - UTIL_Remove( this ); - } -} - - -// -// CGamePlayerZone / game_player_zone -- players in the zone fire my target when I'm fired -// -// Needs master? -class CGamePlayerZone : public CRuleBrushEntity -{ -public: - void KeyValue( KeyValueData *pkvd ); - void Use( CBaseEntity *pActivator, CBaseEntity *pCaller, USE_TYPE useType, float value ); - - virtual int Save( CSave &save ); - virtual int Restore( CRestore &restore ); - static TYPEDESCRIPTION m_SaveData[]; - -private: - string_t m_iszInTarget; - string_t m_iszOutTarget; - string_t m_iszInCount; - string_t m_iszOutCount; -}; - -LINK_ENTITY_TO_CLASS( game_zone_player, CGamePlayerZone ); -TYPEDESCRIPTION CGamePlayerZone::m_SaveData[] = -{ - DEFINE_FIELD( CGamePlayerZone, m_iszInTarget, FIELD_STRING ), - DEFINE_FIELD( CGamePlayerZone, m_iszOutTarget, FIELD_STRING ), - DEFINE_FIELD( CGamePlayerZone, m_iszInCount, FIELD_STRING ), - DEFINE_FIELD( CGamePlayerZone, m_iszOutCount, FIELD_STRING ), -}; - -IMPLEMENT_SAVERESTORE( CGamePlayerZone, CRuleBrushEntity ); - -void CGamePlayerZone::KeyValue( KeyValueData *pkvd ) -{ - if (FStrEq(pkvd->szKeyName, "intarget")) - { - m_iszInTarget = ALLOC_STRING( pkvd->szValue ); - pkvd->fHandled = TRUE; - } - else if (FStrEq(pkvd->szKeyName, "outtarget")) - { - m_iszOutTarget = ALLOC_STRING( pkvd->szValue ); - pkvd->fHandled = TRUE; - } - else if (FStrEq(pkvd->szKeyName, "incount")) - { - m_iszInCount = ALLOC_STRING( pkvd->szValue ); - pkvd->fHandled = TRUE; - } - else if (FStrEq(pkvd->szKeyName, "outcount")) - { - m_iszOutCount = ALLOC_STRING( pkvd->szValue ); - pkvd->fHandled = TRUE; - } - else - CRuleBrushEntity::KeyValue( pkvd ); -} - -void CGamePlayerZone::Use( CBaseEntity *pActivator, CBaseEntity *pCaller, USE_TYPE useType, float value ) -{ - int playersInCount = 0; - int playersOutCount = 0; - - if ( !CanFireForActivator( pActivator ) ) - return; - - CBaseEntity *pPlayer = NULL; - - for ( int i = 1; i <= gpGlobals->maxClients; i++ ) - { - pPlayer = UTIL_PlayerByIndex( i ); - if ( pPlayer ) - { - TraceResult trace; - int hullNumber; - BOOL inside = FALSE; - - if (pev->origin == g_vecZero) //LRC - to support movewith - { - hullNumber = human_hull; - if ( pPlayer->pev->flags & FL_DUCKING ) - { - hullNumber = head_hull; - } - UTIL_TraceModel( pPlayer->pev->origin, pPlayer->pev->origin, hullNumber, edict(), &trace ); - inside = trace.fStartSolid; - } - else - { - //LIMITATION: this doesn't allow for non-cuboid game_zone_player entities. - // (is that a problem?) - inside = this->Intersects(pPlayer); - } - - if ( inside ) - { - playersInCount++; - if ( m_iszInTarget ) - { - FireTargets( STRING(m_iszInTarget), pPlayer, pActivator, useType, value ); - } - } - else - { - playersOutCount++; - if ( m_iszOutTarget ) - { - FireTargets( STRING(m_iszOutTarget), pPlayer, pActivator, useType, value ); - } - } - } - } - - if ( m_iszInCount ) - { - FireTargets( STRING(m_iszInCount), pActivator, this, USE_SET, playersInCount ); - } - - if ( m_iszOutCount ) - { - FireTargets( STRING(m_iszOutCount), pActivator, this, USE_SET, playersOutCount ); - } -} - - - -// -// CGamePlayerHurt / game_player_hurt -- Damages the player who fires it -// Flag: Fire once - -#define SF_PKILL_FIREONCE 0x0001 -class CGamePlayerHurt : public CRulePointEntity -{ -public: - void Use( CBaseEntity *pActivator, CBaseEntity *pCaller, USE_TYPE useType, float value ); - inline BOOL RemoveOnFire( void ) { return (pev->spawnflags & SF_PKILL_FIREONCE) ? TRUE : FALSE; } - -private: -}; - -LINK_ENTITY_TO_CLASS( game_player_hurt, CGamePlayerHurt ); - - -void CGamePlayerHurt::Use( CBaseEntity *pActivator, CBaseEntity *pCaller, USE_TYPE useType, float value ) -{ - if ( !CanFireForActivator( pActivator ) ) - return; - - if ( pActivator->IsPlayer() ) - { - if ( pev->dmg < 0 ) - pActivator->TakeHealth( -pev->dmg, DMG_GENERIC ); - else - pActivator->TakeDamage( pev, pev, pev->dmg, DMG_GENERIC ); - } - - SUB_UseTargets( pActivator, useType, value ); - - if ( RemoveOnFire() ) - { - UTIL_Remove( this ); - } -} - - - -// -// CGameCounter / game_counter -- Counts events and fires target -// Flag: Fire once -// Flag: Reset on Fire - -#define SF_GAMECOUNT_FIREONCE 0x0001 -#define SF_GAMECOUNT_RESET 0x0002 - -class CGameCounter : public CRulePointEntity -{ -public: - void Spawn( void ); - void Use( CBaseEntity *pActivator, CBaseEntity *pCaller, USE_TYPE useType, float value ); - inline BOOL RemoveOnFire( void ) { return (pev->spawnflags & SF_GAMECOUNT_FIREONCE) ? TRUE : FALSE; } - inline BOOL ResetOnFire( void ) { return (pev->spawnflags & SF_GAMECOUNT_RESET) ? TRUE : FALSE; } - - inline void CountUp( void ) { pev->frags++; } - inline void CountDown( void ) { pev->frags--; } - inline void ResetCount( void ) { pev->frags = pev->dmg; } - inline int CountValue( void ) { return pev->frags; } - inline int LimitValue( void ) { return pev->health; } - - inline BOOL HitLimit( void ) { return CountValue() == LimitValue(); } - -private: - - inline void SetCountValue( int value ) { pev->frags = value; } - inline void SetInitialValue( int value ) { pev->dmg = value; } -}; - -LINK_ENTITY_TO_CLASS( game_counter, CGameCounter ); - -void CGameCounter::Spawn( void ) -{ - // Save off the initial count - SetInitialValue( CountValue() ); - CRulePointEntity::Spawn(); -} - - -void CGameCounter::Use( CBaseEntity *pActivator, CBaseEntity *pCaller, USE_TYPE useType, float value ) -{ - if ( !CanFireForActivator( pActivator ) ) - return; - - switch( useType ) - { - case USE_ON: - case USE_TOGGLE: - CountUp(); - break; - - case USE_OFF: - CountDown(); - break; - - case USE_SET: - SetCountValue( (int)value ); - break; - } - - if ( HitLimit() ) - { - SUB_UseTargets( pActivator, USE_TOGGLE, 0 ); - if ( RemoveOnFire() ) - { - UTIL_Remove( this ); - } - - if ( ResetOnFire() ) - { - ResetCount(); - } - } -} - - - -// -// CGameCounterSet / game_counter_set -- Sets the counter's value -// Flag: Fire once - -#define SF_GAMECOUNTSET_FIREONCE 0x0001 - -class CGameCounterSet : public CRulePointEntity -{ -public: - void Use( CBaseEntity *pActivator, CBaseEntity *pCaller, USE_TYPE useType, float value ); - inline BOOL RemoveOnFire( void ) { return (pev->spawnflags & SF_GAMECOUNTSET_FIREONCE) ? TRUE : FALSE; } - -private: -}; - -LINK_ENTITY_TO_CLASS( game_counter_set, CGameCounterSet ); - - -void CGameCounterSet::Use( CBaseEntity *pActivator, CBaseEntity *pCaller, USE_TYPE useType, float value ) -{ - if ( !CanFireForActivator( pActivator ) ) - return; - - SUB_UseTargets( pActivator, USE_SET, pev->frags ); - - if ( RemoveOnFire() ) - { - UTIL_Remove( this ); - } -} - - -// -// CGamePlayerEquip / game_playerequip -- Sets the default player equipment -// Flag: USE Only - -#define SF_PLAYEREQUIP_USEONLY 0x0001 -#define MAX_EQUIP 32 - -class CGamePlayerEquip : public CRulePointEntity -{ -public: - void KeyValue( KeyValueData *pkvd ); - void Touch( CBaseEntity *pOther ); - void Use( CBaseEntity *pActivator, CBaseEntity *pCaller, USE_TYPE useType, float value ); - - inline BOOL UseOnly( void ) { return (pev->spawnflags & SF_PLAYEREQUIP_USEONLY) ? TRUE : FALSE; } - -private: - - void EquipPlayer( CBaseEntity *pPlayer ); - - string_t m_weaponNames[MAX_EQUIP]; - int m_weaponCount[MAX_EQUIP]; -}; - -LINK_ENTITY_TO_CLASS( game_player_equip, CGamePlayerEquip ); - - -void CGamePlayerEquip::KeyValue( KeyValueData *pkvd ) -{ - CRulePointEntity::KeyValue( pkvd ); - - if ( !pkvd->fHandled ) - { - for ( int i = 0; i < MAX_EQUIP; i++ ) - { - if ( !m_weaponNames[i] ) - { - char tmp[128]; - - UTIL_StripToken( pkvd->szKeyName, tmp ); - - m_weaponNames[i] = ALLOC_STRING(tmp); - m_weaponCount[i] = atoi(pkvd->szValue); - m_weaponCount[i] = max(1,m_weaponCount[i]); - pkvd->fHandled = TRUE; - break; - } - } - } -} - - -void CGamePlayerEquip::Touch( CBaseEntity *pOther ) -{ - if ( !CanFireForActivator( pOther ) ) - return; - - if ( UseOnly() ) - return; - - EquipPlayer( pOther ); -} - -void CGamePlayerEquip::EquipPlayer( CBaseEntity *pEntity ) -{ - CBasePlayer *pPlayer = NULL; - - if ( pEntity->IsPlayer() ) - { - pPlayer = (CBasePlayer *)pEntity; - } - - if ( !pPlayer ) - return; - - for ( int i = 0; i < MAX_EQUIP; i++ ) - { - if ( !m_weaponNames[i] ) - break; - for ( int j = 0; j < m_weaponCount[i]; j++ ) - { - pPlayer->GiveNamedItem( STRING(m_weaponNames[i]) ); - } - } -} - - -void CGamePlayerEquip::Use( CBaseEntity *pActivator, CBaseEntity *pCaller, USE_TYPE useType, float value ) -{ - EquipPlayer( pActivator ); -} - - -// -// CGamePlayerTeam / game_player_team -- Changes the team of the player who fired it -// Flag: Fire once -// Flag: Kill Player -// Flag: Gib Player - -#define SF_PTEAM_FIREONCE 0x0001 -#define SF_PTEAM_KILL 0x0002 -#define SF_PTEAM_GIB 0x0004 - -class CGamePlayerTeam : public CRulePointEntity -{ -public: - void Use( CBaseEntity *pActivator, CBaseEntity *pCaller, USE_TYPE useType, float value ); - -private: - - inline BOOL RemoveOnFire( void ) { return (pev->spawnflags & SF_PTEAM_FIREONCE) ? TRUE : FALSE; } - inline BOOL ShouldKillPlayer( void ) { return (pev->spawnflags & SF_PTEAM_KILL) ? TRUE : FALSE; } - inline BOOL ShouldGibPlayer( void ) { return (pev->spawnflags & SF_PTEAM_GIB) ? TRUE : FALSE; } - - const char *TargetTeamName( const char *pszTargetName ); -}; - -LINK_ENTITY_TO_CLASS( game_player_team, CGamePlayerTeam ); - - -const char *CGamePlayerTeam::TargetTeamName( const char *pszTargetName ) -{ - CBaseEntity *pTeamEntity = NULL; - - while ((pTeamEntity = UTIL_FindEntityByTargetname( pTeamEntity, pszTargetName )) != NULL) - { - if ( FClassnameIs( pTeamEntity->pev, "game_team_master" ) ) - return pTeamEntity->TeamID(); - } - - return NULL; -} - - -void CGamePlayerTeam::Use( CBaseEntity *pActivator, CBaseEntity *pCaller, USE_TYPE useType, float value ) -{ - if ( !CanFireForActivator( pActivator ) ) - return; - - if ( pActivator->IsPlayer() ) - { - const char *pszTargetTeam = TargetTeamName( STRING(pev->target) ); - if ( pszTargetTeam ) - { - CBasePlayer *pPlayer = (CBasePlayer *)pActivator; - g_pGameRules->ChangePlayerTeam( pPlayer, pszTargetTeam, ShouldKillPlayer(), ShouldGibPlayer() ); - } - } - - if ( RemoveOnFire() ) - { - UTIL_Remove( this ); - } -} - - +/*** +* +* Copyright (c) 1996-2002, 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. +* +****/ + +// ------------------------------------------- +// +// maprules.cpp +// +// This module contains entities for implementing/changing game +// rules dynamically within each map (.BSP) +// +// ------------------------------------------- + +#include "extdll.h" +#include "eiface.h" +#include "util.h" +#include "gamerules.h" +#include "maprules.h" +#include "cbase.h" +#include "player.h" + +class CRuleEntity : public CBaseEntity +{ +public: + void Spawn( void ); + void KeyValue( KeyValueData *pkvd ); + virtual int Save( CSave &save ); + virtual int Restore( CRestore &restore ); + static TYPEDESCRIPTION m_SaveData[]; + + void SetMaster( int iszMaster ) { m_iszMaster = iszMaster; } + +protected: + BOOL CanFireForActivator( CBaseEntity *pActivator ); + +private: + string_t m_iszMaster; +}; + +TYPEDESCRIPTION CRuleEntity::m_SaveData[] = +{ + DEFINE_FIELD( CRuleEntity, m_iszMaster, FIELD_STRING), +}; + +IMPLEMENT_SAVERESTORE( CRuleEntity, CBaseEntity ); + + +void CRuleEntity::Spawn( void ) +{ + pev->solid = SOLID_NOT; + pev->movetype = MOVETYPE_NONE; + pev->effects = EF_NODRAW; +} + + +void CRuleEntity::KeyValue( KeyValueData *pkvd ) +{ + if (FStrEq(pkvd->szKeyName, "master")) + { + SetMaster( ALLOC_STRING(pkvd->szValue) ); + pkvd->fHandled = TRUE; + } + else + CBaseEntity::KeyValue( pkvd ); +} + +BOOL CRuleEntity::CanFireForActivator( CBaseEntity *pActivator ) +{ + if ( m_iszMaster ) + { + if ( UTIL_IsMasterTriggered( m_iszMaster, pActivator ) ) + return TRUE; + else + return FALSE; + } + + return TRUE; +} + +// +// CRulePointEntity -- base class for all rule "point" entities (not brushes) +// +class CRulePointEntity : public CRuleEntity +{ +public: + void Spawn( void ); +}; + +void CRulePointEntity::Spawn( void ) +{ + CRuleEntity::Spawn(); + pev->frame = 0; + pev->model = 0; +} + +// +// CRuleBrushEntity -- base class for all rule "brush" entities (not brushes) +// Default behavior is to set up like a trigger, invisible, but keep the model for volume testing +// +class CRuleBrushEntity : public CRuleEntity +{ +public: + void Spawn( void ); + +private: +}; + +void CRuleBrushEntity::Spawn( void ) +{ + SET_MODEL( edict(), STRING(pev->model) ); + CRuleEntity::Spawn(); +} + + +// CGameScore / game_score -- award points to player / team +// Points +/- total +// Flag: Allow negative scores SF_SCORE_NEGATIVE +// Flag: Award points to team in teamplay SF_SCORE_TEAM + +#define SF_SCORE_NEGATIVE 0x0001 +#define SF_SCORE_TEAM 0x0002 + +class CGameScore : public CRulePointEntity +{ +public: + void Spawn( void ); + void Use( CBaseEntity *pActivator, CBaseEntity *pCaller, USE_TYPE useType, float value ); + void KeyValue( KeyValueData *pkvd ); + + inline int Points( void ) { return pev->frags; } + inline BOOL AllowNegativeScore( void ) { return pev->spawnflags & SF_SCORE_NEGATIVE; } + inline BOOL AwardToTeam( void ) { return pev->spawnflags & SF_SCORE_TEAM; } + + inline void SetPoints( int points ) { pev->frags = points; } + +private: +}; + +LINK_ENTITY_TO_CLASS( game_score, CGameScore ); + + +void CGameScore::Spawn( void ) +{ + CRulePointEntity::Spawn(); +} + + +void CGameScore::KeyValue( KeyValueData *pkvd ) +{ + if (FStrEq(pkvd->szKeyName, "points")) + { + SetPoints( atoi(pkvd->szValue) ); + pkvd->fHandled = TRUE; + } + else + CRulePointEntity::KeyValue( pkvd ); +} + + + +void CGameScore::Use( CBaseEntity *pActivator, CBaseEntity *pCaller, USE_TYPE useType, float value ) +{ + if ( !CanFireForActivator( pActivator ) ) + return; + + // Only players can use this + if ( pActivator->IsPlayer() ) + { + if ( AwardToTeam() ) + { + pActivator->AddPointsToTeam( Points(), AllowNegativeScore() ); + } + else + { + pActivator->AddPoints( Points(), AllowNegativeScore() ); + } + } +} + + +// CGameEnd / game_end -- Ends the game in MP + +class CGameEnd : public CRulePointEntity +{ +public: + void Use( CBaseEntity *pActivator, CBaseEntity *pCaller, USE_TYPE useType, float value ); +private: +}; + +LINK_ENTITY_TO_CLASS( game_end, CGameEnd ); + + +void CGameEnd::Use( CBaseEntity *pActivator, CBaseEntity *pCaller, USE_TYPE useType, float value ) +{ + if ( !CanFireForActivator( pActivator ) ) + return; + + g_pGameRules->EndMultiplayerGame(); +} + + +// +// CGameText / game_text -- NON-Localized HUD Message (use env_message to display a titles.txt message) +// Flag: All players SF_ENVTEXT_ALLPLAYERS +// + + +#define SF_ENVTEXT_ALLPLAYERS 0x0001 + + +class CGameText : public CRulePointEntity +{ +public: + void Use( CBaseEntity *pActivator, CBaseEntity *pCaller, USE_TYPE useType, float value ); + void KeyValue( KeyValueData *pkvd ); + + virtual int Save( CSave &save ); + virtual int Restore( CRestore &restore ); + static TYPEDESCRIPTION m_SaveData[]; + + inline BOOL MessageToAll( void ) { return (pev->spawnflags & SF_ENVTEXT_ALLPLAYERS); } + inline void MessageSet( const char *pMessage ) { pev->message = ALLOC_STRING(pMessage); } + inline const char *MessageGet( void ) { return STRING(pev->message); } + +private: + + hudtextparms_t m_textParms; +}; + +LINK_ENTITY_TO_CLASS( game_text, CGameText ); + +// Save parms as a block. Will break save/restore if the structure changes, but this entity didn't ship with Half-Life, so +// it can't impact saved Half-Life games. +TYPEDESCRIPTION CGameText::m_SaveData[] = +{ + DEFINE_ARRAY( CGameText, m_textParms, FIELD_CHARACTER, sizeof(hudtextparms_t) ), +}; + +IMPLEMENT_SAVERESTORE( CGameText, CRulePointEntity ); + + +void CGameText::KeyValue( KeyValueData *pkvd ) +{ + if (FStrEq(pkvd->szKeyName, "channel")) + { + m_textParms.channel = atoi( pkvd->szValue ); + pkvd->fHandled = TRUE; + } + else if (FStrEq(pkvd->szKeyName, "x")) + { + m_textParms.x = atof( pkvd->szValue ); + pkvd->fHandled = TRUE; + } + else if (FStrEq(pkvd->szKeyName, "y")) + { + m_textParms.y = atof( pkvd->szValue ); + pkvd->fHandled = TRUE; + } + else if (FStrEq(pkvd->szKeyName, "effect")) + { + m_textParms.effect = atoi( pkvd->szValue ); + pkvd->fHandled = TRUE; + } + else if (FStrEq(pkvd->szKeyName, "color")) + { + int color[4]; + UTIL_StringToIntArray( color, 4, pkvd->szValue ); + m_textParms.r1 = color[0]; + m_textParms.g1 = color[1]; + m_textParms.b1 = color[2]; + m_textParms.a1 = color[3]; + pkvd->fHandled = TRUE; + } + else if (FStrEq(pkvd->szKeyName, "color2")) + { + int color[4]; + UTIL_StringToIntArray( color, 4, pkvd->szValue ); + m_textParms.r2 = color[0]; + m_textParms.g2 = color[1]; + m_textParms.b2 = color[2]; + m_textParms.a2 = color[3]; + pkvd->fHandled = TRUE; + } + else if (FStrEq(pkvd->szKeyName, "fadein")) + { + m_textParms.fadeinTime = atof( pkvd->szValue ); + pkvd->fHandled = TRUE; + } + else if (FStrEq(pkvd->szKeyName, "fadeout")) + { + m_textParms.fadeoutTime = atof( pkvd->szValue ); + pkvd->fHandled = TRUE; + } + else if (FStrEq(pkvd->szKeyName, "holdtime")) + { + m_textParms.holdTime = atof( pkvd->szValue ); + pkvd->fHandled = TRUE; + } + else if (FStrEq(pkvd->szKeyName, "fxtime")) + { + m_textParms.fxTime = atof( pkvd->szValue ); + pkvd->fHandled = TRUE; + } + else + CRulePointEntity::KeyValue( pkvd ); +} + + +void CGameText::Use( CBaseEntity *pActivator, CBaseEntity *pCaller, USE_TYPE useType, float value ) +{ + if ( !CanFireForActivator( pActivator ) ) + return; + + if ( MessageToAll() ) + { + UTIL_HudMessageAll( m_textParms, MessageGet() ); + } + else + { + if ( pActivator->IsNetClient() ) + { + UTIL_HudMessage( pActivator, m_textParms, MessageGet() ); + } + } +} + + +// +// CGameTeamMaster / game_team_master -- "Masters" like multisource, but based on the team of the activator +// Only allows mastered entity to fire if the team matches my team +// +// team index (pulled from server team list "mp_teamlist" +// Flag: Remove on Fire +// Flag: Any team until set? -- Any team can use this until the team is set (otherwise no teams can use it) +// + +#define SF_TEAMMASTER_FIREONCE 0x0001 +#define SF_TEAMMASTER_ANYTEAM 0x0002 + +class CGameTeamMaster : public CRulePointEntity +{ +public: + void KeyValue( KeyValueData *pkvd ); + void Use( CBaseEntity *pActivator, CBaseEntity *pCaller, USE_TYPE useType, float value ); + int ObjectCaps( void ) { return CRulePointEntity:: ObjectCaps() | FCAP_MASTER; } + + BOOL IsTriggered( CBaseEntity *pActivator ); + const char *TeamID( void ); + inline BOOL RemoveOnFire( void ) { return (pev->spawnflags & SF_TEAMMASTER_FIREONCE) ? TRUE : FALSE; } + inline BOOL AnyTeam( void ) { return (pev->spawnflags & SF_TEAMMASTER_ANYTEAM) ? TRUE : FALSE; } + +private: + BOOL TeamMatch( CBaseEntity *pActivator ); + + int m_teamIndex; + USE_TYPE triggerType; +}; + +LINK_ENTITY_TO_CLASS( game_team_master, CGameTeamMaster ); + +void CGameTeamMaster::KeyValue( KeyValueData *pkvd ) +{ + if (FStrEq(pkvd->szKeyName, "teamindex")) + { + m_teamIndex = atoi( pkvd->szValue ); + pkvd->fHandled = TRUE; + } + else if (FStrEq(pkvd->szKeyName, "triggerstate")) + { + int type = atoi( pkvd->szValue ); + switch( type ) + { + case 0: + triggerType = USE_OFF; + break; + case 2: + triggerType = USE_TOGGLE; + break; + default: + triggerType = USE_ON; + break; + } + pkvd->fHandled = TRUE; + } + else + CRulePointEntity::KeyValue( pkvd ); +} + + +void CGameTeamMaster::Use( CBaseEntity *pActivator, CBaseEntity *pCaller, USE_TYPE useType, float value ) +{ + if ( !CanFireForActivator( pActivator ) ) + return; + + if ( useType == USE_SET ) + { + if ( value < 0 ) + { + m_teamIndex = -1; + } + else + { + m_teamIndex = g_pGameRules->GetTeamIndex( pActivator->TeamID() ); + } + return; + } + + if ( TeamMatch( pActivator ) ) + { + SUB_UseTargets( pActivator, triggerType, value ); + if ( RemoveOnFire() ) + UTIL_Remove( this ); + } +} + + +BOOL CGameTeamMaster::IsTriggered( CBaseEntity *pActivator ) +{ + return TeamMatch( pActivator ); +} + + +const char *CGameTeamMaster::TeamID( void ) +{ + if ( m_teamIndex < 0 ) // Currently set to "no team" + return ""; + + return g_pGameRules->GetIndexedTeamName( m_teamIndex ); // UNDONE: Fill this in with the team from the "teamlist" +} + + +BOOL CGameTeamMaster::TeamMatch( CBaseEntity *pActivator ) +{ + if ( m_teamIndex < 0 && AnyTeam() ) + return TRUE; + + if ( !pActivator ) + return FALSE; + + return UTIL_TeamsMatch( pActivator->TeamID(), TeamID() ); +} + + +// +// CGameTeamSet / game_team_set -- Changes the team of the entity it targets to the activator's team +// Flag: Fire once +// Flag: Clear team -- Sets the team to "NONE" instead of activator + +#define SF_TEAMSET_FIREONCE 0x0001 +#define SF_TEAMSET_CLEARTEAM 0x0002 + +class CGameTeamSet : public CRulePointEntity +{ +public: + void Use( CBaseEntity *pActivator, CBaseEntity *pCaller, USE_TYPE useType, float value ); + inline BOOL RemoveOnFire( void ) { return (pev->spawnflags & SF_TEAMSET_FIREONCE) ? TRUE : FALSE; } + inline BOOL ShouldClearTeam( void ) { return (pev->spawnflags & SF_TEAMSET_CLEARTEAM) ? TRUE : FALSE; } + +private: +}; + +LINK_ENTITY_TO_CLASS( game_team_set, CGameTeamSet ); + + +void CGameTeamSet::Use( CBaseEntity *pActivator, CBaseEntity *pCaller, USE_TYPE useType, float value ) +{ + if ( !CanFireForActivator( pActivator ) ) + return; + + if ( ShouldClearTeam() ) + { + SUB_UseTargets( pActivator, USE_SET, -1 ); + } + else + { + SUB_UseTargets( pActivator, USE_SET, 0 ); + } + + if ( RemoveOnFire() ) + { + UTIL_Remove( this ); + } +} + + +// +// CGamePlayerZone / game_player_zone -- players in the zone fire my target when I'm fired +// +// Needs master? +class CGamePlayerZone : public CRuleBrushEntity +{ +public: + void KeyValue( KeyValueData *pkvd ); + void Use( CBaseEntity *pActivator, CBaseEntity *pCaller, USE_TYPE useType, float value ); + + virtual int Save( CSave &save ); + virtual int Restore( CRestore &restore ); + static TYPEDESCRIPTION m_SaveData[]; + +private: + string_t m_iszInTarget; + string_t m_iszOutTarget; + string_t m_iszInCount; + string_t m_iszOutCount; +}; + +LINK_ENTITY_TO_CLASS( game_zone_player, CGamePlayerZone ); +TYPEDESCRIPTION CGamePlayerZone::m_SaveData[] = +{ + DEFINE_FIELD( CGamePlayerZone, m_iszInTarget, FIELD_STRING ), + DEFINE_FIELD( CGamePlayerZone, m_iszOutTarget, FIELD_STRING ), + DEFINE_FIELD( CGamePlayerZone, m_iszInCount, FIELD_STRING ), + DEFINE_FIELD( CGamePlayerZone, m_iszOutCount, FIELD_STRING ), +}; + +IMPLEMENT_SAVERESTORE( CGamePlayerZone, CRuleBrushEntity ); + +void CGamePlayerZone::KeyValue( KeyValueData *pkvd ) +{ + if (FStrEq(pkvd->szKeyName, "intarget")) + { + m_iszInTarget = ALLOC_STRING( pkvd->szValue ); + pkvd->fHandled = TRUE; + } + else if (FStrEq(pkvd->szKeyName, "outtarget")) + { + m_iszOutTarget = ALLOC_STRING( pkvd->szValue ); + pkvd->fHandled = TRUE; + } + else if (FStrEq(pkvd->szKeyName, "incount")) + { + m_iszInCount = ALLOC_STRING( pkvd->szValue ); + pkvd->fHandled = TRUE; + } + else if (FStrEq(pkvd->szKeyName, "outcount")) + { + m_iszOutCount = ALLOC_STRING( pkvd->szValue ); + pkvd->fHandled = TRUE; + } + else + CRuleBrushEntity::KeyValue( pkvd ); +} + +void CGamePlayerZone::Use( CBaseEntity *pActivator, CBaseEntity *pCaller, USE_TYPE useType, float value ) +{ + int playersInCount = 0; + int playersOutCount = 0; + + if ( !CanFireForActivator( pActivator ) ) + return; + + CBaseEntity *pPlayer = NULL; + + for ( int i = 1; i <= gpGlobals->maxClients; i++ ) + { + pPlayer = UTIL_PlayerByIndex( i ); + if ( pPlayer ) + { + TraceResult trace; + int hullNumber; + + hullNumber = human_hull; + if ( pPlayer->pev->flags & FL_DUCKING ) + { + hullNumber = head_hull; + } + + UTIL_TraceModel( pPlayer->pev->origin, pPlayer->pev->origin, hullNumber, edict(), &trace ); + + if ( trace.fStartSolid ) + { + playersInCount++; + if ( m_iszInTarget ) + { + FireTargets( STRING(m_iszInTarget), pPlayer, pActivator, useType, value ); + } + } + else + { + playersOutCount++; + if ( m_iszOutTarget ) + { + FireTargets( STRING(m_iszOutTarget), pPlayer, pActivator, useType, value ); + } + } + } + } + + if ( m_iszInCount ) + { + FireTargets( STRING(m_iszInCount), pActivator, this, USE_SET, playersInCount ); + } + + if ( m_iszOutCount ) + { + FireTargets( STRING(m_iszOutCount), pActivator, this, USE_SET, playersOutCount ); + } +} + + + +// +// CGamePlayerHurt / game_player_hurt -- Damages the player who fires it +// Flag: Fire once + +#define SF_PKILL_FIREONCE 0x0001 +class CGamePlayerHurt : public CRulePointEntity +{ +public: + void Use( CBaseEntity *pActivator, CBaseEntity *pCaller, USE_TYPE useType, float value ); + inline BOOL RemoveOnFire( void ) { return (pev->spawnflags & SF_PKILL_FIREONCE) ? TRUE : FALSE; } + +private: +}; + +LINK_ENTITY_TO_CLASS( game_player_hurt, CGamePlayerHurt ); + + +void CGamePlayerHurt::Use( CBaseEntity *pActivator, CBaseEntity *pCaller, USE_TYPE useType, float value ) +{ + if ( !CanFireForActivator( pActivator ) ) + return; + + if ( pActivator->IsPlayer() ) + { + if ( pev->dmg < 0 ) + pActivator->TakeHealth( -pev->dmg, DMG_GENERIC ); + else + pActivator->TakeDamage( pev, pev, pev->dmg, DMG_GENERIC ); + } + + SUB_UseTargets( pActivator, useType, value ); + + if ( RemoveOnFire() ) + { + UTIL_Remove( this ); + } +} + + + +// +// CGameCounter / game_counter -- Counts events and fires target +// Flag: Fire once +// Flag: Reset on Fire + +#define SF_GAMECOUNT_FIREONCE 0x0001 +#define SF_GAMECOUNT_RESET 0x0002 + +class CGameCounter : public CRulePointEntity +{ +public: + void Spawn( void ); + void Use( CBaseEntity *pActivator, CBaseEntity *pCaller, USE_TYPE useType, float value ); + inline BOOL RemoveOnFire( void ) { return (pev->spawnflags & SF_GAMECOUNT_FIREONCE) ? TRUE : FALSE; } + inline BOOL ResetOnFire( void ) { return (pev->spawnflags & SF_GAMECOUNT_RESET) ? TRUE : FALSE; } + + inline void CountUp( void ) { pev->frags++; } + inline void CountDown( void ) { pev->frags--; } + inline void ResetCount( void ) { pev->frags = pev->dmg; } + inline int CountValue( void ) { return pev->frags; } + inline int LimitValue( void ) { return pev->health; } + + inline BOOL HitLimit( void ) { return CountValue() == LimitValue(); } + +private: + + inline void SetCountValue( int value ) { pev->frags = value; } + inline void SetInitialValue( int value ) { pev->dmg = value; } +}; + +LINK_ENTITY_TO_CLASS( game_counter, CGameCounter ); + +void CGameCounter::Spawn( void ) +{ + // Save off the initial count + SetInitialValue( CountValue() ); + CRulePointEntity::Spawn(); +} + + +void CGameCounter::Use( CBaseEntity *pActivator, CBaseEntity *pCaller, USE_TYPE useType, float value ) +{ + if ( !CanFireForActivator( pActivator ) ) + return; + + switch( useType ) + { + case USE_ON: + case USE_TOGGLE: + CountUp(); + break; + + case USE_OFF: + CountDown(); + break; + + case USE_SET: + SetCountValue( (int)value ); + break; + } + + if ( HitLimit() ) + { + SUB_UseTargets( pActivator, USE_TOGGLE, 0 ); + if ( RemoveOnFire() ) + { + UTIL_Remove( this ); + } + + if ( ResetOnFire() ) + { + ResetCount(); + } + } +} + + + +// +// CGameCounterSet / game_counter_set -- Sets the counter's value +// Flag: Fire once + +#define SF_GAMECOUNTSET_FIREONCE 0x0001 + +class CGameCounterSet : public CRulePointEntity +{ +public: + void Use( CBaseEntity *pActivator, CBaseEntity *pCaller, USE_TYPE useType, float value ); + inline BOOL RemoveOnFire( void ) { return (pev->spawnflags & SF_GAMECOUNTSET_FIREONCE) ? TRUE : FALSE; } + +private: +}; + +LINK_ENTITY_TO_CLASS( game_counter_set, CGameCounterSet ); + + +void CGameCounterSet::Use( CBaseEntity *pActivator, CBaseEntity *pCaller, USE_TYPE useType, float value ) +{ + if ( !CanFireForActivator( pActivator ) ) + return; + + SUB_UseTargets( pActivator, USE_SET, pev->frags ); + + if ( RemoveOnFire() ) + { + UTIL_Remove( this ); + } +} + + +// +// CGamePlayerEquip / game_playerequip -- Sets the default player equipment +// Flag: USE Only + +#define SF_PLAYEREQUIP_USEONLY 0x0001 +#define MAX_EQUIP 32 + +class CGamePlayerEquip : public CRulePointEntity +{ +public: + void KeyValue( KeyValueData *pkvd ); + void Touch( CBaseEntity *pOther ); + void Use( CBaseEntity *pActivator, CBaseEntity *pCaller, USE_TYPE useType, float value ); + + inline BOOL UseOnly( void ) { return (pev->spawnflags & SF_PLAYEREQUIP_USEONLY) ? TRUE : FALSE; } + +private: + + void EquipPlayer( CBaseEntity *pPlayer ); + + string_t m_weaponNames[MAX_EQUIP]; + int m_weaponCount[MAX_EQUIP]; +}; + +LINK_ENTITY_TO_CLASS( game_player_equip, CGamePlayerEquip ); + + +void CGamePlayerEquip::KeyValue( KeyValueData *pkvd ) +{ + CRulePointEntity::KeyValue( pkvd ); + + if ( !pkvd->fHandled ) + { + for ( int i = 0; i < MAX_EQUIP; i++ ) + { + if ( !m_weaponNames[i] ) + { + char tmp[128]; + + UTIL_StripToken( pkvd->szKeyName, tmp ); + + m_weaponNames[i] = ALLOC_STRING(tmp); + m_weaponCount[i] = atoi(pkvd->szValue); + m_weaponCount[i] = max(1,m_weaponCount[i]); + pkvd->fHandled = TRUE; + break; + } + } + } +} + + +void CGamePlayerEquip::Touch( CBaseEntity *pOther ) +{ + if ( !CanFireForActivator( pOther ) ) + return; + + if ( UseOnly() ) + return; + + EquipPlayer( pOther ); +} + +void CGamePlayerEquip::EquipPlayer( CBaseEntity *pEntity ) +{ + CBasePlayer *pPlayer = NULL; + + if ( pEntity->IsPlayer() ) + { + pPlayer = (CBasePlayer *)pEntity; + } + + if ( !pPlayer ) + return; + + for ( int i = 0; i < MAX_EQUIP; i++ ) + { + if ( !m_weaponNames[i] ) + break; + for ( int j = 0; j < m_weaponCount[i]; j++ ) + { + pPlayer->GiveNamedItem( STRING(m_weaponNames[i]) ); + } + } +} + + +void CGamePlayerEquip::Use( CBaseEntity *pActivator, CBaseEntity *pCaller, USE_TYPE useType, float value ) +{ + EquipPlayer( pActivator ); +} + + +// +// CGamePlayerTeam / game_player_team -- Changes the team of the player who fired it +// Flag: Fire once +// Flag: Kill Player +// Flag: Gib Player + +#define SF_PTEAM_FIREONCE 0x0001 +#define SF_PTEAM_KILL 0x0002 +#define SF_PTEAM_GIB 0x0004 + +class CGamePlayerTeam : public CRulePointEntity +{ +public: + void Use( CBaseEntity *pActivator, CBaseEntity *pCaller, USE_TYPE useType, float value ); + +private: + + inline BOOL RemoveOnFire( void ) { return (pev->spawnflags & SF_PTEAM_FIREONCE) ? TRUE : FALSE; } + inline BOOL ShouldKillPlayer( void ) { return (pev->spawnflags & SF_PTEAM_KILL) ? TRUE : FALSE; } + inline BOOL ShouldGibPlayer( void ) { return (pev->spawnflags & SF_PTEAM_GIB) ? TRUE : FALSE; } + + const char *TargetTeamName( const char *pszTargetName ); +}; + +LINK_ENTITY_TO_CLASS( game_player_team, CGamePlayerTeam ); + + +const char *CGamePlayerTeam::TargetTeamName( const char *pszTargetName ) +{ + CBaseEntity *pTeamEntity = NULL; + + while ((pTeamEntity = UTIL_FindEntityByTargetname( pTeamEntity, pszTargetName )) != NULL) + { + if ( FClassnameIs( pTeamEntity->pev, "game_team_master" ) ) + return pTeamEntity->TeamID(); + } + + return NULL; +} + + +void CGamePlayerTeam::Use( CBaseEntity *pActivator, CBaseEntity *pCaller, USE_TYPE useType, float value ) +{ + if ( !CanFireForActivator( pActivator ) ) + return; + + if ( pActivator->IsPlayer() ) + { + const char *pszTargetTeam = TargetTeamName( STRING(pev->target) ); + if ( pszTargetTeam ) + { + CBasePlayer *pPlayer = (CBasePlayer *)pActivator; + g_pGameRules->ChangePlayerTeam( pPlayer, pszTargetTeam, ShouldKillPlayer(), ShouldGibPlayer() ); + } + } + + if ( RemoveOnFire() ) + { + UTIL_Remove( this ); + } +} + + diff --git a/spirit/maprules.h b/dlls/maprules.h similarity index 96% rename from spirit/maprules.h rename to dlls/maprules.h index 57f9939b..975dafa1 100644 --- a/spirit/maprules.h +++ b/dlls/maprules.h @@ -1,22 +1,22 @@ -/*** -* -* Copyright (c) 1996-2002, 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. -* -****/ - -#ifndef MAPRULES_H -#define MAPRULES_H - - - -#endif // MAPRULES_H - +/*** +* +* Copyright (c) 1996-2002, 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. +* +****/ + +#ifndef MAPRULES_H +#define MAPRULES_H + + + +#endif // MAPRULES_H + diff --git a/spirit/monsterevent.h b/dlls/monsterevent.h similarity index 96% rename from spirit/monsterevent.h rename to dlls/monsterevent.h index 46c5624a..58357e18 100644 --- a/spirit/monsterevent.h +++ b/dlls/monsterevent.h @@ -1,34 +1,34 @@ -/*** -* -* Copyright (c) 1996-2002, 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. -* -****/ -#ifndef MONSTEREVENT_H -#define MONSTEREVENT_H - -typedef struct -{ - int event; - char *options; -} MonsterEvent_t; - -#define EVENT_SPECIFIC 0 -#define EVENT_SCRIPTED 1000 -#define EVENT_SHARED 2000 -#define EVENT_CLIENT 5000 - -#define MONSTER_EVENT_BODYDROP_LIGHT 2001 -#define MONSTER_EVENT_BODYDROP_HEAVY 2002 - -#define MONSTER_EVENT_SWISHSOUND 2010 - -#endif // MONSTEREVENT_H +/*** +* +* Copyright (c) 1996-2002, 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. +* +****/ +#ifndef MONSTEREVENT_H +#define MONSTEREVENT_H + +typedef struct +{ + int event; + char *options; +} MonsterEvent_t; + +#define EVENT_SPECIFIC 0 +#define EVENT_SCRIPTED 1000 +#define EVENT_SHARED 2000 +#define EVENT_CLIENT 5000 + +#define MONSTER_EVENT_BODYDROP_LIGHT 2001 +#define MONSTER_EVENT_BODYDROP_HEAVY 2002 + +#define MONSTER_EVENT_SWISHSOUND 2010 + +#endif // MONSTEREVENT_H diff --git a/spirit/monstermaker.cpp b/dlls/monstermaker.cpp similarity index 66% rename from spirit/monstermaker.cpp rename to dlls/monstermaker.cpp index 5e07393c..1b4b62a3 100644 --- a/spirit/monstermaker.cpp +++ b/dlls/monstermaker.cpp @@ -27,8 +27,6 @@ #define SF_MONSTERMAKER_START_ON 1 // start active ( if has targetname ) #define SF_MONSTERMAKER_CYCLIC 4 // drop one monster every time fired. #define SF_MONSTERMAKER_MONSTERCLIP 8 // Children are blocked by monsterclip -#define SF_MONSTERMAKER_LEAVECORPSE 16 // Don't fade corpses. -#define SF_MONSTERMAKER_NO_WPN_DROP 1024 // Corpses don't drop weapons. //========================================================= // MonsterMaker - this ent creates monsters during the game. @@ -42,10 +40,8 @@ public: void EXPORT ToggleUse ( CBaseEntity *pActivator, CBaseEntity *pCaller, USE_TYPE useType, float value ); void EXPORT CyclicUse ( CBaseEntity *pActivator, CBaseEntity *pCaller, USE_TYPE useType, float value ); void EXPORT MakerThink ( void ); - void EXPORT MakeMonsterThink( void ); void DeathNotice ( entvars_t *pevChild );// monster maker children use this to tell the monster maker that they have died. - void TryMakeMonster( void ); //LRC - to allow for a spawndelay - CBaseMonster* MakeMonster( void ); //LRC - actually make a monster (and return the new creation) + void MakeMonster( void ); virtual int Save( CSave &save ); virtual int Restore( CRestore &restore ); @@ -64,7 +60,6 @@ public: BOOL m_fActive; BOOL m_fFadeChildren;// should we make the children fadeout? - float m_fSpawnDelay;// LRC- delay between triggering targets and making a child (for env_warpball, mainly) }; LINK_ENTITY_TO_CLASS( monstermaker, CMonsterMaker ); @@ -78,7 +73,6 @@ TYPEDESCRIPTION CMonsterMaker::m_SaveData[] = DEFINE_FIELD( CMonsterMaker, m_iMaxLiveChildren, FIELD_INTEGER ), DEFINE_FIELD( CMonsterMaker, m_fActive, FIELD_BOOLEAN ), DEFINE_FIELD( CMonsterMaker, m_fFadeChildren, FIELD_BOOLEAN ), - DEFINE_FIELD( CMonsterMaker, m_fSpawnDelay, FIELD_FLOAT ), }; @@ -102,11 +96,6 @@ void CMonsterMaker :: KeyValue( KeyValueData *pkvd ) m_iszMonsterClassname = ALLOC_STRING( pkvd->szValue ); pkvd->fHandled = TRUE; } - else if ( FStrEq(pkvd->szKeyName, "spawndelay") ) - { - m_fSpawnDelay = atof( pkvd->szValue ); - pkvd->fHandled = TRUE; - } else CBaseMonster::KeyValue( pkvd ); } @@ -122,33 +111,32 @@ void CMonsterMaker :: Spawn( ) { if ( pev->spawnflags & SF_MONSTERMAKER_CYCLIC ) { - SetUse(&CMonsterMaker :: CyclicUse );// drop one monster each time we fire - m_fActive = FALSE; + SetUse ( CyclicUse );// drop one monster each time we fire } else { - SetUse(&CMonsterMaker :: ToggleUse );// can be turned on/off + SetUse ( ToggleUse );// so can be turned on/off + } - if ( FBitSet ( pev->spawnflags, SF_MONSTERMAKER_START_ON ) ) - {// start making monsters as soon as monstermaker spawns - m_fActive = TRUE; - SetThink(&CMonsterMaker :: MakerThink ); - } - else - {// wait to be activated. - m_fActive = FALSE; - SetThink(&CMonsterMaker :: SUB_DoNothing ); - } + if ( FBitSet ( pev->spawnflags, SF_MONSTERMAKER_START_ON ) ) + {// start making monsters as soon as monstermaker spawns + m_fActive = TRUE; + SetThink ( MakerThink ); + } + else + {// wait to be activated. + m_fActive = FALSE; + SetThink ( SUB_DoNothing ); } } else {// no targetname, just start. - SetNextThink( m_flDelay ); + pev->nextthink = gpGlobals->time + m_flDelay; m_fActive = TRUE; - SetThink(&CMonsterMaker :: MakerThink ); + SetThink ( MakerThink ); } - if ( m_cNumMonsters == 1 || (m_cNumMonsters != -1 && pev->spawnflags & SF_MONSTERMAKER_LEAVECORPSE )) + if ( m_cNumMonsters == 1 ) { m_fFadeChildren = FALSE; } @@ -168,10 +156,13 @@ void CMonsterMaker :: Precache( void ) } //========================================================= -// TryMakeMonster- check that it's ok to drop a monster. +// MakeMonster- this is the code that drops the monster //========================================================= -void CMonsterMaker::TryMakeMonster( void ) +void CMonsterMaker::MakeMonster( void ) { + edict_t *pent; + entvars_t *pevCreate; + if ( m_iMaxLiveChildren > 0 && m_cLiveChildren >= m_iMaxLiveChildren ) {// not allowed to make a new one yet. Too many live ones out right now. return; @@ -199,56 +190,19 @@ void CMonsterMaker::TryMakeMonster( void ) return; } - if (m_fSpawnDelay) - { - // If I have a target, fire. (no locus) - if ( !FStringNull ( pev->target ) ) - { - // delay already overloaded for this entity, so can't call SUB_UseTargets() - FireTargets( STRING(pev->target), this, this, USE_TOGGLE, 0 ); - } - -// ALERT(at_console,"Making Monster in %f seconds\n",m_fSpawnDelay); - SetThink(&CMonsterMaker:: MakeMonsterThink ); - SetNextThink( m_fSpawnDelay ); - } - else - { -// ALERT(at_console,"No delay. Making monster.\n",m_fSpawnDelay); - CBaseMonster* pMonst = MakeMonster(); - - // If I have a target, fire! (the new monster is the locus) - if ( !FStringNull ( pev->target ) ) - { - FireTargets( STRING(pev->target), pMonst, this, USE_TOGGLE, 0 ); - } - } -} - -//========================================================= -// MakeMonsterThink- a really trivial think function -//========================================================= -void CMonsterMaker::MakeMonsterThink( void ) -{ - MakeMonster(); -} - -//========================================================= -// MakeMonster- this is the code that drops the monster -//========================================================= -CBaseMonster* CMonsterMaker::MakeMonster( void ) -{ - edict_t *pent; - entvars_t *pevCreate; - -// ALERT(at_console,"Making Monster NOW\n"); - pent = CREATE_NAMED_ENTITY( m_iszMonsterClassname ); if ( FNullEnt( pent ) ) { - ALERT ( at_debug, "NULL Ent in MonsterMaker!\n" ); - return NULL; + ALERT ( at_console, "NULL Ent in MonsterMaker!\n" ); + return; + } + + // If I have a target, fire! + if ( !FStringNull ( pev->target ) ) + { + // delay already overloaded for this entity, so can't call SUB_UseTargets() + FireTargets( STRING(pev->target), this, this, USE_TOGGLE, 0 ); } pevCreate = VARS( pent ); @@ -256,9 +210,6 @@ CBaseMonster* CMonsterMaker::MakeMonster( void ) pevCreate->angles = pev->angles; SetBits( pevCreate->spawnflags, SF_MONSTER_FALL_TO_GROUND ); - if (pev->spawnflags & SF_MONSTERMAKER_NO_WPN_DROP) - SetBits( pevCreate->spawnflags, SF_MONSTER_NO_WPN_DROP); - // Children hit monsterclip brushes if ( pev->spawnflags & SF_MONSTERMAKER_MONSTERCLIP ) SetBits( pevCreate->spawnflags, SF_MONSTER_HITMONSTERCLIP ); @@ -266,15 +217,6 @@ CBaseMonster* CMonsterMaker::MakeMonster( void ) DispatchSpawn( ENT( pevCreate ) ); pevCreate->owner = edict(); - //LRC - custom monster behaviour - CBaseEntity *pEntity = CBaseEntity::Instance( pevCreate ); - CBaseMonster *pMonst = NULL; - if (pEntity && (pMonst = pEntity->MyMonsterPointer()) != NULL) - { - pMonst->m_iClass = this->m_iClass; - pMonst->m_iPlayerReact = this->m_iPlayerReact; - } - if ( !FStringNull( pev->netname ) ) { // if I have a netname (overloaded), give the child monster that name as a targetname @@ -290,13 +232,6 @@ CBaseMonster* CMonsterMaker::MakeMonster( void ) SetThink( NULL ); SetUse( NULL ); } - else if (m_fActive) - { - SetNextThink( m_flDelay ); - SetThink(&CMonsterMaker:: MakerThink ); - } - - return pMonst; } //========================================================= @@ -305,8 +240,7 @@ CBaseMonster* CMonsterMaker::MakeMonster( void ) //========================================================= void CMonsterMaker::CyclicUse ( CBaseEntity *pActivator, CBaseEntity *pCaller, USE_TYPE useType, float value ) { - TryMakeMonster(); -// ALERT(at_console,"CyclicUse complete\n"); + MakeMonster(); } //========================================================= @@ -325,10 +259,10 @@ void CMonsterMaker :: ToggleUse ( CBaseEntity *pActivator, CBaseEntity *pCaller, else { m_fActive = TRUE; - SetThink(&CMonsterMaker :: MakerThink ); + SetThink ( MakerThink ); } - SetNextThink( 0 ); + pev->nextthink = gpGlobals->time; } //========================================================= @@ -336,9 +270,9 @@ void CMonsterMaker :: ToggleUse ( CBaseEntity *pActivator, CBaseEntity *pCaller, //========================================================= void CMonsterMaker :: MakerThink ( void ) { - SetNextThink( m_flDelay ); + pev->nextthink = gpGlobals->time + m_flDelay; - TryMakeMonster(); + MakeMonster(); } diff --git a/spirit/monsters.cpp b/dlls/monsters.cpp similarity index 88% rename from spirit/monsters.cpp rename to dlls/monsters.cpp index 2816f7cb..e9e94432 100644 --- a/spirit/monsters.cpp +++ b/dlls/monsters.cpp @@ -1,6 +1,6 @@ /*** * -* Copyright (c) 1999, 2000 Valve LLC. All rights reserved. +* Copyright (c) 1996-2002, Valve LLC. All rights reserved. * * This product contains software technology licensed from Id * Software, Inc. ("Id Technology"). Id Technology (c) 1996 Id Software, Inc. @@ -58,10 +58,6 @@ TYPEDESCRIPTION CBaseMonster::m_SaveData[] = DEFINE_FIELD( CBaseMonster, m_hTargetEnt, FIELD_EHANDLE ), DEFINE_ARRAY( CBaseMonster, m_hOldEnemy, FIELD_EHANDLE, MAX_OLD_ENEMIES ), DEFINE_ARRAY( CBaseMonster, m_vecOldEnemy, FIELD_POSITION_VECTOR, MAX_OLD_ENEMIES ), - - DEFINE_FIELD( CBaseMonster, m_iClass, FIELD_INTEGER ), - DEFINE_FIELD( CBaseMonster, m_iPlayerReact, FIELD_INTEGER ), - DEFINE_FIELD( CBaseMonster, m_flFieldOfView, FIELD_FLOAT ), DEFINE_FIELD( CBaseMonster, m_flWaitFinished, FIELD_TIME ), DEFINE_FIELD( CBaseMonster, m_flMoveWaitFinished, FIELD_TIME ), @@ -119,10 +115,7 @@ int CBaseMonster::Save( CSave &save ) { if ( !CBaseToggle::Save(save) ) return 0; - if ( pev->targetname ) - return save.WriteFields( STRING(pev->targetname), "CBaseMonster", this, m_SaveData, ARRAYSIZE(m_SaveData) ); - else - return save.WriteFields( STRING(pev->classname), "CBaseMonster", this, m_SaveData, ARRAYSIZE(m_SaveData) ); + return save.WriteFields( "CBaseMonster", this, m_SaveData, ARRAYSIZE(m_SaveData) ); } int CBaseMonster::Restore( CRestore &restore ) @@ -333,7 +326,6 @@ void CBaseMonster :: Look ( int iDistance ) { pSightEnt = pList[i]; // !!!temporarily only considering other monsters and clients, don't see prisoners - if ( pSightEnt != this && !FBitSet( pSightEnt->pev->spawnflags, SF_MONSTER_PRISONER ) && pSightEnt->pev->health > 0 ) @@ -382,7 +374,7 @@ void CBaseMonster :: Look ( int iDistance ) case R_NM: iSighted |= bits_COND_SEE_NEMESIS; break; - case R_HT: + case R_HT: iSighted |= bits_COND_SEE_HATE; break; case R_DL: @@ -528,12 +520,12 @@ CSound* CBaseMonster :: PBestScent ( void ) //========================================================= void CBaseMonster :: MonsterThink ( void ) { - SetNextThink( 0.1 );// keep monster thinking. + pev->nextthink = gpGlobals->time + 0.1;// keep monster thinking. + RunAI(); float flInterval = StudioFrameAdvance( ); // animate - // start or end a fidget // This needs a better home -- switching animations over time should be encapsulated on a per-activity basis // perhaps MaintainActivity() or a ShiftAnimationOverTime() or something. @@ -629,7 +621,7 @@ void CBaseMonster :: RouteNew ( void ) } //========================================================= -// FRouteClear - returns TRUE if the Route is cleared out +// FRouteClear - returns TRUE is the Route is cleared out // ( invalid ) //========================================================= BOOL CBaseMonster :: FRouteClear ( void ) @@ -761,7 +753,7 @@ void DrawRoute( entvars_t *pev, WayPoint_t *m_Route, int m_iRouteIndex, int r, i // UTIL_ParticleEffect ( m_Route[ m_iRouteIndex ].vecLocation, g_vecZero, 255, 25 ); - MESSAGE_BEGIN( MSG_BROADCAST, gmsgTempEntity ); + MESSAGE_BEGIN( MSG_BROADCAST, SVC_TEMPENTITY ); WRITE_BYTE( TE_BEAMPOINTS); WRITE_COORD( pev->origin.x ); WRITE_COORD( pev->origin.y ); @@ -789,7 +781,7 @@ void DrawRoute( entvars_t *pev, WayPoint_t *m_Route, int m_iRouteIndex, int r, i break; - MESSAGE_BEGIN( MSG_BROADCAST, gmsgTempEntity ); + MESSAGE_BEGIN( MSG_BROADCAST, SVC_TEMPENTITY ); WRITE_BYTE( TE_BEAMPOINTS ); WRITE_COORD( m_Route[ i ].vecLocation.x ); WRITE_COORD( m_Route[ i ].vecLocation.y ); @@ -1321,7 +1313,7 @@ int CBaseMonster :: CheckLocalMove ( const Vector &vecStart, const Vector &vecEn iReturn = LOCALMOVE_VALID;// assume everything will be ok. // move the monster to the start of the local move that's to be checked. - UTIL_SetOrigin( this, vecStart );// !!!BUGBUG - won't this fire triggers? - nope, SetOrigin doesn't fire + UTIL_SetOrigin( pev, vecStart );// !!!BUGBUG - won't this fire triggers? - nope, SetOrigin doesn't fire if ( !(pev->flags & (FL_FLY|FL_SWIM)) ) { @@ -1356,7 +1348,7 @@ int CBaseMonster :: CheckLocalMove ( const Vector &vecStart, const Vector &vecEn if ( !WALK_MOVE( ENT(pev), flYaw, stepSize, WALKMOVE_CHECKONLY ) ) {// can't take the next step, fail! - + if ( pflDist != NULL ) { *pflDist = flStep; @@ -1391,7 +1383,7 @@ int CBaseMonster :: CheckLocalMove ( const Vector &vecStart, const Vector &vecEn } /* // uncommenting this block will draw a line representing the nearest legal move. - WRITE_BYTE(MSG_BROADCAST, gmsgTempEntity); + WRITE_BYTE(MSG_BROADCAST, SVC_TEMPENTITY); WRITE_BYTE(MSG_BROADCAST, TE_SHOWLINE); WRITE_COORD(MSG_BROADCAST, pev->origin.x); WRITE_COORD(MSG_BROADCAST, pev->origin.y); @@ -1402,7 +1394,7 @@ int CBaseMonster :: CheckLocalMove ( const Vector &vecStart, const Vector &vecEn */ // since we've actually moved the monster during the check, undo the move. - UTIL_SetOrigin( this, vecStartPos ); + UTIL_SetOrigin( pev, vecStartPos ); return iReturn; } @@ -1422,23 +1414,26 @@ float CBaseMonster :: OpenDoorAndWait( entvars_t *pevDoor ) //ALERT(at_aiconsole, "pevDoor->ltime = %d ms\n", (int)(1000*pevDoor->ltime)); //ALERT(at_aiconsole, "pev-> nextthink = %d ms\n", (int)(1000*pev->nextthink)); //ALERT(at_aiconsole, "pev->ltime = %d ms\n", (int)(1000*pev->ltime)); - - flTravelTime = pcbeDoor->m_fNextThink - pevDoor->ltime; - + flTravelTime = pevDoor->nextthink - pevDoor->ltime; //ALERT(at_aiconsole, "Waiting %d ms\n", (int)(1000*flTravelTime)); if ( pcbeDoor->pev->targetname ) { - CBaseEntity *pTarget = NULL; + edict_t *pentTarget = NULL; for (;;) { - pTarget = UTIL_FindEntityByTargetname( pTarget, STRING(pcbeDoor->pev->targetname)); - if (!pTarget) - break; + pentTarget = FIND_ENTITY_BY_TARGETNAME( pentTarget, STRING(pcbeDoor->pev->targetname)); - if ( VARS( pTarget->pev ) != pcbeDoor->pev && - FClassnameIs ( pTarget->pev, STRING(pcbeDoor->pev->classname) ) ) + if ( VARS( pentTarget ) != pcbeDoor->pev ) { - pTarget->Use(this, this, USE_ON, 0.0); + if (FNullEnt(pentTarget)) + break; + + if ( FClassnameIs ( pentTarget, STRING(pcbeDoor->pev->classname) ) ) + { + CBaseEntity *pDoor = Instance(pentTarget); + if ( pDoor ) + pDoor->Use(this, this, USE_ON, 0.0); + } } } } @@ -1569,7 +1564,7 @@ BOOL CBaseMonster :: BuildRoute ( const Vector &vecGoal, int iMoveFlag, CBaseEnt m_Route[ 1 ].iType = iMoveFlag | bits_MF_IS_GOAL; /* - WRITE_BYTE(MSG_BROADCAST, gmsgTempEntity); + WRITE_BYTE(MSG_BROADCAST, SVC_TEMPENTITY); WRITE_BYTE(MSG_BROADCAST, TE_SHOWLINE); WRITE_COORD(MSG_BROADCAST, vecApex.x ); WRITE_COORD(MSG_BROADCAST, vecApex.y ); @@ -1680,7 +1675,7 @@ BOOL CBaseMonster :: FTriangulate ( const Vector &vecStart , const Vector &vecEn { // Debug, Draw the triangulation #if 0 - MESSAGE_BEGIN( MSG_BROADCAST, gmsgTempEntity ); + MESSAGE_BEGIN( MSG_BROADCAST, SVC_TEMPENTITY ); WRITE_BYTE( TE_SHOWLINE); WRITE_COORD( pev->origin.x ); WRITE_COORD( pev->origin.y ); @@ -1690,7 +1685,7 @@ BOOL CBaseMonster :: FTriangulate ( const Vector &vecStart , const Vector &vecEn WRITE_COORD( vecRight.z ); MESSAGE_END(); - MESSAGE_BEGIN( MSG_BROADCAST, gmsgTempEntity ); + MESSAGE_BEGIN( MSG_BROADCAST, SVC_TEMPENTITY ); WRITE_BYTE( TE_SHOWLINE ); WRITE_COORD( pev->origin.x ); WRITE_COORD( pev->origin.y ); @@ -1704,7 +1699,7 @@ BOOL CBaseMonster :: FTriangulate ( const Vector &vecStart , const Vector &vecEn #if 0 if (pev->movetype == MOVETYPE_FLY) { - MESSAGE_BEGIN( MSG_BROADCAST, gmsgTempEntity ); + MESSAGE_BEGIN( MSG_BROADCAST, SVC_TEMPENTITY ); WRITE_BYTE( TE_SHOWLINE ); WRITE_COORD( pev->origin.x ); WRITE_COORD( pev->origin.y ); @@ -1714,7 +1709,7 @@ BOOL CBaseMonster :: FTriangulate ( const Vector &vecStart , const Vector &vecEn WRITE_COORD( vecTop.z ); MESSAGE_END(); - MESSAGE_BEGIN( MSG_BROADCAST, gmsgTempEntity ); + MESSAGE_BEGIN( MSG_BROADCAST, SVC_TEMPENTITY ); WRITE_BYTE( TE_SHOWLINE ); WRITE_COORD( pev->origin.x ); WRITE_COORD( pev->origin.y ); @@ -2049,9 +2044,9 @@ void CBaseMonster :: MonsterInit ( void ) // set eye position SetEyePosition(); - SetThink(&CBaseMonster :: MonsterInitThink ); - SetNextThink( 0.1 ); - SetUse(&CBaseMonster :: MonsterUse ); + SetThink( MonsterInitThink ); + pev->nextthink = gpGlobals->time + 0.1; + SetUse ( MonsterUse ); } //========================================================= @@ -2063,39 +2058,6 @@ void CBaseMonster :: MonsterInitThink ( void ) StartMonster(); } - -void CBaseMonster :: StartPatrol ( CBaseEntity* path ) -{ - m_pGoalEnt = path; - - if ( !m_pGoalEnt ) - { - ALERT(at_error, "ReadyMonster()--%s couldn't find target \"%s\"\n", STRING(pev->classname), STRING(pev->target)); - } - else - { - // Monster will start turning towards his destination -// MakeIdealYaw ( m_pGoalEnt->pev->origin ); - - // set the monster up to walk a path corner path. - // !!!BUGBUG - this is a minor bit of a hack. - // JAYJAY - m_movementGoal = MOVEGOAL_PATHCORNER; - - if ( pev->movetype == MOVETYPE_FLY ) - m_movementActivity = ACT_FLY; - else - m_movementActivity = ACT_WALK; - - if ( !FRefreshRoute() ) - { - ALERT ( at_aiconsole, "Can't Create Route!\n" ); - } - SetState( MONSTERSTATE_IDLE ); - ChangeSchedule( GetScheduleOfType( SCHED_IDLE_WALK ) ); - } -} - //========================================================= // StartMonster - final bit of initization before a monster // is turned over to the AI. @@ -2126,10 +2088,9 @@ void CBaseMonster :: StartMonster ( void ) pev->origin.z += 1; DROP_TO_FLOOR ( ENT(pev) ); // Try to move the monster to make sure it's not stuck in a brush. - //LRC- there are perfectly good reasons for making a monster stuck, so it shouldn't always be an error. - if (!WALK_MOVE ( ENT(pev), 0, 0, WALKMOVE_NORMAL ) && !FBitSet( pev->spawnflags, SF_MONSTER_NO_YELLOW_BLOBS)) + if (!WALK_MOVE ( ENT(pev), 0, 0, WALKMOVE_NORMAL ) ) { - ALERT(at_debug, "%s \"%s\" stuck in wall--level design error\n", STRING(pev->classname), STRING(pev->targetname)); + ALERT(at_error, "Monster %s stuck in wall--level design error", STRING(pev->classname)); pev->effects = EF_BRIGHTFIELD; } } @@ -2140,7 +2101,44 @@ void CBaseMonster :: StartMonster ( void ) if ( !FStringNull(pev->target) )// this monster has a target { - StartPatrol(UTIL_FindEntityByTargetname( NULL, STRING( pev->target ))); + // Find the monster's initial target entity, stash it + m_pGoalEnt = CBaseEntity::Instance( FIND_ENTITY_BY_TARGETNAME ( NULL, STRING( pev->target ) ) ); + + if ( !m_pGoalEnt ) + { + ALERT(at_error, "ReadyMonster()--%s couldn't find target %s", STRING(pev->classname), STRING(pev->target)); + } + else + { + // Monster will start turning towards his destination + MakeIdealYaw ( m_pGoalEnt->pev->origin ); + + // JAY: How important is this error message? Big Momma doesn't obey this rule, so I took it out. +#if 0 + // At this point, we expect only a path_corner as initial goal + if (!FClassnameIs( m_pGoalEnt->pev, "path_corner")) + { + ALERT(at_warning, "ReadyMonster--monster's initial goal '%s' is not a path_corner", STRING(pev->target)); + } +#endif + + // set the monster up to walk a path corner path. + // !!!BUGBUG - this is a minor bit of a hack. + // JAYJAY + m_movementGoal = MOVEGOAL_PATHCORNER; + + if ( pev->movetype == MOVETYPE_FLY ) + m_movementActivity = ACT_FLY; + else + m_movementActivity = ACT_WALK; + + if ( !FRefreshRoute() ) + { + ALERT ( at_aiconsole, "Can't Create Route!\n" ); + } + SetState( MONSTERSTATE_IDLE ); + ChangeSchedule( GetScheduleOfType( SCHED_IDLE_WALK ) ); + } } //SetState ( m_IdealMonsterState ); @@ -2148,8 +2146,8 @@ void CBaseMonster :: StartMonster ( void ) // Delay drop to floor to make sure each door in the level has had its chance to spawn // Spread think times so that they don't all happen at the same time (Carmack) - SetThink(&CBaseMonster :: CallMonsterThink ); - AbsoluteNextThink( m_fNextThink + RANDOM_FLOAT(0.1, 0.4) ); // spread think times. + SetThink ( CallMonsterThink ); + pev->nextthink += RANDOM_FLOAT(0.1, 0.4); // spread think times. if ( !FStringNull(pev->targetname) )// wait until triggered { @@ -2160,6 +2158,7 @@ void CBaseMonster :: StartMonster ( void ) } } + void CBaseMonster :: MovementComplete( void ) { switch( m_iTaskStatus ) @@ -2199,42 +2198,25 @@ int CBaseMonster::TaskIsRunning( void ) //========================================================= int CBaseMonster::IRelationship ( CBaseEntity *pTarget ) { - static int iEnemy[17][17] = - { // NONE MACH PLYR HPASS HMIL AMIL APASS AMONST APREY APRED INSECT PLRALY PBWPN ABWPN FACT_A FACT_B FACT_C - /*NONE*/ { R_NO, R_NO, R_NO, R_NO, R_NO, R_NO, R_NO, R_NO, R_NO, R_NO, R_NO, R_NO, R_NO, R_NO, R_NO, R_NO, R_NO }, - /*MACHINE*/ { R_NO, R_NO, R_DL, R_DL, R_NO, R_DL, R_DL, R_DL, R_DL, R_DL, R_NO, R_DL, R_DL, R_DL, R_DL, R_DL, R_DL }, - /*PLAYER*/ { R_NO, R_DL, R_NO, R_NO, R_DL, R_DL, R_DL, R_DL, R_DL, R_DL, R_NO, R_NO, R_DL, R_DL, R_DL, R_DL, R_DL }, - /*HUMANPASSIVE*/{ R_NO, R_NO, R_AL, R_AL, R_HT, R_FR, R_NO, R_HT, R_DL, R_FR, R_NO, R_AL, R_NO, R_NO, R_DL, R_DL, R_DL }, - /*HUMANMILITAR*/{ R_NO, R_NO, R_HT, R_DL, R_NO, R_HT, R_DL, R_DL, R_DL, R_DL, R_NO, R_HT, R_NO, R_NO, R_DL, R_DL, R_DL }, - /*ALIENMILITAR*/{ R_NO, R_DL, R_HT, R_DL, R_HT, R_NO, R_NO, R_NO, R_NO, R_NO, R_NO, R_DL, R_NO, R_NO, R_DL, R_DL, R_DL }, - /*ALIENPASSIVE*/{ R_NO, R_NO, R_NO, R_NO, R_NO, R_NO, R_NO, R_NO, R_NO, R_NO, R_NO, R_NO, R_NO, R_NO, R_DL, R_DL, R_DL }, - /*ALIENMONSTER*/{ R_NO, R_DL, R_DL, R_DL, R_DL, R_NO, R_NO, R_NO, R_NO, R_NO, R_NO, R_DL, R_NO, R_NO, R_DL, R_DL, R_DL }, - /*ALIENPREY */{ R_NO, R_NO, R_DL, R_DL, R_DL, R_NO, R_NO, R_NO, R_NO, R_FR, R_NO, R_DL, R_NO, R_NO, R_DL, R_DL, R_DL }, - /*ALIENPREDATO*/{ R_NO, R_NO, R_DL, R_DL, R_DL, R_NO, R_NO, R_NO, R_HT, R_DL, R_NO, R_DL, R_NO, R_NO, R_DL, R_DL, R_DL }, - /*INSECT*/ { R_FR, R_FR, R_FR, R_FR, R_FR, R_NO, R_FR, R_FR, R_FR, R_FR, R_NO, R_FR, R_NO, R_NO, R_FR, R_FR, R_FR }, - /*PLAYERALLY*/ { R_NO, R_DL, R_AL, R_AL, R_DL, R_DL, R_DL, R_DL, R_DL, R_DL, R_NO, R_NO, R_NO, R_NO, R_DL, R_DL, R_DL }, - /*PBIOWEAPON*/ { R_NO, R_NO, R_DL, R_DL, R_DL, R_DL, R_DL, R_DL, R_DL, R_DL, R_NO, R_DL, R_NO, R_DL, R_DL, R_DL, R_DL }, - /*ABIOWEAPON*/ { R_NO, R_NO, R_DL, R_DL, R_DL, R_AL, R_NO, R_DL, R_DL, R_NO, R_NO, R_DL, R_DL, R_NO, R_DL, R_DL, R_DL }, - /*FACTION_A*/ { R_NO, R_DL, R_DL, R_DL, R_DL, R_DL, R_DL, R_DL, R_DL, R_DL, R_NO, R_DL, R_DL, R_DL, R_AL, R_DL, R_DL }, - /*FACTION_B*/ { R_NO, R_DL, R_DL, R_DL, R_DL, R_DL, R_DL, R_DL, R_DL, R_DL, R_NO, R_DL, R_DL, R_DL, R_DL, R_AL, R_DL }, - /*FACTION_C*/ { R_NO, R_DL, R_DL, R_DL, R_DL, R_DL, R_DL, R_DL, R_DL, R_DL, R_NO, R_DL, R_DL, R_DL, R_DL, R_DL, R_AL } + static int iEnemy[14][14] = + { // NONE MACH PLYR HPASS HMIL AMIL APASS AMONST APREY APRED INSECT PLRALY PBWPN ABWPN + /*NONE*/ { R_NO ,R_NO ,R_NO ,R_NO ,R_NO ,R_NO ,R_NO ,R_NO ,R_NO ,R_NO ,R_NO ,R_NO, R_NO, R_NO }, + /*MACHINE*/ { R_NO ,R_NO ,R_DL ,R_DL ,R_NO ,R_DL ,R_DL ,R_DL ,R_DL ,R_DL ,R_NO ,R_DL, R_DL, R_DL }, + /*PLAYER*/ { R_NO ,R_DL ,R_NO ,R_NO ,R_DL ,R_DL ,R_DL ,R_DL ,R_DL ,R_DL ,R_NO ,R_NO, R_DL, R_DL }, + /*HUMANPASSIVE*/{ R_NO ,R_NO ,R_AL ,R_AL ,R_HT ,R_FR ,R_NO ,R_HT ,R_DL ,R_FR ,R_NO ,R_AL, R_NO, R_NO }, + /*HUMANMILITAR*/{ R_NO ,R_NO ,R_HT ,R_DL ,R_NO ,R_HT ,R_DL ,R_DL ,R_DL ,R_DL ,R_NO ,R_HT, R_NO, R_NO }, + /*ALIENMILITAR*/{ R_NO ,R_DL ,R_HT ,R_DL ,R_HT ,R_NO ,R_NO ,R_NO ,R_NO ,R_NO ,R_NO ,R_DL, R_NO, R_NO }, + /*ALIENPASSIVE*/{ R_NO ,R_NO ,R_NO ,R_NO ,R_NO ,R_NO ,R_NO ,R_NO ,R_NO ,R_NO ,R_NO ,R_NO, R_NO, R_NO }, + /*ALIENMONSTER*/{ R_NO ,R_DL ,R_DL ,R_DL ,R_DL ,R_NO ,R_NO ,R_NO ,R_NO ,R_NO ,R_NO ,R_DL, R_NO, R_NO }, + /*ALIENPREY */{ R_NO ,R_NO ,R_DL ,R_DL ,R_DL ,R_NO ,R_NO ,R_NO ,R_NO ,R_FR ,R_NO ,R_DL, R_NO, R_NO }, + /*ALIENPREDATO*/{ R_NO ,R_NO ,R_DL ,R_DL ,R_DL ,R_NO ,R_NO ,R_NO ,R_HT ,R_DL ,R_NO ,R_DL, R_NO, R_NO }, + /*INSECT*/ { R_FR ,R_FR ,R_FR ,R_FR ,R_FR ,R_NO ,R_FR ,R_FR ,R_FR ,R_FR ,R_NO ,R_FR, R_NO, R_NO }, + /*PLAYERALLY*/ { R_NO ,R_DL ,R_AL ,R_AL ,R_DL ,R_DL ,R_DL ,R_DL ,R_DL ,R_DL ,R_NO ,R_NO, R_NO, R_NO }, + /*PBIOWEAPON*/ { R_NO ,R_NO ,R_DL ,R_DL ,R_DL ,R_DL ,R_DL ,R_DL ,R_DL ,R_DL ,R_NO ,R_DL, R_NO, R_DL }, + /*ABIOWEAPON*/ { R_NO ,R_NO ,R_DL ,R_DL ,R_DL ,R_AL ,R_NO ,R_DL ,R_DL ,R_NO ,R_NO ,R_DL, R_DL, R_NO } }; - - int iTargClass = pTarget->Classify(); - if (iTargClass == CLASS_PLAYER && m_iPlayerReact) //LRC - { - if (m_iPlayerReact == 1) // Ignore player - return R_NO; - else if (m_iPlayerReact == 4) - return R_HT; - else if (m_afMemory & bits_MEMORY_PROVOKED) - return R_HT; - else - return R_NO; - } - - return iEnemy[ Classify() ][ iTargClass ]; + return iEnemy[ Classify() ][ pTarget->Classify() ]; } //========================================================= @@ -2269,7 +2251,7 @@ BOOL CBaseMonster :: FindCover ( Vector vecThreat, Vector vecViewOffset, float f if ( flMinDist > 0.5 * flMaxDist) { #if _DEBUG - ALERT ( at_debug, "FindCover MinDist (%.0f) too close to MaxDist (%.0f)\n", flMinDist, flMaxDist ); + ALERT ( at_console, "FindCover MinDist (%.0f) too close to MaxDist (%.0f)\n", flMinDist, flMaxDist ); #endif flMinDist = 0.5 * flMaxDist; } @@ -2324,7 +2306,7 @@ BOOL CBaseMonster :: FindCover ( Vector vecThreat, Vector vecViewOffset, float f if ( FValidateCover ( node.m_vecOrigin ) && MoveToLocation( ACT_RUN, 0, node.m_vecOrigin ) ) { /* - MESSAGE_BEGIN( MSG_BROADCAST, gmsgTempEntity ); + MESSAGE_BEGIN( MSG_BROADCAST, SVC_TEMPENTITY ); WRITE_BYTE( TE_SHOWLINE); WRITE_COORD( node.m_vecOrigin.x ); @@ -2374,7 +2356,7 @@ BOOL CBaseMonster :: BuildNearestRoute ( Vector vecThreat, Vector vecViewOffset, if ( flMinDist > 0.5 * flMaxDist) { #if _DEBUG - ALERT ( at_debug, "FindCover MinDist (%.0f) too close to MaxDist (%.0f)\n", flMinDist, flMaxDist ); + ALERT ( at_console, "FindCover MinDist (%.0f) too close to MaxDist (%.0f)\n", flMinDist, flMaxDist ); #endif flMinDist = 0.5 * flMaxDist; } @@ -2662,13 +2644,11 @@ void CBaseMonster :: HandleAnimEvent( MonsterEvent_t *pEvent ) break; case SCRIPT_EVENT_SOUND: // Play a named wave file - if ( !(pev->spawnflags & SF_MONSTER_GAG) || m_MonsterState != MONSTERSTATE_IDLE) - EMIT_SOUND( edict(), CHAN_BODY, pEvent->options, 1.0, ATTN_IDLE ); + EMIT_SOUND( edict(), CHAN_BODY, pEvent->options, 1.0, ATTN_IDLE ); break; case SCRIPT_EVENT_SOUND_VOICE: - if ( !(pev->spawnflags & SF_MONSTER_GAG) || m_MonsterState != MONSTERSTATE_IDLE) - EMIT_SOUND( edict(), CHAN_VOICE, pEvent->options, 1.0, ATTN_IDLE ); + EMIT_SOUND( edict(), CHAN_VOICE, pEvent->options, 1.0, ATTN_IDLE ); break; case SCRIPT_EVENT_SENTENCE_RND1: // Play a named sentence group 33% of the time @@ -3001,16 +2981,6 @@ void CBaseMonster :: KeyValue( KeyValueData *pkvd ) m_iTriggerCondition = atoi( pkvd->szValue ); pkvd->fHandled = TRUE; } - else if (FStrEq(pkvd->szKeyName, "m_iClass") ) //LRC - { - m_iClass = atoi( pkvd->szValue ); - pkvd->fHandled = TRUE; - } - else if (FStrEq(pkvd->szKeyName, "m_iPlayerReact") ) //LRC - { - m_iPlayerReact = atoi( pkvd->szValue ); - pkvd->fHandled = TRUE; - } else { CBaseToggle::KeyValue( pkvd ); @@ -3125,36 +3095,15 @@ BOOL CBaseMonster :: FCheckAITrigger ( void ) // will be sucked into the script no matter what state it is // in. ONLY Scripted AI ents should allow this. //========================================================= - -//LRC - to help debug when sequences won't play... -#define DEBUG_CANTPLAY - -int CBaseMonster :: CanPlaySequence( int interruptFlags ) +int CBaseMonster :: CanPlaySequence( BOOL fDisregardMonsterState, int interruptLevel ) { - if ( m_pCine ) + if ( m_pCine || !IsAlive() || m_MonsterState == MONSTERSTATE_PRONE ) { - if ( interruptFlags & SS_INTERRUPT_SCRIPTS ) - { - return true; - } - else - { -#ifdef DEBUG_CANTPLAY - ALERT(at_debug, "CANTPLAY: Already playing %s \"%s\"!\n", STRING(m_pCine->pev->classname), STRING(m_pCine->pev->targetname)); -#endif - return false; - } - } - else if ( !IsAlive() || m_MonsterState == MONSTERSTATE_PRONE ) - { -#ifdef DEBUG_CANTPLAY - ALERT(at_debug, "CANTPLAY: Dead/Barnacled!\n"); -#endif // monster is already running a scripted sequence or dead! return FALSE; } - if ( interruptFlags & SS_INTERRUPT_ANYSTATE ) + if ( fDisregardMonsterState ) { // ok to go, no matter what the monster state. (scripted AI) return TRUE; @@ -3166,13 +3115,10 @@ int CBaseMonster :: CanPlaySequence( int interruptFlags ) return TRUE; } - if ( m_MonsterState == MONSTERSTATE_ALERT && interruptFlags & SS_INTERRUPT_ALERT ) + if ( m_MonsterState == MONSTERSTATE_ALERT && interruptLevel >= SS_INTERRUPT_BY_NAME ) return TRUE; // unknown situation -#ifdef DEBUG_CANTPLAY - ALERT(at_debug, "CANTPLAY: non-interruptable state.\n"); -#endif return FALSE; } @@ -3241,16 +3187,14 @@ BOOL CBaseMonster :: FindLateralCover ( const Vector &vecThreat, const Vector &v Vector CBaseMonster :: ShootAtEnemy( const Vector &shootOrigin ) { - if (m_pCine != NULL && m_hTargetEnt != NULL && (m_pCine->m_fTurnType == 1)) + CBaseEntity *pEnemy = m_hEnemy; + + if ( pEnemy ) { - Vector vecDest = ( m_hTargetEnt->pev->absmin + m_hTargetEnt->pev->absmax ) / 2; - return ( vecDest - shootOrigin ).Normalize(); + return ( (pEnemy->BodyTarget( shootOrigin ) - pEnemy->pev->origin) + m_vecEnemyLKP - shootOrigin ).Normalize(); } - else if ( m_hEnemy ) - { - return ( (m_hEnemy->BodyTarget( shootOrigin ) - m_hEnemy->pev->origin) + m_vecEnemyLKP - shootOrigin ).Normalize(); - } - else return gpGlobals->v_forward; + else + return gpGlobals->v_forward; } @@ -3318,10 +3262,10 @@ void CBaseMonster::CorpseFallThink( void ) SetThink ( NULL ); SetSequenceBox( ); - UTIL_SetOrigin( this, pev->origin );// link into world. + UTIL_SetOrigin( pev, pev->origin );// link into world. } else - SetNextThink( 0.1 ); + pev->nextthink = gpGlobals->time + 0.1; } // Call after animation/pose is set up @@ -3341,12 +3285,12 @@ void CBaseMonster :: MonsterInitDead( void ) pev->deadflag = DEAD_DEAD; UTIL_SetSize(pev, g_vecZero, g_vecZero ); - UTIL_SetOrigin( this, pev->origin ); + UTIL_SetOrigin( pev, pev->origin ); // Setup health counters, etc. BecomeDead(); - SetThink(&CBaseMonster :: CorpseFallThink ); - SetNextThink( 0.5 ); + SetThink( CorpseFallThink ); + pev->nextthink = gpGlobals->time + 0.5; } //========================================================= @@ -3472,7 +3416,7 @@ CBaseEntity* CBaseMonster :: DropItem ( char *pszItemName, const Vector &vecPos, { if ( !pszItemName ) { - ALERT ( at_debug, "DropItem() - No item name!\n" ); + ALERT ( at_console, "DropItem() - No item name!\n" ); return NULL; } @@ -3487,7 +3431,7 @@ CBaseEntity* CBaseMonster :: DropItem ( char *pszItemName, const Vector &vecPos, } else { - ALERT ( at_debug, "DropItem() - Didn't create!\n" ); + ALERT ( at_console, "DropItem() - Didn't create!\n" ); return FALSE; } @@ -3502,41 +3446,3 @@ BOOL CBaseMonster :: ShouldFadeOnDeath( void ) return FALSE; } - - - - -//LRC - an entity for monsters to shoot at. -#define SF_MONSTERTARGET_OFF 1 -class CMonsterTarget : public CBaseEntity -{ -public: - void Spawn( void ); - void Use( CBaseEntity *pActivator, CBaseEntity *pCaller, USE_TYPE useType, float value ); - int Classify( void ) { return pev->frags; }; - STATE GetState( void ) - { - return pev->health?STATE_ON:STATE_OFF; - }; -}; -LINK_ENTITY_TO_CLASS( monster_target, CMonsterTarget ); - -void CMonsterTarget :: Spawn ( void ) -{ - if (pev->spawnflags & SF_MONSTERTARGET_OFF) - pev->health = 0; - else - pev->health = 1; // Don't ignore me, I'm not dead. I'm quite well really. I think I'll go for a walk... - SetBits (pev->flags, FL_MONSTER); -} - -void CMonsterTarget::Use( CBaseEntity *pActivator, CBaseEntity *pCaller, USE_TYPE useType, float value ) -{ - if (ShouldToggle( useType )) - { - if (pev->health) - pev->health = 0; - else - pev->health = 1; - } -} \ No newline at end of file diff --git a/spirit/monsters.h b/dlls/monsters.h similarity index 86% rename from spirit/monsters.h rename to dlls/monsters.h index a672e368..75c945fd 100644 --- a/spirit/monsters.h +++ b/dlls/monsters.h @@ -48,13 +48,9 @@ #define SF_MONSTER_PRISONER 16 // monster won't attack anyone, no one will attacke him. // 32 // 64 -#define SF_MONSTER_NO_YELLOW_BLOBS 128 //LRC- if the monster is stuck, don't give errors or show yellow blobs. -//LRC- wasn't implemented. #define SF_MONSTER_WAIT_FOR_SCRIPT 128 //spawnflag that makes monsters wait to check for attacking until the script is done or they've been attacked +#define SF_MONSTER_WAIT_FOR_SCRIPT 128 //spawnflag that makes monsters wait to check for attacking until the script is done or they've been attacked #define SF_MONSTER_PREDISASTER 256 //this is a predisaster scientist or barney. Influences how they speak. #define SF_MONSTER_FADECORPSE 512 // Fade out corpse after death -#define SF_MONSTER_NO_WPN_DROP 1024 //LRC- never drop your weapon (player can't pick it up.) -//LRC - this clashes with 'not in deathmatch'. Replaced with m_iPlayerReact. -//#define SF_MONSTER_INVERT_PLAYERREACT 2048 //LRC- if this monster would usually attack the player, don't attack unless provoked. If you would usually NOT attack the player, attack him. #define SF_MONSTER_FALL_TO_GROUND 0x80000000 // specialty spawnflags @@ -62,6 +58,8 @@ #define SF_MONSTER_TURRET_STARTINACTIVE 64 #define SF_MONSTER_WAIT_UNTIL_PROVOKED 64 // don't attack the player unless provoked + + // MoveToOrigin stuff #define MOVE_START_TURN_DIST 64 // when this far away from moveGoal, start turning to face next goal #define MOVE_STUCK_DIST 32 // if a monster can't step this far, it is stuck. @@ -154,9 +152,7 @@ public: virtual int ObjectCaps( void ) { return (CBaseEntity :: ObjectCaps() & ~FCAP_ACROSS_TRANSITION) | FCAP_DONT_SAVE; } static void SpawnHeadGib( entvars_t *pevVictim ); - static void SpawnHeadGib( entvars_t *pevVictim, const char *szGibModel ); static void SpawnRandomGibs( entvars_t *pevVictim, int cGibs, int human ); - static void SpawnRandomGibs( entvars_t *pevVictim, int cGibs, int notfirst, const char *szGibModel ); //LRC static void SpawnStickyGibs( entvars_t *pevVictim, Vector vecOrigin, int cGibs ); int m_bloodColor; diff --git a/spirit/monsterstate.cpp b/dlls/monsterstate.cpp similarity index 94% rename from spirit/monsterstate.cpp rename to dlls/monsterstate.cpp index ed9b8181..aca95b35 100644 --- a/spirit/monsterstate.cpp +++ b/dlls/monsterstate.cpp @@ -62,7 +62,7 @@ void CBaseMonster :: SetState ( MONSTERSTATE State ) void CBaseMonster :: RunAI ( void ) { // to test model's eye height - // UTIL_ParticleEffect ( pev->origin + pev->view_ofs, g_vecZero, 255, 10 ); + //UTIL_ParticleEffect ( pev->origin + pev->view_ofs, g_vecZero, 255, 10 ); // IDLE sound permitted in ALERT state is because monsters were silent in ALERT state. Only play IDLE sound in IDLE state // once we have sounds for that state. @@ -80,7 +80,7 @@ void CBaseMonster :: RunAI ( void ) // things will happen before the player gets there! // UPDATE: We now let COMBAT state monsters think and act fully outside of player PVS. This allows the player to leave // an area where monsters are fighting, and the fight will continue. - if ( !FNullEnt( FIND_CLIENT_IN_PVS( edict() ) ) || ( m_MonsterState == MONSTERSTATE_COMBAT ) || HaveCamerasInPVS( edict() )) + if ( !FNullEnt( FIND_CLIENT_IN_PVS( edict() ) ) || ( m_MonsterState == MONSTERSTATE_COMBAT ) ) { Look( m_flDistLook ); Listen();// check for audible sounds. diff --git a/spirit/mortar.cpp b/dlls/mortar.cpp similarity index 92% rename from spirit/mortar.cpp rename to dlls/mortar.cpp index 34d6bbd0..e23d7b60 100644 --- a/spirit/mortar.cpp +++ b/dlls/mortar.cpp @@ -1,323 +1,323 @@ -/*** -* -* Copyright (c) 1996-2002, 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. -* -****/ -/* - -===== mortar.cpp ======================================================== - - the "LaBuznik" mortar device - -*/ - -#include "extdll.h" -#include "util.h" -#include "cbase.h" -#include "saverestore.h" -#include "weapons.h" -#include "decals.h" -#include "soundent.h" - -class CFuncMortarField : public CBaseToggle -{ -public: - void Spawn( void ); - void Precache( void ); - void KeyValue( KeyValueData *pkvd ); - - // Bmodels don't go across transitions - virtual int ObjectCaps( void ) { return CBaseToggle :: ObjectCaps() & ~FCAP_ACROSS_TRANSITION; } - - virtual int Save( CSave &save ); - virtual int Restore( CRestore &restore ); - - static TYPEDESCRIPTION m_SaveData[]; - - void EXPORT FieldUse( CBaseEntity *pActivator, CBaseEntity *pCaller, USE_TYPE useType, float value ); - - int m_iszXController; - int m_iszYController; - float m_flSpread; - float m_flDelay; - int m_iCount; - int m_fControl; -}; - -LINK_ENTITY_TO_CLASS( func_mortar_field, CFuncMortarField ); - -TYPEDESCRIPTION CFuncMortarField::m_SaveData[] = -{ - DEFINE_FIELD( CFuncMortarField, m_iszXController, FIELD_STRING ), - DEFINE_FIELD( CFuncMortarField, m_iszYController, FIELD_STRING ), - DEFINE_FIELD( CFuncMortarField, m_flSpread, FIELD_FLOAT ), - DEFINE_FIELD( CFuncMortarField, m_flDelay, FIELD_FLOAT ), - DEFINE_FIELD( CFuncMortarField, m_iCount, FIELD_INTEGER ), - DEFINE_FIELD( CFuncMortarField, m_fControl, FIELD_INTEGER ), -}; - -IMPLEMENT_SAVERESTORE( CFuncMortarField, CBaseToggle ); - - -void CFuncMortarField :: KeyValue( KeyValueData *pkvd ) -{ - if (FStrEq(pkvd->szKeyName, "m_iszXController")) - { - m_iszXController = ALLOC_STRING(pkvd->szValue); - pkvd->fHandled = TRUE; - } - else if (FStrEq(pkvd->szKeyName, "m_iszYController")) - { - m_iszYController = ALLOC_STRING(pkvd->szValue); - pkvd->fHandled = TRUE; - } - else if (FStrEq(pkvd->szKeyName, "m_flSpread")) - { - m_flSpread = atof(pkvd->szValue); - pkvd->fHandled = TRUE; - } - else if (FStrEq(pkvd->szKeyName, "m_fControl")) - { - m_fControl = atoi(pkvd->szValue); - pkvd->fHandled = TRUE; - } - else if (FStrEq(pkvd->szKeyName, "m_iCount")) - { - m_iCount = atoi(pkvd->szValue); - pkvd->fHandled = TRUE; - } -} - - -// Drop bombs from above -void CFuncMortarField :: Spawn( void ) -{ - pev->solid = SOLID_NOT; - SET_MODEL(ENT(pev), STRING(pev->model)); // set size and link into world - pev->movetype = MOVETYPE_NONE; - SetBits( pev->effects, EF_NODRAW ); - SetUse(&CFuncMortarField :: FieldUse ); - Precache(); -} - - -void CFuncMortarField :: Precache( void ) -{ - PRECACHE_SOUND ("weapons/mortar.wav"); - PRECACHE_SOUND ("weapons/mortarhit.wav"); - PRECACHE_MODEL( "sprites/lgtning.spr" ); -} - - -// If connected to a table, then use the table controllers, else hit where the trigger is. -void CFuncMortarField :: FieldUse( CBaseEntity *pActivator, CBaseEntity *pCaller, USE_TYPE useType, float value ) -{ - Vector vecStart; - - vecStart.x = RANDOM_FLOAT( pev->mins.x, pev->maxs.x ); - vecStart.y = RANDOM_FLOAT( pev->mins.y, pev->maxs.y ); - vecStart.z = pev->maxs.z; - - switch( m_fControl ) - { - case 0: // random - break; - case 1: // Trigger Activator - if (pActivator != NULL) - { - vecStart.x = pActivator->pev->origin.x; - vecStart.y = pActivator->pev->origin.y; - } - break; - case 2: // table - { - CBaseEntity *pController; - - if (!FStringNull(m_iszXController)) - { - pController = UTIL_FindEntityByTargetname( NULL, STRING(m_iszXController)); - if (pController != NULL) - { - vecStart.x = pev->mins.x + pController->pev->ideal_yaw * (pev->size.x); - } - } - if (!FStringNull(m_iszYController)) - { - pController = UTIL_FindEntityByTargetname( NULL, STRING(m_iszYController)); - if (pController != NULL) - { - vecStart.y = pev->mins.y + pController->pev->ideal_yaw * (pev->size.y); - } - } - } - break; - } - - int pitch = RANDOM_LONG(95,124); - - EMIT_SOUND_DYN(ENT(pev), CHAN_VOICE, "weapons/mortar.wav", 1.0, ATTN_NONE, 0, pitch); - - float t = 2.5; - for (int i = 0; i < m_iCount; i++) - { - Vector vecSpot = vecStart; - vecSpot.x += RANDOM_FLOAT( -m_flSpread, m_flSpread ); - vecSpot.y += RANDOM_FLOAT( -m_flSpread, m_flSpread ); - - TraceResult tr; - UTIL_TraceLine( vecSpot, vecSpot + Vector( 0, 0, -1 ) * 4096, ignore_monsters, ENT(pev), &tr ); - - edict_t *pentOwner = NULL; - if (pActivator) pentOwner = pActivator->edict(); - - CBaseEntity *pMortar = Create("monster_mortar", tr.vecEndPos, Vector( 0, 0, 0 ), pentOwner ); - pMortar->SetNextThink( t ); - t += RANDOM_FLOAT( 0.2, 0.5 ); - - if (i == 0) - CSoundEnt::InsertSound ( bits_SOUND_DANGER, tr.vecEndPos, 400, 0.3 ); - } -} - - -class CMortar : public CGrenade -{ -public: - void Spawn( void ); - void Precache( void ); - - void EXPORT MortarExplode( void ); - - int m_spriteTexture; -}; - -LINK_ENTITY_TO_CLASS( monster_mortar, CMortar ); - -void CMortar::Spawn( ) -{ - pev->movetype = MOVETYPE_NONE; - pev->solid = SOLID_NOT; - - pev->dmg = 200; - - SetThink(&CMortar:: MortarExplode ); - DontThink(); - - Precache( ); - - -} - - -void CMortar::Precache( ) -{ - m_spriteTexture = PRECACHE_MODEL( "sprites/lgtning.spr" ); -} - -void CMortar::MortarExplode( void ) -{ -#if 1 - // mortar beam - MESSAGE_BEGIN( MSG_BROADCAST, gmsgTempEntity ); - WRITE_BYTE( TE_BEAMPOINTS ); - WRITE_COORD(pev->origin.x); - WRITE_COORD(pev->origin.y); - WRITE_COORD(pev->origin.z); - WRITE_COORD(pev->origin.x); - WRITE_COORD(pev->origin.y); - WRITE_COORD(pev->origin.z + 1024); - WRITE_SHORT(m_spriteTexture ); - WRITE_BYTE( 0 ); // framerate - WRITE_BYTE( 0 ); // framerate - WRITE_BYTE( 1 ); // life - WRITE_BYTE( 40 ); // width - WRITE_BYTE( 0 ); // noise - WRITE_BYTE( 255 ); // r, g, b - WRITE_BYTE( 160 ); // r, g, b - WRITE_BYTE( 100 ); // r, g, b - WRITE_BYTE( 128 ); // brightness - WRITE_BYTE( 0 ); // speed - MESSAGE_END(); -#endif - -#if 0 - // blast circle - MESSAGE_BEGIN( MSG_BROADCAST, gmsgTempEntity ); - WRITE_BYTE( TE_BEAMTORUS); - WRITE_COORD(pev->origin.x); - WRITE_COORD(pev->origin.y); - WRITE_COORD(pev->origin.z + 32); - WRITE_COORD(pev->origin.x); - WRITE_COORD(pev->origin.y); - WRITE_COORD(pev->origin.z + 32 + pev->dmg * 2 / .2); // reach damage radius over .3 seconds - WRITE_SHORT(m_spriteTexture ); - WRITE_BYTE( 0 ); // startframe - WRITE_BYTE( 0 ); // framerate - WRITE_BYTE( 2 ); // life - WRITE_BYTE( 12 ); // width - WRITE_BYTE( 0 ); // noise - WRITE_BYTE( 255 ); // r, g, b - WRITE_BYTE( 160 ); // r, g, b - WRITE_BYTE( 100 ); // r, g, b - WRITE_BYTE( 255 ); // brightness - WRITE_BYTE( 0 ); // speed - MESSAGE_END(); -#endif - - TraceResult tr; - UTIL_TraceLine( pev->origin + Vector( 0, 0, 1024 ), pev->origin - Vector( 0, 0, 1024 ), dont_ignore_monsters, ENT(pev), &tr ); - - Explode( &tr, DMG_BLAST | DMG_MORTAR ); - UTIL_ScreenShake( tr.vecEndPos, 25.0, 150.0, 1.0, 750 ); - -#if 0 - int pitch = RANDOM_LONG(95,124); - EMIT_SOUND_DYN(ENT(pev), CHAN_VOICE, "weapons/mortarhit.wav", 1.0, 0.55, 0, pitch); - - // ForceSound( SNDRADIUS_MP5, bits_SOUND_COMBAT ); - - // ExplodeModel( pev->origin, 400, g_sModelIndexShrapnel, 30 ); - - RadiusDamage ( pev, VARS(pev->owner), pev->dmg, CLASS_NONE, DMG_BLAST ); - - /* - if ( RANDOM_FLOAT ( 0 , 1 ) < 0.5 ) - { - UTIL_DecalTrace( pTrace, DECAL_SCORCH1 ); - } - else - { - UTIL_DecalTrace( pTrace, DECAL_SCORCH2 ); - } - */ - - SetThink(&CMortar:: SUB_Remove ); - SetNextThink( 0.1 ); -#endif - -} - - -#if 0 -void CMortar::ShootTimed( EVARS *pevOwner, Vector vecStart, float time ) -{ - CMortar *pMortar = GetClassPtr( (CMortar *)NULL ); - pMortar->Spawn(); - - TraceResult tr; - UTIL_TraceLine( vecStart, vecStart + Vector( 0, 0, -1 ) * 4096, ignore_monsters, ENT(pMortar->pev), &tr ); - - pMortar->SetNextThink( time ); - - UTIL_SetOrigin( pMortar->pev, tr.vecEndPos ); -} -#endif +/*** +* +* Copyright (c) 1996-2002, 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. +* +****/ +/* + +===== mortar.cpp ======================================================== + + the "LaBuznik" mortar device + +*/ + +#include "extdll.h" +#include "util.h" +#include "cbase.h" +#include "saverestore.h" +#include "weapons.h" +#include "decals.h" +#include "soundent.h" + +class CFuncMortarField : public CBaseToggle +{ +public: + void Spawn( void ); + void Precache( void ); + void KeyValue( KeyValueData *pkvd ); + + // Bmodels don't go across transitions + virtual int ObjectCaps( void ) { return CBaseToggle :: ObjectCaps() & ~FCAP_ACROSS_TRANSITION; } + + virtual int Save( CSave &save ); + virtual int Restore( CRestore &restore ); + + static TYPEDESCRIPTION m_SaveData[]; + + void EXPORT FieldUse( CBaseEntity *pActivator, CBaseEntity *pCaller, USE_TYPE useType, float value ); + + int m_iszXController; + int m_iszYController; + float m_flSpread; + float m_flDelay; + int m_iCount; + int m_fControl; +}; + +LINK_ENTITY_TO_CLASS( func_mortar_field, CFuncMortarField ); + +TYPEDESCRIPTION CFuncMortarField::m_SaveData[] = +{ + DEFINE_FIELD( CFuncMortarField, m_iszXController, FIELD_STRING ), + DEFINE_FIELD( CFuncMortarField, m_iszYController, FIELD_STRING ), + DEFINE_FIELD( CFuncMortarField, m_flSpread, FIELD_FLOAT ), + DEFINE_FIELD( CFuncMortarField, m_flDelay, FIELD_FLOAT ), + DEFINE_FIELD( CFuncMortarField, m_iCount, FIELD_INTEGER ), + DEFINE_FIELD( CFuncMortarField, m_fControl, FIELD_INTEGER ), +}; + +IMPLEMENT_SAVERESTORE( CFuncMortarField, CBaseToggle ); + + +void CFuncMortarField :: KeyValue( KeyValueData *pkvd ) +{ + if (FStrEq(pkvd->szKeyName, "m_iszXController")) + { + m_iszXController = ALLOC_STRING(pkvd->szValue); + pkvd->fHandled = TRUE; + } + else if (FStrEq(pkvd->szKeyName, "m_iszYController")) + { + m_iszYController = ALLOC_STRING(pkvd->szValue); + pkvd->fHandled = TRUE; + } + else if (FStrEq(pkvd->szKeyName, "m_flSpread")) + { + m_flSpread = atof(pkvd->szValue); + pkvd->fHandled = TRUE; + } + else if (FStrEq(pkvd->szKeyName, "m_fControl")) + { + m_fControl = atoi(pkvd->szValue); + pkvd->fHandled = TRUE; + } + else if (FStrEq(pkvd->szKeyName, "m_iCount")) + { + m_iCount = atoi(pkvd->szValue); + pkvd->fHandled = TRUE; + } +} + + +// Drop bombs from above +void CFuncMortarField :: Spawn( void ) +{ + pev->solid = SOLID_NOT; + SET_MODEL(ENT(pev), STRING(pev->model)); // set size and link into world + pev->movetype = MOVETYPE_NONE; + SetBits( pev->effects, EF_NODRAW ); + SetUse( FieldUse ); + Precache(); +} + + +void CFuncMortarField :: Precache( void ) +{ + PRECACHE_SOUND ("weapons/mortar.wav"); + PRECACHE_SOUND ("weapons/mortarhit.wav"); + PRECACHE_MODEL( "sprites/lgtning.spr" ); +} + + +// If connected to a table, then use the table controllers, else hit where the trigger is. +void CFuncMortarField :: FieldUse( CBaseEntity *pActivator, CBaseEntity *pCaller, USE_TYPE useType, float value ) +{ + Vector vecStart; + + vecStart.x = RANDOM_FLOAT( pev->mins.x, pev->maxs.x ); + vecStart.y = RANDOM_FLOAT( pev->mins.y, pev->maxs.y ); + vecStart.z = pev->maxs.z; + + switch( m_fControl ) + { + case 0: // random + break; + case 1: // Trigger Activator + if (pActivator != NULL) + { + vecStart.x = pActivator->pev->origin.x; + vecStart.y = pActivator->pev->origin.y; + } + break; + case 2: // table + { + CBaseEntity *pController; + + if (!FStringNull(m_iszXController)) + { + pController = UTIL_FindEntityByTargetname( NULL, STRING(m_iszXController)); + if (pController != NULL) + { + vecStart.x = pev->mins.x + pController->pev->ideal_yaw * (pev->size.x); + } + } + if (!FStringNull(m_iszYController)) + { + pController = UTIL_FindEntityByTargetname( NULL, STRING(m_iszYController)); + if (pController != NULL) + { + vecStart.y = pev->mins.y + pController->pev->ideal_yaw * (pev->size.y); + } + } + } + break; + } + + int pitch = RANDOM_LONG(95,124); + + EMIT_SOUND_DYN(ENT(pev), CHAN_VOICE, "weapons/mortar.wav", 1.0, ATTN_NONE, 0, pitch); + + float t = 2.5; + for (int i = 0; i < m_iCount; i++) + { + Vector vecSpot = vecStart; + vecSpot.x += RANDOM_FLOAT( -m_flSpread, m_flSpread ); + vecSpot.y += RANDOM_FLOAT( -m_flSpread, m_flSpread ); + + TraceResult tr; + UTIL_TraceLine( vecSpot, vecSpot + Vector( 0, 0, -1 ) * 4096, ignore_monsters, ENT(pev), &tr ); + + edict_t *pentOwner = NULL; + if (pActivator) pentOwner = pActivator->edict(); + + CBaseEntity *pMortar = Create("monster_mortar", tr.vecEndPos, Vector( 0, 0, 0 ), pentOwner ); + pMortar->pev->nextthink = gpGlobals->time + t; + t += RANDOM_FLOAT( 0.2, 0.5 ); + + if (i == 0) + CSoundEnt::InsertSound ( bits_SOUND_DANGER, tr.vecEndPos, 400, 0.3 ); + } +} + + +class CMortar : public CGrenade +{ +public: + void Spawn( void ); + void Precache( void ); + + void EXPORT MortarExplode( void ); + + int m_spriteTexture; +}; + +LINK_ENTITY_TO_CLASS( monster_mortar, CMortar ); + +void CMortar::Spawn( ) +{ + pev->movetype = MOVETYPE_NONE; + pev->solid = SOLID_NOT; + + pev->dmg = 200; + + SetThink( MortarExplode ); + pev->nextthink = 0; + + Precache( ); + + +} + + +void CMortar::Precache( ) +{ + m_spriteTexture = PRECACHE_MODEL( "sprites/lgtning.spr" ); +} + +void CMortar::MortarExplode( void ) +{ +#if 1 + // mortar beam + MESSAGE_BEGIN( MSG_BROADCAST, SVC_TEMPENTITY ); + WRITE_BYTE( TE_BEAMPOINTS ); + WRITE_COORD(pev->origin.x); + WRITE_COORD(pev->origin.y); + WRITE_COORD(pev->origin.z); + WRITE_COORD(pev->origin.x); + WRITE_COORD(pev->origin.y); + WRITE_COORD(pev->origin.z + 1024); + WRITE_SHORT(m_spriteTexture ); + WRITE_BYTE( 0 ); // framerate + WRITE_BYTE( 0 ); // framerate + WRITE_BYTE( 1 ); // life + WRITE_BYTE( 40 ); // width + WRITE_BYTE( 0 ); // noise + WRITE_BYTE( 255 ); // r, g, b + WRITE_BYTE( 160 ); // r, g, b + WRITE_BYTE( 100 ); // r, g, b + WRITE_BYTE( 128 ); // brightness + WRITE_BYTE( 0 ); // speed + MESSAGE_END(); +#endif + +#if 0 + // blast circle + MESSAGE_BEGIN( MSG_BROADCAST, SVC_TEMPENTITY ); + WRITE_BYTE( TE_BEAMTORUS); + WRITE_COORD(pev->origin.x); + WRITE_COORD(pev->origin.y); + WRITE_COORD(pev->origin.z + 32); + WRITE_COORD(pev->origin.x); + WRITE_COORD(pev->origin.y); + WRITE_COORD(pev->origin.z + 32 + pev->dmg * 2 / .2); // reach damage radius over .3 seconds + WRITE_SHORT(m_spriteTexture ); + WRITE_BYTE( 0 ); // startframe + WRITE_BYTE( 0 ); // framerate + WRITE_BYTE( 2 ); // life + WRITE_BYTE( 12 ); // width + WRITE_BYTE( 0 ); // noise + WRITE_BYTE( 255 ); // r, g, b + WRITE_BYTE( 160 ); // r, g, b + WRITE_BYTE( 100 ); // r, g, b + WRITE_BYTE( 255 ); // brightness + WRITE_BYTE( 0 ); // speed + MESSAGE_END(); +#endif + + TraceResult tr; + UTIL_TraceLine( pev->origin + Vector( 0, 0, 1024 ), pev->origin - Vector( 0, 0, 1024 ), dont_ignore_monsters, ENT(pev), &tr ); + + Explode( &tr, DMG_BLAST | DMG_MORTAR ); + UTIL_ScreenShake( tr.vecEndPos, 25.0, 150.0, 1.0, 750 ); + +#if 0 + int pitch = RANDOM_LONG(95,124); + EMIT_SOUND_DYN(ENT(pev), CHAN_VOICE, "weapons/mortarhit.wav", 1.0, 0.55, 0, pitch); + + // ForceSound( SNDRADIUS_MP5, bits_SOUND_COMBAT ); + + // ExplodeModel( pev->origin, 400, g_sModelIndexShrapnel, 30 ); + + RadiusDamage ( pev, VARS(pev->owner), pev->dmg, CLASS_NONE, DMG_BLAST ); + + /* + if ( RANDOM_FLOAT ( 0 , 1 ) < 0.5 ) + { + UTIL_DecalTrace( pTrace, DECAL_SCORCH1 ); + } + else + { + UTIL_DecalTrace( pTrace, DECAL_SCORCH2 ); + } + */ + + SetThink( SUB_Remove ); + pev->nextthink = gpGlobals->time + 0.1; +#endif + +} + + +#if 0 +void CMortar::ShootTimed( EVARS *pevOwner, Vector vecStart, float time ) +{ + CMortar *pMortar = GetClassPtr( (CMortar *)NULL ); + pMortar->Spawn(); + + TraceResult tr; + UTIL_TraceLine( vecStart, vecStart + Vector( 0, 0, -1 ) * 4096, ignore_monsters, ENT(pMortar->pev), &tr ); + + pMortar->pev->nextthink = gpGlobals->time + time; + + UTIL_SetOrigin( pMortar->pev, tr.vecEndPos ); +} +#endif \ No newline at end of file diff --git a/dlls/mp5.cpp b/dlls/mp5.cpp new file mode 100644 index 00000000..8ee2ce89 --- /dev/null +++ b/dlls/mp5.cpp @@ -0,0 +1,385 @@ +/*** +* +* Copyright (c) 1996-2002, 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. +* +****/ + +#include "extdll.h" +#include "util.h" +#include "cbase.h" +#include "monsters.h" +#include "weapons.h" +#include "nodes.h" +#include "player.h" +#include "soundent.h" +#include "gamerules.h" + +enum mp5_e +{ + MP5_LONGIDLE = 0, + MP5_IDLE1, + MP5_LAUNCH, + MP5_RELOAD, + MP5_DEPLOY, + MP5_FIRE1, + MP5_FIRE2, + MP5_FIRE3, +}; + + + +LINK_ENTITY_TO_CLASS( weapon_mp5, CMP5 ); +LINK_ENTITY_TO_CLASS( weapon_9mmAR, CMP5 ); + + +//========================================================= +//========================================================= +int CMP5::SecondaryAmmoIndex( void ) +{ + return m_iSecondaryAmmoType; +} + +void CMP5::Spawn( ) +{ + pev->classname = MAKE_STRING("weapon_9mmAR"); // hack to allow for old names + Precache( ); + SET_MODEL(ENT(pev), "models/w_9mmAR.mdl"); + m_iId = WEAPON_MP5; + + m_iDefaultAmmo = MP5_DEFAULT_GIVE; + + FallInit();// get ready to fall down. +} + + +void CMP5::Precache( void ) +{ + PRECACHE_MODEL("models/v_9mmAR.mdl"); + PRECACHE_MODEL("models/w_9mmAR.mdl"); + PRECACHE_MODEL("models/p_9mmAR.mdl"); + + m_iShell = PRECACHE_MODEL ("models/shell.mdl");// brass shellTE_MODEL + + PRECACHE_MODEL("models/grenade.mdl"); // grenade + + PRECACHE_MODEL("models/w_9mmARclip.mdl"); + PRECACHE_SOUND("items/9mmclip1.wav"); + + PRECACHE_SOUND("items/clipinsert1.wav"); + PRECACHE_SOUND("items/cliprelease1.wav"); + + PRECACHE_SOUND ("weapons/hks1.wav");// H to the K + PRECACHE_SOUND ("weapons/hks2.wav");// H to the K + PRECACHE_SOUND ("weapons/hks3.wav");// H to the K + + PRECACHE_SOUND( "weapons/glauncher.wav" ); + PRECACHE_SOUND( "weapons/glauncher2.wav" ); + + PRECACHE_SOUND ("weapons/357_cock1.wav"); + + m_usMP5 = PRECACHE_EVENT( 1, "events/mp5.sc" ); + m_usMP52 = PRECACHE_EVENT( 1, "events/mp52.sc" ); +} + +int CMP5::GetItemInfo(ItemInfo *p) +{ + p->pszName = STRING(pev->classname); + p->pszAmmo1 = "9mm"; + p->iMaxAmmo1 = _9MM_MAX_CARRY; + p->pszAmmo2 = "ARgrenades"; + p->iMaxAmmo2 = M203_GRENADE_MAX_CARRY; + p->iMaxClip = MP5_MAX_CLIP; + p->iSlot = 2; + p->iPosition = 0; + p->iFlags = 0; + p->iId = m_iId = WEAPON_MP5; + p->iWeight = MP5_WEIGHT; + + return 1; +} + +int CMP5::AddToPlayer( CBasePlayer *pPlayer ) +{ + if ( CBasePlayerWeapon::AddToPlayer( pPlayer ) ) + { + MESSAGE_BEGIN( MSG_ONE, gmsgWeapPickup, NULL, pPlayer->pev ); + WRITE_BYTE( m_iId ); + MESSAGE_END(); + return TRUE; + } + return FALSE; +} + +BOOL CMP5::Deploy( ) +{ + return DefaultDeploy( "models/v_9mmAR.mdl", "models/p_9mmAR.mdl", MP5_DEPLOY, "mp5" ); +} + + +void CMP5::PrimaryAttack() +{ + // don't fire underwater + if (m_pPlayer->pev->waterlevel == 3) + { + PlayEmptySound( ); + m_flNextPrimaryAttack = 0.15; + return; + } + + if (m_iClip <= 0) + { + PlayEmptySound(); + m_flNextPrimaryAttack = 0.15; + return; + } + + m_pPlayer->m_iWeaponVolume = NORMAL_GUN_VOLUME; + m_pPlayer->m_iWeaponFlash = NORMAL_GUN_FLASH; + + m_iClip--; + + + m_pPlayer->pev->effects = (int)(m_pPlayer->pev->effects) | EF_MUZZLEFLASH; + + // player "shoot" animation + m_pPlayer->SetAnimation( PLAYER_ATTACK1 ); + + Vector vecSrc = m_pPlayer->GetGunPosition( ); + Vector vecAiming = m_pPlayer->GetAutoaimVector( AUTOAIM_5DEGREES ); + Vector vecDir; + +#ifdef CLIENT_DLL + if ( !bIsMultiplayer() ) +#else + if ( !g_pGameRules->IsMultiplayer() ) +#endif + { + // optimized multiplayer. Widened to make it easier to hit a moving player + vecDir = m_pPlayer->FireBulletsPlayer( 1, vecSrc, vecAiming, VECTOR_CONE_6DEGREES, 8192, BULLET_PLAYER_MP5, 2, 0, m_pPlayer->pev, m_pPlayer->random_seed ); + } + else + { + // single player spread + vecDir = m_pPlayer->FireBulletsPlayer( 1, vecSrc, vecAiming, VECTOR_CONE_3DEGREES, 8192, BULLET_PLAYER_MP5, 2, 0, m_pPlayer->pev, m_pPlayer->random_seed ); + } + + int flags; +#if defined( CLIENT_WEAPONS ) + flags = FEV_NOTHOST; +#else + flags = 0; +#endif + + PLAYBACK_EVENT_FULL( flags, m_pPlayer->edict(), m_usMP5, 0.0, (float *)&g_vecZero, (float *)&g_vecZero, vecDir.x, vecDir.y, 0, 0, 0, 0 ); + + if (!m_iClip && m_pPlayer->m_rgAmmo[m_iPrimaryAmmoType] <= 0) + // HEV suit - indicate out of ammo condition + m_pPlayer->SetSuitUpdate("!HEV_AMO0", FALSE, 0); + + m_flNextPrimaryAttack = UTIL_WeaponTimeBase() + 0.1; + + if ( m_flNextPrimaryAttack < UTIL_WeaponTimeBase() ) + m_flNextPrimaryAttack = UTIL_WeaponTimeBase() + 0.1; + + m_flTimeWeaponIdle = UTIL_WeaponTimeBase() + UTIL_SharedRandomFloat( m_pPlayer->random_seed, 10, 15 ); +} + + + +void CMP5::SecondaryAttack( void ) +{ + // don't fire underwater + if (m_pPlayer->pev->waterlevel == 3) + { + PlayEmptySound( ); + m_flNextPrimaryAttack = 0.15; + return; + } + + if (m_pPlayer->m_rgAmmo[m_iSecondaryAmmoType] == 0) + { + PlayEmptySound( ); + return; + } + + m_pPlayer->m_iWeaponVolume = NORMAL_GUN_VOLUME; + m_pPlayer->m_iWeaponFlash = BRIGHT_GUN_FLASH; + + m_pPlayer->m_iExtraSoundTypes = bits_SOUND_DANGER; + m_pPlayer->m_flStopExtraSoundTime = UTIL_WeaponTimeBase() + 0.2; + + m_pPlayer->m_rgAmmo[m_iSecondaryAmmoType]--; + + // player "shoot" animation + m_pPlayer->SetAnimation( PLAYER_ATTACK1 ); + + UTIL_MakeVectors( m_pPlayer->pev->v_angle + m_pPlayer->pev->punchangle ); + + // we don't add in player velocity anymore. + CGrenade::ShootContact( m_pPlayer->pev, + m_pPlayer->pev->origin + m_pPlayer->pev->view_ofs + gpGlobals->v_forward * 16, + gpGlobals->v_forward * 800 ); + + int flags; +#if defined( CLIENT_WEAPONS ) + flags = FEV_NOTHOST; +#else + flags = 0; +#endif + + PLAYBACK_EVENT( flags, m_pPlayer->edict(), m_usMP52 ); + + m_flNextPrimaryAttack = UTIL_WeaponTimeBase() + 1; + m_flNextSecondaryAttack = UTIL_WeaponTimeBase() + 1; + m_flTimeWeaponIdle = UTIL_WeaponTimeBase() + 5;// idle pretty soon after shooting. + + if (!m_pPlayer->m_rgAmmo[m_iSecondaryAmmoType]) + // HEV suit - indicate out of ammo condition + m_pPlayer->SetSuitUpdate("!HEV_AMO0", FALSE, 0); +} + +void CMP5::Reload( void ) +{ + if ( m_pPlayer->ammo_9mm <= 0 ) + return; + + DefaultReload( MP5_MAX_CLIP, MP5_RELOAD, 1.5 ); +} + + +void CMP5::WeaponIdle( void ) +{ + ResetEmptySound( ); + + m_pPlayer->GetAutoaimVector( AUTOAIM_5DEGREES ); + + if ( m_flTimeWeaponIdle > UTIL_WeaponTimeBase() ) + return; + + int iAnim; + switch ( RANDOM_LONG( 0, 1 ) ) + { + case 0: + iAnim = MP5_LONGIDLE; + break; + + default: + case 1: + iAnim = MP5_IDLE1; + break; + } + + SendWeaponAnim( iAnim ); + + m_flTimeWeaponIdle = UTIL_SharedRandomFloat( m_pPlayer->random_seed, 10, 15 ); // how long till we do this again. +} + + + +class CMP5AmmoClip : public CBasePlayerAmmo +{ + void Spawn( void ) + { + Precache( ); + SET_MODEL(ENT(pev), "models/w_9mmARclip.mdl"); + CBasePlayerAmmo::Spawn( ); + } + void Precache( void ) + { + PRECACHE_MODEL ("models/w_9mmARclip.mdl"); + PRECACHE_SOUND("items/9mmclip1.wav"); + } + BOOL AddAmmo( CBaseEntity *pOther ) + { + int bResult = (pOther->GiveAmmo( AMMO_MP5CLIP_GIVE, "9mm", _9MM_MAX_CARRY) != -1); + if (bResult) + { + EMIT_SOUND(ENT(pev), CHAN_ITEM, "items/9mmclip1.wav", 1, ATTN_NORM); + } + return bResult; + } +}; +LINK_ENTITY_TO_CLASS( ammo_mp5clip, CMP5AmmoClip ); +LINK_ENTITY_TO_CLASS( ammo_9mmAR, CMP5AmmoClip ); + + + +class CMP5Chainammo : public CBasePlayerAmmo +{ + void Spawn( void ) + { + Precache( ); + SET_MODEL(ENT(pev), "models/w_chainammo.mdl"); + CBasePlayerAmmo::Spawn( ); + } + void Precache( void ) + { + PRECACHE_MODEL ("models/w_chainammo.mdl"); + PRECACHE_SOUND("items/9mmclip1.wav"); + } + BOOL AddAmmo( CBaseEntity *pOther ) + { + int bResult = (pOther->GiveAmmo( AMMO_CHAINBOX_GIVE, "9mm", _9MM_MAX_CARRY) != -1); + if (bResult) + { + EMIT_SOUND(ENT(pev), CHAN_ITEM, "items/9mmclip1.wav", 1, ATTN_NORM); + } + return bResult; + } +}; +LINK_ENTITY_TO_CLASS( ammo_9mmbox, CMP5Chainammo ); + + +class CMP5AmmoGrenade : public CBasePlayerAmmo +{ + void Spawn( void ) + { + Precache( ); + SET_MODEL(ENT(pev), "models/w_ARgrenade.mdl"); + CBasePlayerAmmo::Spawn( ); + } + void Precache( void ) + { + PRECACHE_MODEL ("models/w_ARgrenade.mdl"); + PRECACHE_SOUND("items/9mmclip1.wav"); + } + BOOL AddAmmo( CBaseEntity *pOther ) + { + int bResult = (pOther->GiveAmmo( AMMO_M203BOX_GIVE, "ARgrenades", M203_GRENADE_MAX_CARRY ) != -1); + + if (bResult) + { + EMIT_SOUND(ENT(pev), CHAN_ITEM, "items/9mmclip1.wav", 1, ATTN_NORM); + } + return bResult; + } +}; +LINK_ENTITY_TO_CLASS( ammo_mp5grenades, CMP5AmmoGrenade ); +LINK_ENTITY_TO_CLASS( ammo_ARgrenades, CMP5AmmoGrenade ); + + + + + + + + + + + + + + + + + + diff --git a/dlls/mpstubb.cpp b/dlls/mpstubb.cpp new file mode 100644 index 00000000..673c51b1 --- /dev/null +++ b/dlls/mpstubb.cpp @@ -0,0 +1,264 @@ +/*** +* +* Copyright (c) 1996-2002, 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. +* +****/ + + +#include "extdll.h" +#include "util.h" +#include "cbase.h" +#include "monsters.h" +#include "soundent.h" +#include "nodes.h" +#include "talkmonster.h" + + +float CTalkMonster::g_talkWaitTime = 0; // time delay until it's ok to speak: used so that two NPCs don't talk at once + +/*********************************************************/ + + +CGraph WorldGraph; +void CGraph :: InitGraph( void ) { } +int CGraph :: FLoadGraph ( char *szMapName ) { return FALSE; } +int CGraph :: AllocNodes ( void ) { return FALSE; } +int CGraph :: CheckNODFile ( char *szMapName ) { return FALSE; } +int CGraph :: FSetGraphPointers ( void ) { return 0; } +void CGraph :: ShowNodeConnections ( int iNode ) { } +int CGraph :: FindNearestNode ( const Vector &vecOrigin, int afNodeTypes ) { return 0; } + + +/*********************************************************/ + + +void CBaseMonster :: ReportAIState( void ) { } +float CBaseMonster :: ChangeYaw ( int speed ) { return 0; } +void CBaseMonster :: MakeIdealYaw( Vector vecTarget ) { } + + +void CBaseMonster::CorpseFallThink( void ) +{ + if ( pev->flags & FL_ONGROUND ) + { + SetThink ( NULL ); + + SetSequenceBox( ); + UTIL_SetOrigin( pev, pev->origin );// link into world. + } + else + pev->nextthink = gpGlobals->time + 0.1; +} +// Call after animation/pose is set up +void CBaseMonster :: MonsterInitDead( void ) +{ + InitBoneControllers(); + + pev->solid = SOLID_BBOX; + pev->movetype = MOVETYPE_TOSS;// so he'll fall to ground + + pev->frame = 0; + ResetSequenceInfo( ); + pev->framerate = 0; + + // Copy health + pev->max_health = pev->health; + pev->deadflag = DEAD_DEAD; + + UTIL_SetSize(pev, g_vecZero, g_vecZero ); + UTIL_SetOrigin( pev, pev->origin ); + + // Setup health counters, etc. + BecomeDead(); + SetThink( CorpseFallThink ); + pev->nextthink = gpGlobals->time + 0.5; +} + + +BOOL CBaseMonster :: ShouldFadeOnDeath( void ) +{ + return FALSE; +} + +BOOL CBaseMonster :: FCheckAITrigger ( void ) +{ + return FALSE; +} + +void CBaseMonster :: KeyValue( KeyValueData *pkvd ) +{ + CBaseToggle::KeyValue( pkvd ); +} + +int CBaseMonster::IRelationship ( CBaseEntity *pTarget ) +{ + static int iEnemy[14][14] = + { // NONE MACH PLYR HPASS HMIL AMIL APASS AMONST APREY APRED INSECT PLRALY PBWPN ABWPN + /*NONE*/ { R_NO ,R_NO ,R_NO ,R_NO ,R_NO ,R_NO ,R_NO ,R_NO ,R_NO ,R_NO ,R_NO ,R_NO, R_NO, R_NO }, + /*MACHINE*/ { R_NO ,R_NO ,R_DL ,R_DL ,R_NO ,R_DL ,R_DL ,R_DL ,R_DL ,R_DL ,R_NO ,R_DL, R_DL, R_DL }, + /*PLAYER*/ { R_NO ,R_DL ,R_NO ,R_NO ,R_DL ,R_DL ,R_DL ,R_DL ,R_DL ,R_DL ,R_NO ,R_NO, R_DL, R_DL }, + /*HUMANPASSIVE*/{ R_NO ,R_NO ,R_AL ,R_AL ,R_HT ,R_FR ,R_NO ,R_HT ,R_DL ,R_FR ,R_NO ,R_AL, R_NO, R_NO }, + /*HUMANMILITAR*/{ R_NO ,R_NO ,R_HT ,R_DL ,R_NO ,R_HT ,R_DL ,R_DL ,R_DL ,R_DL ,R_NO ,R_HT, R_NO, R_NO }, + /*ALIENMILITAR*/{ R_NO ,R_DL ,R_HT ,R_DL ,R_HT ,R_NO ,R_NO ,R_NO ,R_NO ,R_NO ,R_NO ,R_DL, R_NO, R_NO }, + /*ALIENPASSIVE*/{ R_NO ,R_NO ,R_NO ,R_NO ,R_NO ,R_NO ,R_NO ,R_NO ,R_NO ,R_NO ,R_NO ,R_NO, R_NO, R_NO }, + /*ALIENMONSTER*/{ R_NO ,R_DL ,R_DL ,R_DL ,R_DL ,R_NO ,R_NO ,R_NO ,R_NO ,R_NO ,R_NO ,R_DL, R_NO, R_NO }, + /*ALIENPREY */{ R_NO ,R_NO ,R_DL ,R_DL ,R_DL ,R_NO ,R_NO ,R_NO ,R_NO ,R_FR ,R_NO ,R_DL, R_NO, R_NO }, + /*ALIENPREDATO*/{ R_NO ,R_NO ,R_DL ,R_DL ,R_DL ,R_NO ,R_NO ,R_NO ,R_HT ,R_DL ,R_NO ,R_DL, R_NO, R_NO }, + /*INSECT*/ { R_FR ,R_FR ,R_FR ,R_FR ,R_FR ,R_NO ,R_FR ,R_FR ,R_FR ,R_FR ,R_NO ,R_FR, R_NO, R_NO }, + /*PLAYERALLY*/ { R_NO ,R_DL ,R_AL ,R_AL ,R_DL ,R_DL ,R_DL ,R_DL ,R_DL ,R_DL ,R_NO ,R_NO, R_NO, R_NO }, + /*PBIOWEAPON*/ { R_NO ,R_NO ,R_DL ,R_DL ,R_DL ,R_DL ,R_DL ,R_DL ,R_DL ,R_DL ,R_NO ,R_DL, R_NO, R_DL }, + /*ABIOWEAPON*/ { R_NO ,R_NO ,R_DL ,R_DL ,R_DL ,R_AL ,R_NO ,R_DL ,R_DL ,R_NO ,R_NO ,R_DL, R_DL, R_NO } + }; + + return iEnemy[ Classify() ][ pTarget->Classify() ]; +} + + +//========================================================= +// Look - Base class monster function to find enemies or +// food by sight. iDistance is distance ( in units ) that the +// monster can see. +// +// Sets the sight bits of the m_afConditions mask to indicate +// which types of entities were sighted. +// Function also sets the Looker's m_pLink +// to the head of a link list that contains all visible ents. +// (linked via each ent's m_pLink field) +// +//========================================================= +void CBaseMonster :: Look ( int iDistance ) +{ + int iSighted = 0; + + // DON'T let visibility information from last frame sit around! + ClearConditions(bits_COND_SEE_HATE | bits_COND_SEE_DISLIKE | bits_COND_SEE_ENEMY | bits_COND_SEE_FEAR | bits_COND_SEE_NEMESIS | bits_COND_SEE_CLIENT); + + m_pLink = NULL; + + CBaseEntity *pSightEnt = NULL;// the current visible entity that we're dealing with + + CBaseEntity *pList[100]; + + Vector delta = Vector( iDistance, iDistance, iDistance ); + + // Find only monsters/clients in box, NOT limited to PVS + int count = UTIL_EntitiesInBox( pList, 100, pev->origin - delta, pev->origin + delta, FL_CLIENT|FL_MONSTER ); + for ( int i = 0; i < count; i++ ) + { + pSightEnt = pList[i]; + if ( pSightEnt != this && pSightEnt->pev->health > 0 ) + { + // the looker will want to consider this entity + // don't check anything else about an entity that can't be seen, or an entity that you don't care about. + if ( IRelationship( pSightEnt ) != R_NO && FInViewCone( pSightEnt ) && !FBitSet( pSightEnt->pev->flags, FL_NOTARGET ) && FVisible( pSightEnt ) ) + { + if ( pSightEnt->IsPlayer() ) + { + // if we see a client, remember that (mostly for scripted AI) + iSighted |= bits_COND_SEE_CLIENT; + } + + pSightEnt->m_pLink = m_pLink; + m_pLink = pSightEnt; + + if ( pSightEnt == m_hEnemy ) + { + // we know this ent is visible, so if it also happens to be our enemy, store that now. + iSighted |= bits_COND_SEE_ENEMY; + } + + // don't add the Enemy's relationship to the conditions. We only want to worry about conditions when + // we see monsters other than the Enemy. + switch ( IRelationship ( pSightEnt ) ) + { + case R_NM: + iSighted |= bits_COND_SEE_NEMESIS; + break; + case R_HT: + iSighted |= bits_COND_SEE_HATE; + break; + case R_DL: + iSighted |= bits_COND_SEE_DISLIKE; + break; + case R_FR: + iSighted |= bits_COND_SEE_FEAR; + break; + case R_AL: + break; + default: + ALERT ( at_aiconsole, "%s can't assess %s\n", STRING(pev->classname), STRING(pSightEnt->pev->classname ) ); + break; + } + } + } + } + + SetConditions( iSighted ); +} + + +//========================================================= +// BestVisibleEnemy - this functions searches the link +// list whose head is the caller's m_pLink field, and returns +// a pointer to the enemy entity in that list that is nearest the +// caller. +// +// !!!UNDONE - currently, this only returns the closest enemy. +// we'll want to consider distance, relationship, attack types, back turned, etc. +//========================================================= +CBaseEntity *CBaseMonster :: BestVisibleEnemy ( void ) +{ + CBaseEntity *pReturn; + CBaseEntity *pNextEnt; + int iNearest; + int iDist; + int iBestRelationship; + + iNearest = 8192;// so first visible entity will become the closest. + pNextEnt = m_pLink; + pReturn = NULL; + iBestRelationship = R_NO; + + while ( pNextEnt != NULL ) + { + if ( pNextEnt->IsAlive() ) + { + if ( IRelationship( pNextEnt) > iBestRelationship ) + { + // this entity is disliked MORE than the entity that we + // currently think is the best visible enemy. No need to do + // a distance check, just get mad at this one for now. + iBestRelationship = IRelationship ( pNextEnt ); + iNearest = ( pNextEnt->pev->origin - pev->origin ).Length(); + pReturn = pNextEnt; + } + else if ( IRelationship( pNextEnt) == iBestRelationship ) + { + // this entity is disliked just as much as the entity that + // we currently think is the best visible enemy, so we only + // get mad at it if it is closer. + iDist = ( pNextEnt->pev->origin - pev->origin ).Length(); + + if ( iDist <= iNearest ) + { + iNearest = iDist; + iBestRelationship = IRelationship ( pNextEnt ); + pReturn = pNextEnt; + } + } + } + + pNextEnt = pNextEnt->m_pLink; + } + + return pReturn; +} diff --git a/spirit/multiplay_gamerules.cpp b/dlls/multiplay_gamerules.cpp similarity index 89% rename from spirit/multiplay_gamerules.cpp rename to dlls/multiplay_gamerules.cpp index c2e09ba9..17ae207e 100644 --- a/spirit/multiplay_gamerules.cpp +++ b/dlls/multiplay_gamerules.cpp @@ -15,8 +15,6 @@ // // teamplay_gamerules.cpp // -#include - #include "extdll.h" #include "util.h" #include "cbase.h" @@ -27,6 +25,7 @@ #include "skill.h" #include "game.h" #include "items.h" +#include "hltv.h" extern DLL_GLOBAL CGameRules *g_pGameRules; extern DLL_GLOBAL BOOL g_fGameOver; @@ -71,7 +70,7 @@ CHalfLifeMultiplay :: CHalfLifeMultiplay() { char szCommand[256]; - ALERT( at_debug, "Executing dedicated server config file\n" ); + ALERT( at_console, "Executing dedicated server config file\n" ); sprintf( szCommand, "exec %s\n", servercfgfile ); SERVER_COMMAND( szCommand ); } @@ -85,7 +84,7 @@ CHalfLifeMultiplay :: CHalfLifeMultiplay() { char szCommand[256]; - ALERT( at_debug, "Executing listen server config file\n" ); + ALERT( at_console, "Executing listen server config file\n" ); sprintf( szCommand, "exec %s\n", lservercfgfile ); SERVER_COMMAND( szCommand ); } @@ -234,13 +233,13 @@ void CHalfLifeMultiplay :: Think ( void ) // Updates when frags change if ( frags_remaining != last_frags ) { - CVAR_SET_STRING( "mp_fragsleft", UTIL_VarArgs( "%i", frags_remaining ) ); + g_engfuncs.pfnCvar_DirectSet( fragsleft, UTIL_VarArgs( "%i", frags_remaining ) ); } // Updates once per second if ( timeleft->value != last_time ) { - CVAR_SET_STRING( "mp_timeleft", UTIL_VarArgs( "%i", time_remaining ) ); + g_engfuncs.pfnCvar_DirectSet( timeleft, UTIL_VarArgs( "%i", time_remaining ) ); } last_frags = frags_remaining; @@ -368,7 +367,7 @@ BOOL CHalfLifeMultiplay :: GetNextBestWeapon( CBasePlayer *pPlayer, CBasePlayerI //========================================================= //========================================================= -BOOL CHalfLifeMultiplay :: ClientConnected( edict_t *pEntity, const char *pszName, const char *pszAddress, char szRejectReason[128] ) +BOOL CHalfLifeMultiplay :: ClientConnected( edict_t *pEntity, const char *pszName, const char *pszAddress, char szRejectReason[ 128 ] ) { return TRUE; } @@ -441,7 +440,7 @@ void CHalfLifeMultiplay :: InitHUD( CBasePlayer *pl ) if ( g_fGameOver ) { - MESSAGE_BEGIN( MSG_ONE, gmsgIntermission, NULL, pl->edict() ); + MESSAGE_BEGIN( MSG_ONE, SVC_INTERMISSION, NULL, pl->edict() ); MESSAGE_END(); } } @@ -458,7 +457,6 @@ void CHalfLifeMultiplay :: ClientDisconnected( edict_t *pClient ) { FireTargets( "game_playerleave", pPlayer, pPlayer, USE_TOGGLE, 0 ); - // team match? if ( g_teamplay ) { @@ -486,7 +484,7 @@ void CHalfLifeMultiplay :: ClientDisconnected( edict_t *pClient ) //========================================================= float CHalfLifeMultiplay :: FlPlayerFallDamage( CBasePlayer *pPlayer ) { - int iFallDamage = falldamage->integer; + int iFallDamage = (int)falldamage->value; switch ( iFallDamage ) { @@ -531,8 +529,8 @@ void CHalfLifeMultiplay :: PlayerSpawn( CBasePlayer *pPlayer ) { BOOL addDefault; CBaseEntity *pWeaponEntity = NULL; - - pPlayer->pev->weapons |= ITEM_SUIT; + + pPlayer->pev->weapons |= (1<frags -= 1; this should be victim (we don't want to give the world frags) - pVictim->pev->frags -= 1; -//END + pKiller->frags -= 1; } // update the scores @@ -789,6 +785,17 @@ void CHalfLifeMultiplay::DeathNotice( CBasePlayer *pVictim, entvars_t *pKiller, } } + MESSAGE_BEGIN( MSG_SPEC, SVC_DIRECTOR ); + WRITE_BYTE ( 9 ); // command length in bytes + WRITE_BYTE ( DRC_CMD_EVENT ); // player killed + WRITE_SHORT( ENTINDEX(pVictim->edict()) ); // index number of primary entity + if (pevInflictor) + WRITE_SHORT( ENTINDEX(ENT(pevInflictor)) ); // index number of secondary entity + else + WRITE_SHORT( ENTINDEX(ENT(pKiller)) ); // index number of secondary entity + WRITE_LONG( 7 | DRC_FLAG_DRAMATIC); // eventflags (priority and flags) + MESSAGE_END(); + // Print a standard message // TODO: make this go direct to console return; // just remove for now @@ -878,7 +885,7 @@ float CHalfLifeMultiplay :: FlWeaponTryRespawn( CBasePlayerItem *pWeapon ) { if ( pWeapon && pWeapon->m_iId && (pWeapon->iFlags() & ITEM_FLAG_LIMITINWORLD) ) { - if ( gpGlobals->numEntities < (gpGlobals->maxEntities - ENTITY_INTOLERANCE) ) + if ( NUMBER_OF_ENTITIES() < (gpGlobals->maxEntities - ENTITY_INTOLERANCE) ) return 0; // we're past the entity tolerance level, so delay the respawn @@ -1087,14 +1094,14 @@ BOOL CHalfLifeMultiplay :: PlayFootstepSounds( CBasePlayer *pl, float fvol ) BOOL CHalfLifeMultiplay :: FAllowFlashlight( void ) { - return flashlight->integer != 0; + return flashlight->value != 0; } //========================================================= //========================================================= BOOL CHalfLifeMultiplay :: FAllowMonsters( void ) { - return ( allowmonsters->integer != 0 ); + return ( allowmonsters->value != 0 ); } //========================================================= @@ -1106,7 +1113,7 @@ void CHalfLifeMultiplay :: GoToIntermission( void ) if ( g_fGameOver ) return; // intermission has already been triggered, so ignore. - MESSAGE_BEGIN(MSG_ALL, gmsgIntermission); + MESSAGE_BEGIN(MSG_ALL, SVC_INTERMISSION); MESSAGE_END(); // bounds check @@ -1116,7 +1123,7 @@ void CHalfLifeMultiplay :: GoToIntermission( void ) else if ( time > MAX_INTERMISSION_TIME ) CVAR_SET_STRING( "mp_chattime", UTIL_dtos1( MAX_INTERMISSION_TIME ) ); - m_flIntermissionEndTime = gpGlobals->time + ( mp_chattime->integer ); + m_flIntermissionEndTime = gpGlobals->time + ( (int)mp_chattime->value ); g_flIntermissionStartTime = gpGlobals->time; g_fGameOver = TRUE; @@ -1168,6 +1175,85 @@ void DestroyMapCycle( mapcycle_t *cycle ) cycle->next_item = NULL; } +static char com_token[ 1500 ]; + +/* +============== +COM_Parse + +Parse a token out of a string +============== +*/ +char *COM_Parse (char *data) +{ + int c; + int len; + + len = 0; + com_token[0] = 0; + + if (!data) + return NULL; + +// skip whitespace +skipwhite: + while ( (c = *data) <= ' ') + { + if (c == 0) + return NULL; // end of file; + data++; + } + +// skip // comments + if (c=='/' && data[1] == '/') + { + while (*data && *data != '\n') + data++; + goto skipwhite; + } + + +// handle quoted strings specially + if (c == '\"') + { + data++; + while (1) + { + c = *data++; + if (c=='\"' || !c) + { + com_token[len] = 0; + return data; + } + com_token[len] = c; + len++; + } + } + +// parse single characters + if (c=='{' || c=='}'|| c==')'|| c=='(' || c=='\'' || c == ',' ) + { + com_token[len] = c; + len++; + com_token[len] = 0; + return data+1; + } + +// parse a regular word + do + { + com_token[len] = c; + data++; + len++; + c = *data; + if (c=='{' || c=='}'|| c==')'|| c=='(' || c=='\'' || c == ',' ) + break; + } while (c>32); + + com_token[len] = 0; + return data; +} + /* ============== COM_TokenWaiting @@ -1175,9 +1261,9 @@ COM_TokenWaiting Returns 1 if additional data is waiting to be processed on this line ============== */ -int COM_TokenWaiting( const char *buffer ) +int COM_TokenWaiting( char *buffer ) { - const char *p; + char *p; p = buffer; while ( *p && *p!='\n') @@ -1191,6 +1277,8 @@ int COM_TokenWaiting( const char *buffer ) return 0; } + + /* ============== ReloadMapCycleFile @@ -1204,9 +1292,8 @@ int ReloadMapCycleFile( char *filename, mapcycle_t *cycle ) char szBuffer[ MAX_RULE_BUFFER ]; char szMap[ 32 ]; int length; - char *pToken; - char *aFileList = (char *)LOAD_FILE_FOR_ME( filename, &length ); - const char *pFileList = aFileList; + char *pFileList; + char *aFileList = pFileList = (char*)LOAD_FILE_FOR_ME( filename, &length ); int hasbuffer; mapcycle_item_s *item, *newlist = NULL, *next; @@ -1218,22 +1305,20 @@ int ReloadMapCycleFile( char *filename, mapcycle_t *cycle ) hasbuffer = 0; memset( szBuffer, 0, MAX_RULE_BUFFER ); - pToken = COM_Parse( &pFileList ); - if ( !pToken ) break; - - if ( strlen( pToken ) <= 0 ) + pFileList = COM_Parse( pFileList ); + if ( strlen( com_token ) <= 0 ) break; - strcpy( szMap, pToken ); + strcpy( szMap, com_token ); // Any more tokens on this line? if ( COM_TokenWaiting( pFileList ) ) { - pToken = COM_Parse( &pFileList ); - if ( strlen( pToken ) > 0 ) + pFileList = COM_Parse( pFileList ); + if ( strlen( com_token ) > 0 ) { hasbuffer = 1; - strcpy( szBuffer, pToken ); + strcpy( szBuffer, com_token ); } } diff --git a/spirit/nihilanth.cpp b/dlls/nihilanth.cpp similarity index 91% rename from spirit/nihilanth.cpp rename to dlls/nihilanth.cpp index 132454e4..061c9a53 100644 --- a/spirit/nihilanth.cpp +++ b/dlls/nihilanth.cpp @@ -1,6 +1,6 @@ /*** * -* Copyright (c) 1999, 2000 Valve LLC. All rights reserved. +* Copyright (c) 1996-2002, Valve LLC. All rights reserved. * * This product contains software technology licensed from Id * Software, Inc. ("Id Technology"). Id Technology (c) 1996 Id Software, Inc. @@ -283,18 +283,14 @@ void CNihilanth :: Spawn( void ) pev->movetype = MOVETYPE_FLY; pev->solid = SOLID_BBOX; - if (pev->model) - SET_MODEL(ENT(pev), STRING(pev->model)); //LRC - else - SET_MODEL(edict(), "models/nihilanth.mdl"); + SET_MODEL(edict(), "models/nihilanth.mdl"); // UTIL_SetSize(pev, Vector( -300, -300, 0), Vector(300, 300, 512)); UTIL_SetSize(pev, Vector( -32, -32, 0), Vector(32, 32, 64)); - UTIL_SetOrigin( this, pev->origin ); + UTIL_SetOrigin( pev, pev->origin ); - pev->flags |= FL_MONSTER | FL_FLY; + pev->flags |= FL_MONSTER; pev->takedamage = DAMAGE_AIM; - if (pev->health == 0) - pev->health = gSkillData.nihilanthHealth; + pev->health = gSkillData.nihilanthHealth; pev->view_ofs = Vector( 0, 0, 300 ); m_flFieldOfView = -1; // 360 degrees @@ -304,8 +300,8 @@ void CNihilanth :: Spawn( void ) InitBoneControllers(); - SetThink(&CNihilanth :: StartupThink ); - SetNextThink( 0.1 ); + SetThink( StartupThink ); + pev->nextthink = gpGlobals->time + 0.1; m_vecDesired = Vector( 1, 0, 0 ); m_posDesired = Vector( pev->origin.x, pev->origin.y, 512 ); @@ -332,10 +328,7 @@ void CNihilanth :: Spawn( void ) void CNihilanth::Precache( void ) { - if (pev->model) - PRECACHE_MODEL((char*)STRING(pev->model)); //LRC - else - PRECACHE_MODEL("models/nihilanth.mdl"); + PRECACHE_MODEL("models/nihilanth.mdl"); PRECACHE_MODEL("sprites/lgtning.spr"); UTIL_PrecacheOther( "nihilanth_energy_ball" ); UTIL_PrecacheOther( "monster_alien_controller" ); @@ -379,15 +372,15 @@ void CNihilanth :: DeathSound( void ) void CNihilanth::NullThink( void ) { StudioFrameAdvance( ); - SetNextThink( 0.5 ); + pev->nextthink = gpGlobals->time + 0.5; } void CNihilanth::StartupUse( CBaseEntity *pActivator, CBaseEntity *pCaller, USE_TYPE useType, float value ) { - SetThink(&CNihilanth:: HuntThink ); - SetNextThink( 0.1 ); - SetUse(&CNihilanth:: CommandUse ); + SetThink( HuntThink ); + pev->nextthink = gpGlobals->time + 0.1; + SetUse( CommandUse ); } @@ -417,9 +410,9 @@ void CNihilanth::StartupThink( void ) } m_hRecharger = NULL; - SetThink(&CNihilanth:: HuntThink); - SetUse(&CNihilanth:: CommandUse ); - SetNextThink( 0.1 ); + SetThink( HuntThink); + SetUse( CommandUse ); + pev->nextthink = gpGlobals->time + 0.1; } @@ -430,7 +423,7 @@ void CNihilanth :: Killed( entvars_t *pevAttacker, int iGib ) void CNihilanth :: DyingThink( void ) { - SetNextThink( 0.1 ); + pev->nextthink = gpGlobals->time + 0.1; DispatchAnimEvents( ); StudioFrameAdvance( ); @@ -517,7 +510,7 @@ void CNihilanth :: DyingThink( void ) UTIL_TraceLine( vecSrc, vecSrc + vecDir * 4096, ignore_monsters, ENT(pev), &tr ); - MESSAGE_BEGIN( MSG_BROADCAST, gmsgTempEntity ); + MESSAGE_BEGIN( MSG_BROADCAST, SVC_TEMPENTITY ); WRITE_BYTE( TE_BEAMENTPOINT ); WRITE_SHORT( entindex() + 0x1000 * iAttachment ); WRITE_COORD( tr.vecEndPos.x); @@ -552,7 +545,7 @@ void CNihilanth::CrashTouch( CBaseEntity *pOther ) if ( pOther->pev->solid == SOLID_BSP) { SetTouch( NULL ); - SetNextThink( 0 ); + pev->nextthink = gpGlobals->time; } } @@ -702,7 +695,7 @@ void CNihilanth :: NextActivity( ) if (m_pBall) { - MESSAGE_BEGIN( MSG_BROADCAST, gmsgTempEntity ); + MESSAGE_BEGIN( MSG_BROADCAST, SVC_TEMPENTITY ); WRITE_BYTE( TE_ELIGHT ); WRITE_SHORT( entindex( ) + 0x1000 ); // entity, attachment WRITE_COORD( pev->origin.x ); // origin @@ -773,7 +766,7 @@ void CNihilanth :: NextActivity( ) sprintf( szText, "%s%d", m_szDrawUse, m_iLevel ); FireTargets( szText, this, this, USE_ON, 1.0 ); - ALERT( at_debug, "fireing %s\n", szText ); + ALERT( at_console, "fireing %s\n", szText ); } pev->sequence = LookupSequence( "recharge" ); } @@ -844,7 +837,7 @@ void CNihilanth :: NextActivity( ) void CNihilanth :: HuntThink( void ) { - SetNextThink( 0.1 ); + pev->nextthink = gpGlobals->time + 0.1; DispatchAnimEvents( ); StudioFrameAdvance( ); @@ -853,7 +846,7 @@ void CNihilanth :: HuntThink( void ) // if dead, force cancelation of current animation if (pev->health <= 0) { - SetThink(&CNihilanth :: DyingThink ); + SetThink( DyingThink ); m_fSequenceFinished = TRUE; return; } @@ -969,7 +962,7 @@ void CNihilanth :: Flight( void ) m_flForce -= 10; } - UTIL_SetOrigin( this, pev->origin + m_velocity * 0.1 ); + UTIL_SetOrigin( pev, pev->origin + m_velocity * 0.1 ); pev->angles = pev->angles + m_avelocity * 0.1; // ALERT( at_console, "%5.0f %5.0f : %4.0f : %3.0f : %2.0f\n", m_posDesired.z, pev->origin.z, m_velocity.z, m_avelocity.y, m_flForce ); @@ -1023,12 +1016,10 @@ BOOL CNihilanth :: EmitSphere( void ) } -//LRC- never called(?) No. --PC. void CNihilanth :: TargetSphere( USE_TYPE useType, float value ) { CBaseMonster *pSphere; - int i = 0; - for (i = 0; i < N_SPHERES; i++) + for (int i = 0; i < N_SPHERES; i++) { if (m_hSphere[i] != NULL) { @@ -1044,7 +1035,7 @@ void CNihilanth :: TargetSphere( USE_TYPE useType, float value ) Vector vecSrc, vecAngles; GetAttachment( 2, vecSrc, vecAngles ); - UTIL_SetOrigin( pSphere, vecSrc ); + UTIL_SetOrigin( pSphere->pev, vecSrc ); pSphere->Use( this, this, useType, value ); pSphere->pev->velocity = m_vecDesired * RANDOM_FLOAT( 50, 100 ) + Vector( RANDOM_FLOAT( -50, 50 ), RANDOM_FLOAT( -50, 50 ), RANDOM_FLOAT( -50, 50 ) ); } @@ -1065,7 +1056,7 @@ void CNihilanth :: HandleAnimEvent( MonsterEvent_t *pEvent ) EMIT_SOUND( edict(), CHAN_WEAPON, RANDOM_SOUND_ARRAY( pBallSounds ), 1.0, 0.2 ); - MESSAGE_BEGIN( MSG_BROADCAST, gmsgTempEntity ); + MESSAGE_BEGIN( MSG_BROADCAST, SVC_TEMPENTITY ); WRITE_BYTE( TE_ELIGHT ); WRITE_SHORT( entindex( ) + 0x3000 ); // entity, attachment WRITE_COORD( pev->origin.x ); // origin @@ -1079,7 +1070,7 @@ void CNihilanth :: HandleAnimEvent( MonsterEvent_t *pEvent ) WRITE_COORD( 128 ); // decay MESSAGE_END(); - MESSAGE_BEGIN( MSG_BROADCAST, gmsgTempEntity ); + MESSAGE_BEGIN( MSG_BROADCAST, SVC_TEMPENTITY ); WRITE_BYTE( TE_ELIGHT ); WRITE_SHORT( entindex( ) + 0x4000 ); // entity, attachment WRITE_COORD( pev->origin.x ); // origin @@ -1126,7 +1117,7 @@ void CNihilanth :: HandleAnimEvent( MonsterEvent_t *pEvent ) ALERT( at_aiconsole, "nihilanth can't target %s\n", szText ); - MESSAGE_BEGIN( MSG_BROADCAST, gmsgTempEntity ); + MESSAGE_BEGIN( MSG_BROADCAST, SVC_TEMPENTITY ); WRITE_BYTE( TE_ELIGHT ); WRITE_SHORT( entindex( ) + 0x3000 ); // entity, attachment WRITE_COORD( pev->origin.x ); // origin @@ -1140,7 +1131,7 @@ void CNihilanth :: HandleAnimEvent( MonsterEvent_t *pEvent ) WRITE_COORD( 128 ); // decay MESSAGE_END(); - MESSAGE_BEGIN( MSG_BROADCAST, gmsgTempEntity ); + MESSAGE_BEGIN( MSG_BROADCAST, SVC_TEMPENTITY ); WRITE_BYTE( TE_ELIGHT ); WRITE_SHORT( entindex( ) + 0x4000 ); // entity, attachment WRITE_COORD( pev->origin.x ); // origin @@ -1337,11 +1328,11 @@ void CNihilanthHVR :: CircleInit( CBaseEntity *pTarget ) pev->renderamt = 255; UTIL_SetSize(pev, Vector( 0, 0, 0), Vector(0, 0, 0)); - UTIL_SetOrigin( this, pev->origin ); + UTIL_SetOrigin( pev, pev->origin ); - SetThink(&CNihilanthHVR :: HoverThink ); - SetTouch(&CNihilanthHVR :: BounceTouch ); - SetNextThink( 0.1 ); + SetThink( HoverThink ); + SetTouch( BounceTouch ); + pev->nextthink = gpGlobals->time + 0.1; m_hTargetEnt = pTarget; } @@ -1364,7 +1355,7 @@ CBaseEntity *CNihilanthHVR::RandomClassname( const char *szName ) void CNihilanthHVR :: HoverThink( void ) { - SetNextThink( 0.1 ); + pev->nextthink = gpGlobals->time + 0.1; if (m_hTargetEnt != NULL) { @@ -1383,7 +1374,7 @@ void CNihilanthHVR :: HoverThink( void ) if (pOther && pOther != this) { - MESSAGE_BEGIN( MSG_BROADCAST, gmsgTempEntity ); + MESSAGE_BEGIN( MSG_BROADCAST, SVC_TEMPENTITY ); WRITE_BYTE( TE_BEAMENTS ); WRITE_SHORT( this->entindex() ); WRITE_SHORT( pOther->entindex() ); @@ -1402,7 +1393,7 @@ void CNihilanthHVR :: HoverThink( void ) } */ /* - MESSAGE_BEGIN( MSG_BROADCAST, gmsgTempEntity ); + MESSAGE_BEGIN( MSG_BROADCAST, SVC_TEMPENTITY ); WRITE_BYTE( TE_BEAMENTS ); WRITE_SHORT( this->entindex() ); WRITE_SHORT( m_hTargetEnt->entindex() + 0x1000 ); @@ -1442,16 +1433,16 @@ void CNihilanthHVR :: ZapInit( CBaseEntity *pEnemy ) pev->velocity = (pEnemy->pev->origin - pev->origin).Normalize() * 200; m_hEnemy = pEnemy; - SetThink(&CNihilanthHVR :: ZapThink ); - SetTouch(&CNihilanthHVR :: ZapTouch ); - SetNextThink( 0.1 ); + SetThink( ZapThink ); + SetTouch( ZapTouch ); + pev->nextthink = gpGlobals->time + 0.1; EMIT_SOUND_DYN( edict(), CHAN_WEAPON, "debris/zap4.wav", 1, ATTN_NORM, 0, 100 ); } void CNihilanthHVR :: ZapThink( void ) { - SetNextThink( 0.05 ); + pev->nextthink = gpGlobals->time + 0.05; // check world boundaries if (m_hEnemy == NULL || pev->origin.x < -4096 || pev->origin.x > 4096 || pev->origin.y < -4096 || pev->origin.y > 4096 || pev->origin.z < -4096 || pev->origin.z > 4096) @@ -1483,7 +1474,7 @@ void CNihilanthHVR :: ZapThink( void ) ApplyMultiDamage( pev, pev ); } - MESSAGE_BEGIN( MSG_BROADCAST, gmsgTempEntity ); + MESSAGE_BEGIN( MSG_BROADCAST, SVC_TEMPENTITY ); WRITE_BYTE( TE_BEAMENTPOINT ); WRITE_SHORT( entindex() ); WRITE_COORD( tr.vecEndPos.x ); @@ -1506,13 +1497,13 @@ void CNihilanthHVR :: ZapThink( void ) SetTouch( NULL ); UTIL_Remove( this ); - SetNextThink( 0.2 ); + pev->nextthink = gpGlobals->time + 0.2; return; } pev->frame = (int)(pev->frame + 1) % 11; - MESSAGE_BEGIN( MSG_BROADCAST, gmsgTempEntity ); + MESSAGE_BEGIN( MSG_BROADCAST, SVC_TEMPENTITY ); WRITE_BYTE( TE_ELIGHT ); WRITE_SHORT( entindex( ) ); // entity, attachment WRITE_COORD( pev->origin.x ); // origin @@ -1546,7 +1537,7 @@ void CNihilanthHVR::ZapTouch( CBaseEntity *pOther ) SetTouch( NULL ); UTIL_Remove( this ); - SetNextThink( 0.2 ); + pev->nextthink = gpGlobals->time + 0.2; } @@ -1568,9 +1559,9 @@ void CNihilanthHVR :: TeleportInit( CNihilanth *pOwner, CBaseEntity *pEnemy, CBa m_hTargetEnt = pTarget; m_hTouch = pTouch; - SetThink(&CNihilanthHVR :: TeleportThink ); - SetTouch(&CNihilanthHVR :: TeleportTouch ); - SetNextThink( 0.1 ); + SetThink( TeleportThink ); + SetTouch( TeleportTouch ); + pev->nextthink = gpGlobals->time + 0.1; EMIT_SOUND_DYN( edict(), CHAN_WEAPON, "x/x_teleattack1.wav", 1, 0.2, 0, 100 ); } @@ -1588,13 +1579,13 @@ void CNihilanthHVR :: GreenBallInit( ) SET_MODEL(edict(), "sprites/exit1.spr"); - SetTouch(&CNihilanthHVR :: RemoveTouch ); + SetTouch( RemoveTouch ); } void CNihilanthHVR :: TeleportThink( void ) { - SetNextThink( 0.1 ); + pev->nextthink = gpGlobals->time + 0.1; // check world boundaries if (m_hEnemy == NULL || !m_hEnemy->IsAlive() || pev->origin.x < -4096 || pev->origin.x > 4096 || pev->origin.y < -4096 || pev->origin.y > 4096 || pev->origin.z < -4096 || pev->origin.z > 4096) @@ -1620,7 +1611,7 @@ void CNihilanthHVR :: TeleportThink( void ) MovetoTarget( m_hEnemy->Center( ) ); } - MESSAGE_BEGIN( MSG_BROADCAST, gmsgTempEntity ); + MESSAGE_BEGIN( MSG_BROADCAST, SVC_TEMPENTITY ); WRITE_BYTE( TE_ELIGHT ); WRITE_SHORT( entindex( ) ); // entity, attachment WRITE_COORD( pev->origin.x ); // origin @@ -1640,10 +1631,10 @@ void CNihilanthHVR :: TeleportThink( void ) void CNihilanthHVR :: AbsorbInit( void ) { - SetThink(&CNihilanthHVR :: DissipateThink ); + SetThink( DissipateThink ); pev->renderamt = 255; - MESSAGE_BEGIN( MSG_BROADCAST, gmsgTempEntity ); + MESSAGE_BEGIN( MSG_BROADCAST, SVC_TEMPENTITY ); WRITE_BYTE( TE_BEAMENTS ); WRITE_SHORT( this->entindex() ); WRITE_SHORT( m_hTargetEnt->entindex() + 0x1000 ); @@ -1686,7 +1677,7 @@ void CNihilanthHVR::TeleportTouch( CBaseEntity *pOther ) void CNihilanthHVR :: DissipateThink( void ) { - SetNextThink( 0.1 ); + pev->nextthink = gpGlobals->time + 0.1; if (pev->scale > 5.0) UTIL_Remove( this ); @@ -1703,7 +1694,7 @@ void CNihilanthHVR :: DissipateThink( void ) UTIL_Remove( this ); } - MESSAGE_BEGIN( MSG_BROADCAST, gmsgTempEntity ); + MESSAGE_BEGIN( MSG_BROADCAST, SVC_TEMPENTITY ); WRITE_BYTE( TE_ELIGHT ); WRITE_SHORT( entindex( ) ); // entity, attachment WRITE_COORD( pev->origin.x ); // origin @@ -1800,7 +1791,7 @@ void CNihilanthHVR :: Crawl( void ) Vector vecAim = Vector( RANDOM_FLOAT( -1, 1 ), RANDOM_FLOAT( -1, 1 ), RANDOM_FLOAT( -1, 1 ) ).Normalize( ); Vector vecPnt = pev->origin + pev->velocity * 0.2 + vecAim * 128; - MESSAGE_BEGIN( MSG_BROADCAST, gmsgTempEntity ); + MESSAGE_BEGIN( MSG_BROADCAST, SVC_TEMPENTITY ); WRITE_BYTE( TE_BEAMENTPOINT ); WRITE_SHORT( entindex() ); WRITE_COORD( vecPnt.x); diff --git a/spirit/nodes.cpp b/dlls/nodes.cpp similarity index 94% rename from spirit/nodes.cpp rename to dlls/nodes.cpp index 3e700530..6b01750e 100644 --- a/spirit/nodes.cpp +++ b/dlls/nodes.cpp @@ -1,6 +1,6 @@ /*** * -* Copyright (c) 1999, 2000 Valve LLC. All rights reserved. +* Copyright (c) 1996-2002, Valve LLC. All rights reserved. * * This product contains software technology licensed from Id * Software, Inc. ("Id Technology"). Id Technology (c) 1996 Id Software, Inc. @@ -16,9 +16,6 @@ // nodes.cpp - AI node tree stuff. //========================================================= -#include -#include - #include "extdll.h" #include "util.h" #include "cbase.h" @@ -142,8 +139,8 @@ int CGraph :: AllocNodes ( void ) //========================================================= entvars_t* CGraph :: LinkEntForLink ( CLink *pLink, CNode *pNode ) { - CBaseEntity *pSearch; - CBaseEntity *pTrigger; + edict_t *pentSearch; + edict_t *pentTrigger; entvars_t *pevTrigger; entvars_t *pevLinkEnt; TraceResult tr; @@ -152,7 +149,7 @@ entvars_t* CGraph :: LinkEntForLink ( CLink *pLink, CNode *pNode ) if ( !pevLinkEnt ) return NULL; - pSearch = NULL;// start search at the top of the ent list. + pentSearch = NULL;// start search at the top of the ent list. if ( FClassnameIs ( pevLinkEnt, "func_door" ) || FClassnameIs ( pevLinkEnt, "func_door_rotating" ) ) { @@ -167,9 +164,9 @@ entvars_t* CGraph :: LinkEntForLink ( CLink *pLink, CNode *pNode ) while ( 1 ) { - pTrigger = UTIL_FindEntityByTarget ( pSearch, STRING( pevLinkEnt->targetname ) );// find the button or trigger + pentTrigger = FIND_ENTITY_BY_TARGET ( pentSearch, STRING( pevLinkEnt->targetname ) );// find the button or trigger - if ( !pTrigger ) + if ( FNullEnt( pentTrigger ) ) {// no trigger found // right now this is a problem among auto-open doors, or any door that opens through the use @@ -178,8 +175,8 @@ entvars_t* CGraph :: LinkEntForLink ( CLink *pLink, CNode *pNode ) return pevLinkEnt; } - pSearch = pTrigger; - pevTrigger = pTrigger->pev; + pentSearch = pentTrigger; + pevTrigger = VARS( pentTrigger ); if ( FClassnameIs(pevTrigger, "func_button") || FClassnameIs(pevTrigger, "func_rot_button" ) ) {// only buttons are handled right now. @@ -416,7 +413,7 @@ int CGraph :: FindNearestLink ( const Vector &vecTestPoint, int *piNearestLink, /* if ( fSuccess ) { - WRITE_BYTE(MSG_BROADCAST, gmsgTempEntity); + WRITE_BYTE(MSG_BROADCAST, SVC_TEMPENTITY); WRITE_BYTE(MSG_BROADCAST, TE_SHOWLINE); WRITE_COORD(MSG_BROADCAST, m_pNodes[ m_pLinkPool[ iNearestLink ].m_iSrcNode ].m_vecOrigin.x ); @@ -456,7 +453,7 @@ int CGraph::NodeType( const CBaseEntity *pEntity ) { if ( pEntity->pev->movetype == MOVETYPE_FLY) { - if (pEntity->pev->waterlevel != 0 && pEntity->pev->watertype != CONTENTS_FOG) + if (pEntity->pev->waterlevel != 0) { return bits_NODE_WATER; } @@ -484,7 +481,7 @@ float CGraph::PathLength( int iStart, int iDest, int iHull, int afCapMask ) { if (iMaxLoop-- <= 0) { - ALERT( at_debug, "Route Failure\n" ); + ALERT( at_console, "Route Failure\n" ); return 0; } @@ -499,7 +496,7 @@ float CGraph::PathLength( int iStart, int iDest, int iHull, int afCapMask ) HashSearch(iCurrentNode, iNext, iLink); if (iLink < 0) { - ALERT(at_debug, "HashLinks is broken from %d to %d.\n", iCurrentNode, iDest); + ALERT(at_console, "HashLinks is broken from %d to %d.\n", iCurrentNode, iDest); return 0; } CLink &link = Link(iLink); @@ -658,8 +655,7 @@ int CGraph :: FindShortestPath ( int *piPath, int iStart, int iDest, int iHull, // Mark all the nodes as unvisited. // - int i = 0; - for ( i = 0; i < m_cNodes; i++) + for ( int i = 0; i < m_cNodes; i++) { m_pNodes[ i ].m_flClosestSoFar = -1.0; } @@ -743,7 +739,7 @@ int CGraph :: FindShortestPath ( int *piPath, int iStart, int iDest, int iHull, for ( int i = 0 ; i < iNumPathNodes - 1 ; i++ ) { - MESSAGE_BEGIN( MSG_BROADCAST, gmsgTempEntity ); + MESSAGE_BEGIN( MSG_BROADCAST, SVC_TEMPENTITY ); WRITE_BYTE( TE_SHOWLINE); WRITE_COORD( m_pNodes[ piPath[ i ] ].m_vecOrigin.x ); @@ -759,7 +755,7 @@ int CGraph :: FindShortestPath ( int *piPath, int iStart, int iDest, int iHull, #endif #if 0 // MAZE map - MESSAGE_BEGIN( MSG_BROADCAST, gmsgTempEntity ); + MESSAGE_BEGIN( MSG_BROADCAST, SVC_TEMPENTITY ); WRITE_BYTE( TE_SHOWLINE); WRITE_COORD( m_pNodes[ 4 ].m_vecOrigin.x ); @@ -1110,7 +1106,7 @@ void CGraph :: ShowNodeConnections ( int iNode ) pLinkNode = &Node( NodeLink( iNode, i).m_iDestNode ); vecSpot = pLinkNode->m_vecOrigin; - MESSAGE_BEGIN( MSG_BROADCAST, gmsgTempEntity ); + MESSAGE_BEGIN( MSG_BROADCAST, SVC_TEMPENTITY ); WRITE_BYTE( TE_SHOWLINE); WRITE_COORD( m_pNodes[ iNode ].m_vecOrigin.x ); @@ -1462,13 +1458,13 @@ void CTestHull :: Spawn( entvars_t *pevMasterNode ) if ( WorldGraph.m_fGraphPresent ) {// graph loaded from disk, so we don't need the test hull - SetThink(&CTestHull :: SUB_Remove ); - SetNextThink( 0 ); + SetThink ( SUB_Remove ); + pev->nextthink = gpGlobals->time; } else { - SetThink(&CTestHull :: DropDelay ); - SetNextThink( 1 ); + SetThink ( DropDelay ); + pev->nextthink = gpGlobals->time + 1; } // Make this invisible @@ -1485,11 +1481,11 @@ void CTestHull::DropDelay ( void ) { UTIL_CenterPrintAll( "Node Graph out of Date. Rebuilding..." ); - UTIL_SetOrigin ( this, WorldGraph.m_pNodes[ 0 ].m_vecOrigin ); + UTIL_SetOrigin ( VARS(pev), WorldGraph.m_pNodes[ 0 ].m_vecOrigin ); - SetThink(&CTestHull:: CallBuildNodeGraph ); + SetThink ( CallBuildNodeGraph ); - SetNextThink( 1 ); + pev->nextthink = gpGlobals->time + 1; } //========================================================= @@ -1573,7 +1569,7 @@ void CTestHull :: ShowBadNode( void ) UTIL_ParticleEffect ( pev->origin + gpGlobals->v_right * 64, g_vecZero, 255, 25 ); UTIL_ParticleEffect ( pev->origin - gpGlobals->v_right * 64, g_vecZero, 255, 25 ); - SetNextThink( 0.1 ); + pev->nextthink = gpGlobals->time + 0.1; } extern BOOL gTouchDisabled; @@ -1635,8 +1631,8 @@ void CTestHull :: BuildNodeGraph( void ) float flDist; int step; - SetThink(&CTestHull :: SUB_Remove );// no matter what happens, the hull gets rid of itself. - SetNextThink( 0 ); + SetThink ( SUB_Remove );// no matter what happens, the hull gets rid of itself. + pev->nextthink = gpGlobals->time; // malloc a swollen temporary connection pool that we trim down after we know exactly how many connections there are. pTempPool = (CLink *)calloc ( sizeof ( CLink ) , ( WorldGraph.m_cNodes * MAX_NODE_INITIAL_LINKS ) ); @@ -1747,7 +1743,7 @@ void CTestHull :: BuildNodeGraph( void ) { ALERT ( at_aiconsole, "**ConnectVisibleNodes FAILED!\n" ); - SetThink(&CTestHull :: ShowBadNode );// send the hull off to show the offending node. + SetThink ( ShowBadNode );// send the hull off to show the offending node. //pev->solid = SOLID_NOT; pev->origin = WorldGraph.m_pNodes[ iBadNode ].m_vecOrigin; @@ -1809,7 +1805,7 @@ void CTestHull :: BuildNodeGraph( void ) break; } - UTIL_SetOrigin ( this, pSrcNode->m_vecOrigin );// place the hull on the node + UTIL_SetOrigin ( pev, pSrcNode->m_vecOrigin );// place the hull on the node if ( !FBitSet ( pev->flags, FL_ONGROUND ) ) { @@ -2053,7 +2049,7 @@ void CTestHull :: BuildNodeGraph( void ) // save the node graph for this level WorldGraph.FSaveGraph( (char *)STRING( gpGlobals->mapname ) ); - ALERT( at_debug, "Done.\n"); + ALERT( at_console, "Done.\n"); } @@ -2090,7 +2086,7 @@ void CTestHull :: PathFind ( void ) pNextNode = &WorldGraph.m_pNodes[ iPath [ i + 1 ] ]; - MESSAGE_BEGIN( MSG_BROADCAST, gmsgTempEntity ); + MESSAGE_BEGIN( MSG_BROADCAST, SVC_TEMPENTITY ); WRITE_BYTE( TE_SHOWLINE); WRITE_COORD( pNode->m_vecOrigin.x ); @@ -2559,7 +2555,7 @@ int CGraph :: FSaveGraph ( char *szMapName ) int CGraph :: FSetGraphPointers ( void ) { int i; - CBaseEntity *pLinkEnt; + edict_t *pentLinkEnt; for ( i = 0 ; i < m_cLinks ; i++ ) {// go through all of the links @@ -2574,9 +2570,9 @@ int CGraph :: FSetGraphPointers ( void ) // m_szLinkEntModelname is not necessarily NULL terminated (so we can store it in a more alignment-friendly 4 bytes) memcpy( name, m_pLinkPool[ i ].m_szLinkEntModelname, 4 ); name[4] = 0; - pLinkEnt = UTIL_FindEntityByString( NULL, "model", name ); + pentLinkEnt = FIND_ENTITY_BY_STRING( NULL, "model", name ); - if ( !pLinkEnt ) + if ( FNullEnt ( pentLinkEnt ) ) { // the ent isn't around anymore? Either there is a major problem, or it was removed from the world // ( like a func_breakable that's been destroyed or something ). Make sure that LinkEnt is null. @@ -2585,7 +2581,7 @@ int CGraph :: FSetGraphPointers ( void ) } else { - m_pLinkPool[ i ].m_pLinkEnt = pLinkEnt->pev; + m_pLinkPool[ i ].m_pLinkEnt = VARS( pentLinkEnt ); if ( !FBitSet( m_pLinkPool[ i ].m_pLinkEnt->flags, FL_GRAPHED ) ) { @@ -2736,8 +2732,7 @@ void CGraph::HashChoosePrimes(int TableSize) // We divide this interval into 16 equal sized zones. We want to find // one prime number that best represents that zone. // - int iZone = 0, iPrime = 0; - for (iZone = 1, iPrime = 0; iPrime < 16; iZone += Spacing) + for (int iZone = 1, iPrime = 0; iPrime < 16; iZone += Spacing) { // Search for a prime number that is less than the target zone // number given by iZone. @@ -2795,8 +2790,7 @@ void CGraph::SortNodes(void) // int iNodeCnt = 0; m_pNodes[0].m_iPreviousNode = iNodeCnt++; - int i = 0; - for (i = 1; i < m_cNodes; i++) + for (int i = 1; i < m_cNodes; i++) { m_pNodes[i].m_iPreviousNode = UNNUMBERED_NODE; } @@ -2861,8 +2855,7 @@ void CGraph::BuildLinkLookups(void) ALERT(at_aiconsole, "Couldn't allocated Link Lookup Table.\n"); return; } - int i = 0; - for (i = 0; i < m_nHashLinks; i++) + for (int i = 0; i < m_nHashLinks; i++) { m_pHashLinks[i] = ENTRY_STATE_EMPTY; } @@ -2902,8 +2895,7 @@ void CGraph::BuildRegionTables(void) // Calculate regions for all the nodes. // // - int i = 0; - for (i = 0; i < 3; i++) + for (int i = 0; i < 3; i++) { m_RegionMin[i] = 999999999.0; // just a big number out there; m_RegionMax[i] = -999999999.0; // just a big number out there; @@ -2933,8 +2925,7 @@ void CGraph::BuildRegionTables(void) for (i = 0; i < 3; i++) { - int j = 0; - for ( j = 0; j < NUM_RANGES; j++) + for (int j = 0; j < NUM_RANGES; j++) { m_RangeStart[i][j] = 255; m_RangeEnd[i][j] = 0; @@ -3068,8 +3059,7 @@ void CGraph :: ComputeStaticRoutingTables( void ) // Initialize Routing table to uncalculated. // - int iFrom = 0; - for (iFrom = 0; iFrom < m_cNodes; iFrom++) + for (int iFrom = 0; iFrom < m_cNodes; iFrom++) { for (int iTo = 0; iTo < m_cNodes; iTo++) { @@ -3285,8 +3275,7 @@ void CGraph :: ComputeStaticRoutingTables( void ) int nRoute = p - pRoute; if (m_pRouteInfo) { - int i = 0; - for (i = 0; i < m_nRouteInfo - nRoute; i++) + for (int i = 0; i < m_nRouteInfo - nRoute; i++) { if (memcmp(m_pRouteInfo + i, pRoute, nRoute) == 0) { @@ -3378,8 +3367,7 @@ void CGraph :: TestRoutingTables( void ) // #if 1 float flDistance1 = 0.0; - int i = 0; - for (i = 0; i < cPathSize1-1; i++) + for (int i = 0; i < cPathSize1-1; i++) { // Find the link from pMyPath[i] to pMyPath[i+1] // @@ -3433,8 +3421,7 @@ void CGraph :: TestRoutingTables( void ) #endif ALERT(at_aiconsole, "Routing is inconsistent!!!\n"); ALERT(at_aiconsole, "(%d to %d |%d/%d)1:", iFrom, iTo, iHull, iCap); - int i = 0; - for (i = 0; i < cPathSize1; i++) + for (int i = 0; i < cPathSize1; i++) { ALERT(at_aiconsole, "%d ", pMyPath[i]); } @@ -3505,7 +3492,7 @@ void CNodeViewer::Spawn( ) { if ( !WorldGraph.m_fGraphPresent || !WorldGraph.m_fGraphPointersSet ) {// protect us in the case that the node graph isn't available or built - ALERT ( at_debug, "Graph not ready!\n" ); + ALERT ( at_console, "Graph not ready!\n" ); UTIL_Remove( this ); return; } @@ -3535,7 +3522,7 @@ void CNodeViewer::Spawn( ) if ( m_iBaseNode < 0 ) { - ALERT( at_debug, "No nearby node\n" ); + ALERT( at_console, "No nearby node\n" ); return; } @@ -3571,8 +3558,8 @@ void CNodeViewer::Spawn( ) ALERT( at_aiconsole, "%d nodes\n", m_nVisited ); m_iDraw = 0; - SetThink(&CNodeViewer:: DrawThink ); - SetNextThink( 0 ); + SetThink( DrawThink ); + pev->nextthink = gpGlobals->time; } @@ -3613,7 +3600,7 @@ void CNodeViewer::AddNode( int iFrom, int iTo ) void CNodeViewer :: DrawThink( void ) { - SetNextThink( 0 ); + pev->nextthink = gpGlobals->time; for (int i = 0; i < 10; i++) { @@ -3624,7 +3611,7 @@ void CNodeViewer :: DrawThink( void ) } extern short g_sModelIndexLaser; - MESSAGE_BEGIN( MSG_BROADCAST, gmsgTempEntity ); + MESSAGE_BEGIN( MSG_BROADCAST, SVC_TEMPENTITY ); WRITE_BYTE( TE_BEAMPOINTS ); WRITE_COORD( WorldGraph.m_pNodes[ m_aFrom[m_iDraw] ].m_vecOrigin.x ); WRITE_COORD( WorldGraph.m_pNodes[ m_aFrom[m_iDraw] ].m_vecOrigin.y ); diff --git a/spirit/nodes.h b/dlls/nodes.h similarity index 100% rename from spirit/nodes.h rename to dlls/nodes.h diff --git a/spirit/osprey.cpp b/dlls/osprey.cpp similarity index 82% rename from spirit/osprey.cpp rename to dlls/osprey.cpp index 1c163eaa..435107e2 100644 --- a/spirit/osprey.cpp +++ b/dlls/osprey.cpp @@ -1,6 +1,6 @@ /*** * -* Copyright (c) 1999, 2000 Valve LLC. All rights reserved. +* Copyright (c) 1996-2002, Valve LLC. All rights reserved. * * This product contains software technology licensed from Id * Software, Inc. ("Id Technology"). Id Technology (c) 1996 Id Software, Inc. @@ -20,6 +20,7 @@ #include "nodes.h" #include "soundent.h" #include "effects.h" +#include "customentity.h" typedef struct { @@ -43,7 +44,7 @@ public: int Restore( CRestore &restore ); static TYPEDESCRIPTION m_SaveData[]; int ObjectCaps( void ) { return CBaseMonster :: ObjectCaps() & ~FCAP_ACROSS_TRANSITION; } - + void Spawn( void ); void Precache( void ); int Classify( void ) { return CLASS_MACHINE; }; @@ -140,6 +141,7 @@ TYPEDESCRIPTION COsprey::m_SaveData[] = }; IMPLEMENT_SAVERESTORE( COsprey, CBaseMonster ); + void COsprey :: Spawn( void ) { Precache( ); @@ -147,23 +149,16 @@ void COsprey :: Spawn( void ) pev->movetype = MOVETYPE_FLY; pev->solid = SOLID_BBOX; - if (pev->model) - SET_MODEL(ENT(pev), STRING(pev->model)); //LRC - else - SET_MODEL(ENT(pev), "models/osprey.mdl"); + SET_MODEL(ENT(pev), "models/osprey.mdl"); UTIL_SetSize(pev, Vector( -400, -400, -100), Vector(400, 400, 32)); - UTIL_SetOrigin( this, pev->origin ); + UTIL_SetOrigin( pev, pev->origin ); - //ALERT(at_console, "Osprey origin %f %f %f\n", pev->origin.x, pev->origin.y, pev->origin.z); - - pev->flags |= FL_MONSTER | FL_FLY; + pev->flags |= FL_MONSTER; pev->takedamage = DAMAGE_YES; m_flRightHealth = 200; m_flLeftHealth = 200; pev->health = 400; - pev->speed = 80; //LRC - default speed, in case path corners don't give a speed. - m_flFieldOfView = 0; // 180 degrees pev->sequence = 0; @@ -172,12 +167,12 @@ void COsprey :: Spawn( void ) InitBoneControllers(); - SetThink(&COsprey :: FindAllThink ); - SetUse(&COsprey :: CommandUse ); + SetThink( FindAllThink ); + SetUse( CommandUse ); if (!(pev->spawnflags & SF_WAITFORTRIGGER)) { - SetNextThink( 1.0 ); + pev->nextthink = gpGlobals->time + 1.0; } m_pos2 = pev->origin; @@ -190,10 +185,7 @@ void COsprey::Precache( void ) { UTIL_PrecacheOther( "monster_human_grunt" ); - if (pev->model) - PRECACHE_MODEL((char*)STRING(pev->model)); //LRC - else - PRECACHE_MODEL("models/osprey.mdl"); + PRECACHE_MODEL("models/osprey.mdl"); PRECACHE_MODEL("models/HVR.mdl"); PRECACHE_SOUND("apache/ap_rotor4.wav"); @@ -209,7 +201,7 @@ void COsprey::Precache( void ) void COsprey::CommandUse( CBaseEntity *pActivator, CBaseEntity *pCaller, USE_TYPE useType, float value ) { - SetNextThink( 0.1 ); + pev->nextthink = gpGlobals->time + 0.1; } void COsprey :: FindAllThink( void ) @@ -229,14 +221,12 @@ void COsprey :: FindAllThink( void ) if (m_iUnits == 0) { - m_iUnits = 4; //LRC - stop whining, just make the damn grunts... - -// ALERT( at_console, "osprey error: no grunts to resupply\n"); -// UTIL_Remove( this ); -// return; + ALERT( at_console, "osprey error: no grunts to resupply\n"); + UTIL_Remove( this ); + return; } - SetThink(&COsprey :: FlyThink ); - SetNextThink( 0.1 ); + SetThink( FlyThink ); + pev->nextthink = gpGlobals->time + 0.1; m_startTime = gpGlobals->time; } @@ -267,8 +257,8 @@ void COsprey :: DeployThink( void ) vecSrc = pev->origin + vecForward * -64 + vecRight * -100 + vecUp * -96; m_hRepel[3] = MakeGrunt( vecSrc ); - SetThink(&COsprey :: HoverThink ); - SetNextThink( 0.1 ); + SetThink( HoverThink ); + pev->nextthink = gpGlobals->time + 0.1; } @@ -315,11 +305,11 @@ CBaseMonster *COsprey :: MakeGrunt( Vector vecSrc ) pGrunt->SetActivity( ACT_GLIDE ); CBeam *pBeam = CBeam::BeamCreate( "sprites/rope.spr", 10 ); - pBeam->PointEntInit( vecSrc + Vector(0,0,112), pGrunt->edict() ); - pBeam->SetFlags( FBEAM_SOLID ); + pBeam->PointEntInit( vecSrc + Vector(0,0,112), pGrunt->entindex() ); + pBeam->SetFlags( BEAM_FSOLID ); pBeam->SetColor( 255, 255, 255 ); - pBeam->SetThink(&CBeam:: SUB_Remove ); - pBeam->SetNextThink( -4096.0 * tr.flFraction / pGrunt->pev->velocity.z + 0.5 ); + pBeam->SetThink( SUB_Remove ); + pBeam->pev->nextthink = gpGlobals->time + -4096.0 * tr.flFraction / pGrunt->pev->velocity.z + 0.5; // ALERT( at_console, "%d at %.0f %.0f %.0f\n", i, m_vecOrigin[i].x, m_vecOrigin[i].y, m_vecOrigin[i].z ); pGrunt->m_vecLastPosition = m_vecOrigin[i]; @@ -346,10 +336,10 @@ void COsprey :: HoverThink( void ) if (i == 4) { m_startTime = gpGlobals->time; - SetThink(&COsprey :: FlyThink ); + SetThink( FlyThink ); } - SetNextThink( 0.1 ); + pev->nextthink = gpGlobals->time + 0.1; UTIL_MakeAimVectors( pev->angles ); ShowDamage( ); } @@ -365,17 +355,10 @@ void COsprey::UpdateGoal( ) m_pos2 = m_pGoalEnt->pev->origin; m_ang2 = m_pGoalEnt->pev->angles; UTIL_MakeAimVectors( Vector( 0, m_ang2.y, 0 ) ); - - //LRC - ugh. we shouldn't require our path corners to specify a speed! - if (m_pGoalEnt->pev->speed) - pev->speed = m_pGoalEnt->pev->speed; - - m_vel2 = gpGlobals->v_forward * pev->speed; //LRC + m_vel2 = gpGlobals->v_forward * m_pGoalEnt->pev->speed; m_startTime = m_startTime + m_dTime; - m_dTime = 2.0 * (m_pos1 - m_pos2).Length() / (m_vel1.Length() + pev->speed); - - //ALERT(at_console, "osprey m_dTime = %f / %f + %f\n", (m_pos1 - m_pos2).Length(), m_vel1.Length(), m_pGoalEnt->pev->speed); + m_dTime = 2.0 * (m_pos1 - m_pos2).Length() / (m_vel1.Length() + m_pGoalEnt->pev->speed); if (m_ang1.y - m_ang2.y < -180) { @@ -386,14 +369,14 @@ void COsprey::UpdateGoal( ) m_ang1.y -= 360; } - if (pev->speed < 400) + if (m_pGoalEnt->pev->speed < 400) m_flIdealtilt = 0; else m_flIdealtilt = -90; } else { - ALERT( at_debug, "osprey missing target"); + ALERT( at_console, "osprey missing target"); } } @@ -401,11 +384,11 @@ void COsprey::UpdateGoal( ) void COsprey::FlyThink( void ) { StudioFrameAdvance( ); - SetNextThink( 0.1 ); + pev->nextthink = gpGlobals->time + 0.1; if ( m_pGoalEnt == NULL && !FStringNull(pev->target) )// this monster has a target { - m_pGoalEnt = UTIL_FindEntityByTargetname( NULL, STRING( pev->target ) ); + m_pGoalEnt = CBaseEntity::Instance( FIND_ENTITY_BY_TARGETNAME ( NULL, STRING( pev->target ) ) ); UpdateGoal( ); } @@ -413,13 +396,11 @@ void COsprey::FlyThink( void ) { if (m_pGoalEnt->pev->speed == 0) { - SetThink(&COsprey:: DeployThink ); + SetThink( DeployThink ); } - int loopbreaker = 100; //LRC - don't loop indefinitely! do { - m_pGoalEnt = UTIL_FindEntityByTargetname( NULL, STRING( m_pGoalEnt->pev->target ) ); - loopbreaker--; //LRC - } while (m_pGoalEnt->pev->speed < 400 && !HasDead() && loopbreaker > 0); + m_pGoalEnt = CBaseEntity::Instance( FIND_ENTITY_BY_TARGETNAME ( NULL, STRING( m_pGoalEnt->pev->target ) ) ); + } while (m_pGoalEnt->pev->speed < 400 && !HasDead()); UpdateGoal( ); } @@ -435,13 +416,11 @@ void COsprey::Flight( ) float f = UTIL_SplineFraction( t * scale, 1.0 ); -// ALERT(at_console, "Osprey setorigin m_pos1 %f, m_vel1 %f, m_pos2 %f, m_vel2 %f, m_dTime %f, t %f, f %f\n", m_pos1.x, m_vel1.x, m_pos2.x, m_vel2.x, m_dTime, t, f); - Vector pos = (m_pos1 + m_vel1 * t) * (1.0 - f) + (m_pos2 - m_vel2 * (m_dTime - t)) * f; Vector ang = (m_ang1) * (1.0 - f) + (m_ang2) * f; m_velocity = m_vel1 * (1.0 - f) + m_vel2 * f; - UTIL_SetOrigin( this, pos ); + UTIL_SetOrigin( pev, pos ); pev->angles = ang; UTIL_MakeAimVectors( pev->angles ); float flSpeed = DotProduct( gpGlobals->v_forward, m_velocity ); @@ -508,7 +487,7 @@ void COsprey::Flight( ) void COsprey::HitTouch( CBaseEntity *pOther ) { - SetNextThink( 2.0 ); + pev->nextthink = gpGlobals->time + 2.0; } @@ -539,9 +518,9 @@ void COsprey :: Killed( entvars_t *pevAttacker, int iGib ) STOP_SOUND( ENT(pev), CHAN_STATIC, "apache/ap_rotor4.wav" ); UTIL_SetSize( pev, Vector( -32, -32, -64), Vector( 32, 32, 0) ); - SetThink(&COsprey :: DyingThink ); - SetTouch(&COsprey :: CrashTouch ); - SetNextThink( 0.1 ); + SetThink( DyingThink ); + SetTouch( CrashTouch ); + pev->nextthink = gpGlobals->time + 0.1; pev->health = 0; pev->takedamage = DAMAGE_NO; @@ -555,7 +534,7 @@ void COsprey::CrashTouch( CBaseEntity *pOther ) { SetTouch( NULL ); m_startTime = gpGlobals->time; - SetNextThink( 0 ); + pev->nextthink = gpGlobals->time; m_velocity = pev->velocity; } } @@ -564,7 +543,7 @@ void COsprey::CrashTouch( CBaseEntity *pOther ) void COsprey :: DyingThink( void ) { StudioFrameAdvance( ); - SetNextThink( 0.1 ); + pev->nextthink = gpGlobals->time + 0.1; pev->avelocity = pev->avelocity * 1.02; @@ -577,7 +556,7 @@ void COsprey :: DyingThink( void ) Vector vecSpot = pev->origin + pev->velocity * 0.2; // random explosions - MESSAGE_BEGIN( MSG_PVS, gmsgTempEntity, vecSpot ); + MESSAGE_BEGIN( MSG_PVS, SVC_TEMPENTITY, vecSpot ); WRITE_BYTE( TE_EXPLOSION); // This just makes a dynamic light now WRITE_COORD( vecSpot.x + RANDOM_FLOAT( -150, 150 )); WRITE_COORD( vecSpot.y + RANDOM_FLOAT( -150, 150 )); @@ -589,7 +568,7 @@ void COsprey :: DyingThink( void ) MESSAGE_END(); // lots of smoke - MESSAGE_BEGIN( MSG_PVS, gmsgTempEntity, vecSpot ); + MESSAGE_BEGIN( MSG_PVS, SVC_TEMPENTITY, vecSpot ); WRITE_BYTE( TE_SMOKE ); WRITE_COORD( vecSpot.x + RANDOM_FLOAT( -150, 150 )); WRITE_COORD( vecSpot.y + RANDOM_FLOAT( -150, 150 )); @@ -601,7 +580,7 @@ void COsprey :: DyingThink( void ) vecSpot = pev->origin + (pev->mins + pev->maxs) * 0.5; - MESSAGE_BEGIN( MSG_PVS, gmsgTempEntity, vecSpot ); + MESSAGE_BEGIN( MSG_PVS, SVC_TEMPENTITY, vecSpot ); WRITE_BYTE( TE_BREAKMODEL); // position @@ -640,7 +619,7 @@ void COsprey :: DyingThink( void ) // don't stop it we touch a entity pev->flags &= ~FL_ONGROUND; - SetNextThink( 0.2 ); + pev->nextthink = gpGlobals->time + 0.2; return; } else @@ -648,7 +627,7 @@ void COsprey :: DyingThink( void ) Vector vecSpot = pev->origin + (pev->mins + pev->maxs) * 0.5; /* - MESSAGE_BEGIN( MSG_BROADCAST, gmsgTempEntity ); + MESSAGE_BEGIN( MSG_BROADCAST, SVC_TEMPENTITY ); WRITE_BYTE( TE_EXPLOSION); // This just makes a dynamic light now WRITE_COORD( vecSpot.x ); WRITE_COORD( vecSpot.y ); @@ -660,7 +639,7 @@ void COsprey :: DyingThink( void ) */ // gibs - MESSAGE_BEGIN( MSG_PVS, gmsgTempEntity, vecSpot ); + MESSAGE_BEGIN( MSG_PVS, SVC_TEMPENTITY, vecSpot ); WRITE_BYTE( TE_SPRITE ); WRITE_COORD( vecSpot.x ); WRITE_COORD( vecSpot.y ); @@ -671,7 +650,7 @@ void COsprey :: DyingThink( void ) MESSAGE_END(); /* - MESSAGE_BEGIN( MSG_BROADCAST, gmsgTempEntity ); + MESSAGE_BEGIN( MSG_BROADCAST, SVC_TEMPENTITY ); WRITE_BYTE( TE_SMOKE ); WRITE_COORD( vecSpot.x ); WRITE_COORD( vecSpot.y ); @@ -683,7 +662,7 @@ void COsprey :: DyingThink( void ) */ // blast circle - MESSAGE_BEGIN( MSG_PAS, gmsgTempEntity, pev->origin ); + MESSAGE_BEGIN( MSG_PAS, SVC_TEMPENTITY, pev->origin ); WRITE_BYTE( TE_BEAMCYLINDER ); WRITE_COORD( pev->origin.x); WRITE_COORD( pev->origin.y); @@ -710,7 +689,7 @@ void COsprey :: DyingThink( void ) // gibs vecSpot = pev->origin + (pev->mins + pev->maxs) * 0.5; - MESSAGE_BEGIN( MSG_PAS, gmsgTempEntity, vecSpot ); + MESSAGE_BEGIN( MSG_PAS, SVC_TEMPENTITY, vecSpot ); WRITE_BYTE( TE_BREAKMODEL); // position @@ -755,7 +734,7 @@ void COsprey :: ShowDamage( void ) if (m_iDoLeftSmokePuff > 0 || RANDOM_LONG(0,99) > m_flLeftHealth) { Vector vecSrc = pev->origin + gpGlobals->v_right * -340; - MESSAGE_BEGIN( MSG_PVS, gmsgTempEntity, vecSrc ); + MESSAGE_BEGIN( MSG_PVS, SVC_TEMPENTITY, vecSrc ); WRITE_BYTE( TE_SMOKE ); WRITE_COORD( vecSrc.x ); WRITE_COORD( vecSrc.y ); @@ -770,7 +749,7 @@ void COsprey :: ShowDamage( void ) if (m_iDoRightSmokePuff > 0 || RANDOM_LONG(0,99) > m_flRightHealth) { Vector vecSrc = pev->origin + gpGlobals->v_right * 340; - MESSAGE_BEGIN( MSG_PVS, gmsgTempEntity, vecSrc ); + MESSAGE_BEGIN( MSG_PVS, SVC_TEMPENTITY, vecSrc ); WRITE_BYTE( TE_SMOKE ); WRITE_COORD( vecSrc.x ); WRITE_COORD( vecSrc.y ); diff --git a/spirit/pathcorner.cpp b/dlls/pathcorner.cpp similarity index 86% rename from spirit/pathcorner.cpp rename to dlls/pathcorner.cpp index 0939f799..b7216720 100644 --- a/spirit/pathcorner.cpp +++ b/dlls/pathcorner.cpp @@ -1,440 +1,428 @@ -/*** -* -* Copyright (c) 1996-2002, 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. -* -****/ -// -// ========================== PATH_CORNER =========================== -// - -#include "extdll.h" -#include "util.h" -#include "cbase.h" -#include "trains.h" -#include "saverestore.h" - -class CPathCorner : public CPointEntity -{ -public: - void Spawn( ); - void KeyValue( KeyValueData* pkvd ); - float GetDelay( void ) { return m_flWait; } -// void Touch( CBaseEntity *pOther ); - virtual int Save( CSave &save ); - virtual int Restore( CRestore &restore ); - - static TYPEDESCRIPTION m_SaveData[]; - -private: - float m_flWait; -}; - -LINK_ENTITY_TO_CLASS( path_corner, CPathCorner ); - -// Global Savedata for Delay -TYPEDESCRIPTION CPathCorner::m_SaveData[] = -{ - DEFINE_FIELD( CPathCorner, m_flWait, FIELD_FLOAT ), -}; - -IMPLEMENT_SAVERESTORE( CPathCorner, CPointEntity ); - -// -// Cache user-entity-field values until spawn is called. -// -void CPathCorner :: KeyValue( KeyValueData *pkvd ) -{ - if (FStrEq(pkvd->szKeyName, "wait")) - { - m_flWait = atof(pkvd->szValue); - pkvd->fHandled = TRUE; - } - else if (FStrEq(pkvd->szKeyName, "turnspeed")) //LRC - { - if (pkvd->szValue[0]) // if the field is blank, don't set the spawnflag. - { - pev->spawnflags |= SF_CORNER_AVELOCITY; - UTIL_StringToVector( (float*)pev->avelocity, pkvd->szValue); - } - pkvd->fHandled = TRUE; - } - else - CPointEntity::KeyValue( pkvd ); -} - - -void CPathCorner :: Spawn( ) -{ - ASSERTSZ(!FStringNull(pev->targetname), "path_corner without a targetname"); -} - -#if 0 -void CPathCorner :: Touch( CBaseEntity *pOther ) -{ - entvars_t* pevToucher = pOther->pev; - - if ( FBitSet ( pevToucher->flags, FL_MONSTER ) ) - {// monsters don't navigate path corners based on touch anymore - return; - } - - // If OTHER isn't explicitly looking for this path_corner, bail out - if ( pOther->m_pGoalEnt != this ) - { - return; - } - - // If OTHER has an enemy, this touch is incidental, ignore - if ( !FNullEnt(pevToucher->enemy) ) - { - return; // fighting, not following a path - } - - // UNDONE: support non-zero flWait - /* - if (m_flWait != 0) - ALERT(at_warning, "Non-zero path-cornder waits NYI"); - */ - - // Find the next "stop" on the path, make it the goal of the "toucher". - if (FStringNull(pev->target)) - { - ALERT(at_warning, "PathCornerTouch: no next stop specified"); - } - - pOther->m_pGoalEnt = UTIL_FindEntityByTargetname ( NULL, STRING(pev->target) ); - - // If "next spot" was not found (does not exist - level design error) - if ( !pOther->m_pGoalEnt ) - { - ALERT(at_debug, "PathCornerTouch--%s couldn't find next stop in path: %s", STRING(pev->classname), STRING(pev->target)); - return; - } - - // Turn towards the next stop in the path. - pevToucher->ideal_yaw = UTIL_VecToYaw ( pOther->m_pGoalEnt->pev->origin - pevToucher->origin ); -} -#endif - - - -TYPEDESCRIPTION CPathTrack::m_SaveData[] = -{ - DEFINE_FIELD( CPathTrack, m_length, FIELD_FLOAT ), - DEFINE_FIELD( CPathTrack, m_pnext, FIELD_CLASSPTR ), - DEFINE_FIELD( CPathTrack, m_paltpath, FIELD_CLASSPTR ), - DEFINE_FIELD( CPathTrack, m_pprevious, FIELD_CLASSPTR ), - DEFINE_FIELD( CPathTrack, m_altName, FIELD_STRING ), -}; - -IMPLEMENT_SAVERESTORE( CPathTrack, CBaseEntity ); -LINK_ENTITY_TO_CLASS( path_track, CPathTrack ); - -// -// Cache user-entity-field values until spawn is called. -// -void CPathTrack :: KeyValue( KeyValueData *pkvd ) -{ - if (FStrEq(pkvd->szKeyName, "altpath")) - { - m_altName = ALLOC_STRING(pkvd->szValue); - pkvd->fHandled = TRUE; - } - else if (FStrEq(pkvd->szKeyName, "turnspeed")) //LRC - { - if (pkvd->szValue[0]) // if the field is blank, don't set the spawnflag. - { - pev->spawnflags |= SF_PATH_AVELOCITY; - UTIL_StringToVector( (float*)pev->avelocity, pkvd->szValue); - } - pkvd->fHandled = TRUE; - } - else - CPointEntity::KeyValue( pkvd ); -} - -void CPathTrack :: Use( CBaseEntity *pActivator, CBaseEntity *pCaller, USE_TYPE useType, float value ) -{ - int on; - - // Use toggles between two paths - if ( m_paltpath ) - { - on = !FBitSet( pev->spawnflags, SF_PATH_ALTERNATE ); - if ( ShouldToggle( useType, on ) ) - { - if ( on ) - SetBits( pev->spawnflags, SF_PATH_ALTERNATE ); - else - ClearBits( pev->spawnflags, SF_PATH_ALTERNATE ); - } - } - else // Use toggles between enabled/disabled - { - on = !FBitSet( pev->spawnflags, SF_PATH_DISABLED ); - - if ( ShouldToggle( useType, on ) ) - { - if ( on ) - SetBits( pev->spawnflags, SF_PATH_DISABLED ); - else - ClearBits( pev->spawnflags, SF_PATH_DISABLED ); - } - } -} - - -void CPathTrack :: Link( void ) -{ - CBaseEntity *pTarget; - - if ( !FStringNull(pev->target) ) - { - pTarget = UTIL_FindEntityByTargetname( NULL, STRING(pev->target) ); - if ( pTarget ) - { - m_pnext = (CPathTrack*)pTarget; - m_pnext->SetPrevious( this ); - } - else - ALERT( at_debug, "Dead end link %s\n", STRING(pev->target) ); - } - - // Find "alternate" path - if ( m_altName ) - { - pTarget = UTIL_FindEntityByTargetname( NULL, STRING(m_altName) ); - if ( pTarget ) // If no next pointer, this is the end of a path - { - m_paltpath = (CPathTrack*)pTarget; - m_paltpath->SetPrevious( this ); - } - } -} - - -void CPathTrack :: Spawn( void ) -{ - pev->solid = SOLID_TRIGGER; - UTIL_SetSize(pev, Vector(-8, -8, -8), Vector(8, 8, 8)); - - m_pnext = NULL; - m_pprevious = NULL; -// DEBUGGING CODE -#if PATH_SPARKLE_DEBUG - SetThink(&CPathTrack :: Sparkle ); - SetNextThink( 0.5 ); -#endif -} - - -void CPathTrack::Activate( void ) -{ - if ( !FStringNull( pev->targetname ) ) // Link to next, and back-link - Link(); - - CPointEntity::Activate(); -} - -CPathTrack *CPathTrack :: ValidPath( CPathTrack *ppath, int testFlag ) -{ - if ( !ppath ) - return NULL; - - if ( testFlag && FBitSet( ppath->pev->spawnflags, SF_PATH_DISABLED ) ) - return NULL; - - return ppath; -} - - -void CPathTrack :: Project( CPathTrack *pstart, CPathTrack *pend, Vector *origin, float dist ) -{ - if ( pstart && pend ) - { - Vector dir = (pend->pev->origin - pstart->pev->origin); - dir = dir.Normalize(); - *origin = pend->pev->origin + dir * dist; - } -} - -CPathTrack *CPathTrack::GetNext( void ) -{ - if ( m_paltpath && FBitSet( pev->spawnflags, SF_PATH_ALTERNATE ) && !FBitSet( pev->spawnflags, SF_PATH_ALTREVERSE ) ) - return m_paltpath; - - return m_pnext; -} - - - -CPathTrack *CPathTrack::GetPrevious( void ) -{ - if ( m_paltpath && FBitSet( pev->spawnflags, SF_PATH_ALTERNATE ) && FBitSet( pev->spawnflags, SF_PATH_ALTREVERSE ) ) - return m_paltpath; - - return m_pprevious; -} - - - -void CPathTrack::SetPrevious( CPathTrack *pprev ) -{ - // Only set previous if this isn't my alternate path - if ( pprev && !FStrEq( STRING(pprev->pev->targetname), STRING(m_altName) ) ) - m_pprevious = pprev; -} - - -// Assumes this is ALWAYS enabled -CPathTrack *CPathTrack :: LookAhead( Vector *origin, float dist, int move ) -{ - CPathTrack *pcurrent; - float originalDist = dist; - - pcurrent = this; - Vector currentPos = *origin; - - if ( dist < 0 ) // Travelling backwards through path - { - dist = -dist; - while ( dist > 0 ) - { - Vector dir = pcurrent->pev->origin - currentPos; - float length = dir.Length(); - if ( !length ) - { - if ( !ValidPath(pcurrent->GetPrevious(), move) ) // If there is no previous node, or it's disabled, return now. - { - if ( !move ) - Project( pcurrent->GetNext(), pcurrent, origin, dist ); - return NULL; - } - pcurrent = pcurrent->GetPrevious(); - } - else if ( length > dist ) // enough left in this path to move - { - *origin = currentPos + (dir * (dist / length)); - return pcurrent; - } - else - { - dist -= length; - currentPos = pcurrent->pev->origin; - *origin = currentPos; - if ( !ValidPath(pcurrent->GetPrevious(), move) ) // If there is no previous node, or it's disabled, return now. - return NULL; - - pcurrent = pcurrent->GetPrevious(); - } - } - *origin = currentPos; - return pcurrent; - } - else - { - while ( dist > 0 ) - { - if ( !ValidPath(pcurrent->GetNext(), move) ) // If there is no next node, or it's disabled, return now. - { - if ( !move ) - Project( pcurrent->GetPrevious(), pcurrent, origin, dist ); - return NULL; - } - Vector dir = pcurrent->GetNext()->pev->origin - currentPos; - float length = dir.Length(); - if ( !length && !ValidPath( pcurrent->GetNext()->GetNext(), move ) ) - { - if ( dist == originalDist ) // HACK -- up against a dead end - return NULL; - return pcurrent; - } - if ( length > dist ) // enough left in this path to move - { - *origin = currentPos + (dir * (dist / length)); - return pcurrent; - } - else - { - dist -= length; - currentPos = pcurrent->GetNext()->pev->origin; - pcurrent = pcurrent->GetNext(); - *origin = currentPos; - } - } - *origin = currentPos; - } - - return pcurrent; -} - - -// Assumes this is ALWAYS enabled -CPathTrack *CPathTrack :: Nearest( Vector origin ) -{ - int deadCount; - float minDist, dist; - Vector delta; - CPathTrack *ppath, *pnearest; - - - delta = origin - pev->origin; - delta.z = 0; - minDist = delta.Length(); - pnearest = this; - ppath = GetNext(); - - // Hey, I could use the old 2 racing pointers solution to this, but I'm lazy :) - deadCount = 0; - while ( ppath && ppath != this ) - { - deadCount++; - if ( deadCount > 9999 ) - { - ALERT( at_error, "Bad sequence of path_tracks from %s", STRING(pev->targetname) ); - return NULL; - } - delta = origin - ppath->pev->origin; - delta.z = 0; - dist = delta.Length(); - if ( dist < minDist ) - { - minDist = dist; - pnearest = ppath; - } - ppath = ppath->GetNext(); - } - return pnearest; -} - - -CPathTrack *CPathTrack::Instance( edict_t *pent ) -{ - if ( FClassnameIs( pent, "path_track" ) ) - return (CPathTrack *)GET_PRIVATE(pent); - return NULL; -} - - - // DEBUGGING CODE -#if PATH_SPARKLE_DEBUG -void CPathTrack :: Sparkle( void ) -{ - - SetNextThink( 0.2 ); - if ( FBitSet( pev->spawnflags, SF_PATH_DISABLED ) ) - UTIL_ParticleEffect(pev->origin, Vector(0,0,100), 210, 10); - else - UTIL_ParticleEffect(pev->origin, Vector(0,0,100), 84, 10); -} -#endif - +/*** +* +* Copyright (c) 1996-2002, 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. +* +****/ +// +// ========================== PATH_CORNER =========================== +// + +#include "extdll.h" +#include "util.h" +#include "cbase.h" +#include "trains.h" +#include "saverestore.h" + +class CPathCorner : public CPointEntity +{ +public: + void Spawn( ); + void KeyValue( KeyValueData* pkvd ); + float GetDelay( void ) { return m_flWait; } +// void Touch( CBaseEntity *pOther ); + virtual int Save( CSave &save ); + virtual int Restore( CRestore &restore ); + + static TYPEDESCRIPTION m_SaveData[]; + +private: + float m_flWait; +}; + +LINK_ENTITY_TO_CLASS( path_corner, CPathCorner ); + +// Global Savedata for Delay +TYPEDESCRIPTION CPathCorner::m_SaveData[] = +{ + DEFINE_FIELD( CPathCorner, m_flWait, FIELD_FLOAT ), +}; + +IMPLEMENT_SAVERESTORE( CPathCorner, CPointEntity ); + +// +// Cache user-entity-field values until spawn is called. +// +void CPathCorner :: KeyValue( KeyValueData *pkvd ) +{ + if (FStrEq(pkvd->szKeyName, "wait")) + { + m_flWait = atof(pkvd->szValue); + pkvd->fHandled = TRUE; + } + else + CPointEntity::KeyValue( pkvd ); +} + + +void CPathCorner :: Spawn( ) +{ + ASSERTSZ(!FStringNull(pev->targetname), "path_corner without a targetname"); +} + +#if 0 +void CPathCorner :: Touch( CBaseEntity *pOther ) +{ + entvars_t* pevToucher = pOther->pev; + + if ( FBitSet ( pevToucher->flags, FL_MONSTER ) ) + {// monsters don't navigate path corners based on touch anymore + return; + } + + // If OTHER isn't explicitly looking for this path_corner, bail out + if ( pOther->m_pGoalEnt != this ) + { + return; + } + + // If OTHER has an enemy, this touch is incidental, ignore + if ( !FNullEnt(pevToucher->enemy) ) + { + return; // fighting, not following a path + } + + // UNDONE: support non-zero flWait + /* + if (m_flWait != 0) + ALERT(at_warning, "Non-zero path-cornder waits NYI"); + */ + + // Find the next "stop" on the path, make it the goal of the "toucher". + if (FStringNull(pev->target)) + { + ALERT(at_warning, "PathCornerTouch: no next stop specified"); + } + + pOther->m_pGoalEnt = CBaseEntity::Instance( FIND_ENTITY_BY_TARGETNAME ( NULL, STRING(pev->target) ) ); + + // If "next spot" was not found (does not exist - level design error) + if ( !pOther->m_pGoalEnt ) + { + ALERT(at_console, "PathCornerTouch--%s couldn't find next stop in path: %s", STRING(pev->classname), STRING(pev->target)); + return; + } + + // Turn towards the next stop in the path. + pevToucher->ideal_yaw = UTIL_VecToYaw ( pOther->m_pGoalEnt->pev->origin - pevToucher->origin ); +} +#endif + + + +TYPEDESCRIPTION CPathTrack::m_SaveData[] = +{ + DEFINE_FIELD( CPathTrack, m_length, FIELD_FLOAT ), + DEFINE_FIELD( CPathTrack, m_pnext, FIELD_CLASSPTR ), + DEFINE_FIELD( CPathTrack, m_paltpath, FIELD_CLASSPTR ), + DEFINE_FIELD( CPathTrack, m_pprevious, FIELD_CLASSPTR ), + DEFINE_FIELD( CPathTrack, m_altName, FIELD_STRING ), +}; + +IMPLEMENT_SAVERESTORE( CPathTrack, CBaseEntity ); +LINK_ENTITY_TO_CLASS( path_track, CPathTrack ); + +// +// Cache user-entity-field values until spawn is called. +// +void CPathTrack :: KeyValue( KeyValueData *pkvd ) +{ + if (FStrEq(pkvd->szKeyName, "altpath")) + { + m_altName = ALLOC_STRING(pkvd->szValue); + pkvd->fHandled = TRUE; + } + else + CPointEntity::KeyValue( pkvd ); +} + +void CPathTrack :: Use( CBaseEntity *pActivator, CBaseEntity *pCaller, USE_TYPE useType, float value ) +{ + int on; + + // Use toggles between two paths + if ( m_paltpath ) + { + on = !FBitSet( pev->spawnflags, SF_PATH_ALTERNATE ); + if ( ShouldToggle( useType, on ) ) + { + if ( on ) + SetBits( pev->spawnflags, SF_PATH_ALTERNATE ); + else + ClearBits( pev->spawnflags, SF_PATH_ALTERNATE ); + } + } + else // Use toggles between enabled/disabled + { + on = !FBitSet( pev->spawnflags, SF_PATH_DISABLED ); + + if ( ShouldToggle( useType, on ) ) + { + if ( on ) + SetBits( pev->spawnflags, SF_PATH_DISABLED ); + else + ClearBits( pev->spawnflags, SF_PATH_DISABLED ); + } + } +} + + +void CPathTrack :: Link( void ) +{ + edict_t *pentTarget; + + if ( !FStringNull(pev->target) ) + { + pentTarget = FIND_ENTITY_BY_TARGETNAME( NULL, STRING(pev->target) ); + if ( !FNullEnt(pentTarget) ) + { + m_pnext = CPathTrack::Instance( pentTarget ); + + if ( m_pnext ) // If no next pointer, this is the end of a path + { + m_pnext->SetPrevious( this ); + } + } + else + ALERT( at_console, "Dead end link %s\n", STRING(pev->target) ); + } + + // Find "alternate" path + if ( m_altName ) + { + pentTarget = FIND_ENTITY_BY_TARGETNAME( NULL, STRING(m_altName) ); + if ( !FNullEnt(pentTarget) ) + { + m_paltpath = CPathTrack::Instance( pentTarget ); + + if ( m_paltpath ) // If no next pointer, this is the end of a path + { + m_paltpath->SetPrevious( this ); + } + } + } +} + + +void CPathTrack :: Spawn( void ) +{ + pev->solid = SOLID_TRIGGER; + UTIL_SetSize(pev, Vector(-8, -8, -8), Vector(8, 8, 8)); + + m_pnext = NULL; + m_pprevious = NULL; +// DEBUGGING CODE +#if PATH_SPARKLE_DEBUG + SetThink( Sparkle ); + pev->nextthink = gpGlobals->time + 0.5; +#endif +} + + +void CPathTrack::Activate( void ) +{ + if ( !FStringNull( pev->targetname ) ) // Link to next, and back-link + Link(); +} + +CPathTrack *CPathTrack :: ValidPath( CPathTrack *ppath, int testFlag ) +{ + if ( !ppath ) + return NULL; + + if ( testFlag && FBitSet( ppath->pev->spawnflags, SF_PATH_DISABLED ) ) + return NULL; + + return ppath; +} + + +void CPathTrack :: Project( CPathTrack *pstart, CPathTrack *pend, Vector *origin, float dist ) +{ + if ( pstart && pend ) + { + Vector dir = (pend->pev->origin - pstart->pev->origin); + dir = dir.Normalize(); + *origin = pend->pev->origin + dir * dist; + } +} + +CPathTrack *CPathTrack::GetNext( void ) +{ + if ( m_paltpath && FBitSet( pev->spawnflags, SF_PATH_ALTERNATE ) && !FBitSet( pev->spawnflags, SF_PATH_ALTREVERSE ) ) + return m_paltpath; + + return m_pnext; +} + + + +CPathTrack *CPathTrack::GetPrevious( void ) +{ + if ( m_paltpath && FBitSet( pev->spawnflags, SF_PATH_ALTERNATE ) && FBitSet( pev->spawnflags, SF_PATH_ALTREVERSE ) ) + return m_paltpath; + + return m_pprevious; +} + + + +void CPathTrack::SetPrevious( CPathTrack *pprev ) +{ + // Only set previous if this isn't my alternate path + if ( pprev && !FStrEq( STRING(pprev->pev->targetname), STRING(m_altName) ) ) + m_pprevious = pprev; +} + + +// Assumes this is ALWAYS enabled +CPathTrack *CPathTrack :: LookAhead( Vector *origin, float dist, int move ) +{ + CPathTrack *pcurrent; + float originalDist = dist; + + pcurrent = this; + Vector currentPos = *origin; + + if ( dist < 0 ) // Travelling backwards through path + { + dist = -dist; + while ( dist > 0 ) + { + Vector dir = pcurrent->pev->origin - currentPos; + float length = dir.Length(); + if ( !length ) + { + if ( !ValidPath(pcurrent->GetPrevious(), move) ) // If there is no previous node, or it's disabled, return now. + { + if ( !move ) + Project( pcurrent->GetNext(), pcurrent, origin, dist ); + return NULL; + } + pcurrent = pcurrent->GetPrevious(); + } + else if ( length > dist ) // enough left in this path to move + { + *origin = currentPos + (dir * (dist / length)); + return pcurrent; + } + else + { + dist -= length; + currentPos = pcurrent->pev->origin; + *origin = currentPos; + if ( !ValidPath(pcurrent->GetPrevious(), move) ) // If there is no previous node, or it's disabled, return now. + return NULL; + + pcurrent = pcurrent->GetPrevious(); + } + } + *origin = currentPos; + return pcurrent; + } + else + { + while ( dist > 0 ) + { + if ( !ValidPath(pcurrent->GetNext(), move) ) // If there is no next node, or it's disabled, return now. + { + if ( !move ) + Project( pcurrent->GetPrevious(), pcurrent, origin, dist ); + return NULL; + } + Vector dir = pcurrent->GetNext()->pev->origin - currentPos; + float length = dir.Length(); + if ( !length && !ValidPath( pcurrent->GetNext()->GetNext(), move ) ) + { + if ( dist == originalDist ) // HACK -- up against a dead end + return NULL; + return pcurrent; + } + if ( length > dist ) // enough left in this path to move + { + *origin = currentPos + (dir * (dist / length)); + return pcurrent; + } + else + { + dist -= length; + currentPos = pcurrent->GetNext()->pev->origin; + pcurrent = pcurrent->GetNext(); + *origin = currentPos; + } + } + *origin = currentPos; + } + + return pcurrent; +} + + +// Assumes this is ALWAYS enabled +CPathTrack *CPathTrack :: Nearest( Vector origin ) +{ + int deadCount; + float minDist, dist; + Vector delta; + CPathTrack *ppath, *pnearest; + + + delta = origin - pev->origin; + delta.z = 0; + minDist = delta.Length(); + pnearest = this; + ppath = GetNext(); + + // Hey, I could use the old 2 racing pointers solution to this, but I'm lazy :) + deadCount = 0; + while ( ppath && ppath != this ) + { + deadCount++; + if ( deadCount > 9999 ) + { + ALERT( at_error, "Bad sequence of path_tracks from %s", STRING(pev->targetname) ); + return NULL; + } + delta = origin - ppath->pev->origin; + delta.z = 0; + dist = delta.Length(); + if ( dist < minDist ) + { + minDist = dist; + pnearest = ppath; + } + ppath = ppath->GetNext(); + } + return pnearest; +} + + +CPathTrack *CPathTrack::Instance( edict_t *pent ) +{ + if ( FClassnameIs( pent, "path_track" ) ) + return (CPathTrack *)GET_PRIVATE(pent); + return NULL; +} + + + // DEBUGGING CODE +#if PATH_SPARKLE_DEBUG +void CPathTrack :: Sparkle( void ) +{ + + pev->nextthink = gpGlobals->time + 0.2; + if ( FBitSet( pev->spawnflags, SF_PATH_DISABLED ) ) + UTIL_ParticleEffect(pev->origin, Vector(0,0,100), 210, 10); + else + UTIL_ParticleEffect(pev->origin, Vector(0,0,100), 84, 10); +} +#endif + diff --git a/spirit/plane.cpp b/dlls/plane.cpp similarity index 96% rename from spirit/plane.cpp rename to dlls/plane.cpp index 39a91c32..ff5518c7 100644 --- a/spirit/plane.cpp +++ b/dlls/plane.cpp @@ -1,60 +1,60 @@ -/*** -* -* Copyright (c) 1996-2002, 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. -* -****/ -#include "extdll.h" -#include "plane.h" - -//========================================================= -// Plane -//========================================================= -CPlane :: CPlane ( void ) -{ - m_fInitialized = FALSE; -} - -//========================================================= -// InitializePlane - Takes a normal for the plane and a -// point on the plane and -//========================================================= -void CPlane :: InitializePlane ( const Vector &vecNormal, const Vector &vecPoint ) -{ - m_vecNormal = vecNormal; - m_flDist = DotProduct ( m_vecNormal, vecPoint ); - m_fInitialized = TRUE; -} - - -//========================================================= -// PointInFront - determines whether the given vector is -// in front of the plane. -//========================================================= -BOOL CPlane :: PointInFront ( const Vector &vecPoint ) -{ - float flFace; - - if ( !m_fInitialized ) - { - return FALSE; - } - - flFace = DotProduct ( m_vecNormal, vecPoint ) - m_flDist; - - if ( flFace >= 0 ) - { - return TRUE; - } - - return FALSE; -} - +/*** +* +* Copyright (c) 1996-2002, 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. +* +****/ +#include "extdll.h" +#include "plane.h" + +//========================================================= +// Plane +//========================================================= +CPlane :: CPlane ( void ) +{ + m_fInitialized = FALSE; +} + +//========================================================= +// InitializePlane - Takes a normal for the plane and a +// point on the plane and +//========================================================= +void CPlane :: InitializePlane ( const Vector &vecNormal, const Vector &vecPoint ) +{ + m_vecNormal = vecNormal; + m_flDist = DotProduct ( m_vecNormal, vecPoint ); + m_fInitialized = TRUE; +} + + +//========================================================= +// PointInFront - determines whether the given vector is +// in front of the plane. +//========================================================= +BOOL CPlane :: PointInFront ( const Vector &vecPoint ) +{ + float flFace; + + if ( !m_fInitialized ) + { + return FALSE; + } + + flFace = DotProduct ( m_vecNormal, vecPoint ) - m_flDist; + + if ( flFace >= 0 ) + { + return TRUE; + } + + return FALSE; +} + diff --git a/spirit/plane.h b/dlls/plane.h similarity index 96% rename from spirit/plane.h rename to dlls/plane.h index af70f1cc..a54f2457 100644 --- a/spirit/plane.h +++ b/dlls/plane.h @@ -1,43 +1,43 @@ -/*** -* -* Copyright (c) 1996-2002, 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. -* -****/ -#ifndef PLANE_H -#define PLANE_H - -//========================================================= -// Plane -//========================================================= -class CPlane -{ -public: - CPlane ( void ); - - //========================================================= - // InitializePlane - Takes a normal for the plane and a - // point on the plane and - //========================================================= - void InitializePlane ( const Vector &vecNormal, const Vector &vecPoint ); - - //========================================================= - // PointInFront - determines whether the given vector is - // in front of the plane. - //========================================================= - BOOL PointInFront ( const Vector &vecPoint ); - - Vector m_vecNormal; - float m_flDist; - BOOL m_fInitialized; -}; - -#endif // PLANE_H +/*** +* +* Copyright (c) 1996-2002, 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. +* +****/ +#ifndef PLANE_H +#define PLANE_H + +//========================================================= +// Plane +//========================================================= +class CPlane +{ +public: + CPlane ( void ); + + //========================================================= + // InitializePlane - Takes a normal for the plane and a + // point on the plane and + //========================================================= + void InitializePlane ( const Vector &vecNormal, const Vector &vecPoint ); + + //========================================================= + // PointInFront - determines whether the given vector is + // in front of the plane. + //========================================================= + BOOL PointInFront ( const Vector &vecPoint ); + + Vector m_vecNormal; + float m_flDist; + BOOL m_fInitialized; +}; + +#endif // PLANE_H diff --git a/dlls/plats.cpp b/dlls/plats.cpp new file mode 100644 index 00000000..a2de3e2c --- /dev/null +++ b/dlls/plats.cpp @@ -0,0 +1,2286 @@ +/*** +* +* Copyright (c) 1996-2002, 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. +* +****/ +/* + +===== plats.cpp ======================================================== + + spawn, think, and touch functions for trains, etc + +*/ + +#include "extdll.h" +#include "util.h" +#include "cbase.h" +#include "trains.h" +#include "saverestore.h" + +static void PlatSpawnInsideTrigger(entvars_t* pevPlatform); + +#define SF_PLAT_TOGGLE 0x0001 + +class CBasePlatTrain : public CBaseToggle +{ +public: + virtual int ObjectCaps( void ) { return CBaseEntity :: ObjectCaps() & ~FCAP_ACROSS_TRANSITION; } + void KeyValue( KeyValueData* pkvd); + void Precache( void ); + + // This is done to fix spawn flag collisions between this class and a derived class + virtual BOOL IsTogglePlat( void ) { return (pev->spawnflags & SF_PLAT_TOGGLE) ? TRUE : FALSE; } + + virtual int Save( CSave &save ); + virtual int Restore( CRestore &restore ); + static TYPEDESCRIPTION m_SaveData[]; + + BYTE m_bMoveSnd; // sound a plat makes while moving + BYTE m_bStopSnd; // sound a plat makes when it stops + float m_volume; // Sound volume +}; + +TYPEDESCRIPTION CBasePlatTrain::m_SaveData[] = +{ + DEFINE_FIELD( CBasePlatTrain, m_bMoveSnd, FIELD_CHARACTER ), + DEFINE_FIELD( CBasePlatTrain, m_bStopSnd, FIELD_CHARACTER ), + DEFINE_FIELD( CBasePlatTrain, m_volume, FIELD_FLOAT ), +}; + +IMPLEMENT_SAVERESTORE( CBasePlatTrain, CBaseToggle ); + +void CBasePlatTrain :: KeyValue( KeyValueData *pkvd ) +{ + if (FStrEq(pkvd->szKeyName, "lip")) + { + m_flLip = atof(pkvd->szValue); + pkvd->fHandled = TRUE; + } + else if (FStrEq(pkvd->szKeyName, "wait")) + { + m_flWait = atof(pkvd->szValue); + pkvd->fHandled = TRUE; + } + else if (FStrEq(pkvd->szKeyName, "height")) + { + m_flHeight = atof(pkvd->szValue); + pkvd->fHandled = TRUE; + } + else if (FStrEq(pkvd->szKeyName, "rotation")) + { + m_vecFinalAngle.x = atof(pkvd->szValue); + pkvd->fHandled = TRUE; + } + else if (FStrEq(pkvd->szKeyName, "movesnd")) + { + m_bMoveSnd = atof(pkvd->szValue); + pkvd->fHandled = TRUE; + } + else if (FStrEq(pkvd->szKeyName, "stopsnd")) + { + m_bStopSnd = atof(pkvd->szValue); + pkvd->fHandled = TRUE; + } + else if (FStrEq(pkvd->szKeyName, "volume")) + { + m_volume = atof(pkvd->szValue); + pkvd->fHandled = TRUE; + } + else + CBaseToggle::KeyValue( pkvd ); +} + +#define noiseMoving noise +#define noiseArrived noise1 + +void CBasePlatTrain::Precache( void ) +{ +// set the plat's "in-motion" sound + switch (m_bMoveSnd) + { + case 0: + pev->noiseMoving = MAKE_STRING("common/null.wav"); + break; + case 1: + PRECACHE_SOUND ("plats/bigmove1.wav"); + pev->noiseMoving = MAKE_STRING("plats/bigmove1.wav"); + break; + case 2: + PRECACHE_SOUND ("plats/bigmove2.wav"); + pev->noiseMoving = MAKE_STRING("plats/bigmove2.wav"); + break; + case 3: + PRECACHE_SOUND ("plats/elevmove1.wav"); + pev->noiseMoving = MAKE_STRING("plats/elevmove1.wav"); + break; + case 4: + PRECACHE_SOUND ("plats/elevmove2.wav"); + pev->noiseMoving = MAKE_STRING("plats/elevmove2.wav"); + break; + case 5: + PRECACHE_SOUND ("plats/elevmove3.wav"); + pev->noiseMoving = MAKE_STRING("plats/elevmove3.wav"); + break; + case 6: + PRECACHE_SOUND ("plats/freightmove1.wav"); + pev->noiseMoving = MAKE_STRING("plats/freightmove1.wav"); + break; + case 7: + PRECACHE_SOUND ("plats/freightmove2.wav"); + pev->noiseMoving = MAKE_STRING("plats/freightmove2.wav"); + break; + case 8: + PRECACHE_SOUND ("plats/heavymove1.wav"); + pev->noiseMoving = MAKE_STRING("plats/heavymove1.wav"); + break; + case 9: + PRECACHE_SOUND ("plats/rackmove1.wav"); + pev->noiseMoving = MAKE_STRING("plats/rackmove1.wav"); + break; + case 10: + PRECACHE_SOUND ("plats/railmove1.wav"); + pev->noiseMoving = MAKE_STRING("plats/railmove1.wav"); + break; + case 11: + PRECACHE_SOUND ("plats/squeekmove1.wav"); + pev->noiseMoving = MAKE_STRING("plats/squeekmove1.wav"); + break; + case 12: + PRECACHE_SOUND ("plats/talkmove1.wav"); + pev->noiseMoving = MAKE_STRING("plats/talkmove1.wav"); + break; + case 13: + PRECACHE_SOUND ("plats/talkmove2.wav"); + pev->noiseMoving = MAKE_STRING("plats/talkmove2.wav"); + break; + default: + pev->noiseMoving = MAKE_STRING("common/null.wav"); + break; + } + +// set the plat's 'reached destination' stop sound + switch (m_bStopSnd) + { + case 0: + pev->noiseArrived = MAKE_STRING("common/null.wav"); + break; + case 1: + PRECACHE_SOUND ("plats/bigstop1.wav"); + pev->noiseArrived = MAKE_STRING("plats/bigstop1.wav"); + break; + case 2: + PRECACHE_SOUND ("plats/bigstop2.wav"); + pev->noiseArrived = MAKE_STRING("plats/bigstop2.wav"); + break; + case 3: + PRECACHE_SOUND ("plats/freightstop1.wav"); + pev->noiseArrived = MAKE_STRING("plats/freightstop1.wav"); + break; + case 4: + PRECACHE_SOUND ("plats/heavystop2.wav"); + pev->noiseArrived = MAKE_STRING("plats/heavystop2.wav"); + break; + case 5: + PRECACHE_SOUND ("plats/rackstop1.wav"); + pev->noiseArrived = MAKE_STRING("plats/rackstop1.wav"); + break; + case 6: + PRECACHE_SOUND ("plats/railstop1.wav"); + pev->noiseArrived = MAKE_STRING("plats/railstop1.wav"); + break; + case 7: + PRECACHE_SOUND ("plats/squeekstop1.wav"); + pev->noiseArrived = MAKE_STRING("plats/squeekstop1.wav"); + break; + case 8: + PRECACHE_SOUND ("plats/talkstop1.wav"); + pev->noiseArrived = MAKE_STRING("plats/talkstop1.wav"); + break; + + default: + pev->noiseArrived = MAKE_STRING("common/null.wav"); + break; + } +} + +// +//====================== PLAT code ==================================================== +// + + +#define noiseMovement noise +#define noiseStopMoving noise1 + +class CFuncPlat : public CBasePlatTrain +{ +public: + void Spawn( void ); + void Precache( void ); + void Setup( void ); + + virtual void Blocked( CBaseEntity *pOther ); + + + void EXPORT PlatUse( CBaseEntity *pActivator, CBaseEntity *pCaller, USE_TYPE useType, float value ); + + void EXPORT CallGoDown( void ) { GoDown(); } + void EXPORT CallHitTop( void ) { HitTop(); } + void EXPORT CallHitBottom( void ) { HitBottom(); } + + virtual void GoUp( void ); + virtual void GoDown( void ); + virtual void HitTop( void ); + virtual void HitBottom( void ); +}; +LINK_ENTITY_TO_CLASS( func_plat, CFuncPlat ); + + +// UNDONE: Need to save this!!! It needs class & linkage +class CPlatTrigger : public CBaseEntity +{ +public: + virtual int ObjectCaps( void ) { return (CBaseEntity :: ObjectCaps() & ~FCAP_ACROSS_TRANSITION) | FCAP_DONT_SAVE; } + void SpawnInsideTrigger( CFuncPlat *pPlatform ); + void Touch( CBaseEntity *pOther ); + CFuncPlat *m_pPlatform; +}; + + + +/*QUAKED func_plat (0 .5 .8) ? PLAT_LOW_TRIGGER +speed default 150 + +Plats are always drawn in the extended position, so they will light correctly. + +If the plat is the target of another trigger or button, it will start out disabled in +the extended position until it is trigger, when it will lower and become a normal plat. + +If the "height" key is set, that will determine the amount the plat moves, instead of +being implicitly determined by the model's height. + +Set "sounds" to one of the following: +1) base fast +2) chain slow +*/ + +void CFuncPlat :: Setup( void ) +{ + //pev->noiseMovement = MAKE_STRING("plats/platmove1.wav"); + //pev->noiseStopMoving = MAKE_STRING("plats/platstop1.wav"); + + if (m_flTLength == 0) + m_flTLength = 80; + if (m_flTWidth == 0) + m_flTWidth = 10; + + pev->angles = g_vecZero; + + pev->solid = SOLID_BSP; + pev->movetype = MOVETYPE_PUSH; + + UTIL_SetOrigin(pev, pev->origin); // set size and link into world + UTIL_SetSize(pev, pev->mins, pev->maxs); + SET_MODEL(ENT(pev), STRING(pev->model) ); + + // vecPosition1 is the top position, vecPosition2 is the bottom + m_vecPosition1 = pev->origin; + m_vecPosition2 = pev->origin; + if (m_flHeight != 0) + m_vecPosition2.z = pev->origin.z - m_flHeight; + else + m_vecPosition2.z = pev->origin.z - pev->size.z + 8; + if (pev->speed == 0) + pev->speed = 150; + + if ( m_volume == 0 ) + m_volume = 0.85; +} + + +void CFuncPlat :: Precache( ) +{ + CBasePlatTrain::Precache(); + //PRECACHE_SOUND("plats/platmove1.wav"); + //PRECACHE_SOUND("plats/platstop1.wav"); + if ( !IsTogglePlat() ) + PlatSpawnInsideTrigger( pev ); // the "start moving" trigger +} + + +void CFuncPlat :: Spawn( ) +{ + Setup(); + + Precache(); + + // If this platform is the target of some button, it starts at the TOP position, + // and is brought down by that button. Otherwise, it starts at BOTTOM. + if ( !FStringNull(pev->targetname) ) + { + UTIL_SetOrigin (pev, m_vecPosition1); + m_toggle_state = TS_AT_TOP; + SetUse( PlatUse ); + } + else + { + UTIL_SetOrigin (pev, m_vecPosition2); + m_toggle_state = TS_AT_BOTTOM; + } +} + + + +static void PlatSpawnInsideTrigger(entvars_t* pevPlatform) +{ + GetClassPtr( (CPlatTrigger *)NULL)->SpawnInsideTrigger( GetClassPtr( (CFuncPlat *)pevPlatform ) ); +} + + +// +// Create a trigger entity for a platform. +// +void CPlatTrigger :: SpawnInsideTrigger( CFuncPlat *pPlatform ) +{ + m_pPlatform = pPlatform; + // Create trigger entity, "point" it at the owning platform, give it a touch method + pev->solid = SOLID_TRIGGER; + pev->movetype = MOVETYPE_NONE; + pev->origin = pPlatform->pev->origin; + + // Establish the trigger field's size + Vector vecTMin = m_pPlatform->pev->mins + Vector ( 25 , 25 , 0 ); + Vector vecTMax = m_pPlatform->pev->maxs + Vector ( 25 , 25 , 8 ); + vecTMin.z = vecTMax.z - ( m_pPlatform->m_vecPosition1.z - m_pPlatform->m_vecPosition2.z + 8 ); + if (m_pPlatform->pev->size.x <= 50) + { + vecTMin.x = (m_pPlatform->pev->mins.x + m_pPlatform->pev->maxs.x) / 2; + vecTMax.x = vecTMin.x + 1; + } + if (m_pPlatform->pev->size.y <= 50) + { + vecTMin.y = (m_pPlatform->pev->mins.y + m_pPlatform->pev->maxs.y) / 2; + vecTMax.y = vecTMin.y + 1; + } + UTIL_SetSize ( pev, vecTMin, vecTMax ); +} + + +// +// When the platform's trigger field is touched, the platform ??? +// +void CPlatTrigger :: Touch( CBaseEntity *pOther ) +{ + // Ignore touches by non-players + entvars_t* pevToucher = pOther->pev; + if ( !FClassnameIs (pevToucher, "player") ) + return; + + // Ignore touches by corpses + if (!pOther->IsAlive()) + return; + + // Make linked platform go up/down. + if (m_pPlatform->m_toggle_state == TS_AT_BOTTOM) + m_pPlatform->GoUp(); + else if (m_pPlatform->m_toggle_state == TS_AT_TOP) + m_pPlatform->pev->nextthink = m_pPlatform->pev->ltime + 1;// delay going down +} + + +// +// Used by SUB_UseTargets, when a platform is the target of a button. +// Start bringing platform down. +// +void CFuncPlat :: PlatUse( CBaseEntity *pActivator, CBaseEntity *pCaller, USE_TYPE useType, float value ) +{ + if ( IsTogglePlat() ) + { + // Top is off, bottom is on + BOOL on = (m_toggle_state == TS_AT_BOTTOM) ? TRUE : FALSE; + + if ( !ShouldToggle( useType, on ) ) + return; + + if (m_toggle_state == TS_AT_TOP) + GoDown(); + else if ( m_toggle_state == TS_AT_BOTTOM ) + GoUp(); + } + else + { + SetUse( NULL ); + + if (m_toggle_state == TS_AT_TOP) + GoDown(); + } +} + + +// +// Platform is at top, now starts moving down. +// +void CFuncPlat :: GoDown( void ) +{ + if(pev->noiseMovement) + EMIT_SOUND(ENT(pev), CHAN_STATIC, (char*)STRING(pev->noiseMovement), m_volume, ATTN_NORM); + + ASSERT(m_toggle_state == TS_AT_TOP || m_toggle_state == TS_GOING_UP); + m_toggle_state = TS_GOING_DOWN; + SetMoveDone(CallHitBottom); + LinearMove(m_vecPosition2, pev->speed); +} + + +// +// Platform has hit bottom. Stops and waits forever. +// +void CFuncPlat :: HitBottom( void ) +{ + if(pev->noiseMovement) + STOP_SOUND(ENT(pev), CHAN_STATIC, (char*)STRING(pev->noiseMovement)); + + if (pev->noiseStopMoving) + EMIT_SOUND(ENT(pev), CHAN_WEAPON, (char*)STRING(pev->noiseStopMoving), m_volume, ATTN_NORM); + + ASSERT(m_toggle_state == TS_GOING_DOWN); + m_toggle_state = TS_AT_BOTTOM; +} + + +// +// Platform is at bottom, now starts moving up +// +void CFuncPlat :: GoUp( void ) +{ + if (pev->noiseMovement) + EMIT_SOUND(ENT(pev), CHAN_STATIC, (char*)STRING(pev->noiseMovement), m_volume, ATTN_NORM); + + ASSERT(m_toggle_state == TS_AT_BOTTOM || m_toggle_state == TS_GOING_DOWN); + m_toggle_state = TS_GOING_UP; + SetMoveDone(CallHitTop); + LinearMove(m_vecPosition1, pev->speed); +} + + +// +// Platform has hit top. Pauses, then starts back down again. +// +void CFuncPlat :: HitTop( void ) +{ + if(pev->noiseMovement) + STOP_SOUND(ENT(pev), CHAN_STATIC, (char*)STRING(pev->noiseMovement)); + + if (pev->noiseStopMoving) + EMIT_SOUND(ENT(pev), CHAN_WEAPON, (char*)STRING(pev->noiseStopMoving), m_volume, ATTN_NORM); + + ASSERT(m_toggle_state == TS_GOING_UP); + m_toggle_state = TS_AT_TOP; + + if ( !IsTogglePlat() ) + { + // After a delay, the platform will automatically start going down again. + SetThink( CallGoDown ); + pev->nextthink = pev->ltime + 3; + } +} + + +void CFuncPlat :: Blocked( CBaseEntity *pOther ) +{ + ALERT( at_aiconsole, "%s Blocked by %s\n", STRING(pev->classname), STRING(pOther->pev->classname) ); + // Hurt the blocker a little + pOther->TakeDamage(pev, pev, 1, DMG_CRUSH); + + if(pev->noiseMovement) + STOP_SOUND(ENT(pev), CHAN_STATIC, (char*)STRING(pev->noiseMovement)); + + // Send the platform back where it came from + ASSERT(m_toggle_state == TS_GOING_UP || m_toggle_state == TS_GOING_DOWN); + if (m_toggle_state == TS_GOING_UP) + GoDown(); + else if (m_toggle_state == TS_GOING_DOWN) + GoUp (); +} + + +class CFuncPlatRot : public CFuncPlat +{ +public: + void Spawn( void ); + void SetupRotation( void ); + + virtual void GoUp( void ); + virtual void GoDown( void ); + virtual void HitTop( void ); + virtual void HitBottom( void ); + + void RotMove( Vector &destAngle, float time ); + virtual int Save( CSave &save ); + virtual int Restore( CRestore &restore ); + static TYPEDESCRIPTION m_SaveData[]; + + Vector m_end, m_start; +}; +LINK_ENTITY_TO_CLASS( func_platrot, CFuncPlatRot ); +TYPEDESCRIPTION CFuncPlatRot::m_SaveData[] = +{ + DEFINE_FIELD( CFuncPlatRot, m_end, FIELD_VECTOR ), + DEFINE_FIELD( CFuncPlatRot, m_start, FIELD_VECTOR ), +}; + +IMPLEMENT_SAVERESTORE( CFuncPlatRot, CFuncPlat ); + + +void CFuncPlatRot :: SetupRotation( void ) +{ + if ( m_vecFinalAngle.x != 0 ) // This plat rotates too! + { + CBaseToggle :: AxisDir( pev ); + m_start = pev->angles; + m_end = pev->angles + pev->movedir * m_vecFinalAngle.x; + } + else + { + m_start = g_vecZero; + m_end = g_vecZero; + } + if ( !FStringNull(pev->targetname) ) // Start at top + { + pev->angles = m_end; + } +} + + +void CFuncPlatRot :: Spawn( void ) +{ + CFuncPlat :: Spawn(); + SetupRotation(); +} + +void CFuncPlatRot :: GoDown( void ) +{ + CFuncPlat :: GoDown(); + RotMove( m_start, pev->nextthink - pev->ltime ); +} + + +// +// Platform has hit bottom. Stops and waits forever. +// +void CFuncPlatRot :: HitBottom( void ) +{ + CFuncPlat :: HitBottom(); + pev->avelocity = g_vecZero; + pev->angles = m_start; +} + + +// +// Platform is at bottom, now starts moving up +// +void CFuncPlatRot :: GoUp( void ) +{ + CFuncPlat :: GoUp(); + RotMove( m_end, pev->nextthink - pev->ltime ); +} + + +// +// Platform has hit top. Pauses, then starts back down again. +// +void CFuncPlatRot :: HitTop( void ) +{ + CFuncPlat :: HitTop(); + pev->avelocity = g_vecZero; + pev->angles = m_end; +} + + +void CFuncPlatRot :: RotMove( Vector &destAngle, float time ) +{ + // set destdelta to the vector needed to move + Vector vecDestDelta = destAngle - pev->angles; + + // Travel time is so short, we're practically there already; so make it so. + if ( time >= 0.1) + pev->avelocity = vecDestDelta / time; + else + { + pev->avelocity = vecDestDelta; + pev->nextthink = pev->ltime + 1; + } +} + + +// +//====================== TRAIN code ================================================== +// +#define SF_TRAIN_WAIT_RETRIGGER 1 + +class CFuncTrain : public CBasePlatTrain +{ +public: + void Spawn( void ); + void Precache( void ); + void Activate( void ); + void OverrideReset( void ); + + void Blocked( CBaseEntity *pOther ); + void Use( CBaseEntity *pActivator, CBaseEntity *pCaller, USE_TYPE useType, float value ); + void KeyValue( KeyValueData *pkvd ); + + + void EXPORT Wait( void ); + void EXPORT Next( void ); + virtual int Save( CSave &save ); + virtual int Restore( CRestore &restore ); + static TYPEDESCRIPTION m_SaveData[]; + + entvars_t *m_pevCurrentTarget; + int m_sounds; + BOOL m_activated; +}; + +LINK_ENTITY_TO_CLASS( func_train, CFuncTrain ); +TYPEDESCRIPTION CFuncTrain::m_SaveData[] = +{ + DEFINE_FIELD( CFuncTrain, m_sounds, FIELD_INTEGER ), + DEFINE_FIELD( CFuncTrain, m_pevCurrentTarget, FIELD_EVARS ), + DEFINE_FIELD( CFuncTrain, m_activated, FIELD_BOOLEAN ), +}; + +IMPLEMENT_SAVERESTORE( CFuncTrain, CBasePlatTrain ); + + +void CFuncTrain :: KeyValue( KeyValueData *pkvd ) +{ + if (FStrEq(pkvd->szKeyName, "sounds")) + { + m_sounds = atoi(pkvd->szValue); + pkvd->fHandled = TRUE; + } + else + CBasePlatTrain::KeyValue( pkvd ); +} + + +void CFuncTrain :: Blocked( CBaseEntity *pOther ) + +{ + if ( gpGlobals->time < m_flActivateFinished) + return; + + m_flActivateFinished = gpGlobals->time + 0.5; + + pOther->TakeDamage(pev, pev, pev->dmg, DMG_CRUSH); +} + + +void CFuncTrain :: Use( CBaseEntity *pActivator, CBaseEntity *pCaller, USE_TYPE useType, float value ) +{ + if ( pev->spawnflags & SF_TRAIN_WAIT_RETRIGGER ) + { + // Move toward my target + pev->spawnflags &= ~SF_TRAIN_WAIT_RETRIGGER; + Next(); + } + else + { + pev->spawnflags |= SF_TRAIN_WAIT_RETRIGGER; + // Pop back to last target if it's available + if ( pev->enemy ) + pev->target = pev->enemy->v.targetname; + pev->nextthink = 0; + pev->velocity = g_vecZero; + if ( pev->noiseStopMoving ) + EMIT_SOUND (ENT(pev), CHAN_VOICE, (char*)STRING(pev->noiseStopMoving), m_volume, ATTN_NORM); + } +} + + +void CFuncTrain :: Wait( void ) +{ + // Fire the pass target if there is one + if ( m_pevCurrentTarget->message ) + { + FireTargets( STRING(m_pevCurrentTarget->message), this, this, USE_TOGGLE, 0 ); + if ( FBitSet( m_pevCurrentTarget->spawnflags, SF_CORNER_FIREONCE ) ) + m_pevCurrentTarget->message = 0; + } + + // need pointer to LAST target. + if ( FBitSet (m_pevCurrentTarget->spawnflags , SF_TRAIN_WAIT_RETRIGGER ) || ( pev->spawnflags & SF_TRAIN_WAIT_RETRIGGER ) ) + { + pev->spawnflags |= SF_TRAIN_WAIT_RETRIGGER; + // clear the sound channel. + if ( pev->noiseMovement ) + STOP_SOUND( edict(), CHAN_STATIC, (char*)STRING(pev->noiseMovement) ); + if ( pev->noiseStopMoving ) + EMIT_SOUND (ENT(pev), CHAN_VOICE, (char*)STRING(pev->noiseStopMoving), m_volume, ATTN_NORM); + pev->nextthink = 0; + return; + } + + // ALERT ( at_console, "%f\n", m_flWait ); + + if (m_flWait != 0) + {// -1 wait will wait forever! + pev->nextthink = pev->ltime + m_flWait; + if ( pev->noiseMovement ) + STOP_SOUND( edict(), CHAN_STATIC, (char*)STRING(pev->noiseMovement) ); + if ( pev->noiseStopMoving ) + EMIT_SOUND (ENT(pev), CHAN_VOICE, (char*)STRING(pev->noiseStopMoving), m_volume, ATTN_NORM); + SetThink( Next ); + } + else + { + Next();// do it RIGHT now! + } +} + + +// +// Train next - path corner needs to change to next target +// +void CFuncTrain :: Next( void ) +{ + CBaseEntity *pTarg; + + + // now find our next target + pTarg = GetNextTarget(); + + if ( !pTarg ) + { + if ( pev->noiseMovement ) + STOP_SOUND( edict(), CHAN_STATIC, (char*)STRING(pev->noiseMovement) ); + // Play stop sound + if ( pev->noiseStopMoving ) + EMIT_SOUND (ENT(pev), CHAN_VOICE, (char*)STRING(pev->noiseStopMoving), m_volume, ATTN_NORM); + return; + } + + // Save last target in case we need to find it again + pev->message = pev->target; + + pev->target = pTarg->pev->target; + m_flWait = pTarg->GetDelay(); + + if ( m_pevCurrentTarget && m_pevCurrentTarget->speed != 0 ) + {// don't copy speed from target if it is 0 (uninitialized) + pev->speed = m_pevCurrentTarget->speed; + ALERT( at_aiconsole, "Train %s speed to %4.2f\n", STRING(pev->targetname), pev->speed ); + } + m_pevCurrentTarget = pTarg->pev;// keep track of this since path corners change our target for us. + + pev->enemy = pTarg->edict();//hack + + if(FBitSet(m_pevCurrentTarget->spawnflags, SF_CORNER_TELEPORT)) + { + // Path corner has indicated a teleport to the next corner. + SetBits(pev->effects, EF_NOINTERP); + UTIL_SetOrigin(pev, pTarg->pev->origin - (pev->mins + pev->maxs)* 0.5); + Wait(); // Get on with doing the next path corner. + } + else + { + // Normal linear move. + + // CHANGED this from CHAN_VOICE to CHAN_STATIC around OEM beta time because trains should + // use CHAN_STATIC for their movement sounds to prevent sound field problems. + // this is not a hack or temporary fix, this is how things should be. (sjb). + if ( pev->noiseMovement ) + STOP_SOUND( edict(), CHAN_STATIC, (char*)STRING(pev->noiseMovement) ); + if ( pev->noiseMovement ) + EMIT_SOUND (ENT(pev), CHAN_STATIC, (char*)STRING(pev->noiseMovement), m_volume, ATTN_NORM); + ClearBits(pev->effects, EF_NOINTERP); + SetMoveDone( Wait ); + LinearMove (pTarg->pev->origin - (pev->mins + pev->maxs)* 0.5, pev->speed); + } +} + + +void CFuncTrain :: Activate( void ) +{ + // Not yet active, so teleport to first target + if ( !m_activated ) + { + m_activated = TRUE; + entvars_t *pevTarg = VARS( FIND_ENTITY_BY_TARGETNAME (NULL, STRING(pev->target) ) ); + + pev->target = pevTarg->target; + m_pevCurrentTarget = pevTarg;// keep track of this since path corners change our target for us. + + UTIL_SetOrigin (pev, pevTarg->origin - (pev->mins + pev->maxs) * 0.5 ); + + if ( FStringNull(pev->targetname) ) + { // not triggered, so start immediately + pev->nextthink = pev->ltime + 0.1; + SetThink( Next ); + } + else + pev->spawnflags |= SF_TRAIN_WAIT_RETRIGGER; + } +} + +/*QUAKED func_train (0 .5 .8) ? +Trains are moving platforms that players can ride. +The targets origin specifies the min point of the train at each corner. +The train spawns at the first target it is pointing at. +If the train is the target of a button or trigger, it will not begin moving until activated. +speed default 100 +dmg default 2 +sounds +1) ratchet metal +*/ + +void CFuncTrain :: Spawn( void ) +{ + Precache(); + if (pev->speed == 0) + pev->speed = 100; + + if ( FStringNull(pev->target) ) + ALERT(at_console, "FuncTrain with no target"); + + if (pev->dmg == 0) + pev->dmg = 2; + + pev->movetype = MOVETYPE_PUSH; + + if ( FBitSet (pev->spawnflags, SF_TRACKTRAIN_PASSABLE) ) + pev->solid = SOLID_NOT; + else + pev->solid = SOLID_BSP; + + SET_MODEL( ENT(pev), STRING(pev->model) ); + UTIL_SetSize (pev, pev->mins, pev->maxs); + UTIL_SetOrigin(pev, pev->origin); + + m_activated = FALSE; + + if ( m_volume == 0 ) + m_volume = 0.85; +} + + +void CFuncTrain::Precache( void ) +{ + CBasePlatTrain::Precache(); + +#if 0 // obsolete + // otherwise use preset sound + switch (m_sounds) + { + case 0: + pev->noise = 0; + pev->noise1 = 0; + break; + + case 1: + PRECACHE_SOUND ("plats/train2.wav"); + PRECACHE_SOUND ("plats/train1.wav"); + pev->noise = MAKE_STRING("plats/train2.wav"); + pev->noise1 = MAKE_STRING("plats/train1.wav"); + break; + + case 2: + PRECACHE_SOUND ("plats/platmove1.wav"); + PRECACHE_SOUND ("plats/platstop1.wav"); + pev->noise = MAKE_STRING("plats/platstop1.wav"); + pev->noise1 = MAKE_STRING("plats/platmove1.wav"); + break; + } +#endif +} + + +void CFuncTrain::OverrideReset( void ) +{ + CBaseEntity *pTarg; + + // Are we moving? + if ( pev->velocity != g_vecZero && pev->nextthink != 0 ) + { + pev->target = pev->message; + // now find our next target + pTarg = GetNextTarget(); + if ( !pTarg ) + { + pev->nextthink = 0; + pev->velocity = g_vecZero; + } + else // Keep moving for 0.1 secs, then find path_corner again and restart + { + SetThink( Next ); + pev->nextthink = pev->ltime + 0.1; + } + } +} + + + + +// --------------------------------------------------------------------- +// +// Track Train +// +// --------------------------------------------------------------------- + +TYPEDESCRIPTION CFuncTrackTrain::m_SaveData[] = +{ + DEFINE_FIELD( CFuncTrackTrain, m_ppath, FIELD_CLASSPTR ), + DEFINE_FIELD( CFuncTrackTrain, m_length, FIELD_FLOAT ), + DEFINE_FIELD( CFuncTrackTrain, m_height, FIELD_FLOAT ), + DEFINE_FIELD( CFuncTrackTrain, m_speed, FIELD_FLOAT ), + DEFINE_FIELD( CFuncTrackTrain, m_dir, FIELD_FLOAT ), + DEFINE_FIELD( CFuncTrackTrain, m_startSpeed, FIELD_FLOAT ), + DEFINE_FIELD( CFuncTrackTrain, m_controlMins, FIELD_VECTOR ), + DEFINE_FIELD( CFuncTrackTrain, m_controlMaxs, FIELD_VECTOR ), + DEFINE_FIELD( CFuncTrackTrain, m_sounds, FIELD_INTEGER ), + DEFINE_FIELD( CFuncTrackTrain, m_flVolume, FIELD_FLOAT ), + DEFINE_FIELD( CFuncTrackTrain, m_flBank, FIELD_FLOAT ), + DEFINE_FIELD( CFuncTrackTrain, m_oldSpeed, FIELD_FLOAT ), +}; + +IMPLEMENT_SAVERESTORE( CFuncTrackTrain, CBaseEntity ); +LINK_ENTITY_TO_CLASS( func_tracktrain, CFuncTrackTrain ); + +void CFuncTrackTrain :: KeyValue( KeyValueData *pkvd ) +{ + if (FStrEq(pkvd->szKeyName, "wheels")) + { + m_length = atof(pkvd->szValue); + pkvd->fHandled = TRUE; + } + else if (FStrEq(pkvd->szKeyName, "height")) + { + m_height = atof(pkvd->szValue); + pkvd->fHandled = TRUE; + } + else if (FStrEq(pkvd->szKeyName, "startspeed")) + { + m_startSpeed = atof(pkvd->szValue); + pkvd->fHandled = TRUE; + } + else if (FStrEq(pkvd->szKeyName, "sounds")) + { + m_sounds = atoi(pkvd->szValue); + pkvd->fHandled = TRUE; + } + else if (FStrEq(pkvd->szKeyName, "volume")) + { + m_flVolume = (float) (atoi(pkvd->szValue)); + m_flVolume *= 0.1; + pkvd->fHandled = TRUE; + } + else if (FStrEq(pkvd->szKeyName, "bank")) + { + m_flBank = atof(pkvd->szValue); + pkvd->fHandled = TRUE; + } + else + CBaseEntity::KeyValue( pkvd ); +} + + +void CFuncTrackTrain :: NextThink( float thinkTime, BOOL alwaysThink ) +{ + if ( alwaysThink ) + pev->flags |= FL_ALWAYSTHINK; + else + pev->flags &= ~FL_ALWAYSTHINK; + + pev->nextthink = thinkTime; +} + + +void CFuncTrackTrain :: Blocked( CBaseEntity *pOther ) +{ + entvars_t *pevOther = pOther->pev; + + // Blocker is on-ground on the train + if ( FBitSet( pevOther->flags, FL_ONGROUND ) && VARS(pevOther->groundentity) == pev ) + { + float deltaSpeed = fabs(pev->speed); + if ( deltaSpeed > 50 ) + deltaSpeed = 50; + if ( !pevOther->velocity.z ) + pevOther->velocity.z += deltaSpeed; + return; + } + else + pevOther->velocity = (pevOther->origin - pev->origin ).Normalize() * pev->dmg; + + ALERT( at_aiconsole, "TRAIN(%s): Blocked by %s (dmg:%.2f)\n", STRING(pev->targetname), STRING(pOther->pev->classname), pev->dmg ); + if ( pev->dmg <= 0 ) + return; + // we can't hurt this thing, so we're not concerned with it + pOther->TakeDamage(pev, pev, pev->dmg, DMG_CRUSH); +} + + +void CFuncTrackTrain :: Use( CBaseEntity *pActivator, CBaseEntity *pCaller, USE_TYPE useType, float value ) +{ + if ( useType != USE_SET ) + { + if ( !ShouldToggle( useType, (pev->speed != 0) ) ) + return; + + if ( pev->speed == 0 ) + { + pev->speed = m_speed * m_dir; + + Next(); + } + else + { + pev->speed = 0; + pev->velocity = g_vecZero; + pev->avelocity = g_vecZero; + StopSound(); + SetThink( NULL ); + } + } + else + { + float delta = value; + + delta = ((int)(pev->speed * 4) / (int)m_speed)*0.25 + 0.25 * delta; + if ( delta > 1 ) + delta = 1; + else if ( delta < -1 ) + delta = -1; + if ( pev->spawnflags & SF_TRACKTRAIN_FORWARDONLY ) + { + if ( delta < 0 ) + delta = 0; + } + pev->speed = m_speed * delta; + Next(); + ALERT( at_aiconsole, "TRAIN(%s), speed to %.2f\n", STRING(pev->targetname), pev->speed ); + } +} + + +static float Fix( float angle ) +{ + while ( angle < 0 ) + angle += 360; + while ( angle > 360 ) + angle -= 360; + + return angle; +} + + +static void FixupAngles( Vector &v ) +{ + v.x = Fix( v.x ); + v.y = Fix( v.y ); + v.z = Fix( v.z ); +} + +#define TRAIN_STARTPITCH 60 +#define TRAIN_MAXPITCH 200 +#define TRAIN_MAXSPEED 1000 // approx max speed for sound pitch calculation + +void CFuncTrackTrain :: StopSound( void ) +{ + // if sound playing, stop it + if (m_soundPlaying && pev->noise) + { + unsigned short us_encode; + unsigned short us_sound = ( ( unsigned short )( m_sounds ) & 0x0007 ) << 12; + + us_encode = us_sound; + + PLAYBACK_EVENT_FULL( FEV_RELIABLE | FEV_UPDATE, edict(), m_usAdjustPitch, 0.0, + (float *)&g_vecZero, (float *)&g_vecZero, 0.0, 0.0, us_encode, 0, 1, 0 ); + + /* + STOP_SOUND(ENT(pev), CHAN_STATIC, (char*)STRING(pev->noise)); + */ + EMIT_SOUND_DYN(ENT(pev), CHAN_ITEM, "plats/ttrain_brake1.wav", m_flVolume, ATTN_NORM, 0, 100); + } + + m_soundPlaying = 0; +} + +// update pitch based on speed, start sound if not playing +// NOTE: when train goes through transition, m_soundPlaying should go to 0, +// which will cause the looped sound to restart. + +void CFuncTrackTrain :: UpdateSound( void ) +{ + float flpitch; + + if (!pev->noise) + return; + + flpitch = TRAIN_STARTPITCH + (abs(pev->speed) * (TRAIN_MAXPITCH - TRAIN_STARTPITCH) / TRAIN_MAXSPEED); + + if (!m_soundPlaying) + { + // play startup sound for train + EMIT_SOUND_DYN(ENT(pev), CHAN_ITEM, "plats/ttrain_start1.wav", m_flVolume, ATTN_NORM, 0, 100); + EMIT_SOUND_DYN(ENT(pev), CHAN_STATIC, (char*)STRING(pev->noise), m_flVolume, ATTN_NORM, 0, (int) flpitch); + m_soundPlaying = 1; + } + else + { +/* + // update pitch + EMIT_SOUND_DYN(ENT(pev), CHAN_STATIC, (char*)STRING(pev->noise), m_flVolume, ATTN_NORM, SND_CHANGE_PITCH, (int) flpitch); +*/ + // volume 0.0 - 1.0 - 6 bits + // m_sounds 3 bits + // flpitch = 6 bits + // 15 bits total + + unsigned short us_encode; + unsigned short us_sound = ( ( unsigned short )( m_sounds ) & 0x0007 ) << 12; + unsigned short us_pitch = ( ( unsigned short )( flpitch / 10.0 ) & 0x003f ) << 6; + unsigned short us_volume = ( ( unsigned short )( m_flVolume * 40.0 ) & 0x003f ); + + us_encode = us_sound | us_pitch | us_volume; + + PLAYBACK_EVENT_FULL( FEV_RELIABLE | FEV_UPDATE, edict(), m_usAdjustPitch, 0.0, + (float *)&g_vecZero, (float *)&g_vecZero, 0.0, 0.0, us_encode, 0, 0, 0 ); + } +} + + +void CFuncTrackTrain :: Next( void ) +{ + float time = 0.5; + + if ( !pev->speed ) + { + ALERT( at_aiconsole, "TRAIN(%s): Speed is 0\n", STRING(pev->targetname) ); + StopSound(); + return; + } + +// if ( !m_ppath ) +// m_ppath = CPathTrack::Instance(FIND_ENTITY_BY_TARGETNAME( NULL, STRING(pev->target) )); + if ( !m_ppath ) + { + ALERT( at_aiconsole, "TRAIN(%s): Lost path\n", STRING(pev->targetname) ); + StopSound(); + return; + } + + UpdateSound(); + + Vector nextPos = pev->origin; + + nextPos.z -= m_height; + CPathTrack *pnext = m_ppath->LookAhead( &nextPos, pev->speed * 0.1, 1 ); + nextPos.z += m_height; + + pev->velocity = (nextPos - pev->origin) * 10; + Vector nextFront = pev->origin; + + nextFront.z -= m_height; + if ( m_length > 0 ) + m_ppath->LookAhead( &nextFront, m_length, 0 ); + else + m_ppath->LookAhead( &nextFront, 100, 0 ); + nextFront.z += m_height; + + Vector delta = nextFront - pev->origin; + Vector angles = UTIL_VecToAngles( delta ); + // The train actually points west + angles.y += 180; + + // !!! All of this crap has to be done to make the angles not wrap around, revisit this. + FixupAngles( angles ); + FixupAngles( pev->angles ); + + if ( !pnext || (delta.x == 0 && delta.y == 0) ) + angles = pev->angles; + + float vy, vx; + if ( !(pev->spawnflags & SF_TRACKTRAIN_NOPITCH) ) + vx = UTIL_AngleDistance( angles.x, pev->angles.x ); + else + vx = 0; + vy = UTIL_AngleDistance( angles.y, pev->angles.y ); + + pev->avelocity.y = vy * 10; + pev->avelocity.x = vx * 10; + + if ( m_flBank != 0 ) + { + if ( pev->avelocity.y < -5 ) + pev->avelocity.z = UTIL_AngleDistance( UTIL_ApproachAngle( -m_flBank, pev->angles.z, m_flBank*2 ), pev->angles.z); + else if ( pev->avelocity.y > 5 ) + pev->avelocity.z = UTIL_AngleDistance( UTIL_ApproachAngle( m_flBank, pev->angles.z, m_flBank*2 ), pev->angles.z); + else + pev->avelocity.z = UTIL_AngleDistance( UTIL_ApproachAngle( 0, pev->angles.z, m_flBank*4 ), pev->angles.z) * 4; + } + + if ( pnext ) + { + if ( pnext != m_ppath ) + { + CPathTrack *pFire; + if ( pev->speed >= 0 ) + pFire = pnext; + else + pFire = m_ppath; + + m_ppath = pnext; + // Fire the pass target if there is one + if ( pFire->pev->message ) + { + FireTargets( STRING(pFire->pev->message), this, this, USE_TOGGLE, 0 ); + if ( FBitSet( pFire->pev->spawnflags, SF_PATH_FIREONCE ) ) + pFire->pev->message = 0; + } + + if ( pFire->pev->spawnflags & SF_PATH_DISABLE_TRAIN ) + pev->spawnflags |= SF_TRACKTRAIN_NOCONTROL; + + // Don't override speed if under user control + if ( pev->spawnflags & SF_TRACKTRAIN_NOCONTROL ) + { + if ( pFire->pev->speed != 0 ) + {// don't copy speed from target if it is 0 (uninitialized) + pev->speed = pFire->pev->speed; + ALERT( at_aiconsole, "TrackTrain %s speed to %4.2f\n", STRING(pev->targetname), pev->speed ); + } + } + + } + SetThink( Next ); + NextThink( pev->ltime + time, TRUE ); + } + else // end of path, stop + { + StopSound(); + pev->velocity = (nextPos - pev->origin); + pev->avelocity = g_vecZero; + float distance = pev->velocity.Length(); + m_oldSpeed = pev->speed; + + + pev->speed = 0; + + // Move to the dead end + + // Are we there yet? + if ( distance > 0 ) + { + // no, how long to get there? + time = distance / m_oldSpeed; + pev->velocity = pev->velocity * (m_oldSpeed / distance); + SetThink( DeadEnd ); + NextThink( pev->ltime + time, FALSE ); + } + else + { + DeadEnd(); + } + } +} + + +void CFuncTrackTrain::DeadEnd( void ) +{ + // Fire the dead-end target if there is one + CPathTrack *pTrack, *pNext; + + pTrack = m_ppath; + + ALERT( at_aiconsole, "TRAIN(%s): Dead end ", STRING(pev->targetname) ); + // Find the dead end path node + // HACKHACK -- This is bugly, but the train can actually stop moving at a different node depending on it's speed + // so we have to traverse the list to it's end. + if ( pTrack ) + { + if ( m_oldSpeed < 0 ) + { + do + { + pNext = pTrack->ValidPath( pTrack->GetPrevious(), TRUE ); + if ( pNext ) + pTrack = pNext; + } while ( pNext ); + } + else + { + do + { + pNext = pTrack->ValidPath( pTrack->GetNext(), TRUE ); + if ( pNext ) + pTrack = pNext; + } while ( pNext ); + } + } + + pev->velocity = g_vecZero; + pev->avelocity = g_vecZero; + if ( pTrack ) + { + ALERT( at_aiconsole, "at %s\n", STRING(pTrack->pev->targetname) ); + if ( pTrack->pev->netname ) + FireTargets( STRING(pTrack->pev->netname), this, this, USE_TOGGLE, 0 ); + } + else + ALERT( at_aiconsole, "\n" ); +} + + +void CFuncTrackTrain :: SetControls( entvars_t *pevControls ) +{ + Vector offset = pevControls->origin - pev->oldorigin; + + m_controlMins = pevControls->mins + offset; + m_controlMaxs = pevControls->maxs + offset; +} + + +BOOL CFuncTrackTrain :: OnControls( entvars_t *pevTest ) +{ + Vector offset = pevTest->origin - pev->origin; + + if ( pev->spawnflags & SF_TRACKTRAIN_NOCONTROL ) + return FALSE; + + // Transform offset into local coordinates + UTIL_MakeVectors( pev->angles ); + Vector local; + local.x = DotProduct( offset, gpGlobals->v_forward ); + local.y = -DotProduct( offset, gpGlobals->v_right ); + local.z = DotProduct( offset, gpGlobals->v_up ); + + if ( local.x >= m_controlMins.x && local.y >= m_controlMins.y && local.z >= m_controlMins.z && + local.x <= m_controlMaxs.x && local.y <= m_controlMaxs.y && local.z <= m_controlMaxs.z ) + return TRUE; + + return FALSE; +} + + +void CFuncTrackTrain :: Find( void ) +{ + m_ppath = CPathTrack::Instance(FIND_ENTITY_BY_TARGETNAME( NULL, STRING(pev->target) )); + if ( !m_ppath ) + return; + + entvars_t *pevTarget = m_ppath->pev; + if ( !FClassnameIs( pevTarget, "path_track" ) ) + { + ALERT( at_error, "func_track_train must be on a path of path_track\n" ); + m_ppath = NULL; + return; + } + + Vector nextPos = pevTarget->origin; + nextPos.z += m_height; + + Vector look = nextPos; + look.z -= m_height; + m_ppath->LookAhead( &look, m_length, 0 ); + look.z += m_height; + + pev->angles = UTIL_VecToAngles( look - nextPos ); + // The train actually points west + pev->angles.y += 180; + + if ( pev->spawnflags & SF_TRACKTRAIN_NOPITCH ) + pev->angles.x = 0; + UTIL_SetOrigin( pev, nextPos ); + NextThink( pev->ltime + 0.1, FALSE ); + SetThink( Next ); + pev->speed = m_startSpeed; + + UpdateSound(); +} + + +void CFuncTrackTrain :: NearestPath( void ) +{ + CBaseEntity *pTrack = NULL; + CBaseEntity *pNearest = NULL; + float dist, closest; + + closest = 1024; + + while ((pTrack = UTIL_FindEntityInSphere( pTrack, pev->origin, 1024 )) != NULL) + { + // filter out non-tracks + if ( !(pTrack->pev->flags & (FL_CLIENT|FL_MONSTER)) && FClassnameIs( pTrack->pev, "path_track" ) ) + { + dist = (pev->origin - pTrack->pev->origin).Length(); + if ( dist < closest ) + { + closest = dist; + pNearest = pTrack; + } + } + } + + if ( !pNearest ) + { + ALERT( at_console, "Can't find a nearby track !!!\n" ); + SetThink(NULL); + return; + } + + ALERT( at_aiconsole, "TRAIN: %s, Nearest track is %s\n", STRING(pev->targetname), STRING(pNearest->pev->targetname) ); + // If I'm closer to the next path_track on this path, then it's my real path + pTrack = ((CPathTrack *)pNearest)->GetNext(); + if ( pTrack ) + { + if ( (pev->origin - pTrack->pev->origin).Length() < (pev->origin - pNearest->pev->origin).Length() ) + pNearest = pTrack; + } + + m_ppath = (CPathTrack *)pNearest; + + if ( pev->speed != 0 ) + { + NextThink( pev->ltime + 0.1, FALSE ); + SetThink( Next ); + } +} + + +void CFuncTrackTrain::OverrideReset( void ) +{ + NextThink( pev->ltime + 0.1, FALSE ); + SetThink( NearestPath ); +} + + +CFuncTrackTrain *CFuncTrackTrain::Instance( edict_t *pent ) +{ + if ( FClassnameIs( pent, "func_tracktrain" ) ) + return (CFuncTrackTrain *)GET_PRIVATE(pent); + return NULL; +} + +/*QUAKED func_train (0 .5 .8) ? +Trains are moving platforms that players can ride. +The targets origin specifies the min point of the train at each corner. +The train spawns at the first target it is pointing at. +If the train is the target of a button or trigger, it will not begin moving until activated. +speed default 100 +dmg default 2 +sounds +1) ratchet metal +*/ + +void CFuncTrackTrain :: Spawn( void ) +{ + if ( pev->speed == 0 ) + m_speed = 100; + else + m_speed = pev->speed; + + pev->speed = 0; + pev->velocity = g_vecZero; + pev->avelocity = g_vecZero; + pev->impulse = m_speed; + + m_dir = 1; + + if ( FStringNull(pev->target) ) + ALERT( at_console, "FuncTrain with no target" ); + + if ( pev->spawnflags & SF_TRACKTRAIN_PASSABLE ) + pev->solid = SOLID_NOT; + else + pev->solid = SOLID_BSP; + pev->movetype = MOVETYPE_PUSH; + + SET_MODEL( ENT(pev), STRING(pev->model) ); + + UTIL_SetSize( pev, pev->mins, pev->maxs ); + UTIL_SetOrigin( pev, pev->origin ); + + // Cache off placed origin for train controls + pev->oldorigin = pev->origin; + + m_controlMins = pev->mins; + m_controlMaxs = pev->maxs; + m_controlMaxs.z += 72; +// start trains on the next frame, to make sure their targets have had +// a chance to spawn/activate + NextThink( pev->ltime + 0.1, FALSE ); + SetThink( Find ); + Precache(); +} + +void CFuncTrackTrain :: Precache( void ) +{ + if (m_flVolume == 0.0) + m_flVolume = 1.0; + + switch (m_sounds) + { + default: + // no sound + pev->noise = 0; + break; + case 1: PRECACHE_SOUND("plats/ttrain1.wav"); pev->noise = MAKE_STRING("plats/ttrain1.wav");break; + case 2: PRECACHE_SOUND("plats/ttrain2.wav"); pev->noise = MAKE_STRING("plats/ttrain2.wav");break; + case 3: PRECACHE_SOUND("plats/ttrain3.wav"); pev->noise = MAKE_STRING("plats/ttrain3.wav");break; + case 4: PRECACHE_SOUND("plats/ttrain4.wav"); pev->noise = MAKE_STRING("plats/ttrain4.wav");break; + case 5: PRECACHE_SOUND("plats/ttrain6.wav"); pev->noise = MAKE_STRING("plats/ttrain6.wav");break; + case 6: PRECACHE_SOUND("plats/ttrain7.wav"); pev->noise = MAKE_STRING("plats/ttrain7.wav");break; + } + + PRECACHE_SOUND("plats/ttrain_brake1.wav"); + PRECACHE_SOUND("plats/ttrain_start1.wav"); + + m_usAdjustPitch = PRECACHE_EVENT( 1, "events/train.sc" ); +} + +// This class defines the volume of space that the player must stand in to control the train +class CFuncTrainControls : public CBaseEntity +{ +public: + virtual int ObjectCaps( void ) { return CBaseEntity :: ObjectCaps() & ~FCAP_ACROSS_TRANSITION; } + void Spawn( void ); + void EXPORT Find( void ); +}; +LINK_ENTITY_TO_CLASS( func_traincontrols, CFuncTrainControls ); + + +void CFuncTrainControls :: Find( void ) +{ + edict_t *pTarget = NULL; + + do + { + pTarget = FIND_ENTITY_BY_TARGETNAME( pTarget, STRING(pev->target) ); + } while ( !FNullEnt(pTarget) && !FClassnameIs(pTarget, "func_tracktrain") ); + + if ( FNullEnt( pTarget ) ) + { + ALERT( at_console, "No train %s\n", STRING(pev->target) ); + return; + } + + CFuncTrackTrain *ptrain = CFuncTrackTrain::Instance(pTarget); + ptrain->SetControls( pev ); + UTIL_Remove( this ); +} + + +void CFuncTrainControls :: Spawn( void ) +{ + pev->solid = SOLID_NOT; + pev->movetype = MOVETYPE_NONE; + SET_MODEL( ENT(pev), STRING(pev->model) ); + + UTIL_SetSize( pev, pev->mins, pev->maxs ); + UTIL_SetOrigin( pev, pev->origin ); + + SetThink( Find ); + pev->nextthink = gpGlobals->time; +} + + + +// ---------------------------------------------------------------------------- +// +// Track changer / Train elevator +// +// ---------------------------------------------------------------------------- + +#define SF_TRACK_ACTIVATETRAIN 0x00000001 +#define SF_TRACK_RELINK 0x00000002 +#define SF_TRACK_ROTMOVE 0x00000004 +#define SF_TRACK_STARTBOTTOM 0x00000008 +#define SF_TRACK_DONT_MOVE 0x00000010 + +// +// This entity is a rotating/moving platform that will carry a train to a new track. +// It must be larger in X-Y planar area than the train, since it must contain the +// train within these dimensions in order to operate when the train is near it. +// + +typedef enum { TRAIN_SAFE, TRAIN_BLOCKING, TRAIN_FOLLOWING } TRAIN_CODE; + +class CFuncTrackChange : public CFuncPlatRot +{ +public: + void Spawn( void ); + void Precache( void ); + +// virtual void Blocked( void ); + virtual void EXPORT GoUp( void ); + virtual void EXPORT GoDown( void ); + + void KeyValue( KeyValueData* pkvd ); + void Use( CBaseEntity *pActivator, CBaseEntity *pCaller, USE_TYPE useType, float value ); + void EXPORT Find( void ); + TRAIN_CODE EvaluateTrain( CPathTrack *pcurrent ); + void UpdateTrain( Vector &dest ); + virtual void HitBottom( void ); + virtual void HitTop( void ); + void Touch( CBaseEntity *pOther ); + virtual void UpdateAutoTargets( int toggleState ); + virtual BOOL IsTogglePlat( void ) { return TRUE; } + + void DisableUse( void ) { m_use = 0; } + void EnableUse( void ) { m_use = 1; } + int UseEnabled( void ) { return m_use; } + + virtual int Save( CSave &save ); + virtual int Restore( CRestore &restore ); + static TYPEDESCRIPTION m_SaveData[]; + + virtual void OverrideReset( void ); + + + CPathTrack *m_trackTop; + CPathTrack *m_trackBottom; + + CFuncTrackTrain *m_train; + + int m_trackTopName; + int m_trackBottomName; + int m_trainName; + TRAIN_CODE m_code; + int m_targetState; + int m_use; +}; +LINK_ENTITY_TO_CLASS( func_trackchange, CFuncTrackChange ); + +TYPEDESCRIPTION CFuncTrackChange::m_SaveData[] = +{ + DEFINE_GLOBAL_FIELD( CFuncTrackChange, m_trackTop, FIELD_CLASSPTR ), + DEFINE_GLOBAL_FIELD( CFuncTrackChange, m_trackBottom, FIELD_CLASSPTR ), + DEFINE_GLOBAL_FIELD( CFuncTrackChange, m_train, FIELD_CLASSPTR ), + DEFINE_GLOBAL_FIELD( CFuncTrackChange, m_trackTopName, FIELD_STRING ), + DEFINE_GLOBAL_FIELD( CFuncTrackChange, m_trackBottomName, FIELD_STRING ), + DEFINE_GLOBAL_FIELD( CFuncTrackChange, m_trainName, FIELD_STRING ), + DEFINE_FIELD( CFuncTrackChange, m_code, FIELD_INTEGER ), + DEFINE_FIELD( CFuncTrackChange, m_targetState, FIELD_INTEGER ), + DEFINE_FIELD( CFuncTrackChange, m_use, FIELD_INTEGER ), +}; + +IMPLEMENT_SAVERESTORE( CFuncTrackChange, CFuncPlatRot ); + +void CFuncTrackChange :: Spawn( void ) +{ + Setup(); + if ( FBitSet( pev->spawnflags, SF_TRACK_DONT_MOVE ) ) + m_vecPosition2.z = pev->origin.z; + + SetupRotation(); + + if ( FBitSet( pev->spawnflags, SF_TRACK_STARTBOTTOM ) ) + { + UTIL_SetOrigin (pev, m_vecPosition2); + m_toggle_state = TS_AT_BOTTOM; + pev->angles = m_start; + m_targetState = TS_AT_TOP; + } + else + { + UTIL_SetOrigin (pev, m_vecPosition1); + m_toggle_state = TS_AT_TOP; + pev->angles = m_end; + m_targetState = TS_AT_BOTTOM; + } + + EnableUse(); + pev->nextthink = pev->ltime + 2.0; + SetThink( Find ); + Precache(); +} + +void CFuncTrackChange :: Precache( void ) +{ + // Can't trigger sound + PRECACHE_SOUND( "buttons/button11.wav" ); + + CFuncPlatRot::Precache(); +} + + +// UNDONE: Filter touches before re-evaluating the train. +void CFuncTrackChange :: Touch( CBaseEntity *pOther ) +{ +#if 0 + TRAIN_CODE code; + entvars_t *pevToucher = pOther->pev; +#endif +} + + + +void CFuncTrackChange :: KeyValue( KeyValueData *pkvd ) +{ + if ( FStrEq(pkvd->szKeyName, "train") ) + { + m_trainName = ALLOC_STRING( pkvd->szValue ); + pkvd->fHandled = TRUE; + } + else if ( FStrEq(pkvd->szKeyName, "toptrack") ) + { + m_trackTopName = ALLOC_STRING( pkvd->szValue ); + pkvd->fHandled = TRUE; + } + else if ( FStrEq(pkvd->szKeyName, "bottomtrack") ) + { + m_trackBottomName = ALLOC_STRING( pkvd->szValue ); + pkvd->fHandled = TRUE; + } + else + { + CFuncPlatRot::KeyValue( pkvd ); // Pass up to base class + } +} + + +void CFuncTrackChange::OverrideReset( void ) +{ + pev->nextthink = pev->ltime + 1.0; + SetThink( Find ); +} + +void CFuncTrackChange :: Find( void ) +{ + // Find track entities + edict_t *target; + + target = FIND_ENTITY_BY_TARGETNAME( NULL, STRING(m_trackTopName) ); + if ( !FNullEnt(target) ) + { + m_trackTop = CPathTrack::Instance( target ); + target = FIND_ENTITY_BY_TARGETNAME( NULL, STRING(m_trackBottomName) ); + if ( !FNullEnt(target) ) + { + m_trackBottom = CPathTrack::Instance( target ); + target = FIND_ENTITY_BY_TARGETNAME( NULL, STRING(m_trainName) ); + if ( !FNullEnt(target) ) + { + m_train = CFuncTrackTrain::Instance( FIND_ENTITY_BY_TARGETNAME( NULL, STRING(m_trainName) ) ); + if ( !m_train ) + { + ALERT( at_error, "Can't find train for track change! %s\n", STRING(m_trainName) ); + return; + } + Vector center = (pev->absmin + pev->absmax) * 0.5; + m_trackBottom = m_trackBottom->Nearest( center ); + m_trackTop = m_trackTop->Nearest( center ); + UpdateAutoTargets( m_toggle_state ); + SetThink( NULL ); + return; + } + else + { + ALERT( at_error, "Can't find train for track change! %s\n", STRING(m_trainName) ); + target = FIND_ENTITY_BY_TARGETNAME( NULL, STRING(m_trainName) ); + } + } + else + ALERT( at_error, "Can't find bottom track for track change! %s\n", STRING(m_trackBottomName) ); + } + else + ALERT( at_error, "Can't find top track for track change! %s\n", STRING(m_trackTopName) ); +} + + + +TRAIN_CODE CFuncTrackChange :: EvaluateTrain( CPathTrack *pcurrent ) +{ + // Go ahead and work, we don't have anything to switch, so just be an elevator + if ( !pcurrent || !m_train ) + return TRAIN_SAFE; + + if ( m_train->m_ppath == pcurrent || (pcurrent->m_pprevious && m_train->m_ppath == pcurrent->m_pprevious) || + (pcurrent->m_pnext && m_train->m_ppath == pcurrent->m_pnext) ) + { + if ( m_train->pev->speed != 0 ) + return TRAIN_BLOCKING; + + Vector dist = pev->origin - m_train->pev->origin; + float length = dist.Length2D(); + if ( length < m_train->m_length ) // Empirically determined close distance + return TRAIN_FOLLOWING; + else if ( length > (150 + m_train->m_length) ) + return TRAIN_SAFE; + + return TRAIN_BLOCKING; + } + + return TRAIN_SAFE; +} + + +void CFuncTrackChange :: UpdateTrain( Vector &dest ) +{ + float time = (pev->nextthink - pev->ltime); + + m_train->pev->velocity = pev->velocity; + m_train->pev->avelocity = pev->avelocity; + m_train->NextThink( m_train->pev->ltime + time, FALSE ); + + // Attempt at getting the train to rotate properly around the origin of the trackchange + if ( time <= 0 ) + return; + + Vector offset = m_train->pev->origin - pev->origin; + Vector delta = dest - pev->angles; + // Transform offset into local coordinates + UTIL_MakeInvVectors( delta, gpGlobals ); + Vector local; + local.x = DotProduct( offset, gpGlobals->v_forward ); + local.y = DotProduct( offset, gpGlobals->v_right ); + local.z = DotProduct( offset, gpGlobals->v_up ); + + local = local - offset; + m_train->pev->velocity = pev->velocity + (local * (1.0/time)); +} + +void CFuncTrackChange :: GoDown( void ) +{ + if ( m_code == TRAIN_BLOCKING ) + return; + + // HitBottom may get called during CFuncPlat::GoDown(), so set up for that + // before you call GoDown() + + UpdateAutoTargets( TS_GOING_DOWN ); + // If ROTMOVE, move & rotate + if ( FBitSet( pev->spawnflags, SF_TRACK_DONT_MOVE ) ) + { + SetMoveDone( CallHitBottom ); + m_toggle_state = TS_GOING_DOWN; + AngularMove( m_start, pev->speed ); + } + else + { + CFuncPlat :: GoDown(); + SetMoveDone( CallHitBottom ); + RotMove( m_start, pev->nextthink - pev->ltime ); + } + // Otherwise, rotate first, move second + + // If the train is moving with the platform, update it + if ( m_code == TRAIN_FOLLOWING ) + { + UpdateTrain( m_start ); + m_train->m_ppath = NULL; + } +} + + +// +// Platform is at bottom, now starts moving up +// +void CFuncTrackChange :: GoUp( void ) +{ + if ( m_code == TRAIN_BLOCKING ) + return; + + // HitTop may get called during CFuncPlat::GoUp(), so set up for that + // before you call GoUp(); + + UpdateAutoTargets( TS_GOING_UP ); + if ( FBitSet( pev->spawnflags, SF_TRACK_DONT_MOVE ) ) + { + m_toggle_state = TS_GOING_UP; + SetMoveDone( CallHitTop ); + AngularMove( m_end, pev->speed ); + } + else + { + // If ROTMOVE, move & rotate + CFuncPlat :: GoUp(); + SetMoveDone( CallHitTop ); + RotMove( m_end, pev->nextthink - pev->ltime ); + } + + // Otherwise, move first, rotate second + + // If the train is moving with the platform, update it + if ( m_code == TRAIN_FOLLOWING ) + { + UpdateTrain( m_end ); + m_train->m_ppath = NULL; + } +} + + +// Normal track change +void CFuncTrackChange :: UpdateAutoTargets( int toggleState ) +{ + if ( !m_trackTop || !m_trackBottom ) + return; + + if ( toggleState == TS_AT_TOP ) + ClearBits( m_trackTop->pev->spawnflags, SF_PATH_DISABLED ); + else + SetBits( m_trackTop->pev->spawnflags, SF_PATH_DISABLED ); + + if ( toggleState == TS_AT_BOTTOM ) + ClearBits( m_trackBottom->pev->spawnflags, SF_PATH_DISABLED ); + else + SetBits( m_trackBottom->pev->spawnflags, SF_PATH_DISABLED ); +} + + +void CFuncTrackChange :: Use( CBaseEntity *pActivator, CBaseEntity *pCaller, USE_TYPE useType, float value ) +{ + if ( m_toggle_state != TS_AT_TOP && m_toggle_state != TS_AT_BOTTOM ) + return; + + // If train is in "safe" area, but not on the elevator, play alarm sound + if ( m_toggle_state == TS_AT_TOP ) + m_code = EvaluateTrain( m_trackTop ); + else if ( m_toggle_state == TS_AT_BOTTOM ) + m_code = EvaluateTrain( m_trackBottom ); + else + m_code = TRAIN_BLOCKING; + if ( m_code == TRAIN_BLOCKING ) + { + // Play alarm and return + EMIT_SOUND(ENT(pev), CHAN_VOICE, "buttons/button11.wav", 1, ATTN_NORM); + return; + } + + // Otherwise, it's safe to move + // If at top, go down + // at bottom, go up + + DisableUse(); + if (m_toggle_state == TS_AT_TOP) + GoDown(); + else + GoUp(); +} + + +// +// Platform has hit bottom. Stops and waits forever. +// +void CFuncTrackChange :: HitBottom( void ) +{ + CFuncPlatRot :: HitBottom(); + if ( m_code == TRAIN_FOLLOWING ) + { +// UpdateTrain(); + m_train->SetTrack( m_trackBottom ); + } + SetThink( NULL ); + pev->nextthink = -1; + + UpdateAutoTargets( m_toggle_state ); + + EnableUse(); +} + + +// +// Platform has hit bottom. Stops and waits forever. +// +void CFuncTrackChange :: HitTop( void ) +{ + CFuncPlatRot :: HitTop(); + if ( m_code == TRAIN_FOLLOWING ) + { +// UpdateTrain(); + m_train->SetTrack( m_trackTop ); + } + + // Don't let the plat go back down + SetThink( NULL ); + pev->nextthink = -1; + UpdateAutoTargets( m_toggle_state ); + EnableUse(); +} + + + +class CFuncTrackAuto : public CFuncTrackChange +{ +public: + void Use( CBaseEntity *pActivator, CBaseEntity *pCaller, USE_TYPE useType, float value ); + virtual void UpdateAutoTargets( int toggleState ); +}; + +LINK_ENTITY_TO_CLASS( func_trackautochange, CFuncTrackAuto ); + +// Auto track change +void CFuncTrackAuto :: UpdateAutoTargets( int toggleState ) +{ + CPathTrack *pTarget, *pNextTarget; + + if ( !m_trackTop || !m_trackBottom ) + return; + + if ( m_targetState == TS_AT_TOP ) + { + pTarget = m_trackTop->GetNext(); + pNextTarget = m_trackBottom->GetNext(); + } + else + { + pTarget = m_trackBottom->GetNext(); + pNextTarget = m_trackTop->GetNext(); + } + if ( pTarget ) + { + ClearBits( pTarget->pev->spawnflags, SF_PATH_DISABLED ); + if ( m_code == TRAIN_FOLLOWING && m_train && m_train->pev->speed == 0 ) + m_train->Use( this, this, USE_ON, 0 ); + } + + if ( pNextTarget ) + SetBits( pNextTarget->pev->spawnflags, SF_PATH_DISABLED ); + +} + + +void CFuncTrackAuto :: Use( CBaseEntity *pActivator, CBaseEntity *pCaller, USE_TYPE useType, float value ) +{ + CPathTrack *pTarget; + + if ( !UseEnabled() ) + return; + + if ( m_toggle_state == TS_AT_TOP ) + pTarget = m_trackTop; + else if ( m_toggle_state == TS_AT_BOTTOM ) + pTarget = m_trackBottom; + else + pTarget = NULL; + + if ( FClassnameIs( pActivator->pev, "func_tracktrain" ) ) + { + m_code = EvaluateTrain( pTarget ); + // Safe to fire? + if ( m_code == TRAIN_FOLLOWING && m_toggle_state != m_targetState ) + { + DisableUse(); + if (m_toggle_state == TS_AT_TOP) + GoDown(); + else + GoUp(); + } + } + else + { + if ( pTarget ) + pTarget = pTarget->GetNext(); + if ( pTarget && m_train->m_ppath != pTarget && ShouldToggle( useType, m_targetState ) ) + { + if ( m_targetState == TS_AT_TOP ) + m_targetState = TS_AT_BOTTOM; + else + m_targetState = TS_AT_TOP; + } + + UpdateAutoTargets( m_targetState ); + } +} + + +// ---------------------------------------------------------- +// +// +// pev->speed is the travel speed +// pev->health is current health +// pev->max_health is the amount to reset to each time it starts + +#define FGUNTARGET_START_ON 0x0001 + +class CGunTarget : public CBaseMonster +{ +public: + void Spawn( void ); + void Activate( void ); + void EXPORT Next( void ); + void EXPORT Start( void ); + void EXPORT Wait( void ); + void Stop( void ); + + int BloodColor( void ) { return DONT_BLEED; } + int Classify( void ) { return CLASS_MACHINE; } + int TakeDamage( entvars_t *pevInflictor, entvars_t *pevAttacker, float flDamage, int bitsDamageType ); + void Use( CBaseEntity *pActivator, CBaseEntity *pCaller, USE_TYPE useType, float value ); + Vector BodyTarget( const Vector &posSrc ) { return pev->origin; } + + virtual int ObjectCaps( void ) { return CBaseEntity :: ObjectCaps() & ~FCAP_ACROSS_TRANSITION; } + virtual int Save( CSave &save ); + virtual int Restore( CRestore &restore ); + + static TYPEDESCRIPTION m_SaveData[]; + +private: + BOOL m_on; +}; + + +LINK_ENTITY_TO_CLASS( func_guntarget, CGunTarget ); + +TYPEDESCRIPTION CGunTarget::m_SaveData[] = +{ + DEFINE_FIELD( CGunTarget, m_on, FIELD_BOOLEAN ), +}; + +IMPLEMENT_SAVERESTORE( CGunTarget, CBaseMonster ); + + +void CGunTarget::Spawn( void ) +{ + pev->solid = SOLID_BSP; + pev->movetype = MOVETYPE_PUSH; + + UTIL_SetOrigin(pev, pev->origin); + SET_MODEL(ENT(pev), STRING(pev->model) ); + + if ( pev->speed == 0 ) + pev->speed = 100; + + // Don't take damage until "on" + pev->takedamage = DAMAGE_NO; + pev->flags |= FL_MONSTER; + + m_on = FALSE; + pev->max_health = pev->health; + + if ( pev->spawnflags & FGUNTARGET_START_ON ) + { + SetThink( Start ); + pev->nextthink = pev->ltime + 0.3; + } +} + + +void CGunTarget::Activate( void ) +{ + CBaseEntity *pTarg; + + // now find our next target + pTarg = GetNextTarget(); + if ( pTarg ) + { + m_hTargetEnt = pTarg; + UTIL_SetOrigin( pev, pTarg->pev->origin - (pev->mins + pev->maxs) * 0.5 ); + } +} + + +void CGunTarget::Start( void ) +{ + Use( this, this, USE_ON, 0 ); +} + + +void CGunTarget::Next( void ) +{ + SetThink( NULL ); + + m_hTargetEnt = GetNextTarget(); + CBaseEntity *pTarget = m_hTargetEnt; + + if ( !pTarget ) + { + Stop(); + return; + } + SetMoveDone( Wait ); + LinearMove( pTarget->pev->origin - (pev->mins + pev->maxs) * 0.5, pev->speed ); +} + + +void CGunTarget::Wait( void ) +{ + CBaseEntity *pTarget = m_hTargetEnt; + + if ( !pTarget ) + { + Stop(); + return; + } + + // Fire the pass target if there is one + if ( pTarget->pev->message ) + { + FireTargets( STRING(pTarget->pev->message), this, this, USE_TOGGLE, 0 ); + if ( FBitSet( pTarget->pev->spawnflags, SF_CORNER_FIREONCE ) ) + pTarget->pev->message = 0; + } + + m_flWait = pTarget->GetDelay(); + + pev->target = pTarget->pev->target; + SetThink( Next ); + if (m_flWait != 0) + {// -1 wait will wait forever! + pev->nextthink = pev->ltime + m_flWait; + } + else + { + Next();// do it RIGHT now! + } +} + + +void CGunTarget::Stop( void ) +{ + pev->velocity = g_vecZero; + pev->nextthink = 0; + pev->takedamage = DAMAGE_NO; +} + + +int CGunTarget::TakeDamage( entvars_t *pevInflictor, entvars_t *pevAttacker, float flDamage, int bitsDamageType ) +{ + if ( pev->health > 0 ) + { + pev->health -= flDamage; + if ( pev->health <= 0 ) + { + pev->health = 0; + Stop(); + if ( pev->message ) + FireTargets( STRING(pev->message), this, this, USE_TOGGLE, 0 ); + } + } + return 0; +} + + +void CGunTarget::Use( CBaseEntity *pActivator, CBaseEntity *pCaller, USE_TYPE useType, float value ) +{ + if ( !ShouldToggle( useType, m_on ) ) + return; + + if ( m_on ) + { + Stop(); + } + else + { + pev->takedamage = DAMAGE_AIM; + m_hTargetEnt = GetNextTarget(); + if ( m_hTargetEnt == NULL ) + return; + pev->health = pev->max_health; + Next(); + } +} + + + diff --git a/spirit/player.cpp b/dlls/player.cpp similarity index 73% rename from spirit/player.cpp rename to dlls/player.cpp index 522629dd..7598b7cd 100644 --- a/spirit/player.cpp +++ b/dlls/player.cpp @@ -1,9 +1,9 @@ /*** * * Copyright (c) 1996-2002, Valve LLC. All rights reserved. -* -* This product contains software technology licensed from Id -* Software, Inc. ("Id Technology"). Id Technology (c) 1996 Id Software, Inc. +* +* 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 @@ -34,17 +34,15 @@ #include "decals.h" #include "gamerules.h" #include "game.h" -#include "effects.h" //LRC -#include "movewith.h" //LRC +#include "hltv.h" // #define DUCKFIX -extern DLL_GLOBAL ULONG g_ulModelIndexPlayer; -extern DLL_GLOBAL BOOL g_fGameOver; -extern DLL_GLOBAL BOOL g_fDrawLines; +extern DLL_GLOBAL ULONG g_ulModelIndexPlayer; +extern DLL_GLOBAL BOOL g_fGameOver; +extern DLL_GLOBAL BOOL g_fDrawLines; int gEvilImpulse101; -BOOL g_markFrameBounds = 0; // LRC -extern DLL_GLOBAL int g_iSkillLevel, gDisplayTitle; +extern DLL_GLOBAL int g_iSkillLevel, gDisplayTitle; BOOL gInitHUD = TRUE; @@ -57,26 +55,20 @@ extern edict_t *EntSelectSpawnPoint( CBaseEntity *pPlayer ); // the world node graph extern CGraph WorldGraph; -#define TRAIN_ACTIVE 0x80 +#define TRAIN_ACTIVE 0x80 #define TRAIN_NEW 0xc0 #define TRAIN_OFF 0x00 #define TRAIN_NEUTRAL 0x01 #define TRAIN_SLOW 0x02 #define TRAIN_MEDIUM 0x03 -#define TRAIN_FAST 0x04 +#define TRAIN_FAST 0x04 #define TRAIN_BACK 0x05 #define FLASH_DRAIN_TIME 1.2 //100 units/3 minutes #define FLASH_CHARGE_TIME 0.2 // 100 units/20 seconds (seconds per unit) -//#ifdef XENWARRIOR -// float g_fEnvFadeTime = 0; // flashlight can't be used until this time expires. -// // this is just a big hack, doesn't work with saverestore, etc... -// // Doing it properly would just be effort. -//#endif - // Global Savedata for player -TYPEDESCRIPTION CBasePlayer::m_playerSaveData[] = +TYPEDESCRIPTION CBasePlayer::m_playerSaveData[] = { DEFINE_FIELD( CBasePlayer, m_flFlashLightTime, FIELD_TIME ), DEFINE_FIELD( CBasePlayer, m_iFlashBattery, FIELD_INTEGER ), @@ -102,11 +94,9 @@ TYPEDESCRIPTION CBasePlayer::m_playerSaveData[] = DEFINE_FIELD( CBasePlayer, m_lastDamageAmount, FIELD_INTEGER ), DEFINE_ARRAY( CBasePlayer, m_rgpPlayerItems, FIELD_CLASSPTR, MAX_ITEM_TYPES ), - DEFINE_ARRAY( CBasePlayer, m_szAnimExtention, FIELD_CHARACTER, 32), DEFINE_FIELD( CBasePlayer, m_pActiveItem, FIELD_CLASSPTR ), DEFINE_FIELD( CBasePlayer, m_pLastItem, FIELD_CLASSPTR ), - DEFINE_FIELD( CBasePlayer, m_pNextItem, FIELD_CLASSPTR ), - + DEFINE_ARRAY( CBasePlayer, m_rgAmmo, FIELD_INTEGER, MAX_AMMO_SLOTS ), DEFINE_FIELD( CBasePlayer, m_idrowndmg, FIELD_INTEGER ), DEFINE_FIELD( CBasePlayer, m_idrownrestored, FIELD_INTEGER ), @@ -123,38 +113,10 @@ TYPEDESCRIPTION CBasePlayer::m_playerSaveData[] = DEFINE_FIELD( CBasePlayer, m_fInitHUD, FIELD_BOOLEAN ), DEFINE_FIELD( CBasePlayer, m_tbdPrev, FIELD_TIME ), - DEFINE_FIELD( CBasePlayer, m_pTank, FIELD_EHANDLE ), // NB: this points to a CFuncTank*Controls* now. --LRC + DEFINE_FIELD( CBasePlayer, m_pTank, FIELD_EHANDLE ), DEFINE_FIELD( CBasePlayer, m_iHideHUD, FIELD_INTEGER ), - DEFINE_FIELD( CBasePlayer, viewEntity, FIELD_STRING), - DEFINE_FIELD( CBasePlayer, viewFlags, FIELD_INTEGER), - DEFINE_FIELD( CBasePlayer, viewNeedsUpdate, FIELD_INTEGER), - DEFINE_FIELD( CBasePlayer, m_iFogStartDist, FIELD_INTEGER), - DEFINE_FIELD( CBasePlayer, m_iFogEndDist, FIELD_INTEGER ), - DEFINE_FIELD( CBasePlayer, m_iFogFinalEndDist, FIELD_INTEGER ), - DEFINE_FIELD( CBasePlayer, m_FogColor, FIELD_VECTOR ), - DEFINE_FIELD( CBasePlayer, m_FogFadeTime, FIELD_INTEGER), - DEFINE_FIELD( CBasePlayer, m_flStartTime, FIELD_TIME ), - - DEFINE_FIELD( CBasePlayer, Rain_dripsPerSecond, FIELD_INTEGER ), - DEFINE_FIELD( CBasePlayer, Rain_windX, FIELD_FLOAT ), - DEFINE_FIELD( CBasePlayer, Rain_windY, FIELD_FLOAT ), - DEFINE_FIELD( CBasePlayer, Rain_randX, FIELD_FLOAT ), - DEFINE_FIELD( CBasePlayer, Rain_randY, FIELD_FLOAT ), - - DEFINE_FIELD( CBasePlayer, Rain_ideal_dripsPerSecond, FIELD_INTEGER ), - DEFINE_FIELD( CBasePlayer, Rain_ideal_windX, FIELD_FLOAT ), - DEFINE_FIELD( CBasePlayer, Rain_ideal_windY, FIELD_FLOAT ), - DEFINE_FIELD( CBasePlayer, Rain_ideal_randX, FIELD_FLOAT ), - DEFINE_FIELD( CBasePlayer, Rain_ideal_randY, FIELD_FLOAT ), - - DEFINE_FIELD( CBasePlayer, Rain_endFade, FIELD_TIME ), - DEFINE_FIELD( CBasePlayer, Rain_nextFadeUpdate, FIELD_TIME ), - - //LRC - //DEFINE_FIELD( CBasePlayer, m_iFogStartDist, FIELD_INTEGER ), - //DEFINE_FIELD( CBasePlayer, m_iFogEndDist, FIELD_INTEGER ), - //DEFINE_FIELD( CBasePlayer, m_vecFogColor, FIELD_VECTOR ), - + DEFINE_FIELD( CBasePlayer, m_iFOV, FIELD_INTEGER ), + //DEFINE_FIELD( CBasePlayer, m_fDeadTime, FIELD_FLOAT ), // only used in multiplayer games //DEFINE_FIELD( CBasePlayer, m_fGameHUDInitialized, FIELD_INTEGER ), // only used in multiplayer games //DEFINE_FIELD( CBasePlayer, m_flStopExtraSoundTime, FIELD_TIME ), @@ -181,8 +143,8 @@ TYPEDESCRIPTION CBasePlayer::m_playerSaveData[] = //DEFINE_ARRAY( CBasePlayer, m_rgAmmoLast, FIELD_INTEGER, MAX_AMMO_SLOTS ), // Don't need to restore //DEFINE_FIELD( CBasePlayer, m_fOnTarget, FIELD_BOOLEAN ), // Don't need to restore //DEFINE_FIELD( CBasePlayer, m_nCustomSprayFrames, FIELD_INTEGER ), // Don't need to restore - -}; + +}; int giPrecacheGrunt = 0; @@ -193,13 +155,6 @@ int gmsgFlashlight = 0; int gmsgFlashBattery = 0; int gmsgResetHUD = 0; int gmsgInitHUD = 0; -int gmsgSetFog = 0; //LRC -int gmsgKeyedDLight = 0;//LRC -int gmsgSetSky = 0; //LRC -int gmsgHUDColor = 0; //LRC -int gmsgAddShine = 0; // LRC -int gmsgParticle = 0; // LRC -int gmsgPlayMP3 = 0; //Killar int gmsgShowGameTitle = 0; int gmsgCurWeapon = 0; int gmsgHealth = 0; @@ -224,20 +179,15 @@ int gmsgHideWeapon = 0; int gmsgSetCurWeap = 0; int gmsgSayText = 0; int gmsgTextMsg = 0; +int gmsgSetFOV = 0; int gmsgShowMenu = 0; int gmsgGeigerRange = 0; int gmsgTeamNames = 0; -int gmsgStatusIcon = 0; //LRC + int gmsgStatusText = 0; -int gmsgStatusValue = 0; -int gmsgCamData; // for trigger_viewset -int gmsgRainData = 0; -int gmsgSetBody = 0;//change body for view weapon model -int gmsgSetSkin = 0;//change skin for view weapon model -int gmsgTempEntity = 0; -int gmsgWeaponAnim = 0; -int gmsgIntermission = 0; -int gmsgRoomType = 0; +int gmsgStatusValue = 0; + + void LinkUserMessages( void ) { @@ -248,13 +198,12 @@ void LinkUserMessages( void ) } gmsgSelAmmo = REG_USER_MSG("SelAmmo", sizeof(SelAmmo)); - gmsgIntermission = REG_USER_MSG( "Intermission", 0 ); gmsgCurWeapon = REG_USER_MSG("CurWeapon", 3); gmsgGeigerRange = REG_USER_MSG("Geiger", 1); gmsgFlashlight = REG_USER_MSG("Flashlight", 2); gmsgFlashBattery = REG_USER_MSG("FlashBat", 1); gmsgHealth = REG_USER_MSG( "Health", 1 ); - gmsgDamage = REG_USER_MSG( "Damage", 18 ); + gmsgDamage = REG_USER_MSG( "Damage", 12 ); gmsgBattery = REG_USER_MSG( "Battery", 2); gmsgTrain = REG_USER_MSG( "Train", 1); gmsgHudText = REG_USER_MSG( "HudText", -1 ); @@ -263,16 +212,7 @@ void LinkUserMessages( void ) gmsgWeaponList = REG_USER_MSG("WeaponList", -1); gmsgResetHUD = REG_USER_MSG("ResetHUD", 1); // called every respawn gmsgInitHUD = REG_USER_MSG("InitHUD", 0 ); // called every time a new player joins the server - gmsgShowGameTitle = REG_USER_MSG("GameTitle", 0 ); - - gmsgTempEntity = REG_USER_MSG( "TempEntity", -1); - gmsgSetFog = REG_USER_MSG("SetFog", 7 ); //LRC - gmsgKeyedDLight = REG_USER_MSG("KeyedDLight", -1 ); //LRC - gmsgSetSky = REG_USER_MSG( "SetSky", 7 ); //LRC - gmsgHUDColor = REG_USER_MSG( "HUDColor", 4 ); //LRC - gmsgAddShine = REG_USER_MSG( "AddShine", -1 ); //LRC - gmsgParticle = REG_USER_MSG( "Particle", -1); //LRC - + gmsgShowGameTitle = REG_USER_MSG("GameTitle", 1); gmsgDeathMsg = REG_USER_MSG( "DeathMsg", -1 ); gmsgScoreInfo = REG_USER_MSG( "ScoreInfo", 9 ); gmsgTeamInfo = REG_USER_MSG( "TeamInfo", -1 ); // sets the name of a player's team @@ -284,22 +224,16 @@ void LinkUserMessages( void ) gmsgWeapPickup = REG_USER_MSG( "WeapPickup", 1 ); gmsgItemPickup = REG_USER_MSG( "ItemPickup", -1 ); gmsgHideWeapon = REG_USER_MSG( "HideWeapon", 1 ); - gmsgRoomType = REG_USER_MSG( "RoomType", 1 ); - gmsgWeaponAnim = REG_USER_MSG( "WeaponAnim", 3 ); + gmsgSetFOV = REG_USER_MSG( "SetFOV", 1 ); gmsgShowMenu = REG_USER_MSG( "ShowMenu", -1 ); - gmsgShake = REG_USER_MSG("ScreenShake", sizeof( ScreenShake )); - gmsgFade = REG_USER_MSG("ScreenFade", sizeof( ScreenFade )); + gmsgShake = REG_USER_MSG("ScreenShake", sizeof(ScreenShake)); + gmsgFade = REG_USER_MSG("ScreenFade", sizeof(ScreenFade)); gmsgAmmoX = REG_USER_MSG("AmmoX", 2); gmsgTeamNames = REG_USER_MSG( "TeamNames", -1 ); - gmsgStatusIcon = REG_USER_MSG( "StatusIcon", -1 ); gmsgStatusText = REG_USER_MSG("StatusText", -1); - gmsgStatusValue = REG_USER_MSG("StatusValue", 3); - gmsgCamData = REG_USER_MSG("CamData", -1); - gmsgPlayMP3 = REG_USER_MSG("PlayMP3", -1); //Killar - gmsgRainData = REG_USER_MSG("RainData", 28 ); - gmsgSetBody = REG_USER_MSG("SetBody", 1); - gmsgSetSkin = REG_USER_MSG("SetSkin", 1); + gmsgStatusValue = REG_USER_MSG("StatusValue", 3); + } LINK_ENTITY_TO_CLASS( player, CBasePlayer ); @@ -310,17 +244,17 @@ void CBasePlayer :: Pain( void ) { float flRndSound;//sound randomizer - flRndSound = RANDOM_FLOAT ( 0 , 1 ); - + flRndSound = RANDOM_FLOAT ( 0 , 1 ); + if ( flRndSound <= 0.33 ) EMIT_SOUND(ENT(pev), CHAN_VOICE, "player/pl_pain5.wav", 1, ATTN_NORM); - else if ( flRndSound <= 0.66 ) + else if ( flRndSound <= 0.66 ) EMIT_SOUND(ENT(pev), CHAN_VOICE, "player/pl_pain6.wav", 1, ATTN_NORM); else EMIT_SOUND(ENT(pev), CHAN_VOICE, "player/pl_pain7.wav", 1, ATTN_NORM); } -/* +/* * */ Vector VecVelocityForDamage(float flDamage) @@ -333,7 +267,7 @@ Vector VecVelocityForDamage(float flDamage) vec = vec * 2; else vec = vec * 10; - + return vec; } @@ -359,8 +293,8 @@ static void ThrowGib(entvars_t *pev, char *szGibModel, float flDamage) pevNew->frame = 0; pevNew->flags = 0; } - - + + static void ThrowHead(entvars_t *pev, char *szGibModel, floatflDamage) { SET_MODEL(ENT(pev), szGibModel); @@ -378,7 +312,7 @@ static void ThrowHead(entvars_t *pev, char *szGibModel, floatflDamage) } -*/ +*/ #endif int TrainSpeed(int iSpeed, int iMax) @@ -417,28 +351,25 @@ void CBasePlayer :: DeathSound( void ) */ // temporarily using pain sounds for death sounds - switch (RANDOM_LONG(1,5)) + switch (RANDOM_LONG(1,5)) { - case 1: + case 1: EMIT_SOUND(ENT(pev), CHAN_VOICE, "player/pl_pain5.wav", 1, ATTN_NORM); break; - case 2: + case 2: EMIT_SOUND(ENT(pev), CHAN_VOICE, "player/pl_pain6.wav", 1, ATTN_NORM); break; - case 3: + case 3: EMIT_SOUND(ENT(pev), CHAN_VOICE, "player/pl_pain7.wav", 1, ATTN_NORM); break; } // play one of the suit death alarms - //LRC- if no suit, then no flatline sound. (unless it's a deathmatch.) - if ( !(pev->weapons & ITEM_SUIT) && !g_pGameRules->IsDeathmatch() ) - return; EMIT_GROUPNAME_SUIT(ENT(pev), "HEV_DEAD"); } // override takehealth -// bitsDamageType indicates type of damage healed. +// bitsDamageType indicates type of damage healed. int CBasePlayer :: TakeHealth( float flHealth, int bitsDamageType ) { @@ -446,25 +377,12 @@ int CBasePlayer :: TakeHealth( float flHealth, int bitsDamageType ) } -int CBasePlayer :: TakeArmor( float flArmor ) -{ - if(!(pev->weapons & ITEM_SUIT)) return 0; - - if(CBaseMonster::TakeArmor(flArmor )) - { - //force flashlight to charge - m_flFlashLightTime = FLASH_CHARGE_TIME + gpGlobals->time; - return 1; - } - return 0; -} - Vector CBasePlayer :: GetGunPosition( ) { // UTIL_MakeVectors(pev->v_angle); // m_HackedGunPos = pev->view_ofs; Vector origin; - + origin = pev->origin + pev->view_ofs; return origin; @@ -511,7 +429,7 @@ void CBasePlayer :: TraceAttack( entvars_t *pevAttacker, float flDamage, Vector } /* - Take some damage. + Take some damage. NOTE: each call to TakeDamage with bitsDamageType set to a time-based damage type will cause the damage time countdown to be reset. Thus the ongoing effects of poison, radiation etc are implemented with subsequent calls to TakeDamage using DMG_GENERIC. @@ -547,7 +465,7 @@ int CBasePlayer :: TakeDamage( entvars_t *pevInflictor, entvars_t *pevAttacker, return 0; // go take the damage first - + CBaseEntity *pAttacker = CBaseEntity::Instance(pevAttacker); if ( !g_pGameRules->FPlayerCanTakeDamage( this, pAttacker ) ) @@ -559,7 +477,7 @@ int CBasePlayer :: TakeDamage( entvars_t *pevInflictor, entvars_t *pevAttacker, // keep track of amount of damage last sustained m_lastDamageAmount = flDamage; - // Armor. + // Armor. if (pev->armorvalue && !(bitsDamageType & (DMG_FALL | DMG_DROWN)) )// armor doesn't protect against fall or drown damage! { float flNew = flDamage * flRatio; @@ -578,7 +496,7 @@ int CBasePlayer :: TakeDamage( entvars_t *pevInflictor, entvars_t *pevAttacker, } else pev->armorvalue -= flArmor; - + flDamage = flNew; } @@ -594,6 +512,16 @@ int CBasePlayer :: TakeDamage( entvars_t *pevInflictor, entvars_t *pevAttacker, m_rgbTimeBasedDamage[i] = 0; } + // tell director about it + MESSAGE_BEGIN( MSG_SPEC, SVC_DIRECTOR ); + WRITE_BYTE ( 9 ); // command length in bytes + WRITE_BYTE ( DRC_CMD_EVENT ); // take damage event + WRITE_SHORT( ENTINDEX(this->edict()) ); // index number of primary entity + WRITE_SHORT( ENTINDEX(ENT(pevInflictor)) ); // index number of secondary entity + WRITE_LONG( 5 ); // eventflags (priority and flags) + MESSAGE_END(); + + // how bad is it, doc? ftrivial = (pev->health > 75 || m_lastDamageAmount < 5); @@ -607,7 +535,7 @@ int CBasePlayer :: TakeDamage( entvars_t *pevInflictor, entvars_t *pevAttacker, // UNDONE: still need to record damage and heal messages for the following types - // DMG_BURN + // DMG_BURN // DMG_FREEZE // DMG_BLAST // DMG_SHOCK @@ -632,18 +560,18 @@ int CBasePlayer :: TakeDamage( entvars_t *pevInflictor, entvars_t *pevAttacker, SetSuitUpdate("!HEV_DMG5", FALSE, SUIT_NEXT_IN_30SEC); // major fracture else SetSuitUpdate("!HEV_DMG4", FALSE, SUIT_NEXT_IN_30SEC); // minor fracture - + bitsDamage &= ~(DMG_FALL | DMG_CRUSH); ffound = TRUE; } - + if (bitsDamage & DMG_BULLET) { if (m_lastDamageAmount > 5) SetSuitUpdate("!HEV_DMG6", FALSE, SUIT_NEXT_IN_30SEC); // blood loss detected //else // SetSuitUpdate("!HEV_DMG0", FALSE, SUIT_NEXT_IN_30SEC); // minor laceration - + bitsDamage &= ~DMG_BULLET; ffound = TRUE; } @@ -658,7 +586,7 @@ int CBasePlayer :: TakeDamage( entvars_t *pevInflictor, entvars_t *pevAttacker, bitsDamage &= ~DMG_SLASH; ffound = TRUE; } - + if (bitsDamage & DMG_SONIC) { if (fmajor) @@ -703,7 +631,7 @@ int CBasePlayer :: TakeDamage( entvars_t *pevInflictor, entvars_t *pevAttacker, pev->punchangle.x = -2; - if (fTookDamage && !ftrivial && fmajor && flHealthPrev >= 75) + if (fTookDamage && !ftrivial && fmajor && flHealthPrev >= 75) { // first time we take major damage... // turn automedic on if not on @@ -712,7 +640,7 @@ int CBasePlayer :: TakeDamage( entvars_t *pevInflictor, entvars_t *pevAttacker, // give morphine shot if not given recently SetSuitUpdate("!HEV_HEAL7", FALSE, SUIT_NEXT_IN_30MIN); // morphine shot } - + if (fTookDamage && !ftrivial && fcritical && flHealthPrev < 75) { @@ -721,7 +649,7 @@ int CBasePlayer :: TakeDamage( entvars_t *pevInflictor, entvars_t *pevAttacker, SetSuitUpdate("!HEV_HLTH3", FALSE, SUIT_NEXT_IN_10MIN); // near death else if (pev->health < 20) SetSuitUpdate("!HEV_HLTH2", FALSE, SUIT_NEXT_IN_10MIN); // health critical - + // give critical health warnings if (!RANDOM_LONG(0,3) && flHealthPrev < 50) SetSuitUpdate("!HEV_DMG7", FALSE, SUIT_NEXT_IN_5MIN); //seek medical attention @@ -762,7 +690,7 @@ void CBasePlayer::PackDeadPlayerItems( void ) memset(rgpPackWeapons, NULL, sizeof(rgpPackWeapons) ); memset(iPackAmmo, -1, sizeof(iPackAmmo) ); - // get the game rules + // get the game rules iWeaponRules = g_pGameRules->DeadPlayerWeapons( this ); iAmmoRules = g_pGameRules->DeadPlayerAmmo( this ); @@ -821,12 +749,12 @@ void CBasePlayer::PackDeadPlayerItems( void ) break; case GR_PLR_DROP_AMMO_ACTIVE: - if ( m_pActiveItem && i == m_pActiveItem->PrimaryAmmoIndex() ) + if ( m_pActiveItem && i == m_pActiveItem->PrimaryAmmoIndex() ) { // this is the primary ammo type for the active weapon iPackAmmo[ iPA++ ] = i; } - else if ( m_pActiveItem && i == m_pActiveItem->SecondaryAmmoIndex() ) + else if ( m_pActiveItem && i == m_pActiveItem->SecondaryAmmoIndex() ) { // this is the secondary ammo type for the active weapon iPackAmmo[ iPA++ ] = i; @@ -846,8 +774,8 @@ void CBasePlayer::PackDeadPlayerItems( void ) pWeaponBox->pev->angles.x = 0;// don't let weaponbox tilt. pWeaponBox->pev->angles.z = 0; - pWeaponBox->SetThink(& CWeaponBox::Kill ); - pWeaponBox->SetNextThink( 120 ); + pWeaponBox->SetThink( CWeaponBox::Kill ); + pWeaponBox->pev->nextthink = gpGlobals->time + 120; // back these two lists up to their first elements iPA = 0; @@ -892,7 +820,7 @@ void CBasePlayer::RemoveAllItems( BOOL removeSuit ) m_pActiveItem = m_rgpPlayerItems[i]; while (m_pActiveItem) { - pPendingItem = m_pActiveItem->m_pNext; + pPendingItem = m_pActiveItem->m_pNext; m_pActiveItem->Drop( ); m_pActiveItem = pPendingItem; } @@ -902,7 +830,7 @@ void CBasePlayer::RemoveAllItems( BOOL removeSuit ) pev->viewmodel = 0; pev->weaponmodel = 0; - + if ( removeSuit ) pev->weapons = 0; else @@ -920,132 +848,6 @@ void CBasePlayer::RemoveAllItems( BOOL removeSuit ) MESSAGE_END(); } -//LRC -void CBasePlayer::RemoveAmmo( const char* szName, int iAmount ) -{ -// ALERT(at_console, "RemoveAmmo(\"%s\", %d): \n", szName, iAmount); - - if (iAmount == -3 || iAmount == -1) - { - return; - } - - int x = GetAmmoIndex(szName); - - if (iAmount > 0) - { - m_rgAmmo[x] -= iAmount; - if (m_rgAmmo[x] < 0) - { - m_rgAmmo[x] = 0; -// ALERT(at_console, "Reduce to 0\n"); - } -// else -// ALERT(at_console, "Reduce\n"); - } - else - { -// ALERT(at_console, "All\n"); - m_rgAmmo[x] = 0; - } -} - -//LRC -void CBasePlayer::RemoveItems( int iWeaponMask, int i9mm, int i357, int iBuck, int iBolt, int iARGren, int iRock, int iUranium, int iSatchel, int iSnark, int iTrip, int iGren, int iHornet ) -{ - int i; - CBasePlayerItem *pCurrentItem; - - // hornetgun is outside the spawnflags Worldcraft can set - handle it seperately. - if (iHornet) iWeaponMask |= 1<m_pNext) - { - if (!(1<m_pNext->m_iId & iWeaponMask)) - { - ((CBasePlayerWeapon*)pCurrentItem)->DrainClip(this, FALSE, i9mm, i357, iBuck, iBolt, iARGren, iRock, iUranium, iSatchel, iSnark, iTrip, iGren); - //remove pCurrentItem->m_pNext from the list -// ALERT(at_console, "Removing %s. (id = %d)\n", pCurrentItem->m_pNext->pszName(), pCurrentItem->m_pNext->m_iId); - pCurrentItem->m_pNext->Drop( ); - if (m_pLastItem == pCurrentItem->m_pNext) - m_pLastItem = NULL; - pCurrentItem->m_pNext = pCurrentItem->m_pNext->m_pNext; - } - else - { - //we're keeping this, so we need to empty the clip - ((CBasePlayerWeapon*)pCurrentItem)->DrainClip(this, TRUE, i9mm, i357, iBuck, iBolt, iARGren, iRock, iUranium, iSatchel, iSnark, iTrip, iGren); - //now, leave pCurrentItem->m_pNext in the list and go on to the next -// ALERT(at_console, "Keeping %s. (id = %d)\n", pCurrentItem->m_pNext->pszName(), pCurrentItem->m_pNext->m_iId); - pCurrentItem = pCurrentItem->m_pNext; - } - } - // we've gone through items 2+, now we finish off by checking item 1. - if (!(1<m_iId & iWeaponMask)) - { - ((CBasePlayerWeapon*)pCurrentItem)->DrainClip(this, FALSE, i9mm, i357, iBuck, iBolt, iARGren, iRock, iUranium, iSatchel, iSnark, iTrip, iGren); -// ALERT(at_console, "Removing %s. (id = %d)\n", m_rgpPlayerItems[i]->pszName(), m_rgpPlayerItems[i]->m_iId); - m_rgpPlayerItems[i]->Drop( ); - if (m_pLastItem == m_rgpPlayerItems[i]) - m_pLastItem = NULL; - m_rgpPlayerItems[i] = m_rgpPlayerItems[i]->m_pNext; - } - else - { - ((CBasePlayerWeapon*)pCurrentItem)->DrainClip(this, TRUE, i9mm, i357, iBuck, iBolt, iARGren, iRock, iUranium, iSatchel, iSnark, iTrip, iGren); -// ALERT(at_console, "Keeping %s. (id = %d)\n", m_rgpPlayerItems[i]->pszName(), m_rgpPlayerItems[i]->m_iId); - } - } - - pev->weapons &= ~(1<weapons &= iWeaponMask; - - // are we dropping the active item? - if (m_pActiveItem && !(1<m_iId & iWeaponMask)) - { - ResetAutoaim( ); - m_pActiveItem->Holster( ); - pev->viewmodel = 0; - pev->weaponmodel = 0; - m_pActiveItem = NULL; - - UpdateClientData(); - // send Selected Weapon Message to our client - MESSAGE_BEGIN( MSG_ONE, gmsgCurWeapon, NULL, pev ); - WRITE_BYTE(0); - WRITE_BYTE(0); - WRITE_BYTE(0); - MESSAGE_END(); - } - else - { - if (m_pActiveItem && !((CBasePlayerWeapon*)m_pActiveItem)->IsUseable()) - { - //lower the gun if it's out of ammo - ((CBasePlayerWeapon*)m_pActiveItem)->m_flTimeWeaponIdle = UTIL_WeaponTimeBase(); - } - UpdateClientData(); - } -} - /* * GLOBALS ASSUMED SET: g_ulModelIndexPlayer * @@ -1062,8 +864,6 @@ void CBasePlayer::Killed( entvars_t *pevAttacker, int iGib ) if ( m_pActiveItem ) m_pActiveItem->Holster( ); - m_pNextItem = NULL; - g_pGameRules->PlayerKilled( this, pevAttacker, g_pevLastInflictor ); if ( m_pTank != NULL ) @@ -1082,13 +882,11 @@ void CBasePlayer::Killed( entvars_t *pevAttacker, int iGib ) } SetAnimation( PLAYER_DIE ); - + m_iRespawnFrames = 0; pev->modelindex = g_ulModelIndexPlayer; // don't use eyes - if(pev->velocity.x > 40 || pev->velocity.y > 40 || pev->velocity.z > 40) pev->renderfx = kRenderFxDeadPlayer; - pev->deadflag = DEAD_DYING; pev->movetype = MOVETYPE_TOSS; ClearBits( pev->flags, FL_ONGROUND ); @@ -1111,31 +909,32 @@ void CBasePlayer::Killed( entvars_t *pevAttacker, int iGib ) WRITE_BYTE(0xFF); MESSAGE_END(); - viewEntity = 0; - viewFlags = 0; - viewNeedsUpdate = 1; - // reset FOV - pev->fov = 90.0f; + pev->fov = m_iFOV = m_iClientFOV = 0; - //this will cause problems - //UTIL_ScreenFade( this, Vector(128,0,0), 6, 15, 255, FFADE_OUT | FFADE_MODULATE | FFADE_STAYOUT ); + MESSAGE_BEGIN( MSG_ONE, gmsgSetFOV, NULL, pev ); + WRITE_BYTE(0); + MESSAGE_END(); + + + // UNDONE: Put this in, but add FFADE_PERMANENT and make fade time 8.8 instead of 4.12 + // UTIL_ScreenFade( edict(), Vector(128,0,0), 6, 15, 255, FFADE_OUT | FFADE_MODULATE ); if ( ( pev->health < -40 && iGib != GIB_NEVER ) || iGib == GIB_ALWAYS ) { - pev->solid = SOLID_NOT; + pev->solid = SOLID_NOT; GibMonster(); // This clears pev->model pev->effects |= EF_NODRAW; return; } DeathSound(); - + pev->angles.x = 0; pev->angles.z = 0; - - SetThink(&CBasePlayer::PlayerDeathThink); - SetNextThink( 0.1 ); + + SetThink(PlayerDeathThink); + pev->nextthink = gpGlobals->time + 0.1; } @@ -1154,22 +953,22 @@ void CBasePlayer::SetAnimation( PLAYER_ANIM playerAnim ) playerAnim = PLAYER_IDLE; } - switch (playerAnim) + switch (playerAnim) { case PLAYER_JUMP: m_IdealActivity = ACT_HOP; break; - + case PLAYER_SUPERJUMP: m_IdealActivity = ACT_LEAP; break; - + case PLAYER_DIE: m_IdealActivity = ACT_DIESIMPLE; m_IdealActivity = GetDeathActivity( ); break; - case PLAYER_ATTACK1: + case PLAYER_ATTACK1: switch( m_Activity ) { case ACT_HOVER: @@ -1190,7 +989,7 @@ void CBasePlayer::SetAnimation( PLAYER_ANIM playerAnim ) { m_IdealActivity = m_Activity; } - else if ( pev->waterlevel > 1 && pev->watertype != CONTENTS_FOG) + else if ( pev->waterlevel > 1 ) { if ( speed == 0 ) m_IdealActivity = ACT_HOVER; @@ -1313,7 +1112,7 @@ void CBasePlayer::SetAnimation( PLAYER_ANIM playerAnim ) /* =========== TabulateAmmo -This function is used to find and store +This function is used to find and store all the ammo we have into the ammo vars. ============ */ @@ -1352,10 +1151,10 @@ void CBasePlayer::WaterMove() // waterlevel 2 - waist in water // waterlevel 3 - head in water - if (pev->waterlevel != 3 || pev->watertype <= CONTENTS_FLYFIELD) + if (pev->waterlevel != 3) { // not underwater - + // play 'up for air' sound if (pev->air_finished < gpGlobals->time) EMIT_SOUND(ENT(pev), CHAN_VOICE, "player/pl_wade1.wav", 1, ATTN_NORM); @@ -1371,7 +1170,7 @@ void CBasePlayer::WaterMove() // set drowning damage bit. hack - dmg_drownrecover actually // makes the time based damage code 'give back' health over time. // make sure counter is cleared so we start count correctly. - + // NOTE: this actually causes the count to continue restarting // until all drowning damage is healed. @@ -1381,7 +1180,7 @@ void CBasePlayer::WaterMove() } } - else if (pev->watertype > CONTENTS_FLYFIELD) // FLYFIELD, FLYFIELD_GRAVITY & FOG aren't really water... + else { // fully under water // stop restoring damage while underwater m_bitsDamageType &= ~DMG_DROWNRECOVER; @@ -1397,12 +1196,12 @@ void CBasePlayer::WaterMove() pev->dmg = 5; TakeDamage(VARS(eoNullEntity), VARS(eoNullEntity), pev->dmg, DMG_DROWN); pev->pain_finished = gpGlobals->time + 1; - + // track drowning damage, give it back when // player finally takes a breath m_idrowndmg += pev->dmg; - } + } } else { @@ -1410,15 +1209,15 @@ void CBasePlayer::WaterMove() } } - if (!pev->waterlevel || pev->watertype <= CONTENTS_FLYFIELD ) + if (!pev->waterlevel) { if (FBitSet(pev->flags, FL_INWATER)) - { + { ClearBits(pev->flags, FL_INWATER); } return; } - + // make bubbles air = (int)(pev->air_finished - gpGlobals->time); @@ -1433,17 +1232,17 @@ void CBasePlayer::WaterMove() } } - if (pev->watertype == CONTENTS_LAVA) // do damage + if (pev->watertype == CONTENT_LAVA) // do damage { if (pev->dmgtime < gpGlobals->time) TakeDamage(VARS(eoNullEntity), VARS(eoNullEntity), 10 * pev->waterlevel, DMG_BURN); } - else if (pev->watertype == CONTENTS_SLIME) // do damage + else if (pev->watertype == CONTENT_SLIME) // do damage { pev->dmgtime = gpGlobals->time + 1; TakeDamage(VARS(eoNullEntity), VARS(eoNullEntity), 4 * pev->waterlevel, DMG_ACID); } - + if (!FBitSet(pev->flags, FL_INWATER)) { SetBits(pev->flags, FL_INWATER); @@ -1454,7 +1253,7 @@ void CBasePlayer::WaterMove() // TRUE if the player is attached to a ladder BOOL CBasePlayer::IsOnLadder( void ) -{ +{ return ( pev->movetype == MOVETYPE_FLY ); } @@ -1467,7 +1266,7 @@ void CBasePlayer::PlayerDeathThink(void) flForward = pev->velocity.Length() - 20; if (flForward <= 0) pev->velocity = g_vecZero; - else + else pev->velocity = flForward * pev->velocity.Normalize(); } @@ -1497,14 +1296,14 @@ void CBasePlayer::PlayerDeathThink(void) if (pev->deadflag == DEAD_DYING) pev->deadflag = DEAD_DEAD; - + StopAnimation(); pev->effects |= EF_NOINTERP; pev->framerate = 0.0; BOOL fAnyButtonDown = (pev->button & ~IN_SCORE ); - + // wait for all buttons released if (pev->deadflag == DEAD_DEAD) { @@ -1516,21 +1315,21 @@ void CBasePlayer::PlayerDeathThink(void) m_fDeadTime = gpGlobals->time; pev->deadflag = DEAD_RESPAWNABLE; } - + return; } -// if the player has been dead for one second longer than allowed by forcerespawn, -// forcerespawn isn't on. Send the player off to an intermission camera until they +// if the player has been dead for one second longer than allowed by forcerespawn, +// forcerespawn isn't on. Send the player off to an intermission camera until they // choose to respawn. if ( g_pGameRules->IsMultiplayer() && ( gpGlobals->time > (m_fDeadTime + 6) ) && !(m_afPhysicsFlags & PFLAG_OBSERVER) ) { - // go to dead camera. + // go to dead camera. StartDeathCam(); } - + // wait for any button down, or mp_forcerespawn is set and the respawn time is up - if (!fAnyButtonDown + if (!fAnyButtonDown && !( g_pGameRules->IsMultiplayer() && forcerespawn->value > 0 && (gpGlobals->time > (m_fDeadTime + 5))) ) return; @@ -1540,8 +1339,7 @@ void CBasePlayer::PlayerDeathThink(void) //ALERT(at_console, "Respawn\n"); respawn(pev, !(m_afPhysicsFlags & PFLAG_OBSERVER) );// don't copy a corpse if we're in deathcam. - pev->view_ofs.z = m_flViewHeight; // restore viewheight on respawn - DontThink(); + pev->nextthink = -1; } //========================================================= @@ -1550,7 +1348,7 @@ void CBasePlayer::PlayerDeathThink(void) //========================================================= void CBasePlayer::StartDeathCam( void ) { - CBaseEntity *pSpot, *pNewSpot; + edict_t *pSpot, *pNewSpot; int iRand; if ( pev->view_ofs == g_vecZero ) @@ -1559,17 +1357,17 @@ void CBasePlayer::StartDeathCam( void ) return; } - pSpot = UTIL_FindEntityByClassname( NULL, "info_intermission"); + pSpot = FIND_ENTITY_BY_CLASSNAME( NULL, "info_intermission"); - if ( pSpot ) + if ( !FNullEnt( pSpot ) ) { // at least one intermission spot in the world. iRand = RANDOM_LONG( 0, 3 ); while ( iRand > 0 ) { - pNewSpot = UTIL_FindEntityByTargetname( pSpot, "info_intermission"); - + pNewSpot = FIND_ENTITY_BY_CLASSNAME( pSpot, "info_intermission"); + if ( pNewSpot ) { pSpot = pNewSpot; @@ -1579,7 +1377,7 @@ void CBasePlayer::StartDeathCam( void ) } CopyToBodyQue( pev ); - StartObserver( pSpot->pev->origin, pSpot->pev->v_angle ); + StartObserver( pSpot->v.origin, pSpot->v.v_angle ); } else { @@ -1603,10 +1401,10 @@ void CBasePlayer::StartObserver( Vector vecPosition, Vector vecViewAngle ) pev->takedamage = DAMAGE_NO; pev->movetype = MOVETYPE_NONE; pev->modelindex = 0; - UTIL_SetOrigin( this, vecPosition ); + UTIL_SetOrigin( pev, vecPosition ); } -// +// // PlayerUse - handles USE keypress // #define PLAYER_SEARCH_RADIUS (float)64 @@ -1657,58 +1455,40 @@ void CBasePlayer::PlayerUse ( void ) Vector vecLOS; float flMaxDot = VIEW_FIELD_NARROW; float flDot; - TraceResult tr; - int caps; UTIL_MakeVectors ( pev->v_angle );// so we know which way we are facing - - //LRC- try to get an exact entity to use. - // (is this causing "use-buttons-through-walls" problems? Surely not!) - UTIL_TraceLine( pev->origin + pev->view_ofs, pev->origin + pev->view_ofs + (gpGlobals->v_forward * PLAYER_SEARCH_RADIUS), dont_ignore_monsters, ENT(pev), &tr ); - if (tr.pHit) + + while ((pObject = UTIL_FindEntityInSphere( pObject, pev->origin, PLAYER_SEARCH_RADIUS )) != NULL) { - pObject = CBaseEntity::Instance(tr.pHit); - if (!pObject || !(pObject->ObjectCaps() & (FCAP_IMPULSE_USE | FCAP_CONTINUOUS_USE | FCAP_ONOFF_USE))) + + if (pObject->ObjectCaps() & (FCAP_IMPULSE_USE | FCAP_CONTINUOUS_USE | FCAP_ONOFF_USE)) { - pObject = NULL; - } - } - - if (!pObject) //LRC- couldn't find a direct solid object to use, try the normal method - { - while ((pObject = UTIL_FindEntityInSphere( pObject, pev->origin, PLAYER_SEARCH_RADIUS )) != NULL) - { - caps = pObject->ObjectCaps(); - if (caps & (FCAP_IMPULSE_USE | FCAP_CONTINUOUS_USE | FCAP_ONOFF_USE) && !(caps & FCAP_ONLYDIRECT_USE)) //LRC - we can't see 'direct use' entities in this section - { - // !!!PERFORMANCE- should this check be done on a per case basis AFTER we've determined that - // this object is actually usable? This dot is being done for every object within PLAYER_SEARCH_RADIUS - // when player hits the use key. How many objects can be in that area, anyway? (sjb) - vecLOS = (VecBModelOrigin( pObject->pev ) - (pev->origin + pev->view_ofs)); - -// ALERT(at_console, "absmin %f %f %f, absmax %f %f %f, mins %f %f %f, maxs %f %f %f, size %f %f %f\n", pObject->pev->absmin.x, pObject->pev->absmin.y, pObject->pev->absmin.z, pObject->pev->absmax.x, pObject->pev->absmax.y, pObject->pev->absmax.z, pObject->pev->mins.x, pObject->pev->mins.y, pObject->pev->mins.z, pObject->pev->maxs.x, pObject->pev->maxs.y, pObject->pev->maxs.z, pObject->pev->size.x, pObject->pev->size.y, pObject->pev->size.z);//LRCTEMP - // This essentially moves the origin of the target to the corner nearest the player to test to see - // if it's "hull" is in the view cone - vecLOS = UTIL_ClampVectorToBox( vecLOS, pObject->pev->size * 0.5 ); - - flDot = DotProduct (vecLOS , gpGlobals->v_forward); - if (flDot > flMaxDot || vecLOS == g_vecZero ) // LRC - if the player is standing inside this entity, it's also ok to use it. - {// only if the item is in front of the user - pClosest = pObject; - flMaxDot = flDot; -// ALERT( at_console, "%s : %f\n", STRING( pObject->pev->classname ), flDot ); - } + // !!!PERFORMANCE- should this check be done on a per case basis AFTER we've determined that + // this object is actually usable? This dot is being done for every object within PLAYER_SEARCH_RADIUS + // when player hits the use key. How many objects can be in that area, anyway? (sjb) + vecLOS = (VecBModelOrigin( pObject->pev ) - (pev->origin + pev->view_ofs)); + + // This essentially moves the origin of the target to the corner nearest the player to test to see + // if it's "hull" is in the view cone + vecLOS = UTIL_ClampVectorToBox( vecLOS, pObject->pev->size * 0.5 ); + + flDot = DotProduct (vecLOS , gpGlobals->v_forward); + if (flDot > flMaxDot ) + {// only if the item is in front of the user + pClosest = pObject; + flMaxDot = flDot; // ALERT( at_console, "%s : %f\n", STRING( pObject->pev->classname ), flDot ); } +// ALERT( at_console, "%s : %f\n", STRING( pObject->pev->classname ), flDot ); } - pObject = pClosest; } + pObject = pClosest; // Found an object if (pObject ) { - //!!!UNDONE: traceline here to prevent USEing buttons through walls - caps = pObject->ObjectCaps(); + //!!!UNDONE: traceline here to prevent USEing buttons through walls + int caps = pObject->ObjectCaps(); if ( m_afButtonPressed & IN_USE ) EMIT_SOUND( ENT(pev), CHAN_ITEM, "common/wpn_select.wav", 0.4, ATTN_NORM); @@ -1722,8 +1502,6 @@ void CBasePlayer::PlayerUse ( void ) pObject->Use( this, this, USE_SET, 1 ); } // UNDONE: Send different USE codes for ON/OFF. Cache last ONOFF_USE object to send 'off' if you turn away - // (actually, nothing uses on/off. They're either continuous - rechargers and momentary - // buttons - or they're impulse - buttons, doors, tanks, trains, etc.) --LRC else if ( (m_afButtonReleased & IN_USE) && (pObject->ObjectCaps() & FCAP_ONOFF_USE) ) // BUGBUG This is an "off" use { pObject->Use( this, this, USE_SET, 0 ); @@ -1744,11 +1522,11 @@ void CBasePlayer::Jump() Vector vecAdjustedVelocity; Vector vecSpot; TraceResult tr; - + if (FBitSet(pev->flags, FL_WATERJUMP)) return; - - if (pev->waterlevel >= 2 && pev->watertype != CONTENTS_FOG) + + if (pev->waterlevel >= 2) { return; } @@ -1768,7 +1546,7 @@ void CBasePlayer::Jump() UTIL_MakeVectors (pev->angles); // ClearBits(pev->flags, FL_ONGROUND); // don't stairwalk - + SetAnimation( PLAYER_JUMP ); if ( m_fLongJump && @@ -1779,7 +1557,7 @@ void CBasePlayer::Jump() SetAnimation( PLAYER_SUPERJUMP ); } - // If you're standing on a conveyor, add its velocity to yours (for momentum) + // If you're standing on a conveyor, add it's velocity to yours (for momentum) entvars_t *pevGround = VARS(pev->groundentity); if ( pevGround && (pevGround->flags & FL_CONVEYOR) ) { @@ -1809,7 +1587,7 @@ void FixPlayerCrouchStuck( edict_t *pPlayer ) void CBasePlayer::Duck( ) { - if (pev->button & IN_DUCK) + if (pev->button & IN_DUCK) { if ( m_IdealActivity != ACT_LEAP ) { @@ -1836,7 +1614,7 @@ void CBasePlayer::AddPoints( int score, BOOL bAllowNegativeScore ) { if ( pev->frags < 0 ) // Can't go more negative return; - + if ( -score > pev->frags ) // Will this go negative? { score = -pev->frags; // Sum will be 0 @@ -1878,7 +1656,7 @@ void CBasePlayer::AddPointsToTeam( int score, BOOL bAllowNegativeScore ) void CBasePlayer::InitStatusBar() { m_flStatusBarDisappearDelay = 0; - m_SbarString1[0] = m_SbarString0[0] = 0; + m_SbarString1[0] = m_SbarString0[0] = 0; } void CBasePlayer::UpdateStatusBar() @@ -1988,22 +1766,19 @@ void CBasePlayer::UpdateStatusBar() void CBasePlayer::PreThink(void) { int buttonsChanged = (m_afButtonLast ^ pev->button); // These buttons have changed this frame - + // Debounced button codes for pressed/released // UNDONE: Do we need auto-repeat? - m_afButtonPressed = buttonsChanged & pev->button; // The ones that changed and are now down are "pressed" - m_afButtonReleased = buttonsChanged & (~pev->button); // The ones that changed and aren't down are "released" + m_afButtonPressed = buttonsChanged & pev->button; // The changed ones still down are "pressed" + m_afButtonReleased = buttonsChanged & (~pev->button); // The ones not down are "released" g_pGameRules->PlayerThink( this ); -// CheckDesiredList( ); //LRC - //CheckAssistList(); //LRC - if ( g_fGameOver ) return; // intermission or finale UTIL_MakeVectors(pev->v_angle); // is this still used? - + ItemPreFrame( ); WaterMove(); @@ -2015,7 +1790,7 @@ void CBasePlayer::PreThink(void) // JOHN: checks if new client data (for HUD and view control) needs to be sent to the client UpdateClientData(); - + CheckTimeBasedDamage(); CheckSuitUpdate(); @@ -2030,7 +1805,7 @@ void CBasePlayer::PreThink(void) // if ( m_afPhysicsFlags & PFLAG_ONTRAIN ) pev->flags |= FL_ONTRAIN; - else + else pev->flags &= ~FL_ONTRAIN; // Train speed control @@ -2038,7 +1813,7 @@ void CBasePlayer::PreThink(void) { CBaseEntity *pTrain = CBaseEntity::Instance( pev->groundentity ); float vel; - + if ( !pTrain ) { TraceResult trainTrace; @@ -2115,7 +1890,7 @@ void CBasePlayer::PreThink(void) pev->velocity = g_vecZero; } } -/* Time based Damage works as follows: +/* Time based Damage works as follows: 1) There are several types of timebased damage: #define DMG_PARALYZE (1 << 14) // slows affected creature down @@ -2128,12 +1903,12 @@ void CBasePlayer::PreThink(void) #define DMG_SLOWFREEZE (1 << 21) // in a subzero freezer 2) A new hit inflicting tbd restarts the tbd counter - each monster has an 8bit counter, - per damage type. The counter is decremented every second, so the maximum time + per damage type. The counter is decremented every second, so the maximum time an effect will last is 255/60 = 4.25 minutes. Of course, staying within the radius of a damaging effect like fire, nervegas, radiation will continually reset the counter to max. 3) Every second that a tbd counter is running, the player takes damage. The damage - is determined by the type of tdb. + is determined by the type of tdb. Paralyze - 1/2 movement rate, 30 second duration. Nervegas - 5 points per second, 16 second duration = 80 points max dose. Poison - 2 points per second, 25 second duration = 50 points max dose. @@ -2156,8 +1931,8 @@ void CBasePlayer::PreThink(void) Antitoxin Syringe - Each syringe full provides protection vs one poisoning (nervegas or poison). Health kit - Immediate stop to acid/chemical, fire or freeze damage. Radiation Shower - Immediate stop to radiation damage, acid/chemical or fire damage. - - + + */ // If player is taking time based damage, continue doing damage to player - @@ -2193,7 +1968,7 @@ void CBasePlayer::PreThink(void) /* */ -void CBasePlayer::CheckTimeBasedDamage() +void CBasePlayer::CheckTimeBasedDamage() { int i; BYTE bDuration = 0; @@ -2206,7 +1981,7 @@ void CBasePlayer::CheckTimeBasedDamage() // only check for time based damage approx. every 2 seconds if (abs(gpGlobals->time - m_tbdPrev) < 2.0) return; - + m_tbdPrev = gpGlobals->time; for (i = 0; i < CDMG_TIMEBASED; i++) @@ -2221,7 +1996,7 @@ void CBasePlayer::CheckTimeBasedDamage() bDuration = PARALYZE_DURATION; break; case itbd_NerveGas: -// TakeDamage(pev, pev, NERVEGAS_DAMAGE, DMG_GENERIC); +// TakeDamage(pev, pev, NERVEGAS_DAMAGE, DMG_GENERIC); bDuration = NERVEGAS_DURATION; break; case itbd_Poison: @@ -2262,7 +2037,7 @@ void CBasePlayer::CheckTimeBasedDamage() if (m_rgbTimeBasedDamage[i]) { - // use up an antitoxin on poison or nervegas after a few seconds of damage + // use up an antitoxin on poison or nervegas after a few seconds of damage if (((i == itbd_NerveGas) && (m_rgbTimeBasedDamage[i] < NERVEGAS_DURATION)) || ((i == itbd_Poison) && (m_rgbTimeBasedDamage[i] < POISON_DURATION))) { @@ -2280,7 +2055,7 @@ void CBasePlayer::CheckTimeBasedDamage() { m_rgbTimeBasedDamage[i] = 0; // if we're done, clear damage bits - m_bitsDamageType &= ~(DMG_PARALYZE << i); + m_bitsDamageType &= ~(DMG_PARALYZE << i); } } else @@ -2293,17 +2068,17 @@ void CBasePlayer::CheckTimeBasedDamage() /* THE POWER SUIT -The Suit provides 3 main functions: Protection, Notification and Augmentation. -Some functions are automatic, some require power. +The Suit provides 3 main functions: Protection, Notification and Augmentation. +Some functions are automatic, some require power. The player gets the suit shortly after getting off the train in C1A0 and it stays with him for the entire game. Protection Heat/Cold - When the player enters a hot/cold area, the heating/cooling indicator on the suit - will come on and the battery will drain while the player stays in the area. - After the battery is dead, the player starts to take damage. + When the player enters a hot/cold area, the heating/cooling indicator on the suit + will come on and the battery will drain while the player stays in the area. + After the battery is dead, the player starts to take damage. This feature is built into the suit and is automatically engaged. Radiation Syringe This will cause the player to be immune from the effects of radiation for N seconds. Single use item. @@ -2316,45 +2091,45 @@ Protection The armor works using energy to create a protective field that deflects a percentage of damage projectile and explosive attacks. After the armor has been deployed, it will attempt to recharge itself to full capacity with the energy reserves from the battery. - It takes the armor N seconds to fully charge. + It takes the armor N seconds to fully charge. Notification (via the HUD) x Health -x Ammo +x Ammo x Automatic Health Care - Notifies the player when automatic healing has been engaged. + Notifies the player when automatic healing has been engaged. x Geiger counter - Classic Geiger counter sound and status bar at top of HUD + Classic Geiger counter sound and status bar at top of HUD alerts player to dangerous levels of radiation. This is not visible when radiation levels are normal. x Poison Armor - Displays the current level of armor. + Displays the current level of armor. -Augmentation +Augmentation Reanimation (w/adrenaline) - Causes the player to come back to life after he has been dead for 3 seconds. + Causes the player to come back to life after he has been dead for 3 seconds. Will not work if player was gibbed. Single use. Long Jump Used by hitting the ??? key(s). Caused the player to further than normal. - SCUBA - Used automatically after picked up and after player enters the water. - Works for N seconds. Single use. - + SCUBA + Used automatically after picked up and after player enters the water. + Works for N seconds. Single use. + Things powered by the battery - Armor + Armor Uses N watts for every M units of damage. - Heat/Cool + Heat/Cool Uses N watts for every second in hot/cold area. - Long Jump + Long Jump Uses N watts for every jump. - Alien Cloak + Alien Cloak Uses N watts for each use. Each use lasts M seconds. - Alien Shield + Alien Shield Augments armor. Reduces Armor drain by one half - + */ // if in range of radiation source, ping geiger counter @@ -2370,7 +2145,7 @@ void CBasePlayer :: UpdateGeigerCounter( void ) return; m_flgeigerDelay = gpGlobals->time + GEIGERDELAY; - + // send range to radition source to client range = (BYTE) (m_flgeigerRange / 4); @@ -2406,9 +2181,10 @@ void CBasePlayer::CheckSuitUpdate() int i; int isentence = 0; int isearch = m_iSuitPlayNext; - + // Ignore suit updates if no suit - if(!(pev->weapons & ITEM_SUIT)) return; + if ( !(pev->weapons & (1<time + SUITUPDATETIME; } else - // queue is empty, don't check + // queue is empty, don't check m_flSuitUpdate = 0; } } - + // add sentence to suit playlist queue. if fgroup is true, then // name is a sentence group (HEV_AA), otherwise name is a specific // sentence name ie: !HEV_AA0. If iNoRepeat is specified in @@ -2467,10 +2243,10 @@ void CBasePlayer::SetSuitUpdate(char *name, int fgroup, int iNoRepeatTime) int i; int isentence; int iempty = -1; - - + + // Ignore suit updates if no suit - if ( !(pev->weapons & ITEM_SUIT) ) + if ( !(pev->weapons & (1<IsMultiplayer() ) @@ -2492,10 +2268,7 @@ void CBasePlayer::SetSuitUpdate(char *name, int fgroup, int iNoRepeatTime) { isentence = SENTENCEG_Lookup(name, NULL); if (isentence < 0) - { - ALERT(at_debug,"HEV couldn't find sentence %s\n",name); return; - } } else // mark group number as negative @@ -2509,7 +2282,7 @@ void CBasePlayer::SetSuitUpdate(char *name, int fgroup, int iNoRepeatTime) { if (isentence == m_rgiSuitNoRepeat[i]) { - // this sentence or group is already in + // this sentence or group is already in // the norepeat list if (m_rgflSuitNoRepeatTime[i] < gpGlobals->time) @@ -2542,7 +2315,7 @@ void CBasePlayer::SetSuitUpdate(char *name, int fgroup, int iNoRepeatTime) } // find empty spot in queue, or overwrite last spot - + m_rgSuitPlayList[m_iSuitPlayNext++] = isentence; if (m_iSuitPlayNext == CSUITPLAYLIST) m_iSuitPlayNext = 0; @@ -2552,8 +2325,8 @@ void CBasePlayer::SetSuitUpdate(char *name, int fgroup, int iNoRepeatTime) if (m_flSuitUpdate == 0) // play queue is empty, don't delay too long before playback m_flSuitUpdate = gpGlobals->time + SUITFIRSTUPDATETIME; - else - m_flSuitUpdate = gpGlobals->time + SUITUPDATETIME; + else + m_flSuitUpdate = gpGlobals->time + SUITUPDATETIME; } } @@ -2591,7 +2364,7 @@ void CBasePlayer :: UpdatePlayerSound ( void ) if ( !pSound ) { - ALERT ( at_debug, "Client lost reserved sound!\n" ); + ALERT ( at_console, "Client lost reserved sound!\n" ); return; } @@ -2599,13 +2372,13 @@ void CBasePlayer :: UpdatePlayerSound ( void ) // now calculate the best target volume for the sound. If the player's weapon // is louder than his body/movement, use the weapon volume, else, use the body volume. - + if ( FBitSet ( pev->flags, FL_ONGROUND ) ) - { - iBodyVolume = pev->velocity.Length(); + { + iBodyVolume = pev->velocity.Length(); // clamp the noise that can be made by the body, in case a push trigger, - // weapon recoil, or anything shoves the player abnormally fast. + // weapon recoil, or anything shoves the player abnormally fast. if ( iBodyVolume > 512 ) { iBodyVolume = 512; @@ -2626,7 +2399,7 @@ void CBasePlayer :: UpdatePlayerSound ( void ) { m_iTargetVolume = m_iWeaponVolume; - // OR in the bits for COMBAT sound if the weapon is being louder than the player. + // OR in the bits for COMBAT sound if the weapon is being louder than the player. pSound->m_iType |= bits_SOUND_COMBAT; } else @@ -2642,7 +2415,7 @@ void CBasePlayer :: UpdatePlayerSound ( void ) } - // if target volume is greater than the player sound's current volume, we paste the new volume in + // if target volume is greater than the player sound's current volume, we paste the new volume in // immediately. If target is less than the current volume, current volume is not set immediately to the // lower volume, rather works itself towards target volume over time. This gives monsters a much better chance // to hear a sound, especially if they don't listen every frame. @@ -2670,7 +2443,7 @@ void CBasePlayer :: UpdatePlayerSound ( void ) if ( gpGlobals->time > m_flStopExtraSoundTime ) { - // since the extra sound that a weapon emits only lasts for one client frame, we keep that sound around for a server frame or two + // since the extra sound that a weapon emits only lasts for one client frame, we keep that sound around for a server frame or two // after actual emission to make sure it gets heard. m_iExtraSoundTypes = 0; } @@ -2690,8 +2463,8 @@ void CBasePlayer :: UpdatePlayerSound ( void ) //UTIL_MakeVectors ( pev->angles ); //gpGlobals->v_forward.z = 0; - // Below are a couple of useful little bits that make it easier to determine just how much noise the - // player is making. + // Below are a couple of useful little bits that make it easier to determine just how much noise the + // player is making. // UTIL_ParticleEffect ( pev->origin + gpGlobals->v_forward * iVolume, g_vecZero, 255, 25 ); //ALERT ( at_console, "%d/%d\n", iVolume, m_iTargetVolume ); } @@ -2709,9 +2482,8 @@ void CBasePlayer::PostThink() if ( m_pTank != NULL ) { // if they've moved too far from the gun, or selected a weapon, unuse the gun if ( m_pTank->OnControls( pev ) && !pev->weaponmodel ) - { -//LRC - This is now handled with the Think function, by TrackTarget -// m_pTank->Use( this, this, USE_SET, 2 ); // try fire the gun + { + m_pTank->Use( this, this, USE_SET, 2 ); // try fire the gun } else { // they've moved off the platform @@ -2726,24 +2498,24 @@ void CBasePlayer::PostThink() // check to see if player landed hard enough to make a sound // falling farther than half of the maximum safe distance, but not as far a max safe distance will // play a bootscrape sound, and no damage will be inflicted. Fallling a distance shorter than half -// of maximum safe distance will make no sound. Falling farther than max safe distance will play a +// of maximum safe distance will make no sound. Falling farther than max safe distance will play a // fallpain sound, and damage will be inflicted based on how far the player fell if ( (FBitSet(pev->flags, FL_ONGROUND)) && (pev->health > 0) && m_flFallVelocity >= PLAYER_FALL_PUNCH_THRESHHOLD ) { // ALERT ( at_console, "%f\n", m_flFallVelocity ); - if (pev->watertype == CONTENTS_WATER) + if (pev->watertype == CONTENT_WATER) { // Did he hit the world or a non-moving entity? - // BUG - this happens all the time in water, especially when + // BUG - this happens all the time in water, especially when // BUG - water has current force // if ( !pev->groundentity || VARS(pev->groundentity)->velocity.z == 0 ) // EMIT_SOUND(ENT(pev), CHAN_BODY, "player/pl_wade1.wav", 1, ATTN_NORM); } else if ( m_flFallVelocity > PLAYER_MAX_SAFE_FALL_SPEED ) {// after this point, we start doing damage - + float flFallDamage = g_pGameRules->FlPlayerFallDamage( this ); if ( flFallDamage > pev->health ) @@ -2754,7 +2526,7 @@ void CBasePlayer::PostThink() if ( flFallDamage > 0 ) { - TakeDamage(VARS(eoNullEntity), VARS(eoNullEntity), flFallDamage, DMG_FALL ); + TakeDamage(VARS(eoNullEntity), VARS(eoNullEntity), flFallDamage, DMG_FALL ); pev->punchangle.x = 0; } } @@ -2766,7 +2538,7 @@ void CBasePlayer::PostThink() } if (FBitSet(pev->flags, FL_ONGROUND)) - { + { if (m_flFallVelocity > 64 && !g_pGameRules->IsMultiplayer()) { CSoundEnt::InsertSound ( bits_SOUND_PLAYER, pev->origin, m_flFallVelocity, 0.2 ); @@ -2775,7 +2547,7 @@ void CBasePlayer::PostThink() m_flFallVelocity = 0; } - // select the proper animation for the player character + // select the proper animation for the player character if ( IsAlive() ) { if (!pev->velocity.x && !pev->velocity.y) @@ -2794,7 +2566,74 @@ void CBasePlayer::PostThink() // Track button info so we can detect 'pressed' and 'released' buttons next frame m_afButtonLast = pev->button; -pt_end: return; +pt_end: +#if defined( CLIENT_WEAPONS ) + // Decay timers on weapons + // go through all of the weapons and make a list of the ones to pack + for ( int i = 0 ; i < MAX_ITEM_TYPES ; i++ ) + { + if ( m_rgpPlayerItems[ i ] ) + { + CBasePlayerItem *pPlayerItem = m_rgpPlayerItems[ i ]; + + while ( pPlayerItem ) + { + CBasePlayerWeapon *gun; + + gun = (CBasePlayerWeapon *)pPlayerItem->GetWeaponPtr(); + + if ( gun && gun->UseDecrement() ) + { + gun->m_flNextPrimaryAttack = max( gun->m_flNextPrimaryAttack - gpGlobals->frametime, -1.0 ); + gun->m_flNextSecondaryAttack = max( gun->m_flNextSecondaryAttack - gpGlobals->frametime, -0.001 ); + + if ( gun->m_flTimeWeaponIdle != 1000 ) + { + gun->m_flTimeWeaponIdle = max( gun->m_flTimeWeaponIdle - gpGlobals->frametime, -0.001 ); + } + + if ( gun->pev->fuser1 != 1000 ) + { + gun->pev->fuser1 = max( gun->pev->fuser1 - gpGlobals->frametime, -0.001 ); + } + + // Only decrement if not flagged as NO_DECREMENT +// if ( gun->m_flPumpTime != 1000 ) + // { + // gun->m_flPumpTime = max( gun->m_flPumpTime - gpGlobals->frametime, -0.001 ); + // } + + } + + pPlayerItem = pPlayerItem->m_pNext; + } + } + } + + m_flNextAttack -= gpGlobals->frametime; + if ( m_flNextAttack < -0.001 ) + m_flNextAttack = -0.001; + + if ( m_flNextAmmoBurn != 1000 ) + { + m_flNextAmmoBurn -= gpGlobals->frametime; + + if ( m_flNextAmmoBurn < -0.001 ) + m_flNextAmmoBurn = -0.001; + } + + if ( m_flAmmoStartCharge != 1000 ) + { + m_flAmmoStartCharge -= gpGlobals->frametime; + + if ( m_flAmmoStartCharge < -0.001 ) + m_flAmmoStartCharge = -0.001; + } + + +#else + return; +#endif } @@ -2803,7 +2642,7 @@ BOOL IsSpawnPointValid( CBaseEntity *pPlayer, CBaseEntity *pSpot ) { CBaseEntity *ent = NULL; - if ( pSpot->GetState( pPlayer ) != STATE_ON ) + if ( !pSpot->IsTriggered( pPlayer ) ) { return FALSE; } @@ -2820,8 +2659,7 @@ BOOL IsSpawnPointValid( CBaseEntity *pPlayer, CBaseEntity *pSpot ) DLL_GLOBAL CBaseEntity *g_pLastSpawn; -//LRC- moved to cbase.h -//inline int FNullEnt( CBaseEntity *ent ) { return (ent == NULL) || FNullEnt( ent->edict() ); } +inline int FNullEnt( CBaseEntity *ent ) { return (ent == NULL) || FNullEnt( ent->edict() ); } /* ============ @@ -2846,7 +2684,7 @@ edict_t *EntSelectSpawnPoint( CBaseEntity *pPlayer ) if ( !FNullEnt(pSpot) ) goto ReturnSpot; pSpot = UTIL_FindEntityByClassname( g_pLastSpawn, "info_player_start"); - if ( !FNullEnt(pSpot) ) + if ( !FNullEnt(pSpot) ) goto ReturnSpot; } else if ( g_pGameRules->IsDeathmatch() ) @@ -2860,7 +2698,7 @@ edict_t *EntSelectSpawnPoint( CBaseEntity *pPlayer ) CBaseEntity *pFirstSpot = pSpot; - do + do { if ( pSpot ) { @@ -2922,8 +2760,6 @@ ReturnSpot: void CBasePlayer::Spawn( void ) { -// ALERT(at_console, "PLAYER spawns at time %f\n", gpGlobals->time); - pev->classname = MAKE_STRING("player"); pev->health = 100; pev->armorvalue = 0; @@ -2939,39 +2775,24 @@ void CBasePlayer::Spawn( void ) pev->deadflag = DEAD_NO; pev->dmg_take = 0; pev->dmg_save = 0; - pev->skin = atoi(g_engfuncs.pfnInfoKeyValue(g_engfuncs.pfnGetInfoKeyBuffer(edict()), "skin"));// XWider pev->friction = 1.0; pev->gravity = 1.0; - pev->renderfx = 0; - pev->rendercolor = g_vecZero; - pev->v_angle.z = 0; // cut off any camera rolling m_bitsHUDDamage = -1; m_bitsDamageType = 0; m_afPhysicsFlags = 0; - m_fLongJump = FALSE;// no longjump module. - Rain_dripsPerSecond = 0; - Rain_windX = 0; - Rain_windY = 0; - Rain_randX = 0; - Rain_randY = 0; - Rain_ideal_dripsPerSecond = 0; - Rain_ideal_windX = 0; - Rain_ideal_windY = 0; - Rain_ideal_randX = 0; - Rain_ideal_randY = 0; - Rain_endFade = 0; - Rain_nextFadeUpdate = 0; + m_fLongJump = FALSE;// no longjump module. g_engfuncs.pfnSetPhysicsKeyValue( edict(), "slj", "0" ); g_engfuncs.pfnSetPhysicsKeyValue( edict(), "hl", "1" ); - pev->fov = 90.0f;// init field of view. + pev->fov = m_iFOV = 0;// init field of view. + m_iClientFOV = -1; // make sure fov reset is sent m_flNextDecalTime = 0;// let this player decal as soon as he spawns. m_flgeigerDelay = gpGlobals->time + 2.0; // wait a few seconds until user-defined message registrations // are recieved by all clients - + m_flTimeStepSound = 0; m_iStepLeft = 0; m_flFieldOfView = 0.5;// some monsters use this to determine whether or not the player is looking at them. @@ -2980,7 +2801,7 @@ void CBasePlayer::Spawn( void ) m_flNextAttack = UTIL_WeaponTimeBase(); StartSneaking(); - m_iFlashBattery = 0; //discharge flashlight at start + m_iFlashBattery = 99; m_flFlashLightTime = 1; // force first message // dont let uninitialized value here hurt the player @@ -2989,24 +2810,22 @@ void CBasePlayer::Spawn( void ) g_pGameRules->SetDefaultPlayerTeam( this ); g_pGameRules->GetPlayerSpawnSpot( this ); - SetObjectClass( ED_CLIENT ); // critical stuff!!! - SET_MODEL( ENT( pev ), "models/player.mdl" ); - g_ulModelIndexPlayer = pev->modelindex; - pev->sequence = LookupActivity( ACT_IDLE ); + SET_MODEL(ENT(pev), "models/player.mdl"); + g_ulModelIndexPlayer = pev->modelindex; + pev->sequence = LookupActivity( ACT_IDLE ); - if( FBitSet( pev->flags, FL_DUCKING )) - UTIL_SetSize( pev, VEC_DUCK_HULL_MIN, VEC_DUCK_HULL_MAX ); - else UTIL_SetSize( pev, VEC_HULL_MIN, VEC_HULL_MAX ); + if ( FBitSet(pev->flags, FL_DUCKING) ) + UTIL_SetSize(pev, VEC_DUCK_HULL_MIN, VEC_DUCK_HULL_MAX); + else + UTIL_SetSize(pev, VEC_HULL_MIN, VEC_HULL_MAX); - pev->view_ofs = VEC_VIEW; - viewEntity = 0; - viewFlags = 0; + pev->view_ofs = VEC_VIEW; Precache(); m_HackedGunPos = Vector( 0, 32, 0 ); if ( m_iPlayerSound == SOUNDLIST_EMPTY ) { - ALERT ( at_debug, "Couldn't alloc player sound slot!\n" ); + ALERT ( at_console, "Couldn't alloc player sound slot!\n" ); } m_fNoPlayerSound = FALSE;// normal sound behavior. @@ -3017,9 +2836,7 @@ void CBasePlayer::Spawn( void ) m_fWeapon = FALSE; m_pClientActiveItem = NULL; m_iClientBattery = -1; - m_iClientFlashState = -1; - m_iClientFlashlight = -1; - + // reset all ammo values to 0 for ( int i = 0; i < MAX_AMMO_SLOTS; i++ ) { @@ -3028,7 +2845,7 @@ void CBasePlayer::Spawn( void ) } m_lastx = m_lasty = 0; - + m_flNextChatTime = gpGlobals->time; g_pGameRules->PlayerSpawn( this ); @@ -3039,20 +2856,20 @@ void CBasePlayer :: Precache( void ) { // in the event that the player JUST spawned, and the level node graph // was loaded, fix all of the node graph pointers before the game starts. - + // !!!BUGBUG - now that we have multiplayer, this needs to be moved! if ( WorldGraph.m_fGraphPresent && !WorldGraph.m_fGraphPointersSet ) { if ( !WorldGraph.FSetGraphPointers() ) { - ALERT ( at_debug, "**Graph pointers were not set!\n"); + ALERT ( at_console, "**Graph pointers were not set!\n"); } else { - ALERT ( at_debug, "**Graph Pointers Set!\n" ); - } + ALERT ( at_console, "**Graph Pointers Set!\n" ); + } } -// G-Cont. moved to CWorld + // SOUNDS / MODELS ARE PRECACHED in ClientPrecache() (game specific) // because they need to precache before any clients have connected @@ -3066,21 +2883,16 @@ void CBasePlayer :: Precache( void ) m_bitsHUDDamage = -1; m_iClientBattery = -1; - m_iClientFlashState = -1; - m_iClientFlashlight = -1; - + m_iTrain = TRAIN_NEW; // Make sure any necessary user messages have been registered LinkUserMessages(); - viewNeedsUpdate = 1; - fogNeedsUpdate = 1; m_iUpdateTime = 5; // won't update for 1/2 a second if ( gInitHUD ) m_fInitHUD = TRUE; - Rain_needsUpdate = 1; } @@ -3089,7 +2901,7 @@ int CBasePlayer::Save( CSave &save ) if ( !CBaseMonster::Save(save) ) return 0; - return save.WriteFields( "cPLAYER", "PLAYER", this, m_playerSaveData, ARRAYSIZE(m_playerSaveData) ); + return save.WriteFields( "PLAYER", this, m_playerSaveData, ARRAYSIZE(m_playerSaveData) ); } @@ -3113,7 +2925,7 @@ int CBasePlayer::Restore( CRestore &restore ) // landmark isn't present. if ( !pSaveData->fUseLandmark ) { - ALERT( at_debug, "No Landmark:%s\n", pSaveData->szLandmarkName ); + ALERT( at_console, "No Landmark:%s\n", pSaveData->szLandmarkName ); // default to normal spawn edict_t* pentSpawnSpot = EntSelectSpawnPoint( this ); @@ -3130,7 +2942,7 @@ int CBasePlayer::Restore( CRestore &restore ) g_ulModelIndexPlayer = pev->modelindex; - if ( FBitSet(pev->flags, FL_DUCKING) ) + if ( FBitSet(pev->flags, FL_DUCKING) ) { // Use the crouch HACK //FixPlayerCrouchStuck( edict() ); @@ -3154,6 +2966,14 @@ int CBasePlayer::Restore( CRestore &restore ) } RenewItems(); + +#if defined( CLIENT_WEAPONS ) + // HACK: This variable is saved/restored in CBaseMonster as a time variable, but we're using it + // as just a counter. Ideally, this needs its own variable that's saved as a plain float. + // Barring that, we clear it out here instead of using the incorrect restored time value. + m_flNextAttack = UTIL_WeaponTimeBase(); +#endif + return status; } @@ -3164,14 +2984,14 @@ void CBasePlayer::SelectNextItem( int iItem ) CBasePlayerItem *pItem; pItem = m_rgpPlayerItems[ iItem ]; - + if (!pItem) return; if (pItem == m_pActiveItem) { // select the next one in the chain - pItem = m_pActiveItem->m_pNext; + pItem = m_pActiveItem->m_pNext; if (! pItem) { return; @@ -3195,8 +3015,8 @@ void CBasePlayer::SelectNextItem( int iItem ) { m_pActiveItem->Holster( ); } - - QueueItem(pItem); + + m_pActiveItem = pItem; if (m_pActiveItem) { @@ -3205,21 +3025,6 @@ void CBasePlayer::SelectNextItem( int iItem ) } } -void CBasePlayer::QueueItem(CBasePlayerItem *pItem) -{ - if(!m_pActiveItem)// no active weapon - { - m_pActiveItem = pItem; - return;// just set this item as active - } - else - { - m_pLastItem = m_pActiveItem; - m_pActiveItem = NULL;// clear current - } - m_pNextItem = pItem;// add item to queue -} - void CBasePlayer::SelectItem(const char *pstr) { if (!pstr) @@ -3232,7 +3037,7 @@ void CBasePlayer::SelectItem(const char *pstr) if (m_rgpPlayerItems[i]) { pItem = m_rgpPlayerItems[i]; - + while (pItem) { if (FClassnameIs(pItem->pev, pstr)) @@ -3248,7 +3053,7 @@ void CBasePlayer::SelectItem(const char *pstr) if (!pItem) return; - + if (pItem == m_pActiveItem) return; @@ -3257,8 +3062,9 @@ void CBasePlayer::SelectItem(const char *pstr) // FIX, this needs to queue them up and delay if (m_pActiveItem) m_pActiveItem->Holster( ); - - QueueItem(pItem); + + m_pLastItem = m_pActiveItem; + m_pActiveItem = pItem; if (m_pActiveItem) { @@ -3285,14 +3091,12 @@ void CBasePlayer::SelectLastItem(void) // FIX, this needs to queue them up and delay if (m_pActiveItem) m_pActiveItem->Holster( ); - - QueueItem(m_pLastItem); - - if (m_pActiveItem) - { - m_pActiveItem->Deploy( ); - m_pActiveItem->UpdateItemInfo( ); - } + + CBasePlayerItem *pTemp = m_pActiveItem; + m_pActiveItem = m_pLastItem; + m_pLastItem = pTemp; + m_pActiveItem->Deploy( ); + m_pActiveItem->UpdateItemInfo( ); } //============================================== @@ -3348,17 +3152,17 @@ void CSprayCan::Spawn ( entvars_t *pevOwner ) pev->owner = ENT(pevOwner); pev->frame = 0; - SetNextThink( 0.1 ); + pev->nextthink = gpGlobals->time + 0.1; EMIT_SOUND(ENT(pev), CHAN_VOICE, "player/sprayer.wav", 1, ATTN_NORM); } void CSprayCan::Think( void ) { - TraceResult tr; + TraceResult tr; int playernum; int nFrames; CBasePlayer *pPlayer; - + pPlayer = (CBasePlayer *)GET_PRIVATE(pev->owner); if (pPlayer) @@ -3367,7 +3171,7 @@ void CSprayCan::Think( void ) nFrames = -1; playernum = ENTINDEX(pev->owner); - + // ALERT(at_console, "Spray by player %i, %i of %i\n", playernum, (int)(pev->frame + 1), nFrames); UTIL_MakeVectors(pev->angles); @@ -3387,7 +3191,7 @@ void CSprayCan::Think( void ) UTIL_Remove( this ); } - SetNextThink( 0.1 ); + pev->nextthink = gpGlobals->time + 0.1; } class CBloodSplat : public CBaseEntity @@ -3403,25 +3207,23 @@ void CBloodSplat::Spawn ( entvars_t *pevOwner ) pev->angles = pevOwner->v_angle; pev->owner = ENT(pevOwner); - SetThink(&CBloodSplat:: Spray ); - SetNextThink( 0.1 ); + SetThink ( Spray ); + pev->nextthink = gpGlobals->time + 0.1; } void CBloodSplat::Spray ( void ) { - TraceResult tr; - + TraceResult tr; + if ( g_Language != LANGUAGE_GERMAN ) { UTIL_MakeVectors(pev->angles); UTIL_TraceLine ( pev->origin, pev->origin + gpGlobals->v_forward * 128, ignore_monsters, pev->owner, & tr); - CBaseEntity *pHit = CBaseEntity::Instance( tr.pHit ); - PLAYBACK_EVENT_FULL( FEV_RELIABLE|FEV_GLOBAL, edict(), m_usDecals, 0.0, (float *)&tr.vecEndPos, (float *)&g_vecZero, 0.0, 0.0, pHit->entindex(), 1, 0, 0 ); - //UTIL_BloodDecalTrace( &tr, BLOOD_COLOR_RED ); + UTIL_BloodDecalTrace( &tr, BLOOD_COLOR_RED ); } - SetThink(&CBloodSplat:: SUB_Remove ); - SetNextThink( 0.1 ); + SetThink ( SUB_Remove ); + pev->nextthink = gpGlobals->time + 0.1; } //============================================== @@ -3437,7 +3239,7 @@ void CBasePlayer::GiveNamedItem( const char *pszName ) pent = CREATE_NAMED_ENTITY(istr); if ( FNullEnt( pent ) ) { - ALERT ( at_debug, "NULL Ent in GiveNamedItem!\n" ); + ALERT ( at_console, "NULL Ent in GiveNamedItem!\n" ); return; } VARS( pent )->origin = pev->origin; @@ -3472,12 +3274,22 @@ BOOL CBasePlayer :: FlashlightIsOn( void ) void CBasePlayer :: FlashlightTurnOn( void ) { - if ( !g_pGameRules->FAllowFlashlight()) return; + if ( !g_pGameRules->FAllowFlashlight() ) + { + return; + } - if ( pev->weapons & ITEM_SUIT ) + if ( (pev->weapons & (1<effects, EF_DIMLIGHT); + SetBits(pev->effects, EF_DIMLIGHT); + MESSAGE_BEGIN( MSG_ONE, gmsgFlashlight, NULL, pev ); + WRITE_BYTE(1); + WRITE_BYTE(m_iFlashBattery); + MESSAGE_END(); + + m_flFlashLightTime = FLASH_DRAIN_TIME + gpGlobals->time; + } } @@ -3485,7 +3297,14 @@ void CBasePlayer :: FlashlightTurnOn( void ) void CBasePlayer :: FlashlightTurnOff( void ) { EMIT_SOUND_DYN( ENT(pev), CHAN_WEAPON, SOUND_FLASHLIGHT_OFF, 1.0, ATTN_NORM, 0, PITCH_NORM ); - ClearBits(pev->effects, EF_DIMLIGHT); + ClearBits(pev->effects, EF_DIMLIGHT); + MESSAGE_BEGIN( MSG_ONE, gmsgFlashlight, NULL, pev ); + WRITE_BYTE(0); + WRITE_BYTE(m_iFlashBattery); + MESSAGE_END(); + + m_flFlashLightTime = FLASH_CHARGE_TIME + gpGlobals->time; + } /* @@ -3501,17 +3320,10 @@ void CBasePlayer :: ForceClientDllUpdate( void ) { m_iClientHealth = -1; m_iClientBattery = -1; - m_iClientFlashState = -1; - m_iClientFlashlight = -1; - m_iTrain |= TRAIN_NEW; // Force new train message. m_fWeapon = FALSE; // Force weapon send m_fKnownItem = FALSE; // Force weaponinit messages. m_fInitHUD = TRUE; // Force HUD gmsgResetHUD message - m_flFlashLightTime = 1; // Force to update flashlight - Rain_needsUpdate = 1; - viewNeedsUpdate = 1; - fogNeedsUpdate = 1; // Now force all the necessary messages // to be sent. @@ -3531,7 +3343,7 @@ void CBasePlayer::ImpulseCommands( ) // Handle use events PlayerUse(); - + int iImpulse = (int)pev->impulse; switch (iImpulse) { @@ -3544,12 +3356,12 @@ void CBasePlayer::ImpulseCommands( ) { iOn = 1; gmsgLogo = REG_USER_MSG("Logo", 1); - } - else + } + else { iOn = 0; } - + ASSERT( gmsgLogo > 0 ); // send "health" update message MESSAGE_BEGIN( MSG_ONE, gmsgLogo, NULL, pev ); @@ -3566,14 +3378,14 @@ void CBasePlayer::ImpulseCommands( ) { FlashlightTurnOff(); } - else + else { FlashlightTurnOn(); } break; case 201:// paint decal - + if ( gpGlobals->time < m_flNextDecalTime ) { // too early! @@ -3591,15 +3403,13 @@ void CBasePlayer::ImpulseCommands( ) } break; - case 204: // Demo recording, update client dll specific data again. - ForceClientDllUpdate(); - break; + default: // check all of the cheat impulse commands now CheatImpulseCommands( iImpulse ); break; } - + pev->impulse = 0; } @@ -3611,7 +3421,6 @@ void CBasePlayer::CheatImpulseCommands( int iImpulse ) if ( g_flWeaponCheat == 0.0 ) { return; - } CBaseEntity *pEntity; @@ -3621,32 +3430,20 @@ void CBasePlayer::CheatImpulseCommands( int iImpulse ) { case 76: { - UTIL_MakeVectors( Vector( 0, pev->v_angle.y, 0 ) ); - Create("monster_human_grunt", pev->origin + gpGlobals->v_forward * 128, pev->angles); + if (!giPrecacheGrunt) + { + giPrecacheGrunt = 1; + ALERT(at_console, "You must now restart to use Grunt-o-matic.\n"); + } + else + { + UTIL_MakeVectors( Vector( 0, pev->v_angle.y, 0 ) ); + Create("monster_human_grunt", pev->origin + gpGlobals->v_forward * 128, pev->angles); + } break; } - case 90: //LRC - send USE_TOGGLE - { - char *impulsetarget = (char *)CVAR_GET_STRING( "sohl_impulsetarget" ); - if (impulsetarget) - FireTargets(impulsetarget, this, this, USE_TOGGLE, 0); - break; - } - case 91: //LRC - send USE_ON - { - char *impulsetarget = (char *)CVAR_GET_STRING( "sohl_impulsetarget" ); - if (impulsetarget) - FireTargets(impulsetarget, this, this, USE_ON, 0); - break; - } - case 92: //LRC - send USE_OFF - { - char *impulsetarget = (char *)CVAR_GET_STRING( "sohl_impulsetarget" ); - if (impulsetarget) - FireTargets(impulsetarget, this, this, USE_OFF, 0); - break; - } + case 101: gEvilImpulse101 = TRUE; GiveNamedItem( "item_suit" ); @@ -3673,8 +3470,7 @@ void CBasePlayer::CheatImpulseCommands( int iImpulse ) GiveNamedItem( "ammo_rpgclip" ); GiveNamedItem( "weapon_satchel" ); GiveNamedItem( "weapon_snark" ); - GiveNamedItem( "weapon_hornetgun" ); - GiveNamedItem( "item_longjump" ); + GiveNamedItem( "weapon_hornetgun" ); #endif gEvilImpulse101 = FALSE; break; @@ -3704,12 +3500,12 @@ void CBasePlayer::CheatImpulseCommands( int iImpulse ) { if ( m_fNoPlayerSound ) { - ALERT ( at_debug, "Player is audible\n" ); + ALERT ( at_console, "Player is audible\n" ); m_fNoPlayerSound = FALSE; } else { - ALERT ( at_debug, "Player is silent\n" ); + ALERT ( at_console, "Player is silent\n" ); m_fNoPlayerSound = TRUE; } break; @@ -3720,24 +3516,20 @@ void CBasePlayer::CheatImpulseCommands( int iImpulse ) pEntity = FindEntityForward( this ); if ( pEntity ) { - ALERT ( at_debug, "Classname: %s", STRING( pEntity->pev->classname ) ); - + ALERT ( at_console, "Classname: %s", STRING( pEntity->pev->classname ) ); + if ( !FStringNull ( pEntity->pev->targetname ) ) { - ALERT ( at_debug, " - Targetname: %s\n", STRING( pEntity->pev->targetname ) ); + ALERT ( at_console, " - Targetname: %s\n", STRING( pEntity->pev->targetname ) ); } else { - ALERT ( at_debug, " - TargetName: No Targetname\n" ); + ALERT ( at_console, " - TargetName: No Targetname\n" ); } - ALERT ( at_debug, "Model: %s\n", STRING( pEntity->pev->model ) ); + ALERT ( at_console, "Model: %s\n", STRING( pEntity->pev->model ) ); if ( pEntity->pev->globalname ) - ALERT ( at_debug, "Globalname: %s\n", STRING( pEntity->pev->globalname ) ); - ALERT(at_debug, "State: %s\n", GetStringForState( pEntity->GetState() )); //LRC - ALERT(at_debug, "pev->model: %s, pev->rendermode: %d\n", STRING(pEntity->pev->model), pEntity->pev->rendermode); - ALERT(at_debug, "pev->renderfx: %d, pev->rendercolor: %3f %3f %3f\n", pEntity->pev->renderfx, pEntity->pev->rendercolor.x, pEntity->pev->rendercolor.y, pEntity->pev->rendercolor.z); - ALERT(at_debug, "pev->renderamt: %3f, pev->health: %3f\n", pEntity->pev->renderamt, pEntity->pev->health); + ALERT ( at_console, "Globalname: %s\n", STRING( pEntity->pev->globalname ) ); } break; @@ -3754,7 +3546,7 @@ void CBasePlayer::CheatImpulseCommands( int iImpulse ) pWorld = tr.pHit; const char *pTextureName = TRACE_TEXTURE( pWorld, start, end ); if ( pTextureName ) - ALERT( at_debug, "Texture: %s\n", pTextureName ); + ALERT( at_console, "Texture: %s\n", pTextureName ); } break; case 195:// show shortest paths for entire level to nearest node @@ -3764,7 +3556,7 @@ void CBasePlayer::CheatImpulseCommands( int iImpulse ) break; case 196:// show shortest paths for entire level to nearest node { - Create("node_viewer_fly", pev->origin, pev->angles); + Create("node_viewer_large", pev->origin, pev->angles); } break; case 197:// show shortest paths for entire level to nearest node @@ -3774,7 +3566,7 @@ void CBasePlayer::CheatImpulseCommands( int iImpulse ) break; case 199:// show nearest node and all connections { - ALERT ( at_debug, "%d\n", WorldGraph.FindNearestNode ( pev->origin, bits_NODE_GROUP_REALM ) ); + ALERT ( at_console, "%d\n", WorldGraph.FindNearestNode ( pev->origin, bits_NODE_GROUP_REALM ) ); WorldGraph.ShowNodeConnections ( WorldGraph.FindNearestNode ( pev->origin, bits_NODE_GROUP_REALM ) ); } break; @@ -3792,12 +3584,8 @@ void CBasePlayer::CheatImpulseCommands( int iImpulse ) pEntity = FindEntityForward( this ); if ( pEntity ) { - UTIL_Remove (pEntity); - /*if ( pEntity->pev->takedamage ) - { - pEntity->SetThink(&CBaseEntity::SUB_Remove); - pEntity->SetNextThink( 0 ); - }*/ + if ( pEntity->pev->takedamage ) + pEntity->SetThink(SUB_Remove); } break; } @@ -3810,7 +3598,7 @@ void CBasePlayer::CheatImpulseCommands( int iImpulse ) int CBasePlayer::AddPlayerItem( CBasePlayerItem *pItem ) { CBasePlayerItem *pInsert; - + pInsert = m_rgpPlayerItems[pItem->iItemSlot()]; while (pInsert) @@ -3872,7 +3660,7 @@ int CBasePlayer::RemovePlayerItem( CBasePlayerItem *pItem ) { ResetAutoaim( ); pItem->Holster( ); - pItem->DontThink();// crowbar may be trying to swing again, etc. + pItem->pev->nextthink = 0;// crowbar may be trying to swing again, etc. pItem->SetThink( NULL ); m_pActiveItem = NULL; pev->viewmodel = 0; @@ -3959,18 +3747,13 @@ Called every frame by the player PreThink */ void CBasePlayer::ItemPreFrame() { - if ( gpGlobals->time < m_flNextAttack ) - return; - - if (!m_pActiveItem)// XWider +#if defined( CLIENT_WEAPONS ) + if ( m_flNextAttack > 0 ) +#else + if ( gpGlobals->time < m_flNextAttack ) +#endif { - if(m_pNextItem) - { - m_pActiveItem = m_pNextItem; - m_pActiveItem->Deploy(); - m_pActiveItem->UpdateItemInfo(); - m_pNextItem = NULL; - } + return; } if (!m_pActiveItem) @@ -3992,13 +3775,22 @@ void CBasePlayer::ItemPostFrame() static int fInSelect = FALSE; // check if the player is using a tank - if ( m_pTank != NULL ) return; + if ( m_pTank != NULL ) + return; - if ( gpGlobals->time < m_flNextAttack ) return; +#if defined( CLIENT_WEAPONS ) + if ( m_flNextAttack > 0 ) +#else + if ( gpGlobals->time < m_flNextAttack ) +#endif + { + return; + } ImpulseCommands(); - if (!m_pActiveItem) return; + if (!m_pActiveItem) + return; m_pActiveItem->ItemPostFrame( ); } @@ -4059,7 +3851,7 @@ void CBasePlayer::SendAmmoUpdate(void) UpdateClientData resends any changed player HUD info to the client. -Called every frame by Player::PreThink +Called every frame by PlayerPreThink Also called at start of demo recording and playback by ForceClientDllUpdate to ensure the demo gets messages reflecting all of the HUD state info. @@ -4088,6 +3880,7 @@ void CBasePlayer :: UpdateClientData( void ) FireTargets( "game_playerjoin", this, this, USE_TOGGLE, 0 ); } } + FireTargets( "game_playerspawn", this, this, USE_TOGGLE, 0 ); InitStatusBar(); @@ -4098,95 +3891,28 @@ void CBasePlayer :: UpdateClientData( void ) MESSAGE_BEGIN( MSG_ONE, gmsgHideWeapon, NULL, pev ); WRITE_BYTE( m_iHideHUD ); MESSAGE_END(); + m_iClientHideHUD = m_iHideHUD; } - if (viewNeedsUpdate != 0) + if ( m_iFOV != m_iClientFOV ) { - int indexToSend; - //try to find entity by targetname - CBaseEntity *pViewEnt = UTIL_FindEntityByString( NULL, "targetname", STRING(viewEntity) ); - - if (!FNullEnt( pViewEnt )) - { - indexToSend = pViewEnt->entindex(); - if(pViewEnt->pev->flags & FL_MONSTER) viewFlags |= MONSTER_VIEW; - ALERT(at_aiconsole, "Find by name : activated with index %i and flags %i\n", indexToSend, viewFlags); - } - else - { // try to find entity by classname - CBaseEntity *pViewEnt = UTIL_FindEntityByString( NULL, "classname", STRING(viewEntity) ); - - if (!FNullEnt( pViewEnt )) - { - indexToSend = pViewEnt->entindex(); - - //simple check for monster - if(pViewEnt->pev->flags & FL_MONSTER) viewFlags |= MONSTER_VIEW; - ALERT(at_aiconsole, "Find by class : activated with index %i and flags %i\n", indexToSend, viewFlags); - } - else - { - indexToSend = 0; - viewFlags = 0; // clear possibly ACTIVE flag - ALERT(at_aiconsole, "View data : deactivated\n"); - } - } - - if( !( viewFlags & CAMERA_ON )) - { - indexToSend = 0; // inactive - viewFlags = 0; // clear flags - } - - MESSAGE_BEGIN(MSG_ONE, gmsgCamData, NULL, pev); - WRITE_SHORT( indexToSend ); - WRITE_SHORT( viewFlags ); + MESSAGE_BEGIN( MSG_ONE, gmsgSetFOV, NULL, pev ); + WRITE_BYTE( m_iFOV ); MESSAGE_END(); - viewNeedsUpdate = 0; + // cache FOV change at end of function, so weapon updates can see that FOV has changed } // HACKHACK -- send the message to display the game title if (gDisplayTitle) { MESSAGE_BEGIN( MSG_ONE, gmsgShowGameTitle, NULL, pev ); + WRITE_BYTE( 0 ); MESSAGE_END(); gDisplayTitle = 0; } - if(m_FogFadeTime != 0) - { - float flDegree = (gpGlobals->time - m_flStartTime) / m_FogFadeTime; - m_iFogEndDist -= (FOG_HARDWARE_LIMIT - m_iFogFinalEndDist) * (flDegree / m_iFogFinalEndDist); - - // cap it - if (m_iFogEndDist > FOG_HARDWARE_LIMIT ) - { - m_iFogEndDist = FOG_HARDWARE_LIMIT; - m_FogFadeTime = 0; - } - if (m_iFogEndDist < m_iFogFinalEndDist) - { - m_iFogEndDist = m_iFogFinalEndDist; - m_FogFadeTime = 0; - } - fogNeedsUpdate = TRUE; - } - - if(fogNeedsUpdate != 0) - { - //update fog - MESSAGE_BEGIN( MSG_ONE, gmsgSetFog, NULL, pev ); - WRITE_BYTE ( m_FogColor.x ); - WRITE_BYTE ( m_FogColor.y ); - WRITE_BYTE ( m_FogColor.z ); - WRITE_SHORT ( m_iFogStartDist ); - WRITE_SHORT ( m_iFogEndDist ); - MESSAGE_END(); - fogNeedsUpdate = 0; - } - if (pev->health != m_iClientHealth) { int iHealth = max( pev->health, 0 ); // make sure that no negative health values are sent @@ -4202,13 +3928,13 @@ void CBasePlayer :: UpdateClientData( void ) if (pev->armorvalue != m_iClientBattery) { + m_iClientBattery = pev->armorvalue; + ASSERT( gmsgBattery > 0 ); // send "health" update message MESSAGE_BEGIN( MSG_ONE, gmsgBattery, NULL, pev ); WRITE_SHORT( (int)pev->armorvalue); MESSAGE_END(); - - m_iClientBattery = pev->armorvalue; } if (pev->dmg_take || pev->dmg_save || m_bitsHUDDamage != m_bitsDamageType) @@ -4236,173 +3962,46 @@ void CBasePlayer :: UpdateClientData( void ) WRITE_COORD( damageOrigin.y ); WRITE_COORD( damageOrigin.z ); MESSAGE_END(); - + pev->dmg_take = 0; pev->dmg_save = 0; m_bitsHUDDamage = m_bitsDamageType; - + // Clear off non-time-based damage indicators m_bitsDamageType &= DMG_TIMEBASED; } - // calculate and update rain fading - if (Rain_endFade > 0) - { - if (gpGlobals->time < Rain_endFade) - { // we're in fading process - if (Rain_nextFadeUpdate <= gpGlobals->time) - { - int secondsLeft = Rain_endFade - gpGlobals->time + 1; - - Rain_dripsPerSecond += (Rain_ideal_dripsPerSecond - Rain_dripsPerSecond) / secondsLeft; - Rain_windX += (Rain_ideal_windX - Rain_windX) / (float)secondsLeft; - Rain_windY += (Rain_ideal_windY - Rain_windY) / (float)secondsLeft; - Rain_randX += (Rain_ideal_randX - Rain_randX) / (float)secondsLeft; - Rain_randY += (Rain_ideal_randY - Rain_randY) / (float)secondsLeft; - - Rain_nextFadeUpdate = gpGlobals->time + 1; // update once per second - Rain_needsUpdate = 1; - - ALERT(at_aiconsole, "Rain fading: curdrips: %i, idealdrips %i\n", Rain_dripsPerSecond, Rain_ideal_dripsPerSecond); - } - } - else - { // finish fading process - Rain_nextFadeUpdate = 0; - Rain_endFade = 0; - - Rain_dripsPerSecond = Rain_ideal_dripsPerSecond; - Rain_windX = Rain_ideal_windX; - Rain_windY = Rain_ideal_windY; - Rain_randX = Rain_ideal_randX; - Rain_randY = Rain_ideal_randY; - Rain_needsUpdate = 1; - - ALERT(at_aiconsole, "Rain fading finished at %i drips\n", Rain_dripsPerSecond); - } - } - - // send rain message - if (Rain_needsUpdate) - { - //search for rain_settings entity - edict_t *pFind; - pFind = FIND_ENTITY_BY_CLASSNAME( NULL, "rain_settings" ); - if (!FNullEnt( pFind )) - { - // rain allowed on this map - CBaseEntity *pEnt = CBaseEntity::Instance( pFind ); - CRainSettings *pRainSettings = (CRainSettings *)pEnt; - - float raindistance = pRainSettings->Rain_Distance; - float rainheight = pRainSettings->pev->origin[2]; - int rainmode = pRainSettings->Rain_Mode; - - // search for constant rain_modifies - pFind = FIND_ENTITY_BY_CLASSNAME( NULL, "rain_modify" ); - while ( !FNullEnt( pFind ) ) - { - if (pFind->v.spawnflags & 1) - { - // copy settings to player's data and clear fading - CBaseEntity *pEnt = CBaseEntity::Instance( pFind ); - CRainModify *pRainModify = (CRainModify *)pEnt; - - Rain_dripsPerSecond = pRainModify->Rain_Drips; - Rain_windX = pRainModify->Rain_windX; - Rain_windY = pRainModify->Rain_windY; - Rain_randX = pRainModify->Rain_randX; - Rain_randY = pRainModify->Rain_randY; - - Rain_endFade = 0; - break; - } - pFind = FIND_ENTITY_BY_CLASSNAME( pFind, "rain_modify" ); - } - - MESSAGE_BEGIN(MSG_ONE, gmsgRainData, NULL, pev); - WRITE_SHORT(Rain_dripsPerSecond); - WRITE_COORD(raindistance); - WRITE_COORD(Rain_windX); - WRITE_COORD(Rain_windY); - WRITE_COORD(Rain_randX); - WRITE_COORD(Rain_randY); - WRITE_SHORT(rainmode); - WRITE_COORD(rainheight); - MESSAGE_END(); - - if (Rain_dripsPerSecond) - ALERT(at_aiconsole, "Sending enabling rain message\n"); - else - ALERT(at_aiconsole, "Sending disabling rain message\n"); - } - else - { // no rain on this map - Rain_dripsPerSecond = 0; - Rain_windX = 0; - Rain_windY = 0; - Rain_randX = 0; - Rain_randY = 0; - Rain_ideal_dripsPerSecond = 0; - Rain_ideal_windX = 0; - Rain_ideal_windY = 0; - Rain_ideal_randX = 0; - Rain_ideal_randY = 0; - Rain_endFade = 0; - Rain_nextFadeUpdate = 0; - - ALERT(at_aiconsole, "Clearing rain data\n"); - } - - Rain_needsUpdate = 0; - } - - if(FlashlightIsOn() != m_iClientFlashState) - { - MESSAGE_BEGIN( MSG_ONE, gmsgFlashlight, NULL, pev ); - WRITE_BYTE( FlashlightIsOn() ); - WRITE_BYTE( m_iFlashBattery ); - MESSAGE_END(); - - m_iClientFlashState = FlashlightIsOn(); - m_flFlashLightTime = FLASH_DRAIN_TIME + gpGlobals->time; - } - // Update Flashlight - if(m_flFlashLightTime && m_flFlashLightTime <= gpGlobals->time) + if ((m_flFlashLightTime) && (m_flFlashLightTime <= gpGlobals->time)) { if (FlashlightIsOn()) { - if (m_iFlashBattery ) + if (m_iFlashBattery) { - m_iFlashBattery--; - if (!m_iFlashBattery) FlashlightTurnOff(); m_flFlashLightTime = FLASH_DRAIN_TIME + gpGlobals->time; - } - } - else if(pev->armorvalue > 1) - { - if(m_iFlashBattery < 99 ) - { + m_iFlashBattery--; - m_iFlashBattery++; - pev->armorvalue -= (0.05 * gSkillData.flashlightCharge);//g-cont. scale factor - m_flFlashLightTime = FLASH_CHARGE_TIME + gpGlobals->time; + if (!m_iFlashBattery) + FlashlightTurnOff(); } - } - else m_flFlashLightTime = 0; - } + else + { + if (m_iFlashBattery < 100) + { + m_flFlashLightTime = FLASH_CHARGE_TIME + gpGlobals->time; + m_iFlashBattery++; + } + else + m_flFlashLightTime = 0; + } - if(m_iFlashBattery != m_iClientFlashlight) - { - m_iClientFlashlight = m_iFlashBattery; - MESSAGE_BEGIN( MSG_ONE, gmsgFlashBattery, NULL, pev ); - WRITE_BYTE(m_iFlashBattery); + WRITE_BYTE(m_iFlashBattery); MESSAGE_END(); } - + + if (m_iTrain & TRAIN_NEW) { ASSERT( gmsgTrain > 0 ); @@ -4431,9 +4030,9 @@ void CBasePlayer :: UpdateClientData( void ) // byte Ammo2 Type // byte bucket // byte bucket pos - // byte flags + // byte flags // ???? Icons - + // Send ALL the weapon info now int i; @@ -4450,7 +4049,7 @@ void CBasePlayer :: UpdateClientData( void ) else pszName = II.pszName; - MESSAGE_BEGIN( MSG_ONE, gmsgWeaponList, NULL, pev ); + MESSAGE_BEGIN( MSG_ONE, gmsgWeaponList, NULL, pev ); WRITE_STRING(pszName); // string weapon name WRITE_BYTE(GetAmmoIndex(II.pszAmmo1)); // byte Ammo Type WRITE_BYTE(II.iMaxAmmo1); // byte Max Ammo 1 @@ -4476,6 +4075,7 @@ void CBasePlayer :: UpdateClientData( void ) // Cache and client weapon change m_pClientActiveItem = m_pActiveItem; + m_iClientFOV = m_iFOV; // Update Status Bar if ( m_flNextSBarUpdateTime < gpGlobals->time ) @@ -4508,7 +4108,7 @@ void CBasePlayer :: BarnacleVictimBitten ( entvars_t *pevBarnacle ) //========================================================= // BarnacleVictimReleased - overridden for player who has -// physics flags concerns. +// physics flags concerns. //========================================================= void CBasePlayer :: BarnacleVictimReleased ( void ) { @@ -4517,7 +4117,7 @@ void CBasePlayer :: BarnacleVictimReleased ( void ) //========================================================= -// Illumination +// Illumination // return player light level plus virtual muzzle flash //========================================================= int CBasePlayer :: Illumination( void ) @@ -4534,12 +4134,10 @@ int CBasePlayer :: Illumination( void ) void CBasePlayer :: EnableControl(BOOL fControl) { if (!fControl) - { pev->flags |= FL_FROZEN; - pev->velocity = g_vecZero; //LRC - stop view bobbing - } else pev->flags &= ~FL_FROZEN; + } @@ -4630,7 +4228,7 @@ Vector CBasePlayer :: GetAutoaimVector( float flDelta ) m_vecAutoAim.y != m_lasty ) { SET_CROSSHAIRANGLE( edict(), -m_vecAutoAim.x, m_vecAutoAim.y ); - + m_lastx = m_vecAutoAim.x; m_lasty = m_vecAutoAim.y; } @@ -4673,7 +4271,7 @@ Vector CBasePlayer :: AutoaimDeflection( Vector &vecSrc, float flDist, float flD if ( tr.pHit && tr.pHit->v.takedamage != DAMAGE_NO) { // don't look through water - if (!((pev->waterlevel != 3 && tr.pHit->v.waterlevel == 3) + if (!((pev->waterlevel != 3 && tr.pHit->v.waterlevel == 3) || (pev->waterlevel == 3 && tr.pHit->v.waterlevel == 0))) { if (tr.pHit->v.takedamage == DAMAGE_AIM) @@ -4691,7 +4289,7 @@ Vector CBasePlayer :: AutoaimDeflection( Vector &vecSrc, float flDist, float flD if ( pEdict->free ) // Not in use continue; - + if (pEdict->v.takedamage != DAMAGE_AIM) continue; if (pEdict == edict()) @@ -4709,7 +4307,7 @@ Vector CBasePlayer :: AutoaimDeflection( Vector &vecSrc, float flDist, float flD continue; // don't look through water - if ((pev->waterlevel != 3 && pEntity->pev->waterlevel == 3) + if ((pev->waterlevel != 3 && pEntity->pev->waterlevel == 3) || (pev->waterlevel == 3 && pEntity->pev->waterlevel == 0)) continue; @@ -4721,7 +4319,7 @@ Vector CBasePlayer :: AutoaimDeflection( Vector &vecSrc, float flDist, float flD if (DotProduct (dir, gpGlobals->v_forward ) < 0) continue; - dot = fabs( DotProduct (dir, gpGlobals->v_right ) ) + dot = fabs( DotProduct (dir, gpGlobals->v_right ) ) + fabs( DotProduct (dir, gpGlobals->v_up ) ) * 0.5; // tweek for distance @@ -4809,7 +4407,7 @@ int CBasePlayer :: GetCustomDecalFrames( void ) //========================================================= // DropPlayerItem - drop the named item, or if no name, -// the active item. +// the active item. //========================================================= void CBasePlayer::DropPlayerItem ( char *pszItemName ) { @@ -4825,7 +4423,7 @@ void CBasePlayer::DropPlayerItem ( char *pszItemName ) // assume player wants to drop the active item. // make the string null to make future operations in this function easier pszItemName = NULL; - } + } CBasePlayerItem *pWeapon; int i; @@ -4838,10 +4436,10 @@ void CBasePlayer::DropPlayerItem ( char *pszItemName ) { if ( pszItemName ) { - // try to match by name. + // try to match by name. if ( !strcmp( pszItemName, STRING( pWeapon->pev->classname ) ) ) { - // match! + // match! break; } } @@ -4855,17 +4453,17 @@ void CBasePlayer::DropPlayerItem ( char *pszItemName ) } } - pWeapon = pWeapon->m_pNext; + pWeapon = pWeapon->m_pNext; } - - // if we land here with a valid pWeapon pointer, that's because we found the + + // if we land here with a valid pWeapon pointer, that's because we found the // item we want to drop and hit a BREAK; pWeapon is the item. if ( pWeapon ) { g_pGameRules->GetNextBestWeapon( this, pWeapon ); - UTIL_MakeVectors ( pev->angles ); + UTIL_MakeVectors ( pev->angles ); pev->weapons &= ~(1<m_iId);// take item off hud @@ -4874,12 +4472,12 @@ void CBasePlayer::DropPlayerItem ( char *pszItemName ) pWeaponBox->pev->angles.z = 0; pWeaponBox->PackWeapon( pWeapon ); pWeaponBox->pev->velocity = gpGlobals->v_forward * 300 + gpGlobals->v_forward * 100; - + // drop half of the ammo for this weapon. int iAmmoIndex; iAmmoIndex = GetAmmoIndex ( pWeapon->pszAmmo1() ); // ??? - + if ( iAmmoIndex != -1 ) { // this weapon weapon uses ammo, so pack an appropriate amount. @@ -4887,14 +4485,14 @@ void CBasePlayer::DropPlayerItem ( char *pszItemName ) { // pack up all the ammo, this weapon is its own ammo type pWeaponBox->PackAmmo( MAKE_STRING(pWeapon->pszAmmo1()), m_rgAmmo[ iAmmoIndex ] ); - m_rgAmmo[ iAmmoIndex ] = 0; + m_rgAmmo[ iAmmoIndex ] = 0; } else { // pack half of the ammo pWeaponBox->PackAmmo( MAKE_STRING(pWeapon->pszAmmo1()), m_rgAmmo[ iAmmoIndex ] / 2 ); - m_rgAmmo[ iAmmoIndex ] /= 2; + m_rgAmmo[ iAmmoIndex ] /= 2; } } @@ -4930,11 +4528,11 @@ BOOL CBasePlayer::HasNamedPlayerItem( const char *pszItemName ) { CBasePlayerItem *pItem; int i; - + for ( i = 0 ; i < MAX_ITEM_TYPES ; i++ ) { pItem = m_rgpPlayerItems[ i ]; - + while (pItem) { if ( !strcmp( pszItemName, STRING( pItem->pev->classname ) ) ) @@ -4949,36 +4547,30 @@ BOOL CBasePlayer::HasNamedPlayerItem( const char *pszItemName ) } //========================================================= -// +// //========================================================= -BOOL CBasePlayer :: SwitchWeapon( CBasePlayerItem *pWeapon ) +BOOL CBasePlayer :: SwitchWeapon( CBasePlayerItem *pWeapon ) { if ( !pWeapon->CanDeploy() ) { return FALSE; } - + ResetAutoaim( ); - + if (m_pActiveItem) { m_pActiveItem->Holster( ); } - QueueItem(pWeapon); - - if (m_pActiveItem)// XWider: QueueItem sets it if we have no current weapopn - { - m_pActiveItem->Deploy( ); - m_pActiveItem->UpdateItemInfo( ); - } + m_pActiveItem = pWeapon; + pWeapon->Deploy( ); return TRUE; } //========================================================= // Dead HEV suit prop -// LRC- i.e. the dead blokes you see in Xen. //========================================================= class CDeadHEV : public CBaseMonster { @@ -5001,7 +4593,7 @@ void CDeadHEV::KeyValue( KeyValueData *pkvd ) m_iPose = atoi(pkvd->szValue); pkvd->fHandled = TRUE; } - else + else CBaseMonster::KeyValue( pkvd ); } @@ -5025,7 +4617,7 @@ void CDeadHEV :: Spawn( void ) if (pev->sequence == -1) { - ALERT ( at_debug, "Dead hevsuit with bad pose\n" ); + ALERT ( at_console, "Dead hevsuit with bad pose\n" ); pev->sequence = 0; pev->effects = EF_BRIGHTFIELD; } @@ -5041,114 +4633,12 @@ class CStripWeapons : public CPointEntity { public: void Use( CBaseEntity *pActivator, CBaseEntity *pCaller, USE_TYPE useType, float value ); - void KeyValue( KeyValueData *pkvd ); - - virtual int Save( CSave &save ); - virtual int Restore( CRestore &restore ); - static TYPEDESCRIPTION m_SaveData[]; private: -// int m_iAmmo[MAX_WEAPONS]; - int m_i9mm; - int m_i357; - int m_iBuck; - int m_iBolt; - int m_iARGren; - int m_iRock; - int m_iUranium; - int m_iSatchel; - int m_iSnark; - int m_iTrip; - int m_iGren; - int m_iHornet; }; LINK_ENTITY_TO_CLASS( player_weaponstrip, CStripWeapons ); -TYPEDESCRIPTION CStripWeapons::m_SaveData[] = -{ - DEFINE_FIELD( CStripWeapons, m_i9mm, FIELD_INTEGER ), - DEFINE_FIELD( CStripWeapons, m_i357, FIELD_INTEGER ), - DEFINE_FIELD( CStripWeapons, m_iBuck, FIELD_INTEGER ), - DEFINE_FIELD( CStripWeapons, m_iBolt, FIELD_INTEGER ), - DEFINE_FIELD( CStripWeapons, m_iARGren, FIELD_INTEGER ), - DEFINE_FIELD( CStripWeapons, m_iRock, FIELD_INTEGER ), - DEFINE_FIELD( CStripWeapons, m_iUranium, FIELD_INTEGER ), - DEFINE_FIELD( CStripWeapons, m_iSatchel, FIELD_INTEGER ), - DEFINE_FIELD( CStripWeapons, m_iSnark, FIELD_INTEGER ), - DEFINE_FIELD( CStripWeapons, m_iTrip, FIELD_INTEGER ), - DEFINE_FIELD( CStripWeapons, m_iGren, FIELD_INTEGER ), - DEFINE_FIELD( CStripWeapons, m_iHornet, FIELD_INTEGER ), -}; - -IMPLEMENT_SAVERESTORE( CStripWeapons, CPointEntity ); - -void CStripWeapons :: KeyValue( KeyValueData *pkvd ) -{ - if (FStrEq(pkvd->szKeyName, "bullets")) - { - m_i9mm= atoi(pkvd->szValue); - pkvd->fHandled = TRUE; - } - else if (FStrEq(pkvd->szKeyName, "magnum")) - { - m_i357 = atoi(pkvd->szValue); - pkvd->fHandled = TRUE; - } - else if (FStrEq(pkvd->szKeyName, "shotgun")) - { - m_iBuck = atoi(pkvd->szValue); - pkvd->fHandled = TRUE; - } - else if (FStrEq(pkvd->szKeyName, "crossbow")) - { - m_iBolt = atoi(pkvd->szValue); - pkvd->fHandled = TRUE; - } - else if (FStrEq(pkvd->szKeyName, "argrenades")) - { - m_iARGren = atoi(pkvd->szValue); - pkvd->fHandled = TRUE; - } - else if (FStrEq(pkvd->szKeyName, "rockets")) - { - m_iRock = atoi(pkvd->szValue); - pkvd->fHandled = TRUE; - } - else if (FStrEq(pkvd->szKeyName, "uranium")) - { - m_iUranium = atoi(pkvd->szValue); - pkvd->fHandled = TRUE; - } - else if (FStrEq(pkvd->szKeyName, "satchels")) - { - m_iSatchel = atoi(pkvd->szValue); - pkvd->fHandled = TRUE; - } - else if (FStrEq(pkvd->szKeyName, "snarks")) - { - m_iSnark = atoi(pkvd->szValue); - pkvd->fHandled = TRUE; - } - else if (FStrEq(pkvd->szKeyName, "tripmines")) - { - m_iTrip = atoi(pkvd->szValue); - pkvd->fHandled = TRUE; - } - else if (FStrEq(pkvd->szKeyName, "handgrenades")) - { - m_iGren = atoi(pkvd->szValue); - pkvd->fHandled = TRUE; - } - else if (FStrEq(pkvd->szKeyName, "hornetgun")) - { - m_iHornet = atoi(pkvd->szValue); - pkvd->fHandled = TRUE; - } - else - CBaseEntity::KeyValue( pkvd ); -} - void CStripWeapons :: Use( CBaseEntity *pActivator, CBaseEntity *pCaller, USE_TYPE useType, float value ) { CBasePlayer *pPlayer = NULL; @@ -5163,11 +4653,7 @@ void CStripWeapons :: Use( CBaseEntity *pActivator, CBaseEntity *pCaller, USE_TY } if ( pPlayer ) - { - pPlayer->RemoveItems( pev->spawnflags, m_i9mm, m_i357, m_iBuck, m_iBolt, - m_iARGren, m_iRock, m_iUranium, m_iSatchel, m_iSnark, m_iTrip, m_iGren, m_iHornet); -// pPlayer->RemoveAllItems( FALSE ); - } + pPlayer->RemoveAllItems( FALSE ); } @@ -5200,7 +4686,7 @@ private: LINK_ENTITY_TO_CLASS( player_loadsaved, CRevertSaved ); -TYPEDESCRIPTION CRevertSaved::m_SaveData[] = +TYPEDESCRIPTION CRevertSaved::m_SaveData[] = { DEFINE_FIELD( CRevertSaved, m_messageTime, FIELD_FLOAT ), // These are not actual times, but durations, so save as floats DEFINE_FIELD( CRevertSaved, m_loadTime, FIELD_FLOAT ), @@ -5230,15 +4716,15 @@ void CRevertSaved :: KeyValue( KeyValueData *pkvd ) SetLoadTime( atof(pkvd->szValue) ); pkvd->fHandled = TRUE; } - else + else CPointEntity::KeyValue( pkvd ); } void CRevertSaved :: Use( CBaseEntity *pActivator, CBaseEntity *pCaller, USE_TYPE useType, float value ) { UTIL_ScreenFadeAll( pev->rendercolor, Duration(), HoldTime(), pev->renderamt, FFADE_OUT ); - SetNextThink( MessageTime() ); - SetThink(&CRevertSaved :: MessageThink ); + pev->nextthink = gpGlobals->time + MessageTime(); + SetThink( MessageThink ); } @@ -5246,10 +4732,10 @@ void CRevertSaved :: MessageThink( void ) { UTIL_ShowMessageAll( STRING(pev->message) ); float nextThink = LoadTime() - MessageTime(); - if ( nextThink > 0 ) + if ( nextThink > 0 ) { - SetNextThink( nextThink ); - SetThink(&CRevertSaved :: LoadThink ); + pev->nextthink = gpGlobals->time + nextThink; + SetThink( LoadThink ); } else LoadThink(); @@ -5264,56 +4750,6 @@ void CRevertSaved :: LoadThink( void ) } } -//========================================================= -// Trigger to disable a player -//========================================================= -#define SF_FREEZE_LOCUS 1 - -class CPlayerFreeze:public CBaseDelay -{ - void Use( CBaseEntity *pActivator, CBaseEntity *pCaller, USE_TYPE useType, float value ); - void Think( void ); - STATE GetState( void ) { return m_hActivator == NULL? STATE_OFF: STATE_ON; } -}; - -void CPlayerFreeze::Use( CBaseEntity *pActivator, CBaseEntity *pCaller, USE_TYPE useType, float value ) -{ - if (!(pev->spawnflags & SF_FREEZE_LOCUS)) - { - pActivator = UTIL_FindEntityByClassname(NULL, "player"); - } - - if (pActivator && pActivator->pev->flags & FL_CLIENT) - { - if (!ShouldToggle(useType, pActivator->pev->flags & FL_FROZEN)) - return; - - if (pActivator->pev->flags & FL_FROZEN) - { - // unfreeze him - ((CBasePlayer *)((CBaseEntity *)pActivator))->EnableControl(TRUE); - m_hActivator = NULL; - DontThink(); - } - else - { - // freeze him - ((CBasePlayer *)((CBaseEntity *)pActivator))->EnableControl(FALSE); - if (m_flDelay) - { - m_hActivator = pActivator; - SetNextThink(m_flDelay); - } - } - } -} - -void CPlayerFreeze::Think ( void ) -{ - Use(m_hActivator, this, USE_ON, 0); -} - -LINK_ENTITY_TO_CLASS( player_freeze, CPlayerFreeze ); //========================================================= // Multiplayer intermission spots. @@ -5326,91 +4762,28 @@ class CInfoIntermission:public CPointEntity void CInfoIntermission::Spawn( void ) { - UTIL_SetOrigin( this, pev->origin ); + UTIL_SetOrigin( pev, pev->origin ); pev->solid = SOLID_NOT; pev->effects = EF_NODRAW; pev->v_angle = g_vecZero; - SetNextThink( 2 );// let targets spawn! + pev->nextthink = gpGlobals->time + 2;// let targets spawn! } void CInfoIntermission::Think ( void ) { - CBaseEntity *pTarget; + edict_t *pTarget; // find my target - pTarget = UTIL_FindEntityByTargetname( NULL, STRING(pev->target) ); + pTarget = FIND_ENTITY_BY_TARGETNAME( NULL, STRING(pev->target) ); - if ( pTarget ) + if ( !FNullEnt(pTarget) ) { - pev->v_angle = UTIL_VecToAngles( (pTarget->pev->origin - pev->origin).Normalize() ); + pev->v_angle = UTIL_VecToAngles( (pTarget->v.origin - pev->origin).Normalize() ); pev->v_angle.x = -pev->v_angle.x; } } LINK_ENTITY_TO_CLASS( info_intermission, CInfoIntermission ); - - -//============================================================== -// Hud sprite displayer -//============================================================== -#define SF_HUDSPR_ACTIVE 1 - -class CHudSprite:public CBaseEntity -{ - void Spawn( void ); - void Use( CBaseEntity *pActivator, CBaseEntity *pCaller, USE_TYPE useType, float value ); - STATE GetState( void ) { return pev->spawnflags & SF_HUDSPR_ACTIVE? STATE_ON:STATE_OFF; } - void Think( void ); -}; - -void CHudSprite::Spawn( void ) -{ - if (FStringNull(pev->targetname)) - { - pev->spawnflags |= SF_HUDSPR_ACTIVE; - } - - if (pev->spawnflags & SF_HUDSPR_ACTIVE) - { - SetNextThink(2); - } -} - -void CHudSprite::Think( void ) -{ - Use(this, this, USE_ON, 0); -} - -void CHudSprite::Use( CBaseEntity *pActivator, CBaseEntity *pCaller, USE_TYPE useType, float value ) -{ - if ( !pActivator || !pActivator->IsPlayer() ) - { - pActivator = CBaseEntity::Instance(g_engfuncs.pfnPEntityOfEntIndex( 1 )); - } - - if (ShouldToggle(useType)) - { - if (pev->spawnflags & SF_HUDSPR_ACTIVE) - pev->spawnflags &= ~SF_HUDSPR_ACTIVE; - else - pev->spawnflags |= SF_HUDSPR_ACTIVE; - } - -// byte : TRUE = ENABLE icon, FALSE = DISABLE icon -// string : the sprite name to display -// byte : red -// byte : green -// byte : blue - MESSAGE_BEGIN( MSG_ONE, gmsgStatusIcon, NULL, pActivator->pev ); - WRITE_BYTE(pev->spawnflags & SF_HUDSPR_ACTIVE); - WRITE_STRING(STRING(pev->model)); - WRITE_BYTE(pev->rendercolor.x); - WRITE_BYTE(pev->rendercolor.y); - WRITE_BYTE(pev->rendercolor.z); - MESSAGE_END(); -} - -LINK_ENTITY_TO_CLASS( hud_sprite, CHudSprite ); diff --git a/spirit/player.h b/dlls/player.h similarity index 83% rename from spirit/player.h rename to dlls/player.h index c607ee5b..cbf95809 100644 --- a/spirit/player.h +++ b/dlls/player.h @@ -18,6 +18,7 @@ #include "pm_materials.h" + #define PLAYER_FATAL_FALL_SPEED 1024// approx 60 feet #define PLAYER_MAX_SAFE_FALL_SPEED 580// approx 20 feet #define DAMAGE_FOR_FALL_SPEED (float) 100 / ( PLAYER_FATAL_FALL_SPEED - PLAYER_MAX_SAFE_FALL_SPEED )// damage per unit per second. @@ -71,9 +72,6 @@ typedef enum PLAYER_ATTACK1, } PLAYER_ANIM; - -//NB: changing this structure will cause problems! --LRC - #define MAX_ID_RANGE 2048 #define SBAR_STRING_SIZE 128 @@ -154,26 +152,23 @@ public: EHANDLE m_pTank; // the tank which the player is currently controlling, NULL if no tank float m_fDeadTime; // the time at which the player died (used in PlayerDeathThink()) - float m_flViewHeight; // keep value from view_ofs.z that engine sets it when player first entering in multiplayer - + BOOL m_fNoPlayerSound; // a debugging feature. Player makes no sound if this is true. BOOL m_fLongJump; // does this player have the longjump module? float m_tSneaking; - int m_iUpdateTime; // stores the number of frame ticks before sending HUD update messages - int m_iClientHealth; // the health currently known by the client. If this changes, send a new - int m_iClientBattery; // the Battery currently known by the client. If this changes, send a new - int m_iClientFlashlight; // the FlashLight Battery currently known by the client. - int m_iClientFlashState; // the falshlight status - int m_iHideHUD; // the players hud weapon info is to be hidden - int m_iClientHideHUD; - + int m_iUpdateTime; // stores the number of frame ticks before sending HUD update messages + int m_iClientHealth; // the health currently known by the client. If this changes, send a new + int m_iClientBattery; // the Battery currently known by the client. If this changes, send a new + int m_iHideHUD; // the players hud weapon info is to be hidden + int m_iClientHideHUD; + int m_iFOV; // field of view + int m_iClientFOV; // client's known FOV // usable player items CBasePlayerItem *m_rgpPlayerItems[MAX_ITEM_TYPES]; CBasePlayerItem *m_pActiveItem; CBasePlayerItem *m_pClientActiveItem; // client version of the active item CBasePlayerItem *m_pLastItem; - CBasePlayerItem *m_pNextItem; // shared ammo slots int m_rgAmmo[MAX_AMMO_SLOTS]; int m_rgAmmoLast[MAX_AMMO_SLOTS]; @@ -200,7 +195,6 @@ public: virtual void PostThink( void ); virtual Vector GetGunPosition( void ); virtual int TakeHealth( float flHealth, int bitsDamageType ); - virtual int TakeArmor( float flArmor ); 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 void Killed( entvars_t *pevAttacker, int iGib ); @@ -221,8 +215,6 @@ public: void RenewItems(void); void PackDeadPlayerItems( void ); void RemoveAllItems( BOOL removeSuit ); - void RemoveItems( int iWeaponMask, int i9mm, int i357, int iBuck, int iBolt, int iARGren, int iRock, int iEgon, int iSatchel, int iSnark, int iTrip, int iGren, int iHornet ); - void RemoveAmmo( const char* szName, int iAmount ); BOOL SwitchWeapon( CBasePlayerItem *pWeapon ); // JOHN: sends custom messages if player HUD data has changed (eg health, ammo) @@ -265,7 +257,6 @@ public: void SelectNextItem( int iItem ); void SelectLastItem(void); void SelectItem(const char *pstr); - void QueueItem(CBasePlayerItem *pItem); void ItemPreFrame( void ); void ItemPostFrame( void ); void GiveNamedItem( const char *szName ); @@ -302,6 +293,11 @@ public: int GetCustomDecalFrames( void ); void CBasePlayer::TabulateAmmo( void ); + + float m_flStartCharge; + float m_flAmmoStartCharge; + float m_flPlayAftershock; + float m_flNextAmmoBurn;// while charging, when to absorb another unit of player's ammo? //Player ID void InitStatusBar( void ); @@ -311,34 +307,9 @@ public: float m_flStatusBarDisappearDelay; char m_SbarString0[ SBAR_STRING_SIZE ]; char m_SbarString1[ SBAR_STRING_SIZE ]; - - // for trigger_viewset - int viewEntity; // string - int viewFlags; // 1-active, 2-draw hud - int viewNeedsUpdate; // precache sets to 1, UpdateClientData() sets to 0 - float m_flNextChatTime; - - // fog variables - int m_iFogStartDist; - int m_iFogEndDist; - int m_iFogFinalEndDist; - int m_FogFadeTime; - Vector m_FogColor; - int fogNeedsUpdate; - float m_flStartTime; - - int Rain_dripsPerSecond; - float Rain_windX, Rain_windY; - float Rain_randX, Rain_randY; - - int Rain_ideal_dripsPerSecond; - float Rain_ideal_windX, Rain_ideal_windY; - float Rain_ideal_randX, Rain_ideal_randY; - - float Rain_endFade; // 0 means off - float Rain_nextFadeUpdate; - - int Rain_needsUpdate; + + float m_flNextChatTime; + }; #define AUTOAIM_2DEGREES 0.0348994967025 @@ -348,15 +319,6 @@ public: extern int gmsgHudText; -extern int gmsgParticle; // LRC -extern int gmsgSetBody; -extern int gmsgSetSkin; -extern int gmsgShake; -extern int gmsgFade; -extern int gmsgWeaponAnim; -extern int gmsgIntermission; -extern int gmsgRoomType; -extern int gmsgShowGameTitle; extern BOOL gInitHUD; #endif // PLAYER_H diff --git a/spirit/playermonster.cpp b/dlls/playermonster.cpp similarity index 96% rename from spirit/playermonster.cpp rename to dlls/playermonster.cpp index 6497ea6e..bb0a86ac 100644 --- a/spirit/playermonster.cpp +++ b/dlls/playermonster.cpp @@ -1,122 +1,122 @@ -//========= Copyright © 1996-2002, Valve LLC, All rights reserved. ============ -// -// Purpose: New version of the slider bar -// -// $NoKeywords: $ -//============================================================================= - -//========================================================= -// playermonster - for scripted sequence use. -//========================================================= -#include "extdll.h" -#include "util.h" -#include "cbase.h" -#include "monsters.h" -#include "schedule.h" - -// For holograms, make them not solid so the player can walk through them -#define SF_MONSTERPLAYER_NOTSOLID 4 - -//========================================================= -// Monster's Anim Events Go Here -//========================================================= - -class CPlayerMonster : public CBaseMonster -{ -public: - void Spawn( void ); - void Precache( void ); - void SetYawSpeed( void ); - int Classify ( void ); - void HandleAnimEvent( MonsterEvent_t *pEvent ); - int ISoundMask ( void ); -}; -LINK_ENTITY_TO_CLASS( monster_player, CPlayerMonster ); - -//========================================================= -// Classify - indicates this monster's place in the -// relationship table. -//========================================================= -int CPlayerMonster :: Classify ( void ) -{ - return CLASS_PLAYER_ALLY; -} - -//========================================================= -// SetYawSpeed - allows each sequence to have a different -// turn rate associated with it. -//========================================================= -void CPlayerMonster :: SetYawSpeed ( void ) -{ - int ys; - - switch ( m_Activity ) - { - case ACT_IDLE: - default: - ys = 90; - } - - pev->yaw_speed = ys; -} - -//========================================================= -// HandleAnimEvent - catches the monster-specific messages -// that occur when tagged animation frames are played. -//========================================================= -void CPlayerMonster :: HandleAnimEvent( MonsterEvent_t *pEvent ) -{ - switch( pEvent->event ) - { - case 0: - default: - CBaseMonster::HandleAnimEvent( pEvent ); - break; - } -} - -//========================================================= -// ISoundMask - player monster can't hear. -//========================================================= -int CPlayerMonster :: ISoundMask ( void ) -{ - return NULL; -} - -//========================================================= -// Spawn -//========================================================= -void CPlayerMonster :: Spawn() -{ - Precache( ); - - SET_MODEL(ENT(pev), "models/player.mdl"); - UTIL_SetSize(pev, VEC_HULL_MIN, VEC_HULL_MAX); - - pev->solid = SOLID_SLIDEBOX; - pev->movetype = MOVETYPE_STEP; - m_bloodColor = BLOOD_COLOR_RED; - pev->health = 8; - m_flFieldOfView = 0.5;// indicates the width of this monster's forward view cone ( as a dotproduct result ) - m_MonsterState = MONSTERSTATE_NONE; - - - MonsterInit(); - if ( pev->spawnflags & SF_MONSTERPLAYER_NOTSOLID ) - { - pev->solid = SOLID_NOT; - pev->takedamage = DAMAGE_NO; - } -} - -//========================================================= -// Precache - precaches all resources this monster needs -//========================================================= -void CPlayerMonster :: Precache() -{ - PRECACHE_MODEL("models/player.mdl"); -} - -//========================================================= -// AI Schedules Specific to this monster -//========================================================= +//========= Copyright © 1996-2002, Valve LLC, All rights reserved. ============ +// +// Purpose: New version of the slider bar +// +// $NoKeywords: $ +//============================================================================= + +//========================================================= +// playermonster - for scripted sequence use. +//========================================================= +#include "extdll.h" +#include "util.h" +#include "cbase.h" +#include "monsters.h" +#include "schedule.h" + +// For holograms, make them not solid so the player can walk through them +#define SF_MONSTERPLAYER_NOTSOLID 4 + +//========================================================= +// Monster's Anim Events Go Here +//========================================================= + +class CPlayerMonster : public CBaseMonster +{ +public: + void Spawn( void ); + void Precache( void ); + void SetYawSpeed( void ); + int Classify ( void ); + void HandleAnimEvent( MonsterEvent_t *pEvent ); + int ISoundMask ( void ); +}; +LINK_ENTITY_TO_CLASS( monster_player, CPlayerMonster ); + +//========================================================= +// Classify - indicates this monster's place in the +// relationship table. +//========================================================= +int CPlayerMonster :: Classify ( void ) +{ + return CLASS_PLAYER_ALLY; +} + +//========================================================= +// SetYawSpeed - allows each sequence to have a different +// turn rate associated with it. +//========================================================= +void CPlayerMonster :: SetYawSpeed ( void ) +{ + int ys; + + switch ( m_Activity ) + { + case ACT_IDLE: + default: + ys = 90; + } + + pev->yaw_speed = ys; +} + +//========================================================= +// HandleAnimEvent - catches the monster-specific messages +// that occur when tagged animation frames are played. +//========================================================= +void CPlayerMonster :: HandleAnimEvent( MonsterEvent_t *pEvent ) +{ + switch( pEvent->event ) + { + case 0: + default: + CBaseMonster::HandleAnimEvent( pEvent ); + break; + } +} + +//========================================================= +// ISoundMask - player monster can't hear. +//========================================================= +int CPlayerMonster :: ISoundMask ( void ) +{ + return NULL; +} + +//========================================================= +// Spawn +//========================================================= +void CPlayerMonster :: Spawn() +{ + Precache( ); + + SET_MODEL(ENT(pev), "models/player.mdl"); + UTIL_SetSize(pev, VEC_HULL_MIN, VEC_HULL_MAX); + + pev->solid = SOLID_SLIDEBOX; + pev->movetype = MOVETYPE_STEP; + m_bloodColor = BLOOD_COLOR_RED; + pev->health = 8; + m_flFieldOfView = 0.5;// indicates the width of this monster's forward view cone ( as a dotproduct result ) + m_MonsterState = MONSTERSTATE_NONE; + + + MonsterInit(); + if ( pev->spawnflags & SF_MONSTERPLAYER_NOTSOLID ) + { + pev->solid = SOLID_NOT; + pev->takedamage = DAMAGE_NO; + } +} + +//========================================================= +// Precache - precaches all resources this monster needs +//========================================================= +void CPlayerMonster :: Precache() +{ + PRECACHE_MODEL("models/player.mdl"); +} + +//========================================================= +// AI Schedules Specific to this monster +//========================================================= diff --git a/dlls/python.cpp b/dlls/python.cpp new file mode 100644 index 00000000..89d6231c --- /dev/null +++ b/dlls/python.cpp @@ -0,0 +1,320 @@ +/*** +* +* Copyright (c) 1996-2002, 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. +* +****/ +#if !defined( OEM_BUILD ) && !defined( HLDEMO_BUILD ) + +#include "extdll.h" +#include "util.h" +#include "cbase.h" +#include "weapons.h" +#include "monsters.h" +#include "player.h" +#include "gamerules.h" + + +enum python_e { + PYTHON_IDLE1 = 0, + PYTHON_FIDGET, + PYTHON_FIRE1, + PYTHON_RELOAD, + PYTHON_HOLSTER, + PYTHON_DRAW, + PYTHON_IDLE2, + PYTHON_IDLE3 +}; + +LINK_ENTITY_TO_CLASS( weapon_python, CPython ); +LINK_ENTITY_TO_CLASS( weapon_357, CPython ); + +int CPython::GetItemInfo(ItemInfo *p) +{ + p->pszName = STRING(pev->classname); + p->pszAmmo1 = "357"; + p->iMaxAmmo1 = _357_MAX_CARRY; + p->pszAmmo2 = NULL; + p->iMaxAmmo2 = -1; + p->iMaxClip = PYTHON_MAX_CLIP; + p->iFlags = 0; + p->iSlot = 1; + p->iPosition = 1; + p->iId = m_iId = WEAPON_PYTHON; + p->iWeight = PYTHON_WEIGHT; + + return 1; +} + +int CPython::AddToPlayer( CBasePlayer *pPlayer ) +{ + if ( CBasePlayerWeapon::AddToPlayer( pPlayer ) ) + { + MESSAGE_BEGIN( MSG_ONE, gmsgWeapPickup, NULL, pPlayer->pev ); + WRITE_BYTE( m_iId ); + MESSAGE_END(); + return TRUE; + } + return FALSE; +} + +void CPython::Spawn( ) +{ + pev->classname = MAKE_STRING("weapon_357"); // hack to allow for old names + Precache( ); + m_iId = WEAPON_PYTHON; + SET_MODEL(ENT(pev), "models/w_357.mdl"); + + m_iDefaultAmmo = PYTHON_DEFAULT_GIVE; + + FallInit();// get ready to fall down. +} + + +void CPython::Precache( void ) +{ + PRECACHE_MODEL("models/v_357.mdl"); + PRECACHE_MODEL("models/w_357.mdl"); + PRECACHE_MODEL("models/p_357.mdl"); + + PRECACHE_MODEL("models/w_357ammobox.mdl"); + PRECACHE_SOUND("items/9mmclip1.wav"); + + PRECACHE_SOUND ("weapons/357_reload1.wav"); + PRECACHE_SOUND ("weapons/357_cock1.wav"); + PRECACHE_SOUND ("weapons/357_shot1.wav"); + PRECACHE_SOUND ("weapons/357_shot2.wav"); + + m_usFirePython = PRECACHE_EVENT( 1, "events/python.sc" ); +} + +BOOL CPython::Deploy( ) +{ +#ifdef CLIENT_DLL + if ( bIsMultiplayer() ) +#else + if ( g_pGameRules->IsMultiplayer() ) +#endif + { + // enable laser sight geometry. + pev->body = 1; + } + else + { + pev->body = 0; + } + + return DefaultDeploy( "models/v_357.mdl", "models/p_357.mdl", PYTHON_DRAW, "python", UseDecrement(), pev->body ); +} + + +void CPython::Holster( int skiplocal /* = 0 */ ) +{ + m_fInReload = FALSE;// cancel any reload in progress. + + if ( m_fInZoom ) + { + SecondaryAttack(); + } + + m_pPlayer->m_flNextAttack = UTIL_WeaponTimeBase() + 1.0; + m_flTimeWeaponIdle = UTIL_SharedRandomFloat( m_pPlayer->random_seed, 10, 15 ); + SendWeaponAnim( PYTHON_HOLSTER ); +} + +void CPython::SecondaryAttack( void ) +{ +#ifdef CLIENT_DLL + if ( !bIsMultiplayer() ) +#else + if ( !g_pGameRules->IsMultiplayer() ) +#endif + { + return; + } + + if ( m_pPlayer->pev->fov != 0 ) + { + m_fInZoom = FALSE; + m_pPlayer->pev->fov = m_pPlayer->m_iFOV = 0; // 0 means reset to default fov + } + else if ( m_pPlayer->pev->fov != 40 ) + { + m_fInZoom = TRUE; + m_pPlayer->pev->fov = m_pPlayer->m_iFOV = 40; + } + + m_flNextSecondaryAttack = 0.5; +} + +void CPython::PrimaryAttack() +{ + // don't fire underwater + if (m_pPlayer->pev->waterlevel == 3) + { + PlayEmptySound( ); + m_flNextPrimaryAttack = 0.15; + return; + } + + if (m_iClip <= 0) + { + if (!m_fFireOnEmpty) + Reload( ); + else + { + EMIT_SOUND(ENT(m_pPlayer->pev), CHAN_WEAPON, "weapons/357_cock1.wav", 0.8, ATTN_NORM); + m_flNextPrimaryAttack = 0.15; + } + + return; + } + + m_pPlayer->m_iWeaponVolume = LOUD_GUN_VOLUME; + m_pPlayer->m_iWeaponFlash = BRIGHT_GUN_FLASH; + + m_iClip--; + + m_pPlayer->pev->effects = (int)(m_pPlayer->pev->effects) | EF_MUZZLEFLASH; + + // player "shoot" animation + m_pPlayer->SetAnimation( PLAYER_ATTACK1 ); + + + UTIL_MakeVectors( m_pPlayer->pev->v_angle + m_pPlayer->pev->punchangle ); + + Vector vecSrc = m_pPlayer->GetGunPosition( ); + Vector vecAiming = m_pPlayer->GetAutoaimVector( AUTOAIM_10DEGREES ); + + Vector vecDir; + vecDir = m_pPlayer->FireBulletsPlayer( 1, vecSrc, vecAiming, VECTOR_CONE_1DEGREES, 8192, BULLET_PLAYER_357, 0, 0, m_pPlayer->pev, m_pPlayer->random_seed ); + + int flags; +#if defined( CLIENT_WEAPONS ) + flags = FEV_NOTHOST; +#else + flags = 0; +#endif + + PLAYBACK_EVENT_FULL( flags, m_pPlayer->edict(), m_usFirePython, 0.0, (float *)&g_vecZero, (float *)&g_vecZero, vecDir.x, vecDir.y, 0, 0, 0, 0 ); + + if (!m_iClip && m_pPlayer->m_rgAmmo[m_iPrimaryAmmoType] <= 0) + // HEV suit - indicate out of ammo condition + m_pPlayer->SetSuitUpdate("!HEV_AMO0", FALSE, 0); + + m_flNextPrimaryAttack = 0.75; + m_flTimeWeaponIdle = UTIL_SharedRandomFloat( m_pPlayer->random_seed, 10, 15 ); +} + + +void CPython::Reload( void ) +{ + if ( m_pPlayer->ammo_357 <= 0 ) + return; + + if ( m_pPlayer->pev->fov != 0 ) + { + m_fInZoom = FALSE; + m_pPlayer->pev->fov = m_pPlayer->m_iFOV = 0; // 0 means reset to default fov + } + + int bUseScope = FALSE; +#ifdef CLIENT_DLL + bUseScope = bIsMultiplayer(); +#else + bUseScope = g_pGameRules->IsMultiplayer(); +#endif + + if (DefaultReload( 6, PYTHON_RELOAD, 2.0, bUseScope )) + { + m_flSoundDelay = 1.5; + } +} + + +void CPython::WeaponIdle( void ) +{ + ResetEmptySound( ); + + m_pPlayer->GetAutoaimVector( AUTOAIM_10DEGREES ); + + // ALERT( at_console, "%.2f\n", gpGlobals->time - m_flSoundDelay ); + if (m_flSoundDelay != 0 && m_flSoundDelay <= UTIL_WeaponTimeBase() ) + { + EMIT_SOUND(ENT(m_pPlayer->pev), CHAN_WEAPON, "weapons/357_reload1.wav", RANDOM_FLOAT(0.8, 0.9), ATTN_NORM); + m_flSoundDelay = 0; + } + + if (m_flTimeWeaponIdle > UTIL_WeaponTimeBase() ) + return; + + int iAnim; + float flRand = UTIL_SharedRandomFloat( m_pPlayer->random_seed, 10, 15 ); + if (flRand <= 0.5) + { + iAnim = PYTHON_IDLE1; + m_flTimeWeaponIdle = (70.0/30.0); + } + else if (flRand <= 0.7) + { + iAnim = PYTHON_IDLE2; + m_flTimeWeaponIdle = (60.0/30.0); + } + else if (flRand <= 0.9) + { + iAnim = PYTHON_IDLE3; + m_flTimeWeaponIdle = (88.0/30.0); + } + else + { + iAnim = PYTHON_FIDGET; + m_flTimeWeaponIdle = (170.0/30.0); + } + + int bUseScope = FALSE; +#ifdef CLIENT_DLL + bUseScope = bIsMultiplayer(); +#else + bUseScope = g_pGameRules->IsMultiplayer(); +#endif + + SendWeaponAnim( iAnim, UseDecrement() ? 1 : 0, bUseScope ); +} + + + +class CPythonAmmo : public CBasePlayerAmmo +{ + void Spawn( void ) + { + Precache( ); + SET_MODEL(ENT(pev), "models/w_357ammobox.mdl"); + CBasePlayerAmmo::Spawn( ); + } + void Precache( void ) + { + PRECACHE_MODEL ("models/w_357ammobox.mdl"); + PRECACHE_SOUND("items/9mmclip1.wav"); + } + BOOL AddAmmo( CBaseEntity *pOther ) + { + if (pOther->GiveAmmo( AMMO_357BOX_GIVE, "357", _357_MAX_CARRY ) != -1) + { + EMIT_SOUND(ENT(pev), CHAN_ITEM, "items/9mmclip1.wav", 1, ATTN_NORM); + return TRUE; + } + return FALSE; + } +}; +LINK_ENTITY_TO_CLASS( ammo_357, CPythonAmmo ); + + +#endif \ No newline at end of file diff --git a/spirit/rat.cpp b/dlls/rat.cpp similarity index 88% rename from spirit/rat.cpp rename to dlls/rat.cpp index d09e0bf2..0d4c8fb6 100644 --- a/spirit/rat.cpp +++ b/dlls/rat.cpp @@ -1,104 +1,98 @@ -/*** -* -* Copyright (c) 1996-2002, 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. -* -* This source code contains proprietary and confidential information of -* Valve LLC and its suppliers. Access to this code is restricted to -* persons who have executed a written SDK license with Valve. Any access, -* use or distribution of this code by or to any unlicensed person is illegal. -* -****/ -//========================================================= -// rat - environmental monster -//========================================================= - -#include "extdll.h" -#include "util.h" -#include "cbase.h" -#include "monsters.h" -#include "schedule.h" - -//========================================================= -// Monster's Anim Events Go Here -//========================================================= - -class CRat : public CBaseMonster -{ -public: - void Spawn( void ); - void Precache( void ); - void SetYawSpeed( void ); - int Classify ( void ); -}; -LINK_ENTITY_TO_CLASS( monster_rat, CRat ); - -//========================================================= -// Classify - indicates this monster's place in the -// relationship table. -//========================================================= -int CRat :: Classify ( void ) -{ - return m_iClass?m_iClass:CLASS_INSECT; //LRC- maybe someone needs to give them a basic biology lesson... -} - -//========================================================= -// SetYawSpeed - allows each sequence to have a different -// turn rate associated with it. -//========================================================= -void CRat :: SetYawSpeed ( void ) -{ - int ys; - - switch ( m_Activity ) - { - case ACT_IDLE: - default: - ys = 45; - break; - } - - pev->yaw_speed = ys; -} - -//========================================================= -// Spawn -//========================================================= -void CRat :: Spawn() -{ - Precache( ); - - if (pev->model) - SET_MODEL(ENT(pev), STRING(pev->model)); //LRC - else - SET_MODEL(ENT(pev), "models/bigrat.mdl"); - UTIL_SetSize( pev, Vector( 0, 0, 0 ), Vector( 0, 0, 0 ) ); - - pev->solid = SOLID_SLIDEBOX; - pev->movetype = MOVETYPE_STEP; - m_bloodColor = BLOOD_COLOR_RED; - pev->health = 8; - pev->view_ofs = Vector ( 0, 0, 6 );// position of the eyes relative to monster's origin. - m_flFieldOfView = 0.5;// indicates the width of this monster's forward view cone ( as a dotproduct result ) - m_MonsterState = MONSTERSTATE_NONE; - - MonsterInit(); -} - -//========================================================= -// Precache - precaches all resources this monster needs -//========================================================= -void CRat :: Precache() -{ - if (pev->model) - PRECACHE_MODEL((char*)STRING(pev->model)); //LRC - else - PRECACHE_MODEL("models/bigrat.mdl"); -} - -//========================================================= -// AI Schedules Specific to this monster -//========================================================= +/*** +* +* Copyright (c) 1996-2002, 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. +* +* This source code contains proprietary and confidential information of +* Valve LLC and its suppliers. Access to this code is restricted to +* persons who have executed a written SDK license with Valve. Any access, +* use or distribution of this code by or to any unlicensed person is illegal. +* +****/ +//========================================================= +// rat - environmental monster +//========================================================= + +#include "extdll.h" +#include "util.h" +#include "cbase.h" +#include "monsters.h" +#include "schedule.h" + +//========================================================= +// Monster's Anim Events Go Here +//========================================================= + +class CRat : public CBaseMonster +{ +public: + void Spawn( void ); + void Precache( void ); + void SetYawSpeed( void ); + int Classify ( void ); +}; +LINK_ENTITY_TO_CLASS( monster_rat, CRat ); + +//========================================================= +// Classify - indicates this monster's place in the +// relationship table. +//========================================================= +int CRat :: Classify ( void ) +{ + return CLASS_INSECT; +} + +//========================================================= +// SetYawSpeed - allows each sequence to have a different +// turn rate associated with it. +//========================================================= +void CRat :: SetYawSpeed ( void ) +{ + int ys; + + switch ( m_Activity ) + { + case ACT_IDLE: + default: + ys = 45; + break; + } + + pev->yaw_speed = ys; +} + +//========================================================= +// Spawn +//========================================================= +void CRat :: Spawn() +{ + Precache( ); + + SET_MODEL(ENT(pev), "models/bigrat.mdl"); + UTIL_SetSize( pev, Vector( 0, 0, 0 ), Vector( 0, 0, 0 ) ); + + pev->solid = SOLID_SLIDEBOX; + pev->movetype = MOVETYPE_STEP; + m_bloodColor = BLOOD_COLOR_RED; + pev->health = 8; + pev->view_ofs = Vector ( 0, 0, 6 );// position of the eyes relative to monster's origin. + m_flFieldOfView = 0.5;// indicates the width of this monster's forward view cone ( as a dotproduct result ) + m_MonsterState = MONSTERSTATE_NONE; + + MonsterInit(); +} + +//========================================================= +// Precache - precaches all resources this monster needs +//========================================================= +void CRat :: Precache() +{ + PRECACHE_MODEL("models/bigrat.mdl"); +} + +//========================================================= +// AI Schedules Specific to this monster +//========================================================= diff --git a/spirit/roach.cpp b/dlls/roach.cpp similarity index 90% rename from spirit/roach.cpp rename to dlls/roach.cpp index bdc958ea..3c13bf1c 100644 --- a/spirit/roach.cpp +++ b/dlls/roach.cpp @@ -23,7 +23,6 @@ #include "schedule.h" #include "soundent.h" #include "decals.h" -#include "weapons.h" #define ROACH_IDLE 0 #define ROACH_BORED 1 @@ -76,7 +75,7 @@ int CRoach :: ISoundMask ( void ) //========================================================= int CRoach :: Classify ( void ) { - return m_iClass?m_iClass:CLASS_INSECT; + return CLASS_INSECT; } //========================================================= @@ -96,9 +95,7 @@ void CRoach :: Touch ( CBaseEntity *pOther ) UTIL_TraceLine ( vecSpot, vecSpot + Vector ( 0, 0, -24 ), ignore_monsters, ENT(pev), & tr); // This isn't really blood. So you don't have to screen it out based on violence levels (UTIL_ShouldShowBlood()) - CBaseEntity *pHit = CBaseEntity::Instance( tr.pHit ); - PLAYBACK_EVENT_FULL( FEV_RELIABLE|FEV_GLOBAL, edict(), m_usDecals, 0.0, (float *)&tr.vecEndPos, (float *)&g_vecZero, 0.0, 0.0, pHit->entindex(), 2, 0, 0 ); - //UTIL_DecalTrace( &tr, DECAL_YBLOOD1 +RANDOM_LONG(0,5) ); + UTIL_DecalTrace( &tr, DECAL_YBLOOD1 +RANDOM_LONG(0,5) ); TakeDamage( pOther->pev, pOther->pev, pev->health, DMG_CRUSH ); } @@ -123,10 +120,7 @@ void CRoach :: Spawn() { Precache( ); - if (pev->model) - SET_MODEL(ENT(pev), STRING(pev->model)); //LRC - else - SET_MODEL(ENT(pev), "models/roach.mdl"); + SET_MODEL(ENT(pev), "models/roach.mdl"); UTIL_SetSize( pev, Vector( -1, -1, 0 ), Vector( 1, 1, 2 ) ); pev->solid = SOLID_SLIDEBOX; @@ -153,10 +147,7 @@ void CRoach :: Spawn() //========================================================= void CRoach :: Precache() { - if (pev->model) - PRECACHE_MODEL((char*)STRING(pev->model)); //LRC - else - PRECACHE_MODEL("models/roach.mdl"); + PRECACHE_MODEL("models/roach.mdl"); PRECACHE_SOUND("roach/rch_die.wav"); PRECACHE_SOUND("roach/rch_walk.wav"); @@ -196,10 +187,10 @@ void CRoach :: Killed( entvars_t *pevAttacker, int iGib ) //========================================================= void CRoach :: MonsterThink( void ) { - if ( FNullEnt( FIND_CLIENT_IN_PVS( edict() ) ) && !HaveCamerasInPVS( edict() )) - SetNextThink( RANDOM_FLOAT(1,1.5) ); + if ( FNullEnt( FIND_CLIENT_IN_PVS( edict() ) ) ) + pev->nextthink = gpGlobals->time + RANDOM_FLOAT(1,1.5); else - SetNextThink( 0.1 );// keep monster thinking + pev->nextthink = gpGlobals->time + 0.1;// keep monster thinking float flInterval = StudioFrameAdvance( ); // animate @@ -207,7 +198,7 @@ void CRoach :: MonsterThink( void ) { // if light value hasn't been collection for the first time yet, // suspend the creature for a second so the world finishes spawning, then we'll collect the light level. - SetNextThink( 1 ); + pev->nextthink = gpGlobals->time + 1; m_fLightHacked = TRUE; return; } @@ -420,7 +411,7 @@ void CRoach :: Look ( int iDistance ) // don't let monsters outside of the player's PVS act up, or most of the interesting // things will happen before the player gets there! - if ( FNullEnt( FIND_CLIENT_IN_PVS( edict() ) ) && !HaveCamerasInPVS( edict() )) + if ( FNullEnt( FIND_CLIENT_IN_PVS( edict() ) ) ) { return; } @@ -454,7 +445,7 @@ void CRoach :: Look ( int iDistance ) case R_NO: break; default: - ALERT ( at_debug, "%s can't assess %s\n", STRING(pev->classname), STRING(pSightEnt->pev->classname ) ); + ALERT ( at_console, "%s can't asses %s\n", STRING(pev->classname), STRING(pSightEnt->pev->classname ) ); break; } } diff --git a/dlls/rpg.cpp b/dlls/rpg.cpp new file mode 100644 index 00000000..0f297b00 --- /dev/null +++ b/dlls/rpg.cpp @@ -0,0 +1,617 @@ +/*** +* +* Copyright (c) 1996-2002, 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. +* +****/ +#if !defined( OEM_BUILD ) + +#include "extdll.h" +#include "util.h" +#include "cbase.h" +#include "monsters.h" +#include "weapons.h" +#include "nodes.h" +#include "player.h" +#include "gamerules.h" + + + + +enum rpg_e { + RPG_IDLE = 0, + RPG_FIDGET, + RPG_RELOAD, // to reload + RPG_FIRE2, // to empty + RPG_HOLSTER1, // loaded + RPG_DRAW1, // loaded + RPG_HOLSTER2, // unloaded + RPG_DRAW_UL, // unloaded + RPG_IDLE_UL, // unloaded idle + RPG_FIDGET_UL, // unloaded fidget +}; + +LINK_ENTITY_TO_CLASS( weapon_rpg, CRpg ); + +#ifndef CLIENT_DLL + +LINK_ENTITY_TO_CLASS( laser_spot, CLaserSpot ); + +//========================================================= +//========================================================= +CLaserSpot *CLaserSpot::CreateSpot( void ) +{ + CLaserSpot *pSpot = GetClassPtr( (CLaserSpot *)NULL ); + pSpot->Spawn(); + + pSpot->pev->classname = MAKE_STRING("laser_spot"); + + return pSpot; +} + +//========================================================= +//========================================================= +void CLaserSpot::Spawn( void ) +{ + Precache( ); + pev->movetype = MOVETYPE_NONE; + pev->solid = SOLID_NOT; + + pev->rendermode = kRenderGlow; + pev->renderfx = kRenderFxNoDissipation; + pev->renderamt = 255; + + SET_MODEL( ENT( pev ), "sprites/laserdot.spr" ); + UTIL_SetOrigin( pev, pev->origin ); +}; + +//========================================================= +// Suspend- make the laser sight invisible. +//========================================================= +void CLaserSpot::Suspend( float flSuspendTime ) +{ + pev->effects |= EF_NODRAW; + + SetThink( Revive ); + pev->nextthink = gpGlobals->time + flSuspendTime; +} + +//========================================================= +// Revive - bring a suspended laser sight back. +//========================================================= +void CLaserSpot::Revive( void ) +{ + pev->effects &= ~EF_NODRAW; + + SetThink( NULL ); +} + +void CLaserSpot::Precache( void ) +{ + PRECACHE_MODEL("sprites/laserdot.spr"); +}; + +LINK_ENTITY_TO_CLASS( rpg_rocket, CRpgRocket ); + +//========================================================= +//========================================================= +CRpgRocket *CRpgRocket::CreateRpgRocket( Vector vecOrigin, Vector vecAngles, CBaseEntity *pOwner, CRpg *pLauncher ) +{ + CRpgRocket *pRocket = GetClassPtr( (CRpgRocket *)NULL ); + + UTIL_SetOrigin( pRocket->pev, vecOrigin ); + pRocket->pev->angles = vecAngles; + pRocket->Spawn(); + pRocket->SetTouch( CRpgRocket::RocketTouch ); + pRocket->m_pLauncher = pLauncher;// remember what RPG fired me. + pRocket->m_pLauncher->m_cActiveRockets++;// register this missile as active for the launcher + pRocket->pev->owner = pOwner->edict(); + + return pRocket; +} + +//========================================================= +//========================================================= +void CRpgRocket :: Spawn( void ) +{ + Precache( ); + // motor + pev->movetype = MOVETYPE_BOUNCE; + pev->solid = SOLID_BBOX; + + SET_MODEL(ENT(pev), "models/rpgrocket.mdl"); + UTIL_SetSize(pev, Vector( 0, 0, 0), Vector(0, 0, 0)); + UTIL_SetOrigin( pev, pev->origin ); + + pev->classname = MAKE_STRING("rpg_rocket"); + + SetThink( IgniteThink ); + SetTouch( ExplodeTouch ); + + pev->angles.x -= 30; + UTIL_MakeVectors( pev->angles ); + pev->angles.x = -(pev->angles.x + 30); + + pev->velocity = gpGlobals->v_forward * 250; + pev->gravity = 0.5; + + pev->nextthink = gpGlobals->time + 0.4; + + pev->dmg = gSkillData.plrDmgRPG; +} + +//========================================================= +//========================================================= +void CRpgRocket :: RocketTouch ( CBaseEntity *pOther ) +{ + if ( m_pLauncher ) + { + // my launcher is still around, tell it I'm dead. + m_pLauncher->m_cActiveRockets--; + } + + STOP_SOUND( edict(), CHAN_VOICE, "weapons/rocket1.wav" ); + ExplodeTouch( pOther ); +} + +//========================================================= +//========================================================= +void CRpgRocket :: Precache( void ) +{ + PRECACHE_MODEL("models/rpgrocket.mdl"); + m_iTrail = PRECACHE_MODEL("sprites/smoke.spr"); + PRECACHE_SOUND ("weapons/rocket1.wav"); +} + + +void CRpgRocket :: IgniteThink( void ) +{ + // pev->movetype = MOVETYPE_TOSS; + + pev->movetype = MOVETYPE_FLY; + pev->effects |= EF_LIGHT; + + // make rocket sound + EMIT_SOUND( ENT(pev), CHAN_VOICE, "weapons/rocket1.wav", 1, 0.5 ); + + // rocket trail + MESSAGE_BEGIN( MSG_BROADCAST, SVC_TEMPENTITY ); + + WRITE_BYTE( TE_BEAMFOLLOW ); + WRITE_SHORT(entindex()); // entity + WRITE_SHORT(m_iTrail ); // model + WRITE_BYTE( 40 ); // life + WRITE_BYTE( 5 ); // width + WRITE_BYTE( 224 ); // r, g, b + WRITE_BYTE( 224 ); // r, g, b + WRITE_BYTE( 255 ); // r, g, b + WRITE_BYTE( 255 ); // brightness + + MESSAGE_END(); // move PHS/PVS data sending into here (SEND_ALL, SEND_PVS, SEND_PHS) + + m_flIgniteTime = gpGlobals->time; + + // set to follow laser spot + SetThink( FollowThink ); + pev->nextthink = gpGlobals->time + 0.1; +} + + +void CRpgRocket :: FollowThink( void ) +{ + CBaseEntity *pOther = NULL; + Vector vecTarget; + Vector vecDir; + float flDist, flMax, flDot; + TraceResult tr; + + UTIL_MakeAimVectors( pev->angles ); + + vecTarget = gpGlobals->v_forward; + flMax = 4096; + + // Examine all entities within a reasonable radius + while ((pOther = UTIL_FindEntityByClassname( pOther, "laser_spot" )) != NULL) + { + UTIL_TraceLine ( pev->origin, pOther->pev->origin, dont_ignore_monsters, ENT(pev), &tr ); + // ALERT( at_console, "%f\n", tr.flFraction ); + if (tr.flFraction >= 0.90) + { + vecDir = pOther->pev->origin - pev->origin; + flDist = vecDir.Length( ); + vecDir = vecDir.Normalize( ); + flDot = DotProduct( gpGlobals->v_forward, vecDir ); + if ((flDot > 0) && (flDist * (1 - flDot) < flMax)) + { + flMax = flDist * (1 - flDot); + vecTarget = vecDir; + } + } + } + + pev->angles = UTIL_VecToAngles( vecTarget ); + + // this acceleration and turning math is totally wrong, but it seems to respond well so don't change it. + float flSpeed = pev->velocity.Length(); + if (gpGlobals->time - m_flIgniteTime < 1.0) + { + pev->velocity = pev->velocity * 0.2 + vecTarget * (flSpeed * 0.8 + 400); + if (pev->waterlevel == 3) + { + // go slow underwater + if (pev->velocity.Length() > 300) + { + pev->velocity = pev->velocity.Normalize() * 300; + } + UTIL_BubbleTrail( pev->origin - pev->velocity * 0.1, pev->origin, 4 ); + } + else + { + if (pev->velocity.Length() > 2000) + { + pev->velocity = pev->velocity.Normalize() * 2000; + } + } + } + else + { + if (pev->effects & EF_LIGHT) + { + pev->effects = 0; + STOP_SOUND( ENT(pev), CHAN_VOICE, "weapons/rocket1.wav" ); + } + pev->velocity = pev->velocity * 0.2 + vecTarget * flSpeed * 0.798; + if (pev->waterlevel == 0 && pev->velocity.Length() < 1500) + { + Detonate( ); + } + } + // ALERT( at_console, "%.0f\n", flSpeed ); + + pev->nextthink = gpGlobals->time + 0.1; +} +#endif + + + +void CRpg::Reload( void ) +{ + int iResult; + + if ( m_iClip == 1 ) + { + // don't bother with any of this if don't need to reload. + return; + } + + if ( m_pPlayer->ammo_rockets <= 0 ) + return; + + // because the RPG waits to autoreload when no missiles are active while the LTD is on, the + // weapons code is constantly calling into this function, but is often denied because + // a) missiles are in flight, but the LTD is on + // or + // b) player is totally out of ammo and has nothing to switch to, and should be allowed to + // shine the designator around + // + // Set the next attack time into the future so that WeaponIdle will get called more often + // than reload, allowing the RPG LTD to be updated + + m_flNextPrimaryAttack = UTIL_WeaponTimeBase() + 0.5; + + if ( m_cActiveRockets && m_fSpotActive ) + { + // no reloading when there are active missiles tracking the designator. + // ward off future autoreload attempts by setting next attack time into the future for a bit. + return; + } + +#ifndef CLIENT_DLL + if ( m_pSpot && m_fSpotActive ) + { + m_pSpot->Suspend( 2.1 ); + m_flNextSecondaryAttack = UTIL_WeaponTimeBase() + 2.1; + } +#endif + + if ( m_iClip == 0 ) + iResult = DefaultReload( RPG_MAX_CLIP, RPG_RELOAD, 2 ); + + if ( iResult ) + m_flTimeWeaponIdle = UTIL_WeaponTimeBase() + UTIL_SharedRandomFloat( m_pPlayer->random_seed, 10, 15 ); + +} + +void CRpg::Spawn( ) +{ + Precache( ); + m_iId = WEAPON_RPG; + + SET_MODEL(ENT(pev), "models/w_rpg.mdl"); + m_fSpotActive = 1; + +#ifdef CLIENT_DLL + if ( bIsMultiplayer() ) +#else + if ( g_pGameRules->IsMultiplayer() ) +#endif + { + // more default ammo in multiplay. + m_iDefaultAmmo = RPG_DEFAULT_GIVE * 2; + } + else + { + m_iDefaultAmmo = RPG_DEFAULT_GIVE; + } + + FallInit();// get ready to fall down. +} + + +void CRpg::Precache( void ) +{ + PRECACHE_MODEL("models/w_rpg.mdl"); + PRECACHE_MODEL("models/v_rpg.mdl"); + PRECACHE_MODEL("models/p_rpg.mdl"); + + PRECACHE_SOUND("items/9mmclip1.wav"); + + UTIL_PrecacheOther( "laser_spot" ); + UTIL_PrecacheOther( "rpg_rocket" ); + + PRECACHE_SOUND("weapons/rocketfire1.wav"); + PRECACHE_SOUND("weapons/glauncher.wav"); // alternative fire sound + + m_usRpg = PRECACHE_EVENT ( 1, "events/rpg.sc" ); +} + + +int CRpg::GetItemInfo(ItemInfo *p) +{ + p->pszName = STRING(pev->classname); + p->pszAmmo1 = "rockets"; + p->iMaxAmmo1 = ROCKET_MAX_CARRY; + p->pszAmmo2 = NULL; + p->iMaxAmmo2 = -1; + p->iMaxClip = RPG_MAX_CLIP; + p->iSlot = 3; + p->iPosition = 0; + p->iId = m_iId = WEAPON_RPG; + p->iFlags = 0; + p->iWeight = RPG_WEIGHT; + + return 1; +} + +int CRpg::AddToPlayer( CBasePlayer *pPlayer ) +{ + if ( CBasePlayerWeapon::AddToPlayer( pPlayer ) ) + { + MESSAGE_BEGIN( MSG_ONE, gmsgWeapPickup, NULL, pPlayer->pev ); + WRITE_BYTE( m_iId ); + MESSAGE_END(); + return TRUE; + } + return FALSE; +} + +BOOL CRpg::Deploy( ) +{ + if ( m_iClip == 0 ) + { + return DefaultDeploy( "models/v_rpg.mdl", "models/p_rpg.mdl", RPG_DRAW_UL, "rpg" ); + } + + return DefaultDeploy( "models/v_rpg.mdl", "models/p_rpg.mdl", RPG_DRAW1, "rpg" ); +} + + +BOOL CRpg::CanHolster( void ) +{ + if ( m_fSpotActive && m_cActiveRockets ) + { + // can't put away while guiding a missile. + return FALSE; + } + + return TRUE; +} + +void CRpg::Holster( int skiplocal /* = 0 */ ) +{ + m_fInReload = FALSE;// cancel any reload in progress. + + m_pPlayer->m_flNextAttack = UTIL_WeaponTimeBase() + 0.5; + + SendWeaponAnim( RPG_HOLSTER1 ); + +#ifndef CLIENT_DLL + if (m_pSpot) + { + m_pSpot->Killed( NULL, GIB_NEVER ); + m_pSpot = NULL; + } +#endif + +} + + + +void CRpg::PrimaryAttack() +{ + if ( m_iClip ) + { + m_pPlayer->m_iWeaponVolume = LOUD_GUN_VOLUME; + m_pPlayer->m_iWeaponFlash = BRIGHT_GUN_FLASH; + +#ifndef CLIENT_DLL + // player "shoot" animation + m_pPlayer->SetAnimation( PLAYER_ATTACK1 ); + + UTIL_MakeVectors( m_pPlayer->pev->v_angle ); + Vector vecSrc = m_pPlayer->GetGunPosition( ) + gpGlobals->v_forward * 16 + gpGlobals->v_right * 8 + gpGlobals->v_up * -8; + + CRpgRocket *pRocket = CRpgRocket::CreateRpgRocket( vecSrc, m_pPlayer->pev->v_angle, m_pPlayer, this ); + + UTIL_MakeVectors( m_pPlayer->pev->v_angle );// RpgRocket::Create stomps on globals, so remake. + pRocket->pev->velocity = pRocket->pev->velocity + gpGlobals->v_forward * DotProduct( m_pPlayer->pev->velocity, gpGlobals->v_forward ); +#endif + + // firing RPG no longer turns on the designator. ALT fire is a toggle switch for the LTD. + // Ken signed up for this as a global change (sjb) + + int flags; +#if defined( CLIENT_WEAPONS ) + flags = FEV_NOTHOST; +#else + flags = 0; +#endif + + PLAYBACK_EVENT( flags, m_pPlayer->edict(), m_usRpg ); + + m_iClip--; + + m_flNextPrimaryAttack = UTIL_WeaponTimeBase() + 1.5; + m_flTimeWeaponIdle = UTIL_WeaponTimeBase() + 1.5; + } + else + { + PlayEmptySound( ); + } + UpdateSpot( ); +} + + +void CRpg::SecondaryAttack() +{ + m_fSpotActive = ! m_fSpotActive; + +#ifndef CLIENT_DLL + if (!m_fSpotActive && m_pSpot) + { + m_pSpot->Killed( NULL, GIB_NORMAL ); + m_pSpot = NULL; + } +#endif + + m_flNextSecondaryAttack = UTIL_WeaponTimeBase() + 0.2; +} + + +void CRpg::WeaponIdle( void ) +{ + UpdateSpot( ); + + ResetEmptySound( ); + + if ( m_flTimeWeaponIdle > UTIL_WeaponTimeBase() ) + return; + + if ( m_pPlayer->m_rgAmmo[m_iPrimaryAmmoType]) + { + int iAnim; + float flRand = UTIL_SharedRandomFloat( m_pPlayer->random_seed, 0, 1 ); + if (flRand <= 0.75 || m_fSpotActive) + { + if ( m_iClip == 0 ) + iAnim = RPG_IDLE_UL; + else + iAnim = RPG_IDLE; + + m_flTimeWeaponIdle = UTIL_WeaponTimeBase() + 90.0 / 15.0; + } + else + { + if ( m_iClip == 0 ) + iAnim = RPG_FIDGET_UL; + else + iAnim = RPG_FIDGET; + + m_flTimeWeaponIdle = UTIL_WeaponTimeBase() + 3.0; + } + + SendWeaponAnim( iAnim ); + } + else + { + m_flTimeWeaponIdle = UTIL_WeaponTimeBase() + 1; + } +} + + + +void CRpg::UpdateSpot( void ) +{ + +#ifndef CLIENT_DLL + if (m_fSpotActive) + { + if (!m_pSpot) + { + m_pSpot = CLaserSpot::CreateSpot(); + } + + UTIL_MakeVectors( m_pPlayer->pev->v_angle ); + Vector vecSrc = m_pPlayer->GetGunPosition( );; + Vector vecAiming = gpGlobals->v_forward; + + TraceResult tr; + UTIL_TraceLine ( vecSrc, vecSrc + vecAiming * 8192, dont_ignore_monsters, ENT(m_pPlayer->pev), &tr ); + + UTIL_SetOrigin( m_pSpot->pev, tr.vecEndPos ); + } +#endif + +} + + +class CRpgAmmo : public CBasePlayerAmmo +{ + void Spawn( void ) + { + Precache( ); + SET_MODEL(ENT(pev), "models/w_rpgammo.mdl"); + CBasePlayerAmmo::Spawn( ); + } + void Precache( void ) + { + PRECACHE_MODEL ("models/w_rpgammo.mdl"); + PRECACHE_SOUND("items/9mmclip1.wav"); + } + BOOL AddAmmo( CBaseEntity *pOther ) + { + int iGive; + +#ifdef CLIENT_DLL + if ( bIsMultiplayer() ) +#else + if ( g_pGameRules->IsMultiplayer() ) +#endif + { + // hand out more ammo per rocket in multiplayer. + iGive = AMMO_RPGCLIP_GIVE * 2; + } + else + { + iGive = AMMO_RPGCLIP_GIVE; + } + + if (pOther->GiveAmmo( iGive, "rockets", ROCKET_MAX_CARRY ) != -1) + { + EMIT_SOUND(ENT(pev), CHAN_ITEM, "items/9mmclip1.wav", 1, ATTN_NORM); + return TRUE; + } + return FALSE; + } +}; +LINK_ENTITY_TO_CLASS( ammo_rpgclip, CRpgAmmo ); + +#endif \ No newline at end of file diff --git a/spirit/satchel.cpp b/dlls/satchel.cpp similarity index 74% rename from spirit/satchel.cpp rename to dlls/satchel.cpp index 050c215d..7c5ff451 100644 --- a/spirit/satchel.cpp +++ b/dlls/satchel.cpp @@ -12,6 +12,8 @@ * without written permission from Valve LLC. * ****/ +#if !defined( OEM_BUILD ) && !defined( HLDEMO_BUILD ) + #include "extdll.h" #include "util.h" #include "cbase.h" @@ -36,28 +38,7 @@ enum satchel_radio_e { SATCHEL_RADIO_HOLSTER }; -class CSatchel : public CBasePlayerWeapon -{ -public: - int Save( CSave &save ); - int Restore( CRestore &restore ); - static TYPEDESCRIPTION m_SaveData[]; - void Spawn( void ); - void Precache( void ); - int GetItemInfo(ItemInfo *p); - void PrimaryAttack( void ); - void SecondaryAttack( void ); - int AddDuplicate( CBasePlayerItem *pOriginal ); - int CSatchel::AddToPlayer( CBasePlayer *pPlayer ); - BOOL CanDeploy( void ); - BOOL Deploy( void ); - BOOL IsUseable( void ); - - void Holster( ); - void WeaponIdle( void ); - void Throw( void ); -}; class CSatchelCharge : public CGrenade { @@ -92,18 +73,20 @@ void CSatchelCharge :: Spawn( void ) pev->solid = SOLID_BBOX; SET_MODEL(ENT(pev), "models/w_satchel.mdl"); + //UTIL_SetSize(pev, Vector( -16, -16, -4), Vector(16, 16, 32)); // Old box -- size of headcrab monsters/players get blocked by this UTIL_SetSize(pev, Vector( -4, -4, -4), Vector(4, 4, 4)); // Uses point-sized, and can be stepped over - UTIL_SetOrigin( this, pev->origin ); + UTIL_SetOrigin( pev, pev->origin ); + + SetTouch( SatchelSlide ); + SetUse( DetonateUse ); + SetThink( SatchelThink ); + pev->nextthink = gpGlobals->time + 0.1; - SetTouch(&CSatchelCharge :: SatchelSlide ); - SetUse(&CSatchelCharge :: DetonateUse ); - SetThink(&CSatchelCharge :: SatchelThink ); - SetNextThink( 0.1 ); - pev->gravity = 0.5; pev->friction = 0.8; pev->dmg = gSkillData.plrDmgSatchel; + // ResetSequenceInfo( ); pev->sequence = 1; } @@ -113,9 +96,13 @@ void CSatchelCharge::SatchelSlide( CBaseEntity *pOther ) entvars_t *pevOther = pOther->pev; // don't hit the guy that launched this grenade - if ( pOther->edict() == pev->owner ) return; + if ( pOther->edict() == pev->owner ) + return; + + // pev->avelocity = Vector (300, 300, 300); pev->gravity = 1;// normal gravity now + // HACKHACK - On ground isn't always set, so look for ground underneath TraceResult tr; UTIL_TraceLine( pev->origin, pev->origin - Vector(0,0,10), ignore_monsters, edict(), &tr ); @@ -137,7 +124,7 @@ void CSatchelCharge::SatchelSlide( CBaseEntity *pOther ) void CSatchelCharge :: SatchelThink( void ) { StudioFrameAdvance( ); - SetNextThink( 0.1 ); + pev->nextthink = gpGlobals->time + 0.1; if (!IsInWorld()) { @@ -145,17 +132,21 @@ void CSatchelCharge :: SatchelThink( void ) return; } - if (pev->waterlevel == 3 && pev->watertype != CONTENTS_FOG) + if (pev->waterlevel == 3) { pev->movetype = MOVETYPE_FLY; pev->velocity = pev->velocity * 0.8; pev->avelocity = pev->avelocity * 0.9; pev->velocity.z += 8; } - else if (pev->waterlevel == 0 || pev->watertype == CONTENTS_FOG) + else if (pev->waterlevel == 0) + { pev->movetype = MOVETYPE_BOUNCE; - else pev->velocity.z -= 8; - + } + else + { + pev->velocity.z -= 8; + } } void CSatchelCharge :: Precache( void ) @@ -175,6 +166,8 @@ void CSatchelCharge :: BounceSound( void ) case 2: EMIT_SOUND(ENT(pev), CHAN_VOICE, "weapons/g_bounce3.wav", 1, ATTN_NORM); break; } } + + LINK_ENTITY_TO_CLASS( weapon_satchel, CSatchel ); @@ -185,20 +178,24 @@ int CSatchel::AddDuplicate( CBasePlayerItem *pOriginal ) { CSatchel *pSatchel; - if ( IsMultiplayer() ) +#ifdef CLIENT_DLL + if ( bIsMultiplayer() ) +#else + if ( g_pGameRules->IsMultiplayer() ) +#endif { pSatchel = (CSatchel *)pOriginal; - if ( pSatchel->m_chargeReady != 0 )return FALSE; + + if ( pSatchel->m_chargeReady != 0 ) + { + // player has some satchels deployed. Refuse to add more. + return FALSE; + } } + return CBasePlayerWeapon::AddDuplicate ( pOriginal ); } -TYPEDESCRIPTION CSatchel::m_SaveData[] = -{ - DEFINE_FIELD( CSatchel, m_chargeReady, FIELD_INTEGER ), -}; -IMPLEMENT_SAVERESTORE( CSatchel, CBasePlayerWeapon ); - //========================================================= //========================================================= int CSatchel::AddToPlayer( CBasePlayer *pPlayer ) @@ -208,16 +205,21 @@ int CSatchel::AddToPlayer( CBasePlayer *pPlayer ) pPlayer->pev->weapons |= (1<m_rgAmmo[ PrimaryAmmoIndex() ] > 0 ) return TRUE; - if ( m_chargeReady != 0 ) return TRUE; + if ( m_pPlayer->m_rgAmmo[ PrimaryAmmoIndex() ] > 0 ) + { + // player is carrying some satchels + return TRUE; + } + + if ( m_chargeReady != 0 ) + { + // player isn't carrying any satchels, but has some out + return TRUE; + } + return FALSE; } BOOL CSatchel::CanDeploy( void ) { - if ( m_pPlayer->m_rgAmmo[ PrimaryAmmoIndex() ] > 0 ) return TRUE; - if ( m_chargeReady != 0 ) return TRUE; + if ( m_pPlayer->m_rgAmmo[ PrimaryAmmoIndex() ] > 0 ) + { + // player is carrying some satchels + return TRUE; + } + + if ( m_chargeReady != 0 ) + { + // player isn't carrying any satchels, but has some out + return TRUE; + } + return FALSE; } BOOL CSatchel::Deploy( ) { + m_pPlayer->m_flNextAttack = UTIL_WeaponTimeBase() + 1.0; - m_flTimeWeaponIdle = UTIL_WeaponTimeBase() + RANDOM_FLOAT( 10, 15 ); + m_flTimeWeaponIdle = UTIL_WeaponTimeBase() + UTIL_SharedRandomFloat( m_pPlayer->random_seed, 10, 15 ); if ( m_chargeReady ) - return DefaultDeploy( "models/v_satchel_radio.mdl", "models/p_satchel_radio.mdl", SATCHEL_RADIO_DRAW, "hive", 0.6 ); - else return DefaultDeploy( "models/v_satchel.mdl", "models/p_satchel.mdl", SATCHEL_DRAW, "trip", 1.5 ); + return DefaultDeploy( "models/v_satchel_radio.mdl", "models/p_satchel_radio.mdl", SATCHEL_RADIO_DRAW, "hive" ); + else + return DefaultDeploy( "models/v_satchel.mdl", "models/p_satchel.mdl", SATCHEL_DRAW, "trip" ); + + return TRUE; } -void CSatchel::Holster( ) +void CSatchel::Holster( int skiplocal /* = 0 */ ) { - m_pPlayer->m_flNextAttack = UTIL_WeaponTimeBase() + 0.6; + m_pPlayer->m_flNextAttack = UTIL_WeaponTimeBase() + 0.5; - if ( m_chargeReady ) SendWeaponAnim( SATCHEL_RADIO_HOLSTER ); - else SendWeaponAnim( SATCHEL_DROP ); - + if ( m_chargeReady ) + { + SendWeaponAnim( SATCHEL_RADIO_HOLSTER ); + } + else + { + SendWeaponAnim( SATCHEL_DROP ); + } EMIT_SOUND(ENT(m_pPlayer->pev), CHAN_WEAPON, "common/null.wav", 1.0, ATTN_NORM); + if ( !m_pPlayer->m_rgAmmo[m_iPrimaryAmmoType] && !m_chargeReady ) { m_pPlayer->pev->weapons &= ~(1<nextthink = gpGlobals->time + 0.1; } } @@ -301,7 +333,7 @@ void CSatchel::PrimaryAttack() { switch (m_chargeReady) { - case 0: + case 0: { Throw( ); } @@ -309,62 +341,11 @@ void CSatchel::PrimaryAttack() case 1: { SendWeaponAnim( SATCHEL_RADIO_FIRE ); - m_chargeReady = 2; - m_iChargeLevel = 1; - m_flNextPrimaryAttack = UTIL_WeaponTimeBase() + 0.5; - m_flNextSecondaryAttack = UTIL_WeaponTimeBase() + 0.5; - m_flTimeWeaponIdle = UTIL_WeaponTimeBase() + 1.5; - m_flTimeUpdate = UTIL_WeaponTimeBase() + RANDOM_FLOAT( 0.3, 0.5 ); - break; - } - case 2: - { - } - break; - } -} - -void CSatchel::SecondaryAttack( void ) -{ - if ( m_chargeReady != 2) Throw(); -} - - -void CSatchel::Throw( void ) -{ - if ( m_pPlayer->m_rgAmmo[m_iPrimaryAmmoType] ) - { - Vector vecSrc = m_pPlayer->pev->origin; - Vector vecThrow = gpGlobals->v_forward * 274 + m_pPlayer->pev->velocity; - CBaseEntity *pSatchel = Create( "monster_satchel", vecSrc, Vector( 0, 0, 0), m_pPlayer->edict() ); - pSatchel->pev->velocity = vecThrow; - pSatchel->pev->avelocity.y = 400; - - m_chargeReady = 1; - m_pPlayer->pev->viewmodel = MAKE_STRING("models/v_satchel_radio.mdl"); - m_pPlayer->pev->weaponmodel = MAKE_STRING("models/p_satchel_radio.mdl"); - m_pPlayer->m_rgAmmo[m_iPrimaryAmmoType]--; - - SendWeaponAnim( SATCHEL_RADIO_DRAW ); - - // player "shoot" animation - m_pPlayer->SetAnimation( PLAYER_ATTACK1 ); - - m_iOverloadLevel = 0;//reset - m_flNextPrimaryAttack = UTIL_WeaponTimeBase() + 1.0; - m_flNextSecondaryAttack = UTIL_WeaponTimeBase() + 0.5; - m_flTimeWeaponIdle = UTIL_WeaponTimeBase() + 1.5; - } -} - - -void CSatchel::WeaponIdle( void ) -{ - if ( m_flTimeUpdate < UTIL_WeaponTimeBase() && m_iChargeLevel) - { edict_t *pPlayer = m_pPlayer->edict( ); + CBaseEntity *pSatchel = NULL; + while ((pSatchel = UTIL_FindEntityInSphere( pSatchel, m_pPlayer->pev->origin, 4096 )) != NULL) { if (FClassnameIs( pSatchel->pev, "monster_satchel")) @@ -376,11 +357,71 @@ void CSatchel::WeaponIdle( void ) } } } - SendWeaponAnim( SATCHEL_RADIO_HOLSTER ); - m_iChargeLevel = 0; - } - if ( m_flTimeWeaponIdle > UTIL_WeaponTimeBase() ) return; + m_chargeReady = 2; + m_flNextPrimaryAttack = UTIL_WeaponTimeBase() + 0.5; + m_flNextSecondaryAttack = UTIL_WeaponTimeBase() + 0.5; + m_flTimeWeaponIdle = UTIL_WeaponTimeBase() + 0.5; + break; + } + + case 2: + // we're reloading, don't allow fire + { + } + break; + } +} + + +void CSatchel::SecondaryAttack( void ) +{ + if ( m_chargeReady != 2 ) + { + Throw( ); + } +} + + +void CSatchel::Throw( void ) +{ + if ( m_pPlayer->m_rgAmmo[m_iPrimaryAmmoType] ) + { + Vector vecSrc = m_pPlayer->pev->origin; + + Vector vecThrow = gpGlobals->v_forward * 274 + m_pPlayer->pev->velocity; + +#ifndef CLIENT_DLL + CBaseEntity *pSatchel = Create( "monster_satchel", vecSrc, Vector( 0, 0, 0), m_pPlayer->edict() ); + pSatchel->pev->velocity = vecThrow; + pSatchel->pev->avelocity.y = 400; + + m_pPlayer->pev->viewmodel = MAKE_STRING("models/v_satchel_radio.mdl"); + m_pPlayer->pev->weaponmodel = MAKE_STRING("models/p_satchel_radio.mdl"); +#else + LoadVModel ( "models/v_satchel_radio.mdl", m_pPlayer ); +#endif + + SendWeaponAnim( SATCHEL_RADIO_DRAW ); + + // player "shoot" animation + m_pPlayer->SetAnimation( PLAYER_ATTACK1 ); + + m_chargeReady = 1; + + m_pPlayer->m_rgAmmo[m_iPrimaryAmmoType]--; + + m_flNextPrimaryAttack = UTIL_WeaponTimeBase() + 1.0; + m_flNextSecondaryAttack = UTIL_WeaponTimeBase() + 0.5; + } +} + + +void CSatchel::WeaponIdle( void ) +{ + if ( m_flTimeWeaponIdle > UTIL_WeaponTimeBase() ) + return; + switch( m_chargeReady ) { case 0: @@ -401,8 +442,13 @@ void CSatchel::WeaponIdle( void ) return; } +#ifndef CLIENT_DLL m_pPlayer->pev->viewmodel = MAKE_STRING("models/v_satchel.mdl"); m_pPlayer->pev->weaponmodel = MAKE_STRING("models/p_satchel.mdl"); +#else + LoadVModel ( "models/v_satchel.mdl", m_pPlayer ); +#endif + SendWeaponAnim( SATCHEL_DRAW ); // use tripmine animations @@ -413,7 +459,7 @@ void CSatchel::WeaponIdle( void ) m_chargeReady = 0; break; } - m_flTimeWeaponIdle = UTIL_WeaponTimeBase() + RANDOM_LONG( 10, 15 );// how long till we do this again. + m_flTimeWeaponIdle = UTIL_WeaponTimeBase() + UTIL_SharedRandomFloat( m_pPlayer->random_seed, 10, 15 );// how long till we do this again. } //========================================================= @@ -427,6 +473,7 @@ void DeactivateSatchels( CBasePlayer *pOwner ) edict_t *pFind; pFind = FIND_ENTITY_BY_CLASSNAME( NULL, "monster_satchel" ); + while ( !FNullEnt( pFind ) ) { CBaseEntity *pEnt = CBaseEntity::Instance( pFind ); @@ -435,9 +482,13 @@ void DeactivateSatchels( CBasePlayer *pOwner ) if ( pSatchel ) { if ( pSatchel->pev->owner == pOwner->edict() ) + { pSatchel->Deactivate(); + } } pFind = FIND_ENTITY_BY_CLASSNAME( pFind, "monster_satchel" ); } -} \ No newline at end of file +} + +#endif \ No newline at end of file diff --git a/spirit/saverestore.h b/dlls/saverestore.h similarity index 91% rename from spirit/saverestore.h rename to dlls/saverestore.h index 6f77ebf7..c774b62d 100644 --- a/spirit/saverestore.h +++ b/dlls/saverestore.h @@ -1,177 +1,169 @@ -/*** -* -* Copyright (c) 1996-2002, 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. -* -****/ -// Implementation in UTIL.CPP -#ifndef SAVERESTORE_H -#define SAVERESTORE_H - -class CBaseEntity; - -class CSaveRestoreBuffer -{ -public: - CSaveRestoreBuffer( void ); - CSaveRestoreBuffer( SAVERESTOREDATA *pdata ); - ~CSaveRestoreBuffer( void ); - - int EntityIndex( entvars_t *pevLookup ); - int EntityIndex( edict_t *pentLookup ); - int EntityIndex( EOFFSET eoLookup ); - int EntityIndex( CBaseEntity *pEntity ); - - int EntityFlags( int entityIndex, int flags ) { return EntityFlagsSet( entityIndex, 0 ); } - int EntityFlagsSet( int entityIndex, int flags ); - - edict_t *EntityFromIndex( int entityIndex ); - - unsigned short TokenHash( const char *pszToken ); - -protected: - SAVERESTOREDATA *m_pdata; - void BufferRewind( int size ); - unsigned int HashString( const char *pszToken ); -}; - - -class CSave : public CSaveRestoreBuffer -{ -public: - CSave( SAVERESTOREDATA *pdata ) : CSaveRestoreBuffer( pdata ) {}; - - void WriteShort( const char *pname, const short *value, int count ); - void WriteInt( const char *pname, const int *value, int count ); // Save an int - void WriteFloat( const char *pname, const float *value, int count ); // Save a float - void WriteTime( const char *pname, const float *value, int count ); // Save a float (timevalue) - void WriteData( const char *pname, int size, const char *pdata ); // Save a binary data block - void WriteString( const char *pname, const char *pstring ); // Save a null-terminated string - void WriteString( const char *pname, const int *stringId, int count ); // Save a null-terminated string (engine string) - void WriteVector( const char *pname, const Vector &value ); // Save a vector - void WriteVector( const char *pname, const float *value, int count ); // Save a vector - void WritePositionVector( const char *pname, const Vector &value ); // Offset for landmark if necessary - void WritePositionVector( const char *pname, const float *value, int count ); // array of pos vectors - void WriteFunction( const char *pname, const int *value, int count ); // Save a function pointer - // Save a function pointer. (LRC- also pass the classname to allow better error messages) - void WriteFunction( const char* cname, const char *pname, const int *value, int count ); - - int WriteEntVars( const char *pname, entvars_t *pev ); // Save entvars_t (entvars_t) - int WriteFields( const char *pname, void *pBaseData, TYPEDESCRIPTION *pFields, int fieldCount ); - int WriteFields( const char *cname, const char *pname, void *pBaseData, TYPEDESCRIPTION *pFields, int fieldCount ); - -private: - int DataEmpty( const char *pdata, int size ); - void BufferField( const char *pname, int size, const char *pdata ); - void BufferString( char *pdata, int len ); - void BufferData( const char *pdata, int size ); - void BufferHeader( const char *pname, int size ); -}; - -typedef struct -{ - unsigned short size; - unsigned short token; - char *pData; -} HEADER; - -class CRestore : public CSaveRestoreBuffer -{ -public: - CRestore( SAVERESTOREDATA *pdata ) : CSaveRestoreBuffer( pdata ) { m_global = 0; m_precache = TRUE; } - - int ReadEntVars( const char *pname, entvars_t *pev ); // entvars_t - int ReadFields( const char *pname, void *pBaseData, TYPEDESCRIPTION *pFields, int fieldCount ); - int ReadField( void *pBaseData, TYPEDESCRIPTION *pFields, int fieldCount, int startField, int size, char *pName, void *pData ); - int ReadInt( void ); - short ReadShort( void ); - int ReadNamedInt( const char *pName ); - char *ReadNamedString( const char *pName ); - int Empty( void ) { return (m_pdata == NULL) || ((m_pdata->pCurrentData-m_pdata->pBaseData)>=m_pdata->bufferSize); } - inline void SetGlobalMode( int global ) { m_global = global; } - void PrecacheMode( BOOL mode ) { m_precache = mode; } - -private: - char *BufferPointer( void ); - void BufferReadBytes( char *pOutput, int size ); - void BufferSkipBytes( int bytes ); - int BufferSkipZString( void ); - int BufferCheckZString( const char *string ); - - void BufferReadHeader( HEADER *pheader ); - - int m_global; // Restoring a global entity? - BOOL m_precache; -}; - -#define MAX_ENTITYARRAY 64 - -//#define ARRAYSIZE(p) (sizeof(p)/sizeof(p[0])) - -#define IMPLEMENT_SAVERESTORE(derivedClass,baseClass) \ - int derivedClass::Save( CSave &save )\ - {\ - if ( !baseClass::Save(save) )\ - return 0;\ - if (pev->targetname)\ - return save.WriteFields( STRING(pev->targetname), #derivedClass, this, m_SaveData, ARRAYSIZE(m_SaveData) );\ - else\ - return save.WriteFields( STRING(pev->classname), #derivedClass, this, m_SaveData, ARRAYSIZE(m_SaveData) );\ - }\ - int derivedClass::Restore( CRestore &restore )\ - {\ - if ( !baseClass::Restore(restore) )\ - return 0;\ - return restore.ReadFields( #derivedClass, this, m_SaveData, ARRAYSIZE(m_SaveData) );\ - } - - -typedef enum { GLOBAL_OFF = 0, GLOBAL_ON = 1, GLOBAL_DEAD = 2 } GLOBALESTATE; - -typedef struct globalentity_s globalentity_t; - -struct globalentity_s -{ - char name[64]; - char levelName[32]; - GLOBALESTATE state; - globalentity_t *pNext; -}; - -class CGlobalState -{ -public: - CGlobalState(); - void Reset( void ); - void ClearStates( void ); - void EntityAdd( string_t globalname, string_t mapName, GLOBALESTATE state ); - void EntitySetState( string_t globalname, GLOBALESTATE state ); - void EntityUpdate( string_t globalname, string_t mapname ); - const globalentity_t *EntityFromTable( string_t globalname ); - GLOBALESTATE EntityGetState( string_t globalname ); - int EntityInTable( string_t globalname ) { return (Find( globalname ) != NULL) ? 1 : 0; } - int Save( CSave &save ); - int Restore( CRestore &restore ); - static TYPEDESCRIPTION m_SaveData[]; - -//#ifdef _DEBUG - void DumpGlobals( void ); -//#endif - -private: - globalentity_t *Find( string_t globalname ); - globalentity_t *m_pList; - int m_listCount; -}; - -extern CGlobalState gGlobalState; - -#endif //SAVERESTORE_H +/*** +* +* Copyright (c) 1996-2002, 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. +* +****/ +// Implementation in UTIL.CPP +#ifndef SAVERESTORE_H +#define SAVERESTORE_H + +class CBaseEntity; + +class CSaveRestoreBuffer +{ +public: + CSaveRestoreBuffer( void ); + CSaveRestoreBuffer( SAVERESTOREDATA *pdata ); + ~CSaveRestoreBuffer( void ); + + int EntityIndex( entvars_t *pevLookup ); + int EntityIndex( edict_t *pentLookup ); + int EntityIndex( EOFFSET eoLookup ); + int EntityIndex( CBaseEntity *pEntity ); + + int EntityFlags( int entityIndex, int flags ) { return EntityFlagsSet( entityIndex, 0 ); } + int EntityFlagsSet( int entityIndex, int flags ); + + edict_t *EntityFromIndex( int entityIndex ); + + unsigned short TokenHash( const char *pszToken ); + +protected: + SAVERESTOREDATA *m_pdata; + void BufferRewind( int size ); + unsigned int HashString( const char *pszToken ); +}; + + +class CSave : public CSaveRestoreBuffer +{ +public: + CSave( SAVERESTOREDATA *pdata ) : CSaveRestoreBuffer( pdata ) {}; + + void WriteShort( const char *pname, const short *value, int count ); + void WriteInt( const char *pname, const int *value, int count ); // Save an int + void WriteFloat( const char *pname, const float *value, int count ); // Save a float + void WriteTime( const char *pname, const float *value, int count ); // Save a float (timevalue) + void WriteData( const char *pname, int size, const char *pdata ); // Save a binary data block + void WriteString( const char *pname, const char *pstring ); // Save a null-terminated string + void WriteString( const char *pname, const int *stringId, int count ); // Save a null-terminated string (engine string) + void WriteVector( const char *pname, const Vector &value ); // Save a vector + void WriteVector( const char *pname, const float *value, int count ); // Save a vector + void WritePositionVector( const char *pname, const Vector &value ); // Offset for landmark if necessary + void WritePositionVector( const char *pname, const float *value, int count ); // array of pos vectors + void WriteFunction( const char *pname, const int *value, int count ); // Save a function pointer + int WriteEntVars( const char *pname, entvars_t *pev ); // Save entvars_t (entvars_t) + int WriteFields( const char *pname, void *pBaseData, TYPEDESCRIPTION *pFields, int fieldCount ); + +private: + int DataEmpty( const char *pdata, int size ); + void BufferField( const char *pname, int size, const char *pdata ); + void BufferString( char *pdata, int len ); + void BufferData( const char *pdata, int size ); + void BufferHeader( const char *pname, int size ); +}; + +typedef struct +{ + unsigned short size; + unsigned short token; + char *pData; +} HEADER; + +class CRestore : public CSaveRestoreBuffer +{ +public: + CRestore( SAVERESTOREDATA *pdata ) : CSaveRestoreBuffer( pdata ) { m_global = 0; m_precache = TRUE; } + int ReadEntVars( const char *pname, entvars_t *pev ); // entvars_t + int ReadFields( const char *pname, void *pBaseData, TYPEDESCRIPTION *pFields, int fieldCount ); + int ReadField( void *pBaseData, TYPEDESCRIPTION *pFields, int fieldCount, int startField, int size, char *pName, void *pData ); + int ReadInt( void ); + short ReadShort( void ); + int ReadNamedInt( const char *pName ); + char *ReadNamedString( const char *pName ); + int Empty( void ) { return (m_pdata == NULL) || ((m_pdata->pCurrentData-m_pdata->pBaseData)>=m_pdata->bufferSize); } + inline void SetGlobalMode( int global ) { m_global = global; } + void PrecacheMode( BOOL mode ) { m_precache = mode; } + +private: + char *BufferPointer( void ); + void BufferReadBytes( char *pOutput, int size ); + void BufferSkipBytes( int bytes ); + int BufferSkipZString( void ); + int BufferCheckZString( const char *string ); + + void BufferReadHeader( HEADER *pheader ); + + int m_global; // Restoring a global entity? + BOOL m_precache; +}; + +#define MAX_ENTITYARRAY 64 + +//#define ARRAYSIZE(p) (sizeof(p)/sizeof(p[0])) + +#define IMPLEMENT_SAVERESTORE(derivedClass,baseClass) \ + int derivedClass::Save( CSave &save )\ + {\ + if ( !baseClass::Save(save) )\ + return 0;\ + return save.WriteFields( #derivedClass, this, m_SaveData, ARRAYSIZE(m_SaveData) );\ + }\ + int derivedClass::Restore( CRestore &restore )\ + {\ + if ( !baseClass::Restore(restore) )\ + return 0;\ + return restore.ReadFields( #derivedClass, this, m_SaveData, ARRAYSIZE(m_SaveData) );\ + } + + +typedef enum { GLOBAL_OFF = 0, GLOBAL_ON = 1, GLOBAL_DEAD = 2 } GLOBALESTATE; + +typedef struct globalentity_s globalentity_t; + +struct globalentity_s +{ + char name[64]; + char levelName[32]; + GLOBALESTATE state; + globalentity_t *pNext; +}; + +class CGlobalState +{ +public: + CGlobalState(); + void Reset( void ); + void ClearStates( void ); + void EntityAdd( string_t globalname, string_t mapName, GLOBALESTATE state ); + void EntitySetState( string_t globalname, GLOBALESTATE state ); + void EntityUpdate( string_t globalname, string_t mapname ); + const globalentity_t *EntityFromTable( string_t globalname ); + GLOBALESTATE EntityGetState( string_t globalname ); + int EntityInTable( string_t globalname ) { return (Find( globalname ) != NULL) ? 1 : 0; } + int Save( CSave &save ); + int Restore( CRestore &restore ); + static TYPEDESCRIPTION m_SaveData[]; + +//#ifdef _DEBUG + void DumpGlobals( void ); +//#endif + +private: + globalentity_t *Find( string_t globalname ); + globalentity_t *m_pList; + int m_listCount; +}; + +extern CGlobalState gGlobalState; + +#endif //SAVERESTORE_H diff --git a/spirit/schedule.cpp b/dlls/schedule.cpp similarity index 85% rename from spirit/schedule.cpp rename to dlls/schedule.cpp index 077ee7ae..9b4fe6cd 100644 --- a/spirit/schedule.cpp +++ b/dlls/schedule.cpp @@ -1,6 +1,6 @@ /*** * -* Copyright (c) 1999, 2000 Valve LLC. All rights reserved. +* Copyright (c) 1996-2002, Valve LLC. All rights reserved. * * This product contains software technology licensed from Id * Software, Inc. ("Id Technology"). Id Technology (c) 1996 Id Software, Inc. @@ -96,9 +96,40 @@ void CBaseMonster :: ChangeSchedule ( Schedule_t *pNewSchedule ) #if _DEBUG if ( !ScheduleFromName( pNewSchedule->pName ) ) { - ALERT( at_debug, "Schedule %s not in table!!!\n", pNewSchedule->pName ); + ALERT( at_console, "Schedule %s not in table!!!\n", pNewSchedule->pName ); } #endif + +// this is very useful code if you can isolate a test case in a level with a single monster. It will notify +// you of every schedule selection the monster makes. +#if 0 + if ( FClassnameIs( pev, "monster_human_grunt" ) ) + { + Task_t *pTask = GetTask(); + + if ( pTask ) + { + const char *pName = NULL; + + if ( m_pSchedule ) + { + pName = m_pSchedule->pName; + } + else + { + pName = "No Schedule"; + } + + if ( !pName ) + { + pName = "Unknown"; + } + + ALERT( at_aiconsole, "%s: picked schedule %s\n", STRING( pev->classname ), pName ); + } + } +#endif// 0 + } //========================================================= @@ -154,6 +185,8 @@ BOOL CBaseMonster :: FScheduleValid ( void ) if ( HasConditions ( bits_COND_TASK_FAILED ) && m_failSchedule == SCHED_NONE ) { // fail! Send a visual indicator. + ALERT ( at_aiconsole, "Schedule: %s Failed\n", m_pSchedule->pName ); + Vector tmp = pev->origin; tmp.z = pev->absmax.z + 16; UTIL_Sparks( tmp ); @@ -204,8 +237,6 @@ void CBaseMonster :: MaintainSchedule ( void ) if ( m_IdealMonsterState != MONSTERSTATE_DEAD && (m_IdealMonsterState != MONSTERSTATE_SCRIPT || m_IdealMonsterState == m_MonsterState) ) { - // if we're here, then either we're being told to do something (besides dying or playing a script) - // or our current schedule (besides dying) is invalid. -- LRC if ( (m_afConditions && !HasConditions(bits_COND_SCHEDULE_DONE)) || (m_pSchedule && (m_pSchedule->iInterruptMask & bits_COND_SCHEDULE_DONE)) || ((m_MonsterState == MONSTERSTATE_COMBAT) && (m_hEnemy == NULL)) ) @@ -227,9 +258,7 @@ void CBaseMonster :: MaintainSchedule ( void ) { SetState( m_IdealMonsterState ); if ( m_MonsterState == MONSTERSTATE_SCRIPT || m_MonsterState == MONSTERSTATE_DEAD ) - { pNewSchedule = CBaseMonster::GetSchedule(); - } else pNewSchedule = GetSchedule(); ChangeSchedule( pNewSchedule ); @@ -347,7 +376,7 @@ void CBaseMonster :: RunTask ( Task_t *pTask ) } case TASK_WAIT_PVS: { - if ( !FNullEnt(FIND_CLIENT_IN_PVS(edict())) || HaveCamerasInPVS( edict() )) + if ( !FNullEnt(FIND_CLIENT_IN_PVS(edict())) ) { TaskComplete(); } @@ -495,26 +524,19 @@ void CBaseMonster :: RunTask ( Task_t *pTask ) if ( m_pCine->m_iDelay <= 0 && gpGlobals->time >= m_pCine->m_startTime ) { TaskComplete(); + m_pCine->StartSequence( (CBaseMonster *)this, m_pCine->m_iszPlay, TRUE ); + if ( m_fSequenceFinished ) + ClearSchedule(); + pev->framerate = 1.0; + //ALERT( at_aiconsole, "Script %s has begun for %s\n", STRING( m_pCine->m_iszPlay ), STRING(pev->classname) ); } break; } case TASK_PLAY_SCRIPT: { -// ALERT(at_console, "Play Script\n"); if (m_fSequenceFinished) { -// ALERT(at_console, "Anim Finished\n"); - if (m_pCine->m_iRepeatsLeft > 0) - { -// ALERT(at_console, "Frame %f; Repeat %d from %f\n", pev->frame, m_pCine->m_iRepeatsLeft, m_pCine->m_fRepeatFrame); - m_pCine->m_iRepeatsLeft--; - pev->frame = m_pCine->m_fRepeatFrame; - ResetSequenceInfo( ); - } - else - { - TaskComplete(); - } + m_pCine->SequenceDone( this ); } break; } @@ -866,16 +888,16 @@ void CBaseMonster :: StartTask ( Task_t *pTask ) } break; } - case TASK_RUN_TO_SCRIPT: - case TASK_WALK_TO_SCRIPT: + case TASK_RUN_TO_TARGET: + case TASK_WALK_TO_TARGET: { Activity newActivity; - if ( !m_pGoalEnt || (m_pGoalEnt->pev->origin - pev->origin).Length() < 1 ) + if ( (m_hTargetEnt->pev->origin - pev->origin).Length() < 1 ) TaskComplete(); else { - if ( pTask->iTask == TASK_WALK_TO_SCRIPT ) + if ( pTask->iTask == TASK_WALK_TO_TARGET ) newActivity = ACT_WALK; else newActivity = ACT_RUN; @@ -884,22 +906,10 @@ void CBaseMonster :: StartTask ( Task_t *pTask ) TaskComplete(); else { - if ( m_pGoalEnt != NULL ) - { - Vector vecDest; - vecDest = m_pGoalEnt->pev->origin; - - if ( !MoveToLocation( newActivity, 2, vecDest ) ) - { - TaskFail(); - ALERT( at_aiconsole, "%s Failed to reach script!!!\n", STRING(pev->classname) ); - RouteClear(); - } - } - else + if ( m_hTargetEnt == NULL || !MoveToTarget( newActivity, 2 ) ) { TaskFail(); - ALERT( at_aiconsole, "%s: MoveTarget is missing!?!\n", STRING(pev->classname) ); + ALERT( at_aiconsole, "%s Failed to reach target!!!\n", STRING(pev->classname) ); RouteClear(); } } @@ -1019,7 +1029,7 @@ void CBaseMonster :: StartTask ( Task_t *pTask ) break; case TASK_GET_PATH_TO_SPOT: { - CBaseEntity *pPlayer = UTIL_FindEntityByClassname( NULL, "player" ); + CBaseEntity *pPlayer = CBaseEntity::Instance( FIND_ENTITY_BY_CLASSNAME( NULL, "player" ) ); if ( BuildRoute ( m_vecMoveGoal, bits_MF_TO_LOCATION, pPlayer ) ) { TaskComplete(); @@ -1048,21 +1058,6 @@ void CBaseMonster :: StartTask ( Task_t *pTask ) } break; } - case TASK_GET_PATH_TO_SCRIPT: - { - RouteClear(); - if ( m_pCine != NULL && MoveToLocation( m_movementActivity, 1, m_pCine->pev->origin ) ) - { - TaskComplete(); - } - else - { - // no way to get there =( - ALERT ( at_aiconsole, "GetPathToSpot failed!!\n" ); - TaskFail(); - } - break; - } case TASK_GET_PATH_TO_HINTNODE:// for active idles! { if ( MoveToLocation( m_movementActivity, 2, WorldGraph.m_pNodes[ m_iHintNode ].m_vecOrigin ) ) @@ -1255,11 +1250,7 @@ case TASK_GET_PATH_TO_BESTSCENT: } case TASK_WAIT_FOR_SCRIPT: { - if ( m_pCine->m_iDelay <= 0 && gpGlobals->time >= m_pCine->m_startTime ) - { - TaskComplete(); //LRC - start playing immediately - } - else if (!m_pCine->IsAction() && m_pCine->m_iszIdle) + if (m_pCine->m_iszIdle) { m_pCine->StartSequence( (CBaseMonster *)this, m_pCine->m_iszIdle, FALSE ); if (FStrEq( STRING(m_pCine->m_iszIdle), STRING(m_pCine->m_iszPlay))) @@ -1274,40 +1265,8 @@ case TASK_GET_PATH_TO_BESTSCENT: } case TASK_PLAY_SCRIPT: { - if (m_pCine->IsAction()) - { - //ALERT(at_console,"PlayScript: setting idealactivity %d\n",m_pCine->m_fAction); - switch(m_pCine->m_fAction) - { - case 0: - m_IdealActivity = ACT_RANGE_ATTACK1; break; - case 1: - m_IdealActivity = ACT_RANGE_ATTACK2; break; - case 2: - m_IdealActivity = ACT_MELEE_ATTACK1; break; - case 3: - m_IdealActivity = ACT_MELEE_ATTACK2; break; - case 4: - m_IdealActivity = ACT_SPECIAL_ATTACK1; break; - case 5: - m_IdealActivity = ACT_SPECIAL_ATTACK2; break; - case 6: - m_IdealActivity = ACT_RELOAD; break; - case 7: - m_IdealActivity = ACT_HOP; break; - } - pev->framerate = 1.0; // shouldn't be needed, but just in case - pev->movetype = MOVETYPE_FLY; - ClearBits(pev->flags, FL_ONGROUND); - } - else - { - m_pCine->StartSequence( (CBaseMonster *)this, m_pCine->m_iszPlay, TRUE ); - if ( m_fSequenceFinished ) - ClearSchedule(); - pev->framerate = 1.0; - //ALERT( at_aiconsole, "Script %s has begun for %s\n", STRING( m_pCine->m_iszPlay ), STRING(pev->classname) ); - } + pev->movetype = MOVETYPE_FLY; + ClearBits(pev->flags, FL_ONGROUND); m_scriptState = SCRIPT_PLAYING; break; } @@ -1317,33 +1276,11 @@ case TASK_GET_PATH_TO_BESTSCENT: TaskComplete(); break; } -//LRC - case TASK_END_SCRIPT: - { - m_pCine->SequenceDone( this ); - TaskComplete(); - break; - } case TASK_PLANT_ON_SCRIPT: { - if ( m_pCine != NULL ) + if ( m_hTargetEnt != NULL ) { - // Plant on script - // LRC - if it's a teleport script, do the turn too - if (m_pCine->m_fMoveTo == 4 || m_pCine->m_fMoveTo == 6) - { - if (m_pCine->m_fTurnType == 0) //LRC - pev->angles.y = m_hTargetEnt->pev->angles.y; - else if (m_pCine->m_fTurnType == 1) - pev->angles.y = UTIL_VecToYaw(m_hTargetEnt->pev->origin - pev->origin); - pev->ideal_yaw = pev->angles.y; - pev->avelocity = Vector( 0, 0, 0 ); - pev->velocity = Vector( 0, 0, 0 ); - pev->effects |= EF_NOINTERP; - } - - if (m_pCine->m_fMoveTo != 6) - pev->origin = m_pGoalEnt->pev->origin; + pev->origin = m_hTargetEnt->pev->origin; // Plant on target } TaskComplete(); @@ -1351,22 +1288,9 @@ case TASK_GET_PATH_TO_BESTSCENT: } case TASK_FACE_SCRIPT: { - if ( m_pCine != NULL && m_pCine->m_fMoveTo != 0) // movetype "no move" makes us ignore turntype + if ( m_hTargetEnt != NULL ) { - switch (m_pCine->m_fTurnType) - { - case 0: - pev->ideal_yaw = UTIL_AngleMod( m_pCine->pev->angles.y ); - break; - case 1: - // yes, this is inconsistent- turn to face uses the "target" and turn to angle uses the "cine". - if (m_hTargetEnt) - MakeIdealYaw ( m_hTargetEnt->pev->origin ); - else - MakeIdealYaw ( m_pCine->pev->origin ); - break; - // default: don't turn - } + pev->ideal_yaw = UTIL_AngleMod( m_hTargetEnt->pev->angles.y ); } TaskComplete(); @@ -1374,6 +1298,7 @@ case TASK_GET_PATH_TO_BESTSCENT: RouteClear(); break; } + case TASK_SUGGEST_STATE: { m_IdealMonsterState = (MONSTERSTATE)(int)pTask->flData; @@ -1572,7 +1497,6 @@ Schedule_t *CBaseMonster :: GetSchedule ( void ) if ( !m_pCine ) { ALERT( at_aiconsole, "Script failed for %s\n", STRING(pev->classname) ); -// ALERT( at_console, "Script failed for %s\n", STRING(pev->classname) ); CineCleanup(); return GetScheduleOfType( SCHED_IDLE_STAND ); } diff --git a/spirit/schedule.h b/dlls/schedule.h similarity index 96% rename from spirit/schedule.h rename to dlls/schedule.h index 59e4079d..f73e8953 100644 --- a/spirit/schedule.h +++ b/dlls/schedule.h @@ -1,292 +1,290 @@ -/*** -* -* Copyright (c) 1996-2002, 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. -* -* This source code contains proprietary and confidential information of -* Valve LLC and its suppliers. Access to this code is restricted to -* persons who have executed a written SDK license with Valve. Any access, -* use or distribution of this code by or to any unlicensed person is illegal. -* -****/ -//========================================================= -// Scheduling -//========================================================= - -#ifndef SCHEDULE_H -#define SCHEDULE_H - -#define TASKSTATUS_NEW 0 // Just started -#define TASKSTATUS_RUNNING 1 // Running task & movement -#define TASKSTATUS_RUNNING_MOVEMENT 2 // Just running movement -#define TASKSTATUS_RUNNING_TASK 3 // Just running task -#define TASKSTATUS_COMPLETE 4 // Completed, get next task - - -//========================================================= -// These are the schedule types -//========================================================= -typedef enum -{ - SCHED_NONE = 0, - SCHED_IDLE_STAND, - SCHED_IDLE_WALK, - SCHED_WAKE_ANGRY, - SCHED_WAKE_CALLED, - SCHED_ALERT_FACE, - SCHED_ALERT_SMALL_FLINCH, - SCHED_ALERT_BIG_FLINCH, - SCHED_ALERT_STAND, - SCHED_INVESTIGATE_SOUND, - SCHED_COMBAT_FACE, - SCHED_COMBAT_STAND, - SCHED_CHASE_ENEMY, - SCHED_CHASE_ENEMY_FAILED, - SCHED_VICTORY_DANCE, - SCHED_TARGET_FACE, - SCHED_TARGET_CHASE, - SCHED_SMALL_FLINCH, - SCHED_TAKE_COVER_FROM_ENEMY, - SCHED_TAKE_COVER_FROM_BEST_SOUND, - SCHED_TAKE_COVER_FROM_ORIGIN, - SCHED_COWER, // usually a last resort! - SCHED_MELEE_ATTACK1, - SCHED_MELEE_ATTACK2, - SCHED_RANGE_ATTACK1, - SCHED_RANGE_ATTACK2, - SCHED_SPECIAL_ATTACK1, - SCHED_SPECIAL_ATTACK2, - SCHED_STANDOFF, - SCHED_ARM_WEAPON, - SCHED_RELOAD, - SCHED_GUARD, - SCHED_AMBUSH, - SCHED_DIE, - SCHED_WAIT_TRIGGER, - SCHED_FOLLOW, - SCHED_SLEEP, - SCHED_WAKE, - SCHED_BARNACLE_VICTIM_GRAB, - SCHED_BARNACLE_VICTIM_CHOMP, - SCHED_AISCRIPT, - SCHED_FAIL, - - LAST_COMMON_SCHEDULE // Leave this at the bottom -} SCHEDULE_TYPE; - -//========================================================= -// These are the shared tasks -//========================================================= -typedef enum -{ - TASK_INVALID = 0, - TASK_WAIT, - TASK_WAIT_FACE_ENEMY, - TASK_WAIT_PVS, - TASK_SUGGEST_STATE, - TASK_WALK_TO_SCRIPT, - TASK_RUN_TO_SCRIPT, - TASK_MOVE_TO_TARGET_RANGE, - TASK_GET_PATH_TO_ENEMY, - TASK_GET_PATH_TO_ENEMY_LKP, - TASK_GET_PATH_TO_ENEMY_CORPSE, - TASK_GET_PATH_TO_LEADER, - TASK_GET_PATH_TO_SPOT, - TASK_GET_PATH_TO_TARGET, - TASK_GET_PATH_TO_SCRIPT, - TASK_GET_PATH_TO_HINTNODE, - TASK_GET_PATH_TO_LASTPOSITION, - TASK_GET_PATH_TO_BESTSOUND, - TASK_GET_PATH_TO_BESTSCENT, - TASK_RUN_PATH, - TASK_WALK_PATH, - TASK_STRAFE_PATH, - TASK_CLEAR_MOVE_WAIT, - TASK_STORE_LASTPOSITION, - TASK_CLEAR_LASTPOSITION, - TASK_PLAY_ACTIVE_IDLE, - TASK_FIND_HINTNODE, - TASK_CLEAR_HINTNODE, - TASK_SMALL_FLINCH, - TASK_FACE_IDEAL, - TASK_FACE_ROUTE, - TASK_FACE_ENEMY, - TASK_FACE_HINTNODE, - TASK_FACE_TARGET, - TASK_FACE_LASTPOSITION, - TASK_RANGE_ATTACK1, - TASK_RANGE_ATTACK2, - TASK_MELEE_ATTACK1, - TASK_MELEE_ATTACK2, - TASK_RELOAD, - TASK_RANGE_ATTACK1_NOTURN, - TASK_RANGE_ATTACK2_NOTURN, - TASK_MELEE_ATTACK1_NOTURN, - TASK_MELEE_ATTACK2_NOTURN, - TASK_RELOAD_NOTURN, - TASK_SPECIAL_ATTACK1, - TASK_SPECIAL_ATTACK2, - TASK_CROUCH, - TASK_STAND, - TASK_GUARD, - TASK_STEP_LEFT, - TASK_STEP_RIGHT, - TASK_STEP_FORWARD, - TASK_STEP_BACK, - TASK_DODGE_LEFT, - TASK_DODGE_RIGHT, - TASK_SOUND_ANGRY, - TASK_SOUND_DEATH, - TASK_SET_ACTIVITY, - TASK_SET_SCHEDULE, - TASK_SET_FAIL_SCHEDULE, - TASK_CLEAR_FAIL_SCHEDULE, - TASK_PLAY_SEQUENCE, - TASK_PLAY_SEQUENCE_FACE_ENEMY, - TASK_PLAY_SEQUENCE_FACE_TARGET, - TASK_SOUND_IDLE, - TASK_SOUND_WAKE, - TASK_SOUND_PAIN, - TASK_SOUND_DIE, - TASK_FIND_COVER_FROM_BEST_SOUND,// tries lateral cover first, then node cover - TASK_FIND_COVER_FROM_ENEMY,// tries lateral cover first, then node cover - TASK_FIND_LATERAL_COVER_FROM_ENEMY, - TASK_FIND_NODE_COVER_FROM_ENEMY, - TASK_FIND_NEAR_NODE_COVER_FROM_ENEMY,// data for this one is the MAXIMUM acceptable distance to the cover. - TASK_FIND_FAR_NODE_COVER_FROM_ENEMY,// data for this one is there MINIMUM aceptable distance to the cover. - TASK_FIND_COVER_FROM_ORIGIN, - TASK_EAT, - TASK_DIE, - TASK_WAIT_FOR_SCRIPT, - TASK_PLAY_SCRIPT, - TASK_ENABLE_SCRIPT, - TASK_PLANT_ON_SCRIPT, - TASK_FACE_SCRIPT, - TASK_END_SCRIPT, //LRC - TASK_WAIT_RANDOM, - TASK_WAIT_INDEFINITE, - TASK_STOP_MOVING, - TASK_TURN_LEFT, - TASK_TURN_RIGHT, - TASK_REMEMBER, - TASK_FORGET, - TASK_WAIT_FOR_MOVEMENT, // wait until MovementIsComplete() - LAST_COMMON_TASK, // LEAVE THIS AT THE BOTTOM!! (sjb) -} SHARED_TASKS; - - -// These go in the flData member of the TASK_WALK_TO_TARGET, TASK_RUN_TO_TARGET -enum -{ - TARGET_MOVE_NORMAL = 0, - TARGET_MOVE_SCRIPTED = 1, -}; - - -// A goal should be used for a task that requires several schedules to complete. -// The goal index should indicate which schedule (ordinally) the monster is running. -// That way, when tasks fail, the AI can make decisions based on the context of the -// current goal and sequence rather than just the current schedule. -enum -{ - GOAL_ATTACK_ENEMY, - GOAL_MOVE, - GOAL_TAKE_COVER, - GOAL_MOVE_TARGET, - GOAL_EAT, -}; - -// an array of tasks is a task list -// an array of schedules is a schedule list -struct Task_t -{ - - int iTask; - float flData; -}; - -struct Schedule_t -{ - - Task_t *pTasklist; - int cTasks; - int iInterruptMask;// a bit mask of conditions that can interrupt this schedule - - // a more specific mask that indicates which TYPES of sounds will interrupt the schedule in the - // event that the schedule is broken by COND_HEAR_SOUND - int iSoundMask; - const char *pName; -}; - -// an array of waypoints makes up the monster's route. -// !!!LATER- this declaration doesn't belong in this file. -struct WayPoint_t -{ - Vector vecLocation; - int iType; -}; - -// these MoveFlag values are assigned to a WayPoint's TYPE in order to demonstrate the -// type of movement the monster should use to get there. -#define bits_MF_TO_TARGETENT ( 1 << 0 ) // local move to targetent. -#define bits_MF_TO_ENEMY ( 1 << 1 ) // local move to enemy -#define bits_MF_TO_COVER ( 1 << 2 ) // local move to a hiding place -#define bits_MF_TO_DETOUR ( 1 << 3 ) // local move to detour point. -#define bits_MF_TO_PATHCORNER ( 1 << 4 ) // local move to a path corner -#define bits_MF_TO_NODE ( 1 << 5 ) // local move to a node -#define bits_MF_TO_LOCATION ( 1 << 6 ) // local move to an arbitrary point -#define bits_MF_IS_GOAL ( 1 << 7 ) // this waypoint is the goal of the whole move. -#define bits_MF_DONT_SIMPLIFY ( 1 << 8 ) // Don't let the route code simplify this waypoint - -// If you define any flags that aren't _TO_ flags, add them here so we can mask -// them off when doing compares. -#define bits_MF_NOT_TO_MASK (bits_MF_IS_GOAL | bits_MF_DONT_SIMPLIFY) - -#define MOVEGOAL_NONE (0) -#define MOVEGOAL_TARGETENT (bits_MF_TO_TARGETENT) -#define MOVEGOAL_ENEMY (bits_MF_TO_ENEMY) -#define MOVEGOAL_PATHCORNER (bits_MF_TO_PATHCORNER) -#define MOVEGOAL_LOCATION (bits_MF_TO_LOCATION) -#define MOVEGOAL_NODE (bits_MF_TO_NODE) - -// these bits represent conditions that may befall the monster, of which some are allowed -// to interrupt certain schedules. -#define bits_COND_NO_AMMO_LOADED ( 1 << 0 ) // weapon needs to be reloaded! -#define bits_COND_SEE_HATE ( 1 << 1 ) // see something that you hate -#define bits_COND_SEE_FEAR ( 1 << 2 ) // see something that you are afraid of -#define bits_COND_SEE_DISLIKE ( 1 << 3 ) // see something that you dislike -#define bits_COND_SEE_ENEMY ( 1 << 4 ) // target entity is in full view. -#define bits_COND_ENEMY_OCCLUDED ( 1 << 5 ) // target entity occluded by the world -#define bits_COND_SMELL_FOOD ( 1 << 6 ) -#define bits_COND_ENEMY_TOOFAR ( 1 << 7 ) -#define bits_COND_LIGHT_DAMAGE ( 1 << 8 ) // hurt a little -#define bits_COND_HEAVY_DAMAGE ( 1 << 9 ) // hurt a lot -#define bits_COND_CAN_RANGE_ATTACK1 ( 1 << 10) -#define bits_COND_CAN_MELEE_ATTACK1 ( 1 << 11) -#define bits_COND_CAN_RANGE_ATTACK2 ( 1 << 12) -#define bits_COND_CAN_MELEE_ATTACK2 ( 1 << 13) -// #define bits_COND_CAN_RANGE_ATTACK3 ( 1 << 14) -#define bits_COND_PROVOKED ( 1 << 15) -#define bits_COND_NEW_ENEMY ( 1 << 16) -#define bits_COND_HEAR_SOUND ( 1 << 17) // there is an interesting sound -#define bits_COND_SMELL ( 1 << 18) // there is an interesting scent -#define bits_COND_ENEMY_FACING_ME ( 1 << 19) // enemy is facing me -#define bits_COND_ENEMY_DEAD ( 1 << 20) // enemy was killed. If you get this in combat, try to find another enemy. If you get it in alert, victory dance. -#define bits_COND_SEE_CLIENT ( 1 << 21) // see a client -#define bits_COND_SEE_NEMESIS ( 1 << 22) // see my nemesis - -#define bits_COND_SPECIAL1 ( 1 << 28) // Defined by individual monster -#define bits_COND_SPECIAL2 ( 1 << 29) // Defined by individual monster - -#define bits_COND_TASK_FAILED ( 1 << 30) -#define bits_COND_SCHEDULE_DONE ( 1 << 31) - - -#define bits_COND_ALL_SPECIAL (bits_COND_SPECIAL1 | bits_COND_SPECIAL2) - -#define bits_COND_CAN_ATTACK (bits_COND_CAN_RANGE_ATTACK1 | bits_COND_CAN_MELEE_ATTACK1 | bits_COND_CAN_RANGE_ATTACK2 | bits_COND_CAN_MELEE_ATTACK2) - -#endif // SCHEDULE_H +/*** +* +* Copyright (c) 1996-2002, 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. +* +* This source code contains proprietary and confidential information of +* Valve LLC and its suppliers. Access to this code is restricted to +* persons who have executed a written SDK license with Valve. Any access, +* use or distribution of this code by or to any unlicensed person is illegal. +* +****/ +//========================================================= +// Scheduling +//========================================================= + +#ifndef SCHEDULE_H +#define SCHEDULE_H + +#define TASKSTATUS_NEW 0 // Just started +#define TASKSTATUS_RUNNING 1 // Running task & movement +#define TASKSTATUS_RUNNING_MOVEMENT 2 // Just running movement +#define TASKSTATUS_RUNNING_TASK 3 // Just running task +#define TASKSTATUS_COMPLETE 4 // Completed, get next task + + +//========================================================= +// These are the schedule types +//========================================================= +typedef enum +{ + SCHED_NONE = 0, + SCHED_IDLE_STAND, + SCHED_IDLE_WALK, + SCHED_WAKE_ANGRY, + SCHED_WAKE_CALLED, + SCHED_ALERT_FACE, + SCHED_ALERT_SMALL_FLINCH, + SCHED_ALERT_BIG_FLINCH, + SCHED_ALERT_STAND, + SCHED_INVESTIGATE_SOUND, + SCHED_COMBAT_FACE, + SCHED_COMBAT_STAND, + SCHED_CHASE_ENEMY, + SCHED_CHASE_ENEMY_FAILED, + SCHED_VICTORY_DANCE, + SCHED_TARGET_FACE, + SCHED_TARGET_CHASE, + SCHED_SMALL_FLINCH, + SCHED_TAKE_COVER_FROM_ENEMY, + SCHED_TAKE_COVER_FROM_BEST_SOUND, + SCHED_TAKE_COVER_FROM_ORIGIN, + SCHED_COWER, // usually a last resort! + SCHED_MELEE_ATTACK1, + SCHED_MELEE_ATTACK2, + SCHED_RANGE_ATTACK1, + SCHED_RANGE_ATTACK2, + SCHED_SPECIAL_ATTACK1, + SCHED_SPECIAL_ATTACK2, + SCHED_STANDOFF, + SCHED_ARM_WEAPON, + SCHED_RELOAD, + SCHED_GUARD, + SCHED_AMBUSH, + SCHED_DIE, + SCHED_WAIT_TRIGGER, + SCHED_FOLLOW, + SCHED_SLEEP, + SCHED_WAKE, + SCHED_BARNACLE_VICTIM_GRAB, + SCHED_BARNACLE_VICTIM_CHOMP, + SCHED_AISCRIPT, + SCHED_FAIL, + + LAST_COMMON_SCHEDULE // Leave this at the bottom +} SCHEDULE_TYPE; + +//========================================================= +// These are the shared tasks +//========================================================= +typedef enum +{ + TASK_INVALID = 0, + TASK_WAIT, + TASK_WAIT_FACE_ENEMY, + TASK_WAIT_PVS, + TASK_SUGGEST_STATE, + TASK_WALK_TO_TARGET, + TASK_RUN_TO_TARGET, + TASK_MOVE_TO_TARGET_RANGE, + TASK_GET_PATH_TO_ENEMY, + TASK_GET_PATH_TO_ENEMY_LKP, + TASK_GET_PATH_TO_ENEMY_CORPSE, + TASK_GET_PATH_TO_LEADER, + TASK_GET_PATH_TO_SPOT, + TASK_GET_PATH_TO_TARGET, + TASK_GET_PATH_TO_HINTNODE, + TASK_GET_PATH_TO_LASTPOSITION, + TASK_GET_PATH_TO_BESTSOUND, + TASK_GET_PATH_TO_BESTSCENT, + TASK_RUN_PATH, + TASK_WALK_PATH, + TASK_STRAFE_PATH, + TASK_CLEAR_MOVE_WAIT, + TASK_STORE_LASTPOSITION, + TASK_CLEAR_LASTPOSITION, + TASK_PLAY_ACTIVE_IDLE, + TASK_FIND_HINTNODE, + TASK_CLEAR_HINTNODE, + TASK_SMALL_FLINCH, + TASK_FACE_IDEAL, + TASK_FACE_ROUTE, + TASK_FACE_ENEMY, + TASK_FACE_HINTNODE, + TASK_FACE_TARGET, + TASK_FACE_LASTPOSITION, + TASK_RANGE_ATTACK1, + TASK_RANGE_ATTACK2, + TASK_MELEE_ATTACK1, + TASK_MELEE_ATTACK2, + TASK_RELOAD, + TASK_RANGE_ATTACK1_NOTURN, + TASK_RANGE_ATTACK2_NOTURN, + TASK_MELEE_ATTACK1_NOTURN, + TASK_MELEE_ATTACK2_NOTURN, + TASK_RELOAD_NOTURN, + TASK_SPECIAL_ATTACK1, + TASK_SPECIAL_ATTACK2, + TASK_CROUCH, + TASK_STAND, + TASK_GUARD, + TASK_STEP_LEFT, + TASK_STEP_RIGHT, + TASK_STEP_FORWARD, + TASK_STEP_BACK, + TASK_DODGE_LEFT, + TASK_DODGE_RIGHT, + TASK_SOUND_ANGRY, + TASK_SOUND_DEATH, + TASK_SET_ACTIVITY, + TASK_SET_SCHEDULE, + TASK_SET_FAIL_SCHEDULE, + TASK_CLEAR_FAIL_SCHEDULE, + TASK_PLAY_SEQUENCE, + TASK_PLAY_SEQUENCE_FACE_ENEMY, + TASK_PLAY_SEQUENCE_FACE_TARGET, + TASK_SOUND_IDLE, + TASK_SOUND_WAKE, + TASK_SOUND_PAIN, + TASK_SOUND_DIE, + TASK_FIND_COVER_FROM_BEST_SOUND,// tries lateral cover first, then node cover + TASK_FIND_COVER_FROM_ENEMY,// tries lateral cover first, then node cover + TASK_FIND_LATERAL_COVER_FROM_ENEMY, + TASK_FIND_NODE_COVER_FROM_ENEMY, + TASK_FIND_NEAR_NODE_COVER_FROM_ENEMY,// data for this one is the MAXIMUM acceptable distance to the cover. + TASK_FIND_FAR_NODE_COVER_FROM_ENEMY,// data for this one is there MINIMUM aceptable distance to the cover. + TASK_FIND_COVER_FROM_ORIGIN, + TASK_EAT, + TASK_DIE, + TASK_WAIT_FOR_SCRIPT, + TASK_PLAY_SCRIPT, + TASK_ENABLE_SCRIPT, + TASK_PLANT_ON_SCRIPT, + TASK_FACE_SCRIPT, + TASK_WAIT_RANDOM, + TASK_WAIT_INDEFINITE, + TASK_STOP_MOVING, + TASK_TURN_LEFT, + TASK_TURN_RIGHT, + TASK_REMEMBER, + TASK_FORGET, + TASK_WAIT_FOR_MOVEMENT, // wait until MovementIsComplete() + LAST_COMMON_TASK, // LEAVE THIS AT THE BOTTOM!! (sjb) +} SHARED_TASKS; + + +// These go in the flData member of the TASK_WALK_TO_TARGET, TASK_RUN_TO_TARGET +enum +{ + TARGET_MOVE_NORMAL = 0, + TARGET_MOVE_SCRIPTED = 1, +}; + + +// A goal should be used for a task that requires several schedules to complete. +// The goal index should indicate which schedule (ordinally) the monster is running. +// That way, when tasks fail, the AI can make decisions based on the context of the +// current goal and sequence rather than just the current schedule. +enum +{ + GOAL_ATTACK_ENEMY, + GOAL_MOVE, + GOAL_TAKE_COVER, + GOAL_MOVE_TARGET, + GOAL_EAT, +}; + +// an array of tasks is a task list +// an array of schedules is a schedule list +struct Task_t +{ + + int iTask; + float flData; +}; + +struct Schedule_t +{ + + Task_t *pTasklist; + int cTasks; + int iInterruptMask;// a bit mask of conditions that can interrupt this schedule + + // a more specific mask that indicates which TYPES of sounds will interrupt the schedule in the + // event that the schedule is broken by COND_HEAR_SOUND + int iSoundMask; + const char *pName; +}; + +// an array of waypoints makes up the monster's route. +// !!!LATER- this declaration doesn't belong in this file. +struct WayPoint_t +{ + Vector vecLocation; + int iType; +}; + +// these MoveFlag values are assigned to a WayPoint's TYPE in order to demonstrate the +// type of movement the monster should use to get there. +#define bits_MF_TO_TARGETENT ( 1 << 0 ) // local move to targetent. +#define bits_MF_TO_ENEMY ( 1 << 1 ) // local move to enemy +#define bits_MF_TO_COVER ( 1 << 2 ) // local move to a hiding place +#define bits_MF_TO_DETOUR ( 1 << 3 ) // local move to detour point. +#define bits_MF_TO_PATHCORNER ( 1 << 4 ) // local move to a path corner +#define bits_MF_TO_NODE ( 1 << 5 ) // local move to a node +#define bits_MF_TO_LOCATION ( 1 << 6 ) // local move to an arbitrary point +#define bits_MF_IS_GOAL ( 1 << 7 ) // this waypoint is the goal of the whole move. +#define bits_MF_DONT_SIMPLIFY ( 1 << 8 ) // Don't let the route code simplify this waypoint + +// If you define any flags that aren't _TO_ flags, add them here so we can mask +// them off when doing compares. +#define bits_MF_NOT_TO_MASK (bits_MF_IS_GOAL | bits_MF_DONT_SIMPLIFY) + +#define MOVEGOAL_NONE (0) +#define MOVEGOAL_TARGETENT (bits_MF_TO_TARGETENT) +#define MOVEGOAL_ENEMY (bits_MF_TO_ENEMY) +#define MOVEGOAL_PATHCORNER (bits_MF_TO_PATHCORNER) +#define MOVEGOAL_LOCATION (bits_MF_TO_LOCATION) +#define MOVEGOAL_NODE (bits_MF_TO_NODE) + +// these bits represent conditions that may befall the monster, of which some are allowed +// to interrupt certain schedules. +#define bits_COND_NO_AMMO_LOADED ( 1 << 0 ) // weapon needs to be reloaded! +#define bits_COND_SEE_HATE ( 1 << 1 ) // see something that you hate +#define bits_COND_SEE_FEAR ( 1 << 2 ) // see something that you are afraid of +#define bits_COND_SEE_DISLIKE ( 1 << 3 ) // see something that you dislike +#define bits_COND_SEE_ENEMY ( 1 << 4 ) // target entity is in full view. +#define bits_COND_ENEMY_OCCLUDED ( 1 << 5 ) // target entity occluded by the world +#define bits_COND_SMELL_FOOD ( 1 << 6 ) +#define bits_COND_ENEMY_TOOFAR ( 1 << 7 ) +#define bits_COND_LIGHT_DAMAGE ( 1 << 8 ) // hurt a little +#define bits_COND_HEAVY_DAMAGE ( 1 << 9 ) // hurt a lot +#define bits_COND_CAN_RANGE_ATTACK1 ( 1 << 10) +#define bits_COND_CAN_MELEE_ATTACK1 ( 1 << 11) +#define bits_COND_CAN_RANGE_ATTACK2 ( 1 << 12) +#define bits_COND_CAN_MELEE_ATTACK2 ( 1 << 13) +// #define bits_COND_CAN_RANGE_ATTACK3 ( 1 << 14) +#define bits_COND_PROVOKED ( 1 << 15) +#define bits_COND_NEW_ENEMY ( 1 << 16) +#define bits_COND_HEAR_SOUND ( 1 << 17) // there is an interesting sound +#define bits_COND_SMELL ( 1 << 18) // there is an interesting scent +#define bits_COND_ENEMY_FACING_ME ( 1 << 19) // enemy is facing me +#define bits_COND_ENEMY_DEAD ( 1 << 20) // enemy was killed. If you get this in combat, try to find another enemy. If you get it in alert, victory dance. +#define bits_COND_SEE_CLIENT ( 1 << 21) // see a client +#define bits_COND_SEE_NEMESIS ( 1 << 22) // see my nemesis + +#define bits_COND_SPECIAL1 ( 1 << 28) // Defined by individual monster +#define bits_COND_SPECIAL2 ( 1 << 29) // Defined by individual monster + +#define bits_COND_TASK_FAILED ( 1 << 30) +#define bits_COND_SCHEDULE_DONE ( 1 << 31) + + +#define bits_COND_ALL_SPECIAL (bits_COND_SPECIAL1 | bits_COND_SPECIAL2) + +#define bits_COND_CAN_ATTACK (bits_COND_CAN_RANGE_ATTACK1 | bits_COND_CAN_MELEE_ATTACK1 | bits_COND_CAN_RANGE_ATTACK2 | bits_COND_CAN_MELEE_ATTACK2) + +#endif // SCHEDULE_H diff --git a/spirit/scientist.cpp b/dlls/scientist.cpp similarity index 90% rename from spirit/scientist.cpp rename to dlls/scientist.cpp index f4b98336..ab37d9cd 100644 --- a/spirit/scientist.cpp +++ b/dlls/scientist.cpp @@ -428,7 +428,7 @@ void CScientist::DeclineFollowing( void ) { Talk( 10 ); m_hTalkTarget = m_hEnemy; - PlaySentence( m_szGrp[TLK_DECLINE], 2, VOL_NORM, ATTN_NORM ); //LRC + PlaySentence( "SC_POK", 2, VOL_NORM, ATTN_NORM ); } @@ -588,7 +588,7 @@ void CScientist :: RunTask( Task_t *pTask ) //========================================================= int CScientist :: Classify ( void ) { - return m_iClass?m_iClass:CLASS_HUMAN_PASSIVE; + return CLASS_HUMAN_PASSIVE; } @@ -658,17 +658,13 @@ void CScientist :: Spawn( void ) { Precache( ); - if (pev->model) - SET_MODEL(ENT(pev), STRING(pev->model)); //LRC - else - SET_MODEL(ENT(pev), "models/scientist.mdl"); + SET_MODEL(ENT(pev), "models/scientist.mdl"); UTIL_SetSize(pev, VEC_HUMAN_HULL_MIN, VEC_HUMAN_HULL_MAX); pev->solid = SOLID_SLIDEBOX; pev->movetype = MOVETYPE_STEP; m_bloodColor = BLOOD_COLOR_RED; - if (pev->health == 0) - pev->health = gSkillData.scientistHealth; + pev->health = gSkillData.scientistHealth; pev->view_ofs = Vector ( 0, 0, 50 );// position of the eyes relative to monster's origin. m_flFieldOfView = VIEW_FIELD_WIDE; // NOTE: we need a wide field of view so scientists will notice player and say hello m_MonsterState = MONSTERSTATE_NONE; @@ -690,7 +686,7 @@ void CScientist :: Spawn( void ) pev->skin = 1; MonsterInit(); - SetUse(&CScientist :: FollowerUse ); + SetUse( FollowerUse ); } //========================================================= @@ -698,10 +694,7 @@ void CScientist :: Spawn( void ) //========================================================= void CScientist :: Precache( void ) { - if (pev->model) - PRECACHE_MODEL((char*)STRING(pev->model)); //LRC - else - PRECACHE_MODEL("models/scientist.mdl"); + PRECACHE_MODEL("models/scientist.mdl"); PRECACHE_SOUND("scientist/sci_pain1.wav"); PRECACHE_SOUND("scientist/sci_pain2.wav"); PRECACHE_SOUND("scientist/sci_pain3.wav"); @@ -729,40 +722,27 @@ void CScientist :: TalkInit() // scientists speach group names (group names are in sentences.txt) - if (!m_iszSpeakAs) - { - m_szGrp[TLK_ANSWER] = "SC_ANSWER"; - m_szGrp[TLK_QUESTION] = "SC_QUESTION"; - m_szGrp[TLK_IDLE] = "SC_IDLE"; - m_szGrp[TLK_STARE] = "SC_STARE"; - if (pev->spawnflags & SF_MONSTER_PREDISASTER) - m_szGrp[TLK_USE] = "SC_PFOLLOW"; - else - m_szGrp[TLK_USE] = "SC_OK"; - if (pev->spawnflags & SF_MONSTER_PREDISASTER) - m_szGrp[TLK_UNUSE] = "SC_PWAIT"; - else - m_szGrp[TLK_UNUSE] = "SC_WAIT"; - if (pev->spawnflags & SF_MONSTER_PREDISASTER) - m_szGrp[TLK_DECLINE] = "SC_POK"; - else - m_szGrp[TLK_DECLINE] = "SC_NOTOK"; - m_szGrp[TLK_STOP] = "SC_STOP"; - m_szGrp[TLK_NOSHOOT] = "SC_SCARED"; - m_szGrp[TLK_HELLO] = "SC_HELLO"; + m_szGrp[TLK_ANSWER] = "SC_ANSWER"; + m_szGrp[TLK_QUESTION] = "SC_QUESTION"; + m_szGrp[TLK_IDLE] = "SC_IDLE"; + m_szGrp[TLK_STARE] = "SC_STARE"; + m_szGrp[TLK_USE] = "SC_OK"; + m_szGrp[TLK_UNUSE] = "SC_WAIT"; + m_szGrp[TLK_STOP] = "SC_STOP"; + m_szGrp[TLK_NOSHOOT] = "SC_SCARED"; + m_szGrp[TLK_HELLO] = "SC_HELLO"; - m_szGrp[TLK_PLHURT1] = "!SC_CUREA"; - m_szGrp[TLK_PLHURT2] = "!SC_CUREB"; - m_szGrp[TLK_PLHURT3] = "!SC_CUREC"; + m_szGrp[TLK_PLHURT1] = "!SC_CUREA"; + m_szGrp[TLK_PLHURT2] = "!SC_CUREB"; + m_szGrp[TLK_PLHURT3] = "!SC_CUREC"; - m_szGrp[TLK_PHELLO] = "SC_PHELLO"; - m_szGrp[TLK_PIDLE] = "SC_PIDLE"; - m_szGrp[TLK_PQUESTION] = "SC_PQUEST"; - m_szGrp[TLK_SMELL] = "SC_SMELL"; + m_szGrp[TLK_PHELLO] = "SC_PHELLO"; + m_szGrp[TLK_PIDLE] = "SC_PIDLE"; + m_szGrp[TLK_PQUESTION] = "SC_PQUEST"; + m_szGrp[TLK_SMELL] = "SC_SMELL"; - m_szGrp[TLK_WOUND] = "SC_WOUND"; - m_szGrp[TLK_MORTAL] = "SC_MORTAL"; - } + m_szGrp[TLK_WOUND] = "SC_WOUND"; + m_szGrp[TLK_MORTAL] = "SC_MORTAL"; // get voice for head switch (pev->body % 3) @@ -1170,7 +1150,7 @@ void CDeadScientist :: Spawn( ) pev->sequence = LookupSequence( m_szPoses[m_iPose] ); if (pev->sequence == -1) { - ALERT ( at_debug, "Dead scientist with bad pose\n" ); + ALERT ( at_console, "Dead scientist with bad pose\n" ); } // pev->skin += 2; // use bloody skin -- UNDONE: Turn this back on when we have a bloody skin again! @@ -1224,21 +1204,13 @@ SITTING_ANIM_sitting3 } SITTING_ANIM; -#define SF_SITTINGSCI_POSTDISASTER 1024 - // // ********** Scientist SPAWN ********** // void CSittingScientist :: Spawn( ) { - if (pev->model) - PRECACHE_MODEL((char*)STRING(pev->model)); //LRC - else - PRECACHE_MODEL("models/scientist.mdl"); - if (pev->model) - SET_MODEL(ENT(pev), STRING(pev->model)); //LRC - else - SET_MODEL(ENT(pev), "models/scientist.mdl"); + PRECACHE_MODEL("models/scientist.mdl"); + SET_MODEL(ENT(pev), "models/scientist.mdl"); Precache(); InitBoneControllers(); @@ -1254,8 +1226,7 @@ void CSittingScientist :: Spawn( ) m_afCapability = bits_CAP_HEAR | bits_CAP_TURN_HEAD; - if (!FBitSet(pev->spawnflags, SF_SITTINGSCI_POSTDISASTER)) //LRC- allow a sitter to be postdisaster. - SetBits(pev->spawnflags, SF_MONSTER_PREDISASTER); // predisaster only! + SetBits(pev->spawnflags, SF_MONSTER_PREDISASTER); // predisaster only! if ( pev->body == -1 ) {// -1 chooses a random head @@ -1269,8 +1240,8 @@ void CSittingScientist :: Spawn( ) pev->sequence = m_baseSequence + RANDOM_LONG(0,4); ResetSequenceInfo( ); - SetThink(&CSittingScientist ::SittingThink); - SetNextThink( 0.1 ); + SetThink (SittingThink); + pev->nextthink = gpGlobals->time + 0.1; DROP_TO_FLOOR ( ENT(pev) ); } @@ -1286,7 +1257,7 @@ void CSittingScientist :: Precache( void ) //========================================================= int CSittingScientist :: Classify ( void ) { - return m_iClass?m_iClass:CLASS_HUMAN_PASSIVE; + return CLASS_HUMAN_PASSIVE; } @@ -1397,7 +1368,7 @@ void CSittingScientist :: SittingThink( void ) pev->frame = 0; SetBoneController( 0, m_headTurn ); } - SetNextThink( 0.1 ); + pev->nextthink = gpGlobals->time + 0.1; } // prepare sitting scientist to answer a question diff --git a/spirit/scripted.cpp b/dlls/scripted.cpp similarity index 63% rename from spirit/scripted.cpp rename to dlls/scripted.cpp index c39f4dec..9661f788 100644 --- a/spirit/scripted.cpp +++ b/dlls/scripted.cpp @@ -1,6 +1,6 @@ /*** * -* Copyright (c) 1999, 2000 Valve LLC. All rights reserved. +* Copyright (c) 1996-2002, Valve LLC. All rights reserved. * * This product contains software technology licensed from Id * Software, Inc. ("Id Technology"). Id Technology (c) 1996 Id Software, Inc. @@ -23,7 +23,6 @@ #include "util.h" #include "cbase.h" #include "monsters.h" -#include "player.h" #ifndef ANIMATION_H #include "animation.h" @@ -36,7 +35,6 @@ #include "schedule.h" #include "scripted.h" #include "defaultai.h" -#include "movewith.h" @@ -74,67 +72,26 @@ void CCineMonster :: KeyValue( KeyValueData *pkvd ) m_iszEntity = ALLOC_STRING( pkvd->szValue ); pkvd->fHandled = TRUE; } - else if (FStrEq(pkvd->szKeyName, "m_iszAttack")) - { - m_iszAttack = ALLOC_STRING( pkvd->szValue ); - pkvd->fHandled = TRUE; - } - else if (FStrEq(pkvd->szKeyName, "m_iszMoveTarget")) - { - m_iszMoveTarget = ALLOC_STRING( pkvd->szValue ); - pkvd->fHandled = TRUE; - } - else if (FStrEq(pkvd->szKeyName, "m_iszFireOnBegin")) - { - m_iszFireOnBegin = ALLOC_STRING( pkvd->szValue ); - pkvd->fHandled = TRUE; - } else if (FStrEq(pkvd->szKeyName, "m_fMoveTo")) { m_fMoveTo = atoi( pkvd->szValue ); pkvd->fHandled = TRUE; } - else if (FStrEq(pkvd->szKeyName, "m_fTurnType")) + else if (FStrEq(pkvd->szKeyName, "m_flRepeat")) { - m_fTurnType = atoi( pkvd->szValue ); + m_flRepeat = atof( pkvd->szValue ); pkvd->fHandled = TRUE; } - else if (FStrEq(pkvd->szKeyName, "m_fAction")) - { - m_fAction = atoi( pkvd->szValue ); - pkvd->fHandled = TRUE; - } -// LRC else if (FStrEq(pkvd->szKeyName, "m_flRepeat")) -// { -// m_flRepeat = atof( pkvd->szValue ); -// pkvd->fHandled = TRUE; -// } else if (FStrEq(pkvd->szKeyName, "m_flRadius")) { m_flRadius = atof( pkvd->szValue ); pkvd->fHandled = TRUE; } - else if (FStrEq(pkvd->szKeyName, "m_iRepeats")) - { - m_iRepeats = atoi( pkvd->szValue ); - m_iRepeatsLeft = m_iRepeats; - pkvd->fHandled = TRUE; - } - else if (FStrEq(pkvd->szKeyName, "m_fRepeatFrame")) - { - m_fRepeatFrame = atof( pkvd->szValue ); - pkvd->fHandled = TRUE; - } else if (FStrEq(pkvd->szKeyName, "m_iFinishSchedule")) { m_iFinishSchedule = atoi( pkvd->szValue ); pkvd->fHandled = TRUE; } - else if (FStrEq(pkvd->szKeyName, "m_iPriority")) - { - m_iPriority = atoi( pkvd->szValue ); - pkvd->fHandled = TRUE; - } else { CBaseMonster::KeyValue( pkvd ); @@ -143,17 +100,11 @@ void CCineMonster :: KeyValue( KeyValueData *pkvd ) TYPEDESCRIPTION CCineMonster::m_SaveData[] = { - DEFINE_FIELD( CCineMonster, m_iState, FIELD_INTEGER ), //LRC DEFINE_FIELD( CCineMonster, m_iszIdle, FIELD_STRING ), DEFINE_FIELD( CCineMonster, m_iszPlay, FIELD_STRING ), DEFINE_FIELD( CCineMonster, m_iszEntity, FIELD_STRING ), - DEFINE_FIELD( CCineMonster, m_iszAttack, FIELD_STRING ), //LRC - DEFINE_FIELD( CCineMonster, m_iszMoveTarget, FIELD_STRING ), //LRC - DEFINE_FIELD( CCineMonster, m_iszFireOnBegin, FIELD_STRING ), DEFINE_FIELD( CCineMonster, m_fMoveTo, FIELD_INTEGER ), - DEFINE_FIELD( CCineMonster, m_fTurnType, FIELD_INTEGER ), - DEFINE_FIELD( CCineMonster, m_fAction, FIELD_INTEGER ), -//LRC- this is unused DEFINE_FIELD( CCineMonster, m_flRepeat, FIELD_FLOAT ), + DEFINE_FIELD( CCineMonster, m_flRepeat, FIELD_FLOAT ), DEFINE_FIELD( CCineMonster, m_flRadius, FIELD_FLOAT ), DEFINE_FIELD( CCineMonster, m_iDelay, FIELD_INTEGER ), @@ -164,56 +115,66 @@ TYPEDESCRIPTION CCineMonster::m_SaveData[] = DEFINE_FIELD( CCineMonster, m_saved_effects, FIELD_INTEGER ), DEFINE_FIELD( CCineMonster, m_iFinishSchedule, FIELD_INTEGER ), DEFINE_FIELD( CCineMonster, m_interruptable, FIELD_BOOLEAN ), - - //LRC - DEFINE_FIELD( CCineMonster, m_iRepeats, FIELD_INTEGER ), - DEFINE_FIELD( CCineMonster, m_iRepeatsLeft, FIELD_INTEGER ), - DEFINE_FIELD( CCineMonster, m_fRepeatFrame, FIELD_FLOAT ), - DEFINE_FIELD( CCineMonster, m_iPriority, FIELD_INTEGER ), }; IMPLEMENT_SAVERESTORE( CCineMonster, CBaseMonster ); LINK_ENTITY_TO_CLASS( scripted_sequence, CCineMonster ); -LINK_ENTITY_TO_CLASS( scripted_action, CCineMonster ); +#define CLASSNAME "scripted_sequence" + +LINK_ENTITY_TO_CLASS( aiscripted_sequence, CCineAI ); -LINK_ENTITY_TO_CLASS( aiscripted_sequence, CCineMonster ); //LRC - aiscripted sequences don't need to be seperate -//========================================================= -// FCanOverrideState - returns FALSE, scripted sequences -// cannot possess entities regardless of state. -//========================================================= void CCineMonster :: Spawn( void ) { // pev->solid = SOLID_TRIGGER; // UTIL_SetSize(pev, Vector(-8, -8, -8), Vector(8, 8, 8)); pev->solid = SOLID_NOT; - m_iState = STATE_OFF; //LRC - if ( FStringNull(m_iszIdle) && FStringNull(pev->targetname) ) // if no targetname, start now + // REMOVE: The old side-effect +#if 0 + if ( m_iszIdle ) + m_fMoveTo = 4; +#endif + + // if no targetname, start now + if ( FStringNull(pev->targetname) || !FStringNull( m_iszIdle ) ) { - SetThink(&CCineMonster :: CineThink ); - SetNextThink( 1.0 ); - } - else if ( m_iszIdle ) - { - SetThink(&CCineMonster :: InitIdleThink ); - SetNextThink( 1.0 ); + SetThink( CineThink ); + pev->nextthink = gpGlobals->time + 1.0; + // Wait to be used? + if ( pev->targetname ) + m_startTime = gpGlobals->time + 1E6; } if ( pev->spawnflags & SF_SCRIPT_NOINTERRUPT ) m_interruptable = FALSE; else m_interruptable = TRUE; - - //LRC - the only difference between AI and normal sequences - if ( FClassnameIs(pev, "aiscripted_sequence") || pev->spawnflags & SF_SCRIPT_OVERRIDESTATE ) - { - m_iPriority |= SS_INTERRUPT_ANYSTATE; - } } +//========================================================= +// FCanOverrideState - returns FALSE, scripted sequences +// cannot possess entities regardless of state. +//========================================================= +BOOL CCineMonster :: FCanOverrideState( void ) +{ + if ( pev->spawnflags & SF_SCRIPT_OVERRIDESTATE ) + return TRUE; + return FALSE; +} + +//========================================================= +// FCanOverrideState - returns true because scripted AI can +// possess entities regardless of their state. +//========================================================= +BOOL CCineAI :: FCanOverrideState( void ) +{ + return TRUE; +} + + // // CineStart // @@ -228,21 +189,17 @@ void CCineMonster :: Use( CBaseEntity *pActivator, CBaseEntity *pCaller, USE_TYP if ( pTarget ) { -// ALERT(at_console, "Sequence \"%s\" triggered, already has a target.\n", STRING(pev->targetname)); // am I already playing the script? if ( pTarget->m_scriptState == SCRIPT_PLAYING ) return; - m_startTime = gpGlobals->time + 0.05; //why the delay? -- LRC + m_startTime = gpGlobals->time + 0.05; } else { -// ALERT(at_console, "Sequence \"%s\" triggered, can't find target; searching\n", STRING(pev->targetname)); - m_hActivator = pActivator; // if not, try finding them - SetThink(&CCineMonster :: CineThink ); -// SetNextThink( 0 ); - CineThink(); //LRC + SetThink( CineThink ); + pev->nextthink = gpGlobals->time; } } @@ -311,51 +268,53 @@ void CCineMonster :: Pain( void ) // ********** Cinematic Think ********** // -//LRC: now redefined... find a viable entity with the given name, and return it (or NULL if not found). -CBaseMonster* CCineMonster :: FindEntity( const char* sName, CBaseEntity *pActivator ) +// find a viable entity +int CCineMonster :: FindEntity( void ) { - CBaseEntity *pEntity; + edict_t *pentTarget; - pEntity = UTIL_FindEntityByTargetname(NULL, sName, pActivator); - //m_hTargetEnt = NULL; - CBaseMonster *pMonster = NULL; - int numIterations = 0; + pentTarget = FIND_ENTITY_BY_TARGETNAME(NULL, STRING(m_iszEntity)); + m_hTargetEnt = NULL; + CBaseMonster *pTarget = NULL; - while (pEntity) + while (!FNullEnt(pentTarget)) { - if ( FBitSet( pEntity->pev->flags, FL_MONSTER )) + if ( FBitSet( VARS(pentTarget)->flags, FL_MONSTER )) { - pMonster = pEntity->MyMonsterPointer( ); - if ( pMonster && pMonster->CanPlaySequence( m_iPriority | SS_INTERRUPT_ALERT ) ) + pTarget = GetMonsterPointer( pentTarget ); + if ( pTarget && pTarget->CanPlaySequence( FCanOverrideState(), SS_INTERRUPT_BY_NAME ) ) { - return pMonster; + m_hTargetEnt = pTarget; + return TRUE; } - ALERT( at_debug, "Found %s, but can't play!\n", sName ); + ALERT( at_console, "Found %s, but can't play!\n", STRING(m_iszEntity) ); } - pEntity = UTIL_FindEntityByTargetname(pEntity, sName, pActivator); - pMonster = NULL; + pentTarget = FIND_ENTITY_BY_TARGETNAME(pentTarget, STRING(m_iszEntity)); + pTarget = NULL; } - // couldn't find something with the given targetname; assume it's a classname instead. - if ( !pMonster ) + if ( !pTarget ) { - pEntity = NULL; + CBaseEntity *pEntity = NULL; while ((pEntity = UTIL_FindEntityInSphere( pEntity, pev->origin, m_flRadius )) != NULL) { - if (FClassnameIs( pEntity->pev, sName)) + if (FClassnameIs( pEntity->pev, STRING(m_iszEntity))) { if ( FBitSet( pEntity->pev->flags, FL_MONSTER )) { - pMonster = pEntity->MyMonsterPointer( ); - if ( pMonster && pMonster->CanPlaySequence( m_iPriority ) ) + pTarget = pEntity->MyMonsterPointer( ); + if ( pTarget && pTarget->CanPlaySequence( FCanOverrideState(), SS_INTERRUPT_IDLE ) ) { - return pMonster; + m_hTargetEnt = pTarget; + return TRUE; } } } } } - return NULL; + pTarget = NULL; + m_hTargetEnt = NULL; + return FALSE; } // make the entity enter a scripted sequence @@ -366,126 +325,166 @@ void CCineMonster :: PossessEntity( void ) if ( pEntity ) pTarget = pEntity->MyMonsterPointer(); -// ALERT( at_console, "Possess: pEntity %s, pTarget %s\n", STRING(pEntity->pev->targetname), STRING(pTarget->pev->targetname)); - if ( pTarget ) { - if (pTarget->m_pCine) - { - pTarget->m_pCine->CancelScript(); - } + // FindEntity() just checked this! +#if 0 + if ( !pTarget->CanPlaySequence( FCanOverrideState() ) ) + { + ALERT( at_aiconsole, "Can't possess entity %s\n", STRING(pTarget->pev->classname) ); + return; + } +#endif + + pTarget->m_pGoalEnt = this; pTarget->m_pCine = this; - if (m_iszAttack) - { - // anything with that name? - pTarget->m_hTargetEnt = UTIL_FindEntityByTargetname(NULL, STRING(m_iszAttack), m_hActivator); - if ( pTarget->m_hTargetEnt == NULL ) - { // nothing. Anything with that classname? - while ((pTarget->m_hTargetEnt = UTIL_FindEntityInSphere( pTarget->m_hTargetEnt, pev->origin, m_flRadius )) != NULL) - { - if (FClassnameIs( pTarget->m_hTargetEnt->pev, STRING(m_iszAttack))) break; - } - } - if (pTarget->m_hTargetEnt == NULL) - { // nothing. Oh well. - ALERT(at_debug,"%s %s has a missing \"turn target\": %s\n",STRING(pev->classname),STRING(pev->targetname),STRING(m_iszAttack)); - pTarget->m_hTargetEnt = this; - } - } - else - { - pTarget->m_hTargetEnt = this; - } - - if (m_iszMoveTarget) - { - // anything with that name? - pTarget->m_pGoalEnt = UTIL_FindEntityByTargetname(NULL, STRING(m_iszMoveTarget), m_hActivator); - if (pTarget->m_pGoalEnt == NULL) - { // nothing. Oh well. - ALERT(at_debug,"%s %s has a missing \"move target\": %s\n",STRING(pev->classname),STRING(pev->targetname),STRING(m_iszMoveTarget)); - pTarget->m_pGoalEnt = this; - } - } - else - { - pTarget->m_pGoalEnt = this; - } -// if (IsAction()) -// pTarget->PushEnemy(this,pev->origin); + pTarget->m_hTargetEnt = this; m_saved_movetype = pTarget->pev->movetype; m_saved_solid = pTarget->pev->solid; m_saved_effects = pTarget->pev->effects; pTarget->pev->effects |= pev->effects; -// ALERT(at_console, "script. IsAction = %d",IsAction()); - - m_iState = STATE_ON; // LRC: assume we'll set it to 'on', unless proven otherwise... switch (m_fMoveTo) { - case 1: - case 2: - DelayStart( 1 ); - m_iState = STATE_TURN_ON; - // fall through... case 0: - case 4: - case 5: - case 6: pTarget->m_scriptState = SCRIPT_WAIT; break; + + case 1: + pTarget->m_scriptState = SCRIPT_WALK_TO_MARK; + DelayStart( 1 ); + break; + + case 2: + pTarget->m_scriptState = SCRIPT_RUN_TO_MARK; + DelayStart( 1 ); + break; + + case 4: + UTIL_SetOrigin( pTarget->pev, pev->origin ); + pTarget->pev->ideal_yaw = pev->angles.y; + pTarget->pev->avelocity = Vector( 0, 0, 0 ); + pTarget->pev->velocity = Vector( 0, 0, 0 ); + pTarget->pev->effects |= EF_NOINTERP; + pTarget->pev->angles.y = pev->angles.y; + pTarget->m_scriptState = SCRIPT_WAIT; + m_startTime = gpGlobals->time + 1E6; + // UNDONE: Add a flag to do this so people can fixup physics after teleporting monsters + // pTarget->pev->flags &= ~FL_ONGROUND; + break; } // ALERT( at_aiconsole, "\"%s\" found and used (INT: %s)\n", STRING( pTarget->pev->targetname ), FBitSet(pev->spawnflags, SF_SCRIPT_NOINTERRUPT)?"No":"Yes" ); pTarget->m_IdealMonsterState = MONSTERSTATE_SCRIPT; -// if (m_iszIdle) -// { -// ALERT(at_console, "Possess: Play idle sequence\n"); -// StartSequence( pTarget, m_iszIdle, FALSE ); -// if (FStrEq( STRING(m_iszIdle), STRING(m_iszPlay))) -// { -// pTarget->pev->framerate = 0; -// } -// } -// ALERT(at_console, "Finished PossessEntity, ms %d, ims %d\n", pTarget->m_MonsterState, pTarget->m_IdealMonsterState); + if (m_iszIdle) + { + StartSequence( pTarget, m_iszIdle, FALSE ); + if (FStrEq( STRING(m_iszIdle), STRING(m_iszPlay))) + { + pTarget->pev->framerate = 0; + } + } } - } -// at the beginning of the level, set up the idle animation. --LRC -void CCineMonster :: InitIdleThink( void ) +// make the entity carry out the scripted sequence instructions, but without +// destroying the monster's state. +void CCineAI :: PossessEntity( void ) { - if ((m_hTargetEnt = FindEntity(STRING(m_iszEntity), NULL)) != NULL) + Schedule_t *pNewSchedule; + + CBaseEntity *pEntity = m_hTargetEnt; + CBaseMonster *pTarget = NULL; + if ( pEntity ) + pTarget = pEntity->MyMonsterPointer(); + + if ( pTarget ) { - PossessEntity( ); - m_startTime = gpGlobals->time + 1E6; - ALERT( at_aiconsole, "script \"%s\" using monster \"%s\"\n", STRING( pev->targetname ), STRING( m_iszEntity ) ); - } - else - { - CancelScript( ); - ALERT( at_aiconsole, "script \"%s\" can't find monster \"%s\"\n", STRING( pev->targetname ), STRING( m_iszEntity ) ); - SetNextThink( 1.0 ); + if ( !pTarget->CanPlaySequence( FCanOverrideState(), SS_INTERRUPT_AI ) ) + { + ALERT( at_aiconsole, "(AI)Can't possess entity %s\n", STRING(pTarget->pev->classname) ); + return; + } + + pTarget->m_pGoalEnt = this; + pTarget->m_pCine = this; + pTarget->m_hTargetEnt = this; + + m_saved_movetype = pTarget->pev->movetype; + m_saved_solid = pTarget->pev->solid; + m_saved_effects = pTarget->pev->effects; + pTarget->pev->effects |= pev->effects; + + switch (m_fMoveTo) + { + case 0: + case 5: + pTarget->m_scriptState = SCRIPT_WAIT; + break; + + case 1: + pTarget->m_scriptState = SCRIPT_WALK_TO_MARK; + break; + + case 2: + pTarget->m_scriptState = SCRIPT_RUN_TO_MARK; + break; + + case 4: + // zap the monster instantly to the site of the script entity. + UTIL_SetOrigin( pTarget->pev, pev->origin ); + pTarget->pev->ideal_yaw = pev->angles.y; + pTarget->pev->avelocity = Vector( 0, 0, 0 ); + pTarget->pev->velocity = Vector( 0, 0, 0 ); + pTarget->pev->effects |= EF_NOINTERP; + pTarget->pev->angles.y = pev->angles.y; + pTarget->m_scriptState = SCRIPT_WAIT; + m_startTime = gpGlobals->time + 1E6; + // UNDONE: Add a flag to do this so people can fixup physics after teleporting monsters + pTarget->pev->flags &= ~FL_ONGROUND; + break; + default: + ALERT ( at_aiconsole, "aiscript: invalid Move To Position value!" ); + break; + } + + ALERT( at_aiconsole, "\"%s\" found and used\n", STRING( pTarget->pev->targetname ) ); + + pTarget->m_IdealMonsterState = MONSTERSTATE_SCRIPT; + +/* + if (m_iszIdle) + { + StartSequence( pTarget, m_iszIdle, FALSE ); + if (FStrEq( STRING(m_iszIdle), STRING(m_iszPlay))) + { + pTarget->pev->framerate = 0; + } + } +*/ + // Already in a scripted state? + if ( pTarget->m_MonsterState == MONSTERSTATE_SCRIPT ) + { + pNewSchedule = pTarget->GetScheduleOfType( SCHED_AISCRIPT ); + pTarget->ChangeSchedule( pNewSchedule ); + } } } void CCineMonster :: CineThink( void ) { -// ALERT(at_console, "Sequence think, activator %s\n", STRING(m_hActivator->pev->targetname)); - if ((m_hTargetEnt = FindEntity(STRING(m_iszEntity),m_hActivator)) != NULL) + if (FindEntity()) { -// ALERT(at_console, "Sequence found %s \"%s\"\n", STRING(m_hTargetEnt->pev->classname), STRING(m_hTargetEnt->pev->targetname)); PossessEntity( ); ALERT( at_aiconsole, "script \"%s\" using monster \"%s\"\n", STRING( pev->targetname ), STRING( m_iszEntity ) ); } else { -// ALERT(at_console, "Sequence found nothing called %s\n", STRING(m_iszEntity)); CancelScript( ); ALERT( at_aiconsole, "script \"%s\" can't find monster \"%s\"\n", STRING( pev->targetname ), STRING( m_iszEntity ) ); - SetNextThink( 1.0 ); + pev->nextthink = gpGlobals->time + 1.0; } } @@ -493,8 +492,6 @@ void CCineMonster :: CineThink( void ) // lookup a sequence name and setup the target monster to play it BOOL CCineMonster :: StartSequence( CBaseMonster *pTarget, int iszSeq, BOOL completeOnEmpty ) { -// ALERT( at_console, "StartSequence %s \"%s\"\n", STRING(pev->classname), STRING(pev->targetname)); - if ( !iszSeq && completeOnEmpty ) { SequenceDone( pTarget ); @@ -516,7 +513,7 @@ BOOL CCineMonster :: StartSequence( CBaseMonster *pTarget, int iszSeq, BOOL comp else s = "Yes"; - ALERT( at_debug, "%s (%s): started \"%s\":INT:%s\n", STRING( pTarget->pev->targetname ), STRING( pTarget->pev->classname ), STRING( iszSeq), s ); + ALERT( at_console, "%s (%s): started \"%s\":INT:%s\n", STRING( pTarget->pev->targetname ), STRING( pTarget->pev->classname ), STRING( iszSeq), s ); #endif pTarget->pev->frame = 0; @@ -524,6 +521,36 @@ BOOL CCineMonster :: StartSequence( CBaseMonster *pTarget, int iszSeq, BOOL comp return TRUE; } +// lookup a sequence name and setup the target monster to play it +// overridden for CCineAI because it's ok for them to not have an animation sequence +// for the monster to play. For a regular Scripted Sequence, that situation is an error. +BOOL CCineAI :: StartSequence( CBaseMonster *pTarget, int iszSeq, BOOL completeOnEmpty ) +{ + if ( iszSeq == 0 && completeOnEmpty ) + { + // no sequence was provided. Just let the monster proceed, however, we still have to fire any Sequence target + // and remove any non-repeatable CineAI entities here ( because there is code elsewhere that handles those tasks, but + // not until the animation sequence is finished. We have to manually take care of these things where there is no sequence. + + SequenceDone ( pTarget ); + + return TRUE; + } + + pTarget->pev->sequence = pTarget->LookupSequence( STRING( iszSeq ) ); + + if (pTarget->pev->sequence == -1) + { + ALERT( at_error, "%s: unknown aiscripted sequence \"%s\"\n", STRING( pTarget->pev->targetname ), STRING( iszSeq) ); + pTarget->pev->sequence = 0; + // return FALSE; + } + + pTarget->pev->frame = 0; + pTarget->ResetSequenceInfo( ); + return TRUE; +} + //========================================================= // SequenceDone - called when a scripted sequence animation // sequence is done playing ( or when an AI Scripted Sequence @@ -533,14 +560,12 @@ BOOL CCineMonster :: StartSequence( CBaseMonster *pTarget, int iszSeq, BOOL comp //========================================================= void CCineMonster :: SequenceDone ( CBaseMonster *pMonster ) { - m_iRepeatsLeft = m_iRepeats; //LRC - reset the repeater count - m_iState = STATE_OFF; // we've finished. -// ALERT( at_console, "Sequence %s finished\n", STRING(pev->targetname));//STRING( m_pCine->m_iszPlay ) ); + //ALERT( at_aiconsole, "Sequence %s finished\n", STRING( m_pCine->m_iszPlay ) ); if ( !( pev->spawnflags & SF_SCRIPT_REPEATABLE ) ) { - SetThink(&CCineMonster :: SUB_Remove ); - SetNextThink( 0.1 ); + SetThink( SUB_Remove ); + pev->nextthink = gpGlobals->time + 0.1; } // This is done so that another sequence can take over the monster when triggered by the first @@ -561,15 +586,29 @@ void CCineMonster :: SequenceDone ( CBaseMonster *pMonster ) // // Scripted sequences just dirty the Schedule and drop the // monster in Idle State. -// -// or select a specific AMBUSH schedule, regardless of state. //LRC //========================================================= void CCineMonster :: FixScriptMonsterSchedule( CBaseMonster *pMonster ) { if ( pMonster->m_IdealMonsterState != MONSTERSTATE_DEAD ) pMonster->m_IdealMonsterState = MONSTERSTATE_IDLE; -// pMonster->ClearSchedule(); + pMonster->ClearSchedule(); +} +//========================================================= +// When a monster finishes a scripted sequence, we have to +// fix up its state and schedule for it to return to a +// normal AI monster. +// +// AI Scripted sequences will, depending on what the level +// designer selects: +// +// -Dirty the monster's schedule and drop out of the +// sequence in their current state. +// +// -Select a specific AMBUSH schedule, regardless of state. +//========================================================= +void CCineAI :: FixScriptMonsterSchedule( CBaseMonster *pMonster ) +{ switch ( m_iFinishSchedule ) { case SCRIPT_FINISHSCHED_DEFAULT: @@ -630,9 +669,6 @@ int CCineMonster::IgnoreConditions( void ) { if ( CanInterrupt() ) return 0; - - // Big fat BUG: This is an IgnoreConditions function - we need to return the conditions - // that _shouldn't_ be able to break the script, instead of the conditions that _should_!! return SCRIPT_BREAK_CONDITIONS; } @@ -640,7 +676,7 @@ int CCineMonster::IgnoreConditions( void ) void ScriptEntityCancel( edict_t *pentCine ) { // make sure they are a scripted_sequence - if (FClassnameIs( pentCine, "scripted_sequence" ) || FClassnameIs( pentCine, "scripted_action" )) + if (FClassnameIs( pentCine, CLASSNAME )) { CCineMonster *pCineTarget = GetClassPtr((CCineMonster *)VARS(pentCine)); // make sure they have a monster in mind for the script @@ -648,8 +684,6 @@ void ScriptEntityCancel( edict_t *pentCine ) CBaseMonster *pTarget = NULL; if ( pEntity ) pTarget = pEntity->MyMonsterPointer(); - - pCineTarget->m_iState = STATE_OFF; if (pTarget) { @@ -660,8 +694,6 @@ void ScriptEntityCancel( edict_t *pentCine ) pTarget->m_scriptState = CCineMonster::SCRIPT_CLEANUP; // do it now pTarget->CineCleanup( ); - //LRC - clean up so that if another script is starting immediately, the monster will notice it. - pTarget->ClearSchedule( ); } } } @@ -679,44 +711,38 @@ void CCineMonster :: CancelScript( void ) return; } - CBaseEntity *pCineTarget = UTIL_FindEntityByTargetname(NULL, STRING(pev->targetname)); + edict_t *pentCineTarget = FIND_ENTITY_BY_TARGETNAME(NULL, STRING(pev->targetname)); - while (pCineTarget) + while (!FNullEnt(pentCineTarget)) { - ScriptEntityCancel( ENT(pCineTarget->pev) ); - pCineTarget = UTIL_FindEntityByTargetname(pCineTarget, STRING(pev->targetname)); + ScriptEntityCancel( pentCineTarget ); + pentCineTarget = FIND_ENTITY_BY_TARGETNAME(pentCineTarget, STRING(pev->targetname)); } } -// find all the cinematic entities with my targetname and tell them whether to wait before starting +// find all the cinematic entities with my targetname and tell them to wait before starting void CCineMonster :: DelayStart( int state ) { - CBaseEntity *pCine = UTIL_FindEntityByTargetname(NULL, STRING(pev->targetname)); + edict_t *pentCine = FIND_ENTITY_BY_TARGETNAME(NULL, STRING(pev->targetname)); - while ( pCine ) + while (!FNullEnt(pentCine)) { - if (FClassnameIs( pCine->pev, "scripted_sequence" ) || FClassnameIs( pCine->pev, "scripted_action" )) + if (FClassnameIs( pentCine, "scripted_sequence" )) { - CCineMonster *pTarget = GetClassPtr((CCineMonster *)(pCine->pev)); + CCineMonster *pTarget = GetClassPtr((CCineMonster *)VARS(pentCine)); if (state) { -// ALERT(at_console, "Delaying start\n"); pTarget->m_iDelay++; } else { -// ALERT(at_console, "Undelaying start\n"); pTarget->m_iDelay--; if (pTarget->m_iDelay <= 0) - { - pTarget->m_iState = STATE_ON; //LRC - FireTargets(STRING(m_iszFireOnBegin), this, this, USE_TOGGLE, 0); //LRC - pTarget->m_startTime = gpGlobals->time + 0.05; // why the delay? -- LRC - } + pTarget->m_startTime = gpGlobals->time + 0.05; } } - pCine = UTIL_FindEntityByTargetname(pCine, STRING(pev->targetname)); + pentCine = FIND_ENTITY_BY_TARGETNAME(pentCine, STRING(pev->targetname)); } } @@ -725,31 +751,31 @@ void CCineMonster :: DelayStart( int state ) // Find an entity that I'm interested in and precache the sounds he'll need in the sequence. void CCineMonster :: Activate( void ) { - CBaseEntity *pEntity; + edict_t *pentTarget; CBaseMonster *pTarget; // The entity name could be a target name or a classname // Check the targetname - pEntity = UTIL_FindEntityByTargetname(NULL, STRING(m_iszEntity)); + pentTarget = FIND_ENTITY_BY_TARGETNAME(NULL, STRING(m_iszEntity)); pTarget = NULL; - while (!pTarget && pEntity) + while (!pTarget && !FNullEnt(pentTarget)) { - if ( FBitSet( pEntity->pev->flags, FL_MONSTER )) + if ( FBitSet( VARS(pentTarget)->flags, FL_MONSTER )) { - pTarget = pEntity->MyMonsterPointer( ); + pTarget = GetMonsterPointer( pentTarget ); } - pEntity = UTIL_FindEntityByTargetname(pEntity, STRING(m_iszEntity)); + pentTarget = FIND_ENTITY_BY_TARGETNAME(pentTarget, STRING(m_iszEntity)); } // If no entity with that targetname, check the classname if ( !pTarget ) { - pEntity = UTIL_FindEntityByClassname(NULL, STRING(m_iszEntity)); - while (!pTarget && pEntity) + pentTarget = FIND_ENTITY_BY_CLASSNAME(NULL, STRING(m_iszEntity)); + while (!pTarget && !FNullEnt(pentTarget)) { - pTarget = pEntity->MyMonsterPointer( ); - pEntity = UTIL_FindEntityByClassname(pEntity, STRING(m_iszEntity)); + pTarget = GetMonsterPointer( pentTarget ); + pentTarget = FIND_ENTITY_BY_TARGETNAME(pentTarget, STRING(m_iszEntity)); } } // Found a compatible entity @@ -764,8 +790,6 @@ void CCineMonster :: Activate( void ) SequencePrecache( pmodel, STRING( m_iszPlay ) ); } } - - CBaseMonster::Activate(); } @@ -778,14 +802,9 @@ BOOL CBaseMonster :: CineCleanup( ) { // okay, reset me to what it thought I was before m_pCine->m_hTargetEnt = NULL; - pev->movetype = m_pCine->m_saved_movetype; - -// LRC - why mess around with this? Solidity isn't changed by sequences! -// pev->solid = m_pCine->m_saved_solid; - - if (m_pCine->pev->spawnflags & SF_SCRIPT_STAYDEAD) - pev->deadflag = DEAD_DYING; + pev->solid = m_pCine->m_saved_solid; + pev->effects = m_pCine->m_saved_effects; } else { @@ -867,7 +886,7 @@ BOOL CBaseMonster :: CineCleanup( ) // pEntity->pev->origin.z = new_origin.z + 5.0; // damn, got to fix this - UTIL_SetOrigin( this, pev->origin ); + UTIL_SetOrigin( pev, pev->origin ); pev->effects |= EF_NOINTERP; } @@ -892,7 +911,7 @@ BOOL CBaseMonster :: CineCleanup( ) // SetAnimation( m_MonsterState ); - //LRC- removed, was never implemented. ClearBits(pev->spawnflags, SF_MONSTER_WAIT_FOR_SCRIPT ); + ClearBits(pev->spawnflags, SF_MONSTER_WAIT_FOR_SCRIPT ); return TRUE; } @@ -908,17 +927,14 @@ public: void Use( CBaseEntity *pActivator, CBaseEntity *pCaller, USE_TYPE useType, float value ); void EXPORT FindThink( void ); void EXPORT DelayThink( void ); - void EXPORT DurationThink( void ); int ObjectCaps( void ) { return (CBaseToggle :: ObjectCaps() & ~FCAP_ACROSS_TRANSITION); } - STATE GetState() { return m_playing?STATE_ON:STATE_OFF; } - virtual int Save( CSave &save ); virtual int Restore( CRestore &restore ); static TYPEDESCRIPTION m_SaveData[]; - CBaseMonster *FindEntity( CBaseEntity *pActivator ); + CBaseMonster *FindEntity( void ); BOOL AcceptableSpeaker( CBaseMonster *pMonster ); BOOL StartSentence( CBaseMonster *pTarget ); @@ -928,11 +944,10 @@ private: int m_iszEntity; // entity that is wanted for this sentence float m_flRadius; // range to search float m_flDuration; // How long the sentence lasts - float m_flRepeat; // maximum repeat rate + float m_flRepeat; // repeat rate float m_flAttenuation; float m_flVolume; - BOOL m_active; // is the sentence enabled? (for m_flRepeat) - BOOL m_playing; //LRC- is the sentence playing? (for GetState) + BOOL m_active; int m_iszListener; // name of entity to look at while talking }; @@ -951,7 +966,6 @@ TYPEDESCRIPTION CScriptedSentence::m_SaveData[] = DEFINE_FIELD( CScriptedSentence, m_flAttenuation, FIELD_FLOAT ), DEFINE_FIELD( CScriptedSentence, m_flVolume, FIELD_FLOAT ), DEFINE_FIELD( CScriptedSentence, m_active, FIELD_BOOLEAN ), - DEFINE_FIELD( CScriptedSentence, m_playing, FIELD_BOOLEAN ), DEFINE_FIELD( CScriptedSentence, m_iszListener, FIELD_STRING ), }; @@ -1012,9 +1026,8 @@ void CScriptedSentence :: Use( CBaseEntity *pActivator, CBaseEntity *pCaller, US if ( !m_active ) return; // ALERT( at_console, "Firing sentence: %s\n", STRING(m_iszSentence) ); - m_hActivator = pActivator; - SetThink(&CScriptedSentence :: FindThink ); - SetNextThink( 0 ); + SetThink( FindThink ); + pev->nextthink = gpGlobals->time; } @@ -1023,12 +1036,11 @@ void CScriptedSentence :: Spawn( void ) pev->solid = SOLID_NOT; m_active = TRUE; - m_playing = FALSE; //LRC // if no targetname, start now if ( !pev->targetname ) { - SetThink(&CScriptedSentence :: FindThink ); - SetNextThink( 1.0 ); + SetThink( FindThink ); + pev->nextthink = gpGlobals->time + 1.0; } switch( pev->impulse ) @@ -1060,60 +1072,31 @@ void CScriptedSentence :: Spawn( void ) void CScriptedSentence :: FindThink( void ) { - if (!m_iszEntity) //LRC- no target monster given: speak through HEV - { - CBasePlayer* pPlayer = (CBasePlayer*)UTIL_FindEntityByClassname( NULL, "player" ); - if (pPlayer) - { - m_playing = TRUE; - if ((STRING(m_iszSentence))[0] == '!') - pPlayer->SetSuitUpdate((char*)STRING(m_iszSentence),FALSE,0); - else - pPlayer->SetSuitUpdate((char*)STRING(m_iszSentence),TRUE,0); - if ( pev->spawnflags & SF_SENTENCE_ONCE ) - UTIL_Remove( this ); - SetThink(&CScriptedSentence :: DurationThink ); - SetNextThink( m_flDuration ); - m_active = FALSE; - } - else - ALERT( at_debug, "ScriptedSentence: can't find \"player\" to play HEV sentence!?\n"); - return; - } - - CBaseMonster *pMonster = FindEntity( m_hActivator ); + CBaseMonster *pMonster = FindEntity(); if ( pMonster ) { - m_playing = TRUE; StartSentence( pMonster ); if ( pev->spawnflags & SF_SENTENCE_ONCE ) UTIL_Remove( this ); - SetThink(&CScriptedSentence :: DurationThink ); - SetNextThink( m_flDuration ); + SetThink( DelayThink ); + pev->nextthink = gpGlobals->time + m_flDuration + m_flRepeat; m_active = FALSE; // ALERT( at_console, "%s: found monster %s\n", STRING(m_iszSentence), STRING(m_iszEntity) ); } else { // ALERT( at_console, "%s: can't find monster %s\n", STRING(m_iszSentence), STRING(m_iszEntity) ); - SetNextThink( m_flRepeat + 0.5 ); + pev->nextthink = gpGlobals->time + m_flRepeat + 0.5; } } -//LRC -void CScriptedSentence :: DurationThink( void ) -{ - m_playing = FALSE; - SetNextThink( m_flRepeat ); - SetThink(&CScriptedSentence :: DelayThink ); -} void CScriptedSentence :: DelayThink( void ) { m_active = TRUE; if ( !pev->targetname ) - SetNextThink( 0.1 ); - SetThink(&CScriptedSentence :: FindThink ); + pev->nextthink = gpGlobals->time + 0.1; + SetThink( FindThink ); } @@ -1138,34 +1121,35 @@ BOOL CScriptedSentence :: AcceptableSpeaker( CBaseMonster *pMonster ) } -CBaseMonster *CScriptedSentence :: FindEntity( CBaseEntity *pActivator ) +CBaseMonster *CScriptedSentence :: FindEntity( void ) { - CBaseEntity *pTarget; + edict_t *pentTarget; CBaseMonster *pMonster; - pTarget = UTIL_FindEntityByTargetname(NULL, STRING(m_iszEntity), pActivator); + + pentTarget = FIND_ENTITY_BY_TARGETNAME(NULL, STRING(m_iszEntity)); pMonster = NULL; - while ( pTarget ) + while (!FNullEnt(pentTarget)) { - pMonster = pTarget->MyMonsterPointer( ); + pMonster = GetMonsterPointer( pentTarget ); if ( pMonster != NULL ) { if ( AcceptableSpeaker( pMonster ) ) return pMonster; // ALERT( at_console, "%s (%s), not acceptable\n", STRING(pMonster->pev->classname), STRING(pMonster->pev->targetname) ); } - pTarget = UTIL_FindEntityByTargetname(pTarget, STRING(m_iszEntity), pActivator); + pentTarget = FIND_ENTITY_BY_TARGETNAME(pentTarget, STRING(m_iszEntity)); } - pTarget = NULL; - while ((pTarget = UTIL_FindEntityInSphere( pTarget, pev->origin, m_flRadius )) != NULL) + CBaseEntity *pEntity = NULL; + while ((pEntity = UTIL_FindEntityInSphere( pEntity, pev->origin, m_flRadius )) != NULL) { - if (FClassnameIs( pTarget->pev, STRING(m_iszEntity))) + if (FClassnameIs( pEntity->pev, STRING(m_iszEntity))) { - if ( FBitSet( pTarget->pev->flags, FL_MONSTER )) + if ( FBitSet( pEntity->pev->flags, FL_MONSTER )) { - pMonster = pTarget->MyMonsterPointer( ); + pMonster = pEntity->MyMonsterPointer( ); if ( AcceptableSpeaker( pMonster ) ) return pMonster; } @@ -1185,7 +1169,6 @@ BOOL CScriptedSentence :: StartSentence( CBaseMonster *pTarget ) } BOOL bConcurrent = FALSE; - //LRC: Er... if the "concurrent" flag is NOT set, we make bConcurrent true!? if ( !(pev->spawnflags & SF_SENTENCE_CONCURRENT) ) bConcurrent = TRUE; @@ -1236,8 +1219,8 @@ LINK_ENTITY_TO_CLASS( monster_furniture, CFurniture ); //========================================================= void CFurniture :: Die ( void ) { - SetThink(&CFurniture :: SUB_Remove ); - SetNextThink( 0 ); + SetThink ( SUB_Remove ); + pev->nextthink = gpGlobals->time; } //========================================================= @@ -1271,7 +1254,7 @@ void CFurniture :: Spawn( ) //========================================================= int CFurniture::Classify ( void ) { - return m_iClass?m_iClass:CLASS_NONE; + return CLASS_NONE; } diff --git a/spirit/scripted.h b/dlls/scripted.h similarity index 58% rename from spirit/scripted.h rename to dlls/scripted.h index 1cefc563..67a2eb13 100644 --- a/spirit/scripted.h +++ b/dlls/scripted.h @@ -1,6 +1,6 @@ /*** * -* Copyright (c) 1999, 2000 Valve LLC. All rights reserved. +* Copyright (c) 1996-2002, Valve LLC. All rights reserved. * * This product contains software technology licensed from Id * Software, Inc. ("Id Technology"). Id Technology (c) 1996 Id Software, Inc. @@ -27,16 +27,15 @@ #define SF_SCRIPT_NOINTERRUPT 32 #define SF_SCRIPT_OVERRIDESTATE 64 #define SF_SCRIPT_NOSCRIPTMOVEMENT 128 -#define SF_SCRIPT_STAYDEAD 256 // LRC- signifies that the animation kills the monster - // (needed because the monster animations don't use AnimEvent 1000 properly) #define SCRIPT_BREAK_CONDITIONS (bits_COND_LIGHT_DAMAGE|bits_COND_HEAVY_DAMAGE) -//LRC - rearranged into flags -#define SS_INTERRUPT_IDLE 0x0 -#define SS_INTERRUPT_ALERT 0x1 -#define SS_INTERRUPT_ANYSTATE 0x2 -#define SS_INTERRUPT_SCRIPTS 0x4 +enum SS_INTERRUPT +{ + SS_INTERRUPT_IDLE = 0, + SS_INTERRUPT_BY_NAME, + SS_INTERRUPT_AI, +}; // when a monster finishes an AI scripted sequence, we can choose // a schedule to place them in. These defines are the aliases to @@ -60,37 +59,18 @@ public: static TYPEDESCRIPTION m_SaveData[]; - //LRC: states for script entities - virtual STATE GetState( void ) { return m_iState; }; - STATE m_iState; - // void EXPORT CineSpawnThink( void ); void EXPORT CineThink( void ); - void EXPORT InitIdleThink( void ); //LRC void Pain( void ); void Die( void ); void DelayStart( int state ); - CBaseMonster* FindEntity( const char* sName, CBaseEntity *pActivator ); + BOOL FindEntity( void ); virtual void PossessEntity( void ); - inline BOOL IsAction( void ) { return FClassnameIs(pev,"scripted_action"); }; //LRC - - //LRC: Should the monster do a precise attack for this scripted_action? - // (Do a precise attack if we'll be turning to face the target, but we haven't just walked to the target.) - BOOL PreciseAttack( void ) - { - // if (m_fTurnType != 1) { ALERT(at_console,"preciseattack fails check 1\n"); return FALSE; } - // if (m_fMoveTo == 0) { ALERT(at_console,"preciseattack fails check 2\n"); return FALSE; } - // if (m_fMoveTo != 5 && m_iszAttack == 0) { ALERT(at_console,"preciseattack fails check 3\n"); return FALSE; } - // ALERT(at_console,"preciseattack passes!\n"); - // return TRUE; - return m_fTurnType == 1 && ( m_fMoveTo == 5 || (m_fMoveTo != 0 && !FStrEq(STRING(m_iszAttack), STRING(m_iszMoveTarget)) )); - - }; - void ReleaseEntity( CBaseMonster *pEntity ); void CancelScript( void ); virtual BOOL StartSequence( CBaseMonster *pTarget, int iszSeq, BOOL completeOnEmpty ); + virtual BOOL FCanOverrideState ( void ); void SequenceDone ( CBaseMonster *pMonster ); virtual void FixScriptMonsterSchedule( CBaseMonster *pMonster ); BOOL CanInterrupt( void ); @@ -100,19 +80,10 @@ public: int m_iszIdle; // string index for idle animation int m_iszPlay; // string index for scripted animation int m_iszEntity; // entity that is wanted for this script - int m_iszAttack; // entity to attack - int m_iszMoveTarget; // entity to move to - int m_iszFireOnBegin; // entity to fire when the sequence _starts_. int m_fMoveTo; - int m_fTurnType; - int m_fAction; int m_iFinishSchedule; float m_flRadius; // range to search -//LRC- this does nothing!! float m_flRepeat; // repeat rate - int m_iRepeats; //LRC - number of times to repeat the animation - int m_iRepeatsLeft; //LRC - float m_fRepeatFrame; //LRC - int m_iPriority; //LRC + float m_flRepeat; // repeat rate int m_iDelay; float m_startTime; @@ -124,6 +95,13 @@ public: BOOL m_interruptable; }; -//LRC - removed CCineAI, obsolete +class CCineAI : public CCineMonster +{ + BOOL StartSequence( CBaseMonster *pTarget, int iszSeq, BOOL completeOnEmpty ); + void PossessEntity( void ); + BOOL FCanOverrideState ( void ); + virtual void FixScriptMonsterSchedule( CBaseMonster *pMonster ); +}; + #endif //SCRIPTED_H diff --git a/spirit/scriptevent.h b/dlls/scriptevent.h similarity index 98% rename from spirit/scriptevent.h rename to dlls/scriptevent.h index 42377cf0..9a02bd64 100644 --- a/spirit/scriptevent.h +++ b/dlls/scriptevent.h @@ -1,29 +1,29 @@ -/*** -* -* Copyright (c) 1996-2002, 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. -* -****/ -#ifndef SCRIPTEVENT_H -#define SCRIPTEVENT_H - -#define SCRIPT_EVENT_DEAD 1000 // character is now dead -#define SCRIPT_EVENT_NOINTERRUPT 1001 // does not allow interrupt -#define SCRIPT_EVENT_CANINTERRUPT 1002 // will allow interrupt -#define SCRIPT_EVENT_FIREEVENT 1003 // event now fires -#define SCRIPT_EVENT_SOUND 1004 // Play named wave file (on CHAN_BODY) -#define SCRIPT_EVENT_SENTENCE 1005 // Play named sentence -#define SCRIPT_EVENT_INAIR 1006 // Leave the character in air at the end of the sequence (don't find the floor) -#define SCRIPT_EVENT_ENDANIMATION 1007 // Set the animation by name after the sequence completes -#define SCRIPT_EVENT_SOUND_VOICE 1008 // Play named wave file (on CHAN_VOICE) -#define SCRIPT_EVENT_SENTENCE_RND1 1009 // Play sentence group 25% of the time -#define SCRIPT_EVENT_NOT_DEAD 1010 // Bring back to life (for life/death sequences) -#endif //SCRIPTEVENT_H +/*** +* +* Copyright (c) 1996-2002, 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. +* +****/ +#ifndef SCRIPTEVENT_H +#define SCRIPTEVENT_H + +#define SCRIPT_EVENT_DEAD 1000 // character is now dead +#define SCRIPT_EVENT_NOINTERRUPT 1001 // does not allow interrupt +#define SCRIPT_EVENT_CANINTERRUPT 1002 // will allow interrupt +#define SCRIPT_EVENT_FIREEVENT 1003 // event now fires +#define SCRIPT_EVENT_SOUND 1004 // Play named wave file (on CHAN_BODY) +#define SCRIPT_EVENT_SENTENCE 1005 // Play named sentence +#define SCRIPT_EVENT_INAIR 1006 // Leave the character in air at the end of the sequence (don't find the floor) +#define SCRIPT_EVENT_ENDANIMATION 1007 // Set the animation by name after the sequence completes +#define SCRIPT_EVENT_SOUND_VOICE 1008 // Play named wave file (on CHAN_VOICE) +#define SCRIPT_EVENT_SENTENCE_RND1 1009 // Play sentence group 25% of the time +#define SCRIPT_EVENT_NOT_DEAD 1010 // Bring back to life (for life/death sequences) +#endif //SCRIPTEVENT_H diff --git a/spirit/shotgun.cpp b/dlls/shotgun.cpp similarity index 53% rename from spirit/shotgun.cpp rename to dlls/shotgun.cpp index 2ddc2cc2..dfa084b8 100644 --- a/spirit/shotgun.cpp +++ b/dlls/shotgun.cpp @@ -24,45 +24,31 @@ // special deathmatch shotgun spreads #define VECTOR_CONE_DM_SHOTGUN Vector( 0.08716, 0.04362, 0.00 )// 10 degrees by 5 degrees -#define VECTOR_CONE_DM_DOUBLESHOTGUN Vector( 0.17365, 0.04362, 0.00 ) // 20 degrees by 5 degrees +#define VECTOR_CONE_DM_DOUBLESHOTGUN Vector( 0.17365, 0.04362, 0.00 ) // 20 degrees by 5 degrees enum shotgun_e { SHOTGUN_IDLE = 0, - SHOTGUN_DRAW, - SHOTGUN_HOLSTER, SHOTGUN_FIRE, SHOTGUN_FIRE2, - SHOTGUN_START_RELOAD, SHOTGUN_RELOAD, - SHOTGUN_PUMP + SHOTGUN_PUMP, + SHOTGUN_START_RELOAD, + SHOTGUN_DRAW, + SHOTGUN_HOLSTER, + SHOTGUN_IDLE4, + SHOTGUN_IDLE_DEEP }; -class CShotgun : public CBasePlayerWeapon -{ -public: - void Spawn( void ); - void Precache( void ); - int GetItemInfo(ItemInfo *p); - - void PrimaryAttack( void ); - void SecondaryAttack( void ); - BOOL Deploy( ); - void Holster( ); - void Reload( void ); - void WeaponIdle( void ); -private: - int m_iShell; - unsigned short m_usDoubleFire; - unsigned short m_usSingleFire; -}; LINK_ENTITY_TO_CLASS( weapon_shotgun, CShotgun ); void CShotgun::Spawn( ) { Precache( ); - + m_iId = WEAPON_SHOTGUN; SET_MODEL(ENT(pev), "models/w_shotgun.mdl"); + m_iDefaultAmmo = SHOTGUN_DEFAULT_GIVE; + FallInit();// get ready to fall } @@ -75,16 +61,37 @@ void CShotgun::Precache( void ) m_iShell = PRECACHE_MODEL ("models/shotgunshell.mdl");// shotgun shell + PRECACHE_SOUND("items/9mmclip1.wav"); + PRECACHE_SOUND ("weapons/dbarrel1.wav");//shotgun PRECACHE_SOUND ("weapons/sbarrel1.wav");//shotgun PRECACHE_SOUND ("weapons/reload1.wav"); // shotgun reload PRECACHE_SOUND ("weapons/reload3.wav"); // shotgun reload - m_usSingleFire = PRECACHE_EVENT( 1, "evShotgun1" ); - m_usDoubleFire = PRECACHE_EVENT( 1, "evShotgun2" ); +// PRECACHE_SOUND ("weapons/sshell1.wav"); // shotgun reload - played on client +// PRECACHE_SOUND ("weapons/sshell3.wav"); // shotgun reload - played on client + + PRECACHE_SOUND ("weapons/357_cock1.wav"); // gun empty sound + PRECACHE_SOUND ("weapons/scock1.wav"); // cock gun + + m_usSingleFire = PRECACHE_EVENT( 1, "events/shotgun1.sc" ); + m_usDoubleFire = PRECACHE_EVENT( 1, "events/shotgun2.sc" ); } +int CShotgun::AddToPlayer( CBasePlayer *pPlayer ) +{ + if ( CBasePlayerWeapon::AddToPlayer( pPlayer ) ) + { + MESSAGE_BEGIN( MSG_ONE, gmsgWeapPickup, NULL, pPlayer->pev ); + WRITE_BYTE( m_iId ); + MESSAGE_END(); + return TRUE; + } + return FALSE; +} + + int CShotgun::GetItemInfo(ItemInfo *p) { p->pszName = STRING(pev->classname); @@ -102,31 +109,28 @@ int CShotgun::GetItemInfo(ItemInfo *p) return 1; } + + BOOL CShotgun::Deploy( ) { return DefaultDeploy( "models/v_shotgun.mdl", "models/p_shotgun.mdl", SHOTGUN_DRAW, "shotgun" ); } -void CShotgun::Holster( ) -{ - m_pPlayer->m_flNextAttack = UTIL_WeaponTimeBase() + 0.5; - SendWeaponAnim( SHOTGUN_HOLSTER ); -} - void CShotgun::PrimaryAttack() { // don't fire underwater - if (m_pPlayer->pev->waterlevel == 3 ) + if (m_pPlayer->pev->waterlevel == 3) { - PlayEmptySound( 4 ); + PlayEmptySound( ); m_flNextPrimaryAttack = UTIL_WeaponTimeBase() + 0.15; return; } - if (m_iClip == 0) + if (m_iClip <= 0) { - PlayEmptySound( 4 ); Reload( ); + if (m_iClip == 0) + PlayEmptySound( ); return; } @@ -134,55 +138,70 @@ void CShotgun::PrimaryAttack() m_pPlayer->m_iWeaponFlash = NORMAL_GUN_FLASH; m_iClip--; + + int flags; +#if defined( CLIENT_WEAPONS ) + flags = FEV_NOTHOST; +#else + flags = 0; +#endif + + m_pPlayer->pev->effects = (int)(m_pPlayer->pev->effects) | EF_MUZZLEFLASH; Vector vecSrc = m_pPlayer->GetGunPosition( ); Vector vecAiming = m_pPlayer->GetAutoaimVector( AUTOAIM_5DEGREES ); Vector vecDir; - - if ( IsMultiplayer() ) + +#ifdef CLIENT_DLL + if ( bIsMultiplayer() ) +#else + if ( g_pGameRules->IsMultiplayer() ) +#endif + { vecDir = m_pPlayer->FireBulletsPlayer( 4, vecSrc, vecAiming, VECTOR_CONE_DM_SHOTGUN, 2048, BULLET_PLAYER_BUCKSHOT, 0, 0, m_pPlayer->pev, m_pPlayer->random_seed ); - else vecDir = m_pPlayer->FireBulletsPlayer( 6, vecSrc, vecAiming, VECTOR_CONE_10DEGREES, 2048, BULLET_PLAYER_BUCKSHOT, 0, 0, m_pPlayer->pev, m_pPlayer->random_seed ); + } + else + { + // regular old, untouched spread. + vecDir = m_pPlayer->FireBulletsPlayer( 6, vecSrc, vecAiming, VECTOR_CONE_10DEGREES, 2048, BULLET_PLAYER_BUCKSHOT, 0, 0, m_pPlayer->pev, m_pPlayer->random_seed ); + } - PLAYBACK_EVENT_FULL( 0, m_pPlayer->edict(), m_usSingleFire, 0.0, (float *)&g_vecZero, (float *)&g_vecZero, vecDir.x, vecDir.y, pev->body, SHOTGUN_FIRE, 0, 0 ); + PLAYBACK_EVENT_FULL( flags, m_pPlayer->edict(), m_usSingleFire, 0.0, (float *)&g_vecZero, (float *)&g_vecZero, vecDir.x, vecDir.y, 0, 0, 0, 0 ); - // player "shoot" animation - m_pPlayer->SetAnimation( PLAYER_ATTACK1 ); if (!m_iClip && m_pPlayer->m_rgAmmo[m_iPrimaryAmmoType] <= 0) // HEV suit - indicate out of ammo condition m_pPlayer->SetSuitUpdate("!HEV_AMO0", FALSE, 0); - m_flNextPrimaryAttack = UTIL_WeaponTimeBase() + 1.0; - m_flNextSecondaryAttack = UTIL_WeaponTimeBase() + 1.0; + if (m_iClip != 0) + m_flPumpTime = gpGlobals->time + 0.5; - if (m_iClip != 0) m_flTimeWeaponIdle = UTIL_WeaponTimeBase() + 5.0; - else m_flTimeWeaponIdle = UTIL_WeaponTimeBase() + 1.9; - m_iChargeLevel = 0; + m_flNextPrimaryAttack = UTIL_WeaponTimeBase() + 0.75; + m_flNextSecondaryAttack = UTIL_WeaponTimeBase() + 0.75; + if (m_iClip != 0) + m_flTimeWeaponIdle = UTIL_WeaponTimeBase() + 5.0; + else + m_flTimeWeaponIdle = UTIL_WeaponTimeBase() + 0.75; + m_fInSpecialReload = 0; } void CShotgun::SecondaryAttack( void ) { // don't fire underwater - if (m_pPlayer->pev->waterlevel == 3 ) + if (m_pPlayer->pev->waterlevel == 3) { - PlayEmptySound( 4 ); + PlayEmptySound( ); m_flNextPrimaryAttack = UTIL_WeaponTimeBase() + 0.15; return; } - if (m_iClip == 1) + if (m_iClip <= 1) { - PrimaryAttack(); - return; - } - - if (m_iClip == 0) - { - PlayEmptySound( 4 ); Reload( ); + PlayEmptySound( ); return; } @@ -191,6 +210,14 @@ void CShotgun::SecondaryAttack( void ) m_iClip -= 2; + + int flags; +#if defined( CLIENT_WEAPONS ) + flags = FEV_NOTHOST; +#else + flags = 0; +#endif + m_pPlayer->pev->effects = (int)(m_pPlayer->pev->effects) | EF_MUZZLEFLASH; // player "shoot" animation @@ -200,104 +227,152 @@ void CShotgun::SecondaryAttack( void ) Vector vecAiming = m_pPlayer->GetAutoaimVector( AUTOAIM_5DEGREES ); Vector vecDir; - - if ( IsMultiplayer() ) + +#ifdef CLIENT_DLL + if ( bIsMultiplayer() ) +#else + if ( g_pGameRules->IsMultiplayer() ) +#endif + { + // tuned for deathmatch vecDir = m_pPlayer->FireBulletsPlayer( 8, vecSrc, vecAiming, VECTOR_CONE_DM_DOUBLESHOTGUN, 2048, BULLET_PLAYER_BUCKSHOT, 0, 0, m_pPlayer->pev, m_pPlayer->random_seed ); - else vecDir = m_pPlayer->FireBulletsPlayer( 12, vecSrc, vecAiming, VECTOR_CONE_10DEGREES, 2048, BULLET_PLAYER_BUCKSHOT, 0, 0, m_pPlayer->pev, m_pPlayer->random_seed ); - - - PLAYBACK_EVENT_FULL( 0, m_pPlayer->edict(), m_usDoubleFire, 0.0, (float *)&g_vecZero, (float *)&g_vecZero, vecDir.x, vecDir.y, pev->body, SHOTGUN_FIRE2, 0, 0 ); + } + else + { + // untouched default single player + vecDir = m_pPlayer->FireBulletsPlayer( 12, vecSrc, vecAiming, VECTOR_CONE_10DEGREES, 2048, BULLET_PLAYER_BUCKSHOT, 0, 0, m_pPlayer->pev, m_pPlayer->random_seed ); + } + + PLAYBACK_EVENT_FULL( flags, m_pPlayer->edict(), m_usDoubleFire, 0.0, (float *)&g_vecZero, (float *)&g_vecZero, vecDir.x, vecDir.y, 0, 0, 0, 0 ); if (!m_iClip && m_pPlayer->m_rgAmmo[m_iPrimaryAmmoType] <= 0) // HEV suit - indicate out of ammo condition m_pPlayer->SetSuitUpdate("!HEV_AMO0", FALSE, 0); + if (m_iClip != 0) + m_flPumpTime = gpGlobals->time + 0.95; m_flNextPrimaryAttack = UTIL_WeaponTimeBase() + 1.5; m_flNextSecondaryAttack = UTIL_WeaponTimeBase() + 1.5; + if (m_iClip != 0) + m_flTimeWeaponIdle = UTIL_WeaponTimeBase() + 6.0; + else + m_flTimeWeaponIdle = 1.5; + + m_fInSpecialReload = 0; - if (m_iClip != 0) m_flTimeWeaponIdle = UTIL_WeaponTimeBase() + 6.0; - else m_flTimeWeaponIdle = UTIL_WeaponTimeBase() + 1.9; - m_iChargeLevel = 0; - m_pPlayer->pev->punchangle.x -= 5; } void CShotgun::Reload( void ) { - if (m_pPlayer->m_rgAmmo[m_iPrimaryAmmoType] <= 0 || m_iClip == SHOTGUN_MAX_CLIP) return; + if (m_pPlayer->m_rgAmmo[m_iPrimaryAmmoType] <= 0 || m_iClip == SHOTGUN_MAX_CLIP) + return; // don't reload until recoil is done - if (m_flNextPrimaryAttack > UTIL_WeaponTimeBase()) return; + if (m_flNextPrimaryAttack > UTIL_WeaponTimeBase()) + return; // check to see if we're ready to reload - if (m_iChargeLevel == 0) + if (m_fInSpecialReload == 0) { SendWeaponAnim( SHOTGUN_START_RELOAD ); - m_iChargeLevel = 1; + m_fInSpecialReload = 1; m_pPlayer->m_flNextAttack = UTIL_WeaponTimeBase() + 0.6; m_flTimeWeaponIdle = UTIL_WeaponTimeBase() + 0.6; m_flNextPrimaryAttack = UTIL_WeaponTimeBase() + 1.0; m_flNextSecondaryAttack = UTIL_WeaponTimeBase() + 1.0; return; } - else if (m_iChargeLevel == 1) + else if (m_fInSpecialReload == 1) { - if (m_flTimeWeaponIdle > UTIL_WeaponTimeBase()) return; + if (m_flTimeWeaponIdle > UTIL_WeaponTimeBase()) + return; // was waiting for gun to move to side - m_iChargeLevel = 2; + m_fInSpecialReload = 2; - if (RANDOM_LONG(0,1)) EMIT_SOUND_DYN(ENT(m_pPlayer->pev), CHAN_ITEM, "weapons/reload1.wav", 1, ATTN_NORM, 0, 85 + RANDOM_LONG(0,0x1f)); - else EMIT_SOUND_DYN(ENT(m_pPlayer->pev), CHAN_ITEM, "weapons/reload3.wav", 1, ATTN_NORM, 0, 85 + RANDOM_LONG(0,0x1f)); + if (RANDOM_LONG(0,1)) + EMIT_SOUND_DYN(ENT(m_pPlayer->pev), CHAN_ITEM, "weapons/reload1.wav", 1, ATTN_NORM, 0, 85 + RANDOM_LONG(0,0x1f)); + else + EMIT_SOUND_DYN(ENT(m_pPlayer->pev), CHAN_ITEM, "weapons/reload3.wav", 1, ATTN_NORM, 0, 85 + RANDOM_LONG(0,0x1f)); SendWeaponAnim( SHOTGUN_RELOAD ); - m_flTimeWeaponIdle = UTIL_WeaponTimeBase() + 0.6; + m_flNextReload = UTIL_WeaponTimeBase() + 0.5; + m_flTimeWeaponIdle = UTIL_WeaponTimeBase() + 0.5; } else { // Add them to the clip m_iClip += 1; m_pPlayer->m_rgAmmo[m_iPrimaryAmmoType] -= 1; - m_iChargeLevel = 1; - if(m_iClip == SHOTGUN_MAX_CLIP) m_flTimeWeaponIdle = UTIL_WeaponTimeBase() + 0.1; + m_fInSpecialReload = 1; } } - void CShotgun::WeaponIdle( void ) { + ResetEmptySound( ); + m_pPlayer->GetAutoaimVector( AUTOAIM_5DEGREES ); - + + if ( m_flPumpTime && m_flPumpTime < gpGlobals->time ) + { + // play pumping sound + EMIT_SOUND_DYN(ENT(m_pPlayer->pev), CHAN_ITEM, "weapons/scock1.wav", 1, ATTN_NORM, 0, 95 + RANDOM_LONG(0,0x1f)); + m_flPumpTime = 0; + } + if (m_flTimeWeaponIdle < UTIL_WeaponTimeBase() ) { - if (m_iClip == 0 && m_iChargeLevel == 0 && m_pPlayer->m_rgAmmo[m_iPrimaryAmmoType]) Reload( ); - else if (m_iChargeLevel != 0) + if (m_iClip == 0 && m_fInSpecialReload == 0 && m_pPlayer->m_rgAmmo[m_iPrimaryAmmoType]) { - if (m_iClip != SHOTGUN_MAX_CLIP && m_pPlayer->m_rgAmmo[m_iPrimaryAmmoType]) Reload( ); + Reload( ); + } + else if (m_fInSpecialReload != 0) + { + if (m_iClip != 8 && m_pPlayer->m_rgAmmo[m_iPrimaryAmmoType]) + { + Reload( ); + } else { // reload debounce has timed out SendWeaponAnim( SHOTGUN_PUMP ); - m_iChargeLevel = 0; + + // play cocking sound + EMIT_SOUND_DYN(ENT(m_pPlayer->pev), CHAN_ITEM, "weapons/scock1.wav", 1, ATTN_NORM, 0, 95 + RANDOM_LONG(0,0x1f)); + m_fInSpecialReload = 0; m_flTimeWeaponIdle = UTIL_WeaponTimeBase() + 1.5; } } else { - - float flRand = RANDOM_FLOAT(0, 1); - if ( flRand <= 0.5 ) + int iAnim; + float flRand = UTIL_SharedRandomFloat( m_pPlayer->random_seed, 0, 1 ); + if (flRand <= 0.8) { - SendWeaponAnim( SHOTGUN_IDLE ); - m_flTimeWeaponIdle = UTIL_WeaponTimeBase() + RANDOM_FLOAT ( 10, 15 ); + iAnim = SHOTGUN_IDLE_DEEP; + m_flTimeWeaponIdle = UTIL_WeaponTimeBase() + (60.0/12.0);// * RANDOM_LONG(2, 5); } + else if (flRand <= 0.95) + { + iAnim = SHOTGUN_IDLE; + m_flTimeWeaponIdle = UTIL_WeaponTimeBase() + (20.0/9.0); + } + else + { + iAnim = SHOTGUN_IDLE4; + m_flTimeWeaponIdle = UTIL_WeaponTimeBase() + (20.0/9.0); + } + SendWeaponAnim( iAnim ); } } } + class CShotgunAmmo : public CBasePlayerAmmo { void Spawn( void ) diff --git a/spirit/singleplay_gamerules.cpp b/dlls/singleplay_gamerules.cpp similarity index 92% rename from spirit/singleplay_gamerules.cpp rename to dlls/singleplay_gamerules.cpp index f20e1a16..b71b96a1 100644 --- a/spirit/singleplay_gamerules.cpp +++ b/dlls/singleplay_gamerules.cpp @@ -92,7 +92,7 @@ BOOL CHalfLifeRules :: GetNextBestWeapon( CBasePlayer *pPlayer, CBasePlayerItem //========================================================= //========================================================= -BOOL CHalfLifeRules :: ClientConnected( edict_t *pEntity, const char *pszName, const char *pszAddress, char szRejectReason[128] ) +BOOL CHalfLifeRules :: ClientConnected( edict_t *pEntity, const char *pszName, const char *pszAddress, char szRejectReason[ 128 ] ) { return TRUE; } @@ -121,17 +121,6 @@ float CHalfLifeRules::FlPlayerFallDamage( CBasePlayer *pPlayer ) //========================================================= void CHalfLifeRules :: PlayerSpawn( CBasePlayer *pPlayer ) { - CBaseEntity *pWeaponEntity = NULL; - - //LRC- support the new "start with HEV" flag... - if (g_startSuit) pPlayer->pev->weapons |= ITEM_SUIT; - -// LRC what's wrong with allowing "game_player_equip" entities in single player? (The -// level designer is God: if he wants the player to start with a weapon, we should allow it!) - while ( pWeaponEntity = UTIL_FindEntityByClassname( pWeaponEntity, "game_player_equip" )) - { - pWeaponEntity->Touch( pPlayer ); - } } //========================================================= diff --git a/spirit/skill.cpp b/dlls/skill.cpp similarity index 90% rename from spirit/skill.cpp rename to dlls/skill.cpp index f8c21c45..7c0b8529 100644 --- a/spirit/skill.cpp +++ b/dlls/skill.cpp @@ -1,47 +1,47 @@ -/*** -* -* Copyright (c) 1996-2002, 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. -* -****/ -//========================================================= -// skill.cpp - code for skill level concerns -//========================================================= -#include "extdll.h" -#include "util.h" -#include "skill.h" - - -skilldata_t gSkillData; - - -//========================================================= -// take the name of a cvar, tack a digit for the skill level -// on, and return the value.of that Cvar -//========================================================= -float GetSkillCvar( char *pName ) -{ - int iCount; - float flValue; - char szBuffer[ 64 ]; - - iCount = sprintf( szBuffer, "%s%d",pName, gSkillData.iSkillLevel ); - - flValue = CVAR_GET_FLOAT ( szBuffer ); - - if ( flValue <= 0 ) - { - ALERT ( at_debug, "\n\n** GetSkillCVar Got a zero for %s **\n\n", szBuffer ); - } - - return flValue; -} - +/*** +* +* Copyright (c) 1996-2002, 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. +* +****/ +//========================================================= +// skill.cpp - code for skill level concerns +//========================================================= +#include "extdll.h" +#include "util.h" +#include "skill.h" + + +skilldata_t gSkillData; + + +//========================================================= +// take the name of a cvar, tack a digit for the skill level +// on, and return the value.of that Cvar +//========================================================= +float GetSkillCvar( char *pName ) +{ + int iCount; + float flValue; + char szBuffer[ 64 ]; + + iCount = sprintf( szBuffer, "%s%d",pName, gSkillData.iSkillLevel ); + + flValue = CVAR_GET_FLOAT ( szBuffer ); + + if ( flValue <= 0 ) + { + ALERT ( at_console, "\n\n** GetSkillCVar Got a zero for %s **\n\n", szBuffer ); + } + + return flValue; +} + diff --git a/spirit/skill.h b/dlls/skill.h similarity index 94% rename from spirit/skill.h rename to dlls/skill.h index 6f2d6e7e..44340711 100644 --- a/spirit/skill.h +++ b/dlls/skill.h @@ -120,9 +120,8 @@ struct skilldata_t float batteryCapacity; float healthchargerCapacity; float healthkitCapacity; - float flashlightCharge; float scientistHeal; - + // monster damage adj float monHead; float monChest; diff --git a/spirit/sound.cpp b/dlls/sound.cpp similarity index 84% rename from spirit/sound.cpp rename to dlls/sound.cpp index 3871ef0f..174e5f97 100644 --- a/spirit/sound.cpp +++ b/dlls/sound.cpp @@ -16,8 +16,6 @@ // sound.cpp //========================================================= -#include - #include "extdll.h" #include "util.h" #include "cbase.h" @@ -25,7 +23,6 @@ #include "player.h" #include "talkmonster.h" #include "gamerules.h" -#include "locus.h" static char *memfgets( byte *pMemFile, int fileSize, int &filePos, char *pBuffer, int bufferSize ); @@ -122,10 +119,8 @@ class CAmbientGeneric : public CBaseEntity public: void KeyValue( KeyValueData* pkvd); void Spawn( void ); -// void PostSpawn( void ); void Precache( void ); void EXPORT ToggleUse ( CBaseEntity *pActivator, CBaseEntity *pCaller, USE_TYPE useType, float value ); - void EXPORT StartPlayFrom( void ); void EXPORT RampThink( void ); void InitModulationParms(void); @@ -139,8 +134,6 @@ public: BOOL m_fActive; // only TRUE when the entity is playing a looping sound BOOL m_fLooping; // TRUE when the sound played will loop - edict_t *m_pPlayFrom; //LRC - the entity to play from - int m_iChannel; //LRC - the channel to play from, for "play from X" sounds }; LINK_ENTITY_TO_CLASS( ambient_generic, CAmbientGeneric ); @@ -149,8 +142,6 @@ TYPEDESCRIPTION CAmbientGeneric::m_SaveData[] = DEFINE_FIELD( CAmbientGeneric, m_flAttenuation, FIELD_FLOAT ), DEFINE_FIELD( CAmbientGeneric, m_fActive, FIELD_BOOLEAN ), DEFINE_FIELD( CAmbientGeneric, m_fLooping, FIELD_BOOLEAN ), - DEFINE_FIELD( CAmbientGeneric, m_iChannel, FIELD_INTEGER ), //LRC - DEFINE_FIELD( CAmbientGeneric, m_pPlayFrom, FIELD_EDICT ), //LRC // HACKHACK - This is not really in the spirit of the save/restore design, but save this // out as a binary data block. If the dynpitchvol_t is changed, old saved games will NOT @@ -200,10 +191,9 @@ void CAmbientGeneric :: Spawn( void ) if ( FStringNull( pev->message ) || strlen( szSoundFile ) < 1 ) { - ALERT( at_error, "ambient_generic \"%s\" at (%f, %f, %f) has no sound file\n", - STRING(pev->targetname), pev->origin.x, pev->origin.y, pev->origin.z ); - SetNextThink( 0.1 ); - SetThink(&CAmbientGeneric :: SUB_Remove ); + ALERT( at_error, "EMPTY AMBIENT AT: %f, %f, %f\n", pev->origin.x, pev->origin.y, pev->origin.z ); + pev->nextthink = gpGlobals->time + 0.1; + SetThink( SUB_Remove ); return; } pev->solid = SOLID_NOT; @@ -213,12 +203,12 @@ void CAmbientGeneric :: Spawn( void ) // of ambient sound's pitch or volume. Don't // start thinking yet. - SetThink(&CAmbientGeneric ::RampThink); - DontThink(); + SetThink(RampThink); + pev->nextthink = 0; // allow on/off switching via 'use' function. - SetUse(&CAmbientGeneric :: ToggleUse ); + SetUse ( ToggleUse ); m_fActive = FALSE; @@ -229,8 +219,7 @@ void CAmbientGeneric :: Spawn( void ) Precache( ); } -// this function needs to be called when the game is loaded, not just when the entity spawns. -// Don't make this a PostSpawn function. + void CAmbientGeneric :: Precache( void ) { char* szSoundFile = (char*) STRING(pev->message); @@ -249,51 +238,15 @@ void CAmbientGeneric :: Precache( void ) if (m_fLooping) m_fActive = TRUE; } - - if (pev->target) - { - CBaseEntity *pTarget = UTIL_FindEntityByTargetname( NULL, STRING(pev->target)); - if (!pTarget) - { - ALERT(at_debug, "WARNING: ambient_generic \"%s\" can't find \"%s\", its entity to play from.\n", - STRING(pev->targetname), STRING(pev->target)); - } - else - m_pPlayFrom = ENT(pTarget->pev); - } - if ( m_fActive ) { - if (m_pPlayFrom) - { - SetThink(&CAmbientGeneric ::StartPlayFrom); //LRC -// EMIT_SOUND_DYN( m_pPlayFrom, m_iChannel, szSoundFile, //LRC -// (m_dpv.vol * 0.01), m_flAttenuation, SND_SPAWNING, m_dpv.pitch); - -// ALERT(at_console, "AMBGEN: spawn start\n"); - } - else - { UTIL_EmitAmbientSound ( ENT(pev), pev->origin, szSoundFile, (m_dpv.vol * 0.01), m_flAttenuation, SND_SPAWNING, m_dpv.pitch); - } - SetNextThink( 0.1 ); + + pev->nextthink = gpGlobals->time + 0.1; } } -//LRC - for some reason, I can't get other entities to start playing sounds during Activate; -// this function is used to delay the effect until the first Think, which seems to fix the problem. -void CAmbientGeneric :: StartPlayFrom( void ) -{ - char* szSoundFile = (char*) STRING(pev->message); - - EMIT_SOUND_DYN( m_pPlayFrom, m_iChannel, szSoundFile, //LRC - (m_dpv.vol * 0.01), m_flAttenuation, SND_SPAWNING, m_dpv.pitch); - - SetThink(&CAmbientGeneric ::RampThink); - SetNextThink( 0.1 ); -} - // RampThink - Think at 5hz if we are dynamically modifying // pitch or volume of the playing sound. This function will // ramp pitch and/or volume up or down, modify pitch/volume @@ -337,15 +290,8 @@ void CAmbientGeneric :: RampThink( void ) m_dpv.spindown = 0; // done with ramp down // shut sound off - if (m_pPlayFrom) - { - STOP_SOUND( m_pPlayFrom, m_iChannel, szSoundFile); //LRC - } - else - { - UTIL_EmitAmbientSound(ENT(pev), pev->origin, szSoundFile, - 0, 0, SND_STOP, 0); - } + UTIL_EmitAmbientSound(ENT(pev), pev->origin, szSoundFile, + 0, 0, SND_STOP, 0); // return without setting nextthink return; @@ -386,15 +332,8 @@ void CAmbientGeneric :: RampThink( void ) m_dpv.fadeout = 0; // done with ramp down // shut sound off - if (m_pPlayFrom) - { - STOP_SOUND( m_pPlayFrom, m_iChannel, szSoundFile); //LRC - } - else - { - UTIL_EmitAmbientSound(ENT(pev), pev->origin, szSoundFile, - 0, 0, SND_STOP, 0); - } + UTIL_EmitAmbientSound(ENT(pev), pev->origin, szSoundFile, + 0, 0, SND_STOP, 0); // return without setting nextthink return; @@ -494,20 +433,12 @@ void CAmbientGeneric :: RampThink( void ) if (pitch == PITCH_NORM) pitch = PITCH_NORM + 1; // don't send 'no pitch' ! - if (m_pPlayFrom) - { - EMIT_SOUND_DYN( m_pPlayFrom, m_iChannel, szSoundFile, (vol * 0.01), //LRC - m_flAttenuation, flags, pitch); - } - else - { - UTIL_EmitAmbientSound(ENT(pev), pev->origin, szSoundFile, - (vol * 0.01), m_flAttenuation, flags, pitch); - } + UTIL_EmitAmbientSound(ENT(pev), pev->origin, szSoundFile, + (vol * 0.01), m_flAttenuation, flags, pitch); } // update ramps at 5hz - SetNextThink( 0.2 ); + pev->nextthink = gpGlobals->time + 0.2; return; } @@ -622,15 +553,8 @@ void CAmbientGeneric :: ToggleUse ( CBaseEntity *pActivator, CBaseEntity *pCalle m_dpv.pitch = fraction * 255; - if (m_pPlayFrom) - { - EMIT_SOUND_DYN( m_pPlayFrom, m_iChannel, szSoundFile, 0, 0, SND_CHANGE_PITCH, m_dpv.pitch); - } - else - { - UTIL_EmitAmbientSound(ENT(pev), pev->origin, szSoundFile, - 0, 0, SND_CHANGE_PITCH, m_dpv.pitch); - } + UTIL_EmitAmbientSound(ENT(pev), pev->origin, szSoundFile, + 0, 0, SND_CHANGE_PITCH, m_dpv.pitch); return; } @@ -662,7 +586,7 @@ void CAmbientGeneric :: ToggleUse ( CBaseEntity *pActivator, CBaseEntity *pCalle m_dpv.pitchrun = m_dpv.pitchstart + pitchinc * m_dpv.cspincount; if (m_dpv.pitchrun > 255) m_dpv.pitchrun = 255; - SetNextThink( 0.1 ); + pev->nextthink = gpGlobals->time + 0.1; } } @@ -681,17 +605,11 @@ void CAmbientGeneric :: ToggleUse ( CBaseEntity *pActivator, CBaseEntity *pCalle m_dpv.fadeout = m_dpv.fadeoutsav; m_dpv.fadein = 0; - SetNextThink( 0.1 ); - } - else if (m_pPlayFrom) - { - STOP_SOUND( m_pPlayFrom, m_iChannel, szSoundFile); + pev->nextthink = gpGlobals->time + 0.1; } else - { UTIL_EmitAmbientSound(ENT(pev), pev->origin, szSoundFile, 0, 0, SND_STOP, 0); - } } } else @@ -703,39 +621,21 @@ void CAmbientGeneric :: ToggleUse ( CBaseEntity *pActivator, CBaseEntity *pCalle // and then restarted. if (m_fLooping) - { m_fActive = TRUE; - } - else if (m_pPlayFrom) - { - STOP_SOUND( m_pPlayFrom, m_iChannel, szSoundFile); //LRC - } else - { // shut sound off now - may be interrupting a long non-looping sound UTIL_EmitAmbientSound(ENT(pev), pev->origin, szSoundFile, 0, 0, SND_STOP, 0); - } // init all ramp params for startup InitModulationParms(); - // AJH / MJB - [LR] volume field: - if (pev->noise) m_dpv.vol = CalcLocus_Ratio(this, STRING(pev->noise)); - - if (m_pPlayFrom) - { - EMIT_SOUND_DYN( m_pPlayFrom, m_iChannel, szSoundFile, //LRC - (m_dpv.vol * 0.01), m_flAttenuation, 0, m_dpv.pitch); - } - else - { - UTIL_EmitAmbientSound(ENT(pev), pev->origin, szSoundFile, - (m_dpv.vol * 0.01), m_flAttenuation, 0, m_dpv.pitch); - } + UTIL_EmitAmbientSound(ENT(pev), pev->origin, szSoundFile, + (m_dpv.vol * 0.01), m_flAttenuation, 0, m_dpv.pitch); - SetNextThink( 0.1 ); + pev->nextthink = gpGlobals->time + 0.1; + } } // KeyValue - load keyvalue pairs into member data of the @@ -746,15 +646,8 @@ void CAmbientGeneric :: KeyValue( KeyValueData *pkvd ) // NOTE: changing any of the modifiers in this code // NOTE: also requires changing InitModulationParms code. - // channel (e.g. CHAN_STATIC, etc) - if (FStrEq(pkvd->szKeyName, "channel")) - { - m_iChannel = atoi(pkvd->szValue); - pkvd->fHandled = TRUE; - } - // preset - else if (FStrEq(pkvd->szKeyName, "preset")) + if (FStrEq(pkvd->szKeyName, "preset")) { m_dpv.preset = atoi(pkvd->szValue); pkvd->fHandled = TRUE; @@ -1057,8 +950,8 @@ void CEnvSound :: Think( void ) //CLIENT_COMMAND(pentPlayer, "room_type %f", m_flRoomtype); - MESSAGE_BEGIN( MSG_ONE, gmsgRoomType, NULL, pentPlayer ); // use the magic #1 for "one client" - WRITE_BYTE( (int)m_flRoomtype ); // sequence number + MESSAGE_BEGIN( MSG_ONE, SVC_ROOMTYPE, NULL, pentPlayer ); // use the magic #1 for "one client" + WRITE_SHORT( (short)m_flRoomtype ); // sequence number MESSAGE_END(); // crank up nextthink rate for new active sound entity @@ -1073,11 +966,11 @@ void CEnvSound :: Think( void ) // not in range. do nothing, fall through to think_fast... env_sound_Think_fast: - SetNextThink( 0.25 ); + pev->nextthink = gpGlobals->time + 0.25; return; env_sound_Think_slow: - SetNextThink( 0.75 ); + pev->nextthink = gpGlobals->time + 0.75; return; } @@ -1089,81 +982,7 @@ env_sound_Think_slow: void CEnvSound :: Spawn( ) { // spread think times - SetNextThink( RANDOM_FLOAT(0.0, 0.5) ); -} - -//===================== -//LRC - trigger_sound -//===================== -class CTriggerSound : public CBaseDelay -{ -public: - void KeyValue( KeyValueData* pkvd); - void Spawn( void ); - void Touch( CBaseEntity *pOther ); - - virtual int Save( CSave &save ); - virtual int Restore( CRestore &restore ); - static TYPEDESCRIPTION m_SaveData[]; - virtual int ObjectCaps( void ) { return CBaseDelay :: ObjectCaps() & ~FCAP_ACROSS_TRANSITION; } - - float m_flRoomtype; - string_t m_iszMaster; -}; - -LINK_ENTITY_TO_CLASS( trigger_sound, CTriggerSound ); -TYPEDESCRIPTION CTriggerSound::m_SaveData[] = -{ - DEFINE_FIELD( CTriggerSound, m_flRoomtype, FIELD_FLOAT ), - DEFINE_FIELD( CTriggerSound, m_iszMaster, FIELD_FLOAT ), -}; - -IMPLEMENT_SAVERESTORE( CTriggerSound, CBaseDelay ); - -void CTriggerSound :: KeyValue( KeyValueData *pkvd ) -{ - if (FStrEq(pkvd->szKeyName, "roomtype")) - { - m_flRoomtype = atof(pkvd->szValue); - pkvd->fHandled = TRUE; - } - else if (FStrEq(pkvd->szKeyName, "master")) - { - m_iszMaster = ALLOC_STRING(pkvd->szValue); - pkvd->fHandled = TRUE; - } - else - CBaseEntity::KeyValue( pkvd ); -} - -void CTriggerSound :: Touch( CBaseEntity *pOther ) -{ - if (!UTIL_IsMasterTriggered(m_iszMaster, pOther)) return; - - if (pOther->IsPlayer()) - { - CBasePlayer *pPlayer = (CBasePlayer*)pOther; - if (pPlayer->m_pentSndLast != this->edict()) - { - pPlayer->m_pentSndLast = ENT(pev); - pPlayer->m_flSndRoomtype = m_flRoomtype; - pPlayer->m_flSndRange = 0; - - MESSAGE_BEGIN( MSG_ONE, gmsgRoomType, NULL, pPlayer->edict() ); // use the magic #1 for "one client" - WRITE_BYTE( (int)m_flRoomtype ); // sequence number - MESSAGE_END(); - - SUB_UseTargets(pPlayer, USE_TOGGLE, 0); - } - } -} - -void CTriggerSound :: Spawn( ) -{ - pev->solid = SOLID_TRIGGER; - pev->movetype = MOVETYPE_NONE; - SET_MODEL(ENT(pev), STRING(pev->model)); // set size and link into world - SetBits( pev->effects, EF_NODRAW ); + pev->nextthink = gpGlobals->time + RANDOM_FLOAT(0.0, 0.5); } // ==================== SENTENCE GROUPS, UTILITY FUNCTIONS ====================================== @@ -1379,7 +1198,7 @@ int SENTENCEG_PlayRndSz(edict_t *entity, const char *szgroupname, isentenceg = SENTENCEG_GetIndex(szgroupname); if (isentenceg < 0) { - ALERT( at_debug, "No such sentence group %s\n", szgroupname ); + ALERT( at_console, "No such sentence group %s\n", szgroupname ); return -1; } @@ -1461,7 +1280,7 @@ void SENTENCEG_Init() int filePos = 0, fileSize; - byte *pMemFile = g_engfuncs.pfnLoadFile( "sound/sentences.txt", &fileSize ); + byte *pMemFile = g_engfuncs.pfnLoadFileForMe( "sound/sentences.txt", &fileSize ); if ( !pMemFile ) return; @@ -1729,7 +1548,7 @@ void TEXTURETYPE_Init() gcTextures = 0; memset(buffer, 0, 512); - pMemFile = g_engfuncs.pfnLoadFile( "sound/materials.txt", &fileSize ); + pMemFile = g_engfuncs.pfnLoadFileForMe( "sound/materials.txt", &fileSize ); if ( !pMemFile ) return; @@ -1799,7 +1618,6 @@ char TEXTURETYPE_Find(char *name) // play a strike sound based on the texture that was hit by the attack traceline. VecSrc/VecEnd are the // original traceline endpoints used by the attacker, iBulletType is the type of bullet that hit the texture. // returns volume of strike instrument (crowbar) to play -// (this is not used for footsteps, only attack sound effects. --LRC) float TEXTURETYPE_PlaySound(TraceResult *ptr, Vector vecSrc, Vector vecEnd, int iBulletType) { @@ -2005,20 +1823,20 @@ void CSpeaker :: Spawn( void ) if ( !m_preset && (FStringNull( pev->message ) || strlen( szSoundFile ) < 1 )) { ALERT( at_error, "SPEAKER with no Level/Sentence! at: %f, %f, %f\n", pev->origin.x, pev->origin.y, pev->origin.z ); - SetNextThink( 0.1 ); - SetThink(&CSpeaker :: SUB_Remove ); + pev->nextthink = gpGlobals->time + 0.1; + SetThink( SUB_Remove ); return; } pev->solid = SOLID_NOT; pev->movetype = MOVETYPE_NONE; - SetThink(&CSpeaker ::SpeakerThink); - DontThink(); + SetThink(SpeakerThink); + pev->nextthink = 0.0; // allow on/off switching via 'use' function. - SetUse(&CSpeaker :: ToggleUse ); + SetUse ( ToggleUse ); Precache( ); } @@ -2030,7 +1848,7 @@ void CSpeaker :: Precache( void ) { if ( !FBitSet (pev->spawnflags, SPEAKER_START_SILENT ) ) // set first announcement time for random n second - SetNextThink( RANDOM_FLOAT(5.0, 15.0) ); + pev->nextthink = gpGlobals->time + RANDOM_FLOAT(5.0, 15.0); } void CSpeaker :: SpeakerThink( void ) { @@ -2044,7 +1862,7 @@ void CSpeaker :: SpeakerThink( void ) // Wait for the talkmonster to finish first. if (gpGlobals->time <= CTalkMonster::g_talkWaitTime) { - AbsoluteNextThink( CTalkMonster::g_talkWaitTime + RANDOM_FLOAT( 5, 10 ) ); + pev->nextthink = CTalkMonster::g_talkWaitTime + RANDOM_FLOAT( 5, 10 ); return; } @@ -2076,17 +1894,18 @@ void CSpeaker :: SpeakerThink( void ) flvolume, flattenuation, flags, pitch); // shut off and reset - DontThink(); + pev->nextthink = 0.0; } else { // make random announcement from sentence group if (SENTENCEG_PlayRndSz(ENT(pev), szSoundFile, flvolume, flattenuation, flags, pitch) < 0) - ALERT(at_debug, "Level Design Error!\nSPEAKER has bad sentence group name: %s\n",szSoundFile); + ALERT(at_console, "Level Design Error!\nSPEAKER has bad sentence group name: %s\n",szSoundFile); // set next announcement time for random 5 to 10 minute delay - SetNextThink( RANDOM_FLOAT(ANNOUNCE_MINUTES_MIN * 60.0, ANNOUNCE_MINUTES_MAX * 60.0) ); + pev->nextthink = gpGlobals->time + + RANDOM_FLOAT(ANNOUNCE_MINUTES_MIN * 60.0, ANNOUNCE_MINUTES_MAX * 60.0); CTalkMonster::g_talkWaitTime = gpGlobals->time + 5; // time delay until it's ok to speak: used so that two NPCs don't talk at once } @@ -2100,7 +1919,7 @@ void CSpeaker :: SpeakerThink( void ) // void CSpeaker :: ToggleUse ( CBaseEntity *pActivator, CBaseEntity *pCaller, USE_TYPE useType, float value ) { - int fActive = (m_fNextThink > 0.0); + int fActive = (pev->nextthink > 0.0); // fActive is TRUE only if an announcement is pending @@ -2115,14 +1934,14 @@ void CSpeaker :: ToggleUse ( CBaseEntity *pActivator, CBaseEntity *pCaller, USE_ if ( useType == USE_ON ) { // turn on announcements - SetNextThink( 0.1 ); + pev->nextthink = gpGlobals->time + 0.1; return; } if ( useType == USE_OFF ) { // turn off announcements - DontThink(); + pev->nextthink = 0.0; return; } @@ -2133,12 +1952,12 @@ void CSpeaker :: ToggleUse ( CBaseEntity *pActivator, CBaseEntity *pCaller, USE_ if ( fActive ) { // turn off announcements - DontThink(); + pev->nextthink = 0.0; } else { // turn on announcements - SetNextThink( 0.1 ); + pev->nextthink = gpGlobals->time + 0.1; } } @@ -2156,4 +1975,4 @@ void CSpeaker :: KeyValue( KeyValueData *pkvd ) } else CBaseEntity::KeyValue( pkvd ); -} +} \ No newline at end of file diff --git a/spirit/soundent.cpp b/dlls/soundent.cpp similarity index 90% rename from spirit/soundent.cpp rename to dlls/soundent.cpp index 726f74fb..93c70a7e 100644 --- a/spirit/soundent.cpp +++ b/dlls/soundent.cpp @@ -1,379 +1,379 @@ -/*** -* -* Copyright (c) 1996-2002, 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. -* -****/ -#include "extdll.h" -#include "util.h" -#include "cbase.h" -#include "monsters.h" -#include "soundent.h" - - -LINK_ENTITY_TO_CLASS( soundent, CSoundEnt ); - -CSoundEnt *pSoundEnt; - -//========================================================= -// CSound - Clear - zeros all fields for a sound -//========================================================= -void CSound :: Clear ( void ) -{ - m_vecOrigin = g_vecZero; - m_iType = 0; - m_iVolume = 0; - m_flExpireTime = 0; - m_iNext = SOUNDLIST_EMPTY; - m_iNextAudible = 0; -} - -//========================================================= -// Reset - clears the volume, origin, and type for a sound, -// but doesn't expire or unlink it. -//========================================================= -void CSound :: Reset ( void ) -{ - m_vecOrigin = g_vecZero; - m_iType = 0; - m_iVolume = 0; - m_iNext = SOUNDLIST_EMPTY; -} - -//========================================================= -// FIsSound - returns TRUE if the sound is an Audible sound -//========================================================= -BOOL CSound :: FIsSound ( void ) -{ - if ( m_iType & ( bits_SOUND_COMBAT | bits_SOUND_WORLD | bits_SOUND_PLAYER | bits_SOUND_DANGER ) ) - { - return TRUE; - } - - return FALSE; -} - -//========================================================= -// FIsScent - returns TRUE if the sound is actually a scent -//========================================================= -BOOL CSound :: FIsScent ( void ) -{ - if ( m_iType & ( bits_SOUND_CARCASS | bits_SOUND_MEAT | bits_SOUND_GARBAGE ) ) - { - return TRUE; - } - - return FALSE; -} - -//========================================================= -// Spawn -//========================================================= -void CSoundEnt :: Spawn( void ) -{ - pev->solid = SOLID_NOT; - Initialize(); - - SetNextThink( 1 ); -} - -//========================================================= -// Think - at interval, the entire active sound list is checked -// for sounds that have ExpireTimes less than or equal -// to the current world time, and these sounds are deallocated. -//========================================================= -void CSoundEnt :: Think ( void ) -{ - int iSound; - int iPreviousSound; - - SetNextThink( 0.3 );// how often to check the sound list. - - iPreviousSound = SOUNDLIST_EMPTY; - iSound = m_iActiveSound; - - while ( iSound != SOUNDLIST_EMPTY ) - { - if ( m_SoundPool[ iSound ].m_flExpireTime <= gpGlobals->time && m_SoundPool[ iSound ].m_flExpireTime != SOUND_NEVER_EXPIRE ) - { - int iNext = m_SoundPool[ iSound ].m_iNext; - - // move this sound back into the free list - FreeSound( iSound, iPreviousSound ); - - iSound = iNext; - } - else - { - iPreviousSound = iSound; - iSound = m_SoundPool[ iSound ].m_iNext; - } - } - - if ( m_fShowReport ) - { - ALERT ( at_aiconsole, "Soundlist: %d / %d (%d)\n", ISoundsInList( SOUNDLISTTYPE_ACTIVE ),ISoundsInList( SOUNDLISTTYPE_FREE ), ISoundsInList( SOUNDLISTTYPE_ACTIVE ) - m_cLastActiveSounds ); - m_cLastActiveSounds = ISoundsInList ( SOUNDLISTTYPE_ACTIVE ); - } - -} - -//========================================================= -// Precache - dummy function -//========================================================= -void CSoundEnt :: Precache ( void ) -{ -} - -//========================================================= -// FreeSound - clears the passed active sound and moves it -// to the top of the free list. TAKE CARE to only call this -// function for sounds in the Active list!! -//========================================================= -void CSoundEnt :: FreeSound ( int iSound, int iPrevious ) -{ - if ( !pSoundEnt ) - { - // no sound ent! - return; - } - - if ( iPrevious != SOUNDLIST_EMPTY ) - { - // iSound is not the head of the active list, so - // must fix the index for the Previous sound -// pSoundEnt->m_SoundPool[ iPrevious ].m_iNext = m_SoundPool[ iSound ].m_iNext; - pSoundEnt->m_SoundPool[ iPrevious ].m_iNext = pSoundEnt->m_SoundPool[ iSound ].m_iNext; - } - else - { - // the sound we're freeing IS the head of the active list. - pSoundEnt->m_iActiveSound = pSoundEnt->m_SoundPool [ iSound ].m_iNext; - } - - // make iSound the head of the Free list. - pSoundEnt->m_SoundPool[ iSound ].m_iNext = pSoundEnt->m_iFreeSound; - pSoundEnt->m_iFreeSound = iSound; -} - -//========================================================= -// IAllocSound - moves a sound from the Free list to the -// Active list returns the index of the alloc'd sound -//========================================================= -int CSoundEnt :: IAllocSound( void ) -{ - int iNewSound; - - if ( m_iFreeSound == SOUNDLIST_EMPTY ) - { - // no free sound! - ALERT ( at_debug, "Free Sound List is full!\n" ); - return SOUNDLIST_EMPTY; - } - - // there is at least one sound available, so move it to the - // Active sound list, and return its SoundPool index. - - iNewSound = m_iFreeSound;// copy the index of the next free sound - - m_iFreeSound = m_SoundPool[ m_iFreeSound ].m_iNext;// move the index down into the free list. - - m_SoundPool[ iNewSound ].m_iNext = m_iActiveSound;// point the new sound at the top of the active list. - - m_iActiveSound = iNewSound;// now make the new sound the top of the active list. You're done. - - return iNewSound; -} - -//========================================================= -// InsertSound - Allocates a free sound and fills it with -// sound info. -//========================================================= -void CSoundEnt :: InsertSound ( int iType, const Vector &vecOrigin, int iVolume, float flDuration ) -{ - int iThisSound; - - if ( !pSoundEnt ) - { - // no sound ent! - return; - } - - iThisSound = pSoundEnt->IAllocSound(); - - if ( iThisSound == SOUNDLIST_EMPTY ) - { - ALERT ( at_debug, "Could not AllocSound() for InsertSound() (DLL)\n" ); - return; - } - - pSoundEnt->m_SoundPool[ iThisSound ].m_vecOrigin = vecOrigin; - pSoundEnt->m_SoundPool[ iThisSound ].m_iType = iType; - pSoundEnt->m_SoundPool[ iThisSound ].m_iVolume = iVolume; - pSoundEnt->m_SoundPool[ iThisSound ].m_flExpireTime = gpGlobals->time + flDuration; -} - -//========================================================= -// Initialize - clears all sounds and moves them into the -// free sound list. -//========================================================= -void CSoundEnt :: Initialize ( void ) -{ - int i; - int iSound; - - m_cLastActiveSounds; - m_iFreeSound = 0; - m_iActiveSound = SOUNDLIST_EMPTY; - - for ( i = 0 ; i < MAX_WORLD_SOUNDS ; i++ ) - {// clear all sounds, and link them into the free sound list. - m_SoundPool[ i ].Clear(); - m_SoundPool[ i ].m_iNext = i + 1; - } - - m_SoundPool[ i - 1 ].m_iNext = SOUNDLIST_EMPTY;// terminate the list here. - - - // now reserve enough sounds for each client - for ( i = 0 ; i < gpGlobals->maxClients ; i++ ) - { - iSound = pSoundEnt->IAllocSound(); - - if ( iSound == SOUNDLIST_EMPTY ) - { - ALERT ( at_debug, "Could not AllocSound() for Client Reserve! (DLL)\n" ); - return; - } - - pSoundEnt->m_SoundPool[ iSound ].m_flExpireTime = SOUND_NEVER_EXPIRE; - } - - if ( CVAR_GET_FLOAT("displaysoundlist") == 1 ) - { - m_fShowReport = TRUE; - } - else - { - m_fShowReport = FALSE; - } -} - -//========================================================= -// ISoundsInList - returns the number of sounds in the desired -// sound list. -//========================================================= -int CSoundEnt :: ISoundsInList ( int iListType ) -{ - int i; - int iThisSound; - - if ( iListType == SOUNDLISTTYPE_FREE ) - { - iThisSound = m_iFreeSound; - } - else if ( iListType == SOUNDLISTTYPE_ACTIVE ) - { - iThisSound = m_iActiveSound; - } - else - { - ALERT ( at_debug, "Unknown Sound List Type!\n" ); - } - - if ( iThisSound == SOUNDLIST_EMPTY ) - { - return 0; - } - - i = 0; - - while ( iThisSound != SOUNDLIST_EMPTY ) - { - i++; - - iThisSound = m_SoundPool[ iThisSound ].m_iNext; - } - - return i; -} - -//========================================================= -// ActiveList - returns the head of the active sound list -//========================================================= -int CSoundEnt :: ActiveList ( void ) -{ - if ( !pSoundEnt ) - { - return SOUNDLIST_EMPTY; - } - - return pSoundEnt->m_iActiveSound; -} - -//========================================================= -// FreeList - returns the head of the free sound list -//========================================================= -int CSoundEnt :: FreeList ( void ) -{ - if ( !pSoundEnt ) - { - return SOUNDLIST_EMPTY; - } - - return pSoundEnt->m_iFreeSound; -} - -//========================================================= -// SoundPointerForIndex - returns a pointer to the instance -// of CSound at index's position in the sound pool. -//========================================================= -CSound* CSoundEnt :: SoundPointerForIndex( int iIndex ) -{ - if ( !pSoundEnt ) - { - return NULL; - } - - if ( iIndex > ( MAX_WORLD_SOUNDS - 1 ) ) - { - ALERT ( at_debug, "SoundPointerForIndex() - Index too large!\n" ); - return NULL; - } - - if ( iIndex < 0 ) - { - ALERT ( at_debug, "SoundPointerForIndex() - Index < 0!\n" ); - return NULL; - } - - return &pSoundEnt->m_SoundPool[ iIndex ]; -} - -//========================================================= -// Clients are numbered from 1 to MAXCLIENTS, but the client -// reserved sounds in the soundlist are from 0 to MAXCLIENTS - 1, -// so this function ensures that a client gets the proper index -// to his reserved sound in the soundlist. -//========================================================= -int CSoundEnt :: ClientSoundIndex ( edict_t *pClient ) -{ - int iReturn = ENTINDEX( pClient ) - 1; - -#ifdef _DEBUG - if ( iReturn < 0 || iReturn > gpGlobals->maxClients ) - { - ALERT ( at_debug, "** ClientSoundIndex returning a bogus value! **\n" ); - } -#endif // _DEBUG - - return iReturn; -} +/*** +* +* Copyright (c) 1996-2002, 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. +* +****/ +#include "extdll.h" +#include "util.h" +#include "cbase.h" +#include "monsters.h" +#include "soundent.h" + + +LINK_ENTITY_TO_CLASS( soundent, CSoundEnt ); + +CSoundEnt *pSoundEnt; + +//========================================================= +// CSound - Clear - zeros all fields for a sound +//========================================================= +void CSound :: Clear ( void ) +{ + m_vecOrigin = g_vecZero; + m_iType = 0; + m_iVolume = 0; + m_flExpireTime = 0; + m_iNext = SOUNDLIST_EMPTY; + m_iNextAudible = 0; +} + +//========================================================= +// Reset - clears the volume, origin, and type for a sound, +// but doesn't expire or unlink it. +//========================================================= +void CSound :: Reset ( void ) +{ + m_vecOrigin = g_vecZero; + m_iType = 0; + m_iVolume = 0; + m_iNext = SOUNDLIST_EMPTY; +} + +//========================================================= +// FIsSound - returns TRUE if the sound is an Audible sound +//========================================================= +BOOL CSound :: FIsSound ( void ) +{ + if ( m_iType & ( bits_SOUND_COMBAT | bits_SOUND_WORLD | bits_SOUND_PLAYER | bits_SOUND_DANGER ) ) + { + return TRUE; + } + + return FALSE; +} + +//========================================================= +// FIsScent - returns TRUE if the sound is actually a scent +//========================================================= +BOOL CSound :: FIsScent ( void ) +{ + if ( m_iType & ( bits_SOUND_CARCASS | bits_SOUND_MEAT | bits_SOUND_GARBAGE ) ) + { + return TRUE; + } + + return FALSE; +} + +//========================================================= +// Spawn +//========================================================= +void CSoundEnt :: Spawn( void ) +{ + pev->solid = SOLID_NOT; + Initialize(); + + pev->nextthink = gpGlobals->time + 1; +} + +//========================================================= +// Think - at interval, the entire active sound list is checked +// for sounds that have ExpireTimes less than or equal +// to the current world time, and these sounds are deallocated. +//========================================================= +void CSoundEnt :: Think ( void ) +{ + int iSound; + int iPreviousSound; + + pev->nextthink = gpGlobals->time + 0.3;// how often to check the sound list. + + iPreviousSound = SOUNDLIST_EMPTY; + iSound = m_iActiveSound; + + while ( iSound != SOUNDLIST_EMPTY ) + { + if ( m_SoundPool[ iSound ].m_flExpireTime <= gpGlobals->time && m_SoundPool[ iSound ].m_flExpireTime != SOUND_NEVER_EXPIRE ) + { + int iNext = m_SoundPool[ iSound ].m_iNext; + + // move this sound back into the free list + FreeSound( iSound, iPreviousSound ); + + iSound = iNext; + } + else + { + iPreviousSound = iSound; + iSound = m_SoundPool[ iSound ].m_iNext; + } + } + + if ( m_fShowReport ) + { + ALERT ( at_aiconsole, "Soundlist: %d / %d (%d)\n", ISoundsInList( SOUNDLISTTYPE_ACTIVE ),ISoundsInList( SOUNDLISTTYPE_FREE ), ISoundsInList( SOUNDLISTTYPE_ACTIVE ) - m_cLastActiveSounds ); + m_cLastActiveSounds = ISoundsInList ( SOUNDLISTTYPE_ACTIVE ); + } + +} + +//========================================================= +// Precache - dummy function +//========================================================= +void CSoundEnt :: Precache ( void ) +{ +} + +//========================================================= +// FreeSound - clears the passed active sound and moves it +// to the top of the free list. TAKE CARE to only call this +// function for sounds in the Active list!! +//========================================================= +void CSoundEnt :: FreeSound ( int iSound, int iPrevious ) +{ + if ( !pSoundEnt ) + { + // no sound ent! + return; + } + + if ( iPrevious != SOUNDLIST_EMPTY ) + { + // iSound is not the head of the active list, so + // must fix the index for the Previous sound +// pSoundEnt->m_SoundPool[ iPrevious ].m_iNext = m_SoundPool[ iSound ].m_iNext; + pSoundEnt->m_SoundPool[ iPrevious ].m_iNext = pSoundEnt->m_SoundPool[ iSound ].m_iNext; + } + else + { + // the sound we're freeing IS the head of the active list. + pSoundEnt->m_iActiveSound = pSoundEnt->m_SoundPool [ iSound ].m_iNext; + } + + // make iSound the head of the Free list. + pSoundEnt->m_SoundPool[ iSound ].m_iNext = pSoundEnt->m_iFreeSound; + pSoundEnt->m_iFreeSound = iSound; +} + +//========================================================= +// IAllocSound - moves a sound from the Free list to the +// Active list returns the index of the alloc'd sound +//========================================================= +int CSoundEnt :: IAllocSound( void ) +{ + int iNewSound; + + if ( m_iFreeSound == SOUNDLIST_EMPTY ) + { + // no free sound! + ALERT ( at_console, "Free Sound List is full!\n" ); + return SOUNDLIST_EMPTY; + } + + // there is at least one sound available, so move it to the + // Active sound list, and return its SoundPool index. + + iNewSound = m_iFreeSound;// copy the index of the next free sound + + m_iFreeSound = m_SoundPool[ m_iFreeSound ].m_iNext;// move the index down into the free list. + + m_SoundPool[ iNewSound ].m_iNext = m_iActiveSound;// point the new sound at the top of the active list. + + m_iActiveSound = iNewSound;// now make the new sound the top of the active list. You're done. + + return iNewSound; +} + +//========================================================= +// InsertSound - Allocates a free sound and fills it with +// sound info. +//========================================================= +void CSoundEnt :: InsertSound ( int iType, const Vector &vecOrigin, int iVolume, float flDuration ) +{ + int iThisSound; + + if ( !pSoundEnt ) + { + // no sound ent! + return; + } + + iThisSound = pSoundEnt->IAllocSound(); + + if ( iThisSound == SOUNDLIST_EMPTY ) + { + ALERT ( at_console, "Could not AllocSound() for InsertSound() (DLL)\n" ); + return; + } + + pSoundEnt->m_SoundPool[ iThisSound ].m_vecOrigin = vecOrigin; + pSoundEnt->m_SoundPool[ iThisSound ].m_iType = iType; + pSoundEnt->m_SoundPool[ iThisSound ].m_iVolume = iVolume; + pSoundEnt->m_SoundPool[ iThisSound ].m_flExpireTime = gpGlobals->time + flDuration; +} + +//========================================================= +// Initialize - clears all sounds and moves them into the +// free sound list. +//========================================================= +void CSoundEnt :: Initialize ( void ) +{ + int i; + int iSound; + + m_cLastActiveSounds; + m_iFreeSound = 0; + m_iActiveSound = SOUNDLIST_EMPTY; + + for ( i = 0 ; i < MAX_WORLD_SOUNDS ; i++ ) + {// clear all sounds, and link them into the free sound list. + m_SoundPool[ i ].Clear(); + m_SoundPool[ i ].m_iNext = i + 1; + } + + m_SoundPool[ i - 1 ].m_iNext = SOUNDLIST_EMPTY;// terminate the list here. + + + // now reserve enough sounds for each client + for ( i = 0 ; i < gpGlobals->maxClients ; i++ ) + { + iSound = pSoundEnt->IAllocSound(); + + if ( iSound == SOUNDLIST_EMPTY ) + { + ALERT ( at_console, "Could not AllocSound() for Client Reserve! (DLL)\n" ); + return; + } + + pSoundEnt->m_SoundPool[ iSound ].m_flExpireTime = SOUND_NEVER_EXPIRE; + } + + if ( CVAR_GET_FLOAT("displaysoundlist") == 1 ) + { + m_fShowReport = TRUE; + } + else + { + m_fShowReport = FALSE; + } +} + +//========================================================= +// ISoundsInList - returns the number of sounds in the desired +// sound list. +//========================================================= +int CSoundEnt :: ISoundsInList ( int iListType ) +{ + int i; + int iThisSound; + + if ( iListType == SOUNDLISTTYPE_FREE ) + { + iThisSound = m_iFreeSound; + } + else if ( iListType == SOUNDLISTTYPE_ACTIVE ) + { + iThisSound = m_iActiveSound; + } + else + { + ALERT ( at_console, "Unknown Sound List Type!\n" ); + } + + if ( iThisSound == SOUNDLIST_EMPTY ) + { + return 0; + } + + i = 0; + + while ( iThisSound != SOUNDLIST_EMPTY ) + { + i++; + + iThisSound = m_SoundPool[ iThisSound ].m_iNext; + } + + return i; +} + +//========================================================= +// ActiveList - returns the head of the active sound list +//========================================================= +int CSoundEnt :: ActiveList ( void ) +{ + if ( !pSoundEnt ) + { + return SOUNDLIST_EMPTY; + } + + return pSoundEnt->m_iActiveSound; +} + +//========================================================= +// FreeList - returns the head of the free sound list +//========================================================= +int CSoundEnt :: FreeList ( void ) +{ + if ( !pSoundEnt ) + { + return SOUNDLIST_EMPTY; + } + + return pSoundEnt->m_iFreeSound; +} + +//========================================================= +// SoundPointerForIndex - returns a pointer to the instance +// of CSound at index's position in the sound pool. +//========================================================= +CSound* CSoundEnt :: SoundPointerForIndex( int iIndex ) +{ + if ( !pSoundEnt ) + { + return NULL; + } + + if ( iIndex > ( MAX_WORLD_SOUNDS - 1 ) ) + { + ALERT ( at_console, "SoundPointerForIndex() - Index too large!\n" ); + return NULL; + } + + if ( iIndex < 0 ) + { + ALERT ( at_console, "SoundPointerForIndex() - Index < 0!\n" ); + return NULL; + } + + return &pSoundEnt->m_SoundPool[ iIndex ]; +} + +//========================================================= +// Clients are numbered from 1 to MAXCLIENTS, but the client +// reserved sounds in the soundlist are from 0 to MAXCLIENTS - 1, +// so this function ensures that a client gets the proper index +// to his reserved sound in the soundlist. +//========================================================= +int CSoundEnt :: ClientSoundIndex ( edict_t *pClient ) +{ + int iReturn = ENTINDEX( pClient ) - 1; + +#ifdef _DEBUG + if ( iReturn < 0 || iReturn > gpGlobals->maxClients ) + { + ALERT ( at_console, "** ClientSoundIndex returning a bogus value! **\n" ); + } +#endif // _DEBUG + + return iReturn; +} \ No newline at end of file diff --git a/spirit/soundent.h b/dlls/soundent.h similarity index 97% rename from spirit/soundent.h rename to dlls/soundent.h index 150daac7..2393cb40 100644 --- a/spirit/soundent.h +++ b/dlls/soundent.h @@ -1,95 +1,95 @@ -/*** -* -* Copyright (c) 1996-2002, 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. -* -****/ -//========================================================= -// Soundent.h - the entity that spawns when the world -// spawns, and handles the world's active and free sound -// lists. -//========================================================= - -#define MAX_WORLD_SOUNDS 64 // maximum number of sounds handled by the world at one time. - -#define bits_SOUND_NONE 0 -#define bits_SOUND_COMBAT ( 1 << 0 )// gunshots, explosions -#define bits_SOUND_WORLD ( 1 << 1 )// door opening/closing, glass breaking -#define bits_SOUND_PLAYER ( 1 << 2 )// all noises generated by player. walking, shooting, falling, splashing -#define bits_SOUND_CARCASS ( 1 << 3 )// dead body -#define bits_SOUND_MEAT ( 1 << 4 )// gib or pork chop -#define bits_SOUND_DANGER ( 1 << 5 )// pending danger. Grenade that is about to explode, explosive barrel that is damaged, falling crate -#define bits_SOUND_GARBAGE ( 1 << 6 )// trash cans, banana peels, old fast food bags. - -#define bits_ALL_SOUNDS 0xFFFFFFFF - -#define SOUNDLIST_EMPTY -1 - -#define SOUNDLISTTYPE_FREE 1// identifiers passed to functions that can operate on either list, to indicate which list to operate on. -#define SOUNDLISTTYPE_ACTIVE 2 - -#define SOUND_NEVER_EXPIRE -1 // with this set as a sound's ExpireTime, the sound will never expire. - -//========================================================= -// CSound - an instance of a sound in the world. -//========================================================= -class CSound -{ -public: - - void Clear ( void ); - void Reset ( void ); - - Vector m_vecOrigin; // sound's location in space - int m_iType; // what type of sound this is - int m_iVolume; // how loud the sound is - float m_flExpireTime; // when the sound should be purged from the list - int m_iNext; // index of next sound in this list ( Active or Free ) - int m_iNextAudible; // temporary link that monsters use to build a list of audible sounds - - BOOL FIsSound( void ); - BOOL FIsScent( void ); -}; - -//========================================================= -// CSoundEnt - a single instance of this entity spawns when -// the world spawns. The SoundEnt's job is to update the -// world's Free and Active sound lists. -//========================================================= -class CSoundEnt : public CBaseEntity -{ -public: - - void Precache ( void ); - void Spawn( void ); - void Think( void ); - void Initialize ( void ); - - static void InsertSound ( int iType, const Vector &vecOrigin, int iVolume, float flDuration ); - static void FreeSound ( int iSound, int iPrevious ); - static int ActiveList( void );// return the head of the active list - static int FreeList( void );// return the head of the free list - static CSound* SoundPointerForIndex( int iIndex );// return a pointer for this index in the sound list - static int ClientSoundIndex ( edict_t *pClient ); - - BOOL IsEmpty( void ) { return m_iActiveSound == SOUNDLIST_EMPTY; } - int ISoundsInList ( int iListType ); - int IAllocSound ( void ); - virtual int ObjectCaps( void ) { return FCAP_DONT_SAVE; } - - int m_iFreeSound; // index of the first sound in the free sound list - int m_iActiveSound; // indes of the first sound in the active sound list - int m_cLastActiveSounds; // keeps track of the number of active sounds at the last update. (for diagnostic work) - BOOL m_fShowReport; // if true, dump information about free/active sounds. - -private: - CSound m_SoundPool[ MAX_WORLD_SOUNDS ]; -}; +/*** +* +* Copyright (c) 1996-2002, 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. +* +****/ +//========================================================= +// Soundent.h - the entity that spawns when the world +// spawns, and handles the world's active and free sound +// lists. +//========================================================= + +#define MAX_WORLD_SOUNDS 64 // maximum number of sounds handled by the world at one time. + +#define bits_SOUND_NONE 0 +#define bits_SOUND_COMBAT ( 1 << 0 )// gunshots, explosions +#define bits_SOUND_WORLD ( 1 << 1 )// door opening/closing, glass breaking +#define bits_SOUND_PLAYER ( 1 << 2 )// all noises generated by player. walking, shooting, falling, splashing +#define bits_SOUND_CARCASS ( 1 << 3 )// dead body +#define bits_SOUND_MEAT ( 1 << 4 )// gib or pork chop +#define bits_SOUND_DANGER ( 1 << 5 )// pending danger. Grenade that is about to explode, explosive barrel that is damaged, falling crate +#define bits_SOUND_GARBAGE ( 1 << 6 )// trash cans, banana peels, old fast food bags. + +#define bits_ALL_SOUNDS 0xFFFFFFFF + +#define SOUNDLIST_EMPTY -1 + +#define SOUNDLISTTYPE_FREE 1// identifiers passed to functions that can operate on either list, to indicate which list to operate on. +#define SOUNDLISTTYPE_ACTIVE 2 + +#define SOUND_NEVER_EXPIRE -1 // with this set as a sound's ExpireTime, the sound will never expire. + +//========================================================= +// CSound - an instance of a sound in the world. +//========================================================= +class CSound +{ +public: + + void Clear ( void ); + void Reset ( void ); + + Vector m_vecOrigin; // sound's location in space + int m_iType; // what type of sound this is + int m_iVolume; // how loud the sound is + float m_flExpireTime; // when the sound should be purged from the list + int m_iNext; // index of next sound in this list ( Active or Free ) + int m_iNextAudible; // temporary link that monsters use to build a list of audible sounds + + BOOL FIsSound( void ); + BOOL FIsScent( void ); +}; + +//========================================================= +// CSoundEnt - a single instance of this entity spawns when +// the world spawns. The SoundEnt's job is to update the +// world's Free and Active sound lists. +//========================================================= +class CSoundEnt : public CBaseEntity +{ +public: + + void Precache ( void ); + void Spawn( void ); + void Think( void ); + void Initialize ( void ); + + static void InsertSound ( int iType, const Vector &vecOrigin, int iVolume, float flDuration ); + static void FreeSound ( int iSound, int iPrevious ); + static int ActiveList( void );// return the head of the active list + static int FreeList( void );// return the head of the free list + static CSound* SoundPointerForIndex( int iIndex );// return a pointer for this index in the sound list + static int ClientSoundIndex ( edict_t *pClient ); + + BOOL IsEmpty( void ) { return m_iActiveSound == SOUNDLIST_EMPTY; } + int ISoundsInList ( int iListType ); + int IAllocSound ( void ); + virtual int ObjectCaps( void ) { return FCAP_DONT_SAVE; } + + int m_iFreeSound; // index of the first sound in the free sound list + int m_iActiveSound; // indes of the first sound in the active sound list + int m_cLastActiveSounds; // keeps track of the number of active sounds at the last update. (for diagnostic work) + BOOL m_fShowReport; // if true, dump information about free/active sounds. + +private: + CSound m_SoundPool[ MAX_WORLD_SOUNDS ]; +}; diff --git a/spirit/spectator.cpp b/dlls/spectator.cpp similarity index 77% rename from spirit/spectator.cpp rename to dlls/spectator.cpp index c99edfe1..9d6bef9d 100644 --- a/spirit/spectator.cpp +++ b/dlls/spectator.cpp @@ -1,147 +1,149 @@ -/*** -* -* Copyright (c) 1996-2002, 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. -* -****/ -// CBaseSpectator - -// YWB: UNDONE - -// Spectator functions -// -#include "extdll.h" -#include "util.h" -#include "cbase.h" -#include "monsters.h" -#include "spectator.h" - -/* -=========== -SpectatorConnect - -called when a spectator connects to a server -============ -*/ -void CBaseSpectator::SpectatorConnect(void) -{ - pev->flags = FL_SPECTATOR; - pev->solid = SOLID_NOT; - pev->movetype = MOVETYPE_NOCLIP; - - m_pGoalEnt = NULL; -} - -/* -=========== -SpectatorDisconnect - -called when a spectator disconnects from a server -============ -*/ -void CBaseSpectator::SpectatorDisconnect(void) -{ -} - -/* -================ -SpectatorImpulseCommand - -Called by SpectatorThink if the spectator entered an impulse -================ -*/ -void CBaseSpectator::SpectatorImpulseCommand(void) -{ - static edict_t *pGoal = NULL; - CBaseEntity *pPreviousGoal; - CBaseEntity *pCurrentGoal; - BOOL bFound; - - switch (pev->impulse) - { - case 1: - // teleport the spectator to the next spawn point; note that if the spectator is - // tracking, this doesn't do much - pPreviousGoal = (CBaseEntity*)GET_PRIVATE(pGoal); - pCurrentGoal = (CBaseEntity*)GET_PRIVATE(pGoal); - // Start at the current goal, skip the world, and stop if we looped back around - - bFound = FALSE; - while (1) - { - pCurrentGoal = UTIL_FindEntityByClassname(pCurrentGoal, "info_player_deathmatch"); - // Looped around, failure - if (pCurrentGoal == pPreviousGoal) - { - ALERT(at_debug, "Could not find a spawn spot.\n"); - break; - } - // Found a non-world entity, set success, otherwise, look for the next one. - if ( pCurrentGoal ) - { - bFound = TRUE; - break; - } - } - - if (!bFound) // Didn't find a good spot. - break; - - pGoal = ENT(pCurrentGoal->pev); - UTIL_SetOrigin( this, pGoal->v.origin ); - pev->angles = pGoal->v.angles; - pev->fixangle = FALSE; - break; - default: - ALERT(at_debug, "Unknown spectator impulse\n"); - break; - } - - pev->impulse = 0; -} - -/* -================ -SpectatorThink - -Called every frame after physics are run -================ -*/ -void CBaseSpectator::SpectatorThink(void) -{ - if (!(pev->flags & FL_SPECTATOR)) - { - pev->flags = FL_SPECTATOR; - } - - pev->solid = SOLID_NOT; - pev->movetype = MOVETYPE_NOCLIP; - - if (pev->impulse) - SpectatorImpulseCommand(); -} - -/* -=========== -Spawn - - Called when spectator is initialized: - UNDONE: Is this actually being called because spectators are not allocated in normal fashion? -============ -*/ -void CBaseSpectator::Spawn() -{ - pev->flags = FL_SPECTATOR; - pev->solid = SOLID_NOT; - pev->movetype = MOVETYPE_NOCLIP; - - m_pGoalEnt = NULL; -} +/*** +* +* Copyright (c) 1996-2002, 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. +* +****/ +// CBaseSpectator + +// YWB: UNDONE + +// Spectator functions +// +#include "extdll.h" +#include "util.h" +#include "cbase.h" +#include "monsters.h" +#include "spectator.h" + +/* +=========== +SpectatorConnect + +called when a spectator connects to a server +============ +*/ +void CBaseSpectator::SpectatorConnect(void) +{ + pev->flags = FL_SPECTATOR; + pev->solid = SOLID_NOT; + pev->movetype = MOVETYPE_NOCLIP; + + m_pGoalEnt = NULL; +} + +/* +=========== +SpectatorDisconnect + +called when a spectator disconnects from a server +============ +*/ +void CBaseSpectator::SpectatorDisconnect(void) +{ +} + +/* +================ +SpectatorImpulseCommand + +Called by SpectatorThink if the spectator entered an impulse +================ +*/ +void CBaseSpectator::SpectatorImpulseCommand(void) +{ + static edict_t *pGoal = NULL; + edict_t *pPreviousGoal; + edict_t *pCurrentGoal; + BOOL bFound; + + switch (pev->impulse) + { + case 1: + // teleport the spectator to the next spawn point + // note that if the spectator is tracking, this doesn't do + // much + pPreviousGoal = pGoal; + pCurrentGoal = pGoal; + // Start at the current goal, skip the world, and stop if we looped + // back around + + bFound = FALSE; + while (1) + { + pCurrentGoal = FIND_ENTITY_BY_CLASSNAME(pCurrentGoal, "info_player_deathmatch"); + // Looped around, failure + if (pCurrentGoal == pPreviousGoal) + { + ALERT(at_console, "Could not find a spawn spot.\n"); + break; + } + // Found a non-world entity, set success, otherwise, look for the next one. + if (!FNullEnt(pCurrentGoal)) + { + bFound = TRUE; + break; + } + } + + if (!bFound) // Didn't find a good spot. + break; + + pGoal = pCurrentGoal; + UTIL_SetOrigin( pev, pGoal->v.origin ); + pev->angles = pGoal->v.angles; + pev->fixangle = FALSE; + break; + default: + ALERT(at_console, "Unknown spectator impulse\n"); + break; + } + + pev->impulse = 0; +} + +/* +================ +SpectatorThink + +Called every frame after physics are run +================ +*/ +void CBaseSpectator::SpectatorThink(void) +{ + if (!(pev->flags & FL_SPECTATOR)) + { + pev->flags = FL_SPECTATOR; + } + + pev->solid = SOLID_NOT; + pev->movetype = MOVETYPE_NOCLIP; + + if (pev->impulse) + SpectatorImpulseCommand(); +} + +/* +=========== +Spawn + + Called when spectator is initialized: + UNDONE: Is this actually being called because spectators are not allocated in normal fashion? +============ +*/ +void CBaseSpectator::Spawn() +{ + pev->flags = FL_SPECTATOR; + pev->solid = SOLID_NOT; + pev->movetype = MOVETYPE_NOCLIP; + + m_pGoalEnt = NULL; +} diff --git a/spirit/spectator.h b/dlls/spectator.h similarity index 96% rename from spirit/spectator.h rename to dlls/spectator.h index d459a384..f832621a 100644 --- a/spirit/spectator.h +++ b/dlls/spectator.h @@ -1,27 +1,27 @@ -/*** -* -* Copyright (c) 1996-2002, 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. -* -****/ -// Spectator.h - -class CBaseSpectator : public CBaseEntity -{ -public: - void Spawn(); - void SpectatorConnect(void); - void SpectatorDisconnect(void); - void SpectatorThink(void); - -private: - void SpectatorImpulseCommand(void); +/*** +* +* Copyright (c) 1996-2002, 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. +* +****/ +// Spectator.h + +class CBaseSpectator : public CBaseEntity +{ +public: + void Spawn(); + void SpectatorConnect(void); + void SpectatorDisconnect(void); + void SpectatorThink(void); + +private: + void SpectatorImpulseCommand(void); }; \ No newline at end of file diff --git a/spirit/squad.h b/dlls/squad.h similarity index 93% rename from spirit/squad.h rename to dlls/squad.h index 9acfccb2..5f9f2499 100644 --- a/spirit/squad.h +++ b/dlls/squad.h @@ -1,20 +1,20 @@ -//========= Copyright © 1996-2002, Valve LLC, All rights reserved. ============ -// -// Purpose: New version of the slider bar -// -// $NoKeywords: $ -//============================================================================= - -//========================================================= -// squad.h -//========================================================= - -// these are special group roles that are assigned to members when the group is formed. -// the reason these are explicitly assigned and tasks like throwing grenades to flush out -// enemies is that it's bad to have two members trying to flank left at the same time, but -// ok to have two throwing grenades at the same time. When a squad member cannot attack the -// enemy, it will choose to execute its special role. -#define bits_SQUAD_FLANK_LEFT ( 1 << 0 ) -#define bits_SQUAD_FLANK_RIGHT ( 1 << 1 ) -#define bits_SQUAD_ADVANCE ( 1 << 2 ) +//========= Copyright © 1996-2002, Valve LLC, All rights reserved. ============ +// +// Purpose: New version of the slider bar +// +// $NoKeywords: $ +//============================================================================= + +//========================================================= +// squad.h +//========================================================= + +// these are special group roles that are assigned to members when the group is formed. +// the reason these are explicitly assigned and tasks like throwing grenades to flush out +// enemies is that it's bad to have two members trying to flank left at the same time, but +// ok to have two throwing grenades at the same time. When a squad member cannot attack the +// enemy, it will choose to execute its special role. +#define bits_SQUAD_FLANK_LEFT ( 1 << 0 ) +#define bits_SQUAD_FLANK_RIGHT ( 1 << 1 ) +#define bits_SQUAD_ADVANCE ( 1 << 2 ) #define bits_SQUAD_FLUSH_ATTACK ( 1 << 3 ) \ No newline at end of file diff --git a/spirit/squadmonster.cpp b/dlls/squadmonster.cpp similarity index 95% rename from spirit/squadmonster.cpp rename to dlls/squadmonster.cpp index 40666db3..d0a65ba1 100644 --- a/spirit/squadmonster.cpp +++ b/dlls/squadmonster.cpp @@ -1,643 +1,623 @@ -/*** -* -* Copyright (c) 1996-2002, 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. -* -* This source code contains proprietary and confidential information of -* Valve LLC and its suppliers. Access to this code is restricted to -* persons who have executed a written SDK license with Valve. Any access, -* use or distribution of this code by or to any unlicensed person is illegal. -* -****/ -//========================================================= -// Squadmonster functions -//========================================================= -#include "extdll.h" -#include "util.h" -#include "cbase.h" -#include "nodes.h" -#include "monsters.h" -#include "animation.h" -#include "saverestore.h" -#include "squadmonster.h" -#include "plane.h" - -//========================================================= -// Save/Restore -//========================================================= -TYPEDESCRIPTION CSquadMonster::m_SaveData[] = -{ - DEFINE_FIELD( CSquadMonster, m_hSquadLeader, FIELD_EHANDLE ), - DEFINE_ARRAY( CSquadMonster, m_hSquadMember, FIELD_EHANDLE, MAX_SQUAD_MEMBERS - 1 ), - - // DEFINE_FIELD( CSquadMonster, m_afSquadSlots, FIELD_INTEGER ), // these need to be reset after transitions! - DEFINE_FIELD( CSquadMonster, m_fEnemyEluded, FIELD_BOOLEAN ), - DEFINE_FIELD( CSquadMonster, m_flLastEnemySightTime, FIELD_TIME ), - - DEFINE_FIELD( CSquadMonster, m_iMySlot, FIELD_INTEGER ), - - -}; - -IMPLEMENT_SAVERESTORE( CSquadMonster, CBaseMonster ); - - -//========================================================= -// OccupySlot - if any slots of the passed slots are -// available, the monster will be assigned to one. -//========================================================= -BOOL CSquadMonster :: OccupySlot( int iDesiredSlots ) -{ - int i; - int iMask; - int iSquadSlots; - - if ( !InSquad() ) - { - return TRUE; - } - - if ( SquadEnemySplit() ) - { - // if the squad members aren't all fighting the same enemy, slots are disabled - // so that a squad member doesn't get stranded unable to engage his enemy because - // all of the attack slots are taken by squad members fighting other enemies. - m_iMySlot = bits_SLOT_SQUAD_SPLIT; - return TRUE; - } - - CSquadMonster *pSquadLeader = MySquadLeader(); - - if ( !( iDesiredSlots ^ pSquadLeader->m_afSquadSlots ) ) - { - // none of the desired slots are available. - return FALSE; - } - - iSquadSlots = pSquadLeader->m_afSquadSlots; - - for ( i = 0; i < NUM_SLOTS; i++ ) - { - iMask = 1<m_afSquadSlots |= iMask; - m_iMySlot = iMask; -// ALERT ( at_aiconsole, "Took slot %d - %d\n", i, m_hSquadLeader->m_afSquadSlots ); - return TRUE; - } - } - } - - return FALSE; -} - -//========================================================= -// VacateSlot -//========================================================= -void CSquadMonster :: VacateSlot() -{ - if ( m_iMySlot != bits_NO_SLOT && InSquad() ) - { -// ALERT ( at_aiconsole, "Vacated Slot %d - %d\n", m_iMySlot, m_hSquadLeader->m_afSquadSlots ); - MySquadLeader()->m_afSquadSlots &= ~m_iMySlot; - m_iMySlot = bits_NO_SLOT; - } -} - -//========================================================= -// ScheduleChange -//========================================================= -void CSquadMonster :: ScheduleChange ( void ) -{ - VacateSlot(); -} - -//========================================================= -// Killed -//========================================================= -void CSquadMonster :: Killed( entvars_t *pevAttacker, int iGib ) -{ - VacateSlot(); - - if ( InSquad() ) - { - MySquadLeader()->SquadRemove( this ); - } - - CBaseMonster :: Killed ( pevAttacker, iGib ); -} - -// These functions are still awaiting conversion to CSquadMonster - - -//========================================================= -// -// SquadRemove(), remove pRemove from my squad. -// If I am pRemove, promote m_pSquadNext to leader -// -//========================================================= -void CSquadMonster :: SquadRemove( CSquadMonster *pRemove ) -{ - ASSERT( pRemove!=NULL ); - ASSERT( this->IsLeader() ); - ASSERT( pRemove->m_hSquadLeader == this ); - - // If I'm the leader, get rid of my squad - if (pRemove == MySquadLeader()) - { - for (int i = 0; i < MAX_SQUAD_MEMBERS-1;i++) - { - CSquadMonster *pMember = MySquadMember(i); - if (pMember) - { - pMember->m_hSquadLeader = NULL; - m_hSquadMember[i] = NULL; - } - } - } - else - { - CSquadMonster *pSquadLeader = MySquadLeader(); - if (pSquadLeader) - { - for (int i = 0; i < MAX_SQUAD_MEMBERS-1;i++) - { - if (pSquadLeader->m_hSquadMember[i] == this) - { - pSquadLeader->m_hSquadMember[i] = NULL; - break; - } - } - } - } - - pRemove->m_hSquadLeader = NULL; -} - -//========================================================= -// -// SquadAdd(), add pAdd to my squad -// -//========================================================= -BOOL CSquadMonster :: SquadAdd( CSquadMonster *pAdd ) -{ - ASSERT( pAdd!=NULL ); - ASSERT( !pAdd->InSquad() ); - ASSERT( this->IsLeader() ); - - for (int i = 0; i < MAX_SQUAD_MEMBERS-1; i++) - { - if (m_hSquadMember[i] == NULL) - { - m_hSquadMember[i] = pAdd; - pAdd->m_hSquadLeader = this; - return TRUE; - } - } - return FALSE; - // should complain here -} - - -//========================================================= -// -// SquadPasteEnemyInfo - called by squad members that have -// current info on the enemy so that it can be stored for -// members who don't have current info. -// -//========================================================= -void CSquadMonster :: SquadPasteEnemyInfo ( void ) -{ - CSquadMonster *pSquadLeader = MySquadLeader( ); - if (pSquadLeader) - pSquadLeader->m_vecEnemyLKP = m_vecEnemyLKP; -} - -//========================================================= -// -// SquadCopyEnemyInfo - called by squad members who don't -// have current info on the enemy. Reads from the same fields -// in the leader's data that other squad members write to, -// so the most recent data is always available here. -// -//========================================================= -void CSquadMonster :: SquadCopyEnemyInfo ( void ) -{ - CSquadMonster *pSquadLeader = MySquadLeader( ); - if (pSquadLeader) - m_vecEnemyLKP = pSquadLeader->m_vecEnemyLKP; -} - -//========================================================= -// -// SquadMakeEnemy - makes everyone in the squad angry at -// the same entity. -// -//========================================================= -void CSquadMonster :: SquadMakeEnemy ( CBaseEntity *pEnemy ) -{ - if (!InSquad()) - return; - - if ( !pEnemy ) - { - ALERT ( at_debug, "ERROR: SquadMakeEnemy() - pEnemy is NULL!\n" ); - return; - } - - CSquadMonster *pSquadLeader = MySquadLeader( ); - for (int i = 0; i < MAX_SQUAD_MEMBERS; i++) - { - CSquadMonster *pMember = pSquadLeader->MySquadMember(i); - if (pMember) - { - // reset members who aren't activly engaged in fighting - if (pMember->m_hEnemy != pEnemy && !pMember->HasConditions( bits_COND_SEE_ENEMY)) - { - if ( pMember->m_hEnemy != NULL) - { - // remember their current enemy - pMember->PushEnemy( pMember->m_hEnemy, pMember->m_vecEnemyLKP ); - } - // give them a new enemy - pMember->m_hEnemy = pEnemy; - pMember->m_vecEnemyLKP = pEnemy->pev->origin; - pMember->SetConditions ( bits_COND_NEW_ENEMY ); - } - } - } -} - - -//========================================================= -// -// SquadCount(), return the number of members of this squad -// callable from leaders & followers -// -//========================================================= -int CSquadMonster :: SquadCount( void ) -{ - if (!InSquad()) - return 0; - - CSquadMonster *pSquadLeader = MySquadLeader(); - int squadCount = 0; - for (int i = 0; i < MAX_SQUAD_MEMBERS; i++) - { - if (pSquadLeader->MySquadMember(i) != NULL) - squadCount++; - } - - return squadCount; -} - - -//========================================================= -// -// SquadRecruit(), get some monsters of my classification and -// link them as a group. returns the group size -// -//========================================================= -int CSquadMonster :: SquadRecruit( int searchRadius, int maxMembers ) -{ - int squadCount; - int iMyClass = Classify();// cache this monster's class - - - // Don't recruit if I'm already in a group - if ( InSquad() ) - return 0; - - if ( maxMembers < 2 ) - return 0; - - // I am my own leader - m_hSquadLeader = this; - squadCount = 1; - - CBaseEntity *pEntity = NULL; - - if ( !FStringNull( pev->netname ) ) - { - // I have a netname, so unconditionally recruit everyone else with that name. - pEntity = UTIL_FindEntityByString( pEntity, "netname", STRING( pev->netname ) ); - while ( pEntity ) - { - CSquadMonster *pRecruit = pEntity->MySquadMonsterPointer(); - - if ( pRecruit ) - { - if ( !pRecruit->InSquad() && pRecruit->Classify() == iMyClass && pRecruit != this ) - { - // minimum protection here against user error.in worldcraft. - if (!SquadAdd( pRecruit )) - break; - squadCount++; - } - } - - pEntity = UTIL_FindEntityByString( pEntity, "netname", STRING( pev->netname ) ); - } - } - else - { - while ((pEntity = UTIL_FindEntityInSphere( pEntity, pev->origin, searchRadius )) != NULL) - { - CSquadMonster *pRecruit = pEntity->MySquadMonsterPointer( ); - - if ( pRecruit && pRecruit != this && pRecruit->IsAlive() && !pRecruit->m_pCine ) - { - // Can we recruit this guy? - if ( !pRecruit->InSquad() && pRecruit->Classify() == iMyClass && - ( (iMyClass != CLASS_ALIEN_MONSTER) || FStrEq(STRING(pev->classname), STRING(pRecruit->pev->classname))) && - FStringNull( pRecruit->pev->netname ) ) - { - TraceResult tr; - UTIL_TraceLine( pev->origin + pev->view_ofs, pRecruit->pev->origin + pev->view_ofs, ignore_monsters, pRecruit->edict(), &tr );// try to hit recruit with a traceline. - if ( tr.flFraction == 1.0 ) - { - if (!SquadAdd( pRecruit )) - break; - - squadCount++; - } - } - } - } - } - - // no single member squads - if (squadCount == 1) - { - m_hSquadLeader = NULL; - } - - return squadCount; -} - -//========================================================= -// CheckEnemy -//========================================================= -int CSquadMonster :: CheckEnemy ( CBaseEntity *pEnemy ) -{ - int iUpdatedLKP; - - iUpdatedLKP = CBaseMonster :: CheckEnemy ( m_hEnemy ); - - // communicate with squad members about the enemy IF this individual has the same enemy as the squad leader. - if ( InSquad() && (CBaseEntity *)m_hEnemy == MySquadLeader()->m_hEnemy ) - { - if ( iUpdatedLKP ) - { - // have new enemy information, so paste to the squad. - SquadPasteEnemyInfo(); - } - else - { - // enemy unseen, copy from the squad knowledge. - SquadCopyEnemyInfo(); - } - } - - return iUpdatedLKP; -} - -//========================================================= -// StartMonster -//========================================================= -void CSquadMonster :: StartMonster( void ) -{ - CBaseMonster :: StartMonster(); - - if ( ( m_afCapability & bits_CAP_SQUAD ) && !InSquad() ) - { - if ( !FStringNull( pev->netname ) ) - { - // if I have a groupname, I can only recruit if I'm flagged as leader - if ( !( pev->spawnflags & SF_SQUADMONSTER_LEADER ) ) - { - return; - } - } - - // try to form squads now. - int iSquadSize = SquadRecruit( 1024, 4 ); - - if ( iSquadSize ) - { - ALERT ( at_aiconsole, "Squad of %d %s formed\n", iSquadSize, STRING( pev->classname ) ); - } - - if ( IsLeader() && FClassnameIs ( pev, "monster_human_grunt" ) ) - { - SetBodygroup( 1, 1 ); // UNDONE: truly ugly hack - pev->skin = 0; - } - - } -} - -BOOL CSquadMonster :: NoFriendlyFire( void ) -{ - return NoFriendlyFire( FALSE ); //default: don't like the player -} - -//========================================================= -// NoFriendlyFire - checks for possibility of friendly fire -// -// Builds a large box in front of the grunt and checks to see -// if any squad members are in that box. -// -// Can now, also, check whether the player is in the box. LRC -//========================================================= -BOOL CSquadMonster :: NoFriendlyFire( BOOL playerAlly ) -{ - if ( !playerAlly && !InSquad() ) - { - return TRUE; - } - - CPlane backPlane; - CPlane leftPlane; - CPlane rightPlane; - - Vector vecLeftSide; - Vector vecRightSide; - Vector v_left; - - //!!!BUGBUG - to fix this, the planes must be aligned to where the monster will be firing its gun, not the direction it is facing!!! - - if ( m_hEnemy != NULL ) - { - UTIL_MakeVectors ( UTIL_VecToAngles( m_hEnemy->Center() - pev->origin ) ); - } - else - { - // if there's no enemy, pretend there's a friendly in the way, so the grunt won't shoot. - return FALSE; - } - - //UTIL_MakeVectors ( pev->angles ); - - vecLeftSide = pev->origin - ( gpGlobals->v_right * ( pev->size.x * 1.5 ) ); - vecRightSide = pev->origin + ( gpGlobals->v_right * ( pev->size.x * 1.5 ) ); - v_left = gpGlobals->v_right * -1; - - leftPlane.InitializePlane ( gpGlobals->v_right, vecLeftSide ); - rightPlane.InitializePlane ( v_left, vecRightSide ); - backPlane.InitializePlane ( gpGlobals->v_forward, pev->origin ); - -/* - ALERT ( at_console, "LeftPlane: %f %f %f : %f\n", leftPlane.m_vecNormal.x, leftPlane.m_vecNormal.y, leftPlane.m_vecNormal.z, leftPlane.m_flDist ); - ALERT ( at_console, "RightPlane: %f %f %f : %f\n", rightPlane.m_vecNormal.x, rightPlane.m_vecNormal.y, rightPlane.m_vecNormal.z, rightPlane.m_flDist ); - ALERT ( at_console, "BackPlane: %f %f %f : %f\n", backPlane.m_vecNormal.x, backPlane.m_vecNormal.y, backPlane.m_vecNormal.z, backPlane.m_flDist ); -*/ - - CSquadMonster *pSquadLeader = MySquadLeader(); - for (int i = 0; i < MAX_SQUAD_MEMBERS; i++) - { - CSquadMonster *pMember = pSquadLeader->MySquadMember(i); - if (pMember && pMember != this) - { - - if ( backPlane.PointInFront ( pMember->pev->origin ) && - leftPlane.PointInFront ( pMember->pev->origin ) && - rightPlane.PointInFront ( pMember->pev->origin) ) - { - // this guy is in the check volume! Don't shoot! - return FALSE; - } - } - } - - if (playerAlly) - { - edict_t *pentPlayer = FIND_CLIENT_IN_PVS( edict() ); - if (!FNullEnt(pentPlayer) && - backPlane.PointInFront ( pentPlayer->v.origin ) && - leftPlane.PointInFront ( pentPlayer->v.origin ) && - rightPlane.PointInFront ( pentPlayer->v.origin ) ) - { - // the player is in the check volume! Don't shoot! - return FALSE; - } - } - - return TRUE; -} - -//========================================================= -// GetIdealState - surveys the Conditions information available -// and finds the best new state for a monster. -//========================================================= -MONSTERSTATE CSquadMonster :: GetIdealState ( void ) -{ - int iConditions; - - iConditions = IScheduleFlags(); - - // If no schedule conditions, the new ideal state is probably the reason we're in here. - switch ( m_MonsterState ) - { - case MONSTERSTATE_IDLE: - case MONSTERSTATE_ALERT: - if ( HasConditions ( bits_COND_NEW_ENEMY ) && InSquad() ) - { - SquadMakeEnemy ( m_hEnemy ); - } - break; - } - - return CBaseMonster :: GetIdealState(); -} - -//========================================================= -// FValidateCover - determines whether or not the chosen -// cover location is a good one to move to. (currently based -// on proximity to others in the squad) -//========================================================= -BOOL CSquadMonster :: FValidateCover ( const Vector &vecCoverLocation ) -{ - if ( !InSquad() ) - { - return TRUE; - } - - if (SquadMemberInRange( vecCoverLocation, 128 )) - { - // another squad member is too close to this piece of cover. - return FALSE; - } - - return TRUE; -} - -//========================================================= -// SquadEnemySplit- returns TRUE if not all squad members -// are fighting the same enemy. -//========================================================= -BOOL CSquadMonster :: SquadEnemySplit ( void ) -{ - if (!InSquad()) - return FALSE; - - CSquadMonster *pSquadLeader = MySquadLeader(); - CBaseEntity *pEnemy = pSquadLeader->m_hEnemy; - - for (int i = 0; i < MAX_SQUAD_MEMBERS; i++) - { - CSquadMonster *pMember = pSquadLeader->MySquadMember(i); - if (pMember != NULL && pMember->m_hEnemy != NULL && pMember->m_hEnemy != pEnemy) - { - return TRUE; - } - } - return FALSE; -} - -//========================================================= -// FValidateCover - determines whether or not the chosen -// cover location is a good one to move to. (currently based -// on proximity to others in the squad) -//========================================================= -BOOL CSquadMonster :: SquadMemberInRange ( const Vector &vecLocation, float flDist ) -{ - if (!InSquad()) - return FALSE; - - CSquadMonster *pSquadLeader = MySquadLeader(); - - for (int i = 0; i < MAX_SQUAD_MEMBERS; i++) - { - CSquadMonster *pSquadMember = pSquadLeader->MySquadMember(i); - if (pSquadMember && (vecLocation - pSquadMember->pev->origin ).Length2D() <= flDist) - return TRUE; - } - return FALSE; -} - - -extern Schedule_t slChaseEnemyFailed[]; - -Schedule_t *CSquadMonster::GetScheduleOfType( int iType ) -{ - switch ( iType ) - { - - case SCHED_CHASE_ENEMY_FAILED: - { - return &slChaseEnemyFailed[ 0 ]; - } - - default: - return CBaseMonster::GetScheduleOfType( iType ); - } -} - +/*** +* +* Copyright (c) 1996-2002, 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. +* +* This source code contains proprietary and confidential information of +* Valve LLC and its suppliers. Access to this code is restricted to +* persons who have executed a written SDK license with Valve. Any access, +* use or distribution of this code by or to any unlicensed person is illegal. +* +****/ +//========================================================= +// Squadmonster functions +//========================================================= +#include "extdll.h" +#include "util.h" +#include "cbase.h" +#include "nodes.h" +#include "monsters.h" +#include "animation.h" +#include "saverestore.h" +#include "squadmonster.h" +#include "plane.h" + +//========================================================= +// Save/Restore +//========================================================= +TYPEDESCRIPTION CSquadMonster::m_SaveData[] = +{ + DEFINE_FIELD( CSquadMonster, m_hSquadLeader, FIELD_EHANDLE ), + DEFINE_ARRAY( CSquadMonster, m_hSquadMember, FIELD_EHANDLE, MAX_SQUAD_MEMBERS - 1 ), + + // DEFINE_FIELD( CSquadMonster, m_afSquadSlots, FIELD_INTEGER ), // these need to be reset after transitions! + DEFINE_FIELD( CSquadMonster, m_fEnemyEluded, FIELD_BOOLEAN ), + DEFINE_FIELD( CSquadMonster, m_flLastEnemySightTime, FIELD_TIME ), + + DEFINE_FIELD( CSquadMonster, m_iMySlot, FIELD_INTEGER ), + + +}; + +IMPLEMENT_SAVERESTORE( CSquadMonster, CBaseMonster ); + + +//========================================================= +// OccupySlot - if any slots of the passed slots are +// available, the monster will be assigned to one. +//========================================================= +BOOL CSquadMonster :: OccupySlot( int iDesiredSlots ) +{ + int i; + int iMask; + int iSquadSlots; + + if ( !InSquad() ) + { + return TRUE; + } + + if ( SquadEnemySplit() ) + { + // if the squad members aren't all fighting the same enemy, slots are disabled + // so that a squad member doesn't get stranded unable to engage his enemy because + // all of the attack slots are taken by squad members fighting other enemies. + m_iMySlot = bits_SLOT_SQUAD_SPLIT; + return TRUE; + } + + CSquadMonster *pSquadLeader = MySquadLeader(); + + if ( !( iDesiredSlots ^ pSquadLeader->m_afSquadSlots ) ) + { + // none of the desired slots are available. + return FALSE; + } + + iSquadSlots = pSquadLeader->m_afSquadSlots; + + for ( i = 0; i < NUM_SLOTS; i++ ) + { + iMask = 1<m_afSquadSlots |= iMask; + m_iMySlot = iMask; +// ALERT ( at_aiconsole, "Took slot %d - %d\n", i, m_hSquadLeader->m_afSquadSlots ); + return TRUE; + } + } + } + + return FALSE; +} + +//========================================================= +// VacateSlot +//========================================================= +void CSquadMonster :: VacateSlot() +{ + if ( m_iMySlot != bits_NO_SLOT && InSquad() ) + { +// ALERT ( at_aiconsole, "Vacated Slot %d - %d\n", m_iMySlot, m_hSquadLeader->m_afSquadSlots ); + MySquadLeader()->m_afSquadSlots &= ~m_iMySlot; + m_iMySlot = bits_NO_SLOT; + } +} + +//========================================================= +// ScheduleChange +//========================================================= +void CSquadMonster :: ScheduleChange ( void ) +{ + VacateSlot(); +} + +//========================================================= +// Killed +//========================================================= +void CSquadMonster :: Killed( entvars_t *pevAttacker, int iGib ) +{ + VacateSlot(); + + if ( InSquad() ) + { + MySquadLeader()->SquadRemove( this ); + } + + CBaseMonster :: Killed ( pevAttacker, iGib ); +} + +// These functions are still awaiting conversion to CSquadMonster + + +//========================================================= +// +// SquadRemove(), remove pRemove from my squad. +// If I am pRemove, promote m_pSquadNext to leader +// +//========================================================= +void CSquadMonster :: SquadRemove( CSquadMonster *pRemove ) +{ + ASSERT( pRemove!=NULL ); + ASSERT( this->IsLeader() ); + ASSERT( pRemove->m_hSquadLeader == this ); + + // If I'm the leader, get rid of my squad + if (pRemove == MySquadLeader()) + { + for (int i = 0; i < MAX_SQUAD_MEMBERS-1;i++) + { + CSquadMonster *pMember = MySquadMember(i); + if (pMember) + { + pMember->m_hSquadLeader = NULL; + m_hSquadMember[i] = NULL; + } + } + } + else + { + CSquadMonster *pSquadLeader = MySquadLeader(); + if (pSquadLeader) + { + for (int i = 0; i < MAX_SQUAD_MEMBERS-1;i++) + { + if (pSquadLeader->m_hSquadMember[i] == this) + { + pSquadLeader->m_hSquadMember[i] = NULL; + break; + } + } + } + } + + pRemove->m_hSquadLeader = NULL; +} + +//========================================================= +// +// SquadAdd(), add pAdd to my squad +// +//========================================================= +BOOL CSquadMonster :: SquadAdd( CSquadMonster *pAdd ) +{ + ASSERT( pAdd!=NULL ); + ASSERT( !pAdd->InSquad() ); + ASSERT( this->IsLeader() ); + + for (int i = 0; i < MAX_SQUAD_MEMBERS-1; i++) + { + if (m_hSquadMember[i] == NULL) + { + m_hSquadMember[i] = pAdd; + pAdd->m_hSquadLeader = this; + return TRUE; + } + } + return FALSE; + // should complain here +} + + +//========================================================= +// +// SquadPasteEnemyInfo - called by squad members that have +// current info on the enemy so that it can be stored for +// members who don't have current info. +// +//========================================================= +void CSquadMonster :: SquadPasteEnemyInfo ( void ) +{ + CSquadMonster *pSquadLeader = MySquadLeader( ); + if (pSquadLeader) + pSquadLeader->m_vecEnemyLKP = m_vecEnemyLKP; +} + +//========================================================= +// +// SquadCopyEnemyInfo - called by squad members who don't +// have current info on the enemy. Reads from the same fields +// in the leader's data that other squad members write to, +// so the most recent data is always available here. +// +//========================================================= +void CSquadMonster :: SquadCopyEnemyInfo ( void ) +{ + CSquadMonster *pSquadLeader = MySquadLeader( ); + if (pSquadLeader) + m_vecEnemyLKP = pSquadLeader->m_vecEnemyLKP; +} + +//========================================================= +// +// SquadMakeEnemy - makes everyone in the squad angry at +// the same entity. +// +//========================================================= +void CSquadMonster :: SquadMakeEnemy ( CBaseEntity *pEnemy ) +{ + if (!InSquad()) + return; + + if ( !pEnemy ) + { + ALERT ( at_console, "ERROR: SquadMakeEnemy() - pEnemy is NULL!\n" ); + return; + } + + CSquadMonster *pSquadLeader = MySquadLeader( ); + for (int i = 0; i < MAX_SQUAD_MEMBERS; i++) + { + CSquadMonster *pMember = pSquadLeader->MySquadMember(i); + if (pMember) + { + // reset members who aren't activly engaged in fighting + if (pMember->m_hEnemy != pEnemy && !pMember->HasConditions( bits_COND_SEE_ENEMY)) + { + if ( pMember->m_hEnemy != NULL) + { + // remember their current enemy + pMember->PushEnemy( pMember->m_hEnemy, pMember->m_vecEnemyLKP ); + } + // give them a new enemy + pMember->m_hEnemy = pEnemy; + pMember->m_vecEnemyLKP = pEnemy->pev->origin; + pMember->SetConditions ( bits_COND_NEW_ENEMY ); + } + } + } +} + + +//========================================================= +// +// SquadCount(), return the number of members of this squad +// callable from leaders & followers +// +//========================================================= +int CSquadMonster :: SquadCount( void ) +{ + if (!InSquad()) + return 0; + + CSquadMonster *pSquadLeader = MySquadLeader(); + int squadCount = 0; + for (int i = 0; i < MAX_SQUAD_MEMBERS; i++) + { + if (pSquadLeader->MySquadMember(i) != NULL) + squadCount++; + } + + return squadCount; +} + + +//========================================================= +// +// SquadRecruit(), get some monsters of my classification and +// link them as a group. returns the group size +// +//========================================================= +int CSquadMonster :: SquadRecruit( int searchRadius, int maxMembers ) +{ + int squadCount; + int iMyClass = Classify();// cache this monster's class + + + // Don't recruit if I'm already in a group + if ( InSquad() ) + return 0; + + if ( maxMembers < 2 ) + return 0; + + // I am my own leader + m_hSquadLeader = this; + squadCount = 1; + + CBaseEntity *pEntity = NULL; + + if ( !FStringNull( pev->netname ) ) + { + // I have a netname, so unconditionally recruit everyone else with that name. + pEntity = UTIL_FindEntityByString( pEntity, "netname", STRING( pev->netname ) ); + while ( pEntity ) + { + CSquadMonster *pRecruit = pEntity->MySquadMonsterPointer(); + + if ( pRecruit ) + { + if ( !pRecruit->InSquad() && pRecruit->Classify() == iMyClass && pRecruit != this ) + { + // minimum protection here against user error.in worldcraft. + if (!SquadAdd( pRecruit )) + break; + squadCount++; + } + } + + pEntity = UTIL_FindEntityByString( pEntity, "netname", STRING( pev->netname ) ); + } + } + else + { + while ((pEntity = UTIL_FindEntityInSphere( pEntity, pev->origin, searchRadius )) != NULL) + { + CSquadMonster *pRecruit = pEntity->MySquadMonsterPointer( ); + + if ( pRecruit && pRecruit != this && pRecruit->IsAlive() && !pRecruit->m_pCine ) + { + // Can we recruit this guy? + if ( !pRecruit->InSquad() && pRecruit->Classify() == iMyClass && + ( (iMyClass != CLASS_ALIEN_MONSTER) || FStrEq(STRING(pev->classname), STRING(pRecruit->pev->classname))) && + FStringNull( pRecruit->pev->netname ) ) + { + TraceResult tr; + UTIL_TraceLine( pev->origin + pev->view_ofs, pRecruit->pev->origin + pev->view_ofs, ignore_monsters, pRecruit->edict(), &tr );// try to hit recruit with a traceline. + if ( tr.flFraction == 1.0 ) + { + if (!SquadAdd( pRecruit )) + break; + + squadCount++; + } + } + } + } + } + + // no single member squads + if (squadCount == 1) + { + m_hSquadLeader = NULL; + } + + return squadCount; +} + +//========================================================= +// CheckEnemy +//========================================================= +int CSquadMonster :: CheckEnemy ( CBaseEntity *pEnemy ) +{ + int iUpdatedLKP; + + iUpdatedLKP = CBaseMonster :: CheckEnemy ( m_hEnemy ); + + // communicate with squad members about the enemy IF this individual has the same enemy as the squad leader. + if ( InSquad() && (CBaseEntity *)m_hEnemy == MySquadLeader()->m_hEnemy ) + { + if ( iUpdatedLKP ) + { + // have new enemy information, so paste to the squad. + SquadPasteEnemyInfo(); + } + else + { + // enemy unseen, copy from the squad knowledge. + SquadCopyEnemyInfo(); + } + } + + return iUpdatedLKP; +} + +//========================================================= +// StartMonster +//========================================================= +void CSquadMonster :: StartMonster( void ) +{ + CBaseMonster :: StartMonster(); + + if ( ( m_afCapability & bits_CAP_SQUAD ) && !InSquad() ) + { + if ( !FStringNull( pev->netname ) ) + { + // if I have a groupname, I can only recruit if I'm flagged as leader + if ( !( pev->spawnflags & SF_SQUADMONSTER_LEADER ) ) + { + return; + } + } + + // try to form squads now. + int iSquadSize = SquadRecruit( 1024, 4 ); + + if ( iSquadSize ) + { + ALERT ( at_aiconsole, "Squad of %d %s formed\n", iSquadSize, STRING( pev->classname ) ); + } + + if ( IsLeader() && FClassnameIs ( pev, "monster_human_grunt" ) ) + { + SetBodygroup( 1, 1 ); // UNDONE: truly ugly hack + pev->skin = 0; + } + + } +} + +//========================================================= +// NoFriendlyFire - checks for possibility of friendly fire +// +// Builds a large box in front of the grunt and checks to see +// if any squad members are in that box. +//========================================================= +BOOL CSquadMonster :: NoFriendlyFire( void ) +{ + if ( !InSquad() ) + { + return TRUE; + } + + CPlane backPlane; + CPlane leftPlane; + CPlane rightPlane; + + Vector vecLeftSide; + Vector vecRightSide; + Vector v_left; + + //!!!BUGBUG - to fix this, the planes must be aligned to where the monster will be firing its gun, not the direction it is facing!!! + + if ( m_hEnemy != NULL ) + { + UTIL_MakeVectors ( UTIL_VecToAngles( m_hEnemy->Center() - pev->origin ) ); + } + else + { + // if there's no enemy, pretend there's a friendly in the way, so the grunt won't shoot. + return FALSE; + } + + //UTIL_MakeVectors ( pev->angles ); + + vecLeftSide = pev->origin - ( gpGlobals->v_right * ( pev->size.x * 1.5 ) ); + vecRightSide = pev->origin + ( gpGlobals->v_right * ( pev->size.x * 1.5 ) ); + v_left = gpGlobals->v_right * -1; + + leftPlane.InitializePlane ( gpGlobals->v_right, vecLeftSide ); + rightPlane.InitializePlane ( v_left, vecRightSide ); + backPlane.InitializePlane ( gpGlobals->v_forward, pev->origin ); + +/* + ALERT ( at_console, "LeftPlane: %f %f %f : %f\n", leftPlane.m_vecNormal.x, leftPlane.m_vecNormal.y, leftPlane.m_vecNormal.z, leftPlane.m_flDist ); + ALERT ( at_console, "RightPlane: %f %f %f : %f\n", rightPlane.m_vecNormal.x, rightPlane.m_vecNormal.y, rightPlane.m_vecNormal.z, rightPlane.m_flDist ); + ALERT ( at_console, "BackPlane: %f %f %f : %f\n", backPlane.m_vecNormal.x, backPlane.m_vecNormal.y, backPlane.m_vecNormal.z, backPlane.m_flDist ); +*/ + + CSquadMonster *pSquadLeader = MySquadLeader(); + for (int i = 0; i < MAX_SQUAD_MEMBERS; i++) + { + CSquadMonster *pMember = pSquadLeader->MySquadMember(i); + if (pMember && pMember != this) + { + + if ( backPlane.PointInFront ( pMember->pev->origin ) && + leftPlane.PointInFront ( pMember->pev->origin ) && + rightPlane.PointInFront ( pMember->pev->origin) ) + { + // this guy is in the check volume! Don't shoot! + return FALSE; + } + } + } + + return TRUE; +} + +//========================================================= +// GetIdealState - surveys the Conditions information available +// and finds the best new state for a monster. +//========================================================= +MONSTERSTATE CSquadMonster :: GetIdealState ( void ) +{ + int iConditions; + + iConditions = IScheduleFlags(); + + // If no schedule conditions, the new ideal state is probably the reason we're in here. + switch ( m_MonsterState ) + { + case MONSTERSTATE_IDLE: + case MONSTERSTATE_ALERT: + if ( HasConditions ( bits_COND_NEW_ENEMY ) && InSquad() ) + { + SquadMakeEnemy ( m_hEnemy ); + } + break; + } + + return CBaseMonster :: GetIdealState(); +} + +//========================================================= +// FValidateCover - determines whether or not the chosen +// cover location is a good one to move to. (currently based +// on proximity to others in the squad) +//========================================================= +BOOL CSquadMonster :: FValidateCover ( const Vector &vecCoverLocation ) +{ + if ( !InSquad() ) + { + return TRUE; + } + + if (SquadMemberInRange( vecCoverLocation, 128 )) + { + // another squad member is too close to this piece of cover. + return FALSE; + } + + return TRUE; +} + +//========================================================= +// SquadEnemySplit- returns TRUE if not all squad members +// are fighting the same enemy. +//========================================================= +BOOL CSquadMonster :: SquadEnemySplit ( void ) +{ + if (!InSquad()) + return FALSE; + + CSquadMonster *pSquadLeader = MySquadLeader(); + CBaseEntity *pEnemy = pSquadLeader->m_hEnemy; + + for (int i = 0; i < MAX_SQUAD_MEMBERS; i++) + { + CSquadMonster *pMember = pSquadLeader->MySquadMember(i); + if (pMember != NULL && pMember->m_hEnemy != NULL && pMember->m_hEnemy != pEnemy) + { + return TRUE; + } + } + return FALSE; +} + +//========================================================= +// FValidateCover - determines whether or not the chosen +// cover location is a good one to move to. (currently based +// on proximity to others in the squad) +//========================================================= +BOOL CSquadMonster :: SquadMemberInRange ( const Vector &vecLocation, float flDist ) +{ + if (!InSquad()) + return FALSE; + + CSquadMonster *pSquadLeader = MySquadLeader(); + + for (int i = 0; i < MAX_SQUAD_MEMBERS; i++) + { + CSquadMonster *pSquadMember = pSquadLeader->MySquadMember(i); + if (pSquadMember && (vecLocation - pSquadMember->pev->origin ).Length2D() <= flDist) + return TRUE; + } + return FALSE; +} + + +extern Schedule_t slChaseEnemyFailed[]; + +Schedule_t *CSquadMonster::GetScheduleOfType( int iType ) +{ + switch ( iType ) + { + + case SCHED_CHASE_ENEMY_FAILED: + { + return &slChaseEnemyFailed[ 0 ]; + } + + default: + return CBaseMonster::GetScheduleOfType( iType ); + } +} + diff --git a/spirit/squadmonster.h b/dlls/squadmonster.h similarity index 97% rename from spirit/squadmonster.h rename to dlls/squadmonster.h index 8519bdd7..f5297876 100644 --- a/spirit/squadmonster.h +++ b/dlls/squadmonster.h @@ -1,121 +1,120 @@ -/*** -* -* Copyright (c) 1996-2002, 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. -* -* This source code contains proprietary and confidential information of -* Valve LLC and its suppliers. Access to this code is restricted to -* persons who have executed a written SDK license with Valve. Any access, -* use or distribution of this code by or to any unlicensed person is illegal. -* -****/ -//========================================================= -// CSquadMonster - all the extra data for monsters that -// form squads. -//========================================================= - -#define SF_SQUADMONSTER_LEADER 32 - - -#define bits_NO_SLOT 0 - -// HUMAN GRUNT SLOTS -#define bits_SLOT_HGRUNT_ENGAGE1 ( 1 << 0 ) -#define bits_SLOT_HGRUNT_ENGAGE2 ( 1 << 1 ) -#define bits_SLOTS_HGRUNT_ENGAGE ( bits_SLOT_HGRUNT_ENGAGE1 | bits_SLOT_HGRUNT_ENGAGE2 ) - -#define bits_SLOT_HGRUNT_GRENADE1 ( 1 << 2 ) -#define bits_SLOT_HGRUNT_GRENADE2 ( 1 << 3 ) -#define bits_SLOTS_HGRUNT_GRENADE ( bits_SLOT_HGRUNT_GRENADE1 | bits_SLOT_HGRUNT_GRENADE2 ) - -// ALIEN GRUNT SLOTS -#define bits_SLOT_AGRUNT_HORNET1 ( 1 << 4 ) -#define bits_SLOT_AGRUNT_HORNET2 ( 1 << 5 ) -#define bits_SLOT_AGRUNT_CHASE ( 1 << 6 ) -#define bits_SLOTS_AGRUNT_HORNET ( bits_SLOT_AGRUNT_HORNET1 | bits_SLOT_AGRUNT_HORNET2 ) - -// HOUNDEYE SLOTS -#define bits_SLOT_HOUND_ATTACK1 ( 1 << 7 ) -#define bits_SLOT_HOUND_ATTACK2 ( 1 << 8 ) -#define bits_SLOT_HOUND_ATTACK3 ( 1 << 9 ) -#define bits_SLOTS_HOUND_ATTACK ( bits_SLOT_HOUND_ATTACK1 | bits_SLOT_HOUND_ATTACK2 | bits_SLOT_HOUND_ATTACK3 ) - -// global slots -#define bits_SLOT_SQUAD_SPLIT ( 1 << 10 )// squad members don't all have the same enemy - -#define NUM_SLOTS 11// update this every time you add/remove a slot. - -#define MAX_SQUAD_MEMBERS 5 - -//========================================================= -// CSquadMonster - for any monster that forms squads. -//========================================================= -class CSquadMonster : public CBaseMonster -{ -public: - // squad leader info - EHANDLE m_hSquadLeader; // who is my leader - EHANDLE m_hSquadMember[MAX_SQUAD_MEMBERS-1]; // valid only for leader - int m_afSquadSlots; - float m_flLastEnemySightTime; // last time anyone in the squad saw the enemy - BOOL m_fEnemyEluded; - - // squad member info - int m_iMySlot;// this is the behaviour slot that the monster currently holds in the squad. - - int CheckEnemy ( CBaseEntity *pEnemy ); - void StartMonster ( void ); - void VacateSlot( void ); - void ScheduleChange( void ); - void Killed( entvars_t *pevAttacker, int iGib ); - BOOL OccupySlot( int iDesiredSlot ); - BOOL NoFriendlyFire( void ); - BOOL NoFriendlyFire( BOOL playerAlly ); - - // squad functions still left in base class - CSquadMonster *MySquadLeader( ) - { - CSquadMonster *pSquadLeader = (CSquadMonster *)((CBaseEntity *)m_hSquadLeader); - if (pSquadLeader != NULL) - return pSquadLeader; - return this; - } - CSquadMonster *MySquadMember( int i ) - { - if (i >= MAX_SQUAD_MEMBERS-1) - return this; - else - return (CSquadMonster *)((CBaseEntity *)m_hSquadMember[i]); - } - int InSquad ( void ) { return m_hSquadLeader != NULL; } - int IsLeader ( void ) { return m_hSquadLeader == this; } - int SquadJoin ( int searchRadius ); - int SquadRecruit ( int searchRadius, int maxMembers ); - int SquadCount( void ); - void SquadRemove( CSquadMonster *pRemove ); - void SquadUnlink( void ); - BOOL SquadAdd( CSquadMonster *pAdd ); - void SquadDisband( void ); - void SquadAddConditions ( int iConditions ); - void SquadMakeEnemy ( CBaseEntity *pEnemy ); - void SquadPasteEnemyInfo ( void ); - void SquadCopyEnemyInfo ( void ); - BOOL SquadEnemySplit ( void ); - BOOL SquadMemberInRange( const Vector &vecLocation, float flDist ); - - virtual CSquadMonster *MySquadMonsterPointer( void ) { return this; } - - static TYPEDESCRIPTION m_SaveData[]; - - int Save( CSave &save ); - int Restore( CRestore &restore ); - - BOOL FValidateCover ( const Vector &vecCoverLocation ); - - MONSTERSTATE GetIdealState ( void ); - Schedule_t *GetScheduleOfType ( int iType ); -}; - +/*** +* +* Copyright (c) 1996-2002, 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. +* +* This source code contains proprietary and confidential information of +* Valve LLC and its suppliers. Access to this code is restricted to +* persons who have executed a written SDK license with Valve. Any access, +* use or distribution of this code by or to any unlicensed person is illegal. +* +****/ +//========================================================= +// CSquadMonster - all the extra data for monsters that +// form squads. +//========================================================= + +#define SF_SQUADMONSTER_LEADER 32 + + +#define bits_NO_SLOT 0 + +// HUMAN GRUNT SLOTS +#define bits_SLOT_HGRUNT_ENGAGE1 ( 1 << 0 ) +#define bits_SLOT_HGRUNT_ENGAGE2 ( 1 << 1 ) +#define bits_SLOTS_HGRUNT_ENGAGE ( bits_SLOT_HGRUNT_ENGAGE1 | bits_SLOT_HGRUNT_ENGAGE2 ) + +#define bits_SLOT_HGRUNT_GRENADE1 ( 1 << 2 ) +#define bits_SLOT_HGRUNT_GRENADE2 ( 1 << 3 ) +#define bits_SLOTS_HGRUNT_GRENADE ( bits_SLOT_HGRUNT_GRENADE1 | bits_SLOT_HGRUNT_GRENADE2 ) + +// ALIEN GRUNT SLOTS +#define bits_SLOT_AGRUNT_HORNET1 ( 1 << 4 ) +#define bits_SLOT_AGRUNT_HORNET2 ( 1 << 5 ) +#define bits_SLOT_AGRUNT_CHASE ( 1 << 6 ) +#define bits_SLOTS_AGRUNT_HORNET ( bits_SLOT_AGRUNT_HORNET1 | bits_SLOT_AGRUNT_HORNET2 ) + +// HOUNDEYE SLOTS +#define bits_SLOT_HOUND_ATTACK1 ( 1 << 7 ) +#define bits_SLOT_HOUND_ATTACK2 ( 1 << 8 ) +#define bits_SLOT_HOUND_ATTACK3 ( 1 << 9 ) +#define bits_SLOTS_HOUND_ATTACK ( bits_SLOT_HOUND_ATTACK1 | bits_SLOT_HOUND_ATTACK2 | bits_SLOT_HOUND_ATTACK3 ) + +// global slots +#define bits_SLOT_SQUAD_SPLIT ( 1 << 10 )// squad members don't all have the same enemy + +#define NUM_SLOTS 11// update this every time you add/remove a slot. + +#define MAX_SQUAD_MEMBERS 5 + +//========================================================= +// CSquadMonster - for any monster that forms squads. +//========================================================= +class CSquadMonster : public CBaseMonster +{ +public: + // squad leader info + EHANDLE m_hSquadLeader; // who is my leader + EHANDLE m_hSquadMember[MAX_SQUAD_MEMBERS-1]; // valid only for leader + int m_afSquadSlots; + float m_flLastEnemySightTime; // last time anyone in the squad saw the enemy + BOOL m_fEnemyEluded; + + // squad member info + int m_iMySlot;// this is the behaviour slot that the monster currently holds in the squad. + + int CheckEnemy ( CBaseEntity *pEnemy ); + void StartMonster ( void ); + void VacateSlot( void ); + void ScheduleChange( void ); + void Killed( entvars_t *pevAttacker, int iGib ); + BOOL OccupySlot( int iDesiredSlot ); + BOOL NoFriendlyFire( void ); + + // squad functions still left in base class + CSquadMonster *MySquadLeader( ) + { + CSquadMonster *pSquadLeader = (CSquadMonster *)((CBaseEntity *)m_hSquadLeader); + if (pSquadLeader != NULL) + return pSquadLeader; + return this; + } + CSquadMonster *MySquadMember( int i ) + { + if (i >= MAX_SQUAD_MEMBERS-1) + return this; + else + return (CSquadMonster *)((CBaseEntity *)m_hSquadMember[i]); + } + int InSquad ( void ) { return m_hSquadLeader != NULL; } + int IsLeader ( void ) { return m_hSquadLeader == this; } + int SquadJoin ( int searchRadius ); + int SquadRecruit ( int searchRadius, int maxMembers ); + int SquadCount( void ); + void SquadRemove( CSquadMonster *pRemove ); + void SquadUnlink( void ); + BOOL SquadAdd( CSquadMonster *pAdd ); + void SquadDisband( void ); + void SquadAddConditions ( int iConditions ); + void SquadMakeEnemy ( CBaseEntity *pEnemy ); + void SquadPasteEnemyInfo ( void ); + void SquadCopyEnemyInfo ( void ); + BOOL SquadEnemySplit ( void ); + BOOL SquadMemberInRange( const Vector &vecLocation, float flDist ); + + virtual CSquadMonster *MySquadMonsterPointer( void ) { return this; } + + static TYPEDESCRIPTION m_SaveData[]; + + int Save( CSave &save ); + int Restore( CRestore &restore ); + + BOOL FValidateCover ( const Vector &vecCoverLocation ); + + MONSTERSTATE GetIdealState ( void ); + Schedule_t *GetScheduleOfType ( int iType ); +}; + diff --git a/spirit/squeakgrenade.cpp b/dlls/squeakgrenade.cpp similarity index 78% rename from spirit/squeakgrenade.cpp rename to dlls/squeakgrenade.cpp index 7a4825d5..5b7ce72c 100644 --- a/spirit/squeakgrenade.cpp +++ b/dlls/squeakgrenade.cpp @@ -12,6 +12,7 @@ * without written permission from Valve LLC. * ****/ +#if !defined( OEM_BUILD ) && !defined( HLDEMO_BUILD ) #include "extdll.h" #include "util.h" @@ -39,6 +40,8 @@ enum squeak_e { SQUEAK_THROW }; +#ifndef CLIENT_DLL + class CSqueakGrenade : public CGrenade { void Spawn( void ); @@ -50,12 +53,14 @@ class CSqueakGrenade : public CGrenade void Killed( entvars_t *pevAttacker, int iGib ); void GibMonster( void ); - virtual int Save( CSave &save ); - virtual int Restore( CRestore &restore ); + virtual int Save( CSave &save ); + virtual int Restore( CRestore &restore ); + static TYPEDESCRIPTION m_SaveData[]; static float m_flNextBounceSoundTime; + // CBaseEntity *m_pTarget; float m_flDie; Vector m_vecTarget; float m_flNextHunt; @@ -67,22 +72,6 @@ class CSqueakGrenade : public CGrenade float CSqueakGrenade::m_flNextBounceSoundTime = 0; -class CSqueak : public CBasePlayerWeapon -{ -public: - void Spawn( void ); - void Precache( void ); - int GetItemInfo(ItemInfo *p); - - void PrimaryAttack( void ); - BOOL Deploy( void ); - void Holster( ); - void WeaponIdle( void ); - int m_fJustThrown; -private: - unsigned short m_usSnarkFire; -}; - LINK_ENTITY_TO_CLASS( monster_snark, CSqueakGrenade ); TYPEDESCRIPTION CSqueakGrenade::m_SaveData[] = { @@ -93,14 +82,16 @@ TYPEDESCRIPTION CSqueakGrenade::m_SaveData[] = DEFINE_FIELD( CSqueakGrenade, m_posPrev, FIELD_POSITION_VECTOR ), DEFINE_FIELD( CSqueakGrenade, m_hOwner, FIELD_EHANDLE ), }; + IMPLEMENT_SAVERESTORE( CSqueakGrenade, CGrenade ); #define SQUEEK_DETONATE_DELAY 15.0 int CSqueakGrenade :: Classify ( void ) { - if (m_iClass) return m_iClass; - if (m_iMyClass != 0) return m_iMyClass; // protect against recursion + if (m_iMyClass != 0) + return m_iMyClass; // protect against recursion + if (m_hEnemy != NULL) { m_iMyClass = CLASS_INSECT; // no one cares about it @@ -114,6 +105,7 @@ int CSqueakGrenade :: Classify ( void ) } m_iMyClass = 0; } + return CLASS_ALIEN_BIOWEAPON; } @@ -126,26 +118,29 @@ void CSqueakGrenade :: Spawn( void ) SET_MODEL(ENT(pev), "models/w_squeak.mdl"); UTIL_SetSize(pev, Vector( -4, -4, 0), Vector(4, 4, 8)); - UTIL_SetOrigin( this, pev->origin ); + UTIL_SetOrigin( pev, pev->origin ); - SetTouch(&CSqueakGrenade :: SuperBounceTouch ); - SetThink(&CSqueakGrenade :: HuntThink ); - SetNextThink( 0.1 ); + SetTouch( SuperBounceTouch ); + SetThink( HuntThink ); + pev->nextthink = gpGlobals->time + 0.1; m_flNextHunt = gpGlobals->time + 1E6; pev->flags |= FL_MONSTER; pev->takedamage = DAMAGE_AIM; - if (pev->health == 0) - pev->health = gSkillData.snarkHealth; + pev->health = gSkillData.snarkHealth; pev->gravity = 0.5; pev->friction = 0.5; pev->dmg = gSkillData.snarkDmgPop; m_flDie = gpGlobals->time + SQUEEK_DETONATE_DELAY; + m_flFieldOfView = 0; // 180 degrees - if ( pev->owner ) m_hOwner = Instance( pev->owner ); - m_flNextBounceSoundTime = UTIL_WeaponTimeBase();// reset each time a snark is spawned. + + if ( pev->owner ) + m_hOwner = Instance( pev->owner ); + + m_flNextBounceSoundTime = gpGlobals->time;// reset each time a snark is spawned. pev->sequence = WSQUEAK_RUN; ResetSequenceInfo( ); @@ -167,9 +162,9 @@ void CSqueakGrenade::Precache( void ) void CSqueakGrenade :: Killed( entvars_t *pevAttacker, int iGib ) { pev->model = iStringNull;// make invisible - SetThink(&CSqueakGrenade :: SUB_Remove ); + SetThink( SUB_Remove ); SetTouch( NULL ); - SetNextThink( 0.1 ); + pev->nextthink = gpGlobals->time + 0.1; // since squeak grenades never leave a body behind, clear out their takedamage now. // Squeaks do a bit of radius damage when they pop, and that radius damage will @@ -178,15 +173,20 @@ void CSqueakGrenade :: Killed( entvars_t *pevAttacker, int iGib ) // play squeek blast EMIT_SOUND_DYN(ENT(pev), CHAN_ITEM, "squeek/sqk_blast1.wav", 1, 0.5, 0, PITCH_NORM); + CSoundEnt::InsertSound ( bits_SOUND_COMBAT, pev->origin, SMALL_EXPLOSION_VOLUME, 3.0 ); + UTIL_BloodDrips( pev->origin, g_vecZero, BloodColor(), 80 ); if (m_hOwner != NULL) RadiusDamage ( pev, m_hOwner->pev, pev->dmg, CLASS_NONE, DMG_BLAST ); - else RadiusDamage ( pev, pev, pev->dmg, CLASS_NONE, DMG_BLAST ); + else + RadiusDamage ( pev, pev, pev->dmg, CLASS_NONE, DMG_BLAST ); // reset owner so death message happens - if (m_hOwner != NULL) pev->owner = m_hOwner->edict(); + if (m_hOwner != NULL) + pev->owner = m_hOwner->edict(); + CBaseMonster :: Killed( pevAttacker, GIB_ALWAYS ); } @@ -199,6 +199,8 @@ void CSqueakGrenade :: GibMonster( void ) void CSqueakGrenade::HuntThink( void ) { + // ALERT( at_console, "think\n" ); + if (!IsInWorld()) { SetTouch( NULL ); @@ -207,7 +209,7 @@ void CSqueakGrenade::HuntThink( void ) } StudioFrameAdvance( ); - SetNextThink( 0.1 ); + pev->nextthink = gpGlobals->time + 0.1; // explode when ready if (gpGlobals->time >= m_flDie) @@ -219,7 +221,7 @@ void CSqueakGrenade::HuntThink( void ) } // float - if (pev->waterlevel != 0 && pev->watertype != CONTENTS_FOG) + if (pev->waterlevel != 0) { if (pev->movetype == MOVETYPE_BOUNCE) { @@ -229,7 +231,9 @@ void CSqueakGrenade::HuntThink( void ) pev->velocity.z += 8.0; } else if (pev->movetype = MOVETYPE_FLY) + { pev->movetype = MOVETYPE_BOUNCE; + } // return if not time to hunt if (m_flNextHunt > gpGlobals->time) @@ -263,7 +267,8 @@ void CSqueakGrenade::HuntThink( void ) // higher pitch as squeeker gets closer to detonation time float flpitch = 155.0 - 60.0 * ((m_flDie - gpGlobals->time) / SQUEEK_DETONATE_DELAY); - if (flpitch < 80) flpitch = 80; + if (flpitch < 80) + flpitch = 80; if (m_hEnemy != NULL) { @@ -276,11 +281,20 @@ void CSqueakGrenade::HuntThink( void ) float flVel = pev->velocity.Length(); float flAdj = 50.0 / (flVel + 10.0); - if (flAdj > 1.2) flAdj = 1.2; + if (flAdj > 1.2) + flAdj = 1.2; + + // ALERT( at_console, "think : enemy\n"); + + // ALERT( at_console, "%.0f %.2f %.2f %.2f\n", flVel, m_vecTarget.x, m_vecTarget.y, m_vecTarget.z ); + pev->velocity = pev->velocity * flAdj + m_vecTarget * 300; } - if (pev->flags & FL_ONGROUND) pev->avelocity = Vector( 0, 0, 0 ); + if (pev->flags & FL_ONGROUND) + { + pev->avelocity = Vector( 0, 0, 0 ); + } else { if (pev->avelocity == Vector( 0, 0, 0)) @@ -310,31 +324,39 @@ void CSqueakGrenade::SuperBounceTouch( CBaseEntity *pOther ) TraceResult tr = UTIL_GetGlobalTrace( ); // don't hit the guy that launched this grenade - if ( pev->owner && pOther->edict() == pev->owner )return; + if ( pev->owner && pOther->edict() == pev->owner ) + return; // at least until we've bounced once pev->owner = NULL; + pev->angles.x = 0; pev->angles.z = 0; // avoid bouncing too much - if (m_flNextHit > gpGlobals->time) return; + if (m_flNextHit > gpGlobals->time) + return; // higher pitch as squeeker gets closer to detonation time flpitch = 155.0 - 60.0 * ((m_flDie - gpGlobals->time) / SQUEEK_DETONATE_DELAY); if ( pOther->pev->takedamage && m_flNextAttack < gpGlobals->time ) { + // attack! + // make sure it's me who has touched them if (tr.pHit == pOther->edict()) { // and it's not another squeakgrenade if (tr.pHit->v.modelindex != pev->modelindex) { + // ALERT( at_console, "hit enemy\n"); ClearMultiDamage( ); pOther->TraceAttack(pev, gSkillData.snarkDmgBite, gpGlobals->v_forward, &tr, DMG_SLASH ); - if (m_hOwner != NULL) ApplyMultiDamage( pev, m_hOwner->pev ); - else ApplyMultiDamage( pev, pev ); + if (m_hOwner != NULL) + ApplyMultiDamage( pev, m_hOwner->pev ); + else + ApplyMultiDamage( pev, pev ); pev->dmg += gSkillData.snarkDmgPop; // add more explosion damage // m_flDie += 2.0; // add more life @@ -344,15 +366,23 @@ void CSqueakGrenade::SuperBounceTouch( CBaseEntity *pOther ) m_flNextAttack = gpGlobals->time + 0.5; } } + else + { + // ALERT( at_console, "been hit\n"); + } } m_flNextHit = gpGlobals->time + 0.1; m_flNextHunt = gpGlobals->time; - if ( IsMultiplayer() ) + if ( g_pGameRules->IsMultiplayer() ) { // in multiplayer, we limit how often snarks can make their bounce sounds to prevent overflows. - if ( gpGlobals->time < m_flNextBounceSoundTime ) return; + if ( gpGlobals->time < m_flNextBounceSoundTime ) + { + // too soon! + return; + } } if (!(pev->flags & FL_ONGROUND)) @@ -376,15 +406,20 @@ void CSqueakGrenade::SuperBounceTouch( CBaseEntity *pOther ) m_flNextBounceSoundTime = gpGlobals->time + 0.5;// half second. } + +#endif + LINK_ENTITY_TO_CLASS( weapon_snark, CSqueak ); void CSqueak::Spawn( ) { Precache( ); + m_iId = WEAPON_SNARK; SET_MODEL(ENT(pev), "models/w_sqknest.mdl"); FallInit();//get ready to fall down. + m_iDefaultAmmo = SNARK_DEFAULT_GIVE; pev->sequence = 1; @@ -402,7 +437,7 @@ void CSqueak::Precache( void ) PRECACHE_SOUND("squeek/sqk_hunt3.wav"); UTIL_PrecacheOther("monster_snark"); - m_usSnarkFire = PRECACHE_EVENT ( 1, "evSnarkFire" ); + m_usSnarkFire = PRECACHE_EVENT ( 1, "events/snarkfire.sc" ); } @@ -423,30 +458,38 @@ int CSqueak::GetItemInfo(ItemInfo *p) return 1; } + + BOOL CSqueak::Deploy( ) { + // play hunt sound float flRndSound = RANDOM_FLOAT ( 0 , 1 ); + if ( flRndSound <= 0.5 ) EMIT_SOUND_DYN(ENT(pev), CHAN_VOICE, "squeek/sqk_hunt2.wav", 1, ATTN_NORM, 0, 100); - else EMIT_SOUND_DYN(ENT(pev), CHAN_VOICE, "squeek/sqk_hunt3.wav", 1, ATTN_NORM, 0, 100); + else + EMIT_SOUND_DYN(ENT(pev), CHAN_VOICE, "squeek/sqk_hunt3.wav", 1, ATTN_NORM, 0, 100); + m_pPlayer->m_iWeaponVolume = QUIET_GUN_VOLUME; - return DefaultDeploy( "models/v_squeak.mdl", "models/p_squeak.mdl", SQUEAK_UP, "squeak", 1.3 ); + return DefaultDeploy( "models/v_squeak.mdl", "models/p_squeak.mdl", SQUEAK_UP, "squeak" ); } -void CSqueak::Holster( ) -{ - m_pPlayer->m_flNextAttack = UTIL_WeaponTimeBase() + 1.4; - SendWeaponAnim( SQUEAK_DOWN ); - EMIT_SOUND(ENT(m_pPlayer->pev), CHAN_WEAPON, "common/null.wav", 1.0, ATTN_NORM); +void CSqueak::Holster( int skiplocal /* = 0 */ ) +{ + m_pPlayer->m_flNextAttack = UTIL_WeaponTimeBase() + 0.5; + if ( !m_pPlayer->m_rgAmmo[ m_iPrimaryAmmoType ] ) { m_pPlayer->pev->weapons &= ~(1<nextthink = gpGlobals->time + 0.1; return; } + + SendWeaponAnim( SQUEAK_DOWN ); + EMIT_SOUND(ENT(m_pPlayer->pev), CHAN_WEAPON, "common/null.wav", 1.0, ATTN_NORM); } @@ -468,25 +511,38 @@ void CSqueak::PrimaryAttack() // find place to toss monster UTIL_TraceLine( trace_origin + gpGlobals->v_forward * 20, trace_origin + gpGlobals->v_forward * 64, dont_ignore_monsters, NULL, &tr ); - PLAYBACK_EVENT_FULL( 0, m_pPlayer->edict(), m_usSnarkFire, 0.0, (float *)&g_vecZero, (float *)&g_vecZero, 0.0, 0.0, pev->body, 0, 0, 0 ); + + int flags; +#ifdef CLIENT_WEAPONS + flags = FEV_NOTHOST; +#else + flags = 0; +#endif + + PLAYBACK_EVENT_FULL( flags, m_pPlayer->edict(), m_usSnarkFire, 0.0, (float *)&g_vecZero, (float *)&g_vecZero, 0.0, 0.0, 0, 0, 0, 0 ); if ( tr.fAllSolid == 0 && tr.fStartSolid == 0 && tr.flFraction > 0.25 ) { // player "shoot" animation m_pPlayer->SetAnimation( PLAYER_ATTACK1 ); +#ifndef CLIENT_DLL CBaseEntity *pSqueak = CBaseEntity::Create( "monster_snark", tr.vecEndPos, m_pPlayer->pev->v_angle, m_pPlayer->edict() ); pSqueak->pev->velocity = gpGlobals->v_forward * 200 + m_pPlayer->pev->velocity; +#endif // play hunt sound float flRndSound = RANDOM_FLOAT ( 0 , 1 ); if ( flRndSound <= 0.5 ) EMIT_SOUND_DYN(ENT(pev), CHAN_VOICE, "squeek/sqk_hunt2.wav", 1, ATTN_NORM, 0, 105); - else EMIT_SOUND_DYN(ENT(pev), CHAN_VOICE, "squeek/sqk_hunt3.wav", 1, ATTN_NORM, 0, 105); + else + EMIT_SOUND_DYN(ENT(pev), CHAN_VOICE, "squeek/sqk_hunt3.wav", 1, ATTN_NORM, 0, 105); m_pPlayer->m_iWeaponVolume = QUIET_GUN_VOLUME; + m_pPlayer->m_rgAmmo[m_iPrimaryAmmoType]--; + m_fJustThrown = 1; m_flNextPrimaryAttack = UTIL_WeaponTimeBase() + 0.3; @@ -496,9 +552,16 @@ void CSqueak::PrimaryAttack() } +void CSqueak::SecondaryAttack( void ) +{ + +} + + void CSqueak::WeaponIdle( void ) { - if ( m_flTimeWeaponIdle > UTIL_WeaponTimeBase() ) return; + if ( m_flTimeWeaponIdle > UTIL_WeaponTimeBase() ) + return; if (m_fJustThrown) { @@ -511,12 +574,12 @@ void CSqueak::WeaponIdle( void ) } SendWeaponAnim( SQUEAK_UP ); - m_flTimeWeaponIdle = UTIL_WeaponTimeBase() + RANDOM_LONG( 10, 15 ); + m_flTimeWeaponIdle = UTIL_WeaponTimeBase() + UTIL_SharedRandomFloat( m_pPlayer->random_seed, 10, 15 ); return; } int iAnim; - float flRand = RANDOM_FLOAT( 0, 1 ); + float flRand = UTIL_SharedRandomFloat( m_pPlayer->random_seed, 0, 1 ); if (flRand <= 0.75) { iAnim = SQUEAK_IDLE1; @@ -533,4 +596,6 @@ void CSqueak::WeaponIdle( void ) m_flTimeWeaponIdle = UTIL_WeaponTimeBase() + 80.0 / 16.0; } SendWeaponAnim( iAnim ); -} \ No newline at end of file +} + +#endif \ No newline at end of file diff --git a/spirit/stats.cpp b/dlls/stats.cpp similarity index 93% rename from spirit/stats.cpp rename to dlls/stats.cpp index e5e2799f..3323c1ca 100644 --- a/spirit/stats.cpp +++ b/dlls/stats.cpp @@ -1,157 +1,156 @@ -//========= Copyright © 1996-2002, Valve LLC, All rights reserved. ============ -// -// Purpose: New version of the slider bar -// -// $NoKeywords: $ -//============================================================================= - -#include "extdll.h" -#include "util.h" - -#include "cbase.h" -#include "player.h" -#include "trains.h" -#include "nodes.h" -#include "weapons.h" -#include "soundent.h" -#include "monsters.h" -//#include "..\engine\shake.h" -#include "shake.h" -#include "decals.h" -#include "gamerules.h" - - -float AmmoDamage( const char *pName ) -{ - if ( !pName ) - return 0; - - if ( !strcmp( pName, "9mm" ) ) - return gSkillData.plrDmg9MM; - if ( !strcmp( pName, "357" ) ) - return gSkillData.plrDmg357; - if ( !strcmp( pName, "ARgrenades" ) ) - return gSkillData.plrDmgM203Grenade; - if ( !strcmp( pName, "buckshot" ) ) - return gSkillData.plrDmgBuckshot; - if ( !strcmp( pName, "bolts") ) - return gSkillData.plrDmgCrossbowMonster; - if ( !strcmp( pName, "rockets") ) - return gSkillData.plrDmgRPG; - if ( !strcmp( pName, "uranium") ) - return gSkillData.plrDmgGauss; - if ( !strcmp( pName, "Hand Grenade") ) - return gSkillData.plrDmgHandGrenade; - if ( !strcmp( pName, "Satchel Charge") ) - return gSkillData.plrDmgSatchel; - if ( !strcmp( pName, "Trip Mine") ) - return gSkillData.plrDmgTripmine; - - return 0; -} - - -void UpdateStatsFile( float dataTime, char *pMapname, float health, float ammo, int skillLevel ) -{ - FILE *fp; - - fp = fopen( "stats.txt", "a" ); - if ( !fp ) - return; - fprintf( fp, "%6.2f, %6.2f, %6.2f, %s, %2d\n", dataTime, health, ammo, pMapname, skillLevel ); - fclose( fp ); -} - - -#define AMMO_THRESHOLD 10 // This much ammo goes by before it is "interesting" -#define HEALTH_THRESHOLD 10 // Same for health -#define OUTPUT_LATENCY 3 // This many seconds for ammo/health to settle - -typedef struct -{ - int lastAmmo; - float lastHealth; - float lastOutputTime; // NOTE: These times are in "game" time -- a running total of elapsed time since the game started - float nextOutputTime; - float dataTime; - float gameTime; - float lastGameTime; -} TESTSTATS; - -TESTSTATS gStats = {0,0,0,0,0,0,0}; - -void UpdateStats( CBasePlayer *pPlayer ) -{ - int i; - - int ammoCount[ MAX_AMMO_SLOTS ]; - memcpy( ammoCount, pPlayer->m_rgAmmo, MAX_AMMO_SLOTS * sizeof(int) ); - - // Keep a running time, so the graph doesn't overlap - - if ( gpGlobals->time < gStats.lastGameTime ) // Changed level or died, don't b0rk - { - gStats.lastGameTime = gpGlobals->time; - gStats.dataTime = gStats.gameTime; - } - - gStats.gameTime += gpGlobals->time - gStats.lastGameTime; - gStats.lastGameTime = gpGlobals->time; - - for (i = 0; i < MAX_ITEM_TYPES; i++) - { - CBasePlayerItem *p = pPlayer->m_rgpPlayerItems[i]; - while (p) - { - ItemInfo II; - - memset(&II, 0, sizeof(II)); - p->GetItemInfo(&II); - - int index = pPlayer->GetAmmoIndex(II.pszAmmo1); - if ( index >= 0 ) - ammoCount[ index ] += ((CBasePlayerWeapon *)p)->m_iClip; - - p = p->m_pNext; - } - } - - float ammo = 0; - for (i = 1; i < MAX_AMMO_SLOTS; i++) - { - ammo += ammoCount[i] * AmmoDamage( CBasePlayerItem::AmmoInfoArray[i].pszName ); - } - - float health = pPlayer->pev->health + pPlayer->pev->armorvalue * 2; // Armor is 2X health - float ammoDelta = fabs( ammo - gStats.lastAmmo ); - float healthDelta = fabs( health - gStats.lastHealth ); - int forceWrite = 0; - if ( health <= 0 && gStats.lastHealth > 0 ) - forceWrite = 1; - - if ( (ammoDelta > AMMO_THRESHOLD || healthDelta > HEALTH_THRESHOLD) && !forceWrite ) - { - if ( gStats.nextOutputTime == 0 ) - gStats.dataTime = gStats.gameTime; - - gStats.lastAmmo = ammo; - gStats.lastHealth = health; - - gStats.nextOutputTime = gStats.gameTime + OUTPUT_LATENCY; - } - else if ( (gStats.nextOutputTime != 0 && gStats.nextOutputTime < gStats.gameTime) || forceWrite ) - { - UpdateStatsFile( gStats.dataTime, (char *)STRING(gpGlobals->mapname), health, ammo, (int)CVAR_GET_FLOAT("skill") ); - - gStats.lastAmmo = ammo; - gStats.lastHealth = health; - gStats.lastOutputTime = gStats.gameTime; - gStats.nextOutputTime = 0; - } -} - -void InitStats( CBasePlayer *pPlayer ) -{ - gStats.lastGameTime = gpGlobals->time; // Fixup stats time -} - +//========= Copyright © 1996-2002, Valve LLC, All rights reserved. ============ +// +// Purpose: New version of the slider bar +// +// $NoKeywords: $ +//============================================================================= + +#include "extdll.h" +#include "util.h" + +#include "cbase.h" +#include "player.h" +#include "trains.h" +#include "nodes.h" +#include "weapons.h" +#include "soundent.h" +#include "monsters.h" +#include "..\engine\shake.h" +#include "decals.h" +#include "gamerules.h" + + +float AmmoDamage( const char *pName ) +{ + if ( !pName ) + return 0; + + if ( !strcmp( pName, "9mm" ) ) + return gSkillData.plrDmg9MM; + if ( !strcmp( pName, "357" ) ) + return gSkillData.plrDmg357; + if ( !strcmp( pName, "ARgrenades" ) ) + return gSkillData.plrDmgM203Grenade; + if ( !strcmp( pName, "buckshot" ) ) + return gSkillData.plrDmgBuckshot; + if ( !strcmp( pName, "bolts") ) + return gSkillData.plrDmgCrossbowMonster; + if ( !strcmp( pName, "rockets") ) + return gSkillData.plrDmgRPG; + if ( !strcmp( pName, "uranium") ) + return gSkillData.plrDmgGauss; + if ( !strcmp( pName, "Hand Grenade") ) + return gSkillData.plrDmgHandGrenade; + if ( !strcmp( pName, "Satchel Charge") ) + return gSkillData.plrDmgSatchel; + if ( !strcmp( pName, "Trip Mine") ) + return gSkillData.plrDmgTripmine; + + return 0; +} + + +void UpdateStatsFile( float dataTime, char *pMapname, float health, float ammo, int skillLevel ) +{ + FILE *fp; + + fp = fopen( "stats.txt", "a" ); + if ( !fp ) + return; + fprintf( fp, "%6.2f, %6.2f, %6.2f, %s, %2d\n", dataTime, health, ammo, pMapname, skillLevel ); + fclose( fp ); +} + + +#define AMMO_THRESHOLD 10 // This much ammo goes by before it is "interesting" +#define HEALTH_THRESHOLD 10 // Same for health +#define OUTPUT_LATENCY 3 // This many seconds for ammo/health to settle + +typedef struct +{ + int lastAmmo; + float lastHealth; + float lastOutputTime; // NOTE: These times are in "game" time -- a running total of elapsed time since the game started + float nextOutputTime; + float dataTime; + float gameTime; + float lastGameTime; +} TESTSTATS; + +TESTSTATS gStats = {0,0,0,0,0,0,0}; + +void UpdateStats( CBasePlayer *pPlayer ) +{ + int i; + + int ammoCount[ MAX_AMMO_SLOTS ]; + memcpy( ammoCount, pPlayer->m_rgAmmo, MAX_AMMO_SLOTS * sizeof(int) ); + + // Keep a running time, so the graph doesn't overlap + + if ( gpGlobals->time < gStats.lastGameTime ) // Changed level or died, don't b0rk + { + gStats.lastGameTime = gpGlobals->time; + gStats.dataTime = gStats.gameTime; + } + + gStats.gameTime += gpGlobals->time - gStats.lastGameTime; + gStats.lastGameTime = gpGlobals->time; + + for (i = 0; i < MAX_ITEM_TYPES; i++) + { + CBasePlayerItem *p = pPlayer->m_rgpPlayerItems[i]; + while (p) + { + ItemInfo II; + + memset(&II, 0, sizeof(II)); + p->GetItemInfo(&II); + + int index = pPlayer->GetAmmoIndex(II.pszAmmo1); + if ( index >= 0 ) + ammoCount[ index ] += ((CBasePlayerWeapon *)p)->m_iClip; + + p = p->m_pNext; + } + } + + float ammo = 0; + for (i = 1; i < MAX_AMMO_SLOTS; i++) + { + ammo += ammoCount[i] * AmmoDamage( CBasePlayerItem::AmmoInfoArray[i].pszName ); + } + + float health = pPlayer->pev->health + pPlayer->pev->armorvalue * 2; // Armor is 2X health + float ammoDelta = fabs( ammo - gStats.lastAmmo ); + float healthDelta = fabs( health - gStats.lastHealth ); + int forceWrite = 0; + if ( health <= 0 && gStats.lastHealth > 0 ) + forceWrite = 1; + + if ( (ammoDelta > AMMO_THRESHOLD || healthDelta > HEALTH_THRESHOLD) && !forceWrite ) + { + if ( gStats.nextOutputTime == 0 ) + gStats.dataTime = gStats.gameTime; + + gStats.lastAmmo = ammo; + gStats.lastHealth = health; + + gStats.nextOutputTime = gStats.gameTime + OUTPUT_LATENCY; + } + else if ( (gStats.nextOutputTime != 0 && gStats.nextOutputTime < gStats.gameTime) || forceWrite ) + { + UpdateStatsFile( gStats.dataTime, (char *)STRING(gpGlobals->mapname), health, ammo, (int)CVAR_GET_FLOAT("skill") ); + + gStats.lastAmmo = ammo; + gStats.lastHealth = health; + gStats.lastOutputTime = gStats.gameTime; + gStats.nextOutputTime = 0; + } +} + +void InitStats( CBasePlayer *pPlayer ) +{ + gStats.lastGameTime = gpGlobals->time; // Fixup stats time +} + diff --git a/dlls/subs.cpp b/dlls/subs.cpp new file mode 100644 index 00000000..a1093661 --- /dev/null +++ b/dlls/subs.cpp @@ -0,0 +1,559 @@ +/*** +* +* Copyright (c) 1996-2002, 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. +* +****/ +/* + +===== subs.cpp ======================================================== + + frequently used global functions + +*/ + +#include "extdll.h" +#include "util.h" +#include "cbase.h" +#include "saverestore.h" +#include "nodes.h" +#include "doors.h" + +extern CGraph WorldGraph; + +extern BOOL FEntIsVisible(entvars_t* pev, entvars_t* pevTarget); + +extern DLL_GLOBAL int g_iSkillLevel; + + +// Landmark class +void CPointEntity :: Spawn( void ) +{ + pev->solid = SOLID_NOT; +// UTIL_SetSize(pev, g_vecZero, g_vecZero); +} + + +class CNullEntity : public CBaseEntity +{ +public: + void Spawn( void ); +}; + + +// Null Entity, remove on startup +void CNullEntity :: Spawn( void ) +{ + REMOVE_ENTITY(ENT(pev)); +} +LINK_ENTITY_TO_CLASS(info_null,CNullEntity); + +class CBaseDMStart : public CPointEntity +{ +public: + void KeyValue( KeyValueData *pkvd ); + BOOL IsTriggered( CBaseEntity *pEntity ); + +private: +}; + +// These are the new entry points to entities. +LINK_ENTITY_TO_CLASS(info_player_deathmatch,CBaseDMStart); +LINK_ENTITY_TO_CLASS(info_player_start,CPointEntity); +LINK_ENTITY_TO_CLASS(info_landmark,CPointEntity); + +void CBaseDMStart::KeyValue( KeyValueData *pkvd ) +{ + if (FStrEq(pkvd->szKeyName, "master")) + { + pev->netname = ALLOC_STRING(pkvd->szValue); + pkvd->fHandled = TRUE; + } + else + CPointEntity::KeyValue( pkvd ); +} + +BOOL CBaseDMStart::IsTriggered( CBaseEntity *pEntity ) +{ + BOOL master = UTIL_IsMasterTriggered( pev->netname, pEntity ); + + return master; +} + +// This updates global tables that need to know about entities being removed +void CBaseEntity::UpdateOnRemove( void ) +{ + int i; + + if ( FBitSet( pev->flags, FL_GRAPHED ) ) + { + // this entity was a LinkEnt in the world node graph, so we must remove it from + // the graph since we are removing it from the world. + for ( i = 0 ; i < WorldGraph.m_cLinks ; i++ ) + { + if ( WorldGraph.m_pLinkPool [ i ].m_pLinkEnt == pev ) + { + // if this link has a link ent which is the same ent that is removing itself, remove it! + WorldGraph.m_pLinkPool [ i ].m_pLinkEnt = NULL; + } + } + } + if ( pev->globalname ) + gGlobalState.EntitySetState( pev->globalname, GLOBAL_DEAD ); +} + +// Convenient way to delay removing oneself +void CBaseEntity :: SUB_Remove( void ) +{ + UpdateOnRemove(); + if (pev->health > 0) + { + // this situation can screw up monsters who can't tell their entity pointers are invalid. + pev->health = 0; + ALERT( at_aiconsole, "SUB_Remove called on entity with health > 0\n"); + } + + REMOVE_ENTITY(ENT(pev)); +} + + +// Convenient way to explicitly do nothing (passed to functions that require a method) +void CBaseEntity :: SUB_DoNothing( void ) +{ +} + + +// Global Savedata for Delay +TYPEDESCRIPTION CBaseDelay::m_SaveData[] = +{ + DEFINE_FIELD( CBaseDelay, m_flDelay, FIELD_FLOAT ), + DEFINE_FIELD( CBaseDelay, m_iszKillTarget, FIELD_STRING ), +}; + +IMPLEMENT_SAVERESTORE( CBaseDelay, CBaseEntity ); + +void CBaseDelay :: KeyValue( KeyValueData *pkvd ) +{ + if (FStrEq(pkvd->szKeyName, "delay")) + { + m_flDelay = atof( pkvd->szValue ); + pkvd->fHandled = TRUE; + } + else if (FStrEq(pkvd->szKeyName, "killtarget")) + { + m_iszKillTarget = ALLOC_STRING(pkvd->szValue); + pkvd->fHandled = TRUE; + } + else + { + CBaseEntity::KeyValue( pkvd ); + } +} + + +/* +============================== +SUB_UseTargets + +If self.delay is set, a DelayedUse entity will be created that will actually +do the SUB_UseTargets after that many seconds have passed. + +Removes all entities with a targetname that match self.killtarget, +and removes them, so some events can remove other triggers. + +Search for (string)targetname in all entities that +match (string)self.target and call their .use function (if they have one) + +============================== +*/ +void CBaseEntity :: SUB_UseTargets( CBaseEntity *pActivator, USE_TYPE useType, float value ) +{ + // + // fire targets + // + if (!FStringNull(pev->target)) + { + FireTargets( STRING(pev->target), pActivator, this, useType, value ); + } +} + + +void FireTargets( const char *targetName, CBaseEntity *pActivator, CBaseEntity *pCaller, USE_TYPE useType, float value ) +{ + edict_t *pentTarget = NULL; + if ( !targetName ) + return; + + ALERT( at_aiconsole, "Firing: (%s)\n", targetName ); + + for (;;) + { + pentTarget = FIND_ENTITY_BY_TARGETNAME(pentTarget, targetName); + if (FNullEnt(pentTarget)) + break; + + CBaseEntity *pTarget = CBaseEntity::Instance( pentTarget ); + if ( pTarget && !(pTarget->pev->flags & FL_KILLME) ) // Don't use dying ents + { + ALERT( at_aiconsole, "Found: %s, firing (%s)\n", STRING(pTarget->pev->classname), targetName ); + pTarget->Use( pActivator, pCaller, useType, value ); + } + } +} + +LINK_ENTITY_TO_CLASS( DelayedUse, CBaseDelay ); + + +void CBaseDelay :: SUB_UseTargets( CBaseEntity *pActivator, USE_TYPE useType, float value ) +{ + // + // exit immediatly if we don't have a target or kill target + // + if (FStringNull(pev->target) && !m_iszKillTarget) + return; + + // + // check for a delay + // + if (m_flDelay != 0) + { + // create a temp object to fire at a later time + CBaseDelay *pTemp = GetClassPtr( (CBaseDelay *)NULL); + pTemp->pev->classname = MAKE_STRING("DelayedUse"); + + pTemp->pev->nextthink = gpGlobals->time + m_flDelay; + + pTemp->SetThink( DelayThink ); + + // Save the useType + pTemp->pev->button = (int)useType; + pTemp->m_iszKillTarget = m_iszKillTarget; + pTemp->m_flDelay = 0; // prevent "recursion" + pTemp->pev->target = pev->target; + + // HACKHACK + // This wasn't in the release build of Half-Life. We should have moved m_hActivator into this class + // but changing member variable hierarchy would break save/restore without some ugly code. + // This code is not as ugly as that code + if ( pActivator && pActivator->IsPlayer() ) // If a player activates, then save it + { + pTemp->pev->owner = pActivator->edict(); + } + else + { + pTemp->pev->owner = NULL; + } + + return; + } + + // + // kill the killtargets + // + + if ( m_iszKillTarget ) + { + edict_t *pentKillTarget = NULL; + + ALERT( at_aiconsole, "KillTarget: %s\n", STRING(m_iszKillTarget) ); + pentKillTarget = FIND_ENTITY_BY_TARGETNAME( NULL, STRING(m_iszKillTarget) ); + while ( !FNullEnt(pentKillTarget) ) + { + UTIL_Remove( CBaseEntity::Instance(pentKillTarget) ); + + ALERT( at_aiconsole, "killing %s\n", STRING( pentKillTarget->v.classname ) ); + pentKillTarget = FIND_ENTITY_BY_TARGETNAME( pentKillTarget, STRING(m_iszKillTarget) ); + } + } + + // + // fire targets + // + if (!FStringNull(pev->target)) + { + FireTargets( STRING(pev->target), pActivator, this, useType, value ); + } +} + + +/* +void CBaseDelay :: SUB_UseTargetsEntMethod( void ) +{ + SUB_UseTargets(pev); +} +*/ + +/* +QuakeEd only writes a single float for angles (bad idea), so up and down are +just constant angles. +*/ +void SetMovedir( entvars_t *pev ) +{ + if (pev->angles == Vector(0, -1, 0)) + { + pev->movedir = Vector(0, 0, 1); + } + else if (pev->angles == Vector(0, -2, 0)) + { + pev->movedir = Vector(0, 0, -1); + } + else + { + UTIL_MakeVectors(pev->angles); + pev->movedir = gpGlobals->v_forward; + } + + pev->angles = g_vecZero; +} + + + + +void CBaseDelay::DelayThink( void ) +{ + CBaseEntity *pActivator = NULL; + + if ( pev->owner != NULL ) // A player activated this on delay + { + pActivator = CBaseEntity::Instance( pev->owner ); + } + // The use type is cached (and stashed) in pev->button + SUB_UseTargets( pActivator, (USE_TYPE)pev->button, 0 ); + REMOVE_ENTITY(ENT(pev)); +} + + +// Global Savedata for Toggle +TYPEDESCRIPTION CBaseToggle::m_SaveData[] = +{ + DEFINE_FIELD( CBaseToggle, m_toggle_state, FIELD_INTEGER ), + DEFINE_FIELD( CBaseToggle, m_flActivateFinished, FIELD_TIME ), + DEFINE_FIELD( CBaseToggle, m_flMoveDistance, FIELD_FLOAT ), + DEFINE_FIELD( CBaseToggle, m_flWait, FIELD_FLOAT ), + DEFINE_FIELD( CBaseToggle, m_flLip, FIELD_FLOAT ), + DEFINE_FIELD( CBaseToggle, m_flTWidth, FIELD_FLOAT ), + DEFINE_FIELD( CBaseToggle, m_flTLength, FIELD_FLOAT ), + DEFINE_FIELD( CBaseToggle, m_vecPosition1, FIELD_POSITION_VECTOR ), + DEFINE_FIELD( CBaseToggle, m_vecPosition2, FIELD_POSITION_VECTOR ), + DEFINE_FIELD( CBaseToggle, m_vecAngle1, FIELD_VECTOR ), // UNDONE: Position could go through transition, but also angle? + DEFINE_FIELD( CBaseToggle, m_vecAngle2, FIELD_VECTOR ), // UNDONE: Position could go through transition, but also angle? + DEFINE_FIELD( CBaseToggle, m_cTriggersLeft, FIELD_INTEGER ), + DEFINE_FIELD( CBaseToggle, m_flHeight, FIELD_FLOAT ), + DEFINE_FIELD( CBaseToggle, m_hActivator, FIELD_EHANDLE ), + DEFINE_FIELD( CBaseToggle, m_pfnCallWhenMoveDone, FIELD_FUNCTION ), + DEFINE_FIELD( CBaseToggle, m_vecFinalDest, FIELD_POSITION_VECTOR ), + DEFINE_FIELD( CBaseToggle, m_vecFinalAngle, FIELD_VECTOR ), + DEFINE_FIELD( CBaseToggle, m_sMaster, FIELD_STRING), + DEFINE_FIELD( CBaseToggle, m_bitsDamageInflict, FIELD_INTEGER ), // damage type inflicted +}; +IMPLEMENT_SAVERESTORE( CBaseToggle, CBaseAnimating ); + + +void CBaseToggle::KeyValue( KeyValueData *pkvd ) +{ + if (FStrEq(pkvd->szKeyName, "lip")) + { + m_flLip = atof(pkvd->szValue); + pkvd->fHandled = TRUE; + } + else if (FStrEq(pkvd->szKeyName, "wait")) + { + m_flWait = atof(pkvd->szValue); + pkvd->fHandled = TRUE; + } + else if (FStrEq(pkvd->szKeyName, "master")) + { + m_sMaster = ALLOC_STRING(pkvd->szValue); + pkvd->fHandled = TRUE; + } + else if (FStrEq(pkvd->szKeyName, "distance")) + { + m_flMoveDistance = atof(pkvd->szValue); + pkvd->fHandled = TRUE; + } + else + CBaseDelay::KeyValue( pkvd ); +} + +/* +============= +LinearMove + +calculate pev->velocity and pev->nextthink to reach vecDest from +pev->origin traveling at flSpeed +=============== +*/ +void CBaseToggle :: LinearMove( Vector vecDest, float flSpeed ) +{ + ASSERTSZ(flSpeed != 0, "LinearMove: no speed is defined!"); +// ASSERTSZ(m_pfnCallWhenMoveDone != NULL, "LinearMove: no post-move function defined"); + + m_vecFinalDest = vecDest; + + // Already there? + if (vecDest == pev->origin) + { + LinearMoveDone(); + return; + } + + // set destdelta to the vector needed to move + Vector vecDestDelta = vecDest - pev->origin; + + // divide vector length by speed to get time to reach dest + float flTravelTime = vecDestDelta.Length() / flSpeed; + + // set nextthink to trigger a call to LinearMoveDone when dest is reached + pev->nextthink = pev->ltime + flTravelTime; + SetThink( LinearMoveDone ); + + // scale the destdelta vector by the time spent traveling to get velocity + pev->velocity = vecDestDelta / flTravelTime; +} + + +/* +============ +After moving, set origin to exact final destination, call "move done" function +============ +*/ +void CBaseToggle :: LinearMoveDone( void ) +{ + UTIL_SetOrigin(pev, m_vecFinalDest); + pev->velocity = g_vecZero; + pev->nextthink = -1; + if ( m_pfnCallWhenMoveDone ) + (this->*m_pfnCallWhenMoveDone)(); +} + +BOOL CBaseToggle :: IsLockedByMaster( void ) +{ + if (m_sMaster && !UTIL_IsMasterTriggered(m_sMaster, m_hActivator)) + return TRUE; + else + return FALSE; +} + +/* +============= +AngularMove + +calculate pev->velocity and pev->nextthink to reach vecDest from +pev->origin traveling at flSpeed +Just like LinearMove, but rotational. +=============== +*/ +void CBaseToggle :: AngularMove( Vector vecDestAngle, float flSpeed ) +{ + ASSERTSZ(flSpeed != 0, "AngularMove: no speed is defined!"); +// ASSERTSZ(m_pfnCallWhenMoveDone != NULL, "AngularMove: no post-move function defined"); + + m_vecFinalAngle = vecDestAngle; + + // Already there? + if (vecDestAngle == pev->angles) + { + AngularMoveDone(); + return; + } + + // set destdelta to the vector needed to move + Vector vecDestDelta = vecDestAngle - pev->angles; + + // divide by speed to get time to reach dest + float flTravelTime = vecDestDelta.Length() / flSpeed; + + // set nextthink to trigger a call to AngularMoveDone when dest is reached + pev->nextthink = pev->ltime + flTravelTime; + SetThink( AngularMoveDone ); + + // scale the destdelta vector by the time spent traveling to get velocity + pev->avelocity = vecDestDelta / flTravelTime; +} + + +/* +============ +After rotating, set angle to exact final angle, call "move done" function +============ +*/ +void CBaseToggle :: AngularMoveDone( void ) +{ + pev->angles = m_vecFinalAngle; + pev->avelocity = g_vecZero; + pev->nextthink = -1; + if ( m_pfnCallWhenMoveDone ) + (this->*m_pfnCallWhenMoveDone)(); +} + + +float CBaseToggle :: AxisValue( int flags, const Vector &angles ) +{ + if ( FBitSet(flags, SF_DOOR_ROTATE_Z) ) + return angles.z; + if ( FBitSet(flags, SF_DOOR_ROTATE_X) ) + return angles.x; + + return angles.y; +} + + +void CBaseToggle :: AxisDir( entvars_t *pev ) +{ + if ( FBitSet(pev->spawnflags, SF_DOOR_ROTATE_Z) ) + pev->movedir = Vector ( 0, 0, 1 ); // around z-axis + else if ( FBitSet(pev->spawnflags, SF_DOOR_ROTATE_X) ) + pev->movedir = Vector ( 1, 0, 0 ); // around x-axis + else + pev->movedir = Vector ( 0, 1, 0 ); // around y-axis +} + + +float CBaseToggle :: AxisDelta( int flags, const Vector &angle1, const Vector &angle2 ) +{ + if ( FBitSet (flags, SF_DOOR_ROTATE_Z) ) + return angle1.z - angle2.z; + + if ( FBitSet (flags, SF_DOOR_ROTATE_X) ) + return angle1.x - angle2.x; + + return angle1.y - angle2.y; +} + + +/* +============= +FEntIsVisible + +returns TRUE if the passed entity is visible to caller, even if not infront () +============= +*/ + BOOL +FEntIsVisible( + entvars_t* pev, + entvars_t* pevTarget) + { + Vector vecSpot1 = pev->origin + pev->view_ofs; + Vector vecSpot2 = pevTarget->origin + pevTarget->view_ofs; + TraceResult tr; + + UTIL_TraceLine(vecSpot1, vecSpot2, ignore_monsters, ENT(pev), &tr); + + if (tr.fInOpen && tr.fInWater) + return FALSE; // sight line crossed contents + + if (tr.flFraction == 1) + return TRUE; + + return FALSE; + } + + diff --git a/spirit/talkmonster.cpp b/dlls/talkmonster.cpp similarity index 87% rename from spirit/talkmonster.cpp rename to dlls/talkmonster.cpp index 9e490bf5..76ac05b7 100644 --- a/spirit/talkmonster.cpp +++ b/dlls/talkmonster.cpp @@ -42,8 +42,6 @@ TYPEDESCRIPTION CTalkMonster::m_SaveData[] = DEFINE_FIELD( CTalkMonster, m_useTime, FIELD_TIME ), DEFINE_FIELD( CTalkMonster, m_iszUse, FIELD_STRING ), DEFINE_FIELD( CTalkMonster, m_iszUnUse, FIELD_STRING ), - DEFINE_FIELD( CTalkMonster, m_iszDecline, FIELD_STRING ), //LRC - DEFINE_FIELD( CTalkMonster, m_iszSpeakAs, FIELD_STRING ), //LRC DEFINE_FIELD( CTalkMonster, m_flLastSaidSmelled, FIELD_TIME ), DEFINE_FIELD( CTalkMonster, m_flStopTalkTime, FIELD_TIME ), DEFINE_FIELD( CTalkMonster, m_hTalkTarget, FIELD_EHANDLE ), @@ -794,66 +792,8 @@ void CTalkMonster :: TalkInit( void ) CTalkMonster::g_talkWaitTime = 0; - if (m_iszSpeakAs) //LRC: changing voice groups for monsters - { - char szBuf[64]; - strcpy(szBuf,STRING(m_iszSpeakAs)); - strcat(szBuf,"_"); - char *szAssign = &(szBuf[strlen(szBuf)]); - - //LRC - this is pretty dodgy; test with save/restore. - strcpy(szAssign,"ANSWER"); - m_szGrp[TLK_ANSWER] = STRING(ALLOC_STRING(szBuf)); - strcpy(szAssign,"QUESTION"); - m_szGrp[TLK_QUESTION] = STRING(ALLOC_STRING(szBuf)); - strcpy(szAssign,"IDLE"); - m_szGrp[TLK_IDLE] = STRING(ALLOC_STRING(szBuf)); - strcpy(szAssign,"STARE"); - m_szGrp[TLK_STARE] = STRING(ALLOC_STRING(szBuf)); - if (pev->spawnflags & SF_MONSTER_PREDISASTER) //LRC - strcpy(szAssign,"PFOLLOW"); - else - strcpy(szAssign,"OK"); - m_szGrp[TLK_USE] = STRING(ALLOC_STRING(szBuf)); - if (pev->spawnflags & SF_MONSTER_PREDISASTER) //LRC - strcpy(szAssign,"PWAIT"); - else - strcpy(szAssign,"WAIT"); - m_szGrp[TLK_UNUSE] = STRING(ALLOC_STRING(szBuf)); - if (pev->spawnflags & SF_MONSTER_PREDISASTER) //LRC - strcpy(szAssign,"POK"); - else - strcpy(szAssign,"NOTOK"); - m_szGrp[TLK_DECLINE] = STRING(ALLOC_STRING(szBuf)); - strcpy(szAssign,"STOP"); - m_szGrp[TLK_STOP] = STRING(ALLOC_STRING(szBuf)); - strcpy(szAssign,"NOSHOOT"); - m_szGrp[TLK_NOSHOOT] = STRING(ALLOC_STRING(szBuf)); - strcpy(szAssign,"HELLO"); - m_szGrp[TLK_HELLO] = STRING(ALLOC_STRING(szBuf)); - strcpy(szAssign,"PLHURT1"); - m_szGrp[TLK_PLHURT1] = STRING(ALLOC_STRING(szBuf)); - strcpy(szAssign,"PLHURT2"); - m_szGrp[TLK_PLHURT2] = STRING(ALLOC_STRING(szBuf)); - strcpy(szAssign,"PLHURT3"); - m_szGrp[TLK_PLHURT3] = STRING(ALLOC_STRING(szBuf)); - strcpy(szAssign,"PHELLO"); - m_szGrp[TLK_PHELLO] = STRING(ALLOC_STRING(szBuf)); - strcpy(szAssign,"PIDLE"); - m_szGrp[TLK_PIDLE] = STRING(ALLOC_STRING(szBuf)); - strcpy(szAssign,"PQUESTION"); - m_szGrp[TLK_PQUESTION] = STRING(ALLOC_STRING(szBuf)); - strcpy(szAssign,"SMELL"); - m_szGrp[TLK_SMELL] = STRING(ALLOC_STRING(szBuf)); - strcpy(szAssign,"WOUND"); - m_szGrp[TLK_WOUND] = STRING(ALLOC_STRING(szBuf)); - strcpy(szAssign,"MORTAL"); - m_szGrp[TLK_MORTAL] = STRING(ALLOC_STRING(szBuf)); - } - m_voicePitch = 100; } - //========================================================= // FindNearestFriend // Scan for nearest, visible friend. If fPlayer is true, look for @@ -995,7 +935,7 @@ int CTalkMonster :: FOkToSpeak( void ) return FALSE; // if player is not in pvs, don't speak - if (!IsAlive() || (FNullEnt(FIND_CLIENT_IN_PVS(edict())) && !HaveCamerasInPVS( edict() ))) + if (!IsAlive() || FNullEnt(FIND_CLIENT_IN_PVS(edict()))) return FALSE; // don't talk if you're in combat @@ -1457,7 +1397,7 @@ void CTalkMonster::StartFollowing( CBaseEntity *pLeader ) ClearSchedule(); } -//LRC- redefined, now returns true if following would be physically possible + BOOL CTalkMonster::CanFollow( void ) { if ( m_MonsterState == MONSTERSTATE_SCRIPT ) @@ -1469,49 +1409,37 @@ BOOL CTalkMonster::CanFollow( void ) if ( !IsAlive() ) return FALSE; - return TRUE; + return !IsFollowing(); } -//LRC- rewritten void CTalkMonster :: FollowerUse( CBaseEntity *pActivator, CBaseEntity *pCaller, USE_TYPE useType, float value ) { // Don't allow use during a scripted_sentence if ( m_useTime > gpGlobals->time ) return; - //ALERT(at_console,"Talkmonster was Used: "); - - // CanFollow is now true if the monster could physically follow anyone - if ( pCaller != NULL && pCaller->IsPlayer() && CanFollow() ) + if ( pCaller != NULL && pCaller->IsPlayer() ) + { + // Pre-disaster followers can't be used + if ( pev->spawnflags & SF_MONSTER_PREDISASTER ) { - if ( !IsFollowing() ) - { - // Pre-disaster followers can't be used unless they've got a master to override their behaviour... - if (IsLockedByMaster() || (pev->spawnflags & SF_MONSTER_PREDISASTER && !m_sMaster)) - { - //ALERT(at_console,"Decline\n"); DeclineFollowing(); } + else if ( CanFollow() ) + { + LimitFollowers( pCaller , 1 ); + + if ( m_afMemory & bits_MEMORY_PROVOKED ) + ALERT( at_console, "I'm not following you, you evil person!\n" ); else { - LimitFollowers( pCaller , 1 ); - if ( m_afMemory & bits_MEMORY_PROVOKED ) - { - //ALERT(at_console,"Fail\n"); - ALERT( at_aiconsole, "I'm not following you, you evil person!\n" ); - } - else - { - //ALERT(at_console,"Start\n"); - StartFollowing( pCaller ); - SetBits(m_bitsSaid, bit_saidHelloPlayer); // Don't say hi after you've started following - } + StartFollowing( pCaller ); + SetBits(m_bitsSaid, bit_saidHelloPlayer); // Don't say hi after you've started following } } else { - //ALERT(at_console,"Stop\n"); StopFollowing( TRUE ); } } @@ -1529,16 +1457,6 @@ void CTalkMonster::KeyValue( KeyValueData *pkvd ) m_iszUnUse = ALLOC_STRING(pkvd->szValue); pkvd->fHandled = TRUE; } - else if (FStrEq(pkvd->szKeyName, "RefusalSentence")) //LRC - { - m_iszDecline = ALLOC_STRING(pkvd->szValue); - pkvd->fHandled = TRUE; - } - else if (FStrEq(pkvd->szKeyName, "SpeakAs")) //LRC - { - m_iszSpeakAs = ALLOC_STRING(pkvd->szValue); - pkvd->fHandled = TRUE; - } else CBaseMonster::KeyValue( pkvd ); } @@ -1550,7 +1468,5 @@ void CTalkMonster::Precache( void ) m_szGrp[TLK_USE] = STRING( m_iszUse ); if ( m_iszUnUse ) m_szGrp[TLK_UNUSE] = STRING( m_iszUnUse ); - if ( m_iszDecline ) //LRC - m_szGrp[TLK_DECLINE] = STRING( m_iszDecline ); } diff --git a/spirit/talkmonster.h b/dlls/talkmonster.h similarity index 96% rename from spirit/talkmonster.h rename to dlls/talkmonster.h index a6736868..d59c55f4 100644 --- a/spirit/talkmonster.h +++ b/dlls/talkmonster.h @@ -1,186 +1,183 @@ -/*** -* -* Copyright (c) 1996-2002, 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. -* -* This source code contains proprietary and confidential information of -* Valve LLC and its suppliers. Access to this code is restricted to -* persons who have executed a written SDK license with Valve. Any access, -* use or distribution of this code by or to any unlicensed person is illegal. -* -****/ -#ifndef TALKMONSTER_H -#define TALKMONSTER_H - -#ifndef MONSTERS_H -#include "monsters.h" -#endif - -//========================================================= -// Talking monster base class -// Used for scientists and barneys -//========================================================= - -#define TALKRANGE_MIN 500.0 // don't talk to anyone farther away than this - -#define TLK_STARE_DIST 128 // anyone closer than this and looking at me is probably staring at me. - -#define bit_saidDamageLight (1<<0) // bits so we don't repeat key sentences -#define bit_saidDamageMedium (1<<1) -#define bit_saidDamageHeavy (1<<2) -#define bit_saidHelloPlayer (1<<3) -#define bit_saidWoundLight (1<<4) -#define bit_saidWoundHeavy (1<<5) -#define bit_saidHeard (1<<6) -#define bit_saidSmelled (1<<7) - -#define TLK_CFRIENDS 3 - -typedef enum -{ - TLK_ANSWER = 0, - TLK_QUESTION, - TLK_IDLE, - TLK_STARE, - TLK_USE, - TLK_UNUSE, - TLK_DECLINE, //LRC- refuse to accompany - TLK_STOP, - TLK_NOSHOOT, - TLK_HELLO, - TLK_PHELLO, - TLK_PIDLE, - TLK_PQUESTION, - TLK_PLHURT1, - TLK_PLHURT2, - TLK_PLHURT3, - TLK_SMELL, - TLK_WOUND, - TLK_MORTAL, - - TLK_CGROUPS, // MUST be last entry -} TALKGROUPNAMES; - - -enum -{ - SCHED_CANT_FOLLOW = LAST_COMMON_SCHEDULE + 1, - SCHED_MOVE_AWAY, // Try to get out of the player's way - SCHED_MOVE_AWAY_FOLLOW, // same, but follow afterward - SCHED_MOVE_AWAY_FAIL, // Turn back toward player - - LAST_TALKMONSTER_SCHEDULE, // MUST be last -}; - -enum -{ - TASK_CANT_FOLLOW = LAST_COMMON_TASK + 1, - TASK_MOVE_AWAY_PATH, - TASK_WALK_PATH_FOR_UNITS, - - TASK_TLK_RESPOND, // say my response - TASK_TLK_SPEAK, // question or remark - TASK_TLK_HELLO, // Try to say hello to player - TASK_TLK_HEADRESET, // reset head position - TASK_TLK_STOPSHOOTING, // tell player to stop shooting friend - TASK_TLK_STARE, // let the player know I know he's staring at me. - TASK_TLK_LOOK_AT_CLIENT,// faces player if not moving and not talking and in idle. - TASK_TLK_CLIENT_STARE, // same as look at client, but says something if the player stares. - TASK_TLK_EYECONTACT, // maintain eyecontact with person who I'm talking to - TASK_TLK_IDEALYAW, // set ideal yaw to face who I'm talking to - TASK_FACE_PLAYER, // Face the player - - LAST_TALKMONSTER_TASK, // MUST be last -}; - -class CTalkMonster : public CBaseMonster -{ -public: - void TalkInit( void ); - CBaseEntity *FindNearestFriend(BOOL fPlayer); - float TargetDistance( void ); - void StopTalking( void ) { SentenceStop(); } - - // Base Monster functions - void Precache( void ); - int TakeDamage( entvars_t *pevInflictor, entvars_t *pevAttacker, float flDamage, int bitsDamageType); - void Touch( CBaseEntity *pOther ); - void Killed( entvars_t *pevAttacker, int iGib ); - int IRelationship ( CBaseEntity *pTarget ); - virtual int CanPlaySentence( BOOL fDisregardState ); - virtual void PlaySentence( const char *pszSentence, float duration, float volume, float attenuation ); - void PlayScriptedSentence( const char *pszSentence, float duration, float volume, float attenuation, BOOL bConcurrent, CBaseEntity *pListener ); - void KeyValue( KeyValueData *pkvd ); - - // AI functions - void SetActivity ( Activity newActivity ); - Schedule_t *GetScheduleOfType ( int Type ); - void StartTask( Task_t *pTask ); - void RunTask( Task_t *pTask ); - void HandleAnimEvent( MonsterEvent_t *pEvent ); - void PrescheduleThink( void ); - - - // Conversations / communication - int GetVoicePitch( void ); - void IdleRespond( void ); - int FIdleSpeak( void ); - int FIdleStare( void ); - int FIdleHello( void ); - void IdleHeadTurn( Vector &vecFriend ); - int FOkToSpeak( void ); - void TrySmellTalk( void ); - CBaseEntity *EnumFriends( CBaseEntity *pentPrevious, int listNumber, BOOL bTrace ); - void AlertFriends( void ); - void ShutUpFriends( void ); - BOOL IsTalking( void ); - void Talk( float flDuration ); - // For following - BOOL CanFollow( void ); - BOOL IsFollowing( void ) { return m_hTargetEnt != NULL && m_hTargetEnt->IsPlayer(); } - void StopFollowing( BOOL clearSchedule ); - void StartFollowing( CBaseEntity *pLeader ); - virtual void DeclineFollowing( void ) {} - void LimitFollowers( CBaseEntity *pPlayer, int maxFollowers ); - - void EXPORT FollowerUse( CBaseEntity *pActivator, CBaseEntity *pCaller, USE_TYPE useType, float value ); - - virtual void SetAnswerQuestion( CTalkMonster *pSpeaker ); - virtual int FriendNumber( int arrayNumber ) { return arrayNumber; } - - virtual int Save( CSave &save ); - virtual int Restore( CRestore &restore ); - static TYPEDESCRIPTION m_SaveData[]; - - - static char *m_szFriends[TLK_CFRIENDS]; // array of friend names - static float g_talkWaitTime; - - int m_bitsSaid; // set bits for sentences we don't want repeated - int m_nSpeak; // number of times initiated talking - int m_voicePitch; // pitch of voice for this head - const char *m_szGrp[TLK_CGROUPS]; // sentence group names - float m_useTime; // Don't allow +USE until this time - int m_iszUse; // Custom +USE sentence group (follow) - int m_iszUnUse; // Custom +USE sentence group (stop following) - int m_iszDecline; // Custom +USE sentence group (refuse to follow) LRC - int m_iszSpeakAs; // Change the prefix for all this monster's speeches LRC - - float m_flLastSaidSmelled;// last time we talked about something that stinks - float m_flStopTalkTime;// when in the future that I'll be done saying this sentence. - - EHANDLE m_hTalkTarget; // who to look at while talking - CUSTOM_SCHEDULES; -}; - - -// Clients can push talkmonsters out of their way -#define bits_COND_CLIENT_PUSH ( bits_COND_SPECIAL1 ) -// Don't see a client right now. -#define bits_COND_CLIENT_UNSEEN ( bits_COND_SPECIAL2 ) - - -#endif //TALKMONSTER_H +/*** +* +* Copyright (c) 1996-2002, 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. +* +* This source code contains proprietary and confidential information of +* Valve LLC and its suppliers. Access to this code is restricted to +* persons who have executed a written SDK license with Valve. Any access, +* use or distribution of this code by or to any unlicensed person is illegal. +* +****/ +#ifndef TALKMONSTER_H +#define TALKMONSTER_H + +#ifndef MONSTERS_H +#include "monsters.h" +#endif + +//========================================================= +// Talking monster base class +// Used for scientists and barneys +//========================================================= + +#define TALKRANGE_MIN 500.0 // don't talk to anyone farther away than this + +#define TLK_STARE_DIST 128 // anyone closer than this and looking at me is probably staring at me. + +#define bit_saidDamageLight (1<<0) // bits so we don't repeat key sentences +#define bit_saidDamageMedium (1<<1) +#define bit_saidDamageHeavy (1<<2) +#define bit_saidHelloPlayer (1<<3) +#define bit_saidWoundLight (1<<4) +#define bit_saidWoundHeavy (1<<5) +#define bit_saidHeard (1<<6) +#define bit_saidSmelled (1<<7) + +#define TLK_CFRIENDS 3 + +typedef enum +{ + TLK_ANSWER = 0, + TLK_QUESTION, + TLK_IDLE, + TLK_STARE, + TLK_USE, + TLK_UNUSE, + TLK_STOP, + TLK_NOSHOOT, + TLK_HELLO, + TLK_PHELLO, + TLK_PIDLE, + TLK_PQUESTION, + TLK_PLHURT1, + TLK_PLHURT2, + TLK_PLHURT3, + TLK_SMELL, + TLK_WOUND, + TLK_MORTAL, + + TLK_CGROUPS, // MUST be last entry +} TALKGROUPNAMES; + + +enum +{ + SCHED_CANT_FOLLOW = LAST_COMMON_SCHEDULE + 1, + SCHED_MOVE_AWAY, // Try to get out of the player's way + SCHED_MOVE_AWAY_FOLLOW, // same, but follow afterward + SCHED_MOVE_AWAY_FAIL, // Turn back toward player + + LAST_TALKMONSTER_SCHEDULE, // MUST be last +}; + +enum +{ + TASK_CANT_FOLLOW = LAST_COMMON_TASK + 1, + TASK_MOVE_AWAY_PATH, + TASK_WALK_PATH_FOR_UNITS, + + TASK_TLK_RESPOND, // say my response + TASK_TLK_SPEAK, // question or remark + TASK_TLK_HELLO, // Try to say hello to player + TASK_TLK_HEADRESET, // reset head position + TASK_TLK_STOPSHOOTING, // tell player to stop shooting friend + TASK_TLK_STARE, // let the player know I know he's staring at me. + TASK_TLK_LOOK_AT_CLIENT,// faces player if not moving and not talking and in idle. + TASK_TLK_CLIENT_STARE, // same as look at client, but says something if the player stares. + TASK_TLK_EYECONTACT, // maintain eyecontact with person who I'm talking to + TASK_TLK_IDEALYAW, // set ideal yaw to face who I'm talking to + TASK_FACE_PLAYER, // Face the player + + LAST_TALKMONSTER_TASK, // MUST be last +}; + +class CTalkMonster : public CBaseMonster +{ +public: + void TalkInit( void ); + CBaseEntity *FindNearestFriend(BOOL fPlayer); + float TargetDistance( void ); + void StopTalking( void ) { SentenceStop(); } + + // Base Monster functions + void Precache( void ); + int TakeDamage( entvars_t *pevInflictor, entvars_t *pevAttacker, float flDamage, int bitsDamageType); + void Touch( CBaseEntity *pOther ); + void Killed( entvars_t *pevAttacker, int iGib ); + int IRelationship ( CBaseEntity *pTarget ); + virtual int CanPlaySentence( BOOL fDisregardState ); + virtual void PlaySentence( const char *pszSentence, float duration, float volume, float attenuation ); + void PlayScriptedSentence( const char *pszSentence, float duration, float volume, float attenuation, BOOL bConcurrent, CBaseEntity *pListener ); + void KeyValue( KeyValueData *pkvd ); + + // AI functions + void SetActivity ( Activity newActivity ); + Schedule_t *GetScheduleOfType ( int Type ); + void StartTask( Task_t *pTask ); + void RunTask( Task_t *pTask ); + void HandleAnimEvent( MonsterEvent_t *pEvent ); + void PrescheduleThink( void ); + + + // Conversations / communication + int GetVoicePitch( void ); + void IdleRespond( void ); + int FIdleSpeak( void ); + int FIdleStare( void ); + int FIdleHello( void ); + void IdleHeadTurn( Vector &vecFriend ); + int FOkToSpeak( void ); + void TrySmellTalk( void ); + CBaseEntity *EnumFriends( CBaseEntity *pentPrevious, int listNumber, BOOL bTrace ); + void AlertFriends( void ); + void ShutUpFriends( void ); + BOOL IsTalking( void ); + void Talk( float flDuration ); + // For following + BOOL CanFollow( void ); + BOOL IsFollowing( void ) { return m_hTargetEnt != NULL && m_hTargetEnt->IsPlayer(); } + void StopFollowing( BOOL clearSchedule ); + void StartFollowing( CBaseEntity *pLeader ); + virtual void DeclineFollowing( void ) {} + void LimitFollowers( CBaseEntity *pPlayer, int maxFollowers ); + + void EXPORT FollowerUse( CBaseEntity *pActivator, CBaseEntity *pCaller, USE_TYPE useType, float value ); + + virtual void SetAnswerQuestion( CTalkMonster *pSpeaker ); + virtual int FriendNumber( int arrayNumber ) { return arrayNumber; } + + virtual int Save( CSave &save ); + virtual int Restore( CRestore &restore ); + static TYPEDESCRIPTION m_SaveData[]; + + + static char *m_szFriends[TLK_CFRIENDS]; // array of friend names + static float g_talkWaitTime; + + int m_bitsSaid; // set bits for sentences we don't want repeated + int m_nSpeak; // number of times initiated talking + int m_voicePitch; // pitch of voice for this head + const char *m_szGrp[TLK_CGROUPS]; // sentence group names + float m_useTime; // Don't allow +USE until this time + int m_iszUse; // Custom +USE sentence group (follow) + int m_iszUnUse; // Custom +USE sentence group (stop following) + + float m_flLastSaidSmelled;// last time we talked about something that stinks + float m_flStopTalkTime;// when in the future that I'll be done saying this sentence. + + EHANDLE m_hTalkTarget; // who to look at while talking + CUSTOM_SCHEDULES; +}; + + +// Clients can push talkmonsters out of their way +#define bits_COND_CLIENT_PUSH ( bits_COND_SPECIAL1 ) +// Don't see a client right now. +#define bits_COND_CLIENT_UNSEEN ( bits_COND_SPECIAL2 ) + + +#endif //TALKMONSTER_H diff --git a/spirit/teamplay_gamerules.cpp b/dlls/teamplay_gamerules.cpp similarity index 95% rename from spirit/teamplay_gamerules.cpp rename to dlls/teamplay_gamerules.cpp index f40415e0..294dd613 100644 --- a/spirit/teamplay_gamerules.cpp +++ b/dlls/teamplay_gamerules.cpp @@ -120,13 +120,13 @@ void CHalfLifeTeamplay :: Think ( void ) // Updates when frags change if ( frags_remaining != last_frags ) { - CVAR_SET_STRING( "mp_fragsleft", UTIL_VarArgs( "%i", frags_remaining ) ); + g_engfuncs.pfnCvar_DirectSet( fragsleft, UTIL_VarArgs( "%i", frags_remaining ) ); } // Updates once per second if ( timeleft->value != last_time ) { - CVAR_SET_STRING( "mp_timeleft", UTIL_VarArgs( "%i", time_remaining ) ); + g_engfuncs.pfnCvar_DirectSet( timeleft, UTIL_VarArgs( "%i", time_remaining ) ); } last_frags = frags_remaining; diff --git a/spirit/teamplay_gamerules.h b/dlls/teamplay_gamerules.h similarity index 97% rename from spirit/teamplay_gamerules.h rename to dlls/teamplay_gamerules.h index 5d4246bd..c7c351e6 100644 --- a/spirit/teamplay_gamerules.h +++ b/dlls/teamplay_gamerules.h @@ -1,57 +1,57 @@ -/*** -* -* Copyright (c) 1996-2002, 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. -* -****/ -// -// teamplay_gamerules.h -// - -#define MAX_TEAMNAME_LENGTH 16 -#define MAX_TEAMS 32 - -#define TEAMPLAY_TEAMLISTLENGTH MAX_TEAMS*MAX_TEAMNAME_LENGTH - -class CHalfLifeTeamplay : public CHalfLifeMultiplay -{ -public: - CHalfLifeTeamplay(); - - virtual BOOL ClientCommand( CBasePlayer *pPlayer, const char *pcmd ); - virtual void ClientUserInfoChanged( CBasePlayer *pPlayer, char *infobuffer ); - virtual BOOL IsTeamplay( void ); - virtual BOOL FPlayerCanTakeDamage( CBasePlayer *pPlayer, CBaseEntity *pAttacker ); - virtual int PlayerRelationship( CBaseEntity *pPlayer, CBaseEntity *pTarget ); - virtual const char *GetTeamID( CBaseEntity *pEntity ); - virtual BOOL ShouldAutoAim( CBasePlayer *pPlayer, edict_t *target ); - virtual int IPointsForKill( CBasePlayer *pAttacker, CBasePlayer *pKilled ); - virtual void InitHUD( CBasePlayer *pl ); - virtual void DeathNotice( CBasePlayer *pVictim, entvars_t *pKiller, entvars_t *pevInflictor ); - virtual const char *GetGameDescription( void ) { return "HL Teamplay"; } // this is the game name that gets seen in the server browser - virtual void UpdateGameMode( CBasePlayer *pPlayer ); // the client needs to be informed of the current game mode - virtual void PlayerKilled( CBasePlayer *pVictim, entvars_t *pKiller, entvars_t *pInflictor ); - virtual void Think ( void ); - virtual int GetTeamIndex( const char *pTeamName ); - virtual const char *GetIndexedTeamName( int teamIndex ); - virtual BOOL IsValidTeam( const char *pTeamName ); - const char *SetDefaultPlayerTeam( CBasePlayer *pPlayer ); - virtual void ChangePlayerTeam( CBasePlayer *pPlayer, const char *pTeamName, BOOL bKill, BOOL bGib ); - -private: - void RecountTeams( bool bResendInfo = FALSE ); - const char *TeamWithFewestPlayers( void ); - - BOOL m_DisableDeathMessages; - BOOL m_DisableDeathPenalty; - BOOL m_teamLimit; // This means the server set only some teams as valid - char m_szTeamList[TEAMPLAY_TEAMLISTLENGTH]; -}; +/*** +* +* Copyright (c) 1996-2002, 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. +* +****/ +// +// teamplay_gamerules.h +// + +#define MAX_TEAMNAME_LENGTH 16 +#define MAX_TEAMS 32 + +#define TEAMPLAY_TEAMLISTLENGTH MAX_TEAMS*MAX_TEAMNAME_LENGTH + +class CHalfLifeTeamplay : public CHalfLifeMultiplay +{ +public: + CHalfLifeTeamplay(); + + virtual BOOL ClientCommand( CBasePlayer *pPlayer, const char *pcmd ); + virtual void ClientUserInfoChanged( CBasePlayer *pPlayer, char *infobuffer ); + virtual BOOL IsTeamplay( void ); + virtual BOOL FPlayerCanTakeDamage( CBasePlayer *pPlayer, CBaseEntity *pAttacker ); + virtual int PlayerRelationship( CBaseEntity *pPlayer, CBaseEntity *pTarget ); + virtual const char *GetTeamID( CBaseEntity *pEntity ); + virtual BOOL ShouldAutoAim( CBasePlayer *pPlayer, edict_t *target ); + virtual int IPointsForKill( CBasePlayer *pAttacker, CBasePlayer *pKilled ); + virtual void InitHUD( CBasePlayer *pl ); + virtual void DeathNotice( CBasePlayer *pVictim, entvars_t *pKiller, entvars_t *pevInflictor ); + virtual const char *GetGameDescription( void ) { return "HL Teamplay"; } // this is the game name that gets seen in the server browser + virtual void UpdateGameMode( CBasePlayer *pPlayer ); // the client needs to be informed of the current game mode + virtual void PlayerKilled( CBasePlayer *pVictim, entvars_t *pKiller, entvars_t *pInflictor ); + virtual void Think ( void ); + virtual int GetTeamIndex( const char *pTeamName ); + virtual const char *GetIndexedTeamName( int teamIndex ); + virtual BOOL IsValidTeam( const char *pTeamName ); + const char *SetDefaultPlayerTeam( CBasePlayer *pPlayer ); + virtual void ChangePlayerTeam( CBasePlayer *pPlayer, const char *pTeamName, BOOL bKill, BOOL bGib ); + +private: + void RecountTeams( bool bResendInfo = FALSE ); + const char *TeamWithFewestPlayers( void ); + + BOOL m_DisableDeathMessages; + BOOL m_DisableDeathPenalty; + BOOL m_teamLimit; // This means the server set only some teams as valid + char m_szTeamList[TEAMPLAY_TEAMLISTLENGTH]; +}; diff --git a/spirit/tempmonster.cpp b/dlls/tempmonster.cpp similarity index 94% rename from spirit/tempmonster.cpp rename to dlls/tempmonster.cpp index e6bd1a06..2ad893f1 100644 --- a/spirit/tempmonster.cpp +++ b/dlls/tempmonster.cpp @@ -1,121 +1,117 @@ -/*** -* -* Copyright (c) 1996-2002, 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. -* -* This source code contains proprietary and confidential information of -* Valve LLC and its suppliers. Access to this code is restricted to -* persons who have executed a written SDK license with Valve. Any access, -* use or distribution of this code by or to any unlicensed person is illegal. -* -****/ -//========================================================= -// monster template -//========================================================= -#if 0 - -#include "extdll.h" -#include "util.h" -#include "cbase.h" -#include "monsters.h" -#include "schedule.h" - -//========================================================= -// Monster's Anim Events Go Here -//========================================================= - -class CMyMonster : public CBaseMonster -{ -public: - void Spawn( void ); - void Precache( void ); - void SetYawSpeed( void ); - int Classify ( void ); - void HandleAnimEvent( MonsterEvent_t *pEvent ); -}; -LINK_ENTITY_TO_CLASS( my_monster, CMyMonster ); - -//========================================================= -// Classify - indicates this monster's place in the -// relationship table. -//========================================================= -int CMyMonster :: Classify ( void ) -{ - return m_iClass?m_iClass:CLASS_MY_MONSTER; -} - -//========================================================= -// SetYawSpeed - allows each sequence to have a different -// turn rate associated with it. -//========================================================= -void CMyMonster :: SetYawSpeed ( void ) -{ - int ys; - - switch ( m_Activity ) - { - case ACT_IDLE: - default: - ys = 90; - } - - pev->yaw_speed = ys; -} - -//========================================================= -// HandleAnimEvent - catches the monster-specific messages -// that occur when tagged animation frames are played. -//========================================================= -void CMyMonster :: HandleAnimEvent( MonsterEvent_t *pEvent ) -{ - switch( pEvent->event ) - { - case 0: - default: - CBaseMonster::HandleAnimEvent( pEvent ); - break; - } -} - -//========================================================= -// Spawn -//========================================================= -void CMyMonster :: Spawn() -{ - Precache( ); - - if (pev->model) - SET_MODEL(ENT(pev), STRING(pev->model)); //LRC - else - SET_MODEL(ENT(pev), "models/mymodel.mdl"); - UTIL_SetSize( pev, Vector( -12, -12, 0 ), Vector( 12, 12, 24 ) ); - - pev->solid = SOLID_SLIDEBOX; - pev->movetype = MOVETYPE_STEP; - m_bloodColor = BLOOD_COLOR_GREEN; - if (pev->health == 0) - pev->health = 8; - pev->view_ofs = Vector ( 0, 0, 0 );// position of the eyes relative to monster's origin. - m_flFieldOfView = 0.5;// indicates the width of this monster's forward view cone ( as a dotproduct result ) - m_MonsterState = MONSTERSTATE_NONE; - - MonsterInit(); -} - -//========================================================= -// Precache - precaches all resources this monster needs -//========================================================= -void CMyMonster :: Precache() -{ - PRECACHE_SOUND("mysound.wav"); - - PRECACHE_MODEL("models/mymodel.mdl"); -} - -//========================================================= -// AI Schedules Specific to this monster -//========================================================= -#endif 0 +/*** +* +* Copyright (c) 1996-2002, 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. +* +* This source code contains proprietary and confidential information of +* Valve LLC and its suppliers. Access to this code is restricted to +* persons who have executed a written SDK license with Valve. Any access, +* use or distribution of this code by or to any unlicensed person is illegal. +* +****/ +//========================================================= +// monster template +//========================================================= +#if 0 + +#include "extdll.h" +#include "util.h" +#include "cbase.h" +#include "monsters.h" +#include "schedule.h" + +//========================================================= +// Monster's Anim Events Go Here +//========================================================= + +class CMyMonster : public CBaseMonster +{ +public: + void Spawn( void ); + void Precache( void ); + void SetYawSpeed( void ); + int Classify ( void ); + void HandleAnimEvent( MonsterEvent_t *pEvent ); +}; +LINK_ENTITY_TO_CLASS( my_monster, CMyMonster ); + +//========================================================= +// Classify - indicates this monster's place in the +// relationship table. +//========================================================= +int CMyMonster :: Classify ( void ) +{ + return CLASS_MY_MONSTER; +} + +//========================================================= +// SetYawSpeed - allows each sequence to have a different +// turn rate associated with it. +//========================================================= +void CMyMonster :: SetYawSpeed ( void ) +{ + int ys; + + switch ( m_Activity ) + { + case ACT_IDLE: + default: + ys = 90; + } + + pev->yaw_speed = ys; +} + +//========================================================= +// HandleAnimEvent - catches the monster-specific messages +// that occur when tagged animation frames are played. +//========================================================= +void CMyMonster :: HandleAnimEvent( MonsterEvent_t *pEvent ) +{ + switch( pEvent->event ) + { + case 0: + default: + CBaseMonster::HandleAnimEvent( pEvent ); + break; + } +} + +//========================================================= +// Spawn +//========================================================= +void CMyMonster :: Spawn() +{ + Precache( ); + + SET_MODEL(ENT(pev), "models/mymodel.mdl"); + UTIL_SetSize( pev, Vector( -12, -12, 0 ), Vector( 12, 12, 24 ) ); + + pev->solid = SOLID_SLIDEBOX; + pev->movetype = MOVETYPE_STEP; + m_bloodColor = BLOOD_COLOR_GREEN; + pev->health = 8; + pev->view_ofs = Vector ( 0, 0, 0 );// position of the eyes relative to monster's origin. + m_flFieldOfView = 0.5;// indicates the width of this monster's forward view cone ( as a dotproduct result ) + m_MonsterState = MONSTERSTATE_NONE; + + MonsterInit(); +} + +//========================================================= +// Precache - precaches all resources this monster needs +//========================================================= +void CMyMonster :: Precache() +{ + PRECACHE_SOUND("mysound.wav"); + + PRECACHE_MODEL("models/mymodel.mdl"); +} + +//========================================================= +// AI Schedules Specific to this monster +//========================================================= +#endif 0 diff --git a/spirit/tentacle.cpp b/dlls/tentacle.cpp similarity index 93% rename from spirit/tentacle.cpp rename to dlls/tentacle.cpp index 04b9f269..ef3ab574 100644 --- a/spirit/tentacle.cpp +++ b/dlls/tentacle.cpp @@ -71,8 +71,7 @@ public: void Killed( entvars_t *pevAttacker, int iGib ); MONSTERSTATE GetIdealState ( void ) { return MONSTERSTATE_IDLE; }; -// int CanPlaySequence( BOOL fDisregardState ) { return TRUE; }; - int CanPlaySequence( int interruptFlags ) { return TRUE; }; + int CanPlaySequence( BOOL fDisregardState ) { return TRUE; }; int Classify( void ); @@ -242,7 +241,7 @@ typedef enum //========================================================= int CTentacle :: Classify ( void ) { - return m_iClass?m_iClass:CLASS_ALIEN_MONSTER; + return CLASS_ALIEN_MONSTER; } // @@ -262,15 +261,15 @@ void CTentacle :: Spawn( ) UTIL_SetSize( pev, Vector( -32, -32, 0 ), Vector( 32, 32, 64 ) ); pev->takedamage = DAMAGE_AIM; - pev->flags |= FL_MONSTER | FL_FLY; + pev->flags |= FL_MONSTER; m_bloodColor = BLOOD_COLOR_GREEN; - SetThink(&CTentacle :: Start ); - SetTouch(&CTentacle :: HitTouch ); - SetUse(&CTentacle :: CommandUse ); + SetThink( Start ); + SetTouch( HitTouch ); + SetUse( CommandUse ); - SetNextThink( 0.2 ); + pev->nextthink = gpGlobals->time + 0.2; ResetSequenceInfo( ); m_iDir = 1; @@ -290,7 +289,7 @@ void CTentacle :: Spawn( ) m_MonsterState = MONSTERSTATE_IDLE; // SetThink( Test ); - UTIL_SetOrigin( this, pev->origin ); + UTIL_SetOrigin( pev, pev->origin ); } void CTentacle :: Precache( ) @@ -449,7 +448,7 @@ void CTentacle :: Test( void ) pev->sequence = TENTACLE_ANIM_Floor_Strike; pev->framerate = 0; StudioFrameAdvance( ); - SetNextThink( 0.1 ); + pev->nextthink = gpGlobals->time + 0.1; } @@ -460,7 +459,7 @@ void CTentacle :: Test( void ) void CTentacle :: Cycle( void ) { // ALERT( at_console, "%s %.2f %d %d\n", STRING( pev->targetname ), pev->origin.z, m_MonsterState, m_IdealMonsterState ); - SetNextThink( 0.1 ); + pev->nextthink = gpGlobals-> time + 0.1; // ALERT( at_console, "%s %d %d %d %f %f\n", STRING( pev->targetname ), pev->sequence, m_iGoalAnim, m_iDir, pev->framerate, pev->health ); @@ -715,7 +714,7 @@ void CTentacle::CommandUse( CBaseEntity *pActivator, CBaseEntity *pCaller, USE_T { case USE_OFF: pev->takedamage = DAMAGE_NO; - SetThink(&CTentacle:: DieThink ); + SetThink( DieThink ); m_iGoalAnim = TENTACLE_ANIM_Engine_Death1; break; case USE_ON: @@ -729,7 +728,7 @@ void CTentacle::CommandUse( CBaseEntity *pActivator, CBaseEntity *pCaller, USE_T break; case USE_TOGGLE: pev->takedamage = DAMAGE_NO; - SetThink(&CTentacle:: DieThink ); + SetThink( DieThink ); m_iGoalAnim = TENTACLE_ANIM_Engine_Idle; break; } @@ -740,7 +739,7 @@ void CTentacle::CommandUse( CBaseEntity *pActivator, CBaseEntity *pCaller, USE_T void CTentacle :: DieThink( void ) { - SetNextThink( 0.1 ); + pev->nextthink = gpGlobals-> time + 0.1; DispatchAnimEvents( ); StudioFrameAdvance( ); @@ -927,7 +926,7 @@ void CTentacle :: HandleAnimEvent( MonsterEvent_t *pEvent ) // void CTentacle :: Start( CBaseEntity *pActivator, CBaseEntity *pCaller, USE_TYPE useType, float value ) void CTentacle :: Start( void ) { - SetThink(&CTentacle :: Cycle ); + SetThink( Cycle ); if ( !g_fFlySound ) { @@ -941,7 +940,7 @@ void CTentacle :: Start( void ) g_fSquirmSound = TRUE; } - SetNextThink( 0.1 ); + pev->nextthink = gpGlobals->time + 0.1; } @@ -1042,4 +1041,4 @@ void CTentacleMaw :: Precache( ) PRECACHE_MODEL("models/maw.mdl"); } -#endif +#endif \ No newline at end of file diff --git a/spirit/trains.h b/dlls/trains.h similarity index 67% rename from spirit/trains.h rename to dlls/trains.h index f2b7dfde..4ee44112 100644 --- a/spirit/trains.h +++ b/dlls/trains.h @@ -20,9 +20,6 @@ #define SF_TRACKTRAIN_NOCONTROL 0x0002 #define SF_TRACKTRAIN_FORWARDONLY 0x0004 #define SF_TRACKTRAIN_PASSABLE 0x0008 -#define SF_TRACKTRAIN_NOYAW 0x0010 //LRC -#define SF_TRACKTRAIN_AVELOCITY 0x800000 //LRC - avelocity has been set manually, don't turn. -#define SF_TRACKTRAIN_AVEL_GEARS 0x400000 //LRC - avelocity should be scaled up/down when the train changes gear. // Spawnflag for CPathTrack #define SF_PATH_DISABLED 0x00000001 @@ -30,29 +27,11 @@ #define SF_PATH_ALTREVERSE 0x00000004 #define SF_PATH_DISABLE_TRAIN 0x00000008 #define SF_PATH_ALTERNATE 0x00008000 -#define SF_PATH_AVELOCITY 0x00080000 //LRC // Spawnflags of CPathCorner #define SF_CORNER_WAITFORTRIG 0x001 #define SF_CORNER_TELEPORT 0x002 #define SF_CORNER_FIREONCE 0x004 -#define SF_CORNER_AVELOCITY 0x800000 - -//LRC - values in 'armortype' -#define PATHSPEED_SET 0 -#define PATHSPEED_ACCEL 1 -#define PATHSPEED_TIME 2 -#define PATHSPEED_SET_MASTER 3 - -//LRC - values in 'frags' -#define PATHTURN_SET 0 -#define PATHTURN_SET_MASTER 1 -#define PATHTURN_RESET 2 - -//LRC - values in 'armorvalue' -#define PATHMATCH_NO 0 -#define PATHMATCH_YES 1 -#define PATHMATCH_TRACK 2 //#define PATH_SPARKLE_DEBUG 1 // This makes a particle effect around path_track entities for debugging class CPathTrack : public CPointEntity @@ -92,7 +71,6 @@ public: CPathTrack *m_paltpath; }; -class CTrainSequence; class CFuncTrackTrain : public CBaseEntity { @@ -104,15 +82,7 @@ public: void Use( CBaseEntity *pActivator, CBaseEntity *pCaller, USE_TYPE useType, float value ); void KeyValue( KeyValueData* pkvd ); - //LRC - void StartSequence(CTrainSequence *pSequence); - void StopSequence( ); - CTrainSequence *m_pSequence; - - void DesiredAction( void ); //LRC - used to be called Next! - -// void EXPORT Next( void ); - void EXPORT PostponeNext( void ); + void EXPORT Next( void ); void EXPORT Find( void ); void EXPORT NearestPath( void ); void EXPORT DeadEnd( void ); @@ -135,14 +105,10 @@ public: virtual int ObjectCaps( void ) { return (CBaseEntity :: ObjectCaps() & ~FCAP_ACROSS_TRANSITION) | FCAP_DIRECTIONAL_USE; } virtual void OverrideReset( void ); - virtual void ClearPointers( void ); - + CPathTrack *m_ppath; float m_length; float m_height; - // I get it... this records the train's max speed (as set by the level designer), whereas - // pev->speed records the current speed (as set by the player). --LRC - // m_speed is also stored, as an int, in pev->impulse. float m_speed; float m_dir; float m_startSpeed; @@ -153,10 +119,6 @@ public: float m_flVolume; float m_flBank; float m_oldSpeed; - Vector m_vecMasterAvel; //LRC - masterAvel is to avelocity as m_speed is to speed. - Vector m_vecBaseAvel; // LRC - the underlying avelocity, superceded by normal turning behaviour where applicable - - EHANDLE m_hActivator; //AJH (give frags to this entity) private: unsigned short m_usAdjustPitch; diff --git a/dlls/triggers.cpp b/dlls/triggers.cpp new file mode 100644 index 00000000..bd36a082 --- /dev/null +++ b/dlls/triggers.cpp @@ -0,0 +1,2430 @@ +/*** +* +* Copyright (c) 1996-2002, 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. +* +****/ +/* + +===== triggers.cpp ======================================================== + + spawn and use functions for editor-placed triggers + +*/ + +#include "extdll.h" +#include "util.h" +#include "cbase.h" +#include "player.h" +#include "saverestore.h" +#include "trains.h" // trigger_camera has train functionality +#include "gamerules.h" + +#define SF_TRIGGER_PUSH_START_OFF 2//spawnflag that makes trigger_push spawn turned OFF +#define SF_TRIGGER_HURT_TARGETONCE 1// Only fire hurt target once +#define SF_TRIGGER_HURT_START_OFF 2//spawnflag that makes trigger_push spawn turned OFF +#define SF_TRIGGER_HURT_NO_CLIENTS 8//spawnflag that makes trigger_push spawn turned OFF +#define SF_TRIGGER_HURT_CLIENTONLYFIRE 16// trigger hurt will only fire its target if it is hurting a client +#define SF_TRIGGER_HURT_CLIENTONLYTOUCH 32// only clients may touch this trigger. + +extern DLL_GLOBAL BOOL g_fGameOver; + +extern void SetMovedir(entvars_t* pev); +extern Vector VecBModelOrigin( entvars_t* pevBModel ); + +class CFrictionModifier : public CBaseEntity +{ +public: + void Spawn( void ); + void KeyValue( KeyValueData *pkvd ); + void EXPORT ChangeFriction( CBaseEntity *pOther ); + virtual int Save( CSave &save ); + virtual int Restore( CRestore &restore ); + + virtual int ObjectCaps( void ) { return CBaseEntity :: ObjectCaps() & ~FCAP_ACROSS_TRANSITION; } + + static TYPEDESCRIPTION m_SaveData[]; + + float m_frictionFraction; // Sorry, couldn't resist this name :) +}; + +LINK_ENTITY_TO_CLASS( func_friction, CFrictionModifier ); + +// Global Savedata for changelevel friction modifier +TYPEDESCRIPTION CFrictionModifier::m_SaveData[] = +{ + DEFINE_FIELD( CFrictionModifier, m_frictionFraction, FIELD_FLOAT ), +}; + +IMPLEMENT_SAVERESTORE(CFrictionModifier,CBaseEntity); + + +// Modify an entity's friction +void CFrictionModifier :: Spawn( void ) +{ + pev->solid = SOLID_TRIGGER; + SET_MODEL(ENT(pev), STRING(pev->model)); // set size and link into world + pev->movetype = MOVETYPE_NONE; + SetTouch( ChangeFriction ); +} + + +// Sets toucher's friction to m_frictionFraction (1.0 = normal friction) +void CFrictionModifier :: ChangeFriction( CBaseEntity *pOther ) +{ + if ( pOther->pev->movetype != MOVETYPE_BOUNCEMISSILE && pOther->pev->movetype != MOVETYPE_BOUNCE ) + pOther->pev->friction = m_frictionFraction; +} + + + +// Sets toucher's friction to m_frictionFraction (1.0 = normal friction) +void CFrictionModifier :: KeyValue( KeyValueData *pkvd ) +{ + if (FStrEq(pkvd->szKeyName, "modifier")) + { + m_frictionFraction = atof(pkvd->szValue) / 100.0; + pkvd->fHandled = TRUE; + } + else + CBaseEntity::KeyValue( pkvd ); +} + + +// This trigger will fire when the level spawns (or respawns if not fire once) +// It will check a global state before firing. It supports delay and killtargets + +#define SF_AUTO_FIREONCE 0x0001 + +class CAutoTrigger : public CBaseDelay +{ +public: + void KeyValue( KeyValueData *pkvd ); + void Spawn( void ); + void Precache( void ); + void Think( void ); + + int ObjectCaps( void ) { return CBaseDelay::ObjectCaps() & ~FCAP_ACROSS_TRANSITION; } + virtual int Save( CSave &save ); + virtual int Restore( CRestore &restore ); + + static TYPEDESCRIPTION m_SaveData[]; + +private: + int m_globalstate; + USE_TYPE triggerType; +}; +LINK_ENTITY_TO_CLASS( trigger_auto, CAutoTrigger ); + +TYPEDESCRIPTION CAutoTrigger::m_SaveData[] = +{ + DEFINE_FIELD( CAutoTrigger, m_globalstate, FIELD_STRING ), + DEFINE_FIELD( CAutoTrigger, triggerType, FIELD_INTEGER ), +}; + +IMPLEMENT_SAVERESTORE(CAutoTrigger,CBaseDelay); + +void CAutoTrigger::KeyValue( KeyValueData *pkvd ) +{ + if (FStrEq(pkvd->szKeyName, "globalstate")) + { + m_globalstate = ALLOC_STRING( pkvd->szValue ); + pkvd->fHandled = TRUE; + } + else if (FStrEq(pkvd->szKeyName, "triggerstate")) + { + int type = atoi( pkvd->szValue ); + switch( type ) + { + case 0: + triggerType = USE_OFF; + break; + case 2: + triggerType = USE_TOGGLE; + break; + default: + triggerType = USE_ON; + break; + } + pkvd->fHandled = TRUE; + } + else + CBaseDelay::KeyValue( pkvd ); +} + + +void CAutoTrigger::Spawn( void ) +{ + Precache(); +} + + +void CAutoTrigger::Precache( void ) +{ + pev->nextthink = gpGlobals->time + 0.1; +} + + +void CAutoTrigger::Think( void ) +{ + if ( !m_globalstate || gGlobalState.EntityGetState( m_globalstate ) == GLOBAL_ON ) + { + SUB_UseTargets( this, triggerType, 0 ); + if ( pev->spawnflags & SF_AUTO_FIREONCE ) + UTIL_Remove( this ); + } +} + + + +#define SF_RELAY_FIREONCE 0x0001 + +class CTriggerRelay : public CBaseDelay +{ +public: + void KeyValue( KeyValueData *pkvd ); + void Spawn( void ); + void Use( CBaseEntity *pActivator, CBaseEntity *pCaller, USE_TYPE useType, float value ); + + int ObjectCaps( void ) { return CBaseDelay::ObjectCaps() & ~FCAP_ACROSS_TRANSITION; } + virtual int Save( CSave &save ); + virtual int Restore( CRestore &restore ); + + static TYPEDESCRIPTION m_SaveData[]; + +private: + USE_TYPE triggerType; +}; +LINK_ENTITY_TO_CLASS( trigger_relay, CTriggerRelay ); + +TYPEDESCRIPTION CTriggerRelay::m_SaveData[] = +{ + DEFINE_FIELD( CTriggerRelay, triggerType, FIELD_INTEGER ), +}; + +IMPLEMENT_SAVERESTORE(CTriggerRelay,CBaseDelay); + +void CTriggerRelay::KeyValue( KeyValueData *pkvd ) +{ + if (FStrEq(pkvd->szKeyName, "triggerstate")) + { + int type = atoi( pkvd->szValue ); + switch( type ) + { + case 0: + triggerType = USE_OFF; + break; + case 2: + triggerType = USE_TOGGLE; + break; + default: + triggerType = USE_ON; + break; + } + pkvd->fHandled = TRUE; + } + else + CBaseDelay::KeyValue( pkvd ); +} + + +void CTriggerRelay::Spawn( void ) +{ +} + + + + +void CTriggerRelay::Use( CBaseEntity *pActivator, CBaseEntity *pCaller, USE_TYPE useType, float value ) +{ + SUB_UseTargets( this, triggerType, 0 ); + if ( pev->spawnflags & SF_RELAY_FIREONCE ) + UTIL_Remove( this ); +} + + +//********************************************************** +// The Multimanager Entity - when fired, will fire up to 16 targets +// at specified times. +// FLAG: THREAD (create clones when triggered) +// FLAG: CLONE (this is a clone for a threaded execution) + +#define SF_MULTIMAN_CLONE 0x80000000 +#define SF_MULTIMAN_THREAD 0x00000001 + +class CMultiManager : public CBaseToggle +{ +public: + void KeyValue( KeyValueData *pkvd ); + void Spawn ( void ); + void EXPORT ManagerThink ( void ); + void EXPORT ManagerUse ( CBaseEntity *pActivator, CBaseEntity *pCaller, USE_TYPE useType, float value ); + +#if _DEBUG + void EXPORT ManagerReport( void ); +#endif + + BOOL HasTarget( string_t targetname ); + + int ObjectCaps( void ) { return CBaseToggle::ObjectCaps() & ~FCAP_ACROSS_TRANSITION; } + + virtual int Save( CSave &save ); + virtual int Restore( CRestore &restore ); + + static TYPEDESCRIPTION m_SaveData[]; + + int m_cTargets; // the total number of targets in this manager's fire list. + int m_index; // Current target + float m_startTime;// Time we started firing + int m_iTargetName [ MAX_MULTI_TARGETS ];// list if indexes into global string array + float m_flTargetDelay [ MAX_MULTI_TARGETS ];// delay (in seconds) from time of manager fire to target fire +private: + inline BOOL IsClone( void ) { return (pev->spawnflags & SF_MULTIMAN_CLONE) ? TRUE : FALSE; } + inline BOOL ShouldClone( void ) + { + if ( IsClone() ) + return FALSE; + + return (pev->spawnflags & SF_MULTIMAN_THREAD) ? TRUE : FALSE; + } + + CMultiManager *Clone( void ); +}; +LINK_ENTITY_TO_CLASS( multi_manager, CMultiManager ); + +// Global Savedata for multi_manager +TYPEDESCRIPTION CMultiManager::m_SaveData[] = +{ + DEFINE_FIELD( CMultiManager, m_cTargets, FIELD_INTEGER ), + DEFINE_FIELD( CMultiManager, m_index, FIELD_INTEGER ), + DEFINE_FIELD( CMultiManager, m_startTime, FIELD_TIME ), + DEFINE_ARRAY( CMultiManager, m_iTargetName, FIELD_STRING, MAX_MULTI_TARGETS ), + DEFINE_ARRAY( CMultiManager, m_flTargetDelay, FIELD_FLOAT, MAX_MULTI_TARGETS ), +}; + +IMPLEMENT_SAVERESTORE(CMultiManager,CBaseToggle); + +void CMultiManager :: KeyValue( KeyValueData *pkvd ) +{ + // UNDONE: Maybe this should do something like this: + //CBaseToggle::KeyValue( pkvd ); + // if ( !pkvd->fHandled ) + // ... etc. + + if (FStrEq(pkvd->szKeyName, "wait")) + { + m_flWait = atof(pkvd->szValue); + pkvd->fHandled = TRUE; + } + else // add this field to the target list + { + // this assumes that additional fields are targetnames and their values are delay values. + if ( m_cTargets < MAX_MULTI_TARGETS ) + { + char tmp[128]; + + UTIL_StripToken( pkvd->szKeyName, tmp ); + m_iTargetName [ m_cTargets ] = ALLOC_STRING( tmp ); + m_flTargetDelay [ m_cTargets ] = atof (pkvd->szValue); + m_cTargets++; + pkvd->fHandled = TRUE; + } + } +} + + +void CMultiManager :: Spawn( void ) +{ + pev->solid = SOLID_NOT; + SetUse ( ManagerUse ); + SetThink ( ManagerThink); + + // Sort targets + // Quick and dirty bubble sort + int swapped = 1; + + while ( swapped ) + { + swapped = 0; + for ( int i = 1; i < m_cTargets; i++ ) + { + if ( m_flTargetDelay[i] < m_flTargetDelay[i-1] ) + { + // Swap out of order elements + int name = m_iTargetName[i]; + float delay = m_flTargetDelay[i]; + m_iTargetName[i] = m_iTargetName[i-1]; + m_flTargetDelay[i] = m_flTargetDelay[i-1]; + m_iTargetName[i-1] = name; + m_flTargetDelay[i-1] = delay; + swapped = 1; + } + } + } +} + + +BOOL CMultiManager::HasTarget( string_t targetname ) +{ + for ( int i = 0; i < m_cTargets; i++ ) + if ( FStrEq(STRING(targetname), STRING(m_iTargetName[i])) ) + return TRUE; + + return FALSE; +} + + +// Designers were using this to fire targets that may or may not exist -- +// so I changed it to use the standard target fire code, made it a little simpler. +void CMultiManager :: ManagerThink ( void ) +{ + float time; + + time = gpGlobals->time - m_startTime; + while ( m_index < m_cTargets && m_flTargetDelay[ m_index ] <= time ) + { + FireTargets( STRING( m_iTargetName[ m_index ] ), m_hActivator, this, USE_TOGGLE, 0 ); + m_index++; + } + + if ( m_index >= m_cTargets )// have we fired all targets? + { + SetThink( NULL ); + if ( IsClone() ) + { + UTIL_Remove( this ); + return; + } + SetUse ( ManagerUse );// allow manager re-use + } + else + pev->nextthink = m_startTime + m_flTargetDelay[ m_index ]; +} + +CMultiManager *CMultiManager::Clone( void ) +{ + CMultiManager *pMulti = GetClassPtr( (CMultiManager *)NULL ); + + edict_t *pEdict = pMulti->pev->pContainingEntity; + memcpy( pMulti->pev, pev, sizeof(*pev) ); + pMulti->pev->pContainingEntity = pEdict; + + pMulti->pev->spawnflags |= SF_MULTIMAN_CLONE; + pMulti->m_cTargets = m_cTargets; + memcpy( pMulti->m_iTargetName, m_iTargetName, sizeof( m_iTargetName ) ); + memcpy( pMulti->m_flTargetDelay, m_flTargetDelay, sizeof( m_flTargetDelay ) ); + + return pMulti; +} + + +// The USE function builds the time table and starts the entity thinking. +void CMultiManager :: ManagerUse ( CBaseEntity *pActivator, CBaseEntity *pCaller, USE_TYPE useType, float value ) +{ + // In multiplayer games, clone the MM and execute in the clone (like a thread) + // to allow multiple players to trigger the same multimanager + if ( ShouldClone() ) + { + CMultiManager *pClone = Clone(); + pClone->ManagerUse( pActivator, pCaller, useType, value ); + return; + } + + m_hActivator = pActivator; + m_index = 0; + m_startTime = gpGlobals->time; + + SetUse( NULL );// disable use until all targets have fired + + SetThink ( ManagerThink ); + pev->nextthink = gpGlobals->time; +} + +#if _DEBUG +void CMultiManager :: ManagerReport ( void ) +{ + int cIndex; + + for ( cIndex = 0 ; cIndex < m_cTargets ; cIndex++ ) + { + ALERT ( at_console, "%s %f\n", STRING(m_iTargetName[cIndex]), m_flTargetDelay[cIndex] ); + } +} +#endif + +//*********************************************************** + + +// +// Render parameters trigger +// +// This entity will copy its render parameters (renderfx, rendermode, rendercolor, renderamt) +// to its targets when triggered. +// + + +// Flags to indicate masking off various render parameters that are normally copied to the targets +#define SF_RENDER_MASKFX (1<<0) +#define SF_RENDER_MASKAMT (1<<1) +#define SF_RENDER_MASKMODE (1<<2) +#define SF_RENDER_MASKCOLOR (1<<3) + +class CRenderFxManager : public CBaseEntity +{ +public: + void Spawn( void ); + void Use ( CBaseEntity *pActivator, CBaseEntity *pCaller, USE_TYPE useType, float value ); +}; + +LINK_ENTITY_TO_CLASS( env_render, CRenderFxManager ); + + +void CRenderFxManager :: Spawn ( void ) +{ + pev->solid = SOLID_NOT; +} + +void CRenderFxManager :: Use ( CBaseEntity *pActivator, CBaseEntity *pCaller, USE_TYPE useType, float value ) +{ + if (!FStringNull(pev->target)) + { + edict_t* pentTarget = NULL; + while ( 1 ) + { + pentTarget = FIND_ENTITY_BY_TARGETNAME(pentTarget, STRING(pev->target)); + if (FNullEnt(pentTarget)) + break; + + entvars_t *pevTarget = VARS( pentTarget ); + if ( !FBitSet( pev->spawnflags, SF_RENDER_MASKFX ) ) + pevTarget->renderfx = pev->renderfx; + if ( !FBitSet( pev->spawnflags, SF_RENDER_MASKAMT ) ) + pevTarget->renderamt = pev->renderamt; + if ( !FBitSet( pev->spawnflags, SF_RENDER_MASKMODE ) ) + pevTarget->rendermode = pev->rendermode; + if ( !FBitSet( pev->spawnflags, SF_RENDER_MASKCOLOR ) ) + pevTarget->rendercolor = pev->rendercolor; + } + } +} + + + +class CBaseTrigger : public CBaseToggle +{ +public: + void EXPORT TeleportTouch ( CBaseEntity *pOther ); + void KeyValue( KeyValueData *pkvd ); + void EXPORT MultiTouch( CBaseEntity *pOther ); + void EXPORT HurtTouch ( CBaseEntity *pOther ); + void EXPORT CDAudioTouch ( CBaseEntity *pOther ); + void ActivateMultiTrigger( CBaseEntity *pActivator ); + void EXPORT MultiWaitOver( void ); + void EXPORT CounterUse( CBaseEntity *pActivator, CBaseEntity *pCaller, USE_TYPE useType, float value ); + void EXPORT ToggleUse ( CBaseEntity *pActivator, CBaseEntity *pCaller, USE_TYPE useType, float value ); + void InitTrigger( void ); + + virtual int ObjectCaps( void ) { return CBaseToggle :: ObjectCaps() & ~FCAP_ACROSS_TRANSITION; } +}; + +LINK_ENTITY_TO_CLASS( trigger, CBaseTrigger ); + +/* +================ +InitTrigger +================ +*/ +void CBaseTrigger::InitTrigger( ) +{ + // trigger angles are used for one-way touches. An angle of 0 is assumed + // to mean no restrictions, so use a yaw of 360 instead. + if (pev->angles != g_vecZero) + SetMovedir(pev); + pev->solid = SOLID_TRIGGER; + pev->movetype = MOVETYPE_NONE; + SET_MODEL(ENT(pev), STRING(pev->model)); // set size and link into world + if ( CVAR_GET_FLOAT("showtriggers") == 0 ) + SetBits( pev->effects, EF_NODRAW ); +} + + +// +// Cache user-entity-field values until spawn is called. +// + +void CBaseTrigger :: KeyValue( KeyValueData *pkvd ) +{ + if (FStrEq(pkvd->szKeyName, "damage")) + { + pev->dmg = atof(pkvd->szValue); + pkvd->fHandled = TRUE; + } + else if (FStrEq(pkvd->szKeyName, "count")) + { + m_cTriggersLeft = (int) atof(pkvd->szValue); + pkvd->fHandled = TRUE; + } + else if (FStrEq(pkvd->szKeyName, "damagetype")) + { + m_bitsDamageInflict = atoi(pkvd->szValue); + pkvd->fHandled = TRUE; + } + else + CBaseToggle::KeyValue( pkvd ); +} + +class CTriggerHurt : public CBaseTrigger +{ +public: + void Spawn( void ); + void EXPORT RadiationThink( void ); +}; + +LINK_ENTITY_TO_CLASS( trigger_hurt, CTriggerHurt ); + +// +// trigger_monsterjump +// +class CTriggerMonsterJump : public CBaseTrigger +{ +public: + void Spawn( void ); + void Touch( CBaseEntity *pOther ); + void Think( void ); +}; + +LINK_ENTITY_TO_CLASS( trigger_monsterjump, CTriggerMonsterJump ); + + +void CTriggerMonsterJump :: Spawn ( void ) +{ + SetMovedir ( pev ); + + InitTrigger (); + + pev->nextthink = 0; + pev->speed = 200; + m_flHeight = 150; + + if ( !FStringNull ( pev->targetname ) ) + {// if targetted, spawn turned off + pev->solid = SOLID_NOT; + UTIL_SetOrigin( pev, pev->origin ); // Unlink from trigger list + SetUse( ToggleUse ); + } +} + + +void CTriggerMonsterJump :: Think( void ) +{ + pev->solid = SOLID_NOT;// kill the trigger for now !!!UNDONE + UTIL_SetOrigin( pev, pev->origin ); // Unlink from trigger list + SetThink( NULL ); +} + +void CTriggerMonsterJump :: Touch( CBaseEntity *pOther ) +{ + entvars_t *pevOther = pOther->pev; + + if ( !FBitSet ( pevOther->flags , FL_MONSTER ) ) + {// touched by a non-monster. + return; + } + + pevOther->origin.z += 1; + + if ( FBitSet ( pevOther->flags, FL_ONGROUND ) ) + {// clear the onground so physics don't bitch + pevOther->flags &= ~FL_ONGROUND; + } + + // toss the monster! + pevOther->velocity = pev->movedir * pev->speed; + pevOther->velocity.z += m_flHeight; + pev->nextthink = gpGlobals->time; +} + + +//===================================== +// +// trigger_cdaudio - starts/stops cd audio tracks +// +class CTriggerCDAudio : public CBaseTrigger +{ +public: + void Spawn( void ); + + virtual void Use( CBaseEntity *pActivator, CBaseEntity *pCaller, USE_TYPE useType, float value ); + void PlayTrack( void ); + void Touch ( CBaseEntity *pOther ); +}; + +LINK_ENTITY_TO_CLASS( trigger_cdaudio, CTriggerCDAudio ); + +// +// Changes tracks or stops CD when player touches +// +// !!!HACK - overloaded HEALTH to avoid adding new field +void CTriggerCDAudio :: Touch ( CBaseEntity *pOther ) +{ + if ( !pOther->IsPlayer() ) + {// only clients may trigger these events + return; + } + + PlayTrack(); +} + +void CTriggerCDAudio :: Spawn( void ) +{ + InitTrigger(); +} + +void CTriggerCDAudio::Use( CBaseEntity *pActivator, CBaseEntity *pCaller, USE_TYPE useType, float value ) +{ + PlayTrack(); +} + +void PlayCDTrack( int iTrack ) +{ + edict_t *pClient; + + // manually find the single player. + pClient = g_engfuncs.pfnPEntityOfEntIndex( 1 ); + + // Can't play if the client is not connected! + if ( !pClient ) + return; + + if ( iTrack < -1 || iTrack > 30 ) + { + ALERT ( at_console, "TriggerCDAudio - Track %d out of range\n" ); + return; + } + + if ( iTrack == -1 ) + { + CLIENT_COMMAND ( pClient, "cd pause\n"); + } + else + { + char string [ 64 ]; + + sprintf( string, "cd play %3d\n", iTrack ); + CLIENT_COMMAND ( pClient, string); + } +} + + +// only plays for ONE client, so only use in single play! +void CTriggerCDAudio :: PlayTrack( void ) +{ + PlayCDTrack( (int)pev->health ); + + SetTouch( NULL ); + UTIL_Remove( this ); +} + + +// This plays a CD track when fired or when the player enters it's radius +class CTargetCDAudio : public CPointEntity +{ +public: + void Spawn( void ); + void KeyValue( KeyValueData *pkvd ); + + virtual void Use( CBaseEntity *pActivator, CBaseEntity *pCaller, USE_TYPE useType, float value ); + void Think( void ); + void Play( void ); +}; + +LINK_ENTITY_TO_CLASS( target_cdaudio, CTargetCDAudio ); + +void CTargetCDAudio :: KeyValue( KeyValueData *pkvd ) +{ + if (FStrEq(pkvd->szKeyName, "radius")) + { + pev->scale = atof(pkvd->szValue); + pkvd->fHandled = TRUE; + } + else + CPointEntity::KeyValue( pkvd ); +} + +void CTargetCDAudio :: Spawn( void ) +{ + pev->solid = SOLID_NOT; + pev->movetype = MOVETYPE_NONE; + + if ( pev->scale > 0 ) + pev->nextthink = gpGlobals->time + 1.0; +} + +void CTargetCDAudio::Use( CBaseEntity *pActivator, CBaseEntity *pCaller, USE_TYPE useType, float value ) +{ + Play(); +} + +// only plays for ONE client, so only use in single play! +void CTargetCDAudio::Think( void ) +{ + edict_t *pClient; + + // manually find the single player. + pClient = g_engfuncs.pfnPEntityOfEntIndex( 1 ); + + // Can't play if the client is not connected! + if ( !pClient ) + return; + + pev->nextthink = gpGlobals->time + 0.5; + + if ( (pClient->v.origin - pev->origin).Length() <= pev->scale ) + Play(); + +} + +void CTargetCDAudio::Play( void ) +{ + PlayCDTrack( (int)pev->health ); + UTIL_Remove(this); +} + +//===================================== + +// +// trigger_hurt - hurts anything that touches it. if the trigger has a targetname, firing it will toggle state +// +//int gfToggleState = 0; // used to determine when all radiation trigger hurts have called 'RadiationThink' + +void CTriggerHurt :: Spawn( void ) +{ + InitTrigger(); + SetTouch ( HurtTouch ); + + if ( !FStringNull ( pev->targetname ) ) + { + SetUse ( ToggleUse ); + } + else + { + SetUse ( NULL ); + } + + if (m_bitsDamageInflict & DMG_RADIATION) + { + SetThink ( RadiationThink ); + pev->nextthink = gpGlobals->time + RANDOM_FLOAT(0.0, 0.5); + } + + if ( FBitSet (pev->spawnflags, SF_TRIGGER_HURT_START_OFF) )// if flagged to Start Turned Off, make trigger nonsolid. + pev->solid = SOLID_NOT; + + UTIL_SetOrigin( pev, pev->origin ); // Link into the list +} + +// trigger hurt that causes radiation will do a radius +// check and set the player's geiger counter level +// according to distance from center of trigger + +void CTriggerHurt :: RadiationThink( void ) +{ + + edict_t *pentPlayer; + CBasePlayer *pPlayer = NULL; + float flRange; + entvars_t *pevTarget; + Vector vecSpot1; + Vector vecSpot2; + Vector vecRange; + Vector origin; + Vector view_ofs; + + // check to see if a player is in pvs + // if not, continue + + // set origin to center of trigger so that this check works + origin = pev->origin; + view_ofs = pev->view_ofs; + + pev->origin = (pev->absmin + pev->absmax) * 0.5; + pev->view_ofs = pev->view_ofs * 0.0; + + pentPlayer = FIND_CLIENT_IN_PVS(edict()); + + pev->origin = origin; + pev->view_ofs = view_ofs; + + // reset origin + + if (!FNullEnt(pentPlayer)) + { + + pPlayer = GetClassPtr( (CBasePlayer *)VARS(pentPlayer)); + + pevTarget = VARS(pentPlayer); + + // get range to player; + + vecSpot1 = (pev->absmin + pev->absmax) * 0.5; + vecSpot2 = (pevTarget->absmin + pevTarget->absmax) * 0.5; + + vecRange = vecSpot1 - vecSpot2; + flRange = vecRange.Length(); + + // if player's current geiger counter range is larger + // than range to this trigger hurt, reset player's + // geiger counter range + + if (pPlayer->m_flgeigerRange >= flRange) + pPlayer->m_flgeigerRange = flRange; + } + + pev->nextthink = gpGlobals->time + 0.25; +} + +// +// ToggleUse - If this is the USE function for a trigger, its state will toggle every time it's fired +// +void CBaseTrigger :: ToggleUse ( CBaseEntity *pActivator, CBaseEntity *pCaller, USE_TYPE useType, float value ) +{ + if (pev->solid == SOLID_NOT) + {// if the trigger is off, turn it on + pev->solid = SOLID_TRIGGER; + + // Force retouch + gpGlobals->force_retouch++; + } + else + {// turn the trigger off + pev->solid = SOLID_NOT; + } + UTIL_SetOrigin( pev, pev->origin ); +} + +// When touched, a hurt trigger does DMG points of damage each half-second +void CBaseTrigger :: HurtTouch ( CBaseEntity *pOther ) +{ + float fldmg; + + if ( !pOther->pev->takedamage ) + return; + + if ( (pev->spawnflags & SF_TRIGGER_HURT_CLIENTONLYTOUCH) && !pOther->IsPlayer() ) + { + // this trigger is only allowed to touch clients, and this ain't a client. + return; + } + + if ( (pev->spawnflags & SF_TRIGGER_HURT_NO_CLIENTS) && pOther->IsPlayer() ) + return; + + // HACKHACK -- In multiplayer, players touch this based on packet receipt. + // So the players who send packets later aren't always hurt. Keep track of + // how much time has passed and whether or not you've touched that player + if ( g_pGameRules->IsMultiplayer() ) + { + if ( pev->dmgtime > gpGlobals->time ) + { + if ( gpGlobals->time != pev->pain_finished ) + {// too early to hurt again, and not same frame with a different entity + if ( pOther->IsPlayer() ) + { + int playerMask = 1 << (pOther->entindex() - 1); + + // If I've already touched this player (this time), then bail out + if ( pev->impulse & playerMask ) + return; + + // Mark this player as touched + // BUGBUG - There can be only 32 players! + pev->impulse |= playerMask; + } + else + { + return; + } + } + } + else + { + // New clock, "un-touch" all players + pev->impulse = 0; + if ( pOther->IsPlayer() ) + { + int playerMask = 1 << (pOther->entindex() - 1); + + // Mark this player as touched + // BUGBUG - There can be only 32 players! + pev->impulse |= playerMask; + } + } + } + else // Original code -- single player + { + if ( pev->dmgtime > gpGlobals->time && gpGlobals->time != pev->pain_finished ) + {// too early to hurt again, and not same frame with a different entity + return; + } + } + + + + // If this is time_based damage (poison, radiation), override the pev->dmg with a + // default for the given damage type. Monsters only take time-based damage + // while touching the trigger. Player continues taking damage for a while after + // leaving the trigger + + fldmg = pev->dmg * 0.5; // 0.5 seconds worth of damage, pev->dmg is damage/second + + + // JAY: Cut this because it wasn't fully realized. Damage is simpler now. +#if 0 + switch (m_bitsDamageInflict) + { + default: break; + case DMG_POISON: fldmg = POISON_DAMAGE/4; break; + case DMG_NERVEGAS: fldmg = NERVEGAS_DAMAGE/4; break; + case DMG_RADIATION: fldmg = RADIATION_DAMAGE/4; break; + case DMG_PARALYZE: fldmg = PARALYZE_DAMAGE/4; break; // UNDONE: cut this? should slow movement to 50% + case DMG_ACID: fldmg = ACID_DAMAGE/4; break; + case DMG_SLOWBURN: fldmg = SLOWBURN_DAMAGE/4; break; + case DMG_SLOWFREEZE: fldmg = SLOWFREEZE_DAMAGE/4; break; + } +#endif + + if ( fldmg < 0 ) + pOther->TakeHealth( -fldmg, m_bitsDamageInflict ); + else + pOther->TakeDamage( pev, pev, fldmg, m_bitsDamageInflict ); + + // Store pain time so we can get all of the other entities on this frame + pev->pain_finished = gpGlobals->time; + + // Apply damage every half second + pev->dmgtime = gpGlobals->time + 0.5;// half second delay until this trigger can hurt toucher again + + + + if ( pev->target ) + { + // trigger has a target it wants to fire. + if ( pev->spawnflags & SF_TRIGGER_HURT_CLIENTONLYFIRE ) + { + // if the toucher isn't a client, don't fire the target! + if ( !pOther->IsPlayer() ) + { + return; + } + } + + SUB_UseTargets( pOther, USE_TOGGLE, 0 ); + if ( pev->spawnflags & SF_TRIGGER_HURT_TARGETONCE ) + pev->target = 0; + } +} + + +/*QUAKED trigger_multiple (.5 .5 .5) ? notouch +Variable sized repeatable trigger. Must be targeted at one or more entities. +If "health" is set, the trigger must be killed to activate each time. +If "delay" is set, the trigger waits some time after activating before firing. +"wait" : Seconds between triggerings. (.2 default) +If notouch is set, the trigger is only fired by other entities, not by touching. +NOTOUCH has been obsoleted by trigger_relay! +sounds +1) secret +2) beep beep +3) large switch +4) +NEW +if a trigger has a NETNAME, that NETNAME will become the TARGET of the triggered object. +*/ +class CTriggerMultiple : public CBaseTrigger +{ +public: + void Spawn( void ); +}; + +LINK_ENTITY_TO_CLASS( trigger_multiple, CTriggerMultiple ); + + +void CTriggerMultiple :: Spawn( void ) +{ + if (m_flWait == 0) + m_flWait = 0.2; + + InitTrigger(); + + ASSERTSZ(pev->health == 0, "trigger_multiple with health"); +// UTIL_SetOrigin(pev, pev->origin); +// SET_MODEL( ENT(pev), STRING(pev->model) ); +// if (pev->health > 0) +// { +// if (FBitSet(pev->spawnflags, SPAWNFLAG_NOTOUCH)) +// ALERT(at_error, "trigger_multiple spawn: health and notouch don't make sense"); +// pev->max_health = pev->health; +//UNDONE: where to get pfnDie from? +// pev->pfnDie = multi_killed; +// pev->takedamage = DAMAGE_YES; +// pev->solid = SOLID_BBOX; +// UTIL_SetOrigin(pev, pev->origin); // make sure it links into the world +// } +// else + { + SetTouch( MultiTouch ); + } + } + + +/*QUAKED trigger_once (.5 .5 .5) ? notouch +Variable sized trigger. Triggers once, then removes itself. You must set the key "target" to the name of another object in the level that has a matching +"targetname". If "health" is set, the trigger must be killed to activate. +If notouch is set, the trigger is only fired by other entities, not by touching. +if "killtarget" is set, any objects that have a matching "target" will be removed when the trigger is fired. +if "angle" is set, the trigger will only fire when someone is facing the direction of the angle. Use "360" for an angle of 0. +sounds +1) secret +2) beep beep +3) large switch +4) +*/ +class CTriggerOnce : public CTriggerMultiple +{ +public: + void Spawn( void ); +}; + +LINK_ENTITY_TO_CLASS( trigger_once, CTriggerOnce ); +void CTriggerOnce::Spawn( void ) +{ + m_flWait = -1; + + CTriggerMultiple :: Spawn(); +} + + + +void CBaseTrigger :: MultiTouch( CBaseEntity *pOther ) +{ + entvars_t *pevToucher; + + pevToucher = pOther->pev; + + // Only touch clients, monsters, or pushables (depending on flags) + if ( ((pevToucher->flags & FL_CLIENT) && !(pev->spawnflags & SF_TRIGGER_NOCLIENTS)) || + ((pevToucher->flags & FL_MONSTER) && (pev->spawnflags & SF_TRIGGER_ALLOWMONSTERS)) || + (pev->spawnflags & SF_TRIGGER_PUSHABLES) && FClassnameIs(pevToucher,"func_pushable") ) + { + +#if 0 + // if the trigger has an angles field, check player's facing direction + if (pev->movedir != g_vecZero) + { + UTIL_MakeVectors( pevToucher->angles ); + if ( DotProduct( gpGlobals->v_forward, pev->movedir ) < 0 ) + return; // not facing the right way + } +#endif + + ActivateMultiTrigger( pOther ); + } +} + + +// +// the trigger was just touched/killed/used +// self.enemy should be set to the activator so it can be held through a delay +// so wait for the delay time before firing +// +void CBaseTrigger :: ActivateMultiTrigger( CBaseEntity *pActivator ) +{ + if (pev->nextthink > gpGlobals->time) + return; // still waiting for reset time + + if (!UTIL_IsMasterTriggered(m_sMaster,pActivator)) + return; + + if (FClassnameIs(pev, "trigger_secret")) + { + if ( pev->enemy == NULL || !FClassnameIs(pev->enemy, "player")) + return; + gpGlobals->found_secrets++; + } + + if (!FStringNull(pev->noise)) + EMIT_SOUND(ENT(pev), CHAN_VOICE, (char*)STRING(pev->noise), 1, ATTN_NORM); + +// don't trigger again until reset +// pev->takedamage = DAMAGE_NO; + + m_hActivator = pActivator; + SUB_UseTargets( m_hActivator, USE_TOGGLE, 0 ); + + if ( pev->message && pActivator->IsPlayer() ) + { + UTIL_ShowMessage( STRING(pev->message), pActivator ); +// CLIENT_PRINTF( ENT( pActivator->pev ), print_center, STRING(pev->message) ); + } + + if (m_flWait > 0) + { + SetThink( MultiWaitOver ); + pev->nextthink = gpGlobals->time + m_flWait; + } + else + { + // we can't just remove (self) here, because this is a touch function + // called while C code is looping through area links... + SetTouch( NULL ); + pev->nextthink = gpGlobals->time + 0.1; + SetThink( SUB_Remove ); + } +} + + +// the wait time has passed, so set back up for another activation +void CBaseTrigger :: MultiWaitOver( void ) +{ +// if (pev->max_health) +// { +// pev->health = pev->max_health; +// pev->takedamage = DAMAGE_YES; +// pev->solid = SOLID_BBOX; +// } + SetThink( NULL ); +} + + +// ========================= COUNTING TRIGGER ===================================== + +// +// GLOBALS ASSUMED SET: g_eoActivator +// +void CBaseTrigger::CounterUse( CBaseEntity *pActivator, CBaseEntity *pCaller, USE_TYPE useType, float value ) +{ + m_cTriggersLeft--; + m_hActivator = pActivator; + + if (m_cTriggersLeft < 0) + return; + + BOOL fTellActivator = + (m_hActivator != 0) && + FClassnameIs(m_hActivator->pev, "player") && + !FBitSet(pev->spawnflags, SPAWNFLAG_NOMESSAGE); + if (m_cTriggersLeft != 0) + { + if (fTellActivator) + { + // UNDONE: I don't think we want these Quakesque messages + switch (m_cTriggersLeft) + { + case 1: ALERT(at_console, "Only 1 more to go..."); break; + case 2: ALERT(at_console, "Only 2 more to go..."); break; + case 3: ALERT(at_console, "Only 3 more to go..."); break; + default: ALERT(at_console, "There are more to go..."); break; + } + } + return; + } + + // !!!UNDONE: I don't think we want these Quakesque messages + if (fTellActivator) + ALERT(at_console, "Sequence completed!"); + + ActivateMultiTrigger( m_hActivator ); +} + + +/*QUAKED trigger_counter (.5 .5 .5) ? nomessage +Acts as an intermediary for an action that takes multiple inputs. +If nomessage is not set, it will print "1 more.. " etc when triggered and +"sequence complete" when finished. After the counter has been triggered "cTriggersLeft" +times (default 2), it will fire all of it's targets and remove itself. +*/ +class CTriggerCounter : public CBaseTrigger +{ +public: + void Spawn( void ); +}; +LINK_ENTITY_TO_CLASS( trigger_counter, CTriggerCounter ); + +void CTriggerCounter :: Spawn( void ) +{ + // By making the flWait be -1, this counter-trigger will disappear after it's activated + // (but of course it needs cTriggersLeft "uses" before that happens). + m_flWait = -1; + + if (m_cTriggersLeft == 0) + m_cTriggersLeft = 2; + SetUse( CounterUse ); +} + +// ====================== TRIGGER_CHANGELEVEL ================================ + +class CTriggerVolume : public CPointEntity // Derive from point entity so this doesn't move across levels +{ +public: + void Spawn( void ); +}; + +LINK_ENTITY_TO_CLASS( trigger_transition, CTriggerVolume ); + +// Define space that travels across a level transition +void CTriggerVolume :: Spawn( void ) +{ + pev->solid = SOLID_NOT; + pev->movetype = MOVETYPE_NONE; + SET_MODEL(ENT(pev), STRING(pev->model)); // set size and link into world + pev->model = NULL; + pev->modelindex = 0; +} + + +// Fires a target after level transition and then dies +class CFireAndDie : public CBaseDelay +{ +public: + void Spawn( void ); + void Precache( void ); + void Think( void ); + int ObjectCaps( void ) { return CBaseDelay::ObjectCaps() | FCAP_FORCE_TRANSITION; } // Always go across transitions +}; +LINK_ENTITY_TO_CLASS( fireanddie, CFireAndDie ); + +void CFireAndDie::Spawn( void ) +{ + pev->classname = MAKE_STRING("fireanddie"); + // Don't call Precache() - it should be called on restore +} + + +void CFireAndDie::Precache( void ) +{ + // This gets called on restore + pev->nextthink = gpGlobals->time + m_flDelay; +} + + +void CFireAndDie::Think( void ) +{ + SUB_UseTargets( this, USE_TOGGLE, 0 ); + UTIL_Remove( this ); +} + + +#define SF_CHANGELEVEL_USEONLY 0x0002 +class CChangeLevel : public CBaseTrigger +{ +public: + void Spawn( void ); + void KeyValue( KeyValueData *pkvd ); + void EXPORT UseChangeLevel ( CBaseEntity *pActivator, CBaseEntity *pCaller, USE_TYPE useType, float value ); + void EXPORT TriggerChangeLevel( void ); + void EXPORT ExecuteChangeLevel( void ); + void EXPORT TouchChangeLevel( CBaseEntity *pOther ); + void ChangeLevelNow( CBaseEntity *pActivator ); + + static edict_t *FindLandmark( const char *pLandmarkName ); + static int ChangeList( LEVELLIST *pLevelList, int maxList ); + static int AddTransitionToList( LEVELLIST *pLevelList, int listCount, const char *pMapName, const char *pLandmarkName, edict_t *pentLandmark ); + static int InTransitionVolume( CBaseEntity *pEntity, char *pVolumeName ); + + virtual int Save( CSave &save ); + virtual int Restore( CRestore &restore ); + + static TYPEDESCRIPTION m_SaveData[]; + + char m_szMapName[cchMapNameMost]; // trigger_changelevel only: next map + char m_szLandmarkName[cchMapNameMost]; // trigger_changelevel only: landmark on next map + int m_changeTarget; + float m_changeTargetDelay; +}; +LINK_ENTITY_TO_CLASS( trigger_changelevel, CChangeLevel ); + +// Global Savedata for changelevel trigger +TYPEDESCRIPTION CChangeLevel::m_SaveData[] = +{ + DEFINE_ARRAY( CChangeLevel, m_szMapName, FIELD_CHARACTER, cchMapNameMost ), + DEFINE_ARRAY( CChangeLevel, m_szLandmarkName, FIELD_CHARACTER, cchMapNameMost ), + DEFINE_FIELD( CChangeLevel, m_changeTarget, FIELD_STRING ), + DEFINE_FIELD( CChangeLevel, m_changeTargetDelay, FIELD_FLOAT ), +}; + +IMPLEMENT_SAVERESTORE(CChangeLevel,CBaseTrigger); + +// +// Cache user-entity-field values until spawn is called. +// + +void CChangeLevel :: KeyValue( KeyValueData *pkvd ) +{ + if (FStrEq(pkvd->szKeyName, "map")) + { + if (strlen(pkvd->szValue) >= cchMapNameMost) + ALERT( at_error, "Map name '%s' too long (32 chars)\n", pkvd->szValue ); + strcpy(m_szMapName, pkvd->szValue); + pkvd->fHandled = TRUE; + } + else if (FStrEq(pkvd->szKeyName, "landmark")) + { + if (strlen(pkvd->szValue) >= cchMapNameMost) + ALERT( at_error, "Landmark name '%s' too long (32 chars)\n", pkvd->szValue ); + strcpy(m_szLandmarkName, pkvd->szValue); + pkvd->fHandled = TRUE; + } + else if (FStrEq(pkvd->szKeyName, "changetarget")) + { + m_changeTarget = ALLOC_STRING( pkvd->szValue ); + pkvd->fHandled = TRUE; + } + else if (FStrEq(pkvd->szKeyName, "changedelay")) + { + m_changeTargetDelay = atof( pkvd->szValue ); + pkvd->fHandled = TRUE; + } + else + CBaseTrigger::KeyValue( pkvd ); +} + + +/*QUAKED trigger_changelevel (0.5 0.5 0.5) ? NO_INTERMISSION +When the player touches this, he gets sent to the map listed in the "map" variable. Unless the NO_INTERMISSION flag is set, the view will go to the info_intermission spot and display stats. +*/ + +void CChangeLevel :: Spawn( void ) +{ + if ( FStrEq( m_szMapName, "" ) ) + ALERT( at_console, "a trigger_changelevel doesn't have a map" ); + + if ( FStrEq( m_szLandmarkName, "" ) ) + ALERT( at_console, "trigger_changelevel to %s doesn't have a landmark", m_szMapName ); + + if (!FStringNull ( pev->targetname ) ) + { + SetUse ( UseChangeLevel ); + } + InitTrigger(); + if ( !(pev->spawnflags & SF_CHANGELEVEL_USEONLY) ) + SetTouch( TouchChangeLevel ); +// ALERT( at_console, "TRANSITION: %s (%s)\n", m_szMapName, m_szLandmarkName ); +} + + +void CChangeLevel :: ExecuteChangeLevel( void ) +{ + MESSAGE_BEGIN( MSG_ALL, SVC_CDTRACK ); + WRITE_BYTE( 3 ); + WRITE_BYTE( 3 ); + MESSAGE_END(); + + MESSAGE_BEGIN(MSG_ALL, SVC_INTERMISSION); + MESSAGE_END(); +} + + +FILE_GLOBAL char st_szNextMap[cchMapNameMost]; +FILE_GLOBAL char st_szNextSpot[cchMapNameMost]; + +edict_t *CChangeLevel :: FindLandmark( const char *pLandmarkName ) +{ + edict_t *pentLandmark; + + pentLandmark = FIND_ENTITY_BY_STRING( NULL, "targetname", pLandmarkName ); + while ( !FNullEnt( pentLandmark ) ) + { + // Found the landmark + if ( FClassnameIs( pentLandmark, "info_landmark" ) ) + return pentLandmark; + else + pentLandmark = FIND_ENTITY_BY_STRING( pentLandmark, "targetname", pLandmarkName ); + } + ALERT( at_error, "Can't find landmark %s\n", pLandmarkName ); + return NULL; +} + + +//========================================================= +// CChangeLevel :: Use - allows level transitions to be +// triggered by buttons, etc. +// +//========================================================= +void CChangeLevel :: UseChangeLevel ( CBaseEntity *pActivator, CBaseEntity *pCaller, USE_TYPE useType, float value ) +{ + ChangeLevelNow( pActivator ); +} + +void CChangeLevel :: ChangeLevelNow( CBaseEntity *pActivator ) +{ + edict_t *pentLandmark; + LEVELLIST levels[16]; + + ASSERT(!FStrEq(m_szMapName, "")); + + // Don't work in deathmatch + if ( g_pGameRules->IsDeathmatch() ) + return; + + // Some people are firing these multiple times in a frame, disable + if ( gpGlobals->time == pev->dmgtime ) + return; + + pev->dmgtime = gpGlobals->time; + + + CBaseEntity *pPlayer = CBaseEntity::Instance( g_engfuncs.pfnPEntityOfEntIndex( 1 ) ); + if ( !InTransitionVolume( pPlayer, m_szLandmarkName ) ) + { + ALERT( at_aiconsole, "Player isn't in the transition volume %s, aborting\n", m_szLandmarkName ); + return; + } + + // Create an entity to fire the changetarget + if ( m_changeTarget ) + { + CFireAndDie *pFireAndDie = GetClassPtr( (CFireAndDie *)NULL ); + if ( pFireAndDie ) + { + // Set target and delay + pFireAndDie->pev->target = m_changeTarget; + pFireAndDie->m_flDelay = m_changeTargetDelay; + pFireAndDie->pev->origin = pPlayer->pev->origin; + // Call spawn + DispatchSpawn( pFireAndDie->edict() ); + } + } + // This object will get removed in the call to CHANGE_LEVEL, copy the params into "safe" memory + strcpy(st_szNextMap, m_szMapName); + + m_hActivator = pActivator; + SUB_UseTargets( pActivator, USE_TOGGLE, 0 ); + st_szNextSpot[0] = 0; // Init landmark to NULL + + // look for a landmark entity + pentLandmark = FindLandmark( m_szLandmarkName ); + if ( !FNullEnt( pentLandmark ) ) + { + strcpy(st_szNextSpot, m_szLandmarkName); + gpGlobals->vecLandmarkOffset = VARS(pentLandmark)->origin; + } +// ALERT( at_console, "Level touches %d levels\n", ChangeList( levels, 16 ) ); + ALERT( at_console, "CHANGE LEVEL: %s %s\n", st_szNextMap, st_szNextSpot ); + CHANGE_LEVEL( st_szNextMap, st_szNextSpot ); +} + +// +// GLOBALS ASSUMED SET: st_szNextMap +// +void CChangeLevel :: TouchChangeLevel( CBaseEntity *pOther ) +{ + if (!FClassnameIs(pOther->pev, "player")) + return; + + ChangeLevelNow( pOther ); +} + + +// Add a transition to the list, but ignore duplicates +// (a designer may have placed multiple trigger_changelevels with the same landmark) +int CChangeLevel::AddTransitionToList( LEVELLIST *pLevelList, int listCount, const char *pMapName, const char *pLandmarkName, edict_t *pentLandmark ) +{ + int i; + + if ( !pLevelList || !pMapName || !pLandmarkName || !pentLandmark ) + return 0; + + for ( i = 0; i < listCount; i++ ) + { + if ( pLevelList[i].pentLandmark == pentLandmark && strcmp( pLevelList[i].mapName, pMapName ) == 0 ) + return 0; + } + strcpy( pLevelList[listCount].mapName, pMapName ); + strcpy( pLevelList[listCount].landmarkName, pLandmarkName ); + pLevelList[listCount].pentLandmark = pentLandmark; + pLevelList[listCount].vecLandmarkOrigin = VARS(pentLandmark)->origin; + + return 1; +} + +int BuildChangeList( LEVELLIST *pLevelList, int maxList ) +{ + return CChangeLevel::ChangeList( pLevelList, maxList ); +} + + +int CChangeLevel::InTransitionVolume( CBaseEntity *pEntity, char *pVolumeName ) +{ + edict_t *pentVolume; + + + if ( pEntity->ObjectCaps() & FCAP_FORCE_TRANSITION ) + return 1; + + // If you're following another entity, follow it through the transition (weapons follow the player) + if ( pEntity->pev->movetype == MOVETYPE_FOLLOW ) + { + if ( pEntity->pev->aiment != NULL ) + pEntity = CBaseEntity::Instance( pEntity->pev->aiment ); + } + + int inVolume = 1; // Unless we find a trigger_transition, everything is in the volume + + pentVolume = FIND_ENTITY_BY_TARGETNAME( NULL, pVolumeName ); + while ( !FNullEnt( pentVolume ) ) + { + CBaseEntity *pVolume = CBaseEntity::Instance( pentVolume ); + + if ( pVolume && FClassnameIs( pVolume->pev, "trigger_transition" ) ) + { + if ( pVolume->Intersects( pEntity ) ) // It touches one, it's in the volume + return 1; + else + inVolume = 0; // Found a trigger_transition, but I don't intersect it -- if I don't find another, don't go! + } + pentVolume = FIND_ENTITY_BY_TARGETNAME( pentVolume, pVolumeName ); + } + + return inVolume; +} + + +// We can only ever move 512 entities across a transition +#define MAX_ENTITY 512 + +// This has grown into a complicated beast +// Can we make this more elegant? +// This builds the list of all transitions on this level and which entities are in their PVS's and can / should +// be moved across. +int CChangeLevel::ChangeList( LEVELLIST *pLevelList, int maxList ) +{ + edict_t *pentChangelevel, *pentLandmark; + int i, count; + + count = 0; + + // Find all of the possible level changes on this BSP + pentChangelevel = FIND_ENTITY_BY_STRING( NULL, "classname", "trigger_changelevel" ); + if ( FNullEnt( pentChangelevel ) ) + return 0; + while ( !FNullEnt( pentChangelevel ) ) + { + CChangeLevel *pTrigger; + + pTrigger = GetClassPtr((CChangeLevel *)VARS(pentChangelevel)); + if ( pTrigger ) + { + // Find the corresponding landmark + pentLandmark = FindLandmark( pTrigger->m_szLandmarkName ); + if ( pentLandmark ) + { + // Build a list of unique transitions + if ( AddTransitionToList( pLevelList, count, pTrigger->m_szMapName, pTrigger->m_szLandmarkName, pentLandmark ) ) + { + count++; + if ( count >= maxList ) // FULL!! + break; + } + } + } + pentChangelevel = FIND_ENTITY_BY_STRING( pentChangelevel, "classname", "trigger_changelevel" ); + } + + if ( gpGlobals->pSaveData && ((SAVERESTOREDATA *)gpGlobals->pSaveData)->pTable ) + { + CSave saveHelper( (SAVERESTOREDATA *)gpGlobals->pSaveData ); + + for ( i = 0; i < count; i++ ) + { + int j, entityCount = 0; + CBaseEntity *pEntList[ MAX_ENTITY ]; + int entityFlags[ MAX_ENTITY ]; + + // Follow the linked list of entities in the PVS of the transition landmark + edict_t *pent = UTIL_EntitiesInPVS( pLevelList[i].pentLandmark ); + + // Build a list of valid entities in this linked list (we're going to use pent->v.chain again) + while ( !FNullEnt( pent ) ) + { + CBaseEntity *pEntity = CBaseEntity::Instance(pent); + if ( pEntity ) + { +// ALERT( at_console, "Trying %s\n", STRING(pEntity->pev->classname) ); + int caps = pEntity->ObjectCaps(); + if ( !(caps & FCAP_DONT_SAVE) ) + { + int flags = 0; + + // If this entity can be moved or is global, mark it + if ( caps & FCAP_ACROSS_TRANSITION ) + flags |= FENTTABLE_MOVEABLE; + if ( pEntity->pev->globalname && !pEntity->IsDormant() ) + flags |= FENTTABLE_GLOBAL; + if ( flags ) + { + pEntList[ entityCount ] = pEntity; + entityFlags[ entityCount ] = flags; + entityCount++; + if ( entityCount > MAX_ENTITY ) + ALERT( at_error, "Too many entities across a transition!" ); + } +// else +// ALERT( at_console, "Failed %s\n", STRING(pEntity->pev->classname) ); + } +// else +// ALERT( at_console, "DON'T SAVE %s\n", STRING(pEntity->pev->classname) ); + } + pent = pent->v.chain; + } + + for ( j = 0; j < entityCount; j++ ) + { + // Check to make sure the entity isn't screened out by a trigger_transition + if ( entityFlags[j] && InTransitionVolume( pEntList[j], pLevelList[i].landmarkName ) ) + { + // Mark entity table with 1<pev->classname) ); + + } + } + } + + return count; +} + +/* +go to the next level for deathmatch +only called if a time or frag limit has expired +*/ +void NextLevel( void ) +{ + edict_t* pent; + CChangeLevel *pChange; + + // find a trigger_changelevel + pent = FIND_ENTITY_BY_CLASSNAME(NULL, "trigger_changelevel"); + + // go back to start if no trigger_changelevel + if (FNullEnt(pent)) + { + gpGlobals->mapname = ALLOC_STRING("start"); + pChange = GetClassPtr( (CChangeLevel *)NULL ); + strcpy(pChange->m_szMapName, "start"); + } + else + pChange = GetClassPtr( (CChangeLevel *)VARS(pent)); + + strcpy(st_szNextMap, pChange->m_szMapName); + g_fGameOver = TRUE; + + if (pChange->pev->nextthink < gpGlobals->time) + { + pChange->SetThink( CChangeLevel::ExecuteChangeLevel ); + pChange->pev->nextthink = gpGlobals->time + 0.1; + } +} + + +// ============================== LADDER ======================================= + +class CLadder : public CBaseTrigger +{ +public: + void KeyValue( KeyValueData *pkvd ); + void Spawn( void ); + void Precache( void ); +}; +LINK_ENTITY_TO_CLASS( func_ladder, CLadder ); + + +void CLadder :: KeyValue( KeyValueData *pkvd ) +{ + CBaseTrigger::KeyValue( pkvd ); +} + + +//========================================================= +// func_ladder - makes an area vertically negotiable +//========================================================= +void CLadder :: Precache( void ) +{ + // Do all of this in here because we need to 'convert' old saved games + pev->solid = SOLID_NOT; + pev->skin = CONTENTS_LADDER; + if ( CVAR_GET_FLOAT("showtriggers") == 0 ) + { + pev->rendermode = kRenderTransTexture; + pev->renderamt = 0; + } + pev->effects &= ~EF_NODRAW; +} + + +void CLadder :: Spawn( void ) +{ + Precache(); + + SET_MODEL(ENT(pev), STRING(pev->model)); // set size and link into world + pev->movetype = MOVETYPE_PUSH; +} + + +// ========================== A TRIGGER THAT PUSHES YOU =============================== + +class CTriggerPush : public CBaseTrigger +{ +public: + void Spawn( void ); + void KeyValue( KeyValueData *pkvd ); + void Touch( CBaseEntity *pOther ); +}; +LINK_ENTITY_TO_CLASS( trigger_push, CTriggerPush ); + + +void CTriggerPush :: KeyValue( KeyValueData *pkvd ) +{ + CBaseTrigger::KeyValue( pkvd ); +} + + +/*QUAKED trigger_push (.5 .5 .5) ? TRIG_PUSH_ONCE +Pushes the player +*/ + +void CTriggerPush :: Spawn( ) +{ + if ( pev->angles == g_vecZero ) + pev->angles.y = 360; + InitTrigger(); + + if (pev->speed == 0) + pev->speed = 100; + + if ( FBitSet (pev->spawnflags, SF_TRIGGER_PUSH_START_OFF) )// if flagged to Start Turned Off, make trigger nonsolid. + pev->solid = SOLID_NOT; + + SetUse( ToggleUse ); + + UTIL_SetOrigin( pev, pev->origin ); // Link into the list +} + + +void CTriggerPush :: Touch( CBaseEntity *pOther ) +{ + entvars_t* pevToucher = pOther->pev; + + // UNDONE: Is there a better way than health to detect things that have physics? (clients/monsters) + switch( pevToucher->movetype ) + { + case MOVETYPE_NONE: + case MOVETYPE_PUSH: + case MOVETYPE_NOCLIP: + case MOVETYPE_FOLLOW: + return; + } + + if ( pevToucher->solid != SOLID_NOT && pevToucher->solid != SOLID_BSP ) + { + // Instant trigger, just transfer velocity and remove + if (FBitSet(pev->spawnflags, SF_TRIG_PUSH_ONCE)) + { + pevToucher->velocity = pevToucher->velocity + (pev->speed * pev->movedir); + if ( pevToucher->velocity.z > 0 ) + pevToucher->flags &= ~FL_ONGROUND; + UTIL_Remove( this ); + } + else + { // Push field, transfer to base velocity + Vector vecPush = (pev->speed * pev->movedir); + if ( pevToucher->flags & FL_BASEVELOCITY ) + vecPush = vecPush + pevToucher->basevelocity; + + pevToucher->basevelocity = vecPush; + + pevToucher->flags |= FL_BASEVELOCITY; +// ALERT( at_console, "Vel %f, base %f\n", pevToucher->velocity.z, pevToucher->basevelocity.z ); + } + } +} + + +//====================================== +// teleport trigger +// +// + +void CBaseTrigger :: TeleportTouch( CBaseEntity *pOther ) +{ + entvars_t* pevToucher = pOther->pev; + edict_t *pentTarget = NULL; + + // Only teleport monsters or clients + if ( !FBitSet( pevToucher->flags, FL_CLIENT|FL_MONSTER ) ) + return; + + if (!UTIL_IsMasterTriggered(m_sMaster, pOther)) + return; + + if ( !( pev->spawnflags & SF_TRIGGER_ALLOWMONSTERS ) ) + {// no monsters allowed! + if ( FBitSet( pevToucher->flags, FL_MONSTER ) ) + { + return; + } + } + + if ( ( pev->spawnflags & SF_TRIGGER_NOCLIENTS ) ) + {// no clients allowed + if ( pOther->IsPlayer() ) + { + return; + } + } + + pentTarget = FIND_ENTITY_BY_TARGETNAME( pentTarget, STRING(pev->target) ); + if (FNullEnt(pentTarget)) + return; + + Vector tmp = VARS( pentTarget )->origin; + + if ( pOther->IsPlayer() ) + { + tmp.z -= pOther->pev->mins.z;// make origin adjustments in case the teleportee is a player. (origin in center, not at feet) + } + + tmp.z++; + + pevToucher->flags &= ~FL_ONGROUND; + + UTIL_SetOrigin( pevToucher, tmp ); + + pevToucher->angles = pentTarget->v.angles; + + if ( pOther->IsPlayer() ) + { + pevToucher->v_angle = pentTarget->v.angles; + } + + pevToucher->fixangle = TRUE; + pevToucher->velocity = pevToucher->basevelocity = g_vecZero; +} + + +class CTriggerTeleport : public CBaseTrigger +{ +public: + void Spawn( void ); +}; +LINK_ENTITY_TO_CLASS( trigger_teleport, CTriggerTeleport ); + +void CTriggerTeleport :: Spawn( void ) +{ + InitTrigger(); + + SetTouch( TeleportTouch ); +} + + +LINK_ENTITY_TO_CLASS( info_teleport_destination, CPointEntity ); + + + +class CTriggerSave : public CBaseTrigger +{ +public: + void Spawn( void ); + void EXPORT SaveTouch( CBaseEntity *pOther ); +}; +LINK_ENTITY_TO_CLASS( trigger_autosave, CTriggerSave ); + +void CTriggerSave::Spawn( void ) +{ + if ( g_pGameRules->IsDeathmatch() ) + { + REMOVE_ENTITY( ENT(pev) ); + return; + } + + InitTrigger(); + SetTouch( SaveTouch ); +} + +void CTriggerSave::SaveTouch( CBaseEntity *pOther ) +{ + if ( !UTIL_IsMasterTriggered( m_sMaster, pOther ) ) + return; + + // Only save on clients + if ( !pOther->IsPlayer() ) + return; + + SetTouch( NULL ); + UTIL_Remove( this ); + SERVER_COMMAND( "autosave\n" ); +} + +#define SF_ENDSECTION_USEONLY 0x0001 + +class CTriggerEndSection : public CBaseTrigger +{ +public: + void Spawn( void ); + void EXPORT EndSectionTouch( CBaseEntity *pOther ); + void KeyValue( KeyValueData *pkvd ); + void EXPORT EndSectionUse( CBaseEntity *pActivator, CBaseEntity *pCaller, USE_TYPE useType, float value ); +}; +LINK_ENTITY_TO_CLASS( trigger_endsection, CTriggerEndSection ); + + +void CTriggerEndSection::EndSectionUse( CBaseEntity *pActivator, CBaseEntity *pCaller, USE_TYPE useType, float value ) +{ + // Only save on clients + if ( pActivator && !pActivator->IsNetClient() ) + return; + + SetUse( NULL ); + + if ( pev->message ) + { + g_engfuncs.pfnEndSection(STRING(pev->message)); + } + UTIL_Remove( this ); +} + +void CTriggerEndSection::Spawn( void ) +{ + if ( g_pGameRules->IsDeathmatch() ) + { + REMOVE_ENTITY( ENT(pev) ); + return; + } + + InitTrigger(); + + SetUse ( EndSectionUse ); + // If it is a "use only" trigger, then don't set the touch function. + if ( ! (pev->spawnflags & SF_ENDSECTION_USEONLY) ) + SetTouch( EndSectionTouch ); +} + +void CTriggerEndSection::EndSectionTouch( CBaseEntity *pOther ) +{ + // Only save on clients + if ( !pOther->IsNetClient() ) + return; + + SetTouch( NULL ); + + if (pev->message) + { + g_engfuncs.pfnEndSection(STRING(pev->message)); + } + UTIL_Remove( this ); +} + +void CTriggerEndSection :: KeyValue( KeyValueData *pkvd ) +{ + if (FStrEq(pkvd->szKeyName, "section")) + { +// m_iszSectionName = ALLOC_STRING( pkvd->szValue ); + // Store this in message so we don't have to write save/restore for this ent + pev->message = ALLOC_STRING( pkvd->szValue ); + pkvd->fHandled = TRUE; + } + else + CBaseTrigger::KeyValue( pkvd ); +} + + +class CTriggerGravity : public CBaseTrigger +{ +public: + void Spawn( void ); + void EXPORT GravityTouch( CBaseEntity *pOther ); +}; +LINK_ENTITY_TO_CLASS( trigger_gravity, CTriggerGravity ); + +void CTriggerGravity::Spawn( void ) +{ + InitTrigger(); + SetTouch( GravityTouch ); +} + +void CTriggerGravity::GravityTouch( CBaseEntity *pOther ) +{ + // Only save on clients + if ( !pOther->IsPlayer() ) + return; + + pOther->pev->gravity = pev->gravity; +} + + + + + + + +// this is a really bad idea. +class CTriggerChangeTarget : public CBaseDelay +{ +public: + void KeyValue( KeyValueData *pkvd ); + void Spawn( void ); + void Use( CBaseEntity *pActivator, CBaseEntity *pCaller, USE_TYPE useType, float value ); + + int ObjectCaps( void ) { return CBaseDelay::ObjectCaps() & ~FCAP_ACROSS_TRANSITION; } + virtual int Save( CSave &save ); + virtual int Restore( CRestore &restore ); + + static TYPEDESCRIPTION m_SaveData[]; + +private: + int m_iszNewTarget; +}; +LINK_ENTITY_TO_CLASS( trigger_changetarget, CTriggerChangeTarget ); + +TYPEDESCRIPTION CTriggerChangeTarget::m_SaveData[] = +{ + DEFINE_FIELD( CTriggerChangeTarget, m_iszNewTarget, FIELD_STRING ), +}; + +IMPLEMENT_SAVERESTORE(CTriggerChangeTarget,CBaseDelay); + +void CTriggerChangeTarget::KeyValue( KeyValueData *pkvd ) +{ + if (FStrEq(pkvd->szKeyName, "m_iszNewTarget")) + { + m_iszNewTarget = ALLOC_STRING( pkvd->szValue ); + pkvd->fHandled = TRUE; + } + else + CBaseDelay::KeyValue( pkvd ); +} + +void CTriggerChangeTarget::Spawn( void ) +{ +} + + +void CTriggerChangeTarget::Use( CBaseEntity *pActivator, CBaseEntity *pCaller, USE_TYPE useType, float value ) +{ + CBaseEntity *pTarget = UTIL_FindEntityByString( NULL, "targetname", STRING( pev->target ) ); + + if (pTarget) + { + pTarget->pev->target = m_iszNewTarget; + CBaseMonster *pMonster = pTarget->MyMonsterPointer( ); + if (pMonster) + { + pMonster->m_pGoalEnt = NULL; + } + } +} + + + + +#define SF_CAMERA_PLAYER_POSITION 1 +#define SF_CAMERA_PLAYER_TARGET 2 +#define SF_CAMERA_PLAYER_TAKECONTROL 4 + +class CTriggerCamera : public CBaseDelay +{ +public: + void Spawn( void ); + void KeyValue( KeyValueData *pkvd ); + void Use( CBaseEntity *pActivator, CBaseEntity *pCaller, USE_TYPE useType, float value ); + void EXPORT FollowTarget( void ); + void Move(void); + + virtual int Save( CSave &save ); + virtual int Restore( CRestore &restore ); + virtual int ObjectCaps( void ) { return CBaseEntity :: ObjectCaps() & ~FCAP_ACROSS_TRANSITION; } + static TYPEDESCRIPTION m_SaveData[]; + + EHANDLE m_hPlayer; + EHANDLE m_hTarget; + CBaseEntity *m_pentPath; + int m_sPath; + float m_flWait; + float m_flReturnTime; + float m_flStopTime; + float m_moveDistance; + float m_targetSpeed; + float m_initialSpeed; + float m_acceleration; + float m_deceleration; + int m_state; + +}; +LINK_ENTITY_TO_CLASS( trigger_camera, CTriggerCamera ); + +// Global Savedata for changelevel friction modifier +TYPEDESCRIPTION CTriggerCamera::m_SaveData[] = +{ + DEFINE_FIELD( CTriggerCamera, m_hPlayer, FIELD_EHANDLE ), + DEFINE_FIELD( CTriggerCamera, m_hTarget, FIELD_EHANDLE ), + DEFINE_FIELD( CTriggerCamera, m_pentPath, FIELD_CLASSPTR ), + DEFINE_FIELD( CTriggerCamera, m_sPath, FIELD_STRING ), + DEFINE_FIELD( CTriggerCamera, m_flWait, FIELD_FLOAT ), + DEFINE_FIELD( CTriggerCamera, m_flReturnTime, FIELD_TIME ), + DEFINE_FIELD( CTriggerCamera, m_flStopTime, FIELD_TIME ), + DEFINE_FIELD( CTriggerCamera, m_moveDistance, FIELD_FLOAT ), + DEFINE_FIELD( CTriggerCamera, m_targetSpeed, FIELD_FLOAT ), + DEFINE_FIELD( CTriggerCamera, m_initialSpeed, FIELD_FLOAT ), + DEFINE_FIELD( CTriggerCamera, m_acceleration, FIELD_FLOAT ), + DEFINE_FIELD( CTriggerCamera, m_deceleration, FIELD_FLOAT ), + DEFINE_FIELD( CTriggerCamera, m_state, FIELD_INTEGER ), +}; + +IMPLEMENT_SAVERESTORE(CTriggerCamera,CBaseDelay); + +void CTriggerCamera::Spawn( void ) +{ + pev->movetype = MOVETYPE_NOCLIP; + pev->solid = SOLID_NOT; // Remove model & collisions + pev->renderamt = 0; // The engine won't draw this model if this is set to 0 and blending is on + pev->rendermode = kRenderTransTexture; + + m_initialSpeed = pev->speed; + if ( m_acceleration == 0 ) + m_acceleration = 500; + if ( m_deceleration == 0 ) + m_deceleration = 500; +} + + +void CTriggerCamera :: KeyValue( KeyValueData *pkvd ) +{ + if (FStrEq(pkvd->szKeyName, "wait")) + { + m_flWait = atof(pkvd->szValue); + pkvd->fHandled = TRUE; + } + else if (FStrEq(pkvd->szKeyName, "moveto")) + { + m_sPath = ALLOC_STRING( pkvd->szValue ); + pkvd->fHandled = TRUE; + } + else if (FStrEq(pkvd->szKeyName, "acceleration")) + { + m_acceleration = atof( pkvd->szValue ); + pkvd->fHandled = TRUE; + } + else if (FStrEq(pkvd->szKeyName, "deceleration")) + { + m_deceleration = atof( pkvd->szValue ); + pkvd->fHandled = TRUE; + } + else + CBaseDelay::KeyValue( pkvd ); +} + + + +void CTriggerCamera::Use( CBaseEntity *pActivator, CBaseEntity *pCaller, USE_TYPE useType, float value ) +{ + if ( !ShouldToggle( useType, m_state ) ) + return; + + // Toggle state + m_state = !m_state; + if (m_state == 0) + { + m_flReturnTime = gpGlobals->time; + return; + } + if ( !pActivator || !pActivator->IsPlayer() ) + { + pActivator = CBaseEntity::Instance(g_engfuncs.pfnPEntityOfEntIndex( 1 )); + } + + m_hPlayer = pActivator; + + m_flReturnTime = gpGlobals->time + m_flWait; + pev->speed = m_initialSpeed; + m_targetSpeed = m_initialSpeed; + + if (FBitSet (pev->spawnflags, SF_CAMERA_PLAYER_TARGET ) ) + { + m_hTarget = m_hPlayer; + } + else + { + m_hTarget = GetNextTarget(); + } + + // Nothing to look at! + if ( m_hTarget == NULL ) + { + return; + } + + + if (FBitSet (pev->spawnflags, SF_CAMERA_PLAYER_TAKECONTROL ) ) + { + ((CBasePlayer *)pActivator)->EnableControl(FALSE); + } + + if ( m_sPath ) + { + m_pentPath = Instance( FIND_ENTITY_BY_TARGETNAME ( NULL, STRING(m_sPath)) ); + } + else + { + m_pentPath = NULL; + } + + m_flStopTime = gpGlobals->time; + if ( m_pentPath ) + { + if ( m_pentPath->pev->speed != 0 ) + m_targetSpeed = m_pentPath->pev->speed; + + m_flStopTime += m_pentPath->GetDelay(); + } + + // copy over player information + if (FBitSet (pev->spawnflags, SF_CAMERA_PLAYER_POSITION ) ) + { + UTIL_SetOrigin( pev, pActivator->pev->origin + pActivator->pev->view_ofs ); + pev->angles.x = -pActivator->pev->angles.x; + pev->angles.y = pActivator->pev->angles.y; + pev->angles.z = 0; + pev->velocity = pActivator->pev->velocity; + } + else + { + pev->velocity = Vector( 0, 0, 0 ); + } + + SET_VIEW( pActivator->edict(), edict() ); + + SET_MODEL(ENT(pev), STRING(pActivator->pev->model) ); + + // follow the player down + SetThink( FollowTarget ); + pev->nextthink = gpGlobals->time; + + m_moveDistance = 0; + Move(); +} + + +void CTriggerCamera::FollowTarget( ) +{ + if (m_hPlayer == NULL) + return; + + if (m_hTarget == NULL || m_flReturnTime < gpGlobals->time) + { + if (m_hPlayer->IsAlive( )) + { + SET_VIEW( m_hPlayer->edict(), m_hPlayer->edict() ); + ((CBasePlayer *)((CBaseEntity *)m_hPlayer))->EnableControl(TRUE); + } + SUB_UseTargets( this, USE_TOGGLE, 0 ); + pev->avelocity = Vector( 0, 0, 0 ); + m_state = 0; + return; + } + + Vector vecGoal = UTIL_VecToAngles( m_hTarget->pev->origin - pev->origin ); + vecGoal.x = -vecGoal.x; + + if (pev->angles.y > 360) + pev->angles.y -= 360; + + if (pev->angles.y < 0) + pev->angles.y += 360; + + float dx = vecGoal.x - pev->angles.x; + float dy = vecGoal.y - pev->angles.y; + + if (dx < -180) + dx += 360; + if (dx > 180) + dx = dx - 360; + + if (dy < -180) + dy += 360; + if (dy > 180) + dy = dy - 360; + + pev->avelocity.x = dx * 40 * gpGlobals->frametime; + pev->avelocity.y = dy * 40 * gpGlobals->frametime; + + + if (!(FBitSet (pev->spawnflags, SF_CAMERA_PLAYER_TAKECONTROL))) + { + pev->velocity = pev->velocity * 0.8; + if (pev->velocity.Length( ) < 10.0) + pev->velocity = g_vecZero; + } + + pev->nextthink = gpGlobals->time; + + Move(); +} + +void CTriggerCamera::Move() +{ + // Not moving on a path, return + if (!m_pentPath) + return; + + // Subtract movement from the previous frame + m_moveDistance -= pev->speed * gpGlobals->frametime; + + // Have we moved enough to reach the target? + if ( m_moveDistance <= 0 ) + { + // Fire the passtarget if there is one + if ( m_pentPath->pev->message ) + { + FireTargets( STRING(m_pentPath->pev->message), this, this, USE_TOGGLE, 0 ); + if ( FBitSet( m_pentPath->pev->spawnflags, SF_CORNER_FIREONCE ) ) + m_pentPath->pev->message = 0; + } + // Time to go to the next target + m_pentPath = m_pentPath->GetNextTarget(); + + // Set up next corner + if ( !m_pentPath ) + { + pev->velocity = g_vecZero; + } + else + { + if ( m_pentPath->pev->speed != 0 ) + m_targetSpeed = m_pentPath->pev->speed; + + Vector delta = m_pentPath->pev->origin - pev->origin; + m_moveDistance = delta.Length(); + pev->movedir = delta.Normalize(); + m_flStopTime = gpGlobals->time + m_pentPath->GetDelay(); + } + } + + if ( m_flStopTime > gpGlobals->time ) + pev->speed = UTIL_Approach( 0, pev->speed, m_deceleration * gpGlobals->frametime ); + else + pev->speed = UTIL_Approach( m_targetSpeed, pev->speed, m_acceleration * gpGlobals->frametime ); + + float fraction = 2 * gpGlobals->frametime; + pev->velocity = ((pev->movedir * pev->speed) * fraction) + (pev->velocity * (1-fraction)); +} diff --git a/spirit/tripmine.cpp b/dlls/tripmine.cpp similarity index 70% rename from spirit/tripmine.cpp rename to dlls/tripmine.cpp index 550be332..81ff1044 100644 --- a/spirit/tripmine.cpp +++ b/dlls/tripmine.cpp @@ -24,6 +24,8 @@ #define TRIPMINE_PRIMARY_VOLUME 450 + + enum tripmine_e { TRIPMINE_IDLE1 = 0, TRIPMINE_IDLE2, @@ -32,8 +34,13 @@ enum tripmine_e { TRIPMINE_FIDGET, TRIPMINE_HOLSTER, TRIPMINE_DRAW, + TRIPMINE_WORLD, + TRIPMINE_GROUND, }; + +#ifndef CLIENT_DLL + class CTripmineGrenade : public CGrenade { void Spawn( void ); @@ -62,24 +69,11 @@ class CTripmineGrenade : public CGrenade EHANDLE m_hOwner; CBeam *m_pBeam; - CBeam *m_pMirBeam; Vector m_posOwner; Vector m_angleOwner; edict_t *m_pRealOwner;// tracelines don't hit PEV->OWNER, which means a player couldn't detonate his own trip mine, so we store the owner here. }; -class CTripmine : public CBasePlayerWeapon -{ -public: - void Spawn( void ); - void Precache( void ); - int GetItemInfo(ItemInfo *p); - void PrimaryAttack( void ); - BOOL Deploy( void ); - void Holster( ); - void WeaponIdle( void ); -}; - LINK_ENTITY_TO_CLASS( monster_tripmine, CTripmineGrenade ); TYPEDESCRIPTION CTripmineGrenade::m_SaveData[] = @@ -90,7 +84,6 @@ TYPEDESCRIPTION CTripmineGrenade::m_SaveData[] = DEFINE_FIELD( CTripmineGrenade, m_flBeamLength, FIELD_FLOAT ), DEFINE_FIELD( CTripmineGrenade, m_hOwner, FIELD_EHANDLE ), DEFINE_FIELD( CTripmineGrenade, m_pBeam, FIELD_CLASSPTR ), - DEFINE_FIELD( CTripmineGrenade, m_pMirBeam, FIELD_CLASSPTR ), DEFINE_FIELD( CTripmineGrenade, m_posOwner, FIELD_POSITION_VECTOR ), DEFINE_FIELD( CTripmineGrenade, m_angleOwner, FIELD_VECTOR ), DEFINE_FIELD( CTripmineGrenade, m_pRealOwner, FIELD_EDICT ), @@ -106,10 +99,15 @@ void CTripmineGrenade :: Spawn( void ) pev->movetype = MOVETYPE_FLY; pev->solid = SOLID_NOT; - SET_MODEL(ENT(pev), "models/w_tripmine.mdl"); + SET_MODEL(ENT(pev), "models/v_tripmine.mdl"); + pev->frame = 0; + pev->body = 3; + pev->sequence = TRIPMINE_WORLD; + ResetSequenceInfo( ); + pev->framerate = 0; - UTIL_AutoSetSize(); - UTIL_SetOrigin( this, pev->origin ); + UTIL_SetSize(pev, Vector( -8, -8, -8), Vector(8, 8, 8)); + UTIL_SetOrigin( pev, pev->origin ); if (pev->spawnflags & 1) { @@ -122,8 +120,8 @@ void CTripmineGrenade :: Spawn( void ) m_flPowerUp = gpGlobals->time + 2.5; } - SetThink(&CTripmineGrenade :: PowerupThink ); - SetNextThink( 0.2 ); + SetThink( PowerupThink ); + pev->nextthink = gpGlobals->time + 0.2; pev->takedamage = DAMAGE_YES; pev->dmg = gSkillData.plrDmgTripmine; @@ -141,13 +139,13 @@ void CTripmineGrenade :: Spawn( void ) UTIL_MakeAimVectors( pev->angles ); m_vecDir = gpGlobals->v_forward; - m_vecEnd = pev->origin + m_vecDir * 8192; //make beam across level + m_vecEnd = pev->origin + m_vecDir * 2048; } void CTripmineGrenade :: Precache( void ) { - PRECACHE_MODEL("models/w_tripmine.mdl"); + PRECACHE_MODEL("models/v_tripmine.mdl"); PRECACHE_SOUND("weapons/mine_deploy.wav"); PRECACHE_SOUND("weapons/mine_activate.wav"); PRECACHE_SOUND("weapons/mine_charge.wav"); @@ -157,11 +155,11 @@ void CTripmineGrenade :: Precache( void ) void CTripmineGrenade :: WarningThink( void ) { // play warning sound - EMIT_SOUND( ENT(pev), CHAN_VOICE, "buttons/Blip2.wav", 1.0, ATTN_NORM ); + // EMIT_SOUND( ENT(pev), CHAN_VOICE, "buttons/Blip2.wav", 1.0, ATTN_NORM ); // set to power up - SetThink(&CTripmineGrenade :: PowerupThink ); - SetNextThink( 1.0 ); + SetThink( PowerupThink ); + pev->nextthink = gpGlobals->time + 1.0; } @@ -179,7 +177,7 @@ void CTripmineGrenade :: PowerupThink( void ) { pev->owner = oldowner; m_flPowerUp += 0.1; - SetNextThink( 0.1 ); + pev->nextthink = gpGlobals->time + 0.1; return; } if (tr.flFraction < 1.0) @@ -193,9 +191,9 @@ void CTripmineGrenade :: PowerupThink( void ) { STOP_SOUND( ENT(pev), CHAN_VOICE, "weapons/mine_deploy.wav" ); STOP_SOUND( ENT(pev), CHAN_BODY, "weapons/mine_charge.wav" ); - SetThink(&CTripmineGrenade :: SUB_Remove ); - SetNextThink( 0.1 ); - ALERT( at_debug, "WARNING:Tripmine at %.0f, %.0f, %.0f removed\n", pev->origin.x, pev->origin.y, pev->origin.z ); + SetThink( SUB_Remove ); + pev->nextthink = gpGlobals->time + 0.1; + ALERT( at_console, "WARNING:Tripmine at %.0f, %.0f, %.0f removed\n", pev->origin.x, pev->origin.y, pev->origin.z ); KillBeam(); return; } @@ -208,23 +206,25 @@ void CTripmineGrenade :: PowerupThink( void ) CBaseEntity *pMine = Create( "weapon_tripmine", pev->origin + m_vecDir * 24, pev->angles ); pMine->pev->spawnflags |= SF_NORESPAWN; - SetThink(&CTripmineGrenade :: SUB_Remove ); + SetThink( SUB_Remove ); KillBeam(); - SetNextThink( 0.1 ); + pev->nextthink = gpGlobals->time + 0.1; return; } + // ALERT( at_console, "%d %.0f %.0f %0.f\n", pev->owner, m_pOwner->pev->origin.x, m_pOwner->pev->origin.y, m_pOwner->pev->origin.z ); + if (gpGlobals->time > m_flPowerUp) { // make solid pev->solid = SOLID_BBOX; - UTIL_SetOrigin( this, pev->origin ); + UTIL_SetOrigin( pev, pev->origin ); MakeBeam( ); // play enabled sound - EMIT_SOUND_DYN( ENT(pev), CHAN_VOICE, "weapons/mine_activate.wav", 0.5, ATTN_NORM, 1.0, 75 ); + EMIT_SOUND_DYN( ENT(pev), CHAN_VOICE, "weapons/mine_activate.wav", 0.5, ATTN_NORM, 1.0, 75 ); } - SetNextThink( 0.1 ); + pev->nextthink = gpGlobals->time + 0.1; } @@ -235,34 +235,30 @@ void CTripmineGrenade :: KillBeam( void ) UTIL_Remove( m_pBeam ); m_pBeam = NULL; } - if( m_pMirBeam ) - { - UTIL_Remove( m_pMirBeam ); - m_pMirBeam = NULL; - } } void CTripmineGrenade :: MakeBeam( void ) { TraceResult tr; - Vector mirpos, mirend; - + + // ALERT( at_console, "serverflags %f\n", gpGlobals->serverflags ); + UTIL_TraceLine( pev->origin, m_vecEnd, dont_ignore_monsters, ENT( pev ), &tr ); m_flBeamLength = tr.flFraction; // set to follow laser spot - SetThink(&CTripmineGrenade :: BeamBreakThink ); - SetNextThink( 0.1 ); + SetThink( BeamBreakThink ); + pev->nextthink = gpGlobals->time + 0.1; - Vector vecTmpEnd = pev->origin + m_vecDir * 8192 * m_flBeamLength; + Vector vecTmpEnd = pev->origin + m_vecDir * 2048 * m_flBeamLength; - m_pBeam = CBeam::BeamCreate( g_pModelNameLaser, 5 ); - m_pBeam->PointsInit( vecTmpEnd + gpGlobals->v_up * 1.4, pev->origin + gpGlobals->v_up * 1.4); + m_pBeam = CBeam::BeamCreate( g_pModelNameLaser, 10 ); + m_pBeam->PointEntInit( vecTmpEnd, entindex() ); m_pBeam->SetColor( 0, 214, 198 ); m_pBeam->SetScrollRate( 255 ); - m_pBeam->SetBrightness( 50 ); + m_pBeam->SetBrightness( 64 ); } @@ -292,9 +288,12 @@ void CTripmineGrenade :: BeamBreakThink( void ) } else { - if (m_hOwner == NULL) bBlowup = 1; - else if (m_posOwner != m_hOwner->pev->origin) bBlowup = 1; - else if (m_angleOwner != m_hOwner->pev->angles) bBlowup = 1; + if (m_hOwner == NULL) + bBlowup = 1; + else if (m_posOwner != m_hOwner->pev->origin) + bBlowup = 1; + else if (m_angleOwner != m_hOwner->pev->angles) + bBlowup = 1; } if (bBlowup) @@ -309,7 +308,7 @@ void CTripmineGrenade :: BeamBreakThink( void ) return; } - SetNextThink( 0.1 ); + pev->nextthink = gpGlobals->time + 0.1; } int CTripmineGrenade :: TakeDamage( entvars_t *pevInflictor, entvars_t *pevAttacker, float flDamage, int bitsDamageType ) @@ -318,8 +317,8 @@ int CTripmineGrenade :: TakeDamage( entvars_t *pevInflictor, entvars_t *pevAttac { // disable // Create( "weapon_tripmine", pev->origin + m_vecDir * 24, pev->angles ); - SetThink(&CTripmineGrenade :: SUB_Remove ); - SetNextThink( 0.1 ); + SetThink( SUB_Remove ); + pev->nextthink = gpGlobals->time + 0.1; KillBeam(); return FALSE; } @@ -336,8 +335,8 @@ void CTripmineGrenade::Killed( entvars_t *pevAttacker, int iGib ) pev->owner = ENT( pevAttacker ); } - SetThink(&CTripmineGrenade:: DelayDeathThink ); - SetNextThink( RANDOM_FLOAT( 0.1, 0.3 ) ); + SetThink( DelayDeathThink ); + pev->nextthink = gpGlobals->time + RANDOM_FLOAT( 0.1, 0.3 ); EMIT_SOUND( ENT(pev), CHAN_BODY, "common/null.wav", 0.5, ATTN_NORM ); // shut off chargeup } @@ -351,16 +350,33 @@ void CTripmineGrenade::DelayDeathThink( void ) Explode( &tr, DMG_BLAST ); } +#endif + LINK_ENTITY_TO_CLASS( weapon_tripmine, CTripmine ); void CTripmine::Spawn( ) { Precache( ); m_iId = WEAPON_TRIPMINE; - SET_MODEL(ENT(pev), "models/w_tripmine.mdl"); + SET_MODEL(ENT(pev), "models/v_tripmine.mdl"); + pev->frame = 0; + pev->body = 3; + pev->sequence = TRIPMINE_GROUND; + // ResetSequenceInfo( ); + pev->framerate = 0; + FallInit();// get ready to fall down m_iDefaultAmmo = TRIPMINE_DEFAULT_GIVE; + +#ifdef CLIENT_DLL + if ( !bIsMultiplayer() ) +#else + if ( !g_pGameRules->IsDeathmatch() ) +#endif + { + UTIL_SetSize(pev, Vector(-16, -16, 0), Vector(16, 16, 28) ); + } } void CTripmine::Precache( void ) @@ -368,6 +384,8 @@ void CTripmine::Precache( void ) PRECACHE_MODEL ("models/v_tripmine.mdl"); PRECACHE_MODEL ("models/p_tripmine.mdl"); UTIL_PrecacheOther( "monster_tripmine" ); + + m_usTripFire = PRECACHE_EVENT( 1, "events/tripfire.sc" ); } int CTripmine::GetItemInfo(ItemInfo *p) @@ -389,23 +407,21 @@ int CTripmine::GetItemInfo(ItemInfo *p) BOOL CTripmine::Deploy( ) { - m_iBody = 0;//show tripmine + //pev->body = 0; return DefaultDeploy( "models/v_tripmine.mdl", "models/p_tripmine.mdl", TRIPMINE_DRAW, "trip" ); } -void CTripmine::Holster( ) +void CTripmine::Holster( int skiplocal /* = 0 */ ) { - //don't play holster animation if ammo is out - if(m_iBody)m_pPlayer->m_flNextAttack = UTIL_WeaponTimeBase(); - else m_pPlayer->m_flNextAttack = UTIL_WeaponTimeBase() + 0.5; + m_pPlayer->m_flNextAttack = UTIL_WeaponTimeBase() + 0.5; if (!m_pPlayer->m_rgAmmo[m_iPrimaryAmmoType]) { // out of mines m_pPlayer->pev->weapons &= ~(1<nextthink = gpGlobals->time + 0.1; } SendWeaponAnim( TRIPMINE_HOLSTER ); @@ -414,7 +430,8 @@ void CTripmine::Holster( ) void CTripmine::PrimaryAttack( void ) { - if (m_pPlayer->m_rgAmmo[m_iPrimaryAmmoType] <= 0) return; + if (m_pPlayer->m_rgAmmo[m_iPrimaryAmmoType] <= 0) + return; UTIL_MakeVectors( m_pPlayer->pev->v_angle + m_pPlayer->pev->punchangle ); Vector vecSrc = m_pPlayer->GetGunPosition( ); @@ -422,51 +439,69 @@ void CTripmine::PrimaryAttack( void ) TraceResult tr; - UTIL_TraceLine( vecSrc, vecSrc + vecAiming * 45, dont_ignore_monsters, ENT( m_pPlayer->pev ), &tr ); + UTIL_TraceLine( vecSrc, vecSrc + vecAiming * 128, dont_ignore_monsters, ENT( m_pPlayer->pev ), &tr ); + + int flags; +#ifdef CLIENT_WEAPONS + flags = FEV_NOTHOST; +#else + flags = 0; +#endif + + PLAYBACK_EVENT_FULL( flags, m_pPlayer->edict(), m_usTripFire, 0.0, (float *)&g_vecZero, (float *)&g_vecZero, 0.0, 0.0, 0, 0, 0, 0 ); if (tr.flFraction < 1.0) { CBaseEntity *pEntity = CBaseEntity::Instance( tr.pHit ); - if (pEntity && !(pEntity->pev->flags & FL_CONVEYOR) ) + if ( pEntity && !(pEntity->pev->flags & FL_CONVEYOR) ) { + Vector angles = UTIL_VecToAngles( tr.vecPlaneNormal ); + + CBaseEntity *pEnt = CBaseEntity::Create( "monster_tripmine", tr.vecEndPos + tr.vecPlaneNormal * 8, angles, m_pPlayer->edict() ); + m_pPlayer->m_rgAmmo[m_iPrimaryAmmoType]--; // player "shoot" animation m_pPlayer->SetAnimation( PLAYER_ATTACK1 ); - m_iBody = 1; - SendWeaponAnim( TRIPMINE_ARM2 ); - - Vector angles = UTIL_VecToAngles( tr.vecPlaneNormal ); - CBaseEntity *pEnt = CBaseEntity::Create( "monster_tripmine", tr.vecEndPos + tr.vecPlaneNormal * 5 + gpGlobals->v_up * -6, angles, m_pPlayer->edict() ); - CTripmineGrenade *pMine = (CTripmineGrenade *)pEnt; + if ( m_pPlayer->m_rgAmmo[m_iPrimaryAmmoType] <= 0 ) + { + // no more mines! + RetireWeapon(); + return; + } + } + else + { + // ALERT( at_console, "no deploy\n" ); } } + else + { + } + m_flNextPrimaryAttack = UTIL_WeaponTimeBase() + 0.3; - m_flTimeWeaponIdle = UTIL_WeaponTimeBase() + RANDOM_FLOAT( 10, 15 ); - m_flTimeUpdate = UTIL_WeaponTimeBase() + RANDOM_FLOAT( 0.5, 1.0 ); //time to deploy next tripmine + m_flTimeWeaponIdle = UTIL_WeaponTimeBase() + UTIL_SharedRandomFloat( m_pPlayer->random_seed, 10, 15 ); } void CTripmine::WeaponIdle( void ) { - if ( m_flTimeUpdate < UTIL_WeaponTimeBase() && m_iBody) + if ( m_flTimeWeaponIdle > UTIL_WeaponTimeBase() ) + return; + + if ( m_pPlayer->m_rgAmmo[m_iPrimaryAmmoType] > 0 ) { - if ( m_pPlayer->m_rgAmmo[m_iPrimaryAmmoType] > 0 ) - { - m_iBody = 0; - SendWeaponAnim( TRIPMINE_DRAW ); - } - else - { - RetireWeapon(); - return; - } + SendWeaponAnim( TRIPMINE_DRAW ); + } + else + { + RetireWeapon(); + return; } - if ( m_flTimeWeaponIdle > UTIL_WeaponTimeBase() )return; int iAnim; - float flRand = RANDOM_FLOAT( 0, 1 ); + float flRand = UTIL_SharedRandomFloat( m_pPlayer->random_seed, 0, 1 ); if (flRand <= 0.25) { iAnim = TRIPMINE_IDLE1; @@ -477,15 +512,15 @@ void CTripmine::WeaponIdle( void ) iAnim = TRIPMINE_IDLE2; m_flTimeWeaponIdle = UTIL_WeaponTimeBase() + 60.0 / 30.0; } - else if (flRand <= 0.85) + else { iAnim = TRIPMINE_FIDGET; m_flTimeWeaponIdle = UTIL_WeaponTimeBase() + 100.0 / 30.0; } - else - { - iAnim = TRIPMINE_ARM1; - m_flTimeWeaponIdle = UTIL_WeaponTimeBase() + 45.0 / 15.0; - } + SendWeaponAnim( iAnim ); -} \ No newline at end of file +} + + + + diff --git a/spirit/turret.cpp b/dlls/turret.cpp similarity index 87% rename from spirit/turret.cpp rename to dlls/turret.cpp index 74f9cdeb..c0ebac91 100644 --- a/spirit/turret.cpp +++ b/dlls/turret.cpp @@ -115,8 +115,6 @@ public: float m_fTurnRate; // actual turn rate int m_iOrientation; // 0 = floor, 1 = Ceiling int m_iOn; - virtual STATE getState() { if (m_iOn) { return STATE_ON; } else { return STATE_OFF; } } - int m_fBeserk; // Sometimes this bitch will just freak out int m_iAutoStart; // true if the turret auto deploys when a target // enters its range @@ -251,7 +249,7 @@ void CBaseTurret::KeyValue( KeyValueData *pkvd ) void CBaseTurret::Spawn() { Precache( ); - SetNextThink( 1 ); + pev->nextthink = gpGlobals->time + 1; pev->movetype = MOVETYPE_FLY; pev->sequence = 0; pev->frame = 0; @@ -259,7 +257,7 @@ void CBaseTurret::Spawn() pev->takedamage = DAMAGE_AIM; SetBits (pev->flags, FL_MONSTER); - SetUse(&CBaseTurret:: TurretUse ); + SetUse( TurretUse ); if (( pev->spawnflags & SF_MONSTER_TURRET_AUTOACTIVATE ) && !( pev->spawnflags & SF_MONSTER_TURRET_STARTINACTIVE )) @@ -267,12 +265,6 @@ void CBaseTurret::Spawn() m_iAutoStart = TRUE; } - if (m_iOrientation == 1) - { - pev->idealpitch = 180; - pev->angles.x = 180; - } - ResetSequenceInfo( ); SetBoneController( 0, 0 ); SetBoneController( 1, 0 ); @@ -302,12 +294,8 @@ void CBaseTurret::Precache( ) void CTurret::Spawn() { Precache( ); - if (pev->model) - SET_MODEL(ENT(pev), STRING(pev->model)); //LRC - else - SET_MODEL(ENT(pev), "models/turret.mdl"); - if (!pev->health) - pev->health = gSkillData.turretHealth; + SET_MODEL(ENT(pev), "models/turret.mdl"); + pev->health = gSkillData.turretHealth; m_HackedGunPos = Vector( 0, 0, 12.75 ); m_flMaxSpin = TURRET_MAXSPIN; pev->view_ofs.z = 12.75; @@ -319,34 +307,27 @@ void CTurret::Spawn() m_iMinPitch = -15; UTIL_SetSize(pev, Vector(-32, -32, -m_iRetractHeight), Vector(32, 32, m_iRetractHeight)); - SetThink(&CTurret::Initialize); + SetThink(Initialize); m_pEyeGlow = CSprite::SpriteCreate( TURRET_GLOW_SPRITE, pev->origin, FALSE ); m_pEyeGlow->SetTransparency( kRenderGlow, 255, 0, 0, 0, kRenderFxNoDissipation ); m_pEyeGlow->SetAttachment( edict(), 2 ); m_eyeBrightness = 0; - SetNextThink( 0.3 ); + pev->nextthink = gpGlobals->time + 0.3; } void CTurret::Precache() { CBaseTurret::Precache( ); - if (pev->model) - PRECACHE_MODEL((char*)STRING(pev->model)); //LRC - else - PRECACHE_MODEL ("models/turret.mdl"); + PRECACHE_MODEL ("models/turret.mdl"); PRECACHE_MODEL (TURRET_GLOW_SPRITE); } void CMiniTurret::Spawn() { Precache( ); - if (pev->model) - SET_MODEL(ENT(pev), STRING(pev->model)); //LRC - else - SET_MODEL(ENT(pev), "models/miniturret.mdl"); - if (!pev->health) + SET_MODEL(ENT(pev), "models/miniturret.mdl"); pev->health = gSkillData.miniturretHealth; m_HackedGunPos = Vector( 0, 0, 12.75 ); m_flMaxSpin = 0; @@ -358,18 +339,15 @@ void CMiniTurret::Spawn() m_iMinPitch = -15; UTIL_SetSize(pev, Vector(-16, -16, -m_iRetractHeight), Vector(16, 16, m_iRetractHeight)); - SetThink(&CMiniTurret::Initialize); - SetNextThink( 0.3 ); + SetThink(Initialize); + pev->nextthink = gpGlobals->time + 0.3; } void CMiniTurret::Precache() { CBaseTurret::Precache( ); - if (pev->model) - PRECACHE_MODEL((char*)STRING(pev->model)); //LRC - else - PRECACHE_MODEL ("models/miniturret.mdl"); + PRECACHE_MODEL ("models/miniturret.mdl"); PRECACHE_SOUND("weapons/hks1.wav"); PRECACHE_SOUND("weapons/hks2.wav"); PRECACHE_SOUND("weapons/hks3.wav"); @@ -389,8 +367,8 @@ void CBaseTurret::Initialize(void) m_flStartYaw = pev->angles.y; if (m_iOrientation == 1) { -// pev->idealpitch = 180; //This is moved to CBaseTurret::Spawn for fix old bug in original HL. G-Cont. -// pev->angles.x = 180; + pev->idealpitch = 180; + pev->angles.x = 180; pev->view_ofs.z = -pev->view_ofs.z; pev->effects |= EF_INVLIGHT; pev->angles.y = pev->angles.y + 180; @@ -403,11 +381,11 @@ void CBaseTurret::Initialize(void) if (m_iAutoStart) { m_flLastSight = gpGlobals->time + m_flMaxWait; - SetThink(&CBaseTurret::AutoSearchThink); - SetNextThink( 0.1 ); + SetThink(AutoSearchThink); + pev->nextthink = gpGlobals->time + .1; } else - SetThink(&CBaseTurret::SUB_DoNothing); + SetThink(SUB_DoNothing); } void CBaseTurret::TurretUse( CBaseEntity *pActivator, CBaseEntity *pCaller, USE_TYPE useType, float value ) @@ -418,14 +396,14 @@ void CBaseTurret::TurretUse( CBaseEntity *pActivator, CBaseEntity *pCaller, USE_ if (m_iOn) { m_hEnemy = NULL; - SetNextThink( 0.1 ); + pev->nextthink = gpGlobals->time + 0.1; m_iAutoStart = FALSE;// switching off a turret disables autostart //!!!! this should spin down first!!BUGBUG - SetThink(&CBaseTurret::Retire); + SetThink(Retire); } else { - SetNextThink( 0.1 ); // turn on delay + pev->nextthink = gpGlobals->time + 0.1; // turn on delay // if the turret is flagged as an autoactivate turret, re-enable it's ability open self. if ( pev->spawnflags & SF_MONSTER_TURRET_AUTOACTIVATE ) @@ -433,7 +411,7 @@ void CBaseTurret::TurretUse( CBaseEntity *pActivator, CBaseEntity *pCaller, USE_ m_iAutoStart = TRUE; } - SetThink(&CBaseTurret::Deploy); + SetThink(Deploy); } } @@ -487,14 +465,14 @@ void CBaseTurret::ActiveThink(void) int fAttack = 0; Vector vecDirToEnemy; - SetNextThink( 0.1 ); + pev->nextthink = gpGlobals->time + 0.1; StudioFrameAdvance( ); if ((!m_iOn) || (m_hEnemy == NULL)) { m_hEnemy = NULL; m_flLastSight = gpGlobals->time + m_flMaxWait; - SetThink(&CBaseTurret::SearchThink); + SetThink(SearchThink); return; } @@ -511,7 +489,7 @@ void CBaseTurret::ActiveThink(void) { m_hEnemy = NULL; m_flLastSight = gpGlobals->time + m_flMaxWait; - SetThink(&CBaseTurret::SearchThink); + SetThink(SearchThink); return; } } @@ -540,7 +518,7 @@ void CBaseTurret::ActiveThink(void) { m_hEnemy = NULL; m_flLastSight = gpGlobals->time + m_flMaxWait; - SetThink(&CBaseTurret::SearchThink); + SetThink(SearchThink); return; } } @@ -662,7 +640,7 @@ void CMiniTurret::Shoot(Vector &vecSrc, Vector &vecDirToEnemy) void CBaseTurret::Deploy(void) { - SetNextThink( 0.1 ); + pev->nextthink = gpGlobals->time + 0.1; StudioFrameAdvance( ); if (pev->sequence != TURRET_ANIM_DEPLOY) @@ -692,7 +670,7 @@ void CBaseTurret::Deploy(void) SetTurretAnim(TURRET_ANIM_SPIN); pev->framerate = 0; - SetThink(&CBaseTurret::SearchThink); + SetThink(SearchThink); } m_flLastSight = gpGlobals->time + m_flMaxWait; @@ -704,7 +682,7 @@ void CBaseTurret::Retire(void) m_vecGoalAngles.x = 0; m_vecGoalAngles.y = m_flStartYaw; - SetNextThink( 0.1 ); + pev->nextthink = gpGlobals->time + 0.1; StudioFrameAdvance( ); @@ -732,11 +710,11 @@ void CBaseTurret::Retire(void) UTIL_SetSize(pev, pev->mins, pev->maxs); if (m_iAutoStart) { - SetThink(&CBaseTurret::AutoSearchThink); - SetNextThink( 0.1 ); + SetThink(AutoSearchThink); + pev->nextthink = gpGlobals->time + .1; } else - SetThink(&CBaseTurret::SUB_DoNothing); + SetThink(SUB_DoNothing); } } else @@ -749,7 +727,7 @@ void CBaseTurret::Retire(void) void CTurret::SpinUpCall(void) { StudioFrameAdvance( ); - SetNextThink( 0.1 ); + pev->nextthink = gpGlobals->time + 0.1; // Are we already spun up? If not start the two stage process. if (!m_iSpin) @@ -758,7 +736,7 @@ void CTurret::SpinUpCall(void) // for the first pass, spin up the the barrel if (!m_iStartSpin) { - SetNextThink( 1.0 ); // spinup delay + pev->nextthink = gpGlobals->time + 1.0; // spinup delay EMIT_SOUND(ENT(pev), CHAN_BODY, "turret/tu_spinup.wav", TURRET_MACHINE_VOLUME, ATTN_NORM); m_iStartSpin = 1; pev->framerate = 0.1; @@ -766,9 +744,9 @@ void CTurret::SpinUpCall(void) // after the barrel is spun up, turn on the hum else if (pev->framerate >= 1.0) { - SetNextThink( 0.1 ); // retarget delay + pev->nextthink = gpGlobals->time + 0.1; // retarget delay EMIT_SOUND(ENT(pev), CHAN_STATIC, "turret/tu_active2.wav", TURRET_MACHINE_VOLUME, ATTN_NORM); - SetThink(&CTurret::ActiveThink); + SetThink(ActiveThink); m_iStartSpin = 0; m_iSpin = 1; } @@ -780,7 +758,7 @@ void CTurret::SpinUpCall(void) if (m_iSpin) { - SetThink(&CTurret::ActiveThink); + SetThink(ActiveThink); } } @@ -851,7 +829,7 @@ void CBaseTurret::SearchThink(void) // ensure rethink SetTurretAnim(TURRET_ANIM_SPIN); StudioFrameAdvance( ); - SetNextThink( 0.1 ); + pev->nextthink = gpGlobals->time + 0.1; if (m_flSpinUpTime == 0 && m_flMaxSpin) m_flSpinUpTime = gpGlobals->time + m_flMaxSpin; @@ -878,7 +856,7 @@ void CBaseTurret::SearchThink(void) { m_flLastSight = 0; m_flSpinUpTime = 0; - SetThink(&CBaseTurret::ActiveThink); + SetThink(ActiveThink); } else { @@ -888,7 +866,7 @@ void CBaseTurret::SearchThink(void) //Before we retrace, make sure that we are spun down. m_flLastSight = 0; m_flSpinUpTime = 0; - SetThink(&CBaseTurret::Retire); + SetThink(Retire); } // should we stop the spin? else if ((m_flSpinUpTime) && (gpGlobals->time > m_flSpinUpTime)) @@ -913,7 +891,7 @@ void CBaseTurret::AutoSearchThink(void) { // ensure rethink StudioFrameAdvance( ); - SetNextThink( 0.3 ); + pev->nextthink = gpGlobals->time + 0.3; // If we have a target and we're still healthy @@ -933,7 +911,7 @@ void CBaseTurret::AutoSearchThink(void) if (m_hEnemy != NULL) { - SetThink(&CBaseTurret::Deploy); + SetThink(Deploy); EMIT_SOUND(ENT(pev), CHAN_BODY, "turret/tu_alert.wav", TURRET_MACHINE_VOLUME, ATTN_NORM); } } @@ -944,7 +922,7 @@ void CBaseTurret :: TurretDeath( void ) BOOL iActive = FALSE; StudioFrameAdvance( ); - SetNextThink( 0.1 ); + pev->nextthink = gpGlobals->time + 0.1; if (pev->deadflag != DEAD_DEAD) { @@ -976,7 +954,7 @@ void CBaseTurret :: TurretDeath( void ) if (pev->dmgtime + RANDOM_FLOAT( 0, 2 ) > gpGlobals->time) { // lots of smoke - MESSAGE_BEGIN( MSG_BROADCAST, gmsgTempEntity ); + MESSAGE_BEGIN( MSG_BROADCAST, SVC_TEMPENTITY ); WRITE_BYTE( TE_SMOKE ); WRITE_COORD( RANDOM_FLOAT( pev->absmin.x, pev->absmax.x ) ); WRITE_COORD( RANDOM_FLOAT( pev->absmin.y, pev->absmax.y ) ); @@ -1047,9 +1025,9 @@ int CBaseTurret::TakeDamage(entvars_t *pevInflictor, entvars_t *pevAttacker, flo ClearBits (pev->flags, FL_MONSTER); // why are they set in the first place??? SetUse(NULL); - SetThink(&CBaseTurret::TurretDeath); + SetThink(TurretDeath); SUB_UseTargets( this, USE_ON, 0 ); // wake up others - SetNextThink( 0.1 ); + pev->nextthink = gpGlobals->time + 0.1; return 0; } @@ -1059,7 +1037,7 @@ int CBaseTurret::TakeDamage(entvars_t *pevInflictor, entvars_t *pevAttacker, flo if (m_iOn && (1 || RANDOM_LONG(0, 0x7FFF) > 800)) { m_fBeserk = 1; - SetThink(&CBaseTurret::SearchThink); + SetThink(SearchThink); } } @@ -1153,7 +1131,6 @@ int CBaseTurret::MoveTurret(void) // int CBaseTurret::Classify ( void ) { - if (m_iClass) return m_iClass; if (m_iOn || m_iAutoStart) return CLASS_MACHINE; return CLASS_NONE; @@ -1183,25 +1160,17 @@ LINK_ENTITY_TO_CLASS( monster_sentry, CSentry ); void CSentry::Precache() { CBaseTurret::Precache( ); - if (pev->model) - PRECACHE_MODEL((char*)STRING(pev->model)); //LRC - else - PRECACHE_MODEL ("models/sentry.mdl"); + PRECACHE_MODEL ("models/sentry.mdl"); } void CSentry::Spawn() { Precache( ); - if (pev->model) - SET_MODEL(ENT(pev), STRING(pev->model)); //LRC - else - SET_MODEL(ENT(pev), "models/sentry.mdl"); - if (!pev->health) //LRC - pev->health = gSkillData.sentryHealth; + SET_MODEL(ENT(pev), "models/sentry.mdl"); + pev->health = gSkillData.sentryHealth; m_HackedGunPos = Vector( 0, 0, 48 ); pev->view_ofs.z = 48; - //m_flMaxWait = 1E6; - if (m_flMaxWait == 0) m_flMaxWait = TURRET_MAXWAIT;//G-Cont. now sentry can deployed + m_flMaxWait = 1E6; m_flMaxSpin = 1E6; CBaseTurret::Spawn(); @@ -1210,9 +1179,9 @@ void CSentry::Spawn() m_iMinPitch = -60; UTIL_SetSize(pev, Vector(-16, -16, -m_iRetractHeight), Vector(16, 16, m_iRetractHeight)); - SetTouch(&CSentry::SentryTouch); - SetThink(&CSentry::Initialize); - SetNextThink( 0.3 ); + SetTouch(SentryTouch); + SetThink(Initialize); + pev->nextthink = gpGlobals->time + 0.3; } void CSentry::Shoot(Vector &vecSrc, Vector &vecDirToEnemy) @@ -1235,9 +1204,9 @@ int CSentry::TakeDamage(entvars_t *pevInflictor, entvars_t *pevAttacker, float f if (!m_iOn) { - SetThink(&CSentry:: Deploy ); + SetThink( Deploy ); SetUse( NULL ); - SetNextThink( 0.1 ); + pev->nextthink = gpGlobals->time + 0.1; } pev->health -= flDamage; @@ -1250,9 +1219,9 @@ int CSentry::TakeDamage(entvars_t *pevInflictor, entvars_t *pevAttacker, float f ClearBits (pev->flags, FL_MONSTER); // why are they set in the first place??? SetUse(NULL); - SetThink(&CSentry::SentryDeath); + SetThink(SentryDeath); SUB_UseTargets( this, USE_ON, 0 ); // wake up others - SetNextThink( 0.1 ); + pev->nextthink = gpGlobals->time + 0.1; return 0; } @@ -1275,7 +1244,7 @@ void CSentry :: SentryDeath( void ) BOOL iActive = FALSE; StudioFrameAdvance( ); - SetNextThink( 0.1 ); + pev->nextthink = gpGlobals->time + 0.1; if (pev->deadflag != DEAD_DEAD) { @@ -1311,7 +1280,7 @@ void CSentry :: SentryDeath( void ) if (pev->dmgtime + RANDOM_FLOAT( 0, 2 ) > gpGlobals->time) { // lots of smoke - MESSAGE_BEGIN( MSG_BROADCAST, gmsgTempEntity ); + MESSAGE_BEGIN( MSG_BROADCAST, SVC_TEMPENTITY ); WRITE_BYTE( TE_SMOKE ); WRITE_COORD( vecSrc.x + RANDOM_FLOAT( -16, 16 ) ); WRITE_COORD( vecSrc.y + RANDOM_FLOAT( -16, 16 ) ); diff --git a/spirit/util.cpp b/dlls/util.cpp similarity index 66% rename from spirit/util.cpp rename to dlls/util.cpp index 14d8f96a..f23f209a 100644 --- a/spirit/util.cpp +++ b/dlls/util.cpp @@ -30,262 +30,14 @@ #include "player.h" #include "weapons.h" #include "gamerules.h" -#include "movewith.h" -#include "locus.h" - -static const float bytedirs[NUMVERTEXNORMALS][3] = -{ -#include "anorms.h" -}; - -//================================= -// string operations -//================================= - -char *COM_FileExtension (char *in) -{ - static char exten[8]; - int i; - - while (*in && *in != '.') - in++; - if (!*in) - return ""; - in++; - for (i=0 ; i<7 && *in ; i++,in++) - exten[i] = *in; - exten[i] = 0; - return exten; -} - -DLL_GLOBAL int DirToBits( const Vector dir ) -{ - int i, best = 0; - float d, bestd = 0; - BOOL normalized = FALSE; - - if( dir == g_vecZero ) - return NUMVERTEXNORMALS; - - if(( dir.x * dir.x + dir.y * dir.y + dir.z * dir.z ) == 1 ) - normalized = TRUE; - - for( i = 0; i < NUMVERTEXNORMALS; i++ ) - { - d = (dir.x * bytedirs[i][0] + dir.y * bytedirs[i][1] + dir.z * bytedirs[i][2] ); - if(( d == 1 ) && normalized ) - return i; - if( d > bestd ) - { - bestd = d; - best = i; - } - } - return best; -} - -char *COM_ParseToken( const char **data ) -{ - char *com_token = COM_Parse( data ); - - // debug -// ALERT( at_console, "ParseToken: %s\n", com_token ); - - return com_token; -} - -//g-cont. new system for safely precaching and set models. Copyright© 2005 XashXT Group. All Rights Reserved. -void SET_MODEL( edict_t *e, string_t s, char *c )//set default model if not found -{ - if (FStringNull( s ))SET_MODEL( e, c ); - else SET_MODEL( e, s ); -} -void SET_MODEL( edict_t *e, string_t model ){ SET_MODEL( e, STRING(model)); } -void SET_MODEL( edict_t *e, const char *model ) -{ - if(!model || !(*model)) - { - g_engfuncs.pfnSetModel(e, "models/null.mdl"); - return; - } - //is this brush model? - if (model[0] == '*') - { - g_engfuncs.pfnSetModel(e, model); - return; - } - - // verify file exists - if( FILE_EXISTS( model )) - { - g_engfuncs.pfnSetModel( e, model ); - return; - } - - char *ext = COM_FileExtension((char *)model); - - if (FStrEq( ext, "mdl")) - { - //this is model - g_engfuncs.pfnSetModel(e, "models/error.mdl"); - } - else if (FStrEq( ext, "spr")) - { - //this is sprite - g_engfuncs.pfnSetModel(e, "sprites/error.spr"); - } - else - { - //set null model - g_engfuncs.pfnSetModel(e, "models/null.mdl"); - } -} - -int PRECACHE_MODEL( string_t s, char *e )//precache default model if not found -{ - if (FStringNull( s )) - return PRECACHE_MODEL( e ); - return PRECACHE_MODEL( s ); -} -int PRECACHE_MODEL( string_t s ){ return PRECACHE_MODEL( (char*)STRING(s)); } -int PRECACHE_MODEL( char* s ) -{ - if(!s || !*s) - { - ALERT(at_console,"Warning: modelname not specified\n"); - return g_sModelIndexNullModel; //set null model - } - //no need to precacahe brush - if (s[0] == '*') return 0; - - // verify file exists - if( FILE_EXISTS( s )) - { - return g_engfuncs.pfnPrecacheModel( s ); - } - - char *ext = COM_FileExtension( s ); - - if (FStrEq( ext, "mdl")) - { - //this is model - ALERT(at_console,"Warning: model \"%s\" not found!\n",s); - return g_sModelIndexErrorModel; - } - else if (FStrEq( ext, "spr")) - { - //this is sprite - ALERT(at_console,"Warning: sprite \"%s\" not found!\n",s); - return g_sModelIndexErrorSprite; - } - else - { - //unknown format - ALERT(at_console,"Warning: invalid name \"%s\"!\n",s); - return g_sModelIndexNullModel; //set null model - } -} - -int PRECACHE_PARTICLE( string_t s, char *e ) -{ - if (FStringNull( s )) - return PRECACHE_PARTICLE( e ); - return PRECACHE_PARTICLE( s ); -} -int PRECACHE_PARTICLE( string_t s ){ return PRECACHE_PARTICLE( (char*)STRING(s)); } -int PRECACHE_PARTICLE( char* s ) -{ - if ( !s || !*s ) - { - ALERT( at_console, "Warning: particlename not specified\n" ); - return MAKE_STRING( "aurora/error.aur" ); // assume error - } - - char *token = NULL; - char *afile = (char *)LOAD_FILE_FOR_ME( s, NULL ); - const char *pfile = afile; - - if( !afile ) - { - // verify file exists - return MAKE_STRING( "aurora/error.aur" ); - } - - token = COM_ParseToken( &pfile ); - - while( token ) - { - if( !stricmp( token, "sprite" )) - { - token = COM_ParseToken( &pfile ); - PRECACHE_MODEL( token ); - } - token = COM_ParseToken( &pfile ); - } - - FREE_FILE( afile ); - return MAKE_STRING( s ); -} - -int PRECACHE_SOUND( string_t s, char *e )//precache default model if not found -{ - if (FStringNull( s )) - return PRECACHE_SOUND( e ); - return PRECACHE_SOUND( s ); -} -int PRECACHE_SOUND( string_t s ){ return PRECACHE_SOUND( (char*)STRING(s)); } -int PRECACHE_SOUND( char* s ) -{ - if(!s || !*s) return g_sSoundIndexNullSound; //set null sound - - //NOTE: Engine function as predicted for sound folder - //But LOAD_FILE_FOR_ME don't known about this. Set it manualy - - char path[256]; //g-cont. - char *sound = s; //sounds from model events can contains a symbol '*'. - //remove this for sucessfully loading a sound - if (sound[0] == '*')sound++; //only for fake path, engine needs this prefix! - sprintf(path, "sound/%s", sound); - - //verify file exists - if (FILE_EXISTS( path )) - { - return g_engfuncs.pfnPrecacheSound(s); - } - - char *ext = COM_FileExtension( s ); - - if (FStrEq( ext, "wav")) - { - //this is sound - ALERT(at_console,"Warning: sound \"%s\" not found!\n",s); - return g_sSoundIndexNullSound; //set null sound - } - else - { - //unknown format - ALERT(at_console,"Warning: invalid name \"%s\"!\n",s); - return g_sSoundIndexNullSound; //set null sound - } -} - -unsigned short PRECACHE_EVENT( int type, const char* psz ) -{ - // NOTE: Xash3D not used .sc files but names - // so no reason to check something - return g_engfuncs.pfnPrecacheEvent( type, psz ); -} float UTIL_WeaponTimeBase( void ) { +#if defined( CLIENT_WEAPONS ) + return 0.0; +#else return gpGlobals->time; -} - -BOOL IsMultiplayer ( void ) -{ - if( g_pGameRules->IsMultiplayer() ) - return TRUE; - return FALSE; +#endif } static unsigned int glSeed = 0; @@ -385,6 +137,26 @@ float UTIL_SharedRandomFloat( unsigned int seed, float low, float high ) } } +void UTIL_ParametricRocket( entvars_t *pev, Vector vecOrigin, Vector vecAngles, edict_t *owner ) +{ + pev->startpos = vecOrigin; + // Trace out line to end pos + TraceResult tr; + UTIL_MakeVectors( vecAngles ); + UTIL_TraceLine( pev->startpos, pev->startpos + gpGlobals->v_forward * 8192, ignore_monsters, owner, &tr); + pev->endpos = tr.vecEndPos; + + // Now compute how long it will take based on current velocity + Vector vecTravel = pev->endpos - pev->startpos; + float travelTime = 0.0; + if ( pev->velocity.Length() > 0 ) + { + travelTime = vecTravel.Length() / pev->velocity.Length(); + } + pev->starttime = gpGlobals->time; + pev->impacttime = gpGlobals->time + travelTime; +} + int g_groupmask = 0; int g_groupop = 0; @@ -440,7 +212,7 @@ TYPEDESCRIPTION gEntvarsDescription[] = DEFINE_ENTITY_FIELD( avelocity, FIELD_VECTOR ), DEFINE_ENTITY_FIELD( punchangle, FIELD_VECTOR ), DEFINE_ENTITY_FIELD( v_angle, FIELD_VECTOR ), - DEFINE_ENTITY_FIELD( fixangle, FIELD_INTEGER ), + DEFINE_ENTITY_FIELD( fixangle, FIELD_FLOAT ), DEFINE_ENTITY_FIELD( idealpitch, FIELD_FLOAT ), DEFINE_ENTITY_FIELD( pitch_speed, FIELD_FLOAT ), DEFINE_ENTITY_FIELD( ideal_yaw, FIELD_FLOAT ), @@ -470,6 +242,7 @@ TYPEDESCRIPTION gEntvarsDescription[] = DEFINE_ENTITY_FIELD( gravity, FIELD_FLOAT ), DEFINE_ENTITY_FIELD( friction, FIELD_FLOAT ), + DEFINE_ENTITY_FIELD( light_level, FIELD_FLOAT ), DEFINE_ENTITY_FIELD( frame, FIELD_FLOAT ), DEFINE_ENTITY_FIELD( scale, FIELD_FLOAT ), @@ -502,7 +275,7 @@ TYPEDESCRIPTION gEntvarsDescription[] = DEFINE_ENTITY_FIELD( groundentity, FIELD_EDICT ), DEFINE_ENTITY_FIELD( spawnflags, FIELD_INTEGER ), - DEFINE_ENTITY_FIELD( flags, FIELD_INTEGER ), + DEFINE_ENTITY_FIELD( flags, FIELD_FLOAT ), DEFINE_ENTITY_FIELD( colormap, FIELD_INTEGER ), DEFINE_ENTITY_FIELD( team, FIELD_INTEGER ), @@ -524,7 +297,6 @@ TYPEDESCRIPTION gEntvarsDescription[] = DEFINE_ENTITY_FIELD( dmg_save, FIELD_FLOAT ), DEFINE_ENTITY_FIELD( dmg, FIELD_FLOAT ), DEFINE_ENTITY_FIELD( dmgtime, FIELD_TIME ), - DEFINE_ENTITY_FIELD( fov, FIELD_FLOAT ), DEFINE_ENTITY_FIELD( noise, FIELD_SOUNDNAME ), DEFINE_ENTITY_FIELD( noise1, FIELD_SOUNDNAME ), @@ -538,15 +310,16 @@ TYPEDESCRIPTION gEntvarsDescription[] = #define ENTVARS_COUNT (sizeof(gEntvarsDescription)/sizeof(gEntvarsDescription[0])) + #ifdef DEBUG edict_t *DBG_EntOfVars( const entvars_t *pev ) { if (pev->pContainingEntity != NULL) return pev->pContainingEntity; - ALERT(at_debug, "entvars_t pContainingEntity is NULL, calling into engine"); + ALERT(at_console, "entvars_t pContainingEntity is NULL, calling into engine"); edict_t* pent = (*g_engfuncs.pfnFindEntityByVars)((entvars_t*)pev); if (pent == NULL) - ALERT(at_debug, "DAMN! Even the engine couldn't FindEntityByVars!"); + ALERT(at_console, "DAMN! Even the engine couldn't FindEntityByVars!"); ((entvars_t *)pev)->pContainingEntity = pent; return pent; } @@ -566,10 +339,10 @@ DBG_AssertFunction( return; char szOut[512]; if (szMessage != NULL) - sprintf(szOut, "ASSERT FAILED:\n %s \n(%s@%d)\n%s\n", szExpr, szFile, szLine, szMessage); + sprintf(szOut, "ASSERT FAILED:\n %s \n(%s@%d)\n%s", szExpr, szFile, szLine, szMessage); else - sprintf(szOut, "ASSERT FAILED:\n %s \n(%s@%d)\n", szExpr, szFile, szLine); - ALERT(at_debug, szOut); + sprintf(szOut, "ASSERT FAILED:\n %s \n(%s@%d)", szExpr, szFile, szLine); + ALERT(at_console, szOut); } #endif // DEBUG @@ -618,36 +391,6 @@ Vector UTIL_VecToAngles( const Vector &vec ) return Vector(rgflVecOut); } -//LRC - pass in a normalised axis vector and a number of degrees, and this returns the corresponding -// angles value for an entity. -inline Vector UTIL_AxisRotationToAngles( const Vector &vecAxis, float flDegs ) -{ - Vector vecTemp = UTIL_AxisRotationToVec( vecAxis, flDegs ); - float rgflVecOut[3]; - //ugh, mathsy. - rgflVecOut[0] = asin(vecTemp.z) * (-180.0 / M_PI); - rgflVecOut[1] = acos(vecTemp.x) * (180.0 / M_PI); - if (vecTemp.y < 0) - rgflVecOut[1] = -rgflVecOut[1]; - rgflVecOut[2] = 0; //for now - return Vector(rgflVecOut); -} - -//LRC - as above, but returns the position of point 1 0 0 under the given rotation -Vector UTIL_AxisRotationToVec( const Vector &vecAxis, float flDegs ) -{ - float rgflVecOut[3]; - float flRads = flDegs * (M_PI / 180.0); - float c = cos(flRads); - float s = sin(flRads); - float v = vecAxis.x * (1-c); - //ugh, more maths. Thank goodness for internet geometry sites... - rgflVecOut[0] = vecAxis.x*v + c; - rgflVecOut[1] = vecAxis.y*v + vecAxis.z*s; - rgflVecOut[2] = vecAxis.z*v - vecAxis.y*s; - return Vector(rgflVecOut); -} - // float UTIL_MoveToOrigin( edict_t *pent, const Vector vecGoal, float flDist, int iMoveType ) void UTIL_MoveToOrigin( edict_t *pent, const Vector &vecGoal, float flDist, int iMoveType ) { @@ -782,31 +525,17 @@ CBaseEntity *UTIL_FindEntityInSphere( CBaseEntity *pStartEntity, const Vector &v CBaseEntity *UTIL_FindEntityByString( CBaseEntity *pStartEntity, const char *szKeyword, const char *szValue ) { edict_t *pentEntity; - CBaseEntity *pEntity; if (pStartEntity) pentEntity = pStartEntity->edict(); else pentEntity = NULL; - for (;;) - { - // Don't change this to use UTIL_FindEntityByString! - pentEntity = FIND_ENTITY_BY_STRING( pentEntity, szKeyword, szValue ); + pentEntity = FIND_ENTITY_BY_STRING( pentEntity, szKeyword, szValue ); - // if pentEntity (the edict) is null, we're at the end of the entities. Give up. - if (FNullEnt(pentEntity)) - { - return NULL; - } - else - { - // ...but if only pEntity (the classptr) is null, we've just got one dud, so we try again. - pEntity = CBaseEntity::Instance(pentEntity); - if (pEntity) - return pEntity; - } - } + if (!FNullEnt(pentEntity)) + return CBaseEntity::Instance(pentEntity); + return NULL; } CBaseEntity *UTIL_FindEntityByClassname( CBaseEntity *pStartEntity, const char *szName ) @@ -814,246 +543,11 @@ CBaseEntity *UTIL_FindEntityByClassname( CBaseEntity *pStartEntity, const char * return UTIL_FindEntityByString( pStartEntity, "classname", szName ); } -#define MAX_ALIASNAME_LEN 80 - -//LRC - things get messed up if aliases change in the middle of an entity traversal. -// so instead, they record what changes should be made, and wait until this function gets -// called. -void UTIL_FlushAliases( void ) -{ -// ALERT(at_console, "Flushing alias list\n"); - if (!g_pWorld) - { - ALERT(at_debug, "FlushAliases has no AliasList!\n"); - return; - } - - while (g_pWorld->m_pFirstAlias) - { - if (g_pWorld->m_pFirstAlias->m_iLFlags & LF_ALIASLIST) - { -// ALERT(at_console, "call FlushChanges for %s \"%s\"\n", STRING(g_pWorld->m_pFirstAlias->pev->classname), STRING(g_pWorld->m_pFirstAlias->pev->targetname)); - g_pWorld->m_pFirstAlias->FlushChanges(); - g_pWorld->m_pFirstAlias->m_iLFlags &= ~LF_ALIASLIST; - } - g_pWorld->m_pFirstAlias = g_pWorld->m_pFirstAlias->m_pNextAlias; - } -} - -void UTIL_AddToAliasList( CBaseAlias *pAlias ) -{ - if (!g_pWorld) - { - ALERT(at_debug, "AddToAliasList has no AliasList!\n"); - return; - } - - pAlias->m_iLFlags |= LF_ALIASLIST; - -// ALERT(at_console, "Adding %s \"%s\" to alias list\n", STRING(pAlias->pev->classname), STRING(pAlias->pev->targetname)); - if (g_pWorld->m_pFirstAlias == NULL) - { - g_pWorld->m_pFirstAlias = pAlias; - pAlias->m_pNextAlias = NULL; - } - else if (g_pWorld->m_pFirstAlias == pAlias) - { - // already in the list - return; - } - else - { - CBaseAlias *pCurrent = g_pWorld->m_pFirstAlias; - while (pCurrent->m_pNextAlias != NULL) - { - if (pCurrent->m_pNextAlias == pAlias) - { - // already in the list - return; - } - pCurrent = pCurrent->m_pNextAlias; - } - pCurrent->m_pNextAlias = pAlias; - pAlias->m_pNextAlias = NULL; - } -} - -// for every alias which has the given name, find the earliest entity which any of them refers to -// and which is later than pStartEntity. -CBaseEntity *UTIL_FollowAliasReference(CBaseEntity *pStartEntity, const char* szValue) -{ - CBaseEntity* pEntity; - CBaseEntity* pBestEntity = NULL; // the entity we're currently planning to return. - int iBestOffset = -1; // the offset of that entity. - CBaseEntity* pTempEntity; - int iTempOffset; - - pEntity = UTIL_FindEntityByTargetname(NULL,szValue); - - while ( pEntity ) - { - if (pEntity->IsAlias()) - { - pTempEntity = ((CBaseAlias*)pEntity)->FollowAlias( pStartEntity ); - if ( pTempEntity ) - { - // We've found an entity; only use it if its offset is lower than the offset we've currently got. - iTempOffset = OFFSET(pTempEntity->pev); - if (iBestOffset == -1 || iTempOffset < iBestOffset) - { - iBestOffset = iTempOffset; - pBestEntity = pTempEntity; - } - } - } - pEntity = UTIL_FindEntityByTargetname(pEntity,szValue); - } - - return pBestEntity; -} - -// for every info_group which has the given groupname, find the earliest entity which is referred to by its member -// with the given membername and which is later than pStartEntity. -CBaseEntity *UTIL_FollowGroupReference(CBaseEntity *pStartEntity, char* szGroupName, char* szMemberName) -{ - CBaseEntity* pEntity; - CBaseEntity* pBestEntity = NULL; // the entity we're currently planning to return. - int iBestOffset = -1; // the offset of that entity. - CBaseEntity* pTempEntity; - int iTempOffset; - char szBuf[MAX_ALIASNAME_LEN]; - char* szThisMember = szMemberName; - char* szTail = NULL; - int iszMemberValue; - int i; - - // find the first '.' in the membername and if there is one, split the string at that point. - for (i = 0; szMemberName[i]; i++) - { - if (szMemberName[i] == '.') - { - // recursive member-reference - // FIXME: we should probably check that i < MAX_ALIASNAME_LEN. - strncpy(szBuf,szMemberName,i); - szBuf[i] = 0; - szTail = &(szMemberName[i+1]); - szThisMember = szBuf; - break; - } - } - - pEntity = UTIL_FindEntityByTargetname(NULL,szGroupName); - while ( pEntity ) - { - if (FStrEq(STRING(pEntity->pev->classname), "info_group")) - { - iszMemberValue = ((CInfoGroup*)pEntity)->GetMember(szThisMember); -// ALERT(at_console,"survived getMember\n"); -// return NULL; - if (!FStringNull(iszMemberValue)) - { - if (szTail) // do we have more references to follow? - pTempEntity = UTIL_FollowGroupReference(pStartEntity, (char*)STRING(iszMemberValue), szTail); - else - pTempEntity = UTIL_FindEntityByTargetname(pStartEntity,STRING(iszMemberValue)); - - if ( pTempEntity ) - { - iTempOffset = OFFSET(pTempEntity->pev); - if (iBestOffset == -1 || iTempOffset < iBestOffset) - { - iBestOffset = iTempOffset; - pBestEntity = pTempEntity; - } - } - } - } - pEntity = UTIL_FindEntityByTargetname(pEntity,szGroupName); - } - - if (pBestEntity) - { -// ALERT(at_console,"\"%s\".\"%s\" returns %s\n",szGroupName,szMemberName,STRING(pBestEntity->pev->targetname)); - return pBestEntity; - } - return NULL; -} - -// Returns the first entity which szName refers to and which is after pStartEntity. -CBaseEntity *UTIL_FollowReference( CBaseEntity *pStartEntity, const char* szName ) -{ - char szRoot[MAX_ALIASNAME_LEN+1]; // allow room for null-terminator - char* szMember; - int i; - CBaseEntity *pResult; - - if (!szName || szName[0] == 0) return NULL; - - // reference through an info_group? - for (i = 0; szName[i]; i++) - { - if (szName[i] == '.') - { - // yes, it looks like a reference through an info_group... - // FIXME: we should probably check that i < MAX_ALIASNAME_LEN. - strncpy(szRoot,szName,i); - szRoot[i] = 0; - szMember = (char*)&szName[i+1]; - //ALERT(at_console,"Following reference- group %s with member %s\n",szRoot,szMember); - pResult = UTIL_FollowGroupReference(pStartEntity, szRoot, szMember); -// if (pResult) -// ALERT(at_console,"\"%s\".\"%s\" = %s\n",szRoot,szMember,STRING(pResult->pev->targetname)); - return pResult; - } - } - // reference through an info_alias? - if ( szName[0] == '*' ) - { - if (FStrEq(szName, "*player")) - { - CBaseEntity* pPlayer = UTIL_FindEntityByClassname(NULL, "player"); - if (pPlayer && (pStartEntity == NULL || pPlayer->eoffset() > pStartEntity->eoffset())) - return pPlayer; - else - return NULL; - } - //ALERT(at_console,"Following alias %s\n",szName+1); - pResult = UTIL_FollowAliasReference( pStartEntity, szName+1 ); -// if (pResult) -// ALERT(at_console,"alias \"%s\" = %s\n",szName+1,STRING(pResult->pev->targetname)); - return pResult; - } - // not a reference -// ALERT(at_console,"%s is not a reference\n",szName); - return NULL; -} - CBaseEntity *UTIL_FindEntityByTargetname( CBaseEntity *pStartEntity, const char *szName ) { - CBaseEntity *pFound = UTIL_FollowReference( pStartEntity, szName ); - if (pFound) - return pFound; - else - return UTIL_FindEntityByString( pStartEntity, "targetname", szName ); + return UTIL_FindEntityByString( pStartEntity, "targetname", szName ); } -CBaseEntity *UTIL_FindEntityByTargetname( CBaseEntity *pStartEntity, const char *szName, CBaseEntity *pActivator ) -{ - if (FStrEq(szName, "*locus")) - { - if (pActivator && (pStartEntity == NULL || pActivator->eoffset() > pStartEntity->eoffset())) - return pActivator; - else - return NULL; - } - else - return UTIL_FindEntityByTargetname( pStartEntity, szName ); -} - -CBaseEntity *UTIL_FindEntityByTarget( CBaseEntity *pStartEntity, const char *szName ) -{ - return UTIL_FindEntityByString( pStartEntity, "target", szName ); -} CBaseEntity *UTIL_FindEntityGeneric( const char *szWhatever, Vector &vecSrc, float flRadius ) { @@ -1177,77 +671,51 @@ static short FixedSigned16( float value, float scale ) // UNDONE: Allow caller to shake clients not ONGROUND? // UNDONE: Fix falloff model (disabled)? // UNDONE: Affect user controls? -//LRC UNDONE: Work during trigger_camera? -//----------------------------------------------------------------------------- -// Compute shake amplitude -//----------------------------------------------------------------------------- -float ComputeShakeAmplitude( const Vector ¢er, const Vector &shakePt, float amplitude, float radius ) +void UTIL_ScreenShake( const Vector ¢er, float amplitude, float frequency, float duration, float radius ) { - if( radius <= 0 ) - return amplitude; - - float localAmplitude = -1; - Vector delta = center - shakePt; - float distance = delta.Length(); - - if( distance <= radius ) - { - // make the amplitude fall off over distance - float flPerc = 1.0 - (distance / radius); - localAmplitude = amplitude * flPerc; - } - - return localAmplitude; -} - -void UTIL_ScreenShake( const Vector ¢er, float amplitude, float frequency, float duration, float radius, ShakeCommand_t eCommand, BOOL bAirShake ) -{ - int i; + int i; float localAmplitude; ScreenShake shake; - shake.command = (unsigned short)eCommand; - shake.duration = FixedUnsigned16( duration, 1<<12 ); // 4.12 fixed + shake.duration = FixedUnsigned16( duration, 1<<12 ); // 4.12 fixed shake.frequency = FixedUnsigned16( frequency, 1<<8 ); // 8.8 fixed - for( i = 1; i <= gpGlobals->maxClients; i++ ) + for ( i = 1; i <= gpGlobals->maxClients; i++ ) { CBaseEntity *pPlayer = UTIL_PlayerByIndex( i ); - // - // Only shake players that are on the ground. - // - if( !pPlayer || (!bAirShake && !FBitSet( pPlayer->pev->flags, FL_ONGROUND ))) - { + if ( !pPlayer || !(pPlayer->pev->flags & FL_ONGROUND) ) // Don't shake if not onground continue; - } - localAmplitude = ComputeShakeAmplitude( center, pPlayer->pev->origin, amplitude, radius ); + localAmplitude = 0; - // This happens if the player is outside the radius, in which case we should ignore - // all commands - if( localAmplitude < 0 ) continue; - - if (( localAmplitude > 0 ) || ( eCommand == SHAKE_STOP )) + if ( radius <= 0 ) + localAmplitude = amplitude; + else { - if ( eCommand == SHAKE_STOP ) localAmplitude = 0; + Vector delta = center - pPlayer->pev->origin; + float distance = delta.Length(); + + // Had to get rid of this falloff - it didn't work well + if ( distance < radius ) + localAmplitude = amplitude;//radius - distance; + } + if ( localAmplitude ) + { + shake.amplitude = FixedUnsigned16( localAmplitude, 1<<12 ); // 4.12 fixed + + MESSAGE_BEGIN( MSG_ONE, gmsgShake, NULL, pPlayer->edict() ); // use the magic #1 for "one client" + + WRITE_SHORT( shake.amplitude ); // shake amount + WRITE_SHORT( shake.duration ); // shake lasts this long + WRITE_SHORT( shake.frequency ); // shake noise frequency - shake.amplitude = FixedUnsigned16( localAmplitude, 1<<12 ); // 4.12 fixed - - MESSAGE_BEGIN( MSG_ONE, gmsgShake, NULL, pPlayer->edict() ); - WRITE_SHORT( shake.command ); // shake command (SHAKE_START, STOP, FREQUENCY, AMPLITUDE) - WRITE_SHORT( shake.amplitude ); // shake magnitude/amplitude - WRITE_SHORT( shake.duration ); // shake lasts this long - WRITE_SHORT( shake.frequency ); // shake noise frequency MESSAGE_END(); } } } -void UTIL_ScreenShake( const Vector ¢er, float amplitude, float frequency, float duration, float radius ) -{ - UTIL_ScreenShake( center, amplitude, frequency, duration, radius, SHAKE_START, FALSE ); -} + void UTIL_ScreenShakeAll( const Vector ¢er, float amplitude, float frequency, float duration ) { @@ -1276,11 +744,11 @@ void UTIL_ScreenFadeWrite( const ScreenFade &fade, CBaseEntity *pEntity ) WRITE_SHORT( fade.duration ); // fade lasts this long WRITE_SHORT( fade.holdTime ); // fade lasts this long - WRITE_SHORT( fade.fadeFlags ); // fade type (in / out) - WRITE_BYTE( fade.r ); // fade red - WRITE_BYTE( fade.g ); // fade green - WRITE_BYTE( fade.b ); // fade blue - WRITE_BYTE( fade.a ); // fade blue + WRITE_SHORT( fade.fadeFlags ); // fade type (in / out) + WRITE_BYTE( fade.r ); // fade red + WRITE_BYTE( fade.g ); // fade green + WRITE_BYTE( fade.b ); // fade blue + WRITE_BYTE( fade.a ); // fade blue MESSAGE_END(); } @@ -1291,11 +759,13 @@ void UTIL_ScreenFadeAll( const Vector &color, float fadeTime, float fadeHold, in int i; ScreenFade fade; + UTIL_ScreenFadeBuild( fade, color, fadeTime, fadeHold, alpha, flags ); for ( i = 1; i <= gpGlobals->maxClients; i++ ) { CBaseEntity *pPlayer = UTIL_PlayerByIndex( i ); + UTIL_ScreenFadeWrite( fade, pPlayer ); } } @@ -1315,7 +785,7 @@ void UTIL_HudMessage( CBaseEntity *pEntity, const hudtextparms_t &textparms, con if ( !pEntity || !pEntity->IsNetClient() ) return; - MESSAGE_BEGIN( MSG_ONE, gmsgTempEntity, NULL, pEntity->edict() ); + MESSAGE_BEGIN( MSG_ONE, SVC_TEMPENTITY, NULL, pEntity->edict() ); WRITE_BYTE( TE_TEXTMESSAGE ); WRITE_BYTE( textparms.channel & 0xFF ); @@ -1477,46 +947,6 @@ void UTIL_ShowMessageAll( const char *pString ) } } -void UTIL_SetFogAll( Vector color, int iFadeTime, int iStartDist, int iEndDist ) -{ - // loop through all players - if( IsMultiplayer( )) - { - for ( int i = 0; i < gpGlobals->maxClients; i++ ) - UTIL_SetFog( color, iFadeTime, iStartDist, iEndDist, i+1 ); - } - else UTIL_SetFog( color, iFadeTime, iStartDist, iEndDist ); -} - -void UTIL_SetFog( Vector color, int iFadeTime, int iStartDist, int iEndDist, int playernum ) -{ - CBasePlayer *pPlayer = (CBasePlayer*)UTIL_PlayerByIndex( playernum ); - - if ( pPlayer ) - { - if( pPlayer->m_FogFadeTime != 0 ) return;//fading in progress !!!TODO: make smooth re-fading - if( IsMultiplayer( )) iFadeTime = 0; // disable fading in multiplayer - - if( iFadeTime > 0 ) - { - pPlayer->m_iFogEndDist = FOG_HARDWARE_LIMIT; - pPlayer->m_iFogFinalEndDist = iEndDist; - } - else if( iFadeTime < 0 ) - { - pPlayer->m_iFogEndDist = iEndDist; - pPlayer->m_iFogFinalEndDist = iEndDist; - } - else pPlayer->m_iFogEndDist = iEndDist; - - pPlayer->m_iFogStartDist = iStartDist; - pPlayer->m_FogColor = color; - pPlayer->m_FogFadeTime = iFadeTime; - pPlayer->m_flStartTime = gpGlobals->time; - pPlayer->fogNeedsUpdate = TRUE; - } -} - // Overloaded to add IGNORE_GLASS void UTIL_TraceLine( const Vector &vecStart, const Vector &vecEnd, IGNORE_MONSTERS igmon, IGNORE_GLASS ignoreGlass, edict_t *pentIgnore, TraceResult *ptr ) { @@ -1537,8 +967,7 @@ void UTIL_TraceHull( const Vector &vecStart, const Vector &vecEnd, IGNORE_MONSTE void UTIL_TraceModel( const Vector &vecStart, const Vector &vecEnd, int hullNumber, edict_t *pentModel, TraceResult *ptr ) { - // NOTE: actual mins\maxs will be set automatically - g_engfuncs.pfnTraceModel( vecStart, vecEnd, pentModel, ptr ); + g_engfuncs.pfnTraceModel( vecStart, vecEnd, hullNumber, pentModel, ptr ); } @@ -1571,20 +1000,10 @@ float UTIL_VecToYaw( const Vector &vec ) return VEC_TO_YAW(vec); } -void UTIL_SetEdictOrigin( edict_t *pEdict, const Vector &vecOrigin ) -{ - SET_ORIGIN(pEdict, vecOrigin ); -} -// 'links' the entity into the world -void UTIL_SetOrigin( CBaseEntity *pEntity, const Vector &vecOrigin ) +void UTIL_SetOrigin( entvars_t *pev, const Vector &vecOrigin ) { - SET_ORIGIN(ENT(pEntity->pev), vecOrigin ); -} - -void UTIL_SetAngles( CBaseEntity *pEntity, const Vector &vecAngles ) -{ - pEntity->pev->angles = vecAngles; + SET_ORIGIN(ENT(pev), vecOrigin ); } void UTIL_ParticleEffect( const Vector &vecOrigin, const Vector &vecDirection, ULONG ulColor, ULONG ulCount ) @@ -1592,6 +1011,7 @@ void UTIL_ParticleEffect( const Vector &vecOrigin, const Vector &vecDirection, U PARTICLE_EFFECT( vecOrigin, vecDirection, (float)ulColor, (float)ulCount ); } + float UTIL_Approach( float target, float value, float speed ) { float delta = target - value; @@ -1638,10 +1058,9 @@ float UTIL_AngleDistance( float next, float cur ) { float delta = next - cur; -// LRC- correct for deltas > 360 - while ( delta < -180 ) + if ( delta < -180 ) delta += 360; - while ( delta > 180 ) + else if ( delta > 180 ) delta -= 360; return delta; @@ -1677,73 +1096,24 @@ Vector UTIL_GetAimVector( edict_t *pent, float flSpeed ) return tmp; } -BOOL UTIL_IsMasterTriggered(string_t iszMaster, CBaseEntity *pActivator) +int UTIL_IsMasterTriggered(string_t sMaster, CBaseEntity *pActivator) { - int i, j, found = false; - const char *szMaster; - char szBuf[80]; - CBaseEntity *pMaster; - int reverse = false; - - - if (iszMaster) + if (sMaster) { -// ALERT(at_console, "IsMasterTriggered(%s, %s \"%s\")\n", STRING(iszMaster), STRING(pActivator->pev->classname), STRING(pActivator->pev->targetname)); - szMaster = STRING(iszMaster); - if (szMaster[0] == '~') //inverse master + edict_t *pentTarget = FIND_ENTITY_BY_TARGETNAME(NULL, STRING(sMaster)); + + if ( !FNullEnt(pentTarget) ) { - reverse = true; - szMaster++; + CBaseEntity *pMaster = CBaseEntity::Instance(pentTarget); + if ( pMaster && (pMaster->ObjectCaps() & FCAP_MASTER) ) + return pMaster->IsTriggered( pActivator ); } - pMaster = UTIL_FindEntityByTargetname( NULL, szMaster ); - if ( !pMaster ) - { - for (i = 0; szMaster[i]; i++) - { - if (szMaster[i] == '(') - { - for (j = i+1; szMaster[j]; j++) - { - if (szMaster[j] == ')') - { - strncpy(szBuf, szMaster+i+1, (j-i)-1); - szBuf[(j-i)-1] = 0; - pActivator = UTIL_FindEntityByTargetname( NULL, szBuf ); - found = true; - break; - } - } - if (!found) // no ) found - { - ALERT(at_error, "Missing ')' in master \"%s\"\n", szMaster); - return FALSE; - } - break; - } - } - if (!found) // no ( found - { - ALERT(at_debug, "Master \"%s\" not found!\n",szMaster); - return TRUE; - } - - strncpy(szBuf, szMaster, i); - szBuf[i] = 0; - pMaster = UTIL_FindEntityByTargetname( NULL, szBuf ); - } - - if (pMaster) - { - if (reverse) - return (pMaster->GetState( pActivator ) != STATE_ON); - else - return (pMaster->GetState( pActivator ) == STATE_ON); - } + ALERT(at_console, "Master was null or not a master!\n"); } - // if the entity has no master (or the master is missing), just say yes. - return TRUE; + // if this isn't a master entity, just say yes. + return 1; } BOOL UTIL_ShouldShowBlood( int color ) @@ -1778,7 +1148,7 @@ void UTIL_BloodStream( const Vector &origin, const Vector &direction, int color, color = 0; - MESSAGE_BEGIN( MSG_PVS, gmsgTempEntity, origin ); + MESSAGE_BEGIN( MSG_PVS, SVC_TEMPENTITY, origin ); WRITE_BYTE( TE_BLOODSTREAM ); WRITE_COORD( origin.x ); WRITE_COORD( origin.y ); @@ -1811,7 +1181,7 @@ void UTIL_BloodDrips( const Vector &origin, const Vector &direction, int color, if ( amount > 255 ) amount = 255; - MESSAGE_BEGIN( MSG_PVS, gmsgTempEntity, origin ); + MESSAGE_BEGIN( MSG_PVS, SVC_TEMPENTITY, origin ); WRITE_BYTE( TE_BLOODSPRITE ); WRITE_COORD( origin.x); // pos WRITE_COORD( origin.y); @@ -1893,8 +1263,8 @@ void UTIL_DecalTrace( TraceResult *pTrace, int decalNumber ) index -= 256; } } - - MESSAGE_BEGIN( MSG_BROADCAST, gmsgTempEntity ); + + MESSAGE_BEGIN( MSG_BROADCAST, SVC_TEMPENTITY ); WRITE_BYTE( message ); WRITE_COORD( pTrace->vecEndPos.x ); WRITE_COORD( pTrace->vecEndPos.y ); @@ -1933,7 +1303,7 @@ void UTIL_PlayerDecalTrace( TraceResult *pTrace, int playernum, int decalNumber, if (pTrace->flFraction == 1.0) return; - MESSAGE_BEGIN( MSG_BROADCAST, gmsgTempEntity ); + MESSAGE_BEGIN( MSG_BROADCAST, SVC_TEMPENTITY ); WRITE_BYTE( TE_PLAYERDECAL ); WRITE_BYTE ( playernum ); WRITE_COORD( pTrace->vecEndPos.x ); @@ -1950,11 +1320,13 @@ void UTIL_GunshotDecalTrace( TraceResult *pTrace, int decalNumber ) return; int index = gDecals[ decalNumber ].index; - if ( index < 0 ) return; + if ( index < 0 ) + return; - if (pTrace->flFraction == 1.0) return; + if (pTrace->flFraction == 1.0) + return; - MESSAGE_BEGIN( MSG_PAS, gmsgTempEntity, pTrace->vecEndPos ); + MESSAGE_BEGIN( MSG_PAS, SVC_TEMPENTITY, pTrace->vecEndPos ); WRITE_BYTE( TE_GUNSHOTDECAL ); WRITE_COORD( pTrace->vecEndPos.x ); WRITE_COORD( pTrace->vecEndPos.y ); @@ -1967,7 +1339,7 @@ void UTIL_GunshotDecalTrace( TraceResult *pTrace, int decalNumber ) void UTIL_Sparks( const Vector &position ) { - MESSAGE_BEGIN( MSG_PVS, gmsgTempEntity, position ); + MESSAGE_BEGIN( MSG_PVS, SVC_TEMPENTITY, position ); WRITE_BYTE( TE_SPARKS ); WRITE_COORD( position.x ); WRITE_COORD( position.y ); @@ -1978,7 +1350,7 @@ void UTIL_Sparks( const Vector &position ) void UTIL_Ricochet( const Vector &position, float scale ) { - MESSAGE_BEGIN( MSG_PVS, gmsgTempEntity, position ); + MESSAGE_BEGIN( MSG_PVS, SVC_TEMPENTITY, position ); WRITE_BYTE( TE_ARMOR_RICOCHET ); WRITE_COORD( position.x ); WRITE_COORD( position.y ); @@ -2004,23 +1376,6 @@ BOOL UTIL_TeamsMatch( const char *pTeamName1, const char *pTeamName2 ) return FALSE; } -//LRC - moved here from barney.cpp -BOOL UTIL_IsFacing( entvars_t *pevTest, const Vector &reference ) -{ - Vector vecDir = (reference - pevTest->origin); - vecDir.z = 0; - vecDir = vecDir.Normalize(); - Vector forward, angle; - angle = pevTest->v_angle; - angle.x = 0; - UTIL_MakeVectorsPrivate( angle, forward, NULL, NULL ); - // He's facing me, he meant it - if ( DotProduct( forward, vecDir ) > 0.96 ) // +/- 15 degrees or so - { - return TRUE; - } - return FALSE; -} void UTIL_StringToVector( float *pVector, const char *pString ) { @@ -2053,50 +1408,6 @@ void UTIL_StringToVector( float *pVector, const char *pString ) } -//LRC - randomized vectors of the form "0 0 0 .. 1 0 0" -void UTIL_StringToRandomVector( float *pVector, const char *pString ) -{ - char *pstr, *pfront, tempString[128]; - int j; - float pAltVec[3]; - - strcpy( tempString, pString ); - pstr = pfront = tempString; - - for ( j = 0; j < 3; j++ ) // lifted from pr_edict.c - { - pVector[j] = atof( pfront ); - - while ( *pstr && *pstr != ' ' ) pstr++; - if (!*pstr) break; - pstr++; - pfront = pstr; - } - if (j < 2) - { - /* - ALERT( at_error, "Bad field in entity!! %s:%s == \"%s\"\n", - pkvd->szClassName, pkvd->szKeyName, pkvd->szValue ); - */ - for (j = j+1;j < 3; j++) - pVector[j] = 0; - } - else if (*pstr == '.') - { - pstr++; - if (*pstr != '.') return; - pstr++; - if (*pstr != ' ') return; - - UTIL_StringToVector(pAltVec, pstr); - - pVector[0] = RANDOM_FLOAT( pVector[0], pAltVec[0] ); - pVector[1] = RANDOM_FLOAT( pVector[1], pAltVec[1] ); - pVector[2] = RANDOM_FLOAT( pVector[2], pAltVec[2] ); - } -} - - void UTIL_StringToIntArray( int *pVector, int count, const char *pString ) { char *pstr, *pfront, tempString[128]; @@ -2192,7 +1503,7 @@ void UTIL_Bubbles( Vector mins, Vector maxs, int count ) float flHeight = UTIL_WaterLevel( mid, mid.z, mid.z + 1024 ); flHeight = flHeight - mins.z; - MESSAGE_BEGIN( MSG_PAS, gmsgTempEntity, mid ); + MESSAGE_BEGIN( MSG_PAS, SVC_TEMPENTITY, mid ); WRITE_BYTE( TE_BUBBLES ); WRITE_COORD( mins.x ); // mins WRITE_COORD( mins.y ); @@ -2226,7 +1537,7 @@ void UTIL_BubbleTrail( Vector from, Vector to, int count ) if (count > 255) count = 255; - MESSAGE_BEGIN( MSG_BROADCAST, gmsgTempEntity ); + MESSAGE_BEGIN( MSG_BROADCAST, SVC_TEMPENTITY ); WRITE_BYTE( TE_BUBBLETRAIL ); WRITE_COORD( from.x ); // mins WRITE_COORD( from.y ); @@ -2260,6 +1571,7 @@ BOOL UTIL_IsValidEntity( edict_t *pent ) return TRUE; } + void UTIL_PrecacheOther( const char *szClassname ) { edict_t *pent; @@ -2267,7 +1579,7 @@ void UTIL_PrecacheOther( const char *szClassname ) pent = CREATE_NAMED_ENTITY( MAKE_STRING( szClassname ) ); if ( FNullEnt( pent ) ) { - ALERT ( at_debug, "NULL Ent in UTIL_PrecacheOther\n" ); + ALERT ( at_console, "NULL Ent in UTIL_PrecacheOther\n" ); return; } @@ -2325,36 +1637,6 @@ void UTIL_StripToken( const char *pKey, char *pDest ) } -char* GetStringForUseType( USE_TYPE useType ) -{ - switch(useType) - { - case USE_ON: return "USE_ON"; - case USE_OFF: return "USE_OFF"; - case USE_SET: return "USE_SET"; - case USE_KILL: return "USE_KILL"; - case USE_TOGGLE: return "USE_TOGGLE"; - case USE_SAME: return "USE_SAME"; - case USE_NOT: return "USE_NOT"; - default: return "USE_UNKNOWN!?"; - } -} - -char* GetStringForState( STATE state ) -{ - switch(state) - { - case STATE_ON: return "ON"; - case STATE_OFF: return "OFF"; - case STATE_TURN_ON: return "TURN ON"; - case STATE_TURN_OFF: return "TURN OFF"; - case STATE_IN_USE: return "IN USE"; - default: - return "STATE_UNKNOWN!?"; - } -} - - // -------------------------------------------------------------- // // CSave @@ -2382,27 +1664,6 @@ static int gSizes[FIELD_TYPECOUNT] = sizeof(int), // FIELD_SOUNDNAME }; -static const char *gNames[FIELD_TYPECOUNT] = -{ - "float", // FIELD_FLOAT - "string", // FIELD_STRING - "entity", // FIELD_ENTITY - "classptr", // FIELD_CLASSPTR - "ehandle", // FIELD_EHANDLE - "entvars", // FIELD_entvars_t - "edict", // FIELD_EDICT - "vector", // FIELD_VECTOR - "position vector", // FIELD_POSITION_VECTOR - "pointer", // FIELD_POINTER - "integer", // FIELD_INTEGER - "function", // FIELD_FUNCTION - "boolean", // FIELD_BOOLEAN - "short", // FIELD_SHORT - "char", // FIELD_CHARACTER - "time", // FIELD_TIME - "modelname", // FIELD_MODELNAME - "soundname", // FIELD_SOUNDNAME -}; // Base class includes common SAVERESTOREDATA pointer, and manages the entity table CSaveRestoreBuffer :: CSaveRestoreBuffer( void ) @@ -2549,7 +1810,7 @@ unsigned short CSaveRestoreBuffer :: TokenHash( const char *pszToken ) for ( int i=0; itokenCount; i++ ) { #if _DEBUG - static BOOL beentheredonethat = FALSE; + static int beentheredonethat = FALSE; if ( i > 50 && !beentheredonethat ) { beentheredonethat = TRUE; @@ -2704,7 +1965,7 @@ void CSave :: WritePositionVector( const char *pname, const float *value, int co } -void CSave :: WriteFunction( const char* cname, const char *pname, const int *data, int count ) +void CSave :: WriteFunction( const char *pname, const int *data, int count ) { const char *functionName; @@ -2712,7 +1973,7 @@ void CSave :: WriteFunction( const char* cname, const char *pname, const int *da if ( functionName ) BufferField( pname, strlen(functionName) + 1, functionName ); else - ALERT( at_error, "Member \"%s\" of \"%s\" contains an invalid function pointer %p!", pname, cname, *data ); + ALERT( at_error, "Invalid function pointer in entity!" ); } @@ -2768,15 +2029,12 @@ void EntvarsKeyvalue( entvars_t *pev, KeyValueData *pkvd ) int CSave :: WriteEntVars( const char *pname, entvars_t *pev ) { - if (pev->targetname) - return WriteFields( STRING(pev->targetname), pname, pev, gEntvarsDescription, ENTVARS_COUNT ); - else - return WriteFields( STRING(pev->classname), pname, pev, gEntvarsDescription, ENTVARS_COUNT ); + return WriteFields( pname, pev, gEntvarsDescription, ENTVARS_COUNT ); } -int CSave :: WriteFields( const char *cname, const char *pname, void *pBaseData, TYPEDESCRIPTION *pFields, int fieldCount ) +int CSave :: WriteFields( const char *pname, void *pBaseData, TYPEDESCRIPTION *pFields, int fieldCount ) { int i, j, actualCount, emptyCount; TYPEDESCRIPTION *pTest; @@ -2875,7 +2133,7 @@ int CSave :: WriteFields( const char *cname, const char *pname, void *pBaseData, break; case FIELD_FUNCTION: - WriteFunction( cname, pTest->fieldName, (int *)(char *)pOutputData, pTest->fieldSize ); + WriteFunction( pTest->fieldName, (int *)(char *)pOutputData, pTest->fieldSize ); break; default: ALERT( at_error, "Bad field type\n" ); @@ -3036,10 +2294,7 @@ int CRestore::ReadField( void *pBaseData, TYPEDESCRIPTION *pFields, int fieldCou if ( pent ) *((CBaseEntity **)pOutputData) = CBaseEntity::Instance(pent); else - { *((CBaseEntity **)pOutputData) = NULL; - if (entityIndex != -1) ALERT(at_console, "## Restore: invalid entitynum %d\n", entityIndex); - } break; case FIELD_EDICT: entityIndex = *( int *)pInputData; @@ -3106,7 +2361,7 @@ int CRestore::ReadField( void *pBaseData, TYPEDESCRIPTION *pFields, int fieldCou #if 0 else { - ALERT( at_debug, "Skipping global field %s\n", pName ); + ALERT( at_console, "Skipping global field %s\n", pName ); } #endif return fieldNumber; @@ -3137,13 +2392,13 @@ int CRestore::ReadFields( const char *pname, void *pBaseData, TYPEDESCRIPTION *p // Check the struct name if ( token != TokenHash(pname) ) // Field Set marker { - ALERT( at_error, "Expected %s found %s!\n", pname, BufferPointer() ); - BufferRewind( 2 * sizeof( short )); +// ALERT( at_error, "Expected %s found %s!\n", pname, BufferPointer() ); + BufferRewind( 2*sizeof(short) ); return 0; } // Skip over the struct name - fileCount = ReadInt(); // Read field count + fileCount = ReadInt(); // Read field count lastField = 0; // Make searches faster, most data is read/written in the same order @@ -3286,36 +2541,3 @@ int CRestore::BufferCheckZString( const char *string ) return 0; } -//for trigger_viewset -int HaveCamerasInPVS( edict_t* edict ) -{ - CBaseEntity *pViewEnt = NULL; - for ( int i = 1; i <= gpGlobals->maxClients; i++ ) - { - CBaseEntity *pEntity = UTIL_PlayerByIndex( i ); - if (!pEntity) continue; - CBasePlayer *pPlayer = (CBasePlayer *)pEntity; - if (pPlayer && pPlayer->viewFlags & 1) // custom view active - { - CBaseEntity *pViewEnt = UTIL_FindEntityByTargetname(NULL,STRING(pPlayer->viewEntity)); - if (FNullEnt(pViewEnt)) - { - ALERT(at_error, "bad entity string in CamerasInPVS\n"); - return 0; - } - edict_t *view = pViewEnt->edict(); - edict_t *pent = UTIL_EntitiesInPVS( edict ); - - while ( !FNullEnt( pent ) ) - { - if (pent == view) - { - // ALERT(at_console, "CamerasInPVS found camera named %s\n", STRING(pPlayer->viewEntity)); - return TRUE; - } - pent = pent->v.chain; - } - } - } - return 0; -} \ No newline at end of file diff --git a/spirit/util.h b/dlls/util.h similarity index 70% rename from spirit/util.h rename to dlls/util.h index cb7ffa96..f4c5c277 100644 --- a/spirit/util.h +++ b/dlls/util.h @@ -15,43 +15,35 @@ // // Misc utility code // -#ifndef UTIL_H -#define UTIL_H - -#include - -#include "te_shared.h" -#include "shake.h" -#include "game_shared.h" +#ifndef ACTIVITY_H #include "activity.h" +#endif + +#ifndef ENGINECALLBACK_H #include "enginecallback.h" -#include "event_flags.h" - -#define FOG_HARDWARE_LIMIT 8192 - -//g-cont. safe precaching models & sounds -int PRECACHE_MODEL( char* s ); // classic precache -int PRECACHE_MODEL( string_t s ); // pev->model as argument -int PRECACHE_MODEL( string_t s, char *e ); // custom model precache - -int PRECACHE_PARTICLE( char* s ); // classic precache -int PRECACHE_PARTICLE( string_t s ); // pev->message as argument -int PRECACHE_PARTICLE( string_t s, char *e ); // custom particle precache - -int PRECACHE_SOUND( char* s ); // classic precache -int PRECACHE_SOUND( string_t s ); // pev->noise as argument -int PRECACHE_SOUND( string_t s, char *e ); // custom sound precache - -void SET_MODEL( edict_t *e, string_t model ); // pev->model as argument -void SET_MODEL( edict_t *e, const char *model ); // classic set model -void SET_MODEL( edict_t *e, string_t s, char *c );// custom model set - -unsigned short PRECACHE_EVENT( int type, const char *psz ); - +#endif inline void MESSAGE_BEGIN( int msg_dest, int msg_type, const float *pOrigin, entvars_t *ent ); // implementation later in this file extern globalvars_t *gpGlobals; +// defaulting to use StringTable +// #define SYS_SHAREDSTRINGS + +#ifdef SYS_SHAREDSTRINGS + + // Use this instead of ALLOC_STRING on constant strings + #define STRING( offset ) (const char *)(gpGlobals->pStringBase + (int)offset) + #define MAKE_STRING(str) ((int)str - (int)STRING(0)) + +#else + // NOTE: Xash3D have a StringTable system which compress strings in memory + // and not produce duplicated strings. If we want to use this system we must + // reset pStringBase in the GameDllInit (see game.cpp for details) + #define STRING (*g_engfuncs.pfnSzFromIndex) + #define MAKE_STRING ALLOC_STRING + +#endif + inline edict_t *FIND_ENTITY_BY_CLASSNAME(edict_t *entStart, const char *pszName) { return FIND_ENTITY_BY_STRING(entStart, "classname", pszName); @@ -68,10 +60,6 @@ inline edict_t *FIND_ENTITY_BY_TARGET(edict_t *entStart, const char *pszName) return FIND_ENTITY_BY_STRING(entStart, "target", pszName); } -extern int DirToBits( const Vector dir ); - -char *COM_ParseToken( const char **data ); - // Keeps clutter down a bit, when writing key-value pairs #define WRITEKEY_INT(pf, szKeyName, iKeyValue) ENGINE_FPRINTF(pf, "\"%s\" \"%d\"\n", szKeyName, iKeyValue) #define WRITEKEY_FLOAT(pf, szKeyName, flKeyValue) \ @@ -100,6 +88,9 @@ typedef int EOFFSET; // In case it's not alread defined typedef int BOOL; +// In case this ever changes +#define M_PI 3.14159265358979323846 + // Keeps clutter down a bit, when declaring external entity/global method prototypes #define DECLARE_GLOBAL_METHOD(MethodName) extern void DLLEXPORT MethodName( void ) #define GLOBAL_METHOD(funcname) void DLLEXPORT funcname(void) @@ -162,22 +153,17 @@ inline void MESSAGE_BEGIN( int msg_dest, int msg_type, const float *pOrigin, ent } // Testing the three types of "entity" for nullity -//LRC- four types, rather; see cbase.h #define eoNullEntity 0 -inline BOOL FNullEnt(EOFFSET eoffset) { return eoffset == 0; } +inline BOOL FNullEnt(EOFFSET eoffset) { return eoffset == 0; } inline BOOL FNullEnt(const edict_t* pent) { return pent == NULL || FNullEnt(OFFSET(pent)); } -inline BOOL FNullEnt(entvars_t* pev) { return pev == NULL || FNullEnt(OFFSET(pev)); } +inline BOOL FNullEnt(entvars_t* pev) { return pev == NULL || FNullEnt(OFFSET(pev)); } // Testing strings for nullity #define iStringNull 0 -inline BOOL FStringNull(int iString) { return iString == iStringNull; } -inline BOOL FStringNull(char *string) { return strlen(string) - 1; } +inline BOOL FStringNull(int iString) { return iString == iStringNull; } #define cchMapNameMost 32 -// Goes into globalvars_t.trace_flags -#define FTRACE_SIMPLEBOX (1<<0) // Traceline with a simple box - // Dot products for view cone checking #define VIEW_FIELD_FULL (float)-1.0 // +-180 degrees #define VIEW_FIELD_WIDE (float)-0.7 // +-135 degrees 0.1 // +-85 degrees, used for full FOV checks @@ -190,38 +176,9 @@ inline BOOL FStringNull(char *string) { return strlen(string) - 1; } #define BLOOD_COLOR_YELLOW (BYTE)195 #define BLOOD_COLOR_GREEN BLOOD_COLOR_YELLOW -// Break Model Defines - -#define BREAK_TYPEMASK 0x4F -#define BREAK_GLASS 0x01 -#define BREAK_METAL 0x02 -#define BREAK_FLESH 0x04 -#define BREAK_WOOD 0x08 - -#define BREAK_SMOKE 0x10 -#define BREAK_TRANS 0x20 -#define BREAK_CONCRETE 0x40 -#define BREAK_2 0x80 - -// Colliding temp entity sounds -#define BOUNCE_GLASS BREAK_GLASS -#define BOUNCE_METAL BREAK_METAL -#define BOUNCE_FLESH BREAK_FLESH -#define BOUNCE_WOOD BREAK_WOOD -#define BOUNCE_SHRAP 0x10 -#define BOUNCE_SHELL 0x20 -#define BOUNCE_CONCRETE BREAK_CONCRETE -#define BOUNCE_SHOTSHELL 0x80 - -// Temp entity bounce sound types -#define TE_BOUNCE_NULL 0 -#define TE_BOUNCE_SHELL 1 -#define TE_BOUNCE_SHOTSHELL 2 - -extern int gmsgTempEntity; // needs to be in global scope - typedef enum { + MONSTERSTATE_NONE = 0, MONSTERSTATE_IDLE, MONSTERSTATE_COMBAT, @@ -234,30 +191,7 @@ typedef enum } MONSTERSTATE; -typedef enum -{ - USE_OFF = 0, - USE_ON = 1, - USE_SET = 2, - USE_TOGGLE = 3, - USE_KILL = 4, -// special signals, never actually get sent: - USE_SAME = 5, - USE_NOT = 6, -} USE_TYPE; -//LRC- the values used for the new "global states" mechanism. -typedef enum -{ - STATE_OFF = 0, // disabled, inactive, invisible, closed, or stateless. Or non-alert monster. - STATE_TURN_ON, // door opening, env_fade fading in, etc. - STATE_ON, // enabled, active, visisble, or open. Or alert monster. - STATE_TURN_OFF, // door closing, monster dying (?). - STATE_IN_USE, // player is in control (train/tank/barney/scientist). - // In_Use isn't very useful, I'll probably remove it. -} STATE; - -extern char* GetStringForState( STATE state ); // Things that toggle (buttons/triggers/doors) need this typedef enum @@ -279,26 +213,16 @@ inline BOOL FClassnameIs(entvars_t* pev, const char* szClassname) class CBaseEntity; // Misc. Prototypes -extern void UTIL_SetSize (entvars_t* pev, const Vector &vecMin, const Vector &vecMax); -extern float UTIL_VecToYaw (const Vector &vec); -extern Vector UTIL_VecToAngles (const Vector &vec); -extern float UTIL_AngleMod (float a); -extern float UTIL_AngleDiff ( float destAngle, float srcAngle ); - -extern Vector UTIL_AxisRotationToAngles (const Vector &vec, float angle); //LRC -extern Vector UTIL_AxisRotationToVec (const Vector &vec, float angle); //LRC - -//LRC -class CBaseAlias; -extern void UTIL_AddToAliasList( CBaseAlias *pAlias ); -extern void UTIL_FlushAliases( void ); +extern void UTIL_SetSize (entvars_t* pev, const Vector &vecMin, const Vector &vecMax); +extern float UTIL_VecToYaw (const Vector &vec); +extern Vector UTIL_VecToAngles (const Vector &vec); +extern float UTIL_AngleMod (float a); +extern float UTIL_AngleDiff ( float destAngle, float srcAngle ); extern CBaseEntity *UTIL_FindEntityInSphere(CBaseEntity *pStartEntity, const Vector &vecCenter, float flRadius); extern CBaseEntity *UTIL_FindEntityByString(CBaseEntity *pStartEntity, const char *szKeyword, const char *szValue ); extern CBaseEntity *UTIL_FindEntityByClassname(CBaseEntity *pStartEntity, const char *szName ); extern CBaseEntity *UTIL_FindEntityByTargetname(CBaseEntity *pStartEntity, const char *szName ); -extern CBaseEntity *UTIL_FindEntityByTargetname(CBaseEntity *pStartEntity, const char *szName, CBaseEntity *pActivator ); //LRC - for $locus references -extern CBaseEntity *UTIL_FindEntityByTarget(CBaseEntity *pStartEntity, const char *szName ); extern CBaseEntity *UTIL_FindEntityGeneric(const char *szName, Vector &vecSrc, float flRadius ); // returns a CBaseEntity pointer to a player by index. Only returns if the player is spawned and connected @@ -318,28 +242,25 @@ inline void UTIL_MakeVectorsPrivate( const Vector &vecAngles, float *p_vForward, g_engfuncs.pfnAngleVectors( vecAngles, p_vForward, p_vRight, p_vUp ); } -extern void UTIL_MakeAimVectors ( const Vector &vecAngles ); // like MakeVectors, but assumes pitch isn't inverted -extern void UTIL_MakeInvVectors ( const Vector &vec, globalvars_t *pgv ); +extern void UTIL_MakeAimVectors ( const Vector &vecAngles ); // like MakeVectors, but assumes pitch isn't inverted +extern void UTIL_MakeInvVectors ( const Vector &vec, globalvars_t *pgv ); -extern void UTIL_SetEdictOrigin ( edict_t *pEdict, const Vector &vecOrigin ); -extern void UTIL_SetOrigin ( CBaseEntity* pEntity, const Vector &vecOrigin ); -extern void UTIL_SetAngles ( CBaseEntity *pEntity, const Vector &vecAngles ); +extern void UTIL_SetOrigin ( entvars_t* pev, const Vector &vecOrigin ); +extern void UTIL_EmitAmbientSound ( edict_t *entity, const Vector &vecOrigin, const char *samp, float vol, float attenuation, int fFlags, int pitch ); +extern void UTIL_ParticleEffect ( const Vector &vecOrigin, const Vector &vecDirection, ULONG ulColor, ULONG ulCount ); +extern void UTIL_ScreenShake ( const Vector ¢er, float amplitude, float frequency, float duration, float radius ); +extern void UTIL_ScreenShakeAll ( const Vector ¢er, float amplitude, float frequency, float duration ); +extern void UTIL_ShowMessage ( const char *pString, CBaseEntity *pPlayer ); +extern void UTIL_ShowMessageAll ( const char *pString ); +extern void UTIL_ScreenFadeAll ( const Vector &color, float fadeTime, float holdTime, int alpha, int flags ); +extern void UTIL_ScreenFade ( CBaseEntity *pEntity, const Vector &color, float fadeTime, float fadeHold, int alpha, int flags ); -extern void UTIL_EmitAmbientSound ( edict_t *entity, const Vector &vecOrigin, const char *samp, float vol, float attenuation, int fFlags, int pitch ); -extern void UTIL_ParticleEffect ( const Vector &vecOrigin, const Vector &vecDirection, ULONG ulColor, ULONG ulCount ); -extern void UTIL_ScreenShake ( const Vector ¢er, float amplitude, float frequency, float duration, float radius ); -extern void UTIL_ScreenShakeAll ( const Vector ¢er, float amplitude, float frequency, float duration ); -extern void UTIL_ShowMessage ( const char *pString, CBaseEntity *pPlayer ); -extern void UTIL_ShowMessageAll ( const char *pString ); -extern void UTIL_ScreenFadeAll ( const Vector &color, float fadeTime, float holdTime, int alpha, int flags ); -extern void UTIL_ScreenFade ( CBaseEntity *pEntity, const Vector &color, float fadeTime, float fadeHold, int alpha, int flags ); -extern void UTIL_SetFog ( Vector color, int iFadeTime, int iStartDist, int iEndDist, int playernum = 1 ); -extern void UTIL_SetFogAll ( Vector color, int iFadeTime, int iStartDist, int iEndDist ); - - -extern void UTIL_TraceLine( const Vector &vecStart, const Vector &vecEnd, IGNORE_MONSTERS igmon, edict_t *pentIgnore, TraceResult *ptr); -extern void UTIL_TraceLine( const Vector &vecStart, const Vector &vecEnd, IGNORE_MONSTERS igmon, IGNORE_GLASS ignoreGlass, edict_t *pentIgnore, TraceResult *ptr); -extern void UTIL_TraceHull( const Vector &vecStart, const Vector &vecEnd, IGNORE_MONSTERS igmon, int hullNumber, edict_t *pentIgnore, TraceResult *ptr); +typedef enum { ignore_monsters=1, dont_ignore_monsters=0, missile=2 } IGNORE_MONSTERS; +typedef enum { ignore_glass=1, dont_ignore_glass=0 } IGNORE_GLASS; +extern void UTIL_TraceLine (const Vector &vecStart, const Vector &vecEnd, IGNORE_MONSTERS igmon, edict_t *pentIgnore, TraceResult *ptr); +extern void UTIL_TraceLine (const Vector &vecStart, const Vector &vecEnd, IGNORE_MONSTERS igmon, IGNORE_GLASS ignoreGlass, edict_t *pentIgnore, TraceResult *ptr); +typedef enum { point_hull=0, human_hull=1, large_hull=2, head_hull=3 }; +extern void UTIL_TraceHull (const Vector &vecStart, const Vector &vecEnd, IGNORE_MONSTERS igmon, int hullNumber, edict_t *pentIgnore, TraceResult *ptr); extern TraceResult UTIL_GetGlobalTrace (void); extern void UTIL_TraceModel (const Vector &vecStart, const Vector &vecEnd, int hullNumber, edict_t *pentModel, TraceResult *ptr); extern Vector UTIL_GetAimVector (edict_t* pent, float flSpeed); @@ -357,7 +278,6 @@ extern void UTIL_GunshotDecalTrace( TraceResult *pTrace, int decalNumber ); extern void UTIL_Sparks( const Vector &position ); extern void UTIL_Ricochet( const Vector &position, float scale ); extern void UTIL_StringToVector( float *pVector, const char *pString ); -extern void UTIL_StringToRandomVector( float *pVector, const char *pString ); //LRC extern void UTIL_StringToIntArray( int *pVector, int count, const char *pString ); extern Vector UTIL_ClampVectorToBox( const Vector &input, const Vector &clampSize ); extern float UTIL_Approach( float target, float value, float speed ); @@ -368,7 +288,6 @@ extern char *UTIL_VarArgs( char *format, ... ); extern void UTIL_Remove( CBaseEntity *pEntity ); extern BOOL UTIL_IsValidEntity( edict_t *pent ); extern BOOL UTIL_TeamsMatch( const char *pTeamName1, const char *pTeamName2 ); -extern BOOL UTIL_IsFacing( entvars_t *pevTest, const Vector &reference ); //LRC // Use for ease-in, ease-out style interpolation (accel/decel) extern float UTIL_SplineFraction( float value, float scale ); @@ -434,7 +353,6 @@ extern void UTIL_StripToken( const char *pKey, char *pDest );// for redundant ke // Misc functions extern void SetMovedir(entvars_t* pev); -extern Vector GetMovedir( Vector vecAngles ); extern Vector VecBModelOrigin( entvars_t* pevBModel ); extern int BuildChangeList( LEVELLIST *pLevelList, int maxList ); @@ -475,12 +393,17 @@ extern DLL_GLOBAL int g_Language; #define SPEAKER_START_SILENT 1 // wait for trigger 'on' to start announcements +#define SND_SPAWNING (1<<8) // duplicated in protocol.h we're spawing, used in some cases for ambients +#define SND_STOP (1<<5) // duplicated in protocol.h stop sound +#define SND_CHANGE_VOL (1<<6) // duplicated in protocol.h change sound vol +#define SND_CHANGE_PITCH (1<<7) // duplicated in protocol.h change sound pitch + #define LFO_SQUARE 1 #define LFO_TRIANGLE 2 #define LFO_RANDOM 3 // func_rotating -#define SF_BRUSH_ROTATE_Y_AXIS 0 //!?! (LRC) +#define SF_BRUSH_ROTATE_Y_AXIS 0 #define SF_BRUSH_ROTATE_INSTANT 1 #define SF_BRUSH_ROTATE_BACKWARDS 2 #define SF_BRUSH_ROTATE_Z_AXIS 4 @@ -488,6 +411,7 @@ extern DLL_GLOBAL int g_Language; #define SF_PENDULUM_AUTO_RETURN 16 #define SF_PENDULUM_PASSABLE 32 + #define SF_BRUSH_ROTATE_SMALLRADIUS 128 #define SF_BRUSH_ROTATE_MEDIUMRADIUS 256 #define SF_BRUSH_ROTATE_LARGERADIUS 512 @@ -495,46 +419,40 @@ extern DLL_GLOBAL int g_Language; #define PUSH_BLOCK_ONLY_X 1 #define PUSH_BLOCK_ONLY_Y 2 -#define VEC_HULL_MIN gpGlobals->hullmins[0] -#define VEC_HULL_MAX gpGlobals->hullmaxs[0] +#define VEC_HULL_MIN Vector(-16, -16, -36) +#define VEC_HULL_MAX Vector( 16, 16, 36) #define VEC_HUMAN_HULL_MIN Vector( -16, -16, 0 ) #define VEC_HUMAN_HULL_MAX Vector( 16, 16, 72 ) #define VEC_HUMAN_HULL_DUCK Vector( 16, 16, 36 ) -#define VEC_VIEW Vector( 0, 0, gpGlobals->viewheight[0] ) +#define VEC_VIEW Vector( 0, 0, 28 ) -#define VEC_DUCK_HULL_MIN gpGlobals->hullmins[1] -#define VEC_DUCK_HULL_MAX gpGlobals->hullmaxs[1] -#define VEC_DUCK_VIEW Vector( 0, 0, gpGlobals->viewheight[1] ) +#define VEC_DUCK_HULL_MIN Vector(-16, -16, -18 ) +#define VEC_DUCK_HULL_MAX Vector( 16, 16, 18) +#define VEC_DUCK_VIEW Vector( 0, 0, 12 ) + +#define SVC_TEMPENTITY 23 +#define SVC_INTERMISSION 30 +#define SVC_CDTRACK 32 +#define SVC_WEAPONANIM 35 +#define SVC_ROOMTYPE 37 +#define SVC_DIRECTOR 51 -#define SVC_TEMPENTITY gmsgTempEntity -#define SVC_INTERMISSION gmsgIntermission -#define SVC_WEAPONANIM gmsgWeaponAnim -#define SVC_ROOMTYPE gmsgRoomType -// camera flags -#define CAMERA_ON 1 -#define DRAW_HUD 2 -#define INVERSE_X 4 -#define MONSTER_VIEW 8 // triggers #define SF_TRIGGER_ALLOWMONSTERS 1// monsters allowed to fire this trigger #define SF_TRIGGER_NOCLIENTS 2// players not allowed to fire this trigger #define SF_TRIGGER_PUSHABLES 4// only pushables can fire this trigger -#define SF_TRIGGER_EVERYTHING 8// everything else can fire this trigger (e.g. gibs, rockets) // func breakable #define SF_BREAK_TRIGGER_ONLY 1// may only be broken by trigger #define SF_BREAK_TOUCH 2// can be 'crashed through' by running player (plate glass) #define SF_BREAK_PRESSURE 4// can be broken by a player standing on it -#define SF_BREAK_FADE_RESPAWN 8// LRC- fades in gradually when respawned #define SF_BREAK_CROWBAR 256// instant break if hit with crowbar // func_pushable (it's also func_breakable, so don't collide with those flags) #define SF_PUSH_BREAKABLE 128 -#define SF_PUSH_NOPULL 512//LRC -#define SF_PUSH_USECUSTOMSIZE 0x800000 //LRC, not yet used #define SF_LIGHT_START_OFF 1 @@ -634,13 +552,3 @@ int UTIL_SharedRandomLong( unsigned int seed, int low, int high ); float UTIL_SharedRandomFloat( unsigned int seed, float low, float high ); float UTIL_WeaponTimeBase( void ); -int GetStdLightStyle (int iStyle); //LRC- declared here so it can be used by everything that - // needs to deal with the standard lightstyles. -// LRC- for aliases and groups -CBaseEntity* UTIL_FollowReference( CBaseEntity* pStartEntity, const char* szName ); - -// for trigger_viewset -int HaveCamerasInPVS( edict_t* edict ); -BOOL IsMultiplayer ( void ); - -#endif //UTIL_H \ No newline at end of file diff --git a/dlls/vector.h b/dlls/vector.h new file mode 100644 index 00000000..c498c835 --- /dev/null +++ b/dlls/vector.h @@ -0,0 +1,112 @@ +/*** +* +* Copyright (c) 1996-2002, 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. +* +****/ +#ifndef VECTOR_H +#define VECTOR_H + +//========================================================= +// 2DVector - used for many pathfinding and many other +// operations that are treated as planar rather than 3d. +//========================================================= +class Vector2D +{ +public: + inline Vector2D(void) { } + inline Vector2D(float X, float Y) { x = X; y = Y; } + inline Vector2D operator+(const Vector2D& v) const { return Vector2D(x+v.x, y+v.y); } + inline Vector2D operator-(const Vector2D& v) const { return Vector2D(x-v.x, y-v.y); } + inline Vector2D operator*(float fl) const { return Vector2D(x*fl, y*fl); } + inline Vector2D operator/(float fl) const { return Vector2D(x/fl, y/fl); } + + inline float Length(void) const { return sqrt(x*x + y*y ); } + + inline Vector2D Normalize ( void ) const + { + Vector2D vec2; + + float flLen = Length(); + if ( flLen == 0 ) + { + return Vector2D( 0, 0 ); + } + else + { + flLen = 1 / flLen; + return Vector2D( x * flLen, y * flLen ); + } + } + + vec_t x, y; +}; + +inline float DotProduct(const Vector2D& a, const Vector2D& b) { return( a.x*b.x + a.y*b.y ); } +inline Vector2D operator*(float fl, const Vector2D& v) { return v * fl; } + +//========================================================= +// 3D Vector +//========================================================= +class Vector // same data-layout as engine's vec3_t, +{ // which is a vec_t[3] +public: + // Construction/destruction + inline Vector(void) { } + inline Vector(float X, float Y, float Z) { x = X; y = Y; z = Z; } + //inline Vector(double X, double Y, double Z) { x = (float)X; y = (float)Y; z = (float)Z; } + //inline Vector(int X, int Y, int Z) { x = (float)X; y = (float)Y; z = (float)Z; } + inline Vector(const Vector& v) { x = v.x; y = v.y; z = v.z; } + inline Vector(float rgfl[3]) { x = rgfl[0]; y = rgfl[1]; z = rgfl[2]; } + + // Operators + inline Vector operator-(void) const { return Vector(-x,-y,-z); } + inline int operator==(const Vector& v) const { return x==v.x && y==v.y && z==v.z; } + inline int operator!=(const Vector& v) const { return !(*this==v); } + inline Vector operator+(const Vector& v) const { return Vector(x+v.x, y+v.y, z+v.z); } + inline Vector operator-(const Vector& v) const { return Vector(x-v.x, y-v.y, z-v.z); } + inline Vector operator*(float fl) const { return Vector(x*fl, y*fl, z*fl); } + inline Vector operator/(float fl) const { return Vector(x/fl, y/fl, z/fl); } + + // Methods + inline void CopyToArray(float* rgfl) const { rgfl[0] = x, rgfl[1] = y, rgfl[2] = z; } + inline float Length(void) const { return sqrt(x*x + y*y + z*z); } + operator float *() { return &x; } // Vectors will now automatically convert to float * when needed + operator const float *() const { return &x; } // Vectors will now automatically convert to float * when needed + inline Vector Normalize(void) const + { + float flLen = Length(); + if (flLen == 0) return Vector(0,0,1); // ???? + flLen = 1 / flLen; + return Vector(x * flLen, y * flLen, z * flLen); + } + + inline Vector2D Make2D ( void ) const + { + Vector2D Vec2; + + Vec2.x = x; + Vec2.y = y; + + return Vec2; + } + inline float Length2D(void) const { return sqrt(x*x + y*y); } + + // Members + vec_t x, y, z; +}; +inline Vector operator*(float fl, const Vector& v) { return v * fl; } +inline float DotProduct(const Vector& a, const Vector& b) { return(a.x*b.x+a.y*b.y+a.z*b.z); } +inline Vector CrossProduct(const Vector& a, const Vector& b) { return Vector( a.y*b.z - a.z*b.y, a.z*b.x - a.x*b.z, a.x*b.y - a.y*b.x ); } + + + +#endif \ No newline at end of file diff --git a/spirit/weapons.cpp b/dlls/weapons.cpp similarity index 68% rename from spirit/weapons.cpp rename to dlls/weapons.cpp index f10d8df6..f8187790 100644 --- a/spirit/weapons.cpp +++ b/dlls/weapons.cpp @@ -31,29 +31,21 @@ #include "decals.h" #include "gamerules.h" -//extern CGraph WorldGraph; +extern CGraph WorldGraph; extern int gEvilImpulse101; #define NOT_USED 255 -DLL_GLOBAL short g_sModelIndexLaser;// holds the index for the laser beam -DLL_GLOBAL const char *g_pModelNameLaser = "sprites/laserbeam.spr"; -DLL_GLOBAL short g_sModelIndexLaserDot;// holds the index for the laser beam dot -DLL_GLOBAL short g_sModelIndexFireball;// holds the index for the fireball -DLL_GLOBAL short g_sModelIndexSmoke;// holds the index for the smoke cloud -DLL_GLOBAL short g_sModelIndexWExplosion;// holds the index for the underwater explosion -DLL_GLOBAL short g_sModelIndexBubbles;// holds the index for the bubbles model -DLL_GLOBAL short g_sModelIndexBloodDrop;// holds the sprite index for the initial blood -DLL_GLOBAL short g_sModelIndexBloodSpray;// holds the sprite index for splattered blood -DLL_GLOBAL short g_sModelIndexNullModel; //null model index -DLL_GLOBAL short g_sModelIndexErrorModel;//error model index -DLL_GLOBAL short g_sModelIndexNullSprite;//null sprite index -DLL_GLOBAL short g_sModelIndexErrorSprite;//error sprite index -DLL_GLOBAL short g_sSoundIndexNullSound;//null sound index -DLL_GLOBAL unsigned short g_usEventIndexNullEvent;//null event index -DLL_GLOBAL unsigned short m_usDecals; //Decal event -DLL_GLOBAL unsigned short m_usPlayEmptySound; //play empty sound on client side +DLL_GLOBAL short g_sModelIndexLaser;// holds the index for the laser beam +DLL_GLOBAL const char *g_pModelNameLaser = "sprites/laserbeam.spr"; +DLL_GLOBAL short g_sModelIndexLaserDot;// holds the index for the laser beam dot +DLL_GLOBAL short g_sModelIndexFireball;// holds the index for the fireball +DLL_GLOBAL short g_sModelIndexSmoke;// holds the index for the smoke cloud +DLL_GLOBAL short g_sModelIndexWExplosion;// holds the index for the underwater explosion +DLL_GLOBAL short g_sModelIndexBubbles;// holds the index for the bubbles model +DLL_GLOBAL short g_sModelIndexBloodDrop;// holds the sprite index for the initial blood +DLL_GLOBAL short g_sModelIndexBloodSpray;// holds the sprite index for splattered blood ItemInfo CBasePlayerItem::ItemInfoArray[MAX_WEAPONS]; AmmoInfo CBasePlayerItem::AmmoInfoArray[MAX_AMMO_SLOTS]; @@ -80,108 +72,10 @@ int MaxAmmoCarry( int iszName ) return CBasePlayerItem::ItemInfoArray[i].iMaxAmmo2; } - ALERT( at_debug, "MaxAmmoCarry() doesn't recognize '%s'!\n", STRING( iszName ) ); + ALERT( at_console, "MaxAmmoCarry() doesn't recognize '%s'!\n", STRING( iszName ) ); return -1; } -LINK_ENTITY_TO_CLASS( laser_spot, CLaserSpot ); - -//========================================================= -//========================================================= -CLaserSpot *CLaserSpot::CreateSpot( entvars_t *pevOwner ) -{ - CLaserSpot *pSpot = GetClassPtr( (CLaserSpot *)NULL ); - pSpot->Spawn(); - pSpot->pev->classname = MAKE_STRING("laser_spot"); - - if( pevOwner ) - { - // predictable laserspot (cl_lw must be set to 1) - pSpot->pev->flags |= FL_SKIPLOCALHOST; - pSpot->pev->owner = ENT( pevOwner ); - pevOwner->effects |= EF_LASERSPOT; - } - - return pSpot; -} - -//========================================================= -//========================================================= -CLaserSpot *CLaserSpot::CreateSpot( const char* spritename, entvars_t *pevOwner ) -{ - CLaserSpot *pSpot = CreateSpot( pevOwner ); - SET_MODEL( ENT( pSpot->pev ), spritename ); - return pSpot; -} - -//========================================================= -//========================================================= -void CLaserSpot::Spawn( void ) -{ - Precache( ); - pev->movetype = MOVETYPE_NONE; - pev->solid = SOLID_NOT; - - pev->rendermode = kRenderGlow; - pev->rendercolor = Vector( 200, 12, 12 ); - pev->renderfx = kRenderFxNoDissipation; - pev->renderamt = 255; - - SET_MODEL(ENT(pev), "sprites/laserdot.spr"); - UTIL_SetOrigin( this, pev->origin ); -}; - -//========================================================= -// Suspend- make the laser sight invisible. -//========================================================= -void CLaserSpot::Suspend( float flSuspendTime ) -{ - pev->effects |= EF_NODRAW; - - if( !FNullEnt( pev->owner )) - pev->owner->v.effects &= ~EF_LASERSPOT; - - // LRC: -1 means suspend indefinitely - if ( flSuspendTime == -1 ) - { - SetThink( NULL ); - } - else - { - SetThink(&CLaserSpot:: Revive ); - SetNextThink( flSuspendTime ); - } -} - -//========================================================= -// Revive - bring a suspended laser sight back. -//========================================================= -void CLaserSpot::Revive( void ) -{ - if( !FNullEnt( pev->owner )) - pev->owner->v.effects |= EF_LASERSPOT; - - pev->effects &= ~EF_NODRAW; - SetThink( NULL ); -} - -void CLaserSpot::UpdateOnRemove( void ) -{ - // tell the owner about laserspot - if( !FNullEnt( pev->owner )) - pev->owner->v.effects &= ~EF_LASERSPOT; - - CBaseEntity :: UpdateOnRemove (); -} - -void CLaserSpot::Precache( void ) -{ - PRECACHE_MODEL("sprites/laserdot.spr"); -}; - -//========================================================= -//========================================================= - /* ============================================================================== @@ -308,7 +202,7 @@ void EjectBrass ( const Vector &vecOrigin, const Vector &vecVelocity, float rota { // FIX: when the player shoots, their gun isn't in the same position as it is on the model other players see. - MESSAGE_BEGIN( MSG_PVS, gmsgTempEntity, vecOrigin ); + MESSAGE_BEGIN( MSG_PVS, SVC_TEMPENTITY, vecOrigin ); WRITE_BYTE( TE_MODEL); WRITE_COORD( vecOrigin.x); WRITE_COORD( vecOrigin.y); @@ -328,7 +222,7 @@ void EjectBrass ( const Vector &vecOrigin, const Vector &vecVelocity, float rota // UNDONE: This is no longer used? void ExplodeModel( const Vector &vecOrigin, float speed, int model, int count ) { - MESSAGE_BEGIN( MSG_PVS, gmsgTempEntity, vecOrigin ); + MESSAGE_BEGIN( MSG_PVS, SVC_TEMPENTITY, vecOrigin ); WRITE_BYTE ( TE_EXPLODEMODEL ); WRITE_COORD( vecOrigin.x ); WRITE_COORD( vecOrigin.y ); @@ -376,7 +270,7 @@ void UTIL_PrecacheOtherWeapon( const char *szClassname ) pent = CREATE_NAMED_ENTITY( MAKE_STRING( szClassname ) ); if ( FNullEnt( pent ) ) { - ALERT ( at_debug, "NULL Ent in UTIL_PrecacheOtherWeapon\n" ); + ALERT ( at_console, "NULL Ent in UTIL_PrecacheOtherWeapon\n" ); return; } @@ -408,21 +302,13 @@ void UTIL_PrecacheOtherWeapon( const char *szClassname ) REMOVE_ENTITY(pent); } -// called by worldspawn FIXME. Move this to client.cpp +// called by worldspawn void W_Precache(void) { memset( CBasePlayerItem::ItemInfoArray, 0, sizeof(CBasePlayerItem::ItemInfoArray) ); memset( CBasePlayerItem::AmmoInfoArray, 0, sizeof(CBasePlayerItem::AmmoInfoArray) ); giAmmoIndex = 0; - // g-cont. init safe precaching system - // WARNING!!! this is critical stuff! do not edit this - g_sSoundIndexNullSound = g_engfuncs.pfnPrecacheSound ("common/null.wav"); - g_sModelIndexNullModel = g_engfuncs.pfnPrecacheModel ("models/null.mdl"); - g_sModelIndexErrorModel = g_engfuncs.pfnPrecacheModel ("models/error.mdl"); - g_sModelIndexNullSprite = g_engfuncs.pfnPrecacheModel ("sprites/null.spr"); - g_sModelIndexErrorSprite = g_engfuncs.pfnPrecacheModel ("sprites/error.spr"); - // custom items... // common world objects @@ -442,52 +328,70 @@ void W_Precache(void) // glock UTIL_PrecacheOtherWeapon( "weapon_9mmhandgun" ); UTIL_PrecacheOther( "ammo_9mmclip" ); - UTIL_PrecacheOther( "ammo_9mmbox" ); //LRC // mp5 UTIL_PrecacheOtherWeapon( "weapon_9mmAR" ); UTIL_PrecacheOther( "ammo_9mmAR" ); UTIL_PrecacheOther( "ammo_ARgrenades" ); +#if !defined( OEM_BUILD ) && !defined( HLDEMO_BUILD ) // python UTIL_PrecacheOtherWeapon( "weapon_357" ); UTIL_PrecacheOther( "ammo_357" ); - +#endif + +#if !defined( OEM_BUILD ) && !defined( HLDEMO_BUILD ) // gauss UTIL_PrecacheOtherWeapon( "weapon_gauss" ); UTIL_PrecacheOther( "ammo_gaussclip" ); +#endif +#if !defined( OEM_BUILD ) && !defined( HLDEMO_BUILD ) // rpg UTIL_PrecacheOtherWeapon( "weapon_rpg" ); UTIL_PrecacheOther( "ammo_rpgclip" ); +#endif +#if !defined( OEM_BUILD ) && !defined( HLDEMO_BUILD ) // crossbow UTIL_PrecacheOtherWeapon( "weapon_crossbow" ); UTIL_PrecacheOther( "ammo_crossbow" ); +#endif +#if !defined( OEM_BUILD ) && !defined( HLDEMO_BUILD ) // egon UTIL_PrecacheOtherWeapon( "weapon_egon" ); +#endif // tripmine UTIL_PrecacheOtherWeapon( "weapon_tripmine" ); +#if !defined( OEM_BUILD ) && !defined( HLDEMO_BUILD ) // satchel charge UTIL_PrecacheOtherWeapon( "weapon_satchel" ); +#endif // hand grenade UTIL_PrecacheOtherWeapon("weapon_handgrenade"); +#if !defined( OEM_BUILD ) && !defined( HLDEMO_BUILD ) // squeak grenade UTIL_PrecacheOtherWeapon( "weapon_snark" ); +#endif +#if !defined( OEM_BUILD ) && !defined( HLDEMO_BUILD ) // hornetgun UTIL_PrecacheOtherWeapon( "weapon_hornetgun" ); +#endif + +#if !defined( OEM_BUILD ) && !defined( HLDEMO_BUILD ) if ( g_pGameRules->IsDeathmatch() ) { UTIL_PrecacheOther( "weaponbox" );// container for dropped deathmatch weapons } - +#endif + g_sModelIndexFireball = PRECACHE_MODEL ("sprites/zerogxplode.spr");// fireball g_sModelIndexWExplosion = PRECACHE_MODEL ("sprites/WXplo1.spr");// underwater fireball g_sModelIndexSmoke = PRECACHE_MODEL ("sprites/steam1.spr");// smoke @@ -497,22 +401,11 @@ void W_Precache(void) g_sModelIndexLaser = PRECACHE_MODEL( (char *)g_pModelNameLaser ); g_sModelIndexLaserDot = PRECACHE_MODEL("sprites/laserdot.spr"); - m_usPlayEmptySound = PRECACHE_EVENT( 1, "evEmptySound" ); - m_usDecals = PRECACHE_EVENT(1, "evDecals"); - // custom muzzleflashes - PRECACHE_MODEL ("sprites/muzzleflash1.spr"); - PRECACHE_MODEL ("sprites/muzzleflash2.spr"); - PRECACHE_MODEL ("sprites/muzzleflash3.spr"); - PRECACHE_MODEL ("sprites/muzzleflash.spr"); - // ricochet - PRECACHE_MODEL ("sprites/richo1.spr"); - // used by explosions PRECACHE_MODEL ("models/grenade.mdl"); PRECACHE_MODEL ("sprites/explode1.spr"); - PRECACHE_MODEL ("sprites/animglow01.spr"); PRECACHE_SOUND ("weapons/debris1.wav");// explosion aftermaths PRECACHE_SOUND ("weapons/debris2.wav");// explosion aftermaths @@ -529,42 +422,38 @@ void W_Precache(void) } -void CBasePlayerItem::KeyValue( KeyValueData *pkvd ) //AJH -{ - if (FStrEq(pkvd->szKeyName, "master")) - { - m_sMaster = ALLOC_STRING(pkvd->szValue); - pkvd->fHandled = TRUE; - } - else - CBaseDelay::KeyValue( pkvd ); -} + TYPEDESCRIPTION CBasePlayerItem::m_SaveData[] = { DEFINE_FIELD( CBasePlayerItem, m_pPlayer, FIELD_CLASSPTR ), DEFINE_FIELD( CBasePlayerItem, m_pNext, FIELD_CLASSPTR ), + //DEFINE_FIELD( CBasePlayerItem, m_fKnown, FIELD_INTEGER ),Reset to zero on load DEFINE_FIELD( CBasePlayerItem, m_iId, FIELD_INTEGER ), - DEFINE_FIELD( CBasePlayerWeapon, m_sMaster, FIELD_STRING ), // AJH master entity for Lockable weapons + // DEFINE_FIELD( CBasePlayerItem, m_iIdPrimary, FIELD_INTEGER ), + // DEFINE_FIELD( CBasePlayerItem, m_iIdSecondary, FIELD_INTEGER ), }; IMPLEMENT_SAVERESTORE( CBasePlayerItem, CBaseAnimating ); TYPEDESCRIPTION CBasePlayerWeapon::m_SaveData[] = { +#if defined( CLIENT_WEAPONS ) + DEFINE_FIELD( CBasePlayerWeapon, m_flNextPrimaryAttack, FIELD_FLOAT ), + DEFINE_FIELD( CBasePlayerWeapon, m_flNextSecondaryAttack, FIELD_FLOAT ), + DEFINE_FIELD( CBasePlayerWeapon, m_flTimeWeaponIdle, FIELD_FLOAT ), +#else // CLIENT_WEAPONS DEFINE_FIELD( CBasePlayerWeapon, m_flNextPrimaryAttack, FIELD_TIME ), DEFINE_FIELD( CBasePlayerWeapon, m_flNextSecondaryAttack, FIELD_TIME ), DEFINE_FIELD( CBasePlayerWeapon, m_flTimeWeaponIdle, FIELD_TIME ), - DEFINE_FIELD( CBasePlayerWeapon, m_flTimeUpdate, FIELD_TIME ), +#endif // CLIENT_WEAPONS DEFINE_FIELD( CBasePlayerWeapon, m_iPrimaryAmmoType, FIELD_INTEGER ), DEFINE_FIELD( CBasePlayerWeapon, m_iSecondaryAmmoType, FIELD_INTEGER ), DEFINE_FIELD( CBasePlayerWeapon, m_iClip, FIELD_INTEGER ), DEFINE_FIELD( CBasePlayerWeapon, m_iDefaultAmmo, FIELD_INTEGER ), - DEFINE_FIELD( CBasePlayerWeapon, m_iChargeLevel, FIELD_INTEGER ), - DEFINE_FIELD( CBasePlayerWeapon, m_iOverloadLevel, FIELD_INTEGER ), - DEFINE_FIELD( CBasePlayerWeapon, m_fInAttack, FIELD_INTEGER ), - DEFINE_FIELD( CBasePlayerWeapon, m_iBody, FIELD_INTEGER ), +// DEFINE_FIELD( CBasePlayerWeapon, m_iClientClip, FIELD_INTEGER ) , reset to zero on load so hud gets updated correctly +// DEFINE_FIELD( CBasePlayerWeapon, m_iClientWeaponState, FIELD_INTEGER ), reset to zero on load so hud gets updated correctly }; IMPLEMENT_SAVERESTORE( CBasePlayerWeapon, CBasePlayerItem ); @@ -585,13 +474,13 @@ void CBasePlayerItem :: FallInit( void ) pev->movetype = MOVETYPE_TOSS; pev->solid = SOLID_BBOX; - UTIL_SetOrigin( this, pev->origin ); + UTIL_SetOrigin( pev, pev->origin ); UTIL_SetSize(pev, Vector( 0, 0, 0), Vector(0, 0, 0) );//pointsize until it lands on the ground. - SetTouch(&CBasePlayerItem :: DefaultTouch ); - SetThink(&CBasePlayerItem :: FallThink ); + SetTouch( DefaultTouch ); + SetThink( FallThink ); - SetNextThink( 0.1 ); + pev->nextthink = gpGlobals->time + 0.1; } //========================================================= @@ -603,7 +492,7 @@ void CBasePlayerItem :: FallInit( void ) //========================================================= void CBasePlayerItem::FallThink ( void ) { - SetNextThink( 0.1 ); + pev->nextthink = gpGlobals->time + 0.1; if ( pev->flags & FL_ONGROUND ) { @@ -638,8 +527,8 @@ void CBasePlayerItem::Materialize( void ) pev->solid = SOLID_TRIGGER; - UTIL_SetOrigin( this, pev->origin );// link into world. - SetTouch(&CBasePlayerItem::DefaultTouch); + UTIL_SetOrigin( pev, pev->origin );// link into world. + SetTouch (DefaultTouch); SetThink (NULL); } @@ -658,7 +547,7 @@ void CBasePlayerItem::AttemptToMaterialize( void ) return; } - SetNextThink( time ); + pev->nextthink = gpGlobals->time + time; } //========================================================= @@ -692,17 +581,17 @@ CBaseEntity* CBasePlayerItem::Respawn( void ) { pNewWeapon->pev->effects |= EF_NODRAW;// invisible for now pNewWeapon->SetTouch( NULL );// no touch - pNewWeapon->SetThink(&CBasePlayerItem:: AttemptToMaterialize ); + pNewWeapon->SetThink( AttemptToMaterialize ); DROP_TO_FLOOR ( ENT(pev) ); // not a typo! We want to know when the weapon the player just picked up should respawn! This new entity we created is the replacement, // but when it should respawn is based on conditions belonging to the weapon that was taken. - pNewWeapon->AbsoluteNextThink( g_pGameRules->FlWeaponRespawnTime( this ) ); + pNewWeapon->pev->nextthink = g_pGameRules->FlWeaponRespawnTime( this ); } else { - ALERT ( at_debug, "Respawn failed to create %s!\n", STRING( pev->classname ) ); + ALERT ( at_console, "Respawn failed to create %s!\n", STRING( pev->classname ) ); } return pNewWeapon; @@ -730,36 +619,18 @@ void CBasePlayerItem::DefaultTouch( CBaseEntity *pOther ) { AttachToPlayer( pPlayer ); EMIT_SOUND(ENT(pPlayer->pev), CHAN_ITEM, "items/gunpickup2.wav", 1, ATTN_NORM); - - if(!gEvilImpulse101) - { - int i; - char sample[32]; - char weapon_name[32]; - strcpy(weapon_name, STRING(pev->classname)); - - if(strncmp(weapon_name, "weapon_", 7) == 0) - i = 7; - else if (strncmp(weapon_name, "item_", 5) == 0) - i = 5; - - sprintf(sample, "!%s", weapon_name + i); - pPlayer->SetSuitUpdate(sample, FALSE, SUIT_NEXT_IN_30SEC); - } } SUB_UseTargets( pOther, USE_TOGGLE, 0 ); // UNDONE: when should this happen? } -void CBasePlayerItem::Spawn(void) -{ - pev->animtime = gpGlobals->time + 0.1; - CBaseAnimating::Spawn(); -} - BOOL CanAttack( float attack_time, float curtime, BOOL isPredicted ) { +#if defined( CLIENT_WEAPONS ) + if ( !isPredicted ) +#else if ( 1 ) +#endif { return ( attack_time <= curtime ) ? TRUE : FALSE; } @@ -771,13 +642,6 @@ BOOL CanAttack( float attack_time, float curtime, BOOL isPredicted ) void CBasePlayerWeapon::ItemPostFrame( void ) { - RestoreBody();//restore body, skin and last animation - - // catch all - if ( ShouldWeaponIdle() ) - { - WeaponIdle(); - } if ((m_fInReload) && ( m_pPlayer->m_flNextAttack <= UTIL_WeaponTimeBase() ) ) { // complete the reload. @@ -792,7 +656,7 @@ void CBasePlayerWeapon::ItemPostFrame( void ) m_fInReload = FALSE; } - if ((m_pPlayer->pev->button & IN_ATTACK2) && CanAttack( m_flNextSecondaryAttack, gpGlobals->time, 0 ) ) + if ((m_pPlayer->pev->button & IN_ATTACK2) && CanAttack( m_flNextSecondaryAttack, gpGlobals->time, UseDecrement() ) ) { if ( pszAmmo2() && !m_pPlayer->m_rgAmmo[SecondaryAmmoIndex()] ) { @@ -803,7 +667,7 @@ void CBasePlayerWeapon::ItemPostFrame( void ) SecondaryAttack(); m_pPlayer->pev->button &= ~IN_ATTACK2; } - else if ((m_pPlayer->pev->button & IN_ATTACK) && CanAttack( m_flNextPrimaryAttack, gpGlobals->time, 0 ) ) + else if ((m_pPlayer->pev->button & IN_ATTACK) && CanAttack( m_flNextPrimaryAttack, gpGlobals->time, UseDecrement() ) ) { if ( (m_iClip == 0 && pszAmmo1()) || (iMaxClip() == -1 && !m_pPlayer->m_rgAmmo[PrimaryAmmoIndex()] ) ) { @@ -821,43 +685,37 @@ void CBasePlayerWeapon::ItemPostFrame( void ) else if ( !(m_pPlayer->pev->button & (IN_ATTACK|IN_ATTACK2) ) ) { // no fire buttons down + m_fFireOnEmpty = FALSE; - //play sequence holster/deploy if player find or lose suit - if(( PLAYER_HAS_SUIT && !PLAYER_DRAW_SUIT ) || ( !PLAYER_HAS_SUIT && PLAYER_DRAW_SUIT )) - { - m_pPlayer->m_pActiveItem->Holster( ); - m_pPlayer->QueueItem( this ); - if ( m_pPlayer->m_pActiveItem ) - { - m_pPlayer->m_pActiveItem->Deploy(); - m_pPlayer->m_pActiveItem->UpdateItemInfo( ); - } - } - - if ( !IsUseable() && m_flNextPrimaryAttack < gpGlobals->time ) + if ( !IsUseable() && m_flNextPrimaryAttack < ( UseDecrement() ? 0.0 : gpGlobals->time ) ) { // weapon isn't useable, switch. if ( !(iFlags() & ITEM_FLAG_NOAUTOSWITCHEMPTY) && g_pGameRules->GetNextBestWeapon( m_pPlayer, this ) ) { - m_flNextPrimaryAttack = gpGlobals->time + 0.3; + m_flNextPrimaryAttack = ( UseDecrement() ? 0.0 : gpGlobals->time ) + 0.3; return; } } else { // weapon is useable. Reload if empty and weapon has waited as long as it has to after firing - if ( m_iClip == 0 && !(iFlags() & ITEM_FLAG_NOAUTORELOAD) && m_flNextPrimaryAttack < gpGlobals->time ) + if ( m_iClip == 0 && !(iFlags() & ITEM_FLAG_NOAUTORELOAD) && m_flNextPrimaryAttack < ( UseDecrement() ? 0.0 : gpGlobals->time ) ) { Reload(); return; } } - ResetEmptySound(); WeaponIdle( ); return; } + + // catch all + if ( ShouldWeaponIdle() ) + { + WeaponIdle(); + } } void CBasePlayerItem::DestroyItem( void ) @@ -881,18 +739,18 @@ int CBasePlayerItem::AddToPlayer( CBasePlayer *pPlayer ) void CBasePlayerItem::Drop( void ) { SetTouch( NULL ); - SetThink(&CBasePlayerItem::SUB_Remove); - SetNextThink( 0.1 ); + SetThink(SUB_Remove); + pev->nextthink = gpGlobals->time + .1; } void CBasePlayerItem::Kill( void ) { SetTouch( NULL ); - SetThink(&CBasePlayerItem::SUB_Remove); - SetNextThink( 0.1 ); + SetThink(SUB_Remove); + pev->nextthink = gpGlobals->time + .1; } -void CBasePlayerItem::Holster( ) +void CBasePlayerItem::Holster( int skiplocal /* = 0 */ ) { m_pPlayer->pev->viewmodel = 0; m_pPlayer->pev->weaponmodel = 0; @@ -907,24 +765,13 @@ void CBasePlayerItem::AttachToPlayer ( CBasePlayer *pPlayer ) pev->modelindex = 0;// server won't send down to clients if modelindex == 0 pev->model = iStringNull; pev->owner = pPlayer->edict(); - SetNextThink( 0.1 ); + pev->nextthink = gpGlobals->time + .1; SetTouch( NULL ); - SetThink(NULL); } -//LRC -void CBasePlayerWeapon :: SetNextThink( float delay ) -{ - m_fNextThink = UTIL_WeaponTimeBase() + delay; - pev->nextthink = m_fNextThink; -} - -/// CALLED THROUGH the newly-touched weapon's instance. The existing player weapon is pOriginal +// CALLED THROUGH the newly-touched weapon's instance. The existing player weapon is pOriginal int CBasePlayerWeapon::AddDuplicate( CBasePlayerItem *pOriginal ) { - if (!UTIL_IsMasterTriggered(m_sMaster, m_pPlayer)) // - return FALSE; // AJH allows for locked weapons - if ( m_iDefaultAmmo ) { return ExtractAmmo( (CBasePlayerWeapon *)pOriginal ); @@ -939,9 +786,6 @@ int CBasePlayerWeapon::AddDuplicate( CBasePlayerItem *pOriginal ) int CBasePlayerWeapon::AddToPlayer( CBasePlayer *pPlayer ) { - if (!UTIL_IsMasterTriggered(m_sMaster, pPlayer)) // - return FALSE; // AJH allows for locked weapons - int bResult = CBasePlayerItem::AddToPlayer( pPlayer ); pPlayer->pev->weapons |= (1<pev); - WRITE_BYTE(m_iId); - MESSAGE_END(); - + if (bResult) return AddWeapon( ); - } return FALSE; } @@ -972,7 +810,8 @@ int CBasePlayerWeapon::UpdateClientData( CBasePlayer *pPlayer ) { if ( pPlayer->m_fOnTarget ) state = WEAPON_IS_ONTARGET; - else state = 1; + else + state = 1; } // Forcing send of all data! @@ -992,7 +831,9 @@ int CBasePlayerWeapon::UpdateClientData( CBasePlayer *pPlayer ) } // If the ammo, state, or fov has changed, update the weapon - if ( m_iClip != m_iClientClip || state != m_iClientWeaponState || Q_rint( m_iClientFov ) != Q_rint( m_pPlayer->pev->fov )) + if ( m_iClip != m_iClientClip || + state != m_iClientWeaponState || + pPlayer->m_iFOV != pPlayer->m_iClientFOV ) { bSend = TRUE; } @@ -1005,49 +846,42 @@ int CBasePlayerWeapon::UpdateClientData( CBasePlayer *pPlayer ) WRITE_BYTE( m_iClip ); MESSAGE_END(); - m_iClientFov = m_pPlayer->pev->fov; m_iClientClip = m_iClip; m_iClientWeaponState = state; pPlayer->m_fWeapon = TRUE; } - if ( m_pNext ) m_pNext->UpdateClientData( pPlayer ); + if ( m_pNext ) + m_pNext->UpdateClientData( pPlayer ); return 1; } -void CBasePlayerWeapon::SendWeaponAnim( int iAnim, float fps ) +void CBasePlayerWeapon::SendWeaponAnim( int iAnim, int skiplocal, int body ) { - float framerate = 1.0f; // fps multiplier - - if( fps ) - { - studiohdr_t *pstudiohdr = (studiohdr_t *)GET_MODEL_PTR( ENT( pev )); - if( pstudiohdr ) - { - mstudioseqdesc_t *pseqdesc; - - pseqdesc = (mstudioseqdesc_t *)((byte *)pstudiohdr + pstudiohdr->seqindex) + iAnim; - framerate = fps / pseqdesc->fps; - } - } + if ( UseDecrement() ) + skiplocal = 1; + else + skiplocal = 0; m_pPlayer->pev->weaponanim = iAnim; - // calculate additional body for special effects - pev->body = (pev->body % NUM_HANDS) + NUM_HANDS * m_iBody; +#if defined( CLIENT_WEAPONS ) + if ( skiplocal && ENGINE_CANSKIP( m_pPlayer->edict() ) ) + return; +#endif - MESSAGE_BEGIN( MSG_ONE, gmsgWeaponAnim, NULL, m_pPlayer->pev ); - WRITE_BYTE( iAnim ); // sequence number - WRITE_BYTE( pev->body ); // weaponmodel bodygroup. - WRITE_BYTE( framerate * 16 ); + MESSAGE_BEGIN( MSG_ONE, SVC_WEAPONANIM, NULL, m_pPlayer->pev ); + WRITE_BYTE( iAnim ); // sequence number + WRITE_BYTE( pev->body ); // weaponmodel bodygroup. MESSAGE_END(); } BOOL CBasePlayerWeapon :: AddPrimaryAmmo( int iCount, char *szName, int iMaxClip, int iMaxCarry ) { int iIdAmmo; + if (iMaxClip < 1) { m_iClip = -1; @@ -1106,7 +940,16 @@ BOOL CBasePlayerWeapon :: AddSecondaryAmmo( int iCount, char *szName, int iMax ) //========================================================= BOOL CBasePlayerWeapon :: IsUseable( void ) { - return CanDeploy(); + if ( m_iClip <= 0 ) + { + if ( m_pPlayer->m_rgAmmo[ PrimaryAmmoIndex() ] <= 0 && iMaxAmmo1() != -1 ) + { + // clip is empty (or nonexistant) and the player has no more ammo of this type. + return FALSE; + } + } + + return TRUE; } BOOL CBasePlayerWeapon :: CanDeploy( void ) @@ -1139,102 +982,59 @@ BOOL CBasePlayerWeapon :: CanDeploy( void ) return TRUE; } -BOOL CBasePlayerWeapon :: DefaultDeploy( char *szViewModel, char *szWeaponModel, int iAnim, char *szAnimExt, float fDrawTime ) +BOOL CBasePlayerWeapon :: DefaultDeploy( char *szViewModel, char *szWeaponModel, int iAnim, char *szAnimExt, int skiplocal /* = 0 */, int body ) { - if( PLAYER_HAS_SUIT ) - pev->body |= GORDON_SUIT; - else pev->body &= ~GORDON_SUIT; + if (!CanDeploy( )) + return FALSE; - if( IsMultiplayer() && !CanDeploy( )) return FALSE; - m_iLastSkin = -1;//reset last skin info for new weapon - m_iClientFov = -1;// reset last fov info for new weapon - b_Restored = TRUE;//no need update if deploy m_pPlayer->TabulateAmmo(); m_pPlayer->pev->viewmodel = MAKE_STRING(szViewModel); m_pPlayer->pev->weaponmodel = MAKE_STRING(szWeaponModel); strcpy( m_pPlayer->m_szAnimExtention, szAnimExt ); - SendWeaponAnim( iAnim ); + SendWeaponAnim( iAnim, skiplocal, body ); - m_pPlayer->m_flNextAttack = UTIL_WeaponTimeBase() + fDrawTime;//Custom time for deploy - m_flTimeWeaponIdle = UTIL_WeaponTimeBase() + fDrawTime + 0.5; //Make half-second delay beetwen draw and idle animation + m_pPlayer->m_flNextAttack = UTIL_WeaponTimeBase() + 0.5; + m_flTimeWeaponIdle = UTIL_WeaponTimeBase() + 1.0; return TRUE; } -BOOL CBasePlayerWeapon :: DefaultReload( int iClipSize, int iAnim, float fDelay ) +BOOL CBasePlayerWeapon :: DefaultReload( int iClipSize, int iAnim, float fDelay, int body ) { if (m_pPlayer->m_rgAmmo[m_iPrimaryAmmoType] <= 0) return FALSE; int j = min(iClipSize - m_iClip, m_pPlayer->m_rgAmmo[m_iPrimaryAmmoType]); - if (j == 0) return FALSE; + if (j == 0) + return FALSE; m_pPlayer->m_flNextAttack = UTIL_WeaponTimeBase() + fDelay; - SendWeaponAnim( iAnim ); + //!!UNDONE -- reload sound goes here !!! + SendWeaponAnim( iAnim, UseDecrement() ? 1 : 0 ); m_fInReload = TRUE; - m_flTimeWeaponIdle = UTIL_WeaponTimeBase() + fDelay + 0.5; + m_flTimeWeaponIdle = UTIL_WeaponTimeBase() + 3; return TRUE; } -BOOL CBasePlayerWeapon :: PlayEmptySound( int iSoundType ) +BOOL CBasePlayerWeapon :: PlayEmptySound( void ) { - //play custom empty sound - //0 - default cock sound - //1 - electro sound - //2 - electro sound 2 - //3 - flashlight sound - //4 - no sound - if (m_iPlayEmptySound) { - PLAYBACK_EVENT_FULL( 0, m_pPlayer->edict(), m_usPlayEmptySound, 0.0, (float *)&g_vecZero, (float *)&g_vecZero, 0.0, 0.0, iSoundType, 0, 0, 0 ); + EMIT_SOUND(ENT(m_pPlayer->pev), CHAN_WEAPON, "weapons/357_cock1.wav", 0.8, ATTN_NORM); m_iPlayEmptySound = 0; - return FALSE; - } - return FALSE; -} - -//restore pev->body and thirdperson animation after save\load -void CBasePlayerWeapon :: RestoreBody ( void ) -{ - //Global function. restored and just set pev->body & pev->skin - //after each save\load for weapons. For use - insert her in WeaponIdle() - //e.g. see in glock.cpp - RestoreBody ("onehanded"); - - if(!b_Restored ) - { //calculate additional body for special effects - pev->body = (pev->body % NUM_HANDS) + NUM_HANDS * m_iBody; - MESSAGE_BEGIN( MSG_ONE, gmsgSetBody, NULL, m_pPlayer->pev ); - WRITE_BYTE( pev->body ); //weaponmodel body - MESSAGE_END(); - - //restore idle animation and hands position - m_flTimeWeaponIdle = UTIL_WeaponTimeBase(); - - //saved in CBasePlayer - //strcpy( m_pPlayer->m_szAnimExtention, szAnimExt ); - - b_Restored = TRUE;//reset after next save/load - } - - //update weapon skin - if( m_iLastSkin != pev->skin) - { - MESSAGE_BEGIN( MSG_ONE, gmsgSetSkin, NULL, m_pPlayer->pev ); - WRITE_BYTE( pev->skin ); //weaponmodel skin. - MESSAGE_END(); - m_iLastSkin = pev->skin; + return 0; } + return 0; } void CBasePlayerWeapon :: ResetEmptySound( void ) { - m_iPlayEmptySound = 1;//reset empty sound + m_iPlayEmptySound = 1; } //========================================================= @@ -1251,7 +1051,7 @@ int CBasePlayerWeapon::SecondaryAmmoIndex( void ) return -1; } -void CBasePlayerWeapon::Holster( ) +void CBasePlayerWeapon::Holster( int skiplocal /* = 0 */ ) { m_fInReload = FALSE; // cancel any reload in progress. m_pPlayer->pev->viewmodel = 0; @@ -1263,9 +1063,9 @@ void CBasePlayerAmmo::Spawn( void ) pev->movetype = MOVETYPE_TOSS; pev->solid = SOLID_TRIGGER; UTIL_SetSize(pev, Vector(-16, -16, 0), Vector(16, 16, 16)); - UTIL_SetOrigin( this, pev->origin ); + UTIL_SetOrigin( pev, pev->origin ); - SetTouch(&CBasePlayerAmmo:: DefaultTouch ); + SetTouch( DefaultTouch ); } CBaseEntity* CBasePlayerAmmo::Respawn( void ) @@ -1273,10 +1073,10 @@ CBaseEntity* CBasePlayerAmmo::Respawn( void ) pev->effects |= EF_NODRAW; SetTouch( NULL ); - UTIL_SetOrigin( this, g_pGameRules->VecAmmoRespawnSpot( this ) );// move to wherever I'm supposed to repawn. + UTIL_SetOrigin( pev, g_pGameRules->VecAmmoRespawnSpot( this ) );// move to wherever I'm supposed to repawn. - SetThink(&CBasePlayerAmmo:: Materialize ); - AbsoluteNextThink( g_pGameRules->FlAmmoRespawnTime( this ) ); + SetThink( Materialize ); + pev->nextthink = g_pGameRules->FlAmmoRespawnTime( this ); return this; } @@ -1291,7 +1091,7 @@ void CBasePlayerAmmo::Materialize( void ) pev->effects |= EF_MUZZLEFLASH; } - SetTouch(&CBasePlayerAmmo:: DefaultTouch ); + SetTouch( DefaultTouch ); } void CBasePlayerAmmo :: DefaultTouch( CBaseEntity *pOther ) @@ -1301,9 +1101,6 @@ void CBasePlayerAmmo :: DefaultTouch( CBaseEntity *pOther ) return; } - if (!UTIL_IsMasterTriggered(m_sMaster, m_pPlayer)) // - return ; // AJH allows for locked weapons - if (AddAmmo( pOther )) { if ( g_pGameRules->AmmoShouldRespawn( this ) == GR_AMMO_RESPAWN_YES ) @@ -1313,17 +1110,16 @@ void CBasePlayerAmmo :: DefaultTouch( CBaseEntity *pOther ) else { SetTouch( NULL ); - SetThink(&CBasePlayerAmmo ::SUB_Remove); - SetNextThink( 0.1 ); + SetThink(SUB_Remove); + pev->nextthink = gpGlobals->time + .1; } - SUB_UseTargets( pOther, USE_TOGGLE, 0 ); //AJH now ammo can trigger stuff too } else if (gEvilImpulse101) { // evil impulse 101 hack, kill always SetTouch( NULL ); - SetThink(&CBasePlayerAmmo ::SUB_Remove); - SetNextThink( 0.1 ); + SetThink(SUB_Remove); + pev->nextthink = gpGlobals->time + .1; } } @@ -1379,7 +1175,6 @@ int CBasePlayerWeapon::ExtractClipAmmo( CBasePlayerWeapon *pWeapon ) //========================================================= void CBasePlayerWeapon::RetireWeapon( void ) { - Holster(); // first, no viewmodel at all. m_pPlayer->pev->viewmodel = iStringNull; m_pPlayer->pev->weaponmodel = iStringNull; @@ -1388,43 +1183,6 @@ void CBasePlayerWeapon::RetireWeapon( void ) g_pGameRules->GetNextBestWeapon( m_pPlayer, this ); } -//========================================================= -// LRC - remove the specified ammo from this gun -//========================================================= -void CBasePlayerWeapon::DrainClip(CBasePlayer* pPlayer, BOOL keep, int i9mm, int i357, int iBuck, int iBolt, int iARGren, int iRock, int iUranium, int iSatchel, int iSnark, int iTrip, int iGren ) -{ - int iPAI = PrimaryAmmoIndex(); - int iAmt; - if (iPAI == -1) return; - else if (iPAI == pPlayer->GetAmmoIndex("9mm")) iAmt = i9mm; - else if (iPAI == pPlayer->GetAmmoIndex("357")) iAmt = i357; - else if (iPAI == pPlayer->GetAmmoIndex("buckshot")) iAmt = iBuck; - else if (iPAI == pPlayer->GetAmmoIndex("bolts")) iAmt = iBolt; - else if (iPAI == pPlayer->GetAmmoIndex("ARgrenades")) iAmt = iARGren; - else if (iPAI == pPlayer->GetAmmoIndex("uranium")) iAmt = iUranium; - else if (iPAI == pPlayer->GetAmmoIndex("rockets")) iAmt = iRock; - else if (iPAI == pPlayer->GetAmmoIndex("Satchel Charge")) iAmt = iSatchel; - else if (iPAI == pPlayer->GetAmmoIndex("Snarks")) iAmt = iSnark; - else if (iPAI == pPlayer->GetAmmoIndex("Trip Mine")) iAmt = iTrip; - else if (iPAI == pPlayer->GetAmmoIndex("Hand Grenade")) iAmt = iGren; - else return; - - if (iAmt > 0) - { - m_iClip -= iAmt; - if (m_iClip < 0) m_iClip = 0; - } - else if (iAmt >= -1) - { - m_iClip = 0; - } - - // if we're not keeping the gun, transfer the remainder of its clip - // into the main ammo store - if (!keep) - pPlayer->m_rgAmmo[iPAI] = m_iClip; -} - //********************************************************* // weaponbox code: //********************************************************* @@ -1462,7 +1220,7 @@ void CWeaponBox :: KeyValue( KeyValueData *pkvd ) } else { - ALERT ( at_debug, "WeaponBox too full! only %d ammotypes allowed\n", MAX_AMMO_SLOTS ); + ALERT ( at_console, "WeaponBox too full! only %d ammotypes allowed\n", MAX_AMMO_SLOTS ); } } @@ -1497,8 +1255,8 @@ void CWeaponBox::Kill( void ) while ( pWeapon ) { - pWeapon->SetThink(&CBasePlayerItem::SUB_Remove); - pWeapon->SetNextThink( 0.1 ); + pWeapon->SetThink(SUB_Remove); + pWeapon->pev->nextthink = gpGlobals->time + 0.1; pWeapon = pWeapon->m_pNext; } } @@ -1640,7 +1398,7 @@ BOOL CWeaponBox::PackAmmo( int iszName, int iCount ) if ( FStringNull( iszName ) ) { // error here - ALERT ( at_debug, "NULL String in PackAmmo!\n" ); + ALERT ( at_console, "NULL String in PackAmmo!\n" ); return FALSE; } @@ -1690,7 +1448,7 @@ int CWeaponBox::GiveAmmo( int iCount, char *szName, int iMax, int *pIndex/* = NU return i; } - ALERT( at_debug, "out of named ammo slots\n"); + ALERT( at_console, "out of named ammo slots\n"); return i; } @@ -1752,15 +1510,70 @@ void CWeaponBox::SetObjectCollisionBox( void ) void CBasePlayerWeapon::PrintState( void ) { - ALERT( at_debug, "primary: %f\n", m_flNextPrimaryAttack ); - ALERT( at_debug, "idle : %f\n", m_flTimeWeaponIdle ); + ALERT( at_console, "primary: %f\n", m_flNextPrimaryAttack ); + ALERT( at_console, "idle : %f\n", m_flTimeWeaponIdle ); -// ALERT( at_debug, "nextrl : %f\n", m_flNextReload ); -// ALERT( at_debug, "nextpum: %f\n", m_flPumpTime ); +// ALERT( at_console, "nextrl : %f\n", m_flNextReload ); +// ALERT( at_console, "nextpum: %f\n", m_flPumpTime ); -// ALERT( at_debug, "m_frt : %f\n", m_fReloadTime ); - ALERT( at_debug, "m_finre: %i\n", m_fInReload ); -// ALERT( at_debug, "m_finsr: %i\n", m_fInSpecialReload ); +// ALERT( at_console, "m_frt : %f\n", m_fReloadTime ); + ALERT( at_console, "m_finre: %i\n", m_fInReload ); +// ALERT( at_console, "m_finsr: %i\n", m_fInSpecialReload ); - ALERT( at_debug, "m_iclip: %i\n", m_iClip ); + ALERT( at_console, "m_iclip: %i\n", m_iClip ); } + + +TYPEDESCRIPTION CRpg::m_SaveData[] = +{ + DEFINE_FIELD( CRpg, m_fSpotActive, FIELD_INTEGER ), + DEFINE_FIELD( CRpg, m_cActiveRockets, FIELD_INTEGER ), +}; +IMPLEMENT_SAVERESTORE( CRpg, CBasePlayerWeapon ); + +TYPEDESCRIPTION CRpgRocket::m_SaveData[] = +{ + DEFINE_FIELD( CRpgRocket, m_flIgniteTime, FIELD_TIME ), + DEFINE_FIELD( CRpgRocket, m_pLauncher, FIELD_CLASSPTR ), +}; +IMPLEMENT_SAVERESTORE( CRpgRocket, CGrenade ); + +TYPEDESCRIPTION CShotgun::m_SaveData[] = +{ + DEFINE_FIELD( CShotgun, m_flNextReload, FIELD_TIME ), + DEFINE_FIELD( CShotgun, m_fInSpecialReload, FIELD_INTEGER ), + DEFINE_FIELD( CShotgun, m_flNextReload, FIELD_TIME ), + // DEFINE_FIELD( CShotgun, m_iShell, FIELD_INTEGER ), + DEFINE_FIELD( CShotgun, m_flPumpTime, FIELD_TIME ), +}; +IMPLEMENT_SAVERESTORE( CShotgun, CBasePlayerWeapon ); + +TYPEDESCRIPTION CGauss::m_SaveData[] = +{ + DEFINE_FIELD( CGauss, m_fInAttack, FIELD_INTEGER ), +// DEFINE_FIELD( CGauss, m_flStartCharge, FIELD_TIME ), +// DEFINE_FIELD( CGauss, m_flPlayAftershock, FIELD_TIME ), +// DEFINE_FIELD( CGauss, m_flNextAmmoBurn, FIELD_TIME ), + DEFINE_FIELD( CGauss, m_fPrimaryFire, FIELD_BOOLEAN ), +}; +IMPLEMENT_SAVERESTORE( CGauss, CBasePlayerWeapon ); + +TYPEDESCRIPTION CEgon::m_SaveData[] = +{ +// DEFINE_FIELD( CEgon, m_pBeam, FIELD_CLASSPTR ), +// DEFINE_FIELD( CEgon, m_pNoise, FIELD_CLASSPTR ), +// DEFINE_FIELD( CEgon, m_pSprite, FIELD_CLASSPTR ), + DEFINE_FIELD( CEgon, m_shootTime, FIELD_TIME ), + DEFINE_FIELD( CEgon, m_fireState, FIELD_INTEGER ), + DEFINE_FIELD( CEgon, m_fireMode, FIELD_INTEGER ), + DEFINE_FIELD( CEgon, m_shakeTime, FIELD_TIME ), + DEFINE_FIELD( CEgon, m_flAmmoUseTime, FIELD_TIME ), +}; +IMPLEMENT_SAVERESTORE( CEgon, CBasePlayerWeapon ); + +TYPEDESCRIPTION CSatchel::m_SaveData[] = +{ + DEFINE_FIELD( CSatchel, m_chargeReady, FIELD_INTEGER ), +}; +IMPLEMENT_SAVERESTORE( CSatchel, CBasePlayerWeapon ); + diff --git a/dlls/weapons.h b/dlls/weapons.h new file mode 100644 index 00000000..aaa98247 --- /dev/null +++ b/dlls/weapons.h @@ -0,0 +1,1015 @@ +/*** +* +* Copyright (c) 1996-2002, 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. +* +****/ +#ifndef WEAPONS_H +#define WEAPONS_H + +#include "effects.h" + +class CBasePlayer; +extern int gmsgWeapPickup; + +void DeactivateSatchels( CBasePlayer *pOwner ); + +// Contact Grenade / Timed grenade / Satchel Charge +class CGrenade : public CBaseMonster +{ +public: + void Spawn( void ); + + typedef enum { SATCHEL_DETONATE = 0, SATCHEL_RELEASE } SATCHELCODE; + + static CGrenade *ShootTimed( entvars_t *pevOwner, Vector vecStart, Vector vecVelocity, float time ); + static CGrenade *ShootContact( entvars_t *pevOwner, Vector vecStart, Vector vecVelocity ); + static CGrenade *ShootSatchelCharge( entvars_t *pevOwner, Vector vecStart, Vector vecVelocity ); + static void UseSatchelCharges( entvars_t *pevOwner, SATCHELCODE code ); + + void Explode( Vector vecSrc, Vector vecAim ); + void Explode( TraceResult *pTrace, int bitsDamageType ); + void EXPORT Smoke( void ); + + void EXPORT BounceTouch( CBaseEntity *pOther ); + void EXPORT SlideTouch( CBaseEntity *pOther ); + void EXPORT ExplodeTouch( CBaseEntity *pOther ); + void EXPORT DangerSoundThink( void ); + void EXPORT PreDetonate( void ); + void EXPORT Detonate( void ); + void EXPORT DetonateUse( CBaseEntity *pActivator, CBaseEntity *pCaller, USE_TYPE useType, float value ); + void EXPORT TumbleThink( void ); + + virtual void BounceSound( void ); + virtual int BloodColor( void ) { return DONT_BLEED; } + virtual void Killed( entvars_t *pevAttacker, int iGib ); + + BOOL m_fRegisteredSound;// whether or not this grenade has issued its DANGER sound to the world sound list yet. +}; + + +// constant items +#define ITEM_HEALTHKIT 1 +#define ITEM_ANTIDOTE 2 +#define ITEM_SECURITY 3 +#define ITEM_BATTERY 4 + +#define WEAPON_NONE 0 +#define WEAPON_CROWBAR 1 +#define WEAPON_GLOCK 2 +#define WEAPON_PYTHON 3 +#define WEAPON_MP5 4 +#define WEAPON_CHAINGUN 5 +#define WEAPON_CROSSBOW 6 +#define WEAPON_SHOTGUN 7 +#define WEAPON_RPG 8 +#define WEAPON_GAUSS 9 +#define WEAPON_EGON 10 +#define WEAPON_HORNETGUN 11 +#define WEAPON_HANDGRENADE 12 +#define WEAPON_TRIPMINE 13 +#define WEAPON_SATCHEL 14 +#define WEAPON_SNARK 15 + +#define WEAPON_ALLWEAPONS (~(1<absmin = pev->origin + Vector(-16, -16, -5); + pev->absmax = pev->origin + Vector(16, 16, 28); + } + + void PrimaryAttack( void ); + BOOL Deploy( void ); + void Holster( int skiplocal = 0 ); + void WeaponIdle( void ); + + virtual BOOL UseDecrement( void ) + { +#if defined( CLIENT_WEAPONS ) + return TRUE; +#else + return FALSE; +#endif + } + +private: + unsigned short m_usTripFire; + +}; + +class CSqueak : public CBasePlayerWeapon +{ +public: + void Spawn( void ); + void Precache( void ); + int iItemSlot( void ) { return 5; } + int GetItemInfo(ItemInfo *p); + + void PrimaryAttack( void ); + void SecondaryAttack( void ); + BOOL Deploy( void ); + void Holster( int skiplocal = 0 ); + void WeaponIdle( void ); + int m_fJustThrown; + + virtual BOOL UseDecrement( void ) + { +#if defined( CLIENT_WEAPONS ) + return TRUE; +#else + return FALSE; +#endif + } + +private: + unsigned short m_usSnarkFire; +}; + + +#endif // WEAPONS_H diff --git a/spirit/world.cpp b/dlls/world.cpp similarity index 83% rename from spirit/world.cpp rename to dlls/world.cpp index 7636468b..abd86879 100644 --- a/spirit/world.cpp +++ b/dlls/world.cpp @@ -33,7 +33,6 @@ #include "weapons.h" #include "gamerules.h" #include "teamplay_gamerules.h" -#include "movewith.h" //LRC extern CGraph WorldGraph; extern CSoundEnt *pSoundEnt; @@ -106,7 +105,7 @@ BODY QUE class CDecal : public CBaseEntity { public: - void PostSpawn( void ); + void Spawn( void ); void KeyValue( KeyValueData *pkvd ); void EXPORT StaticDecal( void ); void EXPORT TriggerDecal( CBaseEntity *pActivator, CBaseEntity *pCaller, USE_TYPE useType, float value ); @@ -115,7 +114,7 @@ public: LINK_ENTITY_TO_CLASS( infodecal, CDecal ); // UNDONE: These won't get sent to joining players in multi-player -void CDecal :: PostSpawn( void ) +void CDecal :: Spawn( void ) { if ( pev->skin < 0 || (gpGlobals->deathmatch && FBitSet( pev->spawnflags, SF_DECAL_NOTINDEATHMATCH )) ) { @@ -125,15 +124,15 @@ void CDecal :: PostSpawn( void ) if ( FStringNull ( pev->targetname ) ) { - SetThink(&CDecal :: StaticDecal ); + SetThink( StaticDecal ); // if there's no targetname, the decal will spray itself on as soon as the world is done spawning. - SetNextThink( 0.3 ); + pev->nextthink = gpGlobals->time; } else { // if there IS a targetname, the decal sprays itself on when it is triggered. - SetThink(&CDecal :: SUB_DoNothing ); - SetUse(&CDecal ::TriggerDecal); + SetThink ( SUB_DoNothing ); + SetUse(TriggerDecal); } } @@ -146,7 +145,7 @@ void CDecal :: TriggerDecal ( CBaseEntity *pActivator, CBaseEntity *pCaller, USE UTIL_TraceLine( pev->origin - Vector(5,5,5), pev->origin + Vector(5,5,5), ignore_monsters, ENT(pev), &trace ); - MESSAGE_BEGIN( MSG_BROADCAST, gmsgTempEntity); + MESSAGE_BEGIN( MSG_BROADCAST, SVC_TEMPENTITY); WRITE_BYTE( TE_BSPDECAL ); WRITE_COORD( pev->origin.x ); WRITE_COORD( pev->origin.y ); @@ -154,12 +153,12 @@ void CDecal :: TriggerDecal ( CBaseEntity *pActivator, CBaseEntity *pCaller, USE WRITE_SHORT( (int)pev->skin ); entityIndex = (short)ENTINDEX(trace.pHit); WRITE_SHORT( entityIndex ); - if( entityIndex > 0 ) + if ( entityIndex ) WRITE_SHORT( (int)VARS(trace.pHit)->modelindex ); MESSAGE_END(); - SetThink(&CDecal :: SUB_Remove ); - SetNextThink( 0.1 ); + SetThink( SUB_Remove ); + pev->nextthink = gpGlobals->time + 0.1; } @@ -171,7 +170,7 @@ void CDecal :: StaticDecal( void ) UTIL_TraceLine( pev->origin - Vector(5,5,5), pev->origin + Vector(5,5,5), ignore_monsters, ENT(pev), &trace ); entityIndex = (short)ENTINDEX(trace.pHit); - if ( entityIndex > 0 ) + if ( entityIndex ) modelIndex = (int)VARS(trace.pHit)->modelindex; else modelIndex = 0; @@ -191,7 +190,7 @@ void CDecal :: KeyValue( KeyValueData *pkvd ) // Found if ( pev->skin >= 0 ) return; - ALERT( at_debug, "Can't find decal %s\n", pkvd->szValue ); + ALERT( at_console, "Can't find decal %s\n", pkvd->szValue ); } else CBaseEntity::KeyValue( pkvd ); @@ -256,7 +255,7 @@ void CopyToBodyQue(entvars_t *pev) pevHead->sequence = pev->sequence; pevHead->animtime = pev->animtime; - UTIL_SetEdictOrigin(g_pBodyQueueHead, pev->origin); + UTIL_SetOrigin(pevHead, pev->origin); UTIL_SetSize(pevHead, pev->mins, pev->maxs); g_pBodyQueueHead = pevHead->owner; } @@ -302,11 +301,11 @@ void CGlobalState :: DumpGlobals( void ) static char *estates[] = { "Off", "On", "Dead" }; globalentity_t *pTest; - ALERT( at_debug, "-- Globals --\n" ); + ALERT( at_console, "-- Globals --\n" ); pTest = m_pList; while ( pTest ) { - ALERT( at_debug, "%s: %s (%s)\n", pTest->name, pTest->levelName, estates[pTest->state] ); + ALERT( at_console, "%s: %s (%s)\n", pTest->name, pTest->levelName, estates[pTest->state] ); pTest = pTest->pNext; } } @@ -375,13 +374,13 @@ int CGlobalState::Save( CSave &save ) int i; globalentity_t *pEntity; - if ( !save.WriteFields( "cGLOBAL", "GLOBAL", this, m_SaveData, ARRAYSIZE(m_SaveData) ) ) + if ( !save.WriteFields( "GLOBAL", this, m_SaveData, ARRAYSIZE(m_SaveData) ) ) return 0; pEntity = m_pList; for ( i = 0; i < m_listCount && pEntity; i++ ) { - if ( !save.WriteFields( "cGENT", "GENT", pEntity, gGlobalEntitySaveData, ARRAYSIZE(gGlobalEntitySaveData) ) ) + if ( !save.WriteFields( "GENT", pEntity, gGlobalEntitySaveData, ARRAYSIZE(gGlobalEntitySaveData) ) ) return 0; pEntity = pEntity->pNext; @@ -395,6 +394,7 @@ int CGlobalState::Restore( CRestore &restore ) int i, listCount; globalentity_t tmpEntity; + ClearStates(); if ( !restore.ReadFields( "GLOBAL", this, m_SaveData, ARRAYSIZE(m_SaveData) ) ) return 0; @@ -453,8 +453,6 @@ void ResetGlobalState( void ) gInitHUD = TRUE; // Init the HUD on a new game / load game } - - // moved CWorld class definition to cbase.h //======================= // CWorld @@ -467,14 +465,10 @@ LINK_ENTITY_TO_CLASS( worldspawn, CWorld ); #define SF_WORLD_DARK 0x0001 // Fade from black at startup #define SF_WORLD_TITLE 0x0002 // Display game title at startup #define SF_WORLD_FORCETEAM 0x0004 // Force teams -//#define SF_WORLD_STARTSUIT 0x0008 // LRC- Start this level with an HEV suit! extern DLL_GLOBAL BOOL g_fGameOver; float g_flWeaponCheat; -BOOL g_startSuit; //LRC -BOOL g_allowGJump; - void CWorld :: Spawn( void ) { g_fGameOver = FALSE; @@ -483,12 +477,6 @@ void CWorld :: Spawn( void ) void CWorld :: Precache( void ) { - //LRC - set up the world lists - g_pWorld = this; - m_pAssistLink = NULL; - m_pFirstAlias = NULL; -// ALERT(at_console, "Clearing AssistList\n"); - g_pLastSpawn = NULL; #if 1 @@ -511,18 +499,6 @@ void CWorld :: Precache( void ) //!!!UNDONE why is there so much Spawn code in the Precache function? I'll just keep it here -/* if ( WorldGraph.m_fGraphPresent && !WorldGraph.m_fGraphPointersSet ) - { - if ( !WorldGraph.FSetGraphPointers() ) - { - ALERT ( at_debug, "**Graph pointers were not set!\n"); - } - else - { - ALERT ( at_debug, "**Graph Pointers Set!\n" ); - } - }*/ - ///!!!LATER - do we want a sound ent in deathmatch? (sjb) //pSoundEnt = CBaseEntity::Create( "soundent", g_vecZero, g_vecZero, edict() ); pSoundEnt = GetClassPtr( ( CSoundEnt *)NULL ); @@ -530,8 +506,9 @@ void CWorld :: Precache( void ) if ( !pSoundEnt ) { - ALERT ( at_debug, "**COULD NOT CREATE SOUNDENT**\n" ); + ALERT ( at_console, "**COULD NOT CREATE SOUNDENT**\n" ); } + InitBodyQue(); // init sentence group playback stuff from sentences.txt. @@ -576,25 +553,57 @@ void CWorld :: Precache( void ) PRECACHE_SOUND ("weapons/ric3.wav"); PRECACHE_SOUND ("weapons/ric4.wav"); PRECACHE_SOUND ("weapons/ric5.wav"); - - PRECACHE_MODEL( "sprites/null.spr" ); //LRC // // Setup light animation tables. 'a' is total darkness, 'z' is maxbright. // - int i; // 0 normal - for (i = 0; i <= 13; i++) - { - LIGHT_STYLE(i, (char*)STRING(GetStdLightStyle(i))); - } + LIGHT_STYLE(0, "m"); + + // 1 FLICKER (first variety) + LIGHT_STYLE(1, "mmnmmommommnonmmonqnmmo"); + + // 2 SLOW STRONG PULSE + LIGHT_STYLE(2, "abcdefghijklmnopqrstuvwxyzyxwvutsrqponmlkjihgfedcba"); + + // 3 CANDLE (first variety) + LIGHT_STYLE(3, "mmmmmaaaaammmmmaaaaaabcdefgabcdefg"); + + // 4 FAST STROBE + LIGHT_STYLE(4, "mamamamamama"); + + // 5 GENTLE PULSE 1 + LIGHT_STYLE(5,"jklmnopqrstuvwxyzyxwvutsrqponmlkj"); + + // 6 FLICKER (second variety) + LIGHT_STYLE(6, "nmonqnmomnmomomno"); + + // 7 CANDLE (second variety) + LIGHT_STYLE(7, "mmmaaaabcdefgmmmmaaaammmaamm"); + + // 8 CANDLE (third variety) + LIGHT_STYLE(8, "mmmaaammmaaammmabcdefaaaammmmabcdefmmmaaaa"); + + // 9 SLOW STROBE (fourth variety) + LIGHT_STYLE(9, "aaaaaaaazzzzzzzz"); + + // 10 FLUORESCENT FLICKER + LIGHT_STYLE(10, "mmamammmmammamamaaamammma"); + + // 11 SLOW PULSE NOT FADE TO BLACK + LIGHT_STYLE(11, "abcdefghijklmnopqrrqponmlkjihgfedcba"); + + // 12 UNDERWATER LIGHT MUTATION + // this light only distorts the lightmap - no contribution + // is made to the brightness of affected surfaces + LIGHT_STYLE(12, "mmnnmmnnnmmnn"); // styles 32-62 are assigned by the light program for switchable lights // 63 testing LIGHT_STYLE(63, "a"); - for (i = 0; i < ARRAYSIZE(gDecals); i++ ) + for ( int i = 0; i < ARRAYSIZE(gDecals); i++ ) gDecals[i].index = DECAL_INDEX( gDecals[i].name ); // init the WorldGraph. @@ -609,19 +618,19 @@ void CWorld :: Precache( void ) {// Load the node graph for this level if ( !WorldGraph.FLoadGraph ( (char *)STRING( gpGlobals->mapname ) ) ) {// couldn't load, so alloc and prepare to build a graph. - ALERT ( at_debug, "*Error opening .NOD file\n" ); + ALERT ( at_console, "*Error opening .NOD file\n" ); WorldGraph.AllocNodes (); } else { - ALERT ( at_debug, "\n*Graph Loaded!\n" ); + ALERT ( at_console, "\n*Graph Loaded!\n" ); } } if ( pev->speed > 0 ) CVAR_SET_FLOAT( "sv_zmax", pev->speed ); else - CVAR_SET_FLOAT( "sv_zmax", 0 ); // let the renderer calculate optimal value + CVAR_SET_FLOAT( "sv_zmax", 0 ); // g-cont. let the renderer calculate optimal value // g-cont. moved here to right restore global WaveHeight on save\restore level CVAR_SET_FLOAT( "sv_wateramp", pev->scale ); @@ -632,10 +641,10 @@ void CWorld :: Precache( void ) CBaseEntity *pEntity = CBaseEntity::Create( "env_message", g_vecZero, g_vecZero, NULL ); if ( pEntity ) { - pEntity->SetThink(&CWorld::SUB_CallUseToggle ); + pEntity->SetThink( SUB_CallUseToggle ); pEntity->pev->message = pev->netname; pev->netname = 0; - pEntity->SetNextThink( 0.3 ); + pEntity->pev->nextthink = gpGlobals->time + 0.3; pEntity->pev->spawnflags = SF_MESSAGE_ONCE; } } @@ -645,15 +654,15 @@ void CWorld :: Precache( void ) else CVAR_SET_FLOAT( "v_dark", 0.0 ); - pev->spawnflags &= ~SF_WORLD_DARK; // don't apply fade after save\restore - + pev->spawnflags &= ~SF_WORLD_DARK; // g-cont. don't apply fade after save\restore + if ( pev->spawnflags & SF_WORLD_TITLE ) gDisplayTitle = TRUE; // display the game title if this key is set else gDisplayTitle = FALSE; - pev->spawnflags &= ~SF_WORLD_TITLE; // don't show logo after save\restore - + pev->spawnflags &= ~SF_WORLD_TITLE; // g-cont. don't show logo after save\restore + if ( pev->spawnflags & SF_WORLD_FORCETEAM ) { CVAR_SET_FLOAT( "mp_defaultteam", 1 ); @@ -663,6 +672,7 @@ void CWorld :: Precache( void ) CVAR_SET_FLOAT( "mp_defaultteam", 0 ); } + // g-cont. moved here so cheats will working on restore level g_flWeaponCheat = CVAR_GET_FLOAT( "sv_cheats" ); // Is the impulse 101 command allowed? } @@ -672,7 +682,7 @@ void CWorld :: Precache( void ) // void CWorld :: KeyValue( KeyValueData *pkvd ) { - if( FStrEq( pkvd->szKeyName, "skyname" )) + if ( FStrEq(pkvd->szKeyName, "skyname") ) { // Sent over net now. CVAR_SET_STRING( "sv_skyname", pkvd->szValue ); @@ -680,6 +690,7 @@ void CWorld :: KeyValue( KeyValueData *pkvd ) } else if ( FStrEq(pkvd->szKeyName, "sounds") ) { + gpGlobals->cdAudioTrack = atoi(pkvd->szValue); pkvd->fHandled = TRUE; } else if ( FStrEq(pkvd->szKeyName, "WaveHeight") ) @@ -734,26 +745,6 @@ void CWorld :: KeyValue( KeyValueData *pkvd ) } pkvd->fHandled = TRUE; } -//LRC- let map designers start the player with his suit already on - else if ( FStrEq(pkvd->szKeyName, "startsuit") ) - { - g_startSuit = atoi(pkvd->szValue); - pkvd->fHandled = TRUE; - } - else if ( FStrEq(pkvd->szKeyName, "allowmonsters") ) - { - CVAR_SET_FLOAT( "mp_allowmonsters", atof(pkvd->szValue) ); - pkvd->fHandled = TRUE; - } -//LRC- ends - - //AJH- Gauss Jump in single play - else if ( FStrEq(pkvd->szKeyName, "allow_sp_gjump") ) - { - g_allowGJump = atoi(pkvd->szValue); - pkvd->fHandled = TRUE; - } - else CBaseEntity::KeyValue( pkvd ); } diff --git a/dlls/wxdebug.h b/dlls/wxdebug.h new file mode 100644 index 00000000..321b1bcd --- /dev/null +++ b/dlls/wxdebug.h @@ -0,0 +1,137 @@ +#ifndef __WXDEBUG__ +#define __WXDEBUG__ + +// This library provides fairly straight forward debugging functionality, this +// is split into two main sections. The first is assertion handling, there are +// three types of assertions provided here. The most commonly used one is the +// ASSERT(condition) macro which will pop up a message box including the file +// and line number if the condition evaluates to FALSE. Then there is the +// EXECUTE_ASSERT macro which is the same as ASSERT except the condition will +// still be executed in NON debug builds. The final type of assertion is the +// KASSERT macro which is more suitable for pure (perhaps kernel) filters as +// the condition is printed onto the debugger rather than in a message box. +// +// The other part of the debug module facilties is general purpose logging. +// This is accessed by calling DbgLog(). The function takes a type and level +// field which define the type of informational string you are presenting and +// it's relative importance. The type field can be a combination (one or more) +// of LOG_TIMING, LOG_TRACE, LOG_MEMORY, LOG_LOCKING and LOG_ERROR. The level +// is a DWORD value where zero defines highest important. Use of zero as the +// debug logging level is to be encouraged ONLY for major errors or events as +// they will ALWAYS be displayed on the debugger. Other debug output has it's +// level matched against the current debug output level stored in the registry +// for this module and if less than the current setting it will be displayed. +// +// Each module or executable has it's own debug output level for each of the +// five types. These are read in when the DbgInitialise function is called +// for DLLs linking to STRMBASE.LIB this is done automatically when the DLL +// is loaded, executables must call it explicitely with the module instance +// handle given to them through the WINMAIN entry point. An executable must +// also call DbgTerminate when they have finished to clean up the resources +// the debug library uses, once again this is done automatically for DLLs + +// These are the five different categories of logging information + +#ifdef _DEBUG + + +enum +{ + LOG_TRACE = 0x00000001, // General tracing + LOG_ENTRY = 0x00000002, // Function entry logging + LOG_EXIT = 0x00000004, // Function exit logging + LOG_MEMORY = 0x00000008, // Memory alloc/free debugging + LOG_ERROR = 0x00000010, // Error notification + LOG_UNUSED0 = 0x00000020, // reserved + LOG_UNUSED1 = 0x00000040, // reserved + LOG_UNUSED2 = 0x00000080, // reserved + LOG_CHUM = 0x00000100, // Chumtoad debugging + LOG_LEECH = 0x00000200, // Leech debugging + LOG_ICHTHYOSAUR = 0x00000400, // Ichthyosaur debugging +}; + + +// These are public but should be called only by the DLLMain function +void WINAPI DbgInitialise(HINSTANCE hInst); +void WINAPI DbgTerminate(); +// These are public but should be called by macro only +void WINAPI DbgKernelAssert(const TCHAR *pCondition,const TCHAR *pFileName,INT iLine); +void WINAPI DbgLogInfo(DWORD Type,DWORD Level,const TCHAR *pFormat,...); +void WINAPI DbgOutString(LPCTSTR psz); + + +// These are the macros that should be used in code. + +#define DBGASSERT(_x_) \ + if (!(_x_)) \ + DbgKernelAssert(TEXT(#_x_),TEXT(__FILE__),__LINE__) + +#define DBGBREAK(_x_) \ + DbgKernelAssert(TEXT(#_x_),TEXT(__FILE__),__LINE__) + +#define DBGASSERTEXECUTE(_x_) DBGASSERT(_x_) + +#define DBGLOG(_x_) DbgLogInfo _x_ + +#define DBGOUT(_x_) DbgOutString(_x_) + +#define ValidateReadPtr(p,cb) \ + {if(IsBadReadPtr((PVOID)p,cb) == TRUE) \ + DBGBREAK("Invalid read pointer");} + +#define ValidateWritePtr(p,cb) \ + {if(IsBadWritePtr((PVOID)p,cb) == TRUE) \ + DBGBREAK("Invalid write pointer");} + +#define ValidateReadWritePtr(p,cb) \ + {ValidateReadPtr(p,cb) ValidateWritePtr(p,cb)} + +#define ValidateStringPtr(p) \ + {if(IsBadStringPtr((LPCTSTR)p,INFINITE) == TRUE) \ + DBGBREAK("Invalid string pointer");} + +#define ValidateStringPtrA(p) \ + {if(IsBadStringPtrA((LPCSTR)p,INFINITE) == TRUE) \ + DBGBREAK("Invalid ANSII string pointer");} + +#define ValidateStringPtrW(p) \ + {if(IsBadStringPtrW((LPCWSTR)p,INFINITE) == TRUE) \ + DBGBREAK("Invalid UNICODE string pointer");} + +#else // !_DEBUG + +// Retail builds make public debug functions inert - WARNING the source +// files do not define or build any of the entry points in debug builds +// (public entry points compile to nothing) so if you go trying to call +// any of the private entry points in your source they won't compile + +#define DBGASSERT(_x_) +#define DBGBREAK(_x_) +#define DBGASSERTEXECUTE(_x_) _x_ +#define DBGLOG(_x_) +#define DBGOUT(_x_) +#define ValidateReadPtr(p,cb) +#define ValidateWritePtr(p,cb) +#define ValidateReadWritePtr(p,cb) +#define ValidateStringPtr(p) +#define ValidateStringPtrA(p) +#define ValidateStringPtrW(p) + +#endif // !_DEBUG + + +#ifndef REMIND + // REMIND macro - generates warning as reminder to complete coding + // (eg) usage: + // + // #pragma message (REMIND("Add automation support")) + + + #define REMINDQUOTE(x) #x + #define REMINDQQUOTE(y) REMINDQUOTE(y) + #define REMIND(str) __FILE__ "(" REMINDQQUOTE(__LINE__) ") : " str +#endif + +#endif // __WXDEBUG__ + + diff --git a/spirit/xen.cpp b/dlls/xen.cpp similarity index 92% rename from spirit/xen.cpp rename to dlls/xen.cpp index b667691e..a52ea6b5 100644 --- a/spirit/xen.cpp +++ b/dlls/xen.cpp @@ -1,584 +1,584 @@ -/*** -* -* Copyright (c) 1996-2002, 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. -* -****/ -#include "extdll.h" -#include "util.h" -#include "cbase.h" -#include "animation.h" -#include "effects.h" - - -#define XEN_PLANT_GLOW_SPRITE "sprites/flare3.spr" -#define XEN_PLANT_HIDE_TIME 5 - - -class CActAnimating : public CBaseAnimating -{ -public: - void SetActivity( Activity act ); - inline Activity GetActivity( void ) { return m_Activity; } - - virtual int ObjectCaps( void ) { return CBaseAnimating :: ObjectCaps() & ~FCAP_ACROSS_TRANSITION; } - - virtual int Save( CSave &save ); - virtual int Restore( CRestore &restore ); - static TYPEDESCRIPTION m_SaveData[]; - -private: - Activity m_Activity; -}; - -TYPEDESCRIPTION CActAnimating::m_SaveData[] = -{ - DEFINE_FIELD( CActAnimating, m_Activity, FIELD_INTEGER ), -}; - -IMPLEMENT_SAVERESTORE( CActAnimating, CBaseAnimating ); - -void CActAnimating :: SetActivity( Activity act ) -{ - int sequence = LookupActivity( act ); - if ( sequence != ACTIVITY_NOT_AVAILABLE ) - { - pev->sequence = sequence; - m_Activity = act; - pev->frame = 0; - ResetSequenceInfo( ); - } -} - - - - -class CXenPLight : public CActAnimating -{ -public: - void Spawn( void ); - void Precache( void ); - void Touch( CBaseEntity *pOther ); - void Think( void ); - - void LightOn( void ); - void LightOff( void ); - - virtual int Save( CSave &save ); - virtual int Restore( CRestore &restore ); - static TYPEDESCRIPTION m_SaveData[]; - -private: - CSprite *m_pGlow; -}; - -LINK_ENTITY_TO_CLASS( xen_plantlight, CXenPLight ); - -TYPEDESCRIPTION CXenPLight::m_SaveData[] = -{ - DEFINE_FIELD( CXenPLight, m_pGlow, FIELD_CLASSPTR ), -}; - -IMPLEMENT_SAVERESTORE( CXenPLight, CActAnimating ); - -void CXenPLight :: Spawn( void ) -{ - Precache(); - - SET_MODEL( ENT(pev), "models/light.mdl" ); - pev->movetype = MOVETYPE_NONE; - pev->solid = SOLID_TRIGGER; - - UTIL_SetSize( pev, Vector(-80,-80,0), Vector(80,80,32)); - SetActivity( ACT_IDLE ); - SetNextThink( 0.1 ); - pev->frame = RANDOM_FLOAT(0,255); - - m_pGlow = CSprite::SpriteCreate( XEN_PLANT_GLOW_SPRITE, pev->origin + Vector(0,0,(pev->mins.z+pev->maxs.z)*0.5), FALSE ); - m_pGlow->SetTransparency( kRenderGlow, pev->rendercolor.x, pev->rendercolor.y, pev->rendercolor.z, pev->renderamt, pev->renderfx ); - m_pGlow->SetAttachment( edict(), 1 ); -} - - -void CXenPLight :: Precache( void ) -{ - PRECACHE_MODEL( "models/light.mdl" ); - PRECACHE_MODEL( XEN_PLANT_GLOW_SPRITE ); -} - - -void CXenPLight :: Think( void ) -{ - StudioFrameAdvance(); - SetNextThink( 0.1 ); - - switch( GetActivity() ) - { - case ACT_CROUCH: - if ( m_fSequenceFinished ) - { - SetActivity( ACT_CROUCHIDLE ); - LightOff(); - } - break; - - case ACT_CROUCHIDLE: - if ( gpGlobals->time > pev->dmgtime ) - { - SetActivity( ACT_STAND ); - LightOn(); - } - break; - - case ACT_STAND: - if ( m_fSequenceFinished ) - SetActivity( ACT_IDLE ); - break; - - case ACT_IDLE: - default: - break; - } -} - - -void CXenPLight :: Touch( CBaseEntity *pOther ) -{ - if ( pOther->IsPlayer() ) - { - pev->dmgtime = gpGlobals->time + XEN_PLANT_HIDE_TIME; - if ( GetActivity() == ACT_IDLE || GetActivity() == ACT_STAND ) - { - SetActivity( ACT_CROUCH ); - } - } -} - - -void CXenPLight :: LightOn( void ) -{ - SUB_UseTargets( this, USE_ON, 0 ); - if ( m_pGlow ) - m_pGlow->pev->effects &= ~EF_NODRAW; -} - - -void CXenPLight :: LightOff( void ) -{ - SUB_UseTargets( this, USE_OFF, 0 ); - if ( m_pGlow ) - m_pGlow->pev->effects |= EF_NODRAW; -} - - - -class CXenHair : public CActAnimating -{ -public: - void Spawn( void ); - void Precache( void ); - void Think( void ); -}; - -LINK_ENTITY_TO_CLASS( xen_hair, CXenHair ); - -#define SF_HAIR_SYNC 0x0001 - -void CXenHair::Spawn( void ) -{ - Precache(); - SET_MODEL( edict(), "models/hair.mdl" ); - UTIL_SetSize( pev, Vector(-4,-4,0), Vector(4,4,32)); - pev->sequence = 0; - - if ( !(pev->spawnflags & SF_HAIR_SYNC) ) - { - pev->frame = RANDOM_FLOAT(0,255); - pev->framerate = RANDOM_FLOAT( 0.7, 1.4 ); - } - ResetSequenceInfo( ); - - pev->solid = SOLID_NOT; - pev->movetype = MOVETYPE_NONE; - SetNextThink( RANDOM_FLOAT( 0.1, 0.4 ) ); // Load balance these a bit -} - - -void CXenHair::Think( void ) -{ - StudioFrameAdvance(); - SetNextThink( 0.5 ); -} - - -void CXenHair::Precache( void ) -{ - PRECACHE_MODEL( "models/hair.mdl" ); -} - - -class CXenTreeTrigger : public CBaseEntity -{ -public: - void Touch( CBaseEntity *pOther ); - static CXenTreeTrigger *TriggerCreate( edict_t *pOwner, const Vector &position ); -}; -LINK_ENTITY_TO_CLASS( xen_ttrigger, CXenTreeTrigger ); - -CXenTreeTrigger *CXenTreeTrigger :: TriggerCreate( edict_t *pOwner, const Vector &position ) -{ - CXenTreeTrigger *pTrigger = GetClassPtr( (CXenTreeTrigger *)NULL ); - pTrigger->pev->origin = position; - pTrigger->pev->classname = MAKE_STRING("xen_ttrigger"); - pTrigger->pev->solid = SOLID_TRIGGER; - pTrigger->pev->movetype = MOVETYPE_NONE; - pTrigger->pev->owner = pOwner; - - return pTrigger; -} - - -void CXenTreeTrigger::Touch( CBaseEntity *pOther ) -{ - if ( pev->owner ) - { - CBaseEntity *pEntity = CBaseEntity::Instance(pev->owner); - pEntity->Touch( pOther ); - } -} - - -#define TREE_AE_ATTACK 1 - -class CXenTree : public CActAnimating -{ -public: - void Spawn( void ); - void Precache( void ); - void Touch( CBaseEntity *pOther ); - void Think( void ); - int TakeDamage( entvars_t *pevInflictor, entvars_t *pevAttacker, float flDamage, int bitsDamageType ) { Attack(); return 0; } - void HandleAnimEvent( MonsterEvent_t *pEvent ); - void Attack( void ); - int Classify( void ) { return CLASS_BARNACLE; } - - virtual int Save( CSave &save ); - virtual int Restore( CRestore &restore ); - static TYPEDESCRIPTION m_SaveData[]; - - static const char *pAttackHitSounds[]; - static const char *pAttackMissSounds[]; - -private: - CXenTreeTrigger *m_pTrigger; -}; - -LINK_ENTITY_TO_CLASS( xen_tree, CXenTree ); - -TYPEDESCRIPTION CXenTree::m_SaveData[] = -{ - DEFINE_FIELD( CXenTree, m_pTrigger, FIELD_CLASSPTR ), -}; - -IMPLEMENT_SAVERESTORE( CXenTree, CActAnimating ); - -void CXenTree :: Spawn( void ) -{ - Precache(); - - SET_MODEL( ENT(pev), "models/tree.mdl" ); - pev->movetype = MOVETYPE_NONE; - pev->solid = SOLID_BBOX; - - pev->takedamage = DAMAGE_YES; - - UTIL_SetSize( pev, Vector(-30,-30,0), Vector(30,30,188)); - SetActivity( ACT_IDLE ); - SetNextThink( 0.1 ); - pev->frame = RANDOM_FLOAT(0,255); - pev->framerate = RANDOM_FLOAT( 0.7, 1.4 ); - - Vector triggerPosition; - UTIL_MakeVectorsPrivate( pev->angles, triggerPosition, NULL, NULL ); - triggerPosition = pev->origin + (triggerPosition * 64); - // Create the trigger - m_pTrigger = CXenTreeTrigger::TriggerCreate( edict(), triggerPosition ); - UTIL_SetSize( m_pTrigger->pev, Vector( -24, -24, 0 ), Vector( 24, 24, 128 ) ); -} - -const char *CXenTree::pAttackHitSounds[] = -{ - "zombie/claw_strike1.wav", - "zombie/claw_strike2.wav", - "zombie/claw_strike3.wav", -}; - -const char *CXenTree::pAttackMissSounds[] = -{ - "zombie/claw_miss1.wav", - "zombie/claw_miss2.wav", -}; - -void CXenTree :: Precache( void ) -{ - PRECACHE_MODEL( "models/tree.mdl" ); - PRECACHE_MODEL( XEN_PLANT_GLOW_SPRITE ); - PRECACHE_SOUND_ARRAY( pAttackHitSounds ); - PRECACHE_SOUND_ARRAY( pAttackMissSounds ); -} - - -void CXenTree :: Touch( CBaseEntity *pOther ) -{ - if ( !pOther->IsPlayer() && FClassnameIs( pOther->pev, "monster_bigmomma" ) ) - return; - - Attack(); -} - - -void CXenTree :: Attack( void ) -{ - if ( GetActivity() == ACT_IDLE ) - { - SetActivity( ACT_MELEE_ATTACK1 ); - pev->framerate = RANDOM_FLOAT( 1.0, 1.4 ); - EMIT_SOUND_ARRAY_DYN( CHAN_WEAPON, pAttackMissSounds ); - } -} - - -void CXenTree :: HandleAnimEvent( MonsterEvent_t *pEvent ) -{ - switch( pEvent->event ) - { - case TREE_AE_ATTACK: - { - CBaseEntity *pList[8]; - BOOL sound = FALSE; - int count = UTIL_EntitiesInBox( pList, 8, m_pTrigger->pev->absmin, m_pTrigger->pev->absmax, FL_MONSTER|FL_CLIENT ); - Vector forward; - - UTIL_MakeVectorsPrivate( pev->angles, forward, NULL, NULL ); - - for ( int i = 0; i < count; i++ ) - { - if ( pList[i] != this ) - { - if ( pList[i]->pev->owner != edict() ) - { - sound = TRUE; - pList[i]->TakeDamage( pev, pev, 25, DMG_CRUSH | DMG_SLASH ); - pList[i]->pev->punchangle.x = 15; - pList[i]->pev->velocity = pList[i]->pev->velocity + forward * 100; - } - } - } - - if ( sound ) - { - EMIT_SOUND_ARRAY_DYN( CHAN_WEAPON, pAttackHitSounds ); - } - } - return; - } - - CActAnimating::HandleAnimEvent( pEvent ); -} - -void CXenTree :: Think( void ) -{ - float flInterval = StudioFrameAdvance(); - SetNextThink( 0.1 ); - DispatchAnimEvents( flInterval ); - - switch( GetActivity() ) - { - case ACT_MELEE_ATTACK1: - if ( m_fSequenceFinished ) - { - SetActivity( ACT_IDLE ); - pev->framerate = RANDOM_FLOAT( 0.6, 1.4 ); - } - break; - - default: - case ACT_IDLE: - break; - - } -} - - -// UNDONE: These need to smoke somehow when they take damage -// Touch behavior? -// Cause damage in smoke area - -// -// Spores -// -class CXenSpore : public CActAnimating -{ -public: - void Spawn( void ); - void Precache( void ); - void Touch( CBaseEntity *pOther ); - void Think( void ); - int TakeDamage( entvars_t *pevInflictor, entvars_t *pevAttacker, float flDamage, int bitsDamageType ) { Attack(); return 0; } -// void HandleAnimEvent( MonsterEvent_t *pEvent ); - void Attack( void ) {} - - static const char *pModelNames[]; -}; - -class CXenSporeSmall : public CXenSpore -{ - void Spawn( void ); -}; - -class CXenSporeMed : public CXenSpore -{ - void Spawn( void ); -}; - -class CXenSporeLarge : public CXenSpore -{ - void Spawn( void ); - - static const Vector m_hullSizes[]; -}; - -// Fake collision box for big spores -class CXenHull : public CPointEntity -{ -public: - static CXenHull *CreateHull( CBaseEntity *source, const Vector &mins, const Vector &maxs, const Vector &offset ); - int Classify( void ) { return CLASS_BARNACLE; } -}; - -CXenHull *CXenHull :: CreateHull( CBaseEntity *source, const Vector &mins, const Vector &maxs, const Vector &offset ) -{ - CXenHull *pHull = GetClassPtr( (CXenHull *)NULL ); - - UTIL_SetOrigin( pHull, source->pev->origin + offset ); - SET_MODEL( pHull->edict(), STRING(source->pev->model) ); - pHull->pev->solid = SOLID_BBOX; - pHull->pev->classname = MAKE_STRING("xen_hull"); - pHull->pev->movetype = MOVETYPE_NONE; - pHull->pev->owner = source->edict(); - UTIL_SetSize( pHull->pev, mins, maxs ); - pHull->pev->renderamt = 0; - pHull->pev->rendermode = kRenderTransTexture; - // pHull->pev->effects = EF_NODRAW; - - return pHull; -} - - -LINK_ENTITY_TO_CLASS( xen_spore_small, CXenSporeSmall ); -LINK_ENTITY_TO_CLASS( xen_spore_medium, CXenSporeMed ); -LINK_ENTITY_TO_CLASS( xen_spore_large, CXenSporeLarge ); -LINK_ENTITY_TO_CLASS( xen_hull, CXenHull ); - -void CXenSporeSmall::Spawn( void ) -{ - pev->skin = 0; - CXenSpore::Spawn(); - UTIL_SetSize( pev, Vector(-16,-16,0), Vector(16,16,64)); -} -void CXenSporeMed::Spawn( void ) -{ - pev->skin = 1; - CXenSpore::Spawn(); - UTIL_SetSize( pev, Vector(-40,-40,0), Vector(40,40,120)); -} - - -// I just eyeballed these -- fill in hulls for the legs -const Vector CXenSporeLarge::m_hullSizes[] = -{ - Vector( 90, -25, 0 ), - Vector( 25, 75, 0 ), - Vector( -15, -100, 0 ), - Vector( -90, -35, 0 ), - Vector( -90, 60, 0 ), -}; - -void CXenSporeLarge::Spawn( void ) -{ - pev->skin = 2; - CXenSpore::Spawn(); - UTIL_SetSize( pev, Vector(-48,-48,110), Vector(48,48,240)); - - Vector forward, right; - - UTIL_MakeVectorsPrivate( pev->angles, forward, right, NULL ); - - // Rotate the leg hulls into position - for ( int i = 0; i < ARRAYSIZE(m_hullSizes); i++ ) - CXenHull :: CreateHull( this, Vector(-12, -12, 0 ), Vector( 12, 12, 120 ), (m_hullSizes[i].x * forward) + (m_hullSizes[i].y * right) ); -} - -void CXenSpore :: Spawn( void ) -{ - Precache(); - - SET_MODEL( ENT(pev), pModelNames[pev->skin] ); - pev->movetype = MOVETYPE_NONE; - pev->solid = SOLID_BBOX; - pev->takedamage = DAMAGE_YES; - -// SetActivity( ACT_IDLE ); - pev->sequence = 0; - pev->frame = RANDOM_FLOAT(0,255); - pev->framerate = RANDOM_FLOAT( 0.7, 1.4 ); - ResetSequenceInfo( ); - SetNextThink( RANDOM_FLOAT( 0.1, 0.4 ) ); // Load balance these a bit -} - -const char *CXenSpore::pModelNames[] = -{ - "models/fungus(small).mdl", - "models/fungus.mdl", - "models/fungus(large).mdl", -}; - - -void CXenSpore :: Precache( void ) -{ - PRECACHE_MODEL( (char *)pModelNames[pev->skin] ); -} - - -void CXenSpore :: Touch( CBaseEntity *pOther ) -{ -} - - -void CXenSpore :: Think( void ) -{ - float flInterval = StudioFrameAdvance(); - SetNextThink( 0.1 ); - -#if 0 - DispatchAnimEvents( flInterval ); - - switch( GetActivity() ) - { - default: - case ACT_IDLE: - break; - - } -#endif -} - - +/*** +* +* Copyright (c) 1996-2002, 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. +* +****/ +#include "extdll.h" +#include "util.h" +#include "cbase.h" +#include "animation.h" +#include "effects.h" + + +#define XEN_PLANT_GLOW_SPRITE "sprites/flare3.spr" +#define XEN_PLANT_HIDE_TIME 5 + + +class CActAnimating : public CBaseAnimating +{ +public: + void SetActivity( Activity act ); + inline Activity GetActivity( void ) { return m_Activity; } + + virtual int ObjectCaps( void ) { return CBaseAnimating :: ObjectCaps() & ~FCAP_ACROSS_TRANSITION; } + + virtual int Save( CSave &save ); + virtual int Restore( CRestore &restore ); + static TYPEDESCRIPTION m_SaveData[]; + +private: + Activity m_Activity; +}; + +TYPEDESCRIPTION CActAnimating::m_SaveData[] = +{ + DEFINE_FIELD( CActAnimating, m_Activity, FIELD_INTEGER ), +}; + +IMPLEMENT_SAVERESTORE( CActAnimating, CBaseAnimating ); + +void CActAnimating :: SetActivity( Activity act ) +{ + int sequence = LookupActivity( act ); + if ( sequence != ACTIVITY_NOT_AVAILABLE ) + { + pev->sequence = sequence; + m_Activity = act; + pev->frame = 0; + ResetSequenceInfo( ); + } +} + + + + +class CXenPLight : public CActAnimating +{ +public: + void Spawn( void ); + void Precache( void ); + void Touch( CBaseEntity *pOther ); + void Think( void ); + + void LightOn( void ); + void LightOff( void ); + + virtual int Save( CSave &save ); + virtual int Restore( CRestore &restore ); + static TYPEDESCRIPTION m_SaveData[]; + +private: + CSprite *m_pGlow; +}; + +LINK_ENTITY_TO_CLASS( xen_plantlight, CXenPLight ); + +TYPEDESCRIPTION CXenPLight::m_SaveData[] = +{ + DEFINE_FIELD( CXenPLight, m_pGlow, FIELD_CLASSPTR ), +}; + +IMPLEMENT_SAVERESTORE( CXenPLight, CActAnimating ); + +void CXenPLight :: Spawn( void ) +{ + Precache(); + + SET_MODEL( ENT(pev), "models/light.mdl" ); + pev->movetype = MOVETYPE_NONE; + pev->solid = SOLID_TRIGGER; + + UTIL_SetSize( pev, Vector(-80,-80,0), Vector(80,80,32)); + SetActivity( ACT_IDLE ); + pev->nextthink = gpGlobals->time + 0.1; + pev->frame = RANDOM_FLOAT(0,255); + + m_pGlow = CSprite::SpriteCreate( XEN_PLANT_GLOW_SPRITE, pev->origin + Vector(0,0,(pev->mins.z+pev->maxs.z)*0.5), FALSE ); + m_pGlow->SetTransparency( kRenderGlow, pev->rendercolor.x, pev->rendercolor.y, pev->rendercolor.z, pev->renderamt, pev->renderfx ); + m_pGlow->SetAttachment( edict(), 1 ); +} + + +void CXenPLight :: Precache( void ) +{ + PRECACHE_MODEL( "models/light.mdl" ); + PRECACHE_MODEL( XEN_PLANT_GLOW_SPRITE ); +} + + +void CXenPLight :: Think( void ) +{ + StudioFrameAdvance(); + pev->nextthink = gpGlobals->time + 0.1; + + switch( GetActivity() ) + { + case ACT_CROUCH: + if ( m_fSequenceFinished ) + { + SetActivity( ACT_CROUCHIDLE ); + LightOff(); + } + break; + + case ACT_CROUCHIDLE: + if ( gpGlobals->time > pev->dmgtime ) + { + SetActivity( ACT_STAND ); + LightOn(); + } + break; + + case ACT_STAND: + if ( m_fSequenceFinished ) + SetActivity( ACT_IDLE ); + break; + + case ACT_IDLE: + default: + break; + } +} + + +void CXenPLight :: Touch( CBaseEntity *pOther ) +{ + if ( pOther->IsPlayer() ) + { + pev->dmgtime = gpGlobals->time + XEN_PLANT_HIDE_TIME; + if ( GetActivity() == ACT_IDLE || GetActivity() == ACT_STAND ) + { + SetActivity( ACT_CROUCH ); + } + } +} + + +void CXenPLight :: LightOn( void ) +{ + SUB_UseTargets( this, USE_ON, 0 ); + if ( m_pGlow ) + m_pGlow->pev->effects &= ~EF_NODRAW; +} + + +void CXenPLight :: LightOff( void ) +{ + SUB_UseTargets( this, USE_OFF, 0 ); + if ( m_pGlow ) + m_pGlow->pev->effects |= EF_NODRAW; +} + + + +class CXenHair : public CActAnimating +{ +public: + void Spawn( void ); + void Precache( void ); + void Think( void ); +}; + +LINK_ENTITY_TO_CLASS( xen_hair, CXenHair ); + +#define SF_HAIR_SYNC 0x0001 + +void CXenHair::Spawn( void ) +{ + Precache(); + SET_MODEL( edict(), "models/hair.mdl" ); + UTIL_SetSize( pev, Vector(-4,-4,0), Vector(4,4,32)); + pev->sequence = 0; + + if ( !(pev->spawnflags & SF_HAIR_SYNC) ) + { + pev->frame = RANDOM_FLOAT(0,255); + pev->framerate = RANDOM_FLOAT( 0.7, 1.4 ); + } + ResetSequenceInfo( ); + + pev->solid = SOLID_NOT; + pev->movetype = MOVETYPE_NONE; + pev->nextthink = gpGlobals->time + RANDOM_FLOAT( 0.1, 0.4 ); // Load balance these a bit +} + + +void CXenHair::Think( void ) +{ + StudioFrameAdvance(); + pev->nextthink = gpGlobals->time + 0.5; +} + + +void CXenHair::Precache( void ) +{ + PRECACHE_MODEL( "models/hair.mdl" ); +} + + +class CXenTreeTrigger : public CBaseEntity +{ +public: + void Touch( CBaseEntity *pOther ); + static CXenTreeTrigger *TriggerCreate( edict_t *pOwner, const Vector &position ); +}; +LINK_ENTITY_TO_CLASS( xen_ttrigger, CXenTreeTrigger ); + +CXenTreeTrigger *CXenTreeTrigger :: TriggerCreate( edict_t *pOwner, const Vector &position ) +{ + CXenTreeTrigger *pTrigger = GetClassPtr( (CXenTreeTrigger *)NULL ); + pTrigger->pev->origin = position; + pTrigger->pev->classname = MAKE_STRING("xen_ttrigger"); + pTrigger->pev->solid = SOLID_TRIGGER; + pTrigger->pev->movetype = MOVETYPE_NONE; + pTrigger->pev->owner = pOwner; + + return pTrigger; +} + + +void CXenTreeTrigger::Touch( CBaseEntity *pOther ) +{ + if ( pev->owner ) + { + CBaseEntity *pEntity = CBaseEntity::Instance(pev->owner); + pEntity->Touch( pOther ); + } +} + + +#define TREE_AE_ATTACK 1 + +class CXenTree : public CActAnimating +{ +public: + void Spawn( void ); + void Precache( void ); + void Touch( CBaseEntity *pOther ); + void Think( void ); + int TakeDamage( entvars_t *pevInflictor, entvars_t *pevAttacker, float flDamage, int bitsDamageType ) { Attack(); return 0; } + void HandleAnimEvent( MonsterEvent_t *pEvent ); + void Attack( void ); + int Classify( void ) { return CLASS_BARNACLE; } + + virtual int Save( CSave &save ); + virtual int Restore( CRestore &restore ); + static TYPEDESCRIPTION m_SaveData[]; + + static const char *pAttackHitSounds[]; + static const char *pAttackMissSounds[]; + +private: + CXenTreeTrigger *m_pTrigger; +}; + +LINK_ENTITY_TO_CLASS( xen_tree, CXenTree ); + +TYPEDESCRIPTION CXenTree::m_SaveData[] = +{ + DEFINE_FIELD( CXenTree, m_pTrigger, FIELD_CLASSPTR ), +}; + +IMPLEMENT_SAVERESTORE( CXenTree, CActAnimating ); + +void CXenTree :: Spawn( void ) +{ + Precache(); + + SET_MODEL( ENT(pev), "models/tree.mdl" ); + pev->movetype = MOVETYPE_NONE; + pev->solid = SOLID_BBOX; + + pev->takedamage = DAMAGE_YES; + + UTIL_SetSize( pev, Vector(-30,-30,0), Vector(30,30,188)); + SetActivity( ACT_IDLE ); + pev->nextthink = gpGlobals->time + 0.1; + pev->frame = RANDOM_FLOAT(0,255); + pev->framerate = RANDOM_FLOAT( 0.7, 1.4 ); + + Vector triggerPosition; + UTIL_MakeVectorsPrivate( pev->angles, triggerPosition, NULL, NULL ); + triggerPosition = pev->origin + (triggerPosition * 64); + // Create the trigger + m_pTrigger = CXenTreeTrigger::TriggerCreate( edict(), triggerPosition ); + UTIL_SetSize( m_pTrigger->pev, Vector( -24, -24, 0 ), Vector( 24, 24, 128 ) ); +} + +const char *CXenTree::pAttackHitSounds[] = +{ + "zombie/claw_strike1.wav", + "zombie/claw_strike2.wav", + "zombie/claw_strike3.wav", +}; + +const char *CXenTree::pAttackMissSounds[] = +{ + "zombie/claw_miss1.wav", + "zombie/claw_miss2.wav", +}; + +void CXenTree :: Precache( void ) +{ + PRECACHE_MODEL( "models/tree.mdl" ); + PRECACHE_MODEL( XEN_PLANT_GLOW_SPRITE ); + PRECACHE_SOUND_ARRAY( pAttackHitSounds ); + PRECACHE_SOUND_ARRAY( pAttackMissSounds ); +} + + +void CXenTree :: Touch( CBaseEntity *pOther ) +{ + if ( !pOther->IsPlayer() && FClassnameIs( pOther->pev, "monster_bigmomma" ) ) + return; + + Attack(); +} + + +void CXenTree :: Attack( void ) +{ + if ( GetActivity() == ACT_IDLE ) + { + SetActivity( ACT_MELEE_ATTACK1 ); + pev->framerate = RANDOM_FLOAT( 1.0, 1.4 ); + EMIT_SOUND_ARRAY_DYN( CHAN_WEAPON, pAttackMissSounds ); + } +} + + +void CXenTree :: HandleAnimEvent( MonsterEvent_t *pEvent ) +{ + switch( pEvent->event ) + { + case TREE_AE_ATTACK: + { + CBaseEntity *pList[8]; + BOOL sound = FALSE; + int count = UTIL_EntitiesInBox( pList, 8, m_pTrigger->pev->absmin, m_pTrigger->pev->absmax, FL_MONSTER|FL_CLIENT ); + Vector forward; + + UTIL_MakeVectorsPrivate( pev->angles, forward, NULL, NULL ); + + for ( int i = 0; i < count; i++ ) + { + if ( pList[i] != this ) + { + if ( pList[i]->pev->owner != edict() ) + { + sound = TRUE; + pList[i]->TakeDamage( pev, pev, 25, DMG_CRUSH | DMG_SLASH ); + pList[i]->pev->punchangle.x = 15; + pList[i]->pev->velocity = pList[i]->pev->velocity + forward * 100; + } + } + } + + if ( sound ) + { + EMIT_SOUND_ARRAY_DYN( CHAN_WEAPON, pAttackHitSounds ); + } + } + return; + } + + CActAnimating::HandleAnimEvent( pEvent ); +} + +void CXenTree :: Think( void ) +{ + float flInterval = StudioFrameAdvance(); + pev->nextthink = gpGlobals->time + 0.1; + DispatchAnimEvents( flInterval ); + + switch( GetActivity() ) + { + case ACT_MELEE_ATTACK1: + if ( m_fSequenceFinished ) + { + SetActivity( ACT_IDLE ); + pev->framerate = RANDOM_FLOAT( 0.6, 1.4 ); + } + break; + + default: + case ACT_IDLE: + break; + + } +} + + +// UNDONE: These need to smoke somehow when they take damage +// Touch behavior? +// Cause damage in smoke area + +// +// Spores +// +class CXenSpore : public CActAnimating +{ +public: + void Spawn( void ); + void Precache( void ); + void Touch( CBaseEntity *pOther ); + void Think( void ); + int TakeDamage( entvars_t *pevInflictor, entvars_t *pevAttacker, float flDamage, int bitsDamageType ) { Attack(); return 0; } +// void HandleAnimEvent( MonsterEvent_t *pEvent ); + void Attack( void ) {} + + static const char *pModelNames[]; +}; + +class CXenSporeSmall : public CXenSpore +{ + void Spawn( void ); +}; + +class CXenSporeMed : public CXenSpore +{ + void Spawn( void ); +}; + +class CXenSporeLarge : public CXenSpore +{ + void Spawn( void ); + + static const Vector m_hullSizes[]; +}; + +// Fake collision box for big spores +class CXenHull : public CPointEntity +{ +public: + static CXenHull *CreateHull( CBaseEntity *source, const Vector &mins, const Vector &maxs, const Vector &offset ); + int Classify( void ) { return CLASS_BARNACLE; } +}; + +CXenHull *CXenHull :: CreateHull( CBaseEntity *source, const Vector &mins, const Vector &maxs, const Vector &offset ) +{ + CXenHull *pHull = GetClassPtr( (CXenHull *)NULL ); + + UTIL_SetOrigin( pHull->pev, source->pev->origin + offset ); + SET_MODEL( pHull->edict(), STRING(source->pev->model) ); + pHull->pev->solid = SOLID_BBOX; + pHull->pev->classname = MAKE_STRING("xen_hull"); + pHull->pev->movetype = MOVETYPE_NONE; + pHull->pev->owner = source->edict(); + UTIL_SetSize( pHull->pev, mins, maxs ); + pHull->pev->renderamt = 0; + pHull->pev->rendermode = kRenderTransTexture; + // pHull->pev->effects = EF_NODRAW; + + return pHull; +} + + +LINK_ENTITY_TO_CLASS( xen_spore_small, CXenSporeSmall ); +LINK_ENTITY_TO_CLASS( xen_spore_medium, CXenSporeMed ); +LINK_ENTITY_TO_CLASS( xen_spore_large, CXenSporeLarge ); +LINK_ENTITY_TO_CLASS( xen_hull, CXenHull ); + +void CXenSporeSmall::Spawn( void ) +{ + pev->skin = 0; + CXenSpore::Spawn(); + UTIL_SetSize( pev, Vector(-16,-16,0), Vector(16,16,64)); +} +void CXenSporeMed::Spawn( void ) +{ + pev->skin = 1; + CXenSpore::Spawn(); + UTIL_SetSize( pev, Vector(-40,-40,0), Vector(40,40,120)); +} + + +// I just eyeballed these -- fill in hulls for the legs +const Vector CXenSporeLarge::m_hullSizes[] = +{ + Vector( 90, -25, 0 ), + Vector( 25, 75, 0 ), + Vector( -15, -100, 0 ), + Vector( -90, -35, 0 ), + Vector( -90, 60, 0 ), +}; + +void CXenSporeLarge::Spawn( void ) +{ + pev->skin = 2; + CXenSpore::Spawn(); + UTIL_SetSize( pev, Vector(-48,-48,110), Vector(48,48,240)); + + Vector forward, right; + + UTIL_MakeVectorsPrivate( pev->angles, forward, right, NULL ); + + // Rotate the leg hulls into position + for ( int i = 0; i < ARRAYSIZE(m_hullSizes); i++ ) + CXenHull :: CreateHull( this, Vector(-12, -12, 0 ), Vector( 12, 12, 120 ), (m_hullSizes[i].x * forward) + (m_hullSizes[i].y * right) ); +} + +void CXenSpore :: Spawn( void ) +{ + Precache(); + + SET_MODEL( ENT(pev), pModelNames[pev->skin] ); + pev->movetype = MOVETYPE_NONE; + pev->solid = SOLID_BBOX; + pev->takedamage = DAMAGE_YES; + +// SetActivity( ACT_IDLE ); + pev->sequence = 0; + pev->frame = RANDOM_FLOAT(0,255); + pev->framerate = RANDOM_FLOAT( 0.7, 1.4 ); + ResetSequenceInfo( ); + pev->nextthink = gpGlobals->time + RANDOM_FLOAT( 0.1, 0.4 ); // Load balance these a bit +} + +const char *CXenSpore::pModelNames[] = +{ + "models/fungus(small).mdl", + "models/fungus.mdl", + "models/fungus(large).mdl", +}; + + +void CXenSpore :: Precache( void ) +{ + PRECACHE_MODEL( (char *)pModelNames[pev->skin] ); +} + + +void CXenSpore :: Touch( CBaseEntity *pOther ) +{ +} + + +void CXenSpore :: Think( void ) +{ + float flInterval = StudioFrameAdvance(); + pev->nextthink = gpGlobals->time + 0.1; + +#if 0 + DispatchAnimEvents( flInterval ); + + switch( GetActivity() ) + { + default: + case ACT_IDLE: + break; + + } +#endif +} + + diff --git a/spirit/zombie.cpp b/dlls/zombie.cpp similarity index 95% rename from spirit/zombie.cpp rename to dlls/zombie.cpp index 23eb6375..234eafca 100644 --- a/spirit/zombie.cpp +++ b/dlls/zombie.cpp @@ -1,353 +1,346 @@ -/*** -* -* Copyright (c) 1996-2002, 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. -* -* This source code contains proprietary and confidential information of -* Valve LLC and its suppliers. Access to this code is restricted to -* persons who have executed a written SDK license with Valve. Any access, -* use or distribution of this code by or to any unlicensed person is illegal. -* -****/ -//========================================================= -// Zombie -//========================================================= - -// UNDONE: Don't flinch every time you get hit - -#include "extdll.h" -#include "util.h" -#include "cbase.h" -#include "monsters.h" -#include "schedule.h" - - -//========================================================= -// Monster's Anim Events Go Here -//========================================================= -#define ZOMBIE_AE_ATTACK_RIGHT 0x01 -#define ZOMBIE_AE_ATTACK_LEFT 0x02 -#define ZOMBIE_AE_ATTACK_BOTH 0x03 - -#define ZOMBIE_FLINCH_DELAY 2 // at most one flinch every n secs - -class CZombie : public CBaseMonster -{ -public: - void Spawn( void ); - void Precache( void ); - void SetYawSpeed( void ); - int Classify ( void ); - void HandleAnimEvent( MonsterEvent_t *pEvent ); - int IgnoreConditions ( void ); - - float m_flNextFlinch; - - void PainSound( void ); - void AlertSound( void ); - void IdleSound( void ); - void AttackSound( void ); - - static const char *pAttackSounds[]; - static const char *pIdleSounds[]; - static const char *pAlertSounds[]; - static const char *pPainSounds[]; - static const char *pAttackHitSounds[]; - static const char *pAttackMissSounds[]; - - // No range attacks - BOOL CheckRangeAttack1 ( float flDot, float flDist ) { return FALSE; } - BOOL CheckRangeAttack2 ( float flDot, float flDist ) { return FALSE; } - int TakeDamage( entvars_t *pevInflictor, entvars_t *pevAttacker, float flDamage, int bitsDamageType ); -}; - -LINK_ENTITY_TO_CLASS( monster_zombie, CZombie ); - -const char *CZombie::pAttackHitSounds[] = -{ - "zombie/claw_strike1.wav", - "zombie/claw_strike2.wav", - "zombie/claw_strike3.wav", -}; - -const char *CZombie::pAttackMissSounds[] = -{ - "zombie/claw_miss1.wav", - "zombie/claw_miss2.wav", -}; - -const char *CZombie::pAttackSounds[] = -{ - "zombie/zo_attack1.wav", - "zombie/zo_attack2.wav", -}; - -const char *CZombie::pIdleSounds[] = -{ - "zombie/zo_idle1.wav", - "zombie/zo_idle2.wav", - "zombie/zo_idle3.wav", - "zombie/zo_idle4.wav", -}; - -const char *CZombie::pAlertSounds[] = -{ - "zombie/zo_alert10.wav", - "zombie/zo_alert20.wav", - "zombie/zo_alert30.wav", -}; - -const char *CZombie::pPainSounds[] = -{ - "zombie/zo_pain1.wav", - "zombie/zo_pain2.wav", -}; - -//========================================================= -// Classify - indicates this monster's place in the -// relationship table. -//========================================================= -int CZombie :: Classify ( void ) -{ - return m_iClass?m_iClass:CLASS_ALIEN_MONSTER; -} - -//========================================================= -// SetYawSpeed - allows each sequence to have a different -// turn rate associated with it. -//========================================================= -void CZombie :: SetYawSpeed ( void ) -{ - int ys; - - ys = 120; - -#if 0 - switch ( m_Activity ) - { - } -#endif - - pev->yaw_speed = ys; -} - -int CZombie :: TakeDamage( entvars_t *pevInflictor, entvars_t *pevAttacker, float flDamage, int bitsDamageType ) -{ - // Take 30% damage from bullets - if ( bitsDamageType == DMG_BULLET ) - { - Vector vecDir = pev->origin - (pevInflictor->absmin + pevInflictor->absmax) * 0.5; - vecDir = vecDir.Normalize(); - float flForce = DamageForce( flDamage ); - pev->velocity = pev->velocity + vecDir * flForce; - flDamage *= 0.3; - } - - // HACK HACK -- until we fix this. - if ( IsAlive() ) - PainSound(); - return CBaseMonster::TakeDamage( pevInflictor, pevAttacker, flDamage, bitsDamageType ); -} - -void CZombie :: PainSound( void ) -{ - int pitch = 95 + RANDOM_LONG(0,9); - - if (RANDOM_LONG(0,5) < 2) - EMIT_SOUND_DYN ( ENT(pev), CHAN_VOICE, pPainSounds[ RANDOM_LONG(0,ARRAYSIZE(pPainSounds)-1) ], 1.0, ATTN_NORM, 0, pitch ); -} - -void CZombie :: AlertSound( void ) -{ - int pitch = 95 + RANDOM_LONG(0,9); - - EMIT_SOUND_DYN ( ENT(pev), CHAN_VOICE, pAlertSounds[ RANDOM_LONG(0,ARRAYSIZE(pAlertSounds)-1) ], 1.0, ATTN_NORM, 0, pitch ); -} - -void CZombie :: IdleSound( void ) -{ - int pitch = 95 + RANDOM_LONG(0,9); - - // Play a random idle sound - EMIT_SOUND_DYN ( ENT(pev), CHAN_VOICE, pIdleSounds[ RANDOM_LONG(0,ARRAYSIZE(pIdleSounds)-1) ], 1.0, ATTN_NORM, 0, 100 + RANDOM_LONG(-5,5) ); -} - -void CZombie :: AttackSound( void ) -{ - // Play a random attack sound - EMIT_SOUND_DYN ( ENT(pev), CHAN_VOICE, pAttackSounds[ RANDOM_LONG(0,ARRAYSIZE(pAttackSounds)-1) ], 1.0, ATTN_NORM, 0, 100 + RANDOM_LONG(-5,5) ); -} - - -//========================================================= -// HandleAnimEvent - catches the monster-specific messages -// that occur when tagged animation frames are played. -//========================================================= -void CZombie :: HandleAnimEvent( MonsterEvent_t *pEvent ) -{ - switch( pEvent->event ) - { - case ZOMBIE_AE_ATTACK_RIGHT: - { - // do stuff for this event. - // ALERT( at_console, "Slash right!\n" ); - CBaseEntity *pHurt = CheckTraceHullAttack( 70, gSkillData.zombieDmgOneSlash, DMG_SLASH ); - if ( pHurt ) - { - if ( pHurt->pev->flags & (FL_MONSTER|FL_CLIENT) ) - { - pHurt->pev->punchangle.z = -18; - pHurt->pev->punchangle.x = 5; - pHurt->pev->velocity = pHurt->pev->velocity - gpGlobals->v_right * 100; - } - // Play a random attack hit sound - EMIT_SOUND_DYN ( ENT(pev), CHAN_WEAPON, pAttackHitSounds[ RANDOM_LONG(0,ARRAYSIZE(pAttackHitSounds)-1) ], 1.0, ATTN_NORM, 0, 100 + RANDOM_LONG(-5,5) ); - } - else // Play a random attack miss sound - EMIT_SOUND_DYN ( ENT(pev), CHAN_WEAPON, pAttackMissSounds[ RANDOM_LONG(0,ARRAYSIZE(pAttackMissSounds)-1) ], 1.0, ATTN_NORM, 0, 100 + RANDOM_LONG(-5,5) ); - - if (RANDOM_LONG(0,1)) - AttackSound(); - } - break; - - case ZOMBIE_AE_ATTACK_LEFT: - { - // do stuff for this event. - // ALERT( at_console, "Slash left!\n" ); - CBaseEntity *pHurt = CheckTraceHullAttack( 70, gSkillData.zombieDmgOneSlash, DMG_SLASH ); - if ( pHurt ) - { - if ( pHurt->pev->flags & (FL_MONSTER|FL_CLIENT) ) - { - pHurt->pev->punchangle.z = 18; - pHurt->pev->punchangle.x = 5; - pHurt->pev->velocity = pHurt->pev->velocity + gpGlobals->v_right * 100; - } - EMIT_SOUND_DYN ( ENT(pev), CHAN_WEAPON, pAttackHitSounds[ RANDOM_LONG(0,ARRAYSIZE(pAttackHitSounds)-1) ], 1.0, ATTN_NORM, 0, 100 + RANDOM_LONG(-5,5) ); - } - else - EMIT_SOUND_DYN ( ENT(pev), CHAN_WEAPON, pAttackMissSounds[ RANDOM_LONG(0,ARRAYSIZE(pAttackMissSounds)-1) ], 1.0, ATTN_NORM, 0, 100 + RANDOM_LONG(-5,5) ); - - if (RANDOM_LONG(0,1)) - AttackSound(); - } - break; - - case ZOMBIE_AE_ATTACK_BOTH: - { - // do stuff for this event. - CBaseEntity *pHurt = CheckTraceHullAttack( 70, gSkillData.zombieDmgBothSlash, DMG_SLASH ); - if ( pHurt ) - { - if ( pHurt->pev->flags & (FL_MONSTER|FL_CLIENT) ) - { - pHurt->pev->punchangle.x = 5; - pHurt->pev->velocity = pHurt->pev->velocity + gpGlobals->v_forward * -100; - } - EMIT_SOUND_DYN ( ENT(pev), CHAN_WEAPON, pAttackHitSounds[ RANDOM_LONG(0,ARRAYSIZE(pAttackHitSounds)-1) ], 1.0, ATTN_NORM, 0, 100 + RANDOM_LONG(-5,5) ); - } - else - EMIT_SOUND_DYN ( ENT(pev), CHAN_WEAPON, pAttackMissSounds[ RANDOM_LONG(0,ARRAYSIZE(pAttackMissSounds)-1) ], 1.0, ATTN_NORM, 0, 100 + RANDOM_LONG(-5,5) ); - - if (RANDOM_LONG(0,1)) - AttackSound(); - } - break; - - default: - CBaseMonster::HandleAnimEvent( pEvent ); - break; - } -} - -//========================================================= -// Spawn -//========================================================= -void CZombie :: Spawn() -{ - Precache( ); - - if (pev->model) - SET_MODEL(ENT(pev), STRING(pev->model)); //LRC - else - SET_MODEL(ENT(pev), "models/zombie.mdl"); - UTIL_SetSize( pev, VEC_HUMAN_HULL_MIN, VEC_HUMAN_HULL_MAX ); - - pev->solid = SOLID_SLIDEBOX; - pev->movetype = MOVETYPE_STEP; - m_bloodColor = BLOOD_COLOR_GREEN; - if (pev->health == 0) - pev->health = gSkillData.zombieHealth; - pev->view_ofs = VEC_VIEW;// position of the eyes relative to monster's origin. - m_flFieldOfView = 0.5;// indicates the width of this monster's forward view cone ( as a dotproduct result ) - m_MonsterState = MONSTERSTATE_NONE; - m_afCapability = bits_CAP_DOORS_GROUP; - - MonsterInit(); -} - -//========================================================= -// Precache - precaches all resources this monster needs -//========================================================= -void CZombie :: Precache() -{ - int i; - - if (pev->model) - PRECACHE_MODEL((char*)STRING(pev->model)); //LRC - else - PRECACHE_MODEL("models/zombie.mdl"); - - for ( i = 0; i < ARRAYSIZE( pAttackHitSounds ); i++ ) - PRECACHE_SOUND((char *)pAttackHitSounds[i]); - - for ( i = 0; i < ARRAYSIZE( pAttackMissSounds ); i++ ) - PRECACHE_SOUND((char *)pAttackMissSounds[i]); - - for ( i = 0; i < ARRAYSIZE( pAttackSounds ); i++ ) - PRECACHE_SOUND((char *)pAttackSounds[i]); - - for ( i = 0; i < ARRAYSIZE( pIdleSounds ); i++ ) - PRECACHE_SOUND((char *)pIdleSounds[i]); - - for ( i = 0; i < ARRAYSIZE( pAlertSounds ); i++ ) - PRECACHE_SOUND((char *)pAlertSounds[i]); - - for ( i = 0; i < ARRAYSIZE( pPainSounds ); i++ ) - PRECACHE_SOUND((char *)pPainSounds[i]); -} - -//========================================================= -// AI Schedules Specific to this monster -//========================================================= - - - -int CZombie::IgnoreConditions ( void ) -{ - int iIgnore = CBaseMonster::IgnoreConditions(); - - if ((m_Activity == ACT_MELEE_ATTACK1) || (m_Activity == ACT_MELEE_ATTACK1)) - { -#if 0 - if (pev->health < 20) - iIgnore |= (bits_COND_LIGHT_DAMAGE|bits_COND_HEAVY_DAMAGE); - else -#endif - if (m_flNextFlinch >= gpGlobals->time) - iIgnore |= (bits_COND_LIGHT_DAMAGE|bits_COND_HEAVY_DAMAGE); - } - - if ((m_Activity == ACT_SMALL_FLINCH) || (m_Activity == ACT_BIG_FLINCH)) - { - if (m_flNextFlinch < gpGlobals->time) - m_flNextFlinch = gpGlobals->time + ZOMBIE_FLINCH_DELAY; - } - - return iIgnore; - -} +/*** +* +* Copyright (c) 1996-2002, 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. +* +* This source code contains proprietary and confidential information of +* Valve LLC and its suppliers. Access to this code is restricted to +* persons who have executed a written SDK license with Valve. Any access, +* use or distribution of this code by or to any unlicensed person is illegal. +* +****/ +//========================================================= +// Zombie +//========================================================= + +// UNDONE: Don't flinch every time you get hit + +#include "extdll.h" +#include "util.h" +#include "cbase.h" +#include "monsters.h" +#include "schedule.h" + + +//========================================================= +// Monster's Anim Events Go Here +//========================================================= +#define ZOMBIE_AE_ATTACK_RIGHT 0x01 +#define ZOMBIE_AE_ATTACK_LEFT 0x02 +#define ZOMBIE_AE_ATTACK_BOTH 0x03 + +#define ZOMBIE_FLINCH_DELAY 2 // at most one flinch every n secs + +class CZombie : public CBaseMonster +{ +public: + void Spawn( void ); + void Precache( void ); + void SetYawSpeed( void ); + int Classify ( void ); + void HandleAnimEvent( MonsterEvent_t *pEvent ); + int IgnoreConditions ( void ); + + float m_flNextFlinch; + + void PainSound( void ); + void AlertSound( void ); + void IdleSound( void ); + void AttackSound( void ); + + static const char *pAttackSounds[]; + static const char *pIdleSounds[]; + static const char *pAlertSounds[]; + static const char *pPainSounds[]; + static const char *pAttackHitSounds[]; + static const char *pAttackMissSounds[]; + + // No range attacks + BOOL CheckRangeAttack1 ( float flDot, float flDist ) { return FALSE; } + BOOL CheckRangeAttack2 ( float flDot, float flDist ) { return FALSE; } + int TakeDamage( entvars_t *pevInflictor, entvars_t *pevAttacker, float flDamage, int bitsDamageType ); +}; + +LINK_ENTITY_TO_CLASS( monster_zombie, CZombie ); + +const char *CZombie::pAttackHitSounds[] = +{ + "zombie/claw_strike1.wav", + "zombie/claw_strike2.wav", + "zombie/claw_strike3.wav", +}; + +const char *CZombie::pAttackMissSounds[] = +{ + "zombie/claw_miss1.wav", + "zombie/claw_miss2.wav", +}; + +const char *CZombie::pAttackSounds[] = +{ + "zombie/zo_attack1.wav", + "zombie/zo_attack2.wav", +}; + +const char *CZombie::pIdleSounds[] = +{ + "zombie/zo_idle1.wav", + "zombie/zo_idle2.wav", + "zombie/zo_idle3.wav", + "zombie/zo_idle4.wav", +}; + +const char *CZombie::pAlertSounds[] = +{ + "zombie/zo_alert10.wav", + "zombie/zo_alert20.wav", + "zombie/zo_alert30.wav", +}; + +const char *CZombie::pPainSounds[] = +{ + "zombie/zo_pain1.wav", + "zombie/zo_pain2.wav", +}; + +//========================================================= +// Classify - indicates this monster's place in the +// relationship table. +//========================================================= +int CZombie :: Classify ( void ) +{ + return CLASS_ALIEN_MONSTER; +} + +//========================================================= +// SetYawSpeed - allows each sequence to have a different +// turn rate associated with it. +//========================================================= +void CZombie :: SetYawSpeed ( void ) +{ + int ys; + + ys = 120; + +#if 0 + switch ( m_Activity ) + { + } +#endif + + pev->yaw_speed = ys; +} + +int CZombie :: TakeDamage( entvars_t *pevInflictor, entvars_t *pevAttacker, float flDamage, int bitsDamageType ) +{ + // Take 30% damage from bullets + if ( bitsDamageType == DMG_BULLET ) + { + Vector vecDir = pev->origin - (pevInflictor->absmin + pevInflictor->absmax) * 0.5; + vecDir = vecDir.Normalize(); + float flForce = DamageForce( flDamage ); + pev->velocity = pev->velocity + vecDir * flForce; + flDamage *= 0.3; + } + + // HACK HACK -- until we fix this. + if ( IsAlive() ) + PainSound(); + return CBaseMonster::TakeDamage( pevInflictor, pevAttacker, flDamage, bitsDamageType ); +} + +void CZombie :: PainSound( void ) +{ + int pitch = 95 + RANDOM_LONG(0,9); + + if (RANDOM_LONG(0,5) < 2) + EMIT_SOUND_DYN ( ENT(pev), CHAN_VOICE, pPainSounds[ RANDOM_LONG(0,ARRAYSIZE(pPainSounds)-1) ], 1.0, ATTN_NORM, 0, pitch ); +} + +void CZombie :: AlertSound( void ) +{ + int pitch = 95 + RANDOM_LONG(0,9); + + EMIT_SOUND_DYN ( ENT(pev), CHAN_VOICE, pAlertSounds[ RANDOM_LONG(0,ARRAYSIZE(pAlertSounds)-1) ], 1.0, ATTN_NORM, 0, pitch ); +} + +void CZombie :: IdleSound( void ) +{ + int pitch = 95 + RANDOM_LONG(0,9); + + // Play a random idle sound + EMIT_SOUND_DYN ( ENT(pev), CHAN_VOICE, pIdleSounds[ RANDOM_LONG(0,ARRAYSIZE(pIdleSounds)-1) ], 1.0, ATTN_NORM, 0, 100 + RANDOM_LONG(-5,5) ); +} + +void CZombie :: AttackSound( void ) +{ + // Play a random attack sound + EMIT_SOUND_DYN ( ENT(pev), CHAN_VOICE, pAttackSounds[ RANDOM_LONG(0,ARRAYSIZE(pAttackSounds)-1) ], 1.0, ATTN_NORM, 0, 100 + RANDOM_LONG(-5,5) ); +} + + +//========================================================= +// HandleAnimEvent - catches the monster-specific messages +// that occur when tagged animation frames are played. +//========================================================= +void CZombie :: HandleAnimEvent( MonsterEvent_t *pEvent ) +{ + switch( pEvent->event ) + { + case ZOMBIE_AE_ATTACK_RIGHT: + { + // do stuff for this event. + // ALERT( at_console, "Slash right!\n" ); + CBaseEntity *pHurt = CheckTraceHullAttack( 70, gSkillData.zombieDmgOneSlash, DMG_SLASH ); + if ( pHurt ) + { + if ( pHurt->pev->flags & (FL_MONSTER|FL_CLIENT) ) + { + pHurt->pev->punchangle.z = -18; + pHurt->pev->punchangle.x = 5; + pHurt->pev->velocity = pHurt->pev->velocity - gpGlobals->v_right * 100; + } + // Play a random attack hit sound + EMIT_SOUND_DYN ( ENT(pev), CHAN_WEAPON, pAttackHitSounds[ RANDOM_LONG(0,ARRAYSIZE(pAttackHitSounds)-1) ], 1.0, ATTN_NORM, 0, 100 + RANDOM_LONG(-5,5) ); + } + else // Play a random attack miss sound + EMIT_SOUND_DYN ( ENT(pev), CHAN_WEAPON, pAttackMissSounds[ RANDOM_LONG(0,ARRAYSIZE(pAttackMissSounds)-1) ], 1.0, ATTN_NORM, 0, 100 + RANDOM_LONG(-5,5) ); + + if (RANDOM_LONG(0,1)) + AttackSound(); + } + break; + + case ZOMBIE_AE_ATTACK_LEFT: + { + // do stuff for this event. + // ALERT( at_console, "Slash left!\n" ); + CBaseEntity *pHurt = CheckTraceHullAttack( 70, gSkillData.zombieDmgOneSlash, DMG_SLASH ); + if ( pHurt ) + { + if ( pHurt->pev->flags & (FL_MONSTER|FL_CLIENT) ) + { + pHurt->pev->punchangle.z = 18; + pHurt->pev->punchangle.x = 5; + pHurt->pev->velocity = pHurt->pev->velocity + gpGlobals->v_right * 100; + } + EMIT_SOUND_DYN ( ENT(pev), CHAN_WEAPON, pAttackHitSounds[ RANDOM_LONG(0,ARRAYSIZE(pAttackHitSounds)-1) ], 1.0, ATTN_NORM, 0, 100 + RANDOM_LONG(-5,5) ); + } + else + EMIT_SOUND_DYN ( ENT(pev), CHAN_WEAPON, pAttackMissSounds[ RANDOM_LONG(0,ARRAYSIZE(pAttackMissSounds)-1) ], 1.0, ATTN_NORM, 0, 100 + RANDOM_LONG(-5,5) ); + + if (RANDOM_LONG(0,1)) + AttackSound(); + } + break; + + case ZOMBIE_AE_ATTACK_BOTH: + { + // do stuff for this event. + CBaseEntity *pHurt = CheckTraceHullAttack( 70, gSkillData.zombieDmgBothSlash, DMG_SLASH ); + if ( pHurt ) + { + if ( pHurt->pev->flags & (FL_MONSTER|FL_CLIENT) ) + { + pHurt->pev->punchangle.x = 5; + pHurt->pev->velocity = pHurt->pev->velocity + gpGlobals->v_forward * -100; + } + EMIT_SOUND_DYN ( ENT(pev), CHAN_WEAPON, pAttackHitSounds[ RANDOM_LONG(0,ARRAYSIZE(pAttackHitSounds)-1) ], 1.0, ATTN_NORM, 0, 100 + RANDOM_LONG(-5,5) ); + } + else + EMIT_SOUND_DYN ( ENT(pev), CHAN_WEAPON, pAttackMissSounds[ RANDOM_LONG(0,ARRAYSIZE(pAttackMissSounds)-1) ], 1.0, ATTN_NORM, 0, 100 + RANDOM_LONG(-5,5) ); + + if (RANDOM_LONG(0,1)) + AttackSound(); + } + break; + + default: + CBaseMonster::HandleAnimEvent( pEvent ); + break; + } +} + +//========================================================= +// Spawn +//========================================================= +void CZombie :: Spawn() +{ + Precache( ); + + SET_MODEL(ENT(pev), "models/zombie.mdl"); + UTIL_SetSize( pev, VEC_HUMAN_HULL_MIN, VEC_HUMAN_HULL_MAX ); + + pev->solid = SOLID_SLIDEBOX; + pev->movetype = MOVETYPE_STEP; + m_bloodColor = BLOOD_COLOR_GREEN; + pev->health = gSkillData.zombieHealth; + pev->view_ofs = VEC_VIEW;// position of the eyes relative to monster's origin. + m_flFieldOfView = 0.5;// indicates the width of this monster's forward view cone ( as a dotproduct result ) + m_MonsterState = MONSTERSTATE_NONE; + m_afCapability = bits_CAP_DOORS_GROUP; + + MonsterInit(); +} + +//========================================================= +// Precache - precaches all resources this monster needs +//========================================================= +void CZombie :: Precache() +{ + int i; + + PRECACHE_MODEL("models/zombie.mdl"); + + for ( i = 0; i < ARRAYSIZE( pAttackHitSounds ); i++ ) + PRECACHE_SOUND((char *)pAttackHitSounds[i]); + + for ( i = 0; i < ARRAYSIZE( pAttackMissSounds ); i++ ) + PRECACHE_SOUND((char *)pAttackMissSounds[i]); + + for ( i = 0; i < ARRAYSIZE( pAttackSounds ); i++ ) + PRECACHE_SOUND((char *)pAttackSounds[i]); + + for ( i = 0; i < ARRAYSIZE( pIdleSounds ); i++ ) + PRECACHE_SOUND((char *)pIdleSounds[i]); + + for ( i = 0; i < ARRAYSIZE( pAlertSounds ); i++ ) + PRECACHE_SOUND((char *)pAlertSounds[i]); + + for ( i = 0; i < ARRAYSIZE( pPainSounds ); i++ ) + PRECACHE_SOUND((char *)pPainSounds[i]); +} + +//========================================================= +// AI Schedules Specific to this monster +//========================================================= + + + +int CZombie::IgnoreConditions ( void ) +{ + int iIgnore = CBaseMonster::IgnoreConditions(); + + if ((m_Activity == ACT_MELEE_ATTACK1) || (m_Activity == ACT_MELEE_ATTACK1)) + { +#if 0 + if (pev->health < 20) + iIgnore |= (bits_COND_LIGHT_DAMAGE|bits_COND_HEAVY_DAMAGE); + else +#endif + if (m_flNextFlinch >= gpGlobals->time) + iIgnore |= (bits_COND_LIGHT_DAMAGE|bits_COND_HEAVY_DAMAGE); + } + + if ((m_Activity == ACT_SMALL_FLINCH) || (m_Activity == ACT_BIG_FLINCH)) + { + if (m_flNextFlinch < gpGlobals->time) + m_flNextFlinch = gpGlobals->time + ZOMBIE_FLINCH_DELAY; + } + + return iIgnore; + +} \ No newline at end of file diff --git a/engine/client/cl_frame.c b/engine/client/cl_frame.c index 746ff4bb..4715b6ea 100644 --- a/engine/client/cl_frame.c +++ b/engine/client/cl_frame.c @@ -7,6 +7,7 @@ #include "client.h" #include "protocol.h" #include "net_encode.h" +#include "entity_types.h" /* ========================================================================= @@ -18,18 +19,9 @@ FRAME PARSING void CL_UpdateEntityFields( cl_entity_t *ent ) { // FIXME: this very-very temporary stuffffffff + // make the lerping VectorCopy( ent->curstate.origin, ent->origin ); VectorCopy( ent->curstate.angles, ent->angles ); - - if( ent->curstate.ed_flags & ESF_LINKEDICT ) - { - CL_LinkEdict( ent, false ); - // to avoids multiple relinks when wait for next packet - ent->curstate.ed_flags &= ~ESF_LINKEDICT; - } - - // FIXME: replace with char[32] ? - ent->classname = cl.edict_classnames[ent->curstate.classname]; } /* @@ -66,7 +58,7 @@ void CL_UpdateStudioVars( cl_entity_t *ent, const entity_state_t *newstate ) // sequence has changed, hold the previous sequence info if( newstate->sequence != ent->curstate.sequence ) { - if( ent->curstate.ed_type == ED_CLIENT ) + if( ent->curstate.entityType == ET_PLAYER ) ent->latched.sequencetime = ent->curstate.animtime + 0.01f; else ent->latched.sequencetime = ent->curstate.animtime + 0.1f; @@ -140,8 +132,8 @@ void CL_DeltaEntity( sizebuf_t *msg, frame_t *frame, int newnum, entity_state_t if( ent->index <= 0 ) CL_InitEntity( ent ); // some data changes will force no lerping - if( state->ed_flags & ESF_NODELTA ) ent->serverframe = -99; - if( newent ) state->ed_flags |= ESF_LINKEDICT; // need to relink + if( state->effects & EF_NOINTERP ) + ent->serverframe = -99; if( ent->serverframe != cl.frame.serverframe - 1 ) { @@ -406,7 +398,7 @@ CL_AddPacketEntities void CL_AddPacketEntities( frame_t *frame ) { cl_entity_t *ent, *clent; - int e, ed_type; + int e, entityType; // now recalc actual entcount for( ; EDICT_NUM( clgame.globals->numEntities - 1 )->index == -1; clgame.globals->numEntities-- ); @@ -432,16 +424,16 @@ void CL_AddPacketEntities( frame_t *frame ) ent = CL_GetEntityByIndex( e ); if( !ent ) continue; - ed_type = ent->curstate.ed_type; + entityType = ent->curstate.entityType; CL_UpdateEntityFields( ent ); - if( clgame.dllFuncs.pfnAddVisibleEntity( ent, ed_type )) + if( clgame.dllFuncs.pfnAddVisibleEntity( ent, entityType )) { - if( ed_type == ED_PORTAL && !VectorCompare( ent->curstate.origin, ent->curstate.vuser1 )) + if( entityType == ET_PORTAL && !VectorCompare( ent->curstate.origin, ent->curstate.vuser1 )) cl.render_flags |= RDF_PORTALINVIEW; } // NOTE: skyportal entity never added to rendering - if( ed_type == ED_SKYPORTAL ) cl.render_flags |= RDF_SKYPORTALINVIEW; + if( entityType == ET_SKYPORTAL ) cl.render_flags |= RDF_SKYPORTALINVIEW; } } diff --git a/engine/client/cl_game.c b/engine/client/cl_game.c index fb48bf90..e62d7a29 100644 --- a/engine/client/cl_game.c +++ b/engine/client/cl_game.c @@ -48,12 +48,6 @@ CL_AllocString */ string_t CL_AllocString( const char *szValue ) { - if( sys_sharedstrings->integer ) - { - const char *newString; - newString = com.stralloc( clgame.stringspool, szValue, __FILE__, __LINE__ ); - return newString - clgame.globals->pStringBase; - } return StringTable_SetString( clgame.hStringTable, szValue ); } @@ -65,8 +59,6 @@ CL_GetString */ const char *CL_GetString( string_t iString ) { - if( sys_sharedstrings->integer ) - return (clgame.globals->pStringBase + iString); return StringTable_GetString( clgame.hStringTable, iString ); } @@ -535,7 +527,7 @@ void CL_DrawCrosshair( void ) pPlayer = CL_GetLocalPlayer(); - if( cl.frame.cd.deadflag != DEAD_NO || pPlayer->curstate.flags & FL_FROZEN ) + if( cl.frame.cd.deadflag != DEAD_NO || cl.frame.cd.flags & FL_FROZEN ) return; // any camera on @@ -653,7 +645,10 @@ void CL_DrawHUD( int state ) if( state == CL_ACTIVE && cl.refdef.paused ) state = CL_PAUSED; - clgame.dllFuncs.pfnRedraw( cl_time(), state ); + if( state == CL_ACTIVE ) + { + clgame.dllFuncs.pfnRedraw( cl_time(), false ); + } switch( state ) { @@ -768,9 +763,6 @@ void CL_FreeEntity( cl_entity_t *pEdict ) clgame.dllFuncs.pfnUpdateOnRemove( pEdict ); - // unlink from world - CL_UnlinkEdict( pEdict ); - // clear curstate Mem_Set( &pEdict->curstate, 0, sizeof( pEdict->curstate )); pEdict->index = -1; // freed @@ -783,7 +775,6 @@ void CL_InitWorld( void ) ent = EDICT_NUM( 0 ); ent->index = NUM_FOR_EDICT( ent ); - ent->curstate.classname = MAKE_STRING( "worldspawn" ); ent->curstate.modelindex = 1; // world model ent->curstate.solid = SOLID_BSP; ent->curstate.movetype = MOVETYPE_PUSH; @@ -833,22 +824,13 @@ void CL_FreeEdicts( void ) if( cl.frames ) Mem_Free( cl.frames ); // clear globals - if( sys_sharedstrings->integer ) - Mem_EmptyPool( clgame.stringspool ); - else StringTable_Clear( clgame.hStringTable ); + StringTable_Clear( clgame.hStringTable ); clgame.globals->numEntities = 0; clgame.entities = NULL; cl.frames = NULL; } -const char *CL_ClassName( const cl_entity_t *e ) -{ - if( !e ) return "(null)"; - if( e->index == -1 ) return "freed"; - return STRING( e->curstate.classname ); -} - /* =============================================================================== CGame Builtin Functions @@ -1528,7 +1510,7 @@ pfnPointContents */ static int pfnPointContents( const float *rgflVector, int *truecont ) { - return CL_PointContents( rgflVector ); + return CM_PointContents( rgflVector ); // FIXME } /* @@ -1545,7 +1527,7 @@ static cl_entity_t *pfnWaterEntity( const float *rgflPos ) if( !rgflPos ) return NULL; // grab contents from all the water entities - num = CL_AreaEdicts( rgflPos, rgflPos, touch, MAX_EDICTS, AREA_CUSTOM ); + num = 0;// FIXME: CL_AreaEdicts( rgflPos, rgflPos, touch, MAX_EDICTS, AREA_CUSTOM ); for( i = 0; i < num; i++ ) { @@ -1651,7 +1633,7 @@ static void pfnPlaybackEvent( int flags, const cl_entity_t *pInvoker, word event if( VectorIsNull( args.angles )) VectorCopy( pInvoker->curstate.angles, args.angles ); VectorCopy( pInvoker->curstate.velocity, args.velocity ); - args.ducking = (pInvoker->curstate.flags & FL_DUCKING) ? true : false; + args.ducking = pInvoker->curstate.usehull; } CL_QueueEvent( flags, eventindex, delay, &args ); @@ -2466,7 +2448,8 @@ static cl_enginefuncs_t gEngfuncs = pfnCmd_Args, CL_GetLerpFrac, pfnDelCommand, - pfnAlertMessage, + pfnCon_Printf, + pfnCon_DPrintf, pfnPhysInfo_ValueForKey, pfnServerInfo_ValueForKey, pfnGetClientMaxspeed, @@ -2517,10 +2500,7 @@ void CL_UnloadProgs( void ) // deinitialize game clgame.dllFuncs.pfnShutdown(); - - if( sys_sharedstrings->integer ) - Mem_FreePool( &clgame.stringspool ); - else StringTable_Delete( clgame.hStringTable ); + StringTable_Delete( clgame.hStringTable ); FS_FreeLibrary( clgame.hInstance ); Mem_FreePool( &cls.mempool ); @@ -2573,16 +2553,8 @@ bool CL_LoadProgs( const char *name ) clgame.globals->pStringBase = ""; // setup string base - if( sys_sharedstrings->integer ) - { - // just use Half-Life system - base pointer and malloc - clgame.stringspool = Mem_AllocPool( "Client Strings" ); - } - else - { - // 65535 unique strings should be enough ... - clgame.hStringTable = StringTable_Create( "Client", 0x10000 ); - } + // 65535 unique strings should be enough ... + clgame.hStringTable = StringTable_Create( "Client", 0x10000 ); clgame.globals->maxEntities = GI->max_edicts; // merge during loading diff --git a/engine/client/cl_main.c b/engine/client/cl_main.c index da7c9d0b..981fd119 100644 --- a/engine/client/cl_main.c +++ b/engine/client/cl_main.c @@ -595,8 +595,6 @@ void CL_Disconnect( void ) // send a disconnect message to the server CL_SendDisconnectMessage(); - Delta_Shutdown(); - CL_ClearState (); // stop download @@ -829,9 +827,6 @@ void CL_PrepVideo( void ) SCR_RegisterShaders(); // update with new sequence SCR_UpdateScreen(); - // clear physics interaction links - CL_ClearWorld (); - for( i = 0, mdlcount = 0; i < MAX_MODELS && cl.configstrings[CS_MODELS+1+i][0]; i++ ) mdlcount++; // total num models @@ -1271,16 +1266,17 @@ void CL_InitLocal( void ) // userinfo info_password = Cvar_Get( "password", "", CVAR_USERINFO, "player password" ); info_spectator = Cvar_Get( "spectator", "0", CVAR_USERINFO, "spectator mode" ); - name = Cvar_Get( "name", SI->username, CVAR_USERINFO | CVAR_ARCHIVE, "player name" ); - model = Cvar_Get( "model", "player", CVAR_USERINFO | CVAR_ARCHIVE, "player model ('player' it's a single player model)" ); - topcolor = Cvar_Get( "topcolor", "0", CVAR_USERINFO | CVAR_ARCHIVE, "player top color" ); + name = Cvar_Get( "name", SI->username, CVAR_USERINFO|CVAR_ARCHIVE|CVAR_PRINTABLEONLY, "player name" ); + model = Cvar_Get( "model", "player", CVAR_USERINFO|CVAR_ARCHIVE, "player model ('player' it's a single player model)" ); + topcolor = Cvar_Get( "topcolor", "0", CVAR_USERINFO|CVAR_ARCHIVE, "player top color" ); bottomcolor = Cvar_Get( "bottomcolor", "0", CVAR_USERINFO | CVAR_ARCHIVE, "player bottom color" ); rate = Cvar_Get( "rate", "25000", CVAR_USERINFO | CVAR_ARCHIVE, "player network rate" ); // FIXME userinfo = Cvar_Get( "@userinfo", "0", CVAR_READ_ONLY, "" ); // use ->modified value only cl_showfps = Cvar_Get( "cl_showfps", "1", CVAR_ARCHIVE, "show client fps" ); cl_lw = Cvar_Get( "cl_lw", "1", CVAR_ARCHIVE|CVAR_USERINFO, "enable client weapon predicting" ); cl_smooth = Cvar_Get ("cl_smooth", "1", 0, "smooth up stair climbing and interpolate position in multiplayer" ); - + Cvar_Get( "hud_scale", "0", CVAR_ARCHIVE|CVAR_LATCH, "scale hud at current resolution" ); + // register our commands Cmd_AddCommand ("cmd", CL_ForwardToServer_f, "send a console commandline to the server" ); Cmd_AddCommand ("pause", NULL, "pause the game (if the server allows pausing)" ); diff --git a/engine/client/cl_menu.c b/engine/client/cl_menu.c index debd38c9..eed4f7ac 100644 --- a/engine/client/cl_menu.c +++ b/engine/client/cl_menu.c @@ -810,7 +810,8 @@ static ui_enginefuncs_t gEngfuncs = pfnCmd_Argc, pfnCmd_Argv, pfnCmd_Args, - pfnAlertMessage, + pfnCon_Printf, + pfnCon_DPrintf, pfnPlaySound, pfnDrawCharacter, pfnDrawConsoleString, diff --git a/engine/client/cl_parse.c b/engine/client/cl_parse.c index 773ff4ee..a9d93aac 100644 --- a/engine/client/cl_parse.c +++ b/engine/client/cl_parse.c @@ -6,7 +6,6 @@ #include "common.h" #include "client.h" #include "protocol.h" -#include "net_sound.h" #include "net_encode.h" #include "event_flags.h" @@ -584,7 +583,6 @@ void CL_ParseBaseline( sizebuf_t *msg ) else timebase = 1000; // sv.state == ss_loading MSG_ReadDeltaEntity( msg, &nullstate, &ent->baseline, newnum, cl.time ); - CL_LinkEdict( ent, false ); // first entering, link always } /* @@ -652,11 +650,6 @@ void CL_ParseConfigString( sizebuf_t *msg ) { CL_SetEventIndex( cl.configstrings[i], i - CS_EVENTS ); } - else if( i >= CS_CLASSNAMES && i < CS_CLASSNAMES+MAX_CLASSNAMES ) - { - // edicts classnames for search by classname on client - cl.edict_classnames[i-CS_CLASSNAMES] = MAKE_STRING( cl.configstrings[i] ); - } else if( i >= CS_LIGHTSTYLES && i < CS_LIGHTSTYLES+MAX_LIGHTSTYLES ) { CL_SetLightstyle( i - CS_LIGHTSTYLES ); diff --git a/engine/client/cl_pmove.c b/engine/client/cl_pmove.c index bc9ee8ba..9df80b3f 100644 --- a/engine/client/cl_pmove.c +++ b/engine/client/cl_pmove.c @@ -31,7 +31,7 @@ void CL_InitClientMove( void ) clgame.pmove->runfuncs = false; // enumerate client hulls - for( i = 0; i < PM_MAXHULLS; i++ ) + for( i = 0; i < 4; i++ ) clgame.dllFuncs.pfnGetHullBounds( i, clgame.pmove->player_mins[i], clgame.pmove->player_maxs[i] ); // common utilities diff --git a/engine/client/cl_pred.c b/engine/client/cl_pred.c index 5b48a5ea..3b4d0ea6 100644 --- a/engine/client/cl_pred.c +++ b/engine/client/cl_pred.c @@ -26,8 +26,6 @@ bool CL_IsPredicted( void ) if( cls.netchan.outgoing_sequence - cls.netchan.incoming_sequence >= CL_UPDATE_BACKUP - 1 ) return false; - if( player->curstate.ed_flags & ESF_NO_PREDICTION ) - return false; if( !cl_predict->integer ) return false; return true; @@ -124,8 +122,9 @@ void CL_SetIdealPitch( cl_entity_t *ent ) bottom[0] = top[0]; bottom[1] = top[1]; bottom[2] = top[2] - 160; - - tr = CL_Move( top, vec3_origin, vec3_origin, bottom, MOVE_NOMONSTERS, ent ); + +// FIXME: write client trace +// tr = CL_Move( top, vec3_origin, vec3_origin, bottom, MOVE_NOMONSTERS, ent ); if( tr.fAllSolid ) return; // looking at a wall, leave ideal the way is was diff --git a/engine/client/cl_scrn.c b/engine/client/cl_scrn.c index 898266b7..2d1057bb 100644 --- a/engine/client/cl_scrn.c +++ b/engine/client/cl_scrn.c @@ -214,8 +214,6 @@ void SCR_DrawPlaque( void ) levelshot = re->RegisterShader( cl_levelshot_name->string, SHADER_NOMIP ); re->SetParms( levelshot, kRenderNormal, 0 ); re->DrawStretchPic( 0, 0, scr_width->integer, scr_height->integer, 0, 0, 1, 1, levelshot ); - - CL_DrawHUD( CL_LOADING ); // update progress bar } } diff --git a/engine/client/cl_tent.c b/engine/client/cl_tent.c index 7941d805..3e83e884 100644 --- a/engine/client/cl_tent.c +++ b/engine/client/cl_tent.c @@ -42,13 +42,13 @@ void CL_WeaponAnim( int iAnim, int body ) view->curstate.body = body; } -int CL_AddEntity( cl_entity_t *pEnt, int ed_type, shader_t customShader ) +int CL_AddEntity( cl_entity_t *pEnt, int entityType, shader_t customShader ) { - if( !re || !pEnt ) + if( !re || !pEnt || pEnt->index == -1 ) return false; // let the render reject entity without model - return re->AddRefEntity( pEnt, ed_type, customShader ); + return re->AddRefEntity( pEnt, entityType, customShader ); } /* diff --git a/engine/client/cl_view.c b/engine/client/cl_view.c index 527776a4..e51c21bd 100644 --- a/engine/client/cl_view.c +++ b/engine/client/cl_view.c @@ -6,6 +6,7 @@ #include "common.h" #include "client.h" #include "const.h" +#include "entity_types.h" /* ==================== @@ -83,7 +84,7 @@ void V_AddViewModel( void ) if( cl.refdef.nextView ) return; // add viewmodel only at firstperson pass if( !cl.frame.valid || cl.refdef.paused ) return; - clgame.dllFuncs.pfnAddVisibleEntity( &clgame.viewent, ED_VIEWMODEL ); + clgame.dllFuncs.pfnAddVisibleEntity( &clgame.viewent, ET_VIEWENTITY ); } /* diff --git a/engine/client/cl_world.c b/engine/client/cl_world.c deleted file mode 100644 index ca90e56c..00000000 --- a/engine/client/cl_world.c +++ /dev/null @@ -1,523 +0,0 @@ -//======================================================================= -// Copyright XashXT Group 2009 © -// cl_world.c - world query functions -//======================================================================= - -#include "common.h" -#include "client.h" -#include "const.h" -#include "pm_defs.h" - -areanode_t cl_areanodes[AREA_NODES]; -int cl_numareanodes; - -/* -=============== -CL_CreateAreaNode - -builds a uniformly subdivided tree for the given world size -=============== -*/ -areanode_t *CL_CreateAreaNode( int depth, vec3_t mins, vec3_t maxs ) -{ - areanode_t *anode; - vec3_t size; - vec3_t mins1, maxs1; - vec3_t mins2, maxs2; - - anode = &cl_areanodes[cl_numareanodes++]; - - ClearLink( &anode->trigger_edicts ); - ClearLink( &anode->solid_edicts ); - ClearLink( &anode->water_edicts ); - - if( depth == AREA_DEPTH ) - { - anode->axis = -1; - anode->children[0] = anode->children[1] = NULL; - return anode; - } - - VectorSubtract( maxs, mins, size ); - if( size[0] > size[1] ) - anode->axis = 0; - else anode->axis = 1; - - anode->dist = 0.5f * (maxs[anode->axis] + mins[anode->axis]); - VectorCopy( mins, mins1 ); - VectorCopy( mins, mins2 ); - VectorCopy( maxs, maxs1 ); - VectorCopy( maxs, maxs2 ); - - maxs1[anode->axis] = mins2[anode->axis] = anode->dist; - anode->children[0] = CL_CreateAreaNode( depth+1, mins2, maxs2 ); - anode->children[1] = CL_CreateAreaNode( depth+1, mins1, maxs1 ); - - return anode; -} - -/* -=============== -CL_ClearWorld - -=============== -*/ -void CL_ClearWorld( void ) -{ - vec3_t mins, maxs; - int worldIndex = 1; - - cl_numareanodes = 0; - Mod_GetBounds( worldIndex, mins, maxs ); - Mem_Set( cl_areanodes, 0, sizeof( cl_areanodes )); - CL_CreateAreaNode( 0, mins, maxs ); -} - -/* -================= -CL_ClassifyEdict - -sorting edict by type -================= -*/ -void CL_ClassifyEdict( cl_entity_t *cl_ent ) -{ - if( !cl_ent || cl_ent->curstate.ed_type != ED_SPAWNED ) - return; - - cl_ent->curstate.ed_type = ED_TEMPENTITY; // it's client entity - - // display type - // Msg( "%s: <%s>\n", STRING( ent->v.classname ), ed_name[cl_ent->curstate.ed_type] ); -} - -/* -=============== -CL_UnlinkEdict -=============== -*/ -void CL_UnlinkEdict( cl_entity_t *ent ) -{ - // not linked in anywhere - if( !ent->area.prev ) - return; - - RemoveLink( &ent->area ); - ent->area.prev = NULL; - ent->area.next = NULL; -} - -void CL_SetAbsBbox( cl_entity_t *ent ) -{ - if (( ent->curstate.solid == SOLID_BSP ) && !VectorIsNull( ent->angles )) - { - // expand for rotation - float max = 0, v; - int i; - - for ( i = 0; i < 3; i++ ) - { - v = fabs( ent->curstate.mins[i] ); - if ( v > max ) max = v; - v = fabs( ent->curstate.maxs[i] ); - if ( v > max ) max = v; - } - - for ( i = 0; i < 3; i++ ) - { - ent->absmin[i] = ent->origin[i] - max; - ent->absmax[i] = ent->origin[i] + max; - } - } - else - { - VectorAdd( ent->origin, ent->curstate.mins, ent->absmin ); - VectorAdd( ent->origin, ent->curstate.maxs, ent->absmax ); - } - - ent->absmin[0] -= 1; - ent->absmin[1] -= 1; - ent->absmin[2] -= 1; - ent->absmax[0] += 1; - ent->absmax[1] += 1; - ent->absmax[2] += 1; -} - -/* -=============== -CL_LinkEntity -=============== -*/ -void CL_LinkEdict( cl_entity_t *ent, bool touch_triggers ) -{ - areanode_t *node; - - if( !ent ) return; - if( ent->area.prev ) CL_UnlinkEdict( ent ); // unlink from old position - if( ent->index <= 0 ) return; - - // trying to classify unclassified edicts - if( cls.state == ca_active && ent->curstate.ed_type == ED_SPAWNED ) - CL_ClassifyEdict( ent ); - - // set the abs box - CL_SetAbsBbox( ent ); - - // ignore not solid bodies - if( ent->curstate.solid == SOLID_NOT && ent->curstate.skin == CONTENTS_NONE ) - return; - - // find the first node that the ent's box crosses - node = cl_areanodes; - - while( 1 ) - { - if( node->axis == -1 ) break; - if( ent->absmin[node->axis] > node->dist ) - node = node->children[0]; - else if( ent->absmax[node->axis] < node->dist ) - node = node->children[1]; - else break; // crosses the node - } - - // link it in - if( ent->curstate.solid == SOLID_TRIGGER ) - InsertLinkBefore( &ent->area, &node->trigger_edicts, ent->index ); - else if( ent->curstate.solid == SOLID_NOT && ent->curstate.skin != CONTENTS_NONE ) - InsertLinkBefore (&ent->area, &node->water_edicts, ent->index ); - else InsertLinkBefore (&ent->area, &node->solid_edicts, ent->index ); - - if( touch_triggers ) Host_Error( "CL_LinkEdict: touch_tiggers\n" ); -} - -/* -============================================================================ - -AREA QUERY - -Fills in a list of all entities who's absmin / absmax intersects the given -bounds. This does NOT mean that they actually touch in the case of bmodels. -============================================================================ -*/ -/* -==================== -CL_AreaEdicts_r -==================== -*/ -void CL_AreaEdicts_r( areanode_t *node, area_t *ap ) -{ - link_t *l, *next, *start; - cl_entity_t *check; - int count = 0; - - // touch linked edicts - if( ap->type == AREA_SOLID ) - start = &node->solid_edicts; - else if( ap->type == AREA_TRIGGERS ) - start = &node->trigger_edicts; - else start = &node->water_edicts; - - for( l = start->next; l != start; l = next ) - { - next = l->next; - check = EDICT_FROM_AREA( l ); - - if( check->curstate.solid == SOLID_NOT && check->curstate.skin == CONTENTS_NONE ) - continue; // deactivated - if( !BoundsIntersect( check->absmin, check->absmax, ap->mins, ap->maxs )) - continue; // not touching - - if( ap->count == ap->maxcount ) - { - MsgDev( D_WARN, "CL_AreaEdicts: maxcount hit\n" ); - return; - } - - ap->list[ap->count] = check; - ap->count++; - } - - if( node->axis == -1 ) return; // terminal node - - // recurse down both sides - if( ap->maxs[node->axis] > node->dist ) CL_AreaEdicts_r( node->children[0], ap ); - if( ap->mins[node->axis] < node->dist ) CL_AreaEdicts_r( node->children[1], ap ); -} - -/* -================ -CL_AreaEdicts -================ -*/ -int CL_AreaEdicts( const vec3_t mins, const vec3_t maxs, cl_entity_t **list, int maxcount, int areatype ) -{ - area_t ap; - - ap.mins = mins; - ap.maxs = maxs; - ap.list = list; - ap.count = 0; - ap.maxcount = maxcount; - ap.type = areatype; - - CL_AreaEdicts_r( cl_areanodes, &ap ); - - return ap.count; -} - -/* -==================== -CL_ClipToLinks - -Mins and maxs enclose the entire area swept by the move -==================== -*/ -static void CL_ClipToLinks( areanode_t *node, moveclip_t *clip ) -{ - link_t *l, *next; - cl_entity_t *touch; - trace_t trace; - - // touch linked edicts - for( l = node->solid_edicts.next; l != &node->solid_edicts; l = next ) - { - next = l->next; - - touch = EDICT_FROM_AREA( l ); - - if( touch->curstate.solid == SOLID_NOT ) - continue; - if( touch == (cl_entity_t *)clip->passedict ) - continue; - if( touch->curstate.solid == SOLID_TRIGGER ) - { - // throw warn and ignore - MsgDev( D_WARN, "entity %s (%i) with SOLID_TRIGGER in clipping list\n", CL_ClassName( touch ), touch->index ); - touch->curstate.solid = SOLID_NOT; - continue; - } - if( clip->type == MOVE_NOMONSTERS && touch->curstate.solid != SOLID_BSP ) - continue; - - // don't clip points against points (they can't collide) - if( VectorCompare( touch->curstate.mins, touch->curstate.maxs ) && (clip->type != MOVE_MISSILE || !(touch->curstate.flags & FL_MONSTER))) - continue; - - if( clip->type == MOVE_WORLDONLY ) - { - // accept only real bsp models with FL_WORLDBRUSH set - if( CM_GetModelType( touch->curstate.modelindex ) == mod_brush && touch->curstate.flags & FL_WORLDBRUSH ); - else continue; - } - - if( !BoundsIntersect( clip->boxmins, clip->boxmaxs, touch->absmin, touch->absmax )) - continue; - - // monsterclip filter - if( CM_GetModelType( touch->curstate.modelindex ) == mod_brush && ( touch->curstate.flags & FL_MONSTERCLIP )) - { - if( clip->passedict && ((cl_entity_t *)&clip->passedict)->curstate.flags & FL_MONSTERCLIP ); - else continue; - } - - if( clip->flags & FMOVE_IGNORE_GLASS && CM_GetModelType( touch->curstate.modelindex ) == mod_brush ) - { - vec3_t point; - - // we can ignore brushes with rendermode != kRenderNormal - switch( touch->curstate.rendermode ) - { - case kRenderTransTexture: - case kRenderTransAlpha: - case kRenderTransAdd: - if( touch->curstate.renderamt < 200 ) - continue; - // check for translucent contents - if( VectorIsNull( touch->origin )) - VectorAverage( touch->absmin, touch->absmax, point ); - else VectorCopy( touch->origin, point ); - if( CL_PointContents( point ) == CONTENTS_TRANSLUCENT ) - continue; // grate detected - default: break; - } - } - - // might intersect, so do an exact clip - if( clip->trace.fAllSolid ) return; - - if( clip->passedict ) - { - if( CL_GetEntityByIndex( touch->curstate.owner ) == (cl_entity_t *)clip->passedict ) - continue; // don't clip against own missiles - if(CL_GetEntityByIndex( ((cl_entity_t *)&clip->passedict)->curstate.owner ) == touch ) - continue; // don't clip against owner - } -#if 0 - // temporary disabled: wait for moving physic.dll into engine - if( touch->curstate.flags & FL_MONSTER ) - trace = CM_ClipMove( touch, clip->start, clip->mins2, clip->maxs2, clip->end, clip->flags ); - else trace = CM_ClipMove( touch, clip->start, clip->mins, clip->maxs, clip->end, clip->flags ); - - clip->trace = World_CombineTraces( &clip->trace, &trace, touch ); -#endif - } - - // recurse down both sides - if( node->axis == -1 ) return; - - if( clip->boxmaxs[node->axis] > node->dist ) - CL_ClipToLinks( node->children[0], clip ); - if( clip->boxmins[node->axis] < node->dist ) - CL_ClipToLinks( node->children[1], clip ); -} - -/* -================== -CL_Move -================== -*/ -trace_t CL_Move( const vec3_t start, vec3_t mins, vec3_t maxs, const vec3_t end, int type, cl_entity_t *e ) -{ - trace_t trace; -#if 0 - moveclip_t clip; - int i; - - Mem_Set( &clip, 0, sizeof( moveclip_t )); - - clip.start = start; - clip.end = end; - clip.mins = mins; - clip.maxs = maxs; - clip.type = (type & 0xFF); - clip.flags = (type & 0xFF00); - clip.passedict = e; - - // clip to world - clip.trace = CM_ClipMove( EDICT_NUM( 0 ), start, mins, maxs, end, 0 ); - - if( type == MOVE_MISSILE ) - { - for( i = 0; i < 3; i++ ) - { - clip.mins2[i] = -15; - clip.maxs2[i] = 15; - } - } - else - { - VectorCopy( mins, clip.mins2 ); - VectorCopy( maxs, clip.maxs2 ); - } - - // create the bounding box of the entire move - World_MoveBounds( start, clip.mins2, clip.maxs2, end, clip.boxmins, clip.boxmaxs ); - - // clip to entities - CL_ClipToLinks( cl_areanodes, &clip ); - - return clip.trace; -#endif - // FIXME: write client trace - - // fill in a default trace - Mem_Set( &trace, 0, sizeof( trace_t )); - VectorCopy( end, trace.vecEndPos ); - trace.flFraction = 1.0f; - trace.fAllSolid = true; - trace.iHitgroup = -1; - - return trace; -} - -/* -============= -CL_PointContents - -============= -*/ -int CL_TruePointContents( const vec3_t p ) -{ - int i, num, contents; - cl_entity_t *hit, *touch[MAX_EDICTS]; - - // sanity check - if( !p ) return CONTENTS_NONE; - - // get base contents from world - contents = CM_PointContents( p ); - - if( contents != CONTENTS_EMPTY ) - return contents; // have some world contents - - // check contents from all the solid entities - num = CL_AreaEdicts( p, p, touch, MAX_EDICTS, AREA_SOLID ); - - for( i = 0; i < num; i++ ) - { - hit = touch[i]; - - if( hit->curstate.solid != SOLID_BSP ) - continue; // monsters, players - - // solid entity found - return CONTENTS_SOLID; - } - - // check contents from all the custom entities - num = CL_AreaEdicts( p, p, touch, MAX_EDICTS, AREA_CUSTOM ); - - for( i = 0; i < num; i++ ) - { - hit = touch[i]; - - if( hit->curstate.solid != SOLID_NOT || hit->curstate.skin == CONTENTS_NONE ) - continue; // invalid water ? - - // custom contents found - return hit->curstate.skin; - } - return contents; -} - -int CL_PointContents( const vec3_t p ) -{ - int cont = CL_TruePointContents( p ); - - if( cont <= CONTENTS_CURRENT_0 && cont >= CONTENTS_CURRENT_DOWN ) - cont = CONTENTS_WATER; - return cont; -} - -/* -============ -CL_TraceLine - -soundlib light version -============ -*/ -trace_t CL_TraceLine( const vec3_t start, const vec3_t end ) -{ - return CL_Move( start, vec3_origin, vec3_origin, end, MOVE_NOMONSTERS, NULL ); -} - -/* -============ -CL_TestPlayerPosition - -============ -*/ -cl_entity_t *CL_TestPlayerPosition( const vec3_t origin, cl_entity_t *pass, TraceResult *tr ) -{ - float *mins, *maxs; - trace_t result; - - clgame.pmove->usehull = bound( 0, clgame.pmove->usehull, 3 ); - mins = clgame.pmove->player_mins[clgame.pmove->usehull]; - maxs = clgame.pmove->player_maxs[clgame.pmove->usehull]; - - result = CL_Move( origin, mins, maxs, origin, MOVE_NORMAL, pass ); - if( tr ) Mem_Copy( tr, &result, sizeof( *tr )); - - return result.pEnt; -} \ No newline at end of file diff --git a/engine/client/client.h b/engine/client/client.h index 2f3dd449..16f8bfc7 100644 --- a/engine/client/client.h +++ b/engine/client/client.h @@ -98,7 +98,6 @@ typedef struct // locally derived information from server state model_t models[MAX_MODELS]; - string_t edict_classnames[MAX_CLASSNAMES]; sound_t sound_precache[MAX_SOUNDS]; shader_t decal_shaders[MAX_DECALNAMES]; } client_t; @@ -146,6 +145,15 @@ typedef enum scrshot_skyshot // skybox view } scrshot_t; +// client screen state +typedef enum +{ + CL_LOADING = 1, // draw loading progress-bar + CL_ACTIVE, // draw normal hud + CL_PAUSED, // pause when active + CL_CHANGELEVEL, // draw 'loading' during changelevel +} scrstate_t; + typedef struct { char name[32]; @@ -408,7 +416,6 @@ void CL_DrawHUD( int state ); void CL_InitEdicts( void ); void CL_FreeEdicts( void ); void CL_InitWorld( void ); -const char *CL_ClassName( const cl_entity_t *e ); void CL_InitEntity( cl_entity_t *pEdict ); void CL_FreeEntity( cl_entity_t *pEdict ); string_t CL_AllocString( const char *szValue ); @@ -473,7 +480,7 @@ void CL_GetEntitySpatialization( int ent, vec3_t origin, vec3_t velocity ); // // cl_tent.c // -int CL_AddEntity( cl_entity_t *pEnt, int ed_type, shader_t customShader ); +int CL_AddEntity( cl_entity_t *pEnt, int entityType, shader_t customShader ); void CL_WeaponAnim( int iAnim, int body ); void CL_ClearEffects( void ); void CL_TestLights( void ); @@ -532,18 +539,4 @@ void SCR_RunCinematic( void ); void SCR_StopCinematic( void ); void CL_PlayVideo_f( void ); -// -// cl_world.c -// -void CL_ClearWorld( void ); -void CL_UnlinkEdict( cl_entity_t *ent ); -void CL_ClassifyEdict( cl_entity_t *ent ); -void CL_LinkEdict( cl_entity_t *ent, bool touch_triggers ); -int CL_AreaEdicts( const vec3_t mins, const vec3_t maxs, cl_entity_t **list, int maxcount, int areatype ); -trace_t CL_Move( const vec3_t start, vec3_t mins, vec3_t maxs, const vec3_t end, int type, cl_entity_t *e ); -cl_entity_t *CL_TestPlayerPosition( const vec3_t origin, cl_entity_t *pass, TraceResult *tr ); -trace_t CL_MoveToss( cl_entity_t *tossent, cl_entity_t *ignore ); -int CL_TruePointContents( const vec3_t p ); -int CL_PointContents( const vec3_t p ); - #endif//CLIENT_H \ No newline at end of file diff --git a/engine/common.h b/engine/common.h index 73678376..b53ed765 100644 --- a/engine/common.h +++ b/engine/common.h @@ -40,7 +40,6 @@ void DBG_AssertFunction( BOOL fExpr, const char* szExpr, const char* szFile, int extern cvar_t *scr_width; extern cvar_t *scr_height; extern cvar_t *allow_download; -extern cvar_t *sys_sharedstrings; extern cvar_t *host_maxfps; /* @@ -69,6 +68,15 @@ typedef enum RD_PACKET } rdtype_t; +// game print level +typedef enum +{ + PRINT_LOW, // pickup messages + PRINT_MEDIUM, // death messages + PRINT_HIGH, // critical messages + PRINT_CHAT, // chat messages +} messagelevel_t; + typedef struct host_redirect_s { rdtype_t target; @@ -171,6 +179,7 @@ void pfnCVarSetString( const char *szName, const char *szValue ); void pfnCVarSetValue( const char *szName, float flValue ); float pfnCVarGetValue( const char *szName ); char* pfnCVarGetString( const char *szName ); +cvar_t *pfnCVarGetPointer( const char *szVarName ); void pfnFreeFile( void *buffer ); int pfnFileExists( const char *filename ); void *pfnLoadLibrary( const char *name ); @@ -180,11 +189,12 @@ long pfnRandomLong( long lLow, long lHigh ); float pfnRandomFloat( float flLow, float flHigh ); void pfnAddCommand( const char *cmd_name, xcommand_t func, const char *cmd_desc ); void pfnDelCommand( const char *cmd_name ); -void pfnAlertMessage( ALERT_TYPE level, char *szFmt, ... ); void *Cache_Check( byte *mempool, cache_user_t *c ); void pfnGetGameDir( char *szGetGameDir ); const char *pfnCmd_Args( void ); const char *pfnCmd_Argv( int argc ); +void pfnCon_DPrintf( char *fmt, ... ); +void pfnCon_Printf( char *szFmt, ... ); int pfnCmd_Argc( void ); int pfnIsInGame( void ); float pfnTime( void ); @@ -247,6 +257,7 @@ int CL_GetMaxClients( void ); bool CL_IsPlaybackDemo( void ); bool SV_GetComment( const char *savename, char *comment ); bool SV_NewGame( const char *mapName, bool loadGame ); +void SV_SysError( const char *error_string ); bool SV_LoadProgs( const char *name ); void SV_InitGameProgs( void ); void SV_ForceError( void ); diff --git a/engine/common/cm_light.c b/engine/common/cm_light.c index b7b89731..bd3333fc 100644 --- a/engine/common/cm_light.c +++ b/engine/common/cm_light.c @@ -4,7 +4,7 @@ //======================================================================= #include "cm_local.h" -#include "entity_def.h" +#include "edict.h" #include "mathlib.h" int CM_RecursiveLightPoint( vec3_t color, mnode_t *node, const vec3_t start, const vec3_t end ) diff --git a/engine/common/cm_local.h b/engine/common/cm_local.h index 22ec1413..ed026716 100644 --- a/engine/common/cm_local.h +++ b/engine/common/cm_local.h @@ -7,7 +7,8 @@ #include "common.h" #include "bspfile.h" -#include "trace_def.h" +#include "edict.h" +#include "eiface.h" #include "com_model.h" #define FMOVE_IGNORE_GLASS 0x100 diff --git a/engine/common/cm_studio.c b/engine/common/cm_studio.c index abf3f1c2..022b28d5 100644 --- a/engine/common/cm_studio.c +++ b/engine/common/cm_studio.c @@ -6,7 +6,7 @@ #include "cm_local.h" #include "mathlib.h" #include "matrix_lib.h" -#include "entity_def.h" +#include "edict.h" #include "byteorder.h" #include "const.h" diff --git a/engine/common/cm_test.c b/engine/common/cm_test.c index 36b1e82c..ed131ebd 100644 --- a/engine/common/cm_test.c +++ b/engine/common/cm_test.c @@ -242,7 +242,7 @@ CM_PointContents */ int CM_PointContents( const vec3_t p ) { - if( !worldmodel ) return CONTENTS_NONE; + if( !worldmodel ) return 0; return CM_HullPointContents( &worldmodel->hulls[0], 0, p ); } diff --git a/engine/common/cm_trace.c b/engine/common/cm_trace.c index 09198d7a..2c613612 100644 --- a/engine/common/cm_trace.c +++ b/engine/common/cm_trace.c @@ -4,7 +4,7 @@ //======================================================================= #include "cm_local.h" -#include "entity_def.h" +#include "edict.h" #include "mathlib.h" #include "matrix_lib.h" diff --git a/engine/common/engfuncs.c b/engine/common/engfuncs.c index a7edfc91..459c3224 100644 --- a/engine/common/engfuncs.c +++ b/engine/common/engfuncs.c @@ -250,9 +250,9 @@ cvar_t *pfnCVarRegister( const char *szName, const char *szValue, int flags, con if( flags & FCVAR_ARCHIVE ) real_flags |= CVAR_ARCHIVE; if( flags & FCVAR_USERINFO ) real_flags |= CVAR_USERINFO; - if( flags & FCVAR_SERVERINFO ) real_flags |= CVAR_SERVERINFO; - if( flags & FCVAR_PHYSICINFO ) real_flags |= CVAR_PHYSICINFO; - if( flags & FCVAR_LATCH ) real_flags |= CVAR_LATCH; + if( flags & FCVAR_SERVER ) real_flags |= CVAR_SERVERINFO; + if( flags & FCVAR_SPONLY ) real_flags |= CVAR_CHEAT; + if( flags & FCVAR_PRINTABLEONLY ) real_flags |= CVAR_PRINTABLEONLY; return Cvar_Get( szName, szValue, real_flags, szDesc ); } @@ -300,6 +300,18 @@ float pfnCVarGetValue( const char *szName ) return Cvar_VariableValue( szName ); } +/* +============= +pfnCVarGetPointer + +can return NULL +============= +*/ +cvar_t *pfnCVarGetPointer( const char *szVarName ) +{ + return Cvar_FindVar( szVarName ); +} + /* ============= pfnAddCommand @@ -328,45 +340,6 @@ void pfnDelCommand( const char *cmd_name ) Cmd_RemoveCommand( cmd_name ); } -/* -============= -pfnAlertMessage - -============= -*/ -void pfnAlertMessage( ALERT_TYPE level, char *szFmt, ... ) -{ - char buffer[2048]; // must support > 1k messages - va_list args; - - va_start( args, szFmt ); - com.vsnprintf( buffer, 2048, szFmt, args ); - va_end( args ); - - if( host.developer < level ) - return; - - switch( level ) - { - case at_console: - com.print( buffer ); - break; - case at_warning: - com.print( va("^3Warning:^7 %s", buffer )); - break; - case at_error: - com.print( va("^1Error:^7 %s", buffer )); - break; - case at_loading: - com.print( va("^2Loading:^7 %s", buffer )); - break; - case at_aiconsole: - case at_logged: - com.print( buffer ); - break; - } -} - /* ============= pfnCmd_Args @@ -402,6 +375,44 @@ int pfnCmd_Argc( void ) return Cmd_Argc(); } +/* +============= +pfnCon_Printf + +============= +*/ +void pfnCon_Printf( char *szFmt, ... ) +{ + char buffer[2048]; // must support > 1k messages + va_list args; + + va_start( args, szFmt ); + com.vsnprintf( buffer, 2048, szFmt, args ); + va_end( args ); + + com.print( buffer ); +} + +/* +============= +pfnCon_DPrintf + +============= +*/ +void pfnCon_DPrintf( char *szFmt, ... ) +{ + char buffer[2048]; // must support > 1k messages + va_list args; + + if( !host.developer ) return; + + va_start( args, szFmt ); + com.vsnprintf( buffer, 2048, szFmt, args ); + va_end( args ); + + com.print( buffer ); +} + /* ============= pfnLoadLibrary diff --git a/engine/common/net_encode.c b/engine/common/net_encode.c index d5da7e8c..00cb5b99 100644 --- a/engine/common/net_encode.c +++ b/engine/common/net_encode.c @@ -10,6 +10,7 @@ #include "net_encode.h" #include "event_api.h" #include "weaponinfo.h" +#include "entity_types.h" #define DELTA_PATH "delta.lst" static bool delta_init = false; @@ -51,9 +52,18 @@ static const delta_field_t pm_fields[] = { PHYS_DEF( bounce ) }, { PHYS_DEF( stepsize ) }, { PHYS_DEF( maxvelocity ) }, +{ PHYS_DEF( zmax ) }, +{ PHYS_DEF( waveHeight ) }, { PHYS_DEF( footsteps ) }, +{ PHYS_DEF( skyName ) }, { PHYS_DEF( rollangle ) }, { PHYS_DEF( rollspeed ) }, +{ PHYS_DEF( skycolor_r ) }, +{ PHYS_DEF( skycolor_g ) }, +{ PHYS_DEF( skycolor_b ) }, +{ PHYS_DEF( skyvec_x ) }, +{ PHYS_DEF( skyvec_y ) }, +{ PHYS_DEF( skyvec_z ) }, { NULL }, }; @@ -170,9 +180,7 @@ static const delta_field_t cd_fields[] = static const delta_field_t ent_fields[] = { -{ ENTS_DEF( classname ) }, -{ ENTS_DEF( ed_type ) }, -{ ENTS_DEF( ed_flags ) }, +{ ENTS_DEF( entityType ) }, { ENTS_DEF( origin[0] ) }, { ENTS_DEF( origin[1] ) }, { ENTS_DEF( origin[2] ) }, @@ -263,9 +271,6 @@ static const delta_field_t ent_fields[] = { ENTS_DEF( vuser4[0] ) }, { ENTS_DEF( vuser4[1] ) }, { ENTS_DEF( vuser4[2] ) }, - -// Xash3D legacy (needs to be removed) -{ ENTS_DEF( flags ) }, { NULL }, }; @@ -705,7 +710,14 @@ void Delta_InitFields( void ) token_t token; delta_script = Com_OpenScript( DELTA_PATH, NULL, 0 ); - if( !delta_script ) com.error( "DELTA_Load: couldn't load file %s\n", DELTA_PATH ); + if( !delta_script ) + { + static string errormsg; + + com.snprintf( errormsg, sizeof( errormsg ), "DELTA_Load: couldn't load file %s\n", DELTA_PATH ); + SV_SysError( errormsg ); + com.error( errormsg ); + } while( Com_ReadToken( delta_script, SC_ALLOW_NEWLINES|SC_ALLOW_PATHNAMES2, &token )) { @@ -759,9 +771,18 @@ void Delta_Init( void ) Delta_AddField( "movevars_t", "bounce", DT_FLOAT|DT_SIGNED, 16, 8.0f, 1.0f ); Delta_AddField( "movevars_t", "stepsize", DT_FLOAT|DT_SIGNED, 16, 16.0f, 1.0f ); Delta_AddField( "movevars_t", "maxvelocity", DT_FLOAT|DT_SIGNED, 16, 8.0f, 1.0f ); + Delta_AddField( "movevars_t", "zmax", DT_FLOAT|DT_SIGNED, 16, 1.0f, 1.0f ); // no fractional part + Delta_AddField( "movevars_t", "waveHeight", DT_FLOAT|DT_SIGNED, 16, 1.0f, 16.0f ); + Delta_AddField( "movevars_t", "skyName", DT_STRING, 1, 1.0f, 1.0f ); Delta_AddField( "movevars_t", "footsteps", DT_INTEGER, 1, 1.0f, 1.0f ); Delta_AddField( "movevars_t", "rollangle", DT_FLOAT|DT_SIGNED, 16, 32.0f, 1.0f ); Delta_AddField( "movevars_t", "rollspeed", DT_FLOAT|DT_SIGNED, 16, 8.0f, 1.0f ); + Delta_AddField( "movevars_t", "skycolor_r", DT_FLOAT|DT_SIGNED, 9, 1.0f, 1.0f ); // 0 - 264 + Delta_AddField( "movevars_t", "skycolor_g", DT_FLOAT|DT_SIGNED, 9, 1.0f, 1.0f ); + Delta_AddField( "movevars_t", "skycolor_b", DT_FLOAT|DT_SIGNED, 9, 1.0f, 1.0f ); + Delta_AddField( "movevars_t", "skyvec_x", DT_FLOAT|DT_SIGNED, 16, 32.0f, 1.0f ); // 0 - 1 + Delta_AddField( "movevars_t", "skyvec_y", DT_FLOAT|DT_SIGNED, 16, 32.0f, 1.0f ); + Delta_AddField( "movevars_t", "skyvec_z", DT_FLOAT|DT_SIGNED, 16, 32.0f, 1.0f ); // now done dt->bInitialized = true; @@ -1361,7 +1382,7 @@ void MSG_WriteDeltaEntity( entity_state_t *from, entity_state_t *to, sizebuf_t * // a NULL to is a delta remove message BF_WriteWord( msg, from->number ); - BF_WriteOneBit( msg, 1 ); // remove message + BF_WriteOneBit( msg, 1 ); // entity killed return; } @@ -1371,21 +1392,21 @@ void MSG_WriteDeltaEntity( entity_state_t *from, entity_state_t *to, sizebuf_t * Host_Error( "MSG_WriteDeltaEntity: Bad entity number: %i\n", to->number ); BF_WriteWord( msg, to->number ); - BF_WriteOneBit( msg, 0 ); // alive + BF_WriteOneBit( msg, 0 ); // alive - if( to->ed_type != from->ed_type ) + if( to->entityType != from->entityType ) { BF_WriteOneBit( msg, 1 ); - BF_WriteUBitLong( msg, to->ed_type, 5 ); + BF_WriteUBitLong( msg, to->entityType, 4 ); } else BF_WriteOneBit( msg, 0 ); - switch( to->ed_type ) + switch( to->entityType ) { - case ED_CLIENT: + case ET_PLAYER: dt = Delta_FindStruct( "entity_state_player_t" ); break; - case ED_BEAM: + case ET_BEAM: dt = Delta_FindStruct( "custom_entity_state_t" ); break; default: @@ -1445,14 +1466,14 @@ void MSG_ReadDeltaEntity( sizebuf_t *msg, entity_state_t *from, entity_state_t * } if( BF_ReadOneBit( msg )) - to->ed_type = BF_ReadUBitLong( msg, 5 ); + to->entityType = BF_ReadUBitLong( msg, 4 ); - switch( to->ed_type ) + switch( to->entityType ) { - case ED_CLIENT: + case ET_PLAYER: dt = Delta_FindStruct( "entity_state_player_t" ); break; - case ED_BEAM: + case ET_BEAM: dt = Delta_FindStruct( "custom_entity_state_t" ); break; default: diff --git a/engine/common/net_msg.h b/engine/common/net_msg.h index 0ab66906..29abe24f 100644 --- a/engine/common/net_msg.h +++ b/engine/common/net_msg.h @@ -39,9 +39,8 @@ #define CS_SOUNDS (CS_MODELS+MAX_MODELS) // sound names #define CS_DECALS (CS_SOUNDS+MAX_SOUNDS) // server decal indexes #define CS_EVENTS (CS_DECALS+MAX_DECALNAMES) // queue events -#define CS_GENERICS (CS_EVENTS+MAX_EVENTS) // edicts classnames -#define CS_CLASSNAMES (CS_GENERICS+MAX_GENERICS) // generic resources (e.g. color decals) -#define CS_LIGHTSTYLES (CS_CLASSNAMES+MAX_CLASSNAMES) // lightstyle patterns +#define CS_GENERICS (CS_EVENTS+MAX_EVENTS) // generic resources (e.g. color decals) +#define CS_LIGHTSTYLES (CS_GENERICS+MAX_GENERICS) // lightstyle patterns #define MAX_CONFIGSTRINGS (CS_LIGHTSTYLES+MAX_LIGHTSTYLES) // total count // huffman compression diff --git a/engine/common/net_sound.h b/engine/common/net_sound.h index d6da99f4..14997887 100644 --- a/engine/common/net_sound.h +++ b/engine/common/net_sound.h @@ -5,13 +5,4 @@ #ifndef NET_SOUND_H #define NET_SOUND_H -// sound flags -// see declaration flags 1 - 64 in const.h - -#define SND_SENTENCE (1<<7) // set if sound num is actually a sentence num -#define SND_VOLUME (1<<8) // a scaled byte -#define SND_ATTENUATION (1<<9) // a byte -#define SND_PITCH (1<<10) // a byte -#define SND_FIXED_ORIGIN (1<<11) // a vector - #endif//NET_SOUND_H \ No newline at end of file diff --git a/engine/common/world.c b/engine/common/world.c index 7e33acb7..c7fb99b1 100644 --- a/engine/common/world.c +++ b/engine/common/world.c @@ -8,25 +8,16 @@ #include "pm_defs.h" #include "mathlib.h" -const char *ed_name[] = +const char *et_name[] = { - "unknown", - "world", - "static", - "ambient", "normal", - "brush", "player", - "monster", - "tempent", + "tempentity", "beam", - "mover", - "viewmodel", - "physbody", - "trigger", + "fragmented", + "viewentity", "portal", "skyportal", - "error", }; /* diff --git a/engine/common/world.h b/engine/common/world.h index 19aeec90..5ec79d9f 100644 --- a/engine/common/world.h +++ b/engine/common/world.h @@ -10,6 +10,8 @@ #define MOVE_MISSILE 2 // extra size for monsters #define MOVE_WORLDONLY 3 // clip only world +#define CONTENTS_NONE 0 // no custom contents specified + /* =============================================================================== @@ -22,9 +24,13 @@ ENTITY AREA CHECKING #define AREA_NODES 64 #define AREA_DEPTH 5 -#define AREA_SOLID 1 -#define AREA_TRIGGERS 2 -#define AREA_CUSTOM 3 // custom contents - water, lava, fog etc +// link_t is only used for entity area links now +typedef struct link_s +{ + struct link_s *prev; + struct link_s *next; + int entnum; // svs.edicts \ cls.entities actual number +} link_t; typedef struct areanode_s { @@ -62,7 +68,7 @@ typedef struct moveclip_s int flags; // trace flags } moveclip_t; -extern const char *ed_name[]; +extern const char *et_name[]; // linked list void InsertLinkBefore( link_t *l, link_t *before, int entnum ); diff --git a/engine/engine.dsp b/engine/engine.dsp index 74a520e3..bd72061a 100644 --- a/engine/engine.dsp +++ b/engine/engine.dsp @@ -174,10 +174,6 @@ SOURCE=.\client\cl_view.c # End Source File # Begin Source File -SOURCE=.\client\cl_world.c -# End Source File -# Begin Source File - SOURCE=.\common\cm_debug.c # End Source File # Begin Source File diff --git a/engine/host.c b/engine/host.c index 4aebaa59..beb9142c 100644 --- a/engine/host.c +++ b/engine/host.c @@ -17,7 +17,6 @@ stdlib_api_t com, newcom; dll_info_t render_dll = { "", NULL, "CreateAPI", NULL, NULL, 0, sizeof(render_exp_t), sizeof(stdlib_api_t) }; dll_info_t vsound_dll = { "", NULL, "CreateAPI", NULL, NULL, 0, sizeof(vsound_exp_t), sizeof(stdlib_api_t) }; -cvar_t *sys_sharedstrings; cvar_t *host_serverstate; cvar_t *host_cheats; cvar_t *host_maxfps; @@ -575,11 +574,12 @@ void Host_Error( const char *error, ... ) if( host.framecount < 3 || host.state == HOST_SHUTDOWN ) { - Msg( "Host_InitError: " ); - com.error( hosterror1 ); + SV_SysError( hosterror1 ); + com.error( "Host_InitError: %s", hosterror1 ); } else if( host.framecount == host.errorframe ) { + SV_SysError( hosterror2 ); com.error( "Host_MultiError: %s", hosterror2 ); return; } @@ -588,7 +588,8 @@ void Host_Error( const char *error, ... ) if( recursive ) { Msg( "Host_RecursiveError: %s", hosterror2 ); - com.error( va( "%s", hosterror1 )); + SV_SysError( hosterror1 ); + com.error( hosterror1 ); return; // don't multiple executes } @@ -618,6 +619,7 @@ void Sys_Error_f( void ) const char *error = Cmd_Argv( 1 ); if( !*error ) error = "Invoked sys error"; + SV_SysError( error ); com.error( "%s\n", error ); } @@ -741,7 +743,6 @@ void Host_Init( const int argc, const char **argv ) Cmd_AddCommand ( "net_error", Net_Error_f, "send network bad message from random place"); } - sys_sharedstrings = Cvar_Get( "sys_sharedstrings", "0", CVAR_INIT|CVAR_ARCHIVE, "hl1 compatible strings" ); host_video = Cvar_Get( "host_video", "vid_gl.dll", CVAR_INIT|CVAR_ARCHIVE, "name of video rendering library"); host_audio = Cvar_Get( "host_audio", "snd_dx.dll", CVAR_INIT|CVAR_ARCHIVE, "name of sound rendering library"); host_cheats = Cvar_Get( "sv_cheats", "0", CVAR_LATCH, "allow cheat variables to enable" ); diff --git a/engine/server/server.h b/engine/server/server.h index fb8b9151..32abbab7 100644 --- a/engine/server/server.h +++ b/engine/server/server.h @@ -7,8 +7,8 @@ #define SERVER_H #include "mathlib.h" -#include "entity_def.h" -#include "svgame_api.h" +#include "edict.h" +#include "eiface.h" #include "cm_local.h" #include "pm_defs.h" #include "world.h" @@ -24,6 +24,7 @@ extern int SV_UPDATE_BACKUP; // hostflags #define SVF_SKIPLOCALHOST BIT( 0 ) #define SVF_PLAYERSONLY BIT( 1 ) +#define SVF_PORTALPASS BIT( 2 ) // we are do portal pass // mapvalid flags #define MAP_IS_EXIST BIT( 0 ) @@ -158,7 +159,7 @@ typedef struct sv_client_s } sv_client_t; // sv_private_edict_t -struct sv_priv_s +typedef struct sv_priv_s { link_t area; // linked to a division node or leaf sv_client_t *client; // filled for player ents @@ -167,13 +168,13 @@ struct sv_priv_s int num_leafs; short leafnums[MAX_ENT_LEAFS]; int framenum; // update framenumber - bool linked; // passed through SV_LinkEdict + bool send_baseline; // this entity already send baseline ? vec3_t moved_origin; vec3_t moved_angles; entity_state_t s; // baseline (this is a player_state too) -}; +} sv_priv_t; /* ============================================================================= @@ -228,13 +229,18 @@ typedef struct edict_t *edicts; // acess by edict number void *vp; // acess by offset in bytes }; + int numEntities; // actual entities count movevars_t movevars; // curstate movevars_t oldmovevars; // oldstate playermove_t *pmove; // pmove state + vec3_t player_mins[4]; // 4 hulls allowed + vec3_t player_maxs[4]; // 4 hulls allowed + globalvars_t *globals; // server globals DLL_FUNCTIONS dllFuncs; // dll exported funcs + NEW_DLL_FUNCTIONS dllFuncs2; // new dll exported funcs (can be NULL) byte *private; // server.dll private pool byte *mempool; // server premamnent pool: edicts etc byte *stringspool; // for shared strings @@ -307,7 +313,6 @@ void SV_DropClient( sv_client_t *drop ); int SV_ModelIndex( const char *name ); int SV_SoundIndex( const char *name ); -int SV_ClassIndex( const char *name ); int SV_DecalIndex( const char *name ); int SV_EventIndex( const char *name ); int SV_GenericIndex( const char *name ); @@ -329,7 +334,6 @@ void SV_DeactivateServer( void ); void SV_LevelInit( const char *pMapName, char const *pOldLevel, char const *pLandmarkName, bool loadGame ); bool SV_SpawnServer( const char *server, const char *startspot ); int SV_FindIndex( const char *name, int start, int end, bool create ); -void SV_ClassifyEdict( edict_t *ent, int m_iNewClass ); // // sv_phys.c diff --git a/engine/server/sv_client.c b/engine/server/sv_client.c index de7345a5..db29b501 100644 --- a/engine/server/sv_client.c +++ b/engine/server/sv_client.c @@ -8,6 +8,7 @@ #include "server.h" #include "protocol.h" #include "net_encode.h" +#include "entity_types.h" typedef struct ucmd_s { @@ -180,7 +181,7 @@ gotnewcl: edictnum = (newcl - svs.clients) + 1; ent = EDICT_NUM( edictnum ); - ent->pvServerData->client = newcl; + ent->pvEngineData->client = newcl; newcl->edict = ent; newcl->challenge = challenge; // save challenge for checksumming newcl->frames = (client_frame_t *)Z_Malloc( sizeof( client_frame_t ) * SV_UPDATE_BACKUP ); @@ -266,7 +267,7 @@ edict_t *SV_FakeConnect( const char *netname ) edictnum = (newcl - svs.clients) + 1; ent = EDICT_NUM( edictnum ); - ent->pvServerData->client = newcl; + ent->pvEngineData->client = newcl; newcl->edict = ent; newcl->challenge = -1; // fake challenge ent->v.flags |= FL_FAKECLIENT; // mark it as fakeclient @@ -340,11 +341,17 @@ void SV_DropClient( sv_client_t *drop ) svgame.dllFuncs.pfnSpectatorDisconnect( drop->edict ); else svgame.dllFuncs.pfnClientDisconnect( drop->edict ); - drop->edict->pvServerData->s.ed_type = ED_STATIC; // remove from server + // don't send to other clients + drop->edict->v.modelindex = 0; if( drop->edict->pvPrivateData ) { + if( svgame.dllFuncs2.pfnOnFreeEntPrivateData ) + { + // NOTE: new interface can be missing + svgame.dllFuncs2.pfnOnFreeEntPrivateData( drop->edict ); + } + // clear any dlls data but keep engine data - svgame.dllFuncs.pfnOnFreeEntPrivateData( drop->edict ); Mem_Free( drop->edict->pvPrivateData ); drop->edict->pvPrivateData = NULL; } @@ -654,9 +661,7 @@ void SV_PutClientInServer( edict_t *ent ) { sv_client_t *client; - client = ent->pvServerData->client; - - ent->pvServerData->s.ed_type = ED_CLIENT; // init edict type + client = ent->pvEngineData->client; if( !sv.loadgame ) { @@ -684,6 +689,9 @@ void SV_PutClientInServer( edict_t *ent ) } else { + // setup maxspeed and refresh physinfo + SV_SetClientMaxspeed( client, svgame.movevars.maxspeed ); + // NOTE: we needs to setup angles on restore here if( ent->v.fixangle == 1 ) { @@ -693,7 +701,7 @@ void SV_PutClientInServer( edict_t *ent ) SV_DirectSend( MSG_ONE, vec3_origin, client->edict ); ent->v.fixangle = 0; } - ent->pvServerData->s.ed_flags |= (ESF_NODELTA|ESF_NO_PREDICTION); + ent->v.effects |= EF_NOINTERP; } client->pViewEntity = NULL; // reset pViewEntity @@ -973,7 +981,7 @@ void SV_Baselines_f( sv_client_t *cl ) Mem_Set( &nullstate, 0, sizeof( nullstate )); // write a packet full of data - while( BF_GetNumBytesWritten( &cl->netchan.message ) < ( MAX_MSGLEN / 2 ) && start < svgame.globals->numEntities ) + while( BF_GetNumBytesWritten( &cl->netchan.message ) < ( MAX_MSGLEN / 2 ) && start < svgame.numEntities ) { base = &svs.baselines[start]; if( base->modelindex || base->effects ) @@ -984,7 +992,7 @@ void SV_Baselines_f( sv_client_t *cl ) start++; } - if( start == svgame.globals->numEntities ) com.snprintf( cmd, MAX_STRING, "precache %i\n", svs.spawncount ); + if( start == svgame.numEntities ) com.snprintf( cmd, MAX_STRING, "precache %i\n", svs.spawncount ); else com.snprintf( cmd, MAX_STRING, "cmd baselines %i %i\n", svs.spawncount, start ); // send next command @@ -1377,14 +1385,15 @@ connectionless packets. */ void SV_ConnectionlessPacket( netadr_t from, sizebuf_t *msg ) { - char *s; - char *c; + char *args; + char *c, buf[MAX_SYSPATH]; + int len = sizeof( buf ); BF_Clear( msg ); BF_ReadLong( msg );// skip the -1 marker - s = BF_ReadStringLine( msg ); - Cmd_TokenizeString( s ); + args = BF_ReadStringLine( msg ); + Cmd_TokenizeString( args ); c = Cmd_Argv( 0 ); MsgDev( D_NOTE, "SV_ConnectionlessPacket: %s : %s\n", NET_AdrToString( from ), c ); @@ -1396,7 +1405,12 @@ void SV_ConnectionlessPacket( netadr_t from, sizebuf_t *msg ) else if( !com.strcmp( c, "getchallenge" )) SV_GetChallenge( from ); else if( !com.strcmp( c, "connect" )) SV_DirectConnect( from ); else if( !com.strcmp( c, "rcon" )) SV_RemoteCommand( from, msg ); - else MsgDev( D_ERROR, "bad connectionless packet from %s:\n%s\n", NET_AdrToString( from ), s ); + else if( svgame.dllFuncs.pfnConnectionlessPacket( &net_from, args, buf, &len )) + { + // user out of band message (must be handled in CL_ConnectionlessPacket) + if( len > 0 ) Netchan_OutOfBand( NS_SERVER, net_from, len, buf ); + } + else MsgDev( D_ERROR, "bad connectionless packet from %s:\n%s\n", NET_AdrToString( from ), args ); } /* diff --git a/engine/server/sv_frame.c b/engine/server/sv_frame.c index 40ef1f3a..efe28ef0 100644 --- a/engine/server/sv_frame.c +++ b/engine/server/sv_frame.c @@ -8,6 +8,7 @@ #include "const.h" #include "protocol.h" #include "net_encode.h" +#include "entity_types.h" #define MAX_VISIBLE_PACKET 1024 typedef struct @@ -122,12 +123,13 @@ void SV_EmitPacketEntities( client_frame_t *from, client_frame_t *to, sizebuf_t BF_WriteWord( msg, 0 ); // end of packetentities } -static void SV_AddEntitiesToPacket( edict_t *pViewEnt, edict_t *pClient, client_frame_t *frame, sv_ents_t *ents, bool portal ) +static void SV_AddEntitiesToPacket( edict_t *pViewEnt, edict_t *pClient, client_frame_t *frame, sv_ents_t *ents ) { edict_t *ent; byte *pset; bool fullvis = false; - sv_client_t *cl; + sv_client_t *cl, *netclient; + entity_state_t *state; int e; // during an error shutdown message we may need to transmit @@ -135,7 +137,7 @@ static void SV_AddEntitiesToPacket( edict_t *pViewEnt, edict_t *pClient, client_ // specfically check for it if( !sv.state ) return; - if( pClient && !portal ) + if( pClient && !( sv.hostflags & SVF_PORTALPASS )) { // portals can't change hostflags sv.hostflags &= ~SVF_SKIPLOCALHOST; @@ -148,10 +150,10 @@ static void SV_AddEntitiesToPacket( edict_t *pViewEnt, edict_t *pClient, client_ sv.hostflags |= SVF_SKIPLOCALHOST; } - svgame.dllFuncs.pfnSetupVisibility( pViewEnt, pClient, &clientpvs, &clientphs, portal ); + svgame.dllFuncs.pfnSetupVisibility( pViewEnt, pClient, &clientpvs, &clientphs ); if( !clientpvs ) fullvis = true; - for( e = 1; e < svgame.globals->numEntities; e++ ) + for( e = 1; e < svgame.numEntities; e++ ) { ent = EDICT_NUM( e ); if( ent->free ) continue; @@ -164,23 +166,24 @@ static void SV_AddEntitiesToPacket( edict_t *pViewEnt, edict_t *pClient, client_ } // don't double add an entity through portals (already added) - if( ent->pvServerData->framenum == sv.net_framenum ) + if( ent->pvEngineData->framenum == sv.net_framenum ) continue; - if( ent->v.flags & FL_PHS_FILTER ) + if( ent->v.flags & FL_CHECK_PHS ) pset = clientphs; else pset = clientpvs; - // add entity to the net packet - if( svgame.dllFuncs.pfnAddToFullPack( &ent->pvServerData->s, pViewEnt, pClient, ent, sv.hostflags, pset )) - { - sv_client_t *netclient = SV_ClientFromEdict( ent, true ); + state = &ent->pvEngineData->s; + netclient = SV_ClientFromEdict( ent, true ); + // add entity to the net packet + if( svgame.dllFuncs.pfnAddToFullPack( state, e, ent, pClient, sv.hostflags, ( netclient != NULL ), pset )) + { // to prevent adds it twice through portals - ent->pvServerData->framenum = sv.net_framenum; + ent->pvEngineData->framenum = sv.net_framenum; if( netclient && netclient->modelindex ) // apply custom model if present - ent->pvServerData->s.modelindex = netclient->modelindex; + state->modelindex = netclient->modelindex; // if we are full, silently discard entities if( ents->num_entities < MAX_VISIBLE_PACKET ) @@ -194,17 +197,13 @@ static void SV_AddEntitiesToPacket( edict_t *pViewEnt, edict_t *pClient, client_ } if( fullvis ) continue; // portal ents will be added anyway, ignore recursion - // if its a portal entity, add everything visible from its camera position - if( !portal ) - { - switch( ent->pvServerData->s.ed_type ) - { - case ED_PORTAL: - case ED_SKYPORTAL: - SV_AddEntitiesToPacket( ent, pClient, frame, ents, true ); - break; - } + // if its a portal entity, add everything visible from its camera position + if( !( sv.hostflags & SVF_PORTALPASS ) && ent->v.effects & EF_MERGE_VISIBILITY ) + { + sv.hostflags |= SVF_PORTALPASS; + SV_AddEntitiesToPacket( ent, pClient, frame, ents ); + sv.hostflags &= ~SVF_PORTALPASS; } } } @@ -374,7 +373,7 @@ void SV_BuildClientFrame( sv_client_t *cl ) BF_WriteBitAngle( &sv.multicast, clent->v.angles[0], 16 ); BF_WriteBitAngle( &sv.multicast, clent->v.angles[1], 16 ); SV_DirectSend( MSG_ONE, vec3_origin, clent ); - clent->pvServerData->s.ed_flags |= ESF_NO_PREDICTION; + clent->v.effects |= EF_NOINTERP; break; case 2: BF_WriteByte( &sv.multicast, svc_addangle ); @@ -392,14 +391,15 @@ void SV_BuildClientFrame( sv_client_t *cl ) // clear everything in this snapshot frame_ents.num_entities = c_fullsend = 0; - if( !clent->pvServerData->client ) return; // not in game yet + if( !clent->pvEngineData->client ) return; // not in game yet // update clientdata_t svgame.dllFuncs.pfnUpdateClientData( clent, false, &frame->cd ); // add all the entities directly visible to the eye, which // may include portal entities that merge other viewpoints - SV_AddEntitiesToPacket( viewent, clent, frame, &frame_ents, false ); + sv.hostflags &= ~SVF_PORTALPASS; + SV_AddEntitiesToPacket( viewent, clent, frame, &frame_ents ); // if there were portals visible, there may be out of order entities // in the list which will need to be resorted for the delta compression @@ -417,7 +417,7 @@ void SV_BuildClientFrame( sv_client_t *cl ) // add it to the circular client_entities array state = &svs.client_entities[svs.next_client_entities % svs.num_client_entities]; - *state = ent->pvServerData->s; + *state = ent->pvEngineData->s; svs.next_client_entities++; // this should never hit, map should always be restarted first in SV_Frame diff --git a/engine/server/sv_game.c b/engine/server/sv_game.c index b55fda5a..23a0e457 100644 --- a/engine/server/sv_game.c +++ b/engine/server/sv_game.c @@ -6,14 +6,18 @@ #include "common.h" #include "server.h" #include "protocol.h" -#include "net_sound.h" #include "net_encode.h" #include "byteorder.h" #include "matrix_lib.h" #include "event_flags.h" +#include "entity_types.h" #include "pm_defs.h" #include "const.h" +// exports +typedef void (*LINK_ENTITY_FUNC)( entvars_t *pev ); +typedef void (*GIVEFNPTRSTODLL)( enginefuncs_t* engfuncs, globalvars_t *pGlobals ); + /* ============= EntvarsDescription @@ -54,6 +58,11 @@ TYPEDESCRIPTION *SV_GetEntvarsDescirption( int number ) return &gEntvarsDescription[number]; } +void SV_SysError( const char *error_string ) +{ + if( svgame.hInstance ) svgame.dllFuncs.pfnSys_Error( error_string ); +} + void SV_SetMinMaxSize( edict_t *e, const float *min, const float *max ) { int i; @@ -393,10 +402,10 @@ void SV_InitEdict( edict_t *pEdict ) { ASSERT( pEdict ); ASSERT( pEdict->pvPrivateData == NULL ); - ASSERT( pEdict->pvServerData == NULL ); + ASSERT( pEdict->pvEngineData == NULL ); pEdict->v.pContainingEntity = pEdict; // make cross-links for consistency - pEdict->pvServerData = (sv_priv_t *)Mem_Alloc( svgame.mempool, sizeof( sv_priv_t )); + pEdict->pvEngineData = (sv_priv_t *)Mem_Alloc( svgame.mempool, sizeof( sv_priv_t )); pEdict->pvPrivateData = NULL; // will be alloced later by pfnAllocPrivateData pEdict->serialnumber = NUM_FOR_EDICT( pEdict ); pEdict->free = false; @@ -410,10 +419,14 @@ void SV_FreeEdict( edict_t *pEdict ) // unlink from world SV_UnlinkEdict( pEdict ); - if( pEdict->pvServerData ) Mem_Free( pEdict->pvServerData ); + if( pEdict->pvEngineData ) Mem_Free( pEdict->pvEngineData ); if( pEdict->pvPrivateData ) { - svgame.dllFuncs.pfnOnFreeEntPrivateData( pEdict ); + if( svgame.dllFuncs2.pfnOnFreeEntPrivateData ) + { + // NOTE: new interface can be missing + svgame.dllFuncs2.pfnOnFreeEntPrivateData( pEdict ); + } Mem_Free( pEdict->pvPrivateData ); } Mem_Set( pEdict, 0, sizeof( *pEdict )); @@ -429,7 +442,7 @@ edict_t *SV_AllocEdict( void ) edict_t *pEdict; int i; - for( i = svgame.globals->maxClients + 1; i < svgame.globals->numEntities; i++ ) + for( i = svgame.globals->maxClients + 1; i < svgame.numEntities; i++ ) { pEdict = EDICT_NUM( i ); // the first couple seconds of server time can involve a lot of @@ -444,7 +457,7 @@ edict_t *SV_AllocEdict( void ) if( i >= svgame.globals->maxEntities ) Host_Error( "ED_AllocEdict: no free edicts\n" ); - svgame.globals->numEntities++; + svgame.numEntities++; pEdict = EDICT_NUM( i ); SV_InitEdict( pEdict ); @@ -479,7 +492,7 @@ edict_t* SV_AllocPrivateData( edict_t *ent, string_t className ) if( !SpawnEdict ) { // attempt to create custom entity - if( svgame.dllFuncs.pfnCreate( ent, pszClassName ) == -1 ) + if( svgame.dllFuncs2.pfnCreate && svgame.dllFuncs2.pfnCreate( ent, pszClassName ) == -1 ) { ent->v.flags |= FL_KILLME; MsgDev( D_ERROR, "No spawn function for %s\n", STRING( className )); @@ -488,10 +501,7 @@ edict_t* SV_AllocPrivateData( edict_t *ent, string_t className ) } else SpawnEdict( &ent->v ); - ASSERT( ent->pvServerData ) - - // also register classname to send for client - ent->pvServerData->s.classname = SV_ClassIndex( pszClassName ); + ASSERT( ent->pvEngineData ) return ent; } @@ -501,7 +511,7 @@ void SV_FreeEdicts( void ) int i = 0; edict_t *ent; - for( i = 0; i < svgame.globals->numEntities; i++ ) + for( i = 0; i < svgame.numEntities; i++ ) { ent = EDICT_NUM( i ); if( ent->free ) continue; @@ -527,7 +537,7 @@ bool SV_IsValidEdict( const edict_t *e ) { if( !e ) return false; if( e->free ) return false; - if( !e->pvServerData ) return false; + if( !e->pvEngineData ) return false; // edict without pvPrivateData is valid edict // server.dll know how allocate it return true; @@ -592,78 +602,64 @@ void SV_BaselineForEntity( edict_t *pEdict ) { sv_priv_t *sv_ent; - sv_ent = pEdict->pvServerData; + sv_ent = pEdict->pvEngineData; if( !sv_ent ) return; // update baseline for new entity - if( !sv_ent->s.number ) + if( !sv_ent->send_baseline ) { + int usehull, player; int modelindex; + entity_state_t baseline; + float *mins, *maxs; sv_client_t *cl; - sv_ent->s.classname = SV_ClassIndex( STRING( pEdict->v.classname )); - sv_ent->s.number = pEdict->serialnumber; - if( pEdict->v.flags & FL_CLIENT && ( cl = SV_ClientFromEdict( pEdict, false ))) + { + usehull = ( pEdict->v.flags & FL_DUCKING ) ? true : false; modelindex = cl->modelindex ? cl->modelindex : pEdict->v.modelindex; - else modelindex = pEdict->v.modelindex; + mins = svgame.player_mins[usehull]; + maxs = svgame.player_maxs[usehull]; + player = true; + } + else + { + modelindex = pEdict->v.modelindex; + mins = pEdict->v.mins; + maxs = pEdict->v.maxs; + player = false; + } - if( pEdict->v.modelindex || pEdict->v.effects ) + if( modelindex && !( pEdict->v.effects & EF_NODRAW )) { // take current state as baseline - svgame.dllFuncs.pfnCreateBaseline( &sv_ent->s, pEdict, modelindex ); - svs.baselines[pEdict->serialnumber] = sv_ent->s; + Mem_Set( &baseline, 0, sizeof( baseline )); + baseline.number = pEdict->serialnumber; + svgame.dllFuncs.pfnCreateBaseline( player, baseline.number, &baseline, pEdict, modelindex, mins, maxs ); + + // set entity type + if( pEdict->v.flags & FL_CLIENT ) + baseline.entityType = ET_PLAYER; + else if( pEdict->v.flags & FL_CUSTOMENTITY ) + baseline.entityType = ET_BEAM; + else baseline.entityType = ET_NORMAL; + + svs.baselines[pEdict->serialnumber] = baseline; + sv_ent->send_baseline = true; } - if( sv.state == ss_active && ( sv_ent->s.modelindex || sv_ent->s.effects )) + if( sv.state == ss_active && ( modelindex && !( pEdict->v.effects & EF_NODRAW ))) { entity_state_t nullstate; Mem_Set( &nullstate, 0, sizeof( nullstate )); BF_WriteByte( &sv.multicast, svc_spawnbaseline ); - MSG_WriteDeltaEntity( &nullstate, &sv_ent->s, &sv.multicast, true, sv.time ); + MSG_WriteDeltaEntity( &nullstate, &baseline, &sv.multicast, true, sv.time ); SV_DirectSend( MSG_ALL, vec3_origin, NULL ); } } } - -/* -================= -SV_ClassifyEdict - -sorting edict by type -================= -*/ -void SV_ClassifyEdict( edict_t *ent, int m_iNewClass ) -{ - sv_priv_t *sv_ent; - - sv_ent = ent->pvServerData; - if( !sv_ent ) return; - - // take baseline - SV_BaselineForEntity( ent ); - - if( m_iNewClass != ED_SPAWNED ) - { - sv_ent->s.ed_type = m_iNewClass; - return; - } - - if( sv_ent->s.ed_type != ED_SPAWNED ) - return; - - // auto-classify - sv_ent->s.ed_type = svgame.dllFuncs.pfnClassifyEdict( ent ); - - if( sv_ent->s.ed_type != ED_SPAWNED ) - { - MsgDev( D_NOTE, "AutoClass: %s: <%s>\n", STRING( ent->v.classname ), ed_name[sv_ent->s.ed_type] ); - } - // else leave unclassified, wait for next SV_LinkEdict... -} - void SV_SetClientMaxspeed( sv_client_t *cl, float fNewMaxspeed ) { // fakeclients must be changed speed too @@ -680,28 +676,6 @@ void SV_SetClientMaxspeed( sv_client_t *cl, float fNewMaxspeed ) =============================================================================== */ -/* -========= -pfnMemAlloc - -========= -*/ -static void *pfnMemAlloc( size_t cb, const char *filename, const int fileline ) -{ - return com.malloc( svgame.private, cb, filename, fileline ); -} - -/* -========= -pfnMemFree - -========= -*/ -static void pfnMemFree( void *mem, const char *filename, const int fileline ) -{ - com.free( mem, filename, fileline ); -} - /* ========= pfnPrecacheModel @@ -964,7 +938,7 @@ edict_t* pfnFindEntityByString( edict_t *pStartEdict, const char *pszField, cons return svgame.edicts; } - for( e++; e < svgame.globals->numEntities; e++ ) + for( e++; e < svgame.numEntities; e++ ) { ed = EDICT_NUM( e ); @@ -1021,7 +995,7 @@ edict_t* pfnFindEntityInSphere( edict_t *pStartEdict, const float *org, float fl if( SV_IsValidEdict( pStartEdict )) e = NUM_FOR_EDICT( pStartEdict ); - for( e++; e < svgame.globals->numEntities; e++ ) + for( e++; e < svgame.numEntities; e++ ) { ent = EDICT_NUM( e ); if( !SV_IsValidEdict( ent )) continue; @@ -1127,7 +1101,7 @@ edict_t *pfnEntitiesInPVS( edict_t *pplayer ) if( !SV_IsValidEdict( pplayer )) return NULL; - for( chain = NULL, i = svgame.globals->maxClients + 1; i < svgame.globals->numEntities; i++ ) + for( chain = NULL, i = svgame.globals->maxClients + 1; i < svgame.numEntities; i++ ) { pEdict = EDICT_NUM( i ); @@ -1161,7 +1135,7 @@ edict_t *pfnEntitiesInPHS( edict_t *pplayer ) if( !SV_IsValidEdict( pplayer )) return NULL; - for( chain = NULL, i = svgame.globals->maxClients + 1; i < svgame.globals->numEntities; i++ ) + for( chain = NULL, i = svgame.globals->maxClients + 1; i < svgame.numEntities; i++ ) { pEdict = EDICT_NUM( i ); @@ -1253,7 +1227,10 @@ static void pfnMakeStatic( edict_t *ent ) MsgDev( D_WARN, "SV_MakeStatic: invalid entity %s\n", SV_ClassName( ent )); return; } - ent->pvServerData->s.ed_type = ED_STATIC; + + // FIXME: write svc_spawnstatic to the client + ent->v.flags |= FL_KILLME; + } /* @@ -1391,7 +1368,8 @@ void SV_StartSound( edict_t *ent, int chan, const char *sample, float vol, float // can't track this entity on the client. // write static sound - if( !ent->pvServerData->linked ) flags |= SND_FIXED_ORIGIN; + if( !ent->pvEngineData->num_leafs ) + flags |= SND_FIXED_ORIGIN; // ultimate method for detect bsp models with invalid solidity (e.g. func_pushable) if( CM_GetModelType( ent->v.modelindex ) == mod_brush ) @@ -1434,7 +1412,7 @@ void SV_StartSound( edict_t *ent, int chan, const char *sample, float vol, float sound_idx = SV_SoundIndex( sample ); } - if( !ent->pvServerData->linked ) + if( !ent->pvEngineData->num_leafs ) entityIndex = 0; else if( SV_IsValidEdict( ent->v.aiment )) entityIndex = ent->v.aiment->serialnumber; @@ -1642,17 +1620,23 @@ pfnTraceModel ============= */ -static void pfnTraceModel( const float *v1, const float *v2, edict_t *pent, TraceResult *ptr ) +static void pfnTraceModel( const float *v1, const float *v2, int hullNumber, edict_t *pent, TraceResult *ptr ) { + float *mins, *maxs; + if( !SV_IsValidEdict( pent )) { MsgDev( D_WARN, "TraceModel: invalid entity %s\n", SV_ClassName( pent )); return; } + hullNumber = bound( 0, hullNumber, 3 ); + mins = svgame.player_mins[hullNumber]; + maxs = svgame.player_maxs[hullNumber]; + if( VectorIsNAN( v1 ) || VectorIsNAN( v2 )) Host_Error( "TraceModel: NAN errors detected '%f %f %f', '%f %f %f'\n", v1[0], v1[1], v1[2], v2[0], v2[1], v2[2] ); - if( ptr ) *ptr = CM_ClipMove( pent, v1, pent->v.mins, pent->v.maxs, v2, 0 ); + if( ptr ) *ptr = CM_ClipMove( pent, v1, mins, maxs, v2, 0 ); } /* @@ -1678,20 +1662,32 @@ static const char *pfnTraceTexture( edict_t *pTextureEntity, const float *v1, co /* ============= -pfnTestEntityPosition +pfnAreaEdicts returns true if the entity is in solid currently ============= */ -static int pfnTestEntityPosition( edict_t *pTestEdict ) +static int pfnAreaEdicts( const vec3_t mins, const vec3_t maxs, edict_t **list, int maxcount, int areatype ) { - if( !SV_IsValidEdict( pTestEdict )) + if( !mins || !maxs ) { - MsgDev( D_ERROR, "SV_TestEntity: invalid entity %s\n", SV_ClassName( pTestEdict )); - return false; - } + MsgDev( D_ERROR, "SV_AreaEdicts: invalid area bounds\n" ); + return 0; + } - return SV_TestEntityPosition( pTestEdict ); + if( VectorIsNAN( mins ) || VectorIsNAN( maxs )) + Host_Error( "SV_AreaEdicts: NAN errors detected '%f %f %f', '%f %f %f'\n", mins[0], mins[1], mins[2], maxs[0], maxs[1], maxs[2] ); + + if( !list || maxcount <= 0 ) + return 0; + + if( areatype < AREA_SOLID || areatype > AREA_CUSTOM ) + { + MsgDev( D_WARN, "SV_AreaEdicts: bad area type %i. Defaulting to AREA_SOLID\n", areatype ); + areatype = AREA_SOLID; + } + + return SV_AreaEdicts( mins, maxs, list, maxcount, areatype ); } /* @@ -1740,7 +1736,7 @@ void pfnGetAimVector( edict_t* ent, float speed, float *rgflReturn ) bestent = NULL; check = EDICT_NUM( 1 ); // start at first client - for( i = 1; i < svgame.globals->numEntities; i++, check++ ) + for( i = 1; i < svgame.numEntities; i++, check++ ) { if( check->v.takedamage != DAMAGE_AIM ) continue; if( check == ent ) continue; @@ -2139,12 +2135,46 @@ pfnWriteEntity */ void pfnWriteEntity( int iValue ) { - if( iValue < 0 || iValue >= svgame.globals->numEntities ) + if( iValue < 0 || iValue >= svgame.numEntities ) Host_Error( "BF_WriteEntity: invalid entnumber %i\n", iValue ); BF_WriteShort( &sv.multicast, iValue ); svgame.msg_realsize += 2; } +/* +============= +pfnAlertMessage + +============= +*/ +static void pfnAlertMessage( ALERT_TYPE level, char *szFmt, ... ) +{ + char buffer[2048]; // must support > 1k messages + va_list args; + + va_start( args, szFmt ); + com.vsnprintf( buffer, 2048, szFmt, args ); + va_end( args ); + + if( host.developer < level ) + return; + + switch( level ) + { + case at_notice: + case at_console: + case at_aiconsole: + com.print( buffer ); + break; + case at_warning: + com.print( va("^3Warning:^7 %s", buffer )); + break; + case at_error: + com.print( va("^1Error:^7 %s", buffer )); + break; + } +} + /* ============= pfnPvAllocEntPrivateData @@ -2155,7 +2185,7 @@ void *pfnPvAllocEntPrivateData( edict_t *pEdict, long cb ) { ASSERT( pEdict ); ASSERT( pEdict->free == false ); - ASSERT( pEdict->pvServerData ); + ASSERT( pEdict->pvEngineData ); // to avoid multiple alloc pEdict->pvPrivateData = (void *)Mem_Realloc( svgame.private, pEdict->pvPrivateData, cb ); @@ -2198,7 +2228,7 @@ SV_AllocString */ string_t SV_AllocString( const char *szValue ) { - if( sys_sharedstrings->integer ) + if( svgame.globals->pStringBase ) { const char *newString; newString = com.stralloc( svgame.stringspool, szValue, __FILE__, __LINE__ ); @@ -2215,7 +2245,7 @@ SV_GetString */ const char *SV_GetString( string_t iString ) { - if( sys_sharedstrings->integer ) + if( svgame.globals->pStringBase ) return (svgame.globals->pStringBase + iString); return StringTable_GetString( svgame.hStringTable, iString ); } @@ -2276,7 +2306,7 @@ pfnPEntityOfEntIndex */ edict_t* pfnPEntityOfEntIndex( int iEntIndex ) { - if( iEntIndex < 0 || iEntIndex >= svgame.globals->numEntities ) + if( iEntIndex < 0 || iEntIndex >= svgame.numEntities ) return NULL; // out of range return EDICT_NUM( iEntIndex ); } @@ -2296,12 +2326,12 @@ edict_t* pfnFindEntityByVars( entvars_t *pvars ) // don't pass invalid arguments if( !pvars ) return NULL; - for( i = 0; i < svgame.globals->numEntities; i++ ) + for( i = 0; i < svgame.numEntities; i++ ) { e = EDICT_NUM( i ); if( !memcmp( &e->v, pvars, sizeof( entvars_t ))) { - Msg( "FindEntityByVars: %s\n", SV_ClassName( e )); + MsgDev( D_INFO, "FindEntityByVars: %s\n", SV_ClassName( e )); return e; // found it } } @@ -2516,7 +2546,7 @@ pfnCRC32_Init ============= */ -void pfnCRC32_Init( CRC32_t *pulCRC ) +void pfnCRC32_Init( dword *pulCRC ) { CRC32_Init( pulCRC ); } @@ -2527,7 +2557,7 @@ pfnCRC32_ProcessBuffer ============= */ -void pfnCRC32_ProcessBuffer( CRC32_t *pulCRC, void *p, int len ) +void pfnCRC32_ProcessBuffer( dword *pulCRC, void *p, int len ) { CRC32_ProcessBuffer( pulCRC, p, len ); } @@ -2538,7 +2568,7 @@ pfnCRC32_ProcessByte ============= */ -void pfnCRC32_ProcessByte( CRC32_t *pulCRC, byte ch ) +void pfnCRC32_ProcessByte( dword *pulCRC, byte ch ) { CRC32_ProcessByte( pulCRC, ch ); } @@ -2549,7 +2579,7 @@ pfnCRC32_Final ============= */ -CRC32_t pfnCRC32_Final( CRC32_t pulCRC ) +dword pfnCRC32_Final( dword pulCRC ) { CRC32_Final( &pulCRC ); @@ -2598,7 +2628,7 @@ void pfnSetView( const edict_t *pClient, const edict_t *pViewent ) return; } - client = pClient->pvServerData->client; + client = pClient->pvEngineData->client; if( !client ) { MsgDev( D_ERROR, "PF_SetView: not a client!\n" ); @@ -2685,6 +2715,18 @@ int pfnIsDedicatedServer( void ) return (host.type == HOST_DEDICATED); } +/* +============= +pfnGetPlayerWONId + +============= +*/ +uint pfnGetPlayerWONId( edict_t *e ) +{ + // FIXME: implement + return -1; +} + /* ============= pfnIsMapValid @@ -2713,23 +2755,6 @@ int pfnIsMapValid( char *filename ) return false; } -/* -============= -pfnClassifyEdict - -classify edict for render and network usage -============= -*/ -void pfnClassifyEdict( edict_t *pEdict, int class ) -{ - if( !SV_IsValidEdict( pEdict )) - { - MsgDev( D_WARN, "SV_ClassifyEdict: invalid entity %s\n", SV_ClassName( pEdict )); - return; - } - SV_ClassifyEdict( pEdict, class ); -} - /* ============= pfnFadeClientVolume @@ -2827,6 +2852,18 @@ void pfnRunPlayerMove( edict_t *pClient, const float *v_angle, float fmove, floa cl->lastcmd.buttons = 0; // avoid multiple fires on lag } +/* +============= +pfnNumberOfEntities + +returns actual entity count +============= +*/ +int pfnNumberOfEntities( void ) +{ + return svgame.numEntities; +} + /* ============= pfnInfo_RemoveKey @@ -3164,10 +3201,10 @@ pfnSetFatPVS set fat PVS ============= */ -byte *pfnSetFatPVS( const float *org, int portal ) +byte *pfnSetFatPVS( const float *org ) { if( !org ) return NULL; - return CM_FatPVS( org, portal ); + return CM_FatPVS( org, ( sv.hostflags & SVF_PORTALPASS ) ? true : false ); } /* @@ -3177,10 +3214,10 @@ pfnSetFatPHS set fat PHS ============= */ -byte *pfnSetFatPAS( const float *org, int portal ) +byte *pfnSetFatPAS( const float *org ) { if( !org ) return NULL; - return CM_FatPHS( org, portal ); + return CM_FatPHS( org, ( sv.hostflags & SVF_PORTALPASS ) ? true : false ); } /* @@ -3201,31 +3238,28 @@ int pfnCheckVisibility( const edict_t *entity, byte *pset ) if( !pset ) return 1; // vis not set - fullvis enabled - // never send entities that aren't linked in - if( !entity->pvServerData->linked ) return 0; - // check individual leafs - if( !entity->pvServerData->num_leafs ) + if( !entity->pvEngineData->num_leafs ) return 0; // not a linked in - if( entity->pvServerData->num_leafs == -1 ) + if( entity->pvEngineData->num_leafs == -1 ) { // too many leafs for individual check, go by headnode - if( !CM_HeadnodeVisible( entity->pvServerData->headnode, pset )) + if( !CM_HeadnodeVisible( entity->pvEngineData->headnode, pset )) return 0; } else { // check individual leafs - for( i = 0; i < entity->pvServerData->num_leafs; i++ ) + for( i = 0; i < entity->pvEngineData->num_leafs; i++ ) { - l = entity->pvServerData->leafnums[i]; + l = entity->pvEngineData->leafnums[i]; if( pset[l>>3] & (1<<( l & 7 ))) break; } - if( i == entity->pvServerData->num_leafs ) + if( i == entity->pvEngineData->num_leafs ) return 0; // not visible } @@ -3286,6 +3320,18 @@ void pfnSetGroupMask( int mask, int op ) svs.groupop = op; } +/* +============= +pfnCreateInstancedBaseline + +============= +*/ +int pfnCreateInstancedBaseline( int classname, struct entity_state_s *baseline ) +{ + // FIXME: implement + return 0; +} + /* ============= pfnEndSection @@ -3330,25 +3376,11 @@ int pfnGetPlayerUserId( edict_t *e ) /* ============= -pfnGetPlayerAuthId - -These function must returns cd-key hashed value -but Xash3D currently doesn't have any security checks -return nullstring for now -============= -*/ -const char *pfnGetPlayerAuthId( edict_t *e ) -{ - return ""; -} - -/* -============= -pfnPlayMusic +pfnSoundTrack ============= */ -void pfnPlayMusic( const char *trackname, int flags ) +void pfnSoundTrack( const char *trackname, int flags ) { SV_ConfigString( CS_BACKGROUND_TRACK, trackname ); } @@ -3395,6 +3427,28 @@ void pfnGetPlayerStats( const edict_t *pClient, int *ping, int *packet_loss ) if( packet_loss ) *packet_loss = cl->packet_loss; } +/* +============= +pfnCvar_DirectSet + +============= +*/ +void pfnCvar_DirectSet( cvar_t *var, char *value ) +{ + Cvar_DirectSet( var, value ); +} + +/* +============= +pfnForceUnmodified + +============= +*/ +void pfnForceUnmodified( FORCE_TYPE type, float *mins, float *maxs, const char *filename ) +{ + // FIXME: implement +} + /* ============= pfnAddServerCommand @@ -3405,6 +3459,20 @@ void pfnAddServerCommand( const char *cmd_name, void (*function)(void), const ch { Cmd_AddCommand( cmd_name, function, cmd_desc ); } + +/* +============= +pfnGetPlayerAuthId + +These function must returns cd-key hashed value +but Xash3D currently doesn't have any security checks +return nullstring for now +============= +*/ +const char *pfnGetPlayerAuthId( edict_t *e ) +{ + return ""; +} // engine callbacks static enginefuncs_t gEngfuncs = @@ -3446,7 +3514,7 @@ static enginefuncs_t gEngfuncs = pfnTraceHull, pfnTraceModel, pfnTraceTexture, - pfnTestEntityPosition, + pfnAreaEdicts, pfnGetAimVector, pfnServerCommand, pfnServerExecute, @@ -3471,7 +3539,7 @@ static enginefuncs_t gEngfuncs = pfnCVarSetValue, pfnCVarSetString, pfnAlertMessage, - pfnGetProcAddress, + pfnDropClient, pfnPvAllocEntPrivateData, pfnPvEntPrivateData, pfnFreeEntPrivateData, @@ -3509,12 +3577,12 @@ static enginefuncs_t gEngfuncs = pfnEndSection, pfnCompareFileTime, pfnGetGameDir, - pfnClassifyEdict, + Host_Error, pfnFadeClientVolume, pfnSetClientMaxspeed, pfnCreateFakeClient, pfnRunPlayerMove, - pfnFileExists, + pfnNumberOfEntities, pfnGetInfoKeyBuffer, pfnInfoKeyValue, pfnSetKeyValue, @@ -3523,10 +3591,10 @@ static enginefuncs_t gEngfuncs = pfnStaticDecal, pfnPrecacheGeneric, pfnGetPlayerUserId, - pfnPlayMusic, + pfnSoundTrack, pfnIsDedicatedServer, - pfnMemAlloc, - pfnMemFree, + pfnCVarGetPointer, + pfnGetPlayerWONId, pfnInfo_RemoveKey, pfnGetPhysicsKeyValue, pfnSetPhysicsKeyValue, @@ -3545,13 +3613,11 @@ static enginefuncs_t gEngfuncs = Delta_SetFieldByIndex, Delta_UnsetFieldByIndex, pfnSetGroupMask, - pfnDropClient, - Host_Error, - pfnParseToken, + pfnCreateInstancedBaseline, + pfnCvar_DirectSet, + pfnForceUnmodified, pfnGetPlayerStats, pfnAddServerCommand, - pfnLoadLibrary, - pfnFreeLibrary, pfnGetPlayerAuthId, }; @@ -3768,7 +3834,7 @@ void SV_SpawnEntities( const char *mapname, script_t *entities ) // spawn the rest of the entities on the map SV_LoadFromFile( entities ); - MsgDev( D_NOTE, "Total %i entities spawned\n", svgame.globals->numEntities ); + MsgDev( D_NOTE, "Total %i entities spawned\n", svgame.numEntities ); } void SV_UnloadProgs( void ) @@ -3777,7 +3843,7 @@ void SV_UnloadProgs( void ) Delta_Shutdown (); - if( sys_sharedstrings->integer ) + if( svgame.globals->pStringBase ) Mem_FreePool( &svgame.stringspool ); else StringTable_Delete( svgame.hStringTable ); @@ -3790,6 +3856,7 @@ void SV_UnloadProgs( void ) bool SV_LoadProgs( const char *name ) { static APIFUNCTION GetEntityAPI; + static APIFUNCTION2 GetEntityAPI2; static GIVEFNPTRSTODLL GiveFnptrsToDll; static globalvars_t gpGlobals; static playermove_t gpMove; @@ -3806,6 +3873,9 @@ bool SV_LoadProgs( const char *name ) svgame.hInstance = FS_LoadLibrary( name, true ); if( !svgame.hInstance ) return false; + // make sure what new dll functions is cleared + Mem_Set( &svgame.dllFuncs2, 0, sizeof( svgame.dllFuncs2 )); + GetEntityAPI = (APIFUNCTION)FS_GetProcAddress( svgame.hInstance, "GetEntityAPI" ); if( !GetEntityAPI ) @@ -3826,7 +3896,7 @@ bool SV_LoadProgs( const char *name ) return false; } - if( !GetEntityAPI( &svgame.dllFuncs, SV_INTERFACE_VERSION )) + if( !GetEntityAPI( &svgame.dllFuncs, INTERFACE_VERSION )) { FS_FreeLibrary( svgame.hInstance ); MsgDev( D_ERROR, "SV_LoadProgs: couldn't get entity API\n" ); @@ -3836,23 +3906,12 @@ bool SV_LoadProgs( const char *name ) GiveFnptrsToDll( &gEngfuncs, svgame.globals ); - svgame.globals->pStringBase = ""; // setup string base - - if( sys_sharedstrings->integer ) - { - // just use Half-Life system - base pointer and malloc - svgame.stringspool = Mem_AllocPool( "Server Strings" ); - } - else - { - // 65535 unique strings should be enough ... - svgame.hStringTable = StringTable_Create( "Server", 0x10000 ); - } + svgame.globals->pStringBase = ""; // setup string base svgame.globals->maxEntities = GI->max_edicts; svgame.globals->maxClients = sv_maxclients->integer; svgame.edicts = Mem_Alloc( svgame.mempool, sizeof( edict_t ) * svgame.globals->maxEntities ); - svgame.globals->numEntities = svgame.globals->maxClients + 1; // clients + world + svgame.numEntities = svgame.globals->maxClients + 1; // clients + world for( i = 0, e = svgame.edicts; i < svgame.globals->maxEntities; i++, e++ ) e->free = true; // mark all edicts as freed @@ -3863,6 +3922,19 @@ bool SV_LoadProgs( const char *name ) svgame.dllFuncs.pfnGameInit(); pfnRegUserMsg( "svc_bad", 0 ); // register svc_bad message + // NOTE: game can resest pStringBase in case we want use StringTable system + if( svgame.globals->pStringBase ) + { + // just use Half-Life system - base pointer and malloc + svgame.stringspool = Mem_AllocPool( "Server Strings" ); + } + else + { + // 65535 unique strings should be enough ... + MsgDev( D_INFO, "Create stringtable ^2Server^7\n" ); + svgame.hStringTable = StringTable_Create( "Server", 0x10000 ); + } + // initialize pm_shared SV_InitClientMove(); diff --git a/engine/server/sv_init.c b/engine/server/sv_init.c index 3a763ac7..13583acb 100644 --- a/engine/server/sv_init.c +++ b/engine/server/sv_init.c @@ -75,11 +75,6 @@ int SV_GenericIndex( const char *name ) return SV_FindIndex( name, CS_GENERICS, MAX_GENERICS, true ); } -int SV_ClassIndex( const char *name ) -{ - return SV_FindIndex( name, CS_CLASSNAMES, MAX_CLASSNAMES, true ); -} - /* ================ SV_CreateBaseline @@ -94,11 +89,11 @@ void SV_CreateBaseline( void ) edict_t *pEdict; int entnum; - for( entnum = 0; entnum < svgame.globals->numEntities; entnum++ ) + for( entnum = 0; entnum < svgame.numEntities; entnum++ ) { pEdict = EDICT_NUM( entnum ); if( !SV_IsValidEdict( pEdict )) continue; - SV_ClassifyEdict( pEdict, ED_SPAWNED ); + SV_BaselineForEntity( pEdict ); } } @@ -142,7 +137,7 @@ void SV_ActivateServer( void ) } // Activate the DLL server code - svgame.dllFuncs.pfnServerActivate( svgame.edicts, svgame.globals->numEntities, svgame.globals->maxClients ); + svgame.dllFuncs.pfnServerActivate( svgame.edicts, svgame.numEntities, svgame.globals->maxClients ); // create a baseline for more efficient communications SV_CreateBaseline(); @@ -198,7 +193,7 @@ void SV_DeactivateServer( void ) if( sv.state == ss_dead ) return; sv.state = ss_dead; - if( sys_sharedstrings->integer ) + if( svgame.globals->pStringBase ) Mem_EmptyPool( svgame.stringspool ); else StringTable_Clear( svgame.hStringTable ); @@ -215,7 +210,7 @@ void SV_DeactivateServer( void ) svgame.globals->maxEntities = GI->max_edicts; svgame.globals->maxClients = sv_maxclients->integer; - svgame.globals->numEntities = svgame.globals->maxClients + 1; // clients + world + svgame.numEntities = svgame.globals->maxClients + 1; // clients + world svgame.globals->mapname = 0; } @@ -256,6 +251,9 @@ void SV_LevelInit( const char *pMapName, char const *pOldLevel, char const *pLan SV_SpawnEntities( pMapName, CM_GetEntityScript( )); } + // call before sending baselines into the client + svgame.dllFuncs.pfnCreateInstancedBaselines(); + SV_FreeOldEntities (); } @@ -355,6 +353,9 @@ bool SV_SpawnServer( const char *mapname, const char *startspot ) // clear physics interaction links SV_ClearWorld(); + // tell dlls about new level started + svgame.dllFuncs.pfnParmsNewLevel(); + return true; } @@ -461,13 +462,13 @@ void SV_InitGame( void ) // make crosslinks svs.clients[i].edict = ent; - ent->pvServerData->client = svs.clients + i; - ent->pvServerData->client->edict = ent; + ent->pvEngineData->client = svs.clients + i; + ent->pvEngineData->client->edict = ent; Mem_Set( &svs.clients[i].lastcmd, 0, sizeof( svs.clients[i].lastcmd )); Mem_Set( &svs.clients[i].physinfo, 0, sizeof( svs.clients[i].physinfo )); } - svgame.globals->numEntities = svgame.globals->maxClients + 1; // clients + world + svgame.numEntities = svgame.globals->maxClients + 1; // clients + world svs.initialized = true; } diff --git a/engine/server/sv_main.c b/engine/server/sv_main.c index 8e33ecd1..cf230da8 100644 --- a/engine/server/sv_main.c +++ b/engine/server/sv_main.c @@ -42,6 +42,7 @@ cvar_t *sv_stopspeed; cvar_t *hostname; cvar_t *sv_maxclients; cvar_t *sv_check_errors; +cvar_t *sv_footsteps; cvar_t *public_server; // should heartbeats be sent cvar_t *sv_reconnect_limit; // minimum seconds between connect messages cvar_t *serverinfo; @@ -202,6 +203,26 @@ void SV_UpdateMovevars( void ) oldserverflags = svgame.globals->serverflags; } + if( sv_maxspeed->modified ) + { + sv_client_t *cl; + int i; + + // maxspeed is modified, refresh maxspeed for each client + for( i = 0, cl = svs.clients; i < sv_maxclients->integer; i++, cl++ ) + { + if( !SV_IsValidEdict( cl->edict )) + continue; + + // can update even if client it's not active + SV_SetClientMaxspeed( cl, sv_maxspeed->value ); + } + + if( sv.state == ss_active ) + SV_BroadcastPrintf( PRINT_HIGH, "sv_maxspeed is changed to %g\n", sv_maxspeed->value ); + sv_maxspeed->modified = false; + } + if( sv_zmax->modified ) { SV_ConfigString( CS_ZFAR, sv_zmax->string ); @@ -249,9 +270,18 @@ void SV_UpdateMovevars( void ) svgame.movevars.bounce = sv_wallbounce->value; svgame.movevars.stepsize = sv_stepheight->value; svgame.movevars.maxvelocity = sv_maxvelocity->value; - svgame.movevars.footsteps = Cvar_VariableInteger( "mp_footsteps" ); + svgame.movevars.zmax = sv_zmax->value; + svgame.movevars.waveHeight = sv_wateramp->value; + com.strncpy( svgame.movevars.skyName, sv_skyname->string, sizeof( svgame.movevars.skyName )); + svgame.movevars.footsteps = sv_footsteps->integer; svgame.movevars.rollangle = sv_rollangle->value; svgame.movevars.rollspeed = sv_rollspeed->value; + svgame.movevars.skycolor_r = sv_skycolor_r->value; + svgame.movevars.skycolor_g = sv_skycolor_g->value; + svgame.movevars.skycolor_b = sv_skycolor_b->value; + svgame.movevars.skyvec_x = sv_skyvec_x->value; + svgame.movevars.skyvec_y = sv_skyvec_y->value; + svgame.movevars.skyvec_z = sv_skyvec_z->value; BF_Clear( &sv.multicast ); @@ -443,12 +473,11 @@ void SV_PrepWorldFrame( void ) edict_t *ent; int i; - for( i = 1; i < svgame.globals->numEntities; i++ ) + for( i = 1; i < svgame.numEntities; i++ ) { ent = EDICT_NUM( i ); if( ent->free ) continue; - ent->pvServerData->s.ed_flags = 0; ent->v.effects &= ~EF_MUZZLEFLASH; // clear NOINTERP flag automatically only for alive creatures @@ -637,15 +666,16 @@ void SV_Init( void ) Cvar_Get ("sv_language", "0", 0, "game language (currently unused)" ); // half-life shared variables - sv_zmax = Cvar_Get ("sv_zmax", "0", 0, "zfar server value" ); - sv_wateramp = Cvar_Get ("sv_wateramp", "0", 0, "global water wave height" ); - sv_skycolor_r = Cvar_Get ("sv_skycolor_r", "127", 0, "skycolor red (hl1 compatibility)" ); - sv_skycolor_g = Cvar_Get ("sv_skycolor_g", "127", 0, "skycolor green (hl1 compatibility)" ); - sv_skycolor_b = Cvar_Get ("sv_skycolor_b", "127", 0, "skycolor blue (hl1 compatibility)" ); - sv_skyvec_x = Cvar_Get ("sv_skyvec_x", "1", 0, "sky direction x (hl1 compatibility)" ); - sv_skyvec_y = Cvar_Get ("sv_skyvec_y", "0", 0, "sky direction y (hl1 compatibility)" ); - sv_skyvec_z = Cvar_Get ("sv_skyvec_z", "-1", 0, "sky direction z (hl1 compatibility)" ); - sv_skyname = Cvar_Get ("sv_skyname", "2desert", 0, "skybox name (can be dynamically changed in-game)" ); + sv_zmax = Cvar_Get ("sv_zmax", "0", CVAR_PHYSICINFO, "zfar server value" ); + sv_wateramp = Cvar_Get ("sv_wateramp", "0", CVAR_PHYSICINFO, "global water wave height" ); + sv_skycolor_r = Cvar_Get ("sv_skycolor_r", "127", CVAR_PHYSICINFO, "skycolor red (hl1 compatibility)" ); + sv_skycolor_g = Cvar_Get ("sv_skycolor_g", "127", CVAR_PHYSICINFO, "skycolor green (hl1 compatibility)" ); + sv_skycolor_b = Cvar_Get ("sv_skycolor_b", "127", CVAR_PHYSICINFO, "skycolor blue (hl1 compatibility)" ); + sv_skyvec_x = Cvar_Get ("sv_skyvec_x", "1", CVAR_PHYSICINFO, "sky direction x (hl1 compatibility)" ); + sv_skyvec_y = Cvar_Get ("sv_skyvec_y", "0", CVAR_PHYSICINFO, "sky direction y (hl1 compatibility)" ); + sv_skyvec_z = Cvar_Get ("sv_skyvec_z", "-1", CVAR_PHYSICINFO, "sky direction z (hl1 compatibility)" ); + sv_skyname = Cvar_Get ("sv_skyname", "2desert", CVAR_PHYSICINFO, "skybox name (can be dynamically changed in-game)" ); + sv_footsteps = Cvar_Get ("mp_footsteps", "0", CVAR_PHYSICINFO, "can hear footsteps from other players" ); rcon_password = Cvar_Get( "rcon_password", "", 0, "remote connect password" ); sv_fps = Cvar_Get( "sv_fps", "60", CVAR_SERVERINFO|CVAR_ARCHIVE, "network game server fps" ); diff --git a/engine/server/sv_move.c b/engine/server/sv_move.c index 6cd0cf5b..f55c0b1f 100644 --- a/engine/server/sv_move.c +++ b/engine/server/sv_move.c @@ -11,6 +11,9 @@ #include "const.h" #include "pm_defs.h" +#define MOVE_NORMAL 0 // normal move in the direction monster is facing +#define MOVE_STRAFE 1 // moves in direction specified, no matter which way monster is facing + /* ============= SV_CheckBottom diff --git a/engine/server/sv_phys.c b/engine/server/sv_phys.c index 8cbd30d1..f968c418 100644 --- a/engine/server/sv_phys.c +++ b/engine/server/sv_phys.c @@ -64,7 +64,7 @@ void SV_CheckAllEnts( void ) if( !sv_check_errors->integer ) return; // see if any solid entities are inside the final position - for( i = svgame.globals->maxClients + 1; i < svgame.globals->numEntities; i++ ) + for( i = svgame.globals->maxClients + 1; i < svgame.numEntities; i++ ) { e = EDICT_NUM( i ); @@ -188,8 +188,11 @@ bool SV_Impact( edict_t *e1, trace_t *trace ) vec3_t org; // custom user filter - if( !svgame.dllFuncs.pfnShouldCollide( e1, e2 )) - return false; + if( svgame.dllFuncs2.pfnShouldCollide ) + { + if( !svgame.dllFuncs2.pfnShouldCollide( e1, e2 )) + return false; + } SV_CopyTraceToGlobal( trace ); VectorCopy( e1->v.origin, org ); @@ -703,7 +706,6 @@ static bool SV_CanPushed( edict_t *ent ) case MOVETYPE_PUSH: case MOVETYPE_FOLLOW: case MOVETYPE_NOCLIP: - case MOVETYPE_COMPOUND: return false; } return true; @@ -762,7 +764,7 @@ static edict_t *SV_PushMove( edict_t *pusher, float movetime ) // see if any solid entities are inside the final position num_moved = 0; - for( e = 1; e < svgame.globals->numEntities; e++ ) + for( e = 1; e < svgame.numEntities; e++ ) { check = EDICT_NUM( e ); if( !SV_IsValidEdict( check )) continue; @@ -797,7 +799,7 @@ static edict_t *SV_PushMove( edict_t *pusher, float movetime ) check->v.flags &= ~FL_ONGROUND; VectorCopy( check->v.origin, entorg ); - VectorCopy( check->v.origin, check->pvServerData->moved_origin ); + VectorCopy( check->v.origin, check->pvEngineData->moved_origin ); moved_edict[num_moved] = check; num_moved++; @@ -825,7 +827,7 @@ static edict_t *SV_PushMove( edict_t *pusher, float movetime ) { edict_t *ed = moved_edict[i]; - VectorCopy( ed->pvServerData->moved_origin, ed->v.origin ); + VectorCopy( ed->pvEngineData->moved_origin, ed->v.origin ); SV_LinkEdict( ed, false ); } return check; @@ -878,7 +880,7 @@ static edict_t *SV_PushRotate( edict_t *pusher, float movetime ) // see if any solid entities are inside the final position num_moved = 0; - for( e = 1; e < svgame.globals->numEntities; e++ ) + for( e = 1; e < svgame.numEntities; e++ ) { check = EDICT_NUM( e ); if( !SV_IsValidEdict( check )) continue; @@ -905,8 +907,8 @@ static edict_t *SV_PushRotate( edict_t *pusher, float movetime ) } VectorCopy( check->v.origin, entorg ); - VectorCopy( check->v.origin, check->pvServerData->moved_origin ); - VectorCopy( check->v.angles, check->pvServerData->moved_angles ); + VectorCopy( check->v.origin, check->pvEngineData->moved_origin ); + VectorCopy( check->v.angles, check->pvEngineData->moved_angles ); moved_edict[num_moved] = check; num_moved++; @@ -953,8 +955,8 @@ static edict_t *SV_PushRotate( edict_t *pusher, float movetime ) ed->v.fixangle = 0; } - VectorCopy( ed->pvServerData->moved_origin, ed->v.origin ); - VectorCopy( ed->pvServerData->moved_angles, ed->v.angles ); + VectorCopy( ed->pvEngineData->moved_origin, ed->v.origin ); + VectorCopy( ed->pvEngineData->moved_angles, ed->v.angles ); SV_LinkEdict( ed, false ); } return check; @@ -1041,7 +1043,7 @@ void SV_PushComplex( edict_t *pusher, float movetime ) oldsolid = pusher->v.solid; // see if any solid entities are inside the final position - for( e = 1; e < svgame.globals->numEntities; e++ ) + for( e = 1; e < svgame.numEntities; e++ ) { check = EDICT_NUM( e ); if( !SV_IsValidEdict( check )) continue; @@ -1053,7 +1055,6 @@ void SV_PushComplex( edict_t *pusher, float movetime ) case MOVETYPE_PUSH: case MOVETYPE_FOLLOW: case MOVETYPE_NOCLIP: - case MOVETYPE_COMPOUND: continue; default: break; } @@ -1092,8 +1093,8 @@ void SV_PushComplex( edict_t *pusher, float movetime ) // continue; } - VectorCopy( check->v.origin, check->pvServerData->moved_origin ); - VectorCopy( check->v.angles, check->pvServerData->moved_angles ); + VectorCopy( check->v.origin, check->pvEngineData->moved_origin ); + VectorCopy( check->v.angles, check->pvEngineData->moved_angles ); moved_edict[num_moved++] = check; // try moving the contacted entity @@ -1158,8 +1159,8 @@ void SV_PushComplex( edict_t *pusher, float movetime ) ed->v.fixangle = 0; } - VectorCopy( ed->pvServerData->moved_origin, ed->v.origin ); - VectorCopy( ed->pvServerData->moved_angles, ed->v.angles ); + VectorCopy( ed->pvEngineData->moved_origin, ed->v.origin ); + VectorCopy( ed->pvEngineData->moved_angles, ed->v.angles ); SV_LinkEdict( ed, false ); } @@ -1609,60 +1610,6 @@ void SV_Physics_Step( edict_t *ent ) SV_CheckWaterTransition( ent ); } -/* -==================== -SV_Physics_Conveyor - -REAL simple - all we do is check for player riders and adjust their position. -Only gotcha here is we have to make sure we don't end up embedding player in -*another* object that's being moved by the conveyor. - -==================== -*/ -void SV_Physics_Conveyor( edict_t *ent ) -{ - edict_t *player; - int i; - trace_t tr; - vec3_t v, move; - vec3_t point, end; - - VectorScale( ent->v.movedir, ent->v.speed, v ); - VectorScale( v, sv_frametime(), move ); - - for( i = 0; i < svgame.globals->maxClients; i++ ) - { - player = EDICT_NUM( i + 1 ); - if( player->free ) continue; - if( !player->v.groundentity ) continue; - if( player->v.groundentity != ent ) - continue; - - // Look below player; make sure he's on a conveyor - VectorCopy( player->v.origin, point ); - point[2] += 1; - VectorCopy( point, end ); - end[2] -= 256; - - tr = SV_Move( point, player->v.mins, player->v.maxs, end, MOVE_NORMAL, player ); - // tr.pHit HAS to be conveyor, but just in case we screwed something up: - if( tr.pHit == ent ) - { - if( tr.vecPlaneNormal[2] > 0 ) - { - v[2] = ent->v.speed * com.sqrt( 1.0f - tr.vecPlaneNormal[2] * tr.vecPlaneNormal[2] ) / tr.vecPlaneNormal[2]; - if(DotProduct( ent->v.movedir, tr.vecPlaneNormal) > 0.0f ) - v[2] = -v[2]; // then we're moving down - move[2] = v[2] * sv_frametime(); - } - VectorAdd( player->v.origin, move, end ); - tr = SV_Move( player->v.origin, player->v.mins, player->v.maxs, end, MOVE_NORMAL, player ); - VectorCopy( tr.vecEndPos, player->v.origin ); - SV_LinkEdict( player, false ); - } - } -} - /* ============= SV_PhysicsNone @@ -1691,8 +1638,11 @@ static void SV_Physics_Entity( edict_t *ent ) ent->v.flags &= ~FL_BASEVELOCITY; // user dll can override movement type - if( svgame.dllFuncs.pfnPhysicsEntity( ent )) - return; + if( svgame.dllFuncs2.pfnPhysicsEntity ) + { + if( svgame.dllFuncs2.pfnPhysicsEntity( ent )) + return; // overrided + } switch( ent->v.movetype ) { @@ -1719,9 +1669,6 @@ static void SV_Physics_Entity( edict_t *ent ) case MOVETYPE_PUSH: SV_Physics_Pusher( ent ); break; - case MOVETYPE_CONVEYOR: - SV_Physics_Conveyor( ent ); - break; case MOVETYPE_WALK: Host_Error( "SV_Physics: bad movetype %i\n", ent->v.movetype ); break; @@ -1734,7 +1681,7 @@ void SV_FreeOldEntities( void ) int i; // at end of frame kill all entities which supposed to it - for( i = svgame.globals->maxClients + 1; i < svgame.globals->numEntities; i++ ) + for( i = svgame.globals->maxClients + 1; i < svgame.numEntities; i++ ) { ent = EDICT_NUM( i ); if( ent->free ) continue; @@ -1743,8 +1690,8 @@ void SV_FreeOldEntities( void ) SV_FreeEdict( ent ); } - // decrement svgame.globals->numEntities if the highest number entities died - for( ; EDICT_NUM( svgame.globals->numEntities - 1)->free; svgame.globals->numEntities-- ); + // decrement svgame.numEntities if the highest number entities died + for( ; EDICT_NUM( svgame.numEntities - 1)->free; svgame.numEntities-- ); } /* @@ -1767,7 +1714,7 @@ void SV_Physics( void ) SV_CheckAllEnts (); // treat each object in turn - for( i = 1; ( svgame.globals->force_retouch > 0 ) && i < svgame.globals->numEntities; i++ ) + for( i = 1; ( svgame.globals->force_retouch > 0 ) && i < svgame.numEntities; i++ ) { ent = EDICT_NUM( i ); if( !SV_IsValidEdict( ent )) continue; @@ -1779,7 +1726,7 @@ void SV_Physics( void ) // treat each object in turn if( !( sv.hostflags & SVF_PLAYERSONLY )) { - for( i = svgame.globals->maxClients + 1; i < svgame.globals->numEntities; i++ ) + for( i = svgame.globals->maxClients + 1; i < svgame.numEntities; i++ ) { ent = EDICT_NUM( i ); if( !SV_IsValidEdict( ent )) continue; diff --git a/engine/server/sv_pmove.c b/engine/server/sv_pmove.c index c790a409..d61cae58 100644 --- a/engine/server/sv_pmove.c +++ b/engine/server/sv_pmove.c @@ -201,30 +201,6 @@ static void pfnCon_NPrintf( int idx, char *fmt, ... ) BF_WriteString( &cl->netchan.message, string ); } -static void pfnCon_DPrintf( char *fmt, ... ) -{ - va_list argptr; - char string[MAX_SYSPATH]; - - va_start( argptr, fmt ); - com.vsprintf( string, fmt, argptr ); - va_end( argptr ); - - MsgDev( D_INFO, string ); -} - -static void pfnCon_Printf( char *fmt, ... ) -{ - va_list argptr; - char string[MAX_SYSPATH]; - - va_start( argptr, fmt ); - com.vsprintf( string, fmt, argptr ); - va_end( argptr ); - - Msg( string ); -} - static double Sys_FloatTime( void ) { return Sys_DoubleTime(); @@ -436,16 +412,19 @@ void SV_InitClientMove( void ) svgame.pmove->runfuncs = false; // enumerate client hulls - for( i = 0; i < PM_MAXHULLS; i++ ) - svgame.dllFuncs.pfnGetHullBounds( i, svgame.pmove->player_mins[i], svgame.pmove->player_maxs[i] ); + for( i = 0; i < 4; i++ ) + svgame.dllFuncs.pfnGetHullBounds( i, svgame.player_mins[i], svgame.player_maxs[i] ); + + Mem_Copy( svgame.pmove->player_mins, svgame.player_mins, sizeof( svgame.player_mins )); + Mem_Copy( svgame.pmove->player_maxs, svgame.player_maxs, sizeof( svgame.player_maxs )); // common utilities svgame.pmove->PM_Info_ValueForKey = Info_ValueForKey; svgame.pmove->PM_Particle = pfnParticle; svgame.pmove->PM_TestPlayerPosition = pfnTestPlayerPosition; - svgame.pmove->Con_NPrintf = pfnCon_NPrintf; - svgame.pmove->Con_DPrintf = pfnCon_DPrintf; - svgame.pmove->Con_Printf = pfnCon_Printf; + svgame.pmove->ConNPrintf = pfnCon_NPrintf; + svgame.pmove->ConDPrintf = pfnCon_DPrintf; + svgame.pmove->ConPrintf = pfnCon_Printf; svgame.pmove->Sys_FloatTime = Sys_FloatTime; svgame.pmove->PM_StuckTouch = pfnStuckTouch; svgame.pmove->PM_PointContents = pfnPointContents; diff --git a/engine/server/sv_save.c b/engine/server/sv_save.c index 7cb3e07e..1c2c306c 100644 --- a/engine/server/sv_save.c +++ b/engine/server/sv_save.c @@ -614,7 +614,7 @@ SAVERESTOREDATA *SV_SaveInit( int size ) int numents; if( size <= 0 ) size = 0x80000; // Reserve 512K for now, UNDONE: Shrink this after compressing strings - numents = svgame.globals->numEntities; + numents = svgame.numEntities; pSaveData = Mem_Alloc( host.mempool, sizeof(SAVERESTOREDATA) + ( sizeof(ENTITYTABLE) * numents ) + size ); SaveRestore_Init( pSaveData, (char *)(pSaveData + 1), size ); // skip the save structure @@ -648,7 +648,7 @@ void SV_SaveGameStateGlobals( SAVERESTOREDATA *pSaveData ) com.strncpy( header.mapName, sv.name, sizeof( header.mapName )); header.lightStyleCount = 0; - header.entityCount = svgame.globals->numEntities; + header.entityCount = svgame.numEntities; for( i = 0; i < MAX_LIGHTSTYLES; i++ ) { @@ -1037,7 +1037,7 @@ SAVERESTOREDATA *SV_SaveGameState( void ) // Save the data sections.pData = SaveRestore_AccessCurPos( pSaveData ); - numents = svgame.globals->numEntities; + numents = svgame.numEntities; SaveRestore_InitEntityTable( pSaveData, Mem_Alloc( host.mempool, sizeof(ENTITYTABLE) * numents ), numents ); @@ -1045,7 +1045,7 @@ SAVERESTOREDATA *SV_SaveGameState( void ) svgame.dllFuncs.pfnParmsChangeLevel(); // write entity descriptions - for( i = 0; i < svgame.globals->numEntities; i++ ) + for( i = 0; i < svgame.numEntities; i++ ) { edict_t *pent = EDICT_NUM( i ); pTable = &pSaveData->pTable[pSaveData->currentIndex]; diff --git a/engine/server/sv_world.c b/engine/server/sv_world.c index c2dc4c0d..36929980 100644 --- a/engine/server/sv_world.c +++ b/engine/server/sv_world.c @@ -130,13 +130,12 @@ SV_UnlinkEdict void SV_UnlinkEdict( edict_t *ent ) { // not linked in anywhere - if( !ent->pvServerData->area.prev ) + if( !ent->pvEngineData->area.prev ) return; - RemoveLink( &ent->pvServerData->area ); - ent->pvServerData->area.prev = NULL; - ent->pvServerData->area.next = NULL; - ent->pvServerData->linked = false; + RemoveLink( &ent->pvEngineData->area ); + ent->pvEngineData->area.prev = NULL; + ent->pvEngineData->area.next = NULL; } /* @@ -170,8 +169,6 @@ void SV_CheckForOutside( edict_t *ent ) } } - -int EntityInSolid( edict_t *ent ); /* =============== SV_LinkEntity @@ -184,16 +181,16 @@ void SV_LinkEdict( edict_t *ent, bool touch_triggers ) int i, j, num_leafs, topNode; sv_priv_t *sv_ent; - sv_ent = ent->pvServerData; + sv_ent = ent->pvEngineData; if( !sv_ent ) return; if( sv_ent->area.prev ) SV_UnlinkEdict( ent ); // unlink from old position if( ent == EDICT_NUM( 0 )) return; // don't add the world if( ent->free ) return; - // trying to classify unclassified edicts - if( sv.state == ss_active && sv_ent->s.ed_type == ED_SPAWNED ) - SV_ClassifyEdict( ent, ED_SPAWNED ); + // create baseline for new edicts in-game + if( sv.state == ss_active && !sv_ent->send_baseline ) + SV_BaselineForEntity( ent ); // set the abs box svgame.dllFuncs.pfnSetAbsBox( ent ); @@ -247,9 +244,6 @@ void SV_LinkEdict( edict_t *ent, bool touch_triggers ) } } - ent->pvServerData->s.ed_flags |= ESF_LINKEDICT; // change edict state on a client too... - sv_ent->linked = true; - // ignore not solid bodies if( ent->v.solid == SOLID_NOT && ent->v.skin == CONTENTS_NONE ) return; @@ -408,8 +402,11 @@ static void SV_ClipToLinks( areanode_t *node, moveclip_t *clip ) } // custom user filter - if( !svgame.dllFuncs.pfnShouldCollide( touch, clip->passedict )) - continue; + if( svgame.dllFuncs2.pfnShouldCollide ) + { + if( !svgame.dllFuncs2.pfnShouldCollide( touch, clip->passedict )) + continue; + } if( clip->flags & FMOVE_IGNORE_GLASS && CM_GetModelType( touch->v.modelindex ) == mod_brush ) { diff --git a/game_shared/pm_debug.cpp b/game_shared/pm_debug.cpp index 6f7bfa31..d9d097db 100644 --- a/game_shared/pm_debug.cpp +++ b/game_shared/pm_debug.cpp @@ -14,7 +14,6 @@ ****/ #include "mathlib.h" -#include "basetypes.h" #include "const.h" #include "usercmd.h" #include "pm_defs.h" diff --git a/game_shared/pm_defs.h b/game_shared/pm_defs.h index 1316c013..46d301d5 100644 --- a/game_shared/pm_defs.h +++ b/game_shared/pm_defs.h @@ -169,9 +169,9 @@ typedef struct playermove_s const char *(*PM_Info_ValueForKey) ( const char *s, const char *key ); void (*PM_Particle)( float *origin, int color, float life, int zpos, int zvel ); int (*PM_TestPlayerPosition)( float *pos, pmtrace_t *ptrace ); - void (*Con_NPrintf)( int idx, char *fmt, ... ); - void (*Con_DPrintf)( char *fmt, ... ); - void (*Con_Printf)( char *fmt, ... ); + void (*ConNPrintf)( int idx, char *fmt, ... ); + void (*ConDPrintf)( char *fmt, ... ); + void (*ConPrintf)( char *fmt, ... ); double (*Sys_FloatTime)( void ); void (*PM_StuckTouch)( int hitent, pmtrace_t *ptraceresult ); int (*PM_PointContents)( float *p, int *truecontents /*filled in if this is non-null*/ ); diff --git a/game_shared/pm_shared.cpp b/game_shared/pm_shared.cpp index 8bd287f0..df3ed4b7 100644 --- a/game_shared/pm_shared.cpp +++ b/game_shared/pm_shared.cpp @@ -14,14 +14,15 @@ ****/ #include +#include #include "mathlib.h" -#include "basetypes.h" #include "const.h" #include "usercmd.h" #include "pm_defs.h" #include "pm_shared.h" #include "pm_movevars.h" #include "com_model.h" +#include "pm_debug.h" #include // NULL #include // sqrt #include // strcpy @@ -39,7 +40,7 @@ static int pm_shared_initialized = 0; #pragma warning( disable : 4305 ) -playermove_t *pmove = (playermove_t *)NULL; +playermove_t *pmove = NULL; // Ducking time #define TIME_TO_DUCK 0.4 @@ -224,7 +225,7 @@ void PM_InitTextureTypes() bTextureTypeInit = TRUE; } -char PM_FindTextureType( const char *name ) +char PM_FindTextureType( char *name ) { int left, right, pivot; int val; @@ -527,13 +528,13 @@ void PM_UpdateStepSound( void ) fvol = 0.35; pmove->flTimeStepSound = 350; } - else if ( pmove->PM_PointContents ( knee, (int *)NULL ) == CONTENTS_WATER ) + else if ( pmove->PM_PointContents ( knee, NULL ) == CONTENTS_WATER ) { step = STEP_WADE; fvol = 0.65; pmove->flTimeStepSound = 600; } - else if ( pmove->PM_PointContents ( feet, (int *)NULL ) == CONTENTS_WATER ) + else if ( pmove->PM_PointContents ( feet, NULL ) == CONTENTS_WATER ) { step = STEP_SLOSH; fvol = fWalking ? 0.2 : 0.5; @@ -620,7 +621,7 @@ int PM_AddToTouched(pmtrace_t tr, vec3_t impactvelocity) VectorCopy( impactvelocity, tr.deltavelocity ); if (pmove->numtouch >= MAX_PHYSENTS) - pmove->Con_DPrintf("Too many entities were touched!\n"); + pmove->ConDPrintf("Too many entities were touched!\n"); pmove->touchindex[pmove->numtouch++] = tr; return TRUE; @@ -645,24 +646,24 @@ void PM_CheckVelocity () // See if it's bogus. if (IS_NAN(pmove->velocity[i])) { - pmove->Con_Printf ("PM Got a NaN velocity %i\n", i); + pmove->ConPrintf ("PM Got a NaN velocity %i\n", i); pmove->velocity[i] = 0; } if (IS_NAN(pmove->origin[i])) { - pmove->Con_Printf ("PM Got a NaN origin on %i\n", i); + pmove->ConPrintf ("PM Got a NaN origin on %i\n", i); pmove->origin[i] = 0; } // Bound it. if (pmove->velocity[i] > pmove->movevars->maxvelocity) { - pmove->Con_DPrintf ("PM Got a velocity too high on %i\n", i); + pmove->ConDPrintf ("PM Got a velocity too high on %i\n", i); pmove->velocity[i] = pmove->movevars->maxvelocity; } else if (pmove->velocity[i] < -pmove->movevars->maxvelocity) { - pmove->Con_DPrintf ("PM Got a velocity too low on %i\n", i); + pmove->ConDPrintf ("PM Got a velocity too low on %i\n", i); pmove->velocity[i] = -pmove->movevars->maxvelocity; } } @@ -802,7 +803,7 @@ int PM_FlyMove (void) if (trace.allsolid) { // entity is trapped in another solid VectorCopy (vec3_origin, pmove->velocity); - //Con_DPrintf("Trapped 4\n"); + //ConDPrintf("Trapped 4\n"); return 4; } @@ -840,7 +841,7 @@ int PM_FlyMove (void) if (!trace.plane.normal[2]) { blocked |= 2; // step / wall - //Con_DPrintf("Blocked by %i\n", trace.ent); + //ConDPrintf("Blocked by %i\n", trace.ent); } // Reduce amount of pmove->frametime left by total time left * fraction @@ -852,7 +853,7 @@ int PM_FlyMove (void) { // this shouldn't really happen // Stop our movement if so. VectorCopy (vec3_origin, pmove->velocity); - //Con_DPrintf("Too many planes 4\n"); + //ConDPrintf("Too many planes 4\n"); break; } @@ -911,9 +912,9 @@ int PM_FlyMove (void) { // go along the crease if (numplanes != 2) { - //Con_Printf ("clip velocity, numplanes == %i\n",numplanes); + //ConPrintf ("clip velocity, numplanes == %i\n",numplanes); VectorCopy (vec3_origin, pmove->velocity); - //Con_DPrintf("Trapped 4\n"); + //ConDPrintf("Trapped 4\n"); break; } @@ -928,7 +929,7 @@ int PM_FlyMove (void) // if (DotProduct (pmove->velocity, primal_velocity) <= 0) { - //Con_DPrintf("Back\n"); + //ConDPrintf("Back\n"); VectorCopy (vec3_origin, pmove->velocity); break; } @@ -938,7 +939,7 @@ int PM_FlyMove (void) if ( allFraction == 0 ) { VectorCopy (vec3_origin, pmove->velocity); - //Con_DPrintf( "Don't stick\n" ); + //ConDPrintf( "Don't stick\n" ); } return blocked; @@ -1467,7 +1468,7 @@ int PM_CheckWater () // Now check a point that is at the player hull midpoint. point[2] = pmove->origin[2] + heightover2; - cont = pmove->PM_PointContents (point, (int *)NULL ); + cont = pmove->PM_PointContents (point, NULL ); // If that point is also under water... if (cont <= CONTENTS_WATER && cont > CONTENTS_TRANSLUCENT ) { @@ -1477,7 +1478,7 @@ int PM_CheckWater () // Now check the eye position. (view_ofs is relative to the origin) point[2] = pmove->origin[2] + pmove->view_ofs[2]; - cont = pmove->PM_PointContents (point, (int *)NULL ); + cont = pmove->PM_PointContents (point, NULL ); if (cont <= CONTENTS_WATER && cont > CONTENTS_TRANSLUCENT ) pmove->waterlevel = 3; // In over our eyes } @@ -1665,9 +1666,9 @@ int PM_CheckStuck (void) i = PM_GetRandomStuckOffsets(pmove->player_index, pmove->server, offset); VectorAdd(base, offset, test); - if ( ( hitent = pmove->PM_TestPlayerPosition ( test, (pmtrace_t *)NULL ) ) == -1 ) + if ( ( hitent = pmove->PM_TestPlayerPosition ( test, NULL ) ) == -1 ) { - //Con_DPrintf("Nudged\n"); + //ConDPrintf("Nudged\n"); PM_ResetStuckOffsets( pmove->player_index, pmove->server ); @@ -1698,7 +1699,7 @@ int PM_CheckStuck (void) test[1] += y; test[2] += z; - if ( pmove->PM_TestPlayerPosition ( test, (pmtrace_t *)NULL ) == -1 ) + if ( pmove->PM_TestPlayerPosition ( test, NULL ) == -1 ) { VectorCopy( test, pmove->origin ); return 0; @@ -1865,7 +1866,7 @@ void PM_FixPlayerCrouchStuck( int direction ) int i; vec3_t test; - hitent = pmove->PM_TestPlayerPosition ( pmove->origin, (pmtrace_t *)NULL ); + hitent = pmove->PM_TestPlayerPosition ( pmove->origin, NULL ); if (hitent == -1 ) return; @@ -1873,7 +1874,7 @@ void PM_FixPlayerCrouchStuck( int direction ) for ( i = 0; i < 36; i++ ) { pmove->origin[2] += direction; - hitent = pmove->PM_TestPlayerPosition ( pmove->origin, (pmtrace_t *)NULL ); + hitent = pmove->PM_TestPlayerPosition ( pmove->origin, NULL ); if (hitent == -1 ) return; } @@ -1908,7 +1909,7 @@ void PM_UnDuck( void ) if ( trace.startsolid ) { // See if we are stuck? If so, stay ducked with the duck hull until we have a clear spot - //Con_Printf( "unstick got stuck\n" ); + //ConPrintf( "unstick got stuck\n" ); pmove->usehull = 1; return; } @@ -2044,7 +2045,7 @@ void PM_LadderMove( physent_t *pLadder ) VectorCopy( pmove->origin, floor ); floor[2] += pmove->player_mins[pmove->usehull][2] - 1; - if ( pmove->PM_PointContents( floor, (int *)NULL ) == CONTENTS_SOLID ) + if ( pmove->PM_PointContents( floor, NULL ) == CONTENTS_SOLID ) onFloor = TRUE; else onFloor = FALSE; @@ -2057,7 +2058,7 @@ void PM_LadderMove( physent_t *pLadder ) float forward = 0, right = 0; vec3_t vpn, v_right; - AngleVectors( pmove->angles, vpn, v_right, (float *)NULL ); + AngleVectors( pmove->angles, vpn, v_right, NULL ); if ( pmove->cmd.buttons & IN_BACK ) forward -= MAX_CLIMB_SPEED; if ( pmove->cmd.buttons & IN_FORWARD ) @@ -2155,7 +2156,7 @@ physent_t *PM_Ladder( void ) } } - return (physent_t *)NULL; + return NULL; } @@ -2318,7 +2319,7 @@ void PM_Physics_Toss() vel = DotProduct( pmove->velocity, pmove->velocity ); - // Con_DPrintf("%f %f: %.0f %.0f %.0f\n", vel, trace.fraction, ent->velocity[0], ent->velocity[1], ent->velocity[2] ); + // ConDPrintf("%f %f: %.0f %.0f %.0f\n", vel, trace.fraction, ent->velocity[0], ent->velocity[1], ent->velocity[2] ); if (vel < (30 * 30) || (pmove->movetype != MOVETYPE_BOUNCE && pmove->movetype != MOVETYPE_BOUNCEMISSILE)) { @@ -2881,7 +2882,7 @@ were contacted during the move. */ void PM_PlayerMove ( int server ) { - physent_t *pLadder = (physent_t *)NULL; + physent_t *pLadder = NULL; // Are we running server code? pmove->server = server; @@ -2972,7 +2973,7 @@ void PM_PlayerMove ( int server ) switch ( pmove->movetype ) { default: - pmove->Con_DPrintf("Bogus pmove player movetype %i on (%i) 0=cl 1=sv\n", pmove->movetype, pmove->server); + pmove->ConDPrintf("Bogus pmove player movetype %i on (%i) 0=cl 1=sv\n", pmove->movetype, pmove->server); break; case MOVETYPE_NONE: @@ -3295,9 +3296,11 @@ int PM_GetPhysEntInfo( int ent ) int PM_FindPhysEntByIndex( int index ) { + int i; + if ( index >= 0 && index <= pmove->numphysent) { - for( int i = 0; i < pmove->numphysent; i++ ) + for( i = 0; i < pmove->numphysent; i++ ) { if( pmove->physents[i].info == index ) return i; diff --git a/game_shared/pm_shared.h b/game_shared/pm_shared.h index ae4cba95..cf5230d9 100644 --- a/game_shared/pm_shared.h +++ b/game_shared/pm_shared.h @@ -7,7 +7,7 @@ void PM_Init( struct playermove_s *ppmove ); void PM_Move( struct playermove_s *ppmove, int server ); -char PM_FindTextureType( const char *name ); +char PM_FindTextureType( char *name ); // spectator Movement modes (stored in pev->iuser1, so the physics code can get at them) #define OBS_NONE 0 diff --git a/game_shared/shake.h b/game_shared/shake.h index d2f88096..d32cfb27 100644 --- a/game_shared/shake.h +++ b/game_shared/shake.h @@ -1,49 +1,30 @@ -/*** -* -* Copyright (c) 1996-2002, 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. -* -****/ +//======================================================================= +// Copyright XashXT Group 2009 © +// shake.h - screenshake and screenfade events +//======================================================================= #ifndef SHAKE_H #define SHAKE_H // Screen / View effects -// -// Commands for the screen shake effect. -// -enum ShakeCommand_t -{ - SHAKE_START = 0, // Starts the screen shake for all players within the radius. - SHAKE_STOP, // Stops the screen shake for all players within the radius. - SHAKE_AMPLITUDE, // Modifies the amplitude of an active screen shake for all players within the radius. - SHAKE_FREQUENCY, // Modifies the frequency of an active screen shake for all players within the radius. -}; +// screen shake +extern int gmsgShake; typedef struct { - unsigned short command; // ShakeCommand_t unsigned short amplitude; // FIXED 4.12 amount of shake unsigned short duration; // FIXED 4.12 seconds duration - unsigned short frequency; // FIXED 8.8 low frequency is a jerk,high frequency is a rumble + unsigned short frequency; // FIXED 8.8 low frequency is a jerk, high frequency is a rumble } ScreenShake; -#define FFADE_IN 0x0001 // Fade in (not out) -#define FFADE_OUT 0x0002 // Fade out (not in) -#define FFADE_MODULATE 0x0004 // Modulate (don't blend) -#define FFADE_STAYOUT 0x0008 // ignores the duration, stays faded out until new ScreenFade message received -#define FFADE_CUSTOMVIEW 0x0010 // fading only at custom viewing (don't sending this to engine ) -#define FFADE_PURGE 0x0020 // Purges all other fades, replacing them with this one - // Fade in/out +extern int gmsgFade; + +#define FFADE_IN 0x0000 // Fade in (not out) +#define FFADE_OUT 0x0001 // Fade out (not in) +#define FFADE_MODULATE 0x0002 // Modulate (don't blend) +#define FFADE_STAYOUT 0x0004 // ignores the duration, stays faded out until new ScreenFade message received + // This structure is sent over the net to describe a screen fade event typedef struct { @@ -53,5 +34,4 @@ typedef struct byte r, g, b, a; // fade to color ( max alpha ) } ScreenFade; -#endif // SHAKE_H - +#endif // SHAKE_H \ No newline at end of file diff --git a/game_shared/te_shared.h b/game_shared/te_shared.h deleted file mode 100644 index de2648aa..00000000 --- a/game_shared/te_shared.h +++ /dev/null @@ -1,444 +0,0 @@ -//======================================================================= -// Copyright XashXT Group 2009 © -// tempents.h - client temp entities -//======================================================================= -#ifndef TEMPENTS_H -#define TEMPENTS_H - -#define TE_BEAMPOINTS 0 // beam effect between two points -// coord coord coord (start position) -// coord coord coord (end position) -// short (sprite index) -// byte (starting frame) -// byte (frame rate in 0.1's) -// byte (life in 0.1's) -// byte (line width in 0.1's) -// byte (noise amplitude in 0.01's) -// byte,byte,byte (color) -// byte (brightness) -// byte (scroll speed in 0.1's) - -#define TE_BEAMENTPOINT 1 // beam effect between point and entity -// short (start entity) -// coord coord coord (end position) -// short (sprite index) -// byte (starting frame) -// byte (frame rate in 0.1's) -// byte (life in 0.1's) -// byte (line width in 0.1's) -// byte (noise amplitude in 0.01's) -// byte,byte,byte (color) -// byte (brightness) -// byte (scroll speed in 0.1's) - -#define TE_GUNSHOT 2 // particle effect plus ricochet sound -// coord coord coord (position) - -#define TE_EXPLOSION 3 // additive sprite, 2 dynamic lights, flickering particles, explosion sound, move vertically 8 pps -// coord coord coord (position) -// short (sprite index) -// byte (scale in 0.1's) -// byte (framerate) -// byte (flags) - -#define TE_TAREXPLOSION 4 // Quake1 "tarbaby" explosion with sound -// coord coord coord (position) - -#define TE_SMOKE 5 // alphablend sprite, move vertically 30 pps -// coord coord coord (position) -// short (sprite index) -// byte (scale in 0.1's) -// byte (framerate) - -#define TE_TRACER 6 // tracer effect from point to point -// coord, coord, coord (start) -// coord, coord, coord (end) - -#define TE_LIGHTNING 7 // TE_BEAMPOINTS with simplified parameters -// coord, coord, coord (start) -// coord, coord, coord (end) -// byte (life in 0.1's) -// byte (width in 0.1's) -// byte (amplitude in 0.01's) -// short (sprite model index) - -#define TE_BEAMENTS 8 -// short (start entity) -// short (end entity) -// short (sprite index) -// byte (starting frame) -// byte (frame rate in 0.1's) -// byte (life in 0.1's) -// byte (line width in 0.1's) -// byte (noise amplitude in 0.01's) -// byte,byte,byte (color) -// byte (brightness) -// byte (scroll speed in 0.1's) - -#define TE_SPARKS 9 // 8 random tracers with gravity, ricochet sprite -// coord coord coord (position) - -#define TE_LAVASPLASH 10 // Quake1 lava splash -// coord coord coord (position) - -#define TE_TELEPORT 11 // Quake1 teleport splash -// coord coord coord (position) - -#define TE_EXPLOSION2 12 // Quake1 colormaped (base palette) particle explosion with sound -// coord coord coord (position) -// byte (starting color) -// byte (num colors) - -#define TE_BSPDECAL 13 // Decal from the .BSP file -// coord, coord, coord (x,y,z), decal position (center of texture in world) -// short (texture index of precached decal texture name) -// short (entity index) -// [optional - only included if previous short is non-zero (not the world)] short (index of model of above entity) - -#define TE_IMPLOSION 14 // tracers moving toward a point -// coord, coord, coord (position) -// byte (radius) -// byte (count) -// byte (life in 0.1's) - -#define TE_SPRITETRAIL 15 // line of moving glow sprites with gravity, fadeout, and collisions -// coord, coord, coord (start) -// coord, coord, coord (end) -// short (sprite index) -// byte (count) -// byte (life in 0.1's) -// byte (scale in 0.1's) -// byte (velocity along vector in 10's) -// byte (randomness of velocity in 10's) - -#define TE_BEAM 16 // obsolete - -#define TE_SPRITE 17 // additive sprite, plays 1 cycle -// coord, coord, coord (position) -// short (sprite index) -// byte (scale in 0.1's) -// byte (brightness) - -#define TE_BEAMSPRITE 18 // A beam with a sprite at the end -// coord, coord, coord (start position) -// coord, coord, coord (end position) -// short (beam sprite index) -// short (end sprite index) - -#define TE_BEAMTORUS 19 // screen aligned beam ring, expands to max radius over lifetime -// coord coord coord (center position) -// coord coord coord (axis and radius) -// short (sprite index) -// byte (starting frame) -// byte (frame rate in 0.1's) -// byte (life in 0.1's) -// byte (line width in 0.1's) -// byte (noise amplitude in 0.01's) -// byte,byte,byte (color) -// byte (brightness) -// byte (scroll speed in 0.1's) - -#define TE_BEAMDISK 20 // disk that expands to max radius over lifetime -// coord coord coord (center position) -// coord coord coord (axis and radius) -// short (sprite index) -// byte (starting frame) -// byte (frame rate in 0.1's) -// byte (life in 0.1's) -// byte (line width in 0.1's) -// byte (noise amplitude in 0.01's) -// byte,byte,byte (color) -// byte (brightness) -// byte (scroll speed in 0.1's) - -#define TE_BEAMCYLINDER 21 // cylinder that expands to max radius over lifetime -// coord coord coord (center position) -// coord coord coord (axis and radius) -// short (sprite index) -// byte (starting frame) -// byte (frame rate in 0.1's) -// byte (life in 0.1's) -// byte (line width in 0.1's) -// byte (noise amplitude in 0.01's) -// byte,byte,byte (color) -// byte (brightness) -// byte (scroll speed in 0.1's) - -#define TE_BEAMFOLLOW 22 // create a line of decaying beam segments until entity stops moving -// short (entity:attachment to follow) -// short (sprite index) -// byte (life in 0.1's) -// byte (line width in 0.1's) -// byte,byte,byte (color) -// byte (brightness) - -#define TE_GLOWSPRITE 23 -// coord, coord, coord (pos) short (model index) byte (scale / 10) - -#define TE_BEAMRING 24 // connect a beam ring to two entities -// short (start entity) -// short (end entity) -// short (sprite index) -// byte (starting frame) -// byte (frame rate in 0.1's) -// byte (life in 0.1's) -// byte (line width in 0.1's) -// byte (noise amplitude in 0.01's) -// byte,byte,byte (color) -// byte (brightness) -// byte (scroll speed in 0.1's) - -#define TE_STREAK_SPLASH 25 // oriented shower of tracers -// coord coord coord (start position) -// coord coord coord (direction vector) -// byte (color) -// short (count) -// short (base speed) -// short (ramdon velocity) - -#define TE_BEAMHOSE 26 // obsolete - -#define TE_DLIGHT 27 // dynamic light, effect world, minor entity effect -// coord, coord, coord (pos) -// byte (radius in 10's) -// byte byte byte (color) -// byte (life in 10's) -// byte (decay rate in 10's) - -#define TE_ELIGHT 28 // point entity light, no world effect -// short (entity:attachment to follow) -// coord coord coord (initial position) -// coord (radius) -// byte byte byte (color) -// byte (life in 0.1's) -// coord (decay rate) - -#define TE_TEXTMESSAGE 29 -// short 1.2.13 x (-1 = center) -// short 1.2.13 y (-1 = center) -// byte Effect 0 = fade in/fade out - // 1 is flickery credits - // 2 is write out (training room) - -// 4 bytes r,g,b,a color1 (text color) -// 4 bytes r,g,b,a color2 (effect color) -// ushort 8.8 fadein time -// ushort 8.8 fadeout time -// ushort 8.8 hold time -// optional ushort 8.8 fxtime (time the highlight lags behing the leading text in effect 2) -// string text message (512 chars max sz string) - -#define TE_LINE 30 -// coord, coord, coord startpos -// coord, coord, coord endpos -// short life in 0.1 s -// 3 bytes r, g, b - -#define TE_BOX 31 -// coord, coord, coord boxmins -// coord, coord, coord boxmaxs -// short life in 0.1 s -// 3 bytes r, g, b - -#define TE_KILLBEAM 99 // kill all beams attached to entity -// short (entity) - -#define TE_LARGEFUNNEL 100 -// coord coord coord (funnel position) -// short (sprite index) -// short (flags) - -#define TE_BLOODSTREAM 101 // particle spray -// coord coord coord (start position) -// coord coord coord (spray vector) -// byte (color) -// byte (speed) - -#define TE_SHOWLINE 102 // line of particles every 5 units, dies in 30 seconds -// coord coord coord (start position) -// coord coord coord (end position) - -#define TE_BLOOD 103 // particle spray -// coord coord coord (start position) -// coord coord coord (spray vector) -// byte (color) -// byte (speed) - -#define TE_DECAL 104 // Decal applied to a brush entity (not the world) -// coord, coord, coord (x,y,z), decal position (center of texture in world) -// byte (texture index of precached decal texture name) -// short (entity index) - -#define TE_FIZZ 105 // create alpha sprites inside of entity, float upwards -// short (entity) -// short (sprite index) -// byte (density) - -#define TE_MODEL 106 // create a moving model that bounces and makes a sound when it hits -// coord, coord, coord (position) -// coord, coord, coord (velocity) -// angle (initial yaw) -// short (model index) -// byte (bounce sound type) -// byte (life in 0.1's) - -#define TE_EXPLODEMODEL 107 // spherical shower of models, picks from set -// coord, coord, coord (origin) -// coord (velocity) -// short (model index) -// short (count) -// byte (life in 0.1's) - -#define TE_BREAKMODEL 108 // box of models or sprites -// coord, coord, coord (position) -// coord, coord, coord (size) -// coord, coord, coord (velocity) -// byte (random velocity in 10's) -// short (sprite or model index) -// byte (count) -// byte (life in 0.1 secs) -// byte (flags) - -#define TE_GUNSHOTDECAL 109 // decal and ricochet sound -// coord, coord, coord (position) -// short (entity index???) -// byte (decal???) - -#define TE_SPRITE_SPRAY 110 // spay of alpha sprites -// coord, coord, coord (position) -// coord, coord, coord (velocity) -// short (sprite index) -// byte (count) -// byte (speed) -// byte (noise) - -#define TE_ARMOR_RICOCHET 111 // quick spark sprite, client ricochet sound. -// coord, coord, coord (position) -// byte (scale in 0.1's) - -#define TE_PLAYERDECAL 112 // ??? -// byte (playerindex) -// coord, coord, coord (position) -// short (entity???) -// byte (decal number???) -// [optional] short (model index???) - -#define TE_BUBBLES 113 // create alpha sprites inside of box, float upwards -// coord, coord, coord (min start position) -// coord, coord, coord (max start position) -// coord (float height) -// short (model index) -// byte (count) -// coord (speed) - -#define TE_BUBBLETRAIL 114 // create alpha sprites along a line, float upwards -// coord, coord, coord (min start position) -// coord, coord, coord (max start position) -// coord (float height) -// short (model index) -// byte (count) -// coord (speed) - -#define TE_BLOODSPRITE 115 // spray of opaque sprite1's that fall, single sprite2 for 1..2 secs (this is a high-priority tent) -// coord, coord, coord (position) -// short (sprite1 index) -// short (sprite2 index) -// byte (color) -// byte (scale) - -#define TE_WORLDDECAL 116 // Decal applied to the world brush -// coord, coord, coord (x,y,z), decal position (center of texture in world) -// byte (texture index of precached decal texture name) - -#define TE_WORLDDECALHIGH 117 // Decal (with texture index > 256) applied to world brush -// coord, coord, coord (x,y,z), decal position (center of texture in world) -// byte (texture index of precached decal texture name - 256) - -#define TE_DECALHIGH 118 // Same as TE_DECAL, but the texture index was greater than 256 -// coord, coord, coord (x,y,z), decal position (center of texture in world) -// byte (texture index of precached decal texture name - 256) -// short (entity index) - -#define TE_PROJECTILE 119 // Makes a projectile (like a nail) (this is a high-priority tent) -// coord, coord, coord (position) -// coord, coord, coord (velocity) -// short (modelindex) -// byte (life) -// byte (owner) projectile won't collide with owner (if owner == 0, projectile will hit any client). - -#define TE_SPRAY 120 // Throws a shower of sprites or models -// coord, coord, coord (position) -// coord, coord, coord (direction) -// short (modelindex) -// byte (count) -// byte (speed) -// byte (noise) -// byte (rendermode) - -#define TE_PLAYERSPRITES 121 // sprites emit from a player's bounding box (ONLY use for players!) -// byte (playernum) -// short (sprite modelindex) -// byte (count) -// byte (variance) (0 = no variance in size) (10 = 10% variance in size) - -#define TE_PARTICLEBURST 122 // very similar to lavasplash. -// coord (origin) -// short (radius) -// byte (particle color) -// byte (duration * 10) (will be randomized a bit) - -#define TE_FIREFIELD 123 // makes a field of fire. -// coord (origin) -// short (radius) (fire is made in a square around origin. -radius, -radius to radius, radius) -// short (modelindex) -// byte (count) -// byte (flags) -// byte (duration (in seconds) * 10) (will be randomized a bit) -// -// to keep network traffic low, this message has associated flags that fit into a byte: -#define TEFIRE_FLAG_ALLFLOAT 1 // all sprites will drift upwards as they animate -#define TEFIRE_FLAG_SOMEFLOAT 2 // some of the sprites will drift upwards. (50% chance) -#define TEFIRE_FLAG_LOOP 4 // if set, sprite plays at 15 fps, otherwise plays at whatever rate stretches the animation over the sprite's duration. -#define TEFIRE_FLAG_ALPHA 8 // if set, sprite is rendered alpha blended at 50% else, opaque -#define TEFIRE_FLAG_PLANAR 16 // if set, all fire sprites have same initial Z instead of randomly filling a cube. - -#define TE_PLAYERATTACHMENT 124 // attaches a TENT to a player (this is a high-priority tent) -// byte (entity index of player) -// coord (vertical offset) ( attachment origin.z = player origin.z + vertical offset ) -// short (model index) -// short (life * 10 ); - -#define TE_KILLPLAYERATTACHMENTS 125 // will expire all TENTS attached to a player. -// byte (entity index of player) - -#define TE_MULTIGUNSHOT 126 // much more compact shotgun message -// This message is used to make a client approximate a 'spray' of gunfire. -// Any weapon that fires more than one bullet per frame and fires in a bit of a spread is -// a good candidate for MULTIGUNSHOT use. (shotguns) -// -// NOTE: This effect makes the client do traces for each bullet, these client traces ignore -// entities that have studio models.Traces are 4096 long. -// -// coord (origin) -// coord (origin) -// coord (origin) -// coord (direction) -// coord (direction) -// coord (direction) -// coord (x noise * 100) -// coord (y noise * 100) -// byte (count) -// byte (bullethole decal texture index) - -#define TE_USERTRACER 127 // larger message than the standard tracer, but allows some customization. -// coord (origin) -// coord (origin) -// coord (origin) -// coord (velocity) -// coord (velocity) -// coord (velocity) -// byte ( life * 10 ) -// byte ( color ) this is an index into an array of color vectors in the engine. (0 - ) -// byte ( length * 10 ) - -#endif//TEMPENTS_H \ No newline at end of file diff --git a/game_shared/vector.h b/game_shared/vector.h index 03a53dd7..297e36a1 100644 --- a/game_shared/vector.h +++ b/game_shared/vector.h @@ -11,8 +11,6 @@ #include #pragma warning( disable : 4244 ) // int or float down-conversion - -#define NUMVERTEXNORMALS 162 #define bound( min, num, max ) ((num) >= (min) ? ((num) < (max) ? (num) : (max)) : (min)) #ifndef M_PI diff --git a/gameui/basemenu.cpp b/gameui/basemenu.cpp index 7561a87b..01158cf1 100644 --- a/gameui/basemenu.cpp +++ b/gameui/basemenu.cpp @@ -1187,7 +1187,7 @@ void UI_ApplyCustomColors( void ) if( !afile ) { // not error, not warning, just notify - ALERT( at_aiconsole, "UI_SetColors: colors.lst not found\n" ); + Con_Printf( "UI_SetColors: colors.lst not found\n" ); return; } diff --git a/gameui/basemenu.h b/gameui/basemenu.h index c607ffd3..c873f0ed 100644 --- a/gameui/basemenu.h +++ b/gameui/basemenu.h @@ -18,12 +18,11 @@ #define CS_SIZE 64 // size of one config string #define CS_TIME 16 // size of time string -struct netadr_s -{ - int type; // NA_LOOPBACK = 0, NA_BROADCAST = 1, NA_IP = 2 - byte ip[4]; - word port; -}; +// color strings +#define ColorIndex( c ) ((( c ) - '0' ) & 7 ) +#define IsColorString( p ) ( p && *( p ) == '^' && *(( p ) + 1) && *(( p ) + 1) >= '0' && *(( p ) + 1 ) <= '9' ) + +#include "netadr.h" #define ART_BACKGROUND "gfx/shell/splash" #define UI_CURSOR_NORMAL "gfx/shell/cursor" @@ -90,28 +89,28 @@ typedef enum } menuType_t; // Generic flags -#define QMF_LEFT_JUSTIFY BIT(0) -#define QMF_CENTER_JUSTIFY BIT(1) -#define QMF_RIGHT_JUSTIFY BIT(2) -#define QMF_GRAYED BIT(3) // Grays and disables -#define QMF_INACTIVE BIT(4) // Disables any input -#define QMF_HIDDEN BIT(5) // Doesn't draw -#define QMF_NUMBERSONLY BIT(6) // Edit field is only numbers -#define QMF_LOWERCASE BIT(7) // Edit field is all lower case -#define QMF_UPPERCASE BIT(8) // Edit field is all upper case -#define QMF_DRAW_ADDITIVE BIT(9) // enable additive for this bitmap -#define QMF_PULSEIFFOCUS BIT(10) -#define QMF_HIGHLIGHTIFFOCUS BIT(11) -#define QMF_SMALLFONT BIT(12) -#define QMF_BIGFONT BIT(13) -#define QMF_DROPSHADOW BIT(14) -#define QMF_SILENT BIT(15) // Don't play sounds -#define QMF_HASMOUSEFOCUS BIT(16) -#define QMF_MOUSEONLY BIT(17) // Only mouse input allowed -#define QMF_FOCUSBEHIND BIT(18) // Focus draws behind normal item -#define QMF_NOTIFY BIT(19) // draw notify at right screen side -#define QMF_ACT_ONRELEASE BIT(20) // call Key_Event when button is released -#define QMF_ALLOW_COLORSTRINGS BIT(21) // allow colorstring in MENU_FIELD +#define QMF_LEFT_JUSTIFY (1<<0) +#define QMF_CENTER_JUSTIFY (1<<1) +#define QMF_RIGHT_JUSTIFY (1<<2) +#define QMF_GRAYED (1<<3) // Grays and disables +#define QMF_INACTIVE (1<<4) // Disables any input +#define QMF_HIDDEN (1<<5) // Doesn't draw +#define QMF_NUMBERSONLY (1<<6) // Edit field is only numbers +#define QMF_LOWERCASE (1<<7) // Edit field is all lower case +#define QMF_UPPERCASE (1<<8) // Edit field is all upper case +#define QMF_DRAW_ADDITIVE (1<<9) // enable additive for this bitmap +#define QMF_PULSEIFFOCUS (1<<10) +#define QMF_HIGHLIGHTIFFOCUS (1<<11) +#define QMF_SMALLFONT (1<<12) +#define QMF_BIGFONT (1<<13) +#define QMF_DROPSHADOW (1<<14) +#define QMF_SILENT (1<<15) // Don't play sounds +#define QMF_HASMOUSEFOCUS (1<<16) +#define QMF_MOUSEONLY (1<<17) // Only mouse input allowed +#define QMF_FOCUSBEHIND (1<<18) // Focus draws behind normal item +#define QMF_NOTIFY (1<<19) // draw notify at right screen side +#define QMF_ACT_ONRELEASE (1<<20) // call Key_Event when button is released +#define QMF_ALLOW_COLORSTRINGS (1<<21) // allow colorstring in MENU_FIELD // Callback notifications #define QM_GOTFOCUS 1 diff --git a/gameui/enginecallback.h b/gameui/enginecallback.h index dd5afa0e..6f66d7d4 100644 --- a/gameui/enginecallback.h +++ b/gameui/enginecallback.h @@ -60,7 +60,7 @@ #define Cmd_RemoveCommand (*g_engfuncs.pfnDelCommand) #define CMD_ARGC (*g_engfuncs.pfnCmdArgc) #define CMD_ARGV (*g_engfuncs.pfnCmdArgv) -#define ALERT (*g_engfuncs.pfnAlertMessage) +#define Con_Printf (*g_engfuncs.Con_Printf) #define GET_AUDIO_LIST (*g_engfuncs.pfnGetAudioList) #define GET_VIDEO_LIST (*g_engfuncs.pfnGetVideoList) diff --git a/gameui/extdll.h b/gameui/extdll.h index 669cbd4b..daf3962c 100644 --- a/gameui/extdll.h +++ b/gameui/extdll.h @@ -14,7 +14,6 @@ #pragma warning(disable : 4244) // conversion from 'float' to 'int', possible loss of data #include "windows.h" -#include "basetypes.h" // Misc C-runtime library headers #include @@ -30,6 +29,5 @@ #include "vector.h" #include "gameui_api.h" -#include "game_shared.h" #endif//EXTDLL_H \ No newline at end of file diff --git a/gameui/gameui.dsp b/gameui/gameui.dsp index 7b4c3bc0..758ecc87 100644 --- a/gameui/gameui.dsp +++ b/gameui/gameui.dsp @@ -80,7 +80,7 @@ SOURCE="$(InputPath)" # PROP Ignore_Export_Lib 1 # PROP Target_Dir "" # ADD BASE CPP /nologo /MTd /W3 /Gm /GX /ZI /Od /D "WIN32" /D "_DEBUG" /D "_WINDOWS" /D "_MBCS" /D "_USRDLL" /D "PLATFORM_EXPORTS" /YX /FD /GZ /c -# ADD CPP /nologo /MDd /W3 /Gm /Gi /GX /ZI /Od /I "./" /I "../common" /I "../game_shared" /D "WIN32" /D "_DEBUG" /D "_WINDOWS" /FR /FD /GZ /c +# ADD CPP /nologo /MDd /W3 /Gm /Gi- /GX /ZI /Od /I "./" /I "../common" /I "../game_shared" /D "WIN32" /D "_DEBUG" /D "_WINDOWS" /FR /FD /GZ /c # SUBTRACT CPP /YX # ADD BASE MTL /nologo /D "_DEBUG" /mktyplib203 /win32 # ADD MTL /nologo /D "_DEBUG" /mktyplib203 /win32 diff --git a/gameui/menu_controls.cpp b/gameui/menu_controls.cpp index 105cf967..d52be8f8 100644 --- a/gameui/menu_controls.cpp +++ b/gameui/menu_controls.cpp @@ -135,7 +135,7 @@ static void UI_Controls_ParseKeysList( void ) for( ; i < MAX_KEYS; i++ ) uiControls.keysDescriptionPtr[i] = NULL; uiControls.keysList.itemNames = (const char **)uiControls.keysDescriptionPtr; - ALERT( at_error, "UI_Parse_KeysList: kb_act.lst not found\n" ); + Con_Printf( "UI_Parse_KeysList: kb_act.lst not found\n" ); return; } @@ -242,7 +242,7 @@ static void UI_Controls_ResetKeysList( void ) if( !afile ) { - ALERT( at_error, "UI_Parse_KeysList: kb_act.lst not found\n" ); + Con_Printf( "UI_Parse_KeysList: kb_act.lst not found\n" ); return; } diff --git a/gameui/menu_creategame.cpp b/gameui/menu_creategame.cpp index 4800f0dc..f055bf4e 100644 --- a/gameui/menu_creategame.cpp +++ b/gameui/menu_creategame.cpp @@ -184,7 +184,7 @@ static void UI_CreateGame_GetMapsList( void ) { uiCreateGame.done.generic.flags |= QMF_GRAYED; uiCreateGame.mapsList.itemNames = (const char **)uiCreateGame.mapsDescriptionPtr; - ALERT( at_error, "Cmd_GetMapsList: can't open maps.lst\n" ); + Con_Printf( "Cmd_GetMapsList: can't open maps.lst\n" ); return; } diff --git a/gameui/menu_playersetup.cpp b/gameui/menu_playersetup.cpp index 7c57ec5e..9a061bb7 100644 --- a/gameui/menu_playersetup.cpp +++ b/gameui/menu_playersetup.cpp @@ -24,6 +24,7 @@ Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. #include "keydefs.h" #include "ref_params.h" #include "cl_entity.h" +#include "entity_types.h" #define ART_BANNER "gfx/shell/head_customize" @@ -250,7 +251,7 @@ static void UI_PlayerSetup_Ownerdraw( void *self ) uiPlayerSetup.refdef.frametime = gpGlobals->frametime; // draw the player model - R_AddEntity( uiPlayerSetup.ent, ED_NORMAL, -1 ); + R_AddEntity( uiPlayerSetup.ent, ET_NORMAL, -1 ); R_RenderFrame( &uiPlayerSetup.refdef ); } @@ -393,10 +394,7 @@ static void UI_PlayerSetup_Init( void ) uiPlayerSetup.ent = GET_MENU_EDICT (); if( !uiPlayerSetup.ent ) - { - ALERT( at_error, "Unable to find player model edict\n" ); return; - } // adjust entity params uiPlayerSetup.ent->curstate.animtime = gpGlobals->time; // start animation diff --git a/gameui/utils.h b/gameui/utils.h index 57973093..5eeb2790 100644 --- a/gameui/utils.h +++ b/gameui/utils.h @@ -16,6 +16,9 @@ extern ui_enginefuncs_t g_engfuncs; #define MAX_INFO_STRING 512 // engine limit +#define RAD2DEG( x ) ((float)(x) * (float)(180.f / M_PI)) +#define DEG2RAD( x ) ((float)(x) * (float)(M_PI / 180.f)) + // // How did I ever live without ASSERT? // diff --git a/launch/cpuinfo.c b/launch/cpuinfo.c index df3b5dd3..4ed04971 100644 --- a/launch/cpuinfo.c +++ b/launch/cpuinfo.c @@ -6,6 +6,8 @@ #include "const.h" #include "launch.h" +typedef signed __int64 int64; + // Processor Information: typedef struct cpuinfo_s { diff --git a/launch/crclib.c b/launch/crclib.c index da8ae17a..a8b813d2 100644 --- a/launch/crclib.c +++ b/launch/crclib.c @@ -18,7 +18,7 @@ CCITT standard CRC used by XMODEM #define CRC32_INIT_VALUE 0xFFFFFFFFUL #define CRC32_XOR_VALUE 0xFFFFFFFFUL -static const CRC16_t crctable[NUM_BYTES] = +static const word crctable[NUM_BYTES] = { 0x0000, 0x1021, 0x2042, 0x3063, 0x4084, 0x50a5, 0x60c6, 0x70e7, 0x8108, 0x9129, 0xa14a, 0xb16b, 0xc18c, 0xd1ad, 0xe1ce, 0xf1ef, @@ -54,7 +54,7 @@ static const CRC16_t crctable[NUM_BYTES] = 0x6e17, 0x7e36, 0x4e55, 0x5e74, 0x2e93, 0x3eb2, 0x0ed1, 0x1ef0 }; -static const CRC32_t crc32table[NUM_BYTES] = +static const dword crc32table[NUM_BYTES] = { 0x00000000, 0x77073096, 0xee0e612c, 0x990951ba, 0x076dc419, 0x706af48f, 0xe963a535, 0x9e6495a3, @@ -190,19 +190,19 @@ static byte chktbl[1024] = 0x39, 0x4f, 0xdd, 0xe4, 0xb6, 0x19, 0x27, 0xfb, 0xb8, 0xf5, 0x32, 0x73, 0xe5, 0xcb, 0x32 }; -void CRC_Init( CRC16_t *crcvalue ) +void CRC_Init( word *crcvalue ) { *crcvalue = CRC_INIT_VALUE; } -void CRC_ProcessByte( CRC16_t *crcvalue, byte data ) +void CRC_ProcessByte( word *crcvalue, byte data ) { *crcvalue = (*crcvalue << 8) ^ crctable[(*crcvalue >> 8) ^ data]; } -CRC16_t CRC_Block( byte *start, int count ) +word CRC_Block( byte *start, int count ) { - CRC16_t crc; + word crc; CRC_Init (&crc); while(count--) CRC_ProcessByte( &crc, *start++ ); @@ -220,7 +220,7 @@ For proxy protecting byte CRC_BlockSequence(byte *base, int length, int sequence) { int n, x; - CRC16_t crc; + word crc; byte *p, chkb[60 + 4]; if (sequence < 0) sequence = abs(sequence); @@ -242,28 +242,28 @@ byte CRC_BlockSequence(byte *base, int length, int sequence) return crc; } -void CRC32_Init( CRC32_t *pulCRC ) +void CRC32_Init( dword *pulCRC ) { *pulCRC = CRC32_INIT_VALUE; } -void CRC32_Final( CRC32_t *pulCRC ) +void CRC32_Final( dword *pulCRC ) { *pulCRC ^= CRC32_XOR_VALUE; } -void CRC32_ProcessByte( CRC32_t *pulCRC, byte ch ) +void CRC32_ProcessByte( dword *pulCRC, byte ch ) { - CRC32_t ulCrc = *pulCRC; + dword ulCrc = *pulCRC; ulCrc ^= ch; ulCrc = crc32table[(byte)ulCrc] ^ (ulCrc >> 8); *pulCRC = ulCrc; } -void CRC32_ProcessBuffer( CRC32_t *pulCRC, const void *pBuffer, int nBuffer ) +void CRC32_ProcessBuffer( dword *pulCRC, const void *pBuffer, int nBuffer ) { - CRC32_t ulCrc = *pulCRC; + dword ulCrc = *pulCRC; byte *pb = (byte *)pBuffer; uint nFront; int nMain; @@ -275,7 +275,7 @@ JustAfew: case 5: ulCrc = crc32table[*pb++ ^ (byte)ulCrc] ^ (ulCrc >> 8); case 4: - ulCrc ^= *(CRC32_t *)pb; // warning, this only works on little-endian. + ulCrc ^= *(dword *)pb; // warning, this only works on little-endian. ulCrc = crc32table[(byte)ulCrc] ^ (ulCrc >> 8); ulCrc = crc32table[(byte)ulCrc] ^ (ulCrc >> 8); ulCrc = crc32table[(byte)ulCrc] ^ (ulCrc >> 8); @@ -307,12 +307,12 @@ JustAfew: nMain = nBuffer >> 3; while( nMain-- ) { - ulCrc ^= *(CRC32_t *)pb; // warning, this only works on little-endian. + ulCrc ^= *(dword *)pb; // warning, this only works on little-endian. ulCrc = crc32table[(byte)ulCrc] ^ (ulCrc >> 8); ulCrc = crc32table[(byte)ulCrc] ^ (ulCrc >> 8); ulCrc = crc32table[(byte)ulCrc] ^ (ulCrc >> 8); ulCrc = crc32table[(byte)ulCrc] ^ (ulCrc >> 8); - ulCrc ^= *(CRC32_t *)(pb + 4);// warning, this only works on little-endian. + ulCrc ^= *(dword *)(pb + 4);// warning, this only works on little-endian. ulCrc = crc32table[(byte)ulCrc] ^ (ulCrc >> 8); ulCrc = crc32table[(byte)ulCrc] ^ (ulCrc >> 8); ulCrc = crc32table[(byte)ulCrc] ^ (ulCrc >> 8); diff --git a/launch/cvar.c b/launch/cvar.c index 6c358387..c9aeffec 100644 --- a/launch/cvar.c +++ b/launch/cvar.c @@ -9,7 +9,6 @@ #define FILE_HASH_SIZE 256 int cvar_numIndexes; -int cvar_modifiedFlags; cvar_t cvar_indexes[MAX_CVARS]; static cvar_t *hashTable[FILE_HASH_SIZE]; cvar_t *cvar_vars; @@ -185,7 +184,6 @@ cvar_t *Cvar_Get( const char *var_name, const char *var_value, int flags, const var->flags &= ~CVAR_USER_CREATED; Mem_Free( var->reset_string ); var->reset_string = copystring( var_value ); - cvar_modifiedFlags |= flags; } var->flags |= flags; @@ -253,6 +251,106 @@ cvar_t *Cvar_Get( const char *var_name, const char *var_value, int flags, const return var; } +/* +============ +Cvar_DirectSet +============ +*/ +void Cvar_DirectSet( cvar_t *var, const char *value ) +{ + cvar_t *test; + const char *pszValue; + char szNew[MAX_SYSPATH]; + + if( !var ) return; // GET_CVAR_POINTER is failed ? + + // make sure what is really pointer to the cvar + test = Cvar_FindVar( var->name ); + ASSERT( var == test ); + + if( value && !Cvar_ValidateString( value, true )) + { + MsgDev( D_WARN, "invalid cvar value string: %s\n", value ); + value = "default"; + } + + if( !value ) value = var->reset_string; + + if( var->flags & ( CVAR_READ_ONLY|CVAR_INIT|CVAR_RENDERINFO|CVAR_LATCH|CVAR_LATCH_VIDEO|CVAR_LATCH_AUDIO )) + { + // Cvar_DirectSet cannot change these cvars at all + return; + } + + if(( var->flags & CVAR_CHEAT ) && !Cvar_VariableInteger( "sv_cheats" )) + { + // cheats are disabled + return; + } + + pszValue = value; + + // This cvar's string must only contain printable characters. + // Strip out any other crap. + // We'll fill in "empty" if nothing is left + if( var->flags & CVAR_PRINTABLEONLY ) + { + const char *pS; + char *pD; + + // clear out new string + szNew[0] = '\0'; + + pS = pszValue; + pD = szNew; + + // step through the string, only copying back in characters that are printable + while( *pS ) + { + if( *pS < 32 || *pS > 255 ) + { + pS++; + continue; + } + *pD++ = *pS++; + } + + // terminate the new string + *pD = '\0'; + + // if it's empty, then insert a marker string + if( !com.strlen( szNew )) + { + com.strcpy( szNew, "default" ); + } + + // point the value here. + pszValue = szNew; + } + + if( !com.strcmp( pszValue, var->string )) + return; + + // pass all tests + var->modified = true; + var->modificationCount++; + + if( var->flags & CVAR_USERINFO ) + userinfo->modified = true; // transmit at next oportunity + + if( var->flags & CVAR_PHYSICINFO ) + physinfo->modified = true; // transmit at next oportunity + + if( var->flags & CVAR_SERVERINFO ) + serverinfo->modified = true; // transmit at next oportunity + + // free the old value string + Mem_Free( var->string ); + var->string = copystring( pszValue ); + var->value = com.atof( var->string ); + var->integer = com.atoi( var->string ); +} + /* ============ Cvar_Set2 @@ -260,15 +358,17 @@ Cvar_Set2 */ cvar_t *Cvar_Set2( const char *var_name, const char *value, bool force ) { - cvar_t *var; - + cvar_t *var; + const char *pszValue; + char szNew[MAX_SYSPATH]; + if( !Cvar_ValidateString( var_name, false )) { MsgDev( D_WARN, "invalid cvar name string: %s\n", var_name ); var_name = "unknown"; } - if ( value && !Cvar_ValidateString( value, true )) + if( value && !Cvar_ValidateString( value, true )) { MsgDev( D_WARN, "invalid cvar value string: %s\n", value ); value = "default"; @@ -287,9 +387,6 @@ cvar_t *Cvar_Set2( const char *var_name, const char *value, bool force ) if( !value ) value = var->reset_string; if( !com.strcmp( value, var->string )) return var; - // note what types of cvars have been modified (userinfo, archive, serverinfo, renderinfo) - cvar_modifiedFlags |= var->flags; - if( !force ) { if( var->flags & CVAR_READ_ONLY ) @@ -366,8 +463,48 @@ cvar_t *Cvar_Set2( const char *var_name, const char *value, bool force ) } } + pszValue = value; + + // This cvar's string must only contain printable characters. + // Strip out any other crap. + // We'll fill in "empty" if nothing is left + if( var->flags & CVAR_PRINTABLEONLY ) + { + const char *pS; + char *pD; + + // clear out new string + szNew[0] = '\0'; + + pS = pszValue; + pD = szNew; + + // step through the string, only copying back in characters that are printable + while( *pS ) + { + if( *pS < 32 || *pS > 255 ) + { + pS++; + continue; + } + *pD++ = *pS++; + } + + // terminate the new string + *pD = '\0'; + + // if it's empty, then insert a marker string + if( !com.strlen( szNew )) + { + com.strcpy( szNew, "default" ); + } + + // point the value here. + pszValue = szNew; + } + // nothing to change - if( !com.strcmp( value, var->string )) + if( !com.strcmp( pszValue, var->string )) return var; var->modified = true; @@ -381,10 +518,11 @@ cvar_t *Cvar_Set2( const char *var_name, const char *value, bool force ) if( var->flags & CVAR_SERVERINFO ) serverinfo->modified = true; // transmit at next oportunity + // free the old value string Mem_Free( var->string ); - var->string = copystring( value ); + var->string = copystring( pszValue ); var->value = com.atof( var->string ); var->integer = com.atoi( var->string ); @@ -924,7 +1062,7 @@ Reads in all archived cvars void Cvar_Init( void ) { cvar_vars = NULL; - cvar_numIndexes = cvar_modifiedFlags = 0; + cvar_numIndexes = 0; ZeroMemory( cvar_indexes, sizeof( cvar_t ) * MAX_CVARS ); ZeroMemory( hashTable, sizeof( *hashTable ) * FILE_HASH_SIZE ); userinfo = Cvar_Get( "@userinfo", "0", CVAR_READ_ONLY, "" ); // use ->modified value only diff --git a/launch/filesystem.c b/launch/filesystem.c index 93ffcf55..aaf1453c 100644 --- a/launch/filesystem.c +++ b/launch/filesystem.c @@ -1437,7 +1437,7 @@ static bool FS_WriteGameInfo( const char *filepath, gameinfo_t *GameInfo ) if( GameInfo->sp_inhibite_ents ) FS_Print( f, "allow_inhibited_entities\n" ); - for( i = 0; i < 8; i++ ) + for( i = 0; i < 4; i++ ) { float *min, *max; @@ -1449,7 +1449,7 @@ static bool FS_WriteGameInfo( const char *filepath, gameinfo_t *GameInfo ) FS_Printf( f, "hull%i\t\t( %g %g %g ) ( %g %g %g )\n", i, min[0], min[1], min[2], max[0], max[1], max[2] ); } - for( i = 0; i < 8; i++ ) + for( i = 0; i < 4; i++ ) { if( GameInfo->viewheight[i] == 0.0f ) continue; FS_Printf( f, "viewheight%i\t%g\n", i, GameInfo->viewheight[i] ); @@ -1795,7 +1795,7 @@ static bool FS_ParseGameInfo( const char *gamedir, gameinfo_t *GameInfo ) { int hullNum = com.atoi( token.string + 4 ); - if( hullNum < 0 || hullNum > 15 ) + if( hullNum < 0 || hullNum > 3 ) { MsgDev( D_ERROR, "FS_ParseGameInfo: Invalid hull number %i. Ignored.\n", hullNum ); PS_SkipRestOfLine( script ); @@ -1811,7 +1811,7 @@ static bool FS_ParseGameInfo( const char *gamedir, gameinfo_t *GameInfo ) int hullNum = com.atoi( token.string + 10 ); float value; - if( hullNum < 0 || hullNum > ( PM_MAXHULLS - 1 )) + if( hullNum < 0 || hullNum > 3 ) { MsgDev( D_ERROR, "FS_ParseGameInfo: Invalid hull number %i. Ignored.\n", hullNum ); PS_SkipRestOfLine( script ); diff --git a/launch/launch.h b/launch/launch.h index 194371a0..0038decd 100644 --- a/launch/launch.h +++ b/launch/launch.h @@ -88,11 +88,13 @@ typedef struct system_s void (*CmdAuto)( char *complete_string ); } system_t; +// NOTE: if this is changed, it must be changed in cvardef.h too typedef struct cvar_s { // shared part char *name; char *string; // normal string + uint flags; // state flags float value; // atof( string ) int integer; // atoi( string ) bool modified; // set each time the cvar is changed @@ -101,7 +103,6 @@ typedef struct cvar_s char *reset_string; // cvar_restart will reset to this value char *latched_string; // for CVAR_LATCH vars char *description; // variable descrition info - uint flags; // state flags uint modificationCount; // incremented each time the cvar is changed struct cvar_s *next; @@ -395,6 +396,7 @@ void Cvar_SetValue( const char *var_name, float value ); float Cvar_VariableValue( const char *var_name ); int Cvar_VariableInteger( const char *var_name ); char *Cvar_VariableString( const char *var_name ); +void Cvar_DirectSet( cvar_t *var, const char *value ); bool Cvar_Command( void ); void Cvar_WriteVariables( file_t *f ); void Cvar_Init( void ); @@ -449,16 +451,16 @@ bool VFS_Eof( vfile_t *file ); // // crclib.c // -void CRC_Init( CRC16_t *crcvalue ); -CRC16_t CRC_Block( byte *start, int count ); -void CRC_ProcessByte( CRC16_t *crcvalue, byte data ); +void CRC_Init( word *crcvalue ); +word CRC_Block( byte *start, int count ); +void CRC_ProcessByte( word *crcvalue, byte data ); byte CRC_BlockSequence( byte *base, int length, int sequence ); uint Com_BlockChecksum( void *buffer, int length ); uint Com_BlockChecksumKey( void *buffer, int length, int key ); -void CRC32_ProcessBuffer( CRC32_t *pulCRC, const void *pBuffer, int nBuffer ); -void CRC32_ProcessByte( CRC32_t *pulCRC, byte ch ); -void CRC32_Init( CRC32_t *pulCRC ); -void CRC32_Final( CRC32_t *pulCRC ); +void CRC32_ProcessBuffer( dword *pulCRC, const void *pBuffer, int nBuffer ); +void CRC32_ProcessByte( dword *pulCRC, byte ch ); +void CRC32_Init( dword *pulCRC ); +void CRC32_Final( dword *pulCRC ); // // parselib.c diff --git a/launch/system.c b/launch/system.c index a0e19269..e0dc7f5e 100644 --- a/launch/system.c +++ b/launch/system.c @@ -153,6 +153,7 @@ void Sys_GetStdAPI( void ) com.Cvar_GetString = Cvar_VariableString; com.Cvar_LookupVars = Cvar_LookupVars; com.Cvar_FindVar = Cvar_FindVar; + com.Cvar_DirectSet = Cvar_DirectSet; // console commands com.Cmd_Exec = Cbuf_ExecuteText; // process cmd buffer diff --git a/public/engine_api.h b/public/engine_api.h index cfc66139..6ca67868 100644 --- a/public/engine_api.h +++ b/public/engine_api.h @@ -19,11 +19,20 @@ #define MAX_EVENTS 1024 // playback events that can be queued (a byte range, don't touch) #define MAX_MSGLEN 16384 // max length of network message #define MAX_GENERICS 1024 // generic files that can download from server -#define MAX_CLASSNAMES 512 // maxcount of various edicts classnames #define MAX_SOUNDS 2048 // max unique loaded sounds (not counting sequences) #define MAX_MODELS 2048 // total count of brush & studio various models per one map #define MAX_EDICTS 32768 // absolute limit that never be reached, (do not edit!) +// decal flags +#define FDECAL_PERMANENT 0x01 // This decal should not be removed in favor of any new decals +#define FDECAL_CUSTOM 0x02 // This is a custom clan logo and should not be saved/restored +#define FDECAL_DYNAMIC 0x04 // Indicates the decal is dynamic +#define FDECAL_DONTSAVE 0x08 // Decal was loaded from adjacent level, don't save it for this level +#define FDECAL_CLIPTEST 0x10 // Decal needs to be clip-tested +#define FDECAL_NOCLIP 0x20 // Decal is not clipped by containing polygon +#define FDECAL_USESAXIS 0x40 // Uses the s axis field to determine orientation (footprints) +#define FDECAL_ANIMATED 0x80 // this is decal has multiple frames + // world size #define MAX_COORD_INTEGER (16384) // world half-size, modify with precaution #define MIN_COORD_INTEGER (-MAX_COORD_INTEGER) diff --git a/public/launch_api.h b/public/launch_api.h index 410c12da..26866fac 100644 --- a/public/launch_api.h +++ b/public/launch_api.h @@ -18,14 +18,25 @@ #define MAX_MODS 128 // environment games that engine can keep visible #define MAP_DEFAULT_SHADER "*black" // engine built-in default shader #define MAX_STRING_TABLES 8 // seperately stringsystems +#define DLLEXPORT __declspec( dllexport ) +#define BIT( n ) (1<<( n )) #define trace_t TraceResult +#define NULL ((void *)0) + +// color strings +#define ColorIndex( c ) ((( c ) - '0' ) & 7 ) +#define IsColorString( p ) ( p && *( p ) == '^' && *(( p ) + 1) && *(( p ) + 1) >= '0' && *(( p ) + 1 ) <= '9' ) #ifndef __cplusplus #define bool BOOL // sizeof( int ) #endif -#include "basetypes.h" +#include "const.h" +typedef int BOOL; +typedef int func_t; +typedef int sound_t; +typedef int shader_t; typedef vec_t vec2_t[2]; typedef vec_t vec3_t[3]; typedef vec_t vec4_t[4]; @@ -74,7 +85,6 @@ typedef struct { int numfilenames; char **filenames; char *filenamesbuffer; } se typedef void ( *cmsave_t )( void* handle, const void* buffer, size_t size ); typedef void ( *cmdraw_t )( int color, int numpoints, const float *points ); typedef void ( *setpair_t )( const char *key, const char *value, void *buffer, void *numpairs ); -typedef enum { NA_LOOPBACK, NA_BROADCAST, NA_IP } netadrtype_t; typedef enum { NS_CLIENT, NS_SERVER } netsrc_t; typedef void ( *xcommand_t )( void ); @@ -112,14 +122,10 @@ typedef enum CVAR_USER_CREATED = BIT(9), // created by a set command (dll's used) CVAR_LATCH_VIDEO = BIT(10),// save changes until render restart CVAR_LATCH_AUDIO = BIT(11),// save changes until vsound restart + CVAR_PRINTABLEONLY = BIT(12),// this cvar's string cannot contain unprintable characters ( player name ) } cvar_flags_t; -typedef struct netadr_s -{ - netadrtype_t type; - byte ip[4]; - word port; -}; +#include "netadr.h" /* ======================================================================== @@ -232,9 +238,9 @@ typedef struct gameinfo_s string ctf_entity; // e.g. info_player_ctf string team_entity; // e.g. info_player_team - vec3_t client_mins[8]; // PM_MAXHULLS - vec3_t client_maxs[8]; // PM_MAXHULLS - float viewheight[8]; // client viewheight (from hull center) + vec3_t client_mins[4]; // 4 hulls allowed + vec3_t client_maxs[4]; // 4 hulls allowed + float viewheight[4]; // client viewheight (from hull center) int max_edicts; // min edicts is 600, max edicts is 32000 } gameinfo_t; @@ -480,16 +486,16 @@ typedef struct stdilib_api_s sys_event_t (*getevent)( void ); // get system events // crclib.c funcs - void (*crc_init)( CRC16_t *pusCRC ); // set initial crc value - CRC16_t (*crc_block)( byte *start, int count ); // calculate crc block - void (*crc_process)( CRC16_t *crcvalue, byte data ); // process crc byte + void (*crc_init)( word *pusCRC ); // set initial crc value + word (*crc_block)( byte *start, int count ); // calculate crc block + void (*crc_process)( word *crcvalue, byte data ); // process crc byte byte (*crc_sequence)( byte *base, int len, int sequence ); // calculate crc for sequence uint (*crc_blockchecksum)( void *buffer, int length ); // map checksum uint (*crc_blockchecksumkey)( void *buf, int len, int key );// process key checksum - void (*crc32_init)( CRC32_t *pulCRC ); - void (*crc32_process)( CRC32_t *crcvalue, byte data ); // process crc32 byte - void (*crc32_block)( CRC32_t *pulCRC, const void *pBuffer, int nBuffer ); - void (*crc32_final)( CRC32_t *pulCRC ); + void (*crc32_init)( dword *pulCRC ); + void (*crc32_process)( dword *crcvalue, byte data ); // process crc32 byte + void (*crc32_block)( dword *pulCRC, const void *pBuffer, int nBuffer ); + void (*crc32_final)( dword *pulCRC ); // memlib.c funcs void (*memcpy)(void *dest, const void *src, size_t size, const char *file, int line); @@ -587,6 +593,7 @@ typedef struct stdilib_api_s float (*Cvar_GetValue )(const char *name); char *(*Cvar_GetString)(const char *name); cvar_t *(*Cvar_FindVar)(const char *name); + void (*Cvar_DirectSet)( cvar_t *var, const char *value ); // console commands void (*Cmd_Exec)(int exec_when, const char *text); // process cmd buffer @@ -877,6 +884,7 @@ console variables #define Cvar_VariableInteger com.Cvar_GetInteger #define Cvar_VariableString com.Cvar_GetString #define Cvar_FindVar com.Cvar_FindVar +#define Cvar_DirectSet com.Cvar_DirectSet /* =========================================== diff --git a/public/mathlib.h b/public/mathlib.h index 00b94c5c..7c0255a1 100644 --- a/public/mathlib.h +++ b/public/mathlib.h @@ -20,6 +20,9 @@ #define M_PI2 (float)6.28318530717958647692 #endif +#define RAD2DEG( x ) ((float)(x) * (float)(180.f / M_PI)) +#define DEG2RAD( x ) ((float)(x) * (float)(M_PI / 180.f)) + #define SIDE_FRONT 0 #define SIDE_BACK 1 #define SIDE_ON 2 diff --git a/public/physic_api.h b/public/physic_api.h deleted file mode 100644 index 19b70952..00000000 --- a/public/physic_api.h +++ /dev/null @@ -1,79 +0,0 @@ -//======================================================================= -// Copyright XashXT Group 2008 © -// physic_api.h - xash physic library api -//======================================================================= -#ifndef PHYSIC_API_H -#define PHYSIC_API_H - -#include "trace_def.h" - -#define FMOVE_IGNORE_GLASS 0x100 -#define FMOVE_SIMPLEBOX 0x200 - -/* -============================================================================== - -PHYSIC.DLL INTERFACE -============================================================================== -*/ -typedef struct physic_exp_s -{ - // interface validator - size_t api_size; // must matched with sizeof(physic_exp_t) - size_t com_size; // must matched with sizeof(stdlib_api_t) - - // initialize - bool (*Init)( void ); // init all physic systems - void (*Shutdown)( void ); // shutdown all render systems - - void (*DrawCollision)( cmdraw_t callback ); // debug draw world - void (*Frame)( float time ); // physics frame - - // models loading - void (*BeginRegistration)( const char *name, bool clientload, uint *checksum ); - bool (*RegisterModel)( const char *name, int sv_index ); // also build replacement index table - void (*EndRegistration)( void ); - - // testing in leaf - int (*BoxLeafnums)( vec3_t mins, vec3_t maxs, short *list, int listsize, int *topNode ); - bool (*BoxVisible)( const vec3_t mins, const vec3_t maxs, byte *visbits ); - bool (*HeadnodeVisible)( int nodenum, byte *visbits ); - void (*AmbientLevels)( const vec3_t p, byte *pvolumes ); - int (*PointLeafnum)( const vec3_t p ); - byte *(*LeafPVS)( int leafnum ); - byte *(*LeafPHS)( int leafnum ); - byte *(*FatPVS)( const vec3_t org, bool portal ); - byte *(*FatPHS)( const vec3_t org, bool portal ); - - // map info - int (*NumBmodels)( void ); - script_t *(*GetEntityScript)( void ); - - // models info - modtype_t (*Mod_GetType)( model_t handle ); - void *(*Mod_Extradata)( model_t handle ); - void (*Mod_GetFrames)( model_t handle, int *numFrames ); - void (*Mod_GetBounds)( model_t handle, vec3_t mins, vec3_t maxs ); - void (*Mod_GetAttachment)( edict_t *ent, int iAttachment, float *rgflOrigin, float *rgflAngles ); - void (*Mod_GetBonePos)( edict_t *ent, int iBone, float *rgflOrigin, float *rgflAngles ); - - // lighting info - void (*AddLightstyle)( int style, const char* val ); - int (*LightPoint)( edict_t *pEdict ); // for GETENTITYILLUM - - // tracing - int (*PointContents)( const vec3_t p ); - int (*HullPointContents)( chull_t *hull, int num, const vec3_t p ); - trace_t (*Trace)( edict_t *ent, const vec3_t start, vec3_t mins, vec3_t maxs, const vec3_t end, int flags ); - const char *(*TraceTexture)( edict_t *pTextureEntity, const vec3_t v1, const vec3_t v2 ); - chull_t *(*HullForBsp)( edict_t *ent, const vec3_t mins, const vec3_t maxs, float *offset ); -} physic_exp_t; - -typedef struct physic_imp_s -{ - // interface validator - size_t api_size; // must matched with sizeof(physic_imp_t) - -} physic_imp_t; - -#endif//PHYSIC_API_H \ No newline at end of file diff --git a/public/render_api.h b/public/render_api.h index 2f018638..eb7da8cc 100644 --- a/public/render_api.h +++ b/public/render_api.h @@ -6,7 +6,6 @@ #define RENDER_API_H #include "ref_params.h" -#include "trace_def.h" // shader types used for shader loading #define SHADER_SKY 1 // sky box shader @@ -109,7 +108,7 @@ typedef struct render_exp_s void (*FreeShader)( const char *shadername ); // prepare frame to rendering - bool (*AddRefEntity)( struct cl_entity_s *pRefEntity, int ed_type, shader_t customShader ); + bool (*AddRefEntity)( struct cl_entity_s *pRefEntity, int entityType, shader_t customShader ); bool (*DecalShoot)( shader_t decal, int ent, int model, vec3_t pos, vec3_t saxis, int flags, rgba_t color, float fadeTime, float fadeDuration ); bool (*AddDLight)( vec3_t pos, rgb_t color, float radius, int flags ); bool (*AddPolygon)( const poly_t *poly ); diff --git a/public/vsound_api.h b/public/vsound_api.h index 072979ca..eb5b4123 100644 --- a/public/vsound_api.h +++ b/public/vsound_api.h @@ -7,6 +7,17 @@ typedef int sound_t; +// sound flags +#define SND_VOLUME (1<<0) // a scaled byte +#define SND_ATTENUATION (1<<1) // a byte +#define SND_PITCH (1<<2) // a byte +#define SND_FIXED_ORIGIN (1<<3) // a vector +#define SND_SENTENCE (1<<4) // set if sound num is actually a sentence num +#define SND_STOP (1<<5) // stop the sound +#define SND_CHANGE_VOL (1<<6) // change sound vol +#define SND_CHANGE_PITCH (1<<7) // change sound pitch +#define SND_SPAWNING (1<<8) // we're spawning, used in some cases for ambients + /* ============================================================================== diff --git a/release.bat b/release.bat index 26e83fdb..6ab908b8 100644 --- a/release.bat +++ b/release.bat @@ -8,7 +8,7 @@ set build_type=release set BUILD_ERROR= call vcvars32 -%MSDEV% bshift/bshift.dsp %CONFIG%"bshift - Win32 Release" %build_target% +%MSDEV% dlls/hl.dsp %CONFIG%"hl - Win32 Release" %build_target% if errorlevel 1 set BUILD_ERROR=1 %MSDEV% client/client.dsp %CONFIG%"client - Win32 Release" %build_target% diff --git a/snd_dx/snd_dx.dsp b/snd_dx/snd_dx.dsp index 937ea1a1..1bce253d 100644 --- a/snd_dx/snd_dx.dsp +++ b/snd_dx/snd_dx.dsp @@ -43,7 +43,7 @@ RSC=rc.exe # PROP Ignore_Export_Lib 1 # PROP Target_Dir "" # ADD BASE CPP /nologo /MT /W3 /GX /O2 /D "WIN32" /D "NDEBUG" /D "_WINDOWS" /D "_MBCS" /D "_USRDLL" /D "PLATFORM_EXPORTS" /YX /FD /c -# ADD CPP /nologo /MD /W3 /GX /O2 /I "./" /I "../public" /I "../common" /D "WIN32" /D "NDEBUG" /D "_WINDOWS" /FD /c +# ADD CPP /nologo /MD /W3 /GX /O2 /I "./" /I "../public" /I "../common" /I "../game_shared" /D "WIN32" /D "NDEBUG" /D "_WINDOWS" /FD /c # SUBTRACT CPP /YX # ADD BASE MTL /nologo /D "NDEBUG" /mktyplib203 /win32 # ADD MTL /nologo /D "NDEBUG" /mktyplib203 /win32 @@ -80,7 +80,7 @@ SOURCE="$(InputPath)" # PROP Ignore_Export_Lib 1 # PROP Target_Dir "" # ADD BASE CPP /nologo /MTd /W3 /Gm /GX /ZI /Od /D "WIN32" /D "_DEBUG" /D "_WINDOWS" /D "_MBCS" /D "_USRDLL" /D "PLATFORM_EXPORTS" /YX /FD /GZ /c -# ADD CPP /nologo /MDd /W3 /Gm /Gi /GX /ZI /Od /I "./" /I "../public" /I "../common" /D "WIN32" /D "_DEBUG" /D "_WINDOWS" /FR /FD /GZ /c +# ADD CPP /nologo /MDd /W3 /Gm /Gi /GX /ZI /Od /I "./" /I "../public" /I "../common" /I "../game_shared" /D "WIN32" /D "_DEBUG" /D "_WINDOWS" /FR /FD /GZ /c # SUBTRACT CPP /YX # ADD BASE MTL /nologo /D "_DEBUG" /mktyplib203 /win32 # ADD MTL /nologo /D "_DEBUG" /mktyplib203 /win32 diff --git a/snd_dx/sound.h b/snd_dx/sound.h index 57f31cf9..63765340 100644 --- a/snd_dx/sound.h +++ b/snd_dx/sound.h @@ -9,7 +9,7 @@ #include #include "launch_api.h" #include "qfiles_ref.h" -#include "engine_api.h" // trace_t declaration +#include "engine_api.h" #include "vsound_api.h" #include "cl_entity.h" @@ -19,6 +19,10 @@ extern byte *sndpool; #include "mathlib.h" +// local flags (never sending acorss the net) +#define SND_LOCALSOUND (1<<9) // not paused, not looped, for internal use +#define SND_STOP_LOOPING (1<<10) // stop all looping sounds on the entity. + typedef struct { int left; diff --git a/spirit/alias.cpp b/spirit/alias.cpp deleted file mode 100644 index ff76ba44..00000000 --- a/spirit/alias.cpp +++ /dev/null @@ -1,477 +0,0 @@ -/*** -* -* NEW file for the Mod "Spirit of Half-Life", by Laurie R. Cheers. (LRC) -* Created 19/11/00 -* Modified by Andrew J Hamilton (AJH) 2/04/04 -* -***/ -/* - -===== alias.cpp ======================================================== - -Alias entities, replace the less powerful and (IMHO) less intuitive -trigger_changetarget entity. - -*/ - -#include "extdll.h" -#include "util.h" -#include "cbase.h" - -TYPEDESCRIPTION CBaseAlias::m_SaveData[] = -{ - DEFINE_FIELD( CBaseAlias, m_pNextAlias, FIELD_CLASSPTR ), -}; -IMPLEMENT_SAVERESTORE( CBaseAlias, CPointEntity ); - - -/********************* -* Worldcraft entity: info_alias -* -* targetname- alias name -* target- alias destination while ON -* netname- alias destination while OFF -* mode- 0= On/Off mode, 1= 'list' mode -**********************/ - -#define SF_ALIAS_OFF 1 -#define SF_ALIAS_DEBUG 2 -#define MAX_ALIAS_TARGETS 16 //AJH - -class CInfoAlias : public CBaseAlias //AJH Now includes 'listmode' aliasing -{ -public: - int m_cTargets; //AJH the total number of targets in this alias's fire list. - int m_iTargetName [ MAX_ALIAS_TARGETS ];// AJH list of indexes into global string array - int m_iMode; //AJH 0 = On/Off mode, 1 = list mode - int m_iCurrentTarget; //AJH the current target that is being aliased - - void Use(CBaseEntity* pActivator, CBaseEntity* pCaller, USE_TYPE useType, float value); - void Spawn( void ); - STATE GetState() { return (pev->spawnflags & SF_ALIAS_OFF)?STATE_OFF:STATE_ON; } - - CBaseEntity *FollowAlias( CBaseEntity *pFrom ); - void ChangeValue( int iszValue ); - void FlushChanges( void ); - void KeyValue(struct KeyValueData_s *); //AJH - - virtual int Save( CSave &save ); - virtual int Restore( CRestore &restore ); - static TYPEDESCRIPTION m_SaveData[]; -}; - -LINK_ENTITY_TO_CLASS( info_alias, CInfoAlias ); - -TYPEDESCRIPTION CInfoAlias::m_SaveData[] = //AJH -{ - DEFINE_FIELD( CInfoAlias, m_cTargets, FIELD_INTEGER ), - DEFINE_FIELD( CInfoAlias, m_iMode, FIELD_INTEGER ), - DEFINE_FIELD( CInfoAlias, m_iCurrentTarget, FIELD_INTEGER ), - DEFINE_ARRAY( CInfoAlias, m_iTargetName, FIELD_STRING, MAX_ALIAS_TARGETS ), -// DEFINE_FIELD( CInfoAlias, m_pNextAlias, FIELD_CLASSPTR ), -}; -IMPLEMENT_SAVERESTORE( CInfoAlias, CBaseAlias ); - -void CInfoAlias :: KeyValue( KeyValueData *pkvd ) //AJH -{ - - if (FStrEq(pkvd->szKeyName, "mode")) - { - m_iMode = atoi( pkvd->szValue ); - pkvd->fHandled = TRUE; - } - else if (FStrEq(pkvd->szKeyName, "targetname")) - { - pev->targetname = ALLOC_STRING( pkvd->szValue ); - pkvd->fHandled = TRUE; - } - else if (FStrEq(pkvd->szKeyName, "target")) - { - pev->target = ALLOC_STRING( pkvd->szValue ); - pkvd->fHandled = TRUE; - }else if (FStrEq(pkvd->szKeyName, "netname")) - { - pev->netname = ALLOC_STRING( pkvd->szValue ); - pkvd->fHandled = TRUE; - } - else // add this field to the target list - { - // this assumes that additional fields are targetnames, and there values are the order. - if ( m_cTargets < MAX_ALIAS_TARGETS ) - { - char tmp[128]; - - UTIL_StripToken( pkvd->szKeyName, tmp ); - int iValue = atoi(pkvd->szValue); - if(iValue<=MAX_ALIAS_TARGETS&&iValue>0){ - if (m_iTargetName[iValue-1]==NULL){ - m_iTargetName [ iValue-1 ] = ALLOC_STRING( tmp ); - m_cTargets++; - }else{ - ALERT(at_debug,"ERROR: INFO_ALIAS target \"%s\" has a value \"%i\" that has already been used by target \"%s\"\n",ALLOC_STRING(tmp),iValue,STRING(m_iTargetName[iValue-1])); - } - }else{ - ALERT(at_debug,"ERROR: INFO_ALIAS target \"%s\" has an illegal value \"%i\".\nIt must be within the range 1-%i.\n",ALLOC_STRING(tmp),iValue,MAX_ALIAS_TARGETS); - } - pkvd->fHandled = TRUE; - } - //We can't actually have this or we will get array out of bounds exceptions when +using the alias at MAX_ALIAS_TARGETS - /*else // keep a count of how many targets, for the error message - { - m_cTargets++; - }*/ - } -} - -void CInfoAlias::Spawn( void ) -{ - if(m_iMode==0){ - if (pev->spawnflags & SF_ALIAS_OFF) - pev->message = pev->netname; - else - pev->message = pev->target; - } - else { - if (pev->spawnflags & SF_ALIAS_DEBUG) //Don't really need this much debug info - ALERT(at_debug,"DEBUG: info_alias %s contains %d targets\n",STRING(pev->targetname), m_cTargets); - if (m_cTargets > MAX_ALIAS_TARGETS) - { - ALERT(at_debug, "WARNING: info_alias \"%s\" has too many targets (limit is %d)\n", STRING(pev->targetname), MAX_ALIAS_TARGETS); - m_cTargets = MAX_ALIAS_TARGETS; - } - else { - m_iCurrentTarget=0; - pev->message = m_iTargetName[m_iCurrentTarget]; - pev->noise = m_iTargetName[m_iCurrentTarget]; - } - - } -} - -void CInfoAlias::Use( CBaseEntity *pActivator, CBaseEntity *pCaller, USE_TYPE useType, float value ) -{ - if(m_iMode==0){ //Old On/Off Code - - if (pev->spawnflags & SF_ALIAS_OFF) - { - if (pev->spawnflags & SF_ALIAS_DEBUG) - ALERT(at_debug,"DEBUG: info_alias %s turns on\n",STRING(pev->targetname)); - pev->spawnflags &= ~SF_ALIAS_OFF; - pev->noise = pev->target; - } - else - { - if (pev->spawnflags & SF_ALIAS_DEBUG) - ALERT(at_debug,"DEBUG: info_alias %s turns off\n",STRING(pev->targetname)); - pev->spawnflags |= SF_ALIAS_OFF; - pev->noise = pev->netname; - } - UTIL_AddToAliasList( this ); - } - - else{ //AJH - new list mode for info_alias - pev->noise = m_iTargetName[m_iCurrentTarget]; - // pev->message = m_iTargetName[m_iCurrentTarget]; - if (useType==USE_OFF){ - m_iCurrentTarget--; - if(m_iCurrentTarget <= -1) m_iCurrentTarget=m_cTargets; - }else{ - m_iCurrentTarget++; - if(m_iCurrentTarget >= m_cTargets) m_iCurrentTarget=0; - } - - - if (pev->spawnflags & SF_ALIAS_DEBUG) - ALERT(at_debug,"DEBUG: info_alias %s refers to target entity number %d \n",STRING(pev->targetname),m_iTargetName[m_iCurrentTarget]); - ALERT(at_debug,"DEBUG: info_alias %s steps to target %d \n",STRING(pev->targetname),m_iCurrentTarget); - UTIL_AddToAliasList(this); - } -} - -CBaseEntity *CInfoAlias::FollowAlias( CBaseEntity *pFrom ) -{ - CBaseEntity *pFound = UTIL_FindEntityByTargetname( pFrom, STRING(pev->message) ); - - if (pev->spawnflags & SF_ALIAS_DEBUG){ // More excessive debug info - ALERT(at_debug,"DEBUG: info_alias %s refers to target %d \n",STRING(pev->targetname),m_iCurrentTarget); - ALERT(at_debug,"DEBUG: info_alias %s refers to target entity %s \n",STRING(pev->targetname),STRING(pev->message)); - - if (pFound) - ALERT(at_debug,"DEBUG: info_alias %s refers to target entity %s \n",STRING(pev->targetname),STRING(pFound->pev->targetname)); - } - return pFound; -} - -void CInfoAlias::ChangeValue( int iszValue ) -{ - pev->noise = iszValue; - UTIL_AddToAliasList( this ); -} - -void CInfoAlias::FlushChanges( void ) -{ - pev->message = pev->noise; - if (pev->spawnflags & SF_ALIAS_DEBUG) - ALERT(at_debug,"DEBUG: info_alias %s now refers to \"%s\"\n", STRING(pev->targetname), STRING(pev->message)); -} - -/********************* -* Worldcraft entity: info_group -* -* targetname- name -* target- alias entity to affect -* other values are handled in a multi_manager-like way. -**********************/ -// definition in cbase.h - -#define SF_GROUP_DEBUG 2 - -LINK_ENTITY_TO_CLASS( info_group, CInfoGroup ); - -TYPEDESCRIPTION CInfoGroup::m_SaveData[] = -{ - DEFINE_FIELD( CInfoGroup, m_cMembers, FIELD_INTEGER ), - DEFINE_ARRAY( CInfoGroup, m_iszMemberName, FIELD_STRING, MAX_MULTI_TARGETS ), - DEFINE_ARRAY( CInfoGroup, m_iszMemberValue, FIELD_STRING, MAX_MULTI_TARGETS ), - DEFINE_FIELD( CInfoGroup, m_iszDefaultMember, FIELD_STRING ), -}; - -IMPLEMENT_SAVERESTORE(CInfoGroup,CBaseEntity); - -void CInfoGroup :: KeyValue( KeyValueData *pkvd ) -{ - if (FStrEq(pkvd->szKeyName, "defaultmember")) - { - m_iszDefaultMember = ALLOC_STRING(pkvd->szValue); - pkvd->fHandled = TRUE; - } - // this assumes that additional fields are targetnames and their values are delay values. - else if ( m_cMembers < MAX_MULTI_TARGETS ) - { - char tmp[128]; - UTIL_StripToken( pkvd->szKeyName, tmp ); - m_iszMemberName [ m_cMembers ] = ALLOC_STRING( tmp ); - m_iszMemberValue [ m_cMembers ] = ALLOC_STRING (pkvd->szValue); - m_cMembers++; - pkvd->fHandled = TRUE; - } - else - { - ALERT(at_error,"Too many members for info_group %s (limit is %d)\n",STRING(pev->targetname),MAX_MULTI_TARGETS); - } -} - -void CInfoGroup::Use( CBaseEntity *pActivator, CBaseEntity *pCaller, USE_TYPE useType, float value ) -{ - CBaseEntity *pTarget = UTIL_FindEntityByTargetname( NULL, STRING( pev->target ) ); - - if (pTarget && pTarget->IsAlias()) - { - if (pev->spawnflags & SF_GROUP_DEBUG) - ALERT(at_debug, "DEBUG: info_group %s changes the contents of %s \"%s\"\n",STRING(pev->targetname), STRING(pTarget->pev->classname), STRING(pTarget->pev->targetname)); - ((CBaseAlias*)pTarget)->ChangeValue(this); - } - else if (pev->target) - { - ALERT(at_debug, "info_group \"%s\": alias \"%s\" was not found or not an alias!", STRING(pev->targetname), STRING(pev->target)); - } -} - -int CInfoGroup::GetMember( const char* szMemberName ) -{ - if (!szMemberName) - { - ALERT(at_debug,"info_group: GetMember called with null szMemberName!?\n"); - return NULL; - } - for (int i = 0; i < m_cMembers; i++) - { - if (FStrEq(szMemberName, STRING(m_iszMemberName[i]))) - { -// ALERT(at_console,"getMember: found member\n"); - return m_iszMemberValue[i]; - } - } - - if (m_iszDefaultMember) - { - static char szBuffer[128]; - strcpy(szBuffer, STRING(m_iszDefaultMember)); - strcat(szBuffer, szMemberName); - return MAKE_STRING(szBuffer); - // this is a messy way to do it... but currently, only one - // GetMember gets performed at a time, so it works. - } - - ALERT(at_debug,"info_group \"%s\" has no member called \"%s\".\n",STRING(pev->targetname),szMemberName); -// ALERT(at_console,"getMember: fail\n"); - return NULL; -} - -/********************* -* Worldcraft entity: multi_alias -* -* targetname- name -* other values are handled in a multi_manager-like way. -**********************/ -// definition in cbase.h - -LINK_ENTITY_TO_CLASS( multi_alias, CMultiAlias ); - -TYPEDESCRIPTION CMultiAlias::m_SaveData[] = -{ - DEFINE_FIELD( CMultiAlias, m_cTargets, FIELD_INTEGER ), - DEFINE_ARRAY( CMultiAlias, m_iszTargets, FIELD_STRING, MAX_MULTI_TARGETS ), - DEFINE_FIELD( CMultiAlias, m_iTotalValue, FIELD_INTEGER ), - DEFINE_ARRAY( CMultiAlias, m_iValues, FIELD_INTEGER, MAX_MULTI_TARGETS ), - DEFINE_FIELD( CMultiAlias, m_iMode, FIELD_INTEGER ), -}; - -IMPLEMENT_SAVERESTORE(CMultiAlias,CBaseAlias); - -void CMultiAlias :: KeyValue( KeyValueData *pkvd ) -{ - if (FStrEq(pkvd->szKeyName, "m_iMode")) - { - m_iMode = atoi( pkvd->szValue ); - pkvd->fHandled = TRUE; - } - // this assumes that additional fields are targetnames and their values are probability values. - else if ( m_cTargets < MAX_MULTI_TARGETS ) - { - char tmp[128]; - UTIL_StripToken( pkvd->szKeyName, tmp ); - - m_iszTargets [ m_cTargets ] = ALLOC_STRING( tmp ); - m_iValues [ m_cTargets ] = atoi( pkvd->szValue ); - - m_iTotalValue += m_iValues [ m_cTargets ]; - m_cTargets++; - - pkvd->fHandled = TRUE; - } - else - { - ALERT(at_error,"Too many targets for multi_alias %s (limit is %d)\n",STRING(pev->targetname), MAX_MULTI_TARGETS); - } -} - -CBaseEntity *CMultiAlias::FollowAlias( CBaseEntity *pStartEntity ) -{ - CBaseEntity* pBestEntity = NULL; // the entity we're currently planning to return. - int iBestOffset = -1; // the offset of that entity. - CBaseEntity* pTempEntity; - int iTempOffset; - - int i = 0; - if (m_iMode) - { - // During any given 'game moment', this code may be called more than once. It must use the - // same random values each time (because otherwise it gets really messy). I'm using srand - // to arrange this. - srand( (int)(gpGlobals->time * 100) ); - rand(); // throw away the first result - it's just the seed value - if (m_iMode == 1) // 'choose one' mode - { - int iRandom = 1 + (rand() % m_iTotalValue); - for (i = 0; i < m_cTargets; i++) - { - iRandom -= m_iValues[i]; - if (iRandom <= 0) - break; - } - } - else // 'percent chance' mode - { - for (i = 0; i < m_cTargets; i++) - { - if (m_iValues[i] >= rand() % 100) - break; - } - } - } - - while (i < m_cTargets) - { - pTempEntity = UTIL_FindEntityByTargetname(pStartEntity,STRING(m_iszTargets[i])); - if ( pTempEntity ) - { - // We've found an entity; only use it if its offset is lower than the offset we've currently got. - iTempOffset = OFFSET(pTempEntity->pev); - if (iBestOffset == -1 || iTempOffset < iBestOffset) - { - iBestOffset = iTempOffset; - pBestEntity = pTempEntity; - } - } - if (m_iMode == 1) - break; // if it's in "pick one" mode, stop after the first. - else if (m_iMode == 2) - { - i++; - // if it's in "percent chance" mode, try to find another one to fire. - while (i < m_cTargets) - { - if (m_iValues[i] > rand() % 100) - break; - i++; - } - } - else - i++; - } - - return pBestEntity; -} - -/********************* -* Worldcraft entity: trigger_changealias -* -* target- alias entity to affect -* netname- value to change the alias to -**********************/ - -#define SF_CHANGEALIAS_RESOLVE 1 -#define SF_CHANGEALIAS_DEBUG 2 - -class CTriggerChangeAlias : public CBaseEntity -{ -public: - void Spawn( void ); - void Use( CBaseEntity *pActivator, CBaseEntity *pCaller, USE_TYPE useType, float value ); - - int ObjectCaps( void ) { return CBaseEntity::ObjectCaps() & ~FCAP_ACROSS_TRANSITION; } -}; -LINK_ENTITY_TO_CLASS( trigger_changealias, CTriggerChangeAlias ); - -void CTriggerChangeAlias::Spawn( void ) -{ -} - -void CTriggerChangeAlias::Use( CBaseEntity *pActivator, CBaseEntity *pCaller, USE_TYPE useType, float value ) -{ - CBaseEntity *pTarget = UTIL_FindEntityByTargetname( NULL, STRING( pev->target ), pActivator ); - - if (pTarget && pTarget->IsAlias()) - { - CBaseEntity *pValue; - - if (FStrEq(STRING(pev->netname), "*locus")) - { - pValue = pActivator; - } - else if (pev->spawnflags & SF_CHANGEALIAS_RESOLVE) - { - pValue = UTIL_FollowReference(NULL, STRING(pev->netname)); - } - - if (pValue) - ((CBaseAlias*)pTarget)->ChangeValue(pValue); - else - ((CBaseAlias*)pTarget)->ChangeValue(pev->netname); - } - else - { - ALERT(at_error, "trigger_changealias %s: alias \"%s\" was not found or not an alias!", STRING(pev->targetname), STRING(pev->target)); - } -} diff --git a/spirit/cbase.cpp b/spirit/cbase.cpp deleted file mode 100644 index a6d48e8b..00000000 --- a/spirit/cbase.cpp +++ /dev/null @@ -1,1203 +0,0 @@ -/*** -* -* Copyright (c) 1996-2002, 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. -* -****/ -#include "extdll.h" -#include "util.h" -#include "cbase.h" -#include "saverestore.h" -#include "client.h" -#include "decals.h" -#include "gamerules.h" -#include "game.h" -#include "movewith.h" -#include "skill.h" -#include "weapons.h" - -void EntvarsKeyvalue( entvars_t *pev, KeyValueData *pkvd ); - -extern Vector VecBModelOrigin( entvars_t* pevBModel ); -extern DLL_GLOBAL Vector g_vecAttackDir; -extern DLL_GLOBAL int g_iSkillLevel; - -static DLL_FUNCTIONS gFunctionTable = -{ - GameDLLInit, // pfnGameInit - DispatchSpawn, // pfnSpawn - DispatchThink, // pfnThink - DispatchUse, // pfnUse - DispatchTouch, // pfnTouch - DispatchBlocked, // pfnBlocked - DispatchKeyValue, // pfnKeyValue - DispatchSave, // pfnSave - DispatchRestore, // pfnRestore - DispatchObjectCollsionBox, // pfnAbsBox - - SaveWriteFields, // pfnSaveWriteFields - SaveReadFields, // pfnSaveReadFields - - SaveGlobalState, // pfnSaveGlobalState - RestoreGlobalState, // pfnRestoreGlobalState - ResetGlobalState, // pfnResetGlobalState - - ClientConnect, // pfnClientConnect - ClientDisconnect, // pfnClientDisconnect - ClientKill, // pfnClientKill - ClientPutInServer, // pfnClientPutInServer - ClientCommand, // pfnClientCommand - ClientUserInfoChanged, // pfnClientUserInfoChanged - - ServerActivate, // pfnServerActivate - ServerDeactivate, // pfnServerDeactivate - - PlayerPreThink, // pfnPlayerPreThink - PlayerPostThink, // pfnPlayerPostThink - - StartFrame, // pfnStartFrame - DispatchCreate, // pfnCreate - BuildLevelList, // pfnParmsChangeLevel - - GetGameDescription, // pfnGetGameDescription Returns string describing current .dll game. - DispatchFrame, // pfnPhysicsEntity - - SpectatorConnect, // pfnSpectatorConnect Called when spectator joins server - SpectatorDisconnect, // pfnSpectatorDisconnect Called when spectator leaves the server - SpectatorThink, // pfnSpectatorThink Called when spectator sends a command packet (usercmd_t) - - ServerClassifyEdict, // pfnClassifyEdict - - PM_Move, // pfnPM_Move - PM_Init, // pfnPM_Init Server version of player movement initialization - PM_FindTextureType, // pfnPM_FindTextureType - - SetupVisibility, // pfnSetupVisibility - UpdateClientData, // pfnUpdateClientData - AddToFullPack, // pfnAddtoFullPack - CreateBaseline, // fpnCreateBaseline - - - RegisterEncoders, // pfnRegisterEncoders Callbacks for network encoding - GetWeaponData, // pfnGetWeaponData - CmdStart, // pfnCmdStart - CmdEnd, // pfnCmdEnd - - OnFreeEntPrivateData, // pfnOnFreeEntPrivateData - GameDLLShutdown, // pfnGameShutdown - ShouldCollide, // pfnShouldCollide -}; - -static void SetObjectCollisionBox( entvars_t *pev ); - -//======================================================================= -// General API entering point -//======================================================================= -int GetEntityAPI( DLL_FUNCTIONS *pFunctionTable, int interfaceVersion ) -{ - if ( !pFunctionTable || interfaceVersion != SV_INTERFACE_VERSION ) - { - return FALSE; - } - - memcpy( pFunctionTable, &gFunctionTable, sizeof( DLL_FUNCTIONS ) ); - return TRUE; -} - -int DispatchSpawn( edict_t *pent ) -{ - CBaseEntity *pEntity = (CBaseEntity *)GET_PRIVATE(pent); - - if (pEntity) - { - // Initialize these or entities who don't link to the world won't have anything in here - pEntity->pev->absmin = pEntity->pev->origin - Vector(1,1,1); - pEntity->pev->absmax = pEntity->pev->origin + Vector(1,1,1); - -// pEntity->InitMoveWith(); //LRC - pEntity->Spawn(); - - // Try to get the pointer again, in case the spawn function deleted the entity. - // UNDONE: Spawn() should really return a code to ask that the entity be deleted, but - // that would touch too much code for me to do that right now. - pEntity = (CBaseEntity *)GET_PRIVATE(pent); - - if ( pEntity ) - { - if ( g_pGameRules && !g_pGameRules->IsAllowedToSpawn( pEntity ) ) - return -1; // return that this entity should be deleted - if ( pEntity->pev->flags & FL_KILLME ) - return -1; - if ( g_iSkillLevel == SKILL_EASY && pEntity->m_iLFlags & LF_NOTEASY ) - return -1; //LRC - if (g_iSkillLevel == SKILL_MEDIUM && pEntity->m_iLFlags & LF_NOTMEDIUM ) - return -1; //LRC - if (g_iSkillLevel == SKILL_HARD && pEntity->m_iLFlags & LF_NOTHARD ) - return -1; //LRC - } - - - // Handle global stuff here - if ( pEntity && pEntity->pev->globalname ) - { - const globalentity_t *pGlobal = gGlobalState.EntityFromTable( pEntity->pev->globalname ); - if ( pGlobal ) - { - // Already dead? delete - if ( pGlobal->state == GLOBAL_DEAD ) - return -1; - else if ( !FStrEq( STRING(gpGlobals->mapname), pGlobal->levelName ) ) - pEntity->MakeDormant(); // Hasn't been moved to this level yet, wait but stay alive - // In this level & not dead, continue on as normal - } - else - { - // Spawned entities default to 'On' - gGlobalState.EntityAdd( pEntity->pev->globalname, gpGlobals->mapname, GLOBAL_ON ); -// ALERT( at_console, "Added global entity %s (%s)\n", STRING(pEntity->pev->classname), STRING(pEntity->pev->globalname) ); - } - } - - } - - return 0; -} - -int DispatchCreate( edict_t *pent, const char *szName ) -{ - if( FNullEnt( pent ) || !szName || !*szName ) - return -1; -#if 0 - int istr = ALLOC_STRING( szName ); - - // Xash3D extension - // handle virtual entities here - // just example for future weapon_generic - if( !strncmp( szName, "weapon_", 7 )) - { - CBasePlayerWeapon *pWeapon = GetClassPtr((CBasePlayerWeapon *)VARS( pent )); - pWeapon->pev->netname = istr; - return 0; - } -#endif - return -1; -} - -void DispatchKeyValue( edict_t *pentKeyvalue, KeyValueData *pkvd ) -{ - if ( !pkvd || !pentKeyvalue ) - return; - - EntvarsKeyvalue( VARS(pentKeyvalue), pkvd ); - - // If the key was an entity variable, or there's no class set yet, don't look for the object, it may - // not exist yet. - if ( pkvd->fHandled || pkvd->szClassName == NULL ) - return; - - // Get the actualy entity object - CBaseEntity *pEntity = (CBaseEntity *)GET_PRIVATE(pentKeyvalue); - - if ( !pEntity ) - return; - - pEntity->KeyValue( pkvd ); -} - -/* ------------------ -DispatchFrame - -this function can override any physics movement -and let user use custom physic. -e.g. you can replace MOVETYPE_PUSH for new movewith system -and many many other things. ------------------ -*/ -int DispatchFrame( edict_t *pent ) -{ - return 0; -} - -// HACKHACK -- this is a hack to keep the node graph entity from "touching" things (like triggers) -// while it builds the graph -BOOL gTouchDisabled = FALSE; -void DispatchTouch( edict_t *pentTouched, edict_t *pentOther ) -{ - if ( gTouchDisabled ) - return; - - CBaseEntity *pEntity = (CBaseEntity *)GET_PRIVATE(pentTouched); - CBaseEntity *pOther = (CBaseEntity *)GET_PRIVATE( pentOther ); - - if ( pEntity && pOther && ! ((pEntity->pev->flags | pOther->pev->flags) & FL_KILLME) ) - pEntity->Touch( pOther ); -} - - -void DispatchUse( edict_t *pentUsed, edict_t *pentOther ) -{ - CBaseEntity *pEntity = (CBaseEntity *)GET_PRIVATE(pentUsed); - CBaseEntity *pOther = (CBaseEntity *)GET_PRIVATE(pentOther); - - if (pEntity && !(pEntity->pev->flags & FL_KILLME) ) - pEntity->Use( pOther, pOther, USE_TOGGLE, 0 ); -} - -void DispatchThink( edict_t *pent ) -{ - CBaseEntity *pEntity = (CBaseEntity *)GET_PRIVATE(pent); - - if (pEntity) - { - if ( FBitSet( pEntity->pev->flags, FL_DORMANT ) ) - ALERT( at_error, "Dormant entity %s is thinking!!\n", STRING(pEntity->pev->classname) ); - - //if (pEntity->pev->classname) ALERT(at_console, "DispatchThink %s\n", STRING(pEntity->pev->targetname)); - pEntity->Think(); - } -} - -void DispatchBlocked( edict_t *pentBlocked, edict_t *pentOther ) -{ - CBaseEntity *pEntity = (CBaseEntity *)GET_PRIVATE( pentBlocked ); - CBaseEntity *pOther = (CBaseEntity *)GET_PRIVATE( pentOther ); - - if (pEntity) - pEntity->Blocked( pOther ); -} - -void DispatchSave( edict_t *pent, SAVERESTOREDATA *pSaveData ) -{ - CBaseEntity *pEntity = (CBaseEntity *)GET_PRIVATE(pent); - - if ( pEntity && pSaveData ) - { - ENTITYTABLE *pTable = &pSaveData->pTable[ pSaveData->currentIndex ]; - - if ( pTable->pent != pent ) - ALERT( at_error, "ENTITY TABLE OR INDEX IS WRONG!!!!\n" ); - - if ( pEntity->ObjectCaps() & FCAP_DONT_SAVE ) - return; - - // These don't use ltime & nextthink as times really, but we'll fudge around it. - if ( pEntity->pev->movetype == MOVETYPE_PUSH ) - { - //LRC - rearranged so that we can correct m_fNextThink too. - float delta = gpGlobals->time - pEntity->pev->ltime; - pEntity->pev->ltime += delta; - pEntity->pev->nextthink += delta; - pEntity->m_fPevNextThink = pEntity->pev->nextthink; - pEntity->m_fNextThink += delta; - } - - if( gpGlobals->changelevel ) - pEntity->ClearPointers(); - - pTable->location = pSaveData->size; // Remember entity position for file I/O - pTable->classname = pEntity->pev->classname; // Remember entity class for respawn - - CSave saveHelper( pSaveData ); - pEntity->Save( saveHelper ); - - pTable->size = pSaveData->size - pTable->location; // Size of entity block is data size written to block - } -} - - -// Find the matching global entity. Spit out an error if the designer made entities of -// different classes with the same global name -CBaseEntity *FindGlobalEntity( string_t classname, string_t globalname ) -{ - CBaseEntity *pReturn = UTIL_FindEntityByString( NULL, "globalname", STRING(globalname) ); - if ( pReturn ) - { - if ( !FClassnameIs( pReturn->pev, STRING(classname) ) ) - { - ALERT( at_debug, "Global entity found %s, wrong class %s\n", STRING(globalname), STRING(pReturn->pev->classname) ); - pReturn = NULL; - } - } - - return pReturn; -} - - -int DispatchRestore( edict_t *pent, SAVERESTOREDATA *pSaveData, int globalEntity ) -{ - CBaseEntity *pEntity = (CBaseEntity *)GET_PRIVATE(pent); - - if ( pEntity && pSaveData ) - { - entvars_t tmpVars; - Vector oldOffset; - - CRestore restoreHelper( pSaveData ); - if ( globalEntity ) - { - CRestore tmpRestore( pSaveData ); - tmpRestore.PrecacheMode( 0 ); - tmpRestore.ReadEntVars( "ENTVARS", &tmpVars ); - - // HACKHACK - reset the save pointers, we're going to restore for real this time - // NOTE: in Xash3D this pointers already sets by engine - pSaveData->size = pSaveData->pTable[pSaveData->currentIndex].location; - pSaveData->pCurrentData = pSaveData->pBaseData + pSaveData->size; - // ------------------- - - - const globalentity_t *pGlobal = gGlobalState.EntityFromTable( tmpVars.globalname ); - - // Don't overlay any instance of the global that isn't the latest - // pSaveData->szCurrentMap is the level this entity is coming from - // pGlobla->levelName is the last level the global entity was active in. - // If they aren't the same, then this global update is out of date. - if ( !FStrEq( pSaveData->szCurrentMapName, pGlobal->levelName ) ) - return 0; - - // Compute the new global offset - oldOffset = pSaveData->vecLandmarkOffset; - CBaseEntity *pNewEntity = FindGlobalEntity( tmpVars.classname, tmpVars.globalname ); - if ( pNewEntity ) - { -// ALERT( at_console, "Overlay %s with %s\n", STRING(pNewEntity->pev->classname), STRING(tmpVars.classname) ); - // Tell the restore code we're overlaying a global entity from another level - restoreHelper.SetGlobalMode( 1 ); // Don't overwrite global fields - pSaveData->vecLandmarkOffset = (pSaveData->vecLandmarkOffset - pNewEntity->pev->mins) + tmpVars.mins; - pEntity = pNewEntity;// we're going to restore this data OVER the old entity - pent = ENT( pEntity->pev ); - // Update the global table to say that the global definition of this entity should come from this level - gGlobalState.EntityUpdate( pEntity->pev->globalname, gpGlobals->mapname ); - } - else - { - // This entity will be freed automatically by the engine. If we don't do a restore on a matching entity (below) - // or call EntityUpdate() to move it to this level, we haven't changed global state at all. - return 0; - } - - } - - if ( pEntity->ObjectCaps() & FCAP_MUST_SPAWN ) - { - pEntity->Restore( restoreHelper ); - pEntity->Spawn(); - } - else - { - pEntity->Restore( restoreHelper ); - pEntity->Precache( ); - } - - // Again, could be deleted, get the pointer again. - pEntity = (CBaseEntity *)GET_PRIVATE(pent); -#if 0 - if ( pEntity && pEntity->pev->globalname && globalEntity ) - { - ALERT( at_debug, "Global %s is %s\n", STRING(pEntity->pev->globalname), STRING(pEntity->pev->model) ); - } -#endif - - // Is this an overriding global entity (coming over the transition), or one restoring in a level - if ( globalEntity ) - { -// ALERT( at_console, "After: %f %f %f %s\n", pEntity->pev->origin.x, pEntity->pev->origin.y, pEntity->pev->origin.z, STRING(pEntity->pev->model) ); - pSaveData->vecLandmarkOffset = oldOffset; - if ( pEntity ) - { - pEntity->InitMoveWith(); //g-cont. rebuild all parents on next level - UTIL_SetOrigin( pEntity, pEntity->pev->origin ); - pEntity->OverrideReset(); - } - } - else if ( pEntity && pEntity->pev->globalname ) - { - const globalentity_t *pGlobal = gGlobalState.EntityFromTable( pEntity->pev->globalname ); - if ( pGlobal ) - { - // Already dead? delete - if ( pGlobal->state == GLOBAL_DEAD ) - return -1; - else if ( !FStrEq( STRING(gpGlobals->mapname), pGlobal->levelName ) ) - { - pEntity->MakeDormant(); // Hasn't been moved to this level yet, wait but stay alive - } - // In this level & not dead, continue on as normal - } - else - { - ALERT( at_error, "Global Entity %s (%s) not in table!!!\n", STRING(pEntity->pev->globalname), STRING(pEntity->pev->classname) ); - // Spawned entities default to 'On' - gGlobalState.EntityAdd( pEntity->pev->globalname, gpGlobals->mapname, GLOBAL_ON ); - } - } - } - return 0; -} - - -void DispatchObjectCollsionBox( edict_t *pent ) -{ - CBaseEntity *pEntity = (CBaseEntity *)GET_PRIVATE(pent); - if (pEntity) - { - pEntity->SetObjectCollisionBox(); - } - else - SetObjectCollisionBox( &pent->v ); -} - - -void SaveWriteFields( SAVERESTOREDATA *pSaveData, const char *pname, void *pBaseData, TYPEDESCRIPTION *pFields, int fieldCount ) -{ - CSave saveHelper( pSaveData ); - saveHelper.WriteFields( "SWF", pname, pBaseData, pFields, fieldCount ); -} - - -void SaveReadFields( SAVERESTOREDATA *pSaveData, const char *pname, void *pBaseData, TYPEDESCRIPTION *pFields, int fieldCount ) -{ - CRestore restoreHelper( pSaveData ); - restoreHelper.ReadFields( pname, pBaseData, pFields, fieldCount ); -} - -void OnFreeEntPrivateData( edict_t *pEdict ) -{ - if( pEdict && pEdict->pvPrivateData ) - { - ((CBaseEntity*)pEdict->pvPrivateData)->~CBaseEntity(); - } -} - -edict_t * EHANDLE::Get( void ) -{ - if (m_pent) - { - if (m_pent->serialnumber == m_serialnumber) - return m_pent; - else - return NULL; - } - return NULL; -}; - -edict_t * EHANDLE::Set( edict_t *pent ) -{ - m_pent = pent; - if (pent) - m_serialnumber = m_pent->serialnumber; - return pent; -}; - - -EHANDLE :: operator CBaseEntity *() -{ - return (CBaseEntity *)GET_PRIVATE( Get( ) ); -}; - - -CBaseEntity * EHANDLE :: operator = (CBaseEntity *pEntity) -{ - if (pEntity) - { - m_pent = ENT( pEntity->pev ); - if (m_pent) - m_serialnumber = m_pent->serialnumber; - } - else - { - m_pent = NULL; - m_serialnumber = 0; - } - return pEntity; -} - -EHANDLE :: operator int () -{ - return Get() != NULL; -} - -CBaseEntity * EHANDLE :: operator -> () -{ - return (CBaseEntity *)GET_PRIVATE( Get( ) ); -} - -//LRC -void CBaseEntity::Activate( void ) -{ - //LRC - rebuild the new assistlist as the game starts - if (m_iLFlags & LF_ASSISTLIST) - { - UTIL_AddToAssistList(this); - } - - //LRC - and the aliaslist too - if (m_iLFlags & LF_ALIASLIST) - { - UTIL_AddToAliasList((CBaseAlias*)this); - } - - if( gpGlobals->changelevel ) - m_activated = FALSE; - - if (m_activated) return; - m_activated = TRUE; - InitMoveWith(); - - PostSpawn(); -} - -//LRC- called by activate() to support movewith -void CBaseEntity::InitMoveWith( void ) -{ - SetParent( m_MoveWith ); -} - -//g-cont. upgrade system to xashParentSystem 0.3 beta -void CBaseEntity::SetParent( int m_iNewParent, int m_iAttachment ) -{ - if(!m_iNewParent) //unlink entity from chain - { - //ResetParent(); //g-cont. temporary disabled - return;//disable - } - - CBaseEntity* pParent; - - if(!m_iAttachment) //try to extract aiment from name - { - char *name = (char*)STRING(m_iNewParent); - for (char *c = name; *c; c++) - { - if (*c == '.') - { - m_iAttachment = atoi(c+1); - name[strlen(name)-2] = 0; - pParent = UTIL_FindEntityByTargetname( NULL, name); - SetParent( pParent, m_iAttachment); - return; - } - } - } - - pParent = UTIL_FindEntityByTargetname( NULL, STRING(m_iNewParent)); - SetParent( pParent, m_iAttachment );//check pointer to valid later -} - -void CBaseEntity::SetParent( CBaseEntity *pParent, int m_iAttachment ) -{ - //if (!m_MoveWith) return; - - m_pMoveWith = pParent; //UTIL_FindEntityByTargetname(NULL, STRING(m_MoveWith)); - - if (!m_pMoveWith) - { - ALERT(at_debug,"Missing movewith entity %s\n", STRING(m_MoveWith)); - return; - } - -// if (pev->targetname) -// ALERT(at_console,"Init: %s %s moves with %s\n", STRING(pev->classname), STRING(pev->targetname), STRING(m_MoveWith)); -// else -// ALERT(at_console,"Init: %s moves with %s\n", STRING(pev->classname), STRING(m_MoveWith)); - - //check for himself parent - if(m_pMoveWith == this) - { - ALERT(at_debug, "%s has himself parent!\n", STRING(pev->classname) ); - return; - } - - CBaseEntity *pSibling = m_pMoveWith->m_pChildMoveWith; - while (pSibling) // check that this entity isn't already in the list of children - { - if (pSibling == this) break; - pSibling = pSibling->m_pSiblingMoveWith; - } - if (!pSibling) // if movewith is being set up for the first time... - { - // add this entity to the list of children - m_pSiblingMoveWith = m_pMoveWith->m_pChildMoveWith; // may be null: that's fine by me. - m_pMoveWith->m_pChildMoveWith = this; - - if(m_iAttachment)//parent has attcahment - { - if(m_iLFlags & LF_POINTENTITY || pev->flags & FL_MONSTER) - { - pev->colormap = ((pev->colormap & 0xFF00)>>8) | m_iAttachment; - pev->aiment = m_pMoveWith->edict(); - pev->movetype = MOVETYPE_FOLLOW; - } - else //error - { - ALERT(at_debug, "%s not following with aiment %d!(yet)\n", STRING(pev->classname), m_iAttachment ); - } - return; - } - else//appllayed to origin - { - if (pev->movetype == MOVETYPE_NONE) - { - if (pev->solid == SOLID_BSP) - pev->movetype = MOVETYPE_PUSH; - else pev->movetype = MOVETYPE_NOCLIP; // or _FLY, perhaps? - SetBits (m_iLFlags, LF_MOVENONE); //member movetype - } - - if(m_pMoveWith->pev->movetype == MOVETYPE_WALK)//parent is walking monster? - { - SetBits (m_iLFlags, LF_POSTORG);//copy pos from parent every frame - pev->solid = SOLID_NOT;//set non solid - } - m_vecParentOrigin = m_pMoveWith->pev->origin; - m_vecParentAngles = m_pMoveWith->pev->angles; - } - - // was the parent shifted at spawn-time? - if (m_pMoveWith->m_vecSpawnOffset != g_vecZero) - { - //ALERT(at_console,"Corrected using SpawnOffset\n"); - // shift this by the same amount the parent was shifted by. - UTIL_AssignOrigin(this, pev->origin + m_pMoveWith->m_vecSpawnOffset); - //...and inherit the same offset. - m_vecSpawnOffset = m_vecSpawnOffset + m_pMoveWith->m_vecSpawnOffset; - } - - m_vecOffsetOrigin = pev->origin - m_vecParentOrigin; - m_vecOffsetAngles = pev->angles - m_vecParentAngles; - - if((m_pMoveWith->m_iLFlags & LF_ANGULAR && m_vecOffsetOrigin != g_vecZero) || m_pMoveWith->m_iLFlags & LF_POINTENTITY) - { - SetBits (m_iLFlags, LF_POSTORG);//magic stuff - //GetPInfo( this ); - } - - if(g_serveractive)//maybe parent is moving ? - { - pev->velocity = pev->velocity + m_pMoveWith->pev->velocity; - pev->avelocity = pev->avelocity + m_pMoveWith->pev->avelocity; - } - } - -// if (pev->flags & FL_WORLDBRUSH) // not sure what this does, exactly. -// pev->flags &= ~FL_WORLDBRUSH; -} - -void CBaseEntity :: ResetParent( void ) -{ - CBaseEntity* pTemp; - - if(m_iLFlags & LF_MOVENONE)//this entity was static e.g. func_wall - { - ClearBits (m_iLFlags, LF_MOVENONE); - pev->movetype = MOVETYPE_NONE; - } - - if (!g_pWorld) - { - ALERT(at_debug, "ResetParent has no AssistList!\n"); - return; - } - - //LRC - remove this from the AssistList. - for (pTemp = g_pWorld; pTemp->m_pAssistLink != NULL; pTemp = pTemp->m_pAssistLink) - { - if (this == pTemp->m_pAssistLink) - { -// ALERT(at_console,"REMOVE: %s removed from the Assist List.\n", STRING(pev->classname)); - pTemp->m_pAssistLink = this->m_pAssistLink; - this->m_pAssistLink = NULL; - break; - } - } - - //LRC - if (m_pMoveWith) - { - // if I'm moving with another entity, take me out of the list. (otherwise things crash!) - pTemp = m_pMoveWith->m_pChildMoveWith; - if (pTemp == this) - { - m_pMoveWith->m_pChildMoveWith = this->m_pSiblingMoveWith; - } - else - { - while (pTemp->m_pSiblingMoveWith) - { - if (pTemp->m_pSiblingMoveWith == this) - { - pTemp->m_pSiblingMoveWith = this->m_pSiblingMoveWith; - break; - } - pTemp = pTemp->m_pSiblingMoveWith; - } - - } -// ALERT(at_console,"REMOVE: %s removed from the %s ChildMoveWith list.\n", STRING(pev->classname), STRING(m_pMoveWith->pev->targetname)); - } - - //LRC - do the same thing if another entity is moving with _me_. - if (m_pChildMoveWith) - { - CBaseEntity* pCur = m_pChildMoveWith; - CBaseEntity* pNext; - while (pCur != NULL) - { - pNext = pCur->m_pSiblingMoveWith; - // bring children to a stop - UTIL_SetMoveWithVelocity(pCur, g_vecZero, 100); - UTIL_SetMoveWithAvelocity(pCur, g_vecZero, 100); - pCur->m_pMoveWith = NULL; - pCur->m_pSiblingMoveWith = NULL; - pCur = pNext; - } - } -} - -void CBaseEntity :: ClearPointers( void ) -{ - m_pMoveWith = NULL; - m_pChildMoveWith = NULL; - m_pSiblingMoveWith = NULL; - m_pAssistLink = NULL; -} - -//LRC -void CBaseEntity::DontThink( void ) -{ - m_fNextThink = 0; - if (m_pMoveWith == NULL && m_pChildMoveWith == NULL) - { - pev->nextthink = 0; - m_fPevNextThink = 0; - } - -// ALERT(at_console, "DontThink for %s\n", STRING(pev->targetname)); -} - -//LRC -// PUSH entities won't have their velocity applied unless they're thinking. -// make them do so for the foreseeable future. -void CBaseEntity :: SetEternalThink( void ) -{ - if (pev->movetype == MOVETYPE_PUSH) - { - // record m_fPevNextThink as well, because we want to be able to - // tell when the bloody engine CHANGES IT! -// pev->nextthink = 1E9; - pev->nextthink = pev->ltime + 1E6; - m_fPevNextThink = pev->nextthink; - } - - CBaseEntity *pChild; - for (pChild = m_pChildMoveWith; pChild != NULL; pChild = pChild->m_pSiblingMoveWith) - pChild->SetEternalThink( ); -} - -//LRC - for getting round the engine's preconceptions. -// MoveWith entities have to be able to think independently of moving. -// This is how we do so. -void CBaseEntity :: SetNextThink( float delay, BOOL correctSpeed ) -{ - // now monsters use this method, too. - if (m_pMoveWith || m_pChildMoveWith || pev->flags & FL_MONSTER) - { - // use the Assist system, so that thinking doesn't mess up movement. - if (pev->movetype == MOVETYPE_PUSH) - m_fNextThink = pev->ltime + delay; - else - m_fNextThink = gpGlobals->time + delay; - SetEternalThink( ); - UTIL_MarkForAssist( this, correctSpeed ); - -// ALERT(at_console, "SetAssistedThink for %s: %f\n", STRING(pev->targetname), m_fNextThink); - } - else - { - // set nextthink as normal. - if (pev->movetype == MOVETYPE_PUSH) - { - pev->nextthink = pev->ltime + delay; - } - else - { - pev->nextthink = gpGlobals->time + delay; - } - - m_fPevNextThink = m_fNextThink = pev->nextthink; - -// if (pev->classname) ALERT(at_console, "SetNormThink for %s: %f\n", STRING(pev->targetname), m_fNextThink); - } -} - -//LRC -void CBaseEntity :: AbsoluteNextThink( float time, BOOL correctSpeed ) -{ - if (m_pMoveWith || m_pChildMoveWith) - { - // use the Assist system, so that thinking doesn't mess up movement. - m_fNextThink = time; - SetEternalThink( ); - UTIL_MarkForAssist( this, correctSpeed ); - } - else - { - // set nextthink as normal. - pev->nextthink = time; - m_fPevNextThink = m_fNextThink = pev->nextthink; - } -} - -//LRC - check in case the engine has changed our nextthink. (which it does -// on a depressingly frequent basis.) -// for some reason, this doesn't always produce perfect movement - but it's close -// enough for government work. (the player doesn't get stuck, at least.) -void CBaseEntity :: ThinkCorrection( void ) -{ - if (pev->nextthink != m_fPevNextThink) - { - // The engine has changed our nextthink, in its typical endearing way. - // Now we have to apply that change in the _right_ places. -// ALERT(at_console, "StoredThink corrected for %s \"%s\": %f -> %f\n", STRING(pev->classname), STRING(pev->targetname), m_fNextThink, m_fNextThink + pev->nextthink - m_fPevNextThink); - m_fNextThink += pev->nextthink - m_fPevNextThink; - m_fPevNextThink = pev->nextthink; - } -} - -// give health -int CBaseEntity :: TakeHealth( float flHealth, int bitsDamageType ) -{ - if (!pev->takedamage) return 0; - if ( pev->health >= pev->max_health ) return 0; - - pev->health += flHealth; - pev->health = min(pev->health, pev->max_health); - - return 1; -} - -int CBaseEntity :: TakeArmor( float flArmor ) -{ - if(!pev->takedamage) return 0; - if (pev->armorvalue >= MAX_NORMAL_BATTERY) return 0; - - pev->armorvalue += flArmor; - pev->armorvalue = min(pev->armorvalue, MAX_NORMAL_BATTERY); - - return 1; -} - -// inflict damage on this entity. bitsDamageType indicates type of damage inflicted, ie: DMG_CRUSH - -int CBaseEntity :: TakeDamage( entvars_t* pevInflictor, entvars_t* pevAttacker, float flDamage, int bitsDamageType ) -{ - Vector vecTemp; - - if (!pev->takedamage) - return 0; - - // UNDONE: some entity types may be immune or resistant to some bitsDamageType - - // if Attacker == Inflictor, the attack was a melee or other instant-hit attack. - // (that is, no actual entity projectile was involved in the attack so use the shooter's origin). - if ( pevAttacker == pevInflictor ) - { - vecTemp = pevInflictor->origin - ( VecBModelOrigin(pev) ); - } - else - // an actual missile was involved. - { - vecTemp = pevInflictor->origin - ( VecBModelOrigin(pev) ); - } - -// this global is still used for glass and other non-monster killables, along with decals. - g_vecAttackDir = vecTemp.Normalize(); - -// save damage based on the target's armor level - -// figure momentum add (don't let hurt brushes or other triggers move player) - if ((!FNullEnt(pevInflictor)) && (pev->movetype == MOVETYPE_WALK || pev->movetype == MOVETYPE_STEP) && (pevAttacker->solid != SOLID_TRIGGER) ) - { - Vector vecDir = pev->origin - (pevInflictor->absmin + pevInflictor->absmax) * 0.5; - vecDir = vecDir.Normalize(); - - float flForce = flDamage * ((32 * 32 * 72.0) / (pev->size.x * pev->size.y * pev->size.z)) * 5; - - if (flForce > 1000.0) - flForce = 1000.0; - pev->velocity = pev->velocity + vecDir * flForce; - } - -// do the damage - pev->health -= flDamage; - if (pev->health <= 0) - { - Killed( pevAttacker, GIB_NORMAL ); - return 0; - } - - return 1; -} - - -void CBaseEntity :: Killed( entvars_t *pevAttacker, int iGib ) -{ - pev->takedamage = DAMAGE_NO; - pev->deadflag = DEAD_DEAD; - UTIL_Remove( this ); -} - - -CBaseEntity *CBaseEntity::GetNextTarget( void ) -{ - if ( FStringNull( pev->target ) ) - return NULL; - return UTIL_FindEntityByTargetname( NULL, STRING(pev->target)); -} - -// Global Savedata for Delay -TYPEDESCRIPTION CBaseEntity::m_SaveData[] = -{ - DEFINE_FIELD( CBaseEntity, m_pGoalEnt, FIELD_CLASSPTR ), - - DEFINE_FIELD( CBaseEntity, m_MoveWith, FIELD_STRING ), //LRC - DEFINE_FIELD( CBaseEntity, m_pMoveWith, FIELD_CLASSPTR ), //LRC - DEFINE_FIELD( CBaseEntity, m_pChildMoveWith, FIELD_CLASSPTR ), //LRC - DEFINE_FIELD( CBaseEntity, m_pSiblingMoveWith, FIELD_CLASSPTR ), //LRC - - DEFINE_FIELD( CBaseEntity, m_iLFlags, FIELD_INTEGER ), //LRC - DEFINE_FIELD( CBaseEntity, m_iStyle, FIELD_INTEGER ), //LRC - DEFINE_FIELD( CBaseEntity, m_iClassType, FIELD_INTEGER ), - DEFINE_FIELD( CBaseEntity, m_vecOffsetOrigin, FIELD_VECTOR ), //LRC - DEFINE_FIELD( CBaseEntity, m_vecOffsetAngles, FIELD_VECTOR ), //LRC - DEFINE_FIELD( CBaseEntity, m_vecPostAssistOrg, FIELD_VECTOR ), //LRC - DEFINE_FIELD( CBaseEntity, m_vecPostAssistAng, FIELD_VECTOR ), //LRC - DEFINE_FIELD( CBaseEntity, m_activated, FIELD_BOOLEAN ), //LRC - DEFINE_FIELD( CBaseEntity, m_fNextThink, FIELD_TIME ), //LRC - DEFINE_FIELD( CBaseEntity, m_fPevNextThink, FIELD_TIME ), //LRC -// DEFINE_FIELD( CBaseEntity, m_pAssistLink, FIELD_CLASSPTR ), //LRC - don't save this, we'll just rebuild the list on restore - DEFINE_FIELD( CBaseEntity, m_vecPostAssistVel, FIELD_VECTOR ), //LRC - DEFINE_FIELD( CBaseEntity, m_vecPostAssistAVel, FIELD_VECTOR ), //LRC - - DEFINE_FIELD( CBaseEntity, m_pfnThink, FIELD_FUNCTION ), // UNDONE: Build table of these!!! - DEFINE_FIELD( CBaseEntity, m_pfnTouch, FIELD_FUNCTION ), - DEFINE_FIELD( CBaseEntity, m_pfnUse, FIELD_FUNCTION ), - DEFINE_FIELD( CBaseEntity, m_pfnBlocked, FIELD_FUNCTION ), -}; - - -int CBaseEntity::Save( CSave &save ) -{ - ThinkCorrection(); //LRC - - if ( save.WriteEntVars( "ENTVARS", pev ) ) - { - if (pev->targetname) - return save.WriteFields( STRING(pev->targetname), "BASE", this, m_SaveData, ARRAYSIZE(m_SaveData) ); - else - return save.WriteFields( STRING(pev->classname), "BASE", this, m_SaveData, ARRAYSIZE(m_SaveData) ); - } - - return 0; -} - -int CBaseEntity::Restore( CRestore &restore ) -{ - int status; - - status = restore.ReadEntVars( "ENTVARS", pev ); - if ( status ) - status = restore.ReadFields( "BASE", this, m_SaveData, ARRAYSIZE(m_SaveData) ); - - // restore edict class here - SetObjectClass( m_iClassType ); - - if ( pev->modelindex != 0 && !FStringNull(pev->model) ) - { - Vector mins, maxs; - mins = pev->mins; // Set model is about to destroy these - maxs = pev->maxs; - - - PRECACHE_MODEL( (char *)STRING(pev->model) ); - SET_MODEL(ENT(pev), STRING(pev->model)); - UTIL_SetSize(pev, mins, maxs); // Reset them - } - return status; -} - - -// Initialize absmin & absmax to the appropriate box -void SetObjectCollisionBox( entvars_t *pev ) -{ - if ( (pev->solid == SOLID_BSP) && - (pev->angles.x || pev->angles.y|| pev->angles.z) ) - { // expand for rotation - float max, v; - int i; - - max = 0; - for (i=0 ; i<3 ; i++) - { - v = fabs( ((float *)pev->mins)[i]); - if (v > max) - max = v; - v = fabs( ((float *)pev->maxs)[i]); - if (v > max) - max = v; - } - for (i=0 ; i<3 ; i++) - { - ((float *)pev->absmin)[i] = ((float *)pev->origin)[i] - max; - ((float *)pev->absmax)[i] = ((float *)pev->origin)[i] + max; - } - } - else - { - pev->absmin = pev->origin + pev->mins; - pev->absmax = pev->origin + pev->maxs; - } - - pev->absmin.x -= 1; - pev->absmin.y -= 1; - pev->absmin.z -= 1; - pev->absmax.x += 1; - pev->absmax.y += 1; - pev->absmax.z += 1; -} - - -void CBaseEntity::SetObjectCollisionBox( void ) -{ - ::SetObjectCollisionBox( pev ); -} - - -int CBaseEntity :: Intersects( CBaseEntity *pOther ) -{ - if ( pOther->pev->absmin.x > pev->absmax.x || - pOther->pev->absmin.y > pev->absmax.y || - pOther->pev->absmin.z > pev->absmax.z || - pOther->pev->absmax.x < pev->absmin.x || - pOther->pev->absmax.y < pev->absmin.y || - pOther->pev->absmax.z < pev->absmin.z ) - return 0; - return 1; -} - -void CBaseEntity :: MakeDormant( void ) -{ - SetBits( pev->flags, FL_DORMANT ); - - // Don't touch - pev->solid = SOLID_NOT; - // Don't move - pev->movetype = MOVETYPE_NONE; - // Don't draw - SetBits( pev->effects, EF_NODRAW ); - // Don't think - DontThink(); - // Relink - UTIL_SetOrigin( this, pev->origin ); -} - -int CBaseEntity :: IsDormant( void ) -{ - return FBitSet( pev->flags, FL_DORMANT ); -} - -BOOL CBaseEntity :: IsInWorld( void ) -{ - // position - if (pev->origin.x >= 4096) return FALSE; - if (pev->origin.y >= 4096) return FALSE; - if (pev->origin.z >= 4096) return FALSE; - if (pev->origin.x <= -4096) return FALSE; - if (pev->origin.y <= -4096) return FALSE; - if (pev->origin.z <= -4096) return FALSE; - // speed - if (pev->velocity.x >= 2000) return FALSE; - if (pev->velocity.y >= 2000) return FALSE; - if (pev->velocity.z >= 2000) return FALSE; - if (pev->velocity.x <= -2000) return FALSE; - if (pev->velocity.y <= -2000) return FALSE; - if (pev->velocity.z <= -2000) return FALSE; - - return TRUE; -} - -BOOL CBaseEntity::ShouldToggle( USE_TYPE useType, BOOL currentState ) -{ - if ( useType != USE_TOGGLE && useType != USE_SET ) - { - if ( (currentState && useType == USE_ON) || (!currentState && useType == USE_OFF) ) - return FALSE; - } - return TRUE; -} - -BOOL CBaseEntity::ShouldToggle( USE_TYPE useType ) -{ - STATE currentState = GetState(); - if ( useType != USE_TOGGLE && useType != USE_SET ) - { - switch(currentState) - { - case STATE_ON: - case STATE_TURN_ON: - if (useType == USE_ON) return FALSE; - break; - case STATE_OFF: - case STATE_TURN_OFF: - if (useType == USE_OFF) return FALSE; - break; - } - } - return TRUE; -} - - -int CBaseEntity :: DamageDecal( int bitsDamageType ) -{ - if ( pev->rendermode == kRenderTransAlpha ) - return -1; - - if ( pev->rendermode != kRenderNormal ) - return DECAL_BPROOF1; - - return DECAL_GUNSHOT1 + RANDOM_LONG(0,4); -} - - - -// NOTE: szName must be a pointer to constant memory, e.g. "monster_class" because the entity -// will keep a pointer to it after this call. -CBaseEntity * CBaseEntity::Create( char *szName, const Vector &vecOrigin, const Vector &vecAngles, edict_t *pentOwner ) -{ - edict_t *pent; - CBaseEntity *pEntity; - - pent = CREATE_NAMED_ENTITY( MAKE_STRING( szName )); - if ( FNullEnt( pent ) ) - { - ALERT ( at_debug, "NULL Ent in Create!\n" ); - return NULL; - } - pEntity = Instance( pent ); - pEntity->pev->owner = pentOwner; - pEntity->pev->origin = vecOrigin; - pEntity->pev->angles = vecAngles; - DispatchSpawn( pEntity->edict() ); - return pEntity; -} - - diff --git a/spirit/effects.cpp b/spirit/effects.cpp deleted file mode 100644 index c8d570d0..00000000 --- a/spirit/effects.cpp +++ /dev/null @@ -1,4755 +0,0 @@ -/*** -* -* Copyright (c) 1996-2002, 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. -* -****/ -#include "extdll.h" -#include "util.h" -#include "cbase.h" -#include "monsters.h" -#include "effects.h" -#include "weapons.h" -#include "decals.h" -#include "func_break.h" -#include "shake.h" -#include "player.h" //LRC - footstep stuff -#include "locus.h" //LRC - locus utilities -#include "movewith.h" //LRC - the DesiredThink system - -#define SF_GIBSHOOTER_REPEATABLE 1 // allows a gibshooter to be refired - -#define SF_FUNNEL_REVERSE 1 // funnel effect repels particles instead of attracting them. -#define SF_FUNNEL_REPEATABLE 2 // allows a funnel to be refired - - -//LRC - make info_target an entity class in its own right -class CInfoTarget : public CPointEntity -{ -public: - void Spawn( void ); - void Precache( void ); -}; - -LINK_ENTITY_TO_CLASS( info_target, CInfoTarget ); - -//LRC- force an info_target to use the sprite null.spr -#define SF_TARGET_HACK_VISIBLE 1 - -// Landmark class -void CInfoTarget :: Spawn( void ) -{ - //Precache(); - pev->solid = SOLID_NOT; - SetBits( m_iLFlags, LF_POINTENTITY ); - if (pev->spawnflags & SF_TARGET_HACK_VISIBLE) - { - SET_MODEL( ENT( pev ), "sprites/null.spr" ); - UTIL_SetSize( pev, g_vecZero, g_vecZero ); - } -} - -void CInfoTarget :: Precache( void ) -{ - if (pev->spawnflags & SF_TARGET_HACK_VISIBLE) - PRECACHE_MODEL("sprites/null.spr"); -} - - -class CBubbling : public CBaseEntity -{ -public: - void Spawn( void ); - void Precache( void ); - void KeyValue( KeyValueData *pkvd ); - - void EXPORT FizzThink( void ); - void Use( CBaseEntity *pActivator, CBaseEntity *pCaller, USE_TYPE useType, float value ); - - virtual int Save( CSave &save ); - virtual int Restore( CRestore &restore ); - virtual int ObjectCaps( void ) { return CBaseEntity::ObjectCaps() & ~FCAP_ACROSS_TRANSITION; } - static TYPEDESCRIPTION m_SaveData[]; - - int m_density; - int m_frequency; - int m_bubbleModel; - int m_state; - - virtual STATE GetState( void ) { return m_state?STATE_ON:STATE_OFF; }; -}; - -LINK_ENTITY_TO_CLASS( env_bubbles, CBubbling ); - -TYPEDESCRIPTION CBubbling::m_SaveData[] = -{ - DEFINE_FIELD( CBubbling, m_density, FIELD_INTEGER ), - DEFINE_FIELD( CBubbling, m_frequency, FIELD_INTEGER ), - DEFINE_FIELD( CBubbling, m_state, FIELD_INTEGER ), - // Let spawn restore this! - // DEFINE_FIELD( CBubbling, m_bubbleModel, FIELD_INTEGER ), -}; - -IMPLEMENT_SAVERESTORE( CBubbling, CBaseEntity ); - - -#define SF_BUBBLES_STARTOFF 0x0001 - -void CBubbling::Spawn( void ) -{ - Precache( ); - SET_MODEL( ENT(pev), STRING(pev->model) ); // Set size - - pev->solid = SOLID_NOT; // Remove model & collisions - pev->renderamt = 0; // The engine won't draw this model if this is set to 0 and blending is on - pev->rendermode = kRenderTransTexture; - int speed = pev->speed > 0 ? pev->speed : -pev->speed; - - // HACKHACK!!! - Speed in rendercolor - pev->rendercolor.x = speed >> 8; - pev->rendercolor.y = speed & 255; - pev->rendercolor.z = (pev->speed < 0) ? 1 : 0; - - - if ( !(pev->spawnflags & SF_BUBBLES_STARTOFF) ) - { - SetThink(&CBubbling:: FizzThink ); - SetNextThink( 2.0 ); - m_state = 1; - } - else - m_state = 0; -} - -void CBubbling::Precache( void ) -{ - m_bubbleModel = PRECACHE_MODEL("sprites/bubble.spr"); // Precache bubble sprite -} - - -void CBubbling::Use( CBaseEntity *pActivator, CBaseEntity *pCaller, USE_TYPE useType, float value ) -{ - if ( ShouldToggle( useType, m_state ) ) - m_state = !m_state; - - if ( m_state ) - { - SetThink(&CBubbling:: FizzThink ); - SetNextThink( 0.1 ); - } - else - { - SetThink( NULL ); - DontThink(); - } -} - - -void CBubbling::KeyValue( KeyValueData *pkvd ) -{ - if (FStrEq(pkvd->szKeyName, "density")) - { - m_density = atoi(pkvd->szValue); - pkvd->fHandled = TRUE; - } - else if (FStrEq(pkvd->szKeyName, "frequency")) - { - m_frequency = atoi(pkvd->szValue); - pkvd->fHandled = TRUE; - } - else if (FStrEq(pkvd->szKeyName, "current")) - { - pev->speed = atoi(pkvd->szValue); - pkvd->fHandled = TRUE; - } - else - CBaseEntity::KeyValue( pkvd ); -} - - -void CBubbling::FizzThink( void ) -{ - MESSAGE_BEGIN( MSG_PAS, gmsgTempEntity, VecBModelOrigin(pev) ); - WRITE_BYTE( TE_FIZZ ); - WRITE_SHORT( (short)ENTINDEX( edict() ) ); - WRITE_SHORT( (short)m_bubbleModel ); - WRITE_BYTE( m_density ); - MESSAGE_END(); - - if ( m_frequency > 19 ) // frequencies above 20 are treated as 20. - SetNextThink( 0.5 ); - else - SetNextThink( 2.5 - (0.1 * m_frequency) ); -} - -// -------------------------------------------------- -// -// Beams -// -// -------------------------------------------------- - -LINK_ENTITY_TO_CLASS( beam, CBeam ); - -void CBeam::Spawn( void ) -{ - pev->solid = SOLID_NOT; // Remove model & collisions - Precache( ); -} - -// These don't take attachments into account -const Vector &CBeam::GetStartPos( void ) -{ - int type = GetType(); - - if( type == BEAM_ENTS ) - { - edict_t *pent = GetStartEntity(); - - if ( pent ) - return pent->v.origin; - } - return pev->origin; -} - - -const Vector &CBeam::GetEndPos( void ) -{ - int type = GetType(); - - if( type == BEAM_ENTS || type == BEAM_ENTPOINT ) - { - edict_t *pent = GetEndEntity(); - - if ( pent ) - return pent->v.angles; - } - return pev->angles; -} - - -CBeam *CBeam::BeamCreate( const char *pSpriteName, int width ) -{ - // Create a new entity with CBeam private data - CBeam *pBeam = GetClassPtr( (CBeam *)NULL ); - pBeam->pev->classname = MAKE_STRING( "beam" ); - - pBeam->BeamInit( pSpriteName, width ); - - return pBeam; -} - - -void CBeam::BeamInit( const char *pSpriteName, int width ) -{ - SetObjectClass( ED_BEAM ); - - SetColor( 255, 255, 255 ); - SetBrightness( 255 ); - SetNoise( 0 ); - SetFrame( 0 ); - SetScrollRate( 0 ); - pev->model = MAKE_STRING( pSpriteName ); - SetTexture( PRECACHE_MODEL( (char *)pSpriteName ) ); - SetWidth( width ); - pev->skin = 0; - pev->sequence = 0; - pev->rendermode = 0; -} - - -void CBeam::PointsInit( const Vector &start, const Vector &end ) -{ - SetType( BEAM_POINTS ); - SetStartPos( start ); - SetEndPos( end ); - SetStartAttachment( 0 ); - SetEndAttachment( 0 ); - RelinkBeam(); -} - - -void CBeam::HoseInit( const Vector &start, const Vector &direction ) -{ - SetType( BEAM_HOSE ); - SetStartPos( start ); - SetEndPos( direction ); - SetStartAttachment( 0 ); - SetEndAttachment( 0 ); - RelinkBeam(); -} - - -void CBeam::PointEntInit( const Vector &start, edict_t *pEnt ) -{ - SetType( BEAM_ENTPOINT ); - SetStartPos( start ); - SetEndEntity( pEnt ); - SetStartAttachment( 0 ); - SetEndAttachment( 0 ); - RelinkBeam(); -} - -void CBeam::EntsInit( edict_t *pStart, edict_t *pEnd ) -{ - SetType( BEAM_ENTS ); - SetStartEntity( pStart ); - SetEndEntity( pEnd ); - SetStartAttachment( 0 ); - SetEndAttachment( 0 ); - RelinkBeam(); -} - - -void CBeam::RelinkBeam( void ) -{ - const Vector &startPos = GetStartPos(), &endPos = GetEndPos(); - - pev->mins.x = min( startPos.x, endPos.x ); - pev->mins.y = min( startPos.y, endPos.y ); - pev->mins.z = min( startPos.z, endPos.z ); - pev->maxs.x = max( startPos.x, endPos.x ); - pev->maxs.y = max( startPos.y, endPos.y ); - pev->maxs.z = max( startPos.z, endPos.z ); - pev->mins = pev->mins - pev->origin; - pev->maxs = pev->maxs - pev->origin; - - UTIL_SetSize( pev, pev->mins, pev->maxs ); - UTIL_SetOrigin( this, pev->origin ); -} - -#if 0 -void CBeam::SetObjectCollisionBox( void ) -{ - const Vector &startPos = GetStartPos(), &endPos = GetEndPos(); - - pev->absmin.x = min( startPos.x, endPos.x ); - pev->absmin.y = min( startPos.y, endPos.y ); - pev->absmin.z = min( startPos.z, endPos.z ); - pev->absmax.x = max( startPos.x, endPos.x ); - pev->absmax.y = max( startPos.y, endPos.y ); - pev->absmax.z = max( startPos.z, endPos.z ); -} -#endif - - -void CBeam::TriggerTouch( CBaseEntity *pOther ) -{ - if ( pOther->pev->flags & (FL_CLIENT | FL_MONSTER) ) - { - if ( pev->owner ) - { - CBaseEntity *pOwner = CBaseEntity::Instance(pev->owner); - pOwner->Use( pOther, this, USE_TOGGLE, 0 ); - } - ALERT( at_debug, "Firing targets!!!\n" ); - } -} - - -CBaseEntity *CBeam::RandomTargetname( const char *szName ) -{ - int total = 0; - - CBaseEntity *pEntity = NULL; - CBaseEntity *pNewEntity = NULL; - while ((pNewEntity = UTIL_FindEntityByTargetname( pNewEntity, szName )) != NULL) - { - total++; - if (RANDOM_LONG(0,total-1) < 1) - pEntity = pNewEntity; - } - return pEntity; -} - - -void CBeam::DoSparks( const Vector &start, const Vector &end ) -{ - if ( pev->spawnflags & (SF_BEAM_SPARKSTART|SF_BEAM_SPARKEND) ) - { - if ( pev->spawnflags & SF_BEAM_SPARKSTART ) - { - UTIL_Sparks( start ); - } - if ( pev->spawnflags & SF_BEAM_SPARKEND ) - { - UTIL_Sparks( end ); - } - } -} - - -class CLightning : public CBeam -{ -public: - void Spawn( void ); - void Precache( void ); - void KeyValue( KeyValueData *pkvd ); - void Activate( void ); - - void EXPORT StrikeThink( void ); - void EXPORT TripThink( void ); - void RandomArea( void ); - void RandomPoint( Vector &vecSrc ); - void Zap( const Vector &vecSrc, const Vector &vecDest ); - void EXPORT StrikeUse( CBaseEntity *pActivator, CBaseEntity *pCaller, USE_TYPE useType, float value ); - void EXPORT ToggleUse( CBaseEntity *pActivator, CBaseEntity *pCaller, USE_TYPE useType, float value ); - - inline BOOL ServerSide( void ) - { - if ( m_life == 0 && !(pev->spawnflags & SF_BEAM_RING) ) - return TRUE; - return FALSE; - } - - virtual int Save( CSave &save ); - virtual int Restore( CRestore &restore ); - static TYPEDESCRIPTION m_SaveData[]; - - void BeamUpdatePoints( void ); //LRC - void BeamUpdateVars( void ); - - virtual STATE GetState( void ) { return m_active?STATE_OFF:STATE_ON; }; - - int m_active; - int m_iszStartEntity; - int m_iszEndEntity; - float m_life; - int m_boltWidth; - int m_noiseAmplitude; - int m_brightness; - int m_speed; - float m_restrike; - int m_spriteTexture; - int m_iszSpriteName; - int m_frameStart; - - float m_radius; -}; - -LINK_ENTITY_TO_CLASS( env_lightning, CLightning ); -LINK_ENTITY_TO_CLASS( env_beam, CLightning ); - -// UNDONE: Jay -- This is only a test -#if _DEBUG -class CTripBeam : public CLightning -{ - void Spawn( void ); -}; -LINK_ENTITY_TO_CLASS( trip_beam, CTripBeam ); - -void CTripBeam::Spawn( void ) -{ - CLightning::Spawn(); - SetTouch(&CTripBeam:: TriggerTouch ); - pev->solid = SOLID_TRIGGER; - RelinkBeam(); -} -#endif - - - -TYPEDESCRIPTION CLightning::m_SaveData[] = -{ - DEFINE_FIELD( CLightning, m_active, FIELD_INTEGER ), - DEFINE_FIELD( CLightning, m_iszStartEntity, FIELD_STRING ), - DEFINE_FIELD( CLightning, m_iszEndEntity, FIELD_STRING ), - DEFINE_FIELD( CLightning, m_life, FIELD_FLOAT ), - DEFINE_FIELD( CLightning, m_boltWidth, FIELD_INTEGER ), - DEFINE_FIELD( CLightning, m_noiseAmplitude, FIELD_INTEGER ), - DEFINE_FIELD( CLightning, m_brightness, FIELD_INTEGER ), - DEFINE_FIELD( CLightning, m_speed, FIELD_INTEGER ), - DEFINE_FIELD( CLightning, m_restrike, FIELD_FLOAT ), - DEFINE_FIELD( CLightning, m_spriteTexture, FIELD_INTEGER ), - DEFINE_FIELD( CLightning, m_iszSpriteName, FIELD_STRING ), - DEFINE_FIELD( CLightning, m_frameStart, FIELD_INTEGER ), - DEFINE_FIELD( CLightning, m_radius, FIELD_FLOAT ), -}; - -IMPLEMENT_SAVERESTORE( CLightning, CBeam ); - - -void CLightning::Spawn( void ) -{ - if ( FStringNull( m_iszSpriteName ) ) - { - SetThink(&CLightning:: SUB_Remove ); - return; - } - pev->solid = SOLID_NOT; // Remove model & collisions - Precache( ); - - pev->dmgtime = gpGlobals->time; - - //LRC- a convenience for mappers. Will this mess anything up? - if (pev->rendercolor == g_vecZero) - pev->rendercolor = Vector(255, 255, 255); - - if (pev->frags == 0) - { - pev->frags = DMG_ENERGYBEAM; - } - - if ( ServerSide() ) - { - SetObjectClass( ED_BEAM ); - SetThink( NULL ); - if ( pev->dmg != 0 || !FStringNull(pev->target) ) - { - SetThink(&CLightning:: TripThink ); - SetNextThink( 0.1 ); - } - if ( pev->targetname ) - { - if ( !(pev->spawnflags & SF_BEAM_STARTON) ) - { - pev->effects = EF_NODRAW; - m_active = 0; - DontThink(); - } - else - m_active = 1; - - SetUse(&CLightning:: ToggleUse ); - } - } - else - { - m_active = 0; - if ( !FStringNull(pev->targetname) ) - { - SetUse(&CLightning:: StrikeUse ); - } - if ( FStringNull(pev->targetname) || FBitSet(pev->spawnflags, SF_BEAM_STARTON) ) - { - SetThink(&CLightning:: StrikeThink ); - SetNextThink( 1.0 ); - } - } -} - -void CLightning::Precache( void ) -{ - m_spriteTexture = PRECACHE_MODEL( (char *)STRING(m_iszSpriteName) ); - CBeam::Precache(); -} - - -void CLightning::Activate( void ) -{ - if ( ServerSide() ) - BeamUpdateVars(); - - CBeam::Activate(); -} - - -void CLightning::KeyValue( KeyValueData *pkvd ) -{ - if (FStrEq(pkvd->szKeyName, "LightningStart")) - { - m_iszStartEntity = ALLOC_STRING( pkvd->szValue ); - pkvd->fHandled = TRUE; - } - else if (FStrEq(pkvd->szKeyName, "LightningEnd")) - { - m_iszEndEntity = ALLOC_STRING( pkvd->szValue ); - pkvd->fHandled = TRUE; - } - else if (FStrEq(pkvd->szKeyName, "life")) - { - m_life = atof(pkvd->szValue); - pkvd->fHandled = TRUE; - } - else if (FStrEq(pkvd->szKeyName, "BoltWidth")) - { - m_boltWidth = atoi(pkvd->szValue); - pkvd->fHandled = TRUE; - } - else if (FStrEq(pkvd->szKeyName, "NoiseAmplitude")) - { - m_noiseAmplitude = atoi(pkvd->szValue); - pkvd->fHandled = TRUE; - } - else if (FStrEq(pkvd->szKeyName, "TextureScroll")) - { - m_speed = atoi(pkvd->szValue); - pkvd->fHandled = TRUE; - } - else if (FStrEq(pkvd->szKeyName, "StrikeTime")) - { - m_restrike = atof(pkvd->szValue); - pkvd->fHandled = TRUE; - } - else if (FStrEq(pkvd->szKeyName, "texture")) - { - m_iszSpriteName = ALLOC_STRING(pkvd->szValue); - pkvd->fHandled = TRUE; - } - else if (FStrEq(pkvd->szKeyName, "framestart")) - { - m_frameStart = atoi(pkvd->szValue); - pkvd->fHandled = TRUE; - } - else if (FStrEq(pkvd->szKeyName, "Radius")) - { - m_radius = atof( pkvd->szValue ); - pkvd->fHandled = TRUE; - } - else if (FStrEq(pkvd->szKeyName, "damage")) - { - pev->dmg = atof(pkvd->szValue); - pkvd->fHandled = TRUE; - } - else - CBeam::KeyValue( pkvd ); -} - - -void CLightning::ToggleUse( CBaseEntity *pActivator, CBaseEntity *pCaller, USE_TYPE useType, float value ) -{ - if ( !ShouldToggle( useType, m_active ) ) - return; - if ( m_active ) - { - m_active = 0; - SUB_UseTargets( this, USE_OFF, 0 ); //LRC - pev->effects |= EF_NODRAW; - DontThink(); - } - else - { - m_active = 1; - SUB_UseTargets( this, USE_ON, 0 ); //LRC - BeamUpdatePoints(); - pev->effects &= ~EF_NODRAW; - DoSparks( GetStartPos(), GetEndPos() ); - if ( pev->dmg > 0 ) - { - SetNextThink( 0 ); - pev->dmgtime = gpGlobals->time; - } - } -} - - -void CLightning::StrikeUse( CBaseEntity *pActivator, CBaseEntity *pCaller, USE_TYPE useType, float value ) -{ - if ( !ShouldToggle( useType, m_active ) ) - return; - - if ( m_active ) - { - m_active = 0; - SetThink( NULL ); - } - else - { - SetThink(&CLightning:: StrikeThink ); - SetNextThink( 0.1 ); - } - - if ( !FBitSet( pev->spawnflags, SF_BEAM_TOGGLE ) ) - SetUse( NULL ); -} - - -int IsPointEntity( CBaseEntity *pEnt ) -{ - if( pEnt->pev->modelindex && ( pEnt->m_iClassType != ED_BEAM )) - return 0; - else - return 1; - - return 0; -} - - -void CLightning::StrikeThink( void ) -{ - if ( m_life != 0 && m_restrike != -1) //LRC non-restriking beams! what an idea! - { - if ( pev->spawnflags & SF_BEAM_RANDOM ) - SetNextThink( m_life + RANDOM_FLOAT( 0, m_restrike ) ); - else - SetNextThink( m_life + m_restrike ); - } - m_active = 1; - - if (FStringNull(m_iszEndEntity)) - { - if (FStringNull(m_iszStartEntity)) - { - RandomArea( ); - } - else - { - CBaseEntity *pStart = RandomTargetname( STRING(m_iszStartEntity) ); - if (pStart != NULL) - RandomPoint( pStart->pev->origin ); - else - ALERT( at_debug, "env_beam: unknown entity \"%s\"\n", STRING(m_iszStartEntity) ); - } - return; - } - - CBaseEntity *pStart = RandomTargetname( STRING(m_iszStartEntity) ); - CBaseEntity *pEnd = RandomTargetname( STRING(m_iszEndEntity) ); - - if ( pStart != NULL && pEnd != NULL ) - { - MESSAGE_BEGIN( MSG_BROADCAST, gmsgTempEntity ); - if ( IsPointEntity( pStart ) || IsPointEntity( pEnd ) ) - { - if ( pev->spawnflags & SF_BEAM_RING) - { - // don't work - return; - } - if ( !IsPointEntity( pEnd ) ) // One point entity must be in pEnd - { - CBaseEntity *pTemp; - pTemp = pStart; - pStart = pEnd; - pEnd = pTemp; - } - if ( !IsPointEntity( pStart ) ) // One sided - { - WRITE_BYTE( TE_BEAMENTPOINT ); - WRITE_SHORT( pStart->entindex() ); - WRITE_COORD( pEnd->pev->origin.x); - WRITE_COORD( pEnd->pev->origin.y); - WRITE_COORD( pEnd->pev->origin.z); - } - else - { - WRITE_BYTE( TE_BEAMPOINTS); - WRITE_COORD( pStart->pev->origin.x); - WRITE_COORD( pStart->pev->origin.y); - WRITE_COORD( pStart->pev->origin.z); - WRITE_COORD( pEnd->pev->origin.x); - WRITE_COORD( pEnd->pev->origin.y); - WRITE_COORD( pEnd->pev->origin.z); - } - - - } - else - { - if ( pev->spawnflags & SF_BEAM_RING) - WRITE_BYTE( TE_BEAMRING ); - else - WRITE_BYTE( TE_BEAMENTS ); - WRITE_SHORT( pStart->entindex() ); - WRITE_SHORT( pEnd->entindex() ); - } - - WRITE_SHORT( m_spriteTexture ); - WRITE_BYTE( m_frameStart ); // framestart - WRITE_BYTE( (int)pev->framerate); // framerate - WRITE_BYTE( (int)(m_life*10.0) ); // life - WRITE_BYTE( m_boltWidth ); // width - WRITE_BYTE( m_noiseAmplitude ); // noise - WRITE_BYTE( (int)pev->rendercolor.x ); // r, g, b - WRITE_BYTE( (int)pev->rendercolor.y ); // r, g, b - WRITE_BYTE( (int)pev->rendercolor.z ); // r, g, b - WRITE_BYTE( pev->renderamt ); // brightness - WRITE_BYTE( m_speed ); // speed - MESSAGE_END(); - DoSparks( pStart->pev->origin, pEnd->pev->origin ); - if ( pev->dmg || !FStringNull(pev->target)) - { - TraceResult tr; - UTIL_TraceLine( pStart->pev->origin, pEnd->pev->origin, dont_ignore_monsters, NULL, &tr ); - if (pev->dmg) BeamDamageInstant( &tr, pev->dmg ); - - //LRC - tripbeams - CBaseEntity* pTrip; - if (!FStringNull(pev->target) && (pTrip = GetTripEntity( &tr )) != NULL) - FireTargets(STRING(pev->target), pTrip, this, USE_TOGGLE, 0); - } - } -} - - -CBaseEntity* CBeam::GetTripEntity( TraceResult *ptr ) -{ - CBaseEntity* pTrip; - - if (ptr->flFraction == 1.0 || ptr->pHit == NULL) - return NULL; - - pTrip = CBaseEntity::Instance(ptr->pHit); - if (pTrip == NULL) - return NULL; - - if ( FStringNull(pev->netname)) - { - if (pTrip->pev->flags & (FL_CLIENT | FL_MONSTER)) - return pTrip; - else - return NULL; - } - else if ( FClassnameIs(pTrip->pev, STRING(pev->netname) )) - return pTrip; - else if ( FStrEq( STRING(pTrip->pev->targetname), STRING(pev->netname) )) - return pTrip; - else - return NULL; -} - -void CBeam::BeamDamage( TraceResult *ptr ) -{ - RelinkBeam(); - if ( ptr->flFraction != 1.0 && ptr->pHit != NULL ) - { - CBaseEntity *pHit = CBaseEntity::Instance(ptr->pHit); - if ( pHit ) - { - if (pev->dmg > 0) - { if(pev->frags == 0)pev->frags = DMG_ENERGYBEAM; - ClearMultiDamage(); - pHit->TraceAttack( pev, pev->dmg * (gpGlobals->time - pev->dmgtime), (ptr->vecEndPos - pev->origin).Normalize(), ptr, pev->frags ); - ApplyMultiDamage( pev, pev ); - if ( pev->spawnflags & SF_BEAM_DECALS ) - { - if ( pHit->IsBSPModel() ) - UTIL_DecalTrace( ptr, DECAL_BIGSHOT1 + RANDOM_LONG(0,4) ); - } - } - else - { - //LRC - beams that heal people - pHit->TakeHealth( -(pev->dmg * (gpGlobals->time - pev->dmgtime)), DMG_GENERIC ); - } - } - } - pev->dmgtime = gpGlobals->time; -} - -//LRC - used to be DamageThink, but now it's more general. -void CLightning::TripThink( void ) -{ - SetNextThink( 0.1 ); - TraceResult tr; - - //ALERT(at_console,"TripThink\n"); - - UTIL_TraceLine( GetStartPos(), GetEndPos(), dont_ignore_monsters, NULL, &tr ); - BeamDamage( &tr ); - - //LRC - tripbeams - if (!FStringNull(pev->target)) - { - // nicked from monster_tripmine: - //HACKHACK Set simple box using this really nice global! - gpGlobals->trace_flags = FTRACE_SIMPLEBOX; - UTIL_TraceLine( GetStartPos(), GetEndPos(), dont_ignore_monsters, NULL, &tr ); - CBaseEntity *pTrip = GetTripEntity( &tr ); - if (pTrip) - { - if (!FBitSet(pev->spawnflags, SF_BEAM_TRIPPED)) - { - FireTargets(STRING(pev->target), pTrip, this, USE_TOGGLE, 0); - pev->spawnflags |= SF_BEAM_TRIPPED; - } - } - else - { - pev->spawnflags &= ~SF_BEAM_TRIPPED; - } - } -} - - - -void CLightning::Zap( const Vector &vecSrc, const Vector &vecDest ) -{ -#if 1 - MESSAGE_BEGIN( MSG_BROADCAST, gmsgTempEntity ); - WRITE_BYTE( TE_BEAMPOINTS); - WRITE_COORD(vecSrc.x); - WRITE_COORD(vecSrc.y); - WRITE_COORD(vecSrc.z); - WRITE_COORD(vecDest.x); - WRITE_COORD(vecDest.y); - WRITE_COORD(vecDest.z); - WRITE_SHORT( m_spriteTexture ); - WRITE_BYTE( m_frameStart ); // framestart - WRITE_BYTE( (int)pev->framerate); // framerate - WRITE_BYTE( (int)(m_life*10.0) ); // life - WRITE_BYTE( m_boltWidth ); // width - WRITE_BYTE( m_noiseAmplitude ); // noise - WRITE_BYTE( (int)pev->rendercolor.x ); // r, g, b - WRITE_BYTE( (int)pev->rendercolor.y ); // r, g, b - WRITE_BYTE( (int)pev->rendercolor.z ); // r, g, b - WRITE_BYTE( pev->renderamt ); // brightness - WRITE_BYTE( m_speed ); // speed - MESSAGE_END(); -#else - MESSAGE_BEGIN( MSG_BROADCAST, gmsgTempEntity ); - WRITE_BYTE(TE_LIGHTNING); - WRITE_COORD(vecSrc.x); - WRITE_COORD(vecSrc.y); - WRITE_COORD(vecSrc.z); - WRITE_COORD(vecDest.x); - WRITE_COORD(vecDest.y); - WRITE_COORD(vecDest.z); - WRITE_BYTE(10); - WRITE_BYTE(50); - WRITE_BYTE(40); - WRITE_SHORT(m_spriteTexture); - MESSAGE_END(); -#endif - DoSparks( vecSrc, vecDest ); -} - -void CLightning::RandomArea( void ) -{ - int iLoops = 0; - - for (iLoops = 0; iLoops < 10; iLoops++) - { - Vector vecSrc = pev->origin; - - Vector vecDir1 = Vector( RANDOM_FLOAT( -1.0, 1.0 ), RANDOM_FLOAT( -1.0, 1.0 ),RANDOM_FLOAT( -1.0, 1.0 ) ); - vecDir1 = vecDir1.Normalize(); - TraceResult tr1; - UTIL_TraceLine( vecSrc, vecSrc + vecDir1 * m_radius, ignore_monsters, ENT(pev), &tr1 ); - - if (tr1.flFraction == 1.0) - continue; - - Vector vecDir2; - do { - vecDir2 = Vector( RANDOM_FLOAT( -1.0, 1.0 ), RANDOM_FLOAT( -1.0, 1.0 ),RANDOM_FLOAT( -1.0, 1.0 ) ); - } while (DotProduct(vecDir1, vecDir2 ) > 0); - vecDir2 = vecDir2.Normalize(); - TraceResult tr2; - UTIL_TraceLine( vecSrc, vecSrc + vecDir2 * m_radius, ignore_monsters, ENT(pev), &tr2 ); - - if (tr2.flFraction == 1.0) - continue; - - if ((tr1.vecEndPos - tr2.vecEndPos).Length() < m_radius * 0.1) - continue; - - UTIL_TraceLine( tr1.vecEndPos, tr2.vecEndPos, ignore_monsters, ENT(pev), &tr2 ); - - if (tr2.flFraction != 1.0) - continue; - - Zap( tr1.vecEndPos, tr2.vecEndPos ); - - break; - } -} - - -void CLightning::RandomPoint( Vector &vecSrc ) -{ - int iLoops = 0; - - for (iLoops = 0; iLoops < 10; iLoops++) - { - Vector vecDir1 = Vector( RANDOM_FLOAT( -1.0, 1.0 ), RANDOM_FLOAT( -1.0, 1.0 ),RANDOM_FLOAT( -1.0, 1.0 ) ); - vecDir1 = vecDir1.Normalize(); - TraceResult tr1; - UTIL_TraceLine( vecSrc, vecSrc + vecDir1 * m_radius, ignore_monsters, ENT(pev), &tr1 ); - - if ((tr1.vecEndPos - vecSrc).Length() < m_radius * 0.1) - continue; - - if (tr1.flFraction == 1.0) - continue; - - Zap( vecSrc, tr1.vecEndPos ); - break; - } -} - - -// LRC: Called whenever the beam gets turned on, in case an alias changed or one of the points has moved. -void CLightning::BeamUpdatePoints( void ) -{ - int beamType; - int pointStart, pointEnd; - - CBaseEntity *pStart = UTIL_FindEntityByTargetname ( NULL, STRING(m_iszStartEntity) ); - CBaseEntity *pEnd = UTIL_FindEntityByTargetname ( NULL, STRING(m_iszEndEntity) ); - if (!pStart || !pEnd) return; - pointStart = IsPointEntity( pStart ); - pointEnd = IsPointEntity( pEnd ); - - beamType = BEAM_ENTS; - if ( pointStart || pointEnd ) - { - if ( !pointStart ) // One point entity must be in pStart - { - CBaseEntity *pTemp; - // Swap start & end - pTemp = pStart; - pStart = pEnd; - pEnd = pTemp; - int swap = pointStart; - pointStart = pointEnd; - pointEnd = swap; - } - if ( !pointEnd ) - beamType = BEAM_ENTPOINT; - else - beamType = BEAM_POINTS; - } - - SetType( beamType ); - if ( beamType == BEAM_POINTS || beamType == BEAM_ENTPOINT || beamType == BEAM_HOSE ) - { - SetStartPos( pStart->pev->origin ); - if ( beamType == BEAM_POINTS || beamType == BEAM_HOSE ) - SetEndPos( pEnd->pev->origin ); - else - SetEndEntity( ENT( pEnd->pev )); - } - else - { - SetStartEntity( ENT( pStart->pev )); - SetEndEntity( ENT( pEnd->pev )); - } - - RelinkBeam(); -} - -void CLightning::BeamUpdateVars( void ) -{ - pev->skin = 0; - pev->sequence = 0; - pev->rendermode = 0; - pev->model = m_iszSpriteName; - SetTexture( m_spriteTexture ); - - BeamUpdatePoints(); //LRC - - SetWidth( m_boltWidth ); - SetNoise( m_noiseAmplitude ); - SetFrame( m_frameStart ); - SetScrollRate( m_speed ); - if ( pev->spawnflags & SF_BEAM_SHADEIN ) - SetFlags( FBEAM_SHADEIN ); - else if ( pev->spawnflags & SF_BEAM_SHADEOUT ) - SetFlags( FBEAM_SHADEOUT ); - else if ( pev->spawnflags & SF_BEAM_SOLID ) - SetFlags( FBEAM_SOLID ); -} - - -LINK_ENTITY_TO_CLASS( env_laser, CLaser ); - -TYPEDESCRIPTION CLaser::m_SaveData[] = -{ - DEFINE_FIELD( CLaser, m_pStartSprite, FIELD_CLASSPTR ), - DEFINE_FIELD( CLaser, m_pEndSprite, FIELD_CLASSPTR ), - DEFINE_FIELD( CLaser, m_iszStartSpriteName, FIELD_STRING ), - DEFINE_FIELD( CLaser, m_iszEndSpriteName, FIELD_STRING ), - DEFINE_FIELD( CLaser, m_firePosition, FIELD_POSITION_VECTOR ), - DEFINE_FIELD( CLaser, m_iProjection, FIELD_INTEGER ), - DEFINE_FIELD( CLaser, m_iStoppedBy, FIELD_INTEGER ), - DEFINE_FIELD( CLaser, m_iszStartPosition, FIELD_STRING ), -}; - -IMPLEMENT_SAVERESTORE( CLaser, CBeam ); - -void CLaser::Spawn( void ) -{ - if ( FStringNull( pev->model ) ) - { - SetThink(&CLaser:: SUB_Remove ); - return; - } - - SetObjectClass( ED_BEAM ); - pev->solid = SOLID_NOT; // Remove model & collisions - Precache( ); - - SetThink(&CLaser:: StrikeThink ); -} - -void CLaser::PostSpawn( void ) -{ - if ( m_iszStartSpriteName ) - { - //LRC: allow the spritename to be the name of an env_sprite - CBaseEntity *pTemp = UTIL_FindEntityByTargetname(NULL, STRING(m_iszStartSpriteName)); - if (pTemp == NULL) - { - m_pStartSprite = CSprite::SpriteCreate( STRING(m_iszStartSpriteName), pev->origin, TRUE ); - if (m_pStartSprite) - m_pStartSprite->SetTransparency( kRenderGlow, pev->rendercolor.x, pev->rendercolor.y, pev->rendercolor.z, pev->renderamt, pev->renderfx ); - } - else if (!FClassnameIs(pTemp->pev, "env_sprite")) - { - ALERT(at_error, "env_laser \"%s\" found startsprite %s, but can't use: not an env_sprite\n", STRING(pev->targetname), STRING(m_iszStartSpriteName)); - m_pStartSprite = NULL; - } - else - { - // use an env_sprite defined by the mapper - m_pStartSprite = (CSprite*)pTemp; - m_pStartSprite->pev->movetype = MOVETYPE_NOCLIP; - } - } - else if ( pev->spawnflags & SF_LASER_INTERPOLATE) // interpolated lasers must have sprites at the start - { - m_pStartSprite = CSprite::SpriteCreate( "sprites/null.spr", pev->origin, TRUE ); - } - else - m_pStartSprite = NULL; - - - if ( m_iszEndSpriteName ) - { - CBaseEntity *pTemp = UTIL_FindEntityByTargetname(NULL, STRING(m_iszEndSpriteName)); - if (pTemp == NULL) - { - m_pEndSprite = CSprite::SpriteCreate( STRING(m_iszEndSpriteName), pev->origin, TRUE ); - if (m_pEndSprite) - m_pEndSprite->SetTransparency( kRenderGlow, pev->rendercolor.x, pev->rendercolor.y, pev->rendercolor.z, pev->renderamt, pev->renderfx ); - } - else if (!FClassnameIs(pTemp->pev, "env_sprite")) - { - ALERT(at_error, "env_laser \"%s\" found endsprite %s, but can't use: not an env_sprite\n", STRING(pev->targetname), STRING(m_iszEndSpriteName)); - m_pEndSprite = NULL; - } - else - { - // use an env_sprite defined by the mapper - m_pEndSprite = (CSprite*)pTemp; - m_pEndSprite->pev->movetype = MOVETYPE_NOCLIP; - } - } - else if ( pev->spawnflags & SF_LASER_INTERPOLATE) // interpolated lasers must have sprites at the end - { - m_pEndSprite = CSprite::SpriteCreate( "sprites/null.spr", pev->origin, TRUE ); - } - else - m_pEndSprite = NULL; - - //LRC - if ( m_pStartSprite && m_pEndSprite && pev->spawnflags & SF_LASER_INTERPOLATE ) - EntsInit( m_pStartSprite->edict(), m_pEndSprite->edict() ); - else PointsInit( pev->origin, pev->origin ); - - if ( pev->targetname && !(pev->spawnflags & SF_BEAM_STARTON) ) - TurnOff(); - else TurnOn(); -} - -void CLaser::Precache( void ) -{ - PRECACHE_MODEL( "sprites/null.spr" ); - pev->modelindex = PRECACHE_MODEL( (char *)STRING(pev->model) ); - if ( m_iszStartSpriteName ) - { - // UGLY HACK to check whether this is a filename: does it contain a dot? - const char *c = STRING(m_iszStartSpriteName); - while (*c) - { - if (*c == '.') - { - PRECACHE_MODEL( (char *)STRING(m_iszStartSpriteName) ); - break; - } - c++; // the magic word? - } - } - - if ( m_iszEndSpriteName ) - { - const char *c = STRING(m_iszEndSpriteName); - while (*c) - { - if (*c == '.') - { - PRECACHE_MODEL( (char *)STRING(m_iszEndSpriteName) ); - break; - } - c++; - } - } -} - - -void CLaser::KeyValue( KeyValueData *pkvd ) -{ - if (FStrEq(pkvd->szKeyName, "LaserStart")) - { - m_iszStartPosition = ALLOC_STRING( pkvd->szValue ); - pkvd->fHandled = TRUE; - } - else if (FStrEq(pkvd->szKeyName, "LaserTarget")) - { - pev->message = ALLOC_STRING( pkvd->szValue ); - pkvd->fHandled = TRUE; - } - else if (FStrEq(pkvd->szKeyName, "m_iTowardsMode")) - { - m_iTowardsMode = atoi(pkvd->szValue); - pkvd->fHandled = TRUE; - } - else if (FStrEq(pkvd->szKeyName, "width")) - { - SetWidth( atof(pkvd->szValue) ); - pkvd->fHandled = TRUE; - } - else if (FStrEq(pkvd->szKeyName, "NoiseAmplitude")) - { - SetNoise( atoi(pkvd->szValue) ); - pkvd->fHandled = TRUE; - } - else if (FStrEq(pkvd->szKeyName, "TextureScroll")) - { - SetScrollRate( atoi(pkvd->szValue) ); - pkvd->fHandled = TRUE; - } - else if (FStrEq(pkvd->szKeyName, "texture")) - { - pev->model = ALLOC_STRING(pkvd->szValue); - pkvd->fHandled = TRUE; - } - else if (FStrEq(pkvd->szKeyName, "StartSprite")) - { - m_iszStartSpriteName = ALLOC_STRING(pkvd->szValue); - pkvd->fHandled = TRUE; - } - else if (FStrEq(pkvd->szKeyName, "EndSprite")) - { - m_iszEndSpriteName = ALLOC_STRING(pkvd->szValue); - pkvd->fHandled = TRUE; - } - else if (FStrEq(pkvd->szKeyName, "framestart")) - { - pev->frame = atoi(pkvd->szValue); - pkvd->fHandled = TRUE; - } - else if (FStrEq(pkvd->szKeyName, "damage")) - { - pev->dmg = atof(pkvd->szValue); - pkvd->fHandled = TRUE; - } - else if (FStrEq(pkvd->szKeyName, "m_iProjection")) - { - m_iProjection = atoi(pkvd->szValue); - pkvd->fHandled = TRUE; - } - else if (FStrEq(pkvd->szKeyName, "m_iStoppedBy")) - { - m_iStoppedBy = atoi(pkvd->szValue); - pkvd->fHandled = TRUE; - } - else - CBeam::KeyValue( pkvd ); -} - - -void CLaser::TurnOff( void ) -{ - pev->effects |= EF_NODRAW; - DontThink(); - if ( m_pStartSprite ) - { - m_pStartSprite->TurnOff(); - UTIL_SetVelocity(m_pStartSprite, g_vecZero); //LRC - } - if ( m_pEndSprite ) - { - m_pEndSprite->TurnOff(); - UTIL_SetVelocity(m_pEndSprite, g_vecZero); //LRC - } -} - - -void CLaser::TurnOn( void ) -{ - pev->effects &= ~EF_NODRAW; - - if ( m_pStartSprite ) - m_pStartSprite->TurnOn(); - - if ( m_pEndSprite ) - m_pEndSprite->TurnOn(); - - pev->dmgtime = gpGlobals->time; - - if ( pev->spawnflags & SF_BEAM_SHADEIN ) - SetFlags( FBEAM_SHADEIN ); - else if ( pev->spawnflags & SF_BEAM_SHADEOUT ) - SetFlags( FBEAM_SHADEOUT ); - else if ( pev->spawnflags & SF_BEAM_SOLID ) - SetFlags( FBEAM_SOLID ); - - SetNextThink( 0 ); -} - - -void CLaser::Use( CBaseEntity *pActivator, CBaseEntity *pCaller, USE_TYPE useType, float value ) -{ - int active = (GetState() == STATE_ON); - - if ( !ShouldToggle( useType, active ) ) - return; - if ( active ) - TurnOff(); - else - TurnOn(); -} - - -void CLaser::FireAtPoint( Vector startpos, TraceResult &tr ) -{ - if (pev->spawnflags & SF_LASER_INTERPOLATE && m_pStartSprite && m_pEndSprite) - { - UTIL_SetVelocity(m_pStartSprite, (startpos - m_pStartSprite->pev->origin)*10 ); - UTIL_SetVelocity(m_pEndSprite, (tr.vecEndPos - m_pEndSprite->pev->origin)*10 ); - } - else - { - if (m_pStartSprite) - UTIL_AssignOrigin( m_pStartSprite, startpos ); - - if (m_pEndSprite) - UTIL_AssignOrigin( m_pEndSprite, tr.vecEndPos ); - - SetStartPos( startpos ); - SetEndPos( tr.vecEndPos ); - } - - BeamDamage( &tr ); - DoSparks( startpos, tr.vecEndPos ); - SetEndPos( tr.vecEndPos ); - BeamDamage( &tr ); - DoSparks( GetStartPos(), tr.vecEndPos ); -} - -void CLaser::StrikeThink( void ) -{ - Vector startpos = pev->origin; - if (m_iszStartPosition) - { - startpos = CalcLocus_Position(this, NULL, STRING(m_iszStartPosition)); - } - - if (m_iTowardsMode) - { - m_firePosition = startpos + CalcLocus_Velocity(this, NULL, STRING(pev->message)); - } - else - { - CBaseEntity *pEnd = RandomTargetname( STRING(pev->message) ); - - if ( pEnd ) m_firePosition = pEnd->pev->origin; - } - - TraceResult tr; - - //UTIL_TraceLine( pev->origin, m_firePosition, dont_ignore_monsters, NULL, &tr ); - IGNORE_GLASS iIgnoreGlass; - if (m_iStoppedBy % 2) // if it's an odd number - iIgnoreGlass = ignore_glass; - else - iIgnoreGlass = dont_ignore_glass; - - IGNORE_MONSTERS iIgnoreMonsters; - if (m_iStoppedBy <= 1) - iIgnoreMonsters = dont_ignore_monsters; - else if (m_iStoppedBy <= 3) - iIgnoreMonsters = missile; - else - iIgnoreMonsters = ignore_monsters; - - if ( m_iProjection ) - { - Vector vecProject = startpos + 4096*((m_firePosition - startpos).Normalize()); - UTIL_TraceLine( startpos, vecProject, iIgnoreMonsters, iIgnoreGlass, NULL, &tr ); - } - else - { - UTIL_TraceLine( startpos, m_firePosition, iIgnoreMonsters, iIgnoreGlass, NULL, &tr ); - } - - FireAtPoint( startpos, tr ); - - //LRC - tripbeams - if (pev->target) - { - // nicked from monster_tripmine: - //HACKHACK Set simple box using this really nice global! - gpGlobals->trace_flags = FTRACE_SIMPLEBOX; - UTIL_TraceLine( startpos, m_firePosition, dont_ignore_monsters, NULL, &tr ); - CBaseEntity *pTrip = GetTripEntity( &tr ); - if (pTrip) - { - if (!FBitSet(pev->spawnflags, SF_BEAM_TRIPPED)) - { - FireTargets(STRING(pev->target), pTrip, this, USE_TOGGLE, 0); - pev->spawnflags |= SF_BEAM_TRIPPED; - } - } - else - { - pev->spawnflags &= ~SF_BEAM_TRIPPED; - } - } - SetNextThink( 0.1 ); -} - - -class CGlow : public CPointEntity -{ -public: - void Spawn( void ); - void Think( void ); - void Animate( float frames ); - virtual int Save( CSave &save ); - virtual int Restore( CRestore &restore ); - static TYPEDESCRIPTION m_SaveData[]; - - float m_lastTime; - float m_maxFrame; -}; - -LINK_ENTITY_TO_CLASS( env_glow, CGlow ); - -TYPEDESCRIPTION CGlow::m_SaveData[] = -{ - DEFINE_FIELD( CGlow, m_lastTime, FIELD_TIME ), - DEFINE_FIELD( CGlow, m_maxFrame, FIELD_FLOAT ), -}; - -IMPLEMENT_SAVERESTORE( CGlow, CPointEntity ); - -void CGlow::Spawn( void ) -{ - pev->solid = SOLID_NOT; - pev->movetype = MOVETYPE_NONE; - pev->effects = 0; - pev->frame = 0; - - PRECACHE_MODEL( (char *)STRING(pev->model) ); - SET_MODEL( ENT(pev), STRING(pev->model) ); - - m_maxFrame = (float) MODEL_FRAMES( pev->modelindex ) - 1; - if ( m_maxFrame > 1.0 && pev->framerate != 0 ) - SetNextThink( 0.1 ); - - m_lastTime = gpGlobals->time; -} - - -void CGlow::Think( void ) -{ - Animate( pev->framerate * (gpGlobals->time - m_lastTime) ); - - SetNextThink( 0.1 ); - m_lastTime = gpGlobals->time; -} - - -void CGlow::Animate( float frames ) -{ - if ( m_maxFrame > 0 ) - pev->frame = fmod( pev->frame + frames, m_maxFrame ); -} - - -LINK_ENTITY_TO_CLASS( env_sprite, CSprite ); - -TYPEDESCRIPTION CSprite::m_SaveData[] = -{ - DEFINE_FIELD( CSprite, m_lastTime, FIELD_TIME ), - DEFINE_FIELD( CSprite, m_maxFrame, FIELD_FLOAT ), -}; - -IMPLEMENT_SAVERESTORE( CSprite, CPointEntity ); - -void CSprite::Spawn( void ) -{ - pev->solid = SOLID_NOT; - pev->movetype = MOVETYPE_NONE; - pev->effects = 0; - pev->frame = 0; - - Precache(); - SET_MODEL( ENT(pev), STRING(pev->model) ); - - m_maxFrame = (float) MODEL_FRAMES( pev->modelindex ) - 1; - if ( pev->targetname && !(pev->spawnflags & SF_SPRITE_STARTON) ) - TurnOff(); - else - TurnOn(); - - // Worldcraft only sets y rotation, copy to Z - if ( pev->angles.y != 0 && pev->angles.z == 0 ) - { - pev->angles.z = pev->angles.y; - pev->angles.y = 0; - } -} - - -void CSprite::Precache( void ) -{ - PRECACHE_MODEL( (char *)STRING(pev->model) ); - - // Reset attachment after save/restore - if( !pev->aiment ) - { - // Clear attachment - pev->colormap = 0; - } -} - - -void CSprite::SpriteInit( const char *pSpriteName, const Vector &origin ) -{ - pev->model = MAKE_STRING(pSpriteName); - pev->origin = origin; - Spawn(); -} - -CSprite *CSprite::SpriteCreate( const char *pSpriteName, const Vector &origin, BOOL animate ) -{ - CSprite *pSprite = GetClassPtr( (CSprite *)NULL ); - pSprite->SpriteInit( pSpriteName, origin ); - pSprite->pev->classname = MAKE_STRING("env_sprite"); - pSprite->pev->solid = SOLID_NOT; - pSprite->pev->movetype = MOVETYPE_NOCLIP; - if ( animate ) - pSprite->TurnOn(); - - return pSprite; -} - - -void CSprite::AnimateThink( void ) -{ - Animate( pev->framerate * (gpGlobals->time - m_lastTime) ); - - SetNextThink( 0.1 ); - m_lastTime = gpGlobals->time; -} - -void CSprite::AnimateUntilDead( void ) -{ - if ( gpGlobals->time > pev->dmgtime ) - UTIL_Remove(this); - else - { - AnimateThink(); - SetNextThink( 0 ); - } -} - -void CSprite::Expand( float scaleSpeed, float fadeSpeed ) -{ - pev->speed = scaleSpeed; - pev->health = fadeSpeed; - SetThink(&CSprite:: ExpandThink ); - - SetNextThink( 0 ); - m_lastTime = gpGlobals->time; -} - - -void CSprite::ExpandThink( void ) -{ - float frametime = gpGlobals->time - m_lastTime; - pev->scale += pev->speed * frametime; - pev->renderamt -= pev->health * frametime; - if ( pev->renderamt <= 0 ) - { - pev->renderamt = 0; - UTIL_Remove( this ); - } - else - { - SetNextThink( 0.1 ); - m_lastTime = gpGlobals->time; - } -} - - -void CSprite::Animate( float frames ) -{ - pev->frame += frames; - if ( pev->frame > m_maxFrame ) - { - if ( pev->spawnflags & SF_SPRITE_ONCE ) - { - TurnOff(); - } - else - { - if ( m_maxFrame > 0 ) - pev->frame = fmod( pev->frame, m_maxFrame ); - } - } -} - - -void CSprite::TurnOff( void ) -{ - pev->effects = EF_NODRAW; - DontThink(); -} - - -void CSprite::TurnOn( void ) -{ - if (pev->message) - { - CBaseEntity *pTemp = UTIL_FindEntityByTargetname(NULL, STRING(pev->message)); - if (pTemp) - SetAttachment(pTemp->edict(), pev->frags); - else - return; - } - pev->effects = 0; - if ( (pev->framerate && m_maxFrame > 1.0) || (pev->spawnflags & SF_SPRITE_ONCE) ) - { - SetThink(&CSprite:: AnimateThink ); - SetNextThink( 0 ); - m_lastTime = gpGlobals->time; - } - pev->frame = 0; -} - - -void CSprite::Use( CBaseEntity *pActivator, CBaseEntity *pCaller, USE_TYPE useType, float value ) -{ - int on = pev->effects != EF_NODRAW; - if ( ShouldToggle( useType, on ) ) - { - if ( on ) - { - SUB_UseTargets( this, USE_OFF, 0 ); //LRC - TurnOff(); - } - else - { - SUB_UseTargets( this, USE_ON, 0 ); //LRC - TurnOn(); - } - } -} - -//================================================================= -// env_model: like env_sprite, except you can specify a sequence. -//================================================================= -#define SF_ENVMODEL_OFF 1 -#define SF_ENVMODEL_DROPTOFLOOR 2 -#define SF_ENVMODEL_SOLID 4 - -class CEnvModel : public CBaseAnimating -{ - void Spawn( void ); - void Precache( void ); - void EXPORT Think( void ); - void KeyValue( KeyValueData *pkvd ); - STATE GetState( void ); - void Use( CBaseEntity *pActivator, CBaseEntity *pCaller, USE_TYPE useType, float value ); - virtual int ObjectCaps( void ) { return CBaseEntity :: ObjectCaps() & ~FCAP_ACROSS_TRANSITION; } - - virtual int Save( CSave &save ); - virtual int Restore( CRestore &restore ); - static TYPEDESCRIPTION m_SaveData[]; - - void SetSequence( void ); - - string_t m_iszSequence_On; - string_t m_iszSequence_Off; - int m_iAction_On; - int m_iAction_Off; -}; - -TYPEDESCRIPTION CEnvModel::m_SaveData[] = -{ - DEFINE_FIELD( CEnvModel, m_iszSequence_On, FIELD_STRING ), - DEFINE_FIELD( CEnvModel, m_iszSequence_Off, FIELD_STRING ), - DEFINE_FIELD( CEnvModel, m_iAction_On, FIELD_INTEGER ), - DEFINE_FIELD( CEnvModel, m_iAction_Off, FIELD_INTEGER ), -}; - -IMPLEMENT_SAVERESTORE( CEnvModel, CBaseAnimating ); -LINK_ENTITY_TO_CLASS( env_model, CEnvModel ); - -void CEnvModel::KeyValue( KeyValueData *pkvd ) -{ - if (FStrEq(pkvd->szKeyName, "m_iszSequence_On")) - { - m_iszSequence_On = ALLOC_STRING(pkvd->szValue); - pkvd->fHandled = TRUE; - } - else if (FStrEq(pkvd->szKeyName, "m_iszSequence_Off")) - { - m_iszSequence_Off = ALLOC_STRING(pkvd->szValue); - pkvd->fHandled = TRUE; - } - else if (FStrEq(pkvd->szKeyName, "m_iAction_On")) - { - m_iAction_On = atoi(pkvd->szValue); - pkvd->fHandled = TRUE; - } - else if (FStrEq(pkvd->szKeyName, "m_iAction_Off")) - { - m_iAction_Off = atoi(pkvd->szValue); - pkvd->fHandled = TRUE; - } - else - { - CBaseAnimating::KeyValue( pkvd ); - } -} - -void CEnvModel :: Spawn( void ) -{ - Precache(); - SET_MODEL( ENT(pev), STRING(pev->model) ); - UTIL_SetOrigin(this, pev->origin); - -// UTIL_AssignOrigin(this, pev->angles); //AJH - WTF is this here for? - - if (pev->spawnflags & SF_ENVMODEL_SOLID) - { - pev->solid = SOLID_SLIDEBOX; - UTIL_SetSize(pev, Vector(-10, -10, -10), Vector(10, 10, 10)); //LRCT - } - - if (pev->spawnflags & SF_ENVMODEL_DROPTOFLOOR) - { - pev->origin.z += 1; - DROP_TO_FLOOR ( ENT(pev) ); - } - SetBoneController( 0, 0 ); - SetBoneController( 1, 0 ); - - SetSequence(); - - SetNextThink( 0.1 ); -} - -void CEnvModel::Precache( void ) -{ - PRECACHE_MODEL( (char *)STRING(pev->model) ); -} - -STATE CEnvModel::GetState( void ) -{ - if (pev->spawnflags & SF_ENVMODEL_OFF) - return STATE_OFF; - else - return STATE_ON; -} - -void CEnvModel::Use( CBaseEntity *pActivator, CBaseEntity *pCaller, USE_TYPE useType, float value ) -{ - if (ShouldToggle(useType, !(pev->spawnflags & SF_ENVMODEL_OFF))) - { - if (pev->spawnflags & SF_ENVMODEL_OFF) - pev->spawnflags &= ~SF_ENVMODEL_OFF; - else - pev->spawnflags |= SF_ENVMODEL_OFF; - - SetSequence(); - SetNextThink( 0.1 ); - } -} - -void CEnvModel::Think( void ) -{ - int iTemp; - -// ALERT(at_console, "env_model Think fr=%f\n", pev->framerate); - - StudioFrameAdvance ( ); // set m_fSequenceFinished if necessary - -// if (m_fSequenceLoops) -// { -// SetNextThink( 1E6 ); -// return; // our work here is done. -// } - if (m_fSequenceFinished && !m_fSequenceLoops) - { - if (pev->spawnflags & SF_ENVMODEL_OFF) - iTemp = m_iAction_Off; - else - iTemp = m_iAction_On; - - switch (iTemp) - { -// case 1: // loop -// pev->animtime = gpGlobals->time; -// m_fSequenceFinished = FALSE; -// m_flLastEventCheck = gpGlobals->time; -// pev->frame = 0; -// break; - case 2: // change state - if (pev->spawnflags & SF_ENVMODEL_OFF) - pev->spawnflags &= ~SF_ENVMODEL_OFF; - else - pev->spawnflags |= SF_ENVMODEL_OFF; - SetSequence(); - break; - default: //remain frozen - return; - } - } - SetNextThink( 0.1 ); -} - -void CEnvModel :: SetSequence( void ) -{ - int iszSeq; - - if (pev->spawnflags & SF_ENVMODEL_OFF) - iszSeq = m_iszSequence_Off; - else - iszSeq = m_iszSequence_On; - - if (!iszSeq) - return; - pev->sequence = LookupSequence( STRING( iszSeq )); - - if (pev->sequence == -1) - { - if (pev->targetname) - ALERT( at_error, "env_model %s: unknown sequence \"%s\"\n", STRING( pev->targetname ), STRING( iszSeq )); - else - ALERT( at_error, "env_model: unknown sequence \"%s\"\n", STRING( pev->targetname ), STRING( iszSeq )); - pev->sequence = 0; - } - - pev->frame = 0; - ResetSequenceInfo( ); - - if (pev->spawnflags & SF_ENVMODEL_OFF) - { - if (m_iAction_Off == 1) - m_fSequenceLoops = 1; - else - m_fSequenceLoops = 0; - } - else - { - if (m_iAction_On == 1) - m_fSequenceLoops = 1; - else - m_fSequenceLoops = 0; - } -} - - -#define SF_GIBSHOOTER_REPEATABLE 1 // allows a gibshooter to be refired -#define SF_GIBSHOOTER_DEBUG 4 //LRC - -class CGibShooter : public CBaseDelay -{ -public: - virtual void Spawn( void ); - void Precache( void ); - void KeyValue( KeyValueData *pkvd ); - void EXPORT ShootThink( void ); - void Use( CBaseEntity *pActivator, CBaseEntity *pCaller, USE_TYPE useType, float value ); - - virtual CBaseEntity *CreateGib( Vector vecPos, Vector vecVel ); - - virtual int Save( CSave &save ); - virtual int Restore( CRestore &restore ); - static TYPEDESCRIPTION m_SaveData[]; - - int m_iGibs; - int m_iGibCapacity; - int m_iGibMaterial; - int m_iGibModelIndex; -// float m_flGibVelocity; - float m_flVariance; - float m_flGibLife; - int m_iszTargetname; - int m_iszPosition; - int m_iszVelocity; - int m_iszVelFactor; - int m_iszSpawnTarget; - int m_iBloodColor; -}; - -TYPEDESCRIPTION CGibShooter::m_SaveData[] = -{ - DEFINE_FIELD( CGibShooter, m_iGibs, FIELD_INTEGER ), - DEFINE_FIELD( CGibShooter, m_iGibCapacity, FIELD_INTEGER ), - DEFINE_FIELD( CGibShooter, m_iGibMaterial, FIELD_INTEGER ), - DEFINE_FIELD( CGibShooter, m_iGibModelIndex, FIELD_INTEGER ), -// DEFINE_FIELD( CGibShooter, m_flGibVelocity, FIELD_FLOAT ), - DEFINE_FIELD( CGibShooter, m_flVariance, FIELD_FLOAT ), - DEFINE_FIELD( CGibShooter, m_flGibLife, FIELD_FLOAT ), - DEFINE_FIELD( CGibShooter, m_iszTargetname, FIELD_STRING), - DEFINE_FIELD( CGibShooter, m_iszPosition, FIELD_STRING), - DEFINE_FIELD( CGibShooter, m_iszVelocity, FIELD_STRING), - DEFINE_FIELD( CGibShooter, m_iszVelFactor, FIELD_STRING), - DEFINE_FIELD( CGibShooter, m_iszSpawnTarget, FIELD_STRING), - DEFINE_FIELD( CGibShooter, m_iBloodColor, FIELD_INTEGER ), -}; - -IMPLEMENT_SAVERESTORE( CGibShooter, CBaseDelay ); -LINK_ENTITY_TO_CLASS( gibshooter, CGibShooter ); - - -void CGibShooter :: Precache ( void ) -{ - if ( g_Language == LANGUAGE_GERMAN ) - { - m_iGibModelIndex = PRECACHE_MODEL ("models/germanygibs.mdl"); - } - else if (m_iBloodColor == BLOOD_COLOR_YELLOW) - { - m_iGibModelIndex = PRECACHE_MODEL ("models/agibs.mdl"); - } - else - { - m_iGibModelIndex = PRECACHE_MODEL ("models/hgibs.mdl"); - } -} - - -void CGibShooter::KeyValue( KeyValueData *pkvd ) -{ - if (FStrEq(pkvd->szKeyName, "m_iGibs")) - { - m_iGibs = m_iGibCapacity = atoi(pkvd->szValue); - pkvd->fHandled = TRUE; - } - else if (FStrEq(pkvd->szKeyName, "m_flVelocity")) - { - m_iszVelFactor = ALLOC_STRING(pkvd->szValue); - pkvd->fHandled = TRUE; - } - else if (FStrEq(pkvd->szKeyName, "m_flVariance")) - { - m_flVariance = atof(pkvd->szValue); - pkvd->fHandled = TRUE; - } - else if (FStrEq(pkvd->szKeyName, "m_flGibLife")) - { - m_flGibLife = atof(pkvd->szValue); - pkvd->fHandled = TRUE; - } - else if (FStrEq(pkvd->szKeyName, "m_iszTargetName")) - { - m_iszTargetname = ALLOC_STRING(pkvd->szValue); - pkvd->fHandled = TRUE; - } - else if (FStrEq(pkvd->szKeyName, "m_iszPosition")) - { - m_iszPosition = ALLOC_STRING(pkvd->szValue); - pkvd->fHandled = TRUE; - } - else if (FStrEq(pkvd->szKeyName, "m_iszVelocity")) - { - m_iszVelocity = ALLOC_STRING(pkvd->szValue); - pkvd->fHandled = TRUE; - } - else if (FStrEq(pkvd->szKeyName, "m_iszVelFactor")) - { - m_iszVelFactor = ALLOC_STRING(pkvd->szValue); - pkvd->fHandled = TRUE; - } - else if (FStrEq(pkvd->szKeyName, "m_iszSpawnTarget")) - { - m_iszSpawnTarget = ALLOC_STRING(pkvd->szValue); - pkvd->fHandled = TRUE; - } - else if (FStrEq(pkvd->szKeyName, "m_iBloodColor")) - { - m_iBloodColor = atoi(pkvd->szValue); - pkvd->fHandled = TRUE; - } - else - { - CBaseDelay::KeyValue( pkvd ); - } -} - -void CGibShooter::Use( CBaseEntity *pActivator, CBaseEntity *pCaller, USE_TYPE useType, float value ) -{ - m_hActivator = pActivator; - SetThink(&CGibShooter:: ShootThink ); - SetNextThink( 0 ); -} - -void CGibShooter::Spawn( void ) -{ - Precache(); - - pev->solid = SOLID_NOT; - pev->effects = EF_NODRAW; - - if ( m_flDelay == 0 ) - { - m_flDelay = 0.1; - } - - if ( m_flGibLife == 0 ) - { - m_flGibLife = 25; - } - - SetMovedir ( pev ); -// if (pev->body == 0) - pev->body = MODEL_FRAMES( m_iGibModelIndex ); -} - - -CBaseEntity *CGibShooter :: CreateGib ( Vector vecPos, Vector vecVel ) -{ - if ( CVAR_GET_FLOAT("violence_hgibs") == 0 ) - return NULL; - - CGib *pGib = GetClassPtr( (CGib *)NULL ); -// if (pGib) -// ALERT(at_console, "Gib created ok\n"); - - pGib->pev->origin = vecPos; - pGib->pev->velocity = vecVel; - - if (m_iBloodColor == BLOOD_COLOR_YELLOW) - { - pGib->Spawn( "models/agibs.mdl" ); - pGib->m_bloodColor = BLOOD_COLOR_YELLOW; - } - else if (m_iBloodColor) - { - pGib->Spawn( "models/hgibs.mdl" ); - pGib->m_bloodColor = m_iBloodColor; - } - else - { - pGib->Spawn( "models/hgibs.mdl" ); - pGib->m_bloodColor = BLOOD_COLOR_RED; - } - - if ( pev->body <= 1 ) - { - ALERT ( at_aiconsole, "GibShooter Body is <= 1!\n" ); - } - - pGib->pev->body = RANDOM_LONG ( 1, pev->body - 1 );// avoid throwing random amounts of the 0th gib. (skull). - - float thinkTime = pGib->m_fNextThink - gpGlobals->time; - - pGib->m_lifeTime = (m_flGibLife * RANDOM_FLOAT( 0.95, 1.05 )); // +/- 5% - if ( pGib->m_lifeTime < thinkTime ) - { - pGib->SetNextThink( pGib->m_lifeTime ); - pGib->m_lifeTime = 0; - } - - pGib->pev->avelocity.x = RANDOM_FLOAT ( 100, 200 ); - pGib->pev->avelocity.y = RANDOM_FLOAT ( 100, 300 ); - - return pGib; -} - - -void CGibShooter :: ShootThink ( void ) -{ - int i; -/* if (m_flDelay == 0) // LRC - delay is 0, fire them all at once. - { - i = m_iGibs; - } - else - { -*/ i = 1; - SetNextThink( m_flDelay ); -// } - - while (i > 0) - { - Vector vecShootDir; - Vector vecPos; - float flGibVelocity; - if (!FStringNull(m_iszVelFactor)) - flGibVelocity = CalcLocus_Ratio(m_hActivator, STRING(m_iszVelFactor)); - else - flGibVelocity = 1; - - if (!FStringNull(m_iszVelocity)) - { - vecShootDir = CalcLocus_Velocity(this, m_hActivator, STRING(m_iszVelocity)); - flGibVelocity = flGibVelocity * vecShootDir.Length(); - vecShootDir = vecShootDir.Normalize(); - } - else - vecShootDir = pev->movedir; - - vecShootDir = vecShootDir + gpGlobals->v_right * RANDOM_FLOAT( -1, 1) * m_flVariance;; - vecShootDir = vecShootDir + gpGlobals->v_forward * RANDOM_FLOAT( -1, 1) * m_flVariance;; - vecShootDir = vecShootDir + gpGlobals->v_up * RANDOM_FLOAT( -1, 1) * m_flVariance;; - - vecShootDir = vecShootDir.Normalize(); - - if (!FStringNull(m_iszPosition)) - vecPos = CalcLocus_Position(this, m_hActivator, STRING(m_iszPosition)); - else - vecPos = pev->origin; - CBaseEntity *pGib = CreateGib(vecPos, vecShootDir * flGibVelocity); - - if ( pGib ) - { - pGib->pev->targetname = m_iszTargetname; -// pGib->pev->velocity = vecShootDir * flGibVelocity; - - if (pev->spawnflags & SF_GIBSHOOTER_DEBUG) - ALERT(at_debug, "DEBUG: %s \"%s\" creates a shot at %f %f %f; vel %f %f %f; pos \"%s\"\n", STRING(pev->classname), STRING(pev->targetname), pGib->pev->origin.x, pGib->pev->origin.y, pGib->pev->origin.z, pGib->pev->velocity.x, pGib->pev->velocity.y, pGib->pev->velocity.z, STRING(m_iszPosition)); - - if (m_iszSpawnTarget) - FireTargets( STRING(m_iszSpawnTarget), pGib, this, USE_TOGGLE, 0 ); - } - - i--; - m_iGibs--; - } - - if ( m_iGibs <= 0 ) - { - if ( pev->spawnflags & SF_GIBSHOOTER_REPEATABLE ) - { - m_iGibs = m_iGibCapacity; - SetThink ( NULL ); - DontThink(); - } - else - { - SetThink(&CGibShooter :: SUB_Remove ); - SetNextThink( 0 ); - } - } -} - - -// Shooter particle -class CShot : public CSprite -{ -public: - void Touch ( CBaseEntity *pOther ); -}; - -void CShot :: Touch ( CBaseEntity *pOther ) -{ - if (pev->teleport_time > gpGlobals->time) - return; - // don't fire too often in collisions! - // teleport_time is the soonest this can be touched again. - pev->teleport_time = gpGlobals->time + 0.1; - - if (pev->netname) - FireTargets( STRING(pev->netname), this, this, USE_TOGGLE, 0 ); - if (pev->message && pOther && pOther != g_pWorld) - FireTargets( STRING(pev->message), pOther, this, USE_TOGGLE, 0 ); -} - -class CEnvShooter : public CGibShooter -{ - void Precache( void ); - void KeyValue( KeyValueData *pkvd ); - virtual int Save( CSave &save ); - virtual int Restore( CRestore &restore ); - void Spawn( void ); - - static TYPEDESCRIPTION m_SaveData[]; - - CBaseEntity *CreateGib( Vector vecPos, Vector vecVel ); - - int m_iszTouch; - int m_iszTouchOther; - int m_iPhysics; - float m_fFriction; - Vector m_vecSize; -}; - -TYPEDESCRIPTION CEnvShooter::m_SaveData[] = -{ - DEFINE_FIELD( CEnvShooter, m_iszTouch, FIELD_STRING), - DEFINE_FIELD( CEnvShooter, m_iszTouchOther, FIELD_STRING), - DEFINE_FIELD( CEnvShooter, m_iPhysics, FIELD_INTEGER), - DEFINE_FIELD( CEnvShooter, m_fFriction, FIELD_FLOAT), - DEFINE_FIELD( CEnvShooter, m_vecSize, FIELD_VECTOR), -}; - -IMPLEMENT_SAVERESTORE(CEnvShooter,CGibShooter); -LINK_ENTITY_TO_CLASS( env_shooter, CEnvShooter ); - -void CEnvShooter::Spawn( void ) -{ - int iBody = pev->body; - CGibShooter::Spawn(); - pev->body = iBody; -} - -void CEnvShooter :: KeyValue( KeyValueData *pkvd ) -{ - if (FStrEq(pkvd->szKeyName, "shootmodel")) - { - pev->model = ALLOC_STRING(pkvd->szValue); - pkvd->fHandled = TRUE; - } - else if (FStrEq(pkvd->szKeyName, "shootsounds")) - { - int iNoise = atoi(pkvd->szValue); - pkvd->fHandled = TRUE; - switch( iNoise ) - { - case 0: - m_iGibMaterial = matGlass; - break; - case 1: - m_iGibMaterial = matWood; - break; - case 2: - m_iGibMaterial = matMetal; - break; - case 3: - m_iGibMaterial = matFlesh; - break; - case 4: - m_iGibMaterial = matRocks; - break; - - default: - case -1: - m_iGibMaterial = matNone; - break; - } - } - else if (FStrEq(pkvd->szKeyName, "m_iszTouch")) - { - m_iszTouch = ALLOC_STRING(pkvd->szValue); - pkvd->fHandled = TRUE; - } - else if (FStrEq(pkvd->szKeyName, "m_iszTouchOther")) - { - m_iszTouchOther = ALLOC_STRING(pkvd->szValue); - pkvd->fHandled = TRUE; - } - else if (FStrEq(pkvd->szKeyName, "m_iPhysics")) - { - m_iPhysics = atoi(pkvd->szValue); - pkvd->fHandled = TRUE; - } - else if (FStrEq(pkvd->szKeyName, "m_fFriction")) - { - m_fFriction = atof(pkvd->szValue); - pkvd->fHandled = TRUE; - } - else if (FStrEq(pkvd->szKeyName, "m_vecSize")) - { - UTIL_StringToVector((float*)m_vecSize, pkvd->szValue); - m_vecSize = m_vecSize/2; - pkvd->fHandled = TRUE; - } - else - { - CGibShooter::KeyValue( pkvd ); - } -} - - -void CEnvShooter :: Precache ( void ) -{ - if (pev->model) - m_iGibModelIndex = PRECACHE_MODEL( (char *)STRING(pev->model) ); - CBreakable::MaterialSoundPrecache( (Materials)m_iGibMaterial ); -} - - -CBaseEntity *CEnvShooter :: CreateGib ( Vector vecPos, Vector vecVel ) -{ - if( m_iPhysics <= 1 ) // normal gib or sticky gib - { - CGib *pGib = GetClassPtr( (CGib *)NULL ); - - pGib->pev->origin = vecPos; - pGib->pev->velocity = vecVel; - pGib->Spawn( STRING(pev->model) ); - - if( m_iPhysics ) // sticky gib - { - pGib->pev->movetype = MOVETYPE_TOSS; - pGib->pev->solid = SOLID_BBOX; - UTIL_SetSize ( pGib->pev, Vector ( 0, 0 ,0 ), Vector ( 0, 0, 0 ) ); - pGib->SetTouch(&CGib::StickyGibTouch); - } - if ( pev->body > 0 ) - pGib->pev->body = RANDOM_LONG( 0, pev->body-1 ); - if ( m_iBloodColor ) - pGib->m_bloodColor = m_iBloodColor; - else pGib->m_bloodColor = DONT_BLEED; - pGib->m_material = m_iGibMaterial; - - pGib->pev->rendermode = pev->rendermode; - pGib->pev->renderamt = pev->renderamt; - pGib->pev->rendercolor = pev->rendercolor; - pGib->pev->renderfx = pev->renderfx; - pGib->pev->scale = pev->scale; - pGib->pev->skin = pev->skin; - - float thinkTime = pGib->m_fNextThink - gpGlobals->time; - - pGib->m_lifeTime = (m_flGibLife * RANDOM_FLOAT( 0.95, 1.05 )); // +/- 5% - - if ( pGib->m_lifeTime < thinkTime ) - { - pGib->SetNextThink( pGib->m_lifeTime ); - pGib->m_lifeTime = 0; - } - - pGib->pev->avelocity.x = RANDOM_FLOAT ( 100, 200 ); - pGib->pev->avelocity.y = RANDOM_FLOAT ( 100, 300 ); - - return pGib; - } - - // special shot - CShot *pShot = GetClassPtr( (CShot*)NULL ); - pShot->pev->classname = MAKE_STRING( "shot" ); - pShot->pev->solid = SOLID_SLIDEBOX; - pShot->pev->origin = vecPos; - pShot->pev->velocity = vecVel; - SET_MODEL( ENT( pShot->pev ), STRING( pev->model )); - UTIL_SetSize( pShot->pev, -m_vecSize, m_vecSize ); - pShot->pev->renderamt = pev->renderamt; - pShot->pev->rendermode = pev->rendermode; - pShot->pev->rendercolor = pev->rendercolor; - pShot->pev->renderfx = pev->renderfx; - pShot->pev->netname = m_iszTouch; - pShot->pev->message = m_iszTouchOther; - pShot->pev->skin = pev->skin; - pShot->pev->body = pev->body; - pShot->pev->scale = pev->scale; - pShot->pev->frame = pev->frame; - pShot->pev->framerate = pev->framerate; - pShot->pev->friction = m_fFriction; - - switch ( m_iPhysics ) - { - case 2: pShot->pev->movetype = MOVETYPE_NOCLIP; pShot->pev->solid = SOLID_NOT; break; - case 3: pShot->pev->movetype = MOVETYPE_FLYMISSILE; break; - case 4: pShot->pev->movetype = MOVETYPE_BOUNCEMISSILE; break; - case 5: pShot->pev->movetype = MOVETYPE_TOSS; break; - default: pShot->pev->movetype = MOVETYPE_BOUNCE; break; - } - - if ( pShot->pev->framerate ) - { - pShot->m_maxFrame = (float) MODEL_FRAMES( pShot->pev->modelindex ) - 1; - if (pShot->m_maxFrame > 1.0) - { - if (m_flGibLife) - { - pShot->pev->dmgtime = gpGlobals->time + m_flGibLife; - pShot->SetThink(& CSprite::AnimateUntilDead ); - } - else - { - pShot->SetThink(& CSprite::AnimateThink ); - } - pShot->SetNextThink( 0 ); - pShot->m_lastTime = gpGlobals->time; - return pShot; - } - } - - // if it's not animating - if (m_flGibLife) - { - pShot->SetThink(&CShot::SUB_Remove); - pShot->SetNextThink(m_flGibLife); - } - return pShot; -} - - - - -class CTestEffect : public CBaseDelay -{ -public: - void Spawn( void ); - void Precache( void ); - // void KeyValue( KeyValueData *pkvd ); - void EXPORT TestThink( void ); - void Use( CBaseEntity *pActivator, CBaseEntity *pCaller, USE_TYPE useType, float value ); - - int m_iLoop; - int m_iBeam; - CBeam *m_pBeam[24]; - float m_flBeamTime[24]; - float m_flStartTime; -}; - - -LINK_ENTITY_TO_CLASS( test_effect, CTestEffect ); - -void CTestEffect::Spawn( void ) -{ - Precache( ); -} - -void CTestEffect::Precache( void ) -{ - PRECACHE_MODEL( "sprites/lgtning.spr" ); -} - -void CTestEffect::TestThink( void ) -{ - int i; - float t = (gpGlobals->time - m_flStartTime); - - if (m_iBeam < 24) - { - CBeam *pbeam = CBeam::BeamCreate( "sprites/lgtning.spr", 100 ); - - TraceResult tr; - - Vector vecSrc = pev->origin; - Vector vecDir = Vector( RANDOM_FLOAT( -1.0, 1.0 ), RANDOM_FLOAT( -1.0, 1.0 ),RANDOM_FLOAT( -1.0, 1.0 ) ); - vecDir = vecDir.Normalize(); - UTIL_TraceLine( vecSrc, vecSrc + vecDir * 128, ignore_monsters, ENT(pev), &tr); - - pbeam->PointsInit( vecSrc, tr.vecEndPos ); - // pbeam->SetColor( 80, 100, 255 ); - pbeam->SetColor( 255, 180, 100 ); - pbeam->SetWidth( 100 ); - pbeam->SetScrollRate( 12 ); - - m_flBeamTime[m_iBeam] = gpGlobals->time; - m_pBeam[m_iBeam] = pbeam; - m_iBeam++; - -#if 0 - Vector vecMid = (vecSrc + tr.vecEndPos) * 0.5; - MESSAGE_BEGIN( MSG_BROADCAST, gmsgTempEntity ); - WRITE_BYTE(TE_DLIGHT); - WRITE_COORD(vecMid.x); // X - WRITE_COORD(vecMid.y); // Y - WRITE_COORD(vecMid.z); // Z - WRITE_BYTE( 20 ); // radius * 0.1 - WRITE_BYTE( 255 ); // r - WRITE_BYTE( 180 ); // g - WRITE_BYTE( 100 ); // b - WRITE_BYTE( 20 ); // time * 10 - WRITE_BYTE( 0 ); // decay * 0.1 - MESSAGE_END( ); -#endif - } - - if (t < 3.0) - { - for (i = 0; i < m_iBeam; i++) - { - t = (gpGlobals->time - m_flBeamTime[i]) / ( 3 + m_flStartTime - m_flBeamTime[i]); - m_pBeam[i]->SetBrightness( 255 * t ); - // m_pBeam[i]->SetScrollRate( 20 * t ); - } - SetNextThink( 0.1 ); - } - else - { - for (i = 0; i < m_iBeam; i++) - { - UTIL_Remove( m_pBeam[i] ); - } - m_flStartTime = gpGlobals->time; - m_iBeam = 0; - // pev->nextthink = gpGlobals->time; - SetThink( NULL ); - } -} - - -void CTestEffect::Use( CBaseEntity *pActivator, CBaseEntity *pCaller, USE_TYPE useType, float value ) -{ - SetThink(&CTestEffect:: TestThink ); - SetNextThink( 0.1 ); - m_flStartTime = gpGlobals->time; -} - - - -// Blood effects -class CBlood : public CPointEntity -{ -public: - void Spawn( void ); - void Use( CBaseEntity *pActivator, CBaseEntity *pCaller, USE_TYPE useType, float value ); - void KeyValue( KeyValueData *pkvd ); - - inline int Color( void ) { return pev->impulse; } - inline float BloodAmount( void ) { return pev->dmg; } - - inline void SetColor( int color ) { pev->impulse = color; } - inline void SetBloodAmount( float amount ) { pev->dmg = amount; } - - Vector Direction( CBaseEntity *pActivator ); //LRC - added pActivator, for locus system - Vector BloodPosition( CBaseEntity *pActivator ); - -private: -}; - -LINK_ENTITY_TO_CLASS( env_blood, CBlood ); - - - -#define SF_BLOOD_RANDOM 0x0001 -#define SF_BLOOD_STREAM 0x0002 -#define SF_BLOOD_PLAYER 0x0004 -#define SF_BLOOD_DECAL 0x0008 - -void CBlood::Spawn( void ) -{ - pev->solid = SOLID_NOT; - pev->movetype = MOVETYPE_NONE; - pev->effects = 0; - pev->frame = 0; - SetMovedir( pev ); - if (Color() == 0) SetColor( BLOOD_COLOR_RED ); -} - - -void CBlood::KeyValue( KeyValueData *pkvd ) -{ - if (FStrEq(pkvd->szKeyName, "color")) - { - int color = atoi(pkvd->szValue); - switch( color ) - { - case 1: - SetColor( BLOOD_COLOR_YELLOW ); - break; - default: - SetColor( BLOOD_COLOR_RED ); - break; - } - - pkvd->fHandled = TRUE; - } - else if (FStrEq(pkvd->szKeyName, "amount")) - { - SetBloodAmount( atof(pkvd->szValue) ); - pkvd->fHandled = TRUE; - } - else - CPointEntity::KeyValue( pkvd ); -} - - -Vector CBlood::Direction( CBaseEntity *pActivator ) -{ - if ( pev->spawnflags & SF_BLOOD_RANDOM ) - return UTIL_RandomBloodVector(); - else if (pev->netname) - return CalcLocus_Velocity(this, pActivator, STRING(pev->netname)); - else - return pev->movedir; -} - - -Vector CBlood::BloodPosition( CBaseEntity *pActivator ) -{ - if ( pev->spawnflags & SF_BLOOD_PLAYER ) - { - edict_t *pPlayer; - - if ( pActivator && pActivator->IsPlayer() ) - { - pPlayer = pActivator->edict(); - } - else - pPlayer = g_engfuncs.pfnPEntityOfEntIndex( 1 ); - if ( pPlayer ) - return (pPlayer->v.origin + pPlayer->v.view_ofs) + Vector( RANDOM_FLOAT(-10,10), RANDOM_FLOAT(-10,10), RANDOM_FLOAT(-10,10) ); - // if no player found, fall through - } - else if (pev->target) - { - return CalcLocus_Position(this, pActivator, STRING(pev->target)); - } - - return pev->origin; -} - - -void CBlood::Use( CBaseEntity *pActivator, CBaseEntity *pCaller, USE_TYPE useType, float value ) -{ - if ( pev->spawnflags & SF_BLOOD_STREAM ) - UTIL_BloodStream( BloodPosition(pActivator), Direction(pActivator), (Color() == BLOOD_COLOR_RED) ? 70 : Color(), BloodAmount() ); - else - UTIL_BloodDrips( BloodPosition(pActivator), Direction(pActivator), Color(), BloodAmount() ); - - if ( pev->spawnflags & SF_BLOOD_DECAL ) - { - Vector forward = Direction(pActivator); - Vector start = BloodPosition( pActivator ); - TraceResult tr; - - UTIL_TraceLine( start, start + forward * BloodAmount() * 2, ignore_monsters, NULL, &tr ); - if ( tr.flFraction != 1.0 ) - UTIL_BloodDecalTrace( &tr, Color() ); - } -} - - - -// Screen shake -class CShake : public CPointEntity -{ -public: - void Spawn( void ); - void Use( CBaseEntity *pActivator, CBaseEntity *pCaller, USE_TYPE useType, float value ); - void KeyValue( KeyValueData *pkvd ); - - inline float Amplitude( void ) { return pev->scale; } - inline float Frequency( void ) { return pev->dmg_save; } - inline float Duration( void ) { return pev->dmg_take; } - inline float Radius( void ) { return pev->dmg; } - - inline void SetAmplitude( float amplitude ) { pev->scale = amplitude; } - inline void SetFrequency( float frequency ) { pev->dmg_save = frequency; } - inline void SetDuration( float duration ) { pev->dmg_take = duration; } - inline void SetRadius( float radius ) { pev->dmg = radius; } - - STATE m_iState; //LRC - virtual STATE GetState( void ) { return m_iState; }; //LRC - void Think( void ) { m_iState = STATE_OFF; }; //LRC -private: -}; - -LINK_ENTITY_TO_CLASS( env_shake, CShake ); - -// pev->scale is amplitude -// pev->dmg_save is frequency -// pev->dmg_take is duration -// pev->dmg is radius -// radius of 0 means all players -// NOTE: UTIL_ScreenShake() will only shake players who are on the ground - -#define SF_SHAKE_EVERYONE 0x0001 // Don't check radius -// UNDONE: These don't work yet -#define SF_SHAKE_DISRUPT 0x0002 // Disrupt controls -#define SF_SHAKE_INAIR 0x0004 // Shake players in air - -void CShake::Spawn( void ) -{ - pev->solid = SOLID_NOT; - pev->movetype = MOVETYPE_NONE; - pev->effects = 0; - pev->frame = 0; - - m_iState = STATE_OFF; //LRC - - if ( pev->spawnflags & SF_SHAKE_EVERYONE ) - pev->dmg = 0; -} - - -void CShake::KeyValue( KeyValueData *pkvd ) -{ - if (FStrEq(pkvd->szKeyName, "amplitude")) - { - SetAmplitude( atof(pkvd->szValue) ); - pkvd->fHandled = TRUE; - } - else if (FStrEq(pkvd->szKeyName, "frequency")) - { - SetFrequency( atof(pkvd->szValue) ); - pkvd->fHandled = TRUE; - } - else if (FStrEq(pkvd->szKeyName, "duration")) - { - SetDuration( atof(pkvd->szValue) ); - pkvd->fHandled = TRUE; - } - else if (FStrEq(pkvd->szKeyName, "radius")) - { - SetRadius( atof(pkvd->szValue) ); - pkvd->fHandled = TRUE; - } - else - CPointEntity::KeyValue( pkvd ); -} - - -void CShake::Use( CBaseEntity *pActivator, CBaseEntity *pCaller, USE_TYPE useType, float value ) -{ - UTIL_ScreenShake( pev->origin, Amplitude(), Frequency(), Duration(), Radius() ); - m_iState = STATE_ON; //LRC - SetNextThink( Duration() ); //LRC -} - - -class CFade : public CPointEntity -{ -public: - void Spawn( void ); - void Use( CBaseEntity *pActivator, CBaseEntity *pCaller, USE_TYPE useType, float value ); - void KeyValue( KeyValueData *pkvd ); - - virtual STATE GetState( void ) { return m_iState; }; // LRC - void Think( void ); //LRC - - inline float Duration( void ) { return pev->dmg_take; } - inline float HoldTime( void ) { return pev->dmg_save; } - - inline void SetDuration( float duration ) { pev->dmg_take = duration; } - inline void SetHoldTime( float hold ) { pev->dmg_save = hold; } - - STATE m_iState; // LRC. Don't saverestore this value, it's not worth it. -private: -}; - -LINK_ENTITY_TO_CLASS( env_fade, CFade ); - -// pev->dmg_take is duration -// pev->dmg_save is hold duration -#define SF_FADE_IN 0x0001 // Fade in, not out -#define SF_FADE_MODULATE 0x0002 // Modulate, don't blend -#define SF_FADE_ONLYONE 0x0004 -#define SF_FADE_PERMANENT 0x0008 //LRC - hold permanently -#define SF_FADE_CAMERA 0x0010 //fading only for camera - -void CFade::Spawn( void ) -{ - pev->solid = SOLID_NOT; - pev->movetype = MOVETYPE_NONE; - pev->effects = 0; - pev->frame = 0; - - m_iState = STATE_OFF; //LRC -} - - -void CFade::KeyValue( KeyValueData *pkvd ) -{ - if (FStrEq(pkvd->szKeyName, "duration")) - { - SetDuration( atof(pkvd->szValue) ); - pkvd->fHandled = TRUE; - } - else if (FStrEq(pkvd->szKeyName, "holdtime")) - { - SetHoldTime( atof(pkvd->szValue) ); - pkvd->fHandled = TRUE; - } - else - CPointEntity::KeyValue( pkvd ); -} - - -void CFade::Use( CBaseEntity *pActivator, CBaseEntity *pCaller, USE_TYPE useType, float value ) -{ - int fadeFlags = 0; - - if ( pev->spawnflags & SF_FADE_CAMERA ) - { - if ( !pActivator || !pActivator->IsPlayer() ) - { - pActivator = CBaseEntity::Instance(g_engfuncs.pfnPEntityOfEntIndex( 1 )); - } - if ( pActivator && pActivator->IsPlayer() ) - { - if(((CBasePlayer *)pActivator)->viewFlags == 0) - { -// ALERT(at_console, "player is not curently see in camera\n"); - return; - } - - } - } - - m_iState = STATE_TURN_ON; //LRC - SetNextThink( Duration() ); //LRC - - if ( !(pev->spawnflags & SF_FADE_IN) ) - fadeFlags |= FFADE_OUT; - else fadeFlags |= FFADE_IN; - - if ( pev->spawnflags & SF_FADE_MODULATE ) - fadeFlags |= FFADE_MODULATE; - - if ( pev->spawnflags & SF_FADE_PERMANENT ) //LRC - fadeFlags |= FFADE_STAYOUT; //LRC - - if ( pev->spawnflags & SF_FADE_ONLYONE ) - { - if ( pActivator->IsNetClient() ) - { - UTIL_ScreenFade( pActivator, pev->rendercolor, Duration(), HoldTime(), pev->renderamt, fadeFlags ); - } - } - else - { - UTIL_ScreenFadeAll( pev->rendercolor, Duration(), HoldTime(), pev->renderamt, fadeFlags ); - } - SUB_UseTargets( this, USE_TOGGLE, 0 ); -} - -//LRC: a bolt-on state! -void CFade::Think( void ) -{ - if (m_iState == STATE_TURN_ON) - { - m_iState = STATE_ON; - if (!( pev->spawnflags & SF_FADE_PERMANENT)) - SetNextThink( HoldTime() ); - } - else - m_iState = STATE_OFF; -} - - -class CMessage : public CPointEntity -{ -public: - void Spawn( void ); - void Precache( void ); - void Use( CBaseEntity *pActivator, CBaseEntity *pCaller, USE_TYPE useType, float value ); - void KeyValue( KeyValueData *pkvd ); -private: -}; - -LINK_ENTITY_TO_CLASS( env_message, CMessage ); - - -void CMessage::Spawn( void ) -{ - Precache(); - - pev->solid = SOLID_NOT; - pev->movetype = MOVETYPE_NONE; - - switch( pev->impulse ) - { - case 1: // Medium radius - pev->speed = ATTN_STATIC; - break; - - case 2: // Large radius - pev->speed = ATTN_NORM; - break; - - case 3: //EVERYWHERE - pev->speed = ATTN_NONE; - break; - - default: - case 0: // Small radius - pev->speed = ATTN_IDLE; - break; - } - pev->impulse = 0; - - // No volume, use normal - if ( pev->scale <= 0 ) - pev->scale = 1.0; -} - - -void CMessage::Precache( void ) -{ - if ( pev->noise ) - PRECACHE_SOUND( (char *)STRING(pev->noise) ); -} - -void CMessage::KeyValue( KeyValueData *pkvd ) -{ - if (FStrEq(pkvd->szKeyName, "messagesound")) - { - pev->noise = ALLOC_STRING(pkvd->szValue); - pkvd->fHandled = TRUE; - } - else if (FStrEq(pkvd->szKeyName, "messagevolume")) - { - pev->scale = atof(pkvd->szValue) * 0.1; - pkvd->fHandled = TRUE; - } - else if (FStrEq(pkvd->szKeyName, "messageattenuation")) - { - pev->impulse = atoi(pkvd->szValue); - pkvd->fHandled = TRUE; - } - else - CPointEntity::KeyValue( pkvd ); -} - - -void CMessage::Use( CBaseEntity *pActivator, CBaseEntity *pCaller, USE_TYPE useType, float value ) -{ - CBaseEntity *pPlayer = NULL; - - if ( pev->spawnflags & SF_MESSAGE_ALL ) - UTIL_ShowMessageAll( STRING(pev->message) ); - else - { - if ( pActivator && pActivator->IsPlayer() ) - pPlayer = pActivator; - else - { - pPlayer = CBaseEntity::Instance( g_engfuncs.pfnPEntityOfEntIndex( 1 ) ); - } - if ( pPlayer ) - UTIL_ShowMessage( STRING(pev->message), pPlayer ); - } - if ( pev->noise ) - { - EMIT_SOUND( edict(), CHAN_BODY, STRING(pev->noise), pev->scale, pev->speed ); - } - if ( pev->spawnflags & SF_MESSAGE_ONCE ) - UTIL_Remove( this ); - - SUB_UseTargets( this, USE_TOGGLE, 0 ); -} - - - -//========================================================= -// FunnelEffect -//========================================================= -class CEnvFunnel : public CBaseDelay -{ -public: - void Spawn( void ); - void Precache( void ); - void Use( CBaseEntity *pActivator, CBaseEntity *pCaller, USE_TYPE useType, float value ); - - int m_iSprite; // Don't save, precache -}; - -void CEnvFunnel :: Precache ( void ) -{ - //LRC - if (pev->netname) - m_iSprite = PRECACHE_MODEL ( (char*)STRING(pev->netname) ); - else - m_iSprite = PRECACHE_MODEL ( "sprites/flare6.spr" ); -} - -LINK_ENTITY_TO_CLASS( env_funnel, CEnvFunnel ); - -void CEnvFunnel::Use( CBaseEntity *pActivator, CBaseEntity *pCaller, USE_TYPE useType, float value ) -{ - //LRC - Vector vecPos; - if (pev->message) - vecPos = CalcLocus_Position( this, pActivator, STRING(pev->message) ); - else - vecPos = pev->origin; - - MESSAGE_BEGIN( MSG_BROADCAST, gmsgTempEntity ); - WRITE_BYTE( TE_LARGEFUNNEL ); - WRITE_COORD( vecPos.x ); - WRITE_COORD( vecPos.y ); - WRITE_COORD( vecPos.z ); - WRITE_SHORT( m_iSprite ); - - if ( pev->spawnflags & SF_FUNNEL_REVERSE )// funnel flows in reverse? - { - WRITE_SHORT( 1 ); - } - else - { - WRITE_SHORT( 0 ); - } - - - MESSAGE_END(); - - if (!(pev->spawnflags & SF_FUNNEL_REPEATABLE)) - { - SetThink(&CEnvFunnel:: SUB_Remove ); - SetNextThink( 0 ); - } -} - -void CEnvFunnel::Spawn( void ) -{ - Precache(); - pev->solid = SOLID_NOT; - pev->effects = EF_NODRAW; -} - - -//========================================================= -// LRC - All the particle effects from Quake 1 -//========================================================= -#define SF_QUAKEFX_REPEATABLE 1 -class CEnvQuakeFx : public CPointEntity -{ -public: - void Use( CBaseEntity *pActivator, CBaseEntity *pCaller, USE_TYPE useType, float value ); -}; - -LINK_ENTITY_TO_CLASS( env_quakefx, CEnvQuakeFx ); - -void CEnvQuakeFx::Use( CBaseEntity *pActivator, CBaseEntity *pCaller, USE_TYPE useType, float value ) -{ - Vector vecPos; - if (pev->message) - vecPos = CalcLocus_Position( this, pActivator, STRING(pev->message) ); - else - vecPos = pev->origin; - - MESSAGE_BEGIN( MSG_BROADCAST, gmsgTempEntity ); - WRITE_BYTE( pev->impulse ); - WRITE_COORD( vecPos.x ); - WRITE_COORD( vecPos.y ); - WRITE_COORD( vecPos.z ); - if (pev->impulse == TE_PARTICLEBURST) - { - WRITE_SHORT( pev->armortype ); // radius - WRITE_BYTE( pev->frags ); // particle colour - WRITE_BYTE( pev->health * 10 ); // duration - } - else if (pev->impulse == TE_EXPLOSION2) - { - // these fields seem to have no effect - except that it - // crashes when I send "0" for the number of colours.. - WRITE_BYTE( 0 ); // colour - WRITE_BYTE( 1 ); // number of colours - } - MESSAGE_END(); - - if (!(pev->spawnflags & SF_QUAKEFX_REPEATABLE)) - { - SetThink(&CEnvQuakeFx:: SUB_Remove ); - SetNextThink( 0 ); - } -} - - -//========================================================= -// LRC - Beam Trail effect -//========================================================= -#define SF_BEAMTRAIL_OFF 1 -class CEnvBeamTrail : public CPointEntity -{ -public: - void Spawn( void ); - void Precache( void ); - void Use( CBaseEntity *pActivator, CBaseEntity *pCaller, USE_TYPE useType, float value ); - STATE GetState( void ); - void EXPORT StartTrailThink ( void ); - void Affect( CBaseEntity *pTarget, USE_TYPE useType ); - - int m_iSprite; // Don't save, precache -}; - -void CEnvBeamTrail :: Precache ( void ) -{ - if (pev->target) - PRECACHE_MODEL("sprites/null.spr"); - if (pev->netname) - m_iSprite = PRECACHE_MODEL ( (char*)STRING(pev->netname) ); -} - -LINK_ENTITY_TO_CLASS( env_beamtrail, CEnvBeamTrail ); - -STATE CEnvBeamTrail :: GetState ( void ) -{ - if (pev->spawnflags & SF_BEAMTRAIL_OFF) - return STATE_OFF; - else - return STATE_ON; -} - -void CEnvBeamTrail :: StartTrailThink ( void ) -{ - pev->spawnflags |= SF_BEAMTRAIL_OFF; // fake turning off, so the Use turns it on properly - Use(this, this, USE_ON, 0); -} - -void CEnvBeamTrail::Use( CBaseEntity *pActivator, CBaseEntity *pCaller, USE_TYPE useType, float value ) -{ - if (pev->target) - { - CBaseEntity *pTarget = UTIL_FindEntityByTargetname(NULL, STRING(pev->target), pActivator ); - while (pTarget) - { - Affect(pTarget, useType); - pTarget = UTIL_FindEntityByTargetname(pTarget, STRING(pev->target), pActivator ); - } - } - else - { - if (!ShouldToggle( useType )) - return; - Affect(this, useType); - } - - if (useType == USE_ON) - pev->spawnflags &= ~SF_BEAMTRAIL_OFF; - else if (useType == USE_OFF) - pev->spawnflags |= SF_BEAMTRAIL_OFF; - else if (useType == USE_TOGGLE) - { - if (pev->spawnflags & SF_BEAMTRAIL_OFF) - pev->spawnflags &= ~SF_BEAMTRAIL_OFF; - else - pev->spawnflags |= SF_BEAMTRAIL_OFF; - } -} - -void CEnvBeamTrail::Affect( CBaseEntity *pTarget, USE_TYPE useType ) -{ - if (useType == USE_ON || pev->spawnflags & SF_BEAMTRAIL_OFF) - { - MESSAGE_BEGIN( MSG_BROADCAST, gmsgTempEntity ); - WRITE_BYTE( TE_BEAMFOLLOW ); - WRITE_SHORT(pTarget->entindex()); // entity - WRITE_SHORT( m_iSprite ); // model - WRITE_BYTE( pev->health*10 ); // life - WRITE_BYTE( pev->armorvalue ); // width - WRITE_BYTE( pev->rendercolor.x ); // r, g, b - WRITE_BYTE( pev->rendercolor.y ); // r, g, b - WRITE_BYTE( pev->rendercolor.z ); // r, g, b - WRITE_BYTE( pev->renderamt ); // brightness - MESSAGE_END(); - } - else - { - MESSAGE_BEGIN( MSG_BROADCAST, gmsgTempEntity ); - WRITE_BYTE(TE_KILLBEAM); - WRITE_SHORT(pTarget->entindex()); - MESSAGE_END(); - } -} - -void CEnvBeamTrail::Spawn( void ) -{ - Precache(); - - SET_MODEL(ENT(pev), "sprites/null.spr"); - UTIL_SetSize(pev, Vector(0, 0, 0), Vector(0, 0, 0)); - - if (!(pev->spawnflags & SF_BEAMTRAIL_OFF)) - { - SetThink(&CEnvBeamTrail::StartTrailThink); - UTIL_DesiredThink( this ); - } -} - - -//========================================================= -// LRC - custom footstep sounds -//========================================================= -#define SF_FOOTSTEPS_SET 1 -#define SF_FOOTSTEPS_ONCE 2 - -class CEnvFootsteps : public CBaseEntity -{ -public: - void Spawn( void ); - void Precache( void ); - void Use( CBaseEntity *pActivator, CBaseEntity *pCaller, USE_TYPE useType, float value ); - virtual int ObjectCaps( void ) { return CBaseEntity :: ObjectCaps() & ~FCAP_ACROSS_TRANSITION; } - STATE GetState( void ); - STATE GetState( CBaseEntity* pEnt ); - void PrecacheNoise ( const char* szNoise ); -}; - -LINK_ENTITY_TO_CLASS( env_footsteps, CEnvFootsteps ); - -void CEnvFootsteps::Spawn( void ) -{ - Precache(); -} - -void CEnvFootsteps :: PrecacheNoise ( const char* szNoise ) -{ - static char szBuf[128]; - int i = 0, j = 0; - for (i = 0; szNoise[i]; i++) - { - if (szNoise[i] == '?') - { - strcpy(szBuf, szNoise); - for (j = 0; j < 4; j++) - { - szBuf[i] = j+'1'; - PRECACHE_SOUND ( szBuf ); - } - } - } - if (!j) - PRECACHE_SOUND ( (char*)szNoise ); -} - -void CEnvFootsteps :: Precache ( void ) -{ - if (pev->noise) - PrecacheNoise(STRING(pev->noise)); - if (pev->noise1) - PrecacheNoise(STRING(pev->noise1)); - if (pev->noise2) - PrecacheNoise(STRING(pev->noise2)); - if (pev->noise3) - PrecacheNoise(STRING(pev->noise3)); -} - -STATE CEnvFootsteps::GetState() -{ - if (pev->spawnflags & SF_FOOTSTEPS_SET) return STATE_OFF; - return pev->impulse ? STATE_ON : STATE_OFF; -} - -STATE CEnvFootsteps::GetState(CBaseEntity* pEnt) -{ - if (pev->spawnflags & SF_FOOTSTEPS_SET) - return STATE_OFF; - if (pEnt->IsPlayer()) - { - // based on trigger_hurt code - int playerMask = 1 << (pEnt->entindex() - 1); - - if ( pev->impulse & playerMask ) - return STATE_ON; - else - return STATE_OFF; - } - else return GetState(); -} - -void CEnvFootsteps::Use( CBaseEntity *pActivator, CBaseEntity *pCaller, USE_TYPE useType, float value ) -{ -// union floatToString ftsTemp; - - //CONSIDER: add an "all players" spawnflag, like game_text? - if (pActivator && pActivator->IsPlayer()) - { - int playerMask = 1 << (pActivator->entindex() - 1); - - if (pev->spawnflags & SF_FOOTSTEPS_SET || ( !(pev->impulse & playerMask) && (useType == USE_ON || useType == USE_TOGGLE))) - { - pev->impulse |= playerMask; - if (pev->frags) - { - char sTemp[4]; - sprintf(sTemp, "%d", (int)pev->frags); - g_engfuncs.pfnSetPhysicsKeyValue( pActivator->edict(), "stype", sTemp ); - //pActivator->pev->iFootstepType = pev->frags; - } - else if (pev->noise) - { - g_engfuncs.pfnSetPhysicsKeyValue( pActivator->edict(), "ssnd", STRING(pev->noise) ); - } - if (pev->noise1) - { - g_engfuncs.pfnSetPhysicsKeyValue( pActivator->edict(), "lsnd", STRING(pev->noise1) ); - } - if (pev->noise2) - { - g_engfuncs.pfnSetPhysicsKeyValue( pActivator->edict(), "wsnd", STRING(pev->noise2) ); - } - if (pev->noise3) - { - g_engfuncs.pfnSetPhysicsKeyValue( pActivator->edict(), "psnd", STRING(pev->noise3) ); - } - // workaround for physinfo string bug: force the engine to null-terminate it - g_engfuncs.pfnSetPhysicsKeyValue( pActivator->edict(), "x", "0" ); - //ALERT(at_console, "ON, InfoString = %s\n", g_engfuncs.pfnGetPhysicsInfoString(pActivator->edict())); - if (pev->spawnflags & SF_FOOTSTEPS_SET && pev->spawnflags & SF_FOOTSTEPS_ONCE) - { - UTIL_Remove(this); - } - } - else if ( (pev->impulse & playerMask) && (useType == USE_OFF || useType == USE_TOGGLE)) - { - pev->impulse &= ~playerMask; - if (pev->frags) - { - g_engfuncs.pfnSetPhysicsKeyValue( pActivator->edict(), "stype", "0" ); - } - else if (pev->noise) - { - g_engfuncs.pfnSetPhysicsKeyValue( pActivator->edict(), "ssnd", "0" ); - } - if (pev->noise1) - { - g_engfuncs.pfnSetPhysicsKeyValue( pActivator->edict(), "lsnd", "0" ); - } - if (pev->noise2) - { - g_engfuncs.pfnSetPhysicsKeyValue( pActivator->edict(), "wsnd", "0" ); - } - if (pev->noise3) - { - g_engfuncs.pfnSetPhysicsKeyValue( pActivator->edict(), "psnd", "0" ); - } - // workaround for physinfo string bug: force the engine to null-terminate it - g_engfuncs.pfnSetPhysicsKeyValue( pActivator->edict(), "x", "0" ); - //ALERT(at_console, "OFF, InfoString = %s\n", g_engfuncs.pfnGetPhysicsInfoString(pActivator->edict())); - if (pev->spawnflags & SF_FOOTSTEPS_ONCE) - { - UTIL_Remove(this); - } - } - else - { - //ALERT(at_console, "NO EFFECT\n"); - } - } -} - -//========================================================= -//LRC- the long-awaited effect. (Rain, in the desert? :) -// -//FIXME: give designers a _lot_ more control. -//========================================================= -#define MAX_RAIN_BEAMS 32 - -#define AXIS_X 1 -#define AXIS_Y 2 -#define AXIS_Z 0 - -#define EXTENT_OBSTRUCTED 1 -#define EXTENT_ARCING 2 -#define EXTENT_OBSTRUCTED_REVERSE 3 -#define EXTENT_ARCING_REVERSE 4 - -class CEnvRain : public CBaseEntity -{ -public: - void Spawn( void ); - void Use( CBaseEntity *pActivator, CBaseEntity *pCaller, USE_TYPE useType, float value ); - void Think( void ); - void Precache( void ); - void KeyValue( KeyValueData *pkvd ); - virtual int ObjectCaps( void ) { return CBaseEntity :: ObjectCaps() & ~FCAP_ACROSS_TRANSITION; } - - virtual int Save( CSave &save ); - virtual int Restore( CRestore &restore ); - static TYPEDESCRIPTION m_SaveData[]; - - STATE m_iState; - int m_spriteTexture; - int m_iszSpriteName; // have to saverestore this, the beams keep a link to it - int m_dripSize; - int m_minDripSpeed; - int m_maxDripSpeed; - int m_burstSize; - int m_brightness; - int m_pitch; // don't saverestore this - float m_flUpdateTime; - float m_flMaxUpdateTime; -// CBeam* m_pBeams[MAX_RAIN_BEAMS]; - int m_axis; - int m_iExtent; - float m_fLifeTime; - int m_iNoise; - - virtual STATE GetState( void ) { return m_iState; }; -}; - -LINK_ENTITY_TO_CLASS( env_rain, CEnvRain ); - -TYPEDESCRIPTION CEnvRain::m_SaveData[] = -{ - DEFINE_FIELD( CEnvRain, m_iState, FIELD_INTEGER ), - DEFINE_FIELD( CEnvRain, m_spriteTexture, FIELD_INTEGER ), - DEFINE_FIELD( CEnvRain, m_dripSize, FIELD_INTEGER ), - DEFINE_FIELD( CEnvRain, m_minDripSpeed, FIELD_INTEGER ), - DEFINE_FIELD( CEnvRain, m_maxDripSpeed, FIELD_INTEGER ), - DEFINE_FIELD( CEnvRain, m_burstSize, FIELD_INTEGER ), - DEFINE_FIELD( CEnvRain, m_brightness, FIELD_INTEGER ), - DEFINE_FIELD( CEnvRain, m_flUpdateTime, FIELD_FLOAT ), - DEFINE_FIELD( CEnvRain, m_flMaxUpdateTime, FIELD_FLOAT ), - DEFINE_FIELD( CEnvRain, m_iszSpriteName, FIELD_STRING ), - DEFINE_FIELD( CEnvRain, m_axis, FIELD_INTEGER ), - DEFINE_FIELD( CEnvRain, m_iExtent, FIELD_INTEGER ), - DEFINE_FIELD( CEnvRain, m_fLifeTime, FIELD_FLOAT ), - DEFINE_FIELD( CEnvRain, m_iNoise, FIELD_INTEGER ), -// DEFINE_FIELD( CEnvRain, m_pBeams, FIELD_CLASSPTR, MAX_RAIN_BEAMS ), -}; - -IMPLEMENT_SAVERESTORE( CEnvRain, CBaseEntity ); - -void CEnvRain::Precache( void ) -{ - m_spriteTexture = PRECACHE_MODEL( (char *)STRING(m_iszSpriteName) ); -} - -void CEnvRain::KeyValue( KeyValueData *pkvd ) -{ - if (FStrEq(pkvd->szKeyName, "m_dripSize")) - { - m_dripSize = atoi(pkvd->szValue); - pkvd->fHandled = TRUE; - } - else if (FStrEq(pkvd->szKeyName, "m_burstSize")) - { - m_burstSize = atoi(pkvd->szValue); - pkvd->fHandled = TRUE; - } - else if (FStrEq(pkvd->szKeyName, "m_dripSpeed")) - { - int temp = atoi(pkvd->szValue); - m_maxDripSpeed = temp + (temp/4); - m_minDripSpeed = temp - (temp/4); - pkvd->fHandled = TRUE; - } - else if (FStrEq(pkvd->szKeyName, "m_brightness")) - { - m_brightness = atoi(pkvd->szValue); - pkvd->fHandled = TRUE; - } - else if (FStrEq(pkvd->szKeyName, "m_flUpdateTime")) - { - m_flUpdateTime = atof(pkvd->szValue); - pkvd->fHandled = TRUE; - } - else if (FStrEq(pkvd->szKeyName, "m_flMaxUpdateTime")) - { - m_flMaxUpdateTime = atof(pkvd->szValue); - pkvd->fHandled = TRUE; - } - else if (FStrEq(pkvd->szKeyName, "pitch")) - { - m_pitch = atoi(pkvd->szValue); - pkvd->fHandled = TRUE; - } - else if (FStrEq(pkvd->szKeyName, "texture")) - { - m_iszSpriteName = ALLOC_STRING(pkvd->szValue); - pkvd->fHandled = TRUE; - } - else if (FStrEq(pkvd->szKeyName, "m_axis")) - { - m_axis = atoi(pkvd->szValue); - pkvd->fHandled = TRUE; - } - else if (FStrEq(pkvd->szKeyName, "m_iExtent")) - { - m_iExtent = atoi(pkvd->szValue); - pkvd->fHandled = TRUE; - } - else if (FStrEq(pkvd->szKeyName, "m_fLifeTime")) - { - m_fLifeTime = atof(pkvd->szValue); - pkvd->fHandled = TRUE; - } - else if (FStrEq(pkvd->szKeyName, "m_iNoise")) - { - m_iNoise = atoi(pkvd->szValue); - pkvd->fHandled = TRUE; - } - else - CBaseEntity::KeyValue( pkvd ); -} - -void CEnvRain::Use( CBaseEntity *pActivator, CBaseEntity *pCaller, USE_TYPE useType, float value ) -{ - if (!ShouldToggle(useType)) return; - - if (m_iState == STATE_ON) - { - m_iState = STATE_OFF; - DontThink(); - } - else - { - m_iState = STATE_ON; - SetNextThink( 0.1 ); - } -} - -#define SF_RAIN_START_OFF 1 - -void CEnvRain::Spawn( void ) -{ - Precache(); - SET_MODEL( ENT(pev), STRING(pev->model) ); // Set size - pev->solid = SOLID_NOT; - pev->effects = EF_NODRAW; - - if (pev->rendercolor == g_vecZero) - pev->rendercolor = Vector(255,255,255); - - if (m_pitch) - pev->angles.x = m_pitch; - else if (pev->angles.x == 0) // don't allow horizontal rain. - pev->angles.x = 90; - - if (m_burstSize == 0) // in case the level designer forgot to set it. - m_burstSize = 2; - - if (pev->spawnflags & SF_RAIN_START_OFF) - m_iState = STATE_OFF; - else - { - m_iState = STATE_ON; - SetNextThink( 0.1 ); - } -} - -void CEnvRain::Think( void ) -{ -// ALERT(at_console,"RainThink %d %d %d %s\n",m_spriteTexture,m_dripSize,m_brightness,STRING(m_iszSpriteName)); - Vector vecSrc; - Vector vecDest; - - UTIL_MakeVectors(pev->angles); - Vector vecOffs = gpGlobals->v_forward; - switch (m_axis) - { - case AXIS_X: - vecOffs = vecOffs * (pev->size.x / vecOffs.x); - break; - case AXIS_Y: - vecOffs = vecOffs * (pev->size.y / vecOffs.y); - break; - case AXIS_Z: - vecOffs = vecOffs * (pev->size.z / vecOffs.z); - break; - } - -// ALERT(at_console,"RainThink offs.z = %f, size.z = %f\n",vecOffs.z,pev->size.z); - - int repeats; - if (!m_fLifeTime && !m_flUpdateTime && !m_flMaxUpdateTime) - repeats = m_burstSize * 3; - else - repeats = m_burstSize; - - int drawn = 0; - int tries = 0; - TraceResult tr; - BOOL bDraw; - - while (drawn < repeats && tries < (repeats*3)) - { - tries++; - if (m_axis == AXIS_X) - vecSrc.x = pev->maxs.x; - else - vecSrc.x = pev->mins.x + RANDOM_LONG(0, pev->size.x); - if (m_axis == AXIS_Y) - vecSrc.y = pev->maxs.y; - else - vecSrc.y = pev->mins.y + RANDOM_LONG(0, pev->size.y); - if (m_axis == AXIS_Z) - vecSrc.z = pev->maxs.z; - else - vecSrc.z = pev->mins.z + RANDOM_LONG(0, pev->size.z); - vecDest = vecSrc - vecOffs; - bDraw = TRUE; - - switch (m_iExtent) - { - case EXTENT_OBSTRUCTED: - UTIL_TraceLine( vecSrc, vecDest, ignore_monsters, NULL, &tr); - vecDest = tr.vecEndPos; - break; - case EXTENT_OBSTRUCTED_REVERSE: - UTIL_TraceLine( vecDest, vecSrc, ignore_monsters, NULL, &tr); - vecSrc = tr.vecEndPos; - break; - case EXTENT_ARCING: - UTIL_TraceLine( vecSrc, vecDest, ignore_monsters, NULL, &tr); - if (tr.flFraction == 1.0) bDraw = FALSE; - vecDest = tr.vecEndPos; - break; - case EXTENT_ARCING_REVERSE: - UTIL_TraceLine( vecDest, vecSrc, ignore_monsters, NULL, &tr); - if (tr.flFraction == 1.0) bDraw = FALSE; - vecSrc = tr.vecEndPos; - break; - } -// vecDest.z = pev->mins.z; - if (!bDraw) continue; - - drawn++; - - MESSAGE_BEGIN( MSG_BROADCAST, gmsgTempEntity ); - WRITE_BYTE( TE_BEAMPOINTS ); - WRITE_COORD(vecDest.x); - WRITE_COORD(vecDest.y); - WRITE_COORD(vecDest.z); - WRITE_COORD(vecSrc.x); - WRITE_COORD(vecSrc.y); - WRITE_COORD(vecSrc.z); - WRITE_SHORT( m_spriteTexture ); - WRITE_BYTE( (int)0 ); // framestart - WRITE_BYTE( (int)0 ); // framerate - if (m_fLifeTime) // life - WRITE_BYTE( (int)(m_fLifeTime*10) ); - else if (m_flMaxUpdateTime) - WRITE_BYTE( (int)( RANDOM_FLOAT(m_flUpdateTime, m_flMaxUpdateTime)*30 )); - else - WRITE_BYTE( (int)(m_flUpdateTime * 30) ); // life - WRITE_BYTE( m_dripSize ); // width - WRITE_BYTE( m_iNoise ); // noise - WRITE_BYTE( (int)pev->rendercolor.x ); // r, - WRITE_BYTE( (int)pev->rendercolor.y ); // g, - WRITE_BYTE( (int)pev->rendercolor.z ); // b - WRITE_BYTE( m_brightness ); // brightness - WRITE_BYTE( (int)RANDOM_LONG(m_minDripSpeed,m_maxDripSpeed) ); // speed - MESSAGE_END(); - } - - // drawn will be false if we didn't draw anything. - if (pev->target && drawn) - FireTargets(STRING(pev->target), this, this, USE_TOGGLE, 0); - - if (m_flMaxUpdateTime) - SetNextThink( RANDOM_FLOAT(m_flMaxUpdateTime, m_flUpdateTime) ); - else if (m_flUpdateTime) - SetNextThink( m_flUpdateTime ); -} - -//================================================================== -//LRC- Xen monsters' warp-in effect, for those too lazy to build it. :) -//================================================================== -class CEnvWarpBall : public CBaseEntity -{ -public: - void Precache( void ); - void Spawn( void ) { Precache(); } - void Think( void ); - void Use( CBaseEntity *pActivator, CBaseEntity *pCaller, USE_TYPE useType, float value ); - virtual int ObjectCaps( void ) { return CBaseEntity :: ObjectCaps() & ~FCAP_ACROSS_TRANSITION; } -}; - -LINK_ENTITY_TO_CLASS( env_warpball, CEnvWarpBall ); - -void CEnvWarpBall::Precache( void ) -{ - PRECACHE_MODEL( "sprites/lgtning.spr" ); - PRECACHE_MODEL( "sprites/Fexplo1.spr" ); - PRECACHE_MODEL( "sprites/XFlare1.spr" ); - PRECACHE_SOUND( "debris/beamstart2.wav" ); - PRECACHE_SOUND( "debris/beamstart7.wav" ); -} - -void CEnvWarpBall::Use( CBaseEntity *pActivator, CBaseEntity *pCaller, USE_TYPE useType, float value ) -{ - int iTimes = 0; - int iDrawn = 0; - TraceResult tr; - Vector vecDest; - CBeam *pBeam; - while (iDrawnfrags && iTimes<(pev->frags * 3)) // try to draw beams, but give up after 3x tries. - { - vecDest = pev->health * (Vector(RANDOM_FLOAT(-1,1), RANDOM_FLOAT(-1,1), RANDOM_FLOAT(-1,1)).Normalize()); - UTIL_TraceLine( pev->origin, pev->origin + vecDest, ignore_monsters, NULL, &tr); - if (tr.flFraction != 1.0) - { - // we hit something. - iDrawn++; - pBeam = CBeam::BeamCreate("sprites/lgtning.spr",200); - pBeam->PointsInit( pev->origin, tr.vecEndPos ); - pBeam->SetColor( 197, 243, 169 ); - pBeam->SetNoise( 65 ); - pBeam->SetBrightness( 150 ); - pBeam->SetWidth( 18 ); - pBeam->SetScrollRate( 35 ); - pBeam->SetThink(&CBeam:: SUB_Remove ); - pBeam->SetNextThink( 1 ); - } - iTimes++; - } - EMIT_SOUND( edict(), CHAN_BODY, "debris/beamstart2.wav", 1, ATTN_NORM ); - - CSprite *pSpr = CSprite::SpriteCreate( "sprites/Fexplo1.spr", pev->origin, TRUE ); - pSpr->AnimateAndDie( 10 ); - pSpr->SetTransparency(kRenderGlow, 77, 210, 130, 255, kRenderFxNoDissipation); - - pSpr = CSprite::SpriteCreate( "sprites/XFlare1.spr", pev->origin, TRUE ); - pSpr->AnimateAndDie( 10 ); - pSpr->SetTransparency(kRenderGlow, 184, 250, 214, 255, kRenderFxNoDissipation); - - SetNextThink( 0.5 ); -} - -void CEnvWarpBall::Think( void ) -{ - EMIT_SOUND( edict(), CHAN_ITEM, "debris/beamstart7.wav", 1, ATTN_NORM ); - SUB_UseTargets( this, USE_TOGGLE, 0); -} - -//================================================================== -//LRC- Shockwave effect, like when a Houndeye attacks. -//================================================================== -#define SF_SHOCKWAVE_CENTERED 1 -#define SF_SHOCKWAVE_REPEATABLE 2 - -class CEnvShockwave : public CPointEntity -{ -public: - void Precache( void ); - void Spawn( void ) { Precache(); } - void Use( CBaseEntity *pActivator, CBaseEntity *pCaller, USE_TYPE useType, float value ); - void KeyValue( KeyValueData *pkvd ); - virtual int Save( CSave &save ); - virtual int Restore( CRestore &restore ); - static TYPEDESCRIPTION m_SaveData[]; - - void DoEffect( Vector vecPos ); - - int m_iTime; - int m_iRadius; - int m_iHeight; - int m_iScrollRate; - int m_iNoise; - int m_iFrameRate; - int m_iStartFrame; - int m_iSpriteTexture; - char m_cType; - int m_iszPosition; -}; - -LINK_ENTITY_TO_CLASS( env_shockwave, CEnvShockwave ); - -TYPEDESCRIPTION CEnvShockwave::m_SaveData[] = -{ - DEFINE_FIELD( CEnvShockwave, m_iHeight, FIELD_INTEGER ), - DEFINE_FIELD( CEnvShockwave, m_iTime, FIELD_INTEGER ), - DEFINE_FIELD( CEnvShockwave, m_iRadius, FIELD_INTEGER ), - DEFINE_FIELD( CEnvShockwave, m_iScrollRate, FIELD_INTEGER ), - DEFINE_FIELD( CEnvShockwave, m_iNoise, FIELD_INTEGER ), - DEFINE_FIELD( CEnvShockwave, m_iFrameRate, FIELD_INTEGER ), - DEFINE_FIELD( CEnvShockwave, m_iStartFrame, FIELD_INTEGER ), - DEFINE_FIELD( CEnvShockwave, m_iSpriteTexture, FIELD_INTEGER ), - DEFINE_FIELD( CEnvShockwave, m_cType, FIELD_CHARACTER ), - DEFINE_FIELD( CEnvShockwave, m_iszPosition, FIELD_STRING ), -}; - -IMPLEMENT_SAVERESTORE( CEnvShockwave, CBaseEntity ); - -void CEnvShockwave::Precache( void ) -{ - m_iSpriteTexture = PRECACHE_MODEL( (char *)STRING(pev->netname) ); -} - -void CEnvShockwave::KeyValue( KeyValueData *pkvd ) -{ - if (FStrEq(pkvd->szKeyName, "m_iTime")) - { - m_iTime = atoi(pkvd->szValue); - pkvd->fHandled = TRUE; - } - else if (FStrEq(pkvd->szKeyName, "m_iRadius")) - { - m_iRadius = atoi(pkvd->szValue); - pkvd->fHandled = TRUE; - } - else if (FStrEq(pkvd->szKeyName, "m_iHeight")) - { - m_iHeight = atoi(pkvd->szValue)/2; //LRC- the actual height is doubled when drawn - pkvd->fHandled = TRUE; - } - else if (FStrEq(pkvd->szKeyName, "m_iScrollRate")) - { - m_iScrollRate = atoi(pkvd->szValue); - pkvd->fHandled = TRUE; - } - else if (FStrEq(pkvd->szKeyName, "m_iNoise")) - { - m_iNoise = atoi(pkvd->szValue); - pkvd->fHandled = TRUE; - } - else if (FStrEq(pkvd->szKeyName, "m_iFrameRate")) - { - m_iFrameRate = atoi(pkvd->szValue); - pkvd->fHandled = TRUE; - } - else if (FStrEq(pkvd->szKeyName, "m_iStartFrame")) - { - m_iStartFrame = atoi(pkvd->szValue); - pkvd->fHandled = TRUE; - } - else if (FStrEq(pkvd->szKeyName, "m_iszPosition")) - { - m_iszPosition = ALLOC_STRING(pkvd->szValue); - pkvd->fHandled = TRUE; - } - else if (FStrEq(pkvd->szKeyName, "m_cType")) - { - m_cType = atoi(pkvd->szValue); - pkvd->fHandled = TRUE; - } - else - CBaseEntity::KeyValue( pkvd ); -} - -void CEnvShockwave::Use( CBaseEntity *pActivator, CBaseEntity *pCaller, USE_TYPE useType, float value ) -{ - Vector vecPos; - if (m_iszPosition) - vecPos = CalcLocus_Position( this, pActivator, STRING(m_iszPosition) ); - else - vecPos = pev->origin; - - if (!(pev->spawnflags & SF_SHOCKWAVE_CENTERED)) - vecPos.z += m_iHeight; - - if(pev->target) FireTargets(STRING(pev->target),pActivator,pCaller,useType,value); //AJH - - // blast circle - MESSAGE_BEGIN( MSG_PVS, gmsgTempEntity, pev->origin ); - if (m_cType) - WRITE_BYTE( m_cType ); - else - WRITE_BYTE( TE_BEAMCYLINDER ); - WRITE_COORD( vecPos.x );// coord coord coord (center position) - WRITE_COORD( vecPos.y ); - WRITE_COORD( vecPos.z ); - WRITE_COORD( vecPos.x );// coord coord coord (axis and radius) - WRITE_COORD( vecPos.y ); - WRITE_COORD( vecPos.z + m_iRadius ); - WRITE_SHORT( m_iSpriteTexture ); // short (sprite index) - WRITE_BYTE( m_iStartFrame ); // byte (starting frame) - WRITE_BYTE( m_iFrameRate ); // byte (frame rate in 0.1's) - WRITE_BYTE( m_iTime ); // byte (life in 0.1's) - WRITE_BYTE( m_iHeight ); // byte (line width in 0.1's) - WRITE_BYTE( m_iNoise ); // byte (noise amplitude in 0.01's) - WRITE_BYTE( pev->rendercolor.x ); // byte,byte,byte (color) - WRITE_BYTE( pev->rendercolor.y ); - WRITE_BYTE( pev->rendercolor.z ); - WRITE_BYTE( pev->renderamt ); // byte (brightness) - WRITE_BYTE( m_iScrollRate ); // byte (scroll speed in 0.1's) - MESSAGE_END(); - - if (!(pev->spawnflags & SF_SHOCKWAVE_REPEATABLE)) - { - SetThink(&CEnvShockwave:: SUB_Remove ); - SetNextThink( 0 ); - } -} - -//================================================================== -//LRC- env_dlight; Dynamic Entity Light creator -//================================================================== -#define SF_DLIGHT_ONLYONCE 1 -#define SF_DLIGHT_STARTON 2 -class CEnvDLight : public CPointEntity -{ -public: - void PostSpawn( void ); - virtual void Use( CBaseEntity *pActivator, CBaseEntity *pCaller, USE_TYPE useType, float value ); - void Think( void ); - void DesiredAction( void ); - virtual void MakeLight( int iTime ); - virtual int Save( CSave &save ); - virtual int Restore( CRestore &restore ); - static TYPEDESCRIPTION m_SaveData[]; - STATE GetState( void ) - { - if (pev->spawnflags & SF_DLIGHT_STARTON) - return STATE_ON; - else - return STATE_OFF; - } - - Vector m_vecPos; - int m_iKey; - static int ms_iNextFreeKey; -}; - -LINK_ENTITY_TO_CLASS( env_dlight, CEnvDLight ); - -TYPEDESCRIPTION CEnvDLight::m_SaveData[] = -{ - DEFINE_FIELD( CEnvDLight, m_vecPos, FIELD_VECTOR ), - DEFINE_FIELD( CEnvDLight, m_iKey, FIELD_INTEGER ), -}; - -IMPLEMENT_SAVERESTORE( CEnvDLight, CPointEntity ); - -int CEnvDLight::ms_iNextFreeKey = 1; - -void CEnvDLight::PostSpawn( void ) -{ - // each env_dlight uses its own key to reference the light on the client - m_iKey = ms_iNextFreeKey; - ms_iNextFreeKey++; - - if (FStringNull(pev->targetname) || pev->spawnflags & SF_DLIGHT_STARTON) - { - UTIL_DesiredAction(this); - } -} - -void CEnvDLight::DesiredAction( void ) -{ - pev->spawnflags &= ~SF_DLIGHT_STARTON; - Use(this, this, USE_ON, 0); -} - -void CEnvDLight::Use( CBaseEntity *pActivator, CBaseEntity *pCaller, USE_TYPE useType, float value ) -{ - if (!ShouldToggle(useType)) - { - return; - } - if (GetState() == STATE_ON) - { - // turn off - MakeLight(false); - pev->spawnflags &= ~SF_DLIGHT_STARTON; - DontThink(); - return; - } - - if (pev->message) - { - m_vecPos = CalcLocus_Position( this, pActivator, STRING(pev->message) ); - } - else - { - m_vecPos = pev->origin; - } - - // turn on - MakeLight(true); - pev->spawnflags |= SF_DLIGHT_STARTON; - - if (pev->health) - { - SetNextThink(pev->health); - } - else - { - if (pev->spawnflags & SF_DLIGHT_ONLYONCE) - { - SetThink( SUB_Remove ); - SetNextThink( 0 ); - } - } -} - -extern int gmsgKeyedDLight; - -void CEnvDLight::MakeLight( BOOL bActive) -{ - MESSAGE_BEGIN( MSG_ALL, gmsgKeyedDLight, NULL ); - WRITE_BYTE( m_iKey ); - WRITE_BYTE( bActive ); // visible? - if (bActive) - { - WRITE_COORD( m_vecPos.x ); // X - WRITE_COORD( m_vecPos.y ); // Y - WRITE_COORD( m_vecPos.z ); // Z - WRITE_BYTE( pev->renderamt ); // radius * 0.1 - WRITE_BYTE( pev->rendercolor.x ); // r - WRITE_BYTE( pev->rendercolor.y ); // g - WRITE_BYTE( pev->rendercolor.z ); // b - } - MESSAGE_END(); -} - -void CEnvDLight::Think( void ) -{ - // turn off the light - MakeLight( false ); - pev->spawnflags &= ~SF_DLIGHT_STARTON; - - if (pev->spawnflags & SF_DLIGHT_ONLYONCE) - { - SetThink( SUB_Remove ); - SetNextThink( 0 ); - } -} - -//================================================================== -//LRC- env_elight; Dynamic Entity Light creator -//================================================================== -class CEnvELight : public CEnvDLight -{ -public: - void Use( CBaseEntity *pActivator, CBaseEntity *pCaller, USE_TYPE useType, float value ); - void MakeLight(int iTime); - virtual int Save( CSave &save ); - virtual int Restore( CRestore &restore ); - static TYPEDESCRIPTION m_SaveData[]; - - EHANDLE m_hAttach; -}; - -LINK_ENTITY_TO_CLASS( env_elight, CEnvELight ); - -TYPEDESCRIPTION CEnvELight::m_SaveData[] = -{ - DEFINE_FIELD( CEnvELight, m_hAttach, FIELD_EHANDLE ), -}; - -IMPLEMENT_SAVERESTORE( CEnvELight, CEnvDLight ); - -void CEnvELight::Use( CBaseEntity *pActivator, CBaseEntity *pCaller, USE_TYPE useType, float value ) -{ - if (pev->target) - { - m_hAttach = UTIL_FindEntityByTargetname( NULL, STRING(pev->target), pActivator); - if (m_hAttach == NULL) - { - ALERT(at_console, "env_elight \"%s\" can't find target %s\n", STRING(pev->targetname), STRING(pev->target)); - return; // error? - } - } - else - { - m_hAttach = this; - } - - CEnvDLight::Use(pActivator, pCaller, useType, value); -} - -void CEnvELight::MakeLight(int iTime) -{ - if (m_hAttach == NULL) - { - DontThink(); - pev->takedamage = 0; - return; - } - - MESSAGE_BEGIN( MSG_PVS, gmsgTempEntity, pev->origin ); - WRITE_BYTE( TE_ELIGHT ); - WRITE_SHORT( m_hAttach->entindex( ) + 0x1000 * pev->impulse ); // entity, attachment - WRITE_COORD( m_vecPos.x ); // X - WRITE_COORD( m_vecPos.y ); // Y - WRITE_COORD( m_vecPos.z ); // Z - WRITE_COORD( pev->renderamt ); // radius * 0.1 - WRITE_BYTE( pev->rendercolor.x ); // r - WRITE_BYTE( pev->rendercolor.y ); // g - WRITE_BYTE( pev->rendercolor.z ); // b - WRITE_BYTE( iTime ); // time * 10 - WRITE_COORD( pev->frags ); // decay * 0.1 - MESSAGE_END( ); -} - - -//========================================================= -// LRC - Decal effect -//========================================================= -class CEnvDecal : public CPointEntity -{ -public: - void Spawn( void ); - void Use( CBaseEntity *pActivator, CBaseEntity *pCaller, USE_TYPE useType, float value ); -}; - -LINK_ENTITY_TO_CLASS( env_decal, CEnvDecal ); - -void CEnvDecal::Use( CBaseEntity *pActivator, CBaseEntity *pCaller, USE_TYPE useType, float value ) -{ - int iTexture = 0; - - switch(pev->impulse) - { - case 1: iTexture = DECAL_GUNSHOT1 + RANDOM_LONG(0,4); break; - case 2: iTexture = DECAL_BLOOD1 + RANDOM_LONG(0,5); break; - case 3: iTexture = DECAL_YBLOOD1 + RANDOM_LONG(0,5); break; - case 4: iTexture = DECAL_GLASSBREAK1+ RANDOM_LONG(0,2); break; - case 5: iTexture = DECAL_BIGSHOT1 + RANDOM_LONG(0,4); break; - case 6: iTexture = DECAL_SCORCH1 + RANDOM_LONG(0,1); break; - case 7: iTexture = DECAL_SPIT1 + RANDOM_LONG(0,1); break; - } - - if (pev->impulse) - iTexture = gDecals[ iTexture ].index; - else - iTexture = pev->skin; // custom texture - - Vector vecPos; - if (!FStringNull(pev->target)) - vecPos = CalcLocus_Position( this, pActivator, STRING(pev->target) ); - else - vecPos = pev->origin; - - Vector vecOffs; - if (!FStringNull(pev->netname)) - vecOffs = CalcLocus_Velocity( this, pActivator, STRING(pev->netname) ); - else - { - UTIL_MakeVectors(pev->angles); - vecOffs = gpGlobals->v_forward; - } - - if (pev->message) - vecOffs = vecOffs * CalcLocus_Ratio( pActivator, STRING(pev->message) ); - else - vecOffs = vecOffs.Normalize() * 4000; - - - TraceResult trace; - int entityIndex; - - UTIL_TraceLine( vecPos, vecPos+vecOffs, ignore_monsters, NULL, &trace ); - - if (trace.flFraction == 1.0) - return; // didn't hit anything, oh well - - entityIndex = (short)ENTINDEX(trace.pHit); - - MESSAGE_BEGIN( MSG_BROADCAST, gmsgTempEntity); - WRITE_BYTE( TE_BSPDECAL ); - WRITE_COORD( trace.vecEndPos.x ); - WRITE_COORD( trace.vecEndPos.y ); - WRITE_COORD( trace.vecEndPos.z ); - WRITE_SHORT( iTexture ); - WRITE_SHORT( entityIndex ); - if ( entityIndex ) - WRITE_SHORT( (int)VARS(trace.pHit)->modelindex ); - MESSAGE_END(); -} - -void CEnvDecal::Spawn( void ) -{ - if (pev->impulse == 0) - { - pev->skin = DECAL_INDEX( STRING(pev->noise) ); - - if ( pev->skin == 0 ) - ALERT( at_debug, "locus_decal \"%s\" can't find decal \"%s\"\n", STRING(pev->noise) ); - } -} - - -//========================================================= -// Beverage Dispenser -// overloaded pev->frags, is now a flag for whether or not a can is stuck in the dispenser. -// overloaded pev->health, is now how many cans remain in the machine. -//========================================================= -class CEnvBeverage : public CBaseDelay -{ -public: - void Spawn( void ); - void Precache( void ); - void Use( CBaseEntity *pActivator, CBaseEntity *pCaller, USE_TYPE useType, float value ); - - // it's 'on' while there are cans left - virtual STATE GetState( void ) { return (pev->health > 0)?STATE_ON:STATE_OFF; }; -}; - -void CEnvBeverage :: Precache ( void ) -{ - PRECACHE_MODEL( "models/can.mdl" ); - PRECACHE_SOUND( "weapons/g_bounce3.wav" ); -} - -LINK_ENTITY_TO_CLASS( env_beverage, CEnvBeverage ); - -void CEnvBeverage::Use( CBaseEntity *pActivator, CBaseEntity *pCaller, USE_TYPE useType, float value ) -{ - if ( pev->frags != 0 || pev->health <= 0 ) - { - // no more cans while one is waiting in the dispenser, or if I'm out of cans. - return; - } - - Vector vecPos; - if (pev->target) - vecPos = CalcLocus_Position( this, pActivator, STRING(pev->target) ); - else - vecPos = pev->origin; - - CBaseEntity *pCan = CBaseEntity::Create( "item_sodacan", vecPos, pev->angles, edict() ); - - if ( pev->skin == 6 ) - { - // random - pCan->pev->skin = RANDOM_LONG( 0, 5 ); - } - else - { - pCan->pev->skin = pev->skin; - } - - pev->frags = 1; - pev->health--; - - //SetThink (SUB_Remove); - //pev->nextthink = gpGlobals->time; -} - -void CEnvBeverage::Spawn( void ) -{ - Precache(); - pev->solid = SOLID_NOT; - pev->effects = EF_NODRAW; - pev->frags = 0; - - if ( pev->health == 0 ) - { - pev->health = 10; - } -} - -//========================================================= -// Soda can -//========================================================= -class CItemSoda : public CBaseEntity -{ -public: - void Spawn( void ); - void Precache( void ); - void EXPORT CanThink ( void ); - void EXPORT CanTouch ( CBaseEntity *pOther ); -}; - -void CItemSoda :: Precache ( void ) -{ - // added for Nemo1024 --LRC - PRECACHE_MODEL( "models/can.mdl" ); - PRECACHE_SOUND( "weapons/g_bounce3.wav" ); -} - -LINK_ENTITY_TO_CLASS( item_sodacan, CItemSoda ); - -void CItemSoda::Spawn( void ) -{ - Precache(); - pev->solid = SOLID_NOT; - pev->movetype = MOVETYPE_TOSS; - - SET_MODEL ( ENT(pev), "models/can.mdl" ); - UTIL_SetSize ( pev, Vector ( 0, 0, 0 ), Vector ( 0, 0, 0 ) ); - - SetThink(&CItemSoda::CanThink); - SetNextThink( 0.5 ); -} - -void CItemSoda::CanThink ( void ) -{ - EMIT_SOUND (ENT(pev), CHAN_WEAPON, "weapons/g_bounce3.wav", 1, ATTN_NORM ); - - pev->solid = SOLID_TRIGGER; - UTIL_SetSize ( pev, Vector ( -8, -8, 0 ), Vector ( 8, 8, 8 ) ); - SetThink ( NULL ); - SetTouch(&CItemSoda:: CanTouch ); -} - -void CItemSoda::CanTouch ( CBaseEntity *pOther ) -{ - if ( !pOther->IsPlayer() ) - { - return; - } - - // spoit sound here - - pOther->TakeHealth( 1, DMG_GENERIC );// a bit of health. - - if ( !FNullEnt( pev->owner ) ) - { - // tell the machine the can was taken - pev->owner->v.frags = 0; - } - - pev->solid = SOLID_NOT; - pev->movetype = MOVETYPE_NONE; - pev->effects = EF_NODRAW; - SetTouch ( NULL ); - SetThink(&CItemSoda:: SUB_Remove ); - SetNextThink( 0 ); -} - -//========================================================= -// LRC - env_fog, extended a bit from the DMC version -// g-cont - fix save\load issues, add multiplayer support -//========================================================= -#define SF_FOG_ACTIVE 1 -#define SF_FOG_FADING 0x8000 - -class CEnvFog : public CBaseEntity -{ -public: - void Spawn( void ); - void EXPORT TurnOn( void ); - void EXPORT TurnOff( void ); - void EXPORT FadeInDone( void ); - void EXPORT FadeOutDone( void ); - void KeyValue( KeyValueData *pkvd ); - virtual int Save( CSave &save ); - virtual int Restore( CRestore &restore ); - static TYPEDESCRIPTION m_SaveData[]; - void Use( CBaseEntity *pActivator, CBaseEntity *pCaller, USE_TYPE useType, float value ); - - STATE GetState( void ); - - int m_iStartDist; - int m_iEndDist; - float m_iFadeIn; - float m_iFadeOut; - float m_fHoldTime; - float m_fFadeStart; // if we're fading in/out, then when did the fade start? -}; - -TYPEDESCRIPTION CEnvFog::m_SaveData[] = -{ - DEFINE_FIELD( CEnvFog, m_iStartDist, FIELD_INTEGER ), - DEFINE_FIELD( CEnvFog, m_iEndDist, FIELD_INTEGER ), - DEFINE_FIELD( CEnvFog, m_iFadeIn, FIELD_INTEGER ), - DEFINE_FIELD( CEnvFog, m_iFadeOut, FIELD_INTEGER ), - DEFINE_FIELD( CEnvFog, m_fHoldTime, FIELD_FLOAT ), - DEFINE_FIELD( CEnvFog, m_fFadeStart, FIELD_TIME ), -}; - -IMPLEMENT_SAVERESTORE( CEnvFog, CBaseEntity ); - -void CEnvFog :: KeyValue( KeyValueData *pkvd ) -{ - if (FStrEq(pkvd->szKeyName, "startdist")) - { - m_iStartDist = atoi(pkvd->szValue); - pkvd->fHandled = TRUE; - } - else if (FStrEq(pkvd->szKeyName, "enddist")) - { - m_iEndDist = atoi(pkvd->szValue); - pkvd->fHandled = TRUE; - } - else if (FStrEq(pkvd->szKeyName, "fadein")) - { - m_iFadeIn = atoi(pkvd->szValue); - pkvd->fHandled = TRUE; - } - else if (FStrEq(pkvd->szKeyName, "fadeout")) - { - m_iFadeOut = atoi(pkvd->szValue); - pkvd->fHandled = TRUE; - } - else if (FStrEq(pkvd->szKeyName, "holdtime")) - { - m_fHoldTime = atof(pkvd->szValue); - pkvd->fHandled = TRUE; - } - else - CBaseEntity::KeyValue( pkvd ); -} - -STATE CEnvFog::GetState( void ) -{ - if (pev->spawnflags & SF_FOG_ACTIVE) - { - if (pev->spawnflags & SF_FOG_FADING) - return STATE_TURN_ON; - else - return STATE_ON; - } - else - { - if (pev->spawnflags & SF_FOG_FADING) - return STATE_TURN_OFF; - else - return STATE_OFF; - } -} - -void CEnvFog :: Spawn ( void ) -{ - pev->effects |= EF_NODRAW; - - if (pev->targetname == 0) - pev->spawnflags |= SF_FOG_ACTIVE; - - if (pev->spawnflags & SF_FOG_ACTIVE) - { - SetThink(&CEnvFog :: TurnOn ); - UTIL_DesiredThink( this ); - } - - // things get messed up if we try to draw fog with a startdist - // or an enddist of 0, so we don't allow it. - if (m_iStartDist == 0) m_iStartDist = 1; - if (m_iEndDist == 0) m_iEndDist = 1000; -} - -extern int gmsgSetFog; - -void CEnvFog :: TurnOn ( void ) -{ -// ALERT(at_console, "Fog turnon %f\n", gpGlobals->time); - - pev->spawnflags |= SF_FOG_ACTIVE; - - UTIL_SetFogAll( pev->rendercolor, m_iFadeIn, m_iStartDist, m_iEndDist ); - - if( m_iFadeIn ) - { - pev->spawnflags |= SF_FOG_FADING; - SetNextThink( m_iFadeIn ); - SetThink(&CEnvFog :: FadeInDone ); - } - else - { - pev->spawnflags &= ~SF_FOG_FADING; - if (m_fHoldTime) - { - SetNextThink( m_fHoldTime ); - SetThink(&CEnvFog :: TurnOff ); - } - } -} - -void CEnvFog :: TurnOff ( void ) -{ -// ALERT(at_console, "Fog turnoff\n"); - - pev->spawnflags &= ~SF_FOG_ACTIVE; - - UTIL_SetFogAll( pev->rendercolor, -m_iFadeOut, m_iStartDist, m_iEndDist ); - - if( m_iFadeOut ) - { - pev->spawnflags |= SF_FOG_FADING; - SetNextThink( m_iFadeOut ); - SetThink(&CEnvFog :: FadeOutDone ); - } - else - { - pev->spawnflags &= ~SF_FOG_FADING; - DontThink(); - } -} - -void CEnvFog :: FadeInDone ( void ) -{ - pev->spawnflags &= ~SF_FOG_FADING; - - if (m_fHoldTime) - { - SetNextThink( m_fHoldTime ); - SetThink(&CEnvFog :: TurnOff ); - } -} - -void CEnvFog :: FadeOutDone ( void ) -{ - pev->spawnflags &= ~SF_FOG_FADING; -} - -void CEnvFog :: Use( CBaseEntity *pActivator, CBaseEntity *pCaller, USE_TYPE useType, float value ) -{ -// ALERT(at_console, "Fog use %s %s\n", GetStringForUseType(useType), GetStringForState(GetState())); - if (ShouldToggle(useType)) - { - if (pev->spawnflags & SF_FOG_ACTIVE) - TurnOff(); - else - TurnOn(); - } -} -LINK_ENTITY_TO_CLASS( env_fog, CEnvFog ); - -//========================================================= -// LRC - env_sky, an unreal tournament-style sky effect -//========================================================= -class CEnvSky : public CBaseEntity -{ -public: - void PostSpawn( void ) { SetObjectClass( ED_SKYPORTAL ); } -}; -LINK_ENTITY_TO_CLASS( env_sky, CEnvSky ); - -//========================================================= -// LRC - env_particle, uses the aurora particle system -//========================================================= -//extern int gmsgParticle = 0; -#define SF_PARTICLE_ON 1 -#define SF_PARTICLE_SPAWNUSE 2 //AJH for spawnable env_particles - -class CParticle : public CPointEntity -{ -public: - void Spawn( void ); - void Activate( void ); - void Precache( void ); - void DesiredAction( void ); - void EXPORT Think( void ); - - void Use( CBaseEntity *pActivator, CBaseEntity *pCaller, USE_TYPE useType, float value ); -}; - -LINK_ENTITY_TO_CLASS( env_particle, CParticle ); - -void CParticle::Spawn( void ) -{ - SetObjectClass( ED_NORMAL ); - - pev->solid = SOLID_NOT; - pev->movetype = MOVETYPE_NOCLIP; - - // 'body' determines whether the effect is active or not - pev->renderfx = (pev->spawnflags & SF_PARTICLE_ON) ? kRenderFxAurora : kRenderFxNone; - - Precache(); -} - - -void CParticle::Precache( void ) -{ - // NOTE: precacheFunc can change particle name - // to default error particle script - pev->message = PRECACHE_PARTICLE( pev->message ); -} - -void CParticle::Activate( void ) -{ - CPointEntity::Activate(); - UTIL_DesiredAction(this); -} - -void CParticle::DesiredAction( void ) -{ - pev->nextthink = gpGlobals->time + 1; -} - -void CParticle::Think( void ) -{ - MESSAGE_BEGIN( MSG_ALL, gmsgParticle ); - WRITE_SHORT( entindex() ); - WRITE_STRING( STRING( pev->message )); - MESSAGE_END(); -} - -void CParticle::Use( CBaseEntity *pActivator, CBaseEntity *pCaller, USE_TYPE useType, float value ) -{ - if ( pev->spawnflags & SF_PARTICLE_SPAWNUSE ) - { - // Create a new entity with Cparticle private data - CParticle *pParticle = GetClassPtr( (CParticle *)NULL ); - pParticle->pev->classname = MAKE_STRING( "particle" ); - - if ( pev->netname != NULL ) - { - // set childrens name (targetname) from netname - pParticle->pev->targetname = pev->netname; - } - - pParticle->pev->message = pev->message; - pParticle->pev->origin = pActivator->pev->origin; - pParticle->pev->angles = pActivator->pev->angles; - pParticle->Spawn(); - pParticle->pev->renderfx = kRenderFxAurora; // turn children on automatically - pParticle->Think(); - } - else - { - if ( ShouldToggle( useType, pev->renderfx ) ) - { - if( pev->renderfx == kRenderFxAurora ) - pev->renderfx = kRenderFxNone; - else pev->renderfx = kRenderFxAurora; - } - } -} - -//========================================================= -// G-Cont - env_mirror (engine feature) -//========================================================= - -class CEnvMirror : public CBaseEntity -{ -public: - void Spawn( void ); - void Use( CBaseEntity *pActivator, CBaseEntity *pCaller, USE_TYPE useType, float value ); - virtual int ObjectCaps( void ) { return CBaseEntity :: ObjectCaps() & ~FCAP_ACROSS_TRANSITION; } - STATE GetState( void ) { return (pev->effects & EF_NODRAW) ? STATE_OFF : STATE_ON; } -}; - -LINK_ENTITY_TO_CLASS( env_mirror, CEnvMirror ); - -void CEnvMirror :: Spawn( void ) -{ - // setup mirror - SetObjectClass( ED_PORTAL ); - UTIL_SetOrigin( this, pev->origin ); - pev->modelindex = 1; // world - pev->oldorigin = pev->origin; -} - -void CEnvMirror :: Use( CBaseEntity *pActivator, CBaseEntity *pCaller, USE_TYPE useType, float value ) -{ - if ( useType == USE_TOGGLE ) - { - if( GetState() == STATE_ON ) - useType = USE_OFF; - else useType = USE_ON; - } - if ( useType == USE_ON ) - { - pev->effects &= ~EF_NODRAW; - } - else if ( useType == USE_OFF ) - { - pev->effects |= EF_NODRAW; - } -} -//========================================================= -// G-Cont - env_rain, use triAPI -//========================================================= -void CRainSettings::Precache( void ) -{ - if( Rain_Mode ) - { - // snow - PRECACHE_MODEL( "sprites/snowflake.spr" ); - } - else - { - // it's a rainy day :) - PRECACHE_MODEL( "sprites/hi_rain.spr" ); - PRECACHE_MODEL( "sprites/waterring.spr" ); - } -} - -void CRainSettings::Spawn() -{ - Precache(); - - pev->solid = SOLID_NOT; - pev->movetype = MOVETYPE_NONE; - pev->effects |= EF_NODRAW; -} - -void CRainSettings::KeyValue( KeyValueData *pkvd ) -{ - if (FStrEq(pkvd->szKeyName, "m_flDistance")) - { - Rain_Distance = atof(pkvd->szValue); - pkvd->fHandled = TRUE; - } - else if (FStrEq(pkvd->szKeyName, "m_iMode")) - { - Rain_Mode = atoi(pkvd->szValue); - pkvd->fHandled = TRUE; - } - else - { - CBaseEntity::KeyValue( pkvd ); - } -} - -LINK_ENTITY_TO_CLASS( rain_settings, CRainSettings ); - -TYPEDESCRIPTION CRainSettings::m_SaveData[] = -{ - DEFINE_FIELD( CRainSettings, Rain_Distance, FIELD_FLOAT ), - DEFINE_FIELD( CRainSettings, Rain_Mode, FIELD_INTEGER ), -}; -IMPLEMENT_SAVERESTORE( CRainSettings, CBaseEntity ); - - - -void CRainModify::Spawn() -{ - pev->solid = SOLID_NOT; - pev->movetype = MOVETYPE_NONE; - pev->effects |= EF_NODRAW; - - if (FStringNull(pev->targetname)) - pev->spawnflags |= 1; -} - -void CRainModify::KeyValue( KeyValueData *pkvd ) -{ - if (FStrEq(pkvd->szKeyName, "m_iDripsPerSecond")) - { - Rain_Drips = atoi(pkvd->szValue); - pkvd->fHandled = TRUE; - } - else if (FStrEq(pkvd->szKeyName, "m_flWindX")) - { - Rain_windX = atof(pkvd->szValue); - pkvd->fHandled = TRUE; - } - else if (FStrEq(pkvd->szKeyName, "m_flWindY")) - { - Rain_windY = atof(pkvd->szValue); - pkvd->fHandled = TRUE; - } - else if (FStrEq(pkvd->szKeyName, "m_flRandX")) - { - Rain_randX = atof(pkvd->szValue); - pkvd->fHandled = TRUE; - } - else if (FStrEq(pkvd->szKeyName, "m_flRandY")) - { - Rain_randY = atof(pkvd->szValue); - pkvd->fHandled = TRUE; - } - else if (FStrEq(pkvd->szKeyName, "m_flTime")) - { - fadeTime = atof(pkvd->szValue); - pkvd->fHandled = TRUE; - } - else - { - CBaseEntity::KeyValue( pkvd ); - } -} - -void CRainModify::Use( CBaseEntity *pActivator, CBaseEntity *pCaller, USE_TYPE useType, float value ) -{ - if (pev->spawnflags & 1) - return; // constant - - if (gpGlobals->deathmatch) - { - ALERT(at_console, "Rain error: only static rain in multiplayer\n"); - return; // not in multiplayer - } - - CBasePlayer *pPlayer; - pPlayer = (CBasePlayer *)CBaseEntity::Instance(g_engfuncs.pfnPEntityOfEntIndex(1)); - - if (fadeTime) - { // write to 'ideal' settings - pPlayer->Rain_ideal_dripsPerSecond = Rain_Drips; - pPlayer->Rain_ideal_randX = Rain_randX; - pPlayer->Rain_ideal_randY = Rain_randY; - pPlayer->Rain_ideal_windX = Rain_windX; - pPlayer->Rain_ideal_windY = Rain_windY; - - pPlayer->Rain_endFade = gpGlobals->time + fadeTime; - pPlayer->Rain_nextFadeUpdate = gpGlobals->time + 1; - } - else - { - pPlayer->Rain_dripsPerSecond = Rain_Drips; - pPlayer->Rain_randX = Rain_randX; - pPlayer->Rain_randY = Rain_randY; - pPlayer->Rain_windX = Rain_windX; - pPlayer->Rain_windY = Rain_windY; - - pPlayer->Rain_needsUpdate = 1; - } -} - -LINK_ENTITY_TO_CLASS( rain_modify, CRainModify ); - -TYPEDESCRIPTION CRainModify::m_SaveData[] = -{ - DEFINE_FIELD( CRainModify, fadeTime, FIELD_FLOAT ), - DEFINE_FIELD( CRainModify, Rain_Drips, FIELD_INTEGER ), - DEFINE_FIELD( CRainModify, Rain_randX, FIELD_FLOAT ), - DEFINE_FIELD( CRainModify, Rain_randY, FIELD_FLOAT ), - DEFINE_FIELD( CRainModify, Rain_windX, FIELD_FLOAT ), - DEFINE_FIELD( CRainModify, Rain_windY, FIELD_FLOAT ), -}; -IMPLEMENT_SAVERESTORE( CRainModify, CBaseEntity ); - diff --git a/spirit/egon.cpp b/spirit/egon.cpp deleted file mode 100644 index 6f13c706..00000000 --- a/spirit/egon.cpp +++ /dev/null @@ -1,607 +0,0 @@ -/*** -* -* Copyright (c) 1996-2002, 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. -* -****/ - -#include "extdll.h" -#include "util.h" -#include "cbase.h" -#include "player.h" -#include "monsters.h" -#include "weapons.h" -#include "nodes.h" -#include "effects.h" -#include "gamerules.h" - -#define EGON_PRIMARY_VOLUME 450 -#define EGON_BEAM_SPRITE "sprites/xbeam1.spr" -#define EGON_FLARE_SPRITE "sprites/XSpark1.spr" -#define EGON_SOUND_OFF "weapons/egon_off1.wav" -#define EGON_SOUND_RUN "weapons/egon_run3.wav" -#define EGON_SOUND_STARTUP "weapons/egon_windup2.wav" - -#define EGON_SWITCH_NARROW_TIME 0.75 // Time it takes to switch fire modes -#define EGON_SWITCH_WIDE_TIME 1.5 -#define PRIMARY_CHARGE_VOLUME 256 -#define PRIMARY_FIRE_VOLUME 450 - -enum egon_e { - EGON_IDLE1 = 0, - EGON_FIDGET1, - EGON_ALTFIREON, - EGON_ALTFIREOFF, - EGON_FIRESTOP, - EGON_FIRECYCLE, - EGON_DRAW, - EGON_HOLSTER -}; - -class CEgon : public CBasePlayerWeapon -{ -public: - int Save( CSave &save ); - int Restore( CRestore &restore ); - static TYPEDESCRIPTION m_SaveData[]; - - void Spawn( void ); - void Precache( void ); - int GetItemInfo(ItemInfo *p); - - BOOL Deploy( void ); - void Holster( ); - - void CreateEffect ( void ); - void UpdateEffect( const Vector &startPoint, const Vector &endPoint, float timeBlend ); - void DestroyEffect ( void ); - - void EndAttack( void ); - void UpdateScreen( void ); - void ShutdownScreen( void ); - void PrimaryAttack( void ); - void SecondaryAttack( void ); - void WeaponIdle( void ); - - float m_flAmmoUseTime;// since we use < 1 point of ammo per update, we subtract ammo on a timer. - - void Fire( const Vector &vecOrigSrc, const Vector &vecDir ); - - BOOL HasAmmo( void ); - void UseAmmo( int count ); - - enum EGON_FIRESTATE { FIRE_OFF = 0, FIRE_CHARGE }; - enum EGON_FIREMODE { FIRE_NARROW = 0, FIRE_WIDE}; - -private: - float m_shootTime; - CBeam *m_pBeam; - CBeam *m_pNoise; - CSprite *m_pSprite; - EGON_FIRESTATE m_fireState; - EGON_FIREMODE m_fireMode; - float m_shakeTime; - unsigned int m_usEgonFire; - unsigned int m_usEgonStop; -}; - -LINK_ENTITY_TO_CLASS( weapon_egon, CEgon ); - -TYPEDESCRIPTION CEgon::m_SaveData[] = -{ - DEFINE_FIELD( CEgon, m_pBeam, FIELD_CLASSPTR ), - DEFINE_FIELD( CEgon, m_pNoise, FIELD_CLASSPTR ), - DEFINE_FIELD( CEgon, m_pSprite, FIELD_CLASSPTR ), - DEFINE_FIELD( CEgon, m_shootTime, FIELD_TIME ), - DEFINE_FIELD( CEgon, m_fireState, FIELD_INTEGER ), - DEFINE_FIELD( CEgon, m_fireMode, FIELD_INTEGER ), - DEFINE_FIELD( CEgon, m_shakeTime, FIELD_TIME ), - DEFINE_FIELD( CEgon, m_flAmmoUseTime, FIELD_TIME ), -}; -IMPLEMENT_SAVERESTORE( CEgon, CBasePlayerWeapon ); - -void CEgon::Spawn( ) -{ - Precache( ); - SET_MODEL(ENT(pev), "models/w_egon.mdl"); - - m_iDefaultAmmo = EGON_DEFAULT_GIVE; - m_fireMode = FIRE_WIDE;//place it here - FallInit();// get ready to fall down. -} - - -void CEgon::Precache( void ) -{ - PRECACHE_MODEL("models/w_egon.mdl"); - PRECACHE_MODEL("models/v_egon.mdl"); - PRECACHE_MODEL("models/p_egon.mdl"); - PRECACHE_MODEL( EGON_BEAM_SPRITE ); - PRECACHE_MODEL( EGON_FLARE_SPRITE ); - - m_usEgonFire = PRECACHE_EVENT ( 1, "evEgonFire" ); - m_usEgonStop = PRECACHE_EVENT ( 1, "evEgonStop" ); -} - - -BOOL CEgon::Deploy( void ) -{ - m_fireState = FIRE_OFF; - - AnimRestore = TRUE; - return DefaultDeploy( "models/v_egon.mdl", "models/p_egon.mdl", EGON_DRAW, "egon", 0.7 ); -} - -void CEgon::Holster() -{ - if ( m_fireState != FIRE_OFF ) EndAttack(); - - ShutdownScreen(); - m_pPlayer->m_flNextAttack = UTIL_WeaponTimeBase() + 0.5; - SendWeaponAnim( EGON_HOLSTER ); -} - -int CEgon::GetItemInfo(ItemInfo *p) -{ - p->pszName = STRING(pev->classname); - p->pszAmmo1 = "uranium"; - p->iMaxAmmo1 = URANIUM_MAX_CARRY; - p->pszAmmo2 = NULL; - p->iMaxAmmo2 = -1; - p->iMaxClip = WEAPON_NOCLIP; - p->iSlot = 3; - p->iPosition = 2; - p->iId = m_iId = WEAPON_EGON; - p->iFlags = 0; - p->iWeight = EGON_WEIGHT; - - return 1; -} - -#define EGON_PULSE_INTERVAL 0.1 -#define EGON_DISCHARGE_INTERVAL 0.1 - -BOOL CEgon::HasAmmo( void ) -{ - if ( m_pPlayer->m_rgAmmo[m_iPrimaryAmmoType] <= 0 ) return FALSE; - return TRUE; -} - -void CEgon::UseAmmo( int count ) -{ - if ( m_pPlayer->m_rgAmmo[m_iPrimaryAmmoType] >= count ) - m_pPlayer->m_rgAmmo[m_iPrimaryAmmoType] -= count; - else m_pPlayer->m_rgAmmo[m_iPrimaryAmmoType] = 0; -} - -void CEgon::PrimaryAttack( void ) -{ - UpdateScreen(); - - // don't fire underwater - if ( m_pPlayer->pev->waterlevel == 3 ) - { - if ( m_fireState != FIRE_OFF || m_pBeam ) EndAttack(); - else PlayEmptySound( 2 ); - - m_flNextPrimaryAttack = UTIL_WeaponTimeBase() + 0.08; - return; - } - - UTIL_MakeVectors( m_pPlayer->pev->v_angle + m_pPlayer->pev->punchangle ); - Vector vecAiming = gpGlobals->v_forward; - Vector vecSrc = m_pPlayer->GetGunPosition( ); - - switch( m_fireState ) - { - case FIRE_OFF: - { - if ( !HasAmmo() ) - { - m_flNextPrimaryAttack = m_flNextSecondaryAttack = UTIL_WeaponTimeBase() + 0.08; - PlayEmptySound( 1 ); - return; - } - - m_flAmmoUseTime = UTIL_WeaponTimeBase();// start using ammo ASAP. - - PLAYBACK_EVENT_FULL( 0, m_pPlayer->edict(), m_usEgonFire, 0.0, (float *)&g_vecZero, (float *)&g_vecZero, 0.0, 0.0, pev->body, m_fireMode, 1, m_fireState ); - SendWeaponAnim( EGON_FIRECYCLE ); - m_shakeTime = 0; - - m_pPlayer->m_iWeaponVolume = PRIMARY_FIRE_VOLUME; - m_flTimeWeaponIdle = UTIL_WeaponTimeBase() + 0.1; - m_shootTime = UTIL_WeaponTimeBase() + 2; - - pev->dmgtime = UTIL_WeaponTimeBase() + EGON_PULSE_INTERVAL; - m_fireState = FIRE_CHARGE; - } - break; - - case FIRE_CHARGE: - { - Fire( vecSrc, vecAiming ); - m_pPlayer->m_iWeaponVolume = PRIMARY_FIRE_VOLUME; - - if ( m_shootTime != 0 && UTIL_WeaponTimeBase() > m_shootTime ) - { - PLAYBACK_EVENT_FULL( 0, m_pPlayer->edict(), m_usEgonFire, 0, (float *)&g_vecZero, (float *)&g_vecZero, 0.0, 0.0, pev->body, m_fireMode, 0, m_fireState ); - SendWeaponAnim( EGON_FIRECYCLE ); - m_shootTime = 0; - } - if( !AnimRestore ) - { - PLAYBACK_EVENT_FULL( 0, m_pPlayer->edict(), m_usEgonFire, 0, (float *)&g_vecZero, (float *)&g_vecZero, 0.0, 0.0, pev->body, m_fireMode, 0, m_fireState ); - SendWeaponAnim( EGON_FIRECYCLE ); - m_shootTime = 0; - AnimRestore = TRUE; - } - if ( !HasAmmo() ) - { - EndAttack(); - m_fireState = FIRE_OFF; - m_flNextSecondaryAttack = UTIL_WeaponTimeBase() + 1.0; - } - - } - break; - } -} - -void CEgon::SecondaryAttack( void ) -{ - UpdateScreen(); - if ( m_fireState != FIRE_OFF ) EndAttack(); - if( m_iBody == 0 ) - { - SendWeaponAnim( EGON_ALTFIREON ); - m_fireMode = FIRE_NARROW; - m_iBody = 1; - m_flTimeUpdate = UTIL_WeaponTimeBase() + 1.1; - } - else - { - m_iBody = 0; - SendWeaponAnim( EGON_ALTFIREOFF ); - m_fireMode = FIRE_WIDE; - m_flTimeUpdate = UTIL_WeaponTimeBase() + 0.7;//off animation is faster than on - } - - m_flTimeWeaponIdle = UTIL_WeaponTimeBase() + 3; - m_flNextPrimaryAttack = m_flNextSecondaryAttack = UTIL_WeaponTimeBase() + 1.5; -} - -void CEgon::Fire( const Vector &vecOrigSrc, const Vector &vecDir ) -{ - Vector vecDest = vecOrigSrc + vecDir * 2048; - edict_t *pentIgnore; - TraceResult tr; - - pentIgnore = m_pPlayer->edict(); - Vector tmpSrc = vecOrigSrc + gpGlobals->v_up * -8 + gpGlobals->v_right * 3; - - UTIL_TraceLine( vecOrigSrc, vecDest, dont_ignore_monsters, pentIgnore, &tr ); - - if (tr.fAllSolid) return; - - CBaseEntity *pEntity = CBaseEntity::Instance(tr.pHit); - - if (pEntity == NULL) return; - - if ( IsMultiplayer() ) - { - if ( m_pSprite && pEntity->pev->takedamage ) - m_pSprite->pev->effects &= ~EF_NODRAW; - else if ( m_pSprite ) - m_pSprite->pev->effects |= EF_NODRAW; - } - - float timedist; - - switch ( m_fireMode ) - { - case FIRE_NARROW: - if ( pev->dmgtime < UTIL_WeaponTimeBase() ) - { - // Narrow mode only does damage to the entity it hits - ClearMultiDamage(); - if (pEntity->pev->takedamage) - { - pEntity->TraceAttack( m_pPlayer->pev, gSkillData.plrDmgEgonNarrow, vecDir, &tr, DMG_ENERGYBEAM ); - } - ApplyMultiDamage(m_pPlayer->pev, m_pPlayer->pev); - - if ( IsMultiplayer() ) - { - // multiplayer uses 1 ammo every 1/10th second - if ( UTIL_WeaponTimeBase() >= m_flAmmoUseTime ) - { - UseAmmo( 1 ); - m_flAmmoUseTime = UTIL_WeaponTimeBase() + 0.1; - } - } - else - { - // single player, use 3 ammo/second - if ( UTIL_WeaponTimeBase() >= m_flAmmoUseTime ) - { - UseAmmo( 1 ); - m_flAmmoUseTime = UTIL_WeaponTimeBase() + 0.2; - } - } - - pev->dmgtime = UTIL_WeaponTimeBase() + EGON_PULSE_INTERVAL; - } - timedist = ( pev->dmgtime - UTIL_WeaponTimeBase() ) / EGON_PULSE_INTERVAL; - break; - - case FIRE_WIDE: - if ( pev->dmgtime < UTIL_WeaponTimeBase() ) - { - // wide mode does damage to the ent, and radius damage - ClearMultiDamage(); - if (pEntity->pev->takedamage) - { - pEntity->TraceAttack( m_pPlayer->pev, gSkillData.plrDmgEgonWide, vecDir, &tr, DMG_ENERGYBEAM | DMG_ALWAYSGIB); - } - ApplyMultiDamage(m_pPlayer->pev, m_pPlayer->pev); - - if ( IsMultiplayer() ) - { - // radius damage a little more potent in multiplayer. - ::RadiusDamage( tr.vecEndPos, pev, m_pPlayer->pev, gSkillData.plrDmgEgonWide/4, 128, CLASS_NONE, DMG_ENERGYBEAM | DMG_BLAST | DMG_ALWAYSGIB ); - } - - if ( !m_pPlayer->IsAlive() ) return; - - if ( IsMultiplayer() ) - { - //multiplayer uses 5 ammo/second - if ( UTIL_WeaponTimeBase() >= m_flAmmoUseTime ) - { - UseAmmo( 1 ); - m_flAmmoUseTime = UTIL_WeaponTimeBase() + 0.2; - } - } - else - { - // Wide mode uses 10 charges per second in single player - if ( UTIL_WeaponTimeBase() >= m_flAmmoUseTime ) - { - UseAmmo( 1 ); - m_flAmmoUseTime = UTIL_WeaponTimeBase() + 0.1; - } - } - - pev->dmgtime = UTIL_WeaponTimeBase() + EGON_DISCHARGE_INTERVAL; - if ( m_shakeTime < UTIL_WeaponTimeBase() ) - { - UTIL_ScreenShake( tr.vecEndPos, 5.0, 150.0, 0.75, 250.0 ); - m_shakeTime = UTIL_WeaponTimeBase() + 1.5; - } - } - timedist = ( pev->dmgtime - UTIL_WeaponTimeBase() ) / EGON_DISCHARGE_INTERVAL; - break; - } - - if ( timedist < 0 ) - { - timedist = 0; - } - else if ( timedist > 1 ) - { - timedist = 1; - } - - timedist = 1 - timedist; - - UpdateEffect( tmpSrc, tr.vecEndPos, timedist ); - UpdateScreen(); -} - - -void CEgon::UpdateEffect( const Vector &startPoint, const Vector &endPoint, float timeBlend ) -{ - if ( !m_pBeam ) CreateEffect(); - - m_pBeam->SetStartPos( endPoint ); - m_pBeam->SetBrightness( 255 - ( timeBlend * 180 )); - m_pBeam->SetWidth( 40 - ( timeBlend * 20 )); - - if ( m_fireMode == FIRE_WIDE ) - m_pBeam->SetColor( 30 + (25 * timeBlend), 30 + (30 * timeBlend), 64 + 80 * fabs( sin( UTIL_WeaponTimeBase() * 10 ))); - else - m_pBeam->SetColor( 60 + (25 * timeBlend), 120 + (30 * timeBlend), 64 + 80 * fabs( sin( UTIL_WeaponTimeBase() * 10 ))); - - UTIL_SetOrigin( m_pSprite, endPoint ); - m_pSprite->pev->frame += 8 * gpGlobals->frametime; - - if ( m_pSprite->pev->frame > m_pSprite->Frames() ) m_pSprite->pev->frame = 0; - m_pNoise->SetStartPos( endPoint ); -} - -void CEgon::CreateEffect( void ) -{ - DestroyEffect(); - - m_pBeam = CBeam::BeamCreate( EGON_BEAM_SPRITE, 40 ); - m_pBeam->PointEntInit( pev->origin, m_pPlayer->edict() ); - m_pBeam->SetFlags( FBEAM_SINENOISE ); - m_pBeam->SetEndAttachment( 1 ); - m_pBeam->pev->spawnflags |= SF_BEAM_TEMPORARY;// Flag these to be destroyed on save/restore or level transition - m_pBeam->pev->flags |= FL_SKIPLOCALHOST; - m_pBeam->pev->owner = m_pPlayer->edict(); - - m_pNoise = CBeam::BeamCreate( EGON_BEAM_SPRITE, 55 ); - m_pNoise->PointEntInit( pev->origin, m_pPlayer->edict() ); - m_pNoise->SetScrollRate( 25 ); - m_pNoise->SetBrightness( 100 ); - m_pNoise->SetEndAttachment( 1 ); - m_pNoise->pev->spawnflags |= SF_BEAM_TEMPORARY; - m_pNoise->pev->flags |= FL_SKIPLOCALHOST; - m_pNoise->pev->owner = m_pPlayer->edict(); - - m_pSprite = CSprite::SpriteCreate( EGON_FLARE_SPRITE, pev->origin, FALSE ); - m_pSprite->pev->scale = 1.0; - m_pSprite->SetTransparency( kRenderGlow, 255, 255, 255, 255, kRenderFxNoDissipation ); - m_pSprite->pev->spawnflags |= SF_SPRITE_TEMPORARY; - m_pSprite->pev->flags |= FL_SKIPLOCALHOST; - m_pSprite->pev->owner = m_pPlayer->edict(); - - if ( m_fireMode == FIRE_WIDE ) - { - m_pBeam->SetScrollRate( 50 ); - m_pBeam->SetNoise( 20 ); - m_pNoise->SetColor( 50, 50, 255 ); - m_pNoise->SetNoise( 8 ); - } - else - { - m_pBeam->SetScrollRate( 110 ); - m_pBeam->SetNoise( 5 ); - m_pNoise->SetColor( 80, 120, 255 ); - m_pNoise->SetNoise( 2 ); - } - -} - - -void CEgon::DestroyEffect( void ) -{ - if ( m_pBeam ) - { - UTIL_Remove( m_pBeam ); - m_pBeam = NULL; - } - if ( m_pNoise ) - { - UTIL_Remove( m_pNoise ); - m_pNoise = NULL; - } - if ( m_pSprite ) - { - if ( m_fireMode == FIRE_WIDE ) - m_pSprite->Expand( 10, 500 ); - else - UTIL_Remove( m_pSprite ); - m_pSprite = NULL; - } -} - -void CEgon::EndAttack( void ) -{ - PLAYBACK_EVENT_FULL( 0, m_pPlayer->edict(), m_usEgonStop, 0, (float *)&m_pPlayer->pev->origin, (float *)&m_pPlayer->pev->angles, 0.0, 0.0, pev->body, m_fireMode, 0, m_fireState ); - m_fireState = FIRE_OFF; - - m_flTimeWeaponIdle = UTIL_WeaponTimeBase() + 2.0; - m_flNextSecondaryAttack = UTIL_WeaponTimeBase() + 0.5; - - DestroyEffect(); -} - -void CEgon::UpdateScreen( void ) -{ - if ( m_flTimeUpdate > UTIL_WeaponTimeBase() ) return; - - if(HasAmmo() && m_fireState == FIRE_CHARGE) - { - if(m_fireMode == FIRE_NARROW ) - { - if(pev->skin < 9) pev->skin++; - else pev->skin = RANDOM_LONG(9, 13); - } - else - { - if(pev->skin < 3) pev->skin++; - else pev->skin = RANDOM_LONG(3, 5); - } - } - else - { - if( m_fireMode == FIRE_NARROW) - { - if(pev->skin > 6)pev->skin--; - else pev->skin = 6; - } - else - { - if(pev->skin > 0 && pev->skin != 6)pev->skin--; - else pev->skin = 0; - } - } - MESSAGE_BEGIN( MSG_ONE, gmsgSetSkin, NULL, m_pPlayer->pev ); - WRITE_BYTE( pev->skin ); //weaponmodel skin. - MESSAGE_END(); - - m_flTimeUpdate = UTIL_WeaponTimeBase() + 0.08;//refresh rate -} - -void CEgon::ShutdownScreen( void ) -{ - //shutdown screen - if( m_fireMode == FIRE_NARROW) pev->skin = 6; - else pev->skin = 0; - - MESSAGE_BEGIN( MSG_ONE, gmsgSetSkin, NULL, m_pPlayer->pev ); - WRITE_BYTE( pev->skin ); //weaponmodel skin. - MESSAGE_END(); -} - -void CEgon::WeaponIdle( void ) -{ - UpdateScreen(); - - if ( m_flTimeWeaponIdle > UTIL_WeaponTimeBase() ) return; - if ( m_fireState != FIRE_OFF ) EndAttack(); - - if (m_pPlayer->m_rgAmmo[m_iPrimaryAmmoType] > 0) - { - float flRand = RANDOM_FLOAT(0,1); - - if ( flRand > 0.8 ) - { - SendWeaponAnim( EGON_IDLE1 ); - m_flTimeWeaponIdle = UTIL_WeaponTimeBase() + RANDOM_FLOAT(10,15); - } - else if ((m_iBody == 0)&&(flRand < 0.2)) - { - SendWeaponAnim( EGON_FIDGET1 ); - m_flTimeWeaponIdle = UTIL_WeaponTimeBase() + 3; - } - } -} - - - -class CEgonAmmo : public CBasePlayerAmmo -{ - void Spawn( void ) - { - Precache( ); - SET_MODEL(ENT(pev), "models/w_chainammo.mdl"); - CBasePlayerAmmo::Spawn( ); - } - void Precache( void ) - { - PRECACHE_MODEL ("models/w_chainammo.mdl"); - PRECACHE_SOUND("items/9mmclip1.wav"); - } - BOOL AddAmmo( CBaseEntity *pOther ) - { - if (pOther->GiveAmmo( AMMO_URANIUMBOX_GIVE, "uranium", URANIUM_MAX_CARRY ) != -1) - { - EMIT_SOUND(ENT(pev), CHAN_ITEM, "items/9mmclip1.wav", 1, ATTN_NORM); - return TRUE; - } - return FALSE; - } -}; -LINK_ENTITY_TO_CLASS( ammo_egonclip, CEgonAmmo ); diff --git a/spirit/enginecallback.h b/spirit/enginecallback.h deleted file mode 100644 index 19096cbb..00000000 --- a/spirit/enginecallback.h +++ /dev/null @@ -1,166 +0,0 @@ -/*** -* -* Copyright (c) 1996-2002, 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. -* -****/ -#ifndef ENGINECALLBACK_H -#define ENGINECALLBACK_H - -// Must be provided by user of this code -extern enginefuncs_t g_engfuncs; - -// The actual engine callbacks -#define MALLOC( x ) (*g_engfuncs.pfnMemAlloc)( x, __FILE__, __LINE__ ) -#define CALLOC( x, y ) (*g_engfuncs.pfnMemAlloc)((x) * (y), __FILE__, __LINE__ ) -#define FREE( x ) (*g_engfuncs.pfnMemFree)( x, __FILE__, __LINE__ ) - -//g-cont. see override functions in util.cpp -//#define PRECACHE_MODEL (*g_engfuncs.pfnPrecacheModel) -//#define PRECACHE_SOUND (*g_engfuncs.pfnPrecacheSound) -//#define SET_MODEL (*g_engfuncs.pfnSetModel) -//#define PRECACHE_EVENT (*g_engfuncs.pfnPrecacheEvent) - -#define GETPLAYERUSERID (*g_engfuncs.pfnGetPlayerUserId) -#define PRECACHE_GENERIC (*g_engfuncs.pfnPrecacheGeneric) -#define MODEL_INDEX (*g_engfuncs.pfnModelIndex) -#define MODEL_FRAMES (*g_engfuncs.pfnModelFrames) -#define SET_SIZE (*g_engfuncs.pfnSetSize) -#define CHANGE_LEVEL (*g_engfuncs.pfnChangeLevel) -#define GET_SPAWN_PARMS (*g_engfuncs.pfnGetSpawnParms) -#define SAVE_SPAWN_PARMS (*g_engfuncs.pfnSaveSpawnParms) -#define VEC_TO_YAW (*g_engfuncs.pfnVecToYaw) -#define VEC_TO_ANGLES (*g_engfuncs.pfnVecToAngles) -#define MOVE_TO_ORIGIN (*g_engfuncs.pfnMoveToOrigin) -#define oldCHANGE_YAW (*g_engfuncs.pfnChangeYaw) -#define CHANGE_PITCH (*g_engfuncs.pfnChangePitch) -#define MAKE_VECTORS (*g_engfuncs.pfnMakeVectors) -#define CREATE_ENTITY (*g_engfuncs.pfnCreateEntity) -#define REMOVE_ENTITY (*g_engfuncs.pfnRemoveEntity) -#define CREATE_NAMED_ENTITY (*g_engfuncs.pfnCreateNamedEntity) -#define MAKE_STATIC (*g_engfuncs.pfnMakeStatic) -#define LINK_ENTITY (*g_engfuncs.pfnLinkEdict) -#define DROP_TO_FLOOR (*g_engfuncs.pfnDropToFloor) -#define WALK_MOVE (*g_engfuncs.pfnWalkMove) -#define SET_ORIGIN (*g_engfuncs.pfnSetOrigin) -#define EMIT_SOUND_DYN2 (*g_engfuncs.pfnEmitSound) -#define BUILD_SOUND_MSG (*g_engfuncs.pfnBuildSoundMsg) -#define TRACE_LINE (*g_engfuncs.pfnTraceLine) -#define TRACE_TOSS (*g_engfuncs.pfnTraceToss) -#define TRACE_MONSTER_HULL (*g_engfuncs.pfnTraceMonsterHull) -#define TRACE_HULL (*g_engfuncs.pfnTraceHull) -#define GET_AIM_VECTOR (*g_engfuncs.pfnGetAimVector) -#define SERVER_COMMAND (*g_engfuncs.pfnServerCommand) -#define CLIENT_COMMAND (*g_engfuncs.pfnClientCommand) -#define PARTICLE_EFFECT (*g_engfuncs.pfnParticleEffect) -#define LIGHT_STYLE (*g_engfuncs.pfnLightStyle) -#define DECAL_INDEX (*g_engfuncs.pfnDecalIndex) -#define POINT_CONTENTS (*g_engfuncs.pfnPointContents) -#define CRC32_INIT (*g_engfuncs.pfnCRC_Init) -#define CRC32_PROCESS_BUFFER (*g_engfuncs.pfnCRC_ProcessBuffer) -#define CRC32_PROCESS_BYTE (*g_engfuncs.pfnCRC_ProcessByte) -#define CRC32_FINAL (*g_engfuncs.pfnCRC_Final) -#define RANDOM_LONG (*g_engfuncs.pfnRandomLong) -#define RANDOM_FLOAT (*g_engfuncs.pfnRandomFloat) -#define GETPLAYERAUTHID (*g_engfuncs.pfnGetPlayerAuthId) -#define CLASSIFY_EDICT (*g_engfuncs.pfnClassifyEdict) -#define COM_Parse (*g_engfuncs.pfnParseToken) - -inline void MESSAGE_BEGIN( int msg_dest, int msg_type, const float *pOrigin = NULL, edict_t *ed = NULL ) -{ - (*g_engfuncs.pfnMessageBegin)( msg_dest, msg_type, pOrigin, ed ); -} - -#define MESSAGE_END (*g_engfuncs.pfnMessageEnd) -#define WRITE_BYTE (*g_engfuncs.pfnWriteByte) -#define WRITE_CHAR (*g_engfuncs.pfnWriteChar) -#define WRITE_SHORT (*g_engfuncs.pfnWriteShort) -#define WRITE_LONG (*g_engfuncs.pfnWriteLong) -#define WRITE_ANGLE (*g_engfuncs.pfnWriteAngle) -#define WRITE_COORD (*g_engfuncs.pfnWriteCoord) - -inline void WRITE_FLOAT( float flValue ) -{ - union { float f; int l; } dat; - dat.f = flValue; - WRITE_LONG( dat.l ); -} - -#define WRITE_STRING (*g_engfuncs.pfnWriteString) -#define WRITE_ENTITY (*g_engfuncs.pfnWriteEntity) -#define WRITE_DIR( dir ) WRITE_BYTE(DirToBits( dir )) -#define CVAR_GET_FLOAT (*g_engfuncs.pfnCVarGetFloat) -#define CVAR_GET_STRING (*g_engfuncs.pfnCVarGetString) -#define CVAR_SET_FLOAT (*g_engfuncs.pfnCVarSetFloat) -#define CVAR_SET_STRING (*g_engfuncs.pfnCVarSetString) -#define ALERT (*g_engfuncs.pfnAlertMessage) -#define ALLOC_PRIVATE (*g_engfuncs.pfnPvAllocEntPrivateData) -inline void *GET_PRIVATE( edict_t *pent ) -{ - if ( pent ) - return pent->pvPrivateData; - return NULL; -} - -// NOTE: Xash3D using custom StringTable System that using safety methods for access to strings -// and never make duplicated strings, so it make no differences between ALLOC_STRING and MAKE_STRING -// leave macros as legacy -#define ALLOC_STRING (*g_engfuncs.pfnAllocString) -#define MAKE_STRING (*g_engfuncs.pfnAllocString) -#define STRING (*g_engfuncs.pfnSzFromIndex) - -#define FREE_PRIVATE (*g_engfuncs.pfnFreeEntPrivateData) -#define FIND_ENTITY_BY_STRING (*g_engfuncs.pfnFindEntityByString) -#define GETENTITYILLUM (*g_engfuncs.pfnGetEntityIllum) -#define FIND_ENTITY_IN_SPHERE (*g_engfuncs.pfnFindEntityInSphere) -#define FIND_CLIENT_IN_PVS (*g_engfuncs.pfnFindClientInPVS) -#define EMIT_AMBIENT_SOUND (*g_engfuncs.pfnEmitAmbientSound) -#define GET_MODEL_PTR (*g_engfuncs.pfnGetModelPtr) -#define REG_USER_MSG (*g_engfuncs.pfnRegUserMsg) -#define GET_BONE_POSITION (*g_engfuncs.pfnGetBonePosition) -#define FUNCTION_FROM_NAME (*g_engfuncs.pfnFunctionFromName) -#define NAME_FOR_FUNCTION (*g_engfuncs.pfnNameForFunction) -#define TRACE_TEXTURE (*g_engfuncs.pfnTraceTexture) -#define CLIENT_PRINTF (*g_engfuncs.pfnClientPrintf) -#define CMD_ARGS (*g_engfuncs.pfnCmd_Args) -#define CMD_ARGC (*g_engfuncs.pfnCmd_Argc) -#define CMD_ARGV (*g_engfuncs.pfnCmd_Argv) -#define GET_ATTACHMENT (*g_engfuncs.pfnGetAttachment) -#define SET_VIEW (*g_engfuncs.pfnSetView) -#define SET_CROSSHAIRANGLE (*g_engfuncs.pfnCrosshairAngle) -#define LOAD_FILE_FOR_ME (*g_engfuncs.pfnLoadFile) -#define FILE_EXISTS (*g_engfuncs.pfnFileExists) -#define FREE_FILE (*g_engfuncs.pfnFreeFile) -#define CVAR_REGISTER (*g_engfuncs.pfnCVarRegister) -#define COMPARE_FILE_TIME (*g_engfuncs.pfnCompareFileTime) -#define GET_GAME_DIR (*g_engfuncs.pfnGetGameDir) -#define ENGINE_CANSKIP (*g_engfuncs.pfnCanSkipPlayer) -#define SET_BONE_POSITION (*g_engfuncs.pfnSetBonePos) -#define DROP_CLIENT (*g_engfuncs.pfnDropClient) -#define PLAYBACK_EVENT_FULL (*g_engfuncs.pfnPlaybackEvent) -#define ENGINE_CHECK_PVS (*g_engfuncs.pfnCheckVisibility) -#define ENGINE_SET_PVS (*g_engfuncs.pfnSetFatPVS) -#define ENGINE_SET_PAS (*g_engfuncs.pfnSetFatPAS) -#define IS_MAP_VALID (*g_engfuncs.pfnIsMapValid) -#define IS_DEDICATED_SERVER (*g_engfuncs.pfnIsDedicatedServer) -#define DELTA_SET (*g_engfuncs.pfnDeltaSetField) -#define DELTA_UNSET (*g_engfuncs.pfnDeltaUnsetField) -#define DELTA_ADDENCODER (*g_engfuncs.pfnDeltaAddEncoder) -#define ENGINE_CURRENT_PLAYER (*g_engfuncs.pfnGetCurrentPlayer) -#define HOST_ERROR (*g_engfuncs.pfnHostError) -#define ENGINE_GETPHYSINFO (*g_engfuncs.pfnGetPhysicsInfoString) -#define DELTA_FINDFIELD (*g_engfuncs.pfnDeltaFindField) -#define DELTA_SETBYINDEX (*g_engfuncs.pfnDeltaSetFieldByIndex) -#define DELTA_UNSETBYINDEX (*g_engfuncs.pfnDeltaUnsetFieldByIndex) -#define ENGINE_SETGROUPMASK (*g_engfuncs.pfnSetGroupMask) -#define PLAYER_CNX_STATS (*g_engfuncs.pfnGetPlayerStats) - -#endif //ENGINECALLBACK_H \ No newline at end of file diff --git a/spirit/func_tank.cpp b/spirit/func_tank.cpp deleted file mode 100644 index cf0899be..00000000 --- a/spirit/func_tank.cpp +++ /dev/null @@ -1,1994 +0,0 @@ -/*** -* -* Copyright (c) 1996-2002, 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. -* -****/ -#include "extdll.h" -#include "util.h" -#include "cbase.h" -#include "effects.h" -#include "weapons.h" -#include "explode.h" -#include "monsters.h" -#include "movewith.h" - -#include "player.h" - - -#define SF_TANK_ACTIVE 0x0001 -//#define SF_TANK_PLAYER 0x0002 // for internal camera function. G-Cont -//#define SF_TANK_HUMANS 0x0004 // for external camera (override internal camera). G-Cont -//#define SF_TANK_ALIENS 0x0008 -#define SF_TANK_LINEOFSIGHT 0x0010 -#define SF_TANK_CANCONTROL 0x0020 -#define SF_TANK_LASERSPOT 0x0040 //LRC -#define SF_TANK_MATCHTARGET 0x0080 //LRC -#define SF_TANK_SOUNDON 0x8000 -#define SF_TANK_SEQFIRE 0x10000 //LRC - a TankSequence is telling me to fire - -enum TANKBULLET -{ - TANK_BULLET_NONE = 0, - TANK_BULLET_9MM = 1, - TANK_BULLET_MP5 = 2, - TANK_BULLET_12MM = 3, -}; - -class CFuncTank; -class CTankSequence; - -// declare Controls up here to stop the compiler complaining. (come back Java, all is forgiven...) -class CFuncTankControls : public CBaseEntity -{ -public: - virtual int ObjectCaps( void ); - void Spawn( void ); - void Use( CBaseEntity *pActivator, CBaseEntity *pCaller, USE_TYPE useType, float value ); -// void Think( void ); - void KeyValue( KeyValueData *pkvd ); - STATE GetState(void) { return m_active?STATE_ON:STATE_OFF; } - - virtual int Save( CSave &save ); - virtual int Restore( CRestore &restore ); - static TYPEDESCRIPTION m_SaveData[]; - BOOL CFuncTankControls :: OnControls( entvars_t *pevTest ); - - BOOL m_active; // am I being used to control tanks right now? - Vector m_vecControllerUsePos; // where was the player standing when he used me? - // for a 'movewith' controls entity, this is relative to the movewith ent. - CBasePlayer* m_pController; - int m_iCrosshair; //LRC - show a crosshair while in use. (currently this is just yes or no, - // but in future it will be the id of the weapon whose crosshair should be used.) -// CFuncTank *m_pTank; - EHANDLE m_hPlayer; -}; - - -#define SF_TSEQ_DUMPPLAYER 1 -#define SF_TSEQ_REPEATABLE 2 - -#define TSEQ_UNTIL_NONE 0 -#define TSEQ_UNTIL_FACING 1 -#define TSEQ_UNTIL_DEATH 2 - -#define TSEQ_TURN_NO 0 -#define TSEQ_TURN_ANGLE 1 -#define TSEQ_TURN_FACE 2 -#define TSEQ_TURN_ENEMY 3 - -#define TSEQ_SHOOT_NO 0 -#define TSEQ_SHOOT_ONCE 1 -#define TSEQ_SHOOT_ALWAYS 2 -#define TSEQ_SHOOT_FACING 3 - -#define TSEQ_FLAG_NOCHANGE 0 -#define TSEQ_FLAG_ON 1 -#define TSEQ_FLAG_OFF 2 -#define TSEQ_FLAG_TOGGLE 3 - -class CTankSequence : public CBaseEntity -{ -public: - void Use( CBaseEntity *pActivator, CBaseEntity *pCaller, USE_TYPE useType, float value ); - void EndThink( void ); - void TimeOutThink( void ); - void KeyValue( KeyValueData *pkvd ); - STATE GetState( void ) { return m_pTank?STATE_ON:STATE_OFF; } - virtual int ObjectCaps( void ); - - void StopSequence( void ); - void FacingNotify( void ); - void DeadEnemyNotify( void ); - - virtual int Save( CSave &save ); - virtual int Restore( CRestore &restore ); - static TYPEDESCRIPTION m_SaveData[]; - - string_t m_iszEntity; - string_t m_iszEnemy; - int m_iUntil; - float m_fDuration; - int m_iTurn; - int m_iShoot; - int m_iActive; - int m_iControllable; - int m_iLaserSpot; - CFuncTank *m_pTank; // the sequence can only control one tank at a time, for the moment -}; - -// Custom damage -// env_laser (duration is 0.5 rate of fire) -// rockets -// explosion? - -class CFuncTank : public CBaseEntity -{ -public: - void Spawn( void ); - void PostSpawn( void ); - void Precache( void ); - void KeyValue( KeyValueData *pkvd ); - void Use( CBaseEntity *pActivator, CBaseEntity *pCaller, USE_TYPE useType, float value ); - void Think( void ); - void TrackTarget( void ); - CBaseEntity* BestVisibleEnemy( void ); - int IRelationship( CBaseEntity* pTarget ); - - int Classify( void ) { return m_iTankClass; } - - void TryFire( const Vector &barrelEnd, const Vector &forward, entvars_t *pevAttacker ); - virtual void Fire( const Vector &barrelEnd, const Vector &forward, entvars_t *pevAttacker ); - virtual Vector UpdateTargetPosition( CBaseEntity *pTarget ) - { - return pTarget->BodyTarget( pev->origin ); - } - - void StartRotSound( void ); - void StopRotSound( void ); - STATE GetState( void ) { return m_iActive?STATE_ON:STATE_OFF; }//Support this stuff for watcher - int m_iActive; - - // Bmodels don't go across transitions - virtual int ObjectCaps( void ) { return CBaseEntity :: ObjectCaps() & ~FCAP_ACROSS_TRANSITION; } - - inline BOOL IsActive( void ) { return (pev->spawnflags & SF_TANK_ACTIVE)?TRUE:FALSE; } - inline void TankActivate( void ) { pev->spawnflags |= SF_TANK_ACTIVE; SetNextThink(0.1); m_fireLast = 0; } - inline void TankDeactivate( void ) { pev->spawnflags &= ~SF_TANK_ACTIVE; m_fireLast = 0; StopRotSound(); } - inline BOOL CanFire( void ) { return (gpGlobals->time - m_lastSightTime) < m_persist; } - BOOL InRange( float range ); - - // Acquire a target. pPlayer is a player in the PVS - edict_t *FindTarget( edict_t *pPlayer ); - - void TankTrace( const Vector &vecStart, const Vector &vecForward, const Vector &vecSpread, TraceResult &tr ); - - Vector BarrelPosition( void ) - { - Vector forward, right, up; - UTIL_MakeVectorsPrivate( pev->angles, forward, right, up ); - return pev->origin + (forward * m_barrelPos.x) + (right * m_barrelPos.y) + (up * m_barrelPos.z); - } - - void AdjustAnglesForBarrel( Vector &angles, float distance ); - - virtual int Save( CSave &save ); - virtual int Restore( CRestore &restore ); - static TYPEDESCRIPTION m_SaveData[]; - -// BOOL OnControls( entvars_t *pevTest ); - BOOL StartControl( CBasePlayer* pController, CFuncTankControls* pControls ); - void StopControl( CFuncTankControls* pControls ); -// void ControllerPostFrame( void ); - - CFuncTankControls* m_pControls; //LRC - tankcontrols is used as a go-between. - - void StartSequence( CTankSequence *pSequence); - void StopSequence(); - - CTankSequence *m_pSequence; //LRC - if set, then this is the sequence the tank is currently performing - CBaseEntity *m_pSequenceEnemy; //LRC - the entity that our sequence wants us to attack - CLaserSpot* m_pSpot; // Laser spot entity - - //LRC - unprotected these, so that TankSequence can look at them - float m_maxRange; // Max range to aim/track - float m_fireLast; // Last time I fired - float m_fireRate; // How many rounds/second - -protected: -// CBasePlayer* m_pController; - float m_flNextAttack; -//LRC Vector m_vecControllerUsePos; - - float m_yawCenter; // "Center" yaw - float m_yawRate; // Max turn rate to track targets - float m_yawRange; // Range of turning motion (one-sided: 30 is +/- 30 degress from center) - // Zero is full rotation - float m_yawTolerance; // Tolerance angle - - float m_pitchCenter; // "Center" pitch - float m_pitchRate; // Max turn rate on pitch - float m_pitchRange; // Range of pitch motion as above - float m_pitchTolerance; // Tolerance angle - - float m_lastSightTime;// Last time I saw target - float m_persist; // Persistence of firing (how long do I shoot when I can't see) - float m_minRange; // Minimum range to aim/track - - Vector m_barrelPos; // Length of the freakin barrel - float m_spriteScale; // Scale of any sprites we shoot - int m_iszSpriteSmoke; - int m_iszSpriteFlash; - TANKBULLET m_bulletType; // Bullet type - int m_iBulletDamage; // 0 means use Bullet type's default damage - - Vector m_sightOrigin; // Last sight of target - int m_spread; // firing spread - int m_iszMaster; // Master entity - int m_iszFireMaster;//LRC - Fire-Master entity (prevents firing when inactive) - - int m_iTankClass; // Behave As - - void CFuncTank::UpdateSpot( void ); -// CLaserSpot* m_pViewTarg; // Player view indicator - - CPointEntity *m_pFireProxy; //LRC - locus position for custom shots - int m_iszLocusFire; -}; - -TYPEDESCRIPTION CFuncTank::m_SaveData[] = -{ - DEFINE_FIELD( CFuncTank, m_yawCenter, FIELD_FLOAT ), - DEFINE_FIELD( CFuncTank, m_yawRate, FIELD_FLOAT ), - DEFINE_FIELD( CFuncTank, m_yawRange, FIELD_FLOAT ), - DEFINE_FIELD( CFuncTank, m_yawTolerance, FIELD_FLOAT ), - DEFINE_FIELD( CFuncTank, m_pitchCenter, FIELD_FLOAT ), - DEFINE_FIELD( CFuncTank, m_pitchRate, FIELD_FLOAT ), - DEFINE_FIELD( CFuncTank, m_pitchRange, FIELD_FLOAT ), - DEFINE_FIELD( CFuncTank, m_pitchTolerance, FIELD_FLOAT ), - DEFINE_FIELD( CFuncTank, m_fireLast, FIELD_TIME ), - DEFINE_FIELD( CFuncTank, m_fireRate, FIELD_FLOAT ), - DEFINE_FIELD( CFuncTank, m_lastSightTime, FIELD_TIME ), - DEFINE_FIELD( CFuncTank, m_persist, FIELD_FLOAT ), - DEFINE_FIELD( CFuncTank, m_minRange, FIELD_FLOAT ), - DEFINE_FIELD( CFuncTank, m_maxRange, FIELD_FLOAT ), - DEFINE_FIELD( CFuncTank, m_barrelPos, FIELD_VECTOR ), - DEFINE_FIELD( CFuncTank, m_spriteScale, FIELD_FLOAT ), - DEFINE_FIELD( CFuncTank, m_iszSpriteSmoke, FIELD_STRING ), - DEFINE_FIELD( CFuncTank, m_iszSpriteFlash, FIELD_STRING ), - DEFINE_FIELD( CFuncTank, m_bulletType, FIELD_INTEGER ), - DEFINE_FIELD( CFuncTank, m_sightOrigin, FIELD_VECTOR ), - DEFINE_FIELD( CFuncTank, m_spread, FIELD_INTEGER ), - DEFINE_FIELD( CFuncTank, m_pControls, FIELD_CLASSPTR ), //LRC - DEFINE_FIELD( CFuncTank, m_pSequence, FIELD_CLASSPTR ), //LRC - DEFINE_FIELD( CFuncTank, m_pSequenceEnemy, FIELD_CLASSPTR ), //LRC - DEFINE_FIELD( CFuncTank, m_pSpot, FIELD_CLASSPTR ), //LRC -//LRC DEFINE_FIELD( CFuncTank, m_pController, FIELD_CLASSPTR ), -//LRC DEFINE_FIELD( CFuncTank, m_vecControllerUsePos, FIELD_VECTOR ), - DEFINE_FIELD( CFuncTank, m_flNextAttack, FIELD_TIME ), - DEFINE_FIELD( CFuncTank, m_iBulletDamage, FIELD_INTEGER ), - DEFINE_FIELD( CFuncTank, m_iszMaster, FIELD_STRING ), - DEFINE_FIELD( CFuncTank, m_iszFireMaster, FIELD_STRING ), //LRC - DEFINE_FIELD( CFuncTank, m_iszLocusFire, FIELD_STRING ), //LRC - DEFINE_FIELD( CFuncTank, m_pFireProxy, FIELD_CLASSPTR ), //LRC - DEFINE_FIELD( CFuncTank, m_iActive, FIELD_INTEGER ),//G-Cont. -}; - -IMPLEMENT_SAVERESTORE( CFuncTank, CBaseEntity ); - -static Vector gTankSpread[] = -{ - Vector( 0, 0, 0 ), // perfect - Vector( 0.025, 0.025, 0.025 ), // small cone - Vector( 0.05, 0.05, 0.05 ), // medium cone - Vector( 0.1, 0.1, 0.1 ), // large cone - Vector( 0.25, 0.25, 0.25 ), // extra-large cone -}; -#define MAX_FIRING_SPREADS ARRAYSIZE(gTankSpread) - - -void CFuncTank :: Spawn( void ) -{ - Precache(); - - pev->movetype = MOVETYPE_PUSH; // so it doesn't get pushed by anything - pev->solid = SOLID_BSP; - SET_MODEL( ENT(pev), STRING(pev->model) ); - -// if (pev->health) pev->flags |= FL_MONSTER; //LRC - maybe? - - if ( IsActive() ) - { - SetNextThink(1.0); - } - - if (!m_iTankClass) - { - m_iTankClass = 0; - } - - if((m_maxRange == 0) || (FStringNull(m_maxRange))) - { - m_maxRange = 4096; //G-Cont. for normal working func_tank in original HL - } - m_sightOrigin = BarrelPosition(); // Point at the end of the barrel - - if ( m_fireRate <= 0 ) - m_fireRate = 1; - if ( m_spread > MAX_FIRING_SPREADS ) - m_spread = 0; - - pev->oldorigin = pev->origin; - - if (m_iszLocusFire) //LRC - locus trigger - { - m_pFireProxy = GetClassPtr( (CPointEntity*)NULL ); - } -} - -void CFuncTank::PostSpawn( void ) -{ - if (m_pMoveWith) - { - m_yawCenter = pev->angles.y - m_pMoveWith->pev->angles.y; - m_pitchCenter = pev->angles.x - m_pMoveWith->pev->angles.x; - } - else - { - m_yawCenter = pev->angles.y; - m_pitchCenter = pev->angles.x; - } -} - -void CFuncTank :: Precache( void ) -{ -// PRECACHE_MODEL( "sprites/mommablob.spr" ); - if ( m_iszSpriteSmoke ) - PRECACHE_MODEL( (char *)STRING(m_iszSpriteSmoke) ); - if ( m_iszSpriteFlash ) - PRECACHE_MODEL( (char *)STRING(m_iszSpriteFlash) ); - if ( pev->noise ) - PRECACHE_SOUND( (char *)STRING(pev->noise) ); -} - - -void CFuncTank :: KeyValue( KeyValueData *pkvd ) -{ - if (FStrEq(pkvd->szKeyName, "yawrate")) - { - m_yawRate = atof(pkvd->szValue); - pkvd->fHandled = TRUE; - } - else if (FStrEq(pkvd->szKeyName, "yawrange")) - { - m_yawRange = atof(pkvd->szValue); - pkvd->fHandled = TRUE; - } - else if (FStrEq(pkvd->szKeyName, "yawtolerance")) - { - m_yawTolerance = atof(pkvd->szValue); - pkvd->fHandled = TRUE; - } - else if (FStrEq(pkvd->szKeyName, "pitchrange")) - { - m_pitchRange = atof(pkvd->szValue); - pkvd->fHandled = TRUE; - } - else if (FStrEq(pkvd->szKeyName, "pitchrate")) - { - m_pitchRate = atof(pkvd->szValue); - pkvd->fHandled = TRUE; - } - else if (FStrEq(pkvd->szKeyName, "pitchtolerance")) - { - m_pitchTolerance = atof(pkvd->szValue); - pkvd->fHandled = TRUE; - } - else if (FStrEq(pkvd->szKeyName, "firerate")) - { - m_fireRate = atof(pkvd->szValue); - pkvd->fHandled = TRUE; - } - else if (FStrEq(pkvd->szKeyName, "barrel")) - { - m_barrelPos.x = atof(pkvd->szValue); - pkvd->fHandled = TRUE; - } - else if (FStrEq(pkvd->szKeyName, "barrely")) - { - m_barrelPos.y = atof(pkvd->szValue); - pkvd->fHandled = TRUE; - } - else if (FStrEq(pkvd->szKeyName, "barrelz")) - { - m_barrelPos.z = atof(pkvd->szValue); - pkvd->fHandled = TRUE; - } - else if (FStrEq(pkvd->szKeyName, "spritescale")) - { - m_spriteScale = atof(pkvd->szValue); - pkvd->fHandled = TRUE; - } - else if (FStrEq(pkvd->szKeyName, "spritesmoke")) - { - m_iszSpriteSmoke = ALLOC_STRING(pkvd->szValue); - pkvd->fHandled = TRUE; - } - else if (FStrEq(pkvd->szKeyName, "spriteflash")) - { - m_iszSpriteFlash = ALLOC_STRING(pkvd->szValue); - pkvd->fHandled = TRUE; - } - else if (FStrEq(pkvd->szKeyName, "rotatesound")) - { - pev->noise = ALLOC_STRING(pkvd->szValue); - pkvd->fHandled = TRUE; - } - else if (FStrEq(pkvd->szKeyName, "persistence")) - { - m_persist = atof(pkvd->szValue); - pkvd->fHandled = TRUE; - } - else if (FStrEq(pkvd->szKeyName, "bullet")) - { - m_bulletType = (TANKBULLET)atoi(pkvd->szValue); - pkvd->fHandled = TRUE; - } - else if ( FStrEq(pkvd->szKeyName, "bullet_damage" )) - { - m_iBulletDamage = atoi(pkvd->szValue); - pkvd->fHandled = TRUE; - } - else if (FStrEq(pkvd->szKeyName, "firespread")) - { - m_spread = atoi(pkvd->szValue); - pkvd->fHandled = TRUE; - } - else if (FStrEq(pkvd->szKeyName, "minRange")) - { - m_minRange = atof(pkvd->szValue); - pkvd->fHandled = TRUE; - } - else if (FStrEq(pkvd->szKeyName, "maxRange")) - { - m_maxRange = atof(pkvd->szValue); - pkvd->fHandled = TRUE; - } - else if (FStrEq(pkvd->szKeyName, "master")) - { - m_iszMaster = ALLOC_STRING(pkvd->szValue); - pkvd->fHandled = TRUE; - } - else if (FStrEq(pkvd->szKeyName, "firemaster")) - { - m_iszFireMaster = ALLOC_STRING(pkvd->szValue); - pkvd->fHandled = TRUE; - } - else if (FStrEq(pkvd->szKeyName, "m_iClass")) - { - m_iTankClass = atoi(pkvd->szValue); - pkvd->fHandled = TRUE; - } - else if (FStrEq(pkvd->szKeyName, "m_iszLocusFire")) - { - m_iszLocusFire = ALLOC_STRING(pkvd->szValue); - pkvd->fHandled = TRUE; - } - else - CBaseEntity::KeyValue( pkvd ); -} - -//================================================================================== -// TANK CONTROLLING -/*LRC- TankControls checks this instead -BOOL CFuncTank :: OnControls( entvars_t *pevTest ) -{ - if ( !(pev->spawnflags & SF_TANK_CANCONTROL) ) - return FALSE; - - Vector offset = pevTest->origin - pev->origin; - - if ( (m_vecControllerUsePos - pevTest->origin).Length() < 30 ) - return TRUE; - - return FALSE; -} */ - -BOOL CFuncTank :: StartControl( CBasePlayer* pController, CFuncTankControls *pControls ) -{ -// ALERT(at_console, "StartControl\n"); - // we're already being controlled or playing a sequence - if ( m_pControls != NULL || m_pSequence != NULL ) - { -// ALERT(at_debug,"StartControl failed, already in use\n"); - return FALSE; - } - - // Team only or disabled? - if ( m_iszMaster ) - { - if ( !UTIL_IsMasterTriggered( m_iszMaster, pController ) ) - { -// ALERT(at_debug,"StartControl failed, locked\n"); - return FALSE; - } - } - -// ALERT( at_console, "using TANK!\n"); - - m_iActive = 1; - m_pControls = pControls; - - if (m_pSpot) m_pSpot->Revive(); -// if (m_pViewTarg) m_pViewTarg->Revive(); - - SetNextThink(0.1); -// ALERT(at_debug,"StartControl succeeded\n"); - return TRUE; -} - -void CFuncTank :: StopControl( CFuncTankControls* pControls) -{ -//LRC- various commands moved from here to FuncTankControls - if ( !m_pControls || m_pControls != pControls) - { - //ALERT(at_debug,"StopControl failed, not in use\n"); - return; - } - -// ALERT(at_debug,"StopControl succeeded\n"); - -// ALERT( at_debug, "stopped using TANK\n"); - m_iActive = 0; - - if (m_pSpot) m_pSpot->Suspend(-1); -// if (m_pViewTarg) m_pViewTarg->Suspend(-1); - StopRotSound(); //LRC - - DontThink(); - UTIL_SetAvelocity(this, g_vecZero); - m_pControls = NULL; - - if ( IsActive() ) - { - SetNextThink(1.0); - } -} - -void CFuncTank::UpdateSpot( void ) -{ - if ( pev->spawnflags & SF_TANK_LASERSPOT ) - { - - if (!m_pSpot) - { - m_pSpot = CLaserSpot::CreateSpot(); - } - - Vector vecAiming; - UTIL_MakeVectorsPrivate( pev->angles, vecAiming, NULL, NULL ); - Vector vecSrc = BarrelPosition( ); - - TraceResult tr; - UTIL_TraceLine ( vecSrc, vecSrc + vecAiming * 8192, dont_ignore_monsters, ENT(pev), &tr ); - - // ALERT( "%f %f\n", gpGlobals->v_forward.y, vecAiming.y ); - - /* - float a = gpGlobals->v_forward.y * vecAiming.y + gpGlobals->v_forward.x * vecAiming.x; - m_pPlayer->pev->punchangle.y = acos( a ) * (180 / M_PI); - - ALERT( at_console, "%f\n", a ); - */ - - UTIL_SetOrigin( m_pSpot, tr.vecEndPos ); - } -} - -// Called each frame by PostThink, via Use. -// all we do here is handle firing. -// LRC- this is now never called. Think functions are handling it all. -/*void CFuncTank :: ControllerPostFrame( void ) -{ - ASSERT(m_pController != NULL); - - if ( gpGlobals->time < m_flNextAttack ) - return; - - if ( m_pController->pev->button & IN_ATTACK ) - { - Vector vecForward; - UTIL_MakeVectorsPrivate( pev->angles, vecForward, NULL, NULL ); - - m_fireLast = gpGlobals->time - (1/m_fireRate) - 0.01; // to make sure the gun doesn't fire too many bullets - - Fire( BarrelPosition(), vecForward, m_pController->pev ); - - // HACKHACK -- make some noise (that the AI can hear) - if ( m_pController && m_pController->IsPlayer() ) - ((CBasePlayer *)m_pController)->m_iWeaponVolume = LOUD_GUN_VOLUME; - - m_flNextAttack = gpGlobals->time + (1/m_fireRate); - } -}*/ -////////////// END NEW STUFF ////////////// - - -void CFuncTank :: Use( CBaseEntity *pActivator, CBaseEntity *pCaller, USE_TYPE useType, float value ) -{ - if ( pev->spawnflags & SF_TANK_CANCONTROL ) - { // player controlled turret - - if ( pActivator->Classify() != CLASS_PLAYER ) - return; - - // from Player::PostThink. ("try fire the gun") - if ( value == 2 && useType == USE_SET ) - { -// LRC- actually, we handle firing with TrackTarget, to support multitank. -// ControllerPostFrame(); - } - -// LRC- tankcontrols handles all this -// else if ( !m_pController && useType != USE_OFF ) -// { -// // LRC- add one more tank to the ones the player's using -// ((CBasePlayer*)pActivator)->m_pTank = this; -// StartControl( (CBasePlayer*)pActivator ); -// } -// else -// { -// // probably from Player::PostThink- player stopped using tank. -// StopControl(); -// } - } - else - { - if ( !ShouldToggle( useType, IsActive() ) ) - return; - - if ( IsActive() ) - { - TankDeactivate(); - if (m_pSpot) m_pSpot->Suspend(-1); - } - else - { - TankActivate(); - if (m_pSpot) m_pSpot->Revive(); - } - } -} - - -edict_t *CFuncTank :: FindTarget( edict_t *pPlayer ) -{ - return pPlayer; -} - -CBaseEntity *CFuncTank:: BestVisibleEnemy ( void ) -{ - CBaseEntity *pReturn; - int iNearest; - int iDist; - int iBestRelationship; - int iLookDist = m_maxRange?m_maxRange:512; //thanks to Waldo for this. - - iNearest = 8192;// so first visible entity will become the closest. - pReturn = NULL; - iBestRelationship = R_DL; - - CBaseEntity *pList[100]; - - Vector delta = Vector( iLookDist, iLookDist, iLookDist ); - - // Find only monsters/clients in box, NOT limited to PVS - int count = UTIL_EntitiesInBox( pList, 100, pev->origin - delta, pev->origin + delta, FL_CLIENT|FL_MONSTER ); - int i; - - for (i = 0; i < count; i++ ) - { - if ( pList[i]->IsAlive() ) - { - if ( IRelationship( pList[i] ) > iBestRelationship ) - { - // this entity is disliked MORE than the entity that we - // currently think is the best visible enemy. No need to do - // a distance check, just get mad at this one for now. - iBestRelationship = IRelationship ( pList[i] ); - iNearest = ( pList[i]->pev->origin - pev->origin ).Length(); - pReturn = pList[i]; - } - else if ( IRelationship( pList[i] ) == iBestRelationship ) - { - // this entity is disliked just as much as the entity that - // we currently think is the best visible enemy, so we only - // get mad at it if it is closer. - iDist = ( pList[i]->pev->origin - pev->origin ).Length(); - - if ( iDist <= iNearest ) - { - iNearest = iDist; - //these are guaranteed to be the same! iBestRelationship = IRelationship ( pList[i] ); - pReturn = pList[i]; - } - } - } - } - -// if (pReturn) -// ALERT(at_debug, "Tank's best enemy is %s\n", STRING(pReturn->pev->classname)); -// else -// ALERT(at_debug, "Tank has no best enemy\n"); - return pReturn; -} - - -int CFuncTank::IRelationship( CBaseEntity* pTarget ) -{ - int iOtherClass = pTarget->Classify(); - if (iOtherClass == CLASS_NONE) return R_NO; - - if (!m_iTankClass) - { - if (iOtherClass == CLASS_PLAYER) - return R_HT; - else - return R_NO; - } - else if (m_iTankClass == CLASS_PLAYER_ALLY) - { - switch (iOtherClass) - { - case CLASS_HUMAN_MILITARY: - case CLASS_MACHINE: - case CLASS_ALIEN_MILITARY: - case CLASS_ALIEN_MONSTER: - case CLASS_ALIEN_PREDATOR: - case CLASS_ALIEN_PREY: - return R_HT; - default: - return R_NO; - } - } - else if (m_iTankClass == CLASS_HUMAN_MILITARY) - { - switch (iOtherClass) - { - case CLASS_PLAYER: - case CLASS_PLAYER_ALLY: - case CLASS_ALIEN_MILITARY: - case CLASS_ALIEN_MONSTER: - case CLASS_ALIEN_PREDATOR: - case CLASS_ALIEN_PREY: - return R_HT; - case CLASS_HUMAN_PASSIVE: - return R_DL; - default: - return R_NO; - } - } - else if (m_iTankClass == CLASS_ALIEN_MILITARY) - { - switch (iOtherClass) - { - case CLASS_PLAYER: - case CLASS_PLAYER_ALLY: - case CLASS_HUMAN_MILITARY: - return R_HT; - case CLASS_HUMAN_PASSIVE: - return R_DL; - default: - return R_NO; - } - } - else - return R_NO; -} - - -BOOL CFuncTank :: InRange( float range ) -{ - if ( range < m_minRange ) - return FALSE; - if ( m_maxRange > 0 && range > m_maxRange ) - return FALSE; - - return TRUE; -} - -//LRC -void CFuncTank :: StartSequence(CTankSequence *pSequence) -{ - m_pSequence = pSequence; - SetNextThink(1.0); -} - -//LRC -void CFuncTank :: StopSequence( ) -{ - StopRotSound(); - DontThink(); - pev->avelocity = g_vecZero; - m_pSequence = NULL; - m_pSequenceEnemy = NULL; -} - -// NB: tracktarget updates nextthink -void CFuncTank :: Think( void ) -{ -// pev->avelocity = g_vecZero; - TrackTarget(); - - if ( fabs(pev->avelocity.x) > 1 || fabs(pev->avelocity.y) > 1 ) - StartRotSound(); - else - StopRotSound(); -} - -void CFuncTank::TrackTarget( void ) -{ - TraceResult tr; -// edict_t *pPlayer; - BOOL updateTime = FALSE, lineOfSight; - Vector angles, direction, targetPosition, barrelEnd; - Vector v_right, v_up; - CBaseEntity *pTarget; - CBasePlayer* pController = NULL; - -// ALERT(at_console,"TrackTarget\n"); - - // update the barrel position - if (m_pFireProxy) - { - m_pFireProxy->pev->origin = BarrelPosition(); - UTIL_MakeVectorsPrivate( pev->angles, m_pFireProxy->pev->velocity, NULL, NULL ); - } - - // Get a position to aim for - if (m_pSequence) - { - UpdateSpot(); - SetNextThink(0.05, FALSE); - - if (m_pSequence->m_iTurn == TSEQ_TURN_ENEMY) - { - CBaseMonster *pMonst = m_pSequenceEnemy->MyMonsterPointer(); - if (pMonst && !pMonst->IsAlive()) - m_pSequence->DeadEnemyNotify(); - - // Work out what angle we need to face to look at the enemy - targetPosition = m_pSequenceEnemy->pev->origin + m_pSequenceEnemy->pev->view_ofs; - direction = targetPosition - pev->origin; - angles = UTIL_VecToAngles( direction ); - AdjustAnglesForBarrel( angles, direction.Length() ); - } - else if (m_pSequence->m_iTurn == TSEQ_TURN_ANGLE) - { - angles = m_pSequence->pev->angles; - } - else if (m_pSequence->m_iTurn == TSEQ_TURN_FACE) - { - // Work out what angle we need to face to look at the sequence - direction = m_pSequence->pev->origin - pev->origin; - angles = UTIL_VecToAngles( direction ); - AdjustAnglesForBarrel( angles, direction.Length() ); - } - } - else if (m_pControls && m_pControls->m_pController) - { -// ALERT( at_console, "TANK has controller\n"); - UpdateSpot(); - pController = m_pControls->m_pController; - SetNextThink(0.05, FALSE); - - // LRC- changed here to allow "match target" as well as "match angles" mode. - if (pev->spawnflags & SF_TANK_MATCHTARGET) - { - // "Match target" mode: - // first, get the player's angles - angles = pController->pev->v_angle; - // Work out what point the player is looking at - UTIL_MakeVectorsPrivate(angles, direction,NULL,NULL); - - targetPosition = pController->EyePosition() + direction * 1000; - - edict_t *ownerTemp = pev->owner; //LRC store the owner, so we can put it back after the check - pev->owner = pController->edict(); //LRC when doing the matchtarget check, don't hit the player or the tank. - - UTIL_TraceLine( - pController->EyePosition(), - targetPosition, - missile, //the opposite of ignore_monsters: target them if we go anywhere near! - ignore_glass, - edict(), &tr - ); - - pev->owner = ownerTemp; //LRC put the owner back - -// if (!m_pViewTarg) -// { -// m_pViewTarg = CLaserSpot::CreateSpot("sprites/mommablob.spr"); -// } -// UTIL_SetOrigin( m_pViewTarg, tr.vecEndPos ); - - // Work out what angle we need to face to look at that point - direction = tr.vecEndPos - pev->origin; - angles = UTIL_VecToAngles( direction ); - targetPosition = tr.vecEndPos; - -// ALERT( at_console, "TANK: look at pos %.0f %.0f %.0f; target angle %.0f %.0f %.0f\n", -// targetPosition.x,targetPosition.y,targetPosition.z,angles.x,angles.y,angles.z); - - // Calculate the additional rotation to point the end of the barrel at the target - // (instead of the gun's center) - AdjustAnglesForBarrel( angles, direction.Length() ); - } - else - { - // "Match angles" mode - // just get the player's angles - angles = pController->pev->v_angle; - angles[0] = 0 - angles[0]; - - UpdateSpot(); - SetNextThink(0.05);//G-Cont.For more smoothing motion a laser spot - } - } - else - { -// ALERT( at_console, "TANK has no controller\n"); - if ( IsActive() ) - { - SetNextThink(0.1); - } - else - { - DontThink(); - UTIL_SetAvelocity(this, g_vecZero); - return; - } - - UpdateSpot(); - - // if we can't see any players - //pPlayer = FIND_CLIENT_IN_PVS( edict() ); - pTarget = BestVisibleEnemy(); - if ( FNullEnt( pTarget ) ) - { - if ( IsActive() ) - SetNextThink(2); // No enemies visible, wait 2 secs - return; - } - - // Calculate angle needed to aim at target - barrelEnd = BarrelPosition(); - targetPosition = pTarget->pev->origin + pTarget->pev->view_ofs; - float range = (targetPosition - barrelEnd).Length(); - - if ( !InRange( range ) ) - return; - - UTIL_TraceLine( barrelEnd, targetPosition, dont_ignore_monsters, edict(), &tr ); - - if ( tr.flFraction == 1.0 || tr.pHit == ENT(pTarget->pev) ) - { - lineOfSight = TRUE; - - if ( InRange( range ) && pTarget->IsAlive() ) - { - updateTime = TRUE; // I think I saw him, pa! - m_sightOrigin = UpdateTargetPosition( pTarget ); - } - } - else - { - // No line of sight, don't track - lineOfSight = FALSE; - } - - // Track sight origin - -// !!! I'm not sure what i changed (cuh, these Valve cowboys... --LRC) -// m_sightOrigin is the last known location of the player. - - direction = m_sightOrigin - pev->origin; -// direction = m_sightOrigin - barrelEnd; - - angles = UTIL_VecToAngles( direction ); - - // Calculate the additional rotation to point the end of the barrel at the target - // (not the gun's center) - AdjustAnglesForBarrel( angles, direction.Length() ); - } - - angles.x = -angles.x; - - float currentYawCenter, currentPitchCenter; - - // Force the angles to be relative to the center position - if (m_pMoveWith) - { - currentYawCenter = m_yawCenter + m_pMoveWith->pev->angles.y; - currentPitchCenter = m_pitchCenter + m_pMoveWith->pev->angles.x; - } - else - { - currentYawCenter = m_yawCenter; - currentPitchCenter = m_pitchCenter; - } - - angles.y = currentYawCenter + UTIL_AngleDistance( angles.y, currentYawCenter ); - angles.x = currentPitchCenter + UTIL_AngleDistance( angles.x, currentPitchCenter ); - - // Limit against range in y - if (m_yawRange < 360) - { - if ( angles.y > currentYawCenter + m_yawRange ) - { - angles.y = currentYawCenter + m_yawRange; - updateTime = FALSE; // If player is outside fire arc, we didn't really see him - } - else if ( angles.y < (currentYawCenter - m_yawRange) ) - { - angles.y = (currentYawCenter - m_yawRange); - updateTime = FALSE; // If player is outside fire arc, we didn't really see him - } - } - // we can always 'see' the whole vertical arc, so it's just the yaw we needed to check. - - if ( updateTime ) - m_lastSightTime = gpGlobals->time; - - // Move toward target at rate or less - float distY = UTIL_AngleDistance( angles.y, pev->angles.y ); -// ALERT(at_console, "%f -> %f: dist= %f\n", angles.y, pev->angles.y, distY); - Vector setAVel = g_vecZero; - - setAVel.y = distY * 10; - if ( setAVel.y > m_yawRate ) - setAVel.y = m_yawRate; - else if ( setAVel.y < -m_yawRate ) - setAVel.y = -m_yawRate; - - // Limit against range in x - if ( angles.x > currentPitchCenter + m_pitchRange ) - angles.x = currentPitchCenter + m_pitchRange; - else if ( angles.x < currentPitchCenter - m_pitchRange ) - angles.x = currentPitchCenter - m_pitchRange; - - // Move toward target at rate or less - float distX = UTIL_AngleDistance( angles.x, pev->angles.x ); - setAVel.x = distX * 10; - - if ( setAVel.x > m_pitchRate ) - setAVel.x = m_pitchRate; - else if ( setAVel.x < -m_pitchRate ) - setAVel.x = -m_pitchRate; - - UTIL_SetAvelocity(this, setAVel); - - // notify the TankSequence if we're (pretty close to) facing the target - if (m_pSequence && abs(distY) < 0.1 && abs(distX) < 0.1) - m_pSequence->FacingNotify(); - - // firing in tanksequences: - if ( m_pSequence ) - { - if ( gpGlobals->time < m_flNextAttack ) return; - - if ( pev->spawnflags & SF_TANK_SEQFIRE ) // does the sequence want me to fire? - { - Vector forward; - UTIL_MakeVectorsPrivate( pev->angles, forward, NULL, NULL ); - - // to make sure the gun doesn't fire too many bullets - m_fireLast = gpGlobals->time - (1/m_fireRate) - 0.01; - - TryFire( BarrelPosition(), forward, pev ); - - m_flNextAttack = gpGlobals->time + (1/m_fireRate); - } - return; - } - // firing with player-controlled tanks: - else if ( pController ) - { - if ( gpGlobals->time < m_flNextAttack ) - return; - - // FIXME- use m_???Tolerance to fire in the desired direction, - // instead of the one we're facing. - - if ( pController->pev->button & IN_ATTACK ) - { - Vector forward; - UTIL_MakeVectorsPrivate( pev->angles, forward, NULL, NULL ); - - // to make sure the gun doesn't fire too many bullets - m_fireLast = gpGlobals->time - (1/m_fireRate) - 0.01; - - TryFire( BarrelPosition(), forward, pController->pev ); - - // HACKHACK -- make some noise (that the AI can hear) - if ( pController && pController->IsPlayer() ) - ((CBasePlayer *)pController)->m_iWeaponVolume = LOUD_GUN_VOLUME; - - m_flNextAttack = gpGlobals->time + (1/m_fireRate); - } - } - // firing with automatic guns: - else if ( CanFire() && ( (fabs(distX) < m_pitchTolerance && fabs(distY) < m_yawTolerance) || (pev->spawnflags & SF_TANK_LINEOFSIGHT) ) ) - { - BOOL fire = FALSE; - Vector forward; - UTIL_MakeVectorsPrivate( pev->angles, forward, NULL, NULL ); - - if ( pev->spawnflags & SF_TANK_LINEOFSIGHT ) - { - float length = direction.Length(); - UTIL_TraceLine( barrelEnd, barrelEnd + forward * length, dont_ignore_monsters, edict(), &tr ); - if ( tr.pHit == ENT(pTarget->pev) ) - fire = TRUE; - } - else - fire = TRUE; - - if ( fire ) - { - TryFire( BarrelPosition(), forward, pev ); - } - else - m_fireLast = 0; - } - else - m_fireLast = 0; -} - - -// If barrel is offset, add in additional rotation -void CFuncTank::AdjustAnglesForBarrel( Vector &angles, float distance ) -{ - float r2, d2; - - - if ( m_barrelPos.y != 0 || m_barrelPos.z != 0 ) - { - distance -= m_barrelPos.z; - d2 = distance * distance; - if ( m_barrelPos.y ) - { - r2 = m_barrelPos.y * m_barrelPos.y; - angles.y += (180.0 / M_PI) * atan2( m_barrelPos.y, sqrt( d2 - r2 ) ); - } - if ( m_barrelPos.z ) - { - r2 = m_barrelPos.z * m_barrelPos.z; - angles.x += (180.0 / M_PI) * atan2( -m_barrelPos.z, sqrt( d2 - r2 ) ); - } - } -} - -// Check the FireMaster before actually firing -void CFuncTank::TryFire( const Vector &barrelEnd, const Vector &forward, entvars_t *pevAttacker ) -{ -// ALERT(at_console, "TryFire\n"); - if (UTIL_IsMasterTriggered(m_iszFireMaster, NULL)) - { -// ALERT(at_console, "Fire is %p, rocketfire %p, tankfire %p\n", this->Fire, CFuncTankRocket::Fire, CFuncTank::Fire); - Fire( barrelEnd, forward, pevAttacker ); - } -// else -// m_fireLast = 0; -} - -// Fire targets and spawn sprites -void CFuncTank::Fire( const Vector &barrelEnd, const Vector &forward, entvars_t *pevAttacker ) -{ -// ALERT(at_console, "FuncTank::Fire\n"); - - if ( m_fireLast != 0 ) - { - if ( m_iszSpriteSmoke ) - { - CSprite *pSprite = CSprite::SpriteCreate( STRING(m_iszSpriteSmoke), barrelEnd, TRUE ); - pSprite->AnimateAndDie( RANDOM_FLOAT( 15.0, 20.0 ) ); - pSprite->SetTransparency( kRenderTransAlpha, pev->rendercolor.x, pev->rendercolor.y, pev->rendercolor.z, 255, kRenderFxNone ); - pSprite->pev->velocity.z = RANDOM_FLOAT(40, 80); - pSprite->SetScale( m_spriteScale ); - } - if ( m_iszSpriteFlash ) - { - CSprite *pSprite = CSprite::SpriteCreate( STRING(m_iszSpriteFlash), barrelEnd, TRUE ); - pSprite->AnimateAndDie( 60 ); - pSprite->SetTransparency( kRenderTransAdd, 255, 255, 255, 255, kRenderFxNoDissipation ); - pSprite->SetScale( m_spriteScale ); - - // Hack Hack, make it stick around for at least 100 ms. - pSprite->AbsoluteNextThink( pSprite->m_fNextThink + 0.1 ); - } - - //LRC - if (m_iszLocusFire) - { - FireTargets( STRING(m_iszLocusFire), m_pFireProxy, this, USE_TOGGLE, 0 ); - } - - SUB_UseTargets( this, USE_TOGGLE, 0 ); - } - m_fireLast = gpGlobals->time; -} - - -void CFuncTank::TankTrace( const Vector &vecStart, const Vector &vecForward, const Vector &vecSpread, TraceResult &tr ) -{ - // get circular gaussian spread - float x, y, z; - do { - x = RANDOM_FLOAT(-0.5,0.5) + RANDOM_FLOAT(-0.5,0.5); - y = RANDOM_FLOAT(-0.5,0.5) + RANDOM_FLOAT(-0.5,0.5); - z = x*x+y*y; - } while (z > 1); - Vector vecDir = vecForward + - x * vecSpread.x * gpGlobals->v_right + - y * vecSpread.y * gpGlobals->v_up; - Vector vecEnd; - - vecEnd = vecStart + vecDir * 4096; - UTIL_TraceLine( vecStart, vecEnd, dont_ignore_monsters, edict(), &tr ); -} - - -void CFuncTank::StartRotSound( void ) -{ - if ( !pev->noise || (pev->spawnflags & SF_TANK_SOUNDON) ) - return; - pev->spawnflags |= SF_TANK_SOUNDON; - EMIT_SOUND( edict(), CHAN_STATIC, (char*)STRING(pev->noise), 0.85, ATTN_NORM); -} - - -void CFuncTank::StopRotSound( void ) -{ - if ( pev->spawnflags & SF_TANK_SOUNDON ) - STOP_SOUND( edict(), CHAN_STATIC, (char*)STRING(pev->noise) ); - pev->spawnflags &= ~SF_TANK_SOUNDON; -} - -class CFuncTankGun : public CFuncTank -{ -public: - void Fire( const Vector &barrelEnd, const Vector &forward, entvars_t *pevAttacker ); -}; -LINK_ENTITY_TO_CLASS( func_tank, CFuncTankGun ); - -void CFuncTankGun::Fire( const Vector &barrelEnd, const Vector &forward, entvars_t *pevAttacker ) -{ -// ALERT(at_console, "FuncTankGun::Fire\n"); - int i; - - if ( m_fireLast != 0 ) - { - // FireBullets needs gpGlobals->v_up, etc. - UTIL_MakeAimVectors(pev->angles); - - int bulletCount = (gpGlobals->time - m_fireLast) * m_fireRate; - if ( bulletCount > 0 ) - { - for ( i = 0; i < bulletCount; i++ ) - { - switch( m_bulletType ) - { - case TANK_BULLET_9MM: - FireBullets( 1, barrelEnd, forward, gTankSpread[m_spread], 4096, BULLET_MONSTER_9MM, 1, m_iBulletDamage, pevAttacker ); - break; - - case TANK_BULLET_MP5: - FireBullets( 1, barrelEnd, forward, gTankSpread[m_spread], 4096, BULLET_MONSTER_MP5, 1, m_iBulletDamage, pevAttacker ); - break; - - case TANK_BULLET_12MM: - FireBullets( 1, barrelEnd, forward, gTankSpread[m_spread], 4096, BULLET_MONSTER_12MM, 1, m_iBulletDamage, pevAttacker ); - break; - - default: - case TANK_BULLET_NONE: - break; - } - } - CFuncTank::Fire( barrelEnd, forward, pevAttacker ); - } - } - else - CFuncTank::Fire( barrelEnd, forward, pevAttacker ); -} - - - -class CFuncTankLaser : public CFuncTank -{ -public: - void Activate( void ); - void KeyValue( KeyValueData *pkvd ); - void Fire( const Vector &barrelEnd, const Vector &forward, entvars_t *pevAttacker ); - void Think( void ); - CLaser *GetLaser( void ); - - virtual int Save( CSave &save ); - virtual int Restore( CRestore &restore ); - static TYPEDESCRIPTION m_SaveData[]; - -private: - CLaser *m_pLaser; - float m_laserTime; -}; -LINK_ENTITY_TO_CLASS( func_tanklaser, CFuncTankLaser ); - -TYPEDESCRIPTION CFuncTankLaser::m_SaveData[] = -{ - DEFINE_FIELD( CFuncTankLaser, m_pLaser, FIELD_CLASSPTR ), - DEFINE_FIELD( CFuncTankLaser, m_laserTime, FIELD_TIME ), -}; - -IMPLEMENT_SAVERESTORE( CFuncTankLaser, CFuncTank ); - -void CFuncTankLaser::Activate( void ) -{ - if ( !GetLaser() ) - { - UTIL_Remove(this); - ALERT( at_error, "Laser tank with no env_laser!\n" ); - } - else - { - m_pLaser->TurnOff(); - } - CFuncTank::Activate(); -} - - -void CFuncTankLaser::KeyValue( KeyValueData *pkvd ) -{ - if (FStrEq(pkvd->szKeyName, "laserentity")) - { - pev->message = ALLOC_STRING(pkvd->szValue); - pkvd->fHandled = TRUE; - } - else - CFuncTank::KeyValue( pkvd ); -} - - -CLaser *CFuncTankLaser::GetLaser( void ) -{ - if ( m_pLaser ) - return m_pLaser; - - CBaseEntity *pEntity; - - pEntity = UTIL_FindEntityByTargetname( NULL, STRING(pev->message) ); - while ( pEntity ) - { - // Found the laser - if ( FClassnameIs( pEntity->pev, "env_laser" ) ) - { - m_pLaser = (CLaser *)pEntity; - break; - } - else - pEntity = UTIL_FindEntityByTargetname( pEntity, STRING(pev->message) ); - } - - return m_pLaser; -} - - -void CFuncTankLaser::Think( void ) -{ - if ( m_pLaser && (gpGlobals->time > m_laserTime) ) - m_pLaser->TurnOff(); - - CFuncTank::Think(); -} - - -void CFuncTankLaser::Fire( const Vector &barrelEnd, const Vector &forward, entvars_t *pevAttacker ) -{ -// ALERT(at_console, "FuncTankLaser::Fire\n"); - int i; - TraceResult tr; - - if ( m_fireLast != 0 && GetLaser() ) - { - // TankTrace needs gpGlobals->v_up, etc. - UTIL_MakeAimVectors(pev->angles); - - int bulletCount = (gpGlobals->time - m_fireLast) * m_fireRate; - if ( bulletCount ) - { - for ( i = 0; i < bulletCount; i++ ) - { - m_pLaser->pev->origin = barrelEnd; - TankTrace( barrelEnd, forward, gTankSpread[m_spread], tr ); - - m_laserTime = gpGlobals->time; - m_pLaser->TurnOn(); - m_pLaser->pev->dmgtime = gpGlobals->time - 1.0; - m_pLaser->FireAtPoint( barrelEnd, tr ); - - //LRC - tripbeams - CBaseEntity* pTrip; - if (!FStringNull(m_pLaser->pev->target) && (pTrip = m_pLaser->GetTripEntity( &tr )) != NULL) - FireTargets(STRING(m_pLaser->pev->target), pTrip, m_pLaser, USE_TOGGLE, 0); - - m_pLaser->DontThink(); - } - CFuncTank::Fire( barrelEnd, forward, pev ); - } - } - else - { - CFuncTank::Fire( barrelEnd, forward, pev ); - } -} - -class CFuncTankRocket : public CFuncTank -{ -public: - void Precache( void ); - virtual void Fire( const Vector &barrelEnd, const Vector &forward, entvars_t *pevAttacker ); -}; -LINK_ENTITY_TO_CLASS( func_tankrocket, CFuncTankRocket ); - -void CFuncTankRocket::Precache( void ) -{ - UTIL_PrecacheOther( "rpg_rocket" ); - CFuncTank::Precache(); -} - - - -void CFuncTankRocket::Fire( const Vector &barrelEnd, const Vector &forward, entvars_t *pevAttacker ) -{ -// ALERT(at_console, "FuncTankRocket::Fire\n"); - - int i; - - if ( m_fireLast != 0 ) - { - int bulletCount = (gpGlobals->time - m_fireLast) * m_fireRate; - if ( bulletCount > 0 ) - { - for ( i = 0; i < bulletCount; i++ ) - { - CBaseEntity *pRocket = CBaseEntity::Create( "rpg_rocket", barrelEnd, pev->angles, edict() ); - } - CFuncTank::Fire( barrelEnd, forward, pev ); - } - } - else - CFuncTank::Fire( barrelEnd, forward, pev ); -} - - -class CFuncTankMortar : public CFuncTank -{ -public: - void KeyValue( KeyValueData *pkvd ); - void Fire( const Vector &barrelEnd, const Vector &forward, entvars_t *pevAttacker ); -}; -LINK_ENTITY_TO_CLASS( func_tankmortar, CFuncTankMortar ); - - -void CFuncTankMortar::KeyValue( KeyValueData *pkvd ) -{ - if (FStrEq(pkvd->szKeyName, "iMagnitude")) - { - pev->impulse = atoi( pkvd->szValue ); - pkvd->fHandled = TRUE; - } - else - CFuncTank::KeyValue( pkvd ); -} - - -void CFuncTankMortar::Fire( const Vector &barrelEnd, const Vector &forward, entvars_t *pevAttacker ) -{ -// ALERT(at_console, "FuncTankMortar::Fire\n"); - if ( m_fireLast != 0 ) - { - int bulletCount = (gpGlobals->time - m_fireLast) * m_fireRate; - // Only create 1 explosion - if ( bulletCount > 0 ) - { - TraceResult tr; - - // TankTrace needs gpGlobals->v_up, etc. - UTIL_MakeAimVectors(pev->angles); - - TankTrace( barrelEnd, forward, gTankSpread[m_spread], tr ); - - ExplosionCreate( tr.vecEndPos, pev->angles, edict(), pev->impulse, TRUE ); - - CFuncTank::Fire( barrelEnd, forward, pev ); - } - } - else - CFuncTank::Fire( barrelEnd, forward, pev ); -} - - -//============================================================================ -// FUNC TANK CONTROLS -//============================================================================ -#define SF_TANKCONTROLS_NO_USE 1 -#define SF_TANKCONTROLS_VISIBLE 2 - -LINK_ENTITY_TO_CLASS( func_tankcontrols, CFuncTankControls ); - -TYPEDESCRIPTION CFuncTankControls::m_SaveData[] = -{ -// DEFINE_FIELD( CFuncTankControls, m_pTank, FIELD_CLASSPTR ), - DEFINE_FIELD( CFuncTankControls, m_active, FIELD_BOOLEAN ), - DEFINE_FIELD( CFuncTankControls, m_pController, FIELD_CLASSPTR ), - DEFINE_FIELD( CFuncTankControls, m_vecControllerUsePos, FIELD_VECTOR ), - DEFINE_FIELD( CFuncTankControls, m_iCrosshair, FIELD_INTEGER ), //LRC -}; - -IMPLEMENT_SAVERESTORE( CFuncTankControls, CBaseEntity ); - - -void CFuncTankControls :: KeyValue( KeyValueData *pkvd ) -{ - if (FStrEq(pkvd->szKeyName, "crosshair")) - { - m_iCrosshair = atoi(pkvd->szValue); - pkvd->fHandled = TRUE; - } - else - CBaseEntity::KeyValue( pkvd ); -} - -int CFuncTankControls :: ObjectCaps( void ) -{ - if (pev->spawnflags & SF_TANKCONTROLS_NO_USE) - return (CBaseEntity::ObjectCaps() & ~FCAP_ACROSS_TRANSITION); - else - return (CBaseEntity::ObjectCaps() & ~FCAP_ACROSS_TRANSITION) | FCAP_IMPULSE_USE; -} - -//LRC- copied here from FuncTank. -BOOL CFuncTankControls :: OnControls( entvars_t *pevTest ) -{ -// if ( !(pev->spawnflags & SF_TANK_CANCONTROL) ) -// return FALSE; - - Vector offset = pevTest->origin - pev->origin; - - if (pev->frags == -1) - { -// ALERT(at_console, "TANK OnControls: TRUE(full tolerance)\n"); - return TRUE; - } - - if (m_pMoveWith) - { - if ( ((m_vecControllerUsePos + m_pMoveWith->pev->origin) - pevTest->origin).Length() <= pev->frags ) - { -// ALERT(at_console, "TANK OnControls: TRUE(movewith)\n"); - return TRUE; - } - } - else if ( (m_vecControllerUsePos - pevTest->origin).Length() <= pev->frags ) - { -// ALERT(at_console, "TANK OnControls: TRUE\n"); - return TRUE; - } - -// ALERT(at_console, "TANK OnControls: FALSE\n"); - - return FALSE; -} - -void CFuncTankControls :: Use( CBaseEntity *pActivator, CBaseEntity *pCaller, USE_TYPE useType, float value ) -{ -// LRC- rewritten to allow TankControls to be the thing that handles the relationship -// between the player and one or more faithful tanks. - CBaseEntity *tryTank = NULL; - -// ALERT(at_console,"controls %p triggered by \"%s\" %p\n", this, STRING(pCaller->pev->classname), pCaller); - - if ( !m_pController && useType != USE_OFF ) - { - // if not activated by a player, don't work. - if (!pActivator || !(pActivator->IsPlayer())) - return; - // if I've already got a controller, or the player's already using - // another controls, then forget it. - if (m_active != FALSE || ((CBasePlayer*)pActivator)->m_pTank != NULL) - return; - - //LRC- Now uses FindEntityByTargetname, so that aliases work. - while (tryTank = UTIL_FindEntityByTargetname(tryTank, STRING(pev->target))) - { - if (!strncmp( STRING(tryTank->pev->classname), "func_tank", 9 )) - { - if (((CFuncTank*)tryTank)->StartControl((CBasePlayer*)pActivator, this)) - { - //ALERT(at_console,"started controlling tank %s\n",STRING(tryTank->pev->targetname)); - // here's a tank we can control. Phew. - m_active = TRUE; - } - } - } - if (m_active) - { - // we found at least one tank to use, so holster player's weapon - m_pController = (CBasePlayer*)pActivator; - m_pController->m_pTank = this; - if ( m_pController->m_pActiveItem ) - { - m_pController->m_pActiveItem->Holster(); - m_pController->pev->weaponmodel = 0; - m_pController->pev->viewmodel = 0; - } - - //LRC - allow tank crosshairs - if (m_iCrosshair) - m_pController->m_iHideHUD |= ( HIDEHUD_CROSSHAIR | HIDEHUD_WEAPONS ); - else - m_pController->m_iHideHUD |= HIDEHUD_WEAPONS; - - // remember where the player's standing, so we can tell when he walks away - if (m_pMoveWith) - m_vecControllerUsePos = m_pController->pev->origin - m_pMoveWith->pev->origin; - else - m_vecControllerUsePos = m_pController->pev->origin; - ALERT( at_console, "TANK controls activated\n"); - } - } - else if (m_pController && useType != USE_ON) - { - // player stepped away or died, most likely. - ALERT(at_console, "TANK controls deactivated\n"); - - //LRC- Now uses FindEntityByTargetname, so that aliases work. - while (tryTank = UTIL_FindEntityByTargetname(tryTank, STRING(pev->target))) - { - if (FClassnameIs(tryTank->pev, "func_tank") || FClassnameIs(tryTank->pev, "func_tanklaser") || FClassnameIs(tryTank->pev, "func_tankmortar") || FClassnameIs(tryTank->pev, "func_tankrocket")) - { - // this is a tank we're controlling. - ((CFuncTank*)tryTank)->StopControl(this); - } - } -// if (!m_pController) -// return; - - // bring back player's weapons - if ( m_pController->m_pActiveItem ) - m_pController->m_pActiveItem->Deploy(); - - m_pController->m_iHideHUD &= ~ (HIDEHUD_CROSSHAIR | HIDEHUD_WEAPONS); - m_pController->m_pTank = NULL; - - m_pController = NULL; - m_active = false; - ((CBasePlayer *)pActivator)->pev->fov = 90.0f;//reset FOV - ((CBasePlayer *)pActivator)->viewEntity = 0; - ((CBasePlayer *)pActivator)->viewFlags = 0; - ((CBasePlayer *)pActivator)->viewNeedsUpdate = 1; - } - - -// if ( m_pTank ) -// m_pTank->Use( pActivator, pCaller, useType, value ); - -// ASSERT( m_pTank != NULL ); // if this fails, most likely means save/restore hasn't worked properly -} - - -/* LRC- no need to set up m_pTank any more... -void CFuncTankControls :: Think( void ) -{ - CBaseEntity *pTarget = NULL; - - do - { - pTarget = UTIL_FindEntityByClassname( pTarget, STRING(pev->target) ); - } while ( pTarget && strncmp( STRING(pTarget->v.classname), "func_tank", 9 ) ); - - - if ( !pTarget ) - { - ALERT( at_console, "No tank %s\n", STRING(pev->target) ); - return; - } - else - { - m_pTank = (CFuncTank*)pTarget; - do - { - pTarget = UTIL_FindEntityByClassname( pTarget, STRING(pev->target) ); - } while ( pTarget && strncmp( STRING(pTarget->v.classname), "func_tank", 9 ) ); - - if ( pTarget ) - { - m_pTank2 = (CFuncTank*)pTarget; - ALERT( at_console, "Got second tank %s\n", STRING(pev->target) ); - } - } -}*/ - -void CFuncTankControls::Spawn( void ) -{ - pev->solid = SOLID_TRIGGER; - pev->movetype = MOVETYPE_NONE; - if (!(pev->spawnflags & SF_TANKCONTROLS_VISIBLE)) - pev->effects |= EF_NODRAW; - SET_MODEL( ENT(pev), STRING(pev->model) ); - - if (pev->frags == 0) //LRC- in case the level designer didn't set it. - pev->frags = 30; - - UTIL_SetSize( pev, pev->mins, pev->maxs ); - UTIL_SetOrigin( this, pev->origin ); - -//LRC SetNextThink( 0.3 ); // After all the func_tanks have spawned - - CBaseEntity::Spawn(); -} - -//============================================================================ -//LRC - Scripted Tank Sequence -//============================================================================ - -LINK_ENTITY_TO_CLASS( scripted_tanksequence, CTankSequence ); - -TYPEDESCRIPTION CTankSequence::m_SaveData[] = -{ - DEFINE_FIELD( CTankSequence, m_iszEntity, FIELD_STRING ), - DEFINE_FIELD( CTankSequence, m_iszEnemy, FIELD_STRING ), - DEFINE_FIELD( CTankSequence, m_iUntil, FIELD_INTEGER ), - DEFINE_FIELD( CTankSequence, m_fDuration, FIELD_FLOAT ), - DEFINE_FIELD( CTankSequence, m_iTurn, FIELD_INTEGER ), - DEFINE_FIELD( CTankSequence, m_iShoot, FIELD_INTEGER ), - DEFINE_FIELD( CTankSequence, m_iActive, FIELD_INTEGER ), - DEFINE_FIELD( CTankSequence, m_iControllable, FIELD_INTEGER ), - DEFINE_FIELD( CTankSequence, m_iLaserSpot, FIELD_INTEGER ), - DEFINE_FIELD( CTankSequence, m_pTank, FIELD_CLASSPTR), -}; - -IMPLEMENT_SAVERESTORE( CTankSequence, CBaseEntity ); - -int CTankSequence :: ObjectCaps( void ) -{ - return (CBaseEntity::ObjectCaps() & ~FCAP_ACROSS_TRANSITION); -} - -void CTankSequence :: KeyValue( KeyValueData *pkvd ) -{ - if (FStrEq(pkvd->szKeyName, "m_iUntil")) - { - m_iUntil = atoi(pkvd->szValue); - pkvd->fHandled = TRUE; - } - else if (FStrEq(pkvd->szKeyName, "m_iTurn")) - { - m_iTurn = atoi(pkvd->szValue); - pkvd->fHandled = TRUE; - } - else if (FStrEq(pkvd->szKeyName, "m_iShoot")) - { - m_iShoot = atoi(pkvd->szValue); - pkvd->fHandled = TRUE; - } - else if (FStrEq(pkvd->szKeyName, "m_iActive")) - { - m_iActive = atoi(pkvd->szValue); - pkvd->fHandled = TRUE; - } - else if (FStrEq(pkvd->szKeyName, "m_iControllable")) - { - m_iControllable = atoi(pkvd->szValue); - pkvd->fHandled = TRUE; - } - else if (FStrEq(pkvd->szKeyName, "m_iLaserSpot")) - { - m_iLaserSpot = atoi(pkvd->szValue); - pkvd->fHandled = TRUE; - } - else if (FStrEq(pkvd->szKeyName, "m_iszEntity")) - { - m_iszEntity = ALLOC_STRING(pkvd->szValue); - pkvd->fHandled = TRUE; - } - else if (FStrEq(pkvd->szKeyName, "m_iszEnemy")) - { - m_iszEnemy = ALLOC_STRING(pkvd->szValue); - pkvd->fHandled = TRUE; - } - else - CBaseEntity::KeyValue( pkvd ); -} - -void CTankSequence :: Use( CBaseEntity *pActivator, CBaseEntity *pCaller, USE_TYPE useType, float value ) -{ - if (!ShouldToggle(useType)) return; - - if (GetState() == STATE_OFF) - { - // take control of the tank, start the sequence - - CBaseEntity* pEnt = UTIL_FindEntityByTargetname(NULL, STRING(m_iszEntity)); - if (!pEnt || !FStrEq(STRING(pEnt->pev->classname), "func_tank")) - { - ALERT(at_error, "Invalid or missing tank \"%s\" for scripted_tanksequence!\n", STRING(m_iszEntity)); - return; - } - CFuncTank* pTank = (CFuncTank*)pEnt; - - // check whether it's being controlled by another sequence - if (pTank->m_pSequence) - return; - - // check whether it's being controlled by the player - if (pTank->m_pControls) - { - if (pev->spawnflags & SF_TSEQ_DUMPPLAYER) - { - pTank->StopControl( pTank->m_pControls ); - } - else if (!FBitSet(pev->spawnflags, SF_TSEQ_DUMPPLAYER)) - { - //ALERT(at_debug, "scripted_tanksequence can't take tank away from player\n"); - return; - } - } - - //passed all the tests, so we can now take control of it. - if (m_iTurn == TSEQ_TURN_ENEMY) - { - CBaseEntity *pEnemy; - if (m_iszEnemy) - pEnemy = UTIL_FindEntityGeneric(STRING(m_iszEnemy), pTank->pev->origin, pTank->m_maxRange ); - else - pEnemy = pTank->BestVisibleEnemy(); - - if (pEnemy) - { - pTank->m_pSequenceEnemy = pEnemy; - pTank->StartSequence(this); - } - } - else - { - pTank->StartSequence(this); - } - - if (m_iShoot == TSEQ_SHOOT_ALWAYS) - pTank->pev->spawnflags |= SF_TANK_SEQFIRE; - else - pTank->pev->spawnflags &= ~SF_TANK_SEQFIRE; - - m_pTank = pTank; - if (m_fDuration) - { - SetThink(&CTankSequence :: TimeOutThink ); - SetNextThink( m_fDuration ); - } - } - else // don't check UNTIL_TRIGGER - any UNTIL value can be prematurely ended. - { - //disable any other end conditions - DontThink(); - - // release control of the tank - StopSequence(); - } -} - -void CTankSequence :: FacingNotify() -{ - if (m_iUntil == TSEQ_UNTIL_FACING) - { - SetThink(&CTankSequence :: EndThink ); - SetNextThink( 0 ); - } - else if (m_iShoot == TSEQ_SHOOT_FACING) - m_pTank->pev->spawnflags |= SF_TANK_SEQFIRE; -} - -void CTankSequence :: DeadEnemyNotify() -{ - if (m_iUntil == TSEQ_UNTIL_DEATH) - { - SetThink(&CTankSequence :: EndThink ); - SetNextThink( 0 ); - } -// else -// m_pTank->pev->spawnflags &= ~SF_TANK_SEQFIRE; // if the enemy's dead, stop firing -} - -void CTankSequence :: EndThink() -{ - //the sequence has expired. Release control of the tank. - StopSequence(); - if (!FStringNull(pev->target)) - FireTargets(STRING(pev->target), this, this, USE_TOGGLE, 0); -} - -void CTankSequence :: TimeOutThink() -{ - //the sequence has timed out. Release control of the tank. - StopSequence(); - if (!FStringNull(pev->netname)) - FireTargets(STRING(pev->netname), this, this, USE_TOGGLE, 0); -} - -void CTankSequence :: StopSequence() -{ - if (!m_pTank) - { - ALERT(at_error, "TankSeq: StopSequence with no tank!\n"); - return; // this shouldn't happen. Just insurance... - } - - // if we're doing "shoot at end", fire that shot now. - if (m_iShoot == TSEQ_SHOOT_ONCE) - { - m_pTank->m_fireLast = gpGlobals->time - 1/m_pTank->m_fireRate; // exactly one shot. - Vector forward; - UTIL_MakeVectorsPrivate( m_pTank->pev->angles, forward, NULL, NULL ); - m_pTank->TryFire( m_pTank->BarrelPosition(), forward, m_pTank->pev ); - } - - if (m_iLaserSpot) - { - if (m_pTank->pev->spawnflags & SF_TANK_LASERSPOT && m_iLaserSpot != TSEQ_FLAG_ON) - { - m_pTank->pev->spawnflags &= ~SF_TANK_LASERSPOT; - } - else if (!FBitSet(m_pTank->pev->spawnflags, SF_TANK_LASERSPOT) && m_iLaserSpot != TSEQ_FLAG_OFF) - { - m_pTank->pev->spawnflags |= SF_TANK_LASERSPOT; - } - } - - if (m_iControllable) - { - if (m_pTank->pev->spawnflags & SF_TANK_CANCONTROL && m_iControllable != TSEQ_FLAG_ON) - { - m_pTank->pev->spawnflags &= ~SF_TANK_CANCONTROL; - } - else if (!(m_pTank->pev->spawnflags & SF_TANK_CANCONTROL) && m_iControllable != TSEQ_FLAG_OFF) - { - m_pTank->pev->spawnflags |= SF_TANK_CANCONTROL; - } - } - - m_pTank->StopSequence(); - - if (!FBitSet(pev->spawnflags, SF_TSEQ_REPEATABLE)) - UTIL_Remove( this ); - - if (m_pTank->IsActive() && (m_iActive == TSEQ_FLAG_OFF || m_iActive == TSEQ_FLAG_TOGGLE)) - { - m_pTank->TankDeactivate(); - if (m_pTank->m_pSpot) m_pTank->m_pSpot->Suspend(-1); - } - else if (!m_pTank->IsActive() && (m_iActive == TSEQ_FLAG_ON || m_iActive == TSEQ_FLAG_TOGGLE)) - { - m_pTank->TankActivate(); - if (m_pTank->m_pSpot) m_pTank->m_pSpot->Revive(); - } - - m_pTank = NULL; -} diff --git a/spirit/genericmonster.cpp b/spirit/genericmonster.cpp deleted file mode 100644 index 003d5e94..00000000 --- a/spirit/genericmonster.cpp +++ /dev/null @@ -1,425 +0,0 @@ -/*** -* -* Copyright (c) 1996-2002, 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. -* -* This source code contains proprietary and confidential information of -* Valve LLC and its suppliers. Access to this code is restricted to -* persons who have executed a written SDK license with Valve. Any access, -* use or distribution of this code by or to any unlicensed person is illegal. -* -****/ -//========================================================= -// Generic Monster - purely for scripted sequence work. -//========================================================= -#include "extdll.h" -#include "util.h" -#include "cbase.h" -#include "monsters.h" -#include "schedule.h" -#include "animation.h" -#include "talkmonster.h" -#include "effects.h" - -// For holograms, make them not solid so the player can walk through them -//LRC- this seems to interfere with SF_MONSTER_CLIP -#define SF_GENERICMONSTER_NOTSOLID 4 -#define SF_HEAD_CONTROLLER 8 -#define SF_GENERICMONSTER_INVULNERABLE 32 -#define SF_GENERICMONSTER_PLAYERMODEL 64 - -//========================================================= -// Monster's Anim Events Go Here -//========================================================= -//G-Cont. This code - support for htorch model from Op4 ;) -#define HTORCH_AE_SHOWGUN ( 17) -#define HTORCH_AE_SHOWTORCH ( 18) -#define HTORCH_AE_HIDETORCH ( 19) -#define HTORCH_AE_ONGAS ( 20) -#define HTORCH_AE_OFFGAS ( 21) -#define GUN_DEAGLE 0 -#define GUN_TORCH 1 -#define GUN_NONE 2 - -class CGenericMonster : public CTalkMonster -{ -public: - void Spawn( void ); - void Precache( void ); - void SetYawSpeed( void ); - int Classify ( void ); - void HandleAnimEvent( MonsterEvent_t *pEvent ); - int ISoundMask ( void ); - void KeyValue( KeyValueData *pkvd ); - void Torch ( void ); - void MakeGas( void ); - void UpdateGas( void ); - void KillGas( void ); - - virtual int Save( CSave &save ); - virtual int Restore( CRestore &restore ); - static TYPEDESCRIPTION m_SaveData[]; - - virtual int HasCustomGibs( void ) { return m_iszGibModel; } - - CBeam *m_pBeam; - int m_iszGibModel; -}; -LINK_ENTITY_TO_CLASS( monster_generic, CGenericMonster ); - -TYPEDESCRIPTION CGenericMonster::m_SaveData[] = -{ - DEFINE_FIELD( CGenericMonster, m_iszGibModel, FIELD_STRING ), -}; - -IMPLEMENT_SAVERESTORE( CGenericMonster, CBaseMonster ); - -void CGenericMonster::KeyValue( KeyValueData *pkvd ) -{ - if (FStrEq(pkvd->szKeyName, "m_bloodColor")) - { - m_bloodColor = atoi(pkvd->szValue); - pkvd->fHandled = TRUE; - } - else if (FStrEq(pkvd->szKeyName, "m_iszGibModel")) - { - m_iszGibModel = ALLOC_STRING(pkvd->szValue); - pkvd->fHandled = TRUE; - } - else - CBaseMonster::KeyValue( pkvd ); -} - -//========================================================= -// Classify - indicates this monster's place in the -// relationship table. -//========================================================= -int CGenericMonster :: Classify ( void ) -{ - return m_iClass?m_iClass:CLASS_PLAYER_ALLY; -} - -//========================================================= -// SetYawSpeed - allows each sequence to have a different -// turn rate associated with it. -//========================================================= -void CGenericMonster :: SetYawSpeed ( void ) -{ - int ys; - - switch ( m_Activity ) - { - case ACT_IDLE: - default: - ys = 90; - } - - pev->yaw_speed = ys; -} - -//========================================================= -// HandleAnimEvent - catches the monster-specific messages -// that occur when tagged animation frames are played. -//========================================================= -void CGenericMonster :: HandleAnimEvent( MonsterEvent_t *pEvent ) -{ - Vector vecShootDir; - Vector vecShootOrigin; - - switch( pEvent->event ) - { - case HTORCH_AE_SHOWTORCH: - pev->body = GUN_NONE; - pev->body = GUN_TORCH; - break; - - case HTORCH_AE_SHOWGUN: - pev->body = GUN_NONE; - pev->body = GUN_DEAGLE; - break; - - case HTORCH_AE_HIDETORCH: - pev->body = GUN_NONE; - break; - - case HTORCH_AE_ONGAS: - { - int gas = 1; - MakeGas(); - UpdateGas(); - }; - break; - - case HTORCH_AE_OFFGAS: - { - int gas = 0; - KillGas(); - }; - break; - - default: - CBaseMonster::HandleAnimEvent( pEvent ); - } -} - -//========================================================= -// ISoundMask - generic monster can't hear. -//========================================================= -int CGenericMonster :: ISoundMask ( void ) -{ - return NULL; -} - -//========================================================= -// Spawn -//========================================================= -void CGenericMonster :: Spawn() -{ - // store the size, so we can use it to set up the hulls after Set_Model overwrites it. - Vector vecSize = pev->size; - - //LRC - if the level designer forgets to set a model, don't crash! - if (FStringNull(pev->model)) - { - if (pev->targetname) - ALERT(at_error, "No model specified for monster_generic \"%s\"\n", STRING(pev->targetname)); - else - ALERT(at_error, "No model specified for monster_generic at %.2f %.2f %.2f\n", pev->origin.x, pev->origin.y, pev->origin.z); - pev->model = MAKE_STRING("models/player.mdl"); - } - - Precache(); - - SET_MODEL( ENT(pev), STRING(pev->model) ); - - if (vecSize != g_vecZero) - { - Vector vecMax = vecSize/2; - Vector vecMin = -vecMax; - if (!FBitSet(pev->spawnflags,SF_GENERICMONSTER_PLAYERMODEL)) - { - vecMin.z = 0; - vecMax.z = vecSize.z; - } - UTIL_SetSize(pev, vecMin, vecMax); - } - else if ( - pev->spawnflags & SF_GENERICMONSTER_PLAYERMODEL || - FStrEq( STRING(pev->model), "models/player.mdl" ) || - FStrEq( STRING(pev->model), "models/holo.mdl" ) - ) - UTIL_SetSize(pev, VEC_HULL_MIN, VEC_HULL_MAX); - else - UTIL_SetSize(pev, VEC_HUMAN_HULL_MIN, VEC_HUMAN_HULL_MAX); - - pev->solid = SOLID_SLIDEBOX; - pev->movetype = MOVETYPE_STEP; - if (!m_bloodColor) m_bloodColor = BLOOD_COLOR_RED; - if (!pev->health) pev->health = 8; - m_flFieldOfView = 0.5;// indicates the width of this monster's forward view cone ( as a dotproduct result ) - m_MonsterState = MONSTERSTATE_NONE; - - MonsterInit(); - - if ( pev->spawnflags & SF_HEAD_CONTROLLER ) - { - m_afCapability = bits_CAP_TURN_HEAD; - - } - - if ( pev->spawnflags & SF_GENERICMONSTER_NOTSOLID ) - { - pev->solid = SOLID_NOT; - pev->takedamage = DAMAGE_NO; - } - else if ( pev->spawnflags & SF_GENERICMONSTER_INVULNERABLE ) - { - pev->takedamage = DAMAGE_NO; - } -} - -//========================================================= -// Precache - precaches all resources this monster needs -//========================================================= -void CGenericMonster :: Precache() -{ - CTalkMonster::Precache(); - TalkInit(); - PRECACHE_MODEL( (char *)STRING(pev->model) ); - if (m_iszGibModel) - PRECACHE_MODEL( (char*)STRING(m_iszGibModel) ); //LRC -} - -//========================================================= -// AI Schedules Specific to this monster -//========================================================= - -//========================================================= -// monster-specific schedule types -//========================================================= -enum -{ - TASK_TORCH_CHECK_FIRE = LAST_COMMON_SCHEDULE + 1, - TASK_GAS, -}; - -// ========================================================= -// TORCH SUPPORT -// ========================================================= -void CGenericMonster :: Torch ( void ) -{ - Vector vecGunPos; - Vector vecGunAngles; - Vector vecShootDir; - - GetAttachment( 4, vecGunPos, vecGunAngles ); - pev->effects |= EF_MUZZLEFLASH; - - Vector angDir = UTIL_VecToAngles( vecShootDir ); - SetBlending( 0, angDir.x ); -} - -void CGenericMonster::UpdateGas( void ) { } - -void CGenericMonster::MakeGas( void ) -{ - Vector posGun, angleGun; - TraceResult tr; - UTIL_MakeVectors( pev->angles ); - { - KillGas(); - m_pBeam = CBeam::BeamCreate( "sprites/laserbeam.spr", 7 ); - if ( m_pBeam ) - { - GetAttachment( 4, posGun, angleGun ); - GetAttachment( 3, posGun, angleGun ); - - Vector vecEnd = (gpGlobals->v_forward * 5) + posGun; - UTIL_TraceLine( posGun, vecEnd, dont_ignore_monsters, edict(), &tr ); - - m_pBeam->EntsInit( edict(), edict() ); - m_pBeam->SetColor( 24, 121, 239 ); - m_pBeam->SetBrightness( 190 ); - m_pBeam->SetScrollRate( 20 ); - m_pBeam->SetStartAttachment( 4 ); - m_pBeam->SetEndAttachment( 3 ); - m_pBeam->DamageDecal( 28 ); - m_pBeam->DoSparks( tr.vecEndPos, posGun ); - m_pBeam->SetFlags( FBEAM_SHADEIN ); - m_pBeam->pev->spawnflags |= pev->spawnflags & SF_BEAM_SPARKSTART; //| SF_BEAM_DECALS | SF_BEAM_TOGGLE); - EXPORT RelinkBeam(); - - UTIL_Sparks( tr.vecEndPos ); - UTIL_DecalTrace(&tr, 28 + RANDOM_LONG(0,4)); - } - } - // m_flNextAttack = gpGlobals->time + RANDOM_FLOAT( 0.5, 4.0 ); - if ( int gas = 1 ) - { - pev->nextthink = gpGlobals->time; - } -} - -void CGenericMonster :: KillGas( void ) -{ - if ( m_pBeam ) - { - UTIL_Remove( m_pBeam ); - m_pBeam = NULL; - } -} - -//========================================================= -// GENERIC DEAD MONSTER, PROP -//========================================================= -class CDeadGenericMonster : public CBaseMonster -{ -public: - void Spawn( void ); - void Precache( void ); - int Classify ( void ) { return CLASS_PLAYER_ALLY; } - void KeyValue( KeyValueData *pkvd ); - - virtual int Save( CSave &save ); - virtual int Restore( CRestore &restore ); - static TYPEDESCRIPTION m_SaveData[]; - - virtual int HasCustomGibs( void ) { return m_iszGibModel; } - - int m_iszGibModel; -}; - -LINK_ENTITY_TO_CLASS( monster_generic_dead, CDeadGenericMonster ); - -TYPEDESCRIPTION CDeadGenericMonster::m_SaveData[] = -{ - DEFINE_FIELD( CDeadGenericMonster, m_iszGibModel, FIELD_STRING ), -}; - -IMPLEMENT_SAVERESTORE( CDeadGenericMonster, CBaseMonster ); - -void CDeadGenericMonster::KeyValue( KeyValueData *pkvd ) -{ - if (FStrEq(pkvd->szKeyName, "m_bloodColor")) - { - m_bloodColor = atoi(pkvd->szValue); - pkvd->fHandled = TRUE; - } - else if (FStrEq(pkvd->szKeyName, "m_iszGibModel")) - { - m_iszGibModel = ALLOC_STRING(pkvd->szValue); - pkvd->fHandled = TRUE; - } - else - CBaseMonster::KeyValue( pkvd ); -} - -//========================================================= -// ********** DeadGenericMonster SPAWN ********** -//========================================================= -void CDeadGenericMonster :: Spawn( void ) -{ - Precache(); - SET_MODEL(ENT(pev), STRING(pev->model)); - - pev->effects = 0; - pev->yaw_speed = 8; //LRC -- what? - pev->sequence = 0; - - if (pev->netname) - { - pev->sequence = LookupSequence( STRING(pev->netname) ); - - if (pev->sequence == -1) - { - ALERT ( at_debug, "Invalid sequence name \"%s\" in monster_generic_dead\n", STRING(pev->netname) ); - } - } - else - { - pev->sequence = LookupActivity( pev->frags ); -// if (pev->sequence == -1) -// { -// ALERT ( at_error, "monster_generic_dead - specify a sequence name or choose a different death type: model \"%s\" has no available death sequences.\n", STRING(pev->model) ); -// } - //...and if that doesn't work, forget it. - } - - // Corpses have less health - pev->health = 8; - - MonsterInitDead(); - - ResetSequenceInfo( ); - pev->frame = 255; // pose at the _end_ of its death sequence. -} - -void CDeadGenericMonster :: Precache() -{ - PRECACHE_MODEL( (char*)STRING(pev->model) ); - if (m_iszGibModel) - PRECACHE_MODEL( (char*)STRING(m_iszGibModel) ); //LRC -} diff --git a/spirit/glock.cpp b/spirit/glock.cpp deleted file mode 100644 index ef737fe5..00000000 --- a/spirit/glock.cpp +++ /dev/null @@ -1,264 +0,0 @@ -/*** -* -* Copyright (c) 1996-2002, 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. -* -****/ - -#include "extdll.h" -#include "util.h" -#include "cbase.h" -#include "monsters.h" -#include "weapons.h" -#include "nodes.h" -#include "player.h" - -enum glock_e { - GLOCK_IDLE1 = 0, - GLOCK_IDLE2, - GLOCK_IDLE3, - GLOCK_DRAW, - GLOCK_HOLSTER, - GLOCK_SHOOT, - GLOCK_SHOOT_EMPTY, - GLOCK_RELOAD, - GLOCK_RELOAD_NOT_EMPTY, - GLOCK_HOLSTER2, - GLOCK_ADD_SILENCER, - GLOCK_DEL_SILENCER -}; - -class CGlock : public CBasePlayerWeapon -{ -public: - void Spawn( void ); - void Precache( void ); - int GetItemInfo(ItemInfo *p); - - void PrimaryAttack( void ); - void SecondaryAttack( void ); - BOOL Deploy( void ); - void Holster( ); - void Reload( void ); - void WeaponIdle( void ); -private: - unsigned short m_usFireGlock; -}; -LINK_ENTITY_TO_CLASS( weapon_glock, CGlock ); -LINK_ENTITY_TO_CLASS( weapon_9mmhandgun, CGlock ); - - -void CGlock::Spawn( ) -{ - pev->classname = MAKE_STRING("weapon_9mmhandgun"); // hack to allow for old names - Precache( ); - m_iId = WEAPON_GLOCK; - SET_MODEL(ENT(pev), "models/w_9mmhandgun.mdl"); - m_iDefaultAmmo = GLOCK_DEFAULT_GIVE; - FallInit();// get ready to fall down. -} - -void CGlock::Holster( ) -{ - m_pPlayer->m_flNextAttack = UTIL_WeaponTimeBase() + 0.8; - SendWeaponAnim( GLOCK_HOLSTER ); -} - -void CGlock::Precache( void ) -{ - PRECACHE_MODEL("models/v_9mmhandgun.mdl"); - PRECACHE_MODEL("models/w_9mmhandgun.mdl"); - PRECACHE_MODEL("models/p_9mmhandgun.mdl"); - - int m_iShell = PRECACHE_MODEL ("models/shell.mdl");// brass shell - - PRECACHE_SOUND("items/9mmclip1.wav"); - PRECACHE_SOUND("items/9mmclip2.wav"); - - PRECACHE_SOUND ("weapons/pl_gun1.wav");//silenced handgun - PRECACHE_SOUND ("weapons/pl_gun2.wav");//silenced handgun - PRECACHE_SOUND ("weapons/pl_gun3.wav");//handgun - - m_usFireGlock = PRECACHE_EVENT( 1, "evGlock1" ); -} - -int CGlock::GetItemInfo(ItemInfo *p) -{ - p->pszName = STRING(pev->classname); - p->pszAmmo1 = "9mm"; - p->iMaxAmmo1 = _9MM_MAX_CARRY; - p->pszAmmo2 = NULL; - p->iMaxAmmo2 = -1; - p->iMaxClip = GLOCK_MAX_CLIP; - p->iSlot = 1; - p->iPosition = 0; - p->iFlags = 0; - p->iId = m_iId = WEAPON_GLOCK; - p->iWeight = GLOCK_WEIGHT; - - return 1; -} - -BOOL CGlock::Deploy( ) -{ - return DefaultDeploy( "models/v_9mmhandgun.mdl", "models/p_9mmhandgun.mdl", GLOCK_DRAW, "onehanded", 0.8 ); -} - -void CGlock::SecondaryAttack( void ) -{ - if(m_iBody == 0) - { - SendWeaponAnim( GLOCK_HOLSTER2); - m_iBody = 1; - m_iOverloadLevel = 1; - m_flTimeWeaponIdle = UTIL_WeaponTimeBase() + 0.5; - } - else - { - SendWeaponAnim( GLOCK_DEL_SILENCER); - m_iBody = 0; - m_iOverloadLevel = 2; - m_flTimeWeaponIdle = UTIL_WeaponTimeBase() + 3.0; - } - m_flNextSecondaryAttack = m_flNextPrimaryAttack = UTIL_WeaponTimeBase() + 4.0; -} - -void CGlock::PrimaryAttack( void ) -{ - if ( m_iClip && m_pPlayer->pev->waterlevel != 3)//don't fire underwater - { - // player "shoot" animation - m_pPlayer->SetAnimation( PLAYER_ATTACK1 ); - - // silenced - if (m_iBody) - { - m_pPlayer->m_iWeaponVolume = QUIET_GUN_VOLUME; - m_pPlayer->m_iWeaponFlash = DIM_GUN_FLASH; - } - else - { - // non-silenced - m_pPlayer->m_iWeaponVolume = NORMAL_GUN_VOLUME; - m_pPlayer->m_iWeaponFlash = NORMAL_GUN_FLASH; - } - - m_iClip--; - - Vector vecSrc = m_pPlayer->GetGunPosition( ); - Vector vecAiming; - - vecAiming = m_pPlayer->GetAutoaimVector( AUTOAIM_10DEGREES ); - - Vector vecDir; - vecDir = m_pPlayer->FireBulletsPlayer( 1, vecSrc, vecAiming, Vector( 0.02, 0.02, 0.02 ), 8192, BULLET_PLAYER_9MM, 0, 0, m_pPlayer->pev, m_pPlayer->random_seed ); - - int iAnim = ( m_iClip == 0 ) ? GLOCK_SHOOT_EMPTY : GLOCK_SHOOT; - - PLAYBACK_EVENT_FULL( 0, m_pPlayer->edict(), m_usFireGlock, 0.0, (float *)&g_vecZero, (float *)&g_vecZero, vecDir.x, vecDir.y, pev->body, iAnim, 0, m_iBody ); - - m_flNextPrimaryAttack = m_flNextSecondaryAttack = UTIL_WeaponTimeBase() + 0.35; - m_flTimeWeaponIdle = UTIL_WeaponTimeBase() + RANDOM_FLOAT ( 10, 15 ); - } - else - { - PlayEmptySound(); - m_flNextPrimaryAttack = UTIL_WeaponTimeBase() + 0.7; - } -} - -void CGlock::Reload( void ) -{ - if ( m_pPlayer->m_rgAmmo[m_iPrimaryAmmoType] == 0) return; - - if (m_iClip == 0) DefaultReload( 17, GLOCK_RELOAD, 1.5 ); - else DefaultReload( 17, GLOCK_RELOAD_NOT_EMPTY, 1.5 ); - m_flTimeWeaponIdle = UTIL_WeaponTimeBase() + RANDOM_FLOAT ( 10, 15 ); -} - - - -void CGlock::WeaponIdle( void ) -{ - m_pPlayer->GetAutoaimVector( AUTOAIM_10DEGREES ); - - if ( m_flTimeWeaponIdle > UTIL_WeaponTimeBase() ) return; - if (m_iOverloadLevel == 1) - { - SendWeaponAnim( GLOCK_ADD_SILENCER ); - m_iOverloadLevel = 0; //silencer added - m_flTimeWeaponIdle = UTIL_WeaponTimeBase() + 3.0; - return; - } - if (m_iOverloadLevel == 2) - { - SendWeaponAnim( GLOCK_DRAW ); - m_iOverloadLevel = 0; //silencer removed - m_flTimeWeaponIdle = UTIL_WeaponTimeBase() + 1.2; - return; - } - // only idle if the slid isn't back - if (m_iClip) - { - int iAnim; - float flRand = RANDOM_FLOAT(0, 1.2); - if (flRand < 0.2) - { - iAnim = GLOCK_IDLE1; - m_flTimeWeaponIdle = UTIL_WeaponTimeBase() + 4.0; - } - else if ( 0.4 > flRand && flRand > 0.2 ) - { - iAnim = GLOCK_IDLE2; - m_flTimeWeaponIdle = UTIL_WeaponTimeBase() + 2.8; - } - else if ( 0.8 > flRand && flRand > 0.5 ) - { - iAnim = GLOCK_IDLE3; - m_flTimeWeaponIdle = UTIL_WeaponTimeBase() + 3.7; - } - else - { - m_flTimeWeaponIdle = UTIL_WeaponTimeBase() + RANDOM_LONG(10, 30); - return; - } - SendWeaponAnim( iAnim ); - } -} - - - - -class CGlockAmmo : public CBasePlayerAmmo -{ - void Spawn( void ) - { - Precache( ); - SET_MODEL(ENT(pev), "models/w_9mmclip.mdl"); - CBasePlayerAmmo::Spawn( ); - } - void Precache( void ) - { - PRECACHE_MODEL ("models/w_9mmclip.mdl"); - PRECACHE_SOUND("items/9mmclip1.wav"); - } - BOOL AddAmmo( CBaseEntity *pOther ) - { - if (pOther->GiveAmmo( AMMO_GLOCKCLIP_GIVE, "9mm", _9MM_MAX_CARRY ) != -1) - { - EMIT_SOUND(ENT(pev), CHAN_ITEM, "items/9mmclip1.wav", 1, ATTN_NORM); - return TRUE; - } - return FALSE; - } -}; -LINK_ENTITY_TO_CLASS( ammo_glockclip, CGlockAmmo ); -LINK_ENTITY_TO_CLASS( ammo_9mmclip, CGlockAmmo ); \ No newline at end of file diff --git a/spirit/lights.cpp b/spirit/lights.cpp deleted file mode 100644 index 0e6fb44b..00000000 --- a/spirit/lights.cpp +++ /dev/null @@ -1,654 +0,0 @@ -/*** -* -* Copyright (c) 1996-2002, 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. -* -****/ -/* - -===== lights.cpp ======================================================== - - spawn and think functions for editor-placed lights - -*/ - -#include "extdll.h" -#include "util.h" -#include "cbase.h" - -//LRC -int GetStdLightStyle (int iStyle) -{ - switch (iStyle) - { - // 0 normal - case 0: return MAKE_STRING("m"); - - // 1 FLICKER (first variety) - case 1: return MAKE_STRING("mmnmmommommnonmmonqnmmo"); - - // 2 SLOW STRONG PULSE - case 2: return MAKE_STRING("abcdefghijklmnopqrstuvwxyzyxwvutsrqponmlkjihgfedcba"); - - // 3 CANDLE (first variety) - case 3: return MAKE_STRING("mmmmmaaaaammmmmaaaaaabcdefgabcdefg"); - - // 4 FAST STROBE - case 4: return MAKE_STRING("mamamamamama"); - - // 5 GENTLE PULSE 1 - case 5: return MAKE_STRING("jklmnopqrstuvwxyzyxwvutsrqponmlkj"); - - // 6 FLICKER (second variety) - case 6: return MAKE_STRING("nmonqnmomnmomomno"); - - // 7 CANDLE (second variety) - case 7: return MAKE_STRING("mmmaaaabcdefgmmmmaaaammmaamm"); - - // 8 CANDLE (third variety) - case 8: return MAKE_STRING("mmmaaammmaaammmabcdefaaaammmmabcdefmmmaaaa"); - - // 9 SLOW STROBE (fourth variety) - case 9: return MAKE_STRING("aaaaaaaazzzzzzzz"); - - // 10 FLUORESCENT FLICKER - case 10: return MAKE_STRING("mmamammmmammamamaaamammma"); - - // 11 SLOW PULSE NOT FADE TO BLACK - case 11: return MAKE_STRING("abcdefghijklmnopqrrqponmlkjihgfedcba"); - - // 12 UNDERWATER LIGHT MUTATION - // this light only distorts the lightmap - no contribution - // is made to the brightness of affected surfaces - case 12: return MAKE_STRING("mmnnmmnnnmmnn"); - - // 13 OFF (LRC) - case 13: return MAKE_STRING("a"); - - // 14 SLOW FADE IN (LRC) - case 14: return MAKE_STRING("aabbccddeeffgghhiijjkkllmmmmmmmmmmmmmm"); - - // 15 MED FADE IN (LRC) - case 15: return MAKE_STRING("abcdefghijklmmmmmmmmmmmmmmmmmmmmmmmmmm"); - - // 16 FAST FADE IN (LRC) - case 16: return MAKE_STRING("acegikmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmm"); - - // 17 SLOW FADE OUT (LRC) - case 17: return MAKE_STRING("llkkjjiihhggffeeddccbbaaaaaaaaaaaaaaaa"); - - // 18 MED FADE OUT (LRC) - case 18: return MAKE_STRING("lkjihgfedcbaaaaaaaaaaaaaaaaaaaaaaaaaaa"); - - // 19 FAST FADE OUT (LRC) - case 19: return MAKE_STRING("kigecaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa"); - - default: return MAKE_STRING("m"); - } -} - - -class CLight : public CPointEntity -{ -public: - virtual void KeyValue( KeyValueData* pkvd ); - virtual void Spawn( void ); - void Use( CBaseEntity *pActivator, CBaseEntity *pCaller, USE_TYPE useType, float value ); - void Think( void ); - - virtual int Save( CSave &save ); - virtual int Restore( CRestore &restore ); - virtual STATE GetState(void) { return m_iState; }; //LRC - - static TYPEDESCRIPTION m_SaveData[]; - - int GetStyle( void ) { return m_iszCurrentStyle; }; //LRC - void SetStyle( int iszPattern ); //LRC - - void SetCorrectStyle( void ); //LRC - -private: - STATE m_iState; // current state - int m_iOnStyle; // style to use while on - int m_iOffStyle; // style to use while off - int m_iTurnOnStyle; // style to use while turning on - int m_iTurnOffStyle; // style to use while turning off - int m_iTurnOnTime; // time taken to turn on - int m_iTurnOffTime; // time taken to turn off - int m_iszPattern; // custom style to use while on - int m_iszCurrentStyle; // current style string -}; -LINK_ENTITY_TO_CLASS( light, CLight ); - -TYPEDESCRIPTION CLight::m_SaveData[] = -{ - DEFINE_FIELD( CLight, m_iState, FIELD_INTEGER ), - DEFINE_FIELD( CLight, m_iszPattern, FIELD_STRING ), - DEFINE_FIELD( CLight, m_iszCurrentStyle, FIELD_STRING ), - DEFINE_FIELD( CLight, m_iOnStyle, FIELD_INTEGER ), - DEFINE_FIELD( CLight, m_iOffStyle, FIELD_INTEGER ), - DEFINE_FIELD( CLight, m_iTurnOnStyle, FIELD_INTEGER ), - DEFINE_FIELD( CLight, m_iTurnOffStyle, FIELD_INTEGER ), - DEFINE_FIELD( CLight, m_iTurnOnTime, FIELD_INTEGER ), - DEFINE_FIELD( CLight, m_iTurnOffTime, FIELD_INTEGER ), -}; - -IMPLEMENT_SAVERESTORE( CLight, CPointEntity ); - - -// -// Cache user-entity-field values until spawn is called. -// -void CLight :: KeyValue( KeyValueData* pkvd) -{ - if (FStrEq(pkvd->szKeyName, "m_iOnStyle")) - { - m_iOnStyle = atoi(pkvd->szValue); - pkvd->fHandled = TRUE; - } - else if (FStrEq(pkvd->szKeyName, "m_iOffStyle")) - { - m_iOffStyle = atoi(pkvd->szValue); - pkvd->fHandled = TRUE; - } - else if (FStrEq(pkvd->szKeyName, "m_iTurnOnStyle")) - { - m_iTurnOnStyle = atoi(pkvd->szValue); - pkvd->fHandled = TRUE; - } - else if (FStrEq(pkvd->szKeyName, "m_iTurnOffStyle")) - { - m_iTurnOffStyle = atoi(pkvd->szValue); - pkvd->fHandled = TRUE; - } - else if (FStrEq(pkvd->szKeyName, "m_iTurnOnTime")) - { - m_iTurnOnTime = atoi(pkvd->szValue); - pkvd->fHandled = TRUE; - } - else if (FStrEq(pkvd->szKeyName, "m_iTurnOffTime")) - { - m_iTurnOffTime = atoi(pkvd->szValue); - pkvd->fHandled = TRUE; - } - else if (FStrEq(pkvd->szKeyName, "pitch")) - { - pev->angles.x = atoi(pkvd->szValue); - pkvd->fHandled = TRUE; - } - else if (FStrEq(pkvd->szKeyName, "pattern")) - { - m_iszPattern = ALLOC_STRING( pkvd->szValue ); - pkvd->fHandled = TRUE; - } - else if (FStrEq(pkvd->szKeyName, "firetarget")) - { - pev->target = ALLOC_STRING( pkvd->szValue ); - pkvd->fHandled = TRUE; - } - else - { - CPointEntity::KeyValue( pkvd ); - } -} - -void CLight :: SetStyle ( int iszPattern ) -{ - if (m_iStyle < 32) // if it's using a global style, don't change it - return; - m_iszCurrentStyle = iszPattern; -// ALERT(at_console, "SetStyle %d \"%s\"\n", m_iStyle, (char *)STRING( iszPattern )); - LIGHT_STYLE(m_iStyle, (char *)STRING( iszPattern )); -} - -// regardless of what's been set by trigger_lightstyle ents, set the style I think I need -void CLight :: SetCorrectStyle ( void ) -{ - if (m_iStyle >= 32) - { - switch (m_iState) - { - case STATE_ON: - if (m_iszPattern) // custom styles have priority over standard ones - SetStyle( m_iszPattern ); - else if (m_iOnStyle) - SetStyle(GetStdLightStyle(m_iOnStyle)); - else - SetStyle(MAKE_STRING("m")); - break; - case STATE_OFF: - if (m_iOffStyle) - SetStyle(GetStdLightStyle(m_iOffStyle)); - else - SetStyle(MAKE_STRING("a")); - break; - case STATE_TURN_ON: - if (m_iTurnOnStyle) - SetStyle(GetStdLightStyle(m_iTurnOnStyle)); - else - SetStyle(MAKE_STRING("a")); - break; - case STATE_TURN_OFF: - if (m_iTurnOffStyle) - SetStyle(GetStdLightStyle(m_iTurnOffStyle)); - else - SetStyle(MAKE_STRING("m")); - break; - } - } - else - { - m_iszCurrentStyle = GetStdLightStyle( m_iStyle ); - } -} - -void CLight :: Think( void ) -{ - switch (GetState()) - { - case STATE_TURN_ON: - m_iState = STATE_ON; - FireTargets(STRING(pev->target),this,this,USE_ON,0); - break; - case STATE_TURN_OFF: - m_iState = STATE_OFF; - FireTargets(STRING(pev->target),this,this,USE_OFF,0); - break; - } - SetCorrectStyle(); -} - -/*QUAKED light (0 1 0) (-8 -8 -8) (8 8 8) LIGHT_START_OFF -Non-displayed light. -Default light value is 300 -Default style is 0 -If targeted, it will toggle between on or off. -*/ - -void CLight :: Spawn( void ) -{ - if (FStringNull(pev->targetname)) - { // inert light - REMOVE_ENTITY(ENT(pev)); - return; - } - - if (FBitSet(pev->spawnflags,SF_LIGHT_START_OFF)) - m_iState = STATE_OFF; - else - m_iState = STATE_ON; - SetCorrectStyle(); -} - - -void CLight :: Use( CBaseEntity *pActivator, CBaseEntity *pCaller, USE_TYPE useType, float value ) -{ - if (m_iStyle >= 32) - { - if ( !ShouldToggle( useType ) ) - return; - - switch (GetState()) - { - case STATE_ON: - case STATE_TURN_ON: - if (m_iTurnOffTime) - { - m_iState = STATE_TURN_OFF; - SetNextThink( m_iTurnOffTime ); - } - else - m_iState = STATE_OFF; - break; - case STATE_OFF: - case STATE_TURN_OFF: - if (m_iTurnOnTime) - { - m_iState = STATE_TURN_ON; - SetNextThink( m_iTurnOnTime ); - } - else - m_iState = STATE_ON; - break; - } - SetCorrectStyle(); - } -} - -// -// shut up spawn functions for new spotlights -// -LINK_ENTITY_TO_CLASS( light_spot, CLight ); - - -class CEnvLight : public CLight -{ -public: - void KeyValue( KeyValueData* pkvd ); - void Spawn( void ); -}; - -LINK_ENTITY_TO_CLASS( light_environment, CEnvLight ); - -void CEnvLight::KeyValue( KeyValueData* pkvd ) -{ - if (FStrEq(pkvd->szKeyName, "_light")) - { - int r, g, b, v, j; - char szColor[64]; - j = sscanf( pkvd->szValue, "%d %d %d %d\n", &r, &g, &b, &v ); - if (j == 1) - { - g = b = r; - } - else if (j == 4) - { - r = r * (v / 255.0); - g = g * (v / 255.0); - b = b * (v / 255.0); - } - - // simulate qrad direct, ambient,and gamma adjustments, as well as engine scaling - r = pow( r / 114.0, 0.6 ) * 264; - g = pow( g / 114.0, 0.6 ) * 264; - b = pow( b / 114.0, 0.6 ) * 264; - - pkvd->fHandled = TRUE; - sprintf( szColor, "%d", r ); - CVAR_SET_STRING( "sv_skycolor_r", szColor ); - sprintf( szColor, "%d", g ); - CVAR_SET_STRING( "sv_skycolor_g", szColor ); - sprintf( szColor, "%d", b ); - CVAR_SET_STRING( "sv_skycolor_b", szColor ); - } - else - { - CLight::KeyValue( pkvd ); - } -} - - -void CEnvLight :: Spawn( void ) -{ - char szVector[64]; - UTIL_MakeAimVectors( pev->angles ); - - sprintf( szVector, "%f", gpGlobals->v_forward.x ); - CVAR_SET_STRING( "sv_skyvec_x", szVector ); - sprintf( szVector, "%f", gpGlobals->v_forward.y ); - CVAR_SET_STRING( "sv_skyvec_y", szVector ); - sprintf( szVector, "%f", gpGlobals->v_forward.z ); - CVAR_SET_STRING( "sv_skyvec_z", szVector ); - - CLight::Spawn( ); -} - -//********************************************************** -//LRC- the CLightDynamic entity - works like the flashlight. -//********************************************************** - -#define SF_LIGHTDYNAMIC_START_OFF 1 -#define SF_LIGHTDYNAMIC_FLARE 2 - -class CLightDynamic : public CBaseEntity -{ -public: - void Spawn( void ); - void Precache( void ); - void Use( CBaseEntity *pActivator, CBaseEntity *pCaller, USE_TYPE useType, float value ); - int ObjectCaps( void ) { return CBaseEntity::ObjectCaps() & ~FCAP_ACROSS_TRANSITION; } - void SetEffects( void ); - STATE GetState( void ); -}; - -LINK_ENTITY_TO_CLASS( light_glow, CLightDynamic ); - -void CLightDynamic::Spawn( void ) -{ - Precache( ); - - SET_MODEL(ENT(pev), "sprites/null.spr"); - pev->solid = SOLID_NOT; - pev->movetype = MOVETYPE_NONE; - - if (!(pev->spawnflags & SF_LIGHTDYNAMIC_START_OFF)) - { - pev->health = 1; - SetEffects(); - } -} - -void CLightDynamic :: Precache( void ) -{ - PRECACHE_MODEL("sprites/null.spr"); -} - -void CLightDynamic::Use( CBaseEntity *pActivator, CBaseEntity *pCaller, USE_TYPE useType, float value ) -{ - if (ShouldToggle(useType, pev->health)) - { - if (pev->health) - pev->health = 0; - else - pev->health = 1; - SetEffects(); - } -} - -void CLightDynamic::SetEffects( void ) -{ - if (pev->health) - { - if (pev->frags == 2) - pev->effects |= EF_BRIGHTLIGHT; - else if (pev->frags) - pev->effects |= EF_DIMLIGHT; - - if (pev->spawnflags & SF_LIGHTDYNAMIC_FLARE) - pev->effects |= EF_LIGHT; - } - else - { - pev->effects &= ~(EF_DIMLIGHT | EF_BRIGHTLIGHT | EF_LIGHT); - } -} - -STATE CLightDynamic::GetState( void ) -{ - if (pev->health) - return STATE_ON; - else - return STATE_OFF; -} - -//********************************************************** -//LRC- the CTriggerLightstyle entity - changes the style of a light temporarily. -//********************************************************** -class CLightFader : public CPointEntity -{ -public: - void EXPORT FadeThink( void ); - void EXPORT WaitThink( void ); - virtual int Save( CSave &save ); - virtual int Restore( CRestore &restore ); - - static TYPEDESCRIPTION m_SaveData[]; - - CLight *m_pLight; - char m_cFrom; - char m_cTo; - char m_szCurStyle[2]; - float m_fEndTime; - int m_iszPattern; - float m_fStep; - int m_iWait; -}; - -LINK_ENTITY_TO_CLASS( lightfader, CLightFader ); - -TYPEDESCRIPTION CLightFader::m_SaveData[] = -{ - DEFINE_FIELD( CLightFader, m_pLight, FIELD_CLASSPTR ), - DEFINE_FIELD( CLightFader, m_cFrom, FIELD_CHARACTER ), - DEFINE_FIELD( CLightFader, m_cTo, FIELD_CHARACTER ), - DEFINE_ARRAY( CLightFader, m_szCurStyle, FIELD_CHARACTER, 2 ), - DEFINE_FIELD( CLightFader, m_fEndTime, FIELD_FLOAT ), - DEFINE_FIELD( CLightFader, m_iszPattern, FIELD_STRING ), - DEFINE_FIELD( CLightFader, m_fStep, FIELD_FLOAT ), - DEFINE_FIELD( CLightFader, m_iWait, FIELD_INTEGER ), -}; - -IMPLEMENT_SAVERESTORE(CLightFader,CPointEntity); - -void CLightFader::FadeThink( void ) -{ - if (m_fEndTime > gpGlobals->time) - { - m_szCurStyle[0] = m_cTo + (char)((m_cFrom - m_cTo) * (m_fEndTime - gpGlobals->time) * m_fStep); - m_szCurStyle[1] = 0; // null terminator -// ALERT(at_console, "FadeThink: %s %s\n", STRING(m_pLight->pev->classname), m_szCurStyle); - m_pLight->SetStyle(MAKE_STRING(m_szCurStyle)); - SetNextThink( 0.1 ); - } - else - { - // fade is finished - m_pLight->SetStyle(m_iszPattern); - if (m_iWait > -1) - { - // wait until it's time to switch off - SetThink(&CLightFader:: WaitThink ); - SetNextThink( m_iWait ); - } - else - { - // we've finished, kill the fader - SetThink(&CLightFader:: SUB_Remove ); - SetNextThink( 0.1 ); - } - } -} - -// we've finished. revert the light and kill the fader. -void CLightFader::WaitThink( void ) -{ - m_pLight->SetCorrectStyle(); - SetThink(&CLightFader:: SUB_Remove ); - SetNextThink( 0.1 ); -} - - - -class CTriggerLightstyle : public CPointEntity -{ -public: - void KeyValue( KeyValueData *pkvd ); - void Use( CBaseEntity *pActivator, CBaseEntity *pCaller, USE_TYPE useType, float value ); - virtual int Save( CSave &save ); - virtual int Restore( CRestore &restore ); - static TYPEDESCRIPTION m_SaveData[]; - -private: - int m_iszPattern; - int m_iFade; - int m_iWait; -}; - -LINK_ENTITY_TO_CLASS( trigger_lightstyle, CTriggerLightstyle ); - -TYPEDESCRIPTION CTriggerLightstyle::m_SaveData[] = -{ - DEFINE_FIELD( CTriggerLightstyle, m_iszPattern, FIELD_STRING ), - DEFINE_FIELD( CTriggerLightstyle, m_iFade, FIELD_INTEGER ), - DEFINE_FIELD( CTriggerLightstyle, m_iWait, FIELD_INTEGER ), -}; - -IMPLEMENT_SAVERESTORE(CTriggerLightstyle,CBaseEntity); - -void CTriggerLightstyle::KeyValue( KeyValueData *pkvd ) -{ - if (FStrEq(pkvd->szKeyName, "pattern")) - { - m_iszPattern = ALLOC_STRING( pkvd->szValue ); - pkvd->fHandled = TRUE; - } - else if (FStrEq(pkvd->szKeyName, "m_iFade")) - { - m_iFade = atoi( pkvd->szValue ); - pkvd->fHandled = TRUE; - } - else if (FStrEq(pkvd->szKeyName, "m_iWait")) - { - m_iWait = atoi( pkvd->szValue ); - pkvd->fHandled = TRUE; - } - else - CBaseEntity::KeyValue( pkvd ); -} - -void CTriggerLightstyle::Use( CBaseEntity *pActivator, CBaseEntity *pCaller, USE_TYPE useType, float value ) -{ - CBaseEntity *pTarget = NULL; - if ( !pev->target ) - return; - - //ALERT( at_console, "Lightstyle change for: (%s)\n", STRING(pev->target) ); - - for (;;) - { - pTarget = UTIL_FindEntityByTargetname(pTarget,STRING(pev->target), pActivator); - if (FNullEnt(pTarget)) - break; - - int iszPattern; - if (m_iszPattern) - iszPattern = m_iszPattern; - else - iszPattern = GetStdLightStyle(m_iStyle); - - // not a light entity? - if (!FClassnameIs(pTarget->pev, "light") && !FClassnameIs(pTarget->pev, "light_spot") && !FClassnameIs(pTarget->pev, "light_environment")) - { - if (pTarget->m_iStyle >= 32) - LIGHT_STYLE(pTarget->m_iStyle, (char*)STRING(iszPattern)); - } - else - { - CLight *pLight = (CLight*)pTarget; - - if (m_iFade) - { -// ALERT(at_console, "Making fader ent, step 1/%d = %f\n", m_iFade, 1/m_iFade); - CLightFader *pFader = GetClassPtr( (CLightFader*)NULL ); - pFader->m_pLight = pLight; - pFader->m_cFrom = ((char*)STRING(pLight->GetStyle()))[0]; - pFader->m_cTo = ((char*)STRING(iszPattern))[0]; - pFader->m_iszPattern = iszPattern; - pFader->m_fEndTime = gpGlobals->time + m_iFade; - pFader->m_fStep = ((float)1)/m_iFade; - pFader->m_iWait = m_iWait; - pFader->SetThink(&CLightFader::FadeThink ); - pFader->SetNextThink( 0.1 ); - } - else - { - pLight->SetStyle( iszPattern ); - if (m_iWait != -1) - { - CLightFader *pFader = GetClassPtr( (CLightFader*)NULL ); - pFader->m_pLight = pLight; - pFader->SetThink(&CLightFader::WaitThink ); - pFader->SetNextThink( m_iWait ); - } - } - } - } -} diff --git a/spirit/locus.cpp b/spirit/locus.cpp deleted file mode 100644 index 7cc881c2..00000000 --- a/spirit/locus.cpp +++ /dev/null @@ -1,773 +0,0 @@ -//========================================= -// NEW file for Spirit of Half-Life 0.7 -// Created 14/01/02 -//========================================= - -// Spirit of Half-Life's particle system uses "locus triggers" to tell -// entities where to perform their actions. - -#include "extdll.h" -#include "util.h" -#include "cbase.h" -#include "locus.h" -#include "effects.h" -#include "decals.h" - - -Vector CalcLocus_Position( CBaseEntity *pEntity, CBaseEntity *pLocus, const char *szText ) -{ - if ((*szText >= '0' && *szText <= '9') || *szText == '-') - { // it's a vector - Vector tmp; - UTIL_StringToRandomVector( (float *)tmp, szText ); - return tmp; - } - - CBaseEntity *pCalc = UTIL_FindEntityByTargetname(NULL, szText, pLocus); - - if (pCalc != NULL) - { - return pCalc->CalcPosition( pLocus ); - } - - ALERT(at_error, "%s \"%s\" has bad or missing calc_position value \"%s\"\n", STRING(pEntity->pev->classname), STRING(pEntity->pev->targetname), szText); - return g_vecZero; -} - -Vector CalcLocus_Velocity( CBaseEntity *pEntity, CBaseEntity *pLocus, const char *szText ) -{ - if ((*szText >= '0' && *szText <= '9') || *szText == '-') - { // it's a vector - Vector tmp; - UTIL_StringToRandomVector( (float *)tmp, szText ); - return tmp; - } - - CBaseEntity *pCalc = UTIL_FindEntityByTargetname(NULL, szText, pLocus); - - if (pCalc != NULL) - return pCalc->CalcVelocity( pLocus ); - - ALERT(at_error, "%s \"%s\" has bad or missing calc_velocity value \"%s\"\n", STRING(pEntity->pev->classname), STRING(pEntity->pev->targetname), szText); - return g_vecZero; -} - -float CalcLocus_Ratio( CBaseEntity *pLocus, const char *szText ) -{ - if ((*szText >= '0' && *szText <= '9') || *szText == '-') - { // assume it's a float - return atof( szText ); - } - - CBaseEntity *pCalc = UTIL_FindEntityByTargetname(NULL, szText, pLocus); - - if (pCalc != NULL) - return pCalc->CalcRatio( pLocus ); - - ALERT(at_error, "Bad or missing calc_ratio entity \"%s\"\n", szText); - return 0; // we need some signal for "fail". NaN, maybe? -} - -//============================================= -//locus_x effects -//============================================= - -// Entity variable -class CLocusAlias : public CBaseAlias -{ -public: - void PostSpawn( void ); - void Use( CBaseEntity *pActivator, CBaseEntity *pCaller, USE_TYPE useType, float value ); - CBaseEntity *FollowAlias( CBaseEntity *pFrom ); - void FlushChanges( void ); - - virtual int Save( CSave &save ); - virtual int Restore( CRestore &restore ); - static TYPEDESCRIPTION m_SaveData[]; - - EHANDLE m_hValue; - EHANDLE m_hChangeTo; -}; - -TYPEDESCRIPTION CLocusAlias::m_SaveData[] = -{ - DEFINE_FIELD( CLocusAlias, m_hValue, FIELD_EHANDLE), - DEFINE_FIELD( CLocusAlias, m_hChangeTo, FIELD_EHANDLE), -}; - -LINK_ENTITY_TO_CLASS( locus_alias, CLocusAlias ); -IMPLEMENT_SAVERESTORE( CLocusAlias, CBaseAlias ); - -void CLocusAlias::PostSpawn( void ) -{ - m_hValue = UTIL_FindEntityByTargetname( NULL, STRING(pev->netname) ); -} - -void CLocusAlias::Use( CBaseEntity *pActivator, CBaseEntity *pCaller, USE_TYPE useType, float value ) -{ - m_hChangeTo = pActivator; - UTIL_AddToAliasList( this ); -} - -void CLocusAlias::FlushChanges( void ) -{ - m_hValue = m_hChangeTo; - m_hChangeTo = NULL; -} - -CBaseEntity *CLocusAlias::FollowAlias( CBaseEntity *pFrom ) -{ - if (m_hValue == NULL) - return NULL; - else if ( pFrom == NULL || (OFFSET(m_hValue->pev) > OFFSET(pFrom->pev)) ) - { -// ALERT(at_console, "LocusAlias returns %s: %f %f %f\n", STRING(m_pValue->pev->targetname), m_pValue->pev->origin.x, m_pValue->pev->origin.y, m_pValue->pev->origin.z); - return m_hValue; - } - else - return NULL; -} - - - -// Beam maker -#define SF_LBEAM_SHADEIN 128 -#define SF_LBEAM_SHADEOUT 256 -#define SF_LBEAM_SOLID 512 -#define SF_LBEAM_SINE 1024 - -class CLocusBeam : public CPointEntity -{ -public: - void Spawn( void ); - void Precache( void ); - void Use( CBaseEntity *pActivator, CBaseEntity *pCaller, USE_TYPE useType, float value ); - - void KeyValue( KeyValueData *pkvd ); - virtual int Save( CSave &save ); - virtual int Restore( CRestore &restore ); - - static TYPEDESCRIPTION m_SaveData[]; - - int m_iszSprite; - int m_iszTargetName; - int m_iszStart; - int m_iszEnd; - int m_iWidth; - int m_iDistortion; - float m_fFrame; - int m_iScrollRate; - float m_fDuration; - float m_fDamage; - int m_iDamageType; - int m_iFlags; -}; - -TYPEDESCRIPTION CLocusBeam::m_SaveData[] = -{ - DEFINE_FIELD( CLocusBeam, m_iszSprite, FIELD_STRING), - DEFINE_FIELD( CLocusBeam, m_iszTargetName, FIELD_STRING), - DEFINE_FIELD( CLocusBeam, m_iszStart, FIELD_STRING), - DEFINE_FIELD( CLocusBeam, m_iszEnd, FIELD_STRING), - DEFINE_FIELD( CLocusBeam, m_iWidth, FIELD_INTEGER), - DEFINE_FIELD( CLocusBeam, m_iDistortion, FIELD_INTEGER), - DEFINE_FIELD( CLocusBeam, m_fFrame, FIELD_FLOAT), - DEFINE_FIELD( CLocusBeam, m_iScrollRate, FIELD_INTEGER), - DEFINE_FIELD( CLocusBeam, m_fDuration, FIELD_FLOAT), - DEFINE_FIELD( CLocusBeam, m_fDamage, FIELD_FLOAT), - DEFINE_FIELD( CLocusBeam, m_iDamageType, FIELD_INTEGER), - DEFINE_FIELD( CLocusBeam, m_iFlags, FIELD_INTEGER), -}; - -LINK_ENTITY_TO_CLASS( locus_beam, CLocusBeam ); -IMPLEMENT_SAVERESTORE(CLocusBeam,CPointEntity); - -void CLocusBeam :: KeyValue( KeyValueData *pkvd ) -{ - if (FStrEq(pkvd->szKeyName, "m_iszSprite")) - { - m_iszSprite = ALLOC_STRING(pkvd->szValue); - pkvd->fHandled = TRUE; - } - else if (FStrEq(pkvd->szKeyName, "m_iszTargetName")) - { - m_iszTargetName = ALLOC_STRING(pkvd->szValue); - pkvd->fHandled = TRUE; - } - else if (FStrEq(pkvd->szKeyName, "m_iszStart")) - { - m_iszStart = ALLOC_STRING(pkvd->szValue); - pkvd->fHandled = TRUE; - } - else if (FStrEq(pkvd->szKeyName, "m_iszEnd")) - { - m_iszEnd = ALLOC_STRING(pkvd->szValue); - pkvd->fHandled = TRUE; - } - else if (FStrEq(pkvd->szKeyName, "m_iWidth")) - { - m_iWidth = atoi(pkvd->szValue); - pkvd->fHandled = TRUE; - } - else if (FStrEq(pkvd->szKeyName, "m_iDistortion")) - { - m_iDistortion = atoi(pkvd->szValue); - pkvd->fHandled = TRUE; - } - else if (FStrEq(pkvd->szKeyName, "m_fFrame")) - { - m_fFrame = atof(pkvd->szValue); - pkvd->fHandled = TRUE; - } - else if (FStrEq(pkvd->szKeyName, "m_iScrollRate")) - { - m_iScrollRate = atoi(pkvd->szValue); - pkvd->fHandled = TRUE; - } - else if (FStrEq(pkvd->szKeyName, "m_fDuration")) - { - m_fDuration = atof(pkvd->szValue); - pkvd->fHandled = TRUE; - } - else if (FStrEq(pkvd->szKeyName, "m_fDamage")) - { - m_fDamage = atof(pkvd->szValue); - pkvd->fHandled = TRUE; - } - else if (FStrEq(pkvd->szKeyName, "m_iDamageType")) - { - m_iDamageType = atoi(pkvd->szValue); - pkvd->fHandled = TRUE; - } - else - CBaseEntity::KeyValue( pkvd ); -} - -void CLocusBeam :: Precache ( void ) -{ - PRECACHE_MODEL ( (char*)STRING(m_iszSprite) ); -} - -void CLocusBeam::Use( CBaseEntity *pActivator, CBaseEntity *pCaller, USE_TYPE useType, float value ) -{ - CBaseEntity *pStartEnt; - CBaseEntity *pEndEnt; - Vector vecStartPos; - Vector vecEndPos; - CBeam *pBeam; - - switch(pev->impulse) - { - case 0: // ents - pStartEnt = UTIL_FindEntityByTargetname(NULL, STRING(m_iszStart), pActivator); - pEndEnt = UTIL_FindEntityByTargetname(NULL, STRING(m_iszEnd), pActivator); - - if (pStartEnt == NULL || pEndEnt == NULL) - return; - pBeam = CBeam::BeamCreate( STRING(m_iszSprite), m_iWidth ); - pBeam->EntsInit( pStartEnt->edict(), pEndEnt->edict() ); - break; - - case 1: // pointent - vecStartPos = CalcLocus_Position( this, pActivator, STRING(m_iszStart) ); - pEndEnt = UTIL_FindEntityByTargetname(NULL, STRING(m_iszEnd), pActivator); - - if (pEndEnt == NULL) - return; - pBeam = CBeam::BeamCreate( STRING(m_iszSprite), m_iWidth ); - pBeam->PointEntInit( vecStartPos, pEndEnt->edict() ); - break; - case 2: // points - vecStartPos = CalcLocus_Position( this, pActivator, STRING(m_iszStart) ); - vecEndPos = CalcLocus_Position( this, pActivator, STRING(m_iszEnd) ); - - pBeam = CBeam::BeamCreate( STRING(m_iszSprite), m_iWidth ); - pBeam->PointsInit( vecStartPos, vecEndPos ); - break; - case 3: // point & offset - vecStartPos = CalcLocus_Position( this, pActivator, STRING(m_iszStart) ); - vecEndPos = CalcLocus_Velocity( this, pActivator, STRING(m_iszEnd) ); - - pBeam = CBeam::BeamCreate( STRING(m_iszSprite), m_iWidth ); - pBeam->PointsInit( vecStartPos, vecStartPos + vecEndPos ); - break; - } - pBeam->SetColor( pev->rendercolor.x, pev->rendercolor.y, pev->rendercolor.z ); - pBeam->SetBrightness( pev->renderamt ); - pBeam->SetNoise( m_iDistortion ); - pBeam->SetFrame( m_fFrame ); - pBeam->SetScrollRate( m_iScrollRate ); - pBeam->SetFlags( m_iFlags ); - pBeam->pev->dmg = m_fDamage; - pBeam->pev->frags = m_iDamageType; - pBeam->pev->spawnflags |= pev->spawnflags & (SF_BEAM_RING | - SF_BEAM_SPARKSTART | SF_BEAM_SPARKEND | SF_BEAM_DECALS); - if (m_fDuration) - { - pBeam->SetThink(&CBeam:: SUB_Remove ); - pBeam->SetNextThink( m_fDuration ); - } - pBeam->pev->targetname = m_iszTargetName; - - if (pev->target) - { - FireTargets( STRING(pev->target), pBeam, this, USE_TOGGLE, 0 ); - } -} - -void CLocusBeam::Spawn( void ) -{ - Precache(); - m_iFlags = 0; - if (pev->spawnflags & SF_LBEAM_SHADEIN) - m_iFlags |= FBEAM_SHADEIN; - if (pev->spawnflags & SF_LBEAM_SHADEOUT) - m_iFlags |= FBEAM_SHADEOUT; - if (pev->spawnflags & SF_LBEAM_SINE) - m_iFlags |= FBEAM_SINENOISE; - if (pev->spawnflags & SF_LBEAM_SOLID) - m_iFlags |= FBEAM_SOLID; -} - - -//============================================= -//calc_x entities -//============================================= - -class CCalcPosition : public CPointEntity -{ -public: - Vector CalcPosition( CBaseEntity *pLocus ); -}; - -LINK_ENTITY_TO_CLASS( calc_position, CCalcPosition ); - -Vector CCalcPosition::CalcPosition( CBaseEntity *pLocus ) -{ - CBaseEntity *pSubject = UTIL_FindEntityByTargetname(NULL, STRING(pev->netname), pLocus); - - Vector vecOffset = CalcLocus_Velocity( this, pLocus, STRING(pev->message)); - - if( FNullEnt( pSubject )) - return vecOffset; - - Vector vecPosition; - Vector vecJunk; - - Vector vecResult; - switch (pev->impulse) - { - case 1: // eyes - vecResult = vecOffset + pSubject->EyePosition(); - // ALERT(at_console, "calc_subpos returns %f %f %f\n", vecResult.x, vecResult.y, vecResult.z); - return vecResult; - // return vecOffset + pLocus->EyePosition(); - case 2: // top - return vecOffset + pSubject->pev->origin + Vector( - (pSubject->pev->mins.x + pSubject->pev->maxs.x)/2, - (pSubject->pev->mins.y + pSubject->pev->maxs.y)/2, - pSubject->pev->maxs.z - ); - case 3: // centre - return vecOffset + pSubject->pev->origin + Vector( - (pSubject->pev->mins.x + pSubject->pev->maxs.x)/2, - (pSubject->pev->mins.y + pSubject->pev->maxs.y)/2, - (pSubject->pev->mins.z + pSubject->pev->maxs.z)/2 - ); - case 4: // bottom - return vecOffset + pSubject->pev->origin + Vector( - (pSubject->pev->mins.x + pSubject->pev->maxs.x)/2, - (pSubject->pev->mins.y + pSubject->pev->maxs.y)/2, - pSubject->pev->mins.z - ); - case 5: - // this could cause problems. - // is there a good way to check whether it's really a CBaseAnimating? - ((CBaseAnimating*)pSubject)->GetAttachment( 0, vecPosition, vecJunk ); - return vecOffset + vecPosition; - case 6: - ((CBaseAnimating*)pSubject)->GetAttachment( 1, vecPosition, vecJunk ); - return vecOffset + vecPosition; - case 7: - ((CBaseAnimating*)pSubject)->GetAttachment( 2, vecPosition, vecJunk ); - return vecOffset + vecPosition; - case 8: - ((CBaseAnimating*)pSubject)->GetAttachment( 3, vecPosition, vecJunk ); - return vecOffset + vecPosition; - case 9: - return vecOffset + pSubject->pev->origin + Vector( - RANDOM_FLOAT(pSubject->pev->mins.x, pSubject->pev->maxs.x), - RANDOM_FLOAT(pSubject->pev->mins.y, pSubject->pev->maxs.y), - RANDOM_FLOAT(pSubject->pev->mins.z, pSubject->pev->maxs.z) - ); - default: - return vecOffset + pSubject->pev->origin; - } -} - -//======================================================= - -class CCalcRatio : public CPointEntity -{ -public: - float CalcRatio( CBaseEntity *pLocus ); -}; - -LINK_ENTITY_TO_CLASS( calc_ratio, CCalcRatio ); - -float CCalcRatio::CalcRatio( CBaseEntity *pLocus ) -{ - float fBasis = CalcLocus_Ratio( pLocus, STRING(pev->target)); - - switch (pev->impulse) - { - case 1: fBasis = 1-fBasis; break; //reversed - case 2: fBasis = -fBasis; break; //negative - case 3: fBasis = 1/fBasis; break; //reciprocal - } - - fBasis += CalcLocus_Ratio( pLocus, STRING(pev->netname)); - fBasis = fBasis * CalcLocus_Ratio( pLocus, STRING(pev->message)); - - if (!FStringNull(pev->noise)) - { - float fMin = CalcLocus_Ratio( pLocus, STRING(pev->noise)); - - if (!FStringNull(pev->noise1)) - { - float fMax = CalcLocus_Ratio( pLocus, STRING(pev->noise1)); - - if (fBasis >= fMin && fBasis <= fMax) - return fBasis; - switch ((int)pev->frags) - { - case 0: - if (fBasis < fMin) - return fMin; - else - return fMax; - case 1: - while (fBasis < fMin) - fBasis += fMax - fMin; - while (fBasis > fMax) - fBasis -= fMax - fMin; - return fBasis; - case 2: - while (fBasis < fMin || fBasis > fMax) - { - if (fBasis < fMin) - fBasis = fMin + fMax - fBasis; - else - fBasis = fMax + fMax - fBasis; - } - return fBasis; - } - } - - if (fBasis > fMin) - return fBasis; - else - return fMin; // crop to nearest value - } - else if (!FStringNull(pev->noise1)) - { - float fMax = CalcLocus_Ratio( pLocus, STRING(pev->noise1)); - - if (fBasis < fMax) - return fBasis; - else - return fMax; // crop to nearest value - } - else - return fBasis; -} - - //======================================================= -#define SF_CALCVELOCITY_NORMALIZE 1 -#define SF_CALCVELOCITY_SWAPZ 2 -class CCalcSubVelocity : public CPointEntity -{ - Vector Convert( CBaseEntity *pLocus, Vector vecVel ); - Vector ConvertAngles( CBaseEntity *pLocus, Vector vecAngles ); -public: - Vector CalcVelocity( CBaseEntity *pLocus ); -}; - -LINK_ENTITY_TO_CLASS( calc_subvelocity, CCalcSubVelocity ); - -Vector CCalcSubVelocity::CalcVelocity( CBaseEntity *pLocus ) -{ - pLocus = UTIL_FindEntityByTargetname( NULL, STRING(pev->netname), pLocus ); - - Vector vecAngles; - Vector vecJunk; - - switch (pev->impulse) - { - case 1: //angles - return ConvertAngles( pLocus, pLocus->pev->angles ); - case 2: //v_angle - return ConvertAngles( pLocus, pLocus->pev->v_angle ); - case 5: - // this could cause problems. - // is there a good way to check whether it's really a CBaseAnimating? - ((CBaseAnimating*)pLocus)->GetAttachment( 0, vecJunk, vecAngles ); - return ConvertAngles( pLocus, vecAngles ); - case 6: - ((CBaseAnimating*)pLocus)->GetAttachment( 1, vecJunk, vecAngles ); - return ConvertAngles( pLocus, vecAngles ); - case 7: - ((CBaseAnimating*)pLocus)->GetAttachment( 2, vecJunk, vecAngles ); - return ConvertAngles( pLocus, vecAngles ); - case 8: - ((CBaseAnimating*)pLocus)->GetAttachment( 3, vecJunk, vecAngles ); - return ConvertAngles( pLocus, vecAngles ); - default: - return Convert( pLocus, pLocus->pev->velocity ); - } -} - -Vector CCalcSubVelocity::Convert( CBaseEntity *pLocus, Vector vecDir ) -{ - if (pev->spawnflags & SF_CALCVELOCITY_NORMALIZE) - vecDir = vecDir.Normalize(); - - float fRatio = CalcLocus_Ratio( pLocus, STRING(pev->noise) ); - Vector vecOffset = CalcLocus_Velocity( this, pLocus, STRING(pev->message)); - - Vector vecResult = vecOffset + (vecDir*fRatio); - - if (pev->spawnflags & SF_CALCVELOCITY_SWAPZ) - vecResult.z = -vecResult.z; -// ALERT(at_console, "calc_subvel returns (%f %f %f) = (%f %f %f) + ((%f %f %f) * %f)\n", vecResult.x, vecResult.y, vecResult.z, vecOffset.x, vecOffset.y, vecOffset.z, vecDir.x, vecDir.y, vecDir.z, fRatio); - return vecResult; -} - -Vector CCalcSubVelocity::ConvertAngles( CBaseEntity *pLocus, Vector vecAngles ) -{ - UTIL_MakeVectors( vecAngles ); - return Convert( pLocus, gpGlobals->v_forward ); -} - - -//======================================================= -class CCalcVelocityPath : public CPointEntity -{ -public: - Vector CalcVelocity( CBaseEntity *pLocus ); -}; - -LINK_ENTITY_TO_CLASS( calc_velocity_path, CCalcVelocityPath ); - -Vector CCalcVelocityPath::CalcVelocity( CBaseEntity *pLocus ) -{ - Vector vecStart = CalcLocus_Position( this, pLocus, STRING(pev->target) ); -// ALERT(at_console, "vecStart %f %f %f\n", vecStart.x, vecStart.y, vecStart.z); - Vector vecOffs; - float fFactor = CalcLocus_Ratio( pLocus, STRING(pev->noise) ); - - switch ((int)pev->armorvalue) - { - case 0: - vecOffs = CalcLocus_Position( this, pLocus, STRING(pev->netname) ) - vecStart; - break; - case 1: - vecOffs = CalcLocus_Velocity( this, pLocus, STRING(pev->netname) ); - break; - } -// ALERT(at_console, "vecOffs %f %f %f\n", vecOffs.x, vecOffs.y, vecOffs.z); - - if (pev->health) - { - float len = vecOffs.Length(); - switch ((int)pev->health) - { - case 1: - vecOffs = vecOffs/len; - break; - case 2: - vecOffs = vecOffs/(len*len); - break; - case 3: - vecOffs = vecOffs/(len*len*len); - break; - case 4: - vecOffs = vecOffs*len; - break; - } - } - - vecOffs = vecOffs * fFactor; - - if (pev->frags) - { - TraceResult tr; - IGNORE_GLASS iIgnoreGlass = ignore_glass; - IGNORE_MONSTERS iIgnoreMonsters = ignore_monsters; - - switch ((int)pev->frags) - { - case 2: - iIgnoreGlass = dont_ignore_glass; - break; - case 4: - iIgnoreGlass = dont_ignore_glass; - // fall through - case 3: - iIgnoreMonsters = dont_ignore_monsters; - break; - } - - UTIL_TraceLine( vecStart, vecStart+vecOffs, iIgnoreMonsters, iIgnoreGlass, NULL, &tr ); - vecOffs = tr.vecEndPos - vecStart; - } - -// ALERT(at_console, "path: %f %f %f\n", vecOffs.x, vecOffs.y, vecOffs.z); - return vecOffs; -} - - -//======================================================= -class CCalcVelocityPolar : public CPointEntity -{ -public: - Vector CalcVelocity( CBaseEntity *pLocus ); -}; - -LINK_ENTITY_TO_CLASS( calc_velocity_polar, CCalcVelocityPolar ); - -Vector CCalcVelocityPolar::CalcVelocity( CBaseEntity *pLocus ) -{ - Vector vecBasis = CalcLocus_Velocity( this, pLocus, STRING(pev->netname) ); - Vector vecAngles = UTIL_VecToAngles( vecBasis ) + pev->angles; - Vector vecOffset = CalcLocus_Velocity( this, pLocus, STRING(pev->message) ); - - float fFactor = CalcLocus_Ratio( pLocus, STRING(pev->noise) ); - - if (!(pev->spawnflags & SF_CALCVELOCITY_NORMALIZE)) - fFactor = fFactor * vecBasis.Length(); - - UTIL_MakeVectors( vecAngles ); - return (gpGlobals->v_forward * fFactor) + vecOffset; -} - -//======================================================= - -// Position marker -class CMark : public CPointEntity -{ -public: - Vector CalcVelocity(CBaseEntity *pLocus) { return pev->movedir; } - float CalcRatio(CBaseEntity *pLocus) { return pev->frags; } - void Think( void ) { SUB_Remove(); } -}; - -class CLocusVariable : public CPointEntity -{ -public: - void Spawn( void ); - void Use( CBaseEntity *pActivator, CBaseEntity *pCaller, USE_TYPE useType, float value ); - Vector CalcVelocity(CBaseEntity *pLocus) { return pev->movedir; } - float CalcRatio(CBaseEntity *pLocus) { return pev->frags; } - - void KeyValue( KeyValueData *pkvd ); - virtual int Save( CSave &save ); - virtual int Restore( CRestore &restore ); - - static TYPEDESCRIPTION m_SaveData[]; - - int m_iszPosition; - int m_iszVelocity; - int m_iszRatio; - int m_iszTargetName; - int m_iszFireOnSpawn; - float m_fDuration; -}; - -TYPEDESCRIPTION CLocusVariable::m_SaveData[] = -{ - DEFINE_FIELD( CLocusVariable, m_iszPosition, FIELD_STRING), - DEFINE_FIELD( CLocusVariable, m_iszVelocity, FIELD_STRING), - DEFINE_FIELD( CLocusVariable, m_iszRatio, FIELD_STRING), - DEFINE_FIELD( CLocusVariable, m_iszTargetName, FIELD_STRING), - DEFINE_FIELD( CLocusVariable, m_iszFireOnSpawn, FIELD_STRING), - DEFINE_FIELD( CLocusVariable, m_fDuration, FIELD_FLOAT), -}; - -IMPLEMENT_SAVERESTORE( CLocusVariable, CPointEntity ); -LINK_ENTITY_TO_CLASS( locus_variable, CLocusVariable ); - -void CLocusVariable :: KeyValue( KeyValueData *pkvd ) -{ - if (FStrEq(pkvd->szKeyName, "m_iszPosition")) - { - m_iszPosition = ALLOC_STRING(pkvd->szValue); - pkvd->fHandled = TRUE; - } - else if (FStrEq(pkvd->szKeyName, "m_iszVelocity")) - { - m_iszVelocity = ALLOC_STRING(pkvd->szValue); - pkvd->fHandled = TRUE; - } - else if (FStrEq(pkvd->szKeyName, "m_iszRatio")) - { - m_iszRatio = ALLOC_STRING(pkvd->szValue); - pkvd->fHandled = TRUE; - } - else if (FStrEq(pkvd->szKeyName, "m_iszTargetName")) - { - m_iszTargetName = ALLOC_STRING(pkvd->szValue); - pkvd->fHandled = TRUE; - } - else if (FStrEq(pkvd->szKeyName, "m_iszFireOnSpawn")) - { - m_iszFireOnSpawn = ALLOC_STRING(pkvd->szValue); - pkvd->fHandled = TRUE; - } - else if (FStrEq(pkvd->szKeyName, "m_fDuration")) - { - m_fDuration = atof(pkvd->szValue); - pkvd->fHandled = TRUE; - } - else - CPointEntity::KeyValue( pkvd ); -} - -void CLocusVariable::Spawn( void ) -{ - SetMovedir(pev); -} - -void CLocusVariable::Use( CBaseEntity *pActivator, CBaseEntity *pCaller, USE_TYPE useType, float value ) -{ - Vector vecPos = g_vecZero; - Vector vecDir = g_vecZero; - float fRatio = 0; - if (m_iszPosition) - vecPos = CalcLocus_Position(this, pActivator, STRING(m_iszPosition)); - if (m_iszVelocity) - vecDir = CalcLocus_Velocity(this, pActivator, STRING(m_iszVelocity)); - if (m_iszRatio) - fRatio = CalcLocus_Ratio(pActivator, STRING(m_iszRatio)); - - if (m_iszTargetName) - { - CMark *pMark = GetClassPtr( (CMark*)NULL ); - pMark->pev->classname = MAKE_STRING("mark"); - pMark->pev->origin = vecPos; - pMark->pev->movedir = vecDir; - pMark->pev->frags = fRatio; - pMark->pev->targetname = m_iszTargetName; - pMark->SetNextThink(m_fDuration); - - FireTargets(STRING(m_iszFireOnSpawn), pMark, this, USE_TOGGLE, 0); - } - else - { - pev->origin = vecPos; - pev->movedir = vecDir; - pev->frags = fRatio; - - FireTargets(STRING(m_iszFireOnSpawn), this, this, USE_TOGGLE, 0); - } -} diff --git a/spirit/locus.h b/spirit/locus.h deleted file mode 100644 index a0df46f0..00000000 --- a/spirit/locus.h +++ /dev/null @@ -1,3 +0,0 @@ -Vector CalcLocus_Position ( CBaseEntity *pEntity, CBaseEntity *pLocus, const char *szText ); -Vector CalcLocus_Velocity ( CBaseEntity *pEntity, CBaseEntity *pLocus, const char *szText ); -float CalcLocus_Ratio ( CBaseEntity *pLocus, const char *szText); \ No newline at end of file diff --git a/spirit/movewith.cpp b/spirit/movewith.cpp deleted file mode 100644 index 873147f2..00000000 --- a/spirit/movewith.cpp +++ /dev/null @@ -1,684 +0,0 @@ -#include "extdll.h" -#include "util.h" -#include "cbase.h" -#include "movewith.h" -#include "saverestore.h" -#include "player.h" - -CWorld *g_pWorld = NULL; //LRC - -BOOL g_doingDesired = FALSE; //LRC - marks whether the Desired functions are currently -BOOL NeedUpdate( CBaseEntity *pEnt ); //synchronization may be lost (e.g. after changelevel) merge it. // being processed. - -void UTIL_AddToAssistList( CBaseEntity *pEnt ) -{ -// ALERT(at_console, "Add %s \"%s\" to AssistList\n", STRING(pEnt->pev->classname), STRING(pEnt->pev->targetname)); - - if (pEnt->m_pAssistLink) - { -// ALERT(at_console, "Ignored AddToAssistList for %s \"%s\"\n", STRING(pEnt->pev->classname), STRING(pEnt->pev->targetname)); - return; // is pEnt already in the body of the list? - } - - if ( !g_pWorld ) - { - ALERT(at_debug, "AddToAssistList %s \"%s\" has no AssistList!\n", STRING(pEnt->pev->classname), STRING(pEnt->pev->targetname)); - return; - } - - CBaseEntity *pListMember = g_pWorld; - - // find the last entry in the list... - while (pListMember->m_pAssistLink != NULL) - pListMember = pListMember->m_pAssistLink; - - if (pListMember == pEnt) - { -// ALERT(at_console, "(end)Ignored AddToAssistList for %s \"%s\"\n", STRING(pEnt->pev->classname), STRING(pEnt->pev->targetname)); - return; // pEnt is the last entry in the list. - } - - pListMember->m_pAssistLink = pEnt; // it's not in the list already, so add pEnt to the list. - -// int count = 0; -// for (pListMember = g_pWorld->m_pAssistLink; pListMember; pListMember = pListMember->m_pAssistLink) -// { -// count++; -// } -// ALERT(at_console, "Added %s \"%s\" to AssistList, length now %d\n", STRING(pEnt->pev->classname), STRING(pEnt->pev->targetname), count); -} - -void HandlePostAssist( CBaseEntity *pEnt ) -{ - if (pEnt->m_iLFlags & LF_POSTASSISTVEL) - { -// ALERT(at_console, "RestoreVel %s: orign %f %f %f, velocity was %f %f %f, back to %f %f %f\n", -// STRING(pEnt->pev->targetname), -// pEnt->pev->origin.x, pEnt->pev->origin.y, pEnt->pev->origin.z, -// pEnt->pev->velocity.x, pEnt->pev->velocity.y, pEnt->pev->velocity.z, -// pEnt->m_vecPostAssistVel.x, pEnt->m_vecPostAssistVel.y, pEnt->m_vecPostAssistVel.z -// ); - pEnt->pev->velocity = pEnt->m_vecPostAssistVel; - pEnt->m_vecPostAssistVel = g_vecZero; - pEnt->m_iLFlags &= ~LF_POSTASSISTVEL; - } - if (pEnt->m_iLFlags & LF_POSTASSISTAVEL) - { -// ALERT(at_console, "RestoreVel %s: orign %f %f %f, velocity was %f %f %f, back to %f %f %f\n", -// STRING(pEnt->pev->targetname), -// pEnt->pev->origin.x, pEnt->pev->origin.y, pEnt->pev->origin.z, -// pEnt->pev->velocity.x, pEnt->pev->velocity.y, pEnt->pev->velocity.z, -// pEnt->m_vecPostAssistVel.x, pEnt->m_vecPostAssistVel.y, pEnt->m_vecPostAssistVel.z -// ); - pEnt->pev->avelocity = pEnt->m_vecPostAssistAVel; - pEnt->m_vecPostAssistAVel = g_vecZero; - pEnt->m_iLFlags &= ~LF_POSTASSISTAVEL; - } - CBaseEntity *pChild; - for (pChild = pEnt->m_pChildMoveWith; pChild != NULL; pChild = pChild->m_pSiblingMoveWith) - HandlePostAssist( pChild ); -} - -// returns 0 if no desired settings needed, else returns 1 -int ApplyDesiredSettings( CBaseEntity *pListMember ) -{ - if (pListMember->m_iLFlags & LF_DODESIRED) - { - //ALERT(at_console, "Apply for %s\n", STRING(pListMember->pev->classname)); - pListMember->m_iLFlags &= ~LF_DODESIRED; - } - else - { - // don't need to apply any desired settings for this entity. - //ALERT(at_console, "Not apply for %s\n", STRING(pListMember->pev->classname)); - return 0; - } -// ALERT(at_console, "ApplyDesiredSettings for %s \"%s\", pevnt %f, ltime %f, mfnt %f, mpevnt %f, %f\n", STRING(pListMember->pev->classname), STRING(pListMember->pev->targetname), pListMember->pev->nextthink, pListMember->pev->ltime, pListMember->m_fNextThink, pListMember->m_fPevNextThink, pListMember->pev->origin.x); - - if (pListMember->m_iLFlags & LF_DESIRED_ACTION) - { - pListMember->m_iLFlags &= ~LF_DESIRED_ACTION; - pListMember->DesiredAction(); - if(NeedUpdate(pListMember)) SetBits(pListMember->m_pChildMoveWith->m_iLFlags, LF_MERGEPOS); - } - if (pListMember->m_iLFlags & (LF_POSTORG|LF_MERGEPOS)) - { - UTIL_MergePos( pListMember ); - } - if (pListMember->m_iLFlags & LF_DESIRED_INFO) - { - pListMember->m_iLFlags &= ~LF_DESIRED_INFO; - ALERT(at_debug, "DesiredInfo: pos %f %f %f, vel %f %f %f. Child pos %f %f %f, vel %f %f %f\n\n", pListMember->pev->origin.x, pListMember->pev->origin.y, pListMember->pev->origin.z, pListMember->pev->velocity.x, pListMember->pev->velocity.y, pListMember->pev->velocity.z, pListMember->m_pChildMoveWith->pev->origin.x, pListMember->m_pChildMoveWith->pev->origin.y, pListMember->m_pChildMoveWith->pev->origin.z, pListMember->m_pChildMoveWith->pev->velocity.x, pListMember->m_pChildMoveWith->pev->velocity.y, pListMember->m_pChildMoveWith->pev->velocity.z); - } - if (pListMember->m_iLFlags & LF_DESIRED_POSTASSIST) - { - pListMember->m_iLFlags &= ~LF_DESIRED_POSTASSIST; - HandlePostAssist( pListMember ); - } - if (pListMember->m_iLFlags & LF_DESIRED_THINK) - { - pListMember->m_iLFlags &= ~LF_DESIRED_THINK; - //ALERT(at_console, "DesiredThink %s\n", STRING(pListMember->pev->targetname)); - pListMember->Think(); - } - - return 1; -} - -void AssistChildren( CBaseEntity *pEnt, Vector vecAdjustVel, Vector vecAdjustAVel ) -{ - CBaseEntity *pChild; - for(pChild = pEnt->m_pChildMoveWith; pChild != NULL; pChild = pChild->m_pSiblingMoveWith) - { - if (!(pChild->m_iLFlags & LF_POSTASSISTVEL)) - { - pChild->m_vecPostAssistVel = pChild->pev->velocity; - pChild->m_iLFlags |= LF_POSTASSISTVEL; - } - if (!(pChild->m_iLFlags & LF_POSTASSISTAVEL)) - { - pChild->m_vecPostAssistAVel = pChild->pev->avelocity; - pChild->m_iLFlags |= LF_POSTASSISTAVEL; - } - pChild->pev->velocity = pChild->pev->velocity - vecAdjustVel;// (pChild->pev->velocity - pEnt->m_vecPostAssistVel) + pEnt->m_vecPostAssistVel*fFraction; - pChild->pev->avelocity = pChild->pev->avelocity - vecAdjustAVel;// (pChild->pev->avelocity - pEnt->m_vecPostAssistAVel) + pEnt->m_vecPostAssistAVel*fFraction; - - //ALERT(at_console, "AssistChild %s: origin %f %f %f, old vel %f %f %f. fraction %f, new vel %f %f %f, dest %f %f %f\n", STRING(pChild->pev->targetname), pChild->pev->origin.x, pChild->pev->origin.y, pChild->pev->origin.z, pChild->m_vecPostAssistVel.x, pChild->m_vecPostAssistVel.y, pChild->m_vecPostAssistVel.z, fFraction, pChild->pev->velocity.x, pChild->pev->velocity.y, pChild->pev->velocity.z, pChild->pev->origin.x + pChild->pev->velocity.x*gpGlobals->frametime, pChild->pev->origin.y + pChild->pev->velocity.y*gpGlobals->frametime, pChild->pev->origin.z + pChild->pev->velocity.z*gpGlobals->frametime ); - //ALERT(at_console, "AssistChild %s: origin %f %f %f. velocity was %f %f %f, now %f %f %f\n", STRING(pChild->pev->targetname), pChild->pev->origin.x, pChild->pev->origin.y, pChild->pev->origin.z, pChild->m_vecPostAssistVel.x, pChild->m_vecPostAssistVel.y, pChild->m_vecPostAssistVel.z, pChild->pev->velocity.x, pChild->pev->velocity.y, pChild->pev->velocity.z); - - AssistChildren( pChild, vecAdjustVel, vecAdjustAVel ); - } -} - -// pEnt is a PUSH entity. Before physics is applied in a frame, we need to assist: -// 1) this entity, if it will stop moving this turn but its parent will continue. -// 2) this entity's PUSH children, if 1) was applied. -// 3) this entity's non-PUSH children, if this entity will stop moving this turn. -// -// returns 0 if the entity wasn't flagged for DoAssist. -int TryAssistEntity( CBaseEntity *pEnt ) -{ - if (gpGlobals->frametime == 0) - { -// ALERT(at_console, "frametime 0, don't assist\n"); - return 0; - } - - // not flagged as needing assistance? - //if (!(pEnt->m_iLFlags & LF_DOASSIST)) return 0; - -// ALERT(at_console, "AssistList: %s\n", STRING(pEnt->pev->classname)); - - if (pEnt->m_fNextThink <= 0) - { -// ALERT(at_console, "Cancelling assist for %s, %f\n", STRING(pEnt->pev->targetname), pEnt->pev->origin.x); - pEnt->m_iLFlags &= ~LF_DOASSIST; - return 0; // the think has been cancelled. Oh well... - } - -// ALERT(at_console, "Assist, mfNT %f, pevNT %f\n", pEnt->m_fNextThink, pEnt->pev->nextthink); -// pEnt->ThinkCorrection(); - - // a fraction of the current velocity. (the part of it that the engine should see.) - float fFraction = 0; - - // is this the frame when the entity will stop? - if (pEnt->pev->movetype == MOVETYPE_PUSH) - { - if (pEnt->m_fNextThink <= pEnt->pev->ltime + gpGlobals->frametime) - fFraction = (pEnt->m_fNextThink - pEnt->pev->ltime)/gpGlobals->frametime; - } - else if (pEnt->m_fNextThink <= gpGlobals->time + gpGlobals->frametime) - { - fFraction = (pEnt->m_fNextThink - gpGlobals->time)/gpGlobals->frametime; -// ALERT(at_console, "Setting fFraction\n"); - } - - if (fFraction) - { -// ALERT(at_console, "Assisting %s \"%s\", %f <= %f + %f\n", STRING(pEnt->pev->classname), STRING(pEnt->pev->targetname), pEnt->m_fNextThink, pEnt->pev->ltime, gpGlobals->frametime); - - if (pEnt->m_iLFlags & LF_CORRECTSPEED) - { - if (!(pEnt->m_iLFlags & LF_POSTASSISTVEL)) - { - pEnt->m_vecPostAssistVel = pEnt->pev->velocity; - pEnt->m_iLFlags |= LF_POSTASSISTVEL; - } - - if (!(pEnt->m_iLFlags & LF_POSTASSISTAVEL)) - { - pEnt->m_vecPostAssistAVel = pEnt->pev->avelocity; - pEnt->m_iLFlags |= LF_POSTASSISTAVEL; - } - - Vector vecVelTemp = pEnt->pev->velocity; - Vector vecAVelTemp = pEnt->pev->avelocity; - - if (pEnt->m_pMoveWith) - { - pEnt->pev->velocity = (pEnt->pev->velocity - pEnt->m_pMoveWith->pev->velocity)*fFraction + pEnt->m_pMoveWith->pev->velocity; - pEnt->pev->avelocity = (pEnt->pev->avelocity - pEnt->m_pMoveWith->pev->avelocity)*fFraction + pEnt->m_pMoveWith->pev->avelocity; - } - else - { - pEnt->pev->velocity = pEnt->pev->velocity*fFraction; - pEnt->pev->avelocity = pEnt->pev->avelocity*fFraction; - } - - //ALERT(at_console, "Assist %s: origin %f %f %f, old vel %f %f %f. fraction %f, new vel %f %f %f, dest %f %f %f\n", STRING(pEnt->pev->targetname), pEnt->pev->origin.x, pEnt->pev->origin.y, pEnt->pev->origin.z, pEnt->m_vecPostAssistVel.x, pEnt->m_vecPostAssistVel.y, pEnt->m_vecPostAssistVel.z, fFraction, pEnt->pev->velocity.x, pEnt->pev->velocity.y, pEnt->pev->velocity.z, pEnt->pev->origin.x + pEnt->pev->velocity.x*gpGlobals->frametime, pEnt->pev->origin.y + pEnt->pev->velocity.y*gpGlobals->frametime, pEnt->pev->origin.z + pEnt->pev->velocity.z*gpGlobals->frametime); - - AssistChildren( pEnt, vecVelTemp - pEnt->pev->velocity, vecAVelTemp - pEnt->pev->avelocity ); - UTIL_DesiredPostAssist( pEnt ); - } - - UTIL_DesiredThink( pEnt ); -// ALERT(at_console, "Assist sets DesiredThink for %s\n", STRING(pEnt->pev->classname)); - - pEnt->m_iLFlags &= ~LF_DOASSIST; - } - return 1; -} - -// called every frame, by StartFrame -void CheckAssistList( void ) -{ - CBaseEntity *pListMember; - - if ( !g_pWorld ) - { - ALERT(at_error, "CheckAssistList has no AssistList!\n"); - return; - } - - int count = 0; - - for (pListMember = g_pWorld; pListMember; pListMember = pListMember->m_pAssistLink) - { - if (pListMember->m_iLFlags & LF_DOASSIST) - count++; - } - - count = 0; - pListMember = g_pWorld; - - while ( pListMember->m_pAssistLink ) // handle the remaining entries in the list - { - TryAssistEntity(pListMember->m_pAssistLink); - if (!(pListMember->m_pAssistLink->m_iLFlags & LF_ASSISTLIST)) - { - CBaseEntity *pTemp = pListMember->m_pAssistLink; - pListMember->m_pAssistLink = pListMember->m_pAssistLink->m_pAssistLink; - pTemp->m_pAssistLink = NULL; - } - else pListMember = pListMember->m_pAssistLink; - } -} - -// called every frame, by PostThink -void CheckDesiredList( void ) -{ - CBaseEntity *pListMember; - int loopbreaker = 1024; //max edicts - - if (g_doingDesired) ALERT(at_debug, "CheckDesiredList: doingDesired is already set!?\n"); - g_doingDesired = TRUE; - - if (!g_pWorld) - { - ALERT(at_console, "CheckDesiredList has no AssistList!\n"); - return; - } - - pListMember = g_pWorld; - CBaseEntity *pNext; - pListMember = g_pWorld->m_pAssistLink; - - while (pListMember) - { - // cache this, in case ApplyDesiredSettings does a SUB_Remove. - pNext = pListMember->m_pAssistLink; - ApplyDesiredSettings( pListMember ); - pListMember = pNext; - loopbreaker--; - if (loopbreaker <= 0) - { - ALERT(at_error, "Infinite(?) loop in DesiredList!"); - break; - } - } - - g_doingDesired = FALSE; -} - - - -void UTIL_MarkForAssist( CBaseEntity *pEnt, BOOL correctSpeed ) -{ - pEnt->m_iLFlags |= LF_DOASSIST; - - if (correctSpeed) - pEnt->m_iLFlags |= LF_CORRECTSPEED; - else - pEnt->m_iLFlags &= ~LF_CORRECTSPEED; - - UTIL_AddToAssistList( pEnt ); -} - -void UTIL_MarkForDesired ( CBaseEntity *pEnt ) -{ - pEnt->m_iLFlags |= LF_DODESIRED; - - if (g_doingDesired) - { - //ALERT(at_console, "doingDesired is true, start immediately\n"); - ApplyDesiredSettings( pEnt ); - return; - } - - UTIL_AddToAssistList( pEnt ); -} - -void UTIL_DesiredAction ( CBaseEntity *pEnt ) -{ - pEnt->m_iLFlags |= LF_DESIRED_ACTION; - UTIL_MarkForDesired( pEnt ); -} - -void UTIL_DesiredInfo ( CBaseEntity *pEnt ) -{ - pEnt->m_iLFlags |= LF_DESIRED_INFO; - UTIL_MarkForDesired( pEnt ); -} - -void UTIL_DesiredPostAssist ( CBaseEntity *pEnt ) -{ - pEnt->m_iLFlags |= LF_DESIRED_POSTASSIST; - UTIL_MarkForDesired( pEnt ); -} - -void UTIL_DesiredThink ( CBaseEntity *pEnt ) -{ -// ALERT(at_console, "Setting DesiredThink %s\n", STRING(pEnt->pev->targetname)); - pEnt->m_iLFlags |= LF_DESIRED_THINK; - pEnt->DontThink(); - UTIL_MarkForDesired( pEnt ); -} - - - -// LRC- change the origin to the given position, and bring any movewiths along too. -void UTIL_AssignOrigin( CBaseEntity *pEntity, const Vector vecOrigin ) -{ - UTIL_AssignOrigin( pEntity, vecOrigin, TRUE); -} - -// LRC- bInitiator is true if this is being called directly, rather than because pEntity is moving with something else. -void UTIL_AssignOrigin( CBaseEntity *pEntity, const Vector vecOrigin, BOOL bInitiator) -{ - Vector vecDiff = vecOrigin - pEntity->pev->origin; - - UTIL_SetOrigin(pEntity, vecOrigin ); - - if (bInitiator && pEntity->m_pMoveWith) - { - pEntity->m_vecOffsetOrigin = pEntity->pev->origin - pEntity->m_pMoveWith->pev->origin; - } - if (pEntity->m_pChildMoveWith) // now I've moved pEntity, does anything else have to move with it? - { - CBaseEntity* pChild = pEntity->m_pChildMoveWith; - - Vector vecTemp; - while (pChild) - { - if (pChild->pev->movetype != MOVETYPE_PUSH || pChild->pev->velocity == pEntity->pev->velocity) // if the child isn't moving under its own power - { - UTIL_AssignOrigin( pChild, vecOrigin + pChild->m_vecOffsetOrigin, FALSE ); - } - else - { - vecTemp = vecDiff + pChild->pev->origin; - UTIL_AssignOrigin( pChild, vecTemp, FALSE ); - } - pChild = pChild->m_pSiblingMoveWith; - } - } -} - -void UTIL_AssignAngles( CBaseEntity *pEntity, const Vector vecAngles ) -{ - UTIL_AssignAngles( pEntity, vecAngles, TRUE ); -} - -void UTIL_AssignAngles( CBaseEntity *pEntity, const Vector vecAngles, BOOL bInitiator) -{ - Vector vecDiff = vecAngles - pEntity->pev->angles; - if (vecDiff.Length() > 0.01 && CVAR_GET_FLOAT("sohl_mwdebug")) - ALERT(at_debug,"SetAngles %s %s: (%f %f %f) goes to (%f %f %f)\n",STRING(pEntity->pev->classname), STRING(pEntity->pev->targetname), pEntity->pev->angles.x, pEntity->pev->angles.y, pEntity->pev->angles.z, vecAngles.x, vecAngles.y, vecAngles.z); - -// UTIL_SetDesiredAngles(pEntity, vecAngles); - UTIL_SetAngles(pEntity, vecAngles); - - if (bInitiator && pEntity->m_pMoveWith) - { - pEntity->m_vecOffsetAngles = vecAngles - pEntity->m_pMoveWith->pev->angles; - } - if (pEntity->m_pChildMoveWith) // now I've moved pEntity, does anything else have to move with it? - { - CBaseEntity* pChild = pEntity->m_pChildMoveWith; - Vector vecTemp; - while (pChild) - { - if (pChild->pev->avelocity == pEntity->pev->avelocity) // if the child isn't turning under its own power - { - UTIL_AssignAngles( pChild, vecAngles + pChild->m_vecOffsetAngles, FALSE ); - } - else - { - vecTemp = vecDiff + pChild->pev->angles; - UTIL_AssignAngles( pChild, vecTemp, FALSE ); - } - //ALERT(at_console," child origin becomes (%f %f %f)\n",pChild->pev->origin.x,pChild->pev->origin.y,pChild->pev->origin.z); - //ALERT(at_console,"ent %p has sibling %p\n",pChild,pChild->m_pSiblingMoveWith); - pChild = pChild->m_pSiblingMoveWith; - } - } -} - -//LRC- an arbitrary limit. If this number is exceeded we assume there's an infinite loop, and abort. -#define MAX_MOVEWITH_DEPTH 100 - -//LRC- for use in supporting movewith. Tell the entity that whatever it's moving with is about to change velocity. -// loopbreaker is there to prevent the game from hanging... -void UTIL_SetMoveWithVelocity ( CBaseEntity *pEnt, const Vector vecSet, int loopbreaker ) -{ - if (loopbreaker <= 0) - { - ALERT(at_error, "Infinite child list for MoveWith!"); - return; - } - - if (!pEnt->m_pMoveWith) - { - ALERT(at_error,"SetMoveWithVelocity: no MoveWith entity!?\n"); - return; - } - - Vector vecNew; -// if (pEnt->pev->movetype == MOVETYPE_PUSH) -// { -// float fFraction = - vecNew = (pEnt->pev->velocity - pEnt->m_pMoveWith->pev->velocity) + vecSet; -// } -// else -// vecNew = vecSet; // lazy assumption: non-push entities don't move on their own. - -// ALERT(at_console,"SetMoveWithVelocity: %s changes to (%f %f %f) (based on %f %f %f)\n", -// STRING(pEnt->pev->classname), vecNew.x, vecNew.y, vecNew.z, -// pEnt->m_pMoveWith->pev->velocity.x, pEnt->m_pMoveWith->pev->velocity.y, pEnt->m_pMoveWith->pev->velocity.z -// ); - -// ALERT(at_console,"SMWV: %s is sent (%f,%f,%f) - goes from (%f,%f,%f) to (%f,%f,%f)\n", -// STRING(pEnt->pev->targetname), vecSet.x, vecSet.y, vecSet.z, -// pEnt->pev->velocity.x, pEnt->pev->velocity.y, pEnt->pev->velocity.z, -// vecNew.x, vecNew.y, vecNew.z -// ); - - //LRC- velocity is ignored on entities that aren't thinking! (Aargh...) -// if (fThinkTime) -// { -// UTIL_DesiredNextThink( pEnt, fThinkTime ); -// } -// else if (pEnt->pev->nextthink < 1) -// { -// UTIL_DesiredNextThink( pEnt, 1E6 ); -// //pEnt->pev->nextthink = gpGlobals->time + 1E6; -// } - - if ( pEnt->m_pChildMoveWith ) - { - CBaseEntity *pMoving = pEnt->m_pChildMoveWith; - int sloopbreaker = MAX_MOVEWITH_DEPTH; // to prevent the game hanging... - while (pMoving) - { - UTIL_SetMoveWithVelocity(pMoving, vecNew, loopbreaker - 1 ); - pMoving = pMoving->m_pSiblingMoveWith; - sloopbreaker--; - if (sloopbreaker <= 0) - { - ALERT(at_error, "SetMoveWithVelocity: Infinite sibling list for MoveWith!"); - break; - } - } - } - -// if (bImmediate) - pEnt->pev->velocity = vecNew; -// else -// UTIL_SetDesiredVel(pEnt, vecNew); -} - -//LRC -void UTIL_SetVelocity ( CBaseEntity *pEnt, const Vector vecSet ) -{ - Vector vecNew; - if (pEnt->m_pMoveWith) - vecNew = vecSet + pEnt->m_pMoveWith->pev->velocity; - else - vecNew = vecSet; - -// ALERT(at_console,"SetV: %s is sent (%f,%f,%f) - goes from (%f,%f,%f) to (%f,%f,%f)\n", -// STRING(pEnt->pev->targetname), vecSet.x, vecSet.y, vecSet.z, -// pEnt->pev->velocity.x, pEnt->pev->velocity.y, pEnt->pev->velocity.z, -// vecNew.x, vecNew.y, vecNew.z -// ); - - if ( pEnt->m_pChildMoveWith ) - { - CBaseEntity *pMoving = pEnt->m_pChildMoveWith; - int sloopbreaker = MAX_MOVEWITH_DEPTH; // LRC - to save us from infinite loops - while (pMoving) - { - UTIL_SetMoveWithVelocity(pMoving, vecNew, MAX_MOVEWITH_DEPTH ); - if(vecSet != g_vecZero)SetBits(pMoving->m_iLFlags, LF_PARENTMOVE); - else ClearBits(pMoving->m_iLFlags, LF_PARENTMOVE); - pMoving = pMoving->m_pSiblingMoveWith; - sloopbreaker--; - if (sloopbreaker <= 0) - { - ALERT(at_error, "SetVelocity: Infinite sibling list for MoveWith!\n"); - break; - } - } - } - - pEnt->pev->velocity = vecNew; -} - -//LRC - one more MoveWith utility. This one's for the simple version of RotWith. -void UTIL_SetMoveWithAvelocity ( CBaseEntity *pEnt, const Vector vecSet, int loopbreaker ) -{ - if (loopbreaker <= 0) - { - ALERT(at_error, "Infinite child list for MoveWith!"); - return; - } - - if (!pEnt->m_pMoveWith) - { - ALERT(at_error,"SetMoveWithAvelocity: no MoveWith entity!?\n"); - return; - } - - Vector vecNew = (pEnt->pev->avelocity - pEnt->m_pMoveWith->pev->avelocity) + vecSet; - -// ALERT(at_console, "Setting Child AVelocity %f %f %f, was %f %f %f mw %f %f %f\n", vecNew.x, vecNew.y, vecNew.z, pEnt->pev->avelocity.x, pEnt->pev->avelocity.y, pEnt->pev->avelocity.z, pEnt->m_pMoveWith->pev->avelocity.x, pEnt->m_pMoveWith->pev->avelocity.y, pEnt->m_pMoveWith->pev->avelocity.z); - - if ( pEnt->m_pChildMoveWith ) - { - CBaseEntity *pMoving = pEnt->m_pChildMoveWith; - int sloopbreaker = MAX_MOVEWITH_DEPTH; // to prevent the game hanging... - while (pMoving) - { - UTIL_SetMoveWithAvelocity(pMoving, vecNew, loopbreaker - 1 ); - pMoving = pMoving->m_pSiblingMoveWith; - sloopbreaker--; - if (sloopbreaker <= 0) - { - ALERT(at_error, "SetMoveWithVelocity: Infinite sibling list for MoveWith!"); - break; - } - } - } - - //UTIL_SetDesiredAvelocity(pEnt, vecNew); - pEnt->pev->avelocity = vecNew; -} - -void UTIL_SetAvelocity ( CBaseEntity *pEnt, const Vector vecSet ) -{ - Vector vecNew; - if (pEnt->m_pMoveWith) - vecNew = vecSet + pEnt->m_pMoveWith->pev->avelocity; - else - vecNew = vecSet; - -// ALERT(at_console, "Setting AVelocity %f %f %f\n", vecNew.x, vecNew.y, vecNew.z); - - if ( pEnt->m_pChildMoveWith ) - { - CBaseEntity *pMoving = pEnt->m_pChildMoveWith; - int sloopbreaker = MAX_MOVEWITH_DEPTH; // LRC - to save us from infinite loops - while (pMoving) - { - UTIL_SetMoveWithAvelocity(pMoving, vecNew, MAX_MOVEWITH_DEPTH ); - UTIL_MergePos( pMoving ); //force to update - if(vecSet != g_vecZero)SetBits(pMoving->m_iLFlags, LF_PARENTMOVE); - else ClearBits(pMoving->m_iLFlags, LF_PARENTMOVE); - pMoving = pMoving->m_pSiblingMoveWith; - sloopbreaker--; - if (sloopbreaker <= 0) - { - ALERT(at_error, "SetAvelocity: Infinite sibling list for MoveWith!\n"); - break; - } - } - } - pEnt->pev->avelocity = vecNew; -} - -void UTIL_MergePos ( CBaseEntity *pEnt, int loopbreaker ) -{ - if (loopbreaker <= 0)return; - if (!pEnt->m_pMoveWith)return; - - Vector forward, right, up, vecOrg, vecAngles; - - UTIL_MakeVectorsPrivate( pEnt->m_pMoveWith->pev->angles, forward, right, up ); - - if(pEnt->m_pMoveWith->pev->flags & FL_MONSTER) - vecOrg = pEnt->m_vecPostAssistOrg = pEnt->m_pMoveWith->pev->origin + (forward * pEnt->m_vecOffsetOrigin.x) + ( right * pEnt->m_vecOffsetOrigin.y) + (up * pEnt->m_vecOffsetOrigin.z); - else vecOrg = pEnt->m_vecPostAssistOrg = pEnt->m_pMoveWith->pev->origin + (forward * pEnt->m_vecOffsetOrigin.x) + (-right * pEnt->m_vecOffsetOrigin.y) + (up * pEnt->m_vecOffsetOrigin.z); - - vecAngles = pEnt->m_vecPostAssistAng = pEnt->m_pMoveWith->pev->angles + pEnt->m_vecOffsetAngles; - - if ( pEnt->m_pChildMoveWith ) - { - CBaseEntity *pMoving = pEnt->m_pChildMoveWith; - int sloopbreaker = MAX_MOVEWITH_DEPTH; - while (pMoving) - { - UTIL_MergePos(pMoving, loopbreaker - 1 ); - pMoving = pMoving->m_pSiblingMoveWith; - sloopbreaker--; - if (sloopbreaker <= 0)break; - } - } - - if(pEnt->m_iLFlags & LF_MERGEPOS) - { - UTIL_AssignOrigin( pEnt, vecOrg ); - UTIL_AssignAngles( pEnt, vecAngles ); - ClearBits(pEnt->m_iLFlags, LF_MERGEPOS); - } - if(pEnt->m_iLFlags & LF_POSTORG) - { - pEnt->pev->origin = vecOrg; - pEnt->pev->angles = vecAngles; - SetBits(pEnt->m_iLFlags, LF_DODESIRED ); //refresh position every frame - if(!pEnt->m_pAssistLink) UTIL_AddToAssistList(pEnt); - } -} - -BOOL NeedUpdate( CBaseEntity *pEnt ) -{ - if( pEnt->m_pChildMoveWith && pEnt->m_pChildMoveWith->m_vecOffsetOrigin == g_vecZero)//potentially loser - { - if(pEnt->pev->origin != pEnt->m_pChildMoveWith->pev->origin) - { - ALERT( at_console, "Warning %s lose synch with child\n", STRING(pEnt->pev->classname)); - return TRUE; - } - } - return FALSE; -} \ No newline at end of file diff --git a/spirit/movewith.h b/spirit/movewith.h deleted file mode 100644 index 8ac389eb..00000000 --- a/spirit/movewith.h +++ /dev/null @@ -1,47 +0,0 @@ -#define LF_NOTEASY (1<<0) -#define LF_NOTMEDIUM (1<<1) -#define LF_NOTHARD (1<<2) - -#define LF_POSTASSISTVEL (1<<3) -#define LF_POSTASSISTAVEL (1<<4) - -#define LF_DOASSIST (1<<5) -#define LF_CORRECTSPEED (1<<6) - -#define LF_DODESIRED (1<<7) -#define LF_DESIRED_THINK (1<<8) -#define LF_DESIRED_POSTASSIST (1<<9) - -#define LF_DESIRED_INFO (1<<10) -#define LF_DESIRED_ACTION (1<<11) - -#define LF_MOVENONE (1<<12) -#define LF_MERGEPOS (1<<13) -#define LF_PARENTMOVE (1<<14) -#define LF_ANGULAR (1<<15) -#define LF_POSTORG (1<<16) -#define LF_POINTENTITY (1<<17) - -#define LF_ALIASLIST (1<<18) - -// an entity must have one of these flags set in order to be in the AssistList -#define LF_ASSISTLIST (LF_DOASSIST|LF_DODESIRED|LF_MERGEPOS|LF_POSTORG) - -extern void CheckDesiredList( void ); -extern void CheckAssistList ( void ); - -extern void UTIL_DesiredAction ( CBaseEntity *pEnt ); -extern void UTIL_DesiredThink ( CBaseEntity *pEnt ); -extern void UTIL_DesiredInfo ( CBaseEntity *pEnt ); -extern void UTIL_DesiredPostAssist ( CBaseEntity *pEnt ); -extern void UTIL_AddToAssistList ( CBaseEntity *pEnt ); -extern void UTIL_MarkForAssist ( CBaseEntity *pEnt, BOOL correctSpeed ); -extern void UTIL_AssignOrigin ( CBaseEntity* pEntity, const Vector vecOrigin ); -extern void UTIL_AssignOrigin ( CBaseEntity* pEntity, const Vector vecOrigin, BOOL bInitiator ); -extern void UTIL_SetVelocity ( CBaseEntity *pEnt, const Vector vecSet ); -extern void UTIL_AssignAngles ( CBaseEntity* pEntity, const Vector vecAngles ); -extern void UTIL_AssignAngles ( CBaseEntity* pEntity, const Vector vecAngles, BOOL bInitiator ); -extern void UTIL_SetAvelocity ( CBaseEntity *pEnt, const Vector vecSet ); -extern void UTIL_SetMoveWithVelocity( CBaseEntity *pEnt, const Vector vecSet, int loopbreaker ); -extern void UTIL_SetMoveWithAvelocity( CBaseEntity *pEnt, const Vector vecSet, int loopbreaker ); -extern void UTIL_MergePos ( CBaseEntity *pEnt, int loopbreaker = 100 ); \ No newline at end of file diff --git a/spirit/mp5.cpp b/spirit/mp5.cpp deleted file mode 100644 index 67e5933b..00000000 --- a/spirit/mp5.cpp +++ /dev/null @@ -1,293 +0,0 @@ -/*** -* -* Copyright (c) 1996-2002, 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. -* -****/ - -#include "extdll.h" -#include "util.h" -#include "cbase.h" -#include "monsters.h" -#include "weapons.h" -#include "nodes.h" -#include "player.h" -#include "soundent.h" -#include "gamerules.h" - -enum mp5_e -{ - MP5_IDLE = 0, - MP5_DEPLOY, - MP5_HOLSTER, - MP5_FIRE1, - MP5_FIRE2, - MP5_FIRE3, - MP5_LAUNCH, - MP5_RELOAD -}; - -class CMP5 : public CBasePlayerWeapon -{ -public: - void Spawn( void ); - void Precache( void ); - int GetItemInfo(ItemInfo *p); - - void PrimaryAttack( void ); - void SecondaryAttack( void ); - int SecondaryAmmoIndex( void ); - BOOL Deploy( void ); - void Holster( ); - void Reload( void ); - void WeaponIdle( void ); -private: - unsigned short m_usMP5; -}; -LINK_ENTITY_TO_CLASS( weapon_mp5, CMP5 ); -LINK_ENTITY_TO_CLASS( weapon_9mmAR, CMP5 ); - - -int CMP5::SecondaryAmmoIndex( void ) -{ - return m_iSecondaryAmmoType; -} - -void CMP5::Spawn( ) -{ - pev->classname = MAKE_STRING("weapon_9mmAR"); // hack to allow for old names - Precache( ); - SET_MODEL(ENT(pev), "models/w_9mmAR.mdl"); - m_iDefaultAmmo = MP5_DEFAULT_GIVE; - - FallInit();// get ready to fall down. -} - - -void CMP5::Precache( void ) -{ - PRECACHE_MODEL("models/v_9mmAR.mdl"); - PRECACHE_MODEL("models/w_9mmAR.mdl"); - PRECACHE_MODEL("models/p_9mmAR.mdl"); - - int m_iShell = PRECACHE_MODEL ("models/shell.mdl");// brass shellTE_MODEL - - PRECACHE_MODEL("models/grenade.mdl"); // grenade - m_usMP5 = PRECACHE_EVENT( 1, "evMP5" ); -} - -int CMP5::GetItemInfo(ItemInfo *p) -{ - p->pszName = STRING(pev->classname); - p->pszAmmo1 = "9mm"; - p->iMaxAmmo1 = _9MM_MAX_CARRY; - p->pszAmmo2 = "ARgrenades"; - p->iMaxAmmo2 = M203_GRENADE_MAX_CARRY; - p->iMaxClip = MP5_MAX_CLIP; - p->iSlot = 2; - p->iPosition = 0; - p->iFlags = 0; - p->iId = m_iId = WEAPON_MP5; - p->iWeight = MP5_WEIGHT; - - return 1; -} - -BOOL CMP5::Deploy( ) -{ - return DefaultDeploy( "models/v_9mmAR.mdl", "models/p_9mmAR.mdl", MP5_DEPLOY, "mp5", 0.8 ); -} - -void CMP5::Holster( ) -{ - m_pPlayer->m_flNextAttack = UTIL_WeaponTimeBase() + 0.6; - SendWeaponAnim(MP5_HOLSTER); -} - -void CMP5::PrimaryAttack() -{ - // don't fire underwater - if ( m_iClip && m_pPlayer->pev->waterlevel != 3) - { - m_pPlayer->m_iWeaponVolume = NORMAL_GUN_VOLUME; - m_pPlayer->m_iWeaponFlash = NORMAL_GUN_FLASH; - - m_iClip--; - - // player "shoot" animation - m_pPlayer->SetAnimation( PLAYER_ATTACK1 ); - - Vector vecSrc = m_pPlayer->GetGunPosition( ); - Vector vecAiming = m_pPlayer->GetAutoaimVector( AUTOAIM_5DEGREES ); - Vector vecDir; - - if ( !IsMultiplayer() ) - vecDir = m_pPlayer->FireBulletsPlayer( 1, vecSrc, vecAiming, VECTOR_CONE_6DEGREES, 8192, BULLET_PLAYER_MP5, 2, 0, m_pPlayer->pev, m_pPlayer->random_seed ); - else vecDir = m_pPlayer->FireBulletsPlayer( 1, vecSrc, vecAiming, VECTOR_CONE_3DEGREES, 8192, BULLET_PLAYER_MP5, 2, 0, m_pPlayer->pev, m_pPlayer->random_seed ); - - PLAYBACK_EVENT_FULL( 0, m_pPlayer->edict(), m_usMP5, 0.0, (float *)&g_vecZero, (float *)&g_vecZero, vecDir.x, vecDir.y, pev->body, MP5_FIRE1, 0, 0 ); - - m_flNextPrimaryAttack = UTIL_WeaponTimeBase() + 0.1; - - if ( m_flNextPrimaryAttack < UTIL_WeaponTimeBase() ) - m_flNextPrimaryAttack = UTIL_WeaponTimeBase() + 0.2; - m_flTimeWeaponIdle = UTIL_WeaponTimeBase() + RANDOM_FLOAT ( 10, 15 ); - } - else - { - PlayEmptySound( ); - m_flNextPrimaryAttack = UTIL_WeaponTimeBase() + 0.5; - } -} - - - -void CMP5::SecondaryAttack( void ) -{ - // don't fire underwater - if ( m_pPlayer->m_rgAmmo[m_iSecondaryAmmoType] > 0 && m_pPlayer->pev->waterlevel != 3) - { - m_pPlayer->m_iWeaponVolume = NORMAL_GUN_VOLUME; - m_pPlayer->m_iWeaponFlash = BRIGHT_GUN_FLASH; - - m_pPlayer->m_iExtraSoundTypes = bits_SOUND_DANGER; - m_pPlayer->m_flStopExtraSoundTime = UTIL_WeaponTimeBase() + 0.2; - - m_pPlayer->m_rgAmmo[m_iSecondaryAmmoType]--; - - // player "shoot" animation - m_pPlayer->SetAnimation( PLAYER_ATTACK1 ); - - UTIL_MakeVectors( m_pPlayer->pev->v_angle + m_pPlayer->pev->punchangle ); - - // we don't add in player velocity anymore. - CGrenade::ShootContact( m_pPlayer->pev, m_pPlayer->pev->origin + m_pPlayer->pev->view_ofs + gpGlobals->v_forward * 16, gpGlobals->v_forward * 800 ); - - SendWeaponAnim(MP5_LAUNCH); - m_pPlayer->pev->punchangle.x -= 10; - - m_flNextPrimaryAttack = m_flNextSecondaryAttack = UTIL_WeaponTimeBase() + 1; - m_flTimeWeaponIdle = UTIL_WeaponTimeBase() + 5; - } - else - { - PlayEmptySound( ); - m_flNextPrimaryAttack = UTIL_WeaponTimeBase() + 0.5; - } -} - -void CMP5::Reload( void ) -{ - if ( m_pPlayer->m_rgAmmo[m_iPrimaryAmmoType] <= 0 ) return; - DefaultReload( MP5_MAX_CLIP, MP5_RELOAD, 1.5 ); -} - - -void CMP5::WeaponIdle( void ) -{ - m_pPlayer->GetAutoaimVector( AUTOAIM_5DEGREES ); - - if ( m_flTimeWeaponIdle > UTIL_WeaponTimeBase() ) return; - - if(m_iClip) - { - float flRand = RANDOM_FLOAT(0, 1); - if (flRand <= 0.3) - { - SendWeaponAnim(MP5_IDLE); - m_flTimeWeaponIdle = UTIL_WeaponTimeBase() + 41.0/8.0; - } - else m_flTimeWeaponIdle = UTIL_WeaponTimeBase() + RANDOM_LONG(10, 30); - } -} - - - -class CMP5AmmoClip : public CBasePlayerAmmo -{ - void Spawn( void ) - { - Precache( ); - SET_MODEL(ENT(pev), "models/w_9mmARclip.mdl"); - CBasePlayerAmmo::Spawn( ); - } - void Precache( void ) - { - PRECACHE_MODEL ("models/w_9mmARclip.mdl"); - PRECACHE_SOUND("items/9mmclip1.wav"); - } - BOOL AddAmmo( CBaseEntity *pOther ) - { - int bResult = (pOther->GiveAmmo( AMMO_MP5CLIP_GIVE, "9mm", _9MM_MAX_CARRY) != -1); - if (bResult) - { - EMIT_SOUND(ENT(pev), CHAN_ITEM, "items/9mmclip1.wav", 1, ATTN_NORM); - } - return bResult; - } -}; -LINK_ENTITY_TO_CLASS( ammo_mp5clip, CMP5AmmoClip ); -LINK_ENTITY_TO_CLASS( ammo_9mmAR, CMP5AmmoClip ); - - - -class CMP5Chainammo : public CBasePlayerAmmo -{ - void Spawn( void ) - { - Precache( ); - SET_MODEL(ENT(pev), "models/w_chainammo.mdl"); - CBasePlayerAmmo::Spawn( ); - } - void Precache( void ) - { - PRECACHE_MODEL ("models/w_chainammo.mdl"); - PRECACHE_SOUND("items/9mmclip1.wav"); - } - BOOL AddAmmo( CBaseEntity *pOther ) - { - int bResult = (pOther->GiveAmmo( AMMO_CHAINBOX_GIVE, "9mm", _9MM_MAX_CARRY) != -1); - if (bResult) - { - EMIT_SOUND(ENT(pev), CHAN_ITEM, "items/9mmclip1.wav", 1, ATTN_NORM); - } - return bResult; - } -}; -LINK_ENTITY_TO_CLASS( ammo_9mmbox, CMP5Chainammo ); - - -class CMP5AmmoGrenade : public CBasePlayerAmmo -{ - void Spawn( void ) - { - Precache( ); - SET_MODEL(ENT(pev), "models/w_ARgrenade.mdl"); - CBasePlayerAmmo::Spawn( ); - } - void Precache( void ) - { - PRECACHE_MODEL ("models/w_ARgrenade.mdl"); - PRECACHE_SOUND("items/9mmclip1.wav"); - } - BOOL AddAmmo( CBaseEntity *pOther ) - { - int bResult = (pOther->GiveAmmo( AMMO_M203BOX_GIVE, "ARgrenades", M203_GRENADE_MAX_CARRY ) != -1); - - if (bResult) - { - EMIT_SOUND(ENT(pev), CHAN_ITEM, "items/9mmclip1.wav", 1, ATTN_NORM); - } - return bResult; - } -}; -LINK_ENTITY_TO_CLASS( ammo_mp5grenades, CMP5AmmoGrenade ); -LINK_ENTITY_TO_CLASS( ammo_ARgrenades, CMP5AmmoGrenade ); \ No newline at end of file diff --git a/spirit/plats.cpp b/spirit/plats.cpp deleted file mode 100644 index c7e062d5..00000000 --- a/spirit/plats.cpp +++ /dev/null @@ -1,3429 +0,0 @@ -/*** -* -* Copyright (c) 1999, 2000 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. -* -****/ -/* - -===== plats.cpp ======================================================== - - spawn, think, and touch functions for trains, etc - -*/ - -#include "extdll.h" -#include "util.h" -#include "cbase.h" -#include "trains.h" -#include "saverestore.h" -#include "movewith.h" - -// plats -#define PLAT_LOW_TRIGGER 1 - -// Trains -#define SF_TRAIN_WAIT_RETRIGGER 1 -#define SF_TRAIN_START_ON 4 // Train is initially moving -#define SF_TRAIN_PASSABLE 8 // Train is not solid -- used to make water trains - -static void PlatSpawnInsideTrigger(entvars_t* pevPlatform); - -static float Fix( float angle ) -{ - if ( IS_NAN(angle) ) - { - ALERT(at_debug, "NaN error during Fix!\n"); - return angle; - } - while ( angle < 0 ) - angle += 360; - while ( angle > 360 ) - angle -= 360; - - return angle; -} - - -static void FixupAngles( Vector &v ) -{ - v.x = Fix( v.x ); - v.y = Fix( v.y ); - v.z = Fix( v.z ); -} - - -class CFuncTrain; - -//LRC - scripted_trainsequence -class CTrainSequence : public CBaseEntity -{ -public: - void Use( CBaseEntity *pActivator, CBaseEntity *pCaller, USE_TYPE useType, float value ); - void EndThink( void ); - void TimeOutThink( void ); - void KeyValue( KeyValueData *pkvd ); - STATE GetState( void ) { return (m_pTrain||m_pTrackTrain)?STATE_ON:STATE_OFF; } - virtual int ObjectCaps( void ); - - void StopSequence( void ); - void ArrivalNotify( void ); - - virtual int Save( CSave &save ); - virtual int Restore( CRestore &restore ); - static TYPEDESCRIPTION m_SaveData[]; - - string_t m_iszEntity; - string_t m_iszDestination; - string_t m_iszTerminate; - int m_iDirection; - int m_iPostDirection; - float m_fDuration; -// at any given time, at most one of these pointers will be set. - CFuncTrain *m_pTrain; - CFuncTrackTrain *m_pTrackTrain; - - CBaseEntity *m_pDestination; -}; - - - -#define SF_PLAT_TOGGLE 0x0001 - -class CBasePlatTrain : public CBaseToggle -{ -public: - virtual int ObjectCaps( void ) { return CBaseEntity :: ObjectCaps() & ~FCAP_ACROSS_TRANSITION; } - void KeyValue( KeyValueData* pkvd); - void Precache( void ); - - // This is done to fix spawn flag collisions between this class and a derived class - virtual BOOL IsTogglePlat( void ) { return (pev->spawnflags & SF_PLAT_TOGGLE) ? TRUE : FALSE; } - - virtual int Save( CSave &save ); - virtual int Restore( CRestore &restore ); - static TYPEDESCRIPTION m_SaveData[]; - - BYTE m_bMoveSnd; // sound a plat makes while moving - BYTE m_bStopSnd; // sound a plat makes when it stops - float m_volume; // Sound volume -}; - -TYPEDESCRIPTION CBasePlatTrain::m_SaveData[] = -{ - DEFINE_FIELD( CBasePlatTrain, m_bMoveSnd, FIELD_CHARACTER ), - DEFINE_FIELD( CBasePlatTrain, m_bStopSnd, FIELD_CHARACTER ), - DEFINE_FIELD( CBasePlatTrain, m_volume, FIELD_FLOAT ), -}; - -IMPLEMENT_SAVERESTORE( CBasePlatTrain, CBaseToggle ); - -void CBasePlatTrain :: KeyValue( KeyValueData *pkvd ) -{ - if (FStrEq(pkvd->szKeyName, "lip")) - { - m_flLip = atof(pkvd->szValue); - pkvd->fHandled = TRUE; - } - else if (FStrEq(pkvd->szKeyName, "wait")) - { - m_flWait = atof(pkvd->szValue); - pkvd->fHandled = TRUE; - } - else if (FStrEq(pkvd->szKeyName, "height")) - { - m_flHeight = atof(pkvd->szValue); - pkvd->fHandled = TRUE; - } - else if (FStrEq(pkvd->szKeyName, "rotation")) - { - m_vecFinalAngle.x = atof(pkvd->szValue); - pkvd->fHandled = TRUE; - } - else if (FStrEq(pkvd->szKeyName, "movesnd")) - { - m_bMoveSnd = atof(pkvd->szValue); - pkvd->fHandled = TRUE; - } - else if (FStrEq(pkvd->szKeyName, "stopsnd")) - { - m_bStopSnd = atof(pkvd->szValue); - pkvd->fHandled = TRUE; - } - else if (FStrEq(pkvd->szKeyName, "custommovesnd")) - { - pev->noise = ALLOC_STRING(pkvd->szValue); - pkvd->fHandled = TRUE; - } - else if (FStrEq(pkvd->szKeyName, "customstopsnd")) - { - pev->noise1 = ALLOC_STRING(pkvd->szValue); - pkvd->fHandled = TRUE; - } - else if (FStrEq(pkvd->szKeyName, "volume")) - { - m_volume = atof(pkvd->szValue); - pkvd->fHandled = TRUE; - } - else - CBaseToggle::KeyValue( pkvd ); -} - -#define noiseMoving noise -#define noiseArrived noise1 - -void CBasePlatTrain::Precache( void ) -{ -// set the plat's "in-motion" sound - if (FStringNull(pev->noiseMoving)) - { - switch (m_bMoveSnd) - { - case 1: - pev->noiseMoving = MAKE_STRING("plats/bigmove1.wav"); - break; - case 2: - pev->noiseMoving = MAKE_STRING("plats/bigmove2.wav"); - break; - case 3: - pev->noiseMoving = MAKE_STRING("plats/elevmove1.wav"); - break; - case 4: - pev->noiseMoving = MAKE_STRING("plats/elevmove2.wav"); - break; - case 5: - pev->noiseMoving = MAKE_STRING("plats/elevmove3.wav"); - break; - case 6: - pev->noiseMoving = MAKE_STRING("plats/freightmove1.wav"); - break; - case 7: - pev->noiseMoving = MAKE_STRING("plats/freightmove2.wav"); - break; - case 8: - pev->noiseMoving = MAKE_STRING("plats/heavymove1.wav"); - break; - case 9: - pev->noiseMoving = MAKE_STRING("plats/rackmove1.wav"); - break; - case 10: - pev->noiseMoving = MAKE_STRING("plats/railmove1.wav"); - break; - case 11: - pev->noiseMoving = MAKE_STRING("plats/squeekmove1.wav"); - break; - case 12: - pev->noiseMoving = MAKE_STRING("plats/talkmove1.wav"); - break; - case 13: - pev->noiseMoving = MAKE_STRING("plats/talkmove2.wav"); - break; - default: - pev->noiseMoving = MAKE_STRING("common/null.wav"); - break; - } - } - PRECACHE_SOUND ((char*)STRING(pev->noiseMoving)); - -// set the plat's 'reached destination' stop sound - if (FStringNull(pev->noiseArrived)) - { - switch (m_bStopSnd) - { - case 1: - pev->noiseArrived = MAKE_STRING("plats/bigstop1.wav"); - break; - case 2: - pev->noiseArrived = MAKE_STRING("plats/bigstop2.wav"); - break; - case 3: - pev->noiseArrived = MAKE_STRING("plats/freightstop1.wav"); - break; - case 4: - pev->noiseArrived = MAKE_STRING("plats/heavystop2.wav"); - break; - case 5: - pev->noiseArrived = MAKE_STRING("plats/rackstop1.wav"); - break; - case 6: - pev->noiseArrived = MAKE_STRING("plats/railstop1.wav"); - break; - case 7: - pev->noiseArrived = MAKE_STRING("plats/squeekstop1.wav"); - break; - case 8: - pev->noiseArrived = MAKE_STRING("plats/talkstop1.wav"); - break; - default: - pev->noiseArrived = MAKE_STRING("common/null.wav"); - break; - } - } - PRECACHE_SOUND ((char*)STRING(pev->noiseArrived)); -} - -// -//====================== PLAT code ==================================================== -// - - -#define noiseMovement noise -#define noiseStopMoving noise1 - -class CFuncPlat : public CBasePlatTrain -{ -public: - void Spawn( void ); - void Precache( void ); - void Setup( void ); - - virtual void Blocked( CBaseEntity *pOther ); - - void EXPORT PlatUse( CBaseEntity *pActivator, CBaseEntity *pCaller, USE_TYPE useType, float value ); - - void EXPORT CallGoUp( void ) { GoUp(); } - void EXPORT CallGoDown( void ) { GoDown(); } - void EXPORT CallHitTop( void ) { HitTop(); } - void EXPORT CallHitBottom( void ) { HitBottom(); } - - virtual void GoUp( void ); - virtual void GoDown( void ); - virtual void HitTop( void ); - virtual void HitBottom( void ); -}; -LINK_ENTITY_TO_CLASS( func_plat, CFuncPlat ); - - -// UNDONE: Need to save this!!! It needs class & linkage -class CPlatTrigger : public CBaseEntity -{ -public: - virtual int ObjectCaps( void ) { return (CBaseEntity :: ObjectCaps() & ~FCAP_ACROSS_TRANSITION) | FCAP_DONT_SAVE; } - void SpawnInsideTrigger( CFuncPlat *pPlatform ); - void Touch( CBaseEntity *pOther ); - CFuncPlat *m_pPlatform; -}; - - - -/*QUAKED func_plat (0 .5 .8) ? PLAT_LOW_TRIGGER -speed default 150 - -Plats are always drawn in the extended position, so they will light correctly. - -If the plat is the target of another trigger or button, it will start out disabled in -the extended position until it is trigger, when it will lower and become a normal plat. - -If the "height" key is set, that will determine the amount the plat moves, instead of -being implicitly determined by the model's height. - -Set "sounds" to one of the following: -1) base fast -2) chain slow -*/ - -void CFuncPlat :: Setup( void ) -{ - //pev->noiseMovement = MAKE_STRING("plats/platmove1.wav"); - //pev->noiseStopMoving = MAKE_STRING("plats/platstop1.wav"); - - if (m_flTLength == 0) - m_flTLength = 80; - if (m_flTWidth == 0) - m_flTWidth = 10; - - pev->angles = g_vecZero; - - pev->solid = SOLID_BSP; - pev->movetype = MOVETYPE_PUSH; - - UTIL_SetOrigin(this, pev->origin); // set size and link into world - UTIL_SetSize(pev, pev->mins, pev->maxs); - SET_MODEL(ENT(pev), STRING(pev->model) ); - - // vecPosition1 is the top position, vecPosition2 is the bottom - if (m_pMoveWith) - m_vecPosition1 = pev->origin - m_pMoveWith->pev->origin; - else - m_vecPosition1 = pev->origin; - m_vecPosition2 = m_vecPosition1; - if (m_flHeight != 0) - m_vecPosition2.z = m_vecPosition2.z - m_flHeight; - else - m_vecPosition2.z = m_vecPosition2.z - pev->size.z + 8; - if (pev->speed == 0) - pev->speed = 150; - - if ( m_volume == 0 ) - m_volume = 0.85; -} - - -void CFuncPlat :: Precache( ) -{ - CBasePlatTrain::Precache(); - //PRECACHE_SOUND("plats/platmove1.wav"); - //PRECACHE_SOUND("plats/platstop1.wav"); - if ( !IsTogglePlat() ) - PlatSpawnInsideTrigger( pev ); // the "start moving" trigger -} - - -void CFuncPlat :: Spawn( ) -{ - Setup(); - - Precache(); - - // If this platform is the target of some button, it starts at the TOP position, - // and is brought down by that button. Otherwise, it starts at BOTTOM. - if ( !FStringNull(pev->targetname) ) - { - if (m_pMoveWith) - UTIL_AssignOrigin (this, m_vecPosition1 + m_pMoveWith->pev->origin); - else - UTIL_AssignOrigin (this, m_vecPosition1); - m_toggle_state = TS_AT_TOP; - SetUse(&CFuncPlat :: PlatUse ); - } - else - { - if (m_pMoveWith) - UTIL_AssignOrigin (this, m_vecPosition2 + m_pMoveWith->pev->origin); - else - UTIL_AssignOrigin (this, m_vecPosition2); - m_toggle_state = TS_AT_BOTTOM; - } -} - - - -static void PlatSpawnInsideTrigger(entvars_t* pevPlatform) -{ - GetClassPtr( (CPlatTrigger *)NULL)->SpawnInsideTrigger( GetClassPtr( (CFuncPlat *)pevPlatform ) ); -} - - -// -// Create a trigger entity for a platform. -// -void CPlatTrigger :: SpawnInsideTrigger( CFuncPlat *pPlatform ) -{ - m_pPlatform = pPlatform; - // Create trigger entity, "point" it at the owning platform, give it a touch method - pev->solid = SOLID_TRIGGER; - pev->movetype = MOVETYPE_NONE; - pev->origin = pPlatform->pev->origin; - - // Establish the trigger field's size - Vector vecTMin = m_pPlatform->pev->mins + Vector ( 25 , 25 , 0 ); - Vector vecTMax = m_pPlatform->pev->maxs + Vector ( 25 , 25 , 8 ); - vecTMin.z = vecTMax.z - ( m_pPlatform->m_vecPosition1.z - m_pPlatform->m_vecPosition2.z + 8 ); - if (m_pPlatform->pev->size.x <= 50) - { - vecTMin.x = (m_pPlatform->pev->mins.x + m_pPlatform->pev->maxs.x) / 2; - vecTMax.x = vecTMin.x + 1; - } - if (m_pPlatform->pev->size.y <= 50) - { - vecTMin.y = (m_pPlatform->pev->mins.y + m_pPlatform->pev->maxs.y) / 2; - vecTMax.y = vecTMin.y + 1; - } - UTIL_SetSize ( pev, vecTMin, vecTMax ); -} - - -// -// When the platform's trigger field is touched, the platform ??? -// -void CPlatTrigger :: Touch( CBaseEntity *pOther ) -{ - // Ignore touches by non-players - entvars_t* pevToucher = pOther->pev; - if ( !FClassnameIs (pevToucher, "player") ) - return; - - // Ignore touches by corpses - if (!pOther->IsAlive()) - return; - - // Make linked platform go up/down. - if (m_pPlatform->m_toggle_state == TS_AT_BOTTOM) - m_pPlatform->GoUp(); - else if (m_pPlatform->m_toggle_state == TS_AT_TOP) - m_pPlatform->SetNextThink( 1 );// delay going down -} - - -// -// Used by SUB_UseTargets, when a platform is the target of a button. -// Start bringing platform down. -// -void CFuncPlat :: PlatUse( CBaseEntity *pActivator, CBaseEntity *pCaller, USE_TYPE useType, float value ) -{ - m_hActivator = pActivator; //AJH - - if ( IsTogglePlat() ) - { - // Top is off, bottom is on - BOOL on = (m_toggle_state == TS_AT_BOTTOM) ? TRUE : FALSE; - - if ( !ShouldToggle( useType, on ) ) - return; - - if (m_toggle_state == TS_AT_TOP) - { - SetNextThink( 0.01 ); - SetThink(&CFuncPlat :: CallGoDown ); - } - else if ( m_toggle_state == TS_AT_BOTTOM ) - { - SetNextThink( 0.01 ); - SetThink(&CFuncPlat :: CallGoUp ); - } - } - else - { - SetUse( NULL ); - - if (m_toggle_state == TS_AT_TOP) - { - SetNextThink( 0.01 ); - SetThink(&CFuncPlat :: CallGoDown ); - } - } -} - - -// -// Platform is at top, now starts moving down. -// -void CFuncPlat :: GoDown( void ) -{ - if(pev->noiseMovement) - EMIT_SOUND(ENT(pev), CHAN_STATIC, (char*)STRING(pev->noiseMovement), m_volume, ATTN_NORM); - - ASSERT(m_toggle_state == TS_AT_TOP || m_toggle_state == TS_GOING_UP); - m_toggle_state = TS_GOING_DOWN; - SetMoveDone(&CFuncPlat ::CallHitBottom); - LinearMove(m_vecPosition2, pev->speed); -} - - -// -// Platform has hit bottom. Stops and waits forever. -// -void CFuncPlat :: HitBottom( void ) -{ - if(pev->noiseMovement) - STOP_SOUND(ENT(pev), CHAN_STATIC, (char*)STRING(pev->noiseMovement)); - - if (pev->noiseStopMoving) - EMIT_SOUND(ENT(pev), CHAN_WEAPON, (char*)STRING(pev->noiseStopMoving), m_volume, ATTN_NORM); - - ASSERT(m_toggle_state == TS_GOING_DOWN); - m_toggle_state = TS_AT_BOTTOM; -} - - -// -// Platform is at bottom, now starts moving up -// -void CFuncPlat :: GoUp( void ) -{ - if (pev->noiseMovement) - EMIT_SOUND(ENT(pev), CHAN_STATIC, (char*)STRING(pev->noiseMovement), m_volume, ATTN_NORM); - - ASSERT(m_toggle_state == TS_AT_BOTTOM || m_toggle_state == TS_GOING_DOWN); - m_toggle_state = TS_GOING_UP; - SetMoveDone(&CFuncPlat ::CallHitTop); - LinearMove(m_vecPosition1, pev->speed); -} - - -// -// Platform has hit top. Pauses, then starts back down again. -// -void CFuncPlat :: HitTop( void ) -{ - if(pev->noiseMovement) - STOP_SOUND(ENT(pev), CHAN_STATIC, (char*)STRING(pev->noiseMovement)); - - if (pev->noiseStopMoving) - EMIT_SOUND(ENT(pev), CHAN_WEAPON, (char*)STRING(pev->noiseStopMoving), m_volume, ATTN_NORM); - - ASSERT(m_toggle_state == TS_GOING_UP); - m_toggle_state = TS_AT_TOP; - - if ( !IsTogglePlat() ) - { - // After a delay, the platform will automatically start going down again. - SetThink(&CFuncPlat :: CallGoDown ); - SetNextThink( 3 ); - } -} - - -void CFuncPlat :: Blocked( CBaseEntity *pOther ) -{ - //g-cont. simple recursive anouncer for parent system - //tell parent who blocked his - if(!FNullEnt(m_pMoveWith) && m_iLFlags & LF_PARENTMOVE) m_pMoveWith->Blocked( this ); - if(!FNullEnt(m_pChildMoveWith)) - { - if(m_pChildMoveWith == pOther) - { - //ALERT(at_console, "I'am blocked by my child!\n"); - Use( NULL, NULL, USE_OFF, 0 ); - } - } - - ALERT( at_aiconsole, "%s Blocked by %s\n", STRING(pev->classname), STRING(pOther->pev->classname) ); - // Hurt the blocker a little - if (m_hActivator) - pOther->TakeDamage( pev, m_hActivator->pev, 1, DMG_CRUSH ); //AJH Attribute damage to he who switched me. - else - pOther->TakeDamage( pev, pev,1, DMG_CRUSH ); - if(pev->noiseMovement) - STOP_SOUND(ENT(pev), CHAN_STATIC, (char*)STRING(pev->noiseMovement)); - - // Send the platform back where it came from - ASSERT(m_toggle_state == TS_GOING_UP || m_toggle_state == TS_GOING_DOWN); - if (m_toggle_state == TS_GOING_UP) - { - SetNextThink( 0 ); - SetThink(&CFuncPlat :: GoDown ); - } - else if (m_toggle_state == TS_GOING_DOWN) - { - SetNextThink( 0 ); - SetThink(&CFuncPlat :: GoUp ); - } -} - - -class CFuncPlatRot : public CFuncPlat -{ -public: - void Spawn( void ); - void SetupRotation( void ); - virtual void KeyValue( KeyValueData *pkvd ); - - virtual void GoUp( void ); - virtual void GoDown( void ); - virtual void HitTop( void ); - virtual void HitBottom( void ); - - void RotMove( Vector &destAngle, float time ); - virtual int Save( CSave &save ); - virtual int Restore( CRestore &restore ); - static TYPEDESCRIPTION m_SaveData[]; - - Vector m_end, m_start; -}; -LINK_ENTITY_TO_CLASS( func_platrot, CFuncPlatRot ); -TYPEDESCRIPTION CFuncPlatRot::m_SaveData[] = -{ - DEFINE_FIELD( CFuncPlatRot, m_end, FIELD_VECTOR ), - DEFINE_FIELD( CFuncPlatRot, m_start, FIELD_VECTOR ), -}; - -IMPLEMENT_SAVERESTORE( CFuncPlatRot, CFuncPlat ); - -void CFuncPlatRot::KeyValue( KeyValueData *pkvd ) -{ - if (FStrEq(pkvd->szKeyName, "axes")) - { - UTIL_StringToVector((float*)(pev->movedir), pkvd->szValue); - pkvd->fHandled = TRUE; - } - else - CFuncPlat::KeyValue( pkvd ); -} - -void CFuncPlatRot :: SetupRotation( void ) -{ - if ( m_vecFinalAngle.x != 0 ) // This plat rotates too! - { - CBaseToggle :: AxisDir( pev ); - m_start = pev->angles; - m_end = pev->angles + pev->movedir * m_vecFinalAngle.x; - } - else - { - m_start = g_vecZero; - m_end = g_vecZero; - } - if ( !FStringNull(pev->targetname) ) // Start at top - { - UTIL_AssignAngles(this, m_end); - //pev->angles = m_end; - } -} - - -void CFuncPlatRot :: Spawn( void ) -{ - CFuncPlat :: Spawn(); - SetupRotation(); -} - -void CFuncPlatRot :: GoDown( void ) -{ - CFuncPlat :: GoDown(); -// RotMove( m_start, m_fNextThink - pev->ltime ); //G-Cont/ this fix for func_platrot - //Ox, uj mne etot Laury! - Vector vecDest; - if (m_pMoveWith) - { - vecDest = m_vecFinalDest + m_pMoveWith->pev->origin; - } - else - vecDest = m_vecFinalDest; - Vector vecDestDelta = vecDest - pev->origin; - float flTravelTime = vecDestDelta.Length() / m_flLinearMoveSpeed; - - RotMove( m_start, flTravelTime ); -} - - -// -// Platform has hit bottom. Stops and waits forever. -// -void CFuncPlatRot :: HitBottom( void ) -{ - CFuncPlat :: HitBottom(); - UTIL_SetAvelocity(this, g_vecZero); - //pev->avelocity = g_vecZero; - UTIL_AssignAngles(this, m_start); - //pev->angles = m_start; -} - - -// -// Platform is at bottom, now starts moving up -// -void CFuncPlatRot :: GoUp( void ) -{ - CFuncPlat :: GoUp(); -// RotMove( m_end, m_fNextThink - pev->ltime ); - - Vector vecDest; - if (m_pMoveWith) - { - vecDest = m_vecFinalDest + m_pMoveWith->pev->origin; - } - else - vecDest = m_vecFinalDest; - Vector vecDestDelta = vecDest - pev->origin; - float flTravelTime = vecDestDelta.Length() / m_flLinearMoveSpeed; - - RotMove( m_end, flTravelTime ); -} - - -// -// Platform has hit top. Pauses, then starts back down again. -// -void CFuncPlatRot :: HitTop( void ) -{ - CFuncPlat :: HitTop(); - UTIL_SetAvelocity(this, g_vecZero); - //pev->avelocity = g_vecZero; - UTIL_AssignAngles(this, m_end); - //pev->angles = m_end; -} - - -void CFuncPlatRot :: RotMove( Vector &destAngle, float time ) -{ - // set destdelta to the vector needed to move - Vector vecDestDelta = destAngle - pev->angles; - - // Travel time is so short, we're practically there already; make it so. - if ( time >= 0.1) - { - UTIL_SetAvelocity(this, vecDestDelta / time); - //pev->avelocity = vecDestDelta / time; - } - else - { - UTIL_SetAvelocity(this, vecDestDelta); - //pev->avelocity = vecDestDelta; - SetNextThink( 1 ); - } -} - - -// -//====================== TRAIN code ================================================== -// - -//the others are defined in const.h -// SF_TRAIN_WAIT_RETRIGGER 1 -#define SF_TRAIN_SETORIGIN 2 -// SF_TRAIN_START_ON 4 // Train is initially moving -// SF_TRAIN_PASSABLE 8 // Train is not solid -- used to make water trains -#define SF_TRAIN_REVERSE 0x800000 - -class CFuncTrain : public CBasePlatTrain -{ -public: - void Spawn( void ); - void Precache( void ); - void PostSpawn( void ); - void OverrideReset( void ); - - void Blocked( CBaseEntity *pOther ); - void Use( CBaseEntity *pActivator, CBaseEntity *pCaller, USE_TYPE useType, float value ); - void KeyValue( KeyValueData *pkvd ); - - //LRC - void StartSequence(CTrainSequence *pSequence); - void StopSequence( ); - CTrainSequence *m_pSequence; - - void EXPORT Wait( void ); - void EXPORT Next( void ); - void EXPORT ThinkDoNext( void ); - void EXPORT SoundSetup( void ); - - STATE GetState( void ) { return m_iState; } - - virtual void ThinkCorrection( void ); - - virtual int Save( CSave &save ); - virtual int Restore( CRestore &restore ); - static TYPEDESCRIPTION m_SaveData[]; - - entvars_t *m_pevCurrentTarget; - int m_sounds; -//LRC - now part of CBaseEntity: BOOL m_activated; - STATE m_iState; - float m_fStoredThink; - Vector m_vecAvelocity; -}; - -LINK_ENTITY_TO_CLASS( func_train, CFuncTrain ); -TYPEDESCRIPTION CFuncTrain::m_SaveData[] = -{ - DEFINE_FIELD( CFuncTrain, m_sounds, FIELD_INTEGER ), - DEFINE_FIELD( CFuncTrain, m_pevCurrentTarget, FIELD_EVARS ), -//LRC - now part of CBaseEntity: DEFINE_FIELD( CFuncTrain, m_activated, FIELD_BOOLEAN ), - DEFINE_FIELD( CFuncTrain, m_iState, FIELD_INTEGER ), - DEFINE_FIELD( CFuncTrain, m_fStoredThink, FIELD_TIME ), - DEFINE_FIELD( CFuncTrain, m_pSequence, FIELD_CLASSPTR ), //LRC - DEFINE_FIELD( CFuncTrain, m_vecAvelocity, FIELD_VECTOR ), //LRC -}; - -IMPLEMENT_SAVERESTORE( CFuncTrain, CBasePlatTrain ); - - -void CFuncTrain :: KeyValue( KeyValueData *pkvd ) -{ - if (FStrEq(pkvd->szKeyName, "sounds")) - { - m_sounds = atoi(pkvd->szValue); - pkvd->fHandled = TRUE; - } - else - CBasePlatTrain::KeyValue( pkvd ); -} - - -void CFuncTrain :: Blocked( CBaseEntity *pOther ) -{ - //g-cont. simple recursive anouncer for parent system - //tell parent who blocked his - if(!FNullEnt(m_pMoveWith) && m_iLFlags & LF_PARENTMOVE) m_pMoveWith->Blocked( this ); - if(!FNullEnt(m_pChildMoveWith)) - { - if(m_pChildMoveWith == pOther) - { - //ALERT(at_console, "I'am blocked by my child!\n"); - Use( NULL, NULL, USE_OFF, 0 ); - } - } - - // Keep "movewith" entities in line - UTIL_AssignOrigin(this, pev->origin); - - if ( gpGlobals->time < m_flActivateFinished) - return; - - m_flActivateFinished = gpGlobals->time + 0.5; - - if (pev->dmg) - if (m_hActivator) - pOther->TakeDamage( pev, m_hActivator->pev, pev->dmg, DMG_CRUSH ); //AJH Attribute damage to he who switched me. - else - pOther->TakeDamage( pev, pev, pev->dmg, DMG_CRUSH ); -} - - -void CFuncTrain :: Use( CBaseEntity *pActivator, CBaseEntity *pCaller, USE_TYPE useType, float value ) -{ - if ( ShouldToggle( useType ) ) - { - m_hActivator = pActivator; //AJH - - if (pev->spawnflags & SF_TRAIN_WAIT_RETRIGGER) - { - // Move toward my target -// ALERT(at_console, "Unset Retrigger (use)\n"); - pev->spawnflags &= ~SF_TRAIN_WAIT_RETRIGGER; - Next(); - } - else - { -// ALERT(at_console, "Set Retrigger (use)\n"); - pev->spawnflags |= SF_TRAIN_WAIT_RETRIGGER; - // Pop back to last target if it's available - if ( pev->enemy ) - pev->target = pev->enemy->v.targetname; - - DontThink(); - UTIL_SetVelocity(this, g_vecZero); - UTIL_SetAvelocity(this, g_vecZero); - m_iState = STATE_OFF; -// pev->velocity = g_vecZero; - - if ( pev->noiseMovement ) - STOP_SOUND( edict(), CHAN_STATIC, (char*)STRING(pev->noiseMovement) ); - - if ( pev->noiseStopMoving ) - EMIT_SOUND (ENT(pev), CHAN_VOICE, (char*)STRING(pev->noiseStopMoving), m_volume, ATTN_NORM); - } - } -} - - -void CFuncTrain :: Wait( void ) -{ -// ALERT(at_console, "Wait t %s, m %s\n", STRING(pev->target), STRING(pev->message)); - if (m_pSequence) - m_pSequence->ArrivalNotify(); - - // Fire the pass target if there is one - if ( m_pevCurrentTarget->message ) - { - FireTargets( STRING(m_pevCurrentTarget->message), this, this, USE_TOGGLE, 0 ); - if ( FBitSet( m_pevCurrentTarget->spawnflags, SF_CORNER_FIREONCE ) ) - m_pevCurrentTarget->message = 0; - } - - // need pointer to LAST target. - if ( FBitSet (m_pevCurrentTarget->spawnflags , SF_TRAIN_WAIT_RETRIGGER ) || ( pev->spawnflags & SF_TRAIN_WAIT_RETRIGGER ) ) - { -// if (FBitSet (m_pevCurrentTarget->spawnflags , SF_TRAIN_WAIT_RETRIGGER )) -// ALERT(at_console, "Wait: wait for retrigger from path %s\n", STRING(m_pevCurrentTarget->targetname)); -// else -// ALERT(at_console, "Wait: wait for retrigger from train\n"); - pev->spawnflags |= SF_TRAIN_WAIT_RETRIGGER; - m_iState = STATE_OFF; - // clear the sound channel. - if ( pev->noiseMovement ) - STOP_SOUND( edict(), CHAN_STATIC, (char*)STRING(pev->noiseMovement) ); - - if ( pev->noiseStopMoving ) - EMIT_SOUND (ENT(pev), CHAN_VOICE, (char*)STRING(pev->noiseStopMoving), m_volume, ATTN_NORM); - - UTIL_SetVelocity(this, g_vecZero); - UTIL_SetAvelocity(this, g_vecZero); - DontThink(); - return; - } - - // ALERT ( at_console, "%f\n", m_flWait ); - - if (m_flWait != 0) - {// -1 wait will wait forever! - m_iState = STATE_OFF; - UTIL_SetAvelocity(this, g_vecZero); - UTIL_SetVelocity(this, g_vecZero); - SetNextThink( m_flWait ); - if ( pev->noiseMovement ) - STOP_SOUND( edict(), CHAN_STATIC, (char*)STRING(pev->noiseMovement) ); - if ( pev->noiseStopMoving ) - EMIT_SOUND (ENT(pev), CHAN_VOICE, (char*)STRING(pev->noiseStopMoving), m_volume, ATTN_NORM); - SetThink(&CFuncTrain :: Next ); -// ALERT(at_console, "Wait: doing Next in %f\n", m_flWait); - } - else - { -// ALERT(at_console, "Wait: doing Next now\n"); - Next();// do it right now! - } -} - -// -// Train next - path corner needs to change to next target -// -void CFuncTrain :: Next( void ) -{ - CBaseEntity *pTarg; - - // now find our next target - pTarg = GetNextTarget(); - - if ( !pTarg ) - { - // no destination, just come to a halt - m_iState = STATE_OFF; - UTIL_SetVelocity(this, g_vecZero); - UTIL_SetAvelocity(this, g_vecZero); - - if ( pev->noiseMovement ) - STOP_SOUND( edict(), CHAN_STATIC, (char*)STRING(pev->noiseMovement) ); - // Play stop sound - if ( pev->noiseStopMoving ) - EMIT_SOUND (ENT(pev), CHAN_VOICE, (char*)STRING(pev->noiseStopMoving), m_volume, ATTN_NORM); - - return; - } - - // Save last target in case we need to find it again - pev->message = pev->target; - -// if (m_pevCurrentTarget) -// ALERT(at_console, "Next, pTarg %s, pevTarg %s\n", STRING(pTarg->pev->targetname), STRING(m_pevCurrentTarget->targetname)); -// else -// ALERT(at_console, "Next, pTarg %s, pevTarg null\n", STRING(pTarg->pev->targetname)); - - if (pev->spawnflags & SF_TRAIN_REVERSE && m_pSequence) - { - //LRC - search backwards - CBaseEntity *pSearch = m_pSequence->m_pDestination; - while (pSearch) - { - if (FStrEq(STRING(pSearch->pev->target), STRING(pev->target))) - { - // pSearch leads to the current corner, so it's the next thing we're moving to. - pev->target = pSearch->pev->targetname; -// ALERT(at_console, "Next, pSearch %s\n", STRING(pSearch->pev->targetname)); - break; - } - pSearch = pSearch->GetNextTarget(); - } - } - else - { - pev->target = pTarg->pev->target; - } - -// ALERT(at_console, "Next, new pevtarget %s, new message %s\n", STRING(pev->target), STRING(pev->message)); - - m_flWait = pTarg->GetDelay(); - - // don't copy speed from target if it is 0 (uninitialized) - if ( m_pevCurrentTarget ) - { - if ( m_pevCurrentTarget->speed != 0 ) - { - switch ((int)(m_pevCurrentTarget->armortype)) - { - case PATHSPEED_SET: - pev->speed = m_pevCurrentTarget->speed; - ALERT( at_aiconsole, "Train %s speed set to %4.2f\n", STRING(pev->targetname), pev->speed ); - break; - case PATHSPEED_ACCEL: - pev->speed += m_pevCurrentTarget->speed; - ALERT( at_aiconsole, "Train %s speed accel to %4.2f\n", STRING(pev->targetname), pev->speed ); - break; - case PATHSPEED_TIME: - float distance; - if (pev->spawnflags & SF_TRAIN_SETORIGIN) - distance = (pev->origin - pTarg->pev->origin).Length(); - else - distance = (pev->origin - (pTarg->pev->origin - (pev->mins + pev->maxs) * 0.5)).Length(); - - pev->speed = distance / m_pevCurrentTarget->speed; - ALERT( at_aiconsole, "Train %s speed to %4.2f (timed)\n", STRING(pev->targetname), pev->speed ); - break; - } - } - - if (m_pevCurrentTarget->spawnflags & SF_CORNER_AVELOCITY) - { - m_vecAvelocity = pTarg->pev->avelocity; - UTIL_SetAvelocity(this, m_vecAvelocity); - //pev->avelocity = pTarg->pev->avelocity; //LRC - } - - if (m_pevCurrentTarget->armorvalue) - { - UTIL_AssignAngles(this, m_pevCurrentTarget->angles); - //pev->angles = m_pevCurrentTarget->angles; //LRC - if we just passed a "turn to face" corner, set angle exactly. - } - } - - m_pevCurrentTarget = pTarg->pev;// keep track of this since path corners change our target for us. - - pev->enemy = pTarg->edict();//hack - - if( FBitSet(pTarg->pev->spawnflags, SF_CORNER_TELEPORT) ) //LRC - cosmetic change to use pTarg - { - // Path corner has indicated a teleport to the next corner. - SetBits(pev->effects, EF_NOINTERP); - if (m_pMoveWith) - { - if (pev->spawnflags & SF_TRAIN_SETORIGIN) - UTIL_AssignOrigin(this, pTarg->pev->origin - m_pMoveWith->pev->origin ); - else - UTIL_AssignOrigin(this, pTarg->pev->origin - (pev->mins + pev->maxs) * 0.5 - m_pMoveWith->pev->origin ); - } - else - { - if (pev->spawnflags & SF_TRAIN_SETORIGIN) - UTIL_AssignOrigin(this, pTarg->pev->origin ); - else - UTIL_AssignOrigin(this, pTarg->pev->origin - (pev->mins + pev->maxs) * 0.5 ); - } - - if (pTarg->pev->armorvalue) //LRC - "teleport and turn to face" means you set an angle as you teleport. - { - UTIL_AssignAngles(this, pTarg->pev->angles); - //pev->angles = pTarg->pev->angles; - } - - Wait(); // Get on with doing the next path corner. - } - else - { - // Normal linear move. - - // CHANGED this from CHAN_VOICE to CHAN_STATIC around OEM beta time because trains should - // use CHAN_STATIC for their movement sounds to prevent sound field problems. - // this is not a hack or temporary fix, this is how things should be. (sjb). - if ( pev->noiseMovement ) - STOP_SOUND( edict(), CHAN_STATIC, (char*)STRING(pev->noiseMovement) ); - if ( pev->noiseMovement ) - EMIT_SOUND (ENT(pev), CHAN_STATIC, (char*)STRING(pev->noiseMovement), m_volume, ATTN_NORM); - - ClearBits(pev->effects, EF_NOINTERP); - SetMoveDone(&CFuncTrain :: Wait ); - - if (pTarg->pev->armorvalue) //LRC - "turn to face" the next corner - { - Vector vTemp = pev->angles; - FixupAngles( vTemp ); - UTIL_AssignAngles(this, vTemp); - Vector oDelta = pTarg->pev->origin - pev->origin; - Vector aDelta = pTarg->pev->angles - pev->angles; - float timeTaken = oDelta.Length() / pev->speed; - m_vecAvelocity = aDelta / timeTaken; - //pev->avelocity = aDelta / timeTaken; - } - - UTIL_SetAvelocity(this, m_vecAvelocity); - - m_iState = STATE_ON; - - if (m_pMoveWith) - { - if (pev->spawnflags & SF_TRAIN_SETORIGIN) - LinearMove( pTarg->pev->origin - m_pMoveWith->pev->origin, pev->speed ); - else - LinearMove (pTarg->pev->origin - (pev->mins + pev->maxs)* 0.5 - m_pMoveWith->pev->origin, pev->speed); - } - else - { - if (pev->spawnflags & SF_TRAIN_SETORIGIN) - LinearMove( pTarg->pev->origin, pev->speed ); - else - LinearMove (pTarg->pev->origin - (pev->mins + pev->maxs)* 0.5, pev->speed); - } - -// ALERT(at_console, "Next: LMove done\n"); -// ALERT(at_console, "Next ends, nextthink %f, flags %f\n", pev->nextthink, m_iLFlags); - } -} - -//LRC- called by Activate. (but not when a game is loaded.) -void CFuncTrain :: PostSpawn( void ) -{ - CBaseEntity *pTarget = UTIL_FindEntityByTargetname (NULL, STRING(pev->target) ); - entvars_t *pevTarg; - - m_iState = STATE_OFF; - - if (pTarget) - { - pevTarg = pTarget->pev; - } - else - { - ALERT(at_debug, "Missing train target \"%s\"\n", STRING(pev->target)); - return; - } - - pev->message = pevTarg->targetname; //LRC - record the old target so that we can find it again - pev->target = pevTarg->target; - m_pevCurrentTarget = pevTarg;// keep track of this since path corners change our target for us. - - if (pev->avelocity != g_vecZero) - { - m_vecAvelocity = pev->avelocity; - UTIL_SetAvelocity(this, g_vecZero); - } - - if (pev->spawnflags & SF_TRAIN_SETORIGIN) - { - m_vecSpawnOffset = m_vecSpawnOffset + pevTarg->origin - pev->origin; - if (m_pMoveWith) - UTIL_AssignOrigin (this, pevTarg->origin - m_pMoveWith->pev->origin ); - else - UTIL_AssignOrigin (this, pevTarg->origin ); - } - else - { - m_vecSpawnOffset = m_vecSpawnOffset + (pevTarg->origin - (pev->mins + pev->maxs) * 0.5) - pev->origin; - if (m_pMoveWith) - UTIL_AssignOrigin (this, pevTarg->origin - (pev->mins + pev->maxs) * 0.5 - m_pMoveWith->pev->origin ); - else - UTIL_AssignOrigin (this, pevTarg->origin - (pev->mins + pev->maxs) * 0.5 ); - } - - if ( FStringNull(pev->targetname) || pev->spawnflags & SF_TRAIN_START_ON) - { // not triggered, so start immediately - SetNextThink( 1.5 ); - SetThink(&CFuncTrain :: ThinkDoNext ); - } - else - { -// ALERT(at_console, "Set Retrigger (postspawn)\n"); - pev->spawnflags |= SF_TRAIN_WAIT_RETRIGGER; - } - -// ALERT(at_console, "func_train postspawn: origin %f %f %f\n", pev->origin.x, pev->origin.y, pev->origin.z); -} - -void CFuncTrain :: ThinkDoNext( void ) -{ - SetNextThink( 0.1 ); - SetThink(&CFuncTrain :: Next ); -} - -//LRC -void CFuncTrain :: StartSequence(CTrainSequence *pSequence) -{ - m_pSequence = pSequence; -// ALERT(at_console, "Unset Retrigger (startsequence)\n"); - pev->spawnflags &= ~SF_TRAIN_WAIT_RETRIGGER; -// m_iState = STATE_ON; - //... -} - -//LRC -void CFuncTrain :: StopSequence( ) -{ - m_pSequence = NULL; -// pev->spawnflags &= ~SF_TRAIN_WAIT_RETRIGGER; - pev->spawnflags &= ~SF_TRAIN_REVERSE; - Use(this, this, USE_OFF, 0); - //... -} - -/*QUAKED func_train (0 .5 .8) ? -Trains are moving platforms that players can ride. -The targets origin specifies the min point of the train at each corner. -The train spawns at the first target it is pointing at. -If the train is the target of a button or trigger, it will not begin moving until activated. -speed default 100 -dmg default 2 -sounds -1) ratchet metal -*/ - -void CFuncTrain :: Spawn( void ) -{ - Precache(); - if (pev->speed == 0) - pev->speed = 100; - - if ( FStringNull(pev->target) ) - ALERT(at_debug, "func_train \"%s\" has no target\n", STRING(pev->targetname)); - - if (pev->dmg == 0) - pev->dmg = 2; - else if (pev->dmg == -1) //LRC- a train that doesn't crush people! - pev->dmg = 0; - - pev->movetype = MOVETYPE_PUSH; - - if ( FBitSet (pev->spawnflags, SF_TRACKTRAIN_PASSABLE) ) - pev->solid = SOLID_NOT; - else - pev->solid = SOLID_BSP; - - SET_MODEL( ENT(pev), STRING(pev->model) ); - UTIL_SetSize (pev, pev->mins, pev->maxs); - UTIL_SetOrigin(this, pev->origin); - - m_iState = STATE_OFF; - - if ( m_volume == 0 ) - m_volume = 0.85; -} - -//LRC - making movement sounds which continue after a game is loaded. -void CFuncTrain :: SoundSetup( void ) -{ - EMIT_SOUND (ENT(pev), CHAN_STATIC, (char*)STRING(pev->noiseMovement), m_volume, ATTN_NORM); - SetNextThink( m_fStoredThink - pev->ltime ); -// ALERT(at_console, "SoundSetup: mfNT %f, pevNT %f, stored was %f, time %f", m_fNextThink, pev->nextthink, m_fStoredThink, pev->ltime ); - m_fStoredThink = 0; - SetThink(&CFuncTrain :: LinearMoveDone ); -} - -//LRC -void CFuncTrain :: ThinkCorrection( void ) -{ - if (m_fStoredThink && pev->nextthink != m_fPevNextThink) - { -// ALERT(at_console, "StoredThink Correction for train \"%s\", %f -> %f\n", STRING(pev->targetname), m_fStoredThink, m_fStoredThink + pev->nextthink - m_fPevNextThink); - m_fStoredThink += pev->nextthink - m_fPevNextThink; - } - - CBasePlatTrain::ThinkCorrection(); -} - -void CFuncTrain :: Precache( void ) -{ - CBasePlatTrain::Precache(); - - //LRC - continue the movement sound after loading a game - if (m_iState == STATE_ON && pev->noiseMovement) - { - // we can't set up SFX during precache, so get a think to do it. - // Unfortunately, since we're moving, we must be already thinking. - // So we store the current think time, and will restore it after SFX are done. - if (!m_fStoredThink) - m_fStoredThink = m_fNextThink; - SetNextThink( 0.1 ); -// ALERT(at_console, "preparing SoundSetup: stored %f, mfNT %f, pevNT %f, ltime %f", m_fStoredThink, m_fNextThink, pev->nextthink, pev->ltime); - SetThink(&CFuncTrain :: SoundSetup ); - } -#if 0 // obsolete - // otherwise use preset sound - switch (m_sounds) - { - case 0: - pev->noise = 0; - pev->noise1 = 0; - break; - - case 1: - PRECACHE_SOUND ("plats/train2.wav"); - PRECACHE_SOUND ("plats/train1.wav"); - pev->noise = MAKE_STRING("plats/train2.wav"); - pev->noise1 = MAKE_STRING("plats/train1.wav"); - break; - - case 2: - PRECACHE_SOUND ("plats/platmove1.wav"); - PRECACHE_SOUND ("plats/platstop1.wav"); - pev->noise = MAKE_STRING("plats/platstop1.wav"); - pev->noise1 = MAKE_STRING("plats/platmove1.wav"); - break; - } -#endif -} - -void CFuncTrain::OverrideReset( void ) -{ - CBaseEntity *pTarg; - - // Are we moving? - if ( m_iState == STATE_ON ) //pev->velocity != g_vecZero && pev->nextthink != 0 ) - { - pev->target = pev->message; - // now find our next target - pTarg = GetNextTarget(); - if ( !pTarg ) - { - DontThink(); - UTIL_SetVelocity(this, g_vecZero); - m_iState = STATE_OFF; - } - else // Keep moving for 0.1 secs, then find path_corner again and restart - { - SetThink(&CFuncTrain:: Next ); - SetNextThink( 0.1 ); - } - } -} - - - - -// --------------------------------------------------------------------- -// -// Track Train -// -// --------------------------------------------------------------------- -void CFuncTrackTrain :: Spawn( void ) -{ - if ( pev->speed == 0 ) - m_speed = 100; - else - m_speed = pev->speed; - - pev->speed = 0; - pev->velocity = g_vecZero; // why do they set this stuff? --LRC - m_vecBaseAvel = pev->avelocity; //LRC - save it for later - pev->avelocity = g_vecZero; - pev->impulse = m_speed; - - m_dir = 1; - - if ( FStringNull(pev->target) ) - { - if ( FStringNull(pev->targetname) ) - ALERT( at_debug, "func_tracktrain with no target\n" ); - else - ALERT( at_debug, "func_tracktrain %s has no target\n", STRING(pev->targetname)); - } - - if ( pev->spawnflags & SF_TRACKTRAIN_PASSABLE ) - pev->solid = SOLID_NOT; - else - pev->solid = SOLID_BSP; - pev->movetype = MOVETYPE_PUSH; - - SET_MODEL( ENT(pev), STRING(pev->model) ); - - UTIL_SetSize( pev, pev->mins, pev->maxs ); - UTIL_SetOrigin( this, pev->origin ); -// ALERT(at_console, "SpawnOrigin %f %f %f\n", pev->origin.x, pev->origin.y, pev->origin.z); - - // Cache off placed origin for train controls - pev->oldorigin = pev->origin; - - m_controlMins = pev->mins; - m_controlMaxs = pev->maxs; - m_controlMaxs.z += 72; -// start trains on the next frame, to make sure their targets have had -// a chance to spawn/activate - NextThink( 0.1, FALSE ); - SetThink(&CFuncTrackTrain :: Find ); - Precache(); -} - -void CFuncTrackTrain :: Precache( void ) -{ - if (m_flVolume == 0.0) - m_flVolume = 1.0; - - switch (m_sounds) - { - default: - //pev->noise = 0; LRC - allow custom sounds to be set in worldcraft. - break; - case 1: pev->noise = MAKE_STRING("plats/ttrain1.wav");break; - case 2: pev->noise = MAKE_STRING("plats/ttrain2.wav");break; - case 3: pev->noise = MAKE_STRING("plats/ttrain3.wav");break; - case 4: pev->noise = MAKE_STRING("plats/ttrain4.wav");break; - case 5: pev->noise = MAKE_STRING("plats/ttrain6.wav");break; - case 6: pev->noise = MAKE_STRING("plats/ttrain7.wav");break; - } - - if (FStringNull(pev->noise1)) - pev->noise1 = MAKE_STRING("plats/ttrain_brake1.wav"); - - if (FStringNull(pev->noise2)) - pev->noise2 = MAKE_STRING("plats/ttrain_start1.wav"); - - if (pev->noise) - PRECACHE_SOUND((char*)STRING(pev->noise)); //LRC - PRECACHE_SOUND((char*)STRING(pev->noise1)); - PRECACHE_SOUND((char*)STRING(pev->noise2)); - - m_usAdjustPitch = PRECACHE_EVENT( 1, "evTrain" ); -} - -TYPEDESCRIPTION CFuncTrackTrain::m_SaveData[] = -{ - DEFINE_FIELD( CFuncTrackTrain, m_ppath, FIELD_CLASSPTR ), - DEFINE_FIELD( CFuncTrackTrain, m_length, FIELD_FLOAT ), - DEFINE_FIELD( CFuncTrackTrain, m_height, FIELD_FLOAT ), - DEFINE_FIELD( CFuncTrackTrain, m_speed, FIELD_FLOAT ), - DEFINE_FIELD( CFuncTrackTrain, m_dir, FIELD_FLOAT ), - DEFINE_FIELD( CFuncTrackTrain, m_startSpeed, FIELD_FLOAT ), - DEFINE_FIELD( CFuncTrackTrain, m_controlMins, FIELD_VECTOR ), - DEFINE_FIELD( CFuncTrackTrain, m_controlMaxs, FIELD_VECTOR ), - DEFINE_FIELD( CFuncTrackTrain, m_sounds, FIELD_INTEGER ), - DEFINE_FIELD( CFuncTrackTrain, m_flVolume, FIELD_FLOAT ), - DEFINE_FIELD( CFuncTrackTrain, m_flBank, FIELD_FLOAT ), - DEFINE_FIELD( CFuncTrackTrain, m_oldSpeed, FIELD_FLOAT ), - DEFINE_FIELD( CFuncTrackTrain, m_vecMasterAvel, FIELD_VECTOR ), //LRC - DEFINE_FIELD( CFuncTrackTrain, m_vecBaseAvel, FIELD_VECTOR ), //LRC - DEFINE_FIELD( CFuncTrackTrain, m_pSequence, FIELD_CLASSPTR ), //LRC -}; - -IMPLEMENT_SAVERESTORE( CFuncTrackTrain, CBaseEntity ); -LINK_ENTITY_TO_CLASS( func_tracktrain, CFuncTrackTrain ); - -void CFuncTrackTrain :: KeyValue( KeyValueData *pkvd ) -{ - if (FStrEq(pkvd->szKeyName, "wheels")) - { - m_length = atof(pkvd->szValue); - pkvd->fHandled = TRUE; - } - else if (FStrEq(pkvd->szKeyName, "height")) - { - m_height = atof(pkvd->szValue); - pkvd->fHandled = TRUE; - } - else if (FStrEq(pkvd->szKeyName, "startspeed")) - { - m_startSpeed = atof(pkvd->szValue); - pkvd->fHandled = TRUE; - } - else if (FStrEq(pkvd->szKeyName, "sounds")) - { - m_sounds = atoi(pkvd->szValue); - pkvd->fHandled = TRUE; - } - else if (FStrEq(pkvd->szKeyName, "custommovesound")) - { - pev->noise = ALLOC_STRING(pkvd->szValue); - pkvd->fHandled = TRUE; - } - else if (FStrEq(pkvd->szKeyName, "custombrakesound")) - { - pev->noise1 = ALLOC_STRING(pkvd->szValue); - pkvd->fHandled = TRUE; - } - else if (FStrEq(pkvd->szKeyName, "customstartsound")) - { - pev->noise2 = ALLOC_STRING(pkvd->szValue); - pkvd->fHandled = TRUE; - } - else if (FStrEq(pkvd->szKeyName, "volume")) - { - m_flVolume = (float) (atoi(pkvd->szValue)); - m_flVolume *= 0.1; - pkvd->fHandled = TRUE; - } - else if (FStrEq(pkvd->szKeyName, "bank")) - { - m_flBank = atof(pkvd->szValue); - pkvd->fHandled = TRUE; - } - else - CBaseEntity::KeyValue( pkvd ); -} - - -void CFuncTrackTrain :: NextThink( float thinkTime, BOOL alwaysThink ) -{ - if ( alwaysThink ) -// m_iLFlags |= LF_ALWAYSTHINK; - pev->flags |= FL_ALWAYSTHINK; - else -// m_iLFlags &= ~LF_ALWAYSTHINK; - pev->flags &= ~FL_ALWAYSTHINK; - - SetNextThink( thinkTime, TRUE ); -} - - -void CFuncTrackTrain :: Blocked( CBaseEntity *pOther ) -{ - entvars_t *pevOther = pOther->pev; - - //g-cont. simple recursive anouncer for parent system - //tell parent who blocked his - if(!FNullEnt(m_pMoveWith) && m_iLFlags & LF_PARENTMOVE) m_pMoveWith->Blocked( this ); - if(!FNullEnt(m_pChildMoveWith)) - { - if(m_pChildMoveWith == pOther) - { - //ALERT(at_console, "I'am blocked by my child!\n"); - Use( NULL, NULL, USE_OFF, 0 ); - } - } - - // Blocker is on-ground on the train - if ( FBitSet( pevOther->flags, FL_ONGROUND ) && VARS(pevOther->groundentity) == pev ) - { - float deltaSpeed = fabs(pev->speed); - if ( deltaSpeed > 50 ) - deltaSpeed = 50; - if ( !pevOther->velocity.z ) - pevOther->velocity.z += deltaSpeed; - return; - } - else - pevOther->velocity = (pevOther->origin - pev->origin ).Normalize() * pev->dmg; - - ALERT( at_aiconsole, "TRAIN(%s): Blocked by %s (dmg:%.2f)\n", STRING(pev->targetname), STRING(pOther->pev->classname), pev->dmg ); - - if ( pev->dmg <= 0 )// we can't hurt this thing, so we're not concerned with it - return; - - if (m_hActivator) - pOther->TakeDamage( pev, m_hActivator->pev, pev->dmg, DMG_CRUSH ); //AJH Attribute damage to he who switched me. - else - pOther->TakeDamage( pev, pev, pev->dmg, DMG_CRUSH ); -} - - -void CFuncTrackTrain :: Use( CBaseEntity *pActivator, CBaseEntity *pCaller, USE_TYPE useType, float value ) -{ -// ALERT(at_debug, "TRAIN: use\n"); - - m_hActivator = pActivator; //AJH - if ( useType != USE_SET ) - { - if ( !ShouldToggle( useType, (pev->speed != 0) ) ) - return; - - if ( pev->speed == 0 ) - { - pev->speed = m_speed * m_dir; - - PostponeNext(); - } - else - { - pev->speed = 0; - UTIL_SetVelocity(this, g_vecZero); //LRC - //pev->velocity = g_vecZero; - if (!FBitSet(pev->spawnflags, SF_TRACKTRAIN_AVELOCITY)) - UTIL_SetAvelocity(this, g_vecZero); //LRC - //pev->avelocity = g_vecZero; - StopSound(); - SetThink( NULL ); - } - } - else - { - float delta = value; - - delta = ((int)(pev->speed * 4) / (int)m_speed)*0.25 + 0.25 * delta; - if ( delta > 1 ) - delta = 1; - else if ( delta < -1 ) - delta = -1; - if ( pev->spawnflags & SF_TRACKTRAIN_FORWARDONLY ) - { - if ( delta < 0 ) - delta = 0; - } - pev->speed = m_speed * delta; - - if ( pev->spawnflags & SF_TRACKTRAIN_AVEL_GEARS ) - { - UTIL_SetAvelocity(this, m_vecMasterAvel * delta); - //pev->avelocity = m_vecMasterAvel * delta; //LRC - } - - if(m_ppath == NULL) - { - delta = 0; //G-Cont. Set speed to 0, and don't controls, if tracktrain on trackchange - return; - } - PostponeNext(); - ALERT( at_aiconsole, "TRAIN(%s), speed to %.2f\n", STRING(pev->targetname), pev->speed ); - } -} - -#define TRAIN_STARTPITCH 60 -#define TRAIN_MAXPITCH 200 -#define TRAIN_MAXSPEED 1000 // approx max speed for sound pitch calculation - -void CFuncTrackTrain :: StopSound( void ) -{ - // if sound playing, stop it - if (m_soundPlaying && pev->noise) - { - if (m_sounds) //LRC - flashy event-based method, for normal sounds. - { - unsigned short us_encode; - unsigned short us_sound = ( ( unsigned short )( m_sounds ) & 0x0007 ) << 12; - - us_encode = us_sound; - - PLAYBACK_EVENT_FULL( FEV_RELIABLE | FEV_UPDATE, edict(), m_usAdjustPitch, 0.0, - (float *)&g_vecZero, (float *)&g_vecZero, 0.0, 0.0, us_encode, 0, 1, 0 ); - } - else - { - //LRC - down-to-earth method, for custom sounds. - STOP_SOUND(ENT(pev), CHAN_STATIC, (char*)STRING(pev->noise)); - } - - EMIT_SOUND_DYN(ENT(pev), CHAN_ITEM, (char*)STRING(pev->noise1), m_flVolume, ATTN_NORM, 0, 100); - } - - m_soundPlaying = 0; -} - -// update pitch based on speed, start sound if not playing -// NOTE: when train goes through transition, m_soundPlaying should go to 0, -// which will cause the looped sound to restart. - -void CFuncTrackTrain :: UpdateSound( void ) -{ - float flpitch; - - if (!pev->noise) - return; - - flpitch = TRAIN_STARTPITCH + (abs(pev->speed) * (TRAIN_MAXPITCH - TRAIN_STARTPITCH) / TRAIN_MAXSPEED); - - if (!m_soundPlaying) - { - // play startup sound for train - EMIT_SOUND_DYN(ENT(pev), CHAN_ITEM, (char*)STRING(pev->noise2), m_flVolume, ATTN_NORM, 0, 100); - EMIT_SOUND_DYN(ENT(pev), CHAN_STATIC, (char*)STRING(pev->noise), m_flVolume, ATTN_NORM, 0, (int) flpitch); - m_soundPlaying = 1; - } - else - { - if (m_sounds) //LRC - flashy event-based method, for normal sounds. - { - // volume 0.0 - 1.0 - 6 bits - // m_sounds 3 bits - // flpitch = 6 bits - // 15 bits total - - unsigned short us_encode; - unsigned short us_sound = ( ( unsigned short )( m_sounds ) & 0x0007 ) << 12; - unsigned short us_pitch = ( ( unsigned short )( flpitch / 10.0 ) & 0x003f ) << 6; - unsigned short us_volume = ( ( unsigned short )( m_flVolume * 40.0 ) & 0x003f ); - - us_encode = us_sound | us_pitch | us_volume; - - PLAYBACK_EVENT_FULL( FEV_RELIABLE | FEV_UPDATE, edict(), m_usAdjustPitch, 0.0, - (float *)&g_vecZero, (float *)&g_vecZero, 0.0, 0.0, us_encode, 0, 0, 0 ); - } - else - { - //LRC - down-to-earth method, for custom sounds. - // update pitch - EMIT_SOUND_DYN(ENT(pev), CHAN_STATIC, (char*)STRING(pev->noise), m_flVolume, ATTN_NORM, SND_CHANGE_PITCH, (int) flpitch); - } - } -} - -void CFuncTrackTrain::OverrideReset( void ) -{ - NextThink( 0.1, FALSE ); - SetThink(&CFuncTrackTrain:: NearestPath ); -} - -void CFuncTrackTrain :: ClearPointers( void ) -{ - CBaseEntity :: ClearPointers(); - m_ppath = NULL; -} - -void CFuncTrackTrain :: PostponeNext( void ) -{ - //g-cont. well... - if(m_pAssistLink) UTIL_DesiredAction(this); - else DesiredAction(); //this simply fix LAARGE BUG with func_traktrain in spirit ;) g-cont -} - -void CFuncTrackTrain :: DesiredAction( void ) // Next( void ) -{ - float time = 0.5; - -// ALERT(at_console, "Next: pos %f %f %f, vel %f %f %f. Child pos %f %f %f, vel %f %f %f\n", pev->origin.x, pev->origin.y, pev->origin.z, pev->velocity.x, pev->velocity.y, pev->velocity.z, m_pChildMoveWith->pev->origin.x, m_pChildMoveWith->pev->origin.y, m_pChildMoveWith->pev->origin.z, m_pChildMoveWith->pev->velocity.x, m_pChildMoveWith->pev->velocity.y, m_pChildMoveWith->pev->velocity.z); -// UTIL_DesiredInfo(this); - -// static float stime; -// ALERT(at_console, "TRAIN: think delay = %f\n", gpGlobals->time - stime); -// stime = gpGlobals->time; - - if ( !pev->speed ) - { -// ALERT(at_console, "TRAIN: no speed\n"); - UTIL_SetVelocity(this, g_vecZero); - DontThink(); - ALERT( at_aiconsole, "TRAIN(%s): Speed is 0\n", STRING(pev->targetname) ); - StopSound(); - return; - } - -// if ( !m_ppath ) -// m_ppath = UTIL_FindEntityByTargetname( NULL, STRING(pev->target) ); - if ( !m_ppath ) - { -// ALERT(at_debug, "TRAIN: no path\n"); - UTIL_SetVelocity(this, g_vecZero); - DontThink(); - ALERT( at_aiconsole, "TRAIN(%s): Lost path\n", STRING(pev->targetname) ); - StopSound(); - return; - } - - UpdateSound(); - - Vector nextPos = pev->origin; - - nextPos.z -= m_height; - CPathTrack *pnext = m_ppath->LookAhead( &nextPos, pev->speed * 0.1, 1 ); - nextPos.z += m_height; - - UTIL_SetVelocity( this, (nextPos - pev->origin) * 10 ); //LRC -// Vector vD = (nextPos - pev->origin) * 10; -// ALERT(at_debug, "TRAIN: Set vel to (%f %f %f)\n", vD.x, vD.y, vD.z); - //pev->velocity = (nextPos - pev->origin) * 10; - Vector nextFront = pev->origin; - - nextFront.z -= m_height; - if ( m_length > 0 ) - m_ppath->LookAhead( &nextFront, m_length, 0 ); - else - m_ppath->LookAhead( &nextFront, 100, 0 ); - nextFront.z += m_height; - - if (!FBitSet(pev->spawnflags, SF_TRACKTRAIN_AVELOCITY)) //LRC - { - Vector delta = nextFront - pev->origin; - - Vector angles = UTIL_VecToAngles( delta ); - // The train actually points west - angles.y += 180; //LRC, FIXME: add a 'built facing' field. - - // !!! All of this crap has to be done to make the angles not wrap around, revisit this. - FixupAngles( angles ); - FixupAngles( pev->angles ); - - if ( !pnext || (delta.x == 0 && delta.y == 0) ) - angles = pev->angles; - - float vy, vx, vz; - if ( !(pev->spawnflags & SF_TRACKTRAIN_NOPITCH) ) - vx = 10*UTIL_AngleDistance( angles.x, pev->angles.x ); - else - vx = m_vecBaseAvel.x; - - if ( !(pev->spawnflags & SF_TRACKTRAIN_NOYAW) ) //LRC - vy = 10*UTIL_AngleDistance( angles.y, pev->angles.y ); - else - vy = m_vecBaseAvel.y; - - if ( m_flBank != 0 ) - { - if ( pev->avelocity.y < -5 ) - vz = UTIL_AngleDistance( UTIL_ApproachAngle( -m_flBank, pev->angles.z, m_flBank*2 ), pev->angles.z); - else if ( pev->avelocity.y > 5 ) - vz = UTIL_AngleDistance( UTIL_ApproachAngle( m_flBank, pev->angles.z, m_flBank*2 ), pev->angles.z); - else - vz = UTIL_AngleDistance( UTIL_ApproachAngle( 0, pev->angles.z, m_flBank*4 ), pev->angles.z) * 4; - } - else - { - vz = m_vecBaseAvel.z; - } - - UTIL_SetAvelocity(this, Vector(vx, vy, vz)); - //pev->avelocity.y = vy; - //pev->avelocity.x = vx; - } - - if ( pnext ) - { - if ( pnext != m_ppath ) - { -// ALERT(at_debug, "TRAIN: new m_ppath %s, was %s. Origin %f %f %f\n", STRING(pnext->pev->targetname), STRING(m_ppath->pev->targetname), pev->origin.x, pev->origin.y, pev->origin.z); - CPathTrack *pFire; - if ( pev->speed >= 0 ) // check whether we're going forwards or backwards - pFire = pnext; - else - pFire = m_ppath; - - m_ppath = pnext; - // Fire the pass target if there is one - if ( pFire->pev->message ) - { - FireTargets( STRING(pFire->pev->message), this, this, USE_TOGGLE, 0 ); - if ( FBitSet( pFire->pev->spawnflags, SF_PATH_FIREONCE ) ) - pFire->pev->message = 0; - } - - if ( pFire->pev->spawnflags & SF_PATH_DISABLE_TRAIN ) - pev->spawnflags |= SF_TRACKTRAIN_NOCONTROL; - - //LRC is "match angle" set to "Yes"? If so, set the angle exactly, because we've reached the corner. - if ( pFire->pev->armorvalue == PATHMATCH_YES && pev->spawnflags & SF_TRACKTRAIN_AVELOCITY ) - { - Vector vTemp = pFire->pev->angles; - vTemp.y -= 180; //the train is actually built facing west. - UTIL_AssignAngles(this, vTemp); - //pev->angles = pFire->pev->angles; - //pev->angles.y -= 180; //the train is actually built facing west. - } - - float setting = ((int)(pev->speed*4) / (int)m_speed) / 4.0; //LRC - one of { 1, 0.75, 0.5, 0.25, 0, ... -1 } - - //LRC - if ( pFire->pev->frags == PATHTURN_RESET ) - { - pev->spawnflags &= ~(SF_TRACKTRAIN_AVEL_GEARS | SF_TRACKTRAIN_AVELOCITY); - } - else if ( pFire->pev->spawnflags & SF_PATH_AVELOCITY) - { - if ( pFire->pev->frags == PATHTURN_SET_MASTER ) - { - m_vecMasterAvel = pFire->pev->avelocity; - UTIL_SetAvelocity(this, m_vecMasterAvel * setting); - //pev->avelocity = m_vecMasterAvel * setting; - pev->spawnflags |= (SF_TRACKTRAIN_AVEL_GEARS | SF_TRACKTRAIN_AVELOCITY); - } - else if ( pFire->pev->frags == PATHTURN_SET ) - { - UTIL_SetAvelocity(this, pFire->pev->avelocity); - //pev->avelocity = pFire->pev->avelocity; - pev->spawnflags |= SF_TRACKTRAIN_AVELOCITY; - pev->spawnflags &= ~SF_TRACKTRAIN_AVEL_GEARS; - } - } - - CPathTrack* pDest; //LRC - the path_track we're heading for, after pFire. - if (pev->speed > 0) - pDest = pFire->GetNext(); - else - pDest = pFire->GetPrevious(); -// ALERT(at_debug, "and pDest is %s\n", STRING(pDest->pev->targetname)); - - //LRC - // don't look at speed from target if it is 0 (uninitialized) - if ( pFire->pev->speed != 0) - { - //ALERT( at_console, "TrackTrain setting is %d / %d = %.2f\n", (int)(pev->speed*4), (int)m_speed, setting ); - - switch ( (int)(pFire->pev->armortype) ) - { - case PATHSPEED_SET: - // Don't override speed if under user control - if (pev->spawnflags & SF_TRACKTRAIN_NOCONTROL) - pev->speed = pFire->pev->speed; - ALERT( at_aiconsole, "TrackTrain %s speed set to %4.2f\n", STRING(pev->targetname), pev->speed ); - break; - case PATHSPEED_SET_MASTER: - m_speed = pFire->pev->speed; - pev->impulse = m_speed; - pev->speed = setting * m_speed; - ALERT( at_aiconsole, "TrackTrain %s master speed set to %4.2f\n", STRING(pev->targetname), pev->speed ); - break; - case PATHSPEED_ACCEL: - m_speed += pFire->pev->speed; - pev->impulse = m_speed; - pev->speed = setting * m_speed; - ALERT( at_aiconsole, "TrackTrain %s speed accel to %4.2f\n", STRING(pev->targetname), pev->speed ); - break; - case PATHSPEED_TIME: - float distance = (pev->origin - pDest->pev->origin).Length(); - //ALERT(at_debug, "pFire=%s, distance=%.2f, ospeed=%.2f, nspeed=%.2f\n", STRING(pFire->pev->targetname), distance, pev->speed, distance / pFire->pev->speed); - m_speed = distance / pFire->pev->speed; - pev->impulse = m_speed; - pev->speed = setting * m_speed; - ALERT( at_aiconsole, "TrackTrain %s speed to %4.2f (timed)\n", STRING(pev->targetname), pev->speed ); - break; - } - } - - //LRC - if (pDest->pev->armorvalue == PATHMATCH_YES) - { - pev->spawnflags |= SF_TRACKTRAIN_AVELOCITY | SF_TRACKTRAIN_AVEL_GEARS; - Vector vTemp = pev->angles; - FixupAngles( vTemp ); - UTIL_AssignAngles(this, vTemp ); - Vector oDelta = pDest->pev->origin - pev->origin; - Vector aDelta; - if (setting > 0) - { - aDelta.x = UTIL_AngleDistance(pDest->pev->angles.x, pev->angles.x); - aDelta.y = UTIL_AngleDistance(pDest->pev->angles.y, pev->angles.y); - aDelta.z = UTIL_AngleDistance(pDest->pev->angles.z, pev->angles.z); - } - else - { - aDelta.x = UTIL_AngleDistance(pev->angles.x, pDest->pev->angles.x); - aDelta.y = UTIL_AngleDistance(pev->angles.y, pDest->pev->angles.y); - aDelta.z = UTIL_AngleDistance(pev->angles.z, pDest->pev->angles.z); - } - if (aDelta.y > 0) // the train is actually built facing west. - aDelta.y -= 180; - else - aDelta.y += 180; - float timeTaken = oDelta.Length() / m_speed; - m_vecMasterAvel = aDelta / timeTaken; - UTIL_SetAvelocity(this, setting * m_vecMasterAvel); - //pev->avelocity = setting * m_vecMasterAvel; - } - //LRC- FIXME: add support, here, for a Teleport flag. - } -// else -// { -// ALERT(at_debug, "TRAIN: same pnext\n"); -// } - SetThink(&CFuncTrackTrain :: PostponeNext ); - NextThink( time, TRUE ); - } - else // end of path, stop - { - Vector vecTemp; //LRC - StopSound(); - vecTemp = (nextPos - pev->origin); //LRC - -// ALERT(at_debug, "TRAIN: path end\n"); - -// UTIL_SetVelocity( this, (nextPos - pev->origin) * 10 ); //LRC -// pev->velocity = (nextPos - pev->origin); - if (!FBitSet(pev->spawnflags, SF_TRACKTRAIN_AVELOCITY)) //LRC - UTIL_SetAvelocity(this, g_vecZero); - //pev->avelocity = g_vecZero; - float distance = vecTemp.Length(); //LRC - //float distance = pev->velocity.Length(); - m_oldSpeed = pev->speed; - - - pev->speed = 0; - - // Move to the dead end - - // Are we there yet? - if ( distance > 0 ) - { - // no, how long to get there? - time = distance / m_oldSpeed; - UTIL_SetVelocity( this, vecTemp * (m_oldSpeed / distance) ); //LRC - //pev->velocity = pev->velocity * (m_oldSpeed / distance); - SetThink(&CFuncTrackTrain :: DeadEnd ); - NextThink( time, FALSE ); - } - else - { - UTIL_SetVelocity( this, vecTemp ); //LRC - DeadEnd(); - } - } -} - - -void CFuncTrackTrain::DeadEnd( void ) -{ - // Fire the dead-end target if there is one - CPathTrack *pTrack, *pNext; - - pTrack = m_ppath; - - ALERT( at_aiconsole, "TRAIN(%s): Dead end ", STRING(pev->targetname) ); - // Find the dead end path node - // HACKHACK -- This is bugly, but the train can actually stop moving at a different node depending on it's speed - // so we have to traverse the list to it's end. - if ( pTrack ) - { - if ( m_oldSpeed < 0 ) - { - do - { - pNext = pTrack->ValidPath( pTrack->GetPrevious(), TRUE ); - if ( pNext ) - pTrack = pNext; - } while ( pNext ); - } - else - { - do - { - pNext = pTrack->ValidPath( pTrack->GetNext(), TRUE ); - if ( pNext ) - pTrack = pNext; - } while ( pNext ); - } - } - - UTIL_SetVelocity( this, g_vecZero ); //LRC -// pev->velocity = g_vecZero; - if (!FBitSet(pev->spawnflags, SF_TRACKTRAIN_AVELOCITY)) //LRC - UTIL_SetAvelocity(this, g_vecZero ); - //pev->avelocity = g_vecZero; - if ( pTrack ) - { - ALERT( at_aiconsole, "at %s\n", STRING(pTrack->pev->targetname) ); - if ( pTrack->pev->netname ) - FireTargets( STRING(pTrack->pev->netname), this, this, USE_TOGGLE, 0 ); - } - else - ALERT( at_aiconsole, "\n" ); -} - - -void CFuncTrackTrain :: SetControls( entvars_t *pevControls ) -{ - Vector offset = pevControls->origin - pev->oldorigin; - - m_controlMins = pevControls->mins + offset; - m_controlMaxs = pevControls->maxs + offset; -} - - -BOOL CFuncTrackTrain :: OnControls( entvars_t *pevTest ) -{ - Vector offset = pevTest->origin - pev->origin; - - if ( pev->spawnflags & SF_TRACKTRAIN_NOCONTROL ) - return FALSE; - - // Transform offset into local coordinates - UTIL_MakeVectors( pev->angles ); - Vector local; - local.x = DotProduct( offset, gpGlobals->v_forward ); - local.y = -DotProduct( offset, gpGlobals->v_right ); - local.z = DotProduct( offset, gpGlobals->v_up ); - - if ( local.x >= m_controlMins.x && local.y >= m_controlMins.y && local.z >= m_controlMins.z && - local.x <= m_controlMaxs.x && local.y <= m_controlMaxs.y && local.z <= m_controlMaxs.z ) - return TRUE; - - return FALSE; -} - - -void CFuncTrackTrain :: Find( void ) -{ - m_ppath = (CPathTrack*)UTIL_FindEntityByTargetname( NULL, STRING(pev->target) ); - if ( !m_ppath ) - return; - - entvars_t *pevTarget = m_ppath->pev; - if ( !FClassnameIs( pevTarget, "path_track" ) ) - { - ALERT( at_error, "func_track_train must be on a path of path_track\n" ); - m_ppath = NULL; - return; - } - - Vector nextPos = pevTarget->origin; - nextPos.z += m_height; - - Vector look = nextPos; - look.z -= m_height; - m_ppath->LookAhead( &look, m_length, 0 ); - look.z += m_height; - - Vector vTemp = UTIL_VecToAngles( look - nextPos ); - vTemp.y += 180; - // The train actually points west - //pev->angles.y += 180; - - if ( pev->spawnflags & SF_TRACKTRAIN_NOPITCH ) - { - vTemp.x = 0; - //pev->angles.x = 0; - } - - UTIL_AssignAngles(this, vTemp); //LRC - - UTIL_AssignOrigin ( this, nextPos ); //LRC -// ALERT(at_console, "Train Find; origin %f %f %f\n", pev->origin.x, pev->origin.y, pev->origin.z); - //UTIL_SetOrigin( this, nextPos ); - NextThink( 0.1, FALSE ); -// NextThink( 8, FALSE ); //LRC - What was this for?! -// SetThink( Next ); - SetThink(&CFuncTrackTrain :: PostponeNext ); - pev->speed = m_startSpeed; - - UpdateSound(); -} - -void CFuncTrackTrain :: NearestPath( void ) -{ - CBaseEntity *pTrack = NULL; - CBaseEntity *pNearest = NULL; - float dist, closest; - - closest = 1024; - - while ((pTrack = UTIL_FindEntityInSphere( pTrack, pev->origin, 1024 )) != NULL) - { - // filter out non-tracks - if ( !(pTrack->pev->flags & (FL_CLIENT|FL_MONSTER)) && FClassnameIs( pTrack->pev, "path_track" ) ) - { - dist = (pev->origin - pTrack->pev->origin).Length(); - if ( dist < closest ) - { - closest = dist; - pNearest = pTrack; - } - } - } - - if ( !pNearest ) - { - ALERT( at_debug, "Can't find a nearby track !!!\n" ); - SetThink(NULL); - return; - } - - ALERT( at_aiconsole, "TRAIN: %s, Nearest track is %s\n", STRING(pev->targetname), STRING(pNearest->pev->targetname) ); - // If I'm closer to the next path_track on this path, then it's my real path - pTrack = ((CPathTrack *)pNearest)->GetNext(); - if ( pTrack ) - { - if ( (pev->origin - pTrack->pev->origin).Length() < (pev->origin - pNearest->pev->origin).Length() ) - pNearest = pTrack; - } - - m_ppath = (CPathTrack *)pNearest; - - if ( pev->speed != 0 ) - { - NextThink( 0.1, FALSE ); - SetThink(&CFuncTrackTrain :: PostponeNext ); - } -} - -CFuncTrackTrain *CFuncTrackTrain::Instance( edict_t *pent ) -{ - if ( FClassnameIs( pent, "func_tracktrain" ) ) - return (CFuncTrackTrain *)GET_PRIVATE(pent); - return NULL; -} - -//LRC -void CFuncTrackTrain :: StartSequence(CTrainSequence *pSequence) -{ - m_pSequence = pSequence; -// ALERT(at_console, "Unset Retrigger (startsequence)\n"); - pev->spawnflags &= ~SF_TRAIN_WAIT_RETRIGGER; - //... -} - -//LRC -void CFuncTrackTrain :: StopSequence( ) -{ - DontThink(); - m_pSequence = NULL; - //... -} - -// This class defines the volume of space that the player must stand in to control the train -class CFuncTrainControls : public CBaseEntity -{ -public: - virtual int ObjectCaps( void ) { return CBaseEntity :: ObjectCaps() & ~FCAP_ACROSS_TRANSITION; } - void Spawn( void ); - void EXPORT Find( void ); -}; -LINK_ENTITY_TO_CLASS( func_traincontrols, CFuncTrainControls ); - - -void CFuncTrainControls :: Find( void ) -{ - CBaseEntity *pTarget = NULL; - - do - { - pTarget = UTIL_FindEntityByTargetname( pTarget, STRING(pev->target) ); - } while ( pTarget && !FClassnameIs(pTarget->pev, "func_tracktrain") ); - - if ( !pTarget ) - { - ALERT( at_debug, "TrackTrainControls: No train %s\n", STRING(pev->target) ); - return; - } - - CFuncTrackTrain *ptrain = (CFuncTrackTrain*)pTarget; - ptrain->SetControls( pev ); - UTIL_Remove( this ); -} - - -void CFuncTrainControls :: Spawn( void ) -{ - pev->solid = SOLID_NOT; - pev->movetype = MOVETYPE_NONE; - SET_MODEL( ENT(pev), STRING(pev->model) ); - - UTIL_SetSize( pev, pev->mins, pev->maxs ); - UTIL_SetOrigin( this, pev->origin ); - - SetThink(&CFuncTrainControls :: Find ); - SetNextThink( 0 ); -} -// ---------------------------------------------------------------------------- -// -// Func Vehicle /Don't use this! this is undone. -// -//----------------------------------------------------------------------------- - -class CFuncVehicle : public CFuncTrackTrain -{ -public: - void CalcHeight( void ); - float m_fRoof; - float m_fFloor; - float m_nextcalc[1]; -}; - -LINK_ENTITY_TO_CLASS( func_vehicle, CFuncVehicle ); - -void CFuncVehicle::CalcHeight( void ) -{ - TraceResult tr; - Vector vecTop, vecBot; - float total = 0; - int i, j; - float forward, right, up; - Vector angles; - - angles = pev->angles; - angles.y += 180; // Flip it around so front and back are in - // the correct positions. - - FixupAngles( angles ); // Its already in the train coding - // Really basic - - UTIL_MakeVectors( angles ); - - m_fRoof = ( pev->origin + gpGlobals->v_up * ( pev->size.z * 0.5 ) ).z; - // Find the top of our vehicle. - - m_fFloor = ( pev->origin - gpGlobals->v_up * ( pev->size.z * 0.5 ) ).z; - // Find the bottom of our vehicle - - up = pev->size.z * 0.5; - - // This shoots out 100 tracelines, WAY TO MANY! - // Only used this many for local testing! - for( i = 0; i <= 10; i++ ) - { - for( j = 0; j <= 10; j++ ) - { - forward = pev->size.x * ( ( i - 5 ) * 0.1 ); - right = pev->size.y * ( ( j - 5 ) * 0.1 ); - - vecTop = pev->origin + gpGlobals->v_up * up * 1 + gpGlobals->v_forward * forward + gpGlobals->v_right * right; - vecBot = pev->origin - gpGlobals->v_up * up * 2 + gpGlobals->v_forward * forward + gpGlobals->v_right * right; - - UTIL_TraceLine( vecTop, vecBot, ignore_monsters, ENT(pev), &tr); - total += tr.vecEndPos.z; - // So we can average out later - } - } - - pev->velocity.z = ( total * 0.001 /* I.E. total / 100 , the average */ ) - ( m_fFloor - 2 ); - // Ok we know where ware the average floor should be - // Lets get moving. - - m_nextcalc[0] = gpGlobals->time + 0.5; - // Dont worry about this, in fact remove it! - // You dont have this variable, this is just there - // so it only checks the height every 0.5 seconds -} -/* -Ok all this is doing is shooting lines at the ground, gets the average of where they hit, -then compares it to where the floor of the vehicle should be, and then moves us toward it. -Real simple ehh? Now just do that for the sides of the vehicle to keep them from hitting walls. -Another thing I tried was making for single point lines and just checking to see if they were -all solid, and then just negated the velocity. -*/ - -// ---------------------------------------------------------------------------- -// -// Track changer / Train elevator -// -// ---------------------------------------------------------------------------- - -#define SF_TRACK_ACTIVATETRAIN 0x00000001 -#define SF_TRACK_RELINK 0x00000002 -#define SF_TRACK_ROTMOVE 0x00000004 -#define SF_TRACK_STARTBOTTOM 0x00000008 -#define SF_TRACK_DONT_MOVE 0x00000010 - -// -// This entity is a rotating/moving platform that will carry a train to a new track. -// It must be larger in X-Y planar area than the train, since it must contain the -// train within these dimensions in order to operate when the train is near it. -// - -typedef enum { TRAIN_SAFE, TRAIN_BLOCKING, TRAIN_FOLLOWING } TRAIN_CODE; - -class CFuncTrackChange : public CFuncPlatRot -{ -public: - void Spawn( void ); - void Precache( void ); - -// virtual void Blocked( void ); - virtual void EXPORT GoUp( void ); - virtual void EXPORT GoDown( void ); - - void KeyValue( KeyValueData* pkvd ); - void Use( CBaseEntity *pActivator, CBaseEntity *pCaller, USE_TYPE useType, float value ); - void EXPORT Find( void ); - TRAIN_CODE EvaluateTrain( CPathTrack *pcurrent ); - void UpdateTrain( Vector &dest ); - virtual void HitBottom( void ); - virtual void HitTop( void ); - void Touch( CBaseEntity *pOther ); - virtual void UpdateAutoTargets( int toggleState ); - virtual BOOL IsTogglePlat( void ) { return TRUE; } - - void DisableUse( void ) { m_use = 0; } - void EnableUse( void ) { m_use = 1; } - int UseEnabled( void ) { return m_use; } - - virtual int Save( CSave &save ); - virtual int Restore( CRestore &restore ); - static TYPEDESCRIPTION m_SaveData[]; - - virtual void OverrideReset( void ); - - - CPathTrack *m_trackTop; - CPathTrack *m_trackBottom; - - CFuncTrackTrain *m_train; - - int m_trackTopName; - int m_trackBottomName; - int m_trainName; - TRAIN_CODE m_code; - int m_targetState; - int m_use; -}; -LINK_ENTITY_TO_CLASS( func_trackchange, CFuncTrackChange ); - -TYPEDESCRIPTION CFuncTrackChange::m_SaveData[] = -{ - DEFINE_GLOBAL_FIELD( CFuncTrackChange, m_trackTop, FIELD_CLASSPTR ), - DEFINE_GLOBAL_FIELD( CFuncTrackChange, m_trackBottom, FIELD_CLASSPTR ), - DEFINE_GLOBAL_FIELD( CFuncTrackChange, m_train, FIELD_CLASSPTR ), - DEFINE_GLOBAL_FIELD( CFuncTrackChange, m_trackTopName, FIELD_STRING ), - DEFINE_GLOBAL_FIELD( CFuncTrackChange, m_trackBottomName, FIELD_STRING ), - DEFINE_GLOBAL_FIELD( CFuncTrackChange, m_trainName, FIELD_STRING ), - DEFINE_FIELD( CFuncTrackChange, m_code, FIELD_INTEGER ), - DEFINE_FIELD( CFuncTrackChange, m_targetState, FIELD_INTEGER ), - DEFINE_FIELD( CFuncTrackChange, m_use, FIELD_INTEGER ), -}; - -IMPLEMENT_SAVERESTORE( CFuncTrackChange, CFuncPlatRot ); - -void CFuncTrackChange :: Spawn( void ) -{ - Setup(); - if ( FBitSet( pev->spawnflags, SF_TRACK_DONT_MOVE ) ) - m_vecPosition2.z = pev->origin.z; - - SetupRotation(); - - if ( FBitSet( pev->spawnflags, SF_TRACK_STARTBOTTOM ) ) - { - UTIL_SetOrigin (this, m_vecPosition2); - m_toggle_state = TS_AT_BOTTOM; - pev->angles = m_start; - m_targetState = TS_AT_TOP; - } - else - { - UTIL_SetOrigin (this, m_vecPosition1); - m_toggle_state = TS_AT_TOP; - pev->angles = m_end; - m_targetState = TS_AT_BOTTOM; - } - - EnableUse(); - pev->nextthink = pev->ltime + 2.0; - SetThink(&CFuncTrackChange :: Find ); - Precache(); -} - -void CFuncTrackChange :: Precache( void ) -{ - // Can't trigger sound - PRECACHE_SOUND( "buttons/button11.wav" ); - - CFuncPlatRot::Precache(); -} - - -// UNDONE: Filter touches before re-evaluating the train. -void CFuncTrackChange :: Touch( CBaseEntity *pOther ) -{ -#if 0 - TRAIN_CODE code; - entvars_t *pevToucher = pOther->pev; -#endif -} - - - -void CFuncTrackChange :: KeyValue( KeyValueData *pkvd ) -{ - if ( FStrEq(pkvd->szKeyName, "train") ) - { - m_trainName = ALLOC_STRING( pkvd->szValue ); - pkvd->fHandled = TRUE; - } - else if ( FStrEq(pkvd->szKeyName, "toptrack") ) - { - m_trackTopName = ALLOC_STRING( pkvd->szValue ); - pkvd->fHandled = TRUE; - } - else if ( FStrEq(pkvd->szKeyName, "bottomtrack") ) - { - m_trackBottomName = ALLOC_STRING( pkvd->szValue ); - pkvd->fHandled = TRUE; - } - else - { - CFuncPlatRot::KeyValue( pkvd ); // Pass up to base class - } -} - - -void CFuncTrackChange::OverrideReset( void ) -{ - pev->nextthink = pev->ltime + 1.0; - SetThink(&CFuncTrackChange:: Find ); -} - -void CFuncTrackChange :: Find( void ) -{ - // Find track entities - CBaseEntity *pTarget; - - pTarget = UTIL_FindEntityByTargetname( NULL, STRING(m_trackTopName) ); - if ( pTarget && FClassnameIs(pTarget->pev, "path_track")) - { - m_trackTop = (CPathTrack*)pTarget; - pTarget = UTIL_FindEntityByTargetname( NULL, STRING(m_trackBottomName) ); - if ( pTarget && FClassnameIs(pTarget->pev, "path_track")) - { - m_trackBottom = (CPathTrack*)pTarget; - pTarget = UTIL_FindEntityByTargetname( NULL, STRING(m_trainName) ); - if ( pTarget && FClassnameIs(pTarget->pev, "func_tracktrain")) - { - m_train = (CFuncTrackTrain*)pTarget; - Vector center = (pev->absmin + pev->absmax) * 0.5; - m_trackBottom = m_trackBottom->Nearest( center ); - m_trackTop = m_trackTop->Nearest( center ); - UpdateAutoTargets( m_toggle_state ); - SetThink( NULL ); - return; - } - else - ALERT( at_error, "Can't find train for track change! %s\n", STRING(m_trainName) ); - } - else - ALERT( at_error, "Can't find bottom track for track change! %s\n", STRING(m_trackBottomName) ); - } - else - ALERT( at_error, "Can't find top track for track change! %s\n", STRING(m_trackTopName) ); -} - - - -TRAIN_CODE CFuncTrackChange :: EvaluateTrain( CPathTrack *pcurrent ) -{ - // Go ahead and work, we don't have anything to switch, so just be an elevator - if ( !pcurrent || !m_train ) - return TRAIN_SAFE; - - if ( m_train->m_ppath == pcurrent || (pcurrent->m_pprevious && m_train->m_ppath == pcurrent->m_pprevious) || - (pcurrent->m_pnext && m_train->m_ppath == pcurrent->m_pnext) ) - { - if ( m_train->pev->speed != 0 ) - return TRAIN_BLOCKING; - - Vector dist = pev->origin - m_train->pev->origin; - float length = dist.Length2D(); - if ( length < m_train->m_length ) // Empirically determined close distance - return TRAIN_FOLLOWING; - else if ( length > (150 + m_train->m_length) ) - return TRAIN_SAFE; - - return TRAIN_BLOCKING; - } - - return TRAIN_SAFE; -} - -void CFuncTrackChange :: UpdateTrain( Vector &dest ) -{ - float time; - Vector vel = pev->velocity; - - if (m_pfnThink == LinearMoveNow) - { - // we're going to do a LinearMoveNow: calculate the velocity it'll have - Vector vecDest; - if (m_pMoveWith) - vecDest = m_vecFinalDest + m_pMoveWith->pev->origin; - else - vecDest = m_vecFinalDest; - Vector vecDestDelta = vecDest - pev->origin; - time = vecDestDelta.Length() / m_flLinearMoveSpeed; - vel = vecDestDelta / time; - } - else - { - time = (pev->nextthink - pev->ltime); - } - - m_train->pev->velocity = vel; - m_train->pev->avelocity = pev->avelocity; - m_train->NextThink( m_train->pev->ltime + time, FALSE ); - - - // Attempt at getting the train to rotate properly around the origin of the trackchange - if ( time <= 0 ) - { -// ALERT(at_console, "no time, set trainvel %f %f %f\n", m_train->pev->velocity.x, m_train->pev->velocity.y, m_train->pev->velocity.z); - return; - } - - Vector offset = m_train->pev->origin - pev->origin; - Vector delta = dest - pev->angles; - // Transform offset into local coordinates - UTIL_MakeInvVectors( delta, gpGlobals ); - Vector local; - local.x = DotProduct( offset, gpGlobals->v_forward ); - local.y = DotProduct( offset, gpGlobals->v_right ); - local.z = DotProduct( offset, gpGlobals->v_up ); - - local = local - offset; - m_train->pev->velocity = vel + (local * (1.0/time)); - -// ALERT(at_console, "set trainvel %f %f %f\n", m_train->pev->velocity.x, m_train->pev->velocity.y, m_train->pev->velocity.z); -} - -void CFuncTrackChange :: GoDown( void ) -{ - if ( m_code == TRAIN_BLOCKING ) - return; - - // HitBottom may get called during CFuncPlat::GoDown(), so set up for that - // before you call GoDown() - - UpdateAutoTargets( TS_GOING_DOWN ); - // If ROTMOVE, move & rotate - if ( FBitSet( pev->spawnflags, SF_TRACK_DONT_MOVE ) ) - { - SetMoveDone(&CFuncTrackChange :: CallHitBottom ); - m_toggle_state = TS_GOING_DOWN; - AngularMove( m_start, pev->speed ); - } - else - { - CFuncPlat :: GoDown(); - SetMoveDone(&CFuncTrackChange :: CallHitBottom ); - - Vector vecDest; - if (m_pMoveWith) - { - vecDest = m_vecFinalDest + m_pMoveWith->pev->origin; - } - else - vecDest = m_vecFinalDest; - Vector vecDestDelta = vecDest - pev->origin; - float flTravelTime = vecDestDelta.Length() / m_flLinearMoveSpeed; - - RotMove( m_start, flTravelTime ); -// RotMove( m_start, pev->nextthink - pev->ltime ); - } - // Otherwise, rotate first, move second - - // If the train is moving with the platform, update it - if ( m_code == TRAIN_FOLLOWING ) - { - UpdateTrain( m_start ); - m_train->m_ppath = NULL; - } -} - - -// -// Platform is at bottom, now starts moving up -// -void CFuncTrackChange :: GoUp( void ) -{ - if ( m_code == TRAIN_BLOCKING ) - return; - - // HitTop may get called during CFuncPlat::GoUp(), so set up for that - // before you call GoUp(); - - UpdateAutoTargets( TS_GOING_UP ); - if ( FBitSet( pev->spawnflags, SF_TRACK_DONT_MOVE ) ) - { - SetMoveDone(&CFuncTrackChange :: CallHitTop ); - m_toggle_state = TS_GOING_UP; - AngularMove( m_end, pev->speed ); - } - else - { - // If ROTMOVE, move & rotate - CFuncPlat :: GoUp(); - SetMoveDone(&CFuncTrackChange :: CallHitTop ); - - Vector vecDest; - if (m_pMoveWith) - { - vecDest = m_vecFinalDest + m_pMoveWith->pev->origin; - } - else - vecDest = m_vecFinalDest; - Vector vecDestDelta = vecDest - pev->origin; - float flTravelTime = vecDestDelta.Length() / m_flLinearMoveSpeed; - - RotMove( m_end, flTravelTime ); -// RotMove( m_end, pev->nextthink - pev->ltime ); - } - - // Otherwise, move first, rotate second - - // If the train is moving with the platform, update it - if ( m_code == TRAIN_FOLLOWING ) - { - UpdateTrain( m_end ); - m_train->m_ppath = NULL; - } -} - - -// Normal track change -void CFuncTrackChange :: UpdateAutoTargets( int toggleState ) -{ - if ( !m_trackTop || !m_trackBottom ) - return; - - if ( toggleState == TS_AT_TOP ) - { - ClearBits( m_trackTop->pev->spawnflags, SF_PATH_DISABLED ); - } - else - SetBits( m_trackTop->pev->spawnflags, SF_PATH_DISABLED ); - - if ( toggleState == TS_AT_BOTTOM ) - { - ClearBits( m_trackBottom->pev->spawnflags, SF_PATH_DISABLED ); - } - else - SetBits( m_trackBottom->pev->spawnflags, SF_PATH_DISABLED ); - - UpdateTrain( pev->origin );//fix now is func_trackchange BUG. G-Cont -} - - -void CFuncTrackChange :: Use( CBaseEntity *pActivator, CBaseEntity *pCaller, USE_TYPE useType, float value ) -{ - if ( m_toggle_state != TS_AT_TOP && m_toggle_state != TS_AT_BOTTOM ) - return; - - // If train is in "safe" area, but not on the elevator, play alarm sound - if ( m_toggle_state == TS_AT_TOP ) - m_code = EvaluateTrain( m_trackTop ); - else if ( m_toggle_state == TS_AT_BOTTOM ) - m_code = EvaluateTrain( m_trackBottom ); - else - m_code = TRAIN_BLOCKING; - if ( m_code == TRAIN_BLOCKING ) - { - // Play alarm and return - EMIT_SOUND(ENT(pev), CHAN_VOICE, "buttons/button11.wav", 1, ATTN_NORM); - return; - } - - // Otherwise, it's safe to move - // If at top, go down - // at bottom, go up - - DisableUse(); - if (m_toggle_state == TS_AT_TOP) - GoDown(); - else - GoUp(); -} - - -// -// Platform has hit bottom. Stops and waits forever. -// -void CFuncTrackChange :: HitBottom( void ) -{ - CFuncPlatRot :: HitBottom(); - if ( m_code == TRAIN_FOLLOWING ) - { -// UpdateTrain(); - m_train->SetTrack( m_trackBottom ); - } - SetThink( NULL ); - pev->nextthink = -1; - - UpdateAutoTargets( m_toggle_state ); - - EnableUse(); -} - - -// -// Platform has hit bottom. Stops and waits forever. -// -void CFuncTrackChange :: HitTop( void ) -{ - CFuncPlatRot :: HitTop(); - if ( m_code == TRAIN_FOLLOWING ) - { -// UpdateTrain(); - m_train->SetTrack( m_trackTop ); - } - - // Don't let the plat go back down - SetThink( NULL ); - pev->nextthink = -1; - UpdateAutoTargets( m_toggle_state ); - EnableUse(); -} - - - -class CFuncTrackAuto : public CFuncTrackChange -{ -public: - void Use( CBaseEntity *pActivator, CBaseEntity *pCaller, USE_TYPE useType, float value ); - virtual void UpdateAutoTargets( int toggleState ); -}; - -LINK_ENTITY_TO_CLASS( func_trackautochange, CFuncTrackAuto ); - -// Auto track change -void CFuncTrackAuto :: UpdateAutoTargets( int toggleState ) -{ - CPathTrack *pTarget, *pNextTarget; - - if ( !m_trackTop || !m_trackBottom ) - return; - - if ( m_targetState == TS_AT_TOP ) - { - pTarget = m_trackTop->GetNext(); - pNextTarget = m_trackBottom->GetNext(); - } - else - { - pTarget = m_trackBottom->GetNext(); - pNextTarget = m_trackTop->GetNext(); - } - if ( pTarget ) - { - ClearBits( pTarget->pev->spawnflags, SF_PATH_DISABLED ); - if ( m_code == TRAIN_FOLLOWING && m_train && m_train->pev->speed == 0 ) - m_train->Use( this, this, USE_ON, 0 ); - } - - if ( pNextTarget ) - SetBits( pNextTarget->pev->spawnflags, SF_PATH_DISABLED ); - -} - - -void CFuncTrackAuto :: Use( CBaseEntity *pActivator, CBaseEntity *pCaller, USE_TYPE useType, float value ) -{ - CPathTrack *pTarget; - - if ( !UseEnabled() ) - return; - - if ( m_toggle_state == TS_AT_TOP ) - pTarget = m_trackTop; - else if ( m_toggle_state == TS_AT_BOTTOM ) - pTarget = m_trackBottom; - else - pTarget = NULL; - - if ( FClassnameIs( pActivator->pev, "func_tracktrain" ) ) - { - m_code = EvaluateTrain( pTarget ); - // Safe to fire? - if ( m_code == TRAIN_FOLLOWING && m_toggle_state != m_targetState ) - { - DisableUse(); - if (m_toggle_state == TS_AT_TOP) - GoDown(); - else - GoUp(); - } - } - else - { - if ( pTarget ) - pTarget = pTarget->GetNext(); - if ( pTarget && m_train->m_ppath != pTarget && ShouldToggle( useType, m_targetState ) ) - { - if ( m_targetState == TS_AT_TOP ) - m_targetState = TS_AT_BOTTOM; - else - m_targetState = TS_AT_TOP; - } - - UpdateAutoTargets( m_targetState ); - } -} - - -// ---------------------------------------------------------- -// -// -// pev->speed is the travel speed -// pev->health is current health -// pev->max_health is the amount to reset to each time it starts - -#define FGUNTARGET_START_ON 0x0001 - -class CGunTarget : public CBaseMonster -{ -public: - void Spawn( void ); - void Activate( void ); - void EXPORT Next( void ); - void EXPORT Start( void ); - void EXPORT Wait( void ); - void Stop( void ); - - int BloodColor( void ) { return DONT_BLEED; } - int Classify( void ) { return CLASS_MACHINE; } - int TakeDamage( entvars_t *pevInflictor, entvars_t *pevAttacker, float flDamage, int bitsDamageType ); - void Use( CBaseEntity *pActivator, CBaseEntity *pCaller, USE_TYPE useType, float value ); - Vector BodyTarget( const Vector &posSrc ) { return pev->origin; } - - virtual int ObjectCaps( void ) { return CBaseEntity :: ObjectCaps() & ~FCAP_ACROSS_TRANSITION; } - virtual int Save( CSave &save ); - virtual int Restore( CRestore &restore ); - - static TYPEDESCRIPTION m_SaveData[]; - -private: - BOOL m_on; -}; - - -LINK_ENTITY_TO_CLASS( func_guntarget, CGunTarget ); - -TYPEDESCRIPTION CGunTarget::m_SaveData[] = -{ - DEFINE_FIELD( CGunTarget, m_on, FIELD_BOOLEAN ), -}; - -IMPLEMENT_SAVERESTORE( CGunTarget, CBaseMonster ); - - -void CGunTarget::Spawn( void ) -{ - pev->solid = SOLID_BSP; - pev->movetype = MOVETYPE_PUSH; - - UTIL_SetOrigin(this, pev->origin); - SET_MODEL(ENT(pev), STRING(pev->model) ); - - if ( pev->speed == 0 ) - pev->speed = 100; - - // Don't take damage until "on" - pev->takedamage = DAMAGE_NO; - pev->flags |= FL_MONSTER; - - m_on = FALSE; - pev->max_health = pev->health; - - if ( pev->spawnflags & FGUNTARGET_START_ON ) - { - SetThink(&CGunTarget:: Start ); - SetNextThink( 0.3 ); - } -} - - -void CGunTarget::Activate( void ) -{ - CBaseEntity *pTarg; - - // now find our next target - pTarg = GetNextTarget(); - if ( pTarg ) - { - m_hTargetEnt = pTarg; - UTIL_SetOrigin( this, pTarg->pev->origin - (pev->mins + pev->maxs) * 0.5 ); - } - CBaseMonster::Activate(); -} - - -void CGunTarget::Start( void ) -{ - Use( this, this, USE_ON, 0 ); -} - - -void CGunTarget::Next( void ) -{ - SetThink( NULL ); - - m_hTargetEnt = GetNextTarget(); - CBaseEntity *pTarget = m_hTargetEnt; - - if ( !pTarget ) - { - Stop(); - return; - } - SetMoveDone(&CGunTarget:: Wait ); - LinearMove( pTarget->pev->origin - (pev->mins + pev->maxs) * 0.5, pev->speed ); -} - - -void CGunTarget::Wait( void ) -{ - CBaseEntity *pTarget = m_hTargetEnt; - - if ( !pTarget ) - { - Stop(); - return; - } - - // Fire the pass target if there is one - if ( pTarget->pev->message ) - { - FireTargets( STRING(pTarget->pev->message), this, this, USE_TOGGLE, 0 ); - if ( FBitSet( pTarget->pev->spawnflags, SF_CORNER_FIREONCE ) ) - pTarget->pev->message = 0; - } - - m_flWait = pTarget->GetDelay(); - - pev->target = pTarget->pev->target; - SetThink(&CGunTarget:: Next ); - if (m_flWait != 0) - {// -1 wait will wait forever! - SetNextThink( m_flWait ); - } - else - { - Next();// do it RIGHT now! - } -} - - -void CGunTarget::Stop( void ) -{ - pev->velocity = g_vecZero; - DontThink(); - pev->takedamage = DAMAGE_NO; -} - - -int CGunTarget::TakeDamage( entvars_t *pevInflictor, entvars_t *pevAttacker, float flDamage, int bitsDamageType ) -{ - if ( pev->health > 0 ) - { - pev->health -= flDamage; - if ( pev->health <= 0 ) - { - pev->health = 0; - Stop(); - if ( pev->message ) - FireTargets( STRING(pev->message), this, this, USE_TOGGLE, 0 ); - } - } - return 0; -} - - -void CGunTarget::Use( CBaseEntity *pActivator, CBaseEntity *pCaller, USE_TYPE useType, float value ) -{ - if ( !ShouldToggle( useType, m_on ) ) - return; - - if ( m_on ) - { - Stop(); - } - else - { - pev->takedamage = DAMAGE_AIM; - m_hTargetEnt = GetNextTarget(); - if ( m_hTargetEnt == NULL ) - return; - pev->health = pev->max_health; - Next(); - } -} - -//============================================================================ -//LRC - Scripted Train Sequence -//============================================================================ - -#define DIRECTION_NONE 0 -#define DIRECTION_FORWARDS 1 -#define DIRECTION_BACKWARDS 2 -#define DIRECTION_STOP 3 -#define DIRECTION_DESTINATION 4 - -#define SF_TRAINSEQ_REMOVE 2 -#define SF_TRAINSEQ_DIRECT 4 -#define SF_TRAINSEQ_DEBUG 8 - -LINK_ENTITY_TO_CLASS( scripted_trainsequence, CTrainSequence ); - -TYPEDESCRIPTION CTrainSequence::m_SaveData[] = -{ - DEFINE_FIELD( CTrainSequence, m_iszEntity, FIELD_STRING ), - DEFINE_FIELD( CTrainSequence, m_iszDestination, FIELD_STRING ), - DEFINE_FIELD( CTrainSequence, m_pDestination, FIELD_CLASSPTR), - DEFINE_FIELD( CTrainSequence, m_iszTerminate, FIELD_STRING ), - DEFINE_FIELD( CTrainSequence, m_fDuration, FIELD_FLOAT ), - DEFINE_FIELD( CTrainSequence, m_iDirection, FIELD_INTEGER ), - DEFINE_FIELD( CTrainSequence, m_iPostDirection, FIELD_INTEGER ), - DEFINE_FIELD( CTrainSequence, m_pTrain, FIELD_CLASSPTR), - DEFINE_FIELD( CTrainSequence, m_pTrackTrain, FIELD_CLASSPTR), -}; - -IMPLEMENT_SAVERESTORE( CTrainSequence, CBaseEntity ); - -int CTrainSequence :: ObjectCaps( void ) -{ - return (CBaseEntity::ObjectCaps() & ~FCAP_ACROSS_TRANSITION); -} - -void CTrainSequence :: KeyValue( KeyValueData *pkvd ) -{ - if (FStrEq(pkvd->szKeyName, "m_iDirection")) - { - m_iDirection = atoi(pkvd->szValue); - pkvd->fHandled = TRUE; - } - else if (FStrEq(pkvd->szKeyName, "m_iPostDirection")) - { - m_iPostDirection = atoi(pkvd->szValue); - pkvd->fHandled = TRUE; - } - else if (FStrEq(pkvd->szKeyName, "m_iszEntity")) - { - m_iszEntity = ALLOC_STRING(pkvd->szValue); - pkvd->fHandled = TRUE; - } - else if (FStrEq(pkvd->szKeyName, "m_iszDestination")) - { - m_iszDestination = ALLOC_STRING(pkvd->szValue); - pkvd->fHandled = TRUE; - } - else if (FStrEq(pkvd->szKeyName, "m_iszTerminate")) - { - m_iszTerminate = ALLOC_STRING(pkvd->szValue); - pkvd->fHandled = TRUE; - } - else - CBaseEntity::KeyValue( pkvd ); -} - -void CTrainSequence :: Use( CBaseEntity *pActivator, CBaseEntity *pCaller, USE_TYPE useType, float value ) -{ -// ALERT(at_console, "SeqUse\n"); - if (!ShouldToggle(useType)) - { -// ALERT(at_console, "SeqUse, don't toggle\n"); - return; - } - else - { -// ALERT(at_console, "SeqUse ok\n"); - } - - if (GetState() == STATE_OFF) - { - // start the sequence, take control of the train - - CBaseEntity* pEnt = UTIL_FindEntityByTargetname(NULL, STRING(m_iszEntity), pActivator); - if (pEnt) - { - m_pDestination = UTIL_FindEntityByTargetname(NULL, STRING(m_iszDestination), pActivator); - - if (pev->spawnflags & SF_TRAINSEQ_DEBUG) - { - ALERT(at_console, "trainsequence \"%s\" found train \"%s\"", STRING(pev->targetname), STRING(pEnt->pev->targetname)); - if (m_pDestination) - ALERT(at_console, "found destination %s\n", STRING(m_pDestination->pev->targetname)); - else - ALERT(at_console, "missing destination\n"); - } - - if (FStrEq(STRING(pEnt->pev->classname), "func_train")) - { - CFuncTrain *pTrain = (CFuncTrain*)pEnt; - - // check whether it's being controlled by another sequence - if (pTrain->m_pSequence) - { -// ALERT(at_console, "SeqUse: Train sequence already set\n"); - return; - } -// ALERT(at_console, "SeqUse: Train takecontrol\n"); - - //ok, we can now take control of it. - pTrain->StartSequence(this); - m_pTrain = pTrain; - - if (pev->spawnflags & SF_TRAINSEQ_DIRECT) - { - pTrain->pev->target = m_pDestination->pev->targetname; - pTrain->Next(); - } - else - { - int iDir = DIRECTION_NONE; - - switch (m_iDirection) - { - case DIRECTION_DESTINATION: - if (m_pDestination) - { - Vector vecFTemp, vecBTemp; - CBaseEntity *pTrainDest = UTIL_FindEntityByTargetname(NULL, STRING(pTrain->pev->message)); - float fForward; - if (pTrain->pev->spawnflags & SF_TRAIN_SETORIGIN) - fForward = (pTrainDest->pev->origin - pTrain->pev->origin).Length(); - else - fForward = (pTrainDest->pev->origin - (pTrain->pev->origin + (pTrain->pev->maxs + pTrain->pev->mins)*0.5)).Length(); - float fBackward = -fForward; // the further back from the TrainDest entity we are, the shorter the backward distance. - CBaseEntity *pCurForward = pTrainDest; - CBaseEntity *pCurBackward = m_pDestination; - vecFTemp = pCurForward->pev->origin; - vecBTemp = pCurBackward->pev->origin; - int loopbreaker = 10; - while(iDir == DIRECTION_NONE) - { - if (pCurForward) - { - fForward += (pCurForward->pev->origin - vecFTemp).Length(); - vecFTemp = pCurForward->pev->origin; - - // ALERT(at_console, "SeqUse: Forward %f %s (%p == %p)\n", fForward, STRING(pCurForward->pev->targetname), pCurForward, m_pDestination); - // if we've finished tracing the forward line - if (pCurForward == m_pDestination) - { - // if the backward line is longest - if (fBackward >= fForward || pCurBackward == NULL) - iDir = DIRECTION_FORWARDS; - } - else - { - pCurForward = pCurForward->GetNextTarget(); - } - } - if (pCurBackward) - { - fBackward += (pCurBackward->pev->origin - vecBTemp).Length(); - vecBTemp = pCurBackward->pev->origin; - - // ALERT(at_console, "SeqUse: Backward %f %s (%p == %p)\n", fBackward, STRING(pCurBackward->pev->targetname), pCurBackward, pTrainDest); - // if we've finished tracng the backward line - if (pCurBackward == pTrainDest) - { - // if the forward line is shorter - if (fBackward < fForward || pCurForward == NULL) - iDir = DIRECTION_BACKWARDS; - } - else - { - pCurBackward = pCurBackward->GetNextTarget(); - } - } - loopbreaker--; - if (loopbreaker <= 0) - iDir = DIRECTION_STOP; - } - } - else - { - iDir = DIRECTION_STOP; - } - break; - case DIRECTION_FORWARDS: iDir = DIRECTION_FORWARDS; break; - case DIRECTION_BACKWARDS: iDir = DIRECTION_BACKWARDS; break; - case DIRECTION_STOP: iDir = DIRECTION_STOP; break; - } - - // ALERT(at_console, "SeqUse: iDir is %d\n", iDir); - - if (iDir == DIRECTION_BACKWARDS && !(pTrain->pev->spawnflags & SF_TRAIN_REVERSE)) - { -// ALERT(at_console, "Reversing from \"%s\" \"%s\"\n", STRING(pTrain->pev->target), STRING(pTrain->pev->message)); - // change direction - pTrain->pev->spawnflags |= SF_TRAIN_REVERSE; - - CBaseEntity *pSearch = m_pDestination; - while (pSearch) - { - if (FStrEq(STRING(pSearch->pev->target), STRING(pTrain->pev->message))) - { - // ALERT(at_console, "SeqUse reverse: pSearch is %s\n", STRING(pSearch->pev->targetname)); - CBaseEntity *pTrainTarg = pSearch->GetNextTarget(); - if (pTrainTarg) - pTrain->pev->enemy = pTrainTarg->edict(); - else - pTrain->pev->enemy = NULL; - pTrain->pev->target = pSearch->pev->targetname; - break; - } - pSearch = pSearch->GetNextTarget(); - } - - if (!pSearch) - { - // this shouldn't happen. - ALERT(at_error, "Found no path to reach destination! (train has t %s, m %s; dest is %s)\n", STRING(pTrain->pev->target), STRING(pTrain->pev->message), STRING(m_pDestination->pev->targetname)); - return; - } - pTrain->m_pevCurrentTarget = NULL; // we haven't reached the corner, so don't use its settings -// if (pTrain->pev->enemy) -// ALERT(at_console, "SeqUse: pTrain target %s, enemy %s\n", STRING(pTrain->pev->target), STRING(pTrain->pev->enemy->v.targetname)); -// else -// ALERT(at_console, "SeqUse: pTrain target %s, no enemy\n", STRING(pTrain->pev->target)); - pTrain->Next(); - } - else if (iDir == DIRECTION_FORWARDS) - { -// ALERT(at_console, "Dir_Forwards targ %s\n", STRING(pTrain->pev->target)); - pTrain->pev->target = pTrain->pev->message; - pTrain->Next(); - } - else if (iDir == DIRECTION_STOP) - { - SetNextThink(0.1); - SetThink(&CTrainSequence ::EndThink); - return; - } - } - } - else if (FStrEq(STRING(pEnt->pev->classname), "func_tracktrain")) - { - CFuncTrackTrain *pTrackTrain = (CFuncTrackTrain*)pEnt; - - // check whether it's being controlled by another sequence - if (pTrackTrain->m_pSequence) - return; - - //ok, we can now take control of it. - pTrackTrain->StartSequence(this); - m_pTrackTrain = pTrackTrain; - } - else - { - ALERT(at_error, "scripted_trainsequence %s can't affect %s \"%s\": not a train!\n", STRING(pev->targetname), STRING(pEnt->pev->classname), STRING(pEnt->pev->targetname)); - return; - } - } - else // no entity with that name - { - ALERT(at_error, "Missing train \"%s\" for scripted_trainsequence %s!\n", STRING(m_iszEntity), STRING(pev->targetname)); - return; - } - - // if we got here, we've set up a sequence successfully. - // do the rest of the setup. - if (m_fDuration) - { - SetThink(&CTrainSequence :: TimeOutThink ); - SetNextThink( m_fDuration ); - } - -// if (m_pTrain) -// ALERT(at_console, "m_pTrain nextthink %f, flags %f\n", STRING(m_pTrain->pev->nextthink), m_pTrain->m_iLFlags); - } - else // prematurely end the sequence - { - //disable the other end conditions - DontThink(); - - // release control of the train - StopSequence(); - } -} - -void CTrainSequence :: ArrivalNotify() -{ -// ALERT(at_console, "ArrivalNotify\n"); - // check whether the current path is our destination, - // and end the sequence if it is. - if (m_pTrain) - { - if (m_pTrain->m_pevCurrentTarget == m_pDestination->pev) - { - // we've reached the destination. Stop now. -// ALERT(at_console, "ArrivalNotify %s stop\n", STRING(pev->targetname)); - EndThink(); - } - else - { -// ALERT(at_console, "ArrivalNotify %s continue\n", STRING(pev->targetname)); - } - } - else if (m_pTrackTrain) - { - //... - } - else - { - ALERT(at_error, "scripted_trainsequence: ArrivalNotify without a train!?\n"); - return; // this shouldn't happen. - } -} - -void CTrainSequence :: EndThink() -{ - //the sequence has expired. Release control. - StopSequence(); - FireTargets(STRING(pev->target), this, this, USE_TOGGLE, 0); -} - -void CTrainSequence :: TimeOutThink() -{ - //the sequence has timed out. Release control. - StopSequence(); - FireTargets(STRING(pev->netname), this, this, USE_TOGGLE, 0); -} - -void CTrainSequence :: StopSequence() -{ - if (m_pTrain) - { -// ALERT(at_console, "StopSequence called\n"); - //stuff... - m_pTrain->StopSequence(); - m_pTrain = NULL; - - if (FBitSet(pev->spawnflags, SF_TRAINSEQ_REMOVE)) - UTIL_Remove( this ); - } - else if (m_pTrackTrain) - { - //stuff... - } - else - { - ALERT(at_error, "scripted_trainsequence: StopSequence without a train!?\n"); - return; // this shouldn't happen. - } - FireTargets(STRING(m_iszTerminate), this, this, USE_TOGGLE, 0); -} diff --git a/spirit/python.cpp b/spirit/python.cpp deleted file mode 100644 index 98fbf9ef..00000000 --- a/spirit/python.cpp +++ /dev/null @@ -1,301 +0,0 @@ -/*** -* -* Copyright (c) 1996-2002, 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. -* -****/ -#include "extdll.h" -#include "util.h" -#include "cbase.h" -#include "weapons.h" -#include "monsters.h" -#include "player.h" -#include "gamerules.h" - - -enum python_e { - PYTHON_IDLE1 = 0, - PYTHON_FIDGET, - PYTHON_FIRE1, - PYTHON_RELOAD, - PYTHON_HOLSTER, - PYTHON_DRAW, - PYTHON_IDLE2, - PYTHON_IDLE3 -}; - -class CPython : public CBasePlayerWeapon -{ -public: - void Spawn( void ); - void Precache( void ); - int GetItemInfo(ItemInfo *p); - void PrimaryAttack( void ); - void SecondaryAttack( void ); - BOOL Deploy( void ); - void Holster( ); - void Reload( void ); - void WeaponIdle( void ); - void UpdateSpot( void ); - float m_flSoundDelay; - BOOL ShouldWeaponIdle( void ) { return TRUE; }; - CLaserSpot *m_pSpot; -private: - unsigned short m_usFirePython; - int m_fSpotActive;//LTD allow only in multiplayer - no need save\restore - int m_iShell; -}; - -LINK_ENTITY_TO_CLASS( weapon_python, CPython ); -LINK_ENTITY_TO_CLASS( weapon_357, CPython ); - -int CPython::GetItemInfo(ItemInfo *p) -{ - p->pszName = STRING(pev->classname); - p->pszAmmo1 = "357"; - p->iMaxAmmo1 = _357_MAX_CARRY; - p->pszAmmo2 = NULL; - p->iMaxAmmo2 = -1; - p->iMaxClip = PYTHON_MAX_CLIP; - p->iFlags = 0; - p->iSlot = 1; - p->iPosition = 1; - p->iId = m_iId = WEAPON_PYTHON; - p->iWeight = PYTHON_WEIGHT; - - return 1; -} - -void CPython::Spawn( ) -{ - pev->classname = MAKE_STRING("weapon_357"); // hack to allow for old names - Precache( ); - m_iId = WEAPON_PYTHON; - SET_MODEL(ENT(pev), "models/w_357.mdl"); - - m_iDefaultAmmo = PYTHON_DEFAULT_GIVE; - FallInit();// get ready to fall down. -} - - -void CPython::Precache( void ) -{ - PRECACHE_MODEL("models/v_357.mdl"); - PRECACHE_MODEL("models/w_357.mdl"); - PRECACHE_MODEL("models/p_357.mdl"); - - PRECACHE_SOUND ("weapons/357_reload1.wav"); - m_usFirePython = PRECACHE_EVENT( 1, "evPython" ); - m_iShell = PRECACHE_MODEL ("models/shell.mdl");// brass shell TE_MODEL -} - -BOOL CPython::Deploy( ) -{ - if ( IsMultiplayer() ) m_iBody = 1;//enable laser sight geometry - return DefaultDeploy( "models/v_357.mdl", "models/p_357.mdl", PYTHON_DRAW, "python", 0.7 ); -} - - -void CPython::Holster( ) -{ - m_fInReload = FALSE;// cancel any reload in progress. - m_pPlayer->m_flNextAttack = UTIL_WeaponTimeBase() + 0.6; - - SendWeaponAnim( PYTHON_HOLSTER ); - - if (m_pSpot) - { - m_pSpot->Killed( NULL, GIB_NEVER ); - m_pSpot = NULL; - } -} - -void CPython::SecondaryAttack( void ) -{ - if ( IsMultiplayer() ) - { - m_fSpotActive = !m_fSpotActive; - - if (!m_fSpotActive && m_pSpot) - { - EMIT_SOUND(ENT(m_pPlayer->pev), CHAN_ITEM, "weapons/spot_off.wav", 1, ATTN_NORM); - m_pSpot->Killed( NULL, GIB_NORMAL ); - m_pSpot = NULL; - } - - } - m_flNextSecondaryAttack = UTIL_WeaponTimeBase() + 0.3; -} - -void CPython::PrimaryAttack() -{ - if ( m_iClip && m_pPlayer->pev->waterlevel != 3)//don't fire underwater - { - if ( m_pSpot && m_fSpotActive ) m_pSpot->Suspend( 1.0 ); - - m_pPlayer->m_iWeaponVolume = LOUD_GUN_VOLUME; - m_pPlayer->m_iWeaponFlash = BRIGHT_GUN_FLASH; - - m_iClip--; - - // player "shoot" animation - m_pPlayer->SetAnimation( PLAYER_ATTACK1 ); - - UTIL_MakeVectors( m_pPlayer->pev->v_angle + m_pPlayer->pev->punchangle ); - - Vector vecSrc = m_pPlayer->GetGunPosition( ); - Vector vecAiming = m_pPlayer->GetAutoaimVector( AUTOAIM_10DEGREES ); - - Vector vecDir; - vecDir = m_pPlayer->FireBulletsPlayer( 1, vecSrc, vecAiming, VECTOR_CONE_1DEGREES, 8192, BULLET_PLAYER_357, 0, 0, m_pPlayer->pev, m_pPlayer->random_seed ); - - PLAYBACK_EVENT_FULL( 0, m_pPlayer->edict(), m_usFirePython, 0.0, (float *)&g_vecZero, (float *)&g_vecZero, vecDir.x, vecDir.y, pev->body, PYTHON_FIRE1, 0, 0 ); - - m_flNextPrimaryAttack = gpGlobals->time + 1.0; - m_flTimeWeaponIdle = UTIL_WeaponTimeBase() + RANDOM_FLOAT ( 10, 15 ); - } - else - { - PlayEmptySound(); - m_flNextPrimaryAttack = UTIL_WeaponTimeBase() + 0.7; - } -} - - -void CPython::Reload( void ) -{ - if ( m_pPlayer->m_rgAmmo[m_iPrimaryAmmoType] == 0) return; - if ( m_pSpot && m_fSpotActive ) m_pSpot->Suspend( 2.0 ); - - if (DefaultReload( 6, PYTHON_RELOAD, 2.0 )) - { - m_flSoundDelay = gpGlobals->time + 1.5; - } - m_flTimeWeaponIdle = UTIL_WeaponTimeBase() + RANDOM_FLOAT ( 10, 15 ); -} - -void CPython::UpdateSpot( void ) -{ - if (m_fSpotActive) - { - if (!m_pSpot) - { - m_pSpot = CLaserSpot::CreateSpot(); - EMIT_SOUND(ENT(m_pPlayer->pev), CHAN_ITEM, "weapons/spot_on.wav", 1, ATTN_NORM); - } - - UTIL_MakeVectors( m_pPlayer->pev->v_angle ); - Vector vecSrc = m_pPlayer->GetGunPosition( ); - Vector vecAiming = gpGlobals->v_forward; - - TraceResult tr; - UTIL_TraceLine ( vecSrc, vecSrc + vecAiming * 8192, dont_ignore_monsters, ignore_glass, ENT(m_pPlayer->pev), &tr ); - float flLength = (tr.vecEndPos - vecSrc).Length(); - - m_pSpot->pev->scale = flLength / 340; - int m_iSpotBright = (1 / log(flLength / 0.3))*1700; - if (m_iSpotBright > 255 ) m_iSpotBright = 255; - - //ALERT( at_console, "%f\n", flLength / 200); - //ALERT( at_console, "%d\n", m_iSpotBright ); - - m_iSpotBright = m_iSpotBright + RANDOM_LONG (1, flLength / 200); - m_pSpot->pev->renderamt = m_iSpotBright; - - UTIL_SetOrigin( m_pSpot, tr.vecEndPos + tr.vecPlaneNormal * 0.1); - - //allow oriented LTD in multiplayer only, but python has LTD only in multiplayer - remove check - Vector n = tr.vecPlaneNormal; - n.x *= -1; - n.y *= -1; - m_pSpot->pev->angles = UTIL_VecToAngles(n); - - } -} - -void CPython::WeaponIdle( void ) -{ - UpdateSpot( ); - - if (m_flSoundDelay != 0 && m_flSoundDelay <= gpGlobals->time) - { - EMIT_SOUND(ENT(m_pPlayer->pev), CHAN_WEAPON, "weapons/357_reload1.wav", RANDOM_FLOAT(0.8, 0.9), ATTN_NORM); - m_flSoundDelay = 0; - - for (int i = 0; i < 6; i++) - { - EjectBrass ( m_pPlayer->pev->origin, Vector( RANDOM_FLOAT( -10.0, 10.0 ), RANDOM_FLOAT( -10.0, 10.0 ), (float)0.0 ), m_pPlayer->pev->angles.y, m_iShell, TE_BOUNCE_SHELL); - } - } - - m_pPlayer->GetAutoaimVector( AUTOAIM_10DEGREES ); - - if ( m_flTimeWeaponIdle > UTIL_WeaponTimeBase() )return; - - // only idle if the slid isn't back - if (m_iClip) - { - int iAnim; - float flRand = RANDOM_FLOAT(0, 1); - if (flRand <= 0.5) - { - iAnim = PYTHON_IDLE1; - m_flTimeWeaponIdle = UTIL_WeaponTimeBase() + (70.0/30.0); - } - else if (flRand <= 0.7) - { - iAnim = PYTHON_IDLE2; - m_flTimeWeaponIdle = UTIL_WeaponTimeBase() + (60.0/30.0); - } - else if (flRand <= 0.9) - { - iAnim = PYTHON_IDLE3; - m_flTimeWeaponIdle = UTIL_WeaponTimeBase() + (88.0/30.0); - } - else - { - if( !m_fSpotActive ) - { - iAnim = PYTHON_FIDGET; - m_flTimeWeaponIdle = UTIL_WeaponTimeBase() + (170.0/30.0); - } - else return; - } - SendWeaponAnim( iAnim ); - } -} - - - -class CPythonAmmo : public CBasePlayerAmmo -{ - void Spawn( void ) - { - Precache( ); - SET_MODEL(ENT(pev), "models/w_357ammobox.mdl"); - CBasePlayerAmmo::Spawn( ); - } - void Precache( void ) - { - PRECACHE_MODEL ("models/w_357ammobox.mdl"); - PRECACHE_SOUND("items/9mmclip1.wav"); - } - BOOL AddAmmo( CBaseEntity *pOther ) - { - if (pOther->GiveAmmo( AMMO_357BOX_GIVE, "357", _357_MAX_CARRY ) != -1) - { - EMIT_SOUND(ENT(pev), CHAN_ITEM, "items/9mmclip1.wav", 1, ATTN_NORM); - return TRUE; - } - return FALSE; - } -}; -LINK_ENTITY_TO_CLASS( ammo_357, CPythonAmmo ); \ No newline at end of file diff --git a/spirit/rpg.cpp b/spirit/rpg.cpp deleted file mode 100644 index 0c3f64c7..00000000 --- a/spirit/rpg.cpp +++ /dev/null @@ -1,539 +0,0 @@ -/*** -* -* Copyright (c) 1996-2002, 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. -* -****/ - -#include "extdll.h" -#include "util.h" -#include "cbase.h" -#include "monsters.h" -#include "weapons.h" -#include "nodes.h" -#include "player.h" -#include "gamerules.h" - - -enum rpg_e { - RPG_IDLE = 0, - RPG_IDLE2, - RPG_DRAW, - RPG_HOLSTER, - RPG_FIRE, - RPG_RELOAD -}; - -class CRpg : public CBasePlayerWeapon -{ -public: - void Spawn( void ); - void Precache( void ); - void Reload( void ); - int GetItemInfo(ItemInfo *p); - - BOOL Deploy( void ); - void Holster( ); - void UpdateScreen ( void ); - void ShutdownScreen ( void ); - void PrimaryAttack( void ); - void SecondaryAttack( void ); - void WeaponIdle( void ); - void UpdateSpot( void ); - int AddDuplicate( CBasePlayerItem *pOriginal ) { return FALSE; };//don't give second launcher! - BOOL ShouldWeaponIdle( void ) { return TRUE; }; - - CLaserSpot *m_pSpot; -}; - -class CRpgRocket : public CGrenade -{ -public: - int Save( CSave &save ); - int Restore( CRestore &restore ); - static TYPEDESCRIPTION m_SaveData[]; - void Spawn( void ); - void Precache( void ); - void EXPORT FollowThink( void ); - void EXPORT IgniteThink( void ); - void EXPORT RocketTouch( CBaseEntity *pOther ); - void Detonate( void ); - void CreateTrail( void ); - static CRpgRocket *Create ( Vector vecOrigin, Vector vecAngles, CBaseEntity *pOwner, CRpg *pLauncher ); - - int m_iTrail; - float m_flIgniteTime; - CRpg *m_pLauncher;// pointer back to the launcher that fired me. - BOOL b_setup; -}; - -LINK_ENTITY_TO_CLASS( weapon_rpg, CRpg ); -LINK_ENTITY_TO_CLASS( rpg_rocket, CRpgRocket ); - -//=========================== -// -// Rocket code -// -//=========================== - -CRpgRocket *CRpgRocket::Create ( Vector vecOrigin, Vector vecAngles, CBaseEntity *pOwner, CRpg *pLauncher ) -{ - CRpgRocket *pRocket = GetClassPtr( (CRpgRocket *)NULL ); - - pRocket->SetObjectClass( ED_NORMAL ); - UTIL_SetOrigin( pRocket, vecOrigin ); - pRocket->pev->angles = vecAngles; - pRocket->Spawn(); - pRocket->SetTouch(& CRpgRocket::RocketTouch ); - pRocket->m_pLauncher = pLauncher;// remember what RPG fired me. - pRocket->m_pLauncher->m_iChargeLevel++;// register this missile as active for the launcher - pRocket->pev->owner = pOwner->edict(); - - return pRocket; -} - -TYPEDESCRIPTION CRpgRocket::m_SaveData[] = -{ - DEFINE_FIELD( CRpgRocket, m_flIgniteTime, FIELD_TIME ), - DEFINE_FIELD( CRpgRocket, m_pLauncher, FIELD_CLASSPTR ), -}; -IMPLEMENT_SAVERESTORE( CRpgRocket, CGrenade ); - -void CRpgRocket :: Spawn( void ) -{ - Precache( ); - // motor - pev->movetype = MOVETYPE_BOUNCE; - pev->solid = SOLID_BBOX; - SET_MODEL(ENT(pev), "models/rpgrocket.mdl"); - UTIL_SetSize(pev, Vector( 0, 0, 0), Vector(0, 0, 0)); - UTIL_SetOrigin( this, pev->origin ); - - pev->classname = MAKE_STRING("rpg_rocket"); - - SetThink(&CRpgRocket :: IgniteThink ); - SetTouch(&CRpgRocket :: ExplodeTouch ); - - pev->angles.x -= 30; - UTIL_MakeVectors( pev->angles ); - pev->angles.x = -(pev->angles.x + 30); - pev->velocity = gpGlobals->v_forward * 250; - pev->gravity = 0.5; - - SetNextThink( 0.4 ); - - pev->dmg = gSkillData.plrDmgRPG; -} - -void CRpgRocket :: RocketTouch ( CBaseEntity *pOther ) -{ - CBaseEntity *pPlayer = CBaseEntity::Instance(pev->owner); - if ( m_pLauncher ) - { - // my launcher is still around, tell it I'm dead. - m_pLauncher->m_iChargeLevel--; - if(m_pLauncher->m_pSpot)//make sound only if laser spot created - EMIT_SOUND( pPlayer->edict(), CHAN_ITEM, "weapons/beep2.wav", 1, ATTN_NORM);//play sound at player - } - STOP_SOUND( edict(), CHAN_VOICE, "weapons/rocket1.wav" ); - ExplodeTouch( pOther ); -} - -void CRpgRocket::Detonate( void ) -{ - TraceResult tr; - Vector vecSpot;// trace starts here! - CBaseEntity *pPlayer = CBaseEntity::Instance(pev->owner); - if ( m_pLauncher ) - { - // my launcher is still around, tell it I'm dead. - m_pLauncher->m_iChargeLevel--; - if(m_pLauncher->m_pSpot)//make sound only if laser spot created - EMIT_SOUND( pPlayer->edict(), CHAN_ITEM, "weapons/beep2.wav", 1, ATTN_NORM);//play sound at player - } - STOP_SOUND( edict(), CHAN_VOICE, "weapons/rocket1.wav" ); - vecSpot = pev->origin + Vector ( 0 , 0 , 8 ); - UTIL_TraceLine ( vecSpot, vecSpot + Vector ( 0, 0, -40 ), ignore_monsters, ENT(pev), & tr); - - Explode( &tr, DMG_BLAST ); -} - -void CRpgRocket :: Precache( void ) -{ - PRECACHE_MODEL("models/rpgrocket.mdl"); - m_iTrail = PRECACHE_MODEL("sprites/smoke.spr"); - PRECACHE_SOUND ("weapons/rocket1.wav"); - PRECACHE_SOUND("weapons/beep2.wav"); -} - - -void CRpgRocket :: IgniteThink( void ) -{ - pev->movetype = MOVETYPE_FLY; - pev->effects |= EF_LIGHT; - - CreateTrail(); - m_flIgniteTime = gpGlobals->time; - - // set to follow laser spot - SetThink(&CRpgRocket :: FollowThink ); - SetNextThink( 0.1 ); -} - -void CRpgRocket :: CreateTrail( void ) -{ - if(!b_setup) - { - // make rocket sound after save\load - EMIT_SOUND( ENT(pev), CHAN_VOICE, "weapons/rocket1.wav", 1, 0.5 ); - // restore rocket trail - MESSAGE_BEGIN( MSG_BROADCAST, gmsgTempEntity ); - WRITE_BYTE( TE_BEAMFOLLOW ); - WRITE_SHORT(entindex()); // entity - WRITE_SHORT(m_iTrail ); // model - WRITE_BYTE( 30 ); // life - WRITE_BYTE( 5 ); // width - WRITE_BYTE( 200 ); // r, g, b - WRITE_BYTE( 200 ); // r, g, b - WRITE_BYTE( 200 ); // r, g, b - WRITE_BYTE( 200 ); // brightness - MESSAGE_END(); // move PHS/PVS data sending into here (SEND_ALL, SEND_PVS, SEND_PHS) - b_setup = TRUE; - } -} - - -void CRpgRocket :: FollowThink( void ) -{ - CBaseEntity *pOther = NULL; - Vector vecTarget; - Vector vecDir; - float flDist, flMax, flDot; - TraceResult tr; - UTIL_MakeAimVectors( pev->angles ); - - vecTarget = gpGlobals->v_forward; - flMax = 4096; - // Examine all entities within a reasonable radius - while ((pOther = UTIL_FindEntityByClassname( pOther, "laser_spot" )) != NULL) - { - UTIL_TraceLine ( pev->origin, pOther->pev->origin, dont_ignore_monsters, ENT(pev), &tr ); - // ALERT( at_console, "%f\n", tr.flFraction ); - if (tr.flFraction >= 0.90) - { - vecDir = pOther->pev->origin - pev->origin; - flDist = vecDir.Length( ); - vecDir = vecDir.Normalize( ); - flDot = DotProduct( gpGlobals->v_forward, vecDir ); - if ((flDot > 0) && (flDist * (1 - flDot) < flMax)) - { - flMax = flDist * (1 - flDot); - vecTarget = vecDir; - } - } - } - - pev->angles = UTIL_VecToAngles( vecTarget ); - - // this acceleration and turning math is totally wrong, but it seems to respond well so don't change it. - float flSpeed = pev->velocity.Length(); - if (gpGlobals->time - m_flIgniteTime < 1.0) - { - pev->velocity = pev->velocity * 0.2 + vecTarget * (flSpeed * 0.8 + 400); - if (pev->waterlevel == 3 && pev->watertype > CONTENTS_FLYFIELD) - { - // go slow underwater - if (pev->velocity.Length() > 300) - { - pev->velocity = pev->velocity.Normalize() * 300; - } - UTIL_BubbleTrail( pev->origin - pev->velocity * 0.1, pev->origin, 4 ); - } - else - { - if (pev->velocity.Length() > 2000) - { - pev->velocity = pev->velocity.Normalize() * 2000; - } - } - } - else - { - if (pev->effects & EF_LIGHT) - { - pev->effects = 0; - STOP_SOUND( ENT(pev), CHAN_VOICE, "weapons/rocket1.wav" ); - } - pev->velocity = pev->velocity * 0.2 + vecTarget * flSpeed * 0.798; - if ((pev->waterlevel == 0 || pev->watertype == CONTENTS_FOG) && pev->velocity.Length() < 1500) - { - Detonate( ); - } - } - //ALERT( at_console, "%.0f\n", flSpeed ); - - SetNextThink( 0.05 ); -} - -//=========================== -// -// Rocket launcher code -// -//=========================== - -//m_iOverloadLevel is LTD trigger like m_fSpotActive in original HL -//m_iChargeLevel is indicator what indicates how many missiles in air -//fired from this laubcher - -void CRpg::Spawn( ) -{ - Precache( ); - m_iId = WEAPON_RPG; - - SET_MODEL(ENT(pev), "models/w_rpg.mdl"); - m_iOverloadLevel = TRUE;//turn on LTD - - if ( IsMultiplayer() )m_iDefaultAmmo = RPG_DEFAULT_GIVE * 2; - else m_iDefaultAmmo = RPG_DEFAULT_GIVE; - FallInit();// get ready to fall down. -} - -void CRpg::Precache( void ) -{ - PRECACHE_MODEL("models/w_rpg.mdl"); - PRECACHE_MODEL("models/v_rpg.mdl"); - PRECACHE_MODEL("models/p_rpg.mdl"); - - PRECACHE_SOUND("items/9mmclip1.wav"); - - UTIL_PrecacheOther( "laser_spot" ); - UTIL_PrecacheOther( "rpg_rocket" ); - - PRECACHE_SOUND("weapons/rocketfire1.wav"); - PRECACHE_SOUND("weapons/glauncher.wav"); // alternative fire sound - PRECACHE_SOUND("weapons/beep.wav"); - PRECACHE_SOUND("weapons/spot_on.wav"); - PRECACHE_SOUND("weapons/spot_off.wav"); -} - -int CRpg::GetItemInfo(ItemInfo *p) -{ - p->pszName = STRING(pev->classname); - p->pszAmmo1 = "rockets"; - p->iMaxAmmo1 = ROCKET_MAX_CARRY; - p->pszAmmo2 = NULL; - p->iMaxAmmo2 = -1; - p->iMaxClip = RPG_MAX_CLIP; - p->iSlot = 3; - p->iPosition = 0; - p->iId = m_iId = WEAPON_RPG; - p->iFlags = 0; - p->iWeight = RPG_WEIGHT; - - return 1; -} - -BOOL CRpg::Deploy( ) -{ - return DefaultDeploy( "models/v_rpg.mdl", "models/p_rpg.mdl", RPG_DRAW, "rpg" ); -} - -void CRpg::Holster( ) -{ - ShutdownScreen();//set skin to 0 manually - m_fInReload = FALSE;// cancel any reload in progress. - m_pPlayer->m_flNextAttack = UTIL_WeaponTimeBase() + 0.5; - SendWeaponAnim( RPG_HOLSTER ); -} - -void CRpg::PrimaryAttack() -{ - if ( m_iClip ) - { - m_pPlayer->m_iWeaponVolume = LOUD_GUN_VOLUME; - m_pPlayer->m_iWeaponFlash = BRIGHT_GUN_FLASH; - - m_iBody = 1; //hide rocket in laubcher - SendWeaponAnim( RPG_FIRE); - // player "shoot" animation - m_pPlayer->SetAnimation( PLAYER_ATTACK1 ); - - UTIL_MakeVectors( m_pPlayer->pev->v_angle ); - Vector vecSrc = m_pPlayer->GetGunPosition( ) + gpGlobals->v_forward * 16 + gpGlobals->v_right * 8 + gpGlobals->v_up * -8; - - CRpgRocket *pRocket = CRpgRocket::Create ( vecSrc, m_pPlayer->pev->v_angle, m_pPlayer, this ); - UTIL_MakeVectors( m_pPlayer->pev->v_angle );// RpgRocket::Create stomps on globals, so remake. - pRocket->pev->velocity = pRocket->pev->velocity + gpGlobals->v_forward * DotProduct( m_pPlayer->pev->velocity, gpGlobals->v_forward ); - - // firing RPG no longer turns on the designator. ALT fire is a toggle switch for the LTD. - // Ken signed up for this as a global change (sjb) - - m_iClip--; - m_flNextPrimaryAttack = UTIL_WeaponTimeBase() + 1.5; - m_flTimeWeaponIdle = UTIL_WeaponTimeBase() + 1.5; - } - else - { - PlayEmptySound(); - m_flNextPrimaryAttack = UTIL_WeaponTimeBase() + 0.7;//no longer indicate fps :) - } - UpdateSpot( ); -} - -void CRpg::SecondaryAttack() -{ - m_iOverloadLevel = !m_iOverloadLevel; - if (!m_iOverloadLevel && m_pSpot) ShutdownScreen();//simply call shutdown function - - m_flNextSecondaryAttack = m_flNextPrimaryAttack = UTIL_WeaponTimeBase() + 0.3; -} - -void CRpg::Reload( void ) -{ - if ((m_pPlayer->m_rgAmmo[m_iPrimaryAmmoType] == 0) || ( m_iClip == 1 )) return; - - m_flNextPrimaryAttack = UTIL_WeaponTimeBase() + 0.5; - - if (m_iChargeLevel && m_iOverloadLevel ) return; - - if ( m_iClip == 0 ) - { - if ( m_pSpot && m_iOverloadLevel ) m_pSpot->Suspend( 2.1 ); - m_iBody = 0;//show rocket - DefaultReload( RPG_MAX_CLIP, RPG_RELOAD, 2.1 ); - m_flTimeWeaponIdle = UTIL_WeaponTimeBase() + RANDOM_FLOAT ( 10, 15 ); - } -} - -void CRpg::UpdateSpot( void ) -{ - if ( m_iOverloadLevel ) - { - if (!m_pSpot) - { - m_pSpot = CLaserSpot::CreateSpot( m_pPlayer->pev ); - EMIT_SOUND(ENT(m_pPlayer->pev), CHAN_ITEM, "weapons/spot_on.wav", 1, ATTN_NORM); - } - - UTIL_MakeVectors( m_pPlayer->pev->v_angle ); - Vector vecSrc = m_pPlayer->GetGunPosition( ); - Vector vecAiming = gpGlobals->v_forward; - - TraceResult tr; - UTIL_TraceLine ( vecSrc, vecSrc + vecAiming * 8192, dont_ignore_monsters, ignore_glass, ENT(m_pPlayer->pev), &tr ); - float flLength = (tr.vecEndPos - vecSrc).Length(); - - // disable scale feature - // m_pSpot->pev->scale = flLength / 650; - - int m_iSpotBright = ( 1 / log( flLength / 0.3 )) * 1700; - if ( m_iSpotBright > 255 ) m_iSpotBright = 255; - - m_iSpotBright = m_iSpotBright + RANDOM_LONG ( 1, flLength / 200 ); - m_pSpot->pev->renderamt = m_iSpotBright; - - UTIL_SetOrigin( m_pSpot, tr.vecEndPos + tr.vecPlaneNormal * 0.1 ); - } - - UpdateScreen();// update rocket screen -} - -void CRpg::UpdateScreen ( void ) -{ - if ( m_flTimeUpdate > UTIL_WeaponTimeBase() ) return; - - if (m_pSpot) - { - if ( m_iChargeLevel) - { - if( pev->skin >= 4 ) pev->skin = 1; - pev->skin++; - EMIT_SOUND(ENT(m_pPlayer->pev), CHAN_ITEM, "weapons/beep.wav", 1, ATTN_NORM); - } - else pev->skin = 5; - } - else pev->skin = 0; - - m_flTimeUpdate = UTIL_WeaponTimeBase() + 0.3; -} - -void CRpg::ShutdownScreen ( void ) -{ - pev->skin = 0; - if (m_pSpot) - { - m_pSpot->Killed( NULL, GIB_NEVER ); - m_pSpot = NULL; - EMIT_SOUND(ENT(m_pPlayer->pev), CHAN_ITEM, "weapons/spot_off.wav", 1, ATTN_NORM); - } -} - -void CRpg::WeaponIdle( void ) -{ - UpdateSpot( ); - - if ( m_flTimeWeaponIdle > UTIL_WeaponTimeBase() ) return; - - if (m_iClip && !m_iOverloadLevel) - { - int iAnim; - float flRand = RANDOM_FLOAT(0, 1); - if (flRand <= 0.75) - { - iAnim = RPG_IDLE; - m_flTimeWeaponIdle = UTIL_WeaponTimeBase() + 6.3; - } - else - { - iAnim = RPG_IDLE2; - m_flTimeWeaponIdle = UTIL_WeaponTimeBase() + 6.3; - } - SendWeaponAnim( iAnim ); - } -} - - - -class CRpgAmmo : public CBasePlayerAmmo -{ - void Spawn( void ) - { - Precache( ); - SET_MODEL(ENT(pev), "models/w_rpgammo.mdl"); - CBasePlayerAmmo::Spawn( ); - } - void Precache( void ) - { - PRECACHE_MODEL ("models/w_rpgammo.mdl"); - PRECACHE_SOUND("items/9mmclip1.wav"); - } - BOOL AddAmmo( CBaseEntity *pOther ) - { - int iGive; - - if ( IsMultiplayer() ) - { - // hand out more ammo per rocket in multiplayer. - iGive = AMMO_RPGCLIP_GIVE * 2; - } - else - { - iGive = AMMO_RPGCLIP_GIVE; - } - if (pOther->GiveAmmo( iGive, "rockets", ROCKET_MAX_CARRY ) != -1) - { - EMIT_SOUND(ENT(pev), CHAN_ITEM, "items/9mmclip1.wav", 1, ATTN_NORM); - return TRUE; - } - return FALSE; - } -}; -LINK_ENTITY_TO_CLASS( ammo_rpgclip, CRpgAmmo ); \ No newline at end of file diff --git a/spirit/spirit.def b/spirit/spirit.def deleted file mode 100644 index 0f288692..00000000 --- a/spirit/spirit.def +++ /dev/null @@ -1,5 +0,0 @@ -LIBRARY server -EXPORTS - GiveFnptrsToDll @1 -SECTIONS - .data READ WRITE diff --git a/spirit/subs.cpp b/spirit/subs.cpp deleted file mode 100644 index 3a517d65..00000000 --- a/spirit/subs.cpp +++ /dev/null @@ -1,890 +0,0 @@ -/*** -* -* Copyright (c) 1996-2002, 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. -* -****/ -/* - -===== subs.cpp ======================================================== - - frequently used global functions - -*/ - -#include "extdll.h" -#include "util.h" -#include "cbase.h" -#include "saverestore.h" -#include "nodes.h" -#include "doors.h" -#include "movewith.h" -#include "player.h" - -extern CGraph WorldGraph; - -extern BOOL FEntIsVisible(entvars_t* pev, entvars_t* pevTarget); - -extern DLL_GLOBAL int g_iSkillLevel; - -// Landmark class -void CPointEntity :: Spawn( void ) -{ - pev->solid = SOLID_NOT; -} - -class CNullEntity : public CBaseEntity -{ -public: - void Spawn( void ); -}; - - -// Null Entity, remove on startup -void CNullEntity :: Spawn( void ) -{ - REMOVE_ENTITY(ENT(pev)); -} -LINK_ENTITY_TO_CLASS(info_null,CNullEntity); -LINK_ENTITY_TO_CLASS(info_texlights,CNullEntity); // don't complain about Merl's new info entities -LINK_ENTITY_TO_CLASS(info_compile_parameters,CNullEntity); - -class CBaseDMStart : public CPointEntity -{ -public: - void KeyValue( KeyValueData *pkvd ); - STATE GetState( CBaseEntity *pEntity ); - -private: -}; - -// These are the new entry points to entities. -LINK_ENTITY_TO_CLASS(info_player_deathmatch,CBaseDMStart); -LINK_ENTITY_TO_CLASS(info_player_start,CPointEntity); -LINK_ENTITY_TO_CLASS(info_landmark,CPointEntity); - -void CBaseDMStart::KeyValue( KeyValueData *pkvd ) -{ - if (FStrEq(pkvd->szKeyName, "master")) - { - pev->netname = ALLOC_STRING(pkvd->szValue); - pkvd->fHandled = TRUE; - } - else - CPointEntity::KeyValue( pkvd ); -} - -STATE CBaseDMStart::GetState( CBaseEntity *pEntity ) -{ - if (UTIL_IsMasterTriggered( pev->netname, pEntity )) - return STATE_ON; - else - return STATE_OFF; -} - -// This updates global tables that need to know about entities being removed -void CBaseEntity::UpdateOnRemove( void ) -{ - int i; - - ResetParent(); - - if ( FBitSet( pev->flags, FL_GRAPHED ) ) - { - // this entity was a LinkEnt in the world node graph, so we must remove it from - // the graph since we are removing it from the world. - for ( i = 0 ; i < WorldGraph.m_cLinks ; i++ ) - { - if ( WorldGraph.m_pLinkPool [ i ].m_pLinkEnt == pev ) - { - // if this link has a link ent which is the same ent that is removing itself, remove it! - WorldGraph.m_pLinkPool [ i ].m_pLinkEnt = NULL; - } - } - } - if ( pev->globalname ) - gGlobalState.EntitySetState( pev->globalname, GLOBAL_DEAD ); -} - -// Convenient way to delay removing oneself -void CBaseEntity :: SUB_Remove( void ) -{ - UpdateOnRemove(); - if (pev->health > 0) - { - // this situation can screw up monsters who can't tell their entity pointers are invalid. - pev->health = 0; - ALERT( at_aiconsole, "SUB_Remove called on entity with health > 0\n"); - } - - REMOVE_ENTITY(ENT(pev)); -} - - -// Convenient way to explicitly do nothing (passed to functions that require a method) -void CBaseEntity :: SUB_DoNothing( void ) -{ -// if (pev->ltime) -// ALERT(at_console, "Doing Nothing %f\n", pev->ltime); -// else -// ALERT(at_console, "Doing Nothing %f\n", gpGlobals->time); -} - - -// Global Savedata for Delay -TYPEDESCRIPTION CBaseDelay::m_SaveData[] = -{ - DEFINE_FIELD( CBaseDelay, m_flDelay, FIELD_FLOAT ), - DEFINE_FIELD( CBaseDelay, m_iszKillTarget, FIELD_STRING ), - DEFINE_FIELD( CBaseDelay, m_hActivator, FIELD_EHANDLE ), //LRC -}; - -IMPLEMENT_SAVERESTORE( CBaseDelay, CBaseEntity ); - -void CBaseDelay :: KeyValue( KeyValueData *pkvd ) -{ - if (FStrEq(pkvd->szKeyName, "delay")) - { - m_flDelay = atof( pkvd->szValue ); - pkvd->fHandled = TRUE; - } - else if (FStrEq(pkvd->szKeyName, "killtarget")) - { - m_iszKillTarget = ALLOC_STRING(pkvd->szValue); - pkvd->fHandled = TRUE; - } - else - { - CBaseEntity::KeyValue( pkvd ); - } -} - - -/* -============================== -SUB_UseTargets - -If self.delay is set, a DelayedUse entity will be created that will actually -do the SUB_UseTargets after that many seconds have passed. - -Removes all entities with a targetname that match self.killtarget, -and removes them, so some events can remove other triggers. - -Search for (string)targetname in all entities that -match (string)self.target and call their .use function (if they have one) - -============================== -*/ -void CBaseEntity :: SUB_UseTargets( CBaseEntity *pActivator, USE_TYPE useType, float value ) -{ - // - // fire targets - // - if (!FStringNull(pev->target)) - { - FireTargets( STRING(pev->target), pActivator, this, useType, value ); - } -} - -//======================================================================== -// FireTargets - supported prefix "+", "-", "!", ">", "<", "?". -// supported also this and self pointers - e.g. "fadein(mywall)" -//======================================================================== -void FireTargets( string_t targetName, CBaseEntity *pActivator, CBaseEntity *pCaller, USE_TYPE useType, float value ) -{ - //overload version UTIL_FireTargets - if (!targetName) return;//no execute code, if target blank - - FireTargets( STRING(targetName), pActivator, pCaller, useType, value); -} - -void FireTargets( const char *targetName, CBaseEntity *pActivator, CBaseEntity *pCaller, USE_TYPE useType, float value ) -{ - const char *inputTargetName = targetName; - CBaseEntity *inputActivator = pActivator; - CBaseEntity *pTarget = NULL; - int i,j, found = false; - char szBuf[80]; - - if ( !targetName ) return; - - if (targetName[0] == '+') - { - targetName++; - useType = USE_ON; - } - else if (targetName[0] == '-') - { - targetName++; - useType = USE_OFF; - } - else if (targetName[0] == '<') - { - targetName++; - useType = USE_SET; - } - else if (targetName[0] == '!') - { - targetName++; - useType = USE_KILL; - } - - pTarget = UTIL_FindEntityByTargetname(pTarget, targetName, pActivator); - - if( !pTarget )//smart field name ? - { - //try to extract value from name (it's more usefully than "locus" specifier) - for (i = 0; targetName[i]; i++) - { - if (targetName[i] == '.')//value specifier - { - value = atof(&targetName[i+1]); - sprintf(szBuf, targetName); - szBuf[i] = 0; - targetName = szBuf; - pTarget = UTIL_FindEntityByTargetname(NULL, targetName, inputActivator); - break; - } - } - - if( !pTarget )//try to extract activator specified - { - for (i = 0; targetName[i]; i++) - { - if (targetName[i] == '(') - { - i++; - for (j = i; targetName[j]; j++) - { - if (targetName[j] == ')') - { - strncpy(szBuf, targetName+i, j-i); - szBuf[j-i] = 0; - pActivator = UTIL_FindEntityByTargetname(NULL, szBuf, inputActivator); - if (!pActivator) return; //it's a locus specifier, but the locus is invalid. - found = true; - break; - } - } - if (!found) ALERT(at_error, "Missing ')' in targetname: %s", inputTargetName); - break; - } - } - if (!found) return; // no, it's not a locus specifier. - - strncpy(szBuf, targetName, i-1); - szBuf[i-1] = 0; - targetName = szBuf; - pTarget = UTIL_FindEntityByTargetname(NULL, targetName, inputActivator); - - if (!pTarget)return; // it's a locus specifier all right, but the target's invalid. - } - } - - ALERT( at_aiconsole, "Firing: (%s) with %s and value %g\n", targetName, GetStringForUseType( useType ), value ); - - do // start firing targets - { - if ( !(pTarget->pev->flags & FL_KILLME) ) // Don't use dying ents - { - if (useType == USE_KILL) UTIL_Remove( pTarget ); - else pTarget->Use( pActivator, pCaller, useType, value ); - } - pTarget = UTIL_FindEntityByTargetname(pTarget, targetName, inputActivator); - - } while (pTarget); - - //LRC- Firing has finished, aliases can now reflect their new values. - UTIL_FlushAliases(); -} - -LINK_ENTITY_TO_CLASS( DelayedUse, CBaseDelay ); - - -void CBaseDelay :: SUB_UseTargets( CBaseEntity *pActivator, USE_TYPE useType, float value ) -{ - // - // exit immediatly if we don't have a target or kill target - // - if (FStringNull(pev->target) && !m_iszKillTarget) - return; - - // - // check for a delay - // - if (m_flDelay != 0) - { - // create a temp object to fire at a later time - CBaseDelay *pTemp = GetClassPtr( (CBaseDelay *)NULL); - pTemp->pev->classname = MAKE_STRING("DelayedUse"); - - pTemp->SetNextThink( m_flDelay ); - - pTemp->SetThink(&CBaseDelay:: DelayThink ); - - // Save the useType - pTemp->pev->button = (int)useType; - pTemp->m_iszKillTarget = m_iszKillTarget; - pTemp->m_flDelay = 0; // prevent "recursion" - pTemp->pev->target = pev->target; - - //LRC - Valve had a hacked thing here to avoid breaking - // save/restore. In Spirit that's not a problem. - // I've moved m_hActivator into this class, for the "elegant" fix. - pTemp->m_hActivator = pActivator; - - return; - } - - // - // kill the killtargets - // - - if ( m_iszKillTarget ) - { - edict_t *pentKillTarget = NULL; - - ALERT( at_aiconsole, "KillTarget: %s\n", STRING(m_iszKillTarget) ); - //LRC- now just USE_KILLs its killtarget, for consistency. - FireTargets( STRING(m_iszKillTarget), pActivator, this, USE_KILL, 0); - } - - // - // fire targets - // - if (!FStringNull(pev->target)) - { - FireTargets( STRING(pev->target), pActivator, this, useType, value ); - } -} - - -/* -void CBaseDelay :: SUB_UseTargetsEntMethod( void ) -{ - SUB_UseTargets(pev); -} -*/ - -/* -QuakeEd only writes a single float for angles (bad idea), so up and down are -just constant angles. -*/ -void SetMovedir( entvars_t *pev ) -{ - pev->movedir = GetMovedir(pev->angles); - pev->angles = g_vecZero; -} - -Vector GetMovedir( Vector vecAngles ) -{ - if (vecAngles == Vector(0, -1, 0)) - { - return Vector(0, 0, 1); - } - else if (vecAngles == Vector(0, -2, 0)) - { - return Vector(0, 0, -1); - } - else - { - UTIL_MakeVectors(vecAngles); - return gpGlobals->v_forward; - } -} - - - -void CBaseDelay::DelayThink( void ) -{ - CBaseEntity *pActivator = NULL; - - // The use type is cached (and stashed) in pev->button - //LRC - now using m_hActivator. - SUB_UseTargets( m_hActivator, (USE_TYPE)pev->button, 0 ); - REMOVE_ENTITY(ENT(pev)); -} - - -// Global Savedata for Toggle -TYPEDESCRIPTION CBaseToggle::m_SaveData[] = -{ - DEFINE_FIELD( CBaseToggle, m_toggle_state, FIELD_INTEGER ), - DEFINE_FIELD( CBaseToggle, m_flActivateFinished, FIELD_TIME ), - DEFINE_FIELD( CBaseToggle, m_flMoveDistance, FIELD_FLOAT ), - DEFINE_FIELD( CBaseToggle, m_flWait, FIELD_FLOAT ), - DEFINE_FIELD( CBaseToggle, m_flLip, FIELD_FLOAT ), - DEFINE_FIELD( CBaseToggle, m_flTWidth, FIELD_FLOAT ), - DEFINE_FIELD( CBaseToggle, m_flTLength, FIELD_FLOAT ), - DEFINE_FIELD( CBaseToggle, m_vecPosition1, FIELD_POSITION_VECTOR ), - DEFINE_FIELD( CBaseToggle, m_vecPosition2, FIELD_POSITION_VECTOR ), - DEFINE_FIELD( CBaseToggle, m_vecAngle1, FIELD_VECTOR ), // UNDONE: Position could go through transition, but also angle? - DEFINE_FIELD( CBaseToggle, m_vecAngle2, FIELD_VECTOR ), // UNDONE: Position could go through transition, but also angle? - DEFINE_FIELD( CBaseToggle, m_cTriggersLeft, FIELD_INTEGER ), - DEFINE_FIELD( CBaseToggle, m_flHeight, FIELD_FLOAT ), -// DEFINE_FIELD( CBaseToggle, m_hActivator, FIELD_EHANDLE ), - DEFINE_FIELD( CBaseToggle, m_pfnCallWhenMoveDone, FIELD_FUNCTION ), - DEFINE_FIELD( CBaseToggle, m_vecFinalDest, FIELD_POSITION_VECTOR ), - DEFINE_FIELD( CBaseToggle, m_flLinearMoveSpeed, FIELD_FLOAT ), - DEFINE_FIELD( CBaseToggle, m_flAngularMoveSpeed, FIELD_FLOAT ), //LRC - DEFINE_FIELD( CBaseToggle, m_vecFinalAngle, FIELD_VECTOR ), - DEFINE_FIELD( CBaseToggle, m_sMaster, FIELD_STRING), - DEFINE_FIELD( CBaseToggle, m_bitsDamageInflict, FIELD_INTEGER ), // damage type inflicted -}; -IMPLEMENT_SAVERESTORE( CBaseToggle, CBaseAnimating ); - - -void CBaseToggle::KeyValue( KeyValueData *pkvd ) -{ - if (FStrEq(pkvd->szKeyName, "lip")) - { - m_flLip = atof(pkvd->szValue); - pkvd->fHandled = TRUE; - } - else if (FStrEq(pkvd->szKeyName, "wait")) - { - m_flWait = atof(pkvd->szValue); - pkvd->fHandled = TRUE; - } - else if (FStrEq(pkvd->szKeyName, "master")) - { - m_sMaster = ALLOC_STRING(pkvd->szValue); - pkvd->fHandled = TRUE; - } - else if (FStrEq(pkvd->szKeyName, "distance")) - { - m_flMoveDistance = atof(pkvd->szValue); - pkvd->fHandled = TRUE; - } - else - CBaseDelay::KeyValue( pkvd ); -} - -/* -//void CBaseToggle :: LinearMove( Vector vecInput, float flSpeed) -//{ -// LinearMove(vecInput, flSpeed); -//} - -/* -============= -LinearMove - -calculate pev->velocity and pev->nextthink to reach vecDest from -pev->origin traveling at flSpeed -=============== -*/ -void CBaseToggle :: LinearMove( Vector vecInput, float flSpeed )//, BOOL bNow ) -{ -// ALERT(at_console, "LMove %s: %f %f %f, speed %f\n", STRING(pev->targetname), vecInput.x, vecInput.y, vecInput.z, flSpeed); - ASSERTSZ(flSpeed != 0, "LinearMove: no speed is defined!"); -// ASSERTSZ(m_pfnCallWhenMoveDone != NULL, "LinearMove: no post-move function defined"); - - m_flLinearMoveSpeed = flSpeed; - m_vecFinalDest = vecInput; - -// if ((m_pMoveWith || m_pChildMoveWith))// && !bNow) -// { -// ALERT(at_console,"Setting LinearMoveNow to happen after %f\n",gpGlobals->time); - SetThink(&CBaseToggle :: LinearMoveNow ); - UTIL_DesiredThink( this ); - //pev->nextthink = pev->ltime + 0.01; -// } -// else -// { -// LinearMoveNow(); // starring Martin Sheen and Marlon Brando -// } -} - -void CBaseToggle :: LinearMoveNow( void ) -{ -// ALERT(at_console, "LMNow %s\n", STRING(pev->targetname)); - - Vector vecDest; -// if (m_pMoveWith || m_pChildMoveWith ) -// ALERT(at_console,"THINK: LinearMoveNow happens at %f, speed %f\n",gpGlobals->time, m_flLinearMoveSpeed); - - if (m_pMoveWith) - { - vecDest = m_vecFinalDest + m_pMoveWith->pev->origin; - } - else - vecDest = m_vecFinalDest; - -// ALERT(at_console,"LinearMoveNow: Destination is (%f %f %f), finalDest was (%f %f %f)\n", -// vecDest.x,vecDest.y,vecDest.z, -// m_vecFinalDest.x,m_vecFinalDest.y,m_vecFinalDest.z -// ); - - // Already there? - if (vecDest == pev->origin) - { - LinearMoveDone(); - return; - } - - // set destdelta to the vector needed to move - Vector vecDestDelta = vecDest - pev->origin; - - // divide vector length by speed to get time to reach dest - float flTravelTime = vecDestDelta.Length() / m_flLinearMoveSpeed; - - // set nextthink to trigger a call to LinearMoveDone when dest is reached - SetNextThink( flTravelTime, TRUE ); - SetThink(&CBaseToggle :: LinearMoveDone ); - - // scale the destdelta vector by the time spent traveling to get velocity -// pev->velocity = vecDestDelta / flTravelTime; - UTIL_SetVelocity( this, vecDestDelta / flTravelTime ); - -// ALERT(at_console, "LMNow \"%s\": Vel %f %f %f, think %f\n", STRING(pev->targetname), pev->velocity.x, pev->velocity.y, pev->velocity.z, pev->nextthink); -} - - -/* -============ -After moving, set origin to exact final destination, call "move done" function -============ -*/ -/*void CBaseToggle :: LinearMoveDone( void ) -{ - Vector vecDiff; - if (m_pMoveWith) - vecDiff = (m_vecFinalDest + m_pMoveWith->pev->origin) - pev->origin; - else - vecDiff = m_vecFinalDest - pev->origin; - if (vecDiff.Length() > 0.05) //pev->velocity.Length()) - { - // HACK: not there yet, try waiting one more frame. - ALERT(at_console,"Rejecting difference %f\n",vecDiff.Length()); - SetThink(&CBaseToggle ::LinearMoveFinalDone); - pev->nextthink = gpGlobals->time + 0.01; - } - else - { - LinearMoveFinalDone(); - } -}*/ - -void CBaseToggle :: LinearMoveDone( void ) -{ - SetThink(&CBaseToggle ::LinearMoveDoneNow); -// ALERT(at_console, "LMD: desiredThink %s\n", STRING(pev->targetname)); - UTIL_DesiredThink( this ); -} - -void CBaseToggle :: LinearMoveDoneNow( void ) -{ - Vector vecDest; - -// ALERT(at_console, "LMDone %s\n", STRING(pev->targetname)); - - UTIL_SetVelocity(this, g_vecZero);//, TRUE); -// pev->velocity = g_vecZero; - if (m_pMoveWith) - { - vecDest = m_vecFinalDest + m_pMoveWith->pev->origin; -// ALERT(at_console, "LMDone %s: p.origin = %f %f %f, origin = %f %f %f. Set it to %f %f %f\n", STRING(pev->targetname), m_pMoveWith->pev->origin.x, m_pMoveWith->pev->origin.y, m_pMoveWith->pev->origin.z, pev->origin.x, pev->origin.y, pev->origin.z, vecDest.x, vecDest.y, vecDest.z); - } - else - { - vecDest = m_vecFinalDest; -// ALERT(at_console, "LMDone %s: origin = %f %f %f. Set it to %f %f %f\n", STRING(pev->targetname), pev->origin.x, pev->origin.y, pev->origin.z, vecDest.x, vecDest.y, vecDest.z); - } - UTIL_AssignOrigin(this, vecDest); - DontThink(); //LRC - //pev->nextthink = -1; - if ( m_pfnCallWhenMoveDone ) - (this->*m_pfnCallWhenMoveDone)(); -} - -BOOL CBaseToggle :: IsLockedByMaster( void ) -{ - if (UTIL_IsMasterTriggered(m_sMaster, m_hActivator)) - return FALSE; - else - return TRUE; -} - -//LRC- mapping toggle-states to global states -STATE CBaseToggle :: GetState ( void ) -{ - switch (m_toggle_state) - { - case TS_AT_TOP: return STATE_ON; - case TS_AT_BOTTOM: return STATE_OFF; - case TS_GOING_UP: return STATE_TURN_ON; - case TS_GOING_DOWN: return STATE_TURN_OFF; - default: return STATE_OFF; // This should never happen. - } -}; - -/* -============= -AngularMove - -calculate pev->velocity and pev->nextthink to reach vecDest from -pev->origin traveling at flSpeed -Just like LinearMove, but rotational. -=============== -*/ -void CBaseToggle :: AngularMove( Vector vecDestAngle, float flSpeed ) -{ - ASSERTSZ(flSpeed != 0, "AngularMove: no speed is defined!"); -// ASSERTSZ(m_pfnCallWhenMoveDone != NULL, "AngularMove: no post-move function defined"); - - m_vecFinalAngle = vecDestAngle; - m_flAngularMoveSpeed = flSpeed; - -// if ((m_pMoveWith || m_pChildMoveWith))// && !bNow) -// { -// ALERT(at_console,"Setting AngularMoveNow to happen after %f\n",gpGlobals->time); - SetThink(&CBaseToggle :: AngularMoveNow ); - UTIL_DesiredThink( this ); -// ExternalThink( 0.01 ); -// pev->nextthink = pev->ltime + 0.01; -// } -// else -// { -// AngularMoveNow(); // starring Martin Sheen and Marlon Brando -// } -} - -void CBaseToggle :: AngularMoveNow() -{ -// ALERT(at_console, "AngularMoveNow %f\n", pev->ltime); - Vector vecDestAngle; - - if (m_pMoveWith) - vecDestAngle = m_vecFinalAngle + m_pMoveWith->pev->angles; - else - vecDestAngle = m_vecFinalAngle; - - // Already there? - if (vecDestAngle == pev->angles) - { - AngularMoveDone(); - return; - } - - // set destdelta to the vector needed to move - Vector vecDestDelta = vecDestAngle - pev->angles; - - // divide by speed to get time to reach dest - float flTravelTime = vecDestDelta.Length() / m_flAngularMoveSpeed; - - // set nextthink to trigger a call to AngularMoveDone when dest is reached - SetNextThink( flTravelTime, TRUE ); - SetThink(&CBaseToggle :: AngularMoveDone ); - - // scale the destdelta vector by the time spent traveling to get velocity - UTIL_SetAvelocity(this, vecDestDelta / flTravelTime ); -} - -void CBaseToggle :: AngularMoveDone( void ) -{ - SetThink(&CBaseToggle ::AngularMoveDoneNow); -// ALERT(at_console, "LMD: desiredThink %s\n", STRING(pev->targetname)); - UTIL_DesiredThink( this ); -} - -/* -============ -After rotating, set angle to exact final angle, call "move done" function -============ -*/ -void CBaseToggle :: AngularMoveDoneNow( void ) -{ -// ALERT(at_console, "AngularMoveDone %f\n", pev->ltime); - UTIL_SetAvelocity(this, g_vecZero); - if (m_pMoveWith) - { - UTIL_AssignAngles(this, m_vecFinalAngle + m_pMoveWith->pev->angles); - } - else - { - UTIL_AssignAngles(this, m_vecFinalAngle); - } - DontThink(); - if ( m_pfnCallWhenMoveDone ) - (this->*m_pfnCallWhenMoveDone)(); -} - -// this isn't currently used. Otherwise I'd fix it to use movedir the way it should... -float CBaseToggle :: AxisValue( int flags, const Vector &angles ) -{ - if ( FBitSet(flags, SF_DOOR_ROTATE_Z) ) - return angles.z; - if ( FBitSet(flags, SF_DOOR_ROTATE_X) ) - return angles.x; - - return angles.y; -} - - -void CBaseToggle :: AxisDir( entvars_t *pev ) -{ - if ( pev->movedir != g_vecZero) //LRC - return; - - if ( FBitSet(pev->spawnflags, SF_DOOR_ROTATE_Z) ) - pev->movedir = Vector ( 0, 0, 1 ); // around z-axis - else if ( FBitSet(pev->spawnflags, SF_DOOR_ROTATE_X) ) - pev->movedir = Vector ( 1, 0, 0 ); // around x-axis - else - pev->movedir = Vector ( 0, 1, 0 ); // around y-axis -} - - -float CBaseToggle :: AxisDelta( int flags, const Vector &angle1, const Vector &angle2 ) -{ - if ( FBitSet (flags, SF_DOOR_ROTATE_Z) ) - return angle1.z - angle2.z; - - if ( FBitSet (flags, SF_DOOR_ROTATE_X) ) - return angle1.x - angle2.x; - - return angle1.y - angle2.y; -} - - -/* -============= -FEntIsVisible - -returns TRUE if the passed entity is visible to caller, even if not infront () -============= -*/ - BOOL -FEntIsVisible( - entvars_t* pev, - entvars_t* pevTarget) - { - Vector vecSpot1 = pev->origin + pev->view_ofs; - Vector vecSpot2 = pevTarget->origin + pevTarget->view_ofs; - TraceResult tr; - - UTIL_TraceLine(vecSpot1, vecSpot2, ignore_monsters, ENT(pev), &tr); - - if (tr.fInOpen && tr.fInWater) - return FALSE; // sight line crossed contents - - if (tr.flFraction == 1) - return TRUE; - - return FALSE; - } - - -//========================================================= -// LRC - info_movewith, the first entity I've made which -// truly doesn't fit ANY preexisting category. -//========================================================= -#define SF_IMW_INACTIVE 1 -#define SF_IMW_BLOCKABLE 2 - -class CInfoMoveWith : public CBaseEntity -{ -public: - void Spawn( void ); - void Use( CBaseEntity *pActivator, CBaseEntity *pCaller, USE_TYPE useType, float value ); - virtual int ObjectCaps( void ) { return CBaseEntity :: ObjectCaps() & ~FCAP_ACROSS_TRANSITION; } - - STATE GetState() { return (pev->spawnflags & SF_IMW_INACTIVE)?STATE_OFF:STATE_ON; } -}; - -LINK_ENTITY_TO_CLASS(info_movewith, CInfoMoveWith); - -void CInfoMoveWith :: Spawn( void ) -{ - if (pev->spawnflags & SF_IMW_INACTIVE) - m_MoveWith = pev->netname; - else - m_MoveWith = pev->target; - - if (pev->spawnflags & SF_IMW_BLOCKABLE) - { - pev->solid = SOLID_SLIDEBOX; - pev->movetype = MOVETYPE_FLY; - } - // and allow InitMoveWith to set things up as usual. -} - -void CInfoMoveWith :: Use( CBaseEntity *pActivator, CBaseEntity *pCaller, USE_TYPE useType, float value ) -{ - CBaseEntity *pSibling; - - if (!ShouldToggle(useType)) return; - - if (m_pMoveWith) - { - // remove this from the old parent's list of children - pSibling = m_pMoveWith->m_pChildMoveWith; - if (pSibling == this) - m_pMoveWith->m_pChildMoveWith = this->m_pSiblingMoveWith; - else - { - while (pSibling->m_pSiblingMoveWith && pSibling->m_pSiblingMoveWith != this) - { pSibling = pSibling->m_pSiblingMoveWith; } - - if (pSibling->m_pSiblingMoveWith == this) - { - pSibling->m_pSiblingMoveWith = this->m_pSiblingMoveWith; - } - else - { - // failed to find myself in the list, complain - ALERT(at_error, "info_movewith can't find itself\n"); - return; - } - } - m_pMoveWith = NULL; - m_pSiblingMoveWith = NULL; - } - - if (pev->spawnflags & SF_IMW_INACTIVE) - { - pev->spawnflags &= ~SF_IMW_INACTIVE; - m_MoveWith = pev->target; - } - else - { - pev->spawnflags |= SF_IMW_INACTIVE; - m_MoveWith = pev->netname; - } - - // set things up for the new m_MoveWith value - if (!m_MoveWith) - { - UTIL_SetVelocity(this, g_vecZero); // come to a stop - return; - } - - m_pMoveWith = UTIL_FindEntityByTargetname(NULL, STRING(m_MoveWith)); - if (!m_pMoveWith) - { - ALERT(at_debug,"Missing movewith entity %s\n", STRING(m_MoveWith)); - return; - } - - pSibling = m_pMoveWith->m_pChildMoveWith; - while (pSibling) // check that this entity isn't already in the list of children - { - if (pSibling == this) return; - pSibling = pSibling->m_pSiblingMoveWith; - } - - // add this entity to the list of children - m_pSiblingMoveWith = m_pMoveWith->m_pChildMoveWith; // may be null: that's fine by me. - m_pMoveWith->m_pChildMoveWith = this; - m_vecOffsetOrigin = pev->origin - m_pMoveWith->pev->origin; - UTIL_SetVelocity(this, g_vecZero); // match speed with the new entity -} diff --git a/spirit/triggers.cpp b/spirit/triggers.cpp deleted file mode 100644 index 86efc25b..00000000 --- a/spirit/triggers.cpp +++ /dev/null @@ -1,5420 +0,0 @@ -/*** -* -* Copyright (c) 1996-2002, 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. -* -****/ -/* - -===== triggers.cpp ======================================================== - - spawn and use functions for editor-placed triggers - -*/ -//CODIAC - Linux needs this for tolower -#include - -#include "extdll.h" -#include "util.h" -#include "cbase.h" -#include "player.h" -#include "saverestore.h" -#include "trains.h" // trigger_camera has train functionality -#include "gamerules.h" -#include "talkmonster.h" -#include "weapons.h" //LRC, for trigger_hevcharge -#include "movewith.h" //LRC -#include "locus.h" //LRC -//#include "hgrunt.h" -//#include "islave.h" - -#define SF_TRIGGER_PUSH_START_OFF 2//spawnflag that makes trigger_push spawn turned OFF -#define SF_TRIGGER_HURT_TARGETONCE 1// Only fire hurt target once -#define SF_TRIGGER_HURT_START_OFF 2//spawnflag that makes trigger_hurt spawn turned OFF -#define SF_TRIGGER_HURT_NO_CLIENTS 8// clients may not touch this trigger. -#define SF_TRIGGER_HURT_CLIENTONLYFIRE 16// trigger hurt will only fire its target if it is hurting a client -#define SF_TRIGGER_HURT_CLIENTONLYTOUCH 32// only clients may touch this trigger. - -extern DLL_GLOBAL BOOL g_fGameOver; - -extern void SetMovedir(entvars_t* pev); -extern Vector VecBModelOrigin( entvars_t* pevBModel ); - -class CFrictionModifier : public CBaseEntity -{ -public: - void Spawn( void ); - void KeyValue( KeyValueData *pkvd ); - void EXPORT ChangeFriction( CBaseEntity *pOther ); - virtual int Save( CSave &save ); - virtual int Restore( CRestore &restore ); - - virtual int ObjectCaps( void ) { return CBaseEntity :: ObjectCaps() & ~FCAP_ACROSS_TRANSITION; } - - static TYPEDESCRIPTION m_SaveData[]; - - float m_frictionFraction; // Sorry, couldn't resist this name :) -}; - -LINK_ENTITY_TO_CLASS( func_friction, CFrictionModifier ); - -// Global Savedata for changelevel friction modifier -TYPEDESCRIPTION CFrictionModifier::m_SaveData[] = -{ - DEFINE_FIELD( CFrictionModifier, m_frictionFraction, FIELD_FLOAT ), -}; - -IMPLEMENT_SAVERESTORE(CFrictionModifier,CBaseEntity); - - -// Modify an entity's friction -void CFrictionModifier :: Spawn( void ) -{ - pev->solid = SOLID_TRIGGER; - SET_MODEL(ENT(pev), STRING(pev->model)); // set size and link into world - pev->movetype = MOVETYPE_NONE; - SetTouch(&CFrictionModifier :: ChangeFriction ); -} - - -// Sets toucher's friction to m_frictionFraction (1.0 = normal friction) -void CFrictionModifier :: ChangeFriction( CBaseEntity *pOther ) -{ - if ( pOther->pev->movetype != MOVETYPE_BOUNCEMISSILE && pOther->pev->movetype != MOVETYPE_BOUNCE ) - pOther->pev->friction = m_frictionFraction; -} - - - -// Sets toucher's friction to m_frictionFraction (1.0 = normal friction) -void CFrictionModifier :: KeyValue( KeyValueData *pkvd ) -{ - if (FStrEq(pkvd->szKeyName, "modifier")) - { - m_frictionFraction = atof(pkvd->szValue) / 100.0; - pkvd->fHandled = TRUE; - } - else - CBaseEntity::KeyValue( pkvd ); -} - - -// This trigger will fire when the level spawns (or respawns if not fire once) -// It will check a global state before firing. It supports delay and killtargets - -#define SF_AUTO_FIREONCE 0x0001 -#define SF_AUTO_FROMPLAYER 0x0002 - -class CAutoTrigger : public CBaseDelay -{ -public: - void KeyValue( KeyValueData *pkvd ); - void Activate( void ); - void DesiredAction( void ); - - int ObjectCaps( void ) { return CBaseDelay::ObjectCaps() & ~FCAP_ACROSS_TRANSITION; } - virtual int Save( CSave &save ); - virtual int Restore( CRestore &restore ); - - static TYPEDESCRIPTION m_SaveData[]; - -private: - int m_globalstate; - USE_TYPE triggerType; -}; -LINK_ENTITY_TO_CLASS( trigger_auto, CAutoTrigger ); - -TYPEDESCRIPTION CAutoTrigger::m_SaveData[] = -{ - DEFINE_FIELD( CAutoTrigger, m_globalstate, FIELD_STRING ), - DEFINE_FIELD( CAutoTrigger, triggerType, FIELD_INTEGER ), -}; - -IMPLEMENT_SAVERESTORE(CAutoTrigger,CBaseDelay); - -void CAutoTrigger::KeyValue( KeyValueData *pkvd ) -{ - if (FStrEq(pkvd->szKeyName, "globalstate")) - { - m_globalstate = ALLOC_STRING( pkvd->szValue ); - pkvd->fHandled = TRUE; - } - else if (FStrEq(pkvd->szKeyName, "triggerstate")) - { - int type = atoi( pkvd->szValue ); - switch( type ) - { - case 0: - triggerType = USE_OFF; - break; - case 2: - triggerType = USE_TOGGLE; - break; - default: - triggerType = USE_ON; - break; - } - pkvd->fHandled = TRUE; - } - else - CBaseDelay::KeyValue( pkvd ); -} - -void CAutoTrigger::Activate( void ) -{ -// ALERT(at_console, "trigger_auto targetting \"%s\": activate\n", STRING(pev->target)); - UTIL_DesiredAction( this ); //LRC - don't think until the player has spawned. - - CBaseDelay::Activate(); -} - -void CAutoTrigger::DesiredAction( void ) -{ -// ALERT(at_console, "trigger_auto targetting \"%s\": Fire at time %f\n", STRING(pev->target), gpGlobals->time); - if ( !m_globalstate || gGlobalState.EntityGetState( m_globalstate ) == GLOBAL_ON ) - { - if (pev->spawnflags & SF_AUTO_FROMPLAYER) - { - CBaseEntity* pPlayer = UTIL_FindEntityByClassname(NULL, "player"); - if (pPlayer) - SUB_UseTargets( pPlayer, triggerType, 0 ); - else - ALERT(at_error,"trigger_auto: \"From Player\" is ticked, but no player found!\n"); - } - else - { - SUB_UseTargets( this, triggerType, 0 ); - } - if ( pev->spawnflags & SF_AUTO_FIREONCE ) - UTIL_Remove( this ); - } -} - -#define SF_RELAY_FIREONCE 0x00000001 -#define SF_FIRE_CAMERA 0x00000002 -#define SF_RELAY_USESAME 0x80000000 - -class CTriggerRelay : public CBaseDelay -{ -public: - void KeyValue( KeyValueData *pkvd ); - void Spawn( void ); - void Use( CBaseEntity *pActivator, CBaseEntity *pCaller, USE_TYPE useType, float value ); - - int ObjectCaps( void ) { return CBaseDelay::ObjectCaps() & ~FCAP_ACROSS_TRANSITION; } - virtual int Save( CSave &save ); - virtual int Restore( CRestore &restore ); - - static TYPEDESCRIPTION m_SaveData[]; - -private: - USE_TYPE m_triggerType; - int m_sMaster; - int m_iszAltTarget; -}; -LINK_ENTITY_TO_CLASS( trigger_relay, CTriggerRelay ); - -TYPEDESCRIPTION CTriggerRelay::m_SaveData[] = -{ - DEFINE_FIELD( CTriggerRelay, m_triggerType, FIELD_INTEGER ), - DEFINE_FIELD( CTriggerRelay, m_sMaster, FIELD_STRING ), - DEFINE_FIELD( CTriggerRelay, m_iszAltTarget, FIELD_STRING ),//G-Cont. in garagedoordemo code without this stuff, demo don't work with save\load ;) -}; - -IMPLEMENT_SAVERESTORE(CTriggerRelay,CBaseDelay); - -void CTriggerRelay::KeyValue( KeyValueData *pkvd ) -{ - if (FStrEq(pkvd->szKeyName, "master")) - { - m_sMaster = ALLOC_STRING(pkvd->szValue); - pkvd->fHandled = TRUE; - } - else if (FStrEq(pkvd->szKeyName, "m_iszAltTarget")) - { - m_iszAltTarget = ALLOC_STRING(pkvd->szValue); - pkvd->fHandled = TRUE; - } - else if (FStrEq(pkvd->szKeyName, "triggerstate")) - { - int type = atoi( pkvd->szValue ); - switch( type ) - { - case 0: - m_triggerType = (USE_TYPE)-1; // will be changed in spawn - break; - case 2: - m_triggerType = USE_TOGGLE; - break; - case 4: - m_triggerType = USE_KILL; - break; - case 5: - m_triggerType = USE_SAME; - break; - case 7: - m_triggerType = USE_SET; - break; - default: - m_triggerType = USE_ON; - break; - } - pkvd->fHandled = TRUE; - } - else - CBaseDelay::KeyValue( pkvd ); -} - -void CTriggerRelay::Spawn( void ) -{ - if( m_triggerType == -1 ) m_triggerType = USE_OFF;// "triggerstate" is present and set to 'OFF' - else if( !m_triggerType ) m_triggerType = USE_ON; // "triggerstate" is missing - defaulting to 'ON' -} - -void CTriggerRelay::Use( CBaseEntity *pActivator, CBaseEntity *pCaller, USE_TYPE useType, float value ) -{ - if (!UTIL_IsMasterTriggered(m_sMaster,pActivator)) - { - if (m_iszAltTarget) - { - //FIXME: the alternate target should really use m_flDelay. - if (pev->spawnflags & SF_RELAY_USESAME) - FireTargets( STRING(m_iszAltTarget), pActivator, this, useType, 0 ); - else - FireTargets( STRING(m_iszAltTarget), pActivator, this, m_triggerType, 0 ); - if ( pev->spawnflags & SF_RELAY_FIREONCE ) - { - UTIL_Remove( this ); - } - } - return; - } - - if (FStringNull(pev->target) && !m_iszKillTarget) return; - - if ( pev->spawnflags & SF_FIRE_CAMERA )//For triggering once if player see in camera - { - if ( !pActivator || !pActivator->IsPlayer() ) - { - pActivator = CBaseEntity::Instance(g_engfuncs.pfnPEntityOfEntIndex( 1 )); - } - if ( pActivator && pActivator->IsPlayer() ) - { - if(((CBasePlayer *)pActivator)->viewFlags == 0) - { -// ALERT(at_console, "player is not currently see in camera\n"); - return; - } - - } - } - - if (pev->message) value = CalcLocus_Ratio(pActivator, STRING(pev->message)); - - if (m_triggerType == USE_SAME) - { - SUB_UseTargets( pActivator, useType, value ); - } - else if (m_triggerType == USE_SET) - { - SUB_UseTargets( pActivator, m_triggerType, value ); - } - else - { - SUB_UseTargets( pActivator, m_triggerType, 0 ); - } - - if ( pev->spawnflags & SF_RELAY_FIREONCE ) UTIL_Remove( this ); -} - -//=========================================== -//LRC - trigger_rottest, temporary new entity -//=========================================== -class CTriggerRotTest : public CBaseDelay -{ -public: - void PostSpawn( void ); -// void Use( CBaseEntity *pActivator, CBaseEntity *pCaller, USE_TYPE useType, float value ); - void Think( void ); - - virtual int Save( CSave &save ); - virtual int Restore( CRestore &restore ); - - static TYPEDESCRIPTION m_SaveData[]; - -private: - CBaseEntity* m_pMarker; - CBaseEntity* m_pReference; - CBaseEntity* m_pBridge; - CBaseEntity* m_pHinge; -}; -LINK_ENTITY_TO_CLASS( trigger_rottest, CTriggerRotTest ); - -TYPEDESCRIPTION CTriggerRotTest::m_SaveData[] = -{ - DEFINE_FIELD( CTriggerRotTest, m_pMarker, FIELD_CLASSPTR ), - DEFINE_FIELD( CTriggerRotTest, m_pReference, FIELD_CLASSPTR ), - DEFINE_FIELD( CTriggerRotTest, m_pBridge, FIELD_CLASSPTR ), - DEFINE_FIELD( CTriggerRotTest, m_pHinge, FIELD_CLASSPTR ), -}; - -IMPLEMENT_SAVERESTORE(CTriggerRotTest,CBaseDelay); - -void CTriggerRotTest::PostSpawn( void ) -{ - m_pMarker = UTIL_FindEntityByTargetname(NULL, STRING(pev->target)); - m_pReference = UTIL_FindEntityByTargetname(NULL, STRING(pev->netname)); - m_pBridge = UTIL_FindEntityByTargetname(NULL, STRING(pev->noise1)); - m_pHinge = UTIL_FindEntityByTargetname(NULL, STRING(pev->message)); - pev->armorvalue = 0; // initial angle - if (pev->armortype == 0) //angle offset - pev->armortype = 30; - SetNextThink( 1 ); -} - -void CTriggerRotTest::Think( void ) -{ -// ALERT(at_console, "Using angle = %.2f\n", pev->armorvalue); - if (m_pReference) - { - m_pReference->pev->origin = pev->origin; - m_pReference->pev->origin.x = m_pReference->pev->origin.x + pev->health; -// ALERT(at_console, "Set Reference = %.2f %.2f %.2f\n", m_pReference->pev->origin.x, m_pReference->pev->origin.y, m_pReference->pev->origin.z); - } - if (m_pMarker) - { - Vector vecTemp = UTIL_AxisRotationToVec( (m_pHinge->pev->origin - pev->origin).Normalize(), pev->armorvalue ); - m_pMarker->pev->origin = pev->origin + pev->health * vecTemp; - -// ALERT(at_console, "vecTemp = %.2f %.2f %.2f\n", vecTemp.x, vecTemp.y, vecTemp.z); -// ALERT(at_console, "Set Marker = %.2f %.2f %.2f\n", m_pMarker->pev->origin.x, m_pMarker->pev->origin.y, m_pMarker->pev->origin.z); - } - if (m_pBridge) - { - Vector vecTemp = UTIL_AxisRotationToAngles( (m_pHinge->pev->origin - pev->origin).Normalize(), pev->armorvalue ); - m_pBridge->pev->origin = pev->origin; - m_pBridge->pev->angles = vecTemp; - -// ALERT(at_console, "vecTemp = %.2f %.2f %.2f\n", vecTemp.x, vecTemp.y, vecTemp.z); -// ALERT(at_console, "Set Marker = %.2f %.2f %.2f\n", m_pMarker->pev->origin.x, m_pMarker->pev->origin.y, m_pMarker->pev->origin.z); - } - pev->armorvalue += pev->armortype * 0.1; - SetNextThink( 0.1 ); -} - -//********************************************************** -// The Multimanager Entity - when fired, will fire up to 16 targets -// at specified times. -// FLAG: THREAD (create clones when triggered) -// FLAG: CLONE (this is a clone for a threaded execution) - -#define SF_MULTIMAN_CLONE 0x80000000 -#define SF_MULTIMAN_SAMETRIG 0x40000000 -#define SF_MULTIMAN_TRIGCHOSEN 0x20000000 - -#define SF_MULTIMAN_THREAD 0x00000001 -#define SF_MULTIMAN_LOOP 0x00000004 -#define SF_MULTIMAN_ONLYONCE 0x00000008 -#define SF_MULTIMAN_SPAWNFIRE 0x00000010 -#define SF_MULTIMAN_DEBUG 0x00000020 - -#define MM_MODE_CHOOSE 1 -#define MM_MODE_PERCENT 2 -#define MM_MODE_SIMULTANEOUS 3 - -class CMultiManager : public CBaseEntity//Toggle -{ -public: - void KeyValue( KeyValueData *pkvd ); - void Spawn ( void ); - void EXPORT UseThink ( void ); - void EXPORT ManagerThink ( void ); - void EXPORT ManagerUse ( CBaseEntity *pActivator, CBaseEntity *pCaller, USE_TYPE useType, float value ); - -#if _DEBUG - void EXPORT ManagerReport( void ); -#endif - - BOOL HasTarget( string_t targetname ); - - int ObjectCaps( void ) { return CBaseEntity::ObjectCaps() & ~FCAP_ACROSS_TRANSITION; } - - virtual int Save( CSave &save ); - virtual int Restore( CRestore &restore ); - - static TYPEDESCRIPTION m_SaveData[]; - - STATE m_iState; - virtual STATE GetState( void ) { return m_iState; }; - - int m_cTargets; // the total number of targets in this manager's fire list. - int m_index; // Current target - float m_startTime;// Time we started firing - int m_iTargetName [ MAX_MULTI_TARGETS ];// list if indexes into global string array - float m_flTargetDelay [ MAX_MULTI_TARGETS ];// delay (in seconds) from time of manager fire to target fire - - float m_flWait; //LRC- minimum length of time to wait - float m_flMaxWait; //LRC- random, maximum length of time to wait - string_t m_sMaster; //LRC- master - int m_iMode; //LRC- 0 = timed, 1 = pick random, 2 = each random - int m_iszThreadName; //LRC - int m_iszLocusThread; //LRC - - EHANDLE m_hActivator; -private: - USE_TYPE m_triggerType; //LRC - inline BOOL IsClone( void ) { return (pev->spawnflags & SF_MULTIMAN_CLONE) ? TRUE : FALSE; } - inline BOOL ShouldClone( void ) - { - if ( IsClone() ) - return FALSE; - - return (pev->spawnflags & SF_MULTIMAN_THREAD) ? TRUE : FALSE; - } - - CMultiManager *Clone( void ); -}; -LINK_ENTITY_TO_CLASS( multi_manager, CMultiManager ); - -// Global Savedata for multi_manager -TYPEDESCRIPTION CMultiManager::m_SaveData[] = -{ - DEFINE_FIELD( CMultiManager, m_cTargets, FIELD_INTEGER ), - DEFINE_FIELD( CMultiManager, m_index, FIELD_INTEGER ), - DEFINE_FIELD( CMultiManager, m_iState, FIELD_INTEGER ), //LRC - DEFINE_FIELD( CMultiManager, m_iMode, FIELD_INTEGER ), //LRC - DEFINE_FIELD( CMultiManager, m_startTime, FIELD_TIME ), - DEFINE_FIELD( CMultiManager, m_triggerType, FIELD_INTEGER ), //LRC - DEFINE_ARRAY( CMultiManager, m_iTargetName, FIELD_STRING, MAX_MULTI_TARGETS ), - DEFINE_ARRAY( CMultiManager, m_flTargetDelay, FIELD_FLOAT, MAX_MULTI_TARGETS ), - DEFINE_FIELD( CMultiManager, m_sMaster, FIELD_STRING ), //LRC - DEFINE_FIELD( CMultiManager, m_hActivator, FIELD_EHANDLE ), - DEFINE_FIELD( CMultiManager, m_flWait, FIELD_FLOAT ), //LRC - DEFINE_FIELD( CMultiManager, m_flMaxWait, FIELD_FLOAT ), //LRC - DEFINE_FIELD( CMultiManager, m_iszThreadName, FIELD_STRING ), //LRC - DEFINE_FIELD( CMultiManager, m_iszLocusThread, FIELD_STRING ), //LRC -}; - -IMPLEMENT_SAVERESTORE(CMultiManager,CBaseEntity); - -void CMultiManager :: KeyValue( KeyValueData *pkvd ) -{ - - // UNDONE: Maybe this should do something like this: - //CBaseToggle::KeyValue( pkvd ); - // if ( !pkvd->fHandled ) - // ... etc. - // - //LRC- that would support Delay, Killtarget, Lip, Distance, Wait and Master. - // Wait is already supported. I've added master here. To hell with the others. - - if (FStrEq(pkvd->szKeyName, "wait")) - { - m_flWait = atof(pkvd->szValue); - pkvd->fHandled = TRUE; - } - else if (FStrEq(pkvd->szKeyName, "maxwait")) - { - m_flMaxWait = atof(pkvd->szValue); - pkvd->fHandled = TRUE; - } - else if (FStrEq(pkvd->szKeyName, "master")) //LRC - { - m_sMaster = ALLOC_STRING(pkvd->szValue); - pkvd->fHandled = TRUE; - } - else if (FStrEq(pkvd->szKeyName, "m_iszThreadName")) //LRC - { - m_iszThreadName = ALLOC_STRING(pkvd->szValue); - pkvd->fHandled = TRUE; - } - else if (FStrEq(pkvd->szKeyName, "m_iszLocusThread")) //LRC - { - m_iszLocusThread = ALLOC_STRING(pkvd->szValue); - pkvd->fHandled = TRUE; - } - else if (FStrEq(pkvd->szKeyName, "mode")) //LRC - { - m_iMode = atoi( pkvd->szValue ); - pkvd->fHandled = TRUE; - } - else if (FStrEq(pkvd->szKeyName, "triggerstate")) //LRC - { - switch( atoi( pkvd->szValue ) ) - { - case 4: pev->spawnflags |= SF_MULTIMAN_SAMETRIG; break; - case 1: m_triggerType = USE_ON; break; //LRC- yes, this algorithm is different - case 2: m_triggerType = USE_OFF; break; //from the trigger_relay equivalent- - case 3: m_triggerType = USE_KILL; break; //trigger_relay's got to stay backwards - default: m_triggerType = USE_TOGGLE; break; //compatible. - } - pev->spawnflags |= SF_MULTIMAN_TRIGCHOSEN; - pkvd->fHandled = TRUE; - } - else // add this field to the target list - { - // this assumes that additional fields are targetnames and their values are delay values. - if ( m_cTargets < MAX_MULTI_TARGETS ) - { - char tmp[128]; - - UTIL_StripToken( pkvd->szKeyName, tmp ); - m_iTargetName [ m_cTargets ] = ALLOC_STRING( tmp ); - m_flTargetDelay [ m_cTargets ] = atof (pkvd->szValue); - m_cTargets++; - pkvd->fHandled = TRUE; - } - else //LRC - keep a count of how many targets, for the error message - { - m_cTargets++; - } - } -} - - -void CMultiManager :: Spawn( void ) -{ - // LRC - if( m_cTargets > MAX_MULTI_TARGETS ) - { - ALERT(at_warning, "multi_manager \"%s\" has too many targets (limit is %d, it has %d)\n", STRING( pev->targetname ), MAX_MULTI_TARGETS, m_cTargets ); - m_cTargets = MAX_MULTI_TARGETS; - } - - // check for invalid multi_managers - for ( int i = 0; i < m_cTargets; i++ ) - { - if ( FStrEq( STRING( m_iTargetName[i] ), STRING( pev->targetname )) && m_flTargetDelay[i] == 0.0f ) - { - ALERT( at_error, "infinite loop multi_manager with name %s removed from map\n", STRING( pev->targetname )); - UTIL_Remove( this ); - return; - } - } - - pev->solid = SOLID_NOT; - SetUse(&CMultiManager :: ManagerUse ); - SetThink(&CMultiManager :: ManagerThink); - - m_iState = STATE_OFF; - - if (!FBitSet(pev->spawnflags,SF_MULTIMAN_TRIGCHOSEN)) - m_triggerType = USE_TOGGLE; - - // Sort targets - // Quick and dirty bubble sort - int swapped = 1; - - while ( swapped ) - { - swapped = 0; - for ( int i = 1; i < m_cTargets; i++ ) - { - if ( m_flTargetDelay[i] < m_flTargetDelay[i-1] ) - { - // Swap out of order elements - int name = m_iTargetName[i]; - float delay = m_flTargetDelay[i]; - m_iTargetName[i] = m_iTargetName[i-1]; - m_flTargetDelay[i] = m_flTargetDelay[i-1]; - m_iTargetName[i-1] = name; - m_flTargetDelay[i-1] = delay; - swapped = 1; - } - } - } - - if ( pev->spawnflags & SF_MULTIMAN_SPAWNFIRE) - { - SetThink(&CMultiManager :: UseThink ); - SetUse( NULL ); - UTIL_DesiredThink( this ); - } -} - - -BOOL CMultiManager::HasTarget( string_t targetname ) -{ - for ( int i = 0; i < m_cTargets; i++ ) - if ( FStrEq(STRING(targetname), STRING(m_iTargetName[i])) ) - return TRUE; - - return FALSE; -} - -void CMultiManager :: UseThink ( void ) -{ - SetThink(&CMultiManager :: ManagerThink ); - SetUse(&CMultiManager :: ManagerUse ); - Use( this, this, USE_TOGGLE, 0 ); -} - -// Designers were using this to fire targets that may or may not exist -- -// so I changed it to use the standard target fire code, made it a little simpler. -void CMultiManager :: ManagerThink ( void ) -{ - //LRC- different manager modes - if (m_iMode) - { - // special triggers have no time delay, so we can clean up before firing - if (pev->spawnflags & SF_MULTIMAN_LOOP) - { -// ALERT(at_console,"Manager loops back\n"); - // if it's a loop, start again! - if (m_flMaxWait) //LRC- random time to wait? - m_startTime = RANDOM_FLOAT( m_flWait, m_flMaxWait ); - else if (m_flWait) //LRC- constant time to wait? - m_startTime = m_flWait; - else //LRC- just start immediately. - m_startTime = 0; - if (pev->spawnflags & SF_MULTIMAN_DEBUG) - ALERT(at_debug, "DEBUG: multi_manager \"%s\": restarting loop.\n", STRING(pev->targetname)); - SetNextThink( m_startTime ); - m_startTime = m_fNextThink; - m_iState = STATE_TURN_ON; -// ALERT(at_console, "MM loops, nextthink %f\n", m_fNextThink); - } - else if ( IsClone() || pev->spawnflags & SF_MULTIMAN_ONLYONCE ) - { - if (pev->spawnflags & SF_MULTIMAN_DEBUG) - ALERT(at_debug, "DEBUG: multi_manager \"%s\": killed.\n", STRING(pev->targetname)); - SetThink(&CMultiManager :: SUB_Remove ); - SetNextThink( 0.1 ); - SetUse( NULL ); - } - else - { - if (pev->spawnflags & SF_MULTIMAN_DEBUG) - ALERT(at_debug, "DEBUG: multi_manager \"%s\": last burst.\n", STRING(pev->targetname)); - m_iState = STATE_OFF; - SetThink( NULL ); - SetUse(&CMultiManager :: ManagerUse );// allow manager re-use - } - - int i = 0; - if (m_iMode == MM_MODE_CHOOSE) // choose one of the members, and fire it - { - float total = 0; - for (i = 0; i < m_cTargets; i++) { total += m_flTargetDelay[i]; } - - // no weightings given, so just pick one. - if (total == 0) - { - const char *sTarg = STRING(m_iTargetName[RANDOM_LONG(0, m_cTargets-1)]); - if (pev->spawnflags & SF_MULTIMAN_DEBUG) - ALERT(at_debug, "DEBUG: multi_manager \"%s\": firing \"%s\" (random choice).\n", STRING(pev->targetname), sTarg); - FireTargets(sTarg,m_hActivator,this,m_triggerType,0); - } - else // pick one by weighting - { - float chosen = RANDOM_FLOAT(0,total); - float curpos = 0; - for (i = 0; i < m_cTargets; i++) - { - curpos += m_flTargetDelay[i]; - if (curpos >= chosen) - { - if (pev->spawnflags & SF_MULTIMAN_DEBUG) - ALERT(at_debug, "DEBUG: multi_manager \"%s\": firing \"%s\" (weighted random choice).\n", STRING(pev->targetname), STRING(m_iTargetName[i])); - FireTargets(STRING(m_iTargetName[i]),m_hActivator,this,m_triggerType,0); - break; - } - } - } - } - else if (m_iMode == MM_MODE_PERCENT) // try to call each member - { - for (i = 0; i < m_cTargets; i++) - { - if ( RANDOM_LONG( 0, 100 ) <= m_flTargetDelay[i] ) - { - if (pev->spawnflags & SF_MULTIMAN_DEBUG) - ALERT(at_debug, "DEBUG: multi_manager \"%s\": firing \"%s\" (%f%% chance).\n", STRING(pev->targetname), STRING(m_iTargetName[i]), m_flTargetDelay[i]); - FireTargets(STRING(m_iTargetName[i]),m_hActivator,this,m_triggerType,0); - } - } - } - else if (m_iMode == MM_MODE_SIMULTANEOUS) - { - for (i = 0; i < m_cTargets; i++) - { - if (pev->spawnflags & SF_MULTIMAN_DEBUG) - ALERT(at_debug, "DEBUG: multi_manager \"%s\": firing \"%s\" (simultaneous).\n", STRING(pev->targetname), STRING(m_iTargetName[i])); - FireTargets(STRING(m_iTargetName[i]),m_hActivator,this,m_triggerType,0); - } - } - - return; - } - -// ok, so m_iMode is 0; we're doing normal time-based stuff. - - float time; - int finalidx; - int index = m_index; // store the current index - - time = gpGlobals->time - m_startTime; - -// ALERT(at_console,"Manager think for time %f\n",time); - - // find the last index we're going to fire this time - finalidx = m_index; - while (finalidx < m_cTargets && m_flTargetDelay[ finalidx ] <= time) - finalidx++; - - if ( finalidx >= m_cTargets )// will we finish firing targets this time? - { - if (pev->spawnflags & SF_MULTIMAN_LOOP) - { -// ALERT(at_console,"Manager loops back\n"); - // if it's a loop, start again! - m_index = 0; - if (m_flMaxWait) //LRC- random time to wait? - { - m_startTime = RANDOM_FLOAT( m_flWait, m_flMaxWait ); - m_iState = STATE_TURN_ON; // while we're waiting, we're in state TURN_ON - } - else if (m_flWait) //LRC- constant time to wait? - { - m_startTime = m_flWait; - m_iState = STATE_TURN_ON; - } - else //LRC- just start immediately. - { - m_startTime = 0; - m_iState = STATE_ON; - } - if (pev->spawnflags & SF_MULTIMAN_DEBUG) - ALERT(at_debug, "DEBUG: multi_manager \"%s\": restarting loop.\n", STRING(pev->targetname)); - SetNextThink( m_startTime ); - m_startTime += gpGlobals->time; - } - else - { - m_iState = STATE_OFF; //LRC- STATE_OFF means "yes, we've finished". - if ( IsClone() || pev->spawnflags & SF_MULTIMAN_ONLYONCE ) - { - SetThink(&CMultiManager :: SUB_Remove ); - SetNextThink( 0.1 ); - SetUse( NULL ); - if (pev->spawnflags & SF_MULTIMAN_DEBUG) - ALERT(at_debug, "DEBUG: multi_manager \"%s\": killed.\n", STRING(pev->targetname)); - } - else - { - SetThink( NULL ); - SetUse(&CMultiManager :: ManagerUse );// allow manager re-use - if (pev->spawnflags & SF_MULTIMAN_DEBUG) - ALERT(at_debug, "DEBUG: multi_manager \"%s\": last burst.\n", STRING(pev->targetname)); - } - } - } - else - { - m_index = finalidx; - m_iState = STATE_ON; //LRC- while we're in STATE_ON we're firing targets, and haven't finished yet. - AbsoluteNextThink( m_startTime + m_flTargetDelay[ m_index ] ); - } - - while ( index < m_cTargets && m_flTargetDelay[ index ] <= time ) - { -// ALERT(at_console,"Manager sends %d to %s\n",m_triggerType,STRING(m_iTargetName[m_index])); - if (pev->spawnflags & SF_MULTIMAN_DEBUG) - ALERT(at_debug, "DEBUG: multi_manager \"%s\": firing \"%s\".\n", STRING(pev->targetname), STRING( m_iTargetName[ index ] )); - FireTargets( STRING( m_iTargetName[ index ] ), m_hActivator, this, m_triggerType, 0 ); - index++; - } -} - -CMultiManager *CMultiManager::Clone( void ) -{ - CMultiManager *pMulti = GetClassPtr( (CMultiManager *)NULL ); - - edict_t *pEdict = pMulti->pev->pContainingEntity; - memcpy( pMulti->pev, pev, sizeof(*pev) ); - pMulti->pev->pContainingEntity = pEdict; - - pMulti->pev->spawnflags |= SF_MULTIMAN_CLONE; - pMulti->m_cTargets = m_cTargets; - if (m_iszThreadName) pMulti->pev->targetname = m_iszThreadName; //LRC - pMulti->m_triggerType = m_triggerType; //LRC - pMulti->m_iMode = m_iMode; //LRC - pMulti->m_flWait = m_flWait; //LRC - pMulti->m_flMaxWait = m_flMaxWait; //LRC - memcpy( pMulti->m_iTargetName, m_iTargetName, sizeof( m_iTargetName ) ); - memcpy( pMulti->m_flTargetDelay, m_flTargetDelay, sizeof( m_flTargetDelay ) ); - - return pMulti; -} - - -// The USE function builds the time table and starts the entity thinking. -void CMultiManager :: ManagerUse ( CBaseEntity *pActivator, CBaseEntity *pCaller, USE_TYPE useType, float value ) -{ - if (pev->spawnflags & SF_MULTIMAN_LOOP) - { - if (m_iState != STATE_OFF) // if we're on, or turning on... - { - if (useType != USE_ON) // ...then turn it off if we're asked to. - { - if (pev->spawnflags & SF_MULTIMAN_DEBUG) - ALERT(at_debug, "DEBUG: multi_manager \"%s\": Loop halted on request.\n", STRING(pev->targetname)); - m_iState = STATE_OFF; - if ( IsClone() || pev->spawnflags & SF_MULTIMAN_ONLYONCE ) - { - SetThink(&CMultiManager :: SUB_Remove ); - SetNextThink( 0.1 ); - SetUse( NULL ); - if (pev->spawnflags & SF_MULTIMAN_DEBUG) - ALERT(at_debug, "DEBUG: multi_manager \"%s\": loop halted (removing).\n", STRING(pev->targetname)); - } - else - { - SetThink( NULL ); - if (pev->spawnflags & SF_MULTIMAN_DEBUG) - ALERT(at_debug, "DEBUG: multi_manager \"%s\": loop halted.\n", STRING(pev->targetname)); - } - } - // else we're already on and being told to turn on, so do nothing. - else if (pev->spawnflags & SF_MULTIMAN_DEBUG) - ALERT(at_debug, "DEBUG: multi_manager \"%s\": Loop already active.\n", STRING(pev->targetname)); - return; - } - else if (useType == USE_OFF) // it's already off - { - if (pev->spawnflags & SF_MULTIMAN_DEBUG) - ALERT(at_debug, "DEBUG: multi_manager \"%s\": Loop already inactive.\n", STRING(pev->targetname)); - return; - } - // otherwise, start firing targets as normal. - } -// ALERT(at_console,"Manager used, targetting ["); -// for (int i = 0; i < m_cTargets; i++) -// { -// ALERT(at_console," %s(%f)",STRING(m_iTargetName[i]),m_flTargetDelay[i]); -// } -// ALERT(at_console," ]\n"); - - //LRC- "master" support - if (!UTIL_IsMasterTriggered(m_sMaster,pActivator)) - { - if (pev->spawnflags & SF_MULTIMAN_DEBUG) - ALERT(at_debug, "DEBUG: multi_manager \"%s\": Can't trigger, locked by master \"%s\".\n", STRING(pev->targetname), STRING(m_sMaster)); - return; - } - - // In multiplayer games, clone the MM and execute in the clone (like a thread) - // to allow multiple players to trigger the same multimanager - if ( ShouldClone() ) - { - CMultiManager *pClone = Clone(); - if (pev->spawnflags & SF_MULTIMAN_DEBUG) - ALERT(at_debug, "DEBUG: multi_manager \"%s\": Creating clone.\n", STRING(pev->targetname)); - pClone->ManagerUse( pActivator, pCaller, useType, value ); - if (m_iszLocusThread) - FireTargets( STRING(m_iszLocusThread), pClone, this, USE_TOGGLE, 0 ); - return; - } - - m_hActivator = pActivator; - m_index = 0; - float timeOffset; - - if (m_flMaxWait) //LRC- random time to wait? - { - timeOffset = RANDOM_FLOAT( m_flWait, m_flMaxWait ); - m_iState = STATE_TURN_ON; // while we're waiting, we're in state TURN_ON - } - else if (m_flWait) //LRC- constant time to wait? - { - timeOffset = m_flWait; - m_iState = STATE_TURN_ON; - } - else //LRC- just start immediately. - { - timeOffset = 0; - m_iState = STATE_ON; - } - - m_startTime = timeOffset + gpGlobals->time; - - // startTime should not be affected by this next bit - if (m_cTargets > 0 && !m_iMode && m_flTargetDelay[0] < 0) - { - // negative wait on the first target? - timeOffset += m_flTargetDelay[0]; - } - - if (pev->spawnflags & SF_MULTIMAN_SAMETRIG) //LRC - m_triggerType = useType; - - if (pev->spawnflags & SF_MULTIMAN_LOOP) - SetUse(&CMultiManager :: ManagerUse ); // clones won't already have this set - else - SetUse( NULL );// disable use until all targets have fired - - if (timeOffset > 0) - { - if (pev->spawnflags & SF_MULTIMAN_DEBUG) - ALERT(at_debug, "DEBUG: multi_manager \"%s\": Begin in %f seconds.\n", STRING(pev->targetname), timeOffset); - SetThink(&CMultiManager :: ManagerThink ); - SetNextThink( timeOffset ); - } - else - { - SetThink(&CMultiManager :: ManagerThink ); - ManagerThink(); - } -} - -#if _DEBUG -void CMultiManager :: ManagerReport ( void ) -{ - int cIndex; - - for ( cIndex = 0 ; cIndex < m_cTargets ; cIndex++ ) - { - ALERT ( at_debug, "%s %f\n", STRING(m_iTargetName[cIndex]), m_flTargetDelay[cIndex] ); - } -} -#endif - -//*********************************************************** -//LRC- multi_watcher entity: useful? Well, I think it is. And I'm worth it. :) -//*********************************************************** - -#define SF_SWATCHER_SENDTOGGLE 0x1 -#define SF_SWATCHER_DONTSEND_ON 0x2 -#define SF_SWATCHER_DONTSEND_OFF 0x4 -#define SF_SWATCHER_NOTON 0x8 -#define SF_SWATCHER_OFF 0x10 -#define SF_SWATCHER_TURN_ON 0x20 -#define SF_SWATCHER_TURN_OFF 0x40 -#define SF_SWATCHER_IN_USE 0x80 -#define SF_SWATCHER_VALID 0x200 - -#define SWATCHER_LOGIC_AND 0 -#define SWATCHER_LOGIC_OR 1 -#define SWATCHER_LOGIC_NAND 2 -#define SWATCHER_LOGIC_NOR 3 -#define SWATCHER_LOGIC_XOR 4 -#define SWATCHER_LOGIC_XNOR 5 - -class CStateWatcher : public CBaseToggle -{ -public: - void Spawn ( void ); - void EXPORT Think ( void ); - void KeyValue( KeyValueData *pkvd ); - virtual STATE GetState( void ); - virtual STATE GetState( CBaseEntity *pActivator ); - virtual int ObjectCaps( void ) { return CBaseEntity :: ObjectCaps() & ~FCAP_ACROSS_TRANSITION; } - - virtual int Save( CSave &save ); - virtual int Restore( CRestore &restore ); - - static TYPEDESCRIPTION m_SaveData[]; - - int m_fLogic; // Logic by which to combine the targets - int m_cTargets; // the total number of targets in this manager's fire list. - int m_iTargetName [ MAX_MULTI_TARGETS ];// list of indexes into global string array -// CBaseEntity* m_pTargetEnt [ MAX_MULTI_TARGETS ]; - - BOOL EvalLogic ( CBaseEntity *pEntity ); -}; - -LINK_ENTITY_TO_CLASS( multi_watcher, CStateWatcher ); -LINK_ENTITY_TO_CLASS( watcher, CStateWatcher ); - -TYPEDESCRIPTION CStateWatcher::m_SaveData[] = -{ - DEFINE_FIELD( CStateWatcher, m_fLogic, FIELD_INTEGER ), - DEFINE_FIELD( CStateWatcher, m_cTargets, FIELD_INTEGER ), - DEFINE_ARRAY( CStateWatcher, m_iTargetName, FIELD_STRING, MAX_MULTI_TARGETS ), -// DEFINE_ARRAY( CStateWatcher, m_pTargetEnt, FIELD_CLASSPTR, MAX_MULTI_TARGETS ), -}; - -IMPLEMENT_SAVERESTORE(CStateWatcher,CBaseToggle); - -void CStateWatcher :: KeyValue( KeyValueData *pkvd ) -{ - char tmp[128]; - if (FStrEq(pkvd->szKeyName, "m_fLogic")) - { - m_fLogic = atof(pkvd->szValue); - pkvd->fHandled = TRUE; - } - else if (FStrEq(pkvd->szKeyName, "m_iszWatch")) - { - if ( m_cTargets < MAX_MULTI_TARGETS ) - { - m_iTargetName [ m_cTargets ] = ALLOC_STRING(pkvd->szValue); - m_cTargets++; - pkvd->fHandled = TRUE; - } - else - { - ALERT(at_debug,"%s: Too many targets for %s \"%s\" (limit is %d)\n",pkvd->szKeyName,STRING(pev->classname),STRING(pev->targetname), MAX_MULTI_TARGETS); - } - } - else // add this field to the target list - { - // this assumes that additional fields are targetnames and their values are delay values. - if ( m_cTargets < MAX_MULTI_TARGETS ) - { - UTIL_StripToken( pkvd->szKeyName, tmp ); - m_iTargetName [ m_cTargets ] = ALLOC_STRING( tmp ); - m_cTargets++; - pkvd->fHandled = TRUE; - } - else - { - ALERT(at_debug,"WARNING - %s: Too many targets for %s \"%s\" (limit is %d)\n",pkvd->szKeyName,STRING(pev->classname),STRING(pev->targetname), MAX_MULTI_TARGETS); - } - } -} - -void CStateWatcher :: Spawn ( void ) -{ - pev->solid = SOLID_NOT; - if (pev->target) - SetNextThink( 0.5 ); -} - -STATE CStateWatcher :: GetState( void ) -{ - if (EvalLogic( NULL )) - return STATE_ON; - else - return STATE_OFF; -} - -STATE CStateWatcher :: GetState( CBaseEntity *pActivator ) -{ -// if (pActivator) -// ALERT(at_console, "GetState( %s \"%s\" )\n", STRING(pActivator->pev->classname), STRING(pActivator->pev->targetname)); -// else -// ALERT(at_console, "GetState( NULL )\n"); - if (EvalLogic( pActivator )) - return STATE_ON; - else - return STATE_OFF; -} - -void CStateWatcher :: Think ( void ) -{ - SetNextThink( 0.1 ); - int oldflag = pev->spawnflags & SF_SWATCHER_VALID; - - if (EvalLogic( NULL )) - pev->spawnflags |= SF_SWATCHER_VALID; - else - pev->spawnflags &= ~SF_SWATCHER_VALID; - - if ((pev->spawnflags & SF_SWATCHER_VALID) != oldflag) - { - // the update changed my state... - - if (oldflag) - { - // ...to off. Send "off". -// ALERT(at_console,"%s turns off\n",STRING(pev->classname)); - if (!FBitSet(pev->spawnflags, SF_SWATCHER_DONTSEND_OFF)) - { - if (pev->spawnflags & SF_SWATCHER_SENDTOGGLE) - SUB_UseTargets(this, USE_TOGGLE, 0); - else - SUB_UseTargets(this, USE_OFF, 0); - } - } - else - { - // ...to on. Send "on". -// ALERT(at_console,"%s turns on\n",STRING(pev->classname)); - if (!FBitSet(pev->spawnflags, SF_SWATCHER_DONTSEND_ON)) - { - if (pev->spawnflags & SF_SWATCHER_SENDTOGGLE) - SUB_UseTargets(this, USE_TOGGLE, 0); - else - SUB_UseTargets(this, USE_ON, 0); - } - } - } -} - -BOOL CStateWatcher :: EvalLogic ( CBaseEntity *pActivator ) -{ - int i; - BOOL b; - BOOL xorgot = FALSE; - - CBaseEntity* pEntity; - - for (i = 0; i < m_cTargets; i++) - { -// if (m_pTargetEnt[i] == NULL) -// { -// pEntity = m_pTargetEnt[i]; -// } -// else -// { - pEntity = UTIL_FindEntityByTargetname(NULL,STRING(m_iTargetName[i]), pActivator); - if (pEntity != NULL) - { -// if ((STRING(m_iTargetName[i]))[0] != '*') // don't cache alias values -// { -// //ALERT(at_console,"Watcher: entity %s cached\n",STRING(m_iTargetName[i])); -// m_pTargetEnt[i] = pEntity; -// } - //else - //ALERT(at_console,"Watcher: aliased entity %s not cached\n",STRING(m_iTargetName[i])); - } - else - { - //ALERT(at_console,"Watcher: missing entity %s\n",STRING(m_iTargetName[i])); - continue; // couldn't find this entity; don't do the test. - } -// } - - b = FALSE; - switch (pEntity->GetState()) - { - case STATE_ON: if (!(pev->spawnflags & SF_SWATCHER_NOTON)) b = TRUE; break; - case STATE_OFF: if (pev->spawnflags & SF_SWATCHER_OFF) b = TRUE; break; - case STATE_TURN_ON: if (pev->spawnflags & SF_SWATCHER_TURN_ON) b = TRUE; break; - case STATE_TURN_OFF: if (pev->spawnflags & SF_SWATCHER_TURN_ON) b = TRUE; break; - case STATE_IN_USE: if (pev->spawnflags & SF_SWATCHER_IN_USE) b = TRUE; break; - } - // handle the states for this logic mode - if (b) - { - switch (m_fLogic) - { - case SWATCHER_LOGIC_OR: -// ALERT(at_console,"b is TRUE, OR returns true\n"); - return TRUE; - case SWATCHER_LOGIC_NOR: -// ALERT(at_console,"b is TRUE, NOR returns false\n"); - return FALSE; - case SWATCHER_LOGIC_XOR: -// ALERT(at_console,"b is TRUE, XOR\n"); - if (xorgot) return FALSE; - xorgot = TRUE; - break; - case SWATCHER_LOGIC_XNOR: -// ALERT(at_console,"b is TRUE, XNOR\n"); - if (xorgot) return TRUE; - xorgot = TRUE; - break; - } - } - else // b is false - { - switch (m_fLogic) - { - case SWATCHER_LOGIC_AND: -// ALERT(at_console,"b is FALSE, AND returns false\n"); - return FALSE; - case SWATCHER_LOGIC_NAND: -// ALERT(at_console,"b is FALSE, NAND returns true\n"); - return TRUE; - } - } - } -// handle the default cases for each logic mode - switch (m_fLogic) - { - case SWATCHER_LOGIC_AND: - case SWATCHER_LOGIC_NOR: -// ALERT(at_console,"final, AND/NOR returns true\n"); - return TRUE; - case SWATCHER_LOGIC_XOR: -// ALERT(at_console,"final, XOR\n"); - return xorgot; - case SWATCHER_LOGIC_XNOR: -// ALERT(at_console,"final, XNOR\n"); - return !xorgot; - default: // NAND, OR -// ALERT(at_console,"final, NAND/OR returns false\n"); - return FALSE; - } -} - -//*********************************************************** -#define SF_WRCOUNT_FIRESTART 0x0001 -#define SF_WRCOUNT_STARTED 0x8000 -class CWatcherCount : public CBaseToggle -{ -public: - void Spawn ( void ); - void EXPORT Think ( void ); - virtual STATE GetState( void ) { return (pev->spawnflags & SF_SWATCHER_VALID)?STATE_ON:STATE_OFF; }; - virtual int ObjectCaps( void ) { return CBaseEntity :: ObjectCaps() & ~FCAP_ACROSS_TRANSITION; } -}; - -LINK_ENTITY_TO_CLASS( watcher_count, CWatcherCount ); - -void CWatcherCount :: Spawn ( void ) -{ - pev->solid = SOLID_NOT; - SetNextThink( 0.5 ); -} - -void CWatcherCount :: Think ( void ) -{ - SetNextThink( 0.1 ); - int iCount = 0; - CBaseEntity *pCurrent = NULL; - - pCurrent = UTIL_FindEntityByTargetname( NULL, STRING(pev->noise) ); - while (pCurrent != NULL) - { - iCount++; - pCurrent = UTIL_FindEntityByTargetname( pCurrent, STRING(pev->noise) ); - } - - if (pev->spawnflags & SF_WRCOUNT_STARTED) - { - if (iCount > pev->frags) - { - if (iCount < pev->impulse && pev->frags >= pev->impulse) - FireTargets( STRING(pev->netname), this, this, USE_TOGGLE, 0 ); - FireTargets( STRING(pev->noise1), this, this, USE_TOGGLE, 0 ); - } - else if (iCount < pev->frags) - { - if (iCount >= pev->impulse && pev->frags < pev->impulse) - FireTargets( STRING(pev->message), this, this, USE_TOGGLE, 0 ); - FireTargets( STRING(pev->noise2), this, this, USE_TOGGLE, 0 ); - } - } - else - { - pev->spawnflags |= SF_WRCOUNT_STARTED; - if (pev->spawnflags & SF_WRCOUNT_FIRESTART) - { - if (iCount < pev->impulse) - FireTargets( STRING(pev->netname), this, this, USE_TOGGLE, 0 ); - else - FireTargets( STRING(pev->message), this, this, USE_TOGGLE, 0 ); - } - } - pev->frags = iCount; -} - -//*********************************************************** - -// -// Render parameters trigger -// -// This entity will copy its render parameters (renderfx, rendermode, rendercolor, renderamt) -// to its targets when triggered. -// - -// Flags to indicate masking off various render parameters that are normally copied to the targets -#define SF_RENDER_MASKFX (1<<0) -#define SF_RENDER_MASKAMT (1<<1) -#define SF_RENDER_MASKMODE (1<<2) -#define SF_RENDER_MASKCOLOR (1<<3) -//LRC -#define SF_RENDER_KILLTARGET (1<<5) -#define SF_RENDER_ONLYONCE (1<<6) - - -//LRC- RenderFxFader, a subsidiary entity for RenderFxManager -class CRenderFxFader : public CBaseEntity -{ -public: - void Spawn ( void ); - void EXPORT FadeThink ( void ); - virtual int Save( CSave &save ); - virtual int Restore( CRestore &restore ); - virtual int ObjectCaps( void ) { return CBaseEntity :: ObjectCaps() & ~FCAP_ACROSS_TRANSITION; } - - static TYPEDESCRIPTION m_SaveData[]; - - float m_flStartTime; - float m_flDuration; - float m_flCoarseness; - int m_iStartAmt; - int m_iOffsetAmt; - Vector m_vecStartColor; - Vector m_vecOffsetColor; - float m_fStartScale; - float m_fOffsetScale; - EHANDLE m_hTarget; - - int m_iszAmtFactor; -}; - -TYPEDESCRIPTION CRenderFxFader::m_SaveData[] = -{ - DEFINE_FIELD( CRenderFxFader, m_flStartTime, FIELD_FLOAT), - DEFINE_FIELD( CRenderFxFader, m_flDuration, FIELD_FLOAT), - DEFINE_FIELD( CRenderFxFader, m_flCoarseness, FIELD_FLOAT), - DEFINE_FIELD( CRenderFxFader, m_iStartAmt, FIELD_INTEGER), - DEFINE_FIELD( CRenderFxFader, m_iOffsetAmt, FIELD_INTEGER ), - DEFINE_FIELD( CRenderFxFader, m_vecStartColor, FIELD_VECTOR ), - DEFINE_FIELD( CRenderFxFader, m_vecOffsetColor, FIELD_VECTOR ), - DEFINE_FIELD( CRenderFxFader, m_fStartScale, FIELD_FLOAT), - DEFINE_FIELD( CRenderFxFader, m_fOffsetScale, FIELD_FLOAT ), - DEFINE_FIELD( CRenderFxFader, m_hTarget, FIELD_EHANDLE ), -}; - -IMPLEMENT_SAVERESTORE(CRenderFxFader,CBaseEntity); - -void CRenderFxFader :: Spawn( void ) -{ - SetThink(&CRenderFxFader :: FadeThink ); -} - -void CRenderFxFader :: FadeThink( void ) -{ - if (((CBaseEntity*)m_hTarget) == NULL) - { -// ALERT(at_console, "render_fader removed\n"); - SUB_Remove(); - return; - } - - float flDegree = (gpGlobals->time - m_flStartTime)/m_flDuration; - - if (flDegree >= 1) - { -// ALERT(at_console, "render_fader removes self\n"); - - m_hTarget->pev->renderamt = m_iStartAmt + m_iOffsetAmt; - m_hTarget->pev->rendercolor = m_vecStartColor + m_vecOffsetColor; - m_hTarget->pev->scale = m_fStartScale + m_fOffsetScale; - - SUB_UseTargets( m_hTarget, USE_TOGGLE, 0 ); - - if (pev->spawnflags & SF_RENDER_KILLTARGET) - { - m_hTarget->SetThink(&CRenderFxFader::SUB_Remove); - m_hTarget->SetNextThink(0.1); - } - - m_hTarget = NULL; - - SetNextThink( 0.1 ); - SetThink(&CRenderFxFader ::SUB_Remove); - } - else - { - m_hTarget->pev->renderamt = m_iStartAmt + m_iOffsetAmt * flDegree; - - m_hTarget->pev->rendercolor.x = m_vecStartColor.x + m_vecOffsetColor.x * flDegree; - m_hTarget->pev->rendercolor.y = m_vecStartColor.y + m_vecOffsetColor.y * flDegree; - m_hTarget->pev->rendercolor.z = m_vecStartColor.z + m_vecOffsetColor.z * flDegree; - - m_hTarget->pev->scale = m_fStartScale + m_fOffsetScale * flDegree; - - SetNextThink( m_flCoarseness ); //? - } -} - - -// RenderFxManager itself -class CRenderFxManager : public CPointEntity -{ -public: - void Use ( CBaseEntity *pActivator, CBaseEntity *pCaller, USE_TYPE useType, float value ); - void Affect( CBaseEntity *pEntity, BOOL bIsLocus, CBaseEntity *pActivator ); - - void KeyValue( KeyValueData *pkvd ); -}; - -LINK_ENTITY_TO_CLASS( env_render, CRenderFxManager ); - -void CRenderFxManager :: KeyValue( KeyValueData *pkvd ) -{ - if (FStrEq(pkvd->szKeyName, "m_fScale")) - { - pev->scale = atof(pkvd->szValue); - pkvd->fHandled = TRUE; - } - else - CPointEntity::KeyValue( pkvd ); -} - -void CRenderFxManager :: Use ( CBaseEntity *pActivator, CBaseEntity *pCaller, USE_TYPE useType, float value ) -{ - if (!FStringNull(pev->target)) - { - CBaseEntity* pTarget = UTIL_FindEntityByTargetname( NULL, STRING(pev->target), pActivator); - BOOL first = TRUE; - while ( pTarget != NULL ) - { - Affect( pTarget, first, pActivator ); - first = FALSE; - pTarget = UTIL_FindEntityByTargetname( pTarget, STRING(pev->target), pActivator ); - } - } - - if (pev->spawnflags & SF_RENDER_ONLYONCE) - { - SetThink(&CRenderFxManager ::SUB_Remove); - SetNextThink(0.1); - } -} - -void CRenderFxManager::Affect( CBaseEntity *pTarget, BOOL bIsFirst, CBaseEntity *pActivator ) -{ - entvars_t *pevTarget = pTarget->pev; - - float fAmtFactor = 1; - if ( pev->message && !FBitSet( pev->spawnflags, SF_RENDER_MASKAMT ) ) - fAmtFactor = CalcLocus_Ratio(pActivator, STRING(pev->message)); - - if ( !FBitSet( pev->spawnflags, SF_RENDER_MASKFX ) ) - pevTarget->renderfx = pev->renderfx; - if ( !FBitSet( pev->spawnflags, SF_RENDER_MASKMODE ) ) - { - //LRC - amt is often 0 when mode is normal. Set it to be fully visible, for fade purposes. - if (pev->frags && pevTarget->renderamt == 0 && pevTarget->rendermode == kRenderNormal) - pevTarget->renderamt = 255; - pevTarget->rendermode = pev->rendermode; - } - if (pev->frags == 0) // not fading? - { - if ( !FBitSet( pev->spawnflags, SF_RENDER_MASKAMT ) ) - pevTarget->renderamt = pev->renderamt * fAmtFactor; - if ( !FBitSet( pev->spawnflags, SF_RENDER_MASKCOLOR ) ) - pevTarget->rendercolor = pev->rendercolor; - if ( pev->scale ) - pevTarget->scale = pev->scale; - - if (bIsFirst) - FireTargets( STRING(pev->netname), pTarget, this, USE_TOGGLE, 0 ); - } - else - { - //LRC - fade the entity in/out! - // (We create seperate fader entities to do this, one for each entity that needs fading.) - CRenderFxFader *pFader = GetClassPtr( (CRenderFxFader *)NULL ); - pFader->m_hTarget = pTarget; - pFader->m_iStartAmt = pevTarget->renderamt; - pFader->m_vecStartColor = pevTarget->rendercolor; - pFader->m_fStartScale = pevTarget->scale; - if (pFader->m_fStartScale == 0) - pFader->m_fStartScale = 1; // When we're scaling, 0 is treated as 1. Use 1 as the number to fade from. - pFader->pev->spawnflags = pev->spawnflags; - - if (bIsFirst) - pFader->pev->target = pev->netname; - - if ( !FBitSet( pev->spawnflags, SF_RENDER_MASKAMT ) ) - pFader->m_iOffsetAmt = (pev->renderamt * fAmtFactor) - pevTarget->renderamt; - else - pFader->m_iOffsetAmt = 0; - - if ( !FBitSet( pev->spawnflags, SF_RENDER_MASKCOLOR ) ) - { - pFader->m_vecOffsetColor.x = pev->rendercolor.x - pevTarget->rendercolor.x; - pFader->m_vecOffsetColor.y = pev->rendercolor.y - pevTarget->rendercolor.y; - pFader->m_vecOffsetColor.z = pev->rendercolor.z - pevTarget->rendercolor.z; - } - else - { - pFader->m_vecOffsetColor = g_vecZero; - } - - if ( pev->scale ) - pFader->m_fOffsetScale = pev->scale - pevTarget->scale; - else - pFader->m_fOffsetScale = 0; - - pFader->m_flStartTime = gpGlobals->time; - pFader->m_flDuration = pev->frags; - pFader->m_flCoarseness = pev->armorvalue; - pFader->SetNextThink( 0 ); - pFader->Spawn(); - } -} - -//*********************************************************** -// -// EnvCustomize -// -// Changes various properties of an entity (some properties only apply to monsters.) -// - -#define SF_CUSTOM_AFFECTDEAD 1 -#define SF_CUSTOM_ONCE 2 -#define SF_CUSTOM_DEBUG 4 - -#define CUSTOM_FLAG_NOCHANGE 0 -#define CUSTOM_FLAG_ON 1 -#define CUSTOM_FLAG_OFF 2 -#define CUSTOM_FLAG_TOGGLE 3 -#define CUSTOM_FLAG_USETYPE 4 -#define CUSTOM_FLAG_INVUSETYPE 5 - -class CEnvCustomize : public CBaseEntity -{ -public: - void Spawn( void ); - void Precache( void ); - void PostSpawn( void ); - void DesiredAction( void ); - void Use ( CBaseEntity *pActivator, CBaseEntity *pCaller, USE_TYPE useType, float value ); - - void Affect (CBaseEntity *pTarget, USE_TYPE useType); - int GetActionFor( int iField, int iActive, USE_TYPE useType, char *szDebug ); - void SetBoneController (float fController, int cnum, CBaseEntity *pTarget); - - virtual int ObjectCaps( void ) { return CBaseEntity :: ObjectCaps() & ~FCAP_ACROSS_TRANSITION; } - - void KeyValue( KeyValueData *pkvd ); - virtual int Save( CSave &save ); - virtual int Restore( CRestore &restore ); - - static TYPEDESCRIPTION m_SaveData[]; - - float m_flRadius; - int m_iszModel; - int m_iClass; - int m_iPlayerReact; - int m_iPrisoner; - int m_iMonsterClip; - int m_iVisible; - int m_iSolid; - int m_iProvoked; - int m_voicePitch; - int m_iBloodColor; - float m_fFramerate; - float m_fController0; - float m_fController1; - float m_fController2; - float m_fController3; -}; - -LINK_ENTITY_TO_CLASS( env_customize, CEnvCustomize ); - -TYPEDESCRIPTION CEnvCustomize::m_SaveData[] = -{ - DEFINE_FIELD( CEnvCustomize, m_flRadius, FIELD_FLOAT), - DEFINE_FIELD( CEnvCustomize, m_iszModel, FIELD_STRING), - DEFINE_FIELD( CEnvCustomize, m_iClass, FIELD_INTEGER), - DEFINE_FIELD( CEnvCustomize, m_iPlayerReact, FIELD_INTEGER ), - DEFINE_FIELD( CEnvCustomize, m_iPrisoner, FIELD_INTEGER ), - DEFINE_FIELD( CEnvCustomize, m_iMonsterClip, FIELD_INTEGER ), - DEFINE_FIELD( CEnvCustomize, m_iVisible, FIELD_INTEGER ), - DEFINE_FIELD( CEnvCustomize, m_iSolid, FIELD_INTEGER ), - DEFINE_FIELD( CEnvCustomize, m_iProvoked, FIELD_INTEGER ), - DEFINE_FIELD( CEnvCustomize, m_voicePitch, FIELD_INTEGER ), - DEFINE_FIELD( CEnvCustomize, m_iBloodColor, FIELD_INTEGER ), - DEFINE_FIELD( CEnvCustomize, m_fFramerate, FIELD_FLOAT ), - DEFINE_FIELD( CEnvCustomize, m_fController0, FIELD_FLOAT ), - DEFINE_FIELD( CEnvCustomize, m_fController1, FIELD_FLOAT ), - DEFINE_FIELD( CEnvCustomize, m_fController2, FIELD_FLOAT ), - DEFINE_FIELD( CEnvCustomize, m_fController3, FIELD_FLOAT ), -}; - -IMPLEMENT_SAVERESTORE(CEnvCustomize,CBaseEntity); - -void CEnvCustomize :: KeyValue( KeyValueData *pkvd ) -{ - if (FStrEq(pkvd->szKeyName, "m_iVisible")) - { - m_iVisible = atoi(pkvd->szValue); - pkvd->fHandled = TRUE; - } - else if (FStrEq(pkvd->szKeyName, "m_iSolid")) - { - m_iSolid = atoi(pkvd->szValue); - pkvd->fHandled = TRUE; - } - else if (FStrEq(pkvd->szKeyName, "m_iszModel")) - { - m_iszModel = ALLOC_STRING(pkvd->szValue); - pkvd->fHandled = TRUE; - } - else if (FStrEq(pkvd->szKeyName, "m_voicePitch")) - { - m_voicePitch = atoi(pkvd->szValue); - pkvd->fHandled = TRUE; - } - else if (FStrEq(pkvd->szKeyName, "m_iPrisoner")) - { - m_iPrisoner = atoi(pkvd->szValue); - pkvd->fHandled = TRUE; - } - else if (FStrEq(pkvd->szKeyName, "m_iMonsterClip")) - { - m_iMonsterClip = atoi(pkvd->szValue); - pkvd->fHandled = TRUE; - } - else if (FStrEq(pkvd->szKeyName, "m_iClass")) - { - m_iClass = atoi(pkvd->szValue); - pkvd->fHandled = TRUE; - } - else if (FStrEq(pkvd->szKeyName, "m_iPlayerReact")) - { - m_iPlayerReact = atoi(pkvd->szValue); - pkvd->fHandled = TRUE; - } - else if (FStrEq(pkvd->szKeyName, "m_flRadius")) - { - m_flRadius = atof(pkvd->szValue); - pkvd->fHandled = TRUE; - } - else if (FStrEq(pkvd->szKeyName, "m_iProvoked")) - { - m_iProvoked = atoi(pkvd->szValue); - pkvd->fHandled = TRUE; - } - else if (FStrEq(pkvd->szKeyName, "m_bloodColor") || FStrEq(pkvd->szKeyName, "m_iBloodColor")) - { - m_iBloodColor = atoi(pkvd->szValue); - pkvd->fHandled = TRUE; - } - else if (FStrEq(pkvd->szKeyName, "m_fFramerate")) - { - m_fFramerate = atof(pkvd->szValue); - pkvd->fHandled = TRUE; - } - else if (FStrEq(pkvd->szKeyName, "m_fController0")) - { - m_fController0 = atof(pkvd->szValue); - pkvd->fHandled = TRUE; - } - else if (FStrEq(pkvd->szKeyName, "m_fController1")) - { - m_fController1 = atof(pkvd->szValue); - pkvd->fHandled = TRUE; - } - else if (FStrEq(pkvd->szKeyName, "m_fController2")) - { - m_fController2 = atof(pkvd->szValue); - pkvd->fHandled = TRUE; - } - else if (FStrEq(pkvd->szKeyName, "m_fController3")) - { - m_fController3 = atof(pkvd->szValue); - pkvd->fHandled = TRUE; - } - else - CBaseEntity::KeyValue( pkvd ); -} - -void CEnvCustomize :: Spawn ( void ) -{ - Precache(); -} - -void CEnvCustomize :: Precache( void ) -{ - if (m_iszModel) - PRECACHE_MODEL((char*)STRING(m_iszModel)); -} - -void CEnvCustomize :: PostSpawn( void ) -{ - if (!pev->targetname) - { - // no name - just take effect when everything's spawned. - UTIL_DesiredAction( this ); - } -} - -void CEnvCustomize :: DesiredAction ( void ) -{ - Use(this, this, USE_TOGGLE, 0); -} - -void CEnvCustomize :: Use ( CBaseEntity *pActivator, CBaseEntity *pCaller, USE_TYPE useType, float value ) -{ - if ( FStringNull(pev->target) ) - { - if ( pActivator ) - Affect(pActivator, useType); - else if (pev->spawnflags & SF_CUSTOM_DEBUG) - ALERT(at_debug, "DEBUG: env_customize \"%s\" was fired without a locus!\n", STRING(pev->targetname)); - } - else - { - BOOL fail = TRUE; - CBaseEntity *pTarget = UTIL_FindEntityByTargetname(NULL, STRING(pev->target), pActivator); - while (pTarget) - { - Affect(pTarget, useType); - fail = FALSE; - pTarget = UTIL_FindEntityByTargetname(pTarget, STRING(pev->target), pActivator); - } - pTarget = UTIL_FindEntityByClassname(NULL, STRING(pev->target)); - while (pTarget) - { - Affect(pTarget, useType); - fail = FALSE; - pTarget = UTIL_FindEntityByClassname(pTarget, STRING(pev->target)); - } - if (fail && pev->spawnflags & SF_CUSTOM_DEBUG) - ALERT(at_debug, "DEBUG: env_customize \"%s\" does nothing; can't find any entity with name or class \"%s\".\n", STRING(pev->target)); - } - - if (pev->spawnflags & SF_CUSTOM_ONCE) - { - if (pev->spawnflags & SF_CUSTOM_DEBUG) - ALERT(at_debug, "DEBUG: env_customize \"%s\" removes itself.\n", STRING(pev->targetname)); - UTIL_Remove(this); - } -} - -//LRCT -extern DLL_GLOBAL ULONG g_ulModelIndexPlayer; - -void CEnvCustomize :: Affect (CBaseEntity *pTarget, USE_TYPE useType) -{ - CBaseMonster* pMonster = pTarget->MyMonsterPointer(); - if (!FBitSet(pev->spawnflags, SF_CUSTOM_AFFECTDEAD) && pMonster && pMonster->m_MonsterState == MONSTERSTATE_DEAD) - { - if (pev->spawnflags & SF_CUSTOM_DEBUG) - ALERT(at_debug, "DEBUG: env_customize %s does nothing; can't apply to a corpse.\n", STRING(pev->targetname)); - return; - } - - if (pev->spawnflags & SF_CUSTOM_DEBUG) - ALERT(at_debug, "DEBUG: env_customize \"%s\" affects %s \"%s\": [", STRING(pev->targetname), STRING(pTarget->pev->classname), STRING(pTarget->pev->targetname)); - - if (m_iszModel) - { - Vector vecMins, vecMaxs; - vecMins = pTarget->pev->mins; - vecMaxs = pTarget->pev->maxs; - SET_MODEL(pTarget->edict(),STRING(m_iszModel)); -// if (pTarget->pev->flags & FL_CLIENT) -// g_ulModelIndexPlayer = pTarget->pev->modelindex; - UTIL_SetSize(pTarget->pev,vecMins,vecMaxs); - if (pev->spawnflags & SF_CUSTOM_DEBUG) - ALERT(at_debug, " model=%s", STRING(m_iszModel)); - } - SetBoneController( m_fController0, 0, pTarget ); - SetBoneController( m_fController1, 1, pTarget ); - SetBoneController( m_fController2, 2, pTarget ); - SetBoneController( m_fController3, 3, pTarget ); - if (m_fFramerate != -1) - { - //FIXME: check for env_model, stop it from changing its own framerate - pTarget->pev->framerate = m_fFramerate; - if (pev->spawnflags & SF_CUSTOM_DEBUG) - ALERT(at_debug, " framerate=%f", m_fFramerate); - } - if (pev->body != -1) - { - pTarget->pev->body = pev->body; - if (pev->spawnflags & SF_CUSTOM_DEBUG) - ALERT(at_debug, " body = %d", pev->body); - } - if (pev->skin != -1) - { - if (pev->skin == -2) - { - if (pTarget->pev->skin) - { - pTarget->pev->skin = 0; - if (pev->spawnflags & SF_CUSTOM_DEBUG) - ALERT(at_debug, " skin=0"); - } - else - { - pTarget->pev->skin = 1; - if (pev->spawnflags & SF_CUSTOM_DEBUG) - ALERT(at_debug, " skin=1"); - } - } - else if (pev->skin == -99) // special option to set CONTENTS_EMPTY - { - pTarget->pev->skin = -1; - if (pev->spawnflags & SF_CUSTOM_DEBUG) - ALERT(at_debug, " skin=-1"); - } - else - { - pTarget->pev->skin = pev->skin; - if (pev->spawnflags & SF_CUSTOM_DEBUG) - ALERT(at_debug, " skin=%d", pTarget->pev->skin); - } - } - - switch ( GetActionFor(m_iVisible, !(pTarget->pev->effects & EF_NODRAW), useType, "visible")) - { - case CUSTOM_FLAG_ON: pTarget->pev->effects &= ~EF_NODRAW; break; - case CUSTOM_FLAG_OFF: pTarget->pev->effects |= EF_NODRAW; break; - } - - switch ( GetActionFor(m_iSolid, pTarget->pev->solid != SOLID_NOT, useType, "solid")) - { - case CUSTOM_FLAG_ON: - if (*(STRING(pTarget->pev->model)) == '*') - pTarget->pev->solid = SOLID_BSP; - else - pTarget->pev->solid = SOLID_SLIDEBOX; - break; - case CUSTOM_FLAG_OFF: pTarget->pev->solid = SOLID_NOT; break; - } -/* if (m_iVisible != CUSTOM_FLAG_NOCHANGE) - { - if (pTarget->pev->effects & EF_NODRAW && (m_iVisible == CUSTOM_FLAG_TOGGLE || m_iVisible == CUSTOM_FLAG_ON)) - { - if (pev->spawnflags & SF_CUSTOM_DEBUG) - ALERT(at_debug, " visible=YES"); - } - else if (m_iVisible != CUSTOM_FLAG_ON) - { - pTarget->pev->effects |= EF_NODRAW; - if (pev->spawnflags & SF_CUSTOM_DEBUG) - ALERT(at_debug, " visible=NO"); - } - } - - if (m_iSolid != CUSTOM_FLAG_NOCHANGE) - { - if (pTarget->pev->solid == SOLID_NOT && (m_iSolid == CUSTOM_FLAG_TOGGLE || m_iSolid == CUSTOM_FLAG_ON)) - { - if (*(STRING(pTarget->pev->model)) == '*') - { - pTarget->pev->solid = SOLID_BSP; - if (pev->spawnflags & SF_CUSTOM_DEBUG) - ALERT(at_debug, " solid=YES(bsp)"); - } - else - { - pTarget->pev->solid = SOLID_SLIDEBOX; - if (pev->spawnflags & SF_CUSTOM_DEBUG) - ALERT(at_debug, " solid=YES(point)"); - } - } - else if (m_iSolid != CUSTOM_FLAG_ON) - { - pTarget->pev->solid = SOLID_NOT; - if (pev->spawnflags & SF_CUSTOM_DEBUG) - ALERT(at_debug, " solid=NO"); - } - else if (pev->spawnflags & SF_CUSTOM_DEBUG) - ALERT(at_debug, " solid=unchanged"); - } -*/ - if (!pMonster) - { - if (pev->spawnflags & SF_CUSTOM_DEBUG) - ALERT(at_debug, " ]\n"); - return; - } - - if (m_iBloodColor != 0) - { - pMonster->m_bloodColor = m_iBloodColor; - if (pev->spawnflags & SF_CUSTOM_DEBUG) - ALERT(at_debug, " bloodcolor=%d", m_iBloodColor); - } - if (m_voicePitch > 0) - { - if (FClassnameIs(pTarget->pev,"monster_barney") || FClassnameIs(pTarget->pev,"monster_scientist") || FClassnameIs(pTarget->pev,"monster_sitting_scientist")) - { - ((CTalkMonster*)pTarget)->m_voicePitch = m_voicePitch; - if (pev->spawnflags & SF_CUSTOM_DEBUG) - ALERT(at_debug, " voicePitch(talk)=%d", m_voicePitch); - } -// else if (FClassnameIs(pTarget->pev,"monster_human_grunt") || FClassnameIs(pTarget->pev,"monster_human_grunt_repel")) -// ((CHGrunt*)pTarget)->m_voicePitch = m_voicePitch; -// else if (FClassnameIs(pTarget->pev,"monster_alien_slave")) -// ((CISlave*)pTarget)->m_voicePitch = m_voicePitch; - else if (pev->spawnflags & SF_CUSTOM_DEBUG) - ALERT(at_debug, " voicePitch=unchanged"); - } - - if (m_iClass != 0) - { - pMonster->m_iClass = m_iClass; - if (pev->spawnflags & SF_CUSTOM_DEBUG) - ALERT(at_debug, " class=%d", m_iClass); - if (pMonster->m_hEnemy) - { - pMonster->m_hEnemy = NULL; - // make 'em stop attacking... might be better to use a different signal? - pMonster->SetConditions( bits_COND_NEW_ENEMY ); - } - } - if (m_iPlayerReact != -1) - { - pMonster->m_iPlayerReact = m_iPlayerReact; - if (pev->spawnflags & SF_CUSTOM_DEBUG) - ALERT(at_debug, " playerreact=%d", m_iPlayerReact); - } - -// SetCustomFlag( m_iPrisoner, pMonster->pev->spawnflags, SF_MONSTER_PRISONER, useType, "prisoner"); - switch (GetActionFor(m_iPrisoner, pMonster->pev->spawnflags & SF_MONSTER_PRISONER, useType, "prisoner") ) - { - case CUSTOM_FLAG_ON: - pMonster->pev->spawnflags |= SF_MONSTER_PRISONER; - if (pMonster->m_hEnemy) - { - pMonster->m_hEnemy = NULL; - // make 'em stop attacking... might be better to use a different signal? - pMonster->SetConditions( bits_COND_NEW_ENEMY ); - } - break; - case CUSTOM_FLAG_OFF: - pMonster->pev->spawnflags &= ~SF_MONSTER_PRISONER; - break; - } -/* if (m_iPrisoner != CUSTOM_FLAG_NOCHANGE) - { - if (pMonster->pev->spawnflags & SF_MONSTER_PRISONER && (m_iPrisoner == CUSTOM_FLAG_TOGGLE || m_iPrisoner == CUSTOM_FLAG_OFF)) - { - pMonster->pev->spawnflags &= ~SF_MONSTER_PRISONER; - if (pev->spawnflags & SF_CUSTOM_DEBUG) - ALERT(at_debug, " prisoner=NO"); - } - else if (m_iPrisoner != CUSTOM_FLAG_OFF) - { - pMonster->pev->spawnflags |= SF_MONSTER_PRISONER; - if (pev->spawnflags & SF_CUSTOM_DEBUG) - ALERT(at_debug, " prisoner=YES"); - if (pMonster->m_hEnemy) - { - pMonster->m_hEnemy = NULL; - // make 'em stop attacking... might be better to use a different signal? - pMonster->SetConditions( bits_COND_NEW_ENEMY ); - } - } - else if (pev->spawnflags & SF_CUSTOM_DEBUG) - ALERT(at_debug, " prisoner=unchanged"); - } -*/ - switch (GetActionFor(m_iMonsterClip, pMonster->pev->flags & FL_MONSTERCLIP, useType, "monsterclip") ) - { - case CUSTOM_FLAG_ON: pMonster->pev->flags |= FL_MONSTERCLIP; break; - case CUSTOM_FLAG_OFF: pMonster->pev->flags &= ~FL_MONSTERCLIP; break; - } -/* if (m_iMonsterClip != CUSTOM_FLAG_NOCHANGE) - { - if (pMonster->pev->flags & FL_MONSTERCLIP && (m_iMonsterClip == CUSTOM_FLAG_TOGGLE || m_iMonsterClip == CUSTOM_FLAG_OFF)) - { - pMonster->pev->flags &= ~FL_MONSTERCLIP; - if (pev->spawnflags & SF_CUSTOM_DEBUG) - ALERT(at_debug, " monsterclip=NO"); - } - else if (m_iMonsterClip != CUSTOM_FLAG_OFF) - { - pMonster->pev->flags |= FL_MONSTERCLIP; - if (pev->spawnflags & SF_CUSTOM_DEBUG) - ALERT(at_debug, " monsterclip=YES"); - } - else if (pev->spawnflags & SF_CUSTOM_DEBUG) - ALERT(at_debug, " monsterclip=unchanged"); - } -*/ - switch (GetActionFor(m_iProvoked, pMonster->m_afMemory & bits_MEMORY_PROVOKED, useType, "provoked") ) - { - case CUSTOM_FLAG_ON: pMonster->Remember(bits_MEMORY_PROVOKED); break; - case CUSTOM_FLAG_OFF: pMonster->Forget(bits_MEMORY_PROVOKED); break; - } -/* if (m_iProvoked != CUSTOM_FLAG_NOCHANGE) - { - if (pMonster->m_afMemory & bits_MEMORY_PROVOKED && (m_iProvoked == CUSTOM_FLAG_TOGGLE || m_iProvoked == CUSTOM_FLAG_OFF)) - { - pMonster->Forget(bits_MEMORY_PROVOKED); - if (pev->spawnflags & SF_CUSTOM_DEBUG) - ALERT(at_debug, " provoked=NO"); - } - else if (m_iProvoked != CUSTOM_FLAG_OFF) - { - pMonster->Remember(bits_MEMORY_PROVOKED); - if (pev->spawnflags & SF_CUSTOM_DEBUG) - ALERT(at_debug, " provoked=YES"); - } - else if (pev->spawnflags & SF_CUSTOM_DEBUG) - ALERT(at_debug, " provoked=unchanged"); - } -*/ - if (pev->spawnflags & SF_CUSTOM_DEBUG) - ALERT(at_debug, " ]\n"); -} - -int CEnvCustomize::GetActionFor( int iField, int iActive, USE_TYPE useType, char* szDebug) -{ - int iAction = iField; - - if (iAction == CUSTOM_FLAG_USETYPE) - { - if (useType == USE_ON) - iAction = CUSTOM_FLAG_ON; - else if (useType == USE_OFF) - iAction = CUSTOM_FLAG_OFF; - else - iAction = CUSTOM_FLAG_TOGGLE; - } - else if (iAction == CUSTOM_FLAG_INVUSETYPE) - { - if (useType == USE_ON) - iAction = CUSTOM_FLAG_OFF; - else if (useType == USE_OFF) - iAction = CUSTOM_FLAG_ON; - else - iAction = CUSTOM_FLAG_TOGGLE; - } - - if (iAction == CUSTOM_FLAG_TOGGLE) - { - if (iActive) - iAction = CUSTOM_FLAG_OFF; - else - iAction = CUSTOM_FLAG_ON; - } - - if (pev->spawnflags & SF_CUSTOM_DEBUG) - { - if (iAction == CUSTOM_FLAG_ON) - ALERT(at_debug, " %s=YES", szDebug); - else if (iAction == CUSTOM_FLAG_OFF) - ALERT(at_debug, " %s=NO", szDebug); - } - return iAction; -} - -void CEnvCustomize::SetBoneController( float fController, int cnum, CBaseEntity *pTarget) -{ - if (fController) //FIXME: the pTarget isn't necessarily a CBaseAnimating. - { - if (fController == 1024) - { - ((CBaseAnimating*)pTarget)->SetBoneController( cnum, 0 ); - if (pev->spawnflags & SF_CUSTOM_DEBUG) - ALERT(at_debug, " bone%d=0", cnum); - } - else - { - ((CBaseAnimating*)pTarget)->SetBoneController( cnum, fController ); - if (pev->spawnflags & SF_CUSTOM_DEBUG) - ALERT(at_debug, " bone%d=%f", cnum, fController); - } - } -} - -//===================================== -// trigger_x entities - -class CBaseTrigger : public CBaseToggle -{ -public: - //LRC - this was very bloated. I moved lots of methods into the - // subclasses where they belonged. - void InitTrigger( void ); - void EXPORT ToggleUse ( CBaseEntity *pActivator, CBaseEntity *pCaller, USE_TYPE useType, float value ); - BOOL CanTouch( entvars_t *pevToucher ); - - virtual int ObjectCaps( void ) { return CBaseToggle :: ObjectCaps() & ~FCAP_ACROSS_TRANSITION; } -}; - -LINK_ENTITY_TO_CLASS( trigger, CBaseTrigger ); - -BOOL CBaseTrigger :: CanTouch( entvars_t *pevToucher ) -{ - if ( !pev->netname ) - { - // Only touch clients, monsters, or pushables (depending on flags) - if (pevToucher->flags & FL_CLIENT) - return !(pev->spawnflags & SF_TRIGGER_NOCLIENTS); - else if (pevToucher->flags & FL_MONSTER) - return pev->spawnflags & SF_TRIGGER_ALLOWMONSTERS; - else if (FClassnameIs(pevToucher,"func_pushable")) - return pev->spawnflags & SF_TRIGGER_PUSHABLES; - else - return pev->spawnflags & SF_TRIGGER_EVERYTHING; - } - else - { - // If netname is set, it's an entity-specific trigger; we ignore the spawnflags. - if (!FClassnameIs(pevToucher, STRING(pev->netname)) && - (!pevToucher->targetname || !FStrEq(STRING(pevToucher->targetname), STRING(pev->netname)))) - return FALSE; - } - return TRUE; -} - -// -// ToggleUse - If this is the USE function for a trigger, its state will toggle every time it's fired -// -void CBaseTrigger :: ToggleUse ( CBaseEntity *pActivator, CBaseEntity *pCaller, USE_TYPE useType, float value ) -{ - if (pev->solid == SOLID_NOT) - {// if the trigger is off, turn it on - pev->solid = SOLID_TRIGGER; - - // Force retouch - gpGlobals->force_retouch++; - } - else - {// turn the trigger off - pev->solid = SOLID_NOT; - } - UTIL_SetOrigin( this, pev->origin ); -} - -/* -================ -InitTrigger -================ -*/ -void CBaseTrigger::InitTrigger( ) -{ - // trigger angles are used for one-way touches. An angle of 0 is assumed - // to mean no restrictions, so use a yaw of 360 instead. - if (pev->angles != g_vecZero) - SetMovedir(pev); - pev->solid = SOLID_TRIGGER; - pev->movetype = MOVETYPE_NONE; - SET_MODEL(ENT(pev), STRING(pev->model)); // set size and link into world - if ( CVAR_GET_FLOAT("showtriggers") == 0 ) - SetBits( pev->effects, EF_NODRAW ); -} - -//===================================== -// -// trigger_hurt - hurts anything that touches it. if the trigger has a targetname, firing it will toggle state -// -class CTriggerHurt : public CBaseTrigger -{ -public: - void Spawn( void ); - void EXPORT RadiationThink( void ); - void EXPORT HurtTouch ( CBaseEntity *pOther ); - virtual void KeyValue( KeyValueData *pkvd ); -}; - -LINK_ENTITY_TO_CLASS( trigger_hurt, CTriggerHurt ); - -void CTriggerHurt :: KeyValue( KeyValueData *pkvd ) -{ - if (FStrEq(pkvd->szKeyName, "damage")) - { - pev->dmg = atof(pkvd->szValue); - pkvd->fHandled = TRUE; - } - else if (FStrEq(pkvd->szKeyName, "damagetype")) - { - m_bitsDamageInflict |= atoi(pkvd->szValue); - pkvd->fHandled = TRUE; - } - else if (FStrEq(pkvd->szKeyName, "cangib")) - { - switch (atoi(pkvd->szValue)) - { - case 1: - m_bitsDamageInflict |= DMG_ALWAYSGIB; - case 2: - m_bitsDamageInflict |= DMG_NEVERGIB; - } - pkvd->fHandled = TRUE; - } - else - CBaseToggle::KeyValue( pkvd ); -} - -void CTriggerHurt :: Spawn( void ) -{ - InitTrigger(); - SetTouch(&CTriggerHurt :: HurtTouch ); - - if ( !FStringNull ( pev->targetname ) ) - { - SetUse(&CTriggerHurt :: ToggleUse ); - } - else - { - SetUse ( NULL ); - } - - if (m_bitsDamageInflict & DMG_RADIATION) - { - SetThink(&CTriggerHurt :: RadiationThink ); - SetNextThink( RANDOM_FLOAT(0.0, 0.5) ); - } - - if ( FBitSet (pev->spawnflags, SF_TRIGGER_HURT_START_OFF) )// if flagged to Start Turned Off, make trigger nonsolid. - pev->solid = SOLID_NOT; - - UTIL_SetOrigin( this, pev->origin ); // Link into the list -} - -// When touched, a hurt trigger does DMG points of damage each half-second -void CTriggerHurt :: HurtTouch ( CBaseEntity *pOther ) -{ - float fldmg; - - if ( !pOther->pev->takedamage ) - return; - - if ( (pev->spawnflags & SF_TRIGGER_HURT_CLIENTONLYTOUCH) && !pOther->IsPlayer() ) - { - // this trigger is only allowed to touch clients, and this ain't a client. - return; - } - - if ( (pev->spawnflags & SF_TRIGGER_HURT_NO_CLIENTS) && pOther->IsPlayer() ) - return; - - // HACKHACK -- In multiplayer, players touch this based on packet receipt. - // So the players who send packets later aren't always hurt. Keep track of - // how much time has passed and whether or not you've touched that player - if ( g_pGameRules->IsMultiplayer() ) - { - if ( pev->dmgtime > gpGlobals->time ) - { - if ( gpGlobals->time != pev->pain_finished ) - {// too early to hurt again, and not same frame with a different entity - if ( pOther->IsPlayer() ) - { - int playerMask = 1 << (pOther->entindex() - 1); - - // If I've already touched this player (this time), then bail out - if ( pev->impulse & playerMask ) - return; - - // Mark this player as touched - // BUGBUG - There can be only 32 players! - pev->impulse |= playerMask; - } - else - { - return; - } - } - } - else - { - // New clock, "un-touch" all players - pev->impulse = 0; - if ( pOther->IsPlayer() ) - { - int playerMask = 1 << (pOther->entindex() - 1); - - // Mark this player as touched - // BUGBUG - There can be only 32 players! - pev->impulse |= playerMask; - } - } - } - else // Original code -- single player - { - if ( pev->dmgtime > gpGlobals->time && gpGlobals->time != pev->pain_finished ) - {// too early to hurt again, and not same frame with a different entity - return; - } - } - - - - // If this is time_based damage (poison, radiation), override the pev->dmg with a - // default for the given damage type. Monsters only take time-based damage - // while touching the trigger. Player continues taking damage for a while after - // leaving the trigger - - fldmg = pev->dmg * 0.5; // 0.5 seconds worth of damage, pev->dmg is damage/second - - - // JAY: Cut this because it wasn't fully realized. Damage is simpler now. -#if 0 - switch (m_bitsDamageInflict) - { - default: break; - case DMG_POISON: fldmg = POISON_DAMAGE/4; break; - case DMG_NERVEGAS: fldmg = NERVEGAS_DAMAGE/4; break; - case DMG_RADIATION: fldmg = RADIATION_DAMAGE/4; break; - case DMG_PARALYZE: fldmg = PARALYZE_DAMAGE/4; break; // UNDONE: cut this? should slow movement to 50% - case DMG_ACID: fldmg = ACID_DAMAGE/4; break; - case DMG_SLOWBURN: fldmg = SLOWBURN_DAMAGE/4; break; - case DMG_SLOWFREEZE: fldmg = SLOWFREEZE_DAMAGE/4; break; - } -#endif - - if ( fldmg < 0 ) - pOther->TakeHealth( -fldmg, m_bitsDamageInflict ); - else - pOther->TakeDamage( pev, pev, fldmg, m_bitsDamageInflict ); - - // Store pain time so we can get all of the other entities on this frame - pev->pain_finished = gpGlobals->time; - - // Apply damage every half second - pev->dmgtime = gpGlobals->time + 0.5;// half second delay until this trigger can hurt toucher again - - - - if ( pev->target ) - { - // trigger has a target it wants to fire. - if ( pev->spawnflags & SF_TRIGGER_HURT_CLIENTONLYFIRE ) - { - // if the toucher isn't a client, don't fire the target! - if ( !pOther->IsPlayer() ) - { - return; - } - } - - SUB_UseTargets( pOther, USE_TOGGLE, 0 ); - if ( pev->spawnflags & SF_TRIGGER_HURT_TARGETONCE ) - pev->target = 0; - } -} - -// trigger hurt that causes radiation will do a radius -// check and set the player's geiger counter level -// according to distance from center of trigger - -void CTriggerHurt :: RadiationThink( void ) -{ - - edict_t *pentPlayer; - CBasePlayer *pPlayer = NULL; - float flRange; - entvars_t *pevTarget; - Vector vecSpot1; - Vector vecSpot2; - Vector vecRange; - Vector origin; - Vector view_ofs; - - // check to see if a player is in pvs - // if not, continue - - // set origin to center of trigger so that this check works - origin = pev->origin; - view_ofs = pev->view_ofs; - - pev->origin = (pev->absmin + pev->absmax) * 0.5; - pev->view_ofs = pev->view_ofs * 0.0; - - pentPlayer = FIND_CLIENT_IN_PVS(edict()); - - pev->origin = origin; - pev->view_ofs = view_ofs; - - // reset origin - - if (!FNullEnt(pentPlayer)) - { - - pPlayer = GetClassPtr( (CBasePlayer *)VARS(pentPlayer)); - - pevTarget = VARS(pentPlayer); - - // get range to player; - - vecSpot1 = (pev->absmin + pev->absmax) * 0.5; - vecSpot2 = (pevTarget->absmin + pevTarget->absmax) * 0.5; - - vecRange = vecSpot1 - vecSpot2; - flRange = vecRange.Length(); - - // if player's current geiger counter range is larger - // than range to this trigger hurt, reset player's - // geiger counter range - - if (pPlayer->m_flgeigerRange >= flRange) - pPlayer->m_flgeigerRange = flRange; - } - - SetNextThink( 0.25 ); -} - -//===================================== -// -// trigger_hevcharge -// charges/discharges the player's suit. if the trigger has a targetname, firing it will toggle state -//LRC -#define SF_HEVCHARGE_NOANNOUNCE 0x04 - -class CTriggerHevCharge : public CBaseTrigger -{ -public: - void Spawn( void ); - void EXPORT ChargeTouch ( CBaseEntity *pOther ); - void EXPORT AnnounceThink( void ); -}; - -LINK_ENTITY_TO_CLASS( trigger_hevcharge, CTriggerHevCharge ); - -void CTriggerHevCharge :: Spawn( void ) -{ - InitTrigger(); - SetTouch(&CTriggerHevCharge :: ChargeTouch ); - SetThink(&CTriggerHevCharge :: AnnounceThink ); - - if ( !FStringNull ( pev->targetname ) ) - { - SetUse(&CTriggerHevCharge :: ToggleUse ); - } - else - { - SetUse ( NULL ); - } - - if ( FBitSet (pev->spawnflags, SF_TRIGGER_HURT_START_OFF) )// if flagged to Start Turned Off, make trigger nonsolid. - pev->solid = SOLID_NOT; - - UTIL_SetOrigin( this, pev->origin ); // Link into the list -} - -void CTriggerHevCharge :: ChargeTouch ( CBaseEntity *pOther ) -{ - if (IsLockedByMaster()) - return; - - // check that it's a player with an HEV suit - if ( !pOther->IsPlayer() || !FBitSet(pOther->pev->weapons, ITEM_SUIT) ) - return; - - //FIXME: add in the multiplayer fix, from trigger_hurt? - if ( pev->dmgtime > gpGlobals->time ) - return; - pev->dmgtime = gpGlobals->time + 0.5;// half second delay until this trigger can hurt toucher again - - int iNewArmor = pOther->pev->armorvalue + pev->frags; - if (iNewArmor > MAX_NORMAL_BATTERY) iNewArmor = MAX_NORMAL_BATTERY; - if (iNewArmor < 0) - iNewArmor = 0; - if (iNewArmor == pOther->pev->armorvalue) // if no change, we've finished. - return; - - pOther->pev->armorvalue = iNewArmor; - - //FIXME: support the 'target once' flag - if ( pev->target ) - { - SUB_UseTargets( pOther, USE_TOGGLE, 0 ); - } - - // The suit doesn't say much in multiplayer. Which is convenient, since it lets me be lazy here. - if ( g_pGameRules->IsMultiplayer() || pev->spawnflags & SF_HEVCHARGE_NOANNOUNCE) - return; - - // as long as the suit is charging, this think will never actually happen. - //ALERT(at_debug, "%s ", STRING(pev->targetname)); - pev->aiment = ENT(pOther->pev); - SetNextThink( 1 ); -} - -void CTriggerHevCharge :: AnnounceThink ( ) -{ - CBasePlayer *pPlayer = (CBasePlayer*)(CBaseEntity::Instance(pev->aiment)); - - if (!pPlayer || !pPlayer->IsPlayer()) - { - ALERT(at_error, "trigger_hevcharge: invalid player in Announce!\n"); - return; - } - //copied from item_battery... - - int pct; - char szcharge[64]; - - // Suit reports new power level - // For some reason this wasn't working in release build -- round it. - pct = (int)( (float)(pPlayer->pev->armorvalue * 100.0) * (1.0/MAX_NORMAL_BATTERY) + 0.5); - pct = (pct / 5); - if (pct > 0) - pct--; - - sprintf( szcharge,"!HEV_%1dP", pct ); - //ALERT(at_debug, "Announce %s\n", szcharge); - - ((CBasePlayer*)pPlayer)->SetSuitUpdate(szcharge, FALSE, SUIT_REPEAT_OK); -} - - -//===================================== -// -// trigger_monsterjump -// -class CTriggerMonsterJump : public CBaseTrigger -{ -public: - void Spawn( void ); - void Touch( CBaseEntity *pOther ); - void Think( void ); -}; - -LINK_ENTITY_TO_CLASS( trigger_monsterjump, CTriggerMonsterJump ); - - -void CTriggerMonsterJump :: Spawn ( void ) -{ - SetMovedir ( pev ); - - InitTrigger (); - - DontThink(); - pev->speed = 200; - m_flHeight = 150; - - if ( !FStringNull ( pev->targetname ) ) - {// if targetted, spawn turned off - pev->solid = SOLID_NOT; - UTIL_SetOrigin( this, pev->origin ); // Unlink from trigger list - SetUse(&CTriggerMonsterJump :: ToggleUse ); - } -} - - -void CTriggerMonsterJump :: Think( void ) -{ - pev->solid = SOLID_NOT;// kill the trigger for now !!!UNDONE - UTIL_SetOrigin( this, pev->origin ); // Unlink from trigger list - SetThink( NULL ); -} - -void CTriggerMonsterJump :: Touch( CBaseEntity *pOther ) -{ - entvars_t *pevOther = pOther->pev; - - if ( !FBitSet ( pevOther->flags , FL_MONSTER ) ) - {// touched by a non-monster. - return; - } - - pevOther->origin.z += 1; - - if ( FBitSet ( pevOther->flags, FL_ONGROUND ) ) - {// clear the onground so physics don't bitch - pevOther->flags &= ~FL_ONGROUND; - } - - // toss the monster! - pevOther->velocity = pev->movedir * pev->speed; - pevOther->velocity.z += m_flHeight; - SetNextThink( 0 ); -} - -// mp3 player, killar -#define SF_REMOVE_ON_FIRE 1 - -class CTargetFMODAudio : public CPointEntity -{ -public: - void Spawn( void ); - void Activate( void ); - void Use( CBaseEntity *pActivator, CBaseEntity *pCaller, USE_TYPE useType, float value ); - BOOL m_bPlaying; - virtual int Save( CSave &save ); - virtual int Restore( CRestore &restore ); - static TYPEDESCRIPTION m_SaveData[]; -}; - -LINK_ENTITY_TO_CLASS( ambient_music, CTargetFMODAudio ); -LINK_ENTITY_TO_CLASS( ambient_fmodstream, CTargetFMODAudio ); -LINK_ENTITY_TO_CLASS( trigger_mp3audio, CTargetFMODAudio ); - -TYPEDESCRIPTION CTargetFMODAudio::m_SaveData[] = -{ - DEFINE_FIELD( CTargetFMODAudio, m_bPlaying, FIELD_BOOLEAN ), -}; -IMPLEMENT_SAVERESTORE( CTargetFMODAudio, CPointEntity ); - -void CTargetFMODAudio :: Spawn( void ) -{ - pev->solid = SOLID_NOT; - pev->movetype = MOVETYPE_NONE; - - m_bPlaying = FALSE; // start out not playing -} - -void CTargetFMODAudio :: Activate( void ) -{ - if( FClassnameIs( pev, "ambient_music" ) && FBitSet( pev->spawnflags, SF_REMOVE_ON_FIRE )) - { - pev->spawnflags &= ~SF_REMOVE_ON_FIRE; - this->Use( UTIL_PlayerByIndex( 1 ), this, USE_ON, 0 ); - } -} - -void CTargetFMODAudio::Use( CBaseEntity *pActivator, CBaseEntity *pCaller, USE_TYPE useType, float value ) -{ - char command[128]; - - if( !pActivator->IsPlayer( )) // activator should be a player - return; - - if ( !m_bPlaying ) - { - // if we're not playing, start playing! - m_bPlaying = TRUE; - } - else - { - // if we're already playing, stop the mp3 - m_bPlaying = FALSE; - CLIENT_COMMAND( pActivator->edict(), "stopaudio\n" ); - return; - } - - // issue the play/loop command - sprintf( command, "playaudio %s\n", STRING( pev->message )); - - CLIENT_COMMAND( pActivator->edict(), command ); - - // remove if set - if ( FBitSet( pev->spawnflags, SF_REMOVE_ON_FIRE )) - UTIL_Remove( this ); -} - -//===================================== -// -// trigger_cdaudio - starts/stops cd audio tracks -// -class CTriggerCDAudio : public CBaseTrigger -{ -public: - void Spawn( void ); - - virtual void Use( CBaseEntity *pActivator, CBaseEntity *pCaller, USE_TYPE useType, float value ); - void PlayTrack( void ); - void Touch ( CBaseEntity *pOther ); -}; - -LINK_ENTITY_TO_CLASS( trigger_cdaudio, CTriggerCDAudio ); - -// -// Changes tracks or stops CD when player touches -// -// !!!HACK - overloaded HEALTH to avoid adding new field -void CTriggerCDAudio :: Touch ( CBaseEntity *pOther ) -{ - if ( !pOther->IsPlayer() ) - {// only clients may trigger these events - return; - } - - PlayTrack(); -} - -void CTriggerCDAudio :: Spawn( void ) -{ - InitTrigger(); -} - -void CTriggerCDAudio::Use( CBaseEntity *pActivator, CBaseEntity *pCaller, USE_TYPE useType, float value ) -{ - PlayTrack(); -} - -void PlayCDTrack( int iTrack ) -{ - edict_t *pClient; - - // manually find the single player. - pClient = g_engfuncs.pfnPEntityOfEntIndex( 1 ); - - // Can't play if the client is not connected! - if ( !pClient ) - return; - - if ( iTrack < -1 || iTrack > 30 ) - { - ALERT ( at_debug, "TriggerCDAudio - Track %d out of range\n" ); - return; - } - - if ( iTrack == -1 ) - { - CLIENT_COMMAND ( pClient, "cd pause\n"); - } - else - { - char string [ 64 ]; - - sprintf( string, "cd play %3d\n", iTrack ); - CLIENT_COMMAND ( pClient, string); - } -} - - -// only plays for ONE client, so only use in single play! -void CTriggerCDAudio :: PlayTrack( void ) -{ - PlayCDTrack( (int)pev->health ); - - SetTouch( NULL ); - UTIL_Remove( this ); -} - - -// This plays a CD track when fired or when the player enters it's radius -class CTargetCDAudio : public CPointEntity -{ -public: - void Spawn( void ); - void KeyValue( KeyValueData *pkvd ); - - virtual void Use( CBaseEntity *pActivator, CBaseEntity *pCaller, USE_TYPE useType, float value ); - void Think( void ); - void Play( void ); -}; - -LINK_ENTITY_TO_CLASS( target_cdaudio, CTargetCDAudio ); - -void CTargetCDAudio :: KeyValue( KeyValueData *pkvd ) -{ - if (FStrEq(pkvd->szKeyName, "radius")) - { - pev->scale = atof(pkvd->szValue); - pkvd->fHandled = TRUE; - } - else - CPointEntity::KeyValue( pkvd ); -} - -void CTargetCDAudio :: Spawn( void ) -{ - pev->solid = SOLID_NOT; - pev->movetype = MOVETYPE_NONE; - - if ( pev->scale > 0 ) - SetNextThink( 1.0 ); -} - -void CTargetCDAudio::Use( CBaseEntity *pActivator, CBaseEntity *pCaller, USE_TYPE useType, float value ) -{ - Play(); -} - -// only plays for ONE client, so only use in single play! -void CTargetCDAudio::Think( void ) -{ - edict_t *pClient; - - // manually find the single player. - pClient = g_engfuncs.pfnPEntityOfEntIndex( 1 ); - - // Can't play if the client is not connected! - if ( !pClient ) - return; - - SetNextThink( 0.5 ); - - if ( (pClient->v.origin - pev->origin).Length() <= pev->scale ) - Play(); - -} - -void CTargetCDAudio::Play( void ) -{ - PlayCDTrack( (int)pev->health ); - UTIL_Remove(this); -} - - -//===================================== -//trigger_multiple - -class CTriggerMultiple : public CBaseTrigger -{ -public: - void Spawn( void ); - void Precache( void ) - { - if (!FStringNull(pev->noise)) - PRECACHE_SOUND((char*)STRING(pev->noise)); - } - void EXPORT MultiTouch( CBaseEntity *pOther ); - void EXPORT MultiWaitOver( void ); - void ActivateMultiTrigger( CBaseEntity *pActivator ); -}; - -LINK_ENTITY_TO_CLASS( trigger_multiple, CTriggerMultiple ); - - -void CTriggerMultiple :: Spawn( void ) -{ - if (m_flWait == 0) - m_flWait = 0.2; - - InitTrigger(); - - ASSERTSZ(pev->health == 0, "trigger_multiple with health"); - SetTouch(&CTriggerMultiple :: MultiTouch ); - Precache(); -} - -void CTriggerMultiple :: MultiTouch( CBaseEntity *pOther ) -{ - entvars_t *pevToucher; - - pevToucher = pOther->pev; - - if (!CanTouch(pevToucher)) return; - -#if 0 - // if the trigger has an angles field, check player's facing direction - if (pev->movedir != g_vecZero) - { - UTIL_MakeVectors( pevToucher->angles ); - if ( DotProduct( gpGlobals->v_forward, pev->movedir ) < 0 ) - return; // not facing the right way - } -#endif - - ActivateMultiTrigger( pOther ); -} - - -// -// the trigger was just touched/killed/used -// m_hActivator gets set to the activator so it can be held through a delay -// so wait for the delay time before firing -// -void CTriggerMultiple :: ActivateMultiTrigger( CBaseEntity *pActivator ) -{ - if (m_fNextThink > gpGlobals->time) - return; // still waiting for reset time - - if (!UTIL_IsMasterTriggered(m_sMaster,pActivator)) - return; - - if (FClassnameIs(pev, "trigger_secret")) - { - if ( pev->enemy == NULL || !FClassnameIs(pev->enemy, "player")) - return; - gpGlobals->found_secrets++; - } - - if (!FStringNull(pev->noise)) - EMIT_SOUND(ENT(pev), CHAN_VOICE, (char*)STRING(pev->noise), 1, ATTN_NORM); - -// don't trigger again until reset -// pev->takedamage = DAMAGE_NO; - - m_hActivator = pActivator; - SUB_UseTargets( m_hActivator, USE_TOGGLE, 0 ); - - if ( pev->message && pActivator->IsPlayer() ) - { - UTIL_ShowMessage( STRING(pev->message), pActivator ); -// CLIENT_PRINTF( ENT( pActivator->pev ), print_center, STRING(pev->message) ); - } - - if (m_flWait > 0) - { - SetThink(&CTriggerMultiple :: MultiWaitOver ); - SetNextThink( m_flWait ); - } - else - { - // we can't just remove (self) here, because this is a touch function - // called while C code is looping through area links... - SetTouch( NULL ); - SetNextThink( 0.1 ); - SetThink(&CTriggerMultiple :: SUB_Remove ); - } -} - -// the wait time has passed, so set back up for another activation -void CTriggerMultiple :: MultiWaitOver( void ) -{ -// if (pev->max_health) -// { -// pev->health = pev->max_health; -// pev->takedamage = DAMAGE_YES; -// pev->solid = SOLID_BBOX; -// } - SetThink( NULL ); -} - -//===================================== -//trigger_once - -class CTriggerOnce : public CTriggerMultiple -{ -public: - void Spawn( void ); -}; - -LINK_ENTITY_TO_CLASS( trigger_once, CTriggerOnce ); -void CTriggerOnce::Spawn( void ) -{ - m_flWait = -1; - - CTriggerMultiple :: Spawn(); -} - - -//=========================================================== -//LRC - trigger_inout, a trigger which fires _only_ when -// the player enters or leaves it. -// If there's more than one entity it can trigger off, then -// it will trigger for each one that enters and leaves. -//=========================================================== -class CTriggerInOut; - -class CInOutRegister : public CPointEntity -{ -public: - // returns true if found in the list - BOOL IsRegistered ( CBaseEntity *pValue ); - // remove all invalid entries from the list, trigger their targets as appropriate - // returns the new list - CInOutRegister *Prune( void ); - // adds a new entry to the list - CInOutRegister *Add( CBaseEntity *pValue ); - BOOL IsEmpty( void ) { return m_pNext?FALSE:TRUE; }; - - virtual int Save( CSave &save ); - virtual int Restore( CRestore &restore ); - static TYPEDESCRIPTION m_SaveData[]; - - CTriggerInOut *m_pField; - CInOutRegister *m_pNext; - EHANDLE m_hValue; -}; - -class CTriggerInOut : public CBaseTrigger -{ -public: - void Spawn( void ); - void EXPORT Touch( CBaseEntity *pOther ); - void EXPORT Think( void ); - void FireOnEntry( CBaseEntity *pOther ); - void FireOnLeaving( CBaseEntity *pOther ); - - void KeyValue( KeyValueData *pkvd ); - virtual int Save( CSave &save ); - virtual int Restore( CRestore &restore ); - static TYPEDESCRIPTION m_SaveData[]; - - STATE GetState() { return m_pRegister->IsEmpty()?STATE_OFF:STATE_ON; } - - string_t m_iszAltTarget; - string_t m_iszBothTarget; - CInOutRegister *m_pRegister; -}; - - -// CInOutRegister method bodies: - -TYPEDESCRIPTION CInOutRegister::m_SaveData[] = -{ - DEFINE_FIELD( CInOutRegister, m_pField, FIELD_CLASSPTR ), - DEFINE_FIELD( CInOutRegister, m_pNext, FIELD_CLASSPTR ), - DEFINE_FIELD( CInOutRegister, m_hValue, FIELD_EHANDLE ), -}; - -IMPLEMENT_SAVERESTORE(CInOutRegister,CPointEntity); -LINK_ENTITY_TO_CLASS( inout_register, CInOutRegister ); - -BOOL CInOutRegister::IsRegistered ( CBaseEntity *pValue ) -{ - if (m_hValue == pValue) - return TRUE; - else if (m_pNext) - return m_pNext->IsRegistered( pValue ); - else - return FALSE; -} - -CInOutRegister *CInOutRegister::Add( CBaseEntity *pValue ) -{ - if (m_hValue == pValue) - { - // it's already in the list, don't need to do anything - return this; - } - else if (m_pNext) - { - // keep looking - m_pNext = m_pNext->Add( pValue ); - return this; - } - else - { - // reached the end of the list; add the new entry, and trigger - CInOutRegister *pResult = GetClassPtr( (CInOutRegister*)NULL ); - pResult->m_hValue = pValue; - pResult->m_pNext = this; - pResult->m_pField = m_pField; - pResult->pev->classname = MAKE_STRING("inout_register"); - -// ALERT(at_console, "adding; max %.2f %.2f %.2f, min %.2f %.2f %.2f is inside max %.2f %.2f %.2f, min %.2f %.2f %.2f\n", pResult->m_hValue->pev->absmax.x, pResult->m_hValue->pev->absmax.y, pResult->m_hValue->pev->absmax.z, pResult->m_hValue->pev->absmin.x, pResult->m_hValue->pev->absmin.y, pResult->m_hValue->pev->absmin.z, pResult->m_pField->pev->absmax.x, pResult->m_pField->pev->absmax.y, pResult->m_pField->pev->absmax.z, pResult->m_pField->pev->absmin.x, pResult->m_pField->pev->absmin.y, pResult->m_pField->pev->absmin.z); //LRCT - - m_pField->FireOnEntry( pValue ); - return pResult; - } -} - -CInOutRegister *CInOutRegister::Prune( void ) -{ - if ( m_hValue ) - { - ASSERTSZ(m_pNext != NULL, "invalid InOut registry terminator\n"); - if ( m_pField->Intersects(m_hValue) ) - { - // this entity is still inside the field, do nothing - m_pNext = m_pNext->Prune(); - return this; - } - else - { -// ALERT(at_console, "removing; max %.2f %.2f %.2f, min %.2f %.2f %.2f is outside max %.2f %.2f %.2f, min %.2f %.2f %.2f\n", m_hValue->pev->absmax.x, m_hValue->pev->absmax.y, m_hValue->pev->absmax.z, m_hValue->pev->absmin.x, m_hValue->pev->absmin.y, m_hValue->pev->absmin.z, m_pField->pev->absmax.x, m_pField->pev->absmax.y, m_pField->pev->absmax.z, m_pField->pev->absmin.x, m_pField->pev->absmin.y, m_pField->pev->absmin.z); //LRCT - - // this entity has just left the field, trigger - m_pField->FireOnLeaving( m_hValue ); - SetThink(&CInOutRegister:: SUB_Remove ); - SetNextThink( 0.1 ); - return m_pNext->Prune(); - } - } - else - { // this register has a missing or null value - if (m_pNext) - { - // this is an invalid list entry, remove it - SetThink(&CInOutRegister:: SUB_Remove ); - SetNextThink( 0.1 ); - return m_pNext->Prune(); - } - else - { - // this is the list terminator, leave it. - return this; - } - } -} - - - -// CTriggerInOut method bodies: - -LINK_ENTITY_TO_CLASS( trigger_inout, CTriggerInOut ); - -TYPEDESCRIPTION CTriggerInOut::m_SaveData[] = -{ - DEFINE_FIELD( CTriggerInOut, m_iszAltTarget, FIELD_STRING ), - DEFINE_FIELD( CTriggerInOut, m_iszBothTarget, FIELD_STRING ), - DEFINE_FIELD( CTriggerInOut, m_pRegister, FIELD_CLASSPTR ), -}; - -IMPLEMENT_SAVERESTORE(CTriggerInOut,CBaseTrigger); - -void CTriggerInOut::KeyValue( KeyValueData *pkvd ) -{ - if (FStrEq(pkvd->szKeyName, "m_iszAltTarget")) - { - m_iszAltTarget = ALLOC_STRING( pkvd->szValue ); - pkvd->fHandled = TRUE; - } - else if (FStrEq(pkvd->szKeyName, "m_iszBothTarget")) - { - m_iszBothTarget = ALLOC_STRING( pkvd->szValue ); - pkvd->fHandled = TRUE; - } - else - CBaseTrigger::KeyValue( pkvd ); -} - -void CTriggerInOut :: Spawn( void ) -{ - InitTrigger(); - // create a null-terminator for the registry - m_pRegister = GetClassPtr( (CInOutRegister*)NULL ); - m_pRegister->m_hValue = NULL; - m_pRegister->m_pNext = NULL; - m_pRegister->m_pField = this; - m_pRegister->pev->classname = MAKE_STRING("inout_register"); -} - -void CTriggerInOut :: Touch( CBaseEntity *pOther ) -{ - if (!CanTouch(pOther->pev)) return; - - m_pRegister = m_pRegister->Add(pOther); - - if (m_fNextThink <= 0 && !m_pRegister->IsEmpty()) - SetNextThink( 0.1 ); -} - -void CTriggerInOut :: Think( void ) -{ - // Prune handles all Intersects tests and fires targets as appropriate - m_pRegister = m_pRegister->Prune(); - - if (m_pRegister->IsEmpty()) - DontThink(); - else - SetNextThink( 0.1 ); -} - -void CTriggerInOut :: FireOnEntry( CBaseEntity *pOther ) -{ - if (UTIL_IsMasterTriggered(m_sMaster,pOther)) - { - FireTargets(STRING(m_iszBothTarget), pOther, this, USE_ON, 0); - FireTargets(STRING(pev->target), pOther, this, USE_TOGGLE, 0); - } -} - -void CTriggerInOut :: FireOnLeaving( CBaseEntity *pEnt ) -{ - if ( UTIL_IsMasterTriggered(m_sMaster, pEnt) ) - { - FireTargets(STRING(m_iszBothTarget), pEnt, this, USE_OFF, 0); - FireTargets(STRING(m_iszAltTarget), pEnt, this, USE_TOGGLE, 0); - } -} - - -// ============================== -// trigger_counter - -//After the counter has been triggered "cTriggersLeft" -//times (default 2), it will fire all of it's targets and remove itself. -class CTriggerCounter : public CTriggerMultiple -{ -public: - void Spawn( void ); - void EXPORT CounterUse( CBaseEntity *pActivator, CBaseEntity *pCaller, USE_TYPE useType, float value ); - void KeyValue( KeyValueData *pkvd ); -}; -LINK_ENTITY_TO_CLASS( trigger_counter, CTriggerCounter ); - -void CTriggerCounter :: KeyValue( KeyValueData *pkvd ) -{ - if (FStrEq(pkvd->szKeyName, "count")) - { - m_cTriggersLeft = (int) atof(pkvd->szValue); - pkvd->fHandled = TRUE; - } - else - CTriggerMultiple::KeyValue( pkvd ); -} - -void CTriggerCounter :: Spawn( void ) -{ - // By making the flWait be -1, this counter-trigger will disappear after it's activated - // (but of course it needs cTriggersLeft "uses" before that happens). - m_flWait = -1; - - if (m_cTriggersLeft == 0) - m_cTriggersLeft = 2; - SetUse(&CTriggerCounter :: CounterUse ); -} - -void CTriggerCounter::CounterUse( CBaseEntity *pActivator, CBaseEntity *pCaller, USE_TYPE useType, float value ) -{ - m_cTriggersLeft--; - m_hActivator = pActivator; - - if (m_cTriggersLeft < 0) - return; - - BOOL fTellActivator = - (FClassnameIs(m_hActivator->pev, "player") && - !FBitSet(pev->spawnflags, SPAWNFLAG_NOMESSAGE)); - if (m_cTriggersLeft != 0) - { - if (fTellActivator) - { - // UNDONE: I don't think we want these Quakesque messages - switch (m_cTriggersLeft) - { - case 1: ALERT(at_debug, "Only 1 more to go..."); break; - case 2: ALERT(at_debug, "Only 2 more to go..."); break; - case 3: ALERT(at_debug, "Only 3 more to go..."); break; - default: ALERT(at_debug, "There are more to go..."); break; - } - } - return; - } - - // !!!UNDONE: I don't think we want these Quakesque messages - if (fTellActivator) - ALERT(at_debug, "Sequence completed!"); - - ActivateMultiTrigger( m_hActivator ); -} - -// ====================== TRIGGER_CHANGELEVEL ================================ - -class CTriggerVolume : public CPointEntity // Derive from point entity so this doesn't move across levels -{ -public: - void Spawn( void ); -}; - -LINK_ENTITY_TO_CLASS( trigger_transition, CTriggerVolume ); - -// Define space that travels across a level transition -void CTriggerVolume :: Spawn( void ) -{ - pev->solid = SOLID_NOT; - pev->movetype = MOVETYPE_NONE; - SET_MODEL(ENT(pev), STRING(pev->model)); // set size and link into world - pev->model = NULL; - pev->modelindex = 0; -} - - -// Fires a target after level transition and then dies -class CFireAndDie : public CBaseDelay -{ -public: - void Spawn( void ); - void Precache( void ); - void Think( void ); - int ObjectCaps( void ) { return CBaseDelay::ObjectCaps() | FCAP_FORCE_TRANSITION; } // Always go across transitions -}; -LINK_ENTITY_TO_CLASS( fireanddie, CFireAndDie ); - -void CFireAndDie::Spawn( void ) -{ - pev->classname = MAKE_STRING("fireanddie"); - // Don't call Precache() - it should be called on restore -} - - -void CFireAndDie::Precache( void ) -{ - // This gets called on restore - SetNextThink( m_flDelay ); -} - - -void CFireAndDie::Think( void ) -{ - SUB_UseTargets( this, USE_TOGGLE, 0 ); - UTIL_Remove( this ); -} - - -#define SF_CHANGELEVEL_USEONLY 0x0002 -class CChangeLevel : public CBaseTrigger -{ -public: - void Spawn( void ); - void KeyValue( KeyValueData *pkvd ); - void EXPORT UseChangeLevel ( CBaseEntity *pActivator, CBaseEntity *pCaller, USE_TYPE useType, float value ); - void EXPORT TriggerChangeLevel( void ); - void EXPORT ExecuteChangeLevel( void ); - void EXPORT TouchChangeLevel( CBaseEntity *pOther ); - void ChangeLevelNow( CBaseEntity *pActivator ); - - static edict_t *FindLandmark( const char *pLandmarkName ); - static int ChangeList( LEVELLIST *pLevelList, int maxList ); - static int AddTransitionToList( LEVELLIST *pLevelList, int listCount, const char *pMapName, const char *pLandmarkName, edict_t *pentLandmark ); - static int InTransitionVolume( CBaseEntity *pEntity, char *pVolumeName ); - - virtual int Save( CSave &save ); - virtual int Restore( CRestore &restore ); - - static TYPEDESCRIPTION m_SaveData[]; - - char m_szMapName[cchMapNameMost]; // trigger_changelevel only: next map - char m_szLandmarkName[cchMapNameMost]; // trigger_changelevel only: landmark on next map - int m_changeTarget; - float m_changeTargetDelay; -}; -LINK_ENTITY_TO_CLASS( trigger_changelevel, CChangeLevel ); - -// Global Savedata for changelevel trigger -TYPEDESCRIPTION CChangeLevel::m_SaveData[] = -{ - DEFINE_ARRAY( CChangeLevel, m_szMapName, FIELD_CHARACTER, cchMapNameMost ), - DEFINE_ARRAY( CChangeLevel, m_szLandmarkName, FIELD_CHARACTER, cchMapNameMost ), - DEFINE_FIELD( CChangeLevel, m_changeTarget, FIELD_STRING ), - DEFINE_FIELD( CChangeLevel, m_changeTargetDelay, FIELD_FLOAT ), -}; - -IMPLEMENT_SAVERESTORE(CChangeLevel,CBaseTrigger); - -// -// Cache user-entity-field values until spawn is called. -// - -void CChangeLevel :: KeyValue( KeyValueData *pkvd ) -{ - if (FStrEq(pkvd->szKeyName, "map")) - { - if (strlen(pkvd->szValue) >= cchMapNameMost) - ALERT( at_error, "Map name '%s' too long (32 chars)\n", pkvd->szValue ); - - strcpy(m_szMapName, pkvd->szValue); - - //LRC -- don't allow changelevels to contain capital letters; it causes problems -// ALERT(at_console, "MapName %s ", m_szMapName); - for (int i = 0; m_szMapName[i]; i++) { m_szMapName[i] = tolower(m_szMapName[i]); } -// ALERT(at_console, "changed to %s\n", m_szMapName); - - pkvd->fHandled = TRUE; - } - else if (FStrEq(pkvd->szKeyName, "landmark")) - { - if (strlen(pkvd->szValue) >= cchMapNameMost) - ALERT( at_error, "Landmark name '%s' too long (32 chars)\n", pkvd->szValue ); - strcpy(m_szLandmarkName, pkvd->szValue); - pkvd->fHandled = TRUE; - } - else if (FStrEq(pkvd->szKeyName, "changetarget")) - { - m_changeTarget = ALLOC_STRING( pkvd->szValue ); - pkvd->fHandled = TRUE; - } - else if (FStrEq(pkvd->szKeyName, "changedelay")) - { - m_changeTargetDelay = atof( pkvd->szValue ); - pkvd->fHandled = TRUE; - } - else - CBaseTrigger::KeyValue( pkvd ); -} - - -/*QUAKED trigger_changelevel (0.5 0.5 0.5) ? NO_INTERMISSION -When the player touches this, he gets sent to the map listed in the "map" variable. Unless the NO_INTERMISSION flag is set, the view will go to the info_intermission spot and display stats. -*/ - -void CChangeLevel :: Spawn( void ) -{ - if ( FStrEq( m_szMapName, "" ) ) - ALERT( at_debug, "a trigger_changelevel doesn't have a map" ); - - if ( FStrEq( m_szLandmarkName, "" ) ) - ALERT( at_debug, "trigger_changelevel to %s doesn't have a landmark", m_szMapName ); - - if (!FStringNull ( pev->targetname ) ) - { - SetUse(&CChangeLevel :: UseChangeLevel ); - } - InitTrigger(); - if ( !(pev->spawnflags & SF_CHANGELEVEL_USEONLY) ) - SetTouch(&CChangeLevel :: TouchChangeLevel ); -// ALERT( at_console, "TRANSITION: %s (%s)\n", m_szMapName, m_szLandmarkName ); -} - - -void CChangeLevel :: ExecuteChangeLevel( void ) -{ - MESSAGE_BEGIN( MSG_ALL, gmsgIntermission ); - MESSAGE_END(); -} - - -FILE_GLOBAL char st_szNextMap[cchMapNameMost]; -FILE_GLOBAL char st_szNextSpot[cchMapNameMost]; - -edict_t *CChangeLevel :: FindLandmark( const char *pLandmarkName ) -{ - CBaseEntity *pLandmark; - - pLandmark = UTIL_FindEntityByTargetname( NULL, pLandmarkName ); - while ( pLandmark ) - { - // Found the landmark - if ( FClassnameIs( pLandmark->pev, "info_landmark" ) ) - return ENT(pLandmark->pev); - else - pLandmark = UTIL_FindEntityByTargetname( pLandmark, pLandmarkName ); - } - ALERT( at_error, "Can't find landmark %s\n", pLandmarkName ); - return NULL; -} - - -//========================================================= -// CChangeLevel :: Use - allows level transitions to be -// triggered by buttons, etc. -// -//========================================================= -void CChangeLevel :: UseChangeLevel ( CBaseEntity *pActivator, CBaseEntity *pCaller, USE_TYPE useType, float value ) -{ - ChangeLevelNow( pActivator ); -} - -void CChangeLevel :: ChangeLevelNow( CBaseEntity *pActivator ) -{ - edict_t *pentLandmark; - LEVELLIST levels[16]; - - ASSERT(!FStrEq(m_szMapName, "")); - - // Don't work in deathmatch - if ( g_pGameRules->IsDeathmatch() ) - return; - - // Some people are firing these multiple times in a frame, disable - if ( gpGlobals->time == pev->dmgtime ) - return; - - pev->dmgtime = gpGlobals->time; - - - CBaseEntity *pPlayer = CBaseEntity::Instance( g_engfuncs.pfnPEntityOfEntIndex( 1 ) ); - if ( !InTransitionVolume( pPlayer, m_szLandmarkName ) ) - { - ALERT( at_aiconsole, "Player isn't in the transition volume %s, aborting\n", m_szLandmarkName ); - return; - } - - // Create an entity to fire the changetarget - if ( m_changeTarget ) - { - CFireAndDie *pFireAndDie = GetClassPtr( (CFireAndDie *)NULL ); - if ( pFireAndDie ) - { - // Set target and delay - pFireAndDie->pev->target = m_changeTarget; - pFireAndDie->m_flDelay = m_changeTargetDelay; - pFireAndDie->pev->origin = pPlayer->pev->origin; - // Call spawn - DispatchSpawn( pFireAndDie->edict() ); - } - } - // This object will get removed in the call to CHANGE_LEVEL, copy the params into "safe" memory - strcpy(st_szNextMap, m_szMapName); - - m_hActivator = pActivator; - SUB_UseTargets( pActivator, USE_TOGGLE, 0 ); - st_szNextSpot[0] = 0; // Init landmark to NULL - - // look for a landmark entity - pentLandmark = FindLandmark( m_szLandmarkName ); - if ( !FNullEnt( pentLandmark ) ) - { - strcpy(st_szNextSpot, m_szLandmarkName); - gpGlobals->vecLandmarkOffset = VARS(pentLandmark)->origin; - } -// ALERT( at_console, "Level touches %d levels\n", ChangeList( levels, 16 ) ); - ALERT( at_debug, "CHANGE LEVEL: %s %s\n", st_szNextMap, st_szNextSpot ); - CHANGE_LEVEL( st_szNextMap, st_szNextSpot ); -} - -// -// GLOBALS ASSUMED SET: st_szNextMap -// -void CChangeLevel :: TouchChangeLevel( CBaseEntity *pOther ) -{ - if (!FClassnameIs(pOther->pev, "player")) - return; - - ChangeLevelNow( pOther ); -} - - -// Add a transition to the list, but ignore duplicates -// (a designer may have placed multiple trigger_changelevels with the same landmark) -int CChangeLevel::AddTransitionToList( LEVELLIST *pLevelList, int listCount, const char *pMapName, const char *pLandmarkName, edict_t *pentLandmark ) -{ - int i; - - if ( !pLevelList || !pMapName || !pLandmarkName || !pentLandmark ) - return 0; - - for ( i = 0; i < listCount; i++ ) - { - if ( pLevelList[i].pentLandmark == pentLandmark && strcmp( pLevelList[i].mapName, pMapName ) == 0 ) - return 0; - } - strcpy( pLevelList[listCount].mapName, pMapName ); - strcpy( pLevelList[listCount].landmarkName, pLandmarkName ); - pLevelList[listCount].pentLandmark = pentLandmark; - pLevelList[listCount].vecLandmarkOrigin = VARS(pentLandmark)->origin; - - return 1; -} - -int BuildChangeList( LEVELLIST *pLevelList, int maxList ) -{ - return CChangeLevel::ChangeList( pLevelList, maxList ); -} - - -int CChangeLevel::InTransitionVolume( CBaseEntity *pEntity, char *pVolumeName ) -{ - CBaseEntity *pVolume; - - - if ( pEntity->ObjectCaps() & FCAP_FORCE_TRANSITION ) - return 1; - - // If you're following another entity, follow it through the transition (weapons follow the player) - if ( pEntity->pev->movetype == MOVETYPE_FOLLOW ) - { - if ( pEntity->pev->aiment != NULL ) - pEntity = CBaseEntity::Instance( pEntity->pev->aiment ); - } - - int inVolume = 1; // Unless we find a trigger_transition, everything is in the volume - - pVolume = UTIL_FindEntityByTargetname( NULL, pVolumeName ); - while ( pVolume ) - { - if ( FClassnameIs( pVolume->pev, "trigger_transition" ) ) - { - if ( pVolume->Intersects( pEntity ) ) // It touches one, it's in the volume - return 1; - else - inVolume = 0; // Found a trigger_transition, but I don't intersect it -- if I don't find another, don't go! - } - pVolume = UTIL_FindEntityByTargetname( pVolume, pVolumeName ); - } - - return inVolume; -} - - -// We can only ever move 512 entities across a transition -#define MAX_ENTITY 512 - -// This has grown into a complicated beast -// Can we make this more elegant? -// This builds the list of all transitions on this level and which entities are in their PVS's and -// can / should be moved across. -int CChangeLevel::ChangeList( LEVELLIST *pLevelList, int maxList ) -{ - edict_t *pentLandmark; - int i, count; - - count = 0; - - // Find all of the possible level changes on this BSP - CBaseEntity *pChangelevel = UTIL_FindEntityByClassname( NULL, "trigger_changelevel" ); - - if ( !pChangelevel ) - return NULL; - - while ( pChangelevel ) - { - CChangeLevel *pTrigger; - - pTrigger = GetClassPtr((CChangeLevel *)pChangelevel->pev); - if ( pTrigger ) - { - // Find the corresponding landmark - pentLandmark = FindLandmark( pTrigger->m_szLandmarkName ); - if ( pentLandmark ) - { - // Build a list of unique transitions - if ( AddTransitionToList( pLevelList, count, pTrigger->m_szMapName, pTrigger->m_szLandmarkName, pentLandmark ) ) - { - count++; - if ( count >= maxList ) // FULL!! - break; - } - } - } - pChangelevel = UTIL_FindEntityByClassname( pChangelevel, "trigger_changelevel" ); - } - - if ( gpGlobals->pSaveData && ((SAVERESTOREDATA *)gpGlobals->pSaveData)->pTable ) - { - CSave saveHelper( (SAVERESTOREDATA *)gpGlobals->pSaveData ); - - for ( i = 0; i < count; i++ ) - { - int j, entityCount = 0; - CBaseEntity *pEntList[ MAX_ENTITY ]; - int entityFlags[ MAX_ENTITY ]; - - // Follow the linked list of entities in the PVS of the transition landmark - edict_t *pent = UTIL_EntitiesInPVS( pLevelList[i].pentLandmark ); - - // Build a list of valid entities in this linked list (we're going to use pent->v.chain again) - while ( !FNullEnt( pent ) ) - { - CBaseEntity *pEntity = CBaseEntity::Instance(pent); - if ( pEntity ) - { -// ALERT( at_console, "Trying %s\n", STRING(pEntity->pev->classname) ); - int caps = pEntity->ObjectCaps(); - if ( !(caps & FCAP_DONT_SAVE) ) - { - int flags = 0; - - // If this entity can be moved or is global, mark it - if ( caps & FCAP_ACROSS_TRANSITION ) - flags |= FENTTABLE_MOVEABLE; - if ( pEntity->pev->globalname && !pEntity->IsDormant() ) - flags |= FENTTABLE_GLOBAL; - if ( flags ) - { - pEntList[ entityCount ] = pEntity; - entityFlags[ entityCount ] = flags; - entityCount++; - if ( entityCount > MAX_ENTITY ) - ALERT( at_error, "Too many entities across a transition!" ); - } -// else -// ALERT( at_console, "Failed %s\n", STRING(pEntity->pev->classname) ); - } -// else -// ALERT( at_console, "DON'T SAVE %s\n", STRING(pEntity->pev->classname) ); - } - pent = pent->v.chain; - } - - for ( j = 0; j < entityCount; j++ ) - { - // Check to make sure the entity isn't screened out by a trigger_transition - if ( entityFlags[j] && InTransitionVolume( pEntList[j], pLevelList[i].landmarkName ) ) - { - // Mark entity table with 1<pev->classname) ); - - } - } - } - - return count; -} - -/* -go to the next level for deathmatch -only called if a time or frag limit has expired -*/ -void NextLevel( void ) -{ - CBaseEntity* pEnt; - CChangeLevel *pChange; - - // find a trigger_changelevel - pEnt = UTIL_FindEntityByClassname(NULL, "trigger_changelevel"); - - // go back to start if no trigger_changelevel - if ( !pEnt ) - { - gpGlobals->mapname = MAKE_STRING("start"); - pChange = GetClassPtr( (CChangeLevel *)NULL ); - strcpy(pChange->m_szMapName, "start"); - } - else - pChange = GetClassPtr( (CChangeLevel *)pEnt->pev ); - - strcpy(st_szNextMap, pChange->m_szMapName); - g_fGameOver = TRUE; - - pChange->SetNextThink( 0 ); - if (pChange->m_fNextThink) - { - pChange->SetThink(& CChangeLevel::ExecuteChangeLevel ); - pChange->SetNextThink( 0.1 ); - } -} -// ============================== LADDER ======================================= - -#define SF_LADDER_VISIBLE 1 - -class CLadder : public CBaseTrigger -{ -public: - void KeyValue( KeyValueData *pkvd ); - void Spawn( void ); - void Precache( void ); -}; -LINK_ENTITY_TO_CLASS( func_ladder, CLadder ); - - -void CLadder :: KeyValue( KeyValueData *pkvd ) -{ - CBaseTrigger::KeyValue( pkvd ); -} - - -//========================================================= -// func_ladder - makes an area vertically negotiable -//========================================================= -void CLadder :: Precache( void ) -{ - // Do all of this in here because we need to 'convert' old saved games - pev->solid = SOLID_NOT; - pev->skin = CONTENTS_LADDER; - if ( CVAR_GET_FLOAT("showtriggers") == 0 && !(pev->spawnflags & SF_LADDER_VISIBLE)) - { - pev->effects |= EF_NODRAW; - //LRC- NODRAW is a better-performance way to stop things being drawn. - // (unless... would it prevent client-side movement algorithms from working?) -// pev->rendermode = kRenderTransTexture; -// pev->renderamt = 0; - } - else - pev->effects &= ~EF_NODRAW; -} - - -void CLadder :: Spawn( void ) -{ - Precache(); - - SET_MODEL(ENT(pev), STRING(pev->model)); // set size and link into world - pev->movetype = MOVETYPE_PUSH; -} - - -// ========================== A TRIGGER THAT PUSHES YOU =============================== - -class CTriggerPush : public CBaseTrigger -{ -public: - void Spawn( void ); - void KeyValue( KeyValueData *pkvd ); - void Touch( CBaseEntity *pOther ); - - virtual int Save( CSave &save ); - virtual int Restore( CRestore &restore ); - static TYPEDESCRIPTION m_SaveData[]; - - int m_iszPushVel; - int m_iszPushSpeed; -}; -LINK_ENTITY_TO_CLASS( trigger_push, CTriggerPush ); - -TYPEDESCRIPTION CTriggerPush::m_SaveData[] = -{ - DEFINE_FIELD( CTriggerPush, m_iszPushVel, FIELD_STRING ), - DEFINE_FIELD( CTriggerPush, m_iszPushSpeed, FIELD_STRING ), -}; - -IMPLEMENT_SAVERESTORE(CTriggerPush,CBaseTrigger); - -void CTriggerPush :: KeyValue( KeyValueData *pkvd ) -{ - if (FStrEq(pkvd->szKeyName, "m_iszPushSpeed")) - { - m_iszPushSpeed = ALLOC_STRING( pkvd->szValue ); - pkvd->fHandled = TRUE; - } - else if (FStrEq(pkvd->szKeyName, "m_iszPushVel")) - { - m_iszPushVel = ALLOC_STRING( pkvd->szValue ); - pkvd->fHandled = TRUE; - } - else - CBaseTrigger::KeyValue( pkvd ); -} - - -/*QUAKED trigger_push (.5 .5 .5) ? TRIG_PUSH_ONCE -Pushes the player -*/ - -void CTriggerPush :: Spawn( ) -{ - if ( pev->angles == g_vecZero ) - pev->angles.y = 360; - InitTrigger(); - - if ( FBitSet (pev->spawnflags, SF_TRIGGER_PUSH_START_OFF) )// if flagged to Start Turned Off, make trigger nonsolid. - pev->solid = SOLID_NOT; - - SetUse(&CTriggerPush :: ToggleUse ); - - UTIL_SetOrigin( this, pev->origin ); // Link into the list -} - - -void CTriggerPush :: Touch( CBaseEntity *pOther ) -{ - entvars_t* pevToucher = pOther->pev; - - // UNDONE: Is there a better way than health to detect things that have physics? (clients/monsters) - switch( pevToucher->movetype ) - { - case MOVETYPE_NONE: - case MOVETYPE_PUSH: - case MOVETYPE_NOCLIP: - case MOVETYPE_FOLLOW: - return; - } - - Vector vecPush; - if (!FStringNull(m_iszPushVel)) - vecPush = CalcLocus_Velocity( this, pOther, STRING(m_iszPushVel) ); - else vecPush = pev->movedir; - - if (!FStringNull(m_iszPushSpeed)) - vecPush = vecPush * CalcLocus_Ratio( pOther, STRING(m_iszPushSpeed) ); - - if (pev->speed) vecPush = vecPush * pev->speed; - else vecPush = vecPush * 100; - - if ( pevToucher->solid != SOLID_NOT ) //&& pevToucher->solid != SOLID_BSP ) - { - if(pevToucher->movetype == MOVETYPE_PUSHSTEP) //pushable related code - { - pevToucher->velocity = pevToucher->velocity + vecPush; - - if ( pevToucher->velocity.z > 0 ) - pevToucher->flags &= ~FL_ONGROUND; - pev->solid = SOLID_NOT; //push once. re-enable to affect again - } - else //other physobjects - { - // Push field, transfer to base velocity - if ( pevToucher->flags & FL_BASEVELOCITY ) - vecPush = vecPush + pevToucher->basevelocity; - - pevToucher->basevelocity = vecPush; - pevToucher->flags |= FL_BASEVELOCITY; - //ALERT( at_console, "Vel %f, base %f\n", pevToucher->velocity.z, pevToucher->basevelocity.z ); - } - - if (FBitSet( pev->spawnflags, SF_TRIG_PUSH_ONCE )) - { - UTIL_Remove( this ); - } - } -} - - -//=========================================================== -//LRC- trigger_bounce -//=========================================================== -#define SF_BOUNCE_CUTOFF 16 - -class CTriggerBounce : public CBaseTrigger -{ -public: - void Spawn( void ); - void Touch( CBaseEntity *pOther ); -}; - -LINK_ENTITY_TO_CLASS( trigger_bounce, CTriggerBounce ); - - -void CTriggerBounce :: Spawn( void ) -{ - SetMovedir(pev); - InitTrigger(); -} - -void CTriggerBounce :: Touch( CBaseEntity *pOther ) -{ - if (!UTIL_IsMasterTriggered(m_sMaster, pOther)) - return; - if (!CanTouch(pOther->pev)) - return; - - float dot = DotProduct(pev->movedir, pOther->pev->velocity); - if (dot < -pev->armorvalue) - { - if (pev->spawnflags & SF_BOUNCE_CUTOFF) - pOther->pev->velocity = pOther->pev->velocity - (dot + pev->frags*(dot+pev->armorvalue))*pev->movedir; - else - pOther->pev->velocity = pOther->pev->velocity - (dot + pev->frags*dot)*pev->movedir; - SUB_UseTargets( pOther, USE_TOGGLE, 0 ); - } -} - - -//=========================================================== -//LRC- trigger_onsight -//=========================================================== -#define SF_ONSIGHT_NOLOS 0x00001 -#define SF_ONSIGHT_NOGLASS 0x00002 -#define SF_ONSIGHT_ACTIVE 0x08000 -#define SF_ONSIGHT_DEMAND 0x10000 - -class CTriggerOnSight : public CBaseDelay -{ -public: - void Spawn( void ); - void Think( void ); - BOOL VisionCheck( void ); - BOOL CanSee(CBaseEntity *pLooker, CBaseEntity *pSeen); - virtual int ObjectCaps( void ) { return CBaseEntity :: ObjectCaps() & ~FCAP_ACROSS_TRANSITION; } - - STATE GetState(); -}; - -LINK_ENTITY_TO_CLASS( trigger_onsight, CTriggerOnSight ); - -void CTriggerOnSight :: Spawn( void ) -{ - if (pev->target || pev->noise) - // if we're going to have to trigger stuff, start thinking - SetNextThink( 1 ); - else - // otherwise, just check whenever someone asks about our state. - pev->spawnflags |= SF_ONSIGHT_DEMAND; - - if (pev->max_health > 0) - { - pev->health = cos(pev->max_health/2 * M_PI/180.0); -// ALERT(at_debug, "Cosine is %f\n", pev->health); - } -} - -STATE CTriggerOnSight :: GetState( void ) -{ - if (pev->spawnflags & SF_ONSIGHT_DEMAND) - return VisionCheck()?STATE_ON:STATE_OFF; - else - return (pev->spawnflags & SF_ONSIGHT_ACTIVE)?STATE_ON:STATE_OFF; -} - -void CTriggerOnSight :: Think( void ) -{ - // is this a sensible rate? - SetNextThink( 0.1 ); - -// if (!UTIL_IsMasterTriggered(m_sMaster, NULL)) -// { -// pev->spawnflags &= ~SF_ONSIGHT_ACTIVE; -// return; -// } - - if (VisionCheck()) - { - if (!FBitSet(pev->spawnflags, SF_ONSIGHT_ACTIVE)) - { - FireTargets(STRING(pev->target), this, this, USE_TOGGLE, 0); - FireTargets(STRING(pev->noise1), this, this, USE_ON, 0); - pev->spawnflags |= SF_ONSIGHT_ACTIVE; - } - } - else - { - if (pev->spawnflags & SF_ONSIGHT_ACTIVE) - { - FireTargets(STRING(pev->noise), this, this, USE_TOGGLE, 0); - FireTargets(STRING(pev->noise1), this, this, USE_OFF, 0); - pev->spawnflags &= ~SF_ONSIGHT_ACTIVE; - } - } -} - -BOOL CTriggerOnSight :: VisionCheck( void ) -{ // and GetState check (stops dead monsters seeing) - CBaseEntity *pLooker; - if (pev->netname) - { - pLooker = UTIL_FindEntityByTargetname(NULL, STRING(pev->netname)); - if (!pLooker) - return FALSE; // if we can't find the eye entity, give up - } - else - { - pLooker = UTIL_FindEntityByClassname(NULL, "player"); - if (!pLooker) - { - ALERT(at_error, "trigger_onsight can't find player!?\n"); - return FALSE; - } - } - - CBaseEntity *pSeen; - if (pev->message) - pSeen = UTIL_FindEntityByTargetname(NULL, STRING(pev->message)); - else - return CanSee(pLooker, this); - - if (!pSeen) - { - // must be a classname. - pSeen = UTIL_FindEntityByClassname(pSeen, STRING(pev->message)); - while (pSeen != NULL) - { - if (CanSee(pLooker, pSeen)) - return TRUE; - pSeen = UTIL_FindEntityByClassname(pSeen, STRING(pev->message)); - } - return FALSE; - } - else - { - while (pSeen != NULL) - { - if (CanSee(pLooker, pSeen)) - return TRUE; - pSeen = UTIL_FindEntityByTargetname(pSeen, STRING(pev->message)); - } - return FALSE; - } -} - -// by the criteria we're using, can the Looker see the Seen entity? -BOOL CTriggerOnSight :: CanSee(CBaseEntity *pLooker, CBaseEntity *pSeen) -{ - // out of range? - if (pev->frags && (pLooker->pev->origin - pSeen->pev->origin).Length() > pev->frags) - return FALSE; - - // check FOV if appropriate - if (pev->max_health < 360) - { - // copied from CBaseMonster's FInViewCone function - Vector2D vec2LOS; - float flDot; - float flComp = pev->health; - UTIL_MakeVectors ( pLooker->pev->angles ); - vec2LOS = ( pSeen->pev->origin - pLooker->pev->origin ).Make2D(); - vec2LOS = vec2LOS.Normalize(); - flDot = DotProduct (vec2LOS , gpGlobals->v_forward.Make2D() ); - -// ALERT(at_debug, "flDot is %f\n", flDot); - - if ( pev->max_health == -1 ) - { - CBaseMonster *pMonst = pLooker->MyMonsterPointer(); - if (pMonst) - flComp = pMonst->m_flFieldOfView; - else - return FALSE; // not a monster, can't use M-M-M-MonsterVision - } - - // outside field of view - if (flDot <= flComp) - return FALSE; - } - - // check LOS if appropriate - if (!FBitSet(pev->spawnflags, SF_ONSIGHT_NOLOS)) - { - TraceResult tr; - if (SF_ONSIGHT_NOGLASS) - UTIL_TraceLine( pLooker->EyePosition(), pSeen->pev->origin, ignore_monsters, ignore_glass, pLooker->edict(), &tr ); - else - UTIL_TraceLine( pLooker->EyePosition(), pSeen->pev->origin, ignore_monsters, dont_ignore_glass, pLooker->edict(), &tr ); - if (tr.flFraction < 1.0 && tr.pHit != pSeen->edict()) - return FALSE; - } - - return TRUE; -} - -//====================================== -// teleport trigger -// -// -class CTriggerTeleport : public CBaseTrigger -{ -public: - void Spawn( void ); - void EXPORT TeleportTouch ( CBaseEntity *pOther ); -}; -LINK_ENTITY_TO_CLASS( trigger_teleport, CTriggerTeleport ); - -void CTriggerTeleport :: Spawn( void ) -{ - InitTrigger(); - - SetTouch(&CTriggerTeleport :: TeleportTouch ); -} - -void CTriggerTeleport :: TeleportTouch( CBaseEntity *pOther ) -{ - entvars_t* pevToucher = pOther->pev; - CBaseEntity *pTarget = NULL; - - // Only teleport monsters or clients - if ( !FBitSet( pevToucher->flags, FL_CLIENT|FL_MONSTER ) ) - return; - - if (!UTIL_IsMasterTriggered(m_sMaster, pOther)) - return; - - if (!CanTouch(pevToucher)) - return; - - pTarget = UTIL_FindEntityByTargetname( pTarget, STRING(pev->target) ); - if ( !pTarget ) - return; - - //LRC - landmark based teleports - CBaseEntity *pLandmark = UTIL_FindEntityByTargetname( NULL, STRING(pev->message) ); - if ( pLandmark ) - { - Vector vecOriginOffs = pTarget->pev->origin - pLandmark->pev->origin; - //ALERT(at_console, "Offs initially: %f %f %f\n", vecOriginOffs.x, vecOriginOffs.y, vecOriginOffs.z); - - // do we need to rotate the entity? - if ( pLandmark->pev->angles != pTarget->pev->angles ) - { - Vector vecVA; - float ydiff = pTarget->pev->angles.y - pLandmark->pev->angles.y; - - // set new angle to face -// ALERT(at_console, "angles = %f %f %f\n", pOther->pev->angles.x, pOther->pev->angles.y, pOther->pev->angles.z); - pOther->pev->angles.y += ydiff; - if (pOther->IsPlayer()) - { -// ALERT(at_console, "v_angle = %f %f %f\n", pOther->pev->v_angle.x, pOther->pev->v_angle.y, pOther->pev->v_angle.z); - pOther->pev->angles.x = pOther->pev->v_angle.x; -// pOther->pev->v_angles.y += ydiff; - pOther->pev->fixangle = TRUE; - } - - // set new velocity - vecVA = UTIL_VecToAngles(pOther->pev->velocity); - vecVA.y += ydiff; - UTIL_MakeVectors(vecVA); - pOther->pev->velocity = gpGlobals->v_forward * pOther->pev->velocity.Length(); - // fix the ugly "angle to vector" behaviour - a legacy from Quake - pOther->pev->velocity.z = -pOther->pev->velocity.z; - - // set new origin - Vector vecPlayerOffs = pOther->pev->origin - pLandmark->pev->origin; - //ALERT(at_console, "PlayerOffs: %f %f %f\n", vecPlayerOffs.x, vecPlayerOffs.y, vecPlayerOffs.z); - vecVA = UTIL_VecToAngles(vecPlayerOffs); - UTIL_MakeVectors(vecVA); - vecVA.y += ydiff; - UTIL_MakeVectors(vecVA); - Vector vecPlayerOffsNew = gpGlobals->v_forward * vecPlayerOffs.Length(); - vecPlayerOffsNew.z = -vecPlayerOffsNew.z; - //ALERT(at_console, "PlayerOffsNew: %f %f %f\n", vecPlayerOffsNew.x, vecPlayerOffsNew.y, vecPlayerOffsNew.z); - - vecOriginOffs = vecOriginOffs + vecPlayerOffsNew - vecPlayerOffs; - //ALERT(at_console, "vecOriginOffs: %f %f %f\n", vecOriginOffs.x, vecOriginOffs.y, vecOriginOffs.z); -// vecOriginOffs.y++; - } - - UTIL_SetOrigin( pOther, pOther->pev->origin + vecOriginOffs ); - } - else - { - Vector tmp = pTarget->pev->origin; - - if ( pOther->IsPlayer() ) - { - tmp.z -= pOther->pev->mins.z;// make origin adjustments in case the teleportee is a player. (origin in center, not at feet) - } - tmp.z++; - UTIL_SetOrigin( pOther, tmp ); - - pOther->pev->angles = pTarget->pev->angles; - pOther->pev->velocity = pOther->pev->basevelocity = g_vecZero; - if ( pOther->IsPlayer() ) - { - pOther->pev->v_angle = pTarget->pev->angles; //LRC - pOther->pev->fixangle = TRUE; - } - } - - pevToucher->flags &= ~FL_ONGROUND; - pevToucher->fixangle = TRUE; - - FireTargets(STRING(pev->noise), pOther, this, USE_TOGGLE, 0); -} - - -LINK_ENTITY_TO_CLASS( info_teleport_destination, CPointEntity ); - - - -class CTriggerSave : public CBaseTrigger -{ -public: - void Spawn( void ); - void EXPORT SaveTouch( CBaseEntity *pOther ); -}; -LINK_ENTITY_TO_CLASS( trigger_autosave, CTriggerSave ); - -void CTriggerSave::Spawn( void ) -{ - if ( g_pGameRules->IsDeathmatch() ) - { - REMOVE_ENTITY( ENT(pev) ); - return; - } - - InitTrigger(); - SetTouch(&CTriggerSave:: SaveTouch ); -} - -void CTriggerSave::SaveTouch( CBaseEntity *pOther ) -{ - if ( !UTIL_IsMasterTriggered( m_sMaster, pOther ) ) - return; - - // Only save on clients - if ( !pOther->IsPlayer() ) - return; - - SetTouch( NULL ); - UTIL_Remove( this ); - SERVER_COMMAND( "autosave\n" ); -} - -#define SF_ENDSECTION_USEONLY 0x0001 - -class CTriggerEndSection : public CBaseTrigger -{ -public: - void Spawn( void ); - void EXPORT EndSectionTouch( CBaseEntity *pOther ); - void KeyValue( KeyValueData *pkvd ); - void EXPORT EndSectionUse( CBaseEntity *pActivator, CBaseEntity *pCaller, USE_TYPE useType, float value ); -}; -LINK_ENTITY_TO_CLASS( trigger_endsection, CTriggerEndSection ); - - -void CTriggerEndSection::EndSectionUse( CBaseEntity *pActivator, CBaseEntity *pCaller, USE_TYPE useType, float value ) -{ - // Only save on clients - if ( pActivator && !pActivator->IsNetClient() ) - return; - - SetUse( NULL ); - - if ( pev->message ) - { - g_engfuncs.pfnEndSection(STRING(pev->message)); - } - UTIL_Remove( this ); -} - -void CTriggerEndSection::Spawn( void ) -{ - if ( g_pGameRules->IsDeathmatch() ) - { - REMOVE_ENTITY( ENT(pev) ); - return; - } - - InitTrigger(); - - SetUse(&CTriggerEndSection:: EndSectionUse ); - // If it is a "use only" trigger, then don't set the touch function. - if ( ! (pev->spawnflags & SF_ENDSECTION_USEONLY) ) - SetTouch(&CTriggerEndSection:: EndSectionTouch ); -} - -void CTriggerEndSection::EndSectionTouch( CBaseEntity *pOther ) -{ - // Only save on clients - if ( !pOther->IsNetClient() ) - return; - - SetTouch( NULL ); - - if (pev->message) - { - g_engfuncs.pfnEndSection(STRING(pev->message)); - } - UTIL_Remove( this ); -} - -void CTriggerEndSection :: KeyValue( KeyValueData *pkvd ) -{ - if (FStrEq(pkvd->szKeyName, "section")) - { -// m_iszSectionName = ALLOC_STRING( pkvd->szValue ); - // Store this in message so we don't have to write save/restore for this ent - pev->message = ALLOC_STRING( pkvd->szValue ); - pkvd->fHandled = TRUE; - } - else - CBaseTrigger::KeyValue( pkvd ); -} - - -class CTriggerGravity : public CBaseTrigger -{ -public: - void Spawn( void ); - void EXPORT GravityTouch( CBaseEntity *pOther ); -}; -LINK_ENTITY_TO_CLASS( trigger_gravity, CTriggerGravity ); - -void CTriggerGravity::Spawn( void ) -{ - InitTrigger(); - SetTouch(&CTriggerGravity:: GravityTouch ); -} - -void CTriggerGravity::GravityTouch( CBaseEntity *pOther ) -{ - // Only save on clients - if ( !pOther->IsPlayer() ) - return; - - pOther->pev->gravity = pev->gravity; -} - - - - -//=========================================================== -//LRC- trigger_startpatrol -//=========================================================== -class CTriggerSetPatrol : public CBaseDelay -{ -public: - void KeyValue( KeyValueData *pkvd ); - void Spawn( void ); - void Use( CBaseEntity *pActivator, CBaseEntity *pCaller, USE_TYPE useType, float value ); - - int ObjectCaps( void ) { return CBaseDelay::ObjectCaps() & ~FCAP_ACROSS_TRANSITION; } - virtual int Save( CSave &save ); - virtual int Restore( CRestore &restore ); - - static TYPEDESCRIPTION m_SaveData[]; - -private: - int m_iszPath; -}; -LINK_ENTITY_TO_CLASS( trigger_startpatrol, CTriggerSetPatrol ); - -TYPEDESCRIPTION CTriggerSetPatrol::m_SaveData[] = -{ - DEFINE_FIELD( CTriggerSetPatrol, m_iszPath, FIELD_STRING ), -}; - -IMPLEMENT_SAVERESTORE(CTriggerSetPatrol,CBaseDelay); - -void CTriggerSetPatrol::KeyValue( KeyValueData *pkvd ) -{ - if (FStrEq(pkvd->szKeyName, "m_iszPath")) - { - m_iszPath = ALLOC_STRING( pkvd->szValue ); - pkvd->fHandled = TRUE; - } - else - CBaseDelay::KeyValue( pkvd ); -} - -void CTriggerSetPatrol::Spawn( void ) -{ -} - - -void CTriggerSetPatrol::Use( CBaseEntity *pActivator, CBaseEntity *pCaller, USE_TYPE useType, float value ) -{ - CBaseEntity *pTarget = UTIL_FindEntityByTargetname( NULL, STRING( pev->target ), pActivator ); - CBaseEntity *pPath = UTIL_FindEntityByTargetname( NULL, STRING( m_iszPath ), pActivator ); - - if (pTarget && pPath) - { - CBaseMonster *pMonster = pTarget->MyMonsterPointer(); - if (pMonster) pMonster->StartPatrol(pPath); - } -} - - -//=========================================================== -//LRC- trigger_motion -//=========================================================== -#define SF_MOTION_DEBUG 1 - -class CTriggerMotion : public CPointEntity -{ -public: - void Use( CBaseEntity *pActivator, CBaseEntity *pCaller, USE_TYPE useType, float value ); - - virtual int Save( CSave &save ); - virtual int Restore( CRestore &restore ); - static TYPEDESCRIPTION m_SaveData[]; - - void KeyValue( KeyValueData *pkvd ); - - int m_iszPosition; - int m_iPosMode; - int m_iszAngles; - int m_iAngMode; - int m_iszVelocity; - int m_iVelMode; - int m_iszAVelocity; - int m_iAVelMode; -}; -LINK_ENTITY_TO_CLASS( trigger_motion, CTriggerMotion ); - -TYPEDESCRIPTION CTriggerMotion::m_SaveData[] = -{ - DEFINE_FIELD( CTriggerMotion, m_iszPosition, FIELD_STRING ), - DEFINE_FIELD( CTriggerMotion, m_iPosMode, FIELD_INTEGER ), - DEFINE_FIELD( CTriggerMotion, m_iszAngles, FIELD_STRING ), - DEFINE_FIELD( CTriggerMotion, m_iAngMode, FIELD_INTEGER ), - DEFINE_FIELD( CTriggerMotion, m_iszVelocity, FIELD_STRING ), - DEFINE_FIELD( CTriggerMotion, m_iVelMode, FIELD_INTEGER ), - DEFINE_FIELD( CTriggerMotion, m_iszAVelocity, FIELD_STRING ), - DEFINE_FIELD( CTriggerMotion, m_iAVelMode, FIELD_INTEGER ), -}; - -IMPLEMENT_SAVERESTORE(CTriggerMotion,CPointEntity); - -void CTriggerMotion::KeyValue( KeyValueData *pkvd ) -{ - if (FStrEq(pkvd->szKeyName, "m_iszPosition")) - { - m_iszPosition = ALLOC_STRING( pkvd->szValue ); - pkvd->fHandled = TRUE; - } - else if (FStrEq(pkvd->szKeyName, "m_iPosMode")) - { - m_iPosMode = atoi( pkvd->szValue ); - pkvd->fHandled = TRUE; - } - else if (FStrEq(pkvd->szKeyName, "m_iszAngles")) - { - m_iszAngles = ALLOC_STRING( pkvd->szValue ); - pkvd->fHandled = TRUE; - } - else if (FStrEq(pkvd->szKeyName, "m_iAngMode")) - { - m_iAngMode = atoi( pkvd->szValue ); - pkvd->fHandled = TRUE; - } - else if (FStrEq(pkvd->szKeyName, "m_iszVelocity")) - { - m_iszVelocity = ALLOC_STRING( pkvd->szValue ); - pkvd->fHandled = TRUE; - } - else if (FStrEq(pkvd->szKeyName, "m_iVelMode")) - { - m_iVelMode = atoi( pkvd->szValue ); - pkvd->fHandled = TRUE; - } - else if (FStrEq(pkvd->szKeyName, "m_iszAVelocity")) - { - m_iszAVelocity = ALLOC_STRING( pkvd->szValue ); - pkvd->fHandled = TRUE; - } - else if (FStrEq(pkvd->szKeyName, "m_iAVelMode")) - { - m_iAVelMode = atoi( pkvd->szValue ); - pkvd->fHandled = TRUE; - } - else - CPointEntity::KeyValue( pkvd ); -} - -void CTriggerMotion::Use( CBaseEntity *pActivator, CBaseEntity *pCaller, USE_TYPE useType, float value ) -{ - CBaseEntity *pTarget = UTIL_FindEntityByTargetname( NULL, STRING(pev->target), pActivator ); - if (pTarget == NULL || pActivator == NULL) return; - - if (pev->spawnflags & SF_MOTION_DEBUG) - ALERT(at_debug, "DEBUG: trigger_motion affects %s \"%s\":\n", STRING(pTarget->pev->classname), STRING(pTarget->pev->targetname)); - - if (m_iszPosition) - { - switch (m_iPosMode) - { - case 0: - if (pev->spawnflags & SF_MOTION_DEBUG) - ALERT(at_debug, "DEBUG: Set origin from %f %f %f ", pTarget->pev->origin.x, pTarget->pev->origin.y, pTarget->pev->origin.z); - pTarget->pev->origin = CalcLocus_Position( this, pActivator, STRING(m_iszPosition) ); - if (pev->spawnflags & SF_MOTION_DEBUG) - ALERT(at_debug, "to %f %f %f\n", pTarget->pev->origin.x, pTarget->pev->origin.y, pTarget->pev->origin.z); - pTarget->pev->flags &= ~FL_ONGROUND; - break; - case 1: - if (pev->spawnflags & SF_MOTION_DEBUG) - ALERT(at_debug, "DEBUG: Set origin from %f %f %f ", pTarget->pev->origin.x, pTarget->pev->origin.y, pTarget->pev->origin.z); - pTarget->pev->origin = pTarget->pev->origin + CalcLocus_Velocity( this, pActivator, STRING(m_iszPosition) ); - if (pev->spawnflags & SF_MOTION_DEBUG) - ALERT(at_debug, "to %f %f %f\n", pTarget->pev->origin.x, pTarget->pev->origin.y, pTarget->pev->origin.z); - pTarget->pev->flags &= ~FL_ONGROUND; - break; - } - } - - Vector vecTemp; - Vector vecVelAngles; - if (m_iszAngles) - { - switch (m_iAngMode) - { - case 0: - vecTemp = CalcLocus_Velocity( this, pActivator, STRING(m_iszAngles) ); - if (pev->spawnflags & SF_MOTION_DEBUG) - ALERT(at_debug, "DEBUG: Set angles from %f %f %f ", pTarget->pev->angles.x, pTarget->pev->angles.y, pTarget->pev->angles.z); - pTarget->pev->angles = UTIL_VecToAngles( vecTemp ); - if (pev->spawnflags & SF_MOTION_DEBUG) - ALERT(at_debug, "to %f %f %f\n", pTarget->pev->angles.x, pTarget->pev->angles.y, pTarget->pev->angles.z); - break; - case 1: - vecTemp = CalcLocus_Velocity( this, pActivator, STRING(m_iszVelocity) ); - if (pev->spawnflags & SF_MOTION_DEBUG) - ALERT(at_debug, "DEBUG: Rotate angles from %f %f %f ", pTarget->pev->angles.x, pTarget->pev->angles.y, pTarget->pev->angles.z); - pTarget->pev->angles = pTarget->pev->angles + UTIL_VecToAngles( vecTemp ); - if (pev->spawnflags & SF_MOTION_DEBUG) - ALERT(at_debug, "to %f %f %f\n", pTarget->pev->angles.x, pTarget->pev->angles.y, pTarget->pev->angles.z); - break; - case 2: - UTIL_StringToRandomVector( vecTemp, STRING(m_iszAngles) ); - if (pev->spawnflags & SF_MOTION_DEBUG) - ALERT(at_debug, "DEBUG: Rotate angles from %f %f %f ", pTarget->pev->angles.x, pTarget->pev->angles.y, pTarget->pev->angles.z); - pTarget->pev->angles = pTarget->pev->angles + vecTemp; - if (pev->spawnflags & SF_MOTION_DEBUG) - ALERT(at_debug, "to %f %f %f\n", pTarget->pev->angles.x, pTarget->pev->angles.y, pTarget->pev->angles.z); - break; - } - } - - if (m_iszVelocity) - { - switch (m_iVelMode) - { - case 0: - if (pev->spawnflags & SF_MOTION_DEBUG) - ALERT(at_debug, "DEBUG: Set velocity from %f %f %f ", pTarget->pev->velocity.x, pTarget->pev->velocity.y, pTarget->pev->velocity.z); - pTarget->pev->velocity = CalcLocus_Velocity( this, pActivator, STRING(m_iszVelocity) ); - if (pev->spawnflags & SF_MOTION_DEBUG) - ALERT(at_debug, "to %f %f %f\n", pTarget->pev->velocity.x, pTarget->pev->velocity.y, pTarget->pev->velocity.z); - break; - case 1: - if (pev->spawnflags & SF_MOTION_DEBUG) - ALERT(at_debug, "DEBUG: Set velocity from %f %f %f ", pTarget->pev->velocity.x, pTarget->pev->velocity.y, pTarget->pev->velocity.z); - pTarget->pev->velocity = pTarget->pev->velocity + CalcLocus_Velocity( this, pActivator, STRING(m_iszVelocity) ); - if (pev->spawnflags & SF_MOTION_DEBUG) - ALERT(at_debug, "to %f %f %f\n", pTarget->pev->velocity.x, pTarget->pev->velocity.y, pTarget->pev->velocity.z); - break; - case 2: - vecTemp = CalcLocus_Velocity( this, pActivator, STRING(m_iszVelocity) ); - vecVelAngles = UTIL_VecToAngles( vecTemp ) + UTIL_VecToAngles( pTarget->pev->velocity ); - UTIL_MakeVectors( vecVelAngles ); - if (pev->spawnflags & SF_MOTION_DEBUG) - ALERT(at_debug, "DEBUG: Rotate velocity from %f %f %f ", pTarget->pev->velocity.x, pTarget->pev->velocity.y, pTarget->pev->velocity.z); - pTarget->pev->velocity = pTarget->pev->velocity.Length() * gpGlobals->v_forward; - pTarget->pev->velocity.z = -pTarget->pev->velocity.z; //vecToAngles reverses the z angle - if (pev->spawnflags & SF_MOTION_DEBUG) - ALERT(at_debug, "to %f %f %f\n", pTarget->pev->velocity.x, pTarget->pev->velocity.y, pTarget->pev->velocity.z); - break; - case 3: - UTIL_StringToRandomVector( vecTemp, STRING(m_iszVelocity) ); - vecVelAngles = vecTemp + UTIL_VecToAngles( pTarget->pev->velocity ); - UTIL_MakeVectors( vecVelAngles ); - if (pev->spawnflags & SF_MOTION_DEBUG) - ALERT(at_debug, "DEBUG: Rotate velocity from %f %f %f ", pTarget->pev->velocity.x, pTarget->pev->velocity.y, pTarget->pev->velocity.z); - pTarget->pev->velocity = pTarget->pev->velocity.Length() * gpGlobals->v_forward; - pTarget->pev->velocity.z = -pTarget->pev->velocity.z; //vecToAngles reverses the z angle - if (pev->spawnflags & SF_MOTION_DEBUG) - ALERT(at_debug, "to %f %f %f\n", pTarget->pev->velocity.x, pTarget->pev->velocity.y, pTarget->pev->velocity.z); - break; - } - } - - if( m_iszAVelocity ) - { - switch (m_iAVelMode) - { - case 0: - UTIL_StringToRandomVector( vecTemp, STRING(m_iszAVelocity) ); - if (pev->spawnflags & SF_MOTION_DEBUG) - ALERT(at_debug, "DEBUG: Set avelocity from %f %f %f ", pTarget->pev->avelocity.x, pTarget->pev->avelocity.y, pTarget->pev->avelocity.z); - pTarget->pev->avelocity = vecTemp; - if (pev->spawnflags & SF_MOTION_DEBUG) - ALERT(at_debug, "to %f %f %f\n", pTarget->pev->avelocity.x, pTarget->pev->avelocity.y, pTarget->pev->avelocity.z); - break; - case 1: - UTIL_StringToRandomVector( vecTemp, STRING(m_iszAVelocity) ); - if (pev->spawnflags & SF_MOTION_DEBUG) - ALERT(at_debug, "DEBUG: Set avelocity from %f %f %f ", pTarget->pev->avelocity.x, pTarget->pev->avelocity.y, pTarget->pev->avelocity.z); - pTarget->pev->avelocity = pTarget->pev->avelocity + vecTemp; - if (pev->spawnflags & SF_MOTION_DEBUG) - ALERT(at_debug, "to %f %f %f\n", pTarget->pev->avelocity.x, pTarget->pev->avelocity.y, pTarget->pev->avelocity.z); - break; - } - } -} - -//=========================================================== -//LRC- motion_manager -//=========================================================== -class CMotionThread : public CBaseEntity -{ -public: - void Think( void ); - - virtual int Save( CSave &save ); - virtual int Restore( CRestore &restore ); - static TYPEDESCRIPTION m_SaveData[]; - - int m_iszPosition; - int m_iPosMode; - int m_iszFacing; - int m_iFaceMode; - EHANDLE m_hLocus; - EHANDLE m_hTarget; -}; -LINK_ENTITY_TO_CLASS( motion_thread, CMotionThread ); - -TYPEDESCRIPTION CMotionThread::m_SaveData[] = -{ - DEFINE_FIELD( CMotionThread, m_iszPosition, FIELD_STRING ), - DEFINE_FIELD( CMotionThread, m_iPosMode, FIELD_INTEGER ), - DEFINE_FIELD( CMotionThread, m_iszFacing, FIELD_STRING ), - DEFINE_FIELD( CMotionThread, m_iFaceMode, FIELD_INTEGER ), - DEFINE_FIELD( CMotionThread, m_hLocus, FIELD_EHANDLE ), - DEFINE_FIELD( CMotionThread, m_hTarget, FIELD_EHANDLE ), -};IMPLEMENT_SAVERESTORE(CMotionThread, CBaseEntity); - -void CMotionThread::Think( void ) -{ - if (m_hLocus == NULL || m_hTarget == NULL) - { - if (pev->spawnflags & SF_MOTION_DEBUG) - ALERT(at_debug, "motion_thread expires\n"); - SetThink(&CMotionThread:: SUB_Remove ); - SetNextThink( 0.1 ); - return; - } - else - { - SetNextThink( 0 ); // think every frame - } - - if (pev->spawnflags & SF_MOTION_DEBUG) - ALERT(at_debug, "motion_thread affects %s \"%s\":\n", STRING(m_hTarget->pev->classname), STRING(m_hTarget->pev->targetname)); - - Vector vecTemp; - - if (m_iszPosition) - { - switch (m_iPosMode) - { - case 0: // set position - if (pev->spawnflags & SF_MOTION_DEBUG) - ALERT(at_debug, "DEBUG: Set origin from %f %f %f ", m_hTarget->pev->origin.x, m_hTarget->pev->origin.y, m_hTarget->pev->origin.z); - UTIL_AssignOrigin(m_hTarget, CalcLocus_Position( this, m_hLocus, STRING(m_iszPosition) )); - if (pev->spawnflags & SF_MOTION_DEBUG) - ALERT(at_debug, "to %f %f %f\n", m_hTarget->pev->origin.x, m_hTarget->pev->origin.y, m_hTarget->pev->origin.z); - m_hTarget->pev->flags &= ~FL_ONGROUND; - break; - case 1: // offset position (= fake velocity) - if (pev->spawnflags & SF_MOTION_DEBUG) - ALERT(at_debug, "DEBUG: Offset origin from %f %f %f ", m_hTarget->pev->origin.x, m_hTarget->pev->origin.y, m_hTarget->pev->origin.z); - UTIL_AssignOrigin(m_hTarget, m_hTarget->pev->origin + gpGlobals->frametime * CalcLocus_Velocity( this, m_hLocus, STRING(m_iszPosition) )); - if (pev->spawnflags & SF_MOTION_DEBUG) - ALERT(at_debug, "to %f %f %f\n", m_hTarget->pev->origin.x, m_hTarget->pev->origin.y, m_hTarget->pev->origin.z); - m_hTarget->pev->flags &= ~FL_ONGROUND; - break; - case 2: // set velocity - if (pev->spawnflags & SF_MOTION_DEBUG) - ALERT(at_debug, "DEBUG: Set velocity from %f %f %f ", m_hTarget->pev->velocity.x, m_hTarget->pev->velocity.y, m_hTarget->pev->velocity.z); - UTIL_SetVelocity(m_hTarget, CalcLocus_Velocity( this, m_hLocus, STRING(m_iszPosition) )); - if (pev->spawnflags & SF_MOTION_DEBUG) - ALERT(at_debug, "to %f %f %f\n", m_hTarget->pev->velocity.x, m_hTarget->pev->velocity.y, m_hTarget->pev->velocity.z); - break; - case 3: // accelerate - if (pev->spawnflags & SF_MOTION_DEBUG) - ALERT(at_debug, "DEBUG: Accelerate from %f %f %f ", m_hTarget->pev->velocity.x, m_hTarget->pev->velocity.y, m_hTarget->pev->velocity.z); - UTIL_SetVelocity(m_hTarget, m_hTarget->pev->velocity + gpGlobals->frametime * CalcLocus_Velocity( this, m_hLocus, STRING(m_iszPosition) )); - if (pev->spawnflags & SF_MOTION_DEBUG) - ALERT(at_debug, "to %f %f %f\n", m_hTarget->pev->velocity.x, m_hTarget->pev->velocity.y, m_hTarget->pev->velocity.z); - break; - case 4: // follow position - if (pev->spawnflags & SF_MOTION_DEBUG) - ALERT(at_debug, "DEBUG: Set velocity (path) from %f %f %f ", m_hTarget->pev->velocity.x, m_hTarget->pev->velocity.y, m_hTarget->pev->velocity.z); - UTIL_SetVelocity(m_hTarget, CalcLocus_Position( this, m_hLocus, STRING(m_iszPosition) ) - m_hTarget->pev->origin); - if (pev->spawnflags & SF_MOTION_DEBUG) - ALERT(at_debug, "to %f %f %f\n", m_hTarget->pev->velocity.x, m_hTarget->pev->velocity.y, m_hTarget->pev->velocity.z); - break; - } - } - - Vector vecVelAngles; - - if (m_iszFacing) - { - switch (m_iFaceMode) - { - case 0: // set angles - vecTemp = CalcLocus_Velocity( this, m_hLocus, STRING(m_iszFacing) ); - if (vecTemp != g_vecZero) // if the vector is 0 0 0, don't use it - { - if (pev->spawnflags & SF_MOTION_DEBUG) - ALERT(at_debug, "DEBUG: Set angles from %f %f %f ", m_hTarget->pev->angles.x, m_hTarget->pev->angles.y, m_hTarget->pev->angles.z); - UTIL_SetAngles(m_hTarget, UTIL_VecToAngles( vecTemp )); - if (pev->spawnflags & SF_MOTION_DEBUG) - ALERT(at_debug, "to %f %f %f\n", m_hTarget->pev->angles.x, m_hTarget->pev->angles.y, m_hTarget->pev->angles.z); - } - else if (pev->spawnflags & SF_MOTION_DEBUG) - { - ALERT(at_debug, "Zero velocity, don't change angles\n"); - } - break; - case 1: // offset angles (= fake avelocity) - vecTemp = CalcLocus_Velocity( this, m_hLocus, STRING(m_iszFacing) ); - if (vecTemp != g_vecZero) // if the vector is 0 0 0, don't use it - { - if (pev->spawnflags & SF_MOTION_DEBUG) - ALERT(at_debug, "DEBUG: Offset angles from %f %f %f ", m_hTarget->pev->angles.x, m_hTarget->pev->angles.y, m_hTarget->pev->angles.z); - UTIL_SetAngles(m_hTarget, m_hTarget->pev->angles + gpGlobals->frametime * UTIL_VecToAngles( vecTemp )); - if (pev->spawnflags & SF_MOTION_DEBUG) - ALERT(at_debug, "to %f %f %f\n", m_hTarget->pev->angles.x, m_hTarget->pev->angles.y, m_hTarget->pev->angles.z); - } - else if (pev->spawnflags & SF_MOTION_DEBUG) - { - ALERT(at_debug, "Zero velocity, don't change angles\n"); - } - break; - case 2: // offset angles (= fake avelocity) - UTIL_StringToRandomVector( vecVelAngles, STRING(m_iszFacing) ); - if (pev->spawnflags & SF_MOTION_DEBUG) - ALERT(at_debug, "DEBUG: Rotate angles from %f %f %f ", m_hTarget->pev->angles.x, m_hTarget->pev->angles.y, m_hTarget->pev->angles.z); - UTIL_SetAngles(m_hTarget, m_hTarget->pev->angles + gpGlobals->frametime * vecVelAngles); - if (pev->spawnflags & SF_MOTION_DEBUG) - ALERT(at_debug, "to %f %f %f\n", m_hTarget->pev->angles.x, m_hTarget->pev->angles.y, m_hTarget->pev->angles.z); - break; - case 3: // set avelocity - UTIL_StringToRandomVector( vecTemp, STRING(m_iszFacing) ); - if (pev->spawnflags & SF_MOTION_DEBUG) - ALERT(at_debug, "DEBUG: Set avelocity from %f %f %f ", m_hTarget->pev->avelocity.x, m_hTarget->pev->avelocity.y, m_hTarget->pev->avelocity.z); - UTIL_SetAvelocity(m_hTarget, vecTemp); - if (pev->spawnflags & SF_MOTION_DEBUG) - ALERT(at_debug, "to %f %f %f\n", m_hTarget->pev->avelocity.x, m_hTarget->pev->avelocity.y, m_hTarget->pev->avelocity.z); - break; - } - } -} - - -class CMotionManager : public CPointEntity -{ -public: - void Use( CBaseEntity *pActivator, CBaseEntity *pCaller, USE_TYPE useType, float value ); - void KeyValue( KeyValueData *pkvd ); - void Affect( CBaseEntity *pTarget, CBaseEntity *pActivator ); - void PostSpawn( void ); - - virtual int Save( CSave &save ); - virtual int Restore( CRestore &restore ); - static TYPEDESCRIPTION m_SaveData[]; - - int m_iszPosition; - int m_iPosMode; - int m_iszFacing; - int m_iFaceMode; -}; -LINK_ENTITY_TO_CLASS( motion_manager, CMotionManager ); - -TYPEDESCRIPTION CMotionManager::m_SaveData[] = -{ - DEFINE_FIELD( CMotionManager, m_iszPosition, FIELD_STRING ), - DEFINE_FIELD( CMotionManager, m_iPosMode, FIELD_INTEGER ), - DEFINE_FIELD( CMotionManager, m_iszFacing, FIELD_STRING ), - DEFINE_FIELD( CMotionManager, m_iFaceMode, FIELD_INTEGER ), -}; - -IMPLEMENT_SAVERESTORE(CMotionManager,CPointEntity); - -void CMotionManager::KeyValue( KeyValueData *pkvd ) -{ - if (FStrEq(pkvd->szKeyName, "m_iszPosition")) - { - m_iszPosition = ALLOC_STRING( pkvd->szValue ); - pkvd->fHandled = TRUE; - } - else if (FStrEq(pkvd->szKeyName, "m_iPosMode")) - { - m_iPosMode = atoi( pkvd->szValue ); - pkvd->fHandled = TRUE; - } - else if (FStrEq(pkvd->szKeyName, "m_iszFacing")) - { - m_iszFacing = ALLOC_STRING( pkvd->szValue ); - pkvd->fHandled = TRUE; - } - else if (FStrEq(pkvd->szKeyName, "m_iFaceMode")) - { - m_iFaceMode = atoi( pkvd->szValue ); - pkvd->fHandled = TRUE; - } - else - CPointEntity::KeyValue( pkvd ); -} - -void CMotionManager::PostSpawn( void ) -{ - if (FStringNull(pev->targetname)) - Use( this, this, USE_ON, 0 ); -} - -void CMotionManager::Use( CBaseEntity *pActivator, CBaseEntity *pCaller, USE_TYPE useType, float value ) -{ - CBaseEntity *pTarget = pActivator; - if (pev->target) - { - pTarget = UTIL_FindEntityByTargetname(NULL, STRING(pev->target), pActivator); - if (pTarget == NULL) - ALERT(at_error, "motion_manager \"%s\" can't find entity \"%s\" to affect\n", STRING(pev->targetname), STRING(pev->target)); - else - { - do - { - Affect( pTarget, pActivator ); - pTarget = UTIL_FindEntityByTargetname(pTarget, STRING(pev->target), pActivator); - } while ( pTarget ); - } - } -} - -void CMotionManager::Affect( CBaseEntity *pTarget, CBaseEntity *pActivator ) -{ - if ( pev->spawnflags & SF_MOTION_DEBUG ) - ALERT(at_debug, "DEBUG: Creating MotionThread for %s \"%s\"\n", STRING(pTarget->pev->classname), STRING(pTarget->pev->targetname)); - - CMotionThread *pThread = GetClassPtr( (CMotionThread*)NULL ); - if ( pThread == NULL ) return; //error? - pThread->pev->classname = MAKE_STRING( "motion_thread" ); // allow save\restore - pThread->m_hLocus = pActivator; - pThread->m_hTarget = pTarget; - pThread->m_iszPosition = m_iszPosition; - pThread->m_iPosMode = m_iPosMode; - pThread->m_iszFacing = m_iszFacing; - pThread->m_iFaceMode = m_iFaceMode; - pThread->pev->spawnflags = pev->spawnflags; - pThread->SetNextThink( 0 ); -} - - -// this is a really bad idea. -class CTriggerChangeTarget : public CBaseDelay -{ -public: - void KeyValue( KeyValueData *pkvd ); - void Spawn( void ); - void Use( CBaseEntity *pActivator, CBaseEntity *pCaller, USE_TYPE useType, float value ); - - int ObjectCaps( void ) { return CBaseDelay::ObjectCaps() & ~FCAP_ACROSS_TRANSITION; } - virtual int Save( CSave &save ); - virtual int Restore( CRestore &restore ); - - static TYPEDESCRIPTION m_SaveData[]; - -private: - int m_iszNewTarget; -}; -LINK_ENTITY_TO_CLASS( trigger_changetarget, CTriggerChangeTarget ); - -TYPEDESCRIPTION CTriggerChangeTarget::m_SaveData[] = -{ - DEFINE_FIELD( CTriggerChangeTarget, m_iszNewTarget, FIELD_STRING ), -}; - -IMPLEMENT_SAVERESTORE(CTriggerChangeTarget,CBaseDelay); - -void CTriggerChangeTarget::KeyValue( KeyValueData *pkvd ) -{ - if (FStrEq(pkvd->szKeyName, "m_iszNewTarget")) - { - m_iszNewTarget = ALLOC_STRING( pkvd->szValue ); - pkvd->fHandled = TRUE; - } - else - CBaseDelay::KeyValue( pkvd ); -} - -void CTriggerChangeTarget::Spawn( void ) -{ -} - - -void CTriggerChangeTarget::Use( CBaseEntity *pActivator, CBaseEntity *pCaller, USE_TYPE useType, float value ) -{ - CBaseEntity *pTarget = UTIL_FindEntityByTargetname( NULL, STRING( pev->target ), pActivator ); - - if (pTarget) - { - if (FStrEq(STRING(m_iszNewTarget), "*locus")) - { - if (pActivator) - pTarget->pev->target = pActivator->pev->targetname; - else - ALERT(at_error, "trigger_changetarget \"%s\" requires a locus!\n", STRING(pev->targetname)); - } - else - pTarget->pev->target = m_iszNewTarget; - CBaseMonster *pMonster = pTarget->MyMonsterPointer( ); - if (pMonster) - { - pMonster->m_pGoalEnt = NULL; - } - } -} - - -//LRC - you thought _that_ was a bad idea? Check this baby out... -class CTriggerChangeValue : public CBaseDelay -{ -public: - void KeyValue( KeyValueData *pkvd ); - void Use( CBaseEntity *pActivator, CBaseEntity *pCaller, USE_TYPE useType, float value ); - - int ObjectCaps( void ) { return CBaseDelay::ObjectCaps() & ~FCAP_ACROSS_TRANSITION; } - virtual int Save( CSave &save ); - virtual int Restore( CRestore &restore ); - - static TYPEDESCRIPTION m_SaveData[]; - -private: - int m_iszNewValue; -}; -LINK_ENTITY_TO_CLASS( trigger_changevalue, CTriggerChangeValue ); - -TYPEDESCRIPTION CTriggerChangeValue::m_SaveData[] = -{ - DEFINE_FIELD( CTriggerChangeValue, m_iszNewValue, FIELD_STRING ), -}; - -IMPLEMENT_SAVERESTORE(CTriggerChangeValue,CBaseDelay); - -void CTriggerChangeValue::KeyValue( KeyValueData *pkvd ) -{ - if (FStrEq(pkvd->szKeyName, "m_iszNewValue")) - { - m_iszNewValue = ALLOC_STRING( pkvd->szValue ); - pkvd->fHandled = TRUE; - } - else - CBaseDelay::KeyValue( pkvd ); -} - -void CTriggerChangeValue::Use( CBaseEntity *pActivator, CBaseEntity *pCaller, USE_TYPE useType, float value ) -{ - CBaseEntity *pTarget = UTIL_FindEntityByTargetname( NULL, STRING( pev->target ), pActivator ); - - if (pTarget) - { - KeyValueData mypkvd; - mypkvd.szKeyName = (char*)STRING(pev->netname); - mypkvd.szValue = (char*)STRING(m_iszNewValue); - mypkvd.fHandled = FALSE; - pTarget->KeyValue(&mypkvd); - //Error if not handled? - } -} - -//===================================================== -// trigger_command: activate a console command -//===================================================== -class CTriggerCommand : public CBaseEntity -{ -public: - void Use( CBaseEntity *pActivator, CBaseEntity *pCaller, USE_TYPE useType, float value ); - virtual int ObjectCaps( void ) { return CBaseEntity :: ObjectCaps() & ~FCAP_ACROSS_TRANSITION; } -}; -LINK_ENTITY_TO_CLASS( trigger_command, CTriggerCommand ); - -void CTriggerCommand::Use( CBaseEntity *pActivator, CBaseEntity *pCaller, USE_TYPE useType, float value ) -{ - char szCommand[256]; - - if (pev->netname) - { - sprintf( szCommand, "%s\n", STRING(pev->netname) ); - SERVER_COMMAND( szCommand ); - } -} - -//========================================================= -// trigger_changecvar: temporarily set a console variable -//========================================================= -#define SF_CVAR_ACTIVE 0x80000 - -class CTriggerChangeCVar : public CBaseEntity -{ -public: - void Use( CBaseEntity *pActivator, CBaseEntity *pCaller, USE_TYPE useType, float value ); - void EXPORT Think( void ); - virtual int Save( CSave &save ); - virtual int Restore( CRestore &restore ); - virtual int ObjectCaps( void ) { return CBaseEntity :: ObjectCaps() & ~FCAP_ACROSS_TRANSITION; } - - static TYPEDESCRIPTION m_SaveData[]; - - char m_szStoredString[256]; -}; -LINK_ENTITY_TO_CLASS( trigger_changecvar, CTriggerChangeCVar ); - -TYPEDESCRIPTION CTriggerChangeCVar::m_SaveData[] = -{ - DEFINE_ARRAY( CTriggerChangeCVar, m_szStoredString, FIELD_CHARACTER, 256 ), -}; - -IMPLEMENT_SAVERESTORE(CTriggerChangeCVar,CBaseEntity); - -void CTriggerChangeCVar::Use( CBaseEntity *pActivator, CBaseEntity *pCaller, USE_TYPE useType, float value ) -{ - char szCommand[256]; - - if (!(pev->netname)) return; - - if (ShouldToggle(useType, pev->spawnflags & SF_CVAR_ACTIVE)) - { - if (pev->spawnflags & SF_CVAR_ACTIVE) - { - sprintf( szCommand, "%s \"%s\"\n", STRING(pev->netname), m_szStoredString ); - pev->spawnflags &= ~SF_CVAR_ACTIVE; - } - else - { - strncpy(m_szStoredString, CVAR_GET_STRING(STRING(pev->netname)), 256); - sprintf( szCommand, "%s \"%s\"\n", STRING(pev->netname), STRING(pev->message) ); - pev->spawnflags |= SF_CVAR_ACTIVE; - - if (pev->armorvalue >= 0) - { - SetNextThink( pev->armorvalue ); - } - } - SERVER_COMMAND( szCommand ); - } -} - -void CTriggerChangeCVar::Think( void ) -{ - char szCommand[256]; - - if (pev->spawnflags & SF_CVAR_ACTIVE) - { - sprintf( szCommand, "%s %s\n", STRING(pev->netname), m_szStoredString ); - SERVER_COMMAND( szCommand ); - pev->spawnflags &= ~SF_CVAR_ACTIVE; - } -} - - - -#define SF_CAMERA_PLAYER_POSITION 1 -#define SF_CAMERA_PLAYER_TARGET 2 -#define SF_CAMERA_PLAYER_TAKECONTROL 4 -#define SF_CAMERA_NODRAWHUD 16 - -class CTriggerCamera : public CBaseDelay -{ -public: - void Spawn( void ); - void KeyValue( KeyValueData *pkvd ); - void Use( CBaseEntity *pActivator, CBaseEntity *pCaller, USE_TYPE useType, float value ); - void EXPORT FollowTarget( void ); - void Move(void); - - virtual int Save( CSave &save ); - virtual int Restore( CRestore &restore ); - virtual int ObjectCaps( void ) { return CBaseEntity :: ObjectCaps() & ~FCAP_ACROSS_TRANSITION; } - static TYPEDESCRIPTION m_SaveData[]; - - EHANDLE m_hPlayer; - EHANDLE m_hTarget; - CBaseEntity *m_pentPath; - int m_sPath; - float m_flWait; - float m_flReturnTime; - float m_flStopTime; - float m_moveDistance; - float m_targetSpeed; - float m_initialSpeed; - float m_acceleration; - float m_deceleration; - int m_state; - int m_iszViewEntity; - -}; -LINK_ENTITY_TO_CLASS( trigger_camera, CTriggerCamera ); - -// Global Savedata for changelevel friction modifier -TYPEDESCRIPTION CTriggerCamera::m_SaveData[] = -{ - DEFINE_FIELD( CTriggerCamera, m_hPlayer, FIELD_EHANDLE ), - DEFINE_FIELD( CTriggerCamera, m_hTarget, FIELD_EHANDLE ), - DEFINE_FIELD( CTriggerCamera, m_pentPath, FIELD_CLASSPTR ), - DEFINE_FIELD( CTriggerCamera, m_sPath, FIELD_STRING ), - DEFINE_FIELD( CTriggerCamera, m_flWait, FIELD_FLOAT ), - DEFINE_FIELD( CTriggerCamera, m_flReturnTime, FIELD_TIME ), - DEFINE_FIELD( CTriggerCamera, m_flStopTime, FIELD_TIME ), - DEFINE_FIELD( CTriggerCamera, m_moveDistance, FIELD_FLOAT ), - DEFINE_FIELD( CTriggerCamera, m_targetSpeed, FIELD_FLOAT ), - DEFINE_FIELD( CTriggerCamera, m_initialSpeed, FIELD_FLOAT ), - DEFINE_FIELD( CTriggerCamera, m_acceleration, FIELD_FLOAT ), - DEFINE_FIELD( CTriggerCamera, m_deceleration, FIELD_FLOAT ), - DEFINE_FIELD( CTriggerCamera, m_state, FIELD_INTEGER ), - DEFINE_FIELD( CTriggerCamera, m_iszViewEntity, FIELD_STRING ), -}; - -IMPLEMENT_SAVERESTORE(CTriggerCamera,CBaseDelay); - -void CTriggerCamera::Spawn( void ) -{ - // force camera to send on client - SetObjectClass( ED_NORMAL ); - - pev->movetype = MOVETYPE_NOCLIP; - pev->solid = SOLID_NOT; // Remove model & collisions - pev->renderamt = 0; // The engine won't draw this model if this is set to 0 and blending is on - pev->rendermode = kRenderTransTexture; - - m_initialSpeed = pev->speed; - if ( m_acceleration == 0 ) - m_acceleration = 500; - if ( m_deceleration == 0 ) - m_deceleration = 500; -} - - -void CTriggerCamera :: KeyValue( KeyValueData *pkvd ) -{ - if (FStrEq(pkvd->szKeyName, "wait")) - { - m_flWait = atof(pkvd->szValue); - pkvd->fHandled = TRUE; - } - else if (FStrEq(pkvd->szKeyName, "moveto")) - { - m_sPath = ALLOC_STRING( pkvd->szValue ); - pkvd->fHandled = TRUE; - } - else if (FStrEq(pkvd->szKeyName, "acceleration")) - { - m_acceleration = atof( pkvd->szValue ); - pkvd->fHandled = TRUE; - } - else if (FStrEq(pkvd->szKeyName, "deceleration")) - { - m_deceleration = atof( pkvd->szValue ); - pkvd->fHandled = TRUE; - } - else if (FStrEq(pkvd->szKeyName, "m_iszViewEntity")) - { - m_iszViewEntity = ALLOC_STRING( pkvd->szValue ); - pkvd->fHandled = TRUE; - } - else - CBaseDelay::KeyValue( pkvd ); -} - - - -void CTriggerCamera::Use( CBaseEntity *pActivator, CBaseEntity *pCaller, USE_TYPE useType, float value ) -{ - if ( !ShouldToggle( useType, m_state ) ) - return; - - // Toggle state - m_state = !m_state; - if (m_state == 0) - { - m_flReturnTime = gpGlobals->time; - return; - } - if ( !pActivator || !pActivator->IsPlayer() ) - { - pActivator = CBaseEntity::Instance(g_engfuncs.pfnPEntityOfEntIndex( 1 )); - } - - m_hPlayer = pActivator; - - if (m_flWait == -1)//G-Cont. if wait time = -1, set is 1E6 for retriggered only - m_flReturnTime = gpGlobals->time + 1E6; - else - m_flReturnTime = gpGlobals->time + m_flWait; - - pev->speed = m_initialSpeed; - m_targetSpeed = m_initialSpeed; - - if (FBitSet (pev->spawnflags, SF_CAMERA_PLAYER_TARGET ) ) - { - m_hTarget = m_hPlayer; - } - else - { - m_hTarget = GetNextTarget(); - } - - // Nothing to look at! - if ( m_hTarget == NULL ) - { - ALERT(at_debug, "Warning! Trigger Camera don't have target! Set target as player."); - m_hTarget = m_hPlayer;//G-Cont. if cam target don't specified - target is player. - //return; - } - - - if (FBitSet (pev->spawnflags, SF_CAMERA_PLAYER_TAKECONTROL ) ) - { - ((CBasePlayer *)pActivator)->EnableControl(FALSE); - } - - if ( m_sPath ) - { - m_pentPath = UTIL_FindEntityByTargetname( NULL, STRING(m_sPath) ); - } - else - { - m_pentPath = NULL; - } - - m_flStopTime = gpGlobals->time; - if ( m_pentPath ) - { - if ( m_pentPath->pev->speed != 0 ) - m_targetSpeed = m_pentPath->pev->speed; - - m_flStopTime += m_pentPath->GetDelay(); - } - - // copy over player information - if (FBitSet (pev->spawnflags, SF_CAMERA_PLAYER_POSITION ) ) - { - UTIL_SetOrigin( this, pActivator->pev->origin + pActivator->pev->view_ofs ); - pev->angles.x = -pActivator->pev->angles.x; - pev->angles.y = pActivator->pev->angles.y; - pev->angles.z = 0; - pev->velocity = pActivator->pev->velocity; - } - else - { - pev->velocity = Vector( 0, 0, 0 ); - } - - //LRC - if (m_iszViewEntity) - { - CBaseEntity *pEntity = UTIL_FindEntityByTargetname(NULL, STRING(m_iszViewEntity)); - if (pEntity) - { - int sendflags = 0; - sendflags |= CAMERA_ON; - if (pev->spawnflags & SF_CAMERA_NODRAWHUD) - sendflags &= ~DRAW_HUD; - else sendflags |= DRAW_HUD; - ((CBasePlayer *)pActivator)->viewEntity = m_iszViewEntity; - ((CBasePlayer *)pActivator)->viewFlags = sendflags; - ((CBasePlayer *)pActivator)->viewNeedsUpdate = 1; - } - } - else - { - int sendflags = 0; - sendflags |= CAMERA_ON; - if (pev->spawnflags & SF_CAMERA_NODRAWHUD) - sendflags &= ~DRAW_HUD; - else sendflags |= DRAW_HUD; - ((CBasePlayer *)pActivator)->viewEntity = pev->targetname; - ((CBasePlayer *)pActivator)->viewFlags = sendflags; - ((CBasePlayer *)pActivator)->viewNeedsUpdate = 1; - } - - SET_MODEL(ENT(pev), STRING(pActivator->pev->model) ); - - // follow the player down - SetThink(&CTriggerCamera:: FollowTarget ); - SetNextThink( 0 ); - - m_moveDistance = 0; - Move(); -} - - -void CTriggerCamera::FollowTarget( ) -{ - if (m_hPlayer == NULL) - return; - - if (m_hTarget == NULL || m_flReturnTime < gpGlobals->time) - { - if (m_hPlayer->IsAlive( )) - { - ((CBasePlayer *)((CBaseEntity *)m_hPlayer))->viewEntity = 0; - ((CBasePlayer *)((CBaseEntity *)m_hPlayer))->viewFlags = 0; - ((CBasePlayer *)((CBaseEntity *)m_hPlayer))->viewNeedsUpdate = 1; - ((CBasePlayer *)((CBaseEntity *)m_hPlayer))->EnableControl(TRUE); - } - SUB_UseTargets( this, USE_TOGGLE, 0 ); - pev->avelocity = Vector( 0, 0, 0 ); - m_state = 0; - return; - } - - Vector vecGoal = UTIL_VecToAngles( m_hTarget->pev->origin - pev->origin ); - vecGoal.x = -vecGoal.x; - - if (pev->angles.y > 360) - pev->angles.y -= 360; - - if (pev->angles.y < 0) - pev->angles.y += 360; - - float dx = vecGoal.x - pev->angles.x; - float dy = vecGoal.y - pev->angles.y; - - if (dx < -180) - dx += 360; - if (dx > 180) - dx = dx - 360; - - if (dy < -180) - dy += 360; - if (dy > 180) - dy = dy - 360; - - pev->avelocity.x = dx * 40 * gpGlobals->frametime; - pev->avelocity.y = dy * 40 * gpGlobals->frametime; - - - if (!(FBitSet (pev->spawnflags, SF_CAMERA_PLAYER_TAKECONTROL))) - { - pev->velocity = pev->velocity * 0.8; - if (pev->velocity.Length( ) < 10.0) //LRC- whyyyyyy??? - pev->velocity = g_vecZero; - } - - SetNextThink( 0 ); - - Move(); -} - -void CTriggerCamera::Move() -{ - // Not moving on a path, return - if (!m_pentPath) - return; - - // Subtract movement from the previous frame - m_moveDistance -= pev->speed * gpGlobals->frametime; - - // Have we moved enough to reach the target? - if ( m_moveDistance <= 0 ) - { - // Fire the passtarget if there is one - if ( m_pentPath->pev->message ) - { - FireTargets( STRING(m_pentPath->pev->message), this, this, USE_TOGGLE, 0 ); - if ( FBitSet( m_pentPath->pev->spawnflags, SF_CORNER_FIREONCE ) ) - m_pentPath->pev->message = 0; - } - // Time to go to the next target - m_pentPath = m_pentPath->GetNextTarget(); - - // Set up next corner - if ( !m_pentPath ) - { - pev->velocity = g_vecZero; - } - else - { - if ( m_pentPath->pev->speed != 0 ) - m_targetSpeed = m_pentPath->pev->speed; - - Vector delta = m_pentPath->pev->origin - pev->origin; - m_moveDistance = delta.Length(); - pev->movedir = delta.Normalize(); - m_flStopTime = gpGlobals->time + m_pentPath->GetDelay(); - } - } - - if ( m_flStopTime > gpGlobals->time ) - pev->speed = UTIL_Approach( 0, pev->speed, m_deceleration * gpGlobals->frametime ); - else - pev->speed = UTIL_Approach( m_targetSpeed, pev->speed, m_acceleration * gpGlobals->frametime ); - - float fraction = 2 * gpGlobals->frametime; - pev->velocity = ((pev->movedir * pev->speed) * fraction) + (pev->velocity * (1-fraction)); -} - diff --git a/spirit/weapons.h b/spirit/weapons.h deleted file mode 100644 index 965864ff..00000000 --- a/spirit/weapons.h +++ /dev/null @@ -1,507 +0,0 @@ -/*** -* -* Copyright (c) 1996-2002, 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. -* -****/ -#ifndef WEAPONS_H -#define WEAPONS_H - -#include "effects.h" - -class CBasePlayer; -extern int gmsgWeapPickup; - -void DeactivateSatchels( CBasePlayer *pOwner ); - -class CLaserSpot : public CBaseEntity -{ - void Spawn( void ); - void Precache( void ); - - int ObjectCaps( void ) { return FCAP_DONT_SAVE; } - -public: - void Suspend( float flSuspendTime ); - void EXPORT Revive( void ); - void UpdateOnRemove( void ); - - static CLaserSpot *CreateSpot( entvars_t *pevOwner = NULL ); - static CLaserSpot *CreateSpot( const char* spritename, entvars_t *pevOwner = NULL ); -}; - -// Contact Grenade / Timed grenade / Satchel Charge -class CGrenade : public CBaseMonster -{ -public: - void Spawn( void ); - - typedef enum { SATCHEL_DETONATE = 0, SATCHEL_RELEASE } SATCHELCODE; - - static CGrenade *ShootTimed( entvars_t *pevOwner, Vector vecStart, Vector vecVelocity, float time ); - static CGrenade *ShootContact( entvars_t *pevOwner, Vector vecStart, Vector vecVelocity ); - static CGrenade *ShootSatchelCharge( entvars_t *pevOwner, Vector vecStart, Vector vecVelocity ); - static void UseSatchelCharges( entvars_t *pevOwner, SATCHELCODE code ); - - void Explode( Vector vecSrc, Vector vecAim ); - void Explode( TraceResult *pTrace, int bitsDamageType ); - void EXPORT Smoke( void ); - - void EXPORT BounceTouch( CBaseEntity *pOther ); - void EXPORT SlideTouch( CBaseEntity *pOther ); - void EXPORT ExplodeTouch( CBaseEntity *pOther ); - void EXPORT DangerSoundThink( void ); - void EXPORT PreDetonate( void ); - void EXPORT Detonate( void ); - void EXPORT DetonateUse( CBaseEntity *pActivator, CBaseEntity *pCaller, USE_TYPE useType, float value ); - void EXPORT TumbleThink( void ); - - virtual void BounceSound( void ); - virtual int BloodColor( void ) { return DONT_BLEED; } - virtual void Killed( entvars_t *pevAttacker, int iGib ); - - BOOL m_fRegisteredSound;// whether or not this grenade has issued its DANGER sound to the world sound list yet. -}; - - -// constant items -#define ITEM_HEALTHKIT 1 -#define ITEM_ANTIDOTE 2 -#define ITEM_SECURITY 3 -#define ITEM_BATTERY 4 - -#define WEAPON_NONE 0 -#define WEAPON_CROWBAR 1 -#define WEAPON_GLOCK 2 -#define WEAPON_PYTHON 3 -#define WEAPON_GENERIC 4 -#define WEAPON_MP5 5 -#define WEAPON_DEBUG 6 //G-Cont. weapon for hunt bugs. he-he-he -#define WEAPON_CROSSBOW 7 -#define WEAPON_SHOTGUN 8 -#define WEAPON_RPG 9 -#define WEAPON_GAUSS 10 -#define WEAPON_EGON 11 -#define WEAPON_HORNETGUN 12 -#define WEAPON_HANDGRENADE 13 -#define WEAPON_TRIPMINE 14 -#define WEAPON_SATCHEL 15 -#define WEAPON_SNARK 16 - - -#define MAX_NORMAL_BATTERY 100 - - -// weapon weight factors (for auto-switching) (-1 = noswitch) -#define CROWBAR_WEIGHT 0 -#define GLOCK_WEIGHT 10 -#define PYTHON_WEIGHT 15 -#define MP5_WEIGHT 15 -#define SHOTGUN_WEIGHT 15 -#define CROSSBOW_WEIGHT 10 -#define RPG_WEIGHT 20 -#define GAUSS_WEIGHT 20 -#define EGON_WEIGHT 20 -#define HORNETGUN_WEIGHT 10 -#define HANDGRENADE_WEIGHT 5 -#define SNARK_WEIGHT 5 -#define SATCHEL_WEIGHT -10 -#define TRIPMINE_WEIGHT -10 - -// weapon clip/carry ammo capacities -#define URANIUM_MAX_CARRY 100 -#define _9MM_MAX_CARRY 250 -#define _357_MAX_CARRY 36 -#define BUCKSHOT_MAX_CARRY 125 -#define BOLT_MAX_CARRY 50 -#define ROCKET_MAX_CARRY 5 -#define HANDGRENADE_MAX_CARRY 10 -#define SATCHEL_MAX_CARRY 5 -#define TRIPMINE_MAX_CARRY 5 -#define SNARK_MAX_CARRY 15 -#define HORNET_MAX_CARRY 8 -#define M203_GRENADE_MAX_CARRY 10 - -// the maximum amount of ammo each weapon's clip can hold -#define WEAPON_NOCLIP -1 - -//#define CROWBAR_MAX_CLIP WEAPON_NOCLIP -#define GLOCK_MAX_CLIP 17 -#define PYTHON_MAX_CLIP 6 -#define MP5_MAX_CLIP 50 -#define MP5_DEFAULT_AMMO 25 -#define SHOTGUN_MAX_CLIP 8 -#define CROSSBOW_MAX_CLIP 5 -#define RPG_MAX_CLIP 1 -#define GAUSS_MAX_CLIP WEAPON_NOCLIP -#define EGON_MAX_CLIP WEAPON_NOCLIP -#define HORNETGUN_MAX_CLIP WEAPON_NOCLIP -#define HANDGRENADE_MAX_CLIP WEAPON_NOCLIP -#define SATCHEL_MAX_CLIP WEAPON_NOCLIP -#define TRIPMINE_MAX_CLIP WEAPON_NOCLIP -#define SNARK_MAX_CLIP WEAPON_NOCLIP - -// the default amount of ammo that comes with each gun when it spawns -#define GLOCK_DEFAULT_GIVE 17 -#define PYTHON_DEFAULT_GIVE 6 -#define MP5_DEFAULT_GIVE 25 -#define MP5_DEFAULT_AMMO 25 -#define MP5_M203_DEFAULT_GIVE 0 -#define SHOTGUN_DEFAULT_GIVE 12 -#define CROSSBOW_DEFAULT_GIVE 5 -#define RPG_DEFAULT_GIVE 1 -#define GAUSS_DEFAULT_GIVE 20 -#define EGON_DEFAULT_GIVE 20 -#define HANDGRENADE_DEFAULT_GIVE 5 -#define SATCHEL_DEFAULT_GIVE 1 -#define TRIPMINE_DEFAULT_GIVE 1 -#define SNARK_DEFAULT_GIVE 5 -#define HIVEHAND_DEFAULT_GIVE 8 - -// The amount of ammo given to a player by an ammo item. -#define AMMO_URANIUMBOX_GIVE 20 -#define AMMO_GLOCKCLIP_GIVE GLOCK_MAX_CLIP -#define AMMO_357BOX_GIVE PYTHON_MAX_CLIP -#define AMMO_MP5CLIP_GIVE MP5_MAX_CLIP -#define AMMO_CHAINBOX_GIVE 200 -#define AMMO_M203BOX_GIVE 2 -#define AMMO_BUCKSHOTBOX_GIVE 12 -#define AMMO_CROSSBOWCLIP_GIVE CROSSBOW_MAX_CLIP -#define AMMO_RPGCLIP_GIVE RPG_MAX_CLIP -#define AMMO_URANIUMBOX_GIVE 20 -#define AMMO_SNARKBOX_GIVE 5 - -// bullet types -typedef enum -{ - BULLET_NONE = 0, - BULLET_PLAYER_9MM, // glock - BULLET_PLAYER_MP5, // mp5 - BULLET_PLAYER_357, // python - BULLET_PLAYER_BUCKSHOT, // shotgun - BULLET_PLAYER_CROWBAR, // crowbar swipe - - BULLET_MONSTER_9MM, - BULLET_MONSTER_MP5, - BULLET_MONSTER_12MM, -} Bullet; - - -#define ITEM_FLAG_SELECTONEMPTY 1 -#define ITEM_FLAG_NOAUTORELOAD 2 -#define ITEM_FLAG_NOAUTOSWITCHEMPTY 4 -#define ITEM_FLAG_LIMITINWORLD 8 -#define ITEM_FLAG_EXHAUSTIBLE 16 // A player can totally exhaust their ammo supply and lose this weapon - -#define BARNEY_SUIT 0 // just in case -#define GORDON_SUIT 1 // gordon suit -#define NUM_HANDS 2 // number of hands: barney and gordon - -#define PLAYER_HAS_SUIT (m_pPlayer->pev->weapons & ITEM_SUIT) -#define PLAYER_DRAW_SUIT (pev->body & GORDON_SUIT) - -#define WEAPON_IS_ONTARGET 0x40 - -typedef struct -{ - int iSlot; - int iPosition; - const char *pszAmmo1; // ammo 1 type - int iMaxAmmo1; // max ammo 1 - const char *pszAmmo2; // ammo 2 type - int iMaxAmmo2; // max ammo 2 - const char *pszName; - int iMaxClip; - int iId; - int iFlags; - int iWeight;// this value used to determine this weapon's importance in autoselection. -} ItemInfo; - -typedef struct -{ - const char *pszName; - int iId; -} AmmoInfo; - -// Items that the player has in their inventory that they can use -class CBasePlayerItem : public CBaseAnimating -{ -public: - virtual void SetObjectCollisionBox( void ); - virtual void KeyValue( KeyValueData* pkvd); - - virtual int Save( CSave &save ); - virtual int Restore( CRestore &restore ); - - static TYPEDESCRIPTION m_SaveData[]; - - virtual int AddToPlayer( CBasePlayer *pPlayer ); // return TRUE if the item you want the item added to the player inventory - virtual int AddDuplicate( CBasePlayerItem *pItem ) { return FALSE; } // return TRUE if you want your duplicate removed from world - void EXPORT DestroyItem( void ); - void EXPORT DefaultTouch( CBaseEntity *pOther ); // default weapon touch - void EXPORT FallThink ( void );// when an item is first spawned, this think is run to determine when the object has hit the ground. - void EXPORT Materialize( void );// make a weapon visible and tangible - void EXPORT AttemptToMaterialize( void ); // the weapon desires to become visible and tangible, if the game rules allow for it - CBaseEntity* Respawn ( void );// copy a weapon - void FallInit( void ); - void CheckRespawn( void ); - virtual int GetItemInfo(ItemInfo *p) { return 0; }; // returns 0 if struct not filled out - virtual BOOL CanDeploy( void ) { return TRUE; }; - virtual BOOL Deploy( ) // returns is deploy was successful - { return TRUE; }; - - virtual BOOL CanHolster( void ) { return TRUE; };// can this weapon be put away right now? - virtual void Holster( ); - virtual void UpdateItemInfo( void ) { return; }; - - virtual void ItemPreFrame( void ) { return; } // called each frame by the player PreThink - virtual void ItemPostFrame( void ) { return; } // called each frame by the player PostThink - - virtual void Drop( void ); - virtual void Kill( void ); - virtual void AttachToPlayer ( CBasePlayer *pPlayer ); - - virtual int PrimaryAmmoIndex() { return -1; }; - virtual int SecondaryAmmoIndex() { return -1; }; - - virtual int UpdateClientData( CBasePlayer *pPlayer ) { return 0; } - - virtual CBasePlayerItem *GetWeaponPtr( void ) { return NULL; }; - - static ItemInfo ItemInfoArray[ MAX_WEAPONS ]; - static AmmoInfo AmmoInfoArray[ MAX_AMMO_SLOTS ]; - - string_t m_sMaster; //AJH for lockable weapons - - CBasePlayer *m_pPlayer; - CBasePlayerItem *m_pNext; - int m_iId; // WEAPON_??? - - virtual void Spawn(void); - - virtual int iItemSlot( void ) - { - ItemInfo II; - if(GetItemInfo(&II)) - return II.iSlot + 1; - else - return 0;// return 0 to MAX_ITEMS_SLOTS, used in hud - } - - int iItemPosition( void ) { return ItemInfoArray[ m_iId ].iPosition; } - const char *pszAmmo1( void ) { return ItemInfoArray[ m_iId ].pszAmmo1; } - int iMaxAmmo1( void ) { return ItemInfoArray[ m_iId ].iMaxAmmo1; } - const char *pszAmmo2( void ) { return ItemInfoArray[ m_iId ].pszAmmo2; } - int iMaxAmmo2( void ) { return ItemInfoArray[ m_iId ].iMaxAmmo2; } - const char *pszName( void ) { return ItemInfoArray[ m_iId ].pszName; } - int iMaxClip( void ) { return ItemInfoArray[ m_iId ].iMaxClip; } - int iWeight( void ) { return ItemInfoArray[ m_iId ].iWeight; } - int iFlags( void ) { return ItemInfoArray[ m_iId ].iFlags; } -}; - - -// inventory items that -class CBasePlayerWeapon : public CBasePlayerItem -{ -public: - virtual int Save( CSave &save ); - virtual int Restore( CRestore &restore ); - - static TYPEDESCRIPTION m_SaveData[]; - - virtual void SetNextThink( float delay ); //LRC - - // generic weapon versions of CBasePlayerItem calls - virtual int AddToPlayer( CBasePlayer *pPlayer ); - virtual int AddDuplicate( CBasePlayerItem *pItem ); - - virtual int ExtractAmmo( CBasePlayerWeapon *pWeapon ); //{ return TRUE; }; // Return TRUE if you can add ammo to yourself when picked up - virtual int ExtractClipAmmo( CBasePlayerWeapon *pWeapon );// { return TRUE; }; // Return TRUE if you can add ammo to yourself when picked up - - virtual int AddWeapon( void ) { ExtractAmmo( this ); return TRUE; }; // Return TRUE if you want to add yourself to the player - - // generic "shared" ammo handlers - BOOL AddPrimaryAmmo( int iCount, char *szName, int iMaxClip, int iMaxCarry ); - BOOL AddSecondaryAmmo( int iCount, char *szName, int iMaxCarry ); - - virtual void UpdateItemInfo( void ) {}; // updates HUD state - - int m_fFireOnEmpty; // True when the gun is empty and the player is still holding down the - // attack key(s) - BOOL PlayEmptySound( int iSoundType = 0 );//universal empty sound - virtual void ResetEmptySound( void ); - - virtual void SendWeaponAnim( int iAnim, float fps = 0 ); - - virtual BOOL CanDeploy( void ); - virtual BOOL IsUseable( void ); - BOOL DefaultDeploy( char *szViewModel, char *szWeaponModel, int iAnim, char *szAnimExt, float fDrawTime = 0.5 ); - int DefaultReload( int iClipSize, int iAnim, float fDelay ); - - virtual void ItemPostFrame( void ); // called each frame by the player PostThink - // called by CBasePlayerWeapons ItemPostFrame() - virtual void PrimaryAttack( void ) { return; } // do "+ATTACK" - virtual void SecondaryAttack( void ) { return; } // do "+ATTACK2" - virtual void Reload( void ) { return; } // do "+RELOAD" - virtual void WeaponIdle( void ) { return; } // called when no buttons pressed - virtual int UpdateClientData( CBasePlayer *pPlayer ); // sends hud info to client dll, if things have changed - virtual void RetireWeapon( void ); - virtual BOOL ShouldWeaponIdle( void ) {return FALSE; }; - virtual void Holster( void ); - virtual void RestoreBody ( void ); - - //LRC - used by weaponstrip - void DrainClip(CBasePlayer* pPlayer, BOOL keep, int i9mm, int i357, int iBuck, int iBolt, int iARGren, int iRock, int iUranium, int iSatchel, int iSnark, int iTrip, int iGren ); - - int PrimaryAmmoIndex(); - int SecondaryAmmoIndex(); - - void PrintState( void ); - - virtual CBasePlayerItem *GetWeaponPtr( void ) { return (CBasePlayerItem *)this; }; - - float m_flNextPrimaryAttack; // soonest time ItemPostFrame will call PrimaryAttack - float m_flNextSecondaryAttack; // soonest time ItemPostFrame will call SecondaryAttack - float m_flTimeWeaponIdle; // soonest time ItemPostFrame will call WeaponIdle - float m_flTimeUpdate; // special time for additional effects - float m_flChargeTime; // max charge time - used displacer and gauss - float m_flShockTime; // shock time - play sound effects. don't save - int m_iPrimaryAmmoType; // "primary" ammo index into players m_rgAmmo[] - int m_iSecondaryAmmoType; // "secondary" ammo index into players m_rgAmmo[] - int m_iClip; // number of shots left in the primary weapon clip, -1 it not used - int m_iClientClip; // the last version of m_iClip sent to hud dll - int m_iClientFov; // g-cont. just to right update crosshairs - int m_iClientWeaponState; // the last version of the weapon state sent to hud dll (is current weapon, is on target) - int m_fInReload; // Are we in the middle of a reload; - int m_iClipSize;//This required weapon_generic, defintion in same class will crash'es compile - int m_iChargeLevel;//level of energy charge - int m_iOverloadLevel;//level of overload weapon - int m_fInAttack;//attack type - - int m_iDefaultAmmo;// how much ammo you get when you pick up this weapon as placed by a level designer. - int m_iLastSkin; - int m_iBody; - int m_iLastBody; - BOOL b_Restored;//restore body and skin after save/load - BOOL AnimRestore;//restore sound and animation after save/load - BOOL m_iPlayEmptySound; -}; - - -class CBasePlayerAmmo : public CBasePlayerItem //AJH -{ -public: - virtual void Spawn( void ); - void EXPORT DefaultTouch( CBaseEntity *pOther ); // default weapon touch - virtual BOOL AddAmmo( CBaseEntity *pOther ) { return TRUE; }; - - CBaseEntity* Respawn( void ); - void EXPORT Materialize( void ); -}; - - -extern DLL_GLOBAL short g_sModelIndexLaser;// holds the index for the laser beam -extern DLL_GLOBAL const char *g_pModelNameLaser; - -extern DLL_GLOBAL short g_sModelIndexLaserDot;// holds the index for the laser beam dot -extern DLL_GLOBAL short g_sModelIndexFireball;// holds the index for the fireball -extern DLL_GLOBAL short g_sModelIndexSmoke;// holds the index for the smoke cloud -extern DLL_GLOBAL short g_sModelIndexWExplosion;// holds the index for the underwater explosion -extern DLL_GLOBAL short g_sModelIndexBubbles;// holds the index for the bubbles model -extern DLL_GLOBAL short g_sModelIndexBloodDrop;// holds the sprite index for blood drops -extern DLL_GLOBAL short g_sModelIndexBloodSpray;// holds the sprite index for blood spray (bigger) -extern DLL_GLOBAL short g_sModelIndexNullModel; //null model index -extern DLL_GLOBAL short g_sModelIndexErrorModel;//error model index -extern DLL_GLOBAL short g_sModelIndexNullSprite;//null sprite index -extern DLL_GLOBAL short g_sModelIndexErrorSprite;//error sprite index -extern DLL_GLOBAL short g_sSoundIndexNullSound;//null sound index -extern DLL_GLOBAL unsigned short g_usEventIndexNullEvent;//null event index -extern DLL_GLOBAL unsigned short m_usDecals; //Decal event - -extern void ClearMultiDamage(void); -extern void ApplyMultiDamage(entvars_t* pevInflictor, entvars_t* pevAttacker ); -extern void AddMultiDamage( entvars_t *pevInflictor, CBaseEntity *pEntity, float flDamage, int bitsDamageType); - -extern void DecalGunshot( TraceResult *pTrace, int iBulletType ); -extern void SpawnBlood(Vector vecSpot, int bloodColor, float flDamage); -extern int DamageDecal( CBaseEntity *pEntity, int bitsDamageType ); -extern void RadiusDamage( Vector vecSrc, entvars_t *pevInflictor, entvars_t *pevAttacker, float flDamage, float flRadius, int iClassIgnore, int bitsDamageType ); - -typedef struct -{ - CBaseEntity *pEntity; - float amount; - int type; -} MULTIDAMAGE; - -extern MULTIDAMAGE gMultiDamage; - - -#define LOUD_GUN_VOLUME 1000 -#define NORMAL_GUN_VOLUME 600 -#define QUIET_GUN_VOLUME 200 - -#define BRIGHT_GUN_FLASH 512 -#define NORMAL_GUN_FLASH 256 -#define DIM_GUN_FLASH 128 - -#define BIG_EXPLOSION_VOLUME 2048 -#define NORMAL_EXPLOSION_VOLUME 1024 -#define SMALL_EXPLOSION_VOLUME 512 - -#define WEAPON_ACTIVITY_VOLUME 64 - -#define VECTOR_CONE_1DEGREES Vector( 0.00873, 0.00873, 0.00873 ) -#define VECTOR_CONE_2DEGREES Vector( 0.01745, 0.01745, 0.01745 ) -#define VECTOR_CONE_3DEGREES Vector( 0.02618, 0.02618, 0.02618 ) -#define VECTOR_CONE_4DEGREES Vector( 0.03490, 0.03490, 0.03490 ) -#define VECTOR_CONE_5DEGREES Vector( 0.04362, 0.04362, 0.04362 ) -#define VECTOR_CONE_6DEGREES Vector( 0.05234, 0.05234, 0.05234 ) -#define VECTOR_CONE_7DEGREES Vector( 0.06105, 0.06105, 0.06105 ) -#define VECTOR_CONE_8DEGREES Vector( 0.06976, 0.06976, 0.06976 ) -#define VECTOR_CONE_9DEGREES Vector( 0.07846, 0.07846, 0.07846 ) -#define VECTOR_CONE_10DEGREES Vector( 0.08716, 0.08716, 0.08716 ) -#define VECTOR_CONE_15DEGREES Vector( 0.13053, 0.13053, 0.13053 ) -#define VECTOR_CONE_20DEGREES Vector( 0.17365, 0.17365, 0.17365 ) - -//========================================================= -// CWeaponBox - a single entity that can store weapons -// and ammo. -//========================================================= -class CWeaponBox : public CBaseEntity -{ - void Precache( void ); - void Spawn( void ); - void Touch( CBaseEntity *pOther ); - void KeyValue( KeyValueData *pkvd ); - BOOL IsEmpty( void ); - int GiveAmmo( int iCount, char *szName, int iMax, int *pIndex = NULL ); - void SetObjectCollisionBox( void ); - -public: - void EXPORT Kill ( void ); - int Save( CSave &save ); - int Restore( CRestore &restore ); - static TYPEDESCRIPTION m_SaveData[]; - - BOOL HasWeapon( CBasePlayerItem *pCheckItem ); - BOOL PackWeapon( CBasePlayerItem *pWeapon ); - BOOL PackAmmo( int iszName, int iCount ); - - CBasePlayerItem *m_rgpPlayerItems[MAX_ITEM_TYPES];// one slot for each - - int m_rgiszAmmo[MAX_AMMO_SLOTS];// ammo names - int m_rgAmmo[MAX_AMMO_SLOTS];// ammo quantities - - int m_cAmmoTypes;// how many ammo types packed into this box (if packed by a level designer) -}; - -#endif // WEAPONS_H diff --git a/vid_gl/r_backend.c b/vid_gl/r_backend.c index 44c2fa65..376c7ea0 100644 --- a/vid_gl/r_backend.c +++ b/vid_gl/r_backend.c @@ -3140,7 +3140,7 @@ void R_DrawEntitiesDebug( void ) continue; } - if( RI.currententity->ent_type == ED_VIEWMODEL ) + if( RI.currententity->ent_type == ET_VIEWENTITY ) { if( RI.params & RP_NONVIEWERREF ) continue; diff --git a/vid_gl/r_cull.c b/vid_gl/r_cull.c index 08d17400..dadcbf1c 100644 --- a/vid_gl/r_cull.c +++ b/vid_gl/r_cull.c @@ -225,14 +225,22 @@ R_CullModel */ int R_CullModel( ref_entity_t *e, vec3_t mins, vec3_t maxs, float radius ) { - if( e->ent_type == ED_VIEWMODEL ) + if( e->ent_type == ET_VIEWENTITY ) { if( RI.params & RP_NONVIEWERREF ) return 1; return 0; } - if( RP_LOCALCLIENT( e ) && !(RI.refdef.flags & RDF_THIRDPERSON)) + // don't reflect this entity in mirrors + if( e->flags & EF_NOREFLECT && RI.params & RP_MIRRORVIEW ) + return 1; + + // draw only in mirrors + if( e->flags & EF_REFLECTONLY && !( RI.params & RP_MIRRORVIEW )) + return 1; + + if( RP_LOCALCLIENT( e ) && !( RI.refdef.flags & RDF_THIRDPERSON )) { if(!( RI.params & ( RP_MIRRORVIEW|RP_SHADOWMAPVIEW ))) return 1; diff --git a/vid_gl/r_light.c b/vid_gl/r_light.c index 7c44bebb..92834c0a 100644 --- a/vid_gl/r_light.c +++ b/vid_gl/r_light.c @@ -265,7 +265,7 @@ void R_DrawCoronas( void ) float dist; ref_dlight_t *light; meshbuffer_t *mb; - trace_t tr; + pmtrace_t tr; if( r_dynamiclight->integer != 2 ) return; @@ -280,7 +280,7 @@ void R_DrawCoronas( void ) dist -= light->intensity; R_TraceLine( &tr, light->origin, RI.viewOrigin, FTRACE_IGNORE_GLASS ); - if( tr.flFraction != 1.0f ) + if( tr.fraction != 1.0f ) continue; mb = R_AddMeshToList( MB_CORONA, NULL, r_coronaShader, -((signed int)i + 1 )); diff --git a/vid_gl/r_local.h b/vid_gl/r_local.h index 7e2435f3..55608b42 100644 --- a/vid_gl/r_local.h +++ b/vid_gl/r_local.h @@ -26,7 +26,8 @@ Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. #include "qfiles_ref.h" #include "engine_api.h" #include "render_api.h" -#include "trace_def.h" +#include "pmtrace.h" +#include "entity_types.h" #if defined( _MSC_VER ) && ( _MSC_VER >= 1400 ) # define ALIGN(x) __declspec(align(16)) @@ -134,6 +135,7 @@ enum #define SHADOW_PLANAR 1 #define SHADOW_MAPPING 2 +#define LM_STYLES 4 // MAXLIGHTMAPS #define MAX_ENTITIES 2048 // per one frame #define MAX_POLY_VERTS 3000 @@ -202,12 +204,14 @@ typedef struct float white; // highest of RGB } lightstyle_t; +// FIXME: compress this as much as possible typedef struct ref_entity_s { uint ent_type; // entity type uint m_nCachedFrameCount;// keep current render frame int index; // viewmodel has entindex -1 refEntityType_t rtype; + bool doOcclusionTest; // check this entity for occlusion struct ref_model_s *model; // opaque type outside refresh struct ref_entity_s *parent; // link to parent entity (FOLLOW or weaponmodel) @@ -231,7 +235,7 @@ typedef struct ref_entity_s int rendermode; // hl1 rendermode int renderfx; // server will be translate hl1 values into flags int colormap; // q1 and hl1 model colormap (can applied for sprites) - int flags; // q1 effect flags, EF_ROTATE, EF_DIMLIGHT etc + int flags; // q1 effect flags, EF_LIGHT, EF_DIMLIGHT etc // client gait sequence (local stuff) int gaitsequence; // client->sequence + yaw @@ -506,7 +510,7 @@ enum && !((RI).params & RP_NONVIEWERREF) && !((RI).refdef.flags & RDF_NOWORLDMODEL) \ && OCCLUSION_QUERIES_CVAR_HACK( RI ) ) #define OCCLUSION_OPAQUE_SHADER( s ) (((s)->sort == SORT_OPAQUE ) && ((s)->flags & SHADER_DEPTHWRITE ) && !(s)->numDeforms ) -#define OCCLUSION_TEST_ENTITY( e ) (((e)->flags & EF_OCCLUSIONTEST) || ((e)->ent_type == ED_VIEWMODEL)) +#define OCCLUSION_TEST_ENTITY( e ) (((e)->doOcclusionTest ) || ((e)->ent_type == ET_VIEWENTITY )) void R_InitOcclusionQueries( void ); void R_BeginOcclusionPass( void ); @@ -629,7 +633,7 @@ void R_InitCustomColors( void ); void R_SetCustomColor( int num, int r, int g, int b ); int R_GetCustomColor( int num ); -msurface_t *R_TraceLine( trace_t *tr, const vec3_t start, const vec3_t end, int flags ); +msurface_t *R_TraceLine( pmtrace_t *tr, const vec3_t start, const vec3_t end, int flags ); // // r_mesh.c @@ -726,7 +730,7 @@ void R_PushDecal( const meshbuffer_t *mb ); // void R_PushPoly( const meshbuffer_t *mb ); void R_AddPolysToList( void ); -msurface_t *R_TransformedTraceLine( trace_t *tr, const vec3_t start, const vec3_t end, ref_entity_t *test, int flags ); +msurface_t *R_TransformedTraceLine( pmtrace_t *tr, const vec3_t start, const vec3_t end, ref_entity_t *test, int flags ); // // r_sprite.c @@ -752,7 +756,7 @@ void R_StudioRunEvents( ref_entity_t *e ); void R_StudioDrawHitbox( ref_entity_t *e, int iHitbox ); void R_StudioDrawDebug( void ); void R_StudioInit( void ); -bool R_StudioTrace( ref_entity_t *e, const vec3_t start, const vec3_t end, trace_t *tr ); +bool R_StudioTrace( ref_entity_t *e, const vec3_t start, const vec3_t end, pmtrace_t *tr ); void R_StudioAllocExtradata( struct cl_entity_s *in, ref_entity_t *e ); void R_StudioFreeAllExtradata( void ); void R_StudioShutdown( void ); diff --git a/vid_gl/r_main.c b/vid_gl/r_main.c index f8a1f201..b9616fa3 100644 --- a/vid_gl/r_main.c +++ b/vid_gl/r_main.c @@ -1243,7 +1243,7 @@ static void R_CategorizeEntities( void ) r_bmodelentities[r_numbmodelentities++] = RI.currententity; break; case mod_studio: - if(!( RI.currententity->flags & (EF_NOSHADOW|EF_PLANARSHADOW))) + if(!( RI.currententity->flags & EF_NOSHADOW )) R_AddShadowCaster( RI.currententity ); // build groups and mark shadow casters break; case mod_sprite: @@ -1424,7 +1424,7 @@ static void R_DrawNullEntities( void ) if( RI.params & RP_MIRRORVIEW ) { - if( RI.currententity->ent_type == ED_VIEWMODEL ) + if( RI.currententity->ent_type == ET_VIEWENTITY ) continue; } else @@ -1483,7 +1483,7 @@ R_RenderDebugSurface */ void R_RenderDebugSurface( void ) { - trace_t tr; + pmtrace_t tr; vec3_t forward; vec3_t start, end; @@ -1502,16 +1502,16 @@ void R_RenderDebugSurface( void ) r_debug_surface = R_TraceLine( &tr, start, end, 0 ); - if( tr.iHitgroup >= 0 && tr.pHit && r_drawentities->integer != 3 ) + if( tr.hitgroup >= 0 && tr.ent != -1 && r_drawentities->integer != 3 ) { pglDisable( GL_TEXTURE_2D ); GL_SetState( GLSTATE_NO_DEPTH_TEST|GLSTATE_SRCBLEND_SRC_ALPHA|GLSTATE_DSTBLEND_ONE_MINUS_SRC_ALPHA ); pglDepthRange( 0, 0 ); RI.previousentity = NULL; - RI.currententity = (ref_entity_t *)tr.pHit; + RI.currententity = &r_entities[tr.ent]; - R_StudioDrawHitbox( RI.currententity, tr.iHitgroup ); + R_StudioDrawHitbox( RI.currententity, tr.hitgroup ); pglDepthRange( gldepthmin, gldepthmax ); pglEnable( GL_TEXTURE_2D ); @@ -1519,7 +1519,7 @@ void R_RenderDebugSurface( void ) else if( r_debug_surface && r_debug_surface->mesh && !gl_wireframe->integer ) { RI.previousentity = NULL; - RI.currententity = (ref_entity_t *)tr.pHit; + RI.currententity = &r_entities[tr.ent]; R_ClearMeshList( RI.meshlist ); R_AddMeshToList( MB_MODEL, NULL, r_debug_surface->shader, r_debug_surface - r_worldbrushmodel->surfaces + 1 ); @@ -1940,7 +1940,7 @@ int R_ComputeFxBlend( ref_entity_t *e ) offset = ((int)e->index ) * 363.0f; // Use ent index to de-sync these fx - if( e->ent_type == ED_TEMPENTITY ) + if( e->ent_type == ET_TEMPENTITY ) { renderAmt = e->renderamt; } @@ -2168,7 +2168,7 @@ static bool R_WorldToScreen( const float *world, float *screen ) R_TraceLine ============= */ -msurface_t *R_TraceLine( trace_t *tr, const vec3_t start, const vec3_t end, int flags ) +msurface_t *R_TraceLine( pmtrace_t *tr, const vec3_t start, const vec3_t end, int flags ) { int i; msurface_t *surf; @@ -2180,7 +2180,7 @@ msurface_t *R_TraceLine( trace_t *tr, const vec3_t start, const vec3_t end, int // trace against bmodels for( i = 1; i < r_numEntities; i++ ) { - trace_t t2; + pmtrace_t t2; msurface_t *s2; e = &r_entities[i]; @@ -2196,7 +2196,7 @@ msurface_t *R_TraceLine( trace_t *tr, const vec3_t start, const vec3_t end, int continue; s2 = R_TransformedTraceLine( &t2, start, end, e, flags ); - if( t2.flFraction < tr->flFraction ) + if( t2.fraction < tr->fraction ) { *tr = t2; // closer impact point surf = s2; @@ -2306,7 +2306,7 @@ bool R_AddGenericEntity( cl_entity_t *pRefEntity, ref_entity_t *refent ) case mod_brush: if( !refent->model->extradata ) return false; - if( pRefEntity->curstate.skin != CONTENTS_NONE ) + if( pRefEntity->curstate.skin ) refent->waveHeight = refent->scale * 16.0f; refent->scale = 1.0f; // ignore scale for brush models refent->frame = pRefEntity->curstate.frame; // brush properly animating @@ -2323,35 +2323,22 @@ bool R_AddGenericEntity( cl_entity_t *pRefEntity, ref_entity_t *refent ) break; } - // calculate angles - if( refent->flags & EF_ROTATE ) - { - // some bonus items auto-rotate - VectorSet( refent->angles, 0, anglemod( RI.refdef.time / 10), 0 ); - } - else VectorCopy( pRefEntity->angles, refent->angles ); - + VectorCopy( pRefEntity->angles, refent->angles ); VectorCopy( pRefEntity->origin, refent->origin ); Matrix3x3_FromAngles( refent->angles, refent->axis ); VectorClear( refent->origin2 ); - if( refent->ent_type == ED_CLIENT ) + if( refent->ent_type == ET_PLAYER ) { refent->gaitsequence = pRefEntity->curstate.gaitsequence; - refent->flags |= EF_OCCLUSIONTEST; + refent->doOcclusionTest = true; refent->lightingOrigin[2] += refent->model->maxs[2] - 2; // drop shadow to floor } else refent->gaitsequence = 0; + VectorClear( refent->movedir ); - if( refent->ent_type == ED_MOVER || refent->ent_type == ED_BSPBRUSH ) - { - // FIXME: this is very-very temporary!!!! - VectorNormalize2( pRefEntity->curstate.vuser2, refent->movedir ); - } - else VectorClear( refent->movedir ); - - if( refent->ent_type == ED_VIEWMODEL ) + if( refent->ent_type == ET_VIEWENTITY ) { if( r_lefthand->integer == 1 ) VectorNegate( refent->axis[1], refent->axis[1] ); @@ -2367,23 +2354,6 @@ bool R_AddGenericEntity( cl_entity_t *pRefEntity, ref_entity_t *refent ) if( refent->extradata ) Mem_EmptyPool( refent->mempool ); refent->extradata = NULL; } - - // because entity without models never added to scene - if( !refent->ent_type ) - { - switch( refent->model->type ) - { - case mod_brush: - case mod_world: - refent->ent_type = ED_BSPBRUSH; - break; - case mod_studio: - case mod_sprite: - refent->ent_type = ED_NORMAL; - break; - // and ignore all other unset ents - } - } return true; } @@ -2396,19 +2366,12 @@ bool R_AddPortalEntity( cl_entity_t *pRefEntity, ref_entity_t *refent ) VectorCopy( pRefEntity->origin, refent->origin ); VectorCopy( pRefEntity->curstate.vuser1, refent->origin2 ); // FIXME: oldorigin - if( refent->flags & EF_ROTATE ) - { - float phase = pRefEntity->curstate.frame; - float speed = (pRefEntity->curstate.framerate ? pRefEntity->curstate.framerate : 50.0f); - refent->angles[ROLL] = 5 * com.sin(( phase + RI.refdef.time * speed * 0.01f ) * M_PI2); - } - // calculate angles Matrix3x3_FromAngles( refent->angles, refent->axis ); return true; } -bool R_AddEntityToScene( cl_entity_t *pRefEntity, int ed_type, shader_t customShader ) +bool R_AddEntityToScene( cl_entity_t *pRefEntity, int entityType, shader_t customShader ) { ref_entity_t *refent; ref_shader_t *shader; @@ -2423,26 +2386,9 @@ bool R_AddEntityToScene( cl_entity_t *pRefEntity, int ed_type, shader_t customSh } refent = &r_entities[r_numEntities]; - if( pRefEntity->curstate.effects & EF_NODRAW && ed_type != ED_PORTAL ) - return true; // done - if( pRefEntity->curstate.rendermode != kRenderNormal && pRefEntity->curstate.renderamt <= 0.0f ) return true; // done - // filter ents - switch( ed_type ) - { - case ED_MOVER: - case ED_PORTAL: - case ED_CLIENT: - case ED_NORMAL: - case ED_MONSTER: - case ED_BSPBRUSH: - case ED_RIGIDBODY: - case ED_VIEWMODEL: break; - default: return false; - } - // ignore env_sprite flares if supposed if( !r_spriteflares->integer && pRefEntity->curstate.rendermode == kRenderGlow ) { @@ -2453,7 +2399,7 @@ bool R_AddEntityToScene( cl_entity_t *pRefEntity, int ed_type, shader_t customSh // copy state to render refent->index = pRefEntity->index; - refent->ent_type = ed_type; + refent->ent_type = entityType; refent->rendermode = pRefEntity->curstate.rendermode; refent->rendercolor[0] = pRefEntity->curstate.rendercolor.r; refent->rendercolor[1] = pRefEntity->curstate.rendercolor.g; @@ -2468,11 +2414,12 @@ bool R_AddEntityToScene( cl_entity_t *pRefEntity, int ed_type, shader_t customSh refent->model = cl_models[pRefEntity->curstate.modelindex]; refent->movetype = pRefEntity->curstate.movetype; refent->framerate = pRefEntity->curstate.framerate; + refent->doOcclusionTest = false; refent->parent = NULL; refent->lerp = NULL; if( pRefEntity->curstate.rendermode == kRenderGlow && !pRefEntity->curstate.renderfx ) - refent->flags |= EF_OCCLUSIONTEST; + refent->doOcclusionTest = true; // setup custom shader if( customShader >= 0 && customShader < MAX_SHADERS && (shader = &r_shaders[customShader])) @@ -2482,9 +2429,9 @@ bool R_AddEntityToScene( cl_entity_t *pRefEntity, int ed_type, shader_t customSh refent->rtype = RT_MODEL; // setup rtype - switch( ed_type ) + switch( entityType ) { - case ED_PORTAL: + case ET_PORTAL: result = R_AddPortalEntity( pRefEntity, refent ); break; default: @@ -2496,7 +2443,7 @@ bool R_AddEntityToScene( cl_entity_t *pRefEntity, int ed_type, shader_t customSh refent->renderamt = R_ComputeFxBlend( refent ); - if( refent->flags & EF_MINLIGHT ) // FIXME: HACK + if( entityType == ET_VIEWENTITY ) { // run events here to prevent // de-synchronize muzzleflashes movement @@ -2514,7 +2461,7 @@ bool R_AddEntityToScene( cl_entity_t *pRefEntity, int ed_type, shader_t customSh FollowEntity.curstate.movetype = MOVETYPE_FOLLOW; FollowEntity.curstate.weaponmodel = 0; - if( R_AddEntityToScene( &FollowEntity, ED_NORMAL, customShader )) + if( R_AddEntityToScene( &FollowEntity, ET_NORMAL, customShader )) r_entities[r_numEntities-1].parent = refent; // set parent } return result; diff --git a/vid_gl/r_mesh.c b/vid_gl/r_mesh.c index 3a0df007..fdae9141 100644 --- a/vid_gl/r_mesh.c +++ b/vid_gl/r_mesh.c @@ -1167,6 +1167,7 @@ setup_and_render: NormalVectorToAxis( A[0], A ); // build portal_dest-to-world rotation matrix + // FIXME: movedir isn't used, portals are broken VectorCopy( ent->movedir, portal_plane->normal ); NormalVectorToAxis( portal_plane->normal, B ); Matrix3x3_Transpose( C, B ); diff --git a/vid_gl/r_model.c b/vid_gl/r_model.c index 63327891..57cf665a 100644 --- a/vid_gl/r_model.c +++ b/vid_gl/r_model.c @@ -2301,7 +2301,7 @@ R_LightContributionToPoint */ bool R_LightContributionToPoint( const light_t *light, const vec3_t origin, vec3_t color ) { - trace_t trace; + pmtrace_t trace; float add; add = 0; @@ -2321,7 +2321,7 @@ bool R_LightContributionToPoint( const light_t *light, const vec3_t origin, vec3 // test occlusion // clip the line, tracing from the surface towards the light R_TraceLine( &trace, origin, light->origin, FTRACE_IGNORE_GLASS ); - if( trace.flFraction != 1.0f ) return false; + if( trace.fraction != 1.0f ) return false; // calculate the contribution VectorSubtract( light->origin, origin, normal ); @@ -2368,7 +2368,7 @@ bool R_LightContributionToPoint( const light_t *light, const vec3_t origin, vec3 return false; // other light rays must not hit anything - if( trace.flFraction != 1.0f ) + if( trace.fraction != 1.0f ) return false; // add the result @@ -2733,7 +2733,7 @@ void R_BeginRegistration( const char *mapname ) r_worldent->scale = 1.0f; r_worldent->model = r_worldmodel; r_worldent->rtype = RT_MODEL; - r_worldent->ent_type = ED_NORMAL; + r_worldent->ent_type = ET_NORMAL; r_worldent->renderamt = 255; // i'm hope we don't want to see semisolid world :) Matrix3x3_LoadIdentity( r_worldent->axis ); Mod_UpdateShaders( r_worldmodel ); diff --git a/vid_gl/r_poly.c b/vid_gl/r_poly.c index 10c26ba6..16a14713 100644 --- a/vid_gl/r_poly.c +++ b/vid_gl/r_poly.c @@ -328,14 +328,14 @@ loc0: R_TraceLine ================= */ -msurface_t *R_TransformedTraceLine( trace_t *tr, const vec3_t start, const vec3_t end, ref_entity_t *test, int flags ) +msurface_t *R_TransformedTraceLine( pmtrace_t *tr, const vec3_t start, const vec3_t end, ref_entity_t *test, int flags ) { ref_model_t *model; r_fragmentframecount++; // for multi-check avoidance // fill in a default trace - Mem_Set( tr, 0, sizeof( trace_t )); + Mem_Set( tr, 0, sizeof( pmtrace_t )); trace_hitbox = -1; trace_surface = NULL; @@ -344,10 +344,10 @@ msurface_t *R_TransformedTraceLine( trace_t *tr, const vec3_t start, const vec3_ Mem_Set( &trace_plane, 0, sizeof( trace_plane )); // skip glass ents - if(( flags & FTRACE_IGNORE_GLASS ) && test->rendermode != kRenderNormal && test->renderamt < 255 ) + if(( flags & FTRACE_IGNORE_GLASS ) && test->rendermode != kRenderNormal && test->renderamt < 200 ) { - tr->flFraction = trace_fraction; - VectorCopy( trace_impact, tr->vecEndPos ); + tr->fraction = trace_fraction; + VectorCopy( trace_impact, tr->endpos ); return trace_surface; } @@ -414,14 +414,14 @@ msurface_t *R_TransformedTraceLine( trace_t *tr, const vec3_t start, const vec3_ R_TraceAgainstBmodel( bmodel ); else if( model->type == mod_studio ) { - trace_t tr; + pmtrace_t tr; if( R_StudioTrace( test, trace_start, trace_end, &tr )) { - VectorCopy( tr.vecEndPos, trace_impact ); - VectorCopy( tr.vecPlaneNormal, trace_plane.normal ); - trace_fraction = tr.flFraction; - trace_hitbox = tr.iHitgroup; + VectorCopy( tr.endpos, trace_impact ); + VectorCopy( tr.plane.normal, trace_plane.normal ); + trace_fraction = tr.fraction; + trace_hitbox = tr.hitgroup; } } } @@ -430,7 +430,7 @@ msurface_t *R_TransformedTraceLine( trace_t *tr, const vec3_t start, const vec3_ if( rotated && trace_fraction != 1 ) { Matrix3x3_Transpose( axis, test->axis ); - VectorCopy( tr->vecPlaneNormal, temp ); + VectorCopy( tr->plane.normal, temp ); Matrix3x3_Transform( axis, temp, trace_plane.normal ); } } @@ -443,14 +443,14 @@ msurface_t *R_TransformedTraceLine( trace_t *tr, const vec3_t start, const vec3_ trace_plane.dist = DotProduct( trace_plane.normal, trace_impact ); CategorizePlane( &trace_plane ); - tr->flPlaneDist = trace_plane.dist; - VectorCopy( trace_plane.normal, tr->vecPlaneNormal ); - tr->pHit = (edict_t *)test; + tr->plane.dist = trace_plane.dist; + VectorCopy( trace_plane.normal, tr->plane.normal ); + tr->ent = test - r_entities; } - tr->iHitgroup = trace_hitbox; - tr->flFraction = trace_fraction; - VectorCopy( trace_impact, tr->vecEndPos ); + tr->hitgroup = trace_hitbox; + tr->fraction = trace_fraction; + VectorCopy( trace_impact, tr->endpos ); return trace_surface; } diff --git a/vid_gl/r_shadow.c b/vid_gl/r_shadow.c index fccfa495..dd0f3b21 100644 --- a/vid_gl/r_shadow.c +++ b/vid_gl/r_shadow.c @@ -58,7 +58,7 @@ ref_shader_t *R_PlanarShadowShader( void ) R_GetShadowImpactAndDir =============== */ -static void R_GetShadowImpactAndDir( ref_entity_t *e, trace_t *tr, vec3_t lightdir ) +static void R_GetShadowImpactAndDir( ref_entity_t *e, pmtrace_t *tr, vec3_t lightdir ) { vec3_t point; @@ -81,30 +81,30 @@ bool R_CullPlanarShadow( ref_entity_t *e, vec3_t mins, vec3_t maxs, bool occlusi float planedist, dist; vec3_t lightdir, point; vec3_t bbox[8], newmins, newmaxs; - trace_t tr; + pmtrace_t tr; int i; - if((e->flags & EF_NOSHADOW) || (e->ent_type == ED_VIEWMODEL)) + if(( e->flags & EF_NOSHADOW ) || ( e->ent_type == ET_VIEWENTITY )) return true; if( RP_LOCALCLIENT( e )) return false; R_GetShadowImpactAndDir( e, &tr, lightdir ); - if( tr.flFraction == 1.0f ) + if( tr.fraction == 1.0f ) return true; R_TransformEntityBBox( e, mins, maxs, bbox, true ); - VectorSubtract( tr.vecEndPos, e->origin, point ); - planedist = DotProduct( point, tr.vecPlaneNormal ) + 1; - dist = -1.0f / DotProduct( lightdir, tr.vecPlaneNormal ); + VectorSubtract( tr.endpos, e->origin, point ); + planedist = DotProduct( point, tr.plane.normal ) + 1; + dist = -1.0f / DotProduct( lightdir, tr.plane.normal ); VectorScale( lightdir, dist, lightdir ); ClearBounds( newmins, newmaxs ); for( i = 0; i < 8; i++ ) { VectorSubtract( bbox[i], e->origin, bbox[i] ); - dist = DotProduct( bbox[i], tr.vecPlaneNormal ) - planedist; + dist = DotProduct( bbox[i], tr.plane.normal ) - planedist; if( dist > 0 ) VectorMA( bbox[i], dist, lightdir, bbox[i] ); AddPointToBounds( bbox[i], newmins, newmaxs ); } @@ -132,16 +132,16 @@ void R_DeformVPlanarShadow( int numV, float *v ) float planedist, dist; ref_entity_t *e = RI.currententity; vec3_t planenormal, lightdir, lightdir2, point; - trace_t tr; + pmtrace_t tr; R_GetShadowImpactAndDir( e, &tr, lightdir ); Matrix3x3_Transform( e->axis, lightdir, lightdir2 ); - Matrix3x3_Transform( e->axis, tr.vecPlaneNormal, planenormal ); + Matrix3x3_Transform( e->axis, tr.plane.normal, planenormal ); VectorScale( planenormal, e->scale, planenormal ); - VectorSubtract( tr.vecEndPos, e->origin, point ); - planedist = DotProduct( point, tr.vecPlaneNormal ) + 1; + VectorSubtract( tr.endpos, e->origin, point ); + planedist = DotProduct( point, tr.plane.normal ) + 1; dist = -1.0f / DotProduct( lightdir2, planenormal ); VectorScale( lightdir2, dist, lightdir2 ); @@ -301,7 +301,7 @@ add: } r_entShadowBits[ent - r_entities] |= group->bit; - if( ent->ent_type == ED_VIEWMODEL ) + if( ent->ent_type == ET_VIEWENTITY ) return true; // rotate local bounding box and compute the full bounding box for this group diff --git a/vid_gl/r_sprite.c b/vid_gl/r_sprite.c index 95bb4fbf..a45e9a04 100644 --- a/vid_gl/r_sprite.c +++ b/vid_gl/r_sprite.c @@ -655,14 +655,14 @@ static float R_GlowSightDistance( vec3_t glowOrigin ) { float dist; vec3_t glowDist; - trace_t tr; + pmtrace_t tr; VectorSubtract( glowOrigin, RI.viewOrigin, glowDist ); dist = VectorLength( glowDist ); R_TraceLine( &tr, RI.viewOrigin, glowOrigin, FTRACE_IGNORE_GLASS|FTRACE_SIMPLEBOX ); - if(( 1.0 - tr.flFraction ) * dist > 8 ) + if(( 1.0f - tr.fraction ) * dist > 8 ) return -1; return dist; } @@ -767,9 +767,9 @@ void R_DrawSpriteModel( const meshbuffer_t *mb ) // do movewith if( e->parent && e->movetype == MOVETYPE_FOLLOW ) { - if(( e->colormap & 0xFF ) > 0 && e->parent->model && e->parent->model->type == mod_studio ) + if( e->body > 0 && e->parent->model && e->parent->model->type == mod_studio ) { - int num = bound( 1, (e->colormap & 0xFF), MAXSTUDIOATTACHMENTS ); + int num = bound( 1, e->body, MAXSTUDIOATTACHMENTS ); // pev->colormap is hardcoded to attachment number // NOTE: use interpolated origin to avoid flickering attachments @@ -833,7 +833,7 @@ bool R_CullSpriteModel( ref_entity_t *e ) if( !e->model->extradata ) return true; - if( e->ent_type == ED_VIEWMODEL && r_lefthand->integer >= 2 ) + if( e->ent_type == ET_VIEWENTITY && r_lefthand->integer >= 2 ) return true; VectorCopy( e->model->mins, sprite_mins ); diff --git a/vid_gl/r_studio.c b/vid_gl/r_studio.c index d613cdf7..fc28f32f 100644 --- a/vid_gl/r_studio.c +++ b/vid_gl/r_studio.c @@ -13,6 +13,7 @@ #include "const.h" #define DIST_EPSILON (0.03125) +#define EVENT_CLIENT 5000 // less than this value it's a server-side studio events /* ============================================================= @@ -47,7 +48,7 @@ player_info_t *m_pPlayerInfo; studiohdr_t *m_pStudioHeader; studiohdr_t *m_pTextureHeader; mplane_t studio_planes[12]; -trace_t studio_trace; +pmtrace_t studio_trace; vec3_t studio_mins, studio_maxs; float studio_radius; @@ -217,7 +218,7 @@ static void R_StudioSetupRender( ref_entity_t *e, ref_model_t *mod ) m_pchromeright = ((studiovars_t *)e->extradata)->chromeright; // misc info - if( e->ent_type == ED_VIEWMODEL ) + if( e->ent_type == ET_VIEWENTITY ) { // viewmodel can't properly animate without lerping m_fDoInterp = true; @@ -342,27 +343,27 @@ bool R_StudioTraceBox( vec3_t start, vec3_t end ) if( !startout ) { // original point was inside brush - if( !getout ) studio_trace.flFraction = 0.0f; + if( !getout ) studio_trace.fraction = 0.0f; return true; } if( enterFrac < leaveFrac ) { - if( enterFrac > -1 && enterFrac < studio_trace.flFraction ) + if( enterFrac > -1 && enterFrac < studio_trace.fraction ) { if( enterFrac < 0.0f ) enterFrac = 0.0f; - studio_trace.flFraction = enterFrac; - VectorCopy( clipplane->normal, studio_trace.vecPlaneNormal ); - studio_trace.flPlaneDist = clipplane->dist; + studio_trace.fraction = enterFrac; + VectorCopy( clipplane->normal, studio_trace.plane.normal ); + studio_trace.plane.dist = clipplane->dist; return true; } } return false; } -bool R_StudioTrace( ref_entity_t *e, const vec3_t start, const vec3_t end, trace_t *tr ) +bool R_StudioTrace( ref_entity_t *e, const vec3_t start, const vec3_t end, pmtrace_t *tr ) { matrix4x4 m; vec3_t start_l, end_l; @@ -372,17 +373,17 @@ bool R_StudioTrace( ref_entity_t *e, const vec3_t start, const vec3_t end, trace if( !m_pStudioHeader->numhitboxes ) { - tr->iHitgroup = -1; + tr->hitgroup = -1; return false; } // NOTE: we don't need to setup bones because // it's already setup by rendering code - Mem_Set( &studio_trace, 0, sizeof( trace_t )); - VectorCopy( end, studio_trace.vecEndPos ); - studio_trace.fAllSolid = true; - studio_trace.flFraction = 1.0f; - studio_trace.iHitgroup = -1; + Mem_Set( &studio_trace, 0, sizeof( pmtrace_t )); + VectorCopy( end, studio_trace.endpos ); + studio_trace.allsolid = true; + studio_trace.fraction = 1.0f; + studio_trace.hitgroup = -1; outBone = -1; for( i = 0; i < m_pStudioHeader->numhitboxes; i++ ) @@ -398,38 +399,38 @@ bool R_StudioTrace( ref_entity_t *e, const vec3_t start, const vec3_t end, trace if( R_StudioTraceBox( start_l, end_l )) { outBone = phitbox->bone; - studio_trace.iHitgroup = i; // not a hitgroup! + studio_trace.hitgroup = i; // it's a hitbox, not a hitgroup! } - if( studio_trace.flFraction == 0.0f ) + if( studio_trace.fraction == 0.0f ) break; } - if( studio_trace.flFraction > 0.0f ) - studio_trace.fAllSolid = false; + if( studio_trace.fraction > 0.0f ) + studio_trace.allsolid = false; // all hitboxes were swept, get trace result if( outBone >= 0 ) { - tr->flFraction = studio_trace.flFraction; - tr->iHitgroup = studio_trace.iHitgroup; - tr->fAllSolid = studio_trace.fAllSolid; - tr->pHit = (edict_t *)e; + tr->fraction = studio_trace.fraction; + tr->hitgroup = studio_trace.hitgroup; + tr->allsolid = studio_trace.allsolid; + tr->ent = e - r_entities; - Matrix4x4_VectorRotate( m_pbonestransform[outBone], studio_trace.vecEndPos, tr->vecEndPos ); - if( tr->flFraction == 1.0f ) + Matrix4x4_VectorRotate( m_pbonestransform[outBone], studio_trace.endpos, tr->endpos ); + if( tr->fraction == 1.0f ) { - VectorCopy( end, tr->vecEndPos ); + VectorCopy( end, tr->endpos ); } else { mstudiobone_t *pbone = (mstudiobone_t *)((byte*)m_pStudioHeader + m_pStudioHeader->boneindex) + outBone; // MsgDev( D_INFO, "Bone name %s\n", pbone->name ); // debug - VectorLerp( start, tr->flFraction, end, tr->vecEndPos ); + VectorLerp( start, tr->fraction, end, tr->endpos ); r_debug_hitbox = pbone->name; } - tr->flPlaneDist = DotProduct( tr->vecEndPos, tr->vecPlaneNormal ); + tr->plane.dist = DotProduct( tr->endpos, tr->plane.normal ); return true; } @@ -606,7 +607,7 @@ static int R_StudioLoadTextures( ref_model_t *mod, studiohdr_t *phdr ) R_ShaderAddStageTexture( texs[3] ); load_shader: mod->shaders[numshaders] = R_LoadShader( shadername, SHADER_STUDIO, 0, 0, SHADER_INVALID ); - ptexture[numshaders].shader = mod->shaders[numshaders]->shadernum; + ptexture[numshaders].index = mod->shaders[numshaders]->shadernum; numshaders++; } return numshaders; @@ -689,8 +690,8 @@ void R_StudioProcessEvents( ref_entity_t *e, cl_entity_t *ent ) switch( e->ent_type ) { - case ED_VIEWMODEL: - case ED_TEMPENTITY: + case ET_VIEWENTITY: + case ET_TEMPENTITY: break; default: return; } @@ -1220,15 +1221,15 @@ void R_StudioSetUpTransform( ref_entity_t *e, bool trivial_accept ) } // don't rotate clients, only aim - if( e->ent_type == ED_CLIENT ) + if( e->ent_type == ET_PLAYER ) angles[PITCH] = 0; - if( e->ent_type == ED_VIEWMODEL ) + if( e->ent_type == ET_VIEWENTITY ) angles[PITCH] = -angles[PITCH]; // stupid Half-Life bug Matrix4x4_CreateFromEntity( m_protationmatrix, origin[0], origin[1], origin[2], -angles[PITCH], angles[YAW], angles[ROLL], e->scale ); - if( e->ent_type == ED_VIEWMODEL && r_lefthand->integer == 1 ) + if( e->ent_type == ET_VIEWENTITY && r_lefthand->integer == 1 ) { m_protationmatrix[0][1] = -m_protationmatrix[0][1]; m_protationmatrix[1][1] = -m_protationmatrix[1][1]; @@ -1675,7 +1676,7 @@ bool R_StudioComputeBBox( vec3_t bbox[8] ) // rotate the bounding box VectorScale( e->angles, -1, angles ); - if( e->ent_type == ED_CLIENT || e->ent_type == ED_MONSTER ) + if( e->ent_type == ET_PLAYER ) angles[PITCH] = 0; // don't rotate player model, only aim AngleVectorsFLU( angles, vectors[0], vectors[1], vectors[2] ); @@ -2015,7 +2016,7 @@ void R_StudioDrawHulls( void ) vec3_t bbox[8]; // looks ugly, skip - if( RI.currententity->ent_type == ED_VIEWMODEL ) + if( RI.currententity->ent_type == ET_VIEWENTITY ) return; if(!R_StudioComputeBBox( bbox )) return; @@ -2090,7 +2091,7 @@ void R_StudioEstimateGait( ref_entity_t *e, entity_state_t *pplayer ) ASSERT( pstudio ); dt = bound( 0.0f, RI.refdef.frametime, 1.0f ); - if( dt == 0 || e->m_nCachedFrameCount == r_framecount2 ) + if( dt == 0 || m_pPlayerInfo->renderframe == r_framecount2 ) { m_flGaitMovement = 0; return; @@ -2156,8 +2157,9 @@ void R_StudioProcessGait( ref_entity_t *e, entity_state_t *pplayer, studiovars_t e->lerp->curstate.sequence = 0; pseqdesc = (mstudioseqdesc_t *)((byte *)m_pStudioHeader + m_pStudioHeader->seqindex) + e->lerp->curstate.sequence; - R_StudioPlayerBlend( pseqdesc, &iBlend, &e->angles[PITCH] ); + R_StudioPlayerBlend( pseqdesc, &iBlend, &e->lerp->angles[PITCH] ); + pstudio->lerp->latched.prevangles[PITCH] = e->lerp->angles[PITCH]; pstudio->lerp->curstate.blending[0] = iBlend; pstudio->lerp->latched.prevblending[0] = pstudio->lerp->curstate.blending[0]; pstudio->lerp->latched.prevseqblending[0] = pstudio->lerp->curstate.blending[0]; @@ -2171,7 +2173,7 @@ void R_StudioProcessGait( ref_entity_t *e, entity_state_t *pplayer, studiovars_t //Msg( "%f %f\n", e->angles[YAW], pstudio->lerp->gaityaw ); // calc side to side turning - flYaw = e->lerp->angles[YAW] - m_pPlayerInfo->gaityaw; + flYaw = e->angles[YAW] - m_pPlayerInfo->gaityaw; flYaw = flYaw - (int)(flYaw / 360) * 360; if( flYaw < -180 ) flYaw = flYaw + 360; if( flYaw > 180 ) flYaw = flYaw - 360; @@ -2234,7 +2236,7 @@ static bool R_StudioSetupModel( ref_entity_t *e, ref_model_t *mod ) ASSERT( pstudio ); // special handle for player model - if( e->ent_type == ED_CLIENT || e->renderfx == kRenderFxDeadPlayer ) + if( e->ent_type == ET_PLAYER || e->renderfx == kRenderFxDeadPlayer ) { //Msg( "DrawPlayer %d\n", pstudio->lerp->blending[0] ); //Msg( "DrawPlayer %d %d (%d)\n", r_framecount2, m_pEntity->serialnumber, e->lerp->curstate.sequence ); @@ -2252,23 +2254,28 @@ static bool R_StudioSetupModel( ref_entity_t *e, ref_model_t *mod ) if( m_nPlayerIndex < 0 || m_nPlayerIndex >= ri.GetMaxClients( )) return 0; // weird client ? - if( m_pPlayerInfo->gaitsequence <= 0 ) + if( e->gaitsequence ) { - for( i = 0; i < 4; i++ ) // clear torso controllers + vec3_t orig_angles; + + VectorCopy( e->angles, orig_angles ); + + R_StudioProcessGait( e, &e->lerp->curstate, pstudio ); + m_pPlayerInfo->gaitsequence = e->gaitsequence; +// m_pPlayerInfo = NULL; + + R_StudioSetUpTransform ( e, false ); + VectorCopy( orig_angles, e->angles ); + } + else + { + for( i = 0; i < 4; i++ ) // clear torso controllers pstudio->lerp->latched.prevcontroller[i] = pstudio->lerp->curstate.controller[i] = 0x7F; m_pPlayerInfo->gaitsequence = 0; // StudioSetupBones() issuses R_StudioSetUpTransform ( e, false ); } - else - { - vec3_t save_angles; - VectorCopy( e->angles, save_angles ); - R_StudioProcessGait( e, &e->lerp->curstate, pstudio ); - R_StudioSetUpTransform ( e, false ); - VectorCopy( save_angles, e->angles ); - } if( r_himodels->integer ) e->body = 255; // show highest resolution multiplayer model if( !( glw_state.developer == 0 && ri.GetMaxClients() == 1 )) e->body = 1; // force helmet @@ -2291,7 +2298,9 @@ static bool R_StudioSetupModel( ref_entity_t *e, ref_model_t *mod ) R_StudioCalcAttachments( e ); R_StudioProcessEvents( e, e->lerp ); } + e->m_nCachedFrameCount = r_framecount2; // cached frame + if( m_pPlayerInfo ) m_pPlayerInfo->renderframe = r_framecount2; return 1; } @@ -2423,7 +2432,7 @@ void R_DrawStudioModel( const meshbuffer_t *mb ) } // hack the depth range to prevent view model from poking into walls - if( e->ent_type == ED_VIEWMODEL ) + if( e->ent_type == ET_VIEWENTITY ) { pglDepthRange( gldepthmin, gldepthmin + 0.3 * ( gldepthmax - gldepthmin ) ); @@ -2433,7 +2442,7 @@ void R_DrawStudioModel( const meshbuffer_t *mb ) R_StudioDrawPoints( mb, e ); - if( e->ent_type == ED_VIEWMODEL ) + if( e->ent_type == ET_VIEWENTITY ) { pglDepthRange( gldepthmin, gldepthmax ); @@ -2455,7 +2464,7 @@ bool R_CullStudioModel( ref_entity_t *e ) if( !e->model->extradata ) return true; - if( e->ent_type == ED_VIEWMODEL && r_lefthand->integer >= 2 ) + if( e->ent_type == ET_VIEWENTITY && r_lefthand->integer >= 2 ) return true; // hidden modhandle = Mod_Handle( e->model ); @@ -2482,7 +2491,7 @@ bool R_CullStudioModel( ref_entity_t *e ) query = OCCLUSION_QUERIES_ENABLED( RI ) && OCCLUSION_TEST_ENTITY( e ) ? true : false; if( !frustum && query ) R_IssueOcclusionQuery( R_GetOcclusionQueryNum( OQ_ENTITY, e - r_entities ), e, studio_mins, studio_maxs ); - if(( RI.refdef.flags & RDF_NOWORLDMODEL) || (r_shadows->integer != 1 && !(r_shadows->integer == 2 && (e->flags & EF_PLANARSHADOW))) || R_CullPlanarShadow( e, studio_mins, studio_maxs, query )) + if(( RI.refdef.flags & RDF_NOWORLDMODEL ) || ( r_shadows->integer != 1 ) || R_CullPlanarShadow( e, studio_mins, studio_maxs, query )) return frustum; // entity is not in PVS or shadow is culled away by frustum culling R_StudioSetupRender( e, e->model ); @@ -2506,7 +2515,7 @@ bool R_CullStudioModel( ref_entity_t *e ) pmesh = (mstudiomesh_t *)((byte *)m_pStudioHeader + m_pSubModel->meshindex) + j; if( e->customShader ) shader = e->customShader; - else shader = &r_shaders[ptexture[pskinref[pmesh->skinref]].shader]; + else shader = &r_shaders[ptexture[pskinref[pmesh->skinref]].index]; if( shader && ( shader->sort <= SORT_ALPHATEST )) { @@ -2558,7 +2567,7 @@ void R_AddStudioModelToList( ref_entity_t *e ) { if( !r_shadows_self_shadow->integer ) r_entShadowBits[entnum] &= ~RI.shadowGroup->bit; - if( e->ent_type == ED_VIEWMODEL ) + if( e->ent_type == ET_VIEWENTITY ) return; } else @@ -2573,7 +2582,7 @@ void R_AddStudioModelToList( ref_entity_t *e ) { fog = R_FogForSphere( e->origin, studio_radius ); #if 0 - if( !( e->ent_type == ED_VIEWMODEL ) && fog ) + if( !( e->ent_type == ET_VIEWENTITY ) && fog ) { R_StudioModelLerpBBox( e, mod ); if( R_CompletelyFogged( fog, e->origin, studio_radius )) @@ -2602,7 +2611,7 @@ void R_AddStudioModelToList( ref_entity_t *e ) pmesh = (mstudiomesh_t *)((byte *)m_pStudioHeader + m_pSubModel->meshindex) + j; if( e->customShader ) shader = e->customShader; - else shader = &r_shaders[ptexture[pskinref[pmesh->skinref]].shader]; + else shader = &r_shaders[ptexture[pskinref[pmesh->skinref]].index]; if( shader ) R_AddModelMeshToList( modhandle, fog, shader, ((j<<8)|i)); } diff --git a/vid_gl/r_surf.c b/vid_gl/r_surf.c index f02e1188..b5210f15 100644 --- a/vid_gl/r_surf.c +++ b/vid_gl/r_surf.c @@ -703,7 +703,7 @@ void R_MarkLeaves( void ) vis = fatpvs; } - for( i = 0; i < r_worldbrushmodel->numleafs; i++ ) + for( i = 0; i < r_worldbrushmodel->numleafs-1; i++ ) // FIXME: this is right ? { if( vis[i>>3] & ( 1<<( i & 7 ))) { diff --git a/xash.dsw b/xash.dsw index 4515f62e..4bc2aa61 100644 --- a/xash.dsw +++ b/xash.dsw @@ -3,7 +3,7 @@ Microsoft Developer Studio Workspace File, Format Version 6.00 ############################################################################### -Project: "bshift"=".\bshift\bshift.dsp" - Package Owner=<4> +Project: "bshift"=".\dlls\hl.dsp" - Package Owner=<4> Package=<5> {{{ @@ -75,18 +75,6 @@ Package=<4> ############################################################################### -Project: "spirit"=".\spirit\spirit.dsp" - Package Owner=<4> - -Package=<5> -{{{ -}}} - -Package=<4> -{{{ -}}} - -############################################################################### - Project: "vid_gl"=".\vid_gl\vid_gl.dsp" - Package Owner=<4> Package=<5> diff --git a/xtools/ripper/conv_shader.c b/xtools/ripper/conv_shader.c index 07220509..f29ec157 100644 --- a/xtools/ripper/conv_shader.c +++ b/xtools/ripper/conv_shader.c @@ -8,36 +8,36 @@ #include "utils.h" // q2 wal contents -#define CONTENTS_SOLID 0x00000001 // an eye is never valid in a solid -#define CONTENTS_WINDOW 0x00000002 // translucent, but not watery -#define CONTENTS_AUX 0x00000004 -#define CONTENTS_LAVA 0x00000008 -#define CONTENTS_SLIME 0x00000010 -#define CONTENTS_WATER 0x00000020 -#define CONTENTS_MIST 0x00000040 +#define Q2_CONTENTS_SOLID 0x00000001 // an eye is never valid in a solid +#define Q2_CONTENTS_WINDOW 0x00000002 // translucent, but not watery +#define Q2_CONTENTS_AUX 0x00000004 +#define Q2_CONTENTS_LAVA 0x00000008 +#define Q2_CONTENTS_SLIME 0x00000010 +#define Q2_CONTENTS_WATER 0x00000020 +#define Q2_CONTENTS_MIST 0x00000040 // remaining contents are non-visible, and don't eat brushes -#define CONTENTS_AREAPORTAL 0x00008000 -#define CONTENTS_PLAYERCLIP 0x00010000 -#define CONTENTS_MONSTERCLIP 0x00020000 -#define CONTENTS_CLIP (CONTENTS_PLAYERCLIP|CONTENTS_MONSTERCLIP) +#define Q2_CONTENTS_AREAPORTAL 0x00008000 +#define Q2_CONTENTS_PLAYERCLIP 0x00010000 +#define Q2_CONTENTS_MONSTERCLIP 0x00020000 +#define Q2_CONTENTS_CLIP (Q2_CONTENTS_PLAYERCLIP|Q2_CONTENTS_MONSTERCLIP) // currents can be added to any other contents, and may be mixed -#define CONTENTS_CURRENT_0 0x00040000 -#define CONTENTS_CURRENT_90 0x00080000 -#define CONTENTS_CURRENT_180 0x00100000 -#define CONTENTS_CURRENT_270 0x00200000 -#define CONTENTS_CURRENT_UP 0x00400000 -#define CONTENTS_CURRENT_DOWN 0x00800000 +#define Q2_CONTENTS_CURRENT_0 0x00040000 +#define Q2_CONTENTS_CURRENT_90 0x00080000 +#define Q2_CONTENTS_CURRENT_180 0x00100000 +#define Q2_CONTENTS_CURRENT_270 0x00200000 +#define Q2_CONTENTS_CURRENT_UP 0x00400000 +#define Q2_CONTENTS_CURRENT_DOWN 0x00800000 -#define CONTENTS_ORIGIN 0x01000000 // removed before BSP'ing an entity +#define Q2_CONTENTS_ORIGIN 0x01000000 // removed before BSP'ing an entity -#define CONTENTS_MONSTER 0x02000000 // should never be on a brush, only in game -#define CONTENTS_DEADMONSTER 0x04000000 -#define CONTENTS_DETAIL 0x08000000 // brushes to be added after vis leafs -#define CONTENTS_TRANSLUCENT 0x10000000 // auto set if any surface has trans -#define CONTENTS_LADDER 0x20000000 -#define CONTENTS_TRIGGER 0x40000000 // trigger +#define Q2_CONTENTS_MONSTER 0x02000000 // should never be on a brush, only in game +#define Q2_CONTENTS_DEADMONSTER 0x04000000 +#define Q2_CONTENTS_DETAIL 0x08000000 // brushes to be added after vis leafs +#define Q2_CONTENTS_TRANSLUCENT 0x10000000 // auto set if any surface has trans +#define Q2_CONTENTS_LADDER 0x20000000 +#define Q2_CONTENTS_TRIGGER 0x40000000 // trigger #define SURF_LIGHT 0x00000001 // value will hold the light strength #define SURF_SLICK 0x00000002 // effects game physics @@ -163,16 +163,16 @@ check_shader: FS_Printf( f, "\n%s\n{\n", shadername ); // write shadername - if( contents & CONTENTS_CLIP && contents && CONTENTS_PLAYERCLIP ) + if( contents & Q2_CONTENTS_CLIP && contents && Q2_CONTENTS_PLAYERCLIP ) FS_Print( f, "\tsurfaceparm\tclip\n" ); - else if( contents & CONTENTS_MONSTERCLIP ) FS_Print( f, "\tsurfaceparm\tmonsterclip\n" ); - else if( contents & CONTENTS_PLAYERCLIP ) FS_Print( f, "\tsurfaceparm\tplayerclip\n" ); - else if( contents & CONTENTS_WINDOW ) FS_Print( f, "\tsurfaceparm\twindow\n" ); - else if( contents & CONTENTS_ORIGIN ) FS_Print( f, "\tsurfaceparm\torigin\n" ); - else if( contents & CONTENTS_TRANSLUCENT ) FS_Print( f, "\tsurfaceparm\ttrans\n" ); - else if( contents & CONTENTS_AREAPORTAL ) FS_Print( f, "\tsurfaceparm\tareaportal\n" ); - else if( contents & CONTENTS_TRIGGER ) FS_Print( f, "\tsurfaceparm\ttrigger\n" ); - else if( contents & CONTENTS_DETAIL ) FS_Print( f, "\tsurfaceparm\tdetail\n" ); + else if( contents & Q2_CONTENTS_MONSTERCLIP ) FS_Print( f, "\tsurfaceparm\tmonsterclip\n" ); + else if( contents & Q2_CONTENTS_PLAYERCLIP ) FS_Print( f, "\tsurfaceparm\tplayerclip\n" ); + else if( contents & Q2_CONTENTS_WINDOW ) FS_Print( f, "\tsurfaceparm\twindow\n" ); + else if( contents & Q2_CONTENTS_ORIGIN ) FS_Print( f, "\tsurfaceparm\torigin\n" ); + else if( contents & Q2_CONTENTS_TRANSLUCENT ) FS_Print( f, "\tsurfaceparm\ttrans\n" ); + else if( contents & Q2_CONTENTS_AREAPORTAL ) FS_Print( f, "\tsurfaceparm\tareaportal\n" ); + else if( contents & Q2_CONTENTS_TRIGGER ) FS_Print( f, "\tsurfaceparm\ttrigger\n" ); + else if( contents & Q2_CONTENTS_DETAIL ) FS_Print( f, "\tsurfaceparm\tdetail\n" ); if( flags & SURF_LIGHT ) { @@ -192,11 +192,11 @@ check_shader: FS_Print( f, "\ttessSize\t\t64\n\n" ); // server relevant contents - if(contents & CONTENTS_WATER) + if(contents & Q2_CONTENTS_WATER) FS_Print( f, "\tsurfaceparm\twater\n" ); - else if(contents & CONTENTS_SLIME) + else if(contents & Q2_CONTENTS_SLIME) FS_Print( f, "\tsurfaceparm\tslime\n" ); - else if(contents & CONTENTS_LAVA) + else if(contents & Q2_CONTENTS_LAVA) FS_Print( f, "\tsurfaceparm\tlava\n" ); else FS_Print( f, "\tsurfaceparm\twater\n" ); @@ -322,32 +322,32 @@ void Conv_ShaderGetFlags( const char *imagename, const char *shadername, const c if( com.stristr( imagename, "water" )) { - *contents |= CONTENTS_WATER; + *contents |= Q2_CONTENTS_WATER; *flags |= SURF_WARP; // liquids } else if( com.stristr( imagename, "slime" )) { - *contents |= CONTENTS_SLIME; + *contents |= Q2_CONTENTS_SLIME; *flags |= SURF_WARP; // liquids } else if( com.stristr( imagename, "lava" )) { - *contents |= CONTENTS_LAVA; + *contents |= Q2_CONTENTS_LAVA; *flags |= SURF_WARP; // liquids } // search for keywords if( !com.strnicmp( imagename, "sky", 3 )) *flags |= SURF_SKY; - else if( !com.strnicmp( imagename, "origin",6)) *contents |= CONTENTS_ORIGIN; - else if( !com.strnicmp( imagename, "clip", 4 )) *contents |= CONTENTS_CLIP; + else if( !com.strnicmp( imagename, "origin",6)) *contents |= Q2_CONTENTS_ORIGIN; + else if( !com.strnicmp( imagename, "clip", 4 )) *contents |= Q2_CONTENTS_CLIP; else if( !com.strnicmp( imagename, "hint", 4 )) *flags |= SURF_HINT; else if( !com.strnicmp( imagename, "skip", 4 )) *flags |= SURF_SKIP; else if( !com.strnicmp( imagename, "null", 4 )) *flags |= SURF_NODRAW; - else if( !com.strnicmp( imagename, "translucent", 11 )) *contents |= CONTENTS_TRANSLUCENT; + else if( !com.strnicmp( imagename, "translucent", 11 )) *contents |= Q2_CONTENTS_TRANSLUCENT; else if( !com.strnicmp( imagename, "glass", 5 )) *flags |= SURF_TRANS66; else if( !com.strnicmp( imagename, "mirror", 6 )) *flags |= SURF_MIRROR; else if( !com.strnicmp( imagename, "portal", 6 )) *flags |= SURF_PORTAL; - else if( com.stristr( imagename, "trigger" )) *contents |= CONTENTS_TRIGGER; + else if( com.stristr( imagename, "trigger" )) *contents |= Q2_CONTENTS_TRIGGER; else if( com.stristr( imagename, "lite" )) *flags |= SURF_LIGHT; // try to exctract contents and flags directly form mip-name @@ -355,7 +355,7 @@ void Conv_ShaderGetFlags( const char *imagename, const char *shadername, const c else if( imagename[0] == '{' ) { *flags |= SURF_ALPHATEST; // grates - *contents |= CONTENTS_TRANSLUCENT; + *contents |= Q2_CONTENTS_TRANSLUCENT; } else if( imagename[0] == '~' ) *flags |= SURF_LIGHT; // light definition else if( imagename[0] == '+' )