mirror of https://github.com/FWGS/hlsdk-xash3d
Remove useless files.
This commit is contained in:
parent
cde2afff7c
commit
d20abd0465
File diff suppressed because it is too large
Load Diff
395
dlls/Wxdebug.cpp
395
dlls/Wxdebug.cpp
|
@ -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
|
||||
|
||||
|
918
dlls/aflock.cpp
918
dlls/aflock.cpp
|
@ -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;
|
||||
}
|
||||
}
|
1178
dlls/agrunt.cpp
1178
dlls/agrunt.cpp
File diff suppressed because it is too large
Load Diff
1027
dlls/apache.cpp
1027
dlls/apache.cpp
File diff suppressed because it is too large
Load Diff
|
@ -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;
|
||||
}
|
826
dlls/barney.cpp
826
dlls/barney.cpp
|
@ -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();
|
||||
}
|
1186
dlls/bigmomma.cpp
1186
dlls/bigmomma.cpp
File diff suppressed because it is too large
Load Diff
212
dlls/bloater.cpp
212
dlls/bloater.cpp
|
@ -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
|
||||
//=========================================================
|
1265
dlls/bullsquid.cpp
1265
dlls/bullsquid.cpp
File diff suppressed because it is too large
Load Diff
1378
dlls/controller.cpp
1378
dlls/controller.cpp
File diff suppressed because it is too large
Load Diff
|
@ -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
|
544
dlls/egon.cpp
544
dlls/egon.cpp
|
@ -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
|
|
@ -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;
|
||||
}
|
|
@ -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
|
1332
dlls/gargantua.cpp
1332
dlls/gargantua.cpp
File diff suppressed because it is too large
Load Diff
633
dlls/gauss.cpp
633
dlls/gauss.cpp
|
@ -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
|
|
@ -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
|
||||
//=========================================================
|
261
dlls/glock.cpp
261
dlls/glock.cpp
|
@ -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 )
|
236
dlls/gman.cpp
236
dlls/gman.cpp
|
@ -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;
|
||||
}
|
|
@ -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
|
|
@ -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 );
|
||||
}
|
2487
dlls/hgrunt.cpp
2487
dlls/hgrunt.cpp
File diff suppressed because it is too large
Load Diff
435
dlls/hornet.cpp
435
dlls/hornet.cpp
|
@ -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!
|
||||
}
|
|
@ -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
|
1295
dlls/houndeye.cpp
1295
dlls/houndeye.cpp
File diff suppressed because it is too large
Load Diff
1093
dlls/ichthyosaur.cpp
1093
dlls/ichthyosaur.cpp
File diff suppressed because it is too large
Load Diff
842
dlls/islave.cpp
842
dlls/islave.cpp
|
@ -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" );
|
||||
}
|
685
dlls/leech.cpp
685
dlls/leech.cpp
|
@ -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 );
|
||||
}
|
|
@ -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 );
|
||||
}
|
||||
}
|
|
@ -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
|
||||
|
|
@ -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;
|
||||
}
|
||||
}
|
358
dlls/mp5.cpp
358
dlls/mp5.cpp
|
@ -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 )
|
1818
dlls/nihilanth.cpp
1818
dlls/nihilanth.cpp
File diff suppressed because it is too large
Load Diff
776
dlls/osprey.cpp
776
dlls/osprey.cpp
|
@ -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 );
|
||||
}
|
||||
}
|
309
dlls/python.cpp
309
dlls/python.cpp
|
@ -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
|
99
dlls/rat.cpp
99
dlls/rat.cpp
|
@ -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
|
||||
//=========================================================
|
459
dlls/roach.cpp
459
dlls/roach.cpp
|
@ -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
|
||||
//=========================================================
|
480
dlls/satchel.cpp
480
dlls/satchel.cpp
|
@ -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
|
1431
dlls/scientist.cpp
1431
dlls/scientist.cpp
File diff suppressed because it is too large
Load Diff
392
dlls/shotgun.cpp
392
dlls/shotgun.cpp
|
@ -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 )
|
|
@ -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;
|
||||
}
|
|
@ -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
|
151
dlls/stats.cpp
151
dlls/stats.cpp
|
@ -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
|
||||
}
|
1023
dlls/tentacle.cpp
1023
dlls/tentacle.cpp
File diff suppressed because it is too large
Load Diff
|
@ -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 );
|
||||
}
|
137
dlls/wxdebug.h
137
dlls/wxdebug.h
|
@ -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__
|
||||
|
||||
|
338
dlls/zombie.cpp
338
dlls/zombie.cpp
|
@ -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;
|
||||
}
|
Loading…
Reference in New Issue