Remove useless files.

This commit is contained in:
Night Owl 2018-12-07 16:59:27 +05:00
parent cde2afff7c
commit d20abd0465
48 changed files with 0 additions and 33430 deletions

File diff suppressed because it is too large Load Diff

View File

@ -1,395 +0,0 @@
//==========================================================================;
//
// 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\<Module Name>\Type and
// HKEY_LOCAL_MACHINE\SOFTWARE\Debug\<Module Name>\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 <stdarg.h>
#include <stdio.h>
#include "extdll.h"
#include "util.h"
#include "wxdebug.h"
#include <tchar.h>
#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

View File

@ -1,918 +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.
*
****/
//=========================================================
//=========================================================
#include "extdll.h"
#include "util.h"
#include "cbase.h"
#include "monsters.h"
#include "squadmonster.h"
#define AFLOCK_MAX_RECRUIT_RADIUS 1024
#define AFLOCK_FLY_SPEED 125
#define AFLOCK_TURN_RATE 75
#define AFLOCK_ACCELERATE 10
#define AFLOCK_CHECK_DIST 192
#define AFLOCK_TOO_CLOSE 100
#define AFLOCK_TOO_FAR 256
//=========================================================
//=========================================================
class CFlockingFlyerFlock : public CBaseMonster
{
public:
void Spawn( void );
void Precache( void );
void KeyValue( KeyValueData *pkvd );
void SpawnFlock( void );
virtual int Save( CSave &save );
virtual int Restore( CRestore &restore );
static TYPEDESCRIPTION m_SaveData[];
// Sounds are shared by the flock
static void PrecacheFlockSounds( void );
int m_cFlockSize;
float m_flFlockRadius;
};
TYPEDESCRIPTION CFlockingFlyerFlock::m_SaveData[] =
{
DEFINE_FIELD( CFlockingFlyerFlock, m_cFlockSize, FIELD_INTEGER ),
DEFINE_FIELD( CFlockingFlyerFlock, m_flFlockRadius, FIELD_FLOAT ),
};
IMPLEMENT_SAVERESTORE( CFlockingFlyerFlock, CBaseMonster )
//=========================================================
//=========================================================
class CFlockingFlyer : public CBaseMonster
{
public:
void Spawn( void );
void Precache( void );
void SpawnCommonCode( void );
void EXPORT IdleThink( void );
void BoidAdvanceFrame( void );
void EXPORT FormFlock( void );
void EXPORT Start( void );
void EXPORT FlockLeaderThink( void );
void EXPORT FlockFollowerThink( void );
void EXPORT FallHack( void );
void MakeSound( void );
void AlertFlock( void );
void SpreadFlock( void );
void SpreadFlock2( void );
void Killed( entvars_t *pevAttacker, int iGib );
void Poop ( void );
BOOL FPathBlocked( void );
//void KeyValue( KeyValueData *pkvd );
virtual int Save( CSave &save );
virtual int Restore( CRestore &restore );
static TYPEDESCRIPTION m_SaveData[];
int IsLeader( void ) { return m_pSquadLeader == this; }
int InSquad( void ) { return m_pSquadLeader != NULL; }
int SquadCount( void );
void SquadRemove( CFlockingFlyer *pRemove );
void SquadUnlink( void );
void SquadAdd( CFlockingFlyer *pAdd );
void SquadDisband( void );
CFlockingFlyer *m_pSquadLeader;
CFlockingFlyer *m_pSquadNext;
BOOL m_fTurning;// is this boid turning?
BOOL m_fCourseAdjust;// followers set this flag TRUE to override flocking while they avoid something
BOOL m_fPathBlocked;// TRUE if there is an obstacle ahead
Vector m_vecReferencePoint;// last place we saw leader
Vector m_vecAdjustedVelocity;// adjusted velocity (used when fCourseAdjust is TRUE)
float m_flGoalSpeed;
float m_flLastBlockedTime;
float m_flFakeBlockedTime;
float m_flAlertTime;
float m_flFlockNextSoundTime;
};
LINK_ENTITY_TO_CLASS( monster_flyer, CFlockingFlyer )
LINK_ENTITY_TO_CLASS( monster_flyer_flock, CFlockingFlyerFlock )
TYPEDESCRIPTION CFlockingFlyer::m_SaveData[] =
{
DEFINE_FIELD( CFlockingFlyer, m_pSquadLeader, FIELD_CLASSPTR ),
DEFINE_FIELD( CFlockingFlyer, m_pSquadNext, FIELD_CLASSPTR ),
DEFINE_FIELD( CFlockingFlyer, m_fTurning, FIELD_BOOLEAN ),
DEFINE_FIELD( CFlockingFlyer, m_fCourseAdjust, FIELD_BOOLEAN ),
DEFINE_FIELD( CFlockingFlyer, m_fPathBlocked, FIELD_BOOLEAN ),
DEFINE_FIELD( CFlockingFlyer, m_vecReferencePoint, FIELD_POSITION_VECTOR ),
DEFINE_FIELD( CFlockingFlyer, m_vecAdjustedVelocity, FIELD_VECTOR ),
DEFINE_FIELD( CFlockingFlyer, m_flGoalSpeed, FIELD_FLOAT ),
DEFINE_FIELD( CFlockingFlyer, m_flLastBlockedTime, FIELD_TIME ),
DEFINE_FIELD( CFlockingFlyer, m_flFakeBlockedTime, FIELD_TIME ),
DEFINE_FIELD( CFlockingFlyer, m_flAlertTime, FIELD_TIME ),
//DEFINE_FIELD( CFlockingFlyer, m_flFlockNextSoundTime, FIELD_TIME ), // don't need to save
};
IMPLEMENT_SAVERESTORE( CFlockingFlyer, CBaseMonster )
//=========================================================
//=========================================================
void CFlockingFlyerFlock::KeyValue( KeyValueData *pkvd )
{
if( FStrEq( pkvd->szKeyName, "iFlockSize" ) )
{
m_cFlockSize = atoi( pkvd->szValue );
pkvd->fHandled = TRUE;
}
else if( FStrEq( pkvd->szKeyName, "flFlockRadius" ) )
{
m_flFlockRadius = atof( pkvd->szValue );
pkvd->fHandled = TRUE;
}
}
//=========================================================
//=========================================================
void CFlockingFlyerFlock::Spawn()
{
Precache();
SpawnFlock();
REMOVE_ENTITY( ENT( pev ) ); // dump the spawn ent
}
//=========================================================
//=========================================================
void CFlockingFlyerFlock::Precache()
{
//PRECACHE_MODEL( "models/aflock.mdl" );
PRECACHE_MODEL( "models/boid.mdl" );
PrecacheFlockSounds();
}
void CFlockingFlyerFlock::PrecacheFlockSounds( void )
{
PRECACHE_SOUND( "boid/boid_alert1.wav" );
PRECACHE_SOUND( "boid/boid_alert2.wav" );
PRECACHE_SOUND( "boid/boid_idle1.wav" );
PRECACHE_SOUND( "boid/boid_idle2.wav" );
}
//=========================================================
//=========================================================
void CFlockingFlyerFlock::SpawnFlock( void )
{
float R = m_flFlockRadius;
int iCount;
Vector vecSpot;
CFlockingFlyer *pBoid, *pLeader;
pLeader = pBoid = NULL;
for( iCount = 0; iCount < m_cFlockSize; iCount++ )
{
pBoid = GetClassPtr( (CFlockingFlyer *)NULL );
if( !pLeader )
{
// make this guy the leader.
pLeader = pBoid;
pLeader->m_pSquadLeader = pLeader;
pLeader->m_pSquadNext = NULL;
}
vecSpot.x = RANDOM_FLOAT( -R, R );
vecSpot.y = RANDOM_FLOAT( -R, R );
vecSpot.z = RANDOM_FLOAT( 0, 16 );
vecSpot = pev->origin + vecSpot;
UTIL_SetOrigin( pBoid->pev, vecSpot );
pBoid->pev->movetype = MOVETYPE_FLY;
pBoid->SpawnCommonCode();
pBoid->pev->flags &= ~FL_ONGROUND;
pBoid->pev->velocity = g_vecZero;
pBoid->pev->angles = pev->angles;
pBoid->pev->frame = 0;
pBoid->pev->nextthink = gpGlobals->time + 0.2;
pBoid->SetThink( &CFlockingFlyer::IdleThink );
if( pBoid != pLeader )
{
pLeader->SquadAdd( pBoid );
}
}
}
//=========================================================
//=========================================================
void CFlockingFlyer::Spawn()
{
Precache();
SpawnCommonCode();
pev->frame = 0;
pev->nextthink = gpGlobals->time + 0.1;
SetThink( &CFlockingFlyer::IdleThink );
}
//=========================================================
//=========================================================
void CFlockingFlyer::Precache()
{
//PRECACHE_MODEL( "models/aflock.mdl" );
PRECACHE_MODEL( "models/boid.mdl" );
CFlockingFlyerFlock::PrecacheFlockSounds();
}
//=========================================================
//=========================================================
void CFlockingFlyer::MakeSound( void )
{
if( m_flAlertTime > gpGlobals->time )
{
// make agitated sounds
switch ( RANDOM_LONG( 0, 1 ) )
{
case 0:
EMIT_SOUND( ENT( pev ), CHAN_WEAPON, "boid/boid_alert1.wav", 1, ATTN_NORM );
break;
case 1:
EMIT_SOUND( ENT( pev ), CHAN_WEAPON, "boid/boid_alert2.wav", 1, ATTN_NORM );
break;
}
return;
}
// make normal sound
switch( RANDOM_LONG( 0, 1 ) )
{
case 0:
EMIT_SOUND( ENT(pev), CHAN_WEAPON, "boid/boid_idle1.wav", 1, ATTN_NORM );
break;
case 1:
EMIT_SOUND( ENT(pev), CHAN_WEAPON, "boid/boid_idle2.wav", 1, ATTN_NORM );
break;
}
}
//=========================================================
//=========================================================
void CFlockingFlyer::Killed( entvars_t *pevAttacker, int iGib )
{
CFlockingFlyer *pSquad;
pSquad = (CFlockingFlyer *)m_pSquadLeader;
while( pSquad )
{
pSquad->m_flAlertTime = gpGlobals->time + 15;
pSquad = (CFlockingFlyer *)pSquad->m_pSquadNext;
}
if( m_pSquadLeader )
{
m_pSquadLeader->SquadRemove( this );
}
pev->deadflag = DEAD_DEAD;
pev->framerate = 0;
pev->effects = EF_NOINTERP;
UTIL_SetSize( pev, Vector( 0, 0, 0 ), Vector( 0, 0, 0 ) );
pev->movetype = MOVETYPE_TOSS;
SetThink( &CFlockingFlyer::FallHack );
pev->nextthink = gpGlobals->time + 0.1;
}
void CFlockingFlyer::FallHack( void )
{
if( pev->flags & FL_ONGROUND )
{
if( !FClassnameIs ( pev->groundentity, "worldspawn" ) )
{
pev->flags &= ~FL_ONGROUND;
pev->nextthink = gpGlobals->time + 0.1;
}
else
{
pev->velocity = g_vecZero;
SetThink( NULL );
}
}
}
//=========================================================
//=========================================================
void CFlockingFlyer::SpawnCommonCode()
{
pev->deadflag = DEAD_NO;
pev->classname = MAKE_STRING( "monster_flyer" );
pev->solid = SOLID_SLIDEBOX;
pev->movetype = MOVETYPE_FLY;
pev->takedamage = DAMAGE_NO;
pev->health = 1;
m_fPathBlocked = FALSE;// obstacles will be detected
m_flFieldOfView = 0.2;
//SET_MODEL( ENT( pev ), "models/aflock.mdl" );
SET_MODEL( ENT( pev ), "models/boid.mdl" );
//UTIL_SetSize( pev, Vector( 0, 0, 0 ), Vector( 0, 0, 0 ) );
UTIL_SetSize( pev, Vector( -5, -5, 0 ), Vector( 5, 5, 2 ) );
}
//=========================================================
//=========================================================
void CFlockingFlyer::BoidAdvanceFrame()
{
float flapspeed = ( pev->speed - pev->armorvalue ) / AFLOCK_ACCELERATE;
pev->armorvalue = pev->armorvalue * .8 + pev->speed * .2;
if( flapspeed < 0 )
flapspeed = -flapspeed;
if( flapspeed < 0.25 )
flapspeed = 0.25;
if( flapspeed > 1.9 )
flapspeed = 1.9;
pev->framerate = flapspeed;
// lean
pev->avelocity.x = -( pev->angles.x + flapspeed * 5 );
// bank
pev->avelocity.z = -( pev->angles.z + pev->avelocity.y );
// pev->framerate = flapspeed;
StudioFrameAdvance( 0.1 );
}
//=========================================================
//=========================================================
void CFlockingFlyer::IdleThink( void )
{
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() ) ) )
{
SetThink( &CFlockingFlyer::Start );
pev->nextthink = gpGlobals->time + 0.1;
}
}
//=========================================================
// Start - player enters the pvs, so get things going.
//=========================================================
void CFlockingFlyer::Start( void )
{
pev->nextthink = gpGlobals->time + 0.1;
if( IsLeader() )
{
SetThink( &CFlockingFlyer::FlockLeaderThink );
}
else
{
SetThink( &CFlockingFlyer::FlockFollowerThink );
}
/*
Vector vecTakeOff;
vecTakeOff = Vector( 0, 0, 0 );
vecTakeOff.z = 50 + RANDOM_FLOAT( 0, 100 );
vecTakeOff.x = 20 - RANDOM_FLOAT( 0, 40 );
vecTakeOff.y = 20 - RANDOM_FLOAT( 0, 40 );
pev->velocity = vecTakeOff;
pev->speed = pev->velocity.Length();
pev->sequence = 0;
*/
SetActivity( ACT_FLY );
ResetSequenceInfo();
BoidAdvanceFrame();
pev->speed = AFLOCK_FLY_SPEED;// no delay!
}
//=========================================================
// Leader boid calls this to form a flock from surrounding boids
//=========================================================
void CFlockingFlyer::FormFlock( void )
{
if( !InSquad() )
{
// I am my own leader
m_pSquadLeader = this;
m_pSquadNext = NULL;
int squadCount = 1;
CBaseEntity *pEntity = NULL;
while( ( pEntity = UTIL_FindEntityInSphere( pEntity, pev->origin, AFLOCK_MAX_RECRUIT_RADIUS ) ) != NULL )
{
CBaseMonster *pRecruit = pEntity->MyMonsterPointer();
if( pRecruit && pRecruit != this && pRecruit->IsAlive() && !pRecruit->m_pCine )
{
// Can we recruit this guy?
if( FClassnameIs ( pRecruit->pev, "monster_flyer" ) )
{
squadCount++;
SquadAdd( (CFlockingFlyer *)pRecruit );
}
}
}
}
SetThink( &CFlockingFlyer::IdleThink );// now that flock is formed, go to idle and wait for a player to come along.
pev->nextthink = gpGlobals->time;
}
//=========================================================
// Searches for boids that are too close and pushes them away
//=========================================================
void CFlockingFlyer::SpreadFlock()
{
Vector vecDir;
float flSpeed;// holds vector magnitude while we fiddle with the direction
CFlockingFlyer *pList = m_pSquadLeader;
while( pList )
{
if( pList != this && ( pev->origin - pList->pev->origin ).Length() <= AFLOCK_TOO_CLOSE )
{
// push the other away
vecDir = pList->pev->origin - pev->origin;
vecDir = vecDir.Normalize();
// store the magnitude of the other boid's velocity, and normalize it so we
// can average in a course that points away from the leader.
flSpeed = pList->pev->velocity.Length();
pList->pev->velocity = pList->pev->velocity.Normalize();
pList->pev->velocity = ( pList->pev->velocity + vecDir ) * 0.5;
pList->pev->velocity = pList->pev->velocity * flSpeed;
}
pList = pList->m_pSquadNext;
}
}
//=========================================================
// Alters the caller's course if he's too close to others
//
// This function should **ONLY** be called when Caller's velocity is normalized!!
//=========================================================
void CFlockingFlyer::SpreadFlock2()
{
Vector vecDir;
CFlockingFlyer *pList = m_pSquadLeader;
while( pList )
{
if( pList != this && ( pev->origin - pList->pev->origin ).Length() <= AFLOCK_TOO_CLOSE )
{
vecDir = pev->origin - pList->pev->origin;
vecDir = vecDir.Normalize();
pev->velocity = pev->velocity + vecDir;
}
pList = pList->m_pSquadNext;
}
}
//=========================================================
// FBoidPathBlocked - returns TRUE if there is an obstacle ahead
//=========================================================
BOOL CFlockingFlyer::FPathBlocked()
{
TraceResult tr;
Vector vecDist;// used for general measurements
Vector vecDir;// used for general measurements
BOOL fBlocked;
if( m_flFakeBlockedTime > gpGlobals->time )
{
m_flLastBlockedTime = gpGlobals->time;
return TRUE;
}
// use VELOCITY, not angles, not all boids point the direction they are flying
//vecDir = UTIL_VecToAngles( pevBoid->velocity );
UTIL_MakeVectors ( pev->angles );
fBlocked = FALSE;// assume the way ahead is clear
// check for obstacle ahead
UTIL_TraceLine( pev->origin, pev->origin + gpGlobals->v_forward * AFLOCK_CHECK_DIST, ignore_monsters, ENT( pev ), &tr );
if( tr.flFraction != 1.0 )
{
m_flLastBlockedTime = gpGlobals->time;
fBlocked = TRUE;
}
// extra wide checks
UTIL_TraceLine( pev->origin + gpGlobals->v_right * 12, pev->origin + gpGlobals->v_right * 12 + gpGlobals->v_forward * AFLOCK_CHECK_DIST, ignore_monsters, ENT( pev ), &tr );
if( tr.flFraction != 1.0 )
{
m_flLastBlockedTime = gpGlobals->time;
fBlocked = TRUE;
}
UTIL_TraceLine( pev->origin - gpGlobals->v_right * 12, pev->origin - gpGlobals->v_right * 12 + gpGlobals->v_forward * AFLOCK_CHECK_DIST, ignore_monsters, ENT( pev ), &tr );
if( tr.flFraction != 1.0 )
{
m_flLastBlockedTime = gpGlobals->time;
fBlocked = TRUE;
}
if( !fBlocked && gpGlobals->time - m_flLastBlockedTime > 6 )
{
// not blocked, and it's been a few seconds since we've actually been blocked.
m_flFakeBlockedTime = gpGlobals->time + RANDOM_LONG( 1, 3 );
}
return fBlocked;
}
//=========================================================
// Leader boids use this think every tenth
//=========================================================
void CFlockingFlyer::FlockLeaderThink( void )
{
TraceResult tr;
Vector vecDist;// used for general measurements
Vector vecDir;// used for general measurements
float flLeftSide;
float flRightSide;
pev->nextthink = gpGlobals->time + 0.1;
UTIL_MakeVectors( pev->angles );
// is the way ahead clear?
if( !FPathBlocked () )
{
// if the boid is turning, stop the trend.
if( m_fTurning )
{
m_fTurning = FALSE;
pev->avelocity.y = 0;
}
m_fPathBlocked = FALSE;
if( pev->speed <= AFLOCK_FLY_SPEED )
pev->speed += 5;
pev->velocity = gpGlobals->v_forward * pev->speed;
BoidAdvanceFrame();
return;
}
// IF we get this far in the function, the leader's path is blocked!
m_fPathBlocked = TRUE;
if( !m_fTurning )// something in the way and boid is not already turning to avoid
{
// measure clearance on left and right to pick the best dir to turn
UTIL_TraceLine( pev->origin, pev->origin + gpGlobals->v_right * AFLOCK_CHECK_DIST, ignore_monsters, ENT( pev ), &tr );
vecDist = ( tr.vecEndPos - pev->origin );
flRightSide = vecDist.Length();
UTIL_TraceLine( pev->origin, pev->origin - gpGlobals->v_right * AFLOCK_CHECK_DIST, ignore_monsters, ENT( pev ), &tr );
vecDist = tr.vecEndPos - pev->origin;
flLeftSide = vecDist.Length();
// turn right if more clearance on right side
if( flRightSide > flLeftSide )
{
pev->avelocity.y = -AFLOCK_TURN_RATE;
m_fTurning = TRUE;
}
// default to left turn :)
else if( flLeftSide > flRightSide )
{
pev->avelocity.y = AFLOCK_TURN_RATE;
m_fTurning = TRUE;
}
else
{
// equidistant. Pick randomly between left and right.
m_fTurning = TRUE;
if( RANDOM_LONG( 0, 1 ) == 0 )
{
pev->avelocity.y = AFLOCK_TURN_RATE;
}
else
{
pev->avelocity.y = -AFLOCK_TURN_RATE;
}
}
}
SpreadFlock();
pev->velocity = gpGlobals->v_forward * pev->speed;
// check and make sure we aren't about to plow into the ground, don't let it happen
UTIL_TraceLine( pev->origin, pev->origin - gpGlobals->v_up * 16, ignore_monsters, ENT( pev ), &tr );
if( tr.flFraction != 1.0 && pev->velocity.z < 0 )
pev->velocity.z = 0;
// maybe it did, though.
if( FBitSet( pev->flags, FL_ONGROUND ) )
{
UTIL_SetOrigin( pev, pev->origin + Vector( 0, 0, 1 ) );
pev->velocity.z = 0;
}
if( m_flFlockNextSoundTime < gpGlobals->time )
{
MakeSound();
m_flFlockNextSoundTime = gpGlobals->time + RANDOM_FLOAT( 1, 3 );
}
BoidAdvanceFrame( );
return;
}
//=========================================================
// follower boids execute this code when flocking
//=========================================================
void CFlockingFlyer::FlockFollowerThink( void )
{
TraceResult tr;
Vector vecDist;
Vector vecDir;
Vector vecDirToLeader;
float flDistToLeader;
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 );
return;
}
vecDirToLeader = ( m_pSquadLeader->pev->origin - pev->origin );
flDistToLeader = vecDirToLeader.Length();
// match heading with leader
pev->angles = m_pSquadLeader->pev->angles;
//
// We can see the leader, so try to catch up to it
//
if( FInViewCone ( m_pSquadLeader ) )
{
// if we're too far away, speed up
if( flDistToLeader > AFLOCK_TOO_FAR )
{
m_flGoalSpeed = m_pSquadLeader->pev->velocity.Length() * 1.5;
}
// if we're too close, slow down
else if( flDistToLeader < AFLOCK_TOO_CLOSE )
{
m_flGoalSpeed = m_pSquadLeader->pev->velocity.Length() * 0.5;
}
}
else
{
// wait up! the leader isn't out in front, so we slow down to let him pass
m_flGoalSpeed = m_pSquadLeader->pev->velocity.Length() * 0.5;
}
SpreadFlock2();
pev->speed = pev->velocity.Length();
pev->velocity = pev->velocity.Normalize();
// if we are too far from leader, average a vector towards it into our current velocity
if( flDistToLeader > AFLOCK_TOO_FAR )
{
vecDirToLeader = vecDirToLeader.Normalize();
pev->velocity = (pev->velocity + vecDirToLeader) * 0.5;
}
// clamp speeds and handle acceleration
if( m_flGoalSpeed > AFLOCK_FLY_SPEED * 2 )
{
m_flGoalSpeed = AFLOCK_FLY_SPEED * 2;
}
if( pev->speed < m_flGoalSpeed )
{
pev->speed += AFLOCK_ACCELERATE;
}
else if( pev->speed > m_flGoalSpeed )
{
pev->speed -= AFLOCK_ACCELERATE;
}
pev->velocity = pev->velocity * pev->speed;
BoidAdvanceFrame( );
}
/*
// Is this boid's course blocked?
if( FBoidPathBlocked( pev ) )
{
// course is still blocked from last time. Just keep flying along adjusted
// velocity
if( m_fCourseAdjust )
{
pev->velocity = m_vecAdjustedVelocity * pev->speed;
return;
}
else // set course adjust flag and calculate adjusted velocity
{
m_fCourseAdjust = TRUE;
// use VELOCITY, not angles, not all boids point the direction they are flying
//vecDir = UTIL_VecToAngles( pev->velocity );
//UTIL_MakeVectors( vecDir );
UTIL_MakeVectors( pev->angles );
// measure clearance on left and right to pick the best dir to turn
UTIL_TraceLine( pev->origin, pev->origin + gpGlobals->v_right * AFLOCK_CHECK_DIST, ignore_monsters, ENT( pev ), &tr );
vecDist = tr.vecEndPos - pev->origin;
flRightSide = vecDist.Length();
UTIL_TraceLine( pev->origin, pev->origin - gpGlobals->v_right * AFLOCK_CHECK_DIST, ignore_monsters, ENT( pev ), &tr );
vecDist = tr.vecEndPos - pev->origin;
flLeftSide = vecDist.Length();
// slide right if more clearance on right side
if( flRightSide > flLeftSide )
{
m_vecAdjustedVelocity = gpGlobals->v_right;
}
// else slide left
else
{
m_vecAdjustedVelocity = gpGlobals->v_right * -1;
}
}
return;
}
// if we make it this far, boids path is CLEAR!
m_fCourseAdjust = FALSE;
*/
//=========================================================
//
// SquadUnlink(), Unlink the squad pointers.
//
//=========================================================
void CFlockingFlyer::SquadUnlink( void )
{
m_pSquadLeader = NULL;
m_pSquadNext = NULL;
}
//=========================================================
//
// SquadAdd(), add pAdd to my squad
//
//=========================================================
void CFlockingFlyer::SquadAdd( CFlockingFlyer *pAdd )
{
ASSERT( pAdd != NULL );
ASSERT( !pAdd->InSquad() );
ASSERT( this->IsLeader() );
pAdd->m_pSquadNext = m_pSquadNext;
m_pSquadNext = pAdd;
pAdd->m_pSquadLeader = this;
}
//=========================================================
//
// SquadRemove(), remove pRemove from my squad.
// If I am pRemove, promote m_pSquadNext to leader
//
//=========================================================
void CFlockingFlyer::SquadRemove( CFlockingFlyer *pRemove )
{
ASSERT( pRemove != NULL );
ASSERT( this->IsLeader() );
ASSERT( pRemove->m_pSquadLeader == this );
if( SquadCount() > 2 )
{
// Removing the leader, promote m_pSquadNext to leader
if( pRemove == this )
{
CFlockingFlyer *pLeader = m_pSquadNext;
if( pLeader )
{
// copy the enemy LKP to the new leader
pLeader->m_vecEnemyLKP = m_vecEnemyLKP;
CFlockingFlyer *pList = pLeader;
while( pList )
{
pList->m_pSquadLeader = pLeader;
pList = pList->m_pSquadNext;
}
}
SquadUnlink();
}
else // removing a node
{
CFlockingFlyer *pList = this;
// Find the node before pRemove
while( pList->m_pSquadNext != pRemove )
{
// assert to test valid list construction
ASSERT( pList->m_pSquadNext != NULL );
pList = pList->m_pSquadNext;
}
// List validity
ASSERT( pList->m_pSquadNext == pRemove );
// Relink without pRemove
pList->m_pSquadNext = pRemove->m_pSquadNext;
// Unlink pRemove
pRemove->SquadUnlink();
}
}
else
SquadDisband();
}
//=========================================================
//
// SquadCount(), return the number of members of this squad
// callable from leaders & followers
//
//=========================================================
int CFlockingFlyer::SquadCount( void )
{
CFlockingFlyer *pList = m_pSquadLeader;
int squadCount = 0;
while( pList )
{
squadCount++;
pList = pList->m_pSquadNext;
}
return squadCount;
}
//=========================================================
//
// SquadDisband(), Unlink all squad members
//
//=========================================================
void CFlockingFlyer::SquadDisband( void )
{
CFlockingFlyer *pList = m_pSquadLeader;
CFlockingFlyer *pNext;
while( pList )
{
pNext = pList->m_pSquadNext;
pList->SquadUnlink();
pList = pNext;
}
}

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

View File

@ -1,459 +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.
*
****/
//=========================================================
// barnacle - stationary ceiling mounted 'fishing' monster
//=========================================================
#include "extdll.h"
#include "util.h"
#include "cbase.h"
#include "monsters.h"
#include "schedule.h"
#define BARNACLE_BODY_HEIGHT 44 // how 'tall' the barnacle's model is.
#define BARNACLE_PULL_SPEED 8
#define BARNACLE_KILL_VICTIM_DELAY 5 // how many seconds after pulling prey in to gib them.
//=========================================================
// Monster's Anim Events Go Here
//=========================================================
#define BARNACLE_AE_PUKEGIB 2
class CBarnacle : public CBaseMonster
{
public:
void Spawn( void );
void Precache( void );
CBaseEntity *TongueTouchEnt( float *pflLength );
int Classify( void );
void HandleAnimEvent( MonsterEvent_t *pEvent );
void EXPORT BarnacleThink( void );
void EXPORT WaitTillDead( void );
void Killed( entvars_t *pevAttacker, int iGib );
int TakeDamage( entvars_t *pevInflictor, entvars_t *pevAttacker, float flDamage, int bitsDamageType );
virtual int Save( CSave &save );
virtual int Restore( CRestore &restore );
static TYPEDESCRIPTION m_SaveData[];
float m_flAltitude;
float m_flCachedLength; // tongue cached length
float m_flKillVictimTime;
int m_cGibs; // barnacle loads up on gibs each time it kills something.
BOOL m_fTongueExtended;
BOOL m_fLiftingPrey;
float m_flTongueAdj;
// FIXME: need a custom barnacle model with non-generic hitgroup
// otherwise we can apply to damage to tongue instead of body
#ifdef BARNACLE_FIX_VISIBILITY
void SetObjectCollisionBox( void )
{
pev->absmin = pev->origin + Vector( -16, -16, -m_flCachedLength );
pev->absmax = pev->origin + Vector( 16, 16, 0 );
}
#endif
};
LINK_ENTITY_TO_CLASS( monster_barnacle, CBarnacle )
TYPEDESCRIPTION CBarnacle::m_SaveData[] =
{
DEFINE_FIELD( CBarnacle, m_flAltitude, FIELD_FLOAT ),
DEFINE_FIELD( CBarnacle, m_flKillVictimTime, FIELD_TIME ),
DEFINE_FIELD( CBarnacle, m_cGibs, FIELD_INTEGER ),// barnacle loads up on gibs each time it kills something.
DEFINE_FIELD( CBarnacle, m_fTongueExtended, FIELD_BOOLEAN ),
DEFINE_FIELD( CBarnacle, m_fLiftingPrey, FIELD_BOOLEAN ),
DEFINE_FIELD( CBarnacle, m_flTongueAdj, FIELD_FLOAT ),
DEFINE_FIELD( CBarnacle, m_flCachedLength, FIELD_FLOAT ),
};
IMPLEMENT_SAVERESTORE( CBarnacle, CBaseMonster )
//=========================================================
// Classify - indicates this monster's place in the
// relationship table.
//=========================================================
int CBarnacle::Classify( void )
{
return CLASS_ALIEN_MONSTER;
}
//=========================================================
// HandleAnimEvent - catches the monster-specific messages
// that occur when tagged animation frames are played.
//
// Returns number of events handled, 0 if none.
//=========================================================
void CBarnacle::HandleAnimEvent( MonsterEvent_t *pEvent )
{
switch( pEvent->event )
{
case BARNACLE_AE_PUKEGIB:
CGib::SpawnRandomGibs( pev, 1, 1 );
break;
default:
CBaseMonster::HandleAnimEvent( pEvent );
break;
}
}
//=========================================================
// Spawn
//=========================================================
void CBarnacle::Spawn()
{
Precache();
SET_MODEL( ENT( pev ), "models/barnacle.mdl" );
UTIL_SetSize( pev, Vector( -16, -16, -32 ), Vector( 16, 16, 0 ) );
pev->solid = SOLID_SLIDEBOX;
pev->movetype = MOVETYPE_NONE;
pev->takedamage = DAMAGE_AIM;
m_bloodColor = BLOOD_COLOR_RED;
pev->effects = EF_INVLIGHT; // take light from the ceiling
pev->health = 25;
m_flFieldOfView = 0.5;// indicates the width of this monster's forward view cone ( as a dotproduct result )
m_MonsterState = MONSTERSTATE_NONE;
m_flKillVictimTime = 0;
m_flCachedLength = 32; // mins.z
m_cGibs = 0;
m_fLiftingPrey = FALSE;
m_flTongueAdj = -100;
InitBoneControllers();
SetActivity( ACT_IDLE );
SetThink( &CBarnacle::BarnacleThink );
pev->nextthink = gpGlobals->time + 0.5;
UTIL_SetOrigin( pev, pev->origin );
}
int CBarnacle::TakeDamage( entvars_t *pevInflictor, entvars_t *pevAttacker, float flDamage, int bitsDamageType )
{
if( bitsDamageType & DMG_CLUB )
{
flDamage = pev->health;
}
return CBaseMonster::TakeDamage( pevInflictor, pevAttacker, flDamage, bitsDamageType );
}
//=========================================================
//=========================================================
void CBarnacle::BarnacleThink( void )
{
CBaseEntity *pTouchEnt;
CBaseMonster *pVictim;
float flLength;
#ifdef BARNACLE_FIX_VISIBILITY
if( m_flCachedLength != ( m_flAltitude + m_flTongueAdj ) || ( pev->absmin.z != pev->origin.z + -m_flCachedLength ) )
{
// recalc collision box here to avoid barnacle disappears bug
m_flCachedLength = m_flAltitude + m_flTongueAdj;
UTIL_SetOrigin( pev, pev->origin );
}
#endif
pev->nextthink = gpGlobals->time + 0.1;
if( m_hEnemy != 0 )
{
// barnacle has prey.
if( !m_hEnemy->IsAlive() )
{
// someone (maybe even the barnacle) killed the prey. Reset barnacle.
m_fLiftingPrey = FALSE;// indicate that we're not lifting prey.
m_hEnemy = NULL;
return;
}
if( m_fLiftingPrey )
{
if( m_hEnemy != 0 && m_hEnemy->pev->deadflag != DEAD_NO )
{
// crap, someone killed the prey on the way up.
m_hEnemy = NULL;
m_fLiftingPrey = FALSE;
return;
}
// still pulling prey.
Vector vecNewEnemyOrigin = m_hEnemy->pev->origin;
vecNewEnemyOrigin.x = pev->origin.x;
vecNewEnemyOrigin.y = pev->origin.y;
// guess as to where their neck is
vecNewEnemyOrigin.x -= 6 * cos( m_hEnemy->pev->angles.y * M_PI / 180.0 );
vecNewEnemyOrigin.y -= 6 * sin( m_hEnemy->pev->angles.y * M_PI / 180.0 );
m_flAltitude -= BARNACLE_PULL_SPEED;
vecNewEnemyOrigin.z += BARNACLE_PULL_SPEED;
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 )
m_fLiftingPrey = FALSE;
EMIT_SOUND( ENT( pev ), CHAN_WEAPON, "barnacle/bcl_bite3.wav", 1, ATTN_NORM );
pVictim = m_hEnemy->MyMonsterPointer();
m_flKillVictimTime = gpGlobals->time + 10;// now that the victim is in place, the killing bite will be administered in 10 seconds.
if( pVictim )
{
pVictim->BarnacleVictimBitten( pev );
SetActivity( ACT_EAT );
}
}
UTIL_SetOrigin( m_hEnemy->pev, vecNewEnemyOrigin );
}
else
{
// prey is lifted fully into feeding position and is dangling there.
pVictim = m_hEnemy->MyMonsterPointer();
if( m_flKillVictimTime != -1 && gpGlobals->time > m_flKillVictimTime )
{
// kill!
if( pVictim )
{
pVictim->TakeDamage( pev, pev, pVictim->pev->health, DMG_SLASH | DMG_ALWAYSGIB );
m_cGibs = 3;
}
return;
}
// bite prey every once in a while
if( pVictim && ( RANDOM_LONG( 0, 49 ) == 0 ) )
{
switch( RANDOM_LONG( 0, 2 ) )
{
case 0:
EMIT_SOUND( ENT( pev ), CHAN_WEAPON, "barnacle/bcl_chew1.wav", 1, ATTN_NORM );
break;
case 1:
EMIT_SOUND( ENT( pev ), CHAN_WEAPON, "barnacle/bcl_chew2.wav", 1, ATTN_NORM );
break;
case 2:
EMIT_SOUND( ENT( pev ), CHAN_WEAPON, "barnacle/bcl_chew3.wav", 1, ATTN_NORM );
break;
}
pVictim->BarnacleVictimBitten( pev );
}
}
}
else
{
// 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() ) ) )
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.
SetActivity ( ACT_IDLE );
m_flTongueAdj = -100;
}
if( m_cGibs && RANDOM_LONG( 0, 99 ) == 1 )
{
// cough up a gib.
CGib::SpawnRandomGibs( pev, 1, 1 );
m_cGibs--;
switch ( RANDOM_LONG( 0, 2 ) )
{
case 0:
EMIT_SOUND( ENT( pev ), CHAN_WEAPON, "barnacle/bcl_chew1.wav", 1, ATTN_NORM );
break;
case 1:
EMIT_SOUND( ENT( pev ), CHAN_WEAPON, "barnacle/bcl_chew2.wav", 1, ATTN_NORM );
break;
case 2:
EMIT_SOUND( ENT( pev ), CHAN_WEAPON, "barnacle/bcl_chew3.wav", 1, ATTN_NORM );
break;
}
}
pTouchEnt = TongueTouchEnt( &flLength );
if( pTouchEnt != NULL && m_fTongueExtended )
{
// tongue is fully extended, and is touching someone.
if( pTouchEnt->FBecomeProne() )
{
EMIT_SOUND( ENT( pev ), CHAN_WEAPON, "barnacle/bcl_alert2.wav", 1, ATTN_NORM );
SetSequenceByName( "attack1" );
m_flTongueAdj = -20;
m_hEnemy = pTouchEnt;
pTouchEnt->pev->movetype = MOVETYPE_FLY;
pTouchEnt->pev->velocity = g_vecZero;
pTouchEnt->pev->basevelocity = g_vecZero;
pTouchEnt->pev->origin.x = pev->origin.x;
pTouchEnt->pev->origin.y = pev->origin.y;
m_fLiftingPrey = TRUE;// indicate that we should be lifting prey.
m_flKillVictimTime = -1;// set this to a bogus time while the victim is lifted.
m_flAltitude = pev->origin.z - pTouchEnt->EyePosition().z;
}
}
else
{
// calculate a new length for the tongue to be clear of anything else that moves under it.
if( m_flAltitude < flLength )
{
// if tongue is higher than is should be, lower it kind of slowly.
m_flAltitude += BARNACLE_PULL_SPEED;
m_fTongueExtended = FALSE;
}
else
{
m_flAltitude = flLength;
m_fTongueExtended = TRUE;
}
}
}
// ALERT( at_console, "tounge %f\n", m_flAltitude + m_flTongueAdj );
SetBoneController( 0, -( m_flAltitude + m_flTongueAdj ) );
StudioFrameAdvance( 0.1 );
}
//=========================================================
// Killed.
//=========================================================
void CBarnacle::Killed( entvars_t *pevAttacker, int iGib )
{
CBaseMonster *pVictim;
pev->solid = SOLID_NOT;
pev->takedamage = DAMAGE_NO;
if( m_hEnemy != 0 )
{
pVictim = m_hEnemy->MyMonsterPointer();
if( pVictim )
{
pVictim->BarnacleVictimReleased();
}
}
//CGib::SpawnRandomGibs( pev, 4, 1 );
switch( RANDOM_LONG ( 0, 1 ) )
{
case 0:
EMIT_SOUND( ENT( pev ), CHAN_WEAPON, "barnacle/bcl_die1.wav", 1, ATTN_NORM );
break;
case 1:
EMIT_SOUND( ENT( pev ), CHAN_WEAPON, "barnacle/bcl_die3.wav", 1, ATTN_NORM );
break;
}
SetActivity( ACT_DIESIMPLE );
SetBoneController( 0, 0 );
StudioFrameAdvance( 0.1 );
pev->nextthink = gpGlobals->time + 0.1;
SetThink( &CBarnacle::WaitTillDead );
}
//=========================================================
//=========================================================
void CBarnacle::WaitTillDead( void )
{
pev->nextthink = gpGlobals->time + 0.1;
float flInterval = StudioFrameAdvance( 0.1 );
DispatchAnimEvents( flInterval );
if( m_fSequenceFinished )
{
// death anim finished.
StopAnimation();
SetThink( NULL );
}
}
//=========================================================
// Precache - precaches all resources this monster needs
//=========================================================
void CBarnacle::Precache()
{
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
PRECACHE_SOUND( "barnacle/bcl_chew1.wav" );
PRECACHE_SOUND( "barnacle/bcl_chew2.wav" );
PRECACHE_SOUND( "barnacle/bcl_chew3.wav" );
PRECACHE_SOUND( "barnacle/bcl_die1.wav" );
PRECACHE_SOUND( "barnacle/bcl_die3.wav" );
}
//=========================================================
// TongueTouchEnt - does a trace along the barnacle's tongue
// to see if any entity is touching it. Also stores the length
// of the trace in the int pointer provided.
//=========================================================
#define BARNACLE_CHECK_SPACING 8
CBaseEntity *CBarnacle::TongueTouchEnt( float *pflLength )
{
TraceResult tr;
float length;
// trace once to hit architecture and see if the tongue needs to change position.
UTIL_TraceLine( pev->origin, pev->origin - Vector ( 0, 0, 2048 ), ignore_monsters, ENT( pev ), &tr );
length = fabs( pev->origin.z - tr.vecEndPos.z );
if( pflLength )
{
*pflLength = length;
}
Vector delta = Vector( BARNACLE_CHECK_SPACING, BARNACLE_CHECK_SPACING, 0 );
Vector mins = pev->origin - delta;
Vector maxs = pev->origin + delta;
maxs.z = pev->origin.z;
mins.z -= length;
CBaseEntity *pList[10];
int count = UTIL_EntitiesInBox( pList, 10, mins, maxs, ( FL_CLIENT | FL_MONSTER ) );
if( count )
{
for( int i = 0; i < count; i++ )
{
// only clients and monsters
if( pList[i] != this && IRelationship( pList[i] ) > R_NO && pList[ i ]->pev->deadflag == DEAD_NO ) // this ent is one of our enemies. Barnacle tries to eat it.
{
return pList[i];
}
}
}
return NULL;
}

View File

@ -1,826 +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.
*
****/
//=========================================================
// monster template
//=========================================================
// UNDONE: Holster weapon?
#include "extdll.h"
#include "util.h"
#include "cbase.h"
#include "monsters.h"
#include "talkmonster.h"
#include "schedule.h"
#include "defaultai.h"
#include "scripted.h"
#include "weapons.h"
#include "soundent.h"
//=========================================================
// Monster's Anim Events Go Here
//=========================================================
// first flag is barney dying for scripted sequences?
#define BARNEY_AE_DRAW ( 2 )
#define BARNEY_AE_SHOOT ( 3 )
#define BARNEY_AE_HOLSTER ( 4 )
#define BARNEY_BODY_GUNHOLSTERED 0
#define BARNEY_BODY_GUNDRAWN 1
#define BARNEY_BODY_GUNGONE 2
class CBarney : public CTalkMonster
{
public:
void Spawn( void );
void Precache( void );
void SetYawSpeed( void );
int ISoundMask( void );
void BarneyFirePistol( void );
void AlertSound( void );
int Classify( void );
void HandleAnimEvent( MonsterEvent_t *pEvent );
void RunTask( Task_t *pTask );
void StartTask( Task_t *pTask );
virtual int ObjectCaps( void ) { return CTalkMonster :: ObjectCaps() | FCAP_IMPULSE_USE; }
int TakeDamage( entvars_t* pevInflictor, entvars_t* pevAttacker, float flDamage, int bitsDamageType);
BOOL CheckRangeAttack1( float flDot, float flDist );
void DeclineFollowing( void );
// Override these to set behavior
Schedule_t *GetScheduleOfType( int Type );
Schedule_t *GetSchedule( void );
MONSTERSTATE GetIdealState( void );
void DeathSound( void );
void PainSound( void );
void TalkInit( void );
void TraceAttack( entvars_t *pevAttacker, float flDamage, Vector vecDir, TraceResult *ptr, int bitsDamageType);
void Killed( entvars_t *pevAttacker, int iGib );
virtual int Save( CSave &save );
virtual int Restore( CRestore &restore );
static TYPEDESCRIPTION m_SaveData[];
BOOL m_fGunDrawn;
float m_painTime;
float m_checkAttackTime;
BOOL m_lastAttackCheck;
// UNDONE: What is this for? It isn't used?
float m_flPlayerDamage;// how much pain has the player inflicted on me?
CUSTOM_SCHEDULES
};
LINK_ENTITY_TO_CLASS( monster_barney, CBarney )
TYPEDESCRIPTION CBarney::m_SaveData[] =
{
DEFINE_FIELD( CBarney, m_fGunDrawn, FIELD_BOOLEAN ),
DEFINE_FIELD( CBarney, m_painTime, FIELD_TIME ),
DEFINE_FIELD( CBarney, m_checkAttackTime, FIELD_TIME ),
DEFINE_FIELD( CBarney, m_lastAttackCheck, FIELD_BOOLEAN ),
DEFINE_FIELD( CBarney, m_flPlayerDamage, FIELD_FLOAT ),
};
IMPLEMENT_SAVERESTORE( CBarney, CTalkMonster )
//=========================================================
// AI Schedules Specific to this monster
//=========================================================
Task_t tlBaFollow[] =
{
{ TASK_MOVE_TO_TARGET_RANGE, (float)128 }, // Move within 128 of target ent (client)
{ TASK_SET_SCHEDULE, (float)SCHED_TARGET_FACE },
};
Schedule_t slBaFollow[] =
{
{
tlBaFollow,
ARRAYSIZE( tlBaFollow ),
bits_COND_NEW_ENEMY |
bits_COND_LIGHT_DAMAGE |
bits_COND_HEAVY_DAMAGE |
bits_COND_HEAR_SOUND |
bits_COND_PROVOKED,
bits_SOUND_DANGER,
"Follow"
},
};
//=========================================================
// BarneyDraw - much better looking draw schedule for when
// barney knows who he's gonna attack.
//=========================================================
Task_t tlBarneyEnemyDraw[] =
{
{ TASK_STOP_MOVING, 0 },
{ TASK_FACE_ENEMY, 0 },
{ TASK_PLAY_SEQUENCE_FACE_ENEMY, (float) ACT_ARM },
};
Schedule_t slBarneyEnemyDraw[] =
{
{
tlBarneyEnemyDraw,
ARRAYSIZE( tlBarneyEnemyDraw ),
0,
0,
"Barney Enemy Draw"
}
};
Task_t tlBaFaceTarget[] =
{
{ TASK_SET_ACTIVITY, (float)ACT_IDLE },
{ TASK_FACE_TARGET, (float)0 },
{ TASK_SET_ACTIVITY, (float)ACT_IDLE },
{ TASK_SET_SCHEDULE, (float)SCHED_TARGET_CHASE },
};
Schedule_t slBaFaceTarget[] =
{
{
tlBaFaceTarget,
ARRAYSIZE( tlBaFaceTarget ),
bits_COND_CLIENT_PUSH |
bits_COND_NEW_ENEMY |
bits_COND_LIGHT_DAMAGE |
bits_COND_HEAVY_DAMAGE |
bits_COND_HEAR_SOUND |
bits_COND_PROVOKED,
bits_SOUND_DANGER,
"FaceTarget"
},
};
Task_t tlIdleBaStand[] =
{
{ TASK_STOP_MOVING, 0 },
{ TASK_SET_ACTIVITY, (float)ACT_IDLE },
{ TASK_WAIT, (float)2 }, // repick IDLESTAND every two seconds.
{ TASK_TLK_HEADRESET, (float)0 }, // reset head position
};
Schedule_t slIdleBaStand[] =
{
{
tlIdleBaStand,
ARRAYSIZE( tlIdleBaStand ),
bits_COND_NEW_ENEMY |
bits_COND_LIGHT_DAMAGE |
bits_COND_HEAVY_DAMAGE |
bits_COND_HEAR_SOUND |
bits_COND_SMELL |
bits_COND_PROVOKED,
bits_SOUND_COMBAT |// sound flags - change these, and you'll break the talking code.
//bits_SOUND_PLAYER |
//bits_SOUND_WORLD |
bits_SOUND_DANGER |
bits_SOUND_MEAT |// scents
bits_SOUND_CARCASS |
bits_SOUND_GARBAGE,
"IdleStand"
},
};
DEFINE_CUSTOM_SCHEDULES( CBarney )
{
slBaFollow,
slBarneyEnemyDraw,
slBaFaceTarget,
slIdleBaStand,
};
IMPLEMENT_CUSTOM_SCHEDULES( CBarney, CTalkMonster )
void CBarney::StartTask( Task_t *pTask )
{
CTalkMonster::StartTask( pTask );
}
void CBarney::RunTask( Task_t *pTask )
{
switch( pTask->iTask )
{
case TASK_RANGE_ATTACK1:
if( m_hEnemy != 0 && ( m_hEnemy->IsPlayer() ) )
{
pev->framerate = 1.5;
}
CTalkMonster::RunTask( pTask );
break;
default:
CTalkMonster::RunTask( pTask );
break;
}
}
//=========================================================
// ISoundMask - returns a bit mask indicating which types
// of sounds this monster regards.
//=========================================================
int CBarney::ISoundMask( void)
{
return bits_SOUND_WORLD |
bits_SOUND_COMBAT |
bits_SOUND_CARCASS |
bits_SOUND_MEAT |
bits_SOUND_GARBAGE |
bits_SOUND_DANGER |
bits_SOUND_PLAYER;
}
//=========================================================
// Classify - indicates this monster's place in the
// relationship table.
//=========================================================
int CBarney::Classify( void )
{
return CLASS_PLAYER_ALLY;
}
//=========================================================
// ALertSound - barney says "Freeze!"
//=========================================================
void CBarney::AlertSound( void )
{
if( m_hEnemy != 0 )
{
if( FOkToSpeak() )
{
PlaySentence( "BA_ATTACK", RANDOM_FLOAT( 2.8, 3.2 ), VOL_NORM, ATTN_IDLE );
}
}
}
//=========================================================
// SetYawSpeed - allows each sequence to have a different
// turn rate associated with it.
//=========================================================
void CBarney::SetYawSpeed( void )
{
int ys;
ys = 0;
switch ( m_Activity )
{
case ACT_IDLE:
ys = 70;
break;
case ACT_WALK:
ys = 70;
break;
case ACT_RUN:
ys = 90;
break;
default:
ys = 70;
break;
}
pev->yaw_speed = ys;
}
//=========================================================
// CheckRangeAttack1
//=========================================================
BOOL CBarney::CheckRangeAttack1( float flDot, float flDist )
{
if( flDist <= 1024 && flDot >= 0.5 )
{
if( gpGlobals->time > m_checkAttackTime )
{
TraceResult tr;
Vector shootOrigin = pev->origin + Vector( 0, 0, 55 );
CBaseEntity *pEnemy = m_hEnemy;
Vector shootTarget = ( ( pEnemy->BodyTarget( shootOrigin ) - pEnemy->pev->origin ) + m_vecEnemyLKP );
UTIL_TraceLine( shootOrigin, shootTarget, dont_ignore_monsters, ENT( pev ), &tr );
m_checkAttackTime = gpGlobals->time + 1;
if( tr.flFraction == 1.0 || ( tr.pHit != NULL && CBaseEntity::Instance( tr.pHit ) == pEnemy ) )
m_lastAttackCheck = TRUE;
else
m_lastAttackCheck = FALSE;
m_checkAttackTime = gpGlobals->time + 1.5;
}
return m_lastAttackCheck;
}
return FALSE;
}
//=========================================================
// BarneyFirePistol - shoots one round from the pistol at
// the enemy barney is facing.
//=========================================================
void CBarney::BarneyFirePistol( void )
{
Vector vecShootOrigin;
UTIL_MakeVectors( pev->angles );
vecShootOrigin = pev->origin + Vector( 0, 0, 55 );
Vector vecShootDir = ShootAtEnemy( vecShootOrigin );
Vector angDir = UTIL_VecToAngles( vecShootDir );
SetBlending( 0, angDir.x );
pev->effects = EF_MUZZLEFLASH;
FireBullets( 1, vecShootOrigin, vecShootDir, VECTOR_CONE_2DEGREES, 1024, BULLET_MONSTER_9MM );
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 );
// UNDONE: Reload?
m_cAmmoLoaded--;// take away a bullet!
}
//=========================================================
// HandleAnimEvent - catches the monster-specific messages
// that occur when tagged animation frames are played.
//
// Returns number of events handled, 0 if none.
//=========================================================
void CBarney::HandleAnimEvent( MonsterEvent_t *pEvent )
{
switch( pEvent->event )
{
case BARNEY_AE_SHOOT:
BarneyFirePistol();
break;
case BARNEY_AE_DRAW:
// barney's bodygroup switches here so he can pull gun from holster
pev->body = BARNEY_BODY_GUNDRAWN;
m_fGunDrawn = TRUE;
break;
case BARNEY_AE_HOLSTER:
// change bodygroup to replace gun in holster
pev->body = BARNEY_BODY_GUNHOLSTERED;
m_fGunDrawn = FALSE;
break;
default:
CTalkMonster::HandleAnimEvent( pEvent );
}
}
//=========================================================
// Spawn
//=========================================================
void CBarney::Spawn()
{
Precache();
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;
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;
pev->body = 0; // gun in holster
m_fGunDrawn = FALSE;
m_afCapability = bits_CAP_HEAR | bits_CAP_TURN_HEAD | bits_CAP_DOORS_GROUP;
MonsterInit();
SetUse( &CTalkMonster::FollowerUse );
}
//=========================================================
// Precache - precaches all resources this monster needs
//=========================================================
void CBarney::Precache()
{
PRECACHE_MODEL( "models/barney.mdl" );
PRECACHE_SOUND( "barney/ba_attack1.wav" );
PRECACHE_SOUND( "barney/ba_attack2.wav" );
PRECACHE_SOUND( "barney/ba_pain1.wav" );
PRECACHE_SOUND( "barney/ba_pain2.wav" );
PRECACHE_SOUND( "barney/ba_pain3.wav" );
PRECACHE_SOUND( "barney/ba_die1.wav" );
PRECACHE_SOUND( "barney/ba_die2.wav" );
PRECACHE_SOUND( "barney/ba_die3.wav" );
// every new barney must call this, otherwise
// when a level is loaded, nobody will talk (time is reset to 0)
TalkInit();
CTalkMonster::Precache();
}
// Init talk data
void CBarney::TalkInit()
{
CTalkMonster::TalkInit();
// scientists speach group names (group names are in sentences.txt)
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_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_SMELL] = "BA_SMELL";
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;
}
static BOOL 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;
}
int CBarney::TakeDamage( entvars_t *pevInflictor, entvars_t *pevAttacker, float flDamage, int bitsDamageType )
{
// make sure friends talk about it if player hurts talkmonsters...
int ret = CTalkMonster::TakeDamage( pevInflictor, pevAttacker, flDamage, bitsDamageType );
if( !IsAlive() || pev->deadflag == DEAD_DYING )
return ret;
if( m_MonsterState != MONSTERSTATE_PRONE && ( pevAttacker->flags & FL_CLIENT ) )
{
m_flPlayerDamage += flDamage;
// This is a heurstic to determine if the player intended to harm me
// If I have an enemy, we can't establish intent (may just be crossfire)
if( m_hEnemy == 0 )
{
// If the player was facing directly at me, or I'm already suspicious, get mad
if( ( m_afMemory & bits_MEMORY_SUSPICIOUS ) || IsFacing( pevAttacker, pev->origin ) )
{
// Alright, now I'm pissed!
PlaySentence( "BA_MAD", 4, VOL_NORM, ATTN_NORM );
Remember( bits_MEMORY_PROVOKED );
StopFollowing( TRUE );
}
else
{
// Hey, be careful with that
PlaySentence( "BA_SHOT", 4, VOL_NORM, ATTN_NORM );
Remember( bits_MEMORY_SUSPICIOUS );
}
}
else if( !( m_hEnemy->IsPlayer()) && pev->deadflag == DEAD_NO )
{
PlaySentence( "BA_SHOT", 4, VOL_NORM, ATTN_NORM );
}
}
return ret;
}
//=========================================================
// PainSound
//=========================================================
void CBarney::PainSound( void )
{
if( gpGlobals->time < m_painTime )
return;
m_painTime = gpGlobals->time + RANDOM_FLOAT( 0.5, 0.75 );
switch( RANDOM_LONG( 0, 2 ) )
{
case 0:
EMIT_SOUND_DYN( ENT( pev ), CHAN_VOICE, "barney/ba_pain1.wav", 1, ATTN_NORM, 0, GetVoicePitch() );
break;
case 1:
EMIT_SOUND_DYN( ENT( pev ), CHAN_VOICE, "barney/ba_pain2.wav", 1, ATTN_NORM, 0, GetVoicePitch() );
break;
case 2:
EMIT_SOUND_DYN( ENT( pev ), CHAN_VOICE, "barney/ba_pain3.wav", 1, ATTN_NORM, 0, GetVoicePitch() );
break;
}
}
//=========================================================
// DeathSound
//=========================================================
void CBarney::DeathSound( void )
{
switch( RANDOM_LONG( 0, 2 ) )
{
case 0:
EMIT_SOUND_DYN( ENT( pev ), CHAN_VOICE, "barney/ba_die1.wav", 1, ATTN_NORM, 0, GetVoicePitch() );
break;
case 1:
EMIT_SOUND_DYN( ENT( pev ), CHAN_VOICE, "barney/ba_die2.wav", 1, ATTN_NORM, 0, GetVoicePitch() );
break;
case 2:
EMIT_SOUND_DYN( ENT( pev ), CHAN_VOICE, "barney/ba_die3.wav", 1, ATTN_NORM, 0, GetVoicePitch() );
break;
}
}
void CBarney::TraceAttack( entvars_t *pevAttacker, float flDamage, Vector vecDir, TraceResult *ptr, int bitsDamageType )
{
switch( ptr->iHitgroup )
{
case HITGROUP_CHEST:
case HITGROUP_STOMACH:
if (bitsDamageType & ( DMG_BULLET | DMG_SLASH | DMG_BLAST ) )
{
flDamage = flDamage / 2;
}
break;
case 10:
if( bitsDamageType & ( DMG_BULLET | DMG_SLASH | DMG_CLUB ) )
{
flDamage -= 20;
if( flDamage <= 0 )
{
UTIL_Ricochet( ptr->vecEndPos, 1.0 );
flDamage = 0.01;
}
}
// always a head shot
ptr->iHitgroup = HITGROUP_HEAD;
break;
}
CTalkMonster::TraceAttack( pevAttacker, flDamage, vecDir, ptr, bitsDamageType );
}
void CBarney::Killed( entvars_t *pevAttacker, int iGib )
{
if( pev->body < BARNEY_BODY_GUNGONE )
{
// drop the gun!
Vector vecGunPos;
Vector vecGunAngles;
pev->body = BARNEY_BODY_GUNGONE;
GetAttachment( 0, vecGunPos, vecGunAngles );
DropItem( "weapon_9mmhandgun", vecGunPos, vecGunAngles );
}
SetUse( NULL );
CTalkMonster::Killed( pevAttacker, iGib );
}
//=========================================================
// AI Schedules Specific to this monster
//=========================================================
Schedule_t *CBarney::GetScheduleOfType( int Type )
{
Schedule_t *psched;
switch( Type )
{
case SCHED_ARM_WEAPON:
if( m_hEnemy != 0 )
{
// face enemy, then draw.
return slBarneyEnemyDraw;
}
break;
// Hook these to make a looping schedule
case SCHED_TARGET_FACE:
// call base class default so that barney will talk
// when 'used'
psched = CTalkMonster::GetScheduleOfType( Type );
if( psched == slIdleStand )
return slBaFaceTarget; // override this for different target face behavior
else
return psched;
case SCHED_TARGET_CHASE:
return slBaFollow;
case SCHED_IDLE_STAND:
// call base class default so that scientist will talk
// when standing during idle
psched = CTalkMonster::GetScheduleOfType( Type );
if( psched == slIdleStand )
{
// just look straight ahead.
return slIdleBaStand;
}
else
return psched;
}
return CTalkMonster::GetScheduleOfType( Type );
}
//=========================================================
// 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 *CBarney::GetSchedule( void )
{
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_ENEMY_DEAD ) && FOkToSpeak() )
{
PlaySentence( "BA_KILL", 4, VOL_NORM, ATTN_NORM );
}
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();
}
// always act surprized with a new enemy
if( HasConditions( bits_COND_NEW_ENEMY ) && HasConditions( bits_COND_LIGHT_DAMAGE ) )
return GetScheduleOfType( SCHED_SMALL_FLINCH );
// wait for one schedule to draw gun
if( !m_fGunDrawn )
return GetScheduleOfType( SCHED_ARM_WEAPON );
if( HasConditions( bits_COND_HEAVY_DAMAGE ) )
return GetScheduleOfType( SCHED_TAKE_COVER_FROM_ENEMY );
}
break;
case MONSTERSTATE_ALERT:
case MONSTERSTATE_IDLE:
if( HasConditions( bits_COND_LIGHT_DAMAGE | bits_COND_HEAVY_DAMAGE ) )
{
// flinch if hurt
return GetScheduleOfType( SCHED_SMALL_FLINCH );
}
if( m_hEnemy == 0 && IsFollowing() )
{
if( !m_hTargetEnt->IsAlive() )
{
// UNDONE: Comment about the recently dead player here?
StopFollowing( FALSE );
break;
}
else
{
if( HasConditions( bits_COND_CLIENT_PUSH ) )
{
return GetScheduleOfType( SCHED_MOVE_AWAY_FOLLOW );
}
return GetScheduleOfType( SCHED_TARGET_FACE );
}
}
if( HasConditions( bits_COND_CLIENT_PUSH ) )
{
return GetScheduleOfType( SCHED_MOVE_AWAY );
}
// try to say something about smells
TrySmellTalk();
break;
default:
break;
}
return CTalkMonster::GetSchedule();
}
MONSTERSTATE CBarney::GetIdealState( void )
{
return CTalkMonster::GetIdealState();
}
void CBarney::DeclineFollowing( void )
{
PlaySentence( "BA_POK", 2, VOL_NORM, ATTN_NORM );
}
//=========================================================
// DEAD BARNEY PROP
//
// Designer selects a pose in worldcraft, 0 through num_poses-1
// this value is added to what is selected as the 'first dead pose'
// among the monster's normal animations. All dead poses must
// appear sequentially in the model file. Be sure and set
// the m_iFirstPose properly!
//
//=========================================================
class CDeadBarney : public CBaseMonster
{
public:
void Spawn( void );
int Classify( void ) { return CLASS_PLAYER_ALLY; }
void KeyValue( KeyValueData *pkvd );
int m_iPose;// which sequence to display -- temporary, don't need to save
static const char *m_szPoses[3];
};
const char *CDeadBarney::m_szPoses[] = { "lying_on_back", "lying_on_side", "lying_on_stomach" };
void CDeadBarney::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_barney_dead, CDeadBarney )
//=========================================================
// ********** DeadBarney SPAWN **********
//=========================================================
void CDeadBarney::Spawn()
{
PRECACHE_MODEL( "models/barney.mdl" );
SET_MODEL( ENT( pev ), "models/barney.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 barney with bad pose\n" );
}
// Corpses have less health
pev->health = 8;//gSkillData.barneyHealth;
MonsterInitDead();
}

File diff suppressed because it is too large Load Diff

View File

@ -1,212 +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.
*
****/
//=========================================================
// 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
//=========================================================

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

View File

@ -1,561 +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.
*
****/
#if !defined( OEM_BUILD ) && !defined( HLDEMO_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"
#ifndef CLIENT_DLL
#define BOLT_AIR_VELOCITY 2000
#define BOLT_WATER_VELOCITY 1000
extern BOOL gPhysicsInterfaceInitialized;
// 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 );
void Precache( void );
int Classify( void );
void EXPORT BubbleThink( void );
void EXPORT BoltTouch( CBaseEntity *pOther );
void EXPORT ExplodeThink( void );
int m_iTrail;
public:
static CCrossbowBolt *BoltCreate( void );
};
LINK_ENTITY_TO_CLASS( crossbow_bolt, CCrossbowBolt )
CCrossbowBolt *CCrossbowBolt::BoltCreate( void )
{
// Create a new entity with CCrossbowBolt private data
CCrossbowBolt *pBolt = GetClassPtr( (CCrossbowBolt *)NULL );
pBolt->pev->classname = MAKE_STRING( "crossbow_bolt" ); // g-cont. enable save\restore
pBolt->Spawn();
return pBolt;
}
void CCrossbowBolt::Spawn()
{
Precache();
pev->movetype = MOVETYPE_FLY;
pev->solid = SOLID_BBOX;
pev->gravity = 0.5;
SET_MODEL( ENT( pev ), "models/crossbow_bolt.mdl" );
UTIL_SetOrigin( pev, pev->origin );
UTIL_SetSize( pev, Vector( 0, 0, 0 ), Vector( 0, 0, 0 ) );
SetTouch( &CCrossbowBolt::BoltTouch );
SetThink( &CCrossbowBolt::BubbleThink );
pev->nextthink = gpGlobals->time + 0.2;
}
void CCrossbowBolt::Precache()
{
PRECACHE_MODEL( "models/crossbow_bolt.mdl" );
PRECACHE_SOUND( "weapons/xbow_hitbod1.wav" );
PRECACHE_SOUND( "weapons/xbow_hitbod2.wav" );
PRECACHE_SOUND( "weapons/xbow_fly1.wav" );
PRECACHE_SOUND( "weapons/xbow_hit1.wav" );
PRECACHE_SOUND( "fvox/beep.wav" );
m_iTrail = PRECACHE_MODEL( "sprites/streak.spr" );
}
int CCrossbowBolt::Classify( void )
{
return CLASS_NONE;
}
void CCrossbowBolt::BoltTouch( CBaseEntity *pOther )
{
SetTouch( NULL );
SetThink( NULL );
if( pOther->pev->takedamage )
{
TraceResult tr = UTIL_GetGlobalTrace();
entvars_t *pevOwner;
pevOwner = VARS( pev->owner );
// UNDONE: this needs to call TraceAttack instead
ClearMultiDamage();
if( pOther->IsPlayer() )
{
pOther->TraceAttack( pevOwner, gSkillData.plrDmgCrossbowClient, pev->velocity.Normalize(), &tr, DMG_NEVERGIB );
}
else
{
pOther->TraceAttack( pevOwner, gSkillData.plrDmgCrossbowMonster, pev->velocity.Normalize(), &tr, DMG_BULLET | DMG_NEVERGIB );
}
ApplyMultiDamage( pev, pevOwner );
pev->velocity = Vector( 0, 0, 0 );
// play body "thwack" sound
switch( RANDOM_LONG( 0, 1 ) )
{
case 0:
EMIT_SOUND( ENT( pev ), CHAN_BODY, "weapons/xbow_hitbod1.wav", 1, ATTN_NORM );
break;
case 1:
EMIT_SOUND( ENT( pev ), CHAN_BODY, "weapons/xbow_hitbod2.wav", 1, ATTN_NORM );
break;
}
if( !g_pGameRules->IsMultiplayer() )
{
Killed( pev, GIB_NEVER );
}
}
else
{
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( &CBaseEntity::SUB_Remove );
pev->nextthink = gpGlobals->time;// this will get changed below if the bolt is allowed to stick in what it hit.
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;
}
else if( pOther->pev->movetype == MOVETYPE_PUSH || pOther->pev->movetype == MOVETYPE_PUSHSTEP )
{
Vector vecDir = pev->velocity.Normalize();
UTIL_SetOrigin( pev, pev->origin - vecDir * 12 );
pev->angles = UTIL_VecToAngles( vecDir );
pev->solid = SOLID_NOT;
pev->velocity = Vector( 0, 0, 0 );
pev->avelocity.z = 0;
pev->angles.z = RANDOM_LONG( 0, 360 );
pev->nextthink = gpGlobals->time + 10.0;
if (gPhysicsInterfaceInitialized) {
// g-cont. Setup movewith feature
pev->movetype = MOVETYPE_COMPOUND; // set movewith type
pev->aiment = ENT( pOther->pev ); // set parent
}
}
if( UTIL_PointContents( pev->origin ) != CONTENTS_WATER )
{
UTIL_Sparks( pev->origin );
}
}
if( g_pGameRules->IsMultiplayer() )
{
SetThink( &CCrossbowBolt::ExplodeThink );
pev->nextthink = gpGlobals->time + 0.1;
}
}
void CCrossbowBolt::BubbleThink( void )
{
pev->nextthink = gpGlobals->time + 0.1;
if( pev->waterlevel == 0 )
return;
UTIL_BubbleTrail( pev->origin - pev->velocity * 0.1, pev->origin, 1 );
}
void CCrossbowBolt::ExplodeThink( void )
{
int iContents = UTIL_PointContents( pev->origin );
int iScale;
pev->dmg = 40;
iScale = 10;
MESSAGE_BEGIN( MSG_PVS, SVC_TEMPENTITY, pev->origin );
WRITE_BYTE( TE_EXPLOSION );
WRITE_COORD( pev->origin.x );
WRITE_COORD( pev->origin.y );
WRITE_COORD( pev->origin.z );
if( iContents != CONTENTS_WATER )
{
WRITE_SHORT( g_sModelIndexFireball );
}
else
{
WRITE_SHORT( g_sModelIndexWExplosion );
}
WRITE_BYTE( iScale ); // scale * 10
WRITE_BYTE( 15 ); // framerate
WRITE_BYTE( TE_EXPLFLAG_NONE );
MESSAGE_END();
entvars_t *pevOwner;
if( pev->owner )
pevOwner = VARS( pev->owner );
else
pevOwner = NULL;
pev->owner = NULL; // can't traceline attack owner if this is set
::RadiusDamage( pev->origin, pev, pevOwner, pev->dmg, 128, CLASS_NONE, DMG_BLAST | DMG_ALWAYSGIB );
UTIL_Remove( this );
}
#endif
enum crossbow_e
{
CROSSBOW_IDLE1 = 0, // full
CROSSBOW_IDLE2, // empty
CROSSBOW_FIDGET1, // full
CROSSBOW_FIDGET2, // empty
CROSSBOW_FIRE1, // full
CROSSBOW_FIRE2, // reload
CROSSBOW_FIRE3, // empty
CROSSBOW_RELOAD, // from empty
CROSSBOW_DRAW1, // full
CROSSBOW_DRAW2, // empty
CROSSBOW_HOLSTER1, // full
CROSSBOW_HOLSTER2 // empty
};
LINK_ENTITY_TO_CLASS( weapon_crossbow, CCrossbow )
void CCrossbow::Spawn()
{
Precache();
m_iId = WEAPON_CROSSBOW;
SET_MODEL( ENT( pev ), "models/w_crossbow.mdl" );
m_iDefaultAmmo = CROSSBOW_DEFAULT_GIVE;
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" );
PRECACHE_MODEL( "models/v_crossbow.mdl" );
PRECACHE_MODEL( "models/p_crossbow.mdl" );
PRECACHE_SOUND( "weapons/xbow_fire1.wav" );
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" );
}
int CCrossbow::GetItemInfo( ItemInfo *p )
{
p->pszName = STRING( pev->classname );
p->pszAmmo1 = "bolts";
p->iMaxAmmo1 = BOLT_MAX_CARRY;
p->pszAmmo2 = NULL;
p->iMaxAmmo2 = -1;
p->iMaxClip = CROSSBOW_MAX_CLIP;
p->iSlot = 2;
p->iPosition = 2;
p->iId = WEAPON_CROSSBOW;
p->iFlags = 0;
p->iWeight = CROSSBOW_WEIGHT;
return 1;
}
BOOL CCrossbow::Deploy()
{
if( m_iClip )
return DefaultDeploy( "models/v_crossbow.mdl", "models/p_crossbow.mdl", CROSSBOW_DRAW1, "bow" );
return DefaultDeploy( "models/v_crossbow.mdl", "models/p_crossbow.mdl", CROSSBOW_DRAW2, "bow" );
}
void CCrossbow::Holster( int skiplocal /* = 0 */ )
{
m_fInReload = FALSE;// cancel any reload in progress.
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 )
{
#ifdef CLIENT_DLL
if( m_fInZoom && bIsMultiplayer() )
#else
if( m_fInZoom && g_pGameRules->IsMultiplayer() )
#endif
{
FireSniperBolt();
return;
}
FireBolt();
}
// this function only gets called in multiplayer
void CCrossbow::FireSniperBolt()
{
m_flNextPrimaryAttack = GetNextAttackDelay( 0.75 );
if( m_iClip == 0 )
{
PlayEmptySound();
return;
}
TraceResult tr;
m_pPlayer->m_iWeaponVolume = QUIET_GUN_VOLUME;
m_iClip--;
int flags;
#if defined( CLIENT_WEAPONS )
flags = FEV_NOTHOST;
#else
flags = 0;
#endif
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 );
Vector anglesAim = m_pPlayer->pev->v_angle + m_pPlayer->pev->punchangle;
UTIL_MakeVectors( anglesAim );
Vector vecSrc = m_pPlayer->GetGunPosition() - gpGlobals->v_up * 2;
Vector vecDir = gpGlobals->v_forward;
UTIL_TraceLine( vecSrc, vecSrc + vecDir * 8192, dont_ignore_monsters, m_pPlayer->edict(), &tr );
#ifndef CLIENT_DLL
if( tr.pHit->v.takedamage )
{
ClearMultiDamage();
CBaseEntity::Instance( tr.pHit )->TraceAttack( m_pPlayer->pev, 120, vecDir, &tr, DMG_BULLET | DMG_NEVERGIB );
ApplyMultiDamage( pev, m_pPlayer->pev );
}
#endif
}
void CCrossbow::FireBolt()
{
TraceResult tr;
if( m_iClip == 0 )
{
PlayEmptySound();
return;
}
m_pPlayer->m_iWeaponVolume = QUIET_GUN_VOLUME;
m_iClip--;
int flags;
#if defined( CLIENT_WEAPONS )
flags = FEV_NOTHOST;
#else
flags = 0;
#endif
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 );
Vector anglesAim = m_pPlayer->pev->v_angle + m_pPlayer->pev->punchangle;
UTIL_MakeVectors( anglesAim );
anglesAim.x = -anglesAim.x;
#ifndef CLIENT_DLL
Vector vecSrc = m_pPlayer->GetGunPosition() - gpGlobals->v_up * 2;
Vector vecDir = gpGlobals->v_forward;
CCrossbowBolt *pBolt = CCrossbowBolt::BoltCreate();
pBolt->pev->origin = vecSrc;
pBolt->pev->angles = anglesAim;
pBolt->pev->owner = m_pPlayer->edict();
if( m_pPlayer->pev->waterlevel == 3 )
{
pBolt->pev->velocity = vecDir * BOLT_WATER_VELOCITY;
pBolt->pev->speed = BOLT_WATER_VELOCITY;
}
else
{
pBolt->pev->velocity = vecDir * BOLT_AIR_VELOCITY;
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_flNextPrimaryAttack = GetNextAttackDelay( 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;
}
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_pPlayer->m_rgAmmo[m_iPrimaryAmmoType] <= 0 || m_iClip == CROSSBOW_MAX_CLIP )
return;
if( m_pPlayer->pev->fov != 0 )
{
SecondaryAttack();
}
if( DefaultReload( CROSSBOW_MAX_CLIP, 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::WeaponIdle( void )
{
m_pPlayer->GetAutoaimVector( AUTOAIM_2DEGREES ); // get the autoaim vector but ignore it; used for autoaim crosshair in DM
ResetEmptySound();
if( m_flTimeWeaponIdle < UTIL_WeaponTimeBase() )
{
float flRand = UTIL_SharedRandomFloat( m_pPlayer->random_seed, 0, 1 );
if( flRand <= 0.75 )
{
if( m_iClip )
{
SendWeaponAnim( CROSSBOW_IDLE1 );
}
else
{
SendWeaponAnim( CROSSBOW_IDLE2 );
}
m_flTimeWeaponIdle = UTIL_WeaponTimeBase() + UTIL_SharedRandomFloat( m_pPlayer->random_seed, 10, 15 );
}
else
{
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;
}
m_flTimeWeaponIdle = UTIL_WeaponTimeBase() + UTIL_SharedRandomFloat( m_pPlayer->random_seed, 10, 15 );
}
}
}
class CCrossbowAmmo : public CBasePlayerAmmo
{
void Spawn( void )
{
Precache();
SET_MODEL( ENT( pev ), "models/w_crossbow_clip.mdl" );
CBasePlayerAmmo::Spawn();
}
void Precache( void )
{
PRECACHE_MODEL( "models/w_crossbow_clip.mdl" );
PRECACHE_SOUND( "items/9mmclip1.wav" );
}
BOOL AddAmmo( CBaseEntity *pOther )
{
if( pOther->GiveAmmo( AMMO_CROSSBOWCLIP_GIVE, "bolts", BOLT_MAX_CARRY ) != -1 )
{
EMIT_SOUND( ENT( pev ), CHAN_ITEM, "items/9mmclip1.wav", 1, ATTN_NORM );
return TRUE;
}
return FALSE;
}
};
LINK_ENTITY_TO_CLASS( ammo_crossbow, CCrossbowAmmo )
#endif

View File

@ -1,544 +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.
*
****/
#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->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::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 = 0.0f;
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( (int)( 255 - ( timeBlend * 180 )) );
m_pBeam->SetWidth( (int)( 40 - ( timeBlend * 20 ) ) );
if( m_fireMode == FIRE_WIDE )
m_pBeam->SetColor( (int)( 30 + ( 25 * timeBlend ) ), (int)( 30 + ( 30 * timeBlend ) ), (int)( 64 + 80 * fabs( sin( gpGlobals->time * 10 ) ) ) );
else
m_pBeam->SetColor( (int)( 60 + ( 25 * timeBlend ) ), (int)( 120 + ( 30 * timeBlend ) ), (int)( 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 > UTIL_WeaponTimeBase() )
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

View File

@ -1,268 +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.
*
****/
#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;
}

View File

@ -1,49 +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.
*
****/
// Base class for flying monsters. This overrides the movement test & execution code from CBaseMonster
#pragma once
#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

File diff suppressed because it is too large Load Diff

View File

@ -1,633 +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.
*
****/
#if !defined( OEM_BUILD ) && !defined( HLDEMO_BUILD )
#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 "shake.h"
#include "gamerules.h"
#include "game.h"
#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,
GAUSS_IDLE2,
GAUSS_FIDGET,
GAUSS_SPINUP,
GAUSS_SPIN,
GAUSS_FIRE,
GAUSS_FIRE2,
GAUSS_HOLSTER,
GAUSS_DRAW
};
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" );
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, "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 )
{
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 = 1;
p->iId = m_iId = WEAPON_GAUSS;
p->iFlags = 0;
p->iWeight = GAUSS_WEIGHT;
return 1;
}
BOOL CGauss::IsUseable()
{
// Currently charging, allow the player to fire it first. - Solokiller
return CBasePlayerWeapon::IsUseable() || m_fInAttack != 0;
}
BOOL CGauss::Deploy()
{
m_pPlayer->m_flPlayAftershock = 0.0;
return DefaultDeploy( "models/v_gauss.mdl", "models/p_gauss.mdl", GAUSS_DRAW, "gauss" );
}
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;
}
void CGauss::PrimaryAttack()
{
// don't fire underwater
if( m_pPlayer->pev->waterlevel == 3 )
{
PlayEmptySound();
m_flNextSecondaryAttack = m_flNextPrimaryAttack = GetNextAttackDelay( 0.15 );
return;
}
if( m_pPlayer->m_rgAmmo[m_iPrimaryAmmoType] < 2 )
{
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()
{
// don't fire underwater
if( m_pPlayer->pev->waterlevel == 3 )
{
if( m_fInAttack != 0 )
{
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;
}
else
{
PlayEmptySound();
}
m_flNextSecondaryAttack = m_flNextPrimaryAttack = GetNextAttackDelay( 0.5 );
return;
}
if( m_fInAttack == 0 )
{
if( m_pPlayer->m_rgAmmo[m_iPrimaryAmmoType] <= 0 )
{
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;
}
m_fPrimaryFire = FALSE;
m_pPlayer->m_rgAmmo[m_iPrimaryAmmoType]--;// take one ammo just to start the spin
m_pPlayer->m_flNextAmmoBurn = UTIL_WeaponTimeBase();
// spin up
m_pPlayer->m_iWeaponVolume = GAUSS_PRIMARY_CHARGE_VOLUME;
SendWeaponAnim( GAUSS_SPINUP );
m_fInAttack = 1;
m_flTimeWeaponIdle = UTIL_WeaponTimeBase() + 0.5;
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 )
{
if( m_flTimeWeaponIdle < UTIL_WeaponTimeBase() )
{
SendWeaponAnim( GAUSS_SPIN );
m_fInAttack = 2;
}
}
else
{
// Moved to before the ammo burn.
// Because we drained 1 when m_InAttack == 0, then 1 again now before checking if we're out of ammo,
// this resuled in the player having -1 ammo, which in turn caused CanDeploy to think it could be deployed.
// This will need to be fixed further down the line by preventing negative ammo unless explicitly required (infinite ammo?),
// But this check will prevent the problem for now. - Solokiller
// TODO: investigate further.
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;
}
// 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( UTIL_WeaponTimeBase() >= m_pPlayer->m_flAmmoStartCharge )
{
// don't eat any more ammo after gun is fully charged.
m_pPlayer->m_flNextAmmoBurn = 1000;
}
int pitch = (int)( ( 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;
}
}
}
//=========================================================
// StartFire- since all of this code has to run and then
// call Fire(), it was easier at this point to rip it out
// of weaponidle() and make its own function then to try to
// merge this into Fire(), which has some identical variable names
//=========================================================
void CGauss::StartFire( void )
{
float flDamage;
UTIL_MakeVectors( m_pPlayer->pev->v_angle + m_pPlayer->pev->punchangle );
Vector vecAiming = gpGlobals->v_forward;
Vector vecSrc = m_pPlayer->GetGunPosition(); // + gpGlobals->v_up * -8 + gpGlobals->v_right * 8;
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( !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 );
}
// time until aftershock 'static discharge' sound
m_pPlayer->m_flPlayAftershock = gpGlobals->time + UTIL_SharedRandomFloat( m_pPlayer->random_seed, 0.3, 0.8 );
Fire( vecSrc, vecAiming, flDamage );
}
void CGauss::Fire( Vector vecOrigSrc, Vector vecDir, float flDamage )
{
m_pPlayer->m_iWeaponVolume = GAUSS_PRIMARY_FIRE_VOLUME;
TraceResult tr, beam_tr;
#ifndef CLIENT_DLL
Vector vecSrc = vecOrigSrc;
Vector vecDest = vecSrc + vecDir * 8192;
edict_t *pentIgnore;
float flMaxFrac = 1.0;
int nTotal = 0;
int fHasPunched = 0;
int fFirstBeam = 1;
int nMaxHits = 10;
pentIgnore = ENT( m_pPlayer->pev );
#else
if( m_fPrimaryFire == false )
g_irunninggausspred = true;
#endif
// The main firing event is sent unreliably so it won't be delayed.
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_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\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 )
break;
CBaseEntity *pEntity = CBaseEntity::Instance( tr.pHit );
if( pEntity == NULL )
break;
if( fFirstBeam )
{
m_pPlayer->pev->effects |= EF_MUZZLEFLASH;
fFirstBeam = 0;
nTotal += 26;
}
if( pEntity->pev->takedamage )
{
ClearMultiDamage();
pEntity->TraceAttack( m_pPlayer->pev, flDamage, vecDir, &tr, DMG_BULLET );
ApplyMultiDamage( m_pPlayer->pev, m_pPlayer->pev );
}
if( pEntity->ReflectGauss() )
{
float n;
pentIgnore = NULL;
n = -DotProduct( tr.vecPlaneNormal, vecDir );
if( n < 0.5 ) // 60 degrees
{
// ALERT( at_console, "reflect %f\n", n );
// reflect
Vector r;
r = 2.0 * tr.vecPlaneNormal * n + vecDir;
flMaxFrac = flMaxFrac - tr.flFraction;
vecDir = r;
vecSrc = tr.vecEndPos + vecDir * 8;
vecDest = vecSrc + vecDir * 8192;
// explode a bit
m_pPlayer->RadiusDamage( tr.vecEndPos, pev, m_pPlayer->pev, flDamage * n, CLASS_NONE, DMG_BLAST );
nTotal += 34;
// lose energy
if( n == 0 ) n = 0.1;
flDamage = flDamage * ( 1 - n );
}
else
{
nTotal += 13;
// limit it to one hole punch
if( fHasPunched )
break;
fHasPunched = 1;
// try punching through wall if secondary attack (primary is incapable of breaking through)
if( !m_fPrimaryFire )
{
UTIL_TraceLine( tr.vecEndPos + vecDir * 8, vecDest, dont_ignore_monsters, pentIgnore, &beam_tr );
if( !beam_tr.fAllSolid )
{
// trace backwards to find exit point
UTIL_TraceLine( beam_tr.vecEndPos, tr.vecEndPos, dont_ignore_monsters, pentIgnore, &beam_tr );
n = ( beam_tr.vecEndPos - tr.vecEndPos ).Length();
if( n < flDamage )
{
if( n == 0 )
n = 1;
flDamage -= n;
// ALERT( at_console, "punch %f\n", n );
nTotal += 21;
// exit blast damage
//m_pPlayer->RadiusDamage( beam_tr.vecEndPos + vecDir * 8, pev, m_pPlayer->pev, flDamage, CLASS_NONE, DMG_BLAST );
float damage_radius;
if( g_pGameRules->IsMultiplayer() )
{
damage_radius = flDamage * 1.75; // Old code == 2.5
}
else
{
damage_radius = flDamage * 2.5;
}
::RadiusDamage( beam_tr.vecEndPos + vecDir * 8, pev, m_pPlayer->pev, flDamage, damage_radius, CLASS_NONE, DMG_BLAST );
CSoundEnt::InsertSound( bits_SOUND_COMBAT, pev->origin, NORMAL_EXPLOSION_VOLUME, 3.0 );
nTotal += 53;
vecSrc = beam_tr.vecEndPos + vecDir;
}
else if( !selfgauss.value )
{
flDamage = 0;
}
}
else
{
//ALERT( at_console, "blocked %f\n", n );
flDamage = 0;
}
}
else
{
//ALERT( at_console, "blocked solid\n" );
flDamage = 0;
}
}
}
else
{
vecSrc = tr.vecEndPos + vecDir;
pentIgnore = ENT( pEntity->pev );
}
}
#endif
// ALERT( at_console, "%d bytes\n", nTotal );
}
void CGauss::WeaponIdle( void )
{
ResetEmptySound();
// play aftershock static discharge
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
}
m_pPlayer->m_flPlayAftershock = 0.0;
}
if( m_flTimeWeaponIdle > UTIL_WeaponTimeBase() )
return;
if( m_fInAttack != 0 )
{
StartFire();
m_fInAttack = 0;
m_flTimeWeaponIdle = UTIL_WeaponTimeBase() + 2.0;
// Need to set m_flNextPrimaryAttack so the weapon gets a chance to complete its secondary fire animation. - Solokiller
if( m_pPlayer->m_rgAmmo[m_iPrimaryAmmoType] <= 0 )
m_flNextPrimaryAttack = UTIL_WeaponTimeBase() + 0.5;
}
else
{
int iAnim;
float flRand = RANDOM_FLOAT( 0, 1 );
if( flRand <= 0.5 )
{
iAnim = GAUSS_IDLE;
m_flTimeWeaponIdle = UTIL_WeaponTimeBase() + UTIL_SharedRandomFloat( m_pPlayer->random_seed, 10, 15 );
}
else if( flRand <= 0.75 )
{
iAnim = GAUSS_IDLE2;
m_flTimeWeaponIdle = UTIL_WeaponTimeBase() + UTIL_SharedRandomFloat( m_pPlayer->random_seed, 10, 15 );
}
else
{
iAnim = GAUSS_FIDGET;
m_flTimeWeaponIdle = UTIL_WeaponTimeBase() + 3;
}
#ifndef CLIENT_DLL
SendWeaponAnim( iAnim );
#endif
}
}
class CGaussAmmo : public CBasePlayerAmmo
{
void Spawn( void )
{
Precache();
SET_MODEL( ENT( pev ), "models/w_gaussammo.mdl" );
CBasePlayerAmmo::Spawn();
}
void Precache( void )
{
PRECACHE_MODEL( "models/w_gaussammo.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_gaussclip, CGaussAmmo )
#endif

View File

@ -1,139 +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"
// 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 0;
}
//=========================================================
// 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( STRING( pev->model ) );
}
//=========================================================
// AI Schedules Specific to this monster
//=========================================================

View File

@ -1,261 +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_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;
}
int CGlock::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 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 = GetNextAttackDelay( 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 = GetNextAttackDelay( 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->m_rgAmmo[m_iPrimaryAmmoType] <= 0 || m_iClip == GLOCK_MAX_CLIP )
return;
int iResult;
if( m_iClip == 0 )
iResult = DefaultReload( GLOCK_MAX_CLIP, GLOCK_RELOAD, 1.5 );
else
iResult = DefaultReload( GLOCK_MAX_CLIP, 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 )

View File

@ -1,236 +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.
*
****/
//=========================================================
// 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 0;
}
//=========================================================
// 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 == 0 )
{
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 != 0 )
{
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 != 0 && 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;
}

View File

@ -1,992 +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.
*
****/
#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 == 0 )
{
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 != 0 )
{
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 == 0 || 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 = Q_max( pev->renderamt - 50, m_iTargetRanderamt );
pev->rendermode = kRenderTransTexture;
}
else if( pev->renderamt < m_iTargetRanderamt )
{
pev->renderamt = Q_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;
default:
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

View File

@ -1,552 +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.
*
****/
//=========================================================
// 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 != 0 )
{
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( &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 );
}
#define CRAB_ATTN_IDLE (float)1.5
//=========================================================
// IdleSound
//=========================================================
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 != 0 )
return slHCRangeAttack1Fast;
break;
case SCHED_RANGE_ATTACK1:
{
return slHCRangeAttack1Fast;
}
break;
}
return CHeadCrab::GetScheduleOfType( Type );
}

File diff suppressed because it is too large Load Diff

View File

@ -1,435 +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.
*
****/
//=========================================================
// Hornets
//=========================================================
#include "extdll.h"
#include "util.h"
#include "cbase.h"
#include "monsters.h"
#include "weapons.h"
#include "soundent.h"
#include "hornet.h"
#include "gamerules.h"
int iHornetTrail;
int iHornetPuff;
LINK_ENTITY_TO_CLASS( hornet, CHornet )
//=========================================================
// Save/Restore
//=========================================================
TYPEDESCRIPTION CHornet::m_SaveData[] =
{
DEFINE_FIELD( CHornet, m_flStopAttack, FIELD_TIME ),
DEFINE_FIELD( CHornet, m_iHornetType, FIELD_INTEGER ),
DEFINE_FIELD( CHornet, m_flFlySpeed, FIELD_FLOAT ),
};
IMPLEMENT_SAVERESTORE( CHornet, CBaseMonster )
//=========================================================
// don't let hornets gib, ever.
//=========================================================
int CHornet::TakeDamage( entvars_t *pevInflictor, entvars_t *pevAttacker, float flDamage, int bitsDamageType )
{
// filter these bits a little.
bitsDamageType &= ~( DMG_ALWAYSGIB );
bitsDamageType |= DMG_NEVERGIB;
return CBaseMonster::TakeDamage( pevInflictor, pevAttacker, flDamage, bitsDamageType );
}
//=========================================================
//=========================================================
void CHornet::Spawn( void )
{
Precache();
pev->movetype = MOVETYPE_FLY;
pev->solid = SOLID_BBOX;
pev->takedamage = DAMAGE_YES;
pev->flags |= FL_MONSTER;
pev->health = 1;// weak!
if( g_pGameRules->IsMultiplayer() )
{
// hornets don't live as long in multiplayer
m_flStopAttack = gpGlobals->time + 3.5;
}
else
{
m_flStopAttack = gpGlobals->time + 5.0;
}
m_flFieldOfView = 0.9; // +- 25 degrees
if( RANDOM_LONG( 1, 5 ) <= 2 )
{
m_iHornetType = HORNET_TYPE_RED;
m_flFlySpeed = HORNET_RED_SPEED;
}
else
{
m_iHornetType = HORNET_TYPE_ORANGE;
m_flFlySpeed = HORNET_ORANGE_SPEED;
}
SET_MODEL( ENT( pev ), "models/hornet.mdl" );
UTIL_SetSize( pev, Vector( -4, -4, -4 ), Vector( 4, 4, 4 ) );
SetTouch( &CHornet::DieTouch );
SetThink( &CHornet::StartTrack );
edict_t *pSoundEnt = pev->owner;
if( !pSoundEnt )
pSoundEnt = edict();
if( !FNullEnt( pev->owner ) && ( pev->owner->v.flags & FL_CLIENT ) )
{
pev->dmg = gSkillData.plrDmgHornet;
}
else
{
// no real owner, or owner isn't a client.
pev->dmg = gSkillData.monDmgHornet;
}
pev->nextthink = gpGlobals->time + 0.1;
ResetSequenceInfo();
}
void CHornet::Precache()
{
PRECACHE_MODEL( "models/hornet.mdl" );
PRECACHE_SOUND( "agrunt/ag_fire1.wav" );
PRECACHE_SOUND( "agrunt/ag_fire2.wav" );
PRECACHE_SOUND( "agrunt/ag_fire3.wav" );
PRECACHE_SOUND( "hornet/ag_buzz1.wav" );
PRECACHE_SOUND( "hornet/ag_buzz2.wav" );
PRECACHE_SOUND( "hornet/ag_buzz3.wav" );
PRECACHE_SOUND( "hornet/ag_hornethit1.wav" );
PRECACHE_SOUND( "hornet/ag_hornethit2.wav" );
PRECACHE_SOUND( "hornet/ag_hornethit3.wav" );
iHornetPuff = PRECACHE_MODEL( "sprites/muz1.spr" );
iHornetTrail = PRECACHE_MODEL( "sprites/laserbeam.spr" );
}
//=========================================================
// hornets will never get mad at each other, no matter who the owner is.
//=========================================================
int CHornet::IRelationship( CBaseEntity *pTarget )
{
if( pTarget->pev->modelindex == pev->modelindex )
{
return R_NO;
}
return CBaseMonster::IRelationship( pTarget );
}
//=========================================================
// ID's Hornet as their owner
//=========================================================
int CHornet::Classify( void )
{
if( pev->owner && pev->owner->v.flags & FL_CLIENT )
{
return CLASS_PLAYER_BIOWEAPON;
}
return CLASS_ALIEN_BIOWEAPON;
}
//=========================================================
// StartTrack - starts a hornet out tracking its target
//=========================================================
void CHornet::StartTrack( void )
{
IgniteTrail();
SetTouch( &CHornet::TrackTouch );
SetThink( &CHornet::TrackTarget );
pev->nextthink = gpGlobals->time + 0.1;
}
//=========================================================
// StartDart - starts a hornet out just flying straight.
//=========================================================
void CHornet::StartDart( void )
{
IgniteTrail();
SetTouch( &CHornet::DartTouch );
SetThink( &CBaseEntity::SUB_Remove );
pev->nextthink = gpGlobals->time + 4;
}
void CHornet::IgniteTrail( void )
{
/*
ted's suggested trail colors:
r161
g25
b97
r173
g39
b14
old colors
case HORNET_TYPE_RED:
WRITE_BYTE( 255 ); // r, g, b
WRITE_BYTE( 128 ); // r, g, b
WRITE_BYTE( 0 ); // r, g, b
break;
case HORNET_TYPE_ORANGE:
WRITE_BYTE( 0 ); // r, g, b
WRITE_BYTE( 100 ); // r, g, b
WRITE_BYTE( 255 ); // r, g, b
break;
*/
// trail
MESSAGE_BEGIN( MSG_BROADCAST, SVC_TEMPENTITY );
WRITE_BYTE( TE_BEAMFOLLOW );
WRITE_SHORT( entindex() ); // entity
WRITE_SHORT( iHornetTrail ); // model
WRITE_BYTE( 10 ); // life
WRITE_BYTE( 2 ); // width
switch( m_iHornetType )
{
case HORNET_TYPE_RED:
WRITE_BYTE( 179 ); // r, g, b
WRITE_BYTE( 39 ); // r, g, b
WRITE_BYTE( 14 ); // r, g, b
break;
case HORNET_TYPE_ORANGE:
WRITE_BYTE( 255 ); // r, g, b
WRITE_BYTE( 128 ); // r, g, b
WRITE_BYTE( 0 ); // r, g, b
break;
}
WRITE_BYTE( 128 ); // brightness
MESSAGE_END();
}
//=========================================================
// Hornet is flying, gently tracking target
//=========================================================
void CHornet::TrackTarget( void )
{
Vector vecFlightDir;
Vector vecDirToEnemy;
float flDelta;
StudioFrameAdvance();
if( gpGlobals->time > m_flStopAttack )
{
SetTouch( NULL );
SetThink( &CBaseEntity::SUB_Remove );
pev->nextthink = gpGlobals->time + 0.1;
return;
}
// UNDONE: The player pointer should come back after returning from another level
if( m_hEnemy == 0 )
{
// enemy is dead.
Look( 512 );
m_hEnemy = BestVisibleEnemy();
}
if( m_hEnemy != 0 && FVisible( m_hEnemy ) )
{
m_vecEnemyLKP = m_hEnemy->BodyTarget( pev->origin );
}
else
{
m_vecEnemyLKP = m_vecEnemyLKP + pev->velocity * m_flFlySpeed * 0.1;
}
vecDirToEnemy = ( m_vecEnemyLKP - pev->origin ).Normalize();
if( pev->velocity.Length() < 0.1 )
vecFlightDir = vecDirToEnemy;
else
vecFlightDir = pev->velocity.Normalize();
// measure how far the turn is, the wider the turn, the slow we'll go this time.
flDelta = DotProduct( vecFlightDir, vecDirToEnemy );
if( flDelta < 0.5 )
{
// hafta turn wide again. play sound
switch( RANDOM_LONG( 0, 2 ) )
{
case 0:
EMIT_SOUND( ENT( pev ), CHAN_VOICE, "hornet/ag_buzz1.wav", HORNET_BUZZ_VOLUME, ATTN_NORM );
break;
case 1:
EMIT_SOUND( ENT( pev ), CHAN_VOICE, "hornet/ag_buzz2.wav", HORNET_BUZZ_VOLUME, ATTN_NORM );
break;
case 2:
EMIT_SOUND( ENT( pev ), CHAN_VOICE, "hornet/ag_buzz3.wav", HORNET_BUZZ_VOLUME, ATTN_NORM );
break;
}
}
if( flDelta <= 0 && m_iHornetType == HORNET_TYPE_RED )
{
// no flying backwards, but we don't want to invert this, cause we'd go fast when we have to turn REAL far.
flDelta = 0.25;
}
pev->velocity = ( vecFlightDir + vecDirToEnemy ).Normalize();
if( pev->owner && ( pev->owner->v.flags & FL_MONSTER ) )
{
// random pattern only applies to hornets fired by monsters, not players.
pev->velocity.x += RANDOM_FLOAT( -0.10, 0.10 );// scramble the flight dir a bit.
pev->velocity.y += RANDOM_FLOAT( -0.10, 0.10 );
pev->velocity.z += RANDOM_FLOAT( -0.10, 0.10 );
}
switch( m_iHornetType )
{
case HORNET_TYPE_RED:
pev->velocity = pev->velocity * ( m_flFlySpeed * flDelta );// scale the dir by the ( speed * width of turn )
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.
pev->nextthink = gpGlobals->time + 0.1;// fixed think time
break;
}
pev->angles = UTIL_VecToAngles( pev->velocity );
pev->solid = SOLID_BBOX;
// if hornet is close to the enemy, jet in a straight line for a half second.
// (only in the single player game)
if( m_hEnemy != 0 && !g_pGameRules->IsMultiplayer() )
{
if( flDelta >= 0.4 && ( pev->origin - m_vecEnemyLKP ).Length() <= 300 )
{
MESSAGE_BEGIN( MSG_PVS, SVC_TEMPENTITY, pev->origin );
WRITE_BYTE( TE_SPRITE );
WRITE_COORD( pev->origin.x ); // pos
WRITE_COORD( pev->origin.y );
WRITE_COORD( pev->origin.z );
WRITE_SHORT( iHornetPuff ); // model
// WRITE_BYTE( 0 ); // life * 10
WRITE_BYTE( 2 ); // size * 10
WRITE_BYTE( 128 ); // brightness
MESSAGE_END();
switch( RANDOM_LONG( 0, 2 ) )
{
case 0:
EMIT_SOUND( ENT( pev ), CHAN_VOICE, "hornet/ag_buzz1.wav", HORNET_BUZZ_VOLUME, ATTN_NORM );
break;
case 1:
EMIT_SOUND( ENT( pev ), CHAN_VOICE, "hornet/ag_buzz2.wav", HORNET_BUZZ_VOLUME, ATTN_NORM );
break;
case 2:
EMIT_SOUND( ENT( pev ), CHAN_VOICE, "hornet/ag_buzz3.wav", HORNET_BUZZ_VOLUME, ATTN_NORM );
break;
}
pev->velocity = pev->velocity * 2;
pev->nextthink = gpGlobals->time + 1.0;
// don't attack again
m_flStopAttack = gpGlobals->time;
}
}
}
//=========================================================
// Tracking Hornet hit something
//=========================================================
void CHornet::TrackTouch( CBaseEntity *pOther )
{
if( pOther->edict() == pev->owner || pOther->pev->modelindex == pev->modelindex )
{
// bumped into the guy that shot it.
pev->solid = SOLID_NOT;
return;
}
if( IRelationship( pOther ) <= R_NO )
{
// hit something we don't want to hurt, so turn around.
pev->velocity = pev->velocity.Normalize();
pev->velocity.x *= -1;
pev->velocity.y *= -1;
pev->origin = pev->origin + pev->velocity * 4; // bounce the hornet off a bit.
pev->velocity = pev->velocity * m_flFlySpeed;
return;
}
DieTouch( pOther );
}
void CHornet::DartTouch( CBaseEntity *pOther )
{
DieTouch( pOther );
}
void CHornet::DieTouch( CBaseEntity *pOther )
{
if( pOther && pOther->pev->takedamage && pev->owner )
{
// do the damage
switch( RANDOM_LONG( 0, 2 ) )
{
// buzz when you plug someone
case 0:
EMIT_SOUND( ENT( pev ), CHAN_VOICE, "hornet/ag_hornethit1.wav", 1, ATTN_NORM );
break;
case 1:
EMIT_SOUND( ENT( pev ), CHAN_VOICE, "hornet/ag_hornethit2.wav", 1, ATTN_NORM );
break;
case 2:
EMIT_SOUND( ENT( pev ), CHAN_VOICE, "hornet/ag_hornethit3.wav", 1, ATTN_NORM );
break;
}
pOther->TakeDamage( pev, VARS( pev->owner ), pev->dmg, DMG_BULLET );
}
pev->modelindex = 0;// so will disappear for the 0.1 secs we wait until NEXTTHINK gets rid
pev->solid = SOLID_NOT;
SetThink( &CBaseEntity::SUB_Remove );
pev->nextthink = gpGlobals->time + 1;// stick around long enough for the sound to finish!
}

View File

@ -1,285 +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.
*
****/
#if !defined( OEM_BUILD ) && !defined( HLDEMO_BUILD )
#include "extdll.h"
#include "util.h"
#include "cbase.h"
#include "monsters.h"
#include "weapons.h"
#include "nodes.h"
#include "player.h"
#include "hornet.h"
#include "gamerules.h"
enum hgun_e
{
HGUN_IDLE1 = 0,
HGUN_FIDGETSWAY,
HGUN_FIDGETSHAKE,
HGUN_DOWN,
HGUN_UP,
HGUN_SHOOT
};
enum firemode_e
{
FIREMODE_TRACK = 0,
FIREMODE_FAST
};
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.
}
void CHgun::Precache( void )
{
PRECACHE_MODEL( "models/v_hgun.mdl" );
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 );
p->pszAmmo1 = "Hornets";
p->iMaxAmmo1 = HORNET_MAX_CARRY;
p->pszAmmo2 = NULL;
p->iMaxAmmo2 = -1;
p->iMaxClip = WEAPON_NOCLIP;
p->iSlot = 3;
p->iPosition = 3;
p->iId = m_iId = WEAPON_HORNETGUN;
p->iFlags = ITEM_FLAG_NOAUTOSWITCHEMPTY | ITEM_FLAG_NOAUTORELOAD;
p->iWeight = HORNETGUN_WEIGHT;
return 1;
}
BOOL CHgun::Deploy()
{
return DefaultDeploy( "models/v_hgun.mdl", "models/p_hgun.mdl", HGUN_UP, "hive" );
}
void CHgun::Holster( int skiplocal /* = 0 */ )
{
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;
}
}
void CHgun::PrimaryAttack()
{
Reload();
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;
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 );
m_flNextPrimaryAttack = m_flNextPrimaryAttack + 0.25;
if( m_flNextPrimaryAttack < UTIL_WeaponTimeBase() )
{
m_flNextPrimaryAttack = UTIL_WeaponTimeBase() + 0.25;
}
m_flTimeWeaponIdle = UTIL_WeaponTimeBase() + UTIL_SharedRandomFloat( m_pPlayer->random_seed, 10, 15 );
}
void CHgun::SecondaryAttack( void )
{
Reload();
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++;
switch( m_iFirePhase )
{
case 1:
vecSrc = vecSrc + gpGlobals->v_up * 8;
break;
case 2:
vecSrc = vecSrc + gpGlobals->v_up * 8;
vecSrc = vecSrc + gpGlobals->v_right * 8;
break;
case 3:
vecSrc = vecSrc + gpGlobals->v_right * 8;
break;
case 4:
vecSrc = vecSrc + gpGlobals->v_up * -8;
vecSrc = vecSrc + gpGlobals->v_right * 8;
break;
case 5:
vecSrc = vecSrc + gpGlobals->v_up * -8;
break;
case 6:
vecSrc = vecSrc + gpGlobals->v_up * -8;
vecSrc = vecSrc + gpGlobals->v_right * -8;
break;
case 7:
vecSrc = vecSrc + gpGlobals->v_right * -8;
break;
case 8:
vecSrc = vecSrc + gpGlobals->v_up * 8;
vecSrc = vecSrc + gpGlobals->v_right * -8;
m_iFirePhase = 0;
break;
}
pHornet = CBaseEntity::Create( "hornet", vecSrc, m_pPlayer->pev->v_angle, m_pPlayer->edict() );
pHornet->pev->velocity = gpGlobals->v_forward * 1200;
pHornet->pev->angles = UTIL_VecToAngles( pHornet->pev->velocity );
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;
// player "shoot" animation
m_pPlayer->SetAnimation( PLAYER_ATTACK1 );
m_flNextPrimaryAttack = m_flNextSecondaryAttack = UTIL_WeaponTimeBase() + 0.1;
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;
while( m_pPlayer->m_rgAmmo[m_iPrimaryAmmoType] < HORNET_MAX_CARRY && m_flRechargeTime < gpGlobals->time )
{
m_pPlayer->m_rgAmmo[m_iPrimaryAmmoType]++;
m_flRechargeTime += 0.5;
}
}
void CHgun::WeaponIdle( void )
{
Reload();
if( m_flTimeWeaponIdle > UTIL_WeaponTimeBase() )
return;
int iAnim;
float flRand = UTIL_SharedRandomFloat( m_pPlayer->random_seed, 0, 1 );
if( flRand <= 0.75 )
{
iAnim = HGUN_IDLE1;
m_flTimeWeaponIdle = UTIL_WeaponTimeBase() + 30.0 / 16 * ( 2 );
}
else if( flRand <= 0.875 )
{
iAnim = HGUN_FIDGETSWAY;
m_flTimeWeaponIdle = UTIL_WeaponTimeBase() + 40.0 / 16.0;
}
else
{
iAnim = HGUN_FIDGETSHAKE;
m_flTimeWeaponIdle = UTIL_WeaponTimeBase() + 35.0 / 16.0;
}
SendWeaponAnim( iAnim );
}
#endif

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

View File

@ -1,842 +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.
*
****/
//=========================================================
// 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 UpdateOnRemove();
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( const 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( const 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 != 0 )
{
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 != 0 )
{
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 != 0 )
{
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 != 0 )
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()
{
size_t 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( pAttackHitSounds[i] );
for( i = 0; i < ARRAYSIZE( pAttackMissSounds ); i++ )
PRECACHE_SOUND( pAttackMissSounds[i] );
for( i = 0; i < ARRAYSIZE( pPainSounds ); i++ )
PRECACHE_SOUND( pPainSounds[i] );
for( i = 0; i < ARRAYSIZE( pDeathSounds ); i++ )
PRECACHE_SOUND( pDeathSounds[i] );
UTIL_PrecacheOther( "test_effect" );
}
void CISlave::UpdateOnRemove()
{
CBaseEntity::UpdateOnRemove();
ClearBeams();
}
//=========================================================
// 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;
default:
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" );
}

View File

@ -1,685 +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.
*
****/
//=========================================================
// leech - basic little swimming monster
//=========================================================
//
// UNDONE:
// DONE:Steering force model for attack
// DONE:Attack animation control / damage
// DONE:Establish range of up/down motion and steer around vertical obstacles
// DONE:Re-evaluate height periodically
// DONE:Fall (MOVETYPE_TOSS) and play different anim if out of water
// Test in complex room (c2a3?)
// DONE:Sounds? - Kelly will fix
// Blood cloud? Hurt effect?
// Group behavior?
// DONE:Save/restore
// Flop animation - just bind to ACT_TWITCH
// Fix fatal push into wall case
//
// Try this on a bird
// Try this on a model with hulls/tracehull?
//
#include "float.h"
#include "extdll.h"
#include "util.h"
#include "cbase.h"
#include "monsters.h"
// Animation events
#define LEECH_AE_ATTACK 1
#define LEECH_AE_FLOP 2
// Movement constants
#define LEECH_ACCELERATE 10
#define LEECH_CHECK_DIST 45
#define LEECH_SWIM_SPEED 50
#define LEECH_SWIM_ACCEL 80
#define LEECH_SWIM_DECEL 10
#define LEECH_TURN_RATE 90
#define LEECH_SIZEX 10
#define LEECH_FRAMETIME 0.1
#define DEBUG_BEAMS 0
#if DEBUG_BEAMS
#include "effects.h"
#endif
class CLeech : public CBaseMonster
{
public:
void Spawn( void );
void Precache( void );
void EXPORT SwimThink( void );
void EXPORT DeadThink( void );
void Touch( CBaseEntity *pOther )
{
if( pOther->IsPlayer() )
{
// If the client is pushing me, give me some base velocity
if( gpGlobals->trace_ent && gpGlobals->trace_ent == edict() )
{
pev->basevelocity = pOther->pev->velocity;
pev->flags |= FL_BASEVELOCITY;
}
}
}
void SetObjectCollisionBox( void )
{
pev->absmin = pev->origin + Vector( -8, -8, 0 );
pev->absmax = pev->origin + Vector( 8, 8, 2 );
}
void AttackSound( void );
void AlertSound( void );
void UpdateMotion( void );
float ObstacleDistance( CBaseEntity *pTarget );
void MakeVectors( void );
void RecalculateWaterlevel( void );
void SwitchLeechState( void );
// Base entity functions
void HandleAnimEvent( MonsterEvent_t *pEvent );
int BloodColor( void ) { return DONT_BLEED; }
void Killed( entvars_t *pevAttacker, int iGib );
void Activate( void );
int TakeDamage( entvars_t *pevInflictor, entvars_t *pevAttacker, float flDamage, int bitsDamageType );
int Classify( void ) { return CLASS_INSECT; }
int IRelationship( CBaseEntity *pTarget );
virtual int Save( CSave &save );
virtual int Restore( CRestore &restore );
static TYPEDESCRIPTION m_SaveData[];
static const char *pAttackSounds[];
static const char *pAlertSounds[];
private:
// UNDONE: Remove unused boid vars, do group behavior
float m_flTurning;// is this boid turning?
BOOL m_fPathBlocked;// TRUE if there is an obstacle ahead
float m_flAccelerate;
float m_obstacle;
float m_top;
float m_bottom;
float m_height;
float m_waterTime;
float m_sideTime; // Timer to randomly check clearance on sides
float m_zTime;
float m_stateTime;
float m_attackSoundTime;
#if DEBUG_BEAMS
CBeam *m_pb;
CBeam *m_pt;
#endif
};
LINK_ENTITY_TO_CLASS( monster_leech, CLeech )
TYPEDESCRIPTION CLeech::m_SaveData[] =
{
DEFINE_FIELD( CLeech, m_flTurning, FIELD_FLOAT ),
DEFINE_FIELD( CLeech, m_fPathBlocked, FIELD_BOOLEAN ),
DEFINE_FIELD( CLeech, m_flAccelerate, FIELD_FLOAT ),
DEFINE_FIELD( CLeech, m_obstacle, FIELD_FLOAT ),
DEFINE_FIELD( CLeech, m_top, FIELD_FLOAT ),
DEFINE_FIELD( CLeech, m_bottom, FIELD_FLOAT ),
DEFINE_FIELD( CLeech, m_height, FIELD_FLOAT ),
DEFINE_FIELD( CLeech, m_waterTime, FIELD_TIME ),
DEFINE_FIELD( CLeech, m_sideTime, FIELD_TIME ),
DEFINE_FIELD( CLeech, m_zTime, FIELD_TIME ),
DEFINE_FIELD( CLeech, m_stateTime, FIELD_TIME ),
DEFINE_FIELD( CLeech, m_attackSoundTime, FIELD_TIME ),
};
IMPLEMENT_SAVERESTORE( CLeech, CBaseMonster )
const char *CLeech::pAttackSounds[] =
{
"leech/leech_bite1.wav",
"leech/leech_bite2.wav",
"leech/leech_bite3.wav",
};
const char *CLeech::pAlertSounds[] =
{
"leech/leech_alert1.wav",
"leech/leech_alert2.wav",
};
void CLeech::Spawn( void )
{
Precache();
SET_MODEL( ENT( pev ), "models/leech.mdl" );
// Just for fun
// SET_MODEL( ENT( pev ), "models/icky.mdl" );
//UTIL_SetSize( pev, g_vecZero, g_vecZero );
UTIL_SetSize( pev, Vector( -1, -1, 0 ), Vector( 1, 1, 2 ) );
// Don't push the minz down too much or the water check will fail because this entity is really point-sized
pev->solid = SOLID_SLIDEBOX;
pev->movetype = MOVETYPE_FLY;
SetBits( pev->flags, FL_SWIM );
pev->health = gSkillData.leechHealth;
m_flFieldOfView = -0.5; // 180 degree FOV
m_flDistLook = 750;
MonsterInit();
SetThink( &CLeech::SwimThink );
SetUse( NULL );
SetTouch( NULL );
pev->view_ofs = g_vecZero;
m_flTurning = 0;
m_fPathBlocked = FALSE;
SetActivity( ACT_SWIM );
SetState( MONSTERSTATE_IDLE );
m_stateTime = gpGlobals->time + RANDOM_FLOAT( 1, 5 );
}
void CLeech::Activate( void )
{
RecalculateWaterlevel();
}
void CLeech::RecalculateWaterlevel( void )
{
// Calculate boundaries
Vector vecTest = pev->origin - Vector( 0, 0, 400 );
TraceResult tr;
UTIL_TraceLine( pev->origin, vecTest, missile, edict(), &tr );
if( tr.flFraction != 1.0 )
m_bottom = tr.vecEndPos.z + 1;
else
m_bottom = vecTest.z;
m_top = UTIL_WaterLevel( pev->origin, pev->origin.z, pev->origin.z + 400 ) - 1;
// Chop off 20% of the outside range
float newBottom = m_bottom * 0.8 + m_top * 0.2;
m_top = m_bottom * 0.2 + m_top * 0.8;
m_bottom = newBottom;
m_height = RANDOM_FLOAT( m_bottom, m_top );
m_waterTime = gpGlobals->time + RANDOM_FLOAT( 5, 7 );
}
void CLeech::SwitchLeechState( void )
{
m_stateTime = gpGlobals->time + RANDOM_FLOAT( 3, 6 );
if( m_MonsterState == MONSTERSTATE_COMBAT )
{
m_hEnemy = NULL;
SetState( MONSTERSTATE_IDLE );
// We may be up against the player, so redo the side checks
m_sideTime = 0;
}
else
{
Look( m_flDistLook );
CBaseEntity *pEnemy = BestVisibleEnemy();
if( pEnemy && pEnemy->pev->waterlevel != 0 )
{
m_hEnemy = pEnemy;
SetState( MONSTERSTATE_COMBAT );
m_stateTime = gpGlobals->time + RANDOM_FLOAT( 18, 25 );
AlertSound();
}
}
}
int CLeech::IRelationship( CBaseEntity *pTarget )
{
if( pTarget->IsPlayer() )
return R_DL;
return CBaseMonster::IRelationship( pTarget );
}
void CLeech::AttackSound( void )
{
if( gpGlobals->time > m_attackSoundTime )
{
EMIT_SOUND_DYN( ENT( pev ), CHAN_VOICE, pAttackSounds[RANDOM_LONG( 0, ARRAYSIZE( pAttackSounds ) - 1 )], 1.0, ATTN_NORM, 0, PITCH_NORM );
m_attackSoundTime = gpGlobals->time + 0.5;
}
}
void CLeech::AlertSound( void )
{
EMIT_SOUND_DYN( ENT( pev ), CHAN_VOICE, pAlertSounds[RANDOM_LONG( 0, ARRAYSIZE( pAlertSounds ) - 1 )], 1.0, ATTN_NORM * 0.5, 0, PITCH_NORM );
}
void CLeech::Precache( void )
{
size_t i;
//PRECACHE_MODEL( "models/icky.mdl" );
PRECACHE_MODEL( "models/leech.mdl" );
for( i = 0; i < ARRAYSIZE( pAttackSounds ); i++ )
PRECACHE_SOUND( pAttackSounds[i] );
for( i = 0; i < ARRAYSIZE( pAlertSounds ); i++ )
PRECACHE_SOUND( pAlertSounds[i] );
}
int CLeech::TakeDamage( entvars_t *pevInflictor, entvars_t *pevAttacker, float flDamage, int bitsDamageType )
{
pev->velocity = g_vecZero;
// Nudge the leech away from the damage
if( pevInflictor )
{
pev->velocity = ( pev->origin - pevInflictor->origin ).Normalize() * 25;
}
return CBaseMonster::TakeDamage( pevInflictor, pevAttacker, flDamage, bitsDamageType );
}
void CLeech::HandleAnimEvent( MonsterEvent_t *pEvent )
{
switch( pEvent->event )
{
case LEECH_AE_ATTACK:
AttackSound();
CBaseEntity *pEnemy;
pEnemy = m_hEnemy;
if( pEnemy != NULL )
{
Vector dir, face;
UTIL_MakeVectorsPrivate( pev->angles, face, NULL, NULL );
face.z = 0;
dir = (pEnemy->pev->origin - pev->origin);
dir.z = 0;
dir = dir.Normalize();
face = face.Normalize();
if( DotProduct( dir, face ) > 0.9 ) // Only take damage if the leech is facing the prey
pEnemy->TakeDamage( pev, pev, gSkillData.leechDmgBite, DMG_SLASH );
}
m_stateTime -= 2;
break;
case LEECH_AE_FLOP:
// Play flop sound
break;
default:
CBaseMonster::HandleAnimEvent( pEvent );
break;
}
}
void CLeech::MakeVectors( void )
{
Vector tmp = pev->angles;
tmp.x = -tmp.x;
UTIL_MakeVectors( tmp );
}
//
// ObstacleDistance - returns normalized distance to obstacle
//
float CLeech::ObstacleDistance( CBaseEntity *pTarget )
{
TraceResult tr;
Vector vecTest;
// use VELOCITY, not angles, not all boids point the direction they are flying
//Vector vecDir = UTIL_VecToAngles( pev->velocity );
MakeVectors();
// check for obstacle ahead
vecTest = pev->origin + gpGlobals->v_forward * LEECH_CHECK_DIST;
UTIL_TraceLine( pev->origin, vecTest, missile, edict(), &tr );
if( tr.fStartSolid )
{
pev->speed = -LEECH_SWIM_SPEED * 0.5;
//ALERT( at_console, "Stuck from (%f %f %f) to (%f %f %f)\n", pev->oldorigin.x, pev->oldorigin.y, pev->oldorigin.z, pev->origin.x, pev->origin.y, pev->origin.z );
//UTIL_SetOrigin( pev, pev->oldorigin );
}
if( tr.flFraction != 1.0 )
{
if( ( pTarget == NULL || tr.pHit != pTarget->edict() ) )
{
return tr.flFraction;
}
else
{
if( fabs( m_height - pev->origin.z ) > 10 )
return tr.flFraction;
}
}
if( m_sideTime < gpGlobals->time )
{
// extra wide checks
vecTest = pev->origin + gpGlobals->v_right * LEECH_SIZEX * 2 + gpGlobals->v_forward * LEECH_CHECK_DIST;
UTIL_TraceLine( pev->origin, vecTest, missile, edict(), &tr );
if( tr.flFraction != 1.0 )
return tr.flFraction;
vecTest = pev->origin - gpGlobals->v_right * LEECH_SIZEX * 2 + gpGlobals->v_forward * LEECH_CHECK_DIST;
UTIL_TraceLine( pev->origin, vecTest, missile, edict(), &tr );
if( tr.flFraction != 1.0 )
return tr.flFraction;
// Didn't hit either side, so stop testing for another 0.5 - 1 seconds
m_sideTime = gpGlobals->time + RANDOM_FLOAT( 0.5, 1 );
}
return 1.0;
}
void CLeech::DeadThink( void )
{
if( m_fSequenceFinished )
{
if( m_Activity == ACT_DIEFORWARD )
{
SetThink( NULL );
StopAnimation();
return;
}
else if( pev->flags & FL_ONGROUND )
{
pev->solid = SOLID_NOT;
SetActivity(ACT_DIEFORWARD);
}
}
StudioFrameAdvance();
pev->nextthink = gpGlobals->time + 0.1;
// Apply damage velocity, but keep out of the walls
if( pev->velocity.x != 0 || pev->velocity.y != 0 )
{
TraceResult tr;
// Look 0.5 seconds ahead
UTIL_TraceLine( pev->origin, pev->origin + pev->velocity * 0.5, missile, edict(), &tr );
if( tr.flFraction != 1.0 )
{
pev->velocity.x = 0;
pev->velocity.y = 0;
}
}
}
void CLeech::UpdateMotion( void )
{
float flapspeed = ( pev->speed - m_flAccelerate ) / LEECH_ACCELERATE;
m_flAccelerate = m_flAccelerate * 0.8 + pev->speed * 0.2;
if( flapspeed < 0 )
flapspeed = -flapspeed;
flapspeed += 1.0;
if( flapspeed < 0.5 )
flapspeed = 0.5;
if( flapspeed > 1.9 )
flapspeed = 1.9;
pev->framerate = flapspeed;
if( !m_fPathBlocked )
pev->avelocity.y = pev->ideal_yaw;
else
pev->avelocity.y = pev->ideal_yaw * m_obstacle;
if( pev->avelocity.y > 150 )
m_IdealActivity = ACT_TURN_LEFT;
else if( pev->avelocity.y < -150 )
m_IdealActivity = ACT_TURN_RIGHT;
else
m_IdealActivity = ACT_SWIM;
// lean
float targetPitch, delta;
delta = m_height - pev->origin.z;
if( delta < -10 )
targetPitch = -30;
else if( delta > 10 )
targetPitch = 30;
else
targetPitch = 0;
pev->angles.x = UTIL_Approach( targetPitch, pev->angles.x, 60 * LEECH_FRAMETIME );
// bank
pev->avelocity.z = -( pev->angles.z + ( pev->avelocity.y * 0.25 ) );
if( m_MonsterState == MONSTERSTATE_COMBAT && HasConditions( bits_COND_CAN_MELEE_ATTACK1 ) )
m_IdealActivity = ACT_MELEE_ATTACK1;
// Out of water check
if( !pev->waterlevel )
{
pev->movetype = MOVETYPE_TOSS;
m_IdealActivity = ACT_TWITCH;
pev->velocity = g_vecZero;
// Animation will intersect the floor if either of these is non-zero
pev->angles.z = 0;
pev->angles.x = 0;
if( pev->framerate < 1.0 )
pev->framerate = 1.0;
}
else if( pev->movetype == MOVETYPE_TOSS )
{
pev->movetype = MOVETYPE_FLY;
pev->flags &= ~FL_ONGROUND;
RecalculateWaterlevel();
m_waterTime = gpGlobals->time + 2; // Recalc again soon, water may be rising
}
if( m_Activity != m_IdealActivity )
{
SetActivity( m_IdealActivity );
}
float flInterval = StudioFrameAdvance();
DispatchAnimEvents( flInterval );
#if DEBUG_BEAMS
if( !m_pb )
m_pb = CBeam::BeamCreate( "sprites/laserbeam.spr", 5 );
if( !m_pt )
m_pt = CBeam::BeamCreate( "sprites/laserbeam.spr", 5 );
m_pb->PointsInit( pev->origin, pev->origin + gpGlobals->v_forward * LEECH_CHECK_DIST );
m_pt->PointsInit( pev->origin, pev->origin - gpGlobals->v_right * ( pev->avelocity.y * 0.25 ) );
if( m_fPathBlocked )
{
float color = m_obstacle * 30;
if( m_obstacle == 1.0 )
color = 0;
if( color > 255 )
color = 255;
m_pb->SetColor( 255, (int)color, (int)color );
}
else
m_pb->SetColor( 255, 255, 0 );
m_pt->SetColor( 0, 0, 255 );
#endif
}
void CLeech::SwimThink( void )
{
TraceResult tr;
float flLeftSide;
float flRightSide;
float targetSpeed;
float targetYaw = 0;
CBaseEntity *pTarget;
if( FNullEnt( FIND_CLIENT_IN_PVS( edict() ) ) )
{
pev->nextthink = gpGlobals->time + RANDOM_FLOAT( 1, 1.5 );
pev->velocity = g_vecZero;
return;
}
else
pev->nextthink = gpGlobals->time + 0.1;
targetSpeed = LEECH_SWIM_SPEED;
if( m_waterTime < gpGlobals->time )
RecalculateWaterlevel();
if( m_stateTime < gpGlobals->time )
SwitchLeechState();
ClearConditions( bits_COND_CAN_MELEE_ATTACK1 );
switch( m_MonsterState )
{
case MONSTERSTATE_COMBAT:
pTarget = m_hEnemy;
if( !pTarget )
SwitchLeechState();
else
{
// Chase the enemy's eyes
m_height = pTarget->pev->origin.z + pTarget->pev->view_ofs.z - 5;
// Clip to viable water area
if( m_height < m_bottom )
m_height = m_bottom;
else if( m_height > m_top )
m_height = m_top;
Vector location = pTarget->pev->origin - pev->origin;
location.z += (pTarget->pev->view_ofs.z);
if( location.Length() < 40 )
SetConditions( bits_COND_CAN_MELEE_ATTACK1 );
// Turn towards target ent
targetYaw = UTIL_VecToYaw( location );
targetYaw = UTIL_AngleDiff( targetYaw, UTIL_AngleMod( pev->angles.y ) );
if( targetYaw < ( -LEECH_TURN_RATE * 0.75 ) )
targetYaw = ( -LEECH_TURN_RATE * 0.75 );
else if( targetYaw > ( LEECH_TURN_RATE * 0.75 ) )
targetYaw = ( LEECH_TURN_RATE * 0.75 );
else
targetSpeed *= 2;
}
break;
default:
if( m_zTime < gpGlobals->time )
{
float newHeight = RANDOM_FLOAT( m_bottom, m_top );
m_height = 0.5 * m_height + 0.5 * newHeight;
m_zTime = gpGlobals->time + RANDOM_FLOAT( 1, 4 );
}
if( RANDOM_LONG( 0, 100 ) < 10 )
targetYaw = RANDOM_LONG( -30, 30 );
pTarget = NULL;
// oldorigin test
if( ( pev->origin - pev->oldorigin ).Length() < 1 )
{
// If leech didn't move, there must be something blocking it, so try to turn
m_sideTime = 0;
}
break;
}
m_obstacle = ObstacleDistance( pTarget );
pev->oldorigin = pev->origin;
if( m_obstacle < 0.1 )
m_obstacle = 0.1;
// is the way ahead clear?
if( m_obstacle == 1.0 )
{
// if the leech is turning, stop the trend.
if( m_flTurning != 0 )
{
m_flTurning = 0;
}
m_fPathBlocked = FALSE;
pev->speed = UTIL_Approach( targetSpeed, pev->speed, LEECH_SWIM_ACCEL * LEECH_FRAMETIME );
pev->velocity = gpGlobals->v_forward * pev->speed;
}
else
{
m_obstacle = 1.0 / m_obstacle;
// IF we get this far in the function, the leader's path is blocked!
m_fPathBlocked = TRUE;
if( m_flTurning == 0 )// something in the way and leech is not already turning to avoid
{
Vector vecTest;
// measure clearance on left and right to pick the best dir to turn
vecTest = pev->origin + ( gpGlobals->v_right * LEECH_SIZEX ) + ( gpGlobals->v_forward * LEECH_CHECK_DIST );
UTIL_TraceLine( pev->origin, vecTest, missile, edict(), &tr );
flRightSide = tr.flFraction;
vecTest = pev->origin + ( gpGlobals->v_right * -LEECH_SIZEX ) + ( gpGlobals->v_forward * LEECH_CHECK_DIST );
UTIL_TraceLine( pev->origin, vecTest, missile, edict(), &tr );
flLeftSide = tr.flFraction;
// turn left, right or random depending on clearance ratio
float delta = ( flRightSide - flLeftSide );
if( delta > 0.1 || ( delta > -0.1 && RANDOM_LONG( 0, 100 ) < 50 ) )
m_flTurning = -LEECH_TURN_RATE;
else
m_flTurning = LEECH_TURN_RATE;
}
pev->speed = UTIL_Approach( -( LEECH_SWIM_SPEED * 0.5 ), pev->speed, LEECH_SWIM_DECEL * LEECH_FRAMETIME * m_obstacle );
pev->velocity = gpGlobals->v_forward * pev->speed;
}
pev->ideal_yaw = m_flTurning + targetYaw;
UpdateMotion();
}
void CLeech::Killed( entvars_t *pevAttacker, int iGib )
{
Vector vecSplatDir;
TraceResult tr;
//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->angles.z = 0;
pev->angles.x = 0;
pev->origin.z += 1;
pev->avelocity = g_vecZero;
if( RANDOM_LONG( 0, 99 ) < 70 )
pev->avelocity.y = RANDOM_LONG( -720, 720 );
pev->gravity = 0.02;
ClearBits( pev->flags, FL_ONGROUND );
SetActivity( ACT_DIESIMPLE );
}
else
SetActivity( ACT_DIEFORWARD );
pev->movetype = MOVETYPE_TOSS;
pev->takedamage = DAMAGE_NO;
SetThink( &CLeech::DeadThink );
}

View File

@ -1,875 +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.
*
****/
// -------------------------------------------
//
// 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" //empty file
#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 (int)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 (int)pev->frags; }
inline int LimitValue( void ) { return (int)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] = Q_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 );
}
}

View File

@ -1,19 +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 MAPRULES_H
#define MAPRULES_H
#endif //MAPRULES_H

View File

@ -1,289 +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.
*
****/
//=========================================================
// Monster Maker - this is an entity that creates monsters
// in the game.
//=========================================================
#include "extdll.h"
#include "util.h"
#include "cbase.h"
#include "monsters.h"
#include "saverestore.h"
// Monstermaker spawnflags
#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
//=========================================================
// MonsterMaker - this ent creates monsters during the game.
//=========================================================
class CMonsterMaker : public CBaseMonster
{
public:
void Spawn( void );
void Precache( void );
void KeyValue( KeyValueData* pkvd);
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 DeathNotice( entvars_t *pevChild );// monster maker children use this to tell the monster maker that they have died.
void MakeMonster( void );
virtual int Save( CSave &save );
virtual int Restore( CRestore &restore );
static TYPEDESCRIPTION m_SaveData[];
string_t m_iszMonsterClassname;// classname of the monster(s) that will be created.
int m_cNumMonsters;// max number of monsters this ent can create
int m_cLiveChildren;// how many monsters made by this monster maker that are currently alive
int m_iMaxLiveChildren;// max number of monsters that this maker may have out at one time.
float m_flGround; // z coord of the ground under me, used to make sure no monsters are under the maker when it drops a new child
BOOL m_fActive;
BOOL m_fFadeChildren;// should we make the children fadeout?
};
LINK_ENTITY_TO_CLASS( monstermaker, CMonsterMaker )
TYPEDESCRIPTION CMonsterMaker::m_SaveData[] =
{
DEFINE_FIELD( CMonsterMaker, m_iszMonsterClassname, FIELD_STRING ),
DEFINE_FIELD( CMonsterMaker, m_cNumMonsters, FIELD_INTEGER ),
DEFINE_FIELD( CMonsterMaker, m_cLiveChildren, FIELD_INTEGER ),
DEFINE_FIELD( CMonsterMaker, m_flGround, FIELD_FLOAT ),
DEFINE_FIELD( CMonsterMaker, m_iMaxLiveChildren, FIELD_INTEGER ),
DEFINE_FIELD( CMonsterMaker, m_fActive, FIELD_BOOLEAN ),
DEFINE_FIELD( CMonsterMaker, m_fFadeChildren, FIELD_BOOLEAN ),
};
IMPLEMENT_SAVERESTORE( CMonsterMaker, CBaseMonster )
void CMonsterMaker::KeyValue( KeyValueData *pkvd )
{
if( FStrEq( pkvd->szKeyName, "monstercount" ) )
{
m_cNumMonsters = atoi( pkvd->szValue );
pkvd->fHandled = TRUE;
}
else if( FStrEq( pkvd->szKeyName, "m_imaxlivechildren" ) )
{
m_iMaxLiveChildren = atoi( pkvd->szValue );
pkvd->fHandled = TRUE;
}
else if( FStrEq( pkvd->szKeyName, "monstertype" ) )
{
m_iszMonsterClassname = ALLOC_STRING( pkvd->szValue );
pkvd->fHandled = TRUE;
}
else
CBaseMonster::KeyValue( pkvd );
}
void CMonsterMaker::Spawn()
{
pev->solid = SOLID_NOT;
m_cLiveChildren = 0;
Precache();
if( !FStringNull( pev->targetname ) )
{
if( pev->spawnflags & SF_MONSTERMAKER_CYCLIC )
{
SetUse( &CMonsterMaker::CyclicUse );// drop one monster each time we fire
}
else
{
SetUse( &CMonsterMaker::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( &CBaseEntity::SUB_DoNothing );
}
}
else
{
// no targetname, just start.
pev->nextthink = gpGlobals->time + m_flDelay;
m_fActive = TRUE;
SetThink( &CMonsterMaker::MakerThink );
}
if( m_cNumMonsters == 1 )
{
m_fFadeChildren = FALSE;
}
else
{
m_fFadeChildren = TRUE;
}
m_flGround = 0;
}
void CMonsterMaker::Precache( void )
{
CBaseMonster::Precache();
UTIL_PrecacheOther( STRING( m_iszMonsterClassname ) );
}
//=========================================================
// MakeMonster- this is the code that drops the monster
//=========================================================
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;
}
if( !m_flGround )
{
// set altitude. Now that I'm activated, any breakables, etc should be out from under me.
TraceResult tr;
UTIL_TraceLine( pev->origin, pev->origin - Vector( 0, 0, 2048 ), ignore_monsters, ENT( pev ), &tr );
m_flGround = tr.vecEndPos.z;
}
Vector mins = pev->origin - Vector( 34, 34, 0 );
Vector maxs = pev->origin + Vector( 34, 34, 0 );
maxs.z = pev->origin.z;
mins.z = m_flGround;
CBaseEntity *pList[2];
int count = UTIL_EntitiesInBox( pList, 2, mins, maxs, FL_CLIENT | FL_MONSTER );
if( count )
{
// don't build a stack of monsters!
return;
}
pent = CREATE_NAMED_ENTITY( m_iszMonsterClassname );
if( FNullEnt( pent ) )
{
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 );
pevCreate->origin = pev->origin;
pevCreate->angles = pev->angles;
SetBits( pevCreate->spawnflags, SF_MONSTER_FALL_TO_GROUND );
// Children hit monsterclip brushes
if( pev->spawnflags & SF_MONSTERMAKER_MONSTERCLIP )
SetBits( pevCreate->spawnflags, SF_MONSTER_HITMONSTERCLIP );
DispatchSpawn( ENT( pevCreate ) );
pevCreate->owner = edict();
if( !FStringNull( pev->netname ) )
{
// if I have a netname (overloaded), give the child monster that name as a targetname
pevCreate->targetname = pev->netname;
}
m_cLiveChildren++;// count this monster
m_cNumMonsters--;
if( m_cNumMonsters == 0 )
{
// Disable this forever. Don't kill it because it still gets death notices
SetThink( NULL );
SetUse( NULL );
}
}
//=========================================================
// CyclicUse - drops one monster from the monstermaker
// each time we call this.
//=========================================================
void CMonsterMaker::CyclicUse( CBaseEntity *pActivator, CBaseEntity *pCaller, USE_TYPE useType, float value )
{
MakeMonster();
}
//=========================================================
// ToggleUse - activates/deactivates the monster maker
//=========================================================
void CMonsterMaker::ToggleUse( CBaseEntity *pActivator, CBaseEntity *pCaller, USE_TYPE useType, float value )
{
if( !ShouldToggle( useType, m_fActive ) )
return;
if( m_fActive )
{
m_fActive = FALSE;
SetThink( NULL );
}
else
{
m_fActive = TRUE;
SetThink( &CMonsterMaker::MakerThink );
}
pev->nextthink = gpGlobals->time;
}
//=========================================================
// MakerThink - creates a new monster every so often
//=========================================================
void CMonsterMaker::MakerThink( void )
{
pev->nextthink = gpGlobals->time + m_flDelay;
MakeMonster();
}
//=========================================================
//=========================================================
void CMonsterMaker::DeathNotice( entvars_t *pevChild )
{
// ok, we've gotten the deathnotice from our child, now clear out its owner if we don't want it to fade.
m_cLiveChildren--;
if( !m_fFadeChildren )
{
pevChild->owner = NULL;
}
}

View File

@ -1,358 +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_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 = GetNextAttackDelay( 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 = GetNextAttackDelay( 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->m_rgAmmo[m_iPrimaryAmmoType] <= 0 || m_iClip == MP5_MAX_CLIP )
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.
}
BOOL CMP5::IsUseable()
{
//Can be used if the player has AR grenades. - Solokiller
return CBasePlayerWeapon::IsUseable() || m_pPlayer->m_rgAmmo[m_iSecondaryAmmoType] > 0;
}
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 )

File diff suppressed because it is too large Load Diff

View File

@ -1,776 +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.
*
****/
#include "extdll.h"
#include "util.h"
#include "cbase.h"
#include "monsters.h"
#include "weapons.h"
#include "nodes.h"
#include "soundent.h"
#include "effects.h"
#include "customentity.h"
typedef struct
{
int isValid;
EHANDLE hGrunt;
Vector vecOrigin;
Vector vecAngles;
} t_ospreygrunt;
#define SF_WAITFORTRIGGER 0x40
#define MAX_CARRY 24
class COsprey : public CBaseMonster
{
public:
int Save( CSave &save );
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; };
int BloodColor( void ) { return DONT_BLEED; }
void Killed( entvars_t *pevAttacker, int iGib );
void UpdateGoal( void );
BOOL HasDead( void );
void EXPORT FlyThink( void );
void EXPORT DeployThink( void );
void Flight( void );
void EXPORT HitTouch( CBaseEntity *pOther );
void EXPORT FindAllThink( void );
void EXPORT HoverThink( void );
CBaseMonster *MakeGrunt( Vector vecSrc );
void EXPORT CrashTouch( CBaseEntity *pOther );
void EXPORT DyingThink( void );
void EXPORT CommandUse( CBaseEntity *pActivator, CBaseEntity *pCaller, USE_TYPE useType, float value );
// 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 ShowDamage( void );
CBaseEntity *m_pGoalEnt;
Vector m_vel1;
Vector m_vel2;
Vector m_pos1;
Vector m_pos2;
Vector m_ang1;
Vector m_ang2;
float m_startTime;
float m_dTime;
Vector m_velocity;
float m_flIdealtilt;
float m_flRotortilt;
float m_flRightHealth;
float m_flLeftHealth;
int m_iUnits;
EHANDLE m_hGrunt[MAX_CARRY];
Vector m_vecOrigin[MAX_CARRY];
EHANDLE m_hRepel[4];
int m_iSoundState;
int m_iSpriteTexture;
int m_iPitch;
int m_iExplode;
int m_iTailGibs;
int m_iBodyGibs;
int m_iEngineGibs;
int m_iDoLeftSmokePuff;
int m_iDoRightSmokePuff;
};
LINK_ENTITY_TO_CLASS( monster_osprey, COsprey )
TYPEDESCRIPTION COsprey::m_SaveData[] =
{
DEFINE_FIELD( COsprey, m_pGoalEnt, FIELD_CLASSPTR ),
DEFINE_FIELD( COsprey, m_vel1, FIELD_VECTOR ),
DEFINE_FIELD( COsprey, m_vel2, FIELD_VECTOR ),
DEFINE_FIELD( COsprey, m_pos1, FIELD_POSITION_VECTOR ),
DEFINE_FIELD( COsprey, m_pos2, FIELD_POSITION_VECTOR ),
DEFINE_FIELD( COsprey, m_ang1, FIELD_VECTOR ),
DEFINE_FIELD( COsprey, m_ang2, FIELD_VECTOR ),
DEFINE_FIELD( COsprey, m_startTime, FIELD_TIME ),
DEFINE_FIELD( COsprey, m_dTime, FIELD_FLOAT ),
DEFINE_FIELD( COsprey, m_velocity, FIELD_VECTOR ),
DEFINE_FIELD( COsprey, m_flIdealtilt, FIELD_FLOAT ),
DEFINE_FIELD( COsprey, m_flRotortilt, FIELD_FLOAT ),
DEFINE_FIELD( COsprey, m_flRightHealth, FIELD_FLOAT ),
DEFINE_FIELD( COsprey, m_flLeftHealth, FIELD_FLOAT ),
DEFINE_FIELD( COsprey, m_iUnits, FIELD_INTEGER ),
DEFINE_ARRAY( COsprey, m_hGrunt, FIELD_EHANDLE, MAX_CARRY ),
DEFINE_ARRAY( COsprey, m_vecOrigin, FIELD_POSITION_VECTOR, MAX_CARRY ),
DEFINE_ARRAY( COsprey, m_hRepel, FIELD_EHANDLE, 4 ),
// DEFINE_FIELD( COsprey, m_iSoundState, FIELD_INTEGER ),
// DEFINE_FIELD( COsprey, m_iSpriteTexture, FIELD_INTEGER ),
// DEFINE_FIELD( COsprey, m_iPitch, FIELD_INTEGER ),
DEFINE_FIELD( COsprey, m_iDoLeftSmokePuff, FIELD_INTEGER ),
DEFINE_FIELD( COsprey, m_iDoRightSmokePuff, FIELD_INTEGER ),
};
IMPLEMENT_SAVERESTORE( COsprey, CBaseMonster )
void COsprey::Spawn( void )
{
Precache();
// motor
pev->movetype = MOVETYPE_FLY;
pev->solid = SOLID_BBOX;
SET_MODEL( ENT( pev ), "models/osprey.mdl" );
UTIL_SetSize( pev, Vector( -400, -400, -100 ), Vector( 400, 400, 32 ) );
UTIL_SetOrigin( pev, pev->origin );
pev->flags |= FL_MONSTER;
pev->takedamage = DAMAGE_YES;
m_flRightHealth = 200;
m_flLeftHealth = 200;
pev->health = 400;
m_flFieldOfView = 0; // 180 degrees
pev->sequence = 0;
ResetSequenceInfo();
pev->frame = RANDOM_LONG( 0, 0xFF );
InitBoneControllers();
SetThink( &COsprey::FindAllThink );
SetUse( &COsprey::CommandUse );
if( !( pev->spawnflags & SF_WAITFORTRIGGER ) )
{
pev->nextthink = gpGlobals->time + 1.0;
}
m_pos2 = pev->origin;
m_ang2 = pev->angles;
m_vel2 = pev->velocity;
}
void COsprey::Precache( void )
{
UTIL_PrecacheOther( "monster_human_grunt" );
PRECACHE_MODEL( "models/osprey.mdl" );
PRECACHE_MODEL( "models/HVR.mdl" );
PRECACHE_SOUND( "apache/ap_rotor4.wav" );
PRECACHE_SOUND( "weapons/mortarhit.wav" );
m_iSpriteTexture = PRECACHE_MODEL( "sprites/rope.spr" );
m_iExplode = PRECACHE_MODEL( "sprites/fexplo.spr" );
m_iTailGibs = PRECACHE_MODEL( "models/osprey_tailgibs.mdl" );
m_iBodyGibs = PRECACHE_MODEL( "models/osprey_bodygibs.mdl" );
m_iEngineGibs = PRECACHE_MODEL( "models/osprey_enginegibs.mdl" );
}
void COsprey::CommandUse( CBaseEntity *pActivator, CBaseEntity *pCaller, USE_TYPE useType, float value )
{
pev->nextthink = gpGlobals->time + 0.1;
}
void COsprey::FindAllThink( void )
{
CBaseEntity *pEntity = NULL;
m_iUnits = 0;
while( m_iUnits < MAX_CARRY && ( pEntity = UTIL_FindEntityByClassname( pEntity, "monster_human_grunt" ) ) != NULL )
{
if( pEntity->IsAlive() )
{
m_hGrunt[m_iUnits] = pEntity;
m_vecOrigin[m_iUnits] = pEntity->pev->origin;
m_iUnits++;
}
}
if( m_iUnits == 0 )
{
ALERT( at_console, "osprey error: no grunts to resupply\n" );
UTIL_Remove( this );
return;
}
SetThink( &COsprey::FlyThink );
pev->nextthink = gpGlobals->time + 0.1;
m_startTime = gpGlobals->time;
}
void COsprey::DeployThink( void )
{
UTIL_MakeAimVectors( pev->angles );
Vector vecForward = gpGlobals->v_forward;
Vector vecRight = gpGlobals->v_right;
Vector vecUp = gpGlobals->v_up;
Vector vecSrc;
TraceResult tr;
UTIL_TraceLine( pev->origin, pev->origin + Vector( 0, 0, -4096.0 ), ignore_monsters, ENT( pev ), &tr );
CSoundEnt::InsertSound( bits_SOUND_DANGER, tr.vecEndPos, 400, 0.3 );
vecSrc = pev->origin + vecForward * 32 + vecRight * 100 + vecUp * -96;
m_hRepel[0] = MakeGrunt( vecSrc );
vecSrc = pev->origin + vecForward * -64 + vecRight * 100 + vecUp * -96;
m_hRepel[1] = MakeGrunt( vecSrc );
vecSrc = pev->origin + vecForward * 32 + vecRight * -100 + vecUp * -96;
m_hRepel[2] = MakeGrunt( vecSrc );
vecSrc = pev->origin + vecForward * -64 + vecRight * -100 + vecUp * -96;
m_hRepel[3] = MakeGrunt( vecSrc );
SetThink( &COsprey::HoverThink );
pev->nextthink = gpGlobals->time + 0.1;
}
BOOL COsprey::HasDead()
{
for( int i = 0; i < m_iUnits; i++ )
{
if( m_hGrunt[i] == 0 || !m_hGrunt[i]->IsAlive() )
{
return TRUE;
}
else
{
m_vecOrigin[i] = m_hGrunt[i]->pev->origin; // send them to where they died
}
}
return FALSE;
}
CBaseMonster *COsprey::MakeGrunt( Vector vecSrc )
{
CBaseEntity *pEntity;
CBaseMonster *pGrunt;
TraceResult tr;
UTIL_TraceLine( vecSrc, vecSrc + Vector( 0, 0, -4096.0 ), dont_ignore_monsters, ENT( pev ), &tr );
if( tr.pHit && Instance( tr.pHit )->pev->solid != SOLID_BSP )
return NULL;
for( int i = 0; i < m_iUnits; i++ )
{
if( m_hGrunt[i] == 0 || !m_hGrunt[i]->IsAlive() )
{
if( m_hGrunt[i] != 0 && m_hGrunt[i]->pev->rendermode == kRenderNormal )
{
m_hGrunt[i]->SUB_StartFadeOut();
}
pEntity = Create( "monster_human_grunt", vecSrc, pev->angles );
pGrunt = pEntity->MyMonsterPointer();
pGrunt->pev->movetype = MOVETYPE_FLY;
pGrunt->pev->velocity = Vector( 0, 0, RANDOM_FLOAT( -196, -128 ) );
pGrunt->SetActivity( ACT_GLIDE );
CBeam *pBeam = CBeam::BeamCreate( "sprites/rope.spr", 10 );
pBeam->PointEntInit( vecSrc + Vector(0,0,112), pGrunt->entindex() );
pBeam->SetFlags( BEAM_FSOLID );
pBeam->SetColor( 255, 255, 255 );
pBeam->SetThink( &CBaseEntity::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];
m_hGrunt[i] = pGrunt;
return pGrunt;
}
}
// ALERT( at_console, "none dead\n");
return NULL;
}
void COsprey::HoverThink( void )
{
int i;
for( i = 0; i < 4; i++ )
{
if( m_hRepel[i] != 0 && m_hRepel[i]->pev->health > 0 && !( m_hRepel[i]->pev->flags & FL_ONGROUND ) )
{
break;
}
}
if( i == 4 )
{
m_startTime = gpGlobals->time;
SetThink( &COsprey::FlyThink );
}
pev->nextthink = gpGlobals->time + 0.1;
UTIL_MakeAimVectors( pev->angles );
ShowDamage();
}
void COsprey::UpdateGoal()
{
if( m_pGoalEnt )
{
m_pos1 = m_pos2;
m_ang1 = m_ang2;
m_vel1 = m_vel2;
m_pos2 = m_pGoalEnt->pev->origin;
m_ang2 = m_pGoalEnt->pev->angles;
UTIL_MakeAimVectors( Vector( 0, m_ang2.y, 0 ) );
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() + m_pGoalEnt->pev->speed );
if( m_ang1.y - m_ang2.y < -180 )
{
m_ang1.y += 360;
}
else if( m_ang1.y - m_ang2.y > 180 )
{
m_ang1.y -= 360;
}
if( m_pGoalEnt->pev->speed < 400 )
m_flIdealtilt = 0;
else
m_flIdealtilt = -90;
}
else
{
ALERT( at_console, "osprey missing target" );
}
}
void COsprey::FlyThink( void )
{
StudioFrameAdvance();
pev->nextthink = gpGlobals->time + 0.1;
if( m_pGoalEnt == NULL && !FStringNull( pev->target) )// this monster has a target
{
m_pGoalEnt = CBaseEntity::Instance( FIND_ENTITY_BY_TARGETNAME( NULL, STRING( pev->target ) ) );
UpdateGoal();
}
if( gpGlobals->time > m_startTime + m_dTime )
{
if( m_pGoalEnt->pev->speed == 0 )
{
SetThink( &COsprey::DeployThink );
}
do{
m_pGoalEnt = CBaseEntity::Instance( FIND_ENTITY_BY_TARGETNAME( NULL, STRING( m_pGoalEnt->pev->target ) ) );
} while( m_pGoalEnt->pev->speed < 400 && !HasDead() );
UpdateGoal();
}
Flight();
ShowDamage();
}
void COsprey::Flight()
{
float t = ( gpGlobals->time - m_startTime );
float scale = 1.0 / m_dTime;
float f = UTIL_SplineFraction( t * scale, 1.0 );
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( pev, pos );
pev->angles = ang;
UTIL_MakeAimVectors( pev->angles );
float flSpeed = DotProduct( gpGlobals->v_forward, m_velocity );
// float flSpeed = DotProduct( gpGlobals->v_forward, pev->velocity );
float m_flIdealtilt = ( 160 - flSpeed ) / 10.0;
// ALERT( at_console, "%f %f\n", flSpeed, flIdealtilt );
if( m_flRotortilt < m_flIdealtilt )
{
m_flRotortilt += 0.5;
if ( m_flRotortilt > 0 )
m_flRotortilt = 0;
}
if( m_flRotortilt > m_flIdealtilt )
{
m_flRotortilt -= 0.5;
if( m_flRotortilt < -90 )
m_flRotortilt = -90;
}
SetBoneController( 0, m_flRotortilt );
if( m_iSoundState == 0 )
{
EMIT_SOUND_DYN( ENT( pev ), CHAN_STATIC, "apache/ap_rotor4.wav", 1.0, 0.15, 0, 110 );
// EMIT_SOUND_DYN( ENT( pev ), CHAN_STATIC, "apache/ap_whine1.wav", 0.5, 0.2, 0, 110 );
m_iSoundState = SND_CHANGE_PITCH; // hack for going through level transitions
}
else
{
CBaseEntity *pPlayer = NULL;
pPlayer = UTIL_FindEntityByClassname( NULL, "player" );
// UNDONE: this needs to send different sounds to every player for multiplayer.
if( pPlayer )
{
float pitch = DotProduct( m_velocity - pPlayer->pev->velocity, ( pPlayer->pev->origin - pev->origin ).Normalize() );
pitch = (int)( 100 + pitch / 75.0 );
if( pitch > 250 )
pitch = 250;
if( pitch < 50 )
pitch = 50;
if( pitch == 100 )
pitch = 101;
if( pitch != m_iPitch )
{
m_iPitch = pitch;
EMIT_SOUND_DYN( ENT( pev ), CHAN_STATIC, "apache/ap_rotor4.wav", 1.0, 0.15, SND_CHANGE_PITCH | SND_CHANGE_VOL, pitch );
// ALERT( at_console, "%.0f\n", pitch );
}
}
// EMIT_SOUND_DYN( ENT( pev ), CHAN_STATIC, "apache/ap_whine1.wav", flVol, 0.2, SND_CHANGE_PITCH | SND_CHANGE_VOL, pitch );
}
}
void COsprey::HitTouch( CBaseEntity *pOther )
{
pev->nextthink = gpGlobals->time + 2.0;
}
/*
int COsprey::TakeDamage( entvars_t *pevInflictor, entvars_t *pevAttacker, float flDamage, int bitsDamageType )
{
if( m_flRotortilt <= -90 )
{
m_flRotortilt = 0;
}
else
{
m_flRotortilt -= 45;
}
SetBoneController( 0, m_flRotortilt );
return 0;
}
*/
void COsprey::Killed( entvars_t *pevAttacker, int iGib )
{
pev->movetype = MOVETYPE_TOSS;
pev->gravity = 0.3;
pev->velocity = m_velocity;
pev->avelocity = Vector( RANDOM_FLOAT( -20, 20 ), 0, RANDOM_FLOAT( -50, 50 ) );
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 );
pev->nextthink = gpGlobals->time + 0.1;
pev->health = 0;
pev->takedamage = DAMAGE_NO;
m_startTime = gpGlobals->time + 4.0;
}
void COsprey::CrashTouch( CBaseEntity *pOther )
{
// only crash if we hit something solid
if( pOther->pev->solid == SOLID_BSP )
{
SetTouch( NULL );
m_startTime = gpGlobals->time;
pev->nextthink = gpGlobals->time;
m_velocity = pev->velocity;
}
}
void COsprey::DyingThink( void )
{
StudioFrameAdvance();
pev->nextthink = gpGlobals->time + 0.1;
pev->avelocity = pev->avelocity * 1.02;
// still falling?
if( m_startTime > gpGlobals->time )
{
UTIL_MakeAimVectors( pev->angles );
ShowDamage();
Vector vecSpot = pev->origin + pev->velocity * 0.2;
// random explosions
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 ) );
WRITE_COORD( vecSpot.z + RANDOM_FLOAT( -150, -50 ) );
WRITE_SHORT( g_sModelIndexFireball );
WRITE_BYTE( RANDOM_LONG( 0, 29 ) + 30 ); // scale * 10
WRITE_BYTE( 12 ); // framerate
WRITE_BYTE( TE_EXPLFLAG_NONE );
MESSAGE_END();
// lots of smoke
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 ) );
WRITE_COORD( vecSpot.z + RANDOM_FLOAT( -150, -50 ) );
WRITE_SHORT( g_sModelIndexSmoke );
WRITE_BYTE( 100 ); // scale * 10
WRITE_BYTE( 10 ); // framerate
MESSAGE_END();
vecSpot = pev->origin + ( pev->mins + pev->maxs ) * 0.5;
MESSAGE_BEGIN( MSG_PVS, SVC_TEMPENTITY, vecSpot );
WRITE_BYTE( TE_BREAKMODEL);
// position
WRITE_COORD( vecSpot.x );
WRITE_COORD( vecSpot.y );
WRITE_COORD( vecSpot.z );
// size
WRITE_COORD( 800 );
WRITE_COORD( 800 );
WRITE_COORD( 132 );
// velocity
WRITE_COORD( pev->velocity.x );
WRITE_COORD( pev->velocity.y );
WRITE_COORD( pev->velocity.z );
// randomization
WRITE_BYTE( 50 );
// Model
WRITE_SHORT( m_iTailGibs ); //model id#
// # of shards
WRITE_BYTE( 8 ); // let client decide
// duration
WRITE_BYTE( 200 );// 10.0 seconds
// flags
WRITE_BYTE( BREAK_METAL );
MESSAGE_END();
// don't stop it we touch a entity
pev->flags &= ~FL_ONGROUND;
pev->nextthink = gpGlobals->time + 0.2;
return;
}
else
{
Vector vecSpot = pev->origin + ( pev->mins + pev->maxs ) * 0.5;
/*
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 );
WRITE_COORD( vecSpot.z + 512 );
WRITE_SHORT( m_iExplode );
WRITE_BYTE( 250 ); // scale * 10
WRITE_BYTE( 10 ); // framerate
MESSAGE_END();
*/
// gibs
MESSAGE_BEGIN( MSG_PVS, SVC_TEMPENTITY, vecSpot );
WRITE_BYTE( TE_SPRITE );
WRITE_COORD( vecSpot.x );
WRITE_COORD( vecSpot.y );
WRITE_COORD( vecSpot.z + 512 );
WRITE_SHORT( m_iExplode );
WRITE_BYTE( 250 ); // scale * 10
WRITE_BYTE( 255 ); // brightness
MESSAGE_END();
/*
MESSAGE_BEGIN( MSG_BROADCAST, SVC_TEMPENTITY );
WRITE_BYTE( TE_SMOKE );
WRITE_COORD( vecSpot.x );
WRITE_COORD( vecSpot.y );
WRITE_COORD( vecSpot.z + 300 );
WRITE_SHORT( g_sModelIndexSmoke );
WRITE_BYTE( 250 ); // scale * 10
WRITE_BYTE( 6 ); // framerate
MESSAGE_END();
*/
// blast circle
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);
WRITE_COORD( pev->origin.x);
WRITE_COORD( pev->origin.y);
WRITE_COORD( pev->origin.z + 2000 ); // reach damage radius over .2 seconds
WRITE_SHORT( m_iSpriteTexture );
WRITE_BYTE( 0 ); // startframe
WRITE_BYTE( 0 ); // framerate
WRITE_BYTE( 4 ); // life
WRITE_BYTE( 32 ); // width
WRITE_BYTE( 0 ); // noise
WRITE_BYTE( 255 ); // r, g, b
WRITE_BYTE( 255 ); // r, g, b
WRITE_BYTE( 192 ); // r, g, b
WRITE_BYTE( 128 ); // brightness
WRITE_BYTE( 0 ); // speed
MESSAGE_END();
EMIT_SOUND( ENT( pev ), CHAN_STATIC, "weapons/mortarhit.wav", 1.0, 0.3 );
RadiusDamage( pev->origin, pev, pev, 300, CLASS_NONE, DMG_BLAST );
// gibs
vecSpot = pev->origin + ( pev->mins + pev->maxs ) * 0.5;
MESSAGE_BEGIN( MSG_PAS, SVC_TEMPENTITY, vecSpot );
WRITE_BYTE( TE_BREAKMODEL);
// position
WRITE_COORD( vecSpot.x );
WRITE_COORD( vecSpot.y );
WRITE_COORD( vecSpot.z + 64 );
// size
WRITE_COORD( 800 );
WRITE_COORD( 800 );
WRITE_COORD( 128 );
// velocity
WRITE_COORD( m_velocity.x );
WRITE_COORD( m_velocity.y );
WRITE_COORD( fabs( m_velocity.z ) * 0.25 );
// randomization
WRITE_BYTE( 40 );
// Model
WRITE_SHORT( m_iBodyGibs ); //model id#
// # of shards
WRITE_BYTE( 128 );
// duration
WRITE_BYTE( 200 );// 10.0 seconds
// flags
WRITE_BYTE( BREAK_METAL );
MESSAGE_END();
UTIL_Remove( this );
}
}
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, SVC_TEMPENTITY, vecSrc );
WRITE_BYTE( TE_SMOKE );
WRITE_COORD( vecSrc.x );
WRITE_COORD( vecSrc.y );
WRITE_COORD( vecSrc.z );
WRITE_SHORT( g_sModelIndexSmoke );
WRITE_BYTE( RANDOM_LONG( 0, 9 ) + 20 ); // scale * 10
WRITE_BYTE( 12 ); // framerate
MESSAGE_END();
if( m_iDoLeftSmokePuff > 0 )
m_iDoLeftSmokePuff--;
}
if( m_iDoRightSmokePuff > 0 || RANDOM_LONG( 0, 99 ) > m_flRightHealth )
{
Vector vecSrc = pev->origin + gpGlobals->v_right * 340;
MESSAGE_BEGIN( MSG_PVS, SVC_TEMPENTITY, vecSrc );
WRITE_BYTE( TE_SMOKE );
WRITE_COORD( vecSrc.x );
WRITE_COORD( vecSrc.y );
WRITE_COORD( vecSrc.z );
WRITE_SHORT( g_sModelIndexSmoke );
WRITE_BYTE( RANDOM_LONG( 0, 9 ) + 20 ); // scale * 10
WRITE_BYTE( 12 ); // framerate
MESSAGE_END();
if( m_iDoRightSmokePuff > 0 )
m_iDoRightSmokePuff--;
}
}
void COsprey::TraceAttack( entvars_t *pevAttacker, float flDamage, Vector vecDir, TraceResult *ptr, int bitsDamageType )
{
// ALERT( at_console, "%d %.0f\n", ptr->iHitgroup, flDamage );
// only so much per engine
if( ptr->iHitgroup == 3 )
{
if( m_flRightHealth < 0 )
return;
else
m_flRightHealth -= flDamage;
m_iDoLeftSmokePuff = 3 + ( flDamage / 5.0 );
}
if( ptr->iHitgroup == 2 )
{
if( m_flLeftHealth < 0 )
return;
else
m_flLeftHealth -= flDamage;
m_iDoRightSmokePuff = 3 + ( flDamage / 5.0 );
}
// hit hard, hits cockpit, hits engines
if( flDamage > 50 || ptr->iHitgroup == 1 || ptr->iHitgroup == 2 || ptr->iHitgroup == 3 )
{
// ALERT( at_console, "%.0f\n", flDamage );
AddMultiDamage( pevAttacker, this, flDamage, bitsDamageType );
}
else
{
UTIL_Sparks( ptr->vecEndPos );
}
}

View File

@ -1,309 +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.
*
****/
#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_WeaponTimeBase() + 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 = UTIL_WeaponTimeBase() + 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
{
PlayEmptySound();
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_WeaponTimeBase() + UTIL_SharedRandomFloat( m_pPlayer->random_seed, 10, 15 );
}
void CPython::Reload( void )
{
if( m_pPlayer->m_rgAmmo[m_iPrimaryAmmoType] <= 0 || m_iClip == PYTHON_MAX_CLIP )
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( PYTHON_MAX_CLIP, 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, 0.0f, 1.0f );
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
{
iAnim = PYTHON_FIDGET;
m_flTimeWeaponIdle = UTIL_WeaponTimeBase() + ( 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

View File

@ -1,99 +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.
*
****/
//=========================================================
// 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
//=========================================================

View File

@ -1,459 +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.
*
****/
//=========================================================
// cockroach
//=========================================================
#include "extdll.h"
#include "util.h"
#include "cbase.h"
#include "monsters.h"
#include "schedule.h"
#include "soundent.h"
#include "decals.h"
#define ROACH_IDLE 0
#define ROACH_BORED 1
#define ROACH_SCARED_BY_ENT 2
#define ROACH_SCARED_BY_LIGHT 3
#define ROACH_SMELL_FOOD 4
#define ROACH_EAT 5
//=========================================================
// Monster's Anim Events Go Here
//=========================================================
class CRoach : public CBaseMonster
{
public:
void Spawn( void );
void Precache( void );
void SetYawSpeed( void );
void EXPORT MonsterThink ( void );
void Move( float flInterval );
void PickNewDest( int iCondition );
void EXPORT Touch( CBaseEntity *pOther );
void Killed( entvars_t *pevAttacker, int iGib );
float m_flLastLightLevel;
float m_flNextSmellTime;
int Classify( void );
void Look( int iDistance );
int ISoundMask( void );
// UNDONE: These don't necessarily need to be save/restored, but if we add more data, it may
BOOL m_fLightHacked;
int m_iMode;
// -----------------------------
};
LINK_ENTITY_TO_CLASS( monster_cockroach, CRoach )
//=========================================================
// ISoundMask - returns a bit mask indicating which types
// of sounds this monster regards. In the base class implementation,
// monsters care about all sounds, but no scents.
//=========================================================
int CRoach::ISoundMask( void )
{
return bits_SOUND_CARCASS | bits_SOUND_MEAT;
}
//=========================================================
// Classify - indicates this monster's place in the
// relationship table.
//=========================================================
int CRoach::Classify( void )
{
return CLASS_INSECT;
}
//=========================================================
// Touch
//=========================================================
void CRoach::Touch( CBaseEntity *pOther )
{
Vector vecSpot;
TraceResult tr;
if( pOther->pev->velocity == g_vecZero || !pOther->IsPlayer() )
{
return;
}
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 );
// This isn't really blood. So you don't have to screen it out based on violence levels (UTIL_ShouldShowBlood())
UTIL_DecalTrace( &tr, DECAL_YBLOOD1 + RANDOM_LONG( 0, 5 ) );
TakeDamage( pOther->pev, pOther->pev, pev->health, DMG_CRUSH );
}
//=========================================================
// SetYawSpeed - allows each sequence to have a different
// turn rate associated with it.
//=========================================================
void CRoach::SetYawSpeed( void )
{
int ys;
ys = 120;
pev->yaw_speed = ys;
}
//=========================================================
// Spawn
//=========================================================
void CRoach::Spawn()
{
Precache();
SET_MODEL( ENT( pev ), "models/roach.mdl" );
UTIL_SetSize( pev, Vector( -1, -1, 0 ), Vector( 1, 1, 2 ) );
pev->solid = SOLID_SLIDEBOX;
pev->movetype = MOVETYPE_STEP;
m_bloodColor = BLOOD_COLOR_YELLOW;
pev->effects = 0;
pev->health = 1;
m_flFieldOfView = 0.5;// indicates the width of this monster's forward view cone ( as a dotproduct result )
m_MonsterState = MONSTERSTATE_NONE;
MonsterInit();
SetActivity( ACT_IDLE );
pev->view_ofs = Vector( 0, 0, 1 );// position of the eyes relative to monster's origin.
pev->takedamage = DAMAGE_YES;
m_fLightHacked = FALSE;
m_flLastLightLevel = -1;
m_iMode = ROACH_IDLE;
m_flNextSmellTime = gpGlobals->time;
}
//=========================================================
// Precache - precaches all resources this monster needs
//=========================================================
void CRoach::Precache()
{
PRECACHE_MODEL( "models/roach.mdl" );
PRECACHE_SOUND( "roach/rch_die.wav" );
PRECACHE_SOUND( "roach/rch_walk.wav" );
PRECACHE_SOUND( "roach/rch_smash.wav" );
}
//=========================================================
// Killed.
//=========================================================
void CRoach::Killed( entvars_t *pevAttacker, int iGib )
{
pev->solid = SOLID_NOT;
//random sound
if( RANDOM_LONG( 0, 4 ) == 1 )
{
EMIT_SOUND_DYN( ENT( pev ), CHAN_VOICE, "roach/rch_die.wav", 0.8, ATTN_NORM, 0, 80 + RANDOM_LONG( 0, 39 ) );
}
else
{
EMIT_SOUND_DYN( ENT( pev ), CHAN_BODY, "roach/rch_smash.wav", 0.7, ATTN_NORM, 0, 80 + RANDOM_LONG( 0, 39 ) );
}
CSoundEnt::InsertSound( bits_SOUND_WORLD, pev->origin, 128, 1 );
CBaseEntity *pOwner = CBaseEntity::Instance( pev->owner );
if( pOwner )
{
pOwner->DeathNotice( pev );
}
UTIL_Remove( this );
}
//=========================================================
// MonsterThink, overridden for roaches.
//=========================================================
void CRoach::MonsterThink( void )
{
if( FNullEnt( FIND_CLIENT_IN_PVS( edict() ) ) )
pev->nextthink = gpGlobals->time + RANDOM_FLOAT( 1, 1.5 );
else
pev->nextthink = gpGlobals->time + 0.1;// keep monster thinking
float flInterval = StudioFrameAdvance(); // animate
if( !m_fLightHacked )
{
// 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.
pev->nextthink = gpGlobals->time + 1;
m_fLightHacked = TRUE;
return;
}
else if( m_flLastLightLevel < 0 )
{
// collect light level for the first time, now that all of the lightmaps in the roach's area have been calculated.
m_flLastLightLevel = GETENTITYILLUM( ENT( pev ) );
}
switch( m_iMode )
{
case ROACH_IDLE:
case ROACH_EAT:
{
// if not moving, sample environment to see if anything scary is around. Do a radius search 'look' at random.
if( RANDOM_LONG( 0, 3 ) == 1 )
{
Look( 150 );
if( HasConditions( bits_COND_SEE_FEAR ) )
{
// if see something scary
//ALERT( at_aiconsole, "Scared\n" );
Eat( 30 + ( RANDOM_LONG( 0, 14 ) ) );// roach will ignore food for 30 to 45 seconds
PickNewDest( ROACH_SCARED_BY_ENT );
SetActivity( ACT_WALK );
}
else if( RANDOM_LONG( 0, 149 ) == 1 )
{
// if roach doesn't see anything, there's still a chance that it will move. (boredom)
//ALERT( at_aiconsole, "Bored\n" );
PickNewDest( ROACH_BORED );
SetActivity( ACT_WALK );
if( m_iMode == ROACH_EAT )
{
// roach will ignore food for 30 to 45 seconds if it got bored while eating.
Eat( 30 + ( RANDOM_LONG( 0, 14 ) ) );
}
}
}
// don't do this stuff if eating!
if( m_iMode == ROACH_IDLE )
{
if( FShouldEat() )
{
Listen();
}
if( GETENTITYILLUM( ENT( pev ) ) > m_flLastLightLevel )
{
// someone turned on lights!
//ALERT( at_console, "Lights!\n" );
PickNewDest( ROACH_SCARED_BY_LIGHT );
SetActivity( ACT_WALK );
}
else if( HasConditions( bits_COND_SMELL_FOOD ) )
{
CSound *pSound;
pSound = CSoundEnt::SoundPointerForIndex( m_iAudibleList );
// roach smells food and is just standing around. Go to food unless food isn't on same z-plane.
if( pSound && fabs( pSound->m_vecOrigin.z - pev->origin.z ) <= 3.0 )
{
PickNewDest( ROACH_SMELL_FOOD );
SetActivity( ACT_WALK );
}
}
}
break;
}
case ROACH_SCARED_BY_LIGHT:
{
// if roach was scared by light, then stop if we're over a spot at least as dark as where we started!
if( GETENTITYILLUM( ENT( pev ) ) <= m_flLastLightLevel )
{
SetActivity( ACT_IDLE );
m_flLastLightLevel = GETENTITYILLUM( ENT( pev ) );// make this our new light level.
}
break;
}
}
if( m_flGroundSpeed != 0 )
{
Move( flInterval );
}
}
//=========================================================
// Picks a new spot for roach to run to.(
//=========================================================
void CRoach::PickNewDest( int iCondition )
{
Vector vecNewDir;
Vector vecDest;
float flDist;
m_iMode = iCondition;
if( m_iMode == ROACH_SMELL_FOOD )
{
// find the food and go there.
CSound *pSound;
pSound = CSoundEnt::SoundPointerForIndex( m_iAudibleList );
if( pSound )
{
m_Route[0].vecLocation.x = pSound->m_vecOrigin.x + ( 3 - RANDOM_LONG( 0, 5 ) );
m_Route[0].vecLocation.y = pSound->m_vecOrigin.y + ( 3 - RANDOM_LONG( 0, 5 ) );
m_Route[0].vecLocation.z = pSound->m_vecOrigin.z;
m_Route[0].iType = bits_MF_TO_LOCATION;
m_movementGoal = RouteClassify( m_Route[0].iType );
return;
}
}
do
{
// picks a random spot, requiring that it be at least 128 units away
// else, the roach will pick a spot too close to itself and run in
// circles. this is a hack but buys me time to work on the real monsters.
vecNewDir.x = RANDOM_FLOAT( -1, 1 );
vecNewDir.y = RANDOM_FLOAT( -1, 1 );
flDist = 256 + ( RANDOM_LONG( 0, 255 ) );
vecDest = pev->origin + vecNewDir * flDist;
} while( ( vecDest - pev->origin ).Length2D() < 128 );
m_Route[0].vecLocation.x = vecDest.x;
m_Route[0].vecLocation.y = vecDest.y;
m_Route[0].vecLocation.z = pev->origin.z;
m_Route[0].iType = bits_MF_TO_LOCATION;
m_movementGoal = RouteClassify( m_Route[0].iType );
if( RANDOM_LONG( 0, 9 ) == 1 )
{
// every once in a while, a roach will play a skitter sound when they decide to run
EMIT_SOUND_DYN( ENT( pev ), CHAN_BODY, "roach/rch_walk.wav", 1, ATTN_NORM, 0, 80 + RANDOM_LONG( 0, 39 ) );
}
}
//=========================================================
// roach's move function
//=========================================================
void CRoach::Move( float flInterval )
{
float flWaypointDist;
Vector vecApex;
// local move to waypoint.
flWaypointDist = ( m_Route[m_iRouteIndex].vecLocation - pev->origin ).Length2D();
MakeIdealYaw( m_Route[m_iRouteIndex].vecLocation );
ChangeYaw( pev->yaw_speed );
UTIL_MakeVectors( pev->angles );
if( RANDOM_LONG( 0, 7 ) == 1 )
{
// randomly check for blocked path.(more random load balancing)
if( !WALK_MOVE( ENT( pev ), pev->ideal_yaw, 4, WALKMOVE_NORMAL ) )
{
// stuck, so just pick a new spot to run off to
PickNewDest( m_iMode );
}
}
WALK_MOVE( ENT( pev ), pev->ideal_yaw, m_flGroundSpeed * flInterval, WALKMOVE_NORMAL );
// if the waypoint is closer than step size, then stop after next step (ok for roach to overshoot)
if( flWaypointDist <= m_flGroundSpeed * flInterval )
{
// take truncated step and stop
SetActivity( ACT_IDLE );
m_flLastLightLevel = GETENTITYILLUM( ENT( pev ) );// this is roach's new comfortable light level
if( m_iMode == ROACH_SMELL_FOOD )
{
m_iMode = ROACH_EAT;
}
else
{
m_iMode = ROACH_IDLE;
}
}
if( RANDOM_LONG( 0, 149 ) == 1 && m_iMode != ROACH_SCARED_BY_LIGHT && m_iMode != ROACH_SMELL_FOOD )
{
// random skitter while moving as long as not on a b-line to get out of light or going to food
PickNewDest( FALSE );
}
}
//=========================================================
// Look - overriden for the roach, which can virtually see
// 360 degrees.
//=========================================================
void CRoach::Look( int iDistance )
{
CBaseEntity *pSightEnt = NULL;// the current visible entity that we're dealing with
CBaseEntity *pPreviousEnt;// the last entity added to the link list
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 );
// 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() ) ) )
{
return;
}
m_pLink = NULL;
pPreviousEnt = this;
// Does sphere also limit itself to PVS?
// Examine all entities within a reasonable radius
// !!!PERFORMANCE - let's trivially reject the ent list before radius searching!
while( ( pSightEnt = UTIL_FindEntityInSphere( pSightEnt, pev->origin, iDistance ) ) != NULL )
{
// only consider ents that can be damaged. !!!temporarily only considering other monsters and clients
if( pSightEnt->IsPlayer() || FBitSet( pSightEnt->pev->flags, FL_MONSTER ) )
{
if( /*FVisible( pSightEnt ) &&*/ !FBitSet( pSightEnt->pev->flags, FL_NOTARGET ) && pSightEnt->pev->health > 0 )
{
// NULL the Link pointer for each ent added to the link list. If other ents follow, the will overwrite
// this value. If this ent happens to be the last, the list will be properly terminated.
pPreviousEnt->m_pLink = pSightEnt;
pSightEnt->m_pLink = NULL;
pPreviousEnt = pSightEnt;
// 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_FR:
iSighted |= bits_COND_SEE_FEAR;
break;
case R_NO:
break;
default:
ALERT( at_console, "%s can't asses %s\n", STRING( pev->classname ), STRING( pSightEnt->pev->classname ) );
break;
}
}
}
}
SetConditions( iSighted );
}
//=========================================================
// AI Schedules Specific to this monster
//=========================================================

View File

@ -1,480 +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.
*
****/
#if !defined( OEM_BUILD ) && !defined( HLDEMO_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 satchel_state
{
SATCHEL_IDLE = 0,
SATCHEL_READY,
SATCHEL_RELOAD
};
enum satchel_e
{
SATCHEL_IDLE1 = 0,
SATCHEL_FIDGET1,
SATCHEL_DRAW,
SATCHEL_DROP
};
enum satchel_radio_e
{
SATCHEL_RADIO_IDLE1 = 0,
SATCHEL_RADIO_FIDGET1,
SATCHEL_RADIO_DRAW,
SATCHEL_RADIO_FIRE,
SATCHEL_RADIO_HOLSTER
};
class CSatchelCharge : public CGrenade
{
Vector m_lastBounceOrigin; // Used to fix a bug in engine: when object isn't moving, but its speed isn't 0 and on ground isn't set
void Spawn( void );
void Precache( void );
void BounceSound( void );
void EXPORT SatchelSlide( CBaseEntity *pOther );
void EXPORT SatchelThink( void );
public:
void Deactivate( void );
};
LINK_ENTITY_TO_CLASS( monster_satchel, CSatchelCharge )
//=========================================================
// Deactivate - do whatever it is we do to an orphaned
// satchel when we don't want it in the world anymore.
//=========================================================
void CSatchelCharge::Deactivate( void )
{
pev->solid = SOLID_NOT;
UTIL_Remove( this );
}
void CSatchelCharge::Spawn( void )
{
Precache();
// motor
pev->movetype = MOVETYPE_BOUNCE;
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( pev, pev->origin );
SetTouch( &CSatchelCharge::SatchelSlide );
SetUse( &CGrenade::DetonateUse );
SetThink( &CSatchelCharge::SatchelThink );
pev->nextthink = gpGlobals->time + 0.1;
pev->gravity = 0.5;
pev->friction = 0.8;
pev->dmg = gSkillData.plrDmgSatchel;
// ResetSequenceInfo();
pev->sequence = 1;
}
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;
// 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 );
if( tr.flFraction < 1.0 )
{
// add a bit of static friction
pev->velocity = pev->velocity * 0.95;
pev->avelocity = pev->avelocity * 0.9;
// play sliding sound, volume based on velocity
}
if( !( pev->flags & FL_ONGROUND ) && pev->velocity.Length2D() > 10 )
{
// Fix for a bug in engine: when object isn't moving, but its speed isn't 0 and on ground isn't set
if( pev->origin != m_lastBounceOrigin )
BounceSound();
}
m_lastBounceOrigin = pev->origin;
// There is no model animation so commented this out to prevent net traffic
// StudioFrameAdvance();
}
void CSatchelCharge::SatchelThink( void )
{
// There is no model animation so commented this out to prevent net traffic
// StudioFrameAdvance();
pev->nextthink = gpGlobals->time + 0.1;
if( !IsInWorld() )
{
UTIL_Remove( this );
return;
}
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->movetype = MOVETYPE_BOUNCE;
}
else
{
pev->velocity.z -= 8;
}
}
void CSatchelCharge::Precache( void )
{
PRECACHE_MODEL( "models/w_satchel.mdl" );
PRECACHE_SOUND( "weapons/g_bounce1.wav" );
PRECACHE_SOUND( "weapons/g_bounce2.wav" );
PRECACHE_SOUND( "weapons/g_bounce3.wav" );
}
void CSatchelCharge::BounceSound( void )
{
switch( RANDOM_LONG( 0, 2 ) )
{
case 0:
EMIT_SOUND( ENT( pev ), CHAN_VOICE, "weapons/g_bounce1.wav", 1, ATTN_NORM );
break;
case 1:
EMIT_SOUND( ENT( pev ), CHAN_VOICE, "weapons/g_bounce2.wav", 1, ATTN_NORM );
break;
case 2:
EMIT_SOUND( ENT( pev ), CHAN_VOICE, "weapons/g_bounce3.wav", 1, ATTN_NORM );
break;
}
}
LINK_ENTITY_TO_CLASS( weapon_satchel, CSatchel )
//=========================================================
// CALLED THROUGH the newly-touched weapon's instance. The existing player weapon is pOriginal
//=========================================================
int CSatchel::AddDuplicate( CBasePlayerItem *pOriginal )
{
CSatchel *pSatchel;
#ifdef CLIENT_DLL
if( bIsMultiplayer() )
#else
if( g_pGameRules->IsMultiplayer() )
#endif
{
pSatchel = (CSatchel *)pOriginal;
if( pSatchel->m_chargeReady != SATCHEL_IDLE )
{
// player has some satchels deployed. Refuse to add more.
return FALSE;
}
}
return CBasePlayerWeapon::AddDuplicate( pOriginal );
}
//=========================================================
//=========================================================
int CSatchel::AddToPlayer( CBasePlayer *pPlayer )
{
int bResult = CBasePlayerItem::AddToPlayer( pPlayer );
pPlayer->pev->weapons |= ( 1 << m_iId );
m_chargeReady = SATCHEL_IDLE;// this satchel charge weapon now forgets that any satchels are deployed by it.
if( bResult )
{
return AddWeapon();
}
return FALSE;
}
void CSatchel::Spawn()
{
Precache();
m_iId = WEAPON_SATCHEL;
SET_MODEL( ENT( pev ), "models/w_satchel.mdl" );
m_iDefaultAmmo = SATCHEL_DEFAULT_GIVE;
FallInit();// get ready to fall down.
}
void CSatchel::Precache( void )
{
PRECACHE_MODEL( "models/v_satchel.mdl" );
PRECACHE_MODEL( "models/v_satchel_radio.mdl" );
PRECACHE_MODEL( "models/w_satchel.mdl" );
PRECACHE_MODEL( "models/p_satchel.mdl" );
PRECACHE_MODEL( "models/p_satchel_radio.mdl" );
UTIL_PrecacheOther( "monster_satchel" );
}
int CSatchel::GetItemInfo( ItemInfo *p )
{
p->pszName = STRING( pev->classname );
p->pszAmmo1 = "Satchel Charge";
p->iMaxAmmo1 = SATCHEL_MAX_CARRY;
p->pszAmmo2 = NULL;
p->iMaxAmmo2 = -1;
p->iMaxClip = WEAPON_NOCLIP;
p->iSlot = 4;
p->iPosition = 1;
p->iFlags = ITEM_FLAG_SELECTONEMPTY | ITEM_FLAG_LIMITINWORLD | ITEM_FLAG_EXHAUSTIBLE;
p->iId = m_iId = WEAPON_SATCHEL;
p->iWeight = SATCHEL_WEIGHT;
return 1;
}
//=========================================================
//=========================================================
BOOL CSatchel::IsUseable( void )
{
return CanDeploy();
}
BOOL CSatchel::CanDeploy( void )
{
if( m_pPlayer->m_rgAmmo[PrimaryAmmoIndex()] > 0 )
{
// player is carrying some satchels
return TRUE;
}
if( m_chargeReady )
{
// 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() + 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" );
else
return DefaultDeploy( "models/v_satchel.mdl", "models/p_satchel.mdl", SATCHEL_DRAW, "trip" );
return TRUE;
}
void CSatchel::Holster( int skiplocal /* = 0 */ )
{
m_pPlayer->m_flNextAttack = UTIL_WeaponTimeBase() + 0.5;
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 != SATCHEL_READY )
{
m_pPlayer->pev->weapons &= ~( 1 << WEAPON_SATCHEL );
DestroyItem();
}
}
void CSatchel::PrimaryAttack()
{
switch( m_chargeReady )
{
case SATCHEL_IDLE:
{
Throw();
}
break;
case SATCHEL_READY:
{
SendWeaponAnim( SATCHEL_RADIO_FIRE );
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" ) )
{
if( pSatchel->pev->owner == pPlayer )
{
pSatchel->Use( m_pPlayer, m_pPlayer, USE_ON, 0 );
}
}
}
m_chargeReady = SATCHEL_RELOAD;
m_flNextPrimaryAttack = GetNextAttackDelay( 0.5 );
m_flNextSecondaryAttack = UTIL_WeaponTimeBase() + 0.5;
m_flTimeWeaponIdle = UTIL_WeaponTimeBase() + 0.5;
break;
}
case SATCHEL_RELOAD:
// we're reloading, don't allow fire
break;
}
}
void CSatchel::SecondaryAttack( void )
{
if( m_chargeReady != SATCHEL_RELOAD )
{
Throw();
}
}
void CSatchel::Throw( void )
{
if( m_pPlayer->m_rgAmmo[m_iPrimaryAmmoType] )
{
#ifndef CLIENT_DLL
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_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 = SATCHEL_READY;
m_pPlayer->m_rgAmmo[m_iPrimaryAmmoType]--;
m_flNextPrimaryAttack = GetNextAttackDelay( 1.0 );
m_flNextSecondaryAttack = UTIL_WeaponTimeBase() + 0.5;
}
}
void CSatchel::WeaponIdle( void )
{
if( m_flTimeWeaponIdle > UTIL_WeaponTimeBase() )
return;
switch( m_chargeReady )
{
case SATCHEL_IDLE:
SendWeaponAnim( SATCHEL_FIDGET1 );
// use tripmine animations
strcpy( m_pPlayer->m_szAnimExtention, "trip" );
break;
case SATCHEL_READY:
SendWeaponAnim( SATCHEL_RADIO_FIDGET1 );
// use hivehand animations
strcpy( m_pPlayer->m_szAnimExtention, "hive" );
break;
case SATCHEL_RELOAD:
if( !m_pPlayer->m_rgAmmo[m_iPrimaryAmmoType] )
{
m_chargeReady = 0;
RetireWeapon();
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
strcpy( m_pPlayer->m_szAnimExtention, "trip" );
m_flNextPrimaryAttack = GetNextAttackDelay( 0.5 );
m_flNextSecondaryAttack = UTIL_WeaponTimeBase() + 0.5;
m_chargeReady = SATCHEL_IDLE;
break;
}
m_flTimeWeaponIdle = UTIL_WeaponTimeBase() + UTIL_SharedRandomFloat( m_pPlayer->random_seed, 10, 15 );// how long till we do this again.
}
//=========================================================
// DeactivateSatchels - removes all satchels owned by
// the provided player. Should only be used upon death.
//
// Made this global on purpose.
//=========================================================
void DeactivateSatchels( CBasePlayer *pOwner )
{
edict_t *pFind;
pFind = FIND_ENTITY_BY_CLASSNAME( NULL, "monster_satchel" );
while( !FNullEnt( pFind ) )
{
CBaseEntity *pEnt = CBaseEntity::Instance( pFind );
CSatchelCharge *pSatchel = (CSatchelCharge *)pEnt;
if( pSatchel )
{
if( pSatchel->pev->owner == pOwner->edict() )
{
pSatchel->Deactivate();
}
}
pFind = FIND_ENTITY_BY_CLASSNAME( pFind, "monster_satchel" );
}
}
#endif

File diff suppressed because it is too large Load Diff

View File

@ -1,392 +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"
// 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
enum shotgun_e
{
SHOTGUN_IDLE = 0,
SHOTGUN_FIRE,
SHOTGUN_FIRE2,
SHOTGUN_RELOAD,
SHOTGUN_PUMP,
SHOTGUN_START_RELOAD,
SHOTGUN_DRAW,
SHOTGUN_HOLSTER,
SHOTGUN_IDLE4,
SHOTGUN_IDLE_DEEP
};
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
}
void CShotgun::Precache( void )
{
PRECACHE_MODEL( "models/v_shotgun.mdl" );
PRECACHE_MODEL( "models/w_shotgun.mdl" );
PRECACHE_MODEL( "models/p_shotgun.mdl" );
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
//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 );
p->pszAmmo1 = "buckshot";
p->iMaxAmmo1 = BUCKSHOT_MAX_CARRY;
p->pszAmmo2 = NULL;
p->iMaxAmmo2 = -1;
p->iMaxClip = SHOTGUN_MAX_CLIP;
p->iSlot = 2;
p->iPosition = 1;
p->iFlags = 0;
p->iId = m_iId = WEAPON_SHOTGUN;
p->iWeight = SHOTGUN_WEIGHT;
return 1;
}
BOOL CShotgun::Deploy()
{
return DefaultDeploy( "models/v_shotgun.mdl", "models/p_shotgun.mdl", SHOTGUN_DRAW, "shotgun" );
}
void CShotgun::PrimaryAttack()
{
// don't fire underwater
if( m_pPlayer->pev->waterlevel == 3 )
{
PlayEmptySound();
m_flNextPrimaryAttack = GetNextAttackDelay( 0.15 );
return;
}
if( m_iClip <= 0 )
{
Reload();
if( m_iClip == 0 )
PlayEmptySound();
return;
}
m_pPlayer->m_iWeaponVolume = LOUD_GUN_VOLUME;
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;
// 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
{
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
{
// 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( flags, m_pPlayer->edict(), m_usSingleFire, 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.5;
m_flNextPrimaryAttack = GetNextAttackDelay( 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 )
{
PlayEmptySound();
m_flNextPrimaryAttack = GetNextAttackDelay( 0.15 );
return;
}
if( m_iClip <= 1 )
{
Reload();
PlayEmptySound();
return;
}
m_pPlayer->m_iWeaponVolume = LOUD_GUN_VOLUME;
m_pPlayer->m_iWeaponFlash = NORMAL_GUN_FLASH;
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
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
{
// 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
{
// 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 = GetNextAttackDelay( 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;
}
void CShotgun::Reload( void )
{
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;
// check to see if we're ready to reload
if( m_fInSpecialReload == 0 )
{
SendWeaponAnim( SHOTGUN_START_RELOAD );
m_fInSpecialReload = 1;
m_pPlayer->m_flNextAttack = UTIL_WeaponTimeBase() + 0.6;
m_flTimeWeaponIdle = UTIL_WeaponTimeBase() + 0.6;
m_flNextPrimaryAttack = GetNextAttackDelay( 1.0 );
m_flNextSecondaryAttack = UTIL_WeaponTimeBase() + 1.0;
return;
}
else if( m_fInSpecialReload == 1 )
{
if( m_flTimeWeaponIdle > UTIL_WeaponTimeBase() )
return;
// was waiting for gun to move to side
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 ) );
SendWeaponAnim( SHOTGUN_RELOAD );
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_fInSpecialReload = 1;
}
}
void CShotgun::WeaponTick()
{
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;
}
}
void CShotgun::WeaponIdle( void )
{
ResetEmptySound();
m_pPlayer->GetAutoaimVector( AUTOAIM_5DEGREES );
if( m_flTimeWeaponIdle < UTIL_WeaponTimeBase() )
{
if( m_iClip == 0 && m_fInSpecialReload == 0 && m_pPlayer->m_rgAmmo[m_iPrimaryAmmoType] )
{
Reload();
}
else if( m_fInSpecialReload != 0 )
{
if( m_iClip != SHOTGUN_MAX_CLIP && m_pPlayer->m_rgAmmo[m_iPrimaryAmmoType] )
{
Reload();
}
else
{
// reload debounce has timed out
SendWeaponAnim( SHOTGUN_PUMP );
// 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
{
int iAnim;
float flRand = UTIL_SharedRandomFloat( m_pPlayer->random_seed, 0, 1 );
if( flRand <= 0.8 )
{
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 )
{
Precache();
SET_MODEL( ENT( pev ), "models/w_shotbox.mdl" );
CBasePlayerAmmo::Spawn();
}
void Precache( void )
{
PRECACHE_MODEL( "models/w_shotbox.mdl" );
PRECACHE_SOUND( "items/9mmclip1.wav" );
}
BOOL AddAmmo( CBaseEntity *pOther )
{
if( pOther->GiveAmmo( AMMO_BUCKSHOTBOX_GIVE, "buckshot", BUCKSHOT_MAX_CARRY ) != -1 )
{
EMIT_SOUND( ENT( pev ), CHAN_ITEM, "items/9mmclip1.wav", 1, ATTN_NORM );
return TRUE;
}
return FALSE;
}
};
LINK_ENTITY_TO_CLASS( ammo_buckshot, CShotgunAmmo )

View File

@ -1,326 +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.
*
****/
//
// teamplay_gamerules.cpp
//
#include "extdll.h"
#include "util.h"
#include "cbase.h"
#include "player.h"
#include "weapons.h"
#include "gamerules.h"
#include "skill.h"
#include "items.h"
extern DLL_GLOBAL CGameRules *g_pGameRules;
extern DLL_GLOBAL BOOL g_fGameOver;
extern int gmsgDeathMsg; // client dll messages
extern int gmsgScoreInfo;
extern int gmsgMOTD;
//=========================================================
//=========================================================
CHalfLifeRules::CHalfLifeRules( void )
{
RefreshSkillData();
}
//=========================================================
//=========================================================
void CHalfLifeRules::Think( void )
{
}
//=========================================================
//=========================================================
BOOL CHalfLifeRules::IsMultiplayer( void )
{
return FALSE;
}
//=========================================================
//=========================================================
BOOL CHalfLifeRules::IsDeathmatch( void )
{
return FALSE;
}
//=========================================================
//=========================================================
BOOL CHalfLifeRules::IsCoOp( void )
{
return FALSE;
}
//=========================================================
//=========================================================
BOOL CHalfLifeRules::FShouldSwitchWeapon( CBasePlayer *pPlayer, CBasePlayerItem *pWeapon )
{
if( !pPlayer->m_pActiveItem )
{
// player doesn't have an active item!
return TRUE;
}
if( !pPlayer->m_pActiveItem->CanHolster() )
{
return FALSE;
}
return TRUE;
}
//=========================================================
//=========================================================
BOOL CHalfLifeRules::GetNextBestWeapon( CBasePlayer *pPlayer, CBasePlayerItem *pCurrentWeapon )
{
return FALSE;
}
//=========================================================
//=========================================================
BOOL CHalfLifeRules::ClientConnected( edict_t *pEntity, const char *pszName, const char *pszAddress, char szRejectReason[128] )
{
return TRUE;
}
void CHalfLifeRules::InitHUD( CBasePlayer *pl )
{
}
//=========================================================
//=========================================================
void CHalfLifeRules::ClientDisconnected( edict_t *pClient )
{
}
//=========================================================
//=========================================================
float CHalfLifeRules::FlPlayerFallDamage( CBasePlayer *pPlayer )
{
// subtract off the speed at which a player is allowed to fall without being hurt,
// so damage will be based on speed beyond that, not the entire fall
pPlayer->m_flFallVelocity -= PLAYER_MAX_SAFE_FALL_SPEED;
return pPlayer->m_flFallVelocity * DAMAGE_FOR_FALL_SPEED;
}
//=========================================================
//=========================================================
void CHalfLifeRules::PlayerSpawn( CBasePlayer *pPlayer )
{
}
//=========================================================
//=========================================================
BOOL CHalfLifeRules::AllowAutoTargetCrosshair( void )
{
return ( g_iSkillLevel == SKILL_EASY );
}
//=========================================================
//=========================================================
void CHalfLifeRules::PlayerThink( CBasePlayer *pPlayer )
{
}
//=========================================================
//=========================================================
BOOL CHalfLifeRules::FPlayerCanRespawn( CBasePlayer *pPlayer )
{
return TRUE;
}
//=========================================================
//=========================================================
float CHalfLifeRules::FlPlayerSpawnTime( CBasePlayer *pPlayer )
{
return gpGlobals->time;//now!
}
//=========================================================
// IPointsForKill - how many points awarded to anyone
// that kills this player?
//=========================================================
int CHalfLifeRules::IPointsForKill( CBasePlayer *pAttacker, CBasePlayer *pKilled )
{
return 1;
}
//=========================================================
// PlayerKilled - someone/something killed this player
//=========================================================
void CHalfLifeRules::PlayerKilled( CBasePlayer *pVictim, entvars_t *pKiller, entvars_t *pInflictor )
{
}
//=========================================================
// Deathnotice
//=========================================================
void CHalfLifeRules::DeathNotice( CBasePlayer *pVictim, entvars_t *pKiller, entvars_t *pInflictor )
{
}
//=========================================================
// PlayerGotWeapon - player has grabbed a weapon that was
// sitting in the world
//=========================================================
void CHalfLifeRules::PlayerGotWeapon( CBasePlayer *pPlayer, CBasePlayerItem *pWeapon )
{
}
//=========================================================
// FlWeaponRespawnTime - what is the time in the future
// at which this weapon may spawn?
//=========================================================
float CHalfLifeRules::FlWeaponRespawnTime( CBasePlayerItem *pWeapon )
{
return -1;
}
//=========================================================
// FlWeaponRespawnTime - Returns 0 if the weapon can respawn
// now, otherwise it returns the time at which it can try
// to spawn again.
//=========================================================
float CHalfLifeRules::FlWeaponTryRespawn( CBasePlayerItem *pWeapon )
{
return 0;
}
//=========================================================
// VecWeaponRespawnSpot - where should this weapon spawn?
// Some game variations may choose to randomize spawn locations
//=========================================================
Vector CHalfLifeRules::VecWeaponRespawnSpot( CBasePlayerItem *pWeapon )
{
return pWeapon->pev->origin;
}
//=========================================================
// WeaponShouldRespawn - any conditions inhibiting the
// respawning of this weapon?
//=========================================================
int CHalfLifeRules::WeaponShouldRespawn( CBasePlayerItem *pWeapon )
{
return GR_WEAPON_RESPAWN_NO;
}
//=========================================================
//=========================================================
BOOL CHalfLifeRules::CanHaveItem( CBasePlayer *pPlayer, CItem *pItem )
{
return TRUE;
}
//=========================================================
//=========================================================
void CHalfLifeRules::PlayerGotItem( CBasePlayer *pPlayer, CItem *pItem )
{
}
//=========================================================
//=========================================================
int CHalfLifeRules::ItemShouldRespawn( CItem *pItem )
{
return GR_ITEM_RESPAWN_NO;
}
//=========================================================
// At what time in the future may this Item respawn?
//=========================================================
float CHalfLifeRules::FlItemRespawnTime( CItem *pItem )
{
return -1;
}
//=========================================================
// Where should this item respawn?
// Some game variations may choose to randomize spawn locations
//=========================================================
Vector CHalfLifeRules::VecItemRespawnSpot( CItem *pItem )
{
return pItem->pev->origin;
}
//=========================================================
//=========================================================
BOOL CHalfLifeRules::IsAllowedToSpawn( CBaseEntity *pEntity )
{
return TRUE;
}
//=========================================================
//=========================================================
void CHalfLifeRules::PlayerGotAmmo( CBasePlayer *pPlayer, char *szName, int iCount )
{
}
//=========================================================
//=========================================================
int CHalfLifeRules::AmmoShouldRespawn( CBasePlayerAmmo *pAmmo )
{
return GR_AMMO_RESPAWN_NO;
}
//=========================================================
//=========================================================
float CHalfLifeRules::FlAmmoRespawnTime( CBasePlayerAmmo *pAmmo )
{
return -1;
}
//=========================================================
//=========================================================
Vector CHalfLifeRules::VecAmmoRespawnSpot( CBasePlayerAmmo *pAmmo )
{
return pAmmo->pev->origin;
}
//=========================================================
//=========================================================
float CHalfLifeRules::FlHealthChargerRechargeTime( void )
{
return 0;// don't recharge
}
//=========================================================
//=========================================================
int CHalfLifeRules::DeadPlayerWeapons( CBasePlayer *pPlayer )
{
return GR_PLR_DROP_GUN_NO;
}
//=========================================================
//=========================================================
int CHalfLifeRules::DeadPlayerAmmo( CBasePlayer *pPlayer )
{
return GR_PLR_DROP_AMMO_NO;
}
//=========================================================
//=========================================================
int CHalfLifeRules::PlayerRelationship( CBaseEntity *pPlayer, CBaseEntity *pTarget )
{
// why would a single player in half life need this?
return GR_NOTTEAMMATE;
}
//=========================================================
//=========================================================
BOOL CHalfLifeRules::FAllowMonsters( void )
{
return TRUE;
}

View File

@ -1,585 +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.
*
****/
#if !defined( OEM_BUILD ) && !defined( HLDEMO_BUILD )
#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 w_squeak_e
{
WSQUEAK_IDLE1 = 0,
WSQUEAK_FIDGET,
WSQUEAK_JUMP,
WSQUEAK_RUN
};
enum squeak_e
{
SQUEAK_IDLE1 = 0,
SQUEAK_FIDGETFIT,
SQUEAK_FIDGETNIP,
SQUEAK_DOWN,
SQUEAK_UP,
SQUEAK_THROW
};
#ifndef CLIENT_DLL
class CSqueakGrenade : public CGrenade
{
void Spawn( void );
void Precache( void );
int Classify( void );
void EXPORT SuperBounceTouch( CBaseEntity *pOther );
void EXPORT HuntThink( void );
int BloodColor( void ) { return BLOOD_COLOR_YELLOW; }
void Killed( entvars_t *pevAttacker, int iGib );
void GibMonster( void );
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;
float m_flNextHit;
Vector m_posPrev;
EHANDLE m_hOwner;
int m_iMyClass;
};
float CSqueakGrenade::m_flNextBounceSoundTime = 0;
LINK_ENTITY_TO_CLASS( monster_snark, CSqueakGrenade )
TYPEDESCRIPTION CSqueakGrenade::m_SaveData[] =
{
DEFINE_FIELD( CSqueakGrenade, m_flDie, FIELD_TIME ),
DEFINE_FIELD( CSqueakGrenade, m_vecTarget, FIELD_VECTOR ),
DEFINE_FIELD( CSqueakGrenade, m_flNextHunt, FIELD_TIME ),
DEFINE_FIELD( CSqueakGrenade, m_flNextHit, FIELD_TIME ),
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_iMyClass != 0 )
return m_iMyClass; // protect against recursion
if( m_hEnemy != 0 )
{
m_iMyClass = CLASS_INSECT; // no one cares about it
switch( m_hEnemy->Classify() )
{
case CLASS_PLAYER:
case CLASS_HUMAN_PASSIVE:
case CLASS_HUMAN_MILITARY:
m_iMyClass = 0;
return CLASS_ALIEN_MILITARY; // barney's get mad, grunts get mad at it
}
m_iMyClass = 0;
}
return CLASS_ALIEN_BIOWEAPON;
}
void CSqueakGrenade::Spawn( void )
{
Precache();
// motor
pev->movetype = MOVETYPE_BOUNCE;
pev->solid = SOLID_BBOX;
SET_MODEL( ENT( pev ), "models/w_squeak.mdl" );
UTIL_SetSize( pev, Vector( -4, -4, 0 ), Vector( 4, 4, 8 ) );
UTIL_SetOrigin( pev, pev->origin );
SetTouch( &CSqueakGrenade::SuperBounceTouch );
SetThink( &CSqueakGrenade::HuntThink );
pev->nextthink = gpGlobals->time + 0.1;
m_flNextHunt = gpGlobals->time + 1E6;
pev->flags |= FL_MONSTER;
pev->takedamage = DAMAGE_AIM;
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 = gpGlobals->time;// reset each time a snark is spawned.
pev->sequence = WSQUEAK_RUN;
ResetSequenceInfo();
}
void CSqueakGrenade::Precache( void )
{
PRECACHE_MODEL( "models/w_squeak.mdl" );
PRECACHE_SOUND( "squeek/sqk_blast1.wav" );
PRECACHE_SOUND( "common/bodysplat.wav" );
PRECACHE_SOUND( "squeek/sqk_die1.wav" );
PRECACHE_SOUND( "squeek/sqk_hunt1.wav" );
PRECACHE_SOUND( "squeek/sqk_hunt2.wav" );
PRECACHE_SOUND( "squeek/sqk_hunt3.wav" );
PRECACHE_SOUND( "squeek/sqk_deploy1.wav" );
}
void CSqueakGrenade::Killed( entvars_t *pevAttacker, int iGib )
{
pev->model = iStringNull;// make invisible
SetThink( &CBaseEntity::SUB_Remove );
SetTouch( NULL );
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
// continue to call this function unless we acknowledge the Squeak's death now. (sjb)
pev->takedamage = DAMAGE_NO;
// 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 != 0 )
RadiusDamage( pev, m_hOwner->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 != 0 )
pev->owner = m_hOwner->edict();
CBaseMonster::Killed( pevAttacker, GIB_ALWAYS );
}
void CSqueakGrenade::GibMonster( void )
{
EMIT_SOUND_DYN( ENT( pev ), CHAN_VOICE, "common/bodysplat.wav", 0.75, ATTN_NORM, 0, 200 );
}
void CSqueakGrenade::HuntThink( void )
{
// ALERT( at_console, "think\n" );
if( !IsInWorld() )
{
SetTouch( NULL );
UTIL_Remove( this );
return;
}
StudioFrameAdvance();
pev->nextthink = gpGlobals->time + 0.1;
// explode when ready
if( gpGlobals->time >= m_flDie )
{
g_vecAttackDir = pev->velocity.Normalize();
pev->health = -1;
Killed( pev, 0 );
return;
}
// float
if( pev->waterlevel != 0 )
{
if( pev->movetype == MOVETYPE_BOUNCE )
{
pev->movetype = MOVETYPE_FLY;
}
pev->velocity = pev->velocity * 0.9;
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 )
return;
m_flNextHunt = gpGlobals->time + 2.0;
//CBaseEntity *pOther = NULL;
Vector vecDir;
TraceResult tr;
Vector vecFlat = pev->velocity;
vecFlat.z = 0;
vecFlat = vecFlat.Normalize();
UTIL_MakeVectors( pev->angles );
if( m_hEnemy == 0 || !m_hEnemy->IsAlive() )
{
// find target, bounce a bit towards it.
Look( 512 );
m_hEnemy = BestVisibleEnemy();
}
// squeek if it's about time blow up
if( ( m_flDie - gpGlobals->time <= 0.5 ) && ( m_flDie - gpGlobals->time >= 0.3 ) )
{
EMIT_SOUND_DYN( ENT( pev ), CHAN_VOICE, "squeek/sqk_die1.wav", 1, ATTN_NORM, 0, 100 + RANDOM_LONG( 0, 0x3F ) );
CSoundEnt::InsertSound( bits_SOUND_COMBAT, pev->origin, 256, 0.25 );
}
// 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( m_hEnemy != 0 )
{
if( FVisible( m_hEnemy ) )
{
vecDir = m_hEnemy->EyePosition() - pev->origin;
m_vecTarget = vecDir.Normalize();
}
float flVel = pev->velocity.Length();
float flAdj = 50.0 / ( flVel + 10.0 );
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 );
}
else
{
if( pev->avelocity == Vector( 0, 0, 0 ) )
{
pev->avelocity.x = RANDOM_FLOAT( -100, 100 );
pev->avelocity.z = RANDOM_FLOAT( -100, 100 );
}
}
if( ( pev->origin - m_posPrev ).Length() < 1.0 )
{
pev->velocity.x = RANDOM_FLOAT( -100, 100 );
pev->velocity.y = RANDOM_FLOAT( -100, 100 );
}
m_posPrev = pev->origin;
pev->angles = UTIL_VecToAngles( pev->velocity );
pev->angles.z = 0;
pev->angles.x = 0;
}
void CSqueakGrenade::SuperBounceTouch( CBaseEntity *pOther )
{
float flpitch;
TraceResult tr = UTIL_GetGlobalTrace();
// don't hit the guy that launched this grenade
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;
// 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 != 0 )
ApplyMultiDamage( pev, m_hOwner->pev );
else
ApplyMultiDamage( pev, pev );
pev->dmg += gSkillData.snarkDmgPop; // add more explosion damage
// m_flDie += 2.0; // add more life
// make bite sound
EMIT_SOUND_DYN( ENT( pev ), CHAN_WEAPON, "squeek/sqk_deploy1.wav", 1.0, ATTN_NORM, 0, (int)flpitch );
m_flNextAttack = gpGlobals->time + 0.5;
}
}
else
{
// ALERT( at_console, "been hit\n" );
}
}
m_flNextHit = gpGlobals->time + 0.1;
m_flNextHunt = gpGlobals->time;
if( g_pGameRules->IsMultiplayer() )
{
// in multiplayer, we limit how often snarks can make their bounce sounds to prevent overflows.
if( gpGlobals->time < m_flNextBounceSoundTime )
{
// too soon!
return;
}
}
if( !( pev->flags & FL_ONGROUND ) )
{
// play bounce sound
float flRndSound = RANDOM_FLOAT( 0, 1 );
if( flRndSound <= 0.33 )
EMIT_SOUND_DYN( ENT( pev ), CHAN_VOICE, "squeek/sqk_hunt1.wav", 1, ATTN_NORM, 0, (int)flpitch );
else if( flRndSound <= 0.66 )
EMIT_SOUND_DYN( ENT( pev ), CHAN_VOICE, "squeek/sqk_hunt2.wav", 1, ATTN_NORM, 0, (int)flpitch );
else
EMIT_SOUND_DYN( ENT( pev ), CHAN_VOICE, "squeek/sqk_hunt3.wav", 1, ATTN_NORM, 0, (int)flpitch );
CSoundEnt::InsertSound( bits_SOUND_COMBAT, pev->origin, 256, 0.25 );
}
else
{
// skittering sound
CSoundEnt::InsertSound( bits_SOUND_COMBAT, pev->origin, 100, 0.1 );
}
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;
pev->animtime = gpGlobals->time;
pev->framerate = 1.0;
}
void CSqueak::Precache( void )
{
PRECACHE_MODEL( "models/w_sqknest.mdl" );
PRECACHE_MODEL( "models/v_squeak.mdl" );
PRECACHE_MODEL( "models/p_squeak.mdl" );
PRECACHE_SOUND( "squeek/sqk_hunt2.wav" );
PRECACHE_SOUND( "squeek/sqk_hunt3.wav" );
UTIL_PrecacheOther( "monster_snark" );
m_usSnarkFire = PRECACHE_EVENT( 1, "events/snarkfire.sc" );
}
int CSqueak::GetItemInfo( ItemInfo *p )
{
p->pszName = STRING( pev->classname );
p->pszAmmo1 = "Snarks";
p->iMaxAmmo1 = SNARK_MAX_CARRY;
p->pszAmmo2 = NULL;
p->iMaxAmmo2 = -1;
p->iMaxClip = WEAPON_NOCLIP;
p->iSlot = 4;
p->iPosition = 3;
p->iId = m_iId = WEAPON_SNARK;
p->iWeight = SNARK_WEIGHT;
p->iFlags = ITEM_FLAG_LIMITINWORLD | ITEM_FLAG_EXHAUSTIBLE;
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 );
m_pPlayer->m_iWeaponVolume = QUIET_GUN_VOLUME;
return DefaultDeploy( "models/v_squeak.mdl", "models/p_squeak.mdl", SQUEAK_UP, "squeak" );
}
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 << WEAPON_SNARK );
DestroyItem();
return;
}
SendWeaponAnim( SQUEAK_DOWN );
EMIT_SOUND( ENT( m_pPlayer->pev ), CHAN_WEAPON, "common/null.wav", 1.0, ATTN_NORM );
}
void CSqueak::PrimaryAttack()
{
if( m_pPlayer->m_rgAmmo[m_iPrimaryAmmoType] )
{
UTIL_MakeVectors( m_pPlayer->pev->v_angle );
TraceResult tr;
Vector trace_origin;
// HACK HACK: Ugly hacks to handle change in origin based on new physics code for players
// Move origin up if crouched and start trace a bit outside of body ( 20 units instead of 16 )
trace_origin = m_pPlayer->pev->origin;
if( m_pPlayer->pev->flags & FL_DUCKING )
{
trace_origin = trace_origin - ( VEC_HULL_MIN - VEC_DUCK_HULL_MIN );
}
// find place to toss monster
UTIL_TraceLine( trace_origin + gpGlobals->v_forward * 20, trace_origin + gpGlobals->v_forward * 64, dont_ignore_monsters, NULL, &tr );
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 );
m_pPlayer->m_iWeaponVolume = QUIET_GUN_VOLUME;
m_pPlayer->m_rgAmmo[m_iPrimaryAmmoType]--;
m_fJustThrown = 1;
m_flNextPrimaryAttack = GetNextAttackDelay( 0.3 );
m_flTimeWeaponIdle = UTIL_WeaponTimeBase() + 1.0;
}
}
}
void CSqueak::SecondaryAttack( void )
{
}
void CSqueak::WeaponIdle( void )
{
if( m_flTimeWeaponIdle > UTIL_WeaponTimeBase() )
return;
if( m_fJustThrown )
{
m_fJustThrown = 0;
if( !m_pPlayer->m_rgAmmo[PrimaryAmmoIndex()] )
{
RetireWeapon();
return;
}
SendWeaponAnim( SQUEAK_UP );
m_flTimeWeaponIdle = UTIL_WeaponTimeBase() + UTIL_SharedRandomFloat( m_pPlayer->random_seed, 10, 15 );
return;
}
int iAnim;
float flRand = UTIL_SharedRandomFloat( m_pPlayer->random_seed, 0, 1 );
if( flRand <= 0.75 )
{
iAnim = SQUEAK_IDLE1;
m_flTimeWeaponIdle = UTIL_WeaponTimeBase() + 30.0 / 16 * (2);
}
else if( flRand <= 0.875 )
{
iAnim = SQUEAK_FIDGETFIT;
m_flTimeWeaponIdle = UTIL_WeaponTimeBase() + 70.0 / 16.0;
}
else
{
iAnim = SQUEAK_FIDGETNIP;
m_flTimeWeaponIdle = UTIL_WeaponTimeBase() + 80.0 / 16.0;
}
SendWeaponAnim( iAnim );
}
#endif

View File

@ -1,151 +0,0 @@
//========= Copyright (c) 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, const 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 = {0};
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, 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
}

File diff suppressed because it is too large Load Diff

View File

@ -1,518 +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 "effects.h"
#include "gamerules.h"
#define TRIPMINE_PRIMARY_VOLUME 450
enum tripmine_e
{
TRIPMINE_IDLE1 = 0,
TRIPMINE_IDLE2,
TRIPMINE_ARM1,
TRIPMINE_ARM2,
TRIPMINE_FIDGET,
TRIPMINE_HOLSTER,
TRIPMINE_DRAW,
TRIPMINE_WORLD,
TRIPMINE_GROUND
};
#ifndef CLIENT_DLL
class CTripmineGrenade : public CGrenade
{
void Spawn( void );
void Precache( void );
void UpdateOnRemove();
virtual int Save( CSave &save );
virtual int Restore( CRestore &restore );
static TYPEDESCRIPTION m_SaveData[];
int TakeDamage( entvars_t *pevInflictor, entvars_t *pevAttacker, float flDamage, int bitsDamageType );
void EXPORT WarningThink( void );
void EXPORT PowerupThink( void );
void EXPORT BeamBreakThink( void );
void EXPORT DelayDeathThink( void );
void Killed( entvars_t *pevAttacker, int iGib );
void MakeBeam( void );
void KillBeam( void );
float m_flPowerUp;
Vector m_vecDir;
Vector m_vecEnd;
float m_flBeamLength;
EHANDLE m_hOwner;
CBeam *m_pBeam;
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.
};
LINK_ENTITY_TO_CLASS( monster_tripmine, CTripmineGrenade )
TYPEDESCRIPTION CTripmineGrenade::m_SaveData[] =
{
DEFINE_FIELD( CTripmineGrenade, m_flPowerUp, FIELD_TIME ),
DEFINE_FIELD( CTripmineGrenade, m_vecDir, FIELD_VECTOR ),
DEFINE_FIELD( CTripmineGrenade, m_vecEnd, FIELD_POSITION_VECTOR ),
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_posOwner, FIELD_POSITION_VECTOR ),
DEFINE_FIELD( CTripmineGrenade, m_angleOwner, FIELD_VECTOR ),
DEFINE_FIELD( CTripmineGrenade, m_pRealOwner, FIELD_EDICT ),
};
IMPLEMENT_SAVERESTORE( CTripmineGrenade, CGrenade )
void CTripmineGrenade::Spawn( void )
{
Precache();
// motor
pev->movetype = MOVETYPE_FLY;
pev->solid = SOLID_NOT;
SET_MODEL( ENT( pev ), "models/v_tripmine.mdl" );
pev->frame = 0;
pev->body = 3;
pev->sequence = TRIPMINE_WORLD;
ResetSequenceInfo();
pev->framerate = 0;
UTIL_SetSize( pev, Vector( -8, -8, -8 ), Vector( 8, 8, 8 ) );
UTIL_SetOrigin( pev, pev->origin );
if( pev->spawnflags & 1 )
{
// power up quickly
m_flPowerUp = gpGlobals->time + 1.0;
}
else
{
// power up in 2.5 seconds
m_flPowerUp = gpGlobals->time + 2.5;
}
SetThink( &CTripmineGrenade::PowerupThink );
pev->nextthink = gpGlobals->time + 0.2;
pev->takedamage = DAMAGE_YES;
pev->dmg = gSkillData.plrDmgTripmine;
pev->health = 1; // don't let die normally
if( pev->owner != NULL )
{
// play deploy sound
EMIT_SOUND( ENT( pev ), CHAN_VOICE, "weapons/mine_deploy.wav", 1.0, ATTN_NORM );
EMIT_SOUND( ENT( pev ), CHAN_BODY, "weapons/mine_charge.wav", 0.2, ATTN_NORM ); // chargeup
m_pRealOwner = pev->owner;// see CTripmineGrenade for why.
}
UTIL_MakeAimVectors( pev->angles );
m_vecDir = gpGlobals->v_forward;
m_vecEnd = pev->origin + m_vecDir * 2048;
}
void CTripmineGrenade::Precache( void )
{
PRECACHE_MODEL( "models/v_tripmine.mdl" );
PRECACHE_SOUND( "weapons/mine_deploy.wav" );
PRECACHE_SOUND( "weapons/mine_activate.wav" );
PRECACHE_SOUND( "weapons/mine_charge.wav" );
}
void CTripmineGrenade::UpdateOnRemove()
{
CBaseEntity::UpdateOnRemove();
KillBeam();
}
void CTripmineGrenade::WarningThink( void )
{
// play warning sound
// EMIT_SOUND( ENT( pev ), CHAN_VOICE, "buttons/Blip2.wav", 1.0, ATTN_NORM );
// set to power up
SetThink( &CTripmineGrenade::PowerupThink );
pev->nextthink = gpGlobals->time + 1.0;
}
void CTripmineGrenade::PowerupThink( void )
{
TraceResult tr;
if( m_hOwner == 0 )
{
// find an owner
edict_t *oldowner = pev->owner;
pev->owner = NULL;
UTIL_TraceLine( pev->origin + m_vecDir * 8, pev->origin - m_vecDir * 32, dont_ignore_monsters, ENT( pev ), &tr );
if( tr.fStartSolid || ( oldowner && tr.pHit == oldowner ) )
{
pev->owner = oldowner;
m_flPowerUp += 0.1;
pev->nextthink = gpGlobals->time + 0.1;
return;
}
if( tr.flFraction < 1.0 )
{
pev->owner = tr.pHit;
m_hOwner = CBaseEntity::Instance( pev->owner );
m_posOwner = m_hOwner->pev->origin;
m_angleOwner = m_hOwner->pev->angles;
}
else
{
STOP_SOUND( ENT( pev ), CHAN_VOICE, "weapons/mine_deploy.wav" );
STOP_SOUND( ENT( pev ), CHAN_BODY, "weapons/mine_charge.wav" );
SetThink( &CBaseEntity::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;
}
}
else if( m_posOwner != m_hOwner->pev->origin || m_angleOwner != m_hOwner->pev->angles )
{
// disable
STOP_SOUND( ENT( pev ), CHAN_VOICE, "weapons/mine_deploy.wav" );
STOP_SOUND( ENT( pev ), CHAN_BODY, "weapons/mine_charge.wav" );
CBaseEntity *pMine = Create( "weapon_tripmine", pev->origin + m_vecDir * 24, pev->angles );
pMine->pev->spawnflags |= SF_NORESPAWN;
SetThink( &CBaseEntity::SUB_Remove );
KillBeam();
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( pev, pev->origin );
MakeBeam();
// play enabled sound
EMIT_SOUND_DYN( ENT( pev ), CHAN_VOICE, "weapons/mine_activate.wav", 0.5, ATTN_NORM, 1, 75 );
}
pev->nextthink = gpGlobals->time + 0.1;
}
void CTripmineGrenade::KillBeam( void )
{
if( m_pBeam )
{
UTIL_Remove( m_pBeam );
m_pBeam = NULL;
}
}
void CTripmineGrenade::MakeBeam( void )
{
TraceResult tr;
// 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 );
pev->nextthink = gpGlobals->time + 0.1;
Vector vecTmpEnd = pev->origin + m_vecDir * 2048 * m_flBeamLength;
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( 64 );
}
void CTripmineGrenade::BeamBreakThink( void )
{
BOOL bBlowup = 0;
TraceResult tr;
// HACKHACK Set simple box using this really nice global!
gpGlobals->trace_flags = FTRACE_SIMPLEBOX;
UTIL_TraceLine( pev->origin, m_vecEnd, dont_ignore_monsters, ENT( pev ), &tr );
// ALERT( at_console, "%f : %f\n", tr.flFraction, m_flBeamLength );
// respawn detect.
if( !m_pBeam )
{
MakeBeam();
if( tr.pHit )
m_hOwner = CBaseEntity::Instance( tr.pHit ); // reset owner too
}
if( fabs( m_flBeamLength - tr.flFraction ) > 0.001 )
{
bBlowup = 1;
}
else
{
if( m_hOwner == 0 )
bBlowup = 1;
else if( m_posOwner != m_hOwner->pev->origin )
bBlowup = 1;
else if( m_angleOwner != m_hOwner->pev->angles )
bBlowup = 1;
}
if( bBlowup )
{
// a bit of a hack, but all CGrenade code passes pev->owner along to make sure the proper player gets credit for the kill
// so we have to restore pev->owner from pRealOwner, because an entity's tracelines don't strike it's pev->owner which meant
// that a player couldn't trigger his own tripmine. Now that the mine is exploding, it's safe the restore the owner so the
// CGrenade code knows who the explosive really belongs to.
pev->owner = m_pRealOwner;
pev->health = 0;
Killed( VARS( pev->owner ), GIB_NORMAL );
return;
}
pev->nextthink = gpGlobals->time + 0.1;
}
int CTripmineGrenade::TakeDamage( entvars_t *pevInflictor, entvars_t *pevAttacker, float flDamage, int bitsDamageType )
{
if( gpGlobals->time < m_flPowerUp && flDamage < pev->health )
{
// disable
// Create( "weapon_tripmine", pev->origin + m_vecDir * 24, pev->angles );
SetThink( &CBaseEntity::SUB_Remove );
pev->nextthink = gpGlobals->time + 0.1;
KillBeam();
return FALSE;
}
return CGrenade::TakeDamage( pevInflictor, pevAttacker, flDamage, bitsDamageType );
}
void CTripmineGrenade::Killed( entvars_t *pevAttacker, int iGib )
{
pev->takedamage = DAMAGE_NO;
if( pevAttacker && ( pevAttacker->flags & FL_CLIENT ) )
{
// some client has destroyed this mine, he'll get credit for any kills
pev->owner = ENT( pevAttacker );
}
SetThink( &CTripmineGrenade::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
}
void CTripmineGrenade::DelayDeathThink( void )
{
KillBeam();
TraceResult tr;
UTIL_TraceLine( pev->origin + m_vecDir * 8, pev->origin - m_vecDir * 64, dont_ignore_monsters, ENT( pev ), &tr );
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/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 )
{
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 )
{
p->pszName = STRING( pev->classname );
p->pszAmmo1 = "Trip Mine";
p->iMaxAmmo1 = TRIPMINE_MAX_CARRY;
p->pszAmmo2 = NULL;
p->iMaxAmmo2 = -1;
p->iMaxClip = WEAPON_NOCLIP;
p->iSlot = 4;
p->iPosition = 2;
p->iId = m_iId = WEAPON_TRIPMINE;
p->iWeight = TRIPMINE_WEIGHT;
p->iFlags = ITEM_FLAG_LIMITINWORLD | ITEM_FLAG_EXHAUSTIBLE;
return 1;
}
BOOL CTripmine::Deploy()
{
pev->body = 0;
return DefaultDeploy( "models/v_tripmine.mdl", "models/p_tripmine.mdl", TRIPMINE_DRAW, "trip" );
}
void CTripmine::Holster( int skiplocal /* = 0 */ )
{
m_pPlayer->m_flNextAttack = UTIL_WeaponTimeBase() + 0.5;
if( !m_pPlayer->m_rgAmmo[m_iPrimaryAmmoType] )
{
// out of mines
m_pPlayer->pev->weapons &= ~( 1 << WEAPON_TRIPMINE );
DestroyItem();
}
SendWeaponAnim( TRIPMINE_HOLSTER );
EMIT_SOUND( ENT( m_pPlayer->pev ), CHAN_WEAPON, "common/null.wav", 1.0, ATTN_NORM );
}
void CTripmine::PrimaryAttack( void )
{
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();
Vector vecAiming = gpGlobals->v_forward;
TraceResult 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 ) )
{
Vector angles = UTIL_VecToAngles( tr.vecPlaneNormal );
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 );
if( m_pPlayer->m_rgAmmo[m_iPrimaryAmmoType] <= 0 )
{
// no more mines!
RetireWeapon();
return;
}
}
/*else
{
// ALERT( at_console, "no deploy\n" );
}*/
}
/*else
{
}*/
m_flNextPrimaryAttack = GetNextAttackDelay( 0.3 );
m_flTimeWeaponIdle = UTIL_WeaponTimeBase() + UTIL_SharedRandomFloat( m_pPlayer->random_seed, 10, 15 );
}
void CTripmine::WeaponIdle( void )
{
if( m_flTimeWeaponIdle > UTIL_WeaponTimeBase() )
return;
if( m_pPlayer->m_rgAmmo[m_iPrimaryAmmoType] > 0 )
{
SendWeaponAnim( TRIPMINE_DRAW );
}
else
{
RetireWeapon();
return;
}
int iAnim;
float flRand = UTIL_SharedRandomFloat( m_pPlayer->random_seed, 0, 1 );
if( flRand <= 0.25 )
{
iAnim = TRIPMINE_IDLE1;
m_flTimeWeaponIdle = UTIL_WeaponTimeBase() + 90.0 / 30.0;
}
else if( flRand <= 0.75 )
{
iAnim = TRIPMINE_IDLE2;
m_flTimeWeaponIdle = UTIL_WeaponTimeBase() + 60.0 / 30.0;
}
else
{
iAnim = TRIPMINE_FIDGET;
m_flTimeWeaponIdle = UTIL_WeaponTimeBase() + 100.0 / 30.0;
}
SendWeaponAnim( iAnim );
}

View File

@ -1,137 +0,0 @@
#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__

View File

@ -1,338 +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.
*
****/
//=========================================================
// 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, pitch );
}
void CZombie::AttackSound( void )
{
int pitch = 95 + RANDOM_LONG( 0, 9 );
// Play a random attack sound
EMIT_SOUND_DYN( ENT( pev ), CHAN_VOICE, pAttackSounds[RANDOM_LONG( 0, ARRAYSIZE( pAttackSounds ) - 1 )], 1.0, ATTN_NORM, 0, pitch );
}
//=========================================================
// 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()
{
size_t i;
PRECACHE_MODEL( "models/zombie.mdl" );
for( i = 0; i < ARRAYSIZE( pAttackHitSounds ); i++ )
PRECACHE_SOUND( pAttackHitSounds[i] );
for( i = 0; i < ARRAYSIZE( pAttackMissSounds ); i++ )
PRECACHE_SOUND( pAttackMissSounds[i] );
for( i = 0; i < ARRAYSIZE( pAttackSounds ); i++ )
PRECACHE_SOUND( pAttackSounds[i] );
for( i = 0; i < ARRAYSIZE( pIdleSounds ); i++ )
PRECACHE_SOUND( pIdleSounds[i] );
for( i = 0; i < ARRAYSIZE( pAlertSounds ); i++ )
PRECACHE_SOUND( pAlertSounds[i] );
for( i = 0; i < ARRAYSIZE( pPainSounds ); i++ )
PRECACHE_SOUND( 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;
}