mirror of https://github.com/FWGS/hlsdk-xash3d
Merge original "Cthulhu" source code.
This commit is contained in:
parent
2905950cb0
commit
8720c4518c
|
@ -0,0 +1,32 @@
|
|||
|
||||
#ifndef IMAGELABEL_H
|
||||
#define IMAGELABEL_H
|
||||
|
||||
#include<VGUI_Panel.h>
|
||||
#include<VGUI_Frame.h>
|
||||
#include<VGUI_Label.h>
|
||||
|
||||
using namespace vgui;
|
||||
|
||||
// Wrapper for an Image Label without a background
|
||||
class CImageLabel : public Label
|
||||
{
|
||||
public:
|
||||
BitmapTGA *m_pTGA;
|
||||
|
||||
public:
|
||||
CImageLabel( const char* pImageName,int x,int y );
|
||||
CImageLabel( const char* pImageName,int x,int y,int wide,int tall );
|
||||
~CImageLabel();
|
||||
|
||||
virtual int getImageTall();
|
||||
virtual int getImageWide();
|
||||
|
||||
virtual void paintBackground()
|
||||
{
|
||||
// Do nothing, so the background's left transparent.
|
||||
}
|
||||
};
|
||||
|
||||
#endif
|
||||
|
|
@ -0,0 +1,90 @@
|
|||
/***
|
||||
*
|
||||
* Copyright (c) 1999, 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.
|
||||
*
|
||||
****/
|
||||
//
|
||||
// battery.cpp
|
||||
//
|
||||
// implementation of CHudReadBook class
|
||||
//
|
||||
|
||||
#include "hud.h"
|
||||
#include "cl_util.h"
|
||||
#include "parsemsg.h"
|
||||
#include "vgui_TeamFortressViewport.h"
|
||||
|
||||
#include <string.h>
|
||||
#include <stdio.h>
|
||||
|
||||
DECLARE_MESSAGE(m_ReadBook, ReadBook)
|
||||
|
||||
int CHudReadBook::Init(void)
|
||||
{
|
||||
ConsolePrint ("Initialising ReadBook\n");
|
||||
|
||||
pImage = NULL;
|
||||
|
||||
HOOK_MESSAGE(ReadBook);
|
||||
|
||||
gHUD.AddHudElem(this);
|
||||
|
||||
InitHUDData();
|
||||
|
||||
return 1;
|
||||
};
|
||||
|
||||
|
||||
int CHudReadBook::VidInit(void)
|
||||
{
|
||||
return 1;
|
||||
};
|
||||
|
||||
int CHudReadBook:: MsgFunc_ReadBook(const char *pszName, int iSize, void *pbuf )
|
||||
{
|
||||
ConsolePrint ("Beginning ReadBook\n");
|
||||
m_iFlags |= HUD_ACTIVE;
|
||||
|
||||
BEGIN_READ( pbuf, iSize );
|
||||
char* szName = READ_STRING();
|
||||
|
||||
if (strcmp(szName,"") == 0)
|
||||
{
|
||||
ConsolePrint ("Beginning remove ReadBook Image\n");
|
||||
if (pImage)
|
||||
{
|
||||
ConsolePrint ("Removing ReadBook Image\n");
|
||||
gViewPort->removeChild(pImage);
|
||||
// I think the deletion is managed by the framework...anyway, I do not get a memory leak
|
||||
// but if I keep the delete, then I get an exception ONLY in 640x480. Go figure...
|
||||
//pImage->setParent(NULL);
|
||||
//delete pImage;
|
||||
pImage = NULL;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
ConsolePrint ("Beginning create ReadBook Image\n");
|
||||
if (pImage == NULL)
|
||||
{
|
||||
ConsolePrint ("Creating ReadBook Image\n");
|
||||
ConsolePrint ("Image name : ");
|
||||
ConsolePrint (szName);
|
||||
ConsolePrint ("\n");
|
||||
pImage = new CImageLabel(szName,0,YRES(30));
|
||||
pImage->setParent(gViewPort);
|
||||
}
|
||||
}
|
||||
|
||||
return 1;
|
||||
}
|
||||
|
|
@ -0,0 +1,146 @@
|
|||
/***
|
||||
*
|
||||
* Copyright (c) 1999, 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.
|
||||
*
|
||||
****/
|
||||
//
|
||||
// battery.cpp
|
||||
//
|
||||
// implementation of CHudSanity class
|
||||
//
|
||||
|
||||
#include "hud.h"
|
||||
#include "cl_util.h"
|
||||
#include "parsemsg.h"
|
||||
|
||||
#include <string.h>
|
||||
#include <stdio.h>
|
||||
|
||||
DECLARE_MESSAGE(m_Sanity, Sanity)
|
||||
|
||||
int CHudSanity::Init(void)
|
||||
{
|
||||
m_iSan = 100;
|
||||
m_fFade = 0;
|
||||
m_iFlags = 0;
|
||||
|
||||
HOOK_MESSAGE(Sanity);
|
||||
|
||||
gHUD.AddHudElem(this);
|
||||
|
||||
return 1;
|
||||
};
|
||||
|
||||
|
||||
int CHudSanity::VidInit(void)
|
||||
{
|
||||
int HUD_san_zero = gHUD.GetSpriteIndex( "san_zero" );
|
||||
int HUD_san_full = gHUD.GetSpriteIndex( "san_full" );
|
||||
|
||||
m_hSprite1 = m_hSprite2 = 0; // delaying get sprite handles until we know the sprites are loaded
|
||||
m_prc1 = &gHUD.GetSpriteRect( HUD_san_zero );
|
||||
m_prc2 = &gHUD.GetSpriteRect( HUD_san_full );
|
||||
m_iHeight = m_prc2->bottom - m_prc1->top;
|
||||
m_fFade = 0;
|
||||
return 1;
|
||||
};
|
||||
|
||||
int CHudSanity:: MsgFunc_Sanity(const char *pszName, int iSize, void *pbuf )
|
||||
{
|
||||
m_iFlags |= HUD_ACTIVE;
|
||||
|
||||
BEGIN_READ( pbuf, iSize );
|
||||
int x = READ_SHORT();
|
||||
|
||||
if (x != m_iSan)
|
||||
{
|
||||
m_fFade = FADE_TIME;
|
||||
m_iSan = x;
|
||||
}
|
||||
|
||||
return 1;
|
||||
}
|
||||
|
||||
|
||||
int CHudSanity::Draw(float flTime)
|
||||
{
|
||||
if ( gHUD.m_iHideHUDDisplay & HIDEHUD_HEALTH )
|
||||
return 1;
|
||||
|
||||
int r, g, b, x, y, a;
|
||||
wrect_t rc;
|
||||
// wrect_t rc_zero;
|
||||
|
||||
rc = *m_prc2;
|
||||
rc.top += m_iHeight * ((float)(100-(min(100,m_iSan))) * 0.01); // sanity can go from 0 to 100 so * 0.01 goes from 0 to 1
|
||||
|
||||
// rc_zero = *m_prc1;
|
||||
// rc_zero.top += m_iHeight * ((float)(min(100,m_iSan)) * 0.01); // sanity can go from 0 to 100 so * 0.01 goes from 0 to 1
|
||||
|
||||
UnpackRGB(r,g,b, RGB_YELLOWISH);
|
||||
|
||||
if (!(gHUD.m_iWeaponBits & (1<<(WEAPON_SUIT)) ))
|
||||
return 1;
|
||||
|
||||
// Has health changed? Flash the health #
|
||||
if (m_fFade)
|
||||
{
|
||||
if (m_fFade > FADE_TIME)
|
||||
m_fFade = FADE_TIME;
|
||||
|
||||
m_fFade -= (gHUD.m_flTimeDelta * 20);
|
||||
if (m_fFade <= 0)
|
||||
{
|
||||
a = 128;
|
||||
m_fFade = 0;
|
||||
}
|
||||
|
||||
// Fade the health number back to dim
|
||||
|
||||
a = MIN_ALPHA + (m_fFade/FADE_TIME) * 128;
|
||||
|
||||
}
|
||||
else
|
||||
a = MIN_ALPHA;
|
||||
|
||||
ScaleColors(r, g, b, a );
|
||||
|
||||
int iOffset = (m_prc1->bottom - m_prc1->top)/6;
|
||||
|
||||
y = ScreenHeight - gHUD.m_iFontHeight - gHUD.m_iFontHeight / 2;
|
||||
x = ScreenWidth/5;
|
||||
|
||||
// make sure we have the right sprite handles
|
||||
if ( !m_hSprite1 )
|
||||
m_hSprite1 = gHUD.GetSprite( gHUD.GetSpriteIndex( "san_zero" ) );
|
||||
if ( !m_hSprite2 )
|
||||
m_hSprite2 = gHUD.GetSprite( gHUD.GetSpriteIndex( "san_full" ) );
|
||||
|
||||
SPR_Set(m_hSprite1, r, g, b );
|
||||
SPR_DrawAdditive( 0, x, y - iOffset, m_prc1);
|
||||
// if (rc_zero.bottom > rc_zero.top)
|
||||
// {
|
||||
// SPR_Set(m_hSprite1, r, g, b );
|
||||
// SPR_DrawAdditive( 0, x, y - iOffset + (rc_zero.top - m_prc1->top), &rc_zero);
|
||||
// }
|
||||
|
||||
if (rc.bottom > rc.top)
|
||||
{
|
||||
SPR_Set(m_hSprite2, r, g, b );
|
||||
SPR_DrawAdditive( 0, x, y - iOffset + (rc.top - m_prc2->top), &rc);
|
||||
}
|
||||
|
||||
x += (m_prc1->right - m_prc1->left);
|
||||
x = gHUD.DrawHudNumber(x, y, DHN_3DIGITS | DHN_DRAWZERO, m_iSan, r, g, b);
|
||||
|
||||
return 1;
|
||||
}
|
1114
cl_dll/ev_hldm.cpp
1114
cl_dll/ev_hldm.cpp
File diff suppressed because it is too large
Load Diff
|
@ -16,14 +16,21 @@ typedef enum
|
|||
BULLET_PLAYER_9MM, // glock
|
||||
BULLET_PLAYER_MP5, // mp5
|
||||
BULLET_PLAYER_357, // python
|
||||
BULLET_PLAYER_BUCKSHOT, // shotgun
|
||||
BULLET_PLAYER_SHOTGUN, // shotgun
|
||||
BULLET_PLAYER_CROWBAR, // crowbar swipe
|
||||
|
||||
BULLET_PLAYER_KNIFE, // knife swipe
|
||||
BULLET_PLAYER_SWORDCANE, // swordcane swipe
|
||||
BULLET_PLAYER_REVOLVER, // revolver shot
|
||||
BULLET_PLAYER_TOMMYGUN, // tommy gun burst
|
||||
BULLET_PLAYER_RIFLE, // rifle shot
|
||||
|
||||
BULLET_MONSTER_9MM,
|
||||
BULLET_MONSTER_MP5,
|
||||
BULLET_MONSTER_12MM
|
||||
}Bullet;
|
||||
|
||||
/*
|
||||
enum glock_e
|
||||
{
|
||||
GLOCK_IDLE1 = 0,
|
||||
|
@ -37,6 +44,22 @@ enum glock_e
|
|||
GLOCK_HOLSTER,
|
||||
GLOCK_ADD_SILENCER
|
||||
};
|
||||
*/
|
||||
|
||||
enum revolver_e
|
||||
{
|
||||
REVOLVER_IDLE1 = 0,
|
||||
REVOLVER_FIDGET1,
|
||||
REVOLVER_FIRE,
|
||||
REVOLVER_RELOAD,
|
||||
REVOLVER_HOLSTER,
|
||||
REVOLVER_DRAW,
|
||||
REVOLVER_IDLE2,
|
||||
REVOLVER_IDLE3,
|
||||
REVOLVER_QUICKFIRE_READY,
|
||||
REVOLVER_QUICKFIRE_SHOOT,
|
||||
REVOLVER_QUICKFIRE_RELAX
|
||||
};
|
||||
|
||||
enum shotgun_e
|
||||
{
|
||||
|
@ -52,6 +75,39 @@ enum shotgun_e
|
|||
SHOTGUN_IDLE_DEEP
|
||||
};
|
||||
|
||||
enum rifle_e
|
||||
{
|
||||
RIFLE_IDLE1 = 0,
|
||||
RIFLE_FIRE1,
|
||||
RIFLE_RELOAD,
|
||||
RIFLE_CLOSEBREAK,
|
||||
RIFLE_BREAK,
|
||||
RIFLE_DRAW,
|
||||
RIFLE_HOLSTER
|
||||
};
|
||||
|
||||
enum tommygun_e
|
||||
{
|
||||
TOMMYGUN_IDLE = 0,
|
||||
TOMMYGUN_RELOAD,
|
||||
TOMMYGUN_DRAW,
|
||||
TOMMYGUN_FIRE1,
|
||||
TOMMYGUN_FIRE2,
|
||||
TOMMYGUN_EMPTY_IDLE
|
||||
};
|
||||
|
||||
enum LightningGun_e
|
||||
{
|
||||
LIGHTNING_GUN_IDLE = 0,
|
||||
LIGHTNING_GUN_DRAW,
|
||||
LIGHTNING_GUN_HOLSTER,
|
||||
LIGHTNING_GUN_ZAP
|
||||
};
|
||||
|
||||
#define LIGHTNING_PRIMARY_CHARGE_VOLUME 256// how loud lightning is while charging
|
||||
#define LIGHTNING_PRIMARY_FIRE_VOLUME 450// how loud lightning is when discharged
|
||||
|
||||
/*
|
||||
enum mp5_e
|
||||
{
|
||||
MP5_LONGIDLE = 0,
|
||||
|
@ -91,6 +147,7 @@ enum gauss_e
|
|||
GAUSS_HOLSTER,
|
||||
GAUSS_DRAW
|
||||
};
|
||||
*/
|
||||
|
||||
void EV_HLDM_GunshotDecalTrace( pmtrace_t *pTrace, char *decalName );
|
||||
void EV_HLDM_DecalGunshot( pmtrace_t *pTrace, int iBulletType );
|
||||
|
|
|
@ -260,6 +260,7 @@ void CBasePlayer::StartObserver( Vector vecPosition, Vector vecViewAngle ) { }
|
|||
void CBasePlayer::PlayerUse( void ) { }
|
||||
void CBasePlayer::Jump() { }
|
||||
void CBasePlayer::Duck() { }
|
||||
int CBasePlayer::LoseSanity( float flSanLoss ) { return 0; }
|
||||
int CBasePlayer::Classify( void ) { return 0; }
|
||||
void CBasePlayer::PreThink(void) { }
|
||||
void CBasePlayer::CheckTimeBasedDamage() { }
|
||||
|
|
|
@ -20,24 +20,12 @@
|
|||
extern "C"
|
||||
{
|
||||
// HLDM
|
||||
void EV_FireGlock1( struct event_args_s *args );
|
||||
void EV_FireGlock2( struct event_args_s *args );
|
||||
void EV_FireRevolver1( struct event_args_s *args );
|
||||
void EV_FireRevolver2( struct event_args_s *args );
|
||||
void EV_FireShotGunSingle( struct event_args_s *args );
|
||||
void EV_FireShotGunDouble( struct event_args_s *args );
|
||||
void EV_FireMP5( struct event_args_s *args );
|
||||
void EV_FireMP52( struct event_args_s *args );
|
||||
void EV_FirePython( struct event_args_s *args );
|
||||
void EV_FireGauss( struct event_args_s *args );
|
||||
void EV_SpinGauss( struct event_args_s *args );
|
||||
void EV_Crowbar( struct event_args_s *args );
|
||||
void EV_FireCrossbow( struct event_args_s *args );
|
||||
void EV_FireCrossbow2( struct event_args_s *args );
|
||||
void EV_FireRpg( struct event_args_s *args );
|
||||
void EV_EgonFire( struct event_args_s *args );
|
||||
void EV_EgonStop( struct event_args_s *args );
|
||||
void EV_HornetGunFire( struct event_args_s *args );
|
||||
void EV_TripmineFire( struct event_args_s *args );
|
||||
void EV_SnarkFire( struct event_args_s *args );
|
||||
void EV_FireTommy( struct event_args_s *args );
|
||||
void EV_FireRifle( struct event_args_s *args );
|
||||
|
||||
void EV_TrainPitchAdjust( struct event_args_s *args );
|
||||
}
|
||||
|
@ -57,23 +45,12 @@ That was what we were going to do, but we ran out of time...oh well.
|
|||
*/
|
||||
void Game_HookEvents( void )
|
||||
{
|
||||
gEngfuncs.pfnHookEvent( "events/glock1.sc", EV_FireGlock1 );
|
||||
gEngfuncs.pfnHookEvent( "events/glock2.sc", EV_FireGlock2 );
|
||||
gEngfuncs.pfnHookEvent( "events/revolver1.sc", EV_FireRevolver1 );
|
||||
gEngfuncs.pfnHookEvent( "events/revolver2.sc", EV_FireRevolver2 );
|
||||
gEngfuncs.pfnHookEvent( "events/shotgun1.sc", EV_FireShotGunSingle );
|
||||
gEngfuncs.pfnHookEvent( "events/shotgun2.sc", EV_FireShotGunDouble );
|
||||
gEngfuncs.pfnHookEvent( "events/mp5.sc", EV_FireMP5 );
|
||||
gEngfuncs.pfnHookEvent( "events/mp52.sc", EV_FireMP52 );
|
||||
gEngfuncs.pfnHookEvent( "events/python.sc", EV_FirePython );
|
||||
gEngfuncs.pfnHookEvent( "events/gauss.sc", EV_FireGauss );
|
||||
gEngfuncs.pfnHookEvent( "events/gaussspin.sc", EV_SpinGauss );
|
||||
gEngfuncs.pfnHookEvent( "events/tommygun.sc", EV_FireTommy );
|
||||
gEngfuncs.pfnHookEvent( "events/rifle.sc", EV_FireRifle );
|
||||
|
||||
gEngfuncs.pfnHookEvent( "events/train.sc", EV_TrainPitchAdjust );
|
||||
gEngfuncs.pfnHookEvent( "events/crowbar.sc", EV_Crowbar );
|
||||
gEngfuncs.pfnHookEvent( "events/crossbow1.sc", EV_FireCrossbow );
|
||||
gEngfuncs.pfnHookEvent( "events/crossbow2.sc", EV_FireCrossbow2 );
|
||||
gEngfuncs.pfnHookEvent( "events/rpg.sc", EV_FireRpg );
|
||||
gEngfuncs.pfnHookEvent( "events/egon_fire.sc", EV_EgonFire );
|
||||
gEngfuncs.pfnHookEvent( "events/egon_stop.sc", EV_EgonStop );
|
||||
gEngfuncs.pfnHookEvent( "events/firehornet.sc", EV_HornetGunFire );
|
||||
gEngfuncs.pfnHookEvent( "events/tripfire.sc", EV_TripmineFire );
|
||||
gEngfuncs.pfnHookEvent( "events/snarkfire.sc", EV_SnarkFire );
|
||||
}
|
||||
|
|
|
@ -27,77 +27,10 @@
|
|||
#include "entity_types.h"
|
||||
#include "r_efx.h"
|
||||
|
||||
extern BEAM *pBeam;
|
||||
extern BEAM *pBeam2;
|
||||
extern TEMPENTITY *pFlare; // Vit_amiN: egon's energy flare
|
||||
void HUD_GetLastOrg( float *org );
|
||||
|
||||
void UpdateBeams( void )
|
||||
{
|
||||
vec3_t forward, vecSrc, vecEnd, origin, angles, right, up;
|
||||
vec3_t view_ofs;
|
||||
pmtrace_t tr;
|
||||
cl_entity_t *pthisplayer = gEngfuncs.GetLocalPlayer();
|
||||
int idx = pthisplayer->index;
|
||||
|
||||
// Get our exact viewangles from engine
|
||||
gEngfuncs.GetViewAngles( (float *)angles );
|
||||
|
||||
// Determine our last predicted origin
|
||||
HUD_GetLastOrg( (float *)&origin );
|
||||
|
||||
AngleVectors( angles, forward, right, up );
|
||||
|
||||
VectorCopy( origin, vecSrc );
|
||||
|
||||
VectorMA( vecSrc, 2048, forward, vecEnd );
|
||||
|
||||
gEngfuncs.pEventAPI->EV_SetUpPlayerPrediction( false, true );
|
||||
|
||||
// Store off the old count
|
||||
gEngfuncs.pEventAPI->EV_PushPMStates();
|
||||
|
||||
// Now add in all of the players.
|
||||
gEngfuncs.pEventAPI->EV_SetSolidPlayers( idx - 1 );
|
||||
|
||||
gEngfuncs.pEventAPI->EV_SetTraceHull( 2 );
|
||||
gEngfuncs.pEventAPI->EV_PlayerTrace( vecSrc, vecEnd, PM_STUDIO_BOX, -1, &tr );
|
||||
|
||||
gEngfuncs.pEventAPI->EV_PopPMStates();
|
||||
|
||||
if( pBeam )
|
||||
{
|
||||
pBeam->target = tr.endpos;
|
||||
pBeam->die = gEngfuncs.GetClientTime() + 0.1; // We keep it alive just a little bit forward in the future, just in case.
|
||||
}
|
||||
|
||||
if( pBeam2 )
|
||||
{
|
||||
pBeam2->target = tr.endpos;
|
||||
pBeam2->die = gEngfuncs.GetClientTime() + 0.1; // We keep it alive just a little bit forward in the future, just in case.
|
||||
}
|
||||
|
||||
if( pFlare ) // Vit_amiN: beam flare
|
||||
{
|
||||
pFlare->entity.origin = tr.endpos;
|
||||
pFlare->die = gEngfuncs.GetClientTime() + 0.1; // We keep it alive just a little bit forward in the future, just in case.
|
||||
|
||||
if( gEngfuncs.GetMaxClients() != 1 ) // Singleplayer always draws the egon's energy beam flare
|
||||
{
|
||||
pFlare->flags |= FTENT_NOMODEL;
|
||||
|
||||
if( !( tr.allsolid || tr.ent <= 0 || tr.fraction == 1.0 ) ) // Beam hit some non-world entity
|
||||
{
|
||||
physent_t *pEntity = gEngfuncs.pEventAPI->EV_GetPhysent( tr.ent );
|
||||
|
||||
// Not the world, let's assume that we hit something organic ( dog, cat, uncle joe, etc )
|
||||
if( pEntity && !( pEntity->solid == SOLID_BSP || pEntity->movetype == MOVETYPE_PUSHSTEP ) )
|
||||
{
|
||||
pFlare->flags &= ~FTENT_NOMODEL;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
|
@ -109,6 +42,4 @@ Add game specific, client-side objects here
|
|||
*/
|
||||
void Game_AddObjects( void )
|
||||
{
|
||||
if( pBeam || pBeam2 || pFlare ) // Vit_amiN: egon flare added
|
||||
UpdateBeams();
|
||||
}
|
||||
|
|
|
@ -53,20 +53,7 @@ int g_irunninggausspred = 0;
|
|||
vec3_t previousorigin;
|
||||
|
||||
// HLDM Weapon placeholder entities.
|
||||
CGlock g_Glock;
|
||||
CCrowbar g_Crowbar;
|
||||
CPython g_Python;
|
||||
CMP5 g_Mp5;
|
||||
CCrossbow g_Crossbow;
|
||||
CShotgun g_Shotgun;
|
||||
CRpg g_Rpg;
|
||||
CGauss g_Gauss;
|
||||
CEgon g_Egon;
|
||||
CHgun g_HGun;
|
||||
CHandGrenade g_HandGren;
|
||||
CSatchel g_Satchel;
|
||||
CTripmine g_Tripmine;
|
||||
CSqueak g_Snark;
|
||||
CRevolver g_Revolver;
|
||||
|
||||
/*
|
||||
======================
|
||||
|
@ -625,20 +612,7 @@ void HUD_InitClientWeapons( void )
|
|||
HUD_PrepEntity( &player, NULL );
|
||||
|
||||
// Allocate slot(s) for each weapon that we are going to be predicting
|
||||
HUD_PrepEntity( &g_Glock, &player );
|
||||
HUD_PrepEntity( &g_Crowbar, &player );
|
||||
HUD_PrepEntity( &g_Python, &player );
|
||||
HUD_PrepEntity( &g_Mp5, &player );
|
||||
HUD_PrepEntity( &g_Crossbow, &player );
|
||||
HUD_PrepEntity( &g_Shotgun, &player );
|
||||
HUD_PrepEntity( &g_Rpg, &player );
|
||||
HUD_PrepEntity( &g_Gauss, &player );
|
||||
HUD_PrepEntity( &g_Egon, &player );
|
||||
HUD_PrepEntity( &g_HGun, &player );
|
||||
HUD_PrepEntity( &g_HandGren, &player );
|
||||
HUD_PrepEntity( &g_Satchel, &player );
|
||||
HUD_PrepEntity( &g_Tripmine, &player );
|
||||
HUD_PrepEntity( &g_Snark, &player );
|
||||
HUD_PrepEntity( &g_Revolver, &player );
|
||||
}
|
||||
|
||||
/*
|
||||
|
@ -702,47 +676,8 @@ void HUD_WeaponsPostThink( local_state_s *from, local_state_s *to, usercmd_t *cm
|
|||
// FIXME, make this a method in each weapon? where you pass in an entity_state_t *?
|
||||
switch( from->client.m_iId )
|
||||
{
|
||||
case WEAPON_CROWBAR:
|
||||
pWeapon = &g_Crowbar;
|
||||
break;
|
||||
case WEAPON_GLOCK:
|
||||
pWeapon = &g_Glock;
|
||||
break;
|
||||
case WEAPON_PYTHON:
|
||||
pWeapon = &g_Python;
|
||||
break;
|
||||
case WEAPON_MP5:
|
||||
pWeapon = &g_Mp5;
|
||||
break;
|
||||
case WEAPON_CROSSBOW:
|
||||
pWeapon = &g_Crossbow;
|
||||
break;
|
||||
case WEAPON_SHOTGUN:
|
||||
pWeapon = &g_Shotgun;
|
||||
break;
|
||||
case WEAPON_RPG:
|
||||
pWeapon = &g_Rpg;
|
||||
break;
|
||||
case WEAPON_GAUSS:
|
||||
pWeapon = &g_Gauss;
|
||||
break;
|
||||
case WEAPON_EGON:
|
||||
pWeapon = &g_Egon;
|
||||
break;
|
||||
case WEAPON_HORNETGUN:
|
||||
pWeapon = &g_HGun;
|
||||
break;
|
||||
case WEAPON_HANDGRENADE:
|
||||
pWeapon = &g_HandGren;
|
||||
break;
|
||||
case WEAPON_SATCHEL:
|
||||
pWeapon = &g_Satchel;
|
||||
break;
|
||||
case WEAPON_TRIPMINE:
|
||||
pWeapon = &g_Tripmine;
|
||||
break;
|
||||
case WEAPON_SNARK:
|
||||
pWeapon = &g_Snark;
|
||||
case WEAPON_REVOLVER:
|
||||
pWeapon = &g_Revolver;
|
||||
break;
|
||||
}
|
||||
|
||||
|
@ -833,14 +768,7 @@ void HUD_WeaponsPostThink( local_state_s *from, local_state_s *to, usercmd_t *cm
|
|||
player.m_flAmmoStartCharge = from->client.fuser3;
|
||||
|
||||
//Stores all our ammo info, so the client side weapons can use them.
|
||||
player.ammo_9mm = (int)from->client.vuser1[0];
|
||||
player.ammo_357 = (int)from->client.vuser1[1];
|
||||
player.ammo_argrens = (int)from->client.vuser1[2];
|
||||
player.ammo_bolts = (int)from->client.ammo_nails; //is an int anyways...
|
||||
player.ammo_buckshot = (int)from->client.ammo_shells;
|
||||
player.ammo_uranium = (int)from->client.ammo_cells;
|
||||
player.ammo_hornets = (int)from->client.vuser2[0];
|
||||
player.ammo_rockets = (int)from->client.ammo_rockets;
|
||||
player.ammo_revolver = (int)from->client.vuser1[0];
|
||||
|
||||
// Point to current weapon object
|
||||
if( from->client.m_iId )
|
||||
|
@ -848,12 +776,6 @@ void HUD_WeaponsPostThink( local_state_s *from, local_state_s *to, usercmd_t *cm
|
|||
player.m_pActiveItem = g_pWpns[from->client.m_iId];
|
||||
}
|
||||
|
||||
if( player.m_pActiveItem->m_iId == WEAPON_RPG )
|
||||
{
|
||||
( (CRpg *)player.m_pActiveItem )->m_fSpotActive = (int)from->client.vuser2[1];
|
||||
( (CRpg *)player.m_pActiveItem )->m_cActiveRockets = (int)from->client.vuser2[2];
|
||||
}
|
||||
|
||||
// Don't go firing anything if we have died.
|
||||
// Or if we don't have a weapon model deployed
|
||||
if( ( player.pev->deadflag != ( DEAD_DISCARDBODY + 1 ) ) &&
|
||||
|
@ -906,21 +828,7 @@ void HUD_WeaponsPostThink( local_state_s *from, local_state_s *to, usercmd_t *cm
|
|||
to->client.maxspeed = player.pev->maxspeed;
|
||||
|
||||
//HL Weapons
|
||||
to->client.vuser1[0] = player.ammo_9mm;
|
||||
to->client.vuser1[1] = player.ammo_357;
|
||||
to->client.vuser1[2] = player.ammo_argrens;
|
||||
|
||||
to->client.ammo_nails = player.ammo_bolts;
|
||||
to->client.ammo_shells = player.ammo_buckshot;
|
||||
to->client.ammo_cells = player.ammo_uranium;
|
||||
to->client.vuser2[0] = player.ammo_hornets;
|
||||
to->client.ammo_rockets = player.ammo_rockets;
|
||||
|
||||
if( player.m_pActiveItem->m_iId == WEAPON_RPG )
|
||||
{
|
||||
from->client.vuser2[1] = ( (CRpg *)player.m_pActiveItem)->m_fSpotActive;
|
||||
from->client.vuser2[2] = ( (CRpg *)player.m_pActiveItem)->m_cActiveRockets;
|
||||
}
|
||||
to->client.vuser1[0] = player.ammo_revolver;
|
||||
|
||||
// Make sure that weapon animation matches what the game .dll is telling us
|
||||
// over the wire ( fixes some animation glitches )
|
||||
|
@ -928,14 +836,6 @@ void HUD_WeaponsPostThink( local_state_s *from, local_state_s *to, usercmd_t *cm
|
|||
{
|
||||
int body = 2;
|
||||
|
||||
//Pop the model to body 0.
|
||||
if( pWeapon == &g_Tripmine )
|
||||
body = 0;
|
||||
|
||||
//Show laser sight/scope combo
|
||||
if( pWeapon == &g_Python && bIsMultiplayer() )
|
||||
body = 1;
|
||||
|
||||
// Force a fixed anim down to viewmodel
|
||||
HUD_SendWeaponAnim( to->client.weaponanim, body, 1 );
|
||||
}
|
||||
|
|
|
@ -299,7 +299,9 @@ void CHud::Init( void )
|
|||
m_Spectator.Init();
|
||||
m_Geiger.Init();
|
||||
m_Train.Init();
|
||||
m_Battery.Init();
|
||||
// m_Battery.Init();
|
||||
m_Sanity.Init();
|
||||
m_ReadBook.Init();
|
||||
m_Flash.Init();
|
||||
m_Message.Init();
|
||||
m_StatusBar.Init();
|
||||
|
@ -485,7 +487,9 @@ void CHud::VidInit( void )
|
|||
m_Spectator.VidInit();
|
||||
m_Geiger.VidInit();
|
||||
m_Train.VidInit();
|
||||
m_Battery.VidInit();
|
||||
// m_Battery.VidInit();
|
||||
m_Sanity.VidInit();
|
||||
m_ReadBook.VidInit();
|
||||
m_Flash.VidInit();
|
||||
m_Message.VidInit();
|
||||
m_StatusBar.VidInit();
|
||||
|
|
32
cl_dll/hud.h
32
cl_dll/hud.h
|
@ -27,6 +27,8 @@
|
|||
#define RGB_REDISH 0x00FF1010 //255,160,0
|
||||
#define RGB_GREENISH 0x0000A000 //0,160,0
|
||||
|
||||
#include "util_vector.h"
|
||||
#include "const.h"
|
||||
#include "wrect.h"
|
||||
#include "cl_dll.h"
|
||||
#include "ammo.h"
|
||||
|
@ -408,24 +410,42 @@ private:
|
|||
//
|
||||
//-----------------------------------------------------
|
||||
//
|
||||
class CHudBattery : public CHudBase
|
||||
class CHudSanity : public CHudBase
|
||||
{
|
||||
public:
|
||||
int Init( void );
|
||||
int VidInit( void );
|
||||
int Draw( float flTime );
|
||||
int MsgFunc_Battery( const char *pszName, int iSize, void *pbuf );
|
||||
int MsgFunc_Sanity( const char *pszName, int iSize, void *pbuf );
|
||||
|
||||
private:
|
||||
HSPRITE m_hSprite1;
|
||||
HSPRITE m_hSprite2;
|
||||
wrect_t *m_prc1;
|
||||
wrect_t *m_prc2;
|
||||
int m_iBat;
|
||||
int m_iSan;
|
||||
float m_fFade;
|
||||
int m_iHeight; // width of the battery innards
|
||||
int m_iHeight; // width of the sanity innards
|
||||
};
|
||||
|
||||
//
|
||||
//-----------------------------------------------------
|
||||
//
|
||||
#include "ImageLabel.h"
|
||||
|
||||
class CHudReadBook : public CHudBase
|
||||
{
|
||||
public:
|
||||
int Init( void );
|
||||
int VidInit( void );
|
||||
// int Draw(float flTime);
|
||||
int MsgFunc_ReadBook(const char *pszName, int iSize, void *pbuf );
|
||||
|
||||
private:
|
||||
CImageLabel *pImage;
|
||||
};
|
||||
|
||||
|
||||
//
|
||||
//-----------------------------------------------------
|
||||
//
|
||||
|
@ -679,7 +699,9 @@ public:
|
|||
CHudHealth m_Health;
|
||||
CHudSpectator m_Spectator;
|
||||
CHudGeiger m_Geiger;
|
||||
CHudBattery m_Battery;
|
||||
CHudSanity m_Sanity;
|
||||
CHudReadBook m_ReadBook;
|
||||
// CHudBattery m_Battery;
|
||||
CHudTrain m_Train;
|
||||
CHudFlashlight m_Flash;
|
||||
CHudMessage m_Message;
|
||||
|
|
|
@ -31,10 +31,6 @@ float g_fFadeDuration; //negative = fading out
|
|||
|
||||
#define MAX_CLIENTS 32
|
||||
|
||||
extern BEAM *pBeam;
|
||||
extern BEAM *pBeam2;
|
||||
extern TEMPENTITY *pFlare; // Vit_amiN
|
||||
|
||||
extern float g_lastFOV; // Vit_amiN
|
||||
|
||||
/// USER-DEFINED SERVER MESSAGE HANDLERS
|
||||
|
@ -102,10 +98,6 @@ void CHud::MsgFunc_InitHUD( const char *pszName, int iSize, void *pbuf )
|
|||
pList->p->InitHUDData();
|
||||
pList = pList->pNext;
|
||||
}
|
||||
|
||||
//Probably not a good place to put this.
|
||||
pBeam = pBeam2 = NULL;
|
||||
pFlare = NULL; // Vit_amiN: clear egon's beam flare
|
||||
}
|
||||
|
||||
//LRC
|
||||
|
|
|
@ -347,8 +347,9 @@ void CFlockingFlyer::SpawnCommonCode()
|
|||
//=========================================================
|
||||
void CFlockingFlyer::BoidAdvanceFrame()
|
||||
{
|
||||
float flapspeed = ( pev->speed - pev->armorvalue ) / AFLOCK_ACCELERATE;
|
||||
pev->armorvalue = pev->armorvalue * .8 + pev->speed * .2;
|
||||
// float flapspeed = ( pev->speed - pev->armorvalue ) / AFLOCK_ACCELERATE;
|
||||
float flapspeed = pev->speed / AFLOCK_ACCELERATE;
|
||||
// pev->armorvalue = pev->armorvalue * .8 + pev->speed * .2;
|
||||
|
||||
if( flapspeed < 0 )
|
||||
flapspeed = -flapspeed;
|
||||
|
|
|
@ -897,8 +897,13 @@ LINK_ENTITY_TO_CLASS( monster_barney_dead, CDeadBarney )
|
|||
//=========================================================
|
||||
void CDeadBarney::Spawn()
|
||||
{
|
||||
PRECACHE_MODEL( "models/barney.mdl" );
|
||||
SET_MODEL( ENT( pev ), "models/barney.mdl" );
|
||||
const char *szModel = "models/barney.mdl";
|
||||
|
||||
if( pev->model )
|
||||
szModel = STRING( pev->model ); //LRC
|
||||
|
||||
PRECACHE_MODEL( szModel );
|
||||
SET_MODEL( ENT( pev ), szModel );
|
||||
|
||||
pev->effects = 0;
|
||||
pev->yaw_speed = 8;
|
||||
|
|
|
@ -134,6 +134,7 @@ public:
|
|||
virtual BOOL ShouldFadeOnDeath( void );
|
||||
|
||||
// Basic Monster AI functions
|
||||
virtual float GetFleeDistance( void ) { return 500.0f; };
|
||||
virtual float ChangeYaw( int speed );
|
||||
float VecToYaw( Vector vecDir );
|
||||
float FlYawDiff( void );
|
||||
|
@ -211,7 +212,7 @@ public:
|
|||
void PushEnemy( CBaseEntity *pEnemy, Vector &vecLastKnownPos );
|
||||
BOOL PopEnemy( void );
|
||||
|
||||
BOOL FGetNodeRoute( Vector vecDest );
|
||||
virtual BOOL FGetNodeRoute( Vector vecDest );
|
||||
|
||||
inline void TaskComplete( void ) { if ( !HasConditions( bits_COND_TASK_FAILED ) ) m_iTaskStatus = TASKSTATUS_COMPLETE; }
|
||||
void MovementComplete( void );
|
||||
|
@ -229,11 +230,12 @@ public:
|
|||
virtual BOOL FTriangulate( const Vector &vecStart , const Vector &vecEnd, float flDist, CBaseEntity *pTargetEnt, Vector *pApex );
|
||||
void MakeIdealYaw( Vector vecTarget );
|
||||
virtual void SetYawSpeed( void ) { return; };// allows different yaw_speeds for each activity
|
||||
BOOL BuildRoute( const Vector &vecGoal, int iMoveFlag, CBaseEntity *pTarget );
|
||||
virtual BOOL BuildRoute( const Vector &vecGoal, int iMoveFlag, CBaseEntity *pTarget );
|
||||
virtual BOOL BuildNearestRoute( Vector vecThreat, Vector vecViewOffset, float flMinDist, float flMaxDist );
|
||||
int RouteClassify( int iMoveFlag );
|
||||
void InsertWaypoint( Vector vecLocation, int afMoveFlags );
|
||||
|
||||
BOOL RunAwayFromEnemy( void );
|
||||
BOOL FindLateralCover( const Vector &vecThreat, const Vector &vecViewOffset );
|
||||
virtual BOOL FindCover( Vector vecThreat, Vector vecViewOffset, float flMinDist, float flMaxDist );
|
||||
virtual BOOL FValidateCover( const Vector &vecCoverLocation ) { return TRUE; };
|
||||
|
@ -273,6 +275,8 @@ public:
|
|||
|
||||
void SetEyePosition( void );
|
||||
|
||||
void Panic( entvars_t *pevPanic );// make the monster panic for a while.
|
||||
|
||||
BOOL FShouldEat( void );// see if a monster is 'hungry'
|
||||
void Eat( float flFullDuration );// make the monster 'full' for a while.
|
||||
|
||||
|
|
|
@ -26,34 +26,9 @@
|
|||
#include "decals.h"
|
||||
#include "weapons.h"
|
||||
#include "game.h"
|
||||
#include "bm.h"
|
||||
|
||||
//LRC brought in from animation.h
|
||||
#define ACTIVITY_NOT_AVAILABLE -1
|
||||
|
||||
#define SF_INFOBM_RUN 0x0001
|
||||
#define SF_INFOBM_WAIT 0x0002
|
||||
|
||||
// AI Nodes for Big Momma
|
||||
class CInfoBM : public CPointEntity
|
||||
{
|
||||
public:
|
||||
void Spawn( void );
|
||||
void KeyValue( KeyValueData* pkvd );
|
||||
|
||||
// name in pev->targetname
|
||||
// next in pev->target
|
||||
// radius in pev->scale
|
||||
// health in pev->health
|
||||
// Reach target in pev->message
|
||||
// Reach delay in pev->speed
|
||||
// Reach sequence in pev->netname
|
||||
|
||||
virtual int Save( CSave &save );
|
||||
virtual int Restore( CRestore &restore );
|
||||
static TYPEDESCRIPTION m_SaveData[];
|
||||
|
||||
string_t m_preSequence;
|
||||
};
|
||||
int gSpitSprite, gSpitDebrisSprite;
|
||||
|
||||
LINK_ENTITY_TO_CLASS( info_bigmomma, CInfoBM )
|
||||
|
||||
|
@ -102,21 +77,6 @@ void CInfoBM::KeyValue( KeyValueData* pkvd )
|
|||
//=========================================================
|
||||
// Mortar shot entity
|
||||
//=========================================================
|
||||
class CBMortar : public CBaseEntity
|
||||
{
|
||||
public:
|
||||
void Spawn( void );
|
||||
|
||||
static CBMortar *Shoot( edict_t *pOwner, Vector vecStart, Vector vecVelocity );
|
||||
void Touch( CBaseEntity *pOther );
|
||||
void EXPORT Animate( void );
|
||||
|
||||
virtual int Save( CSave &save );
|
||||
virtual int Restore( CRestore &restore );
|
||||
static TYPEDESCRIPTION m_SaveData[];
|
||||
|
||||
int m_maxFrame;
|
||||
};
|
||||
|
||||
LINK_ENTITY_TO_CLASS( bmortar, CBMortar )
|
||||
|
||||
|
@ -162,10 +122,6 @@ IMPLEMENT_SAVERESTORE( CBMortar, CBaseEntity )
|
|||
#define bits_MEMORY_COMPLETED_NODE ( bits_MEMORY_CUSTOM3 )
|
||||
#define bits_MEMORY_FIRED_NODE ( bits_MEMORY_CUSTOM4 )
|
||||
|
||||
int gSpitSprite, gSpitDebrisSprite;
|
||||
Vector VecCheckSplatToss( entvars_t *pev, const Vector &vecSpot1, Vector vecSpot2, float maxHeight );
|
||||
void MortarSpray( const Vector &position, const Vector &direction, int spriteModel, int count );
|
||||
|
||||
// UNDONE:
|
||||
//
|
||||
#define BIG_CHILDCLASS "monster_babycrab"
|
||||
|
@ -1094,164 +1050,4 @@ void CBigMomma::RunTask( Task_t *pTask )
|
|||
}
|
||||
}
|
||||
|
||||
Vector VecCheckSplatToss( entvars_t *pev, const Vector &vecSpot1, Vector vecSpot2, float maxHeight )
|
||||
{
|
||||
TraceResult tr;
|
||||
Vector vecMidPoint;// halfway point between Spot1 and Spot2
|
||||
Vector vecApex;// highest point
|
||||
Vector vecScale;
|
||||
Vector vecGrenadeVel;
|
||||
Vector vecTemp;
|
||||
float flGravity = g_psv_gravity->value;
|
||||
|
||||
// calculate the midpoint and apex of the 'triangle'
|
||||
vecMidPoint = vecSpot1 + ( vecSpot2 - vecSpot1 ) * 0.5;
|
||||
UTIL_TraceLine( vecMidPoint, vecMidPoint + Vector( 0, 0, maxHeight ), ignore_monsters, ENT( pev ), &tr );
|
||||
vecApex = tr.vecEndPos;
|
||||
|
||||
UTIL_TraceLine( vecSpot1, vecApex, dont_ignore_monsters, ENT( pev ), &tr );
|
||||
if( tr.flFraction != 1.0 )
|
||||
{
|
||||
// fail!
|
||||
return g_vecZero;
|
||||
}
|
||||
|
||||
// Don't worry about actually hitting the target, this won't hurt us!
|
||||
|
||||
// How high should the grenade travel (subtract 15 so the grenade doesn't hit the ceiling)?
|
||||
float height = vecApex.z - vecSpot1.z - 15;
|
||||
// How fast does the grenade need to travel to reach that height given gravity?
|
||||
float speed = sqrt( 2 * flGravity * height );
|
||||
|
||||
// How much time does it take to get there?
|
||||
float time = speed / flGravity;
|
||||
vecGrenadeVel = vecSpot2 - vecSpot1;
|
||||
vecGrenadeVel.z = 0;
|
||||
|
||||
// Travel half the distance to the target in that time (apex is at the midpoint)
|
||||
vecGrenadeVel = vecGrenadeVel * ( 0.5 / time );
|
||||
// Speed to offset gravity at the desired height
|
||||
vecGrenadeVel.z = speed;
|
||||
|
||||
return vecGrenadeVel;
|
||||
}
|
||||
|
||||
// ---------------------------------
|
||||
//
|
||||
// Mortar
|
||||
//
|
||||
// ---------------------------------
|
||||
void MortarSpray( const Vector &position, const Vector &direction, int spriteModel, int count )
|
||||
{
|
||||
MESSAGE_BEGIN( MSG_PVS, SVC_TEMPENTITY, position );
|
||||
WRITE_BYTE( TE_SPRITE_SPRAY );
|
||||
WRITE_COORD( position.x ); // pos
|
||||
WRITE_COORD( position.y );
|
||||
WRITE_COORD( position.z );
|
||||
WRITE_COORD( direction.x ); // dir
|
||||
WRITE_COORD( direction.y );
|
||||
WRITE_COORD( direction.z );
|
||||
WRITE_SHORT( spriteModel ); // model
|
||||
WRITE_BYTE ( count ); // count
|
||||
WRITE_BYTE ( 130 ); // speed
|
||||
WRITE_BYTE ( 80 ); // noise ( client will divide by 100 )
|
||||
MESSAGE_END();
|
||||
}
|
||||
|
||||
// UNDONE: right now this is pretty much a copy of the squid spit with minor changes to the way it does damage
|
||||
void CBMortar::Spawn( void )
|
||||
{
|
||||
pev->movetype = MOVETYPE_TOSS;
|
||||
pev->classname = MAKE_STRING( "bmortar" );
|
||||
|
||||
pev->solid = SOLID_BBOX;
|
||||
pev->rendermode = kRenderTransAlpha;
|
||||
pev->renderamt = 255;
|
||||
|
||||
SET_MODEL( ENT( pev ), "sprites/mommaspit.spr" );
|
||||
pev->frame = 0;
|
||||
pev->scale = 0.5;
|
||||
|
||||
UTIL_SetSize( pev, Vector( 0, 0, 0 ), Vector( 0, 0, 0 ) );
|
||||
|
||||
m_maxFrame = (float) MODEL_FRAMES( pev->modelindex ) - 1;
|
||||
pev->dmgtime = gpGlobals->time + 0.4;
|
||||
}
|
||||
|
||||
void CBMortar::Animate( void )
|
||||
{
|
||||
SetNextThink( 0.1 );
|
||||
|
||||
if( gpGlobals->time > pev->dmgtime )
|
||||
{
|
||||
pev->dmgtime = gpGlobals->time + 0.2;
|
||||
MortarSpray( pev->origin, -pev->velocity.Normalize(), gSpitSprite, 3 );
|
||||
}
|
||||
if( pev->frame++ )
|
||||
{
|
||||
if ( pev->frame > m_maxFrame )
|
||||
{
|
||||
pev->frame = 0;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
CBMortar *CBMortar::Shoot( edict_t *pOwner, Vector vecStart, Vector vecVelocity )
|
||||
{
|
||||
CBMortar *pSpit = GetClassPtr( (CBMortar *)NULL );
|
||||
pSpit->Spawn();
|
||||
|
||||
UTIL_SetOrigin( pSpit, vecStart );
|
||||
pSpit->pev->velocity = vecVelocity;
|
||||
pSpit->pev->owner = pOwner;
|
||||
pSpit->pev->scale = 2.5;
|
||||
pSpit->SetThink( &CBMortar::Animate );
|
||||
pSpit->SetNextThink( 0.1 );
|
||||
|
||||
return pSpit;
|
||||
}
|
||||
|
||||
void CBMortar::Touch( CBaseEntity *pOther )
|
||||
{
|
||||
TraceResult tr;
|
||||
int iPitch;
|
||||
|
||||
// splat sound
|
||||
iPitch = RANDOM_FLOAT( 90, 110 );
|
||||
|
||||
EMIT_SOUND_DYN( ENT( pev ), CHAN_VOICE, "bullchicken/bc_acid1.wav", 1, ATTN_NORM, 0, iPitch );
|
||||
|
||||
switch( RANDOM_LONG( 0, 1 ) )
|
||||
{
|
||||
case 0:
|
||||
EMIT_SOUND_DYN( ENT( pev ), CHAN_WEAPON, "bullchicken/bc_spithit1.wav", 1, ATTN_NORM, 0, iPitch );
|
||||
break;
|
||||
case 1:
|
||||
EMIT_SOUND_DYN( ENT( pev ), CHAN_WEAPON, "bullchicken/bc_spithit2.wav", 1, ATTN_NORM, 0, iPitch );
|
||||
break;
|
||||
}
|
||||
|
||||
if( pOther->IsBSPModel() )
|
||||
{
|
||||
|
||||
// make a splat on the wall
|
||||
UTIL_TraceLine( pev->origin, pev->origin + pev->velocity * 10, dont_ignore_monsters, ENT( pev ), &tr );
|
||||
UTIL_DecalTrace( &tr, DECAL_MOMMASPLAT );
|
||||
}
|
||||
else
|
||||
{
|
||||
tr.vecEndPos = pev->origin;
|
||||
tr.vecPlaneNormal = -1 * pev->velocity.Normalize();
|
||||
}
|
||||
|
||||
// make some flecks
|
||||
MortarSpray( tr.vecEndPos, tr.vecPlaneNormal, gSpitSprite, 24 );
|
||||
|
||||
entvars_t *pevOwner = NULL;
|
||||
if( pev->owner )
|
||||
pevOwner = VARS(pev->owner);
|
||||
|
||||
RadiusDamage( pev->origin, pev, pevOwner, gSkillData.bigmommaDmgBlast, gSkillData.bigmommaRadiusBlast, CLASS_NONE, DMG_ACID );
|
||||
UTIL_Remove( this );
|
||||
}
|
||||
#endif
|
||||
|
|
|
@ -24,6 +24,7 @@
|
|||
#include "util.h"
|
||||
#include "cbase.h"
|
||||
#include "doors.h"
|
||||
#include "bmodels.h"
|
||||
#include "movewith.h"
|
||||
|
||||
extern DLL_GLOBAL Vector g_vecAttackDir;
|
||||
|
@ -52,19 +53,6 @@ Vector VecBModelOrigin( entvars_t* pevBModel )
|
|||
/*QUAKED func_wall (0 .5 .8) ?
|
||||
This is just a solid wall if not inhibited
|
||||
*/
|
||||
class CFuncWall : public CBaseEntity
|
||||
{
|
||||
public:
|
||||
void Spawn( void );
|
||||
void Use( CBaseEntity *pActivator, CBaseEntity *pCaller, USE_TYPE useType, float value );
|
||||
|
||||
virtual STATE GetState( void ) { return pev->frame?STATE_ON:STATE_OFF; };
|
||||
|
||||
// Bmodels don't go across transitions
|
||||
virtual int ObjectCaps( void ) { return CBaseEntity :: ObjectCaps() & ~FCAP_ACROSS_TRANSITION; }
|
||||
|
||||
int m_iStyle;
|
||||
};
|
||||
|
||||
LINK_ENTITY_TO_CLASS( func_wall, CFuncWall )
|
||||
|
||||
|
@ -260,6 +248,30 @@ void CFuncIllusionary::Spawn( void )
|
|||
// MAKE_STATIC(ENT(pev));
|
||||
}
|
||||
|
||||
// -------------------------------------------------------------------------------
|
||||
//
|
||||
// Burning clip brush
|
||||
//
|
||||
// This brush will be solid for any entity who has the FL_BURNING_CLIP flag set
|
||||
// in pev->flags
|
||||
//
|
||||
// otherwise it will be invisible and not solid. This can be used to keep
|
||||
// specific monsters out of certain areas
|
||||
//
|
||||
// -------------------------------------------------------------------------------
|
||||
|
||||
LINK_ENTITY_TO_CLASS( func_burning_clip, CFuncBurningClip );
|
||||
|
||||
void CFuncBurningClip::Spawn( void )
|
||||
{
|
||||
CFuncWall::Spawn();
|
||||
|
||||
if( CVAR_GET_FLOAT( "showtriggers" ) == 0 )
|
||||
pev->effects = EF_NODRAW;
|
||||
|
||||
pev->flags |= FL_BURNING_CLIP;
|
||||
}
|
||||
|
||||
// =================== FUNC_SHINE ==============================================
|
||||
|
||||
//LRC - shiny surfaces
|
||||
|
@ -331,12 +343,6 @@ void CFuncShine :: Think( void )
|
|||
// specific monsters out of certain areas
|
||||
//
|
||||
// -------------------------------------------------------------------------------
|
||||
class CFuncMonsterClip : public CFuncWall
|
||||
{
|
||||
public:
|
||||
void Spawn( void );
|
||||
void Use( CBaseEntity *pActivator, CBaseEntity *pCaller, USE_TYPE useType, float value ) {} // Clear out func_wall's use function
|
||||
};
|
||||
|
||||
LINK_ENTITY_TO_CLASS( func_monsterclip, CFuncMonsterClip )
|
||||
|
||||
|
|
|
@ -175,6 +175,12 @@ int DispatchSpawn( edict_t *pent )
|
|||
else if( !FStrEq( STRING( gpGlobals->mapname ), pGlobal->levelName ) )
|
||||
pEntity->MakeDormant(); // Hasn't been moved to this level yet, wait but stay alive
|
||||
// In this level & not dead, continue on as normal
|
||||
//LRCT
|
||||
else
|
||||
{
|
||||
ALERT(at_console, "Deleting %s \"%s\" (\"%s\")\n", STRING(pEntity->pev->classname), STRING(pEntity->pev->targetname), STRING(pEntity->pev->globalname));
|
||||
return -1;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
|
@ -614,7 +620,8 @@ void CBaseEntity :: SetEternalThink( void )
|
|||
void CBaseEntity :: SetNextThink( float delay, BOOL correctSpeed )
|
||||
{
|
||||
// now monsters use this method, too.
|
||||
if (m_pMoveWith || m_pChildMoveWith || pev->flags & FL_MONSTER)
|
||||
if( m_pMoveWith || m_pChildMoveWith )
|
||||
//if (m_pMoveWith || m_pChildMoveWith || pev->flags & FL_MONSTER) // Cthulhu - this causes a level transition bug
|
||||
{
|
||||
// use the Assist system, so that thinking doesn't mess up movement.
|
||||
if (pev->movetype == MOVETYPE_PUSH)
|
||||
|
@ -994,6 +1001,7 @@ CBaseEntity *CBaseEntity::Create( const char *szName, const Vector &vecOrigin, c
|
|||
return NULL;
|
||||
}
|
||||
pEntity = Instance( pent );
|
||||
pEntity->m_pAssistLink = 0;
|
||||
pEntity->pev->owner = pentOwner;
|
||||
pEntity->pev->origin = vecOrigin;
|
||||
pEntity->pev->angles = vecAngles;
|
||||
|
|
15
dlls/cbase.h
15
dlls/cbase.h
|
@ -123,9 +123,12 @@ typedef void(CBaseEntity::*USEPTR)( CBaseEntity *pActivator, CBaseEntity *pCalle
|
|||
#define CLASS_PLAYER_ALLY 11
|
||||
#define CLASS_PLAYER_BIOWEAPON 12 // hornets and snarks.launched by players
|
||||
#define CLASS_ALIEN_BIOWEAPON 13 // hornets and snarks.launched by the alien menace
|
||||
#define CLASS_FACTION_A 14 //LRC - very simple new classes, for use with Behaves As
|
||||
#define CLASS_FACTION_B 15
|
||||
#define CLASS_FACTION_C 16
|
||||
#define CLASS_HUMAN_CULTIST 14
|
||||
#define CLASS_MUNDANE_PREY 15 // like chickens
|
||||
#define CLASS_MUNDANE_PREDATOR 16 // like wolves
|
||||
#define CLASS_FACTION_A 17 //LRC - very simple new classes, for use with Behaves As
|
||||
#define CLASS_FACTION_B 18
|
||||
#define CLASS_FACTION_C 19
|
||||
#define CLASS_BARNACLE 99 // special because no one pays attention to it, and it eats a wide cross-section of creatures.
|
||||
|
||||
class CBaseEntity;
|
||||
|
@ -448,6 +451,8 @@ public:
|
|||
virtual BOOL FVisible( const Vector &vecOrigin );
|
||||
|
||||
//We use this variables to store each ammo count.
|
||||
int ammo_revolver;
|
||||
/*
|
||||
int ammo_9mm;
|
||||
int ammo_357;
|
||||
int ammo_bolts;
|
||||
|
@ -456,6 +461,7 @@ public:
|
|||
int ammo_uranium;
|
||||
int ammo_hornets;
|
||||
int ammo_argrens;
|
||||
*/
|
||||
//Special stuff for grenades and satchels.
|
||||
float m_flStartThrow;
|
||||
float m_flReleaseThrow;
|
||||
|
@ -737,9 +743,10 @@ public:
|
|||
#define DMG_SLOWBURN (1 << 21) // in an oven
|
||||
#define DMG_SLOWFREEZE (1 << 22) // in a subzero freezer
|
||||
#define DMG_MORTAR (1 << 23) // Hit by air raid (done to distinguish grenade from mortar)
|
||||
#define DMG_POWDER_IBN (1 << 24) // hit by powder of ibn...does zero damage but makes Dunwich Horror visible
|
||||
|
||||
// these are the damage types that are allowed to gib corpses
|
||||
#define DMG_GIB_CORPSE ( DMG_CRUSH | DMG_FALL | DMG_BLAST | DMG_SONIC | DMG_CLUB )
|
||||
#define DMG_GIB_CORPSE ( DMG_CRUSH | DMG_SLASH | DMG_FALL | DMG_BLAST | DMG_SONIC | DMG_CLUB )
|
||||
|
||||
// these are the damage types that have client hud art
|
||||
#define DMG_SHOWNHUD (DMG_POISON | DMG_ACID | DMG_FREEZE | DMG_SLOWFREEZE | DMG_DROWN | DMG_BURN | DMG_SLOWBURN | DMG_NERVEGAS | DMG_RADIATION | DMG_SHOCK)
|
||||
|
|
|
@ -864,8 +864,8 @@ void ClientPrecache( void )
|
|||
|
||||
// PRECACHE_SOUND( "player/pl_jumpland2.wav" ); // UNDONE: play 2x step sound
|
||||
|
||||
PRECACHE_SOUND( "player/pl_fallpain2.wav" );
|
||||
PRECACHE_SOUND( "player/pl_fallpain3.wav" );
|
||||
// PRECACHE_SOUND( "player/pl_fallpain2.wav" );
|
||||
// PRECACHE_SOUND( "player/pl_fallpain3.wav" );
|
||||
|
||||
PRECACHE_SOUND( "player/pl_step1.wav" ); // walk on concrete
|
||||
PRECACHE_SOUND( "player/pl_step2.wav" );
|
||||
|
@ -942,11 +942,14 @@ void ClientPrecache( void )
|
|||
PRECACHE_SOUND( "common/bodysplat.wav" );
|
||||
|
||||
// player pain sounds
|
||||
PRECACHE_SOUND( "player/pl_pain2.wav" );
|
||||
PRECACHE_SOUND( "player/pl_pain4.wav" );
|
||||
PRECACHE_SOUND( "player/pl_pain5.wav" );
|
||||
PRECACHE_SOUND( "player/pl_pain6.wav" );
|
||||
PRECACHE_SOUND( "player/pl_pain7.wav" );
|
||||
// PRECACHE_SOUND( "player/pl_pain2.wav" );
|
||||
// PRECACHE_SOUND( "player/pl_pain4.wav" );
|
||||
// PRECACHE_SOUND( "player/pl_pain5.wav" );
|
||||
// PRECACHE_SOUND( "player/pl_pain6.wav" );
|
||||
// PRECACHE_SOUND( "player/pl_pain7.wav" );
|
||||
PRECACHE_SOUND( "player/rs_pain1.wav" );
|
||||
PRECACHE_SOUND( "player/rs_pain2.wav" );
|
||||
PRECACHE_SOUND( "player/rs_pain3.wav" );
|
||||
|
||||
PRECACHE_MODEL( "models/player.mdl" );
|
||||
|
||||
|
@ -1800,6 +1803,7 @@ void UpdateClientData( const struct edict_s *ent, int sendweapons, struct client
|
|||
cd->m_flNextAttack = pl->m_flNextAttack;
|
||||
cd->fuser2 = pl->m_flNextAmmoBurn;
|
||||
cd->fuser3 = pl->m_flAmmoStartCharge;
|
||||
/*
|
||||
cd->vuser1.x = pl->ammo_9mm;
|
||||
cd->vuser1.y = pl->ammo_357;
|
||||
cd->vuser1.z = pl->ammo_argrens;
|
||||
|
@ -1808,7 +1812,7 @@ void UpdateClientData( const struct edict_s *ent, int sendweapons, struct client
|
|||
cd->ammo_rockets = pl->ammo_rockets;
|
||||
cd->ammo_cells = pl->ammo_uranium;
|
||||
cd->vuser2.x = pl->ammo_hornets;
|
||||
|
||||
*/
|
||||
if( pl->m_pActiveItem )
|
||||
{
|
||||
CBasePlayerWeapon *gun;
|
||||
|
|
|
@ -551,6 +551,12 @@ Activity CBaseMonster::GetSmallFlinchActivity( void )
|
|||
flinchActivity = ACT_SMALL_FLINCH;
|
||||
}
|
||||
|
||||
// do we have even a basic flinch???
|
||||
if( LookupActivity( flinchActivity ) == ACTIVITY_NOT_AVAILABLE )
|
||||
{
|
||||
flinchActivity = ACT_IDLE;
|
||||
}
|
||||
|
||||
return flinchActivity;
|
||||
}
|
||||
|
||||
|
@ -1503,6 +1509,22 @@ void CBaseEntity::FireBullets( ULONG cShots, Vector vecSrc, Vector vecDirShootin
|
|||
else switch( iBulletType )
|
||||
{
|
||||
default:
|
||||
case BULLET_PLAYER_REVOLVER:
|
||||
// make distance based!
|
||||
pEntity->TraceAttack( pevAttacker, gSkillData.plrDmgRevolver, vecDir, &tr, DMG_BULLET );
|
||||
break;
|
||||
case BULLET_PLAYER_SHOTGUN:
|
||||
// make distance based!
|
||||
pEntity->TraceAttack(pevAttacker, gSkillData.plrDmgShotgun, vecDir, &tr, DMG_BULLET);
|
||||
break;
|
||||
case BULLET_PLAYER_TOMMYGUN:
|
||||
// make distance based!
|
||||
pEntity->TraceAttack(pevAttacker, gSkillData.plrDmgTommyGun, vecDir, &tr, DMG_BULLET);
|
||||
break;
|
||||
case BULLET_PLAYER_RIFLE:
|
||||
// make distance based!
|
||||
pEntity->TraceAttack(pevAttacker, gSkillData.plrDmgRifle, vecDir, &tr, DMG_BULLET);
|
||||
break;
|
||||
case BULLET_MONSTER_9MM:
|
||||
pEntity->TraceAttack( pevAttacker, gSkillData.monDmg9MM, vecDir, &tr, DMG_BULLET );
|
||||
|
||||
|
@ -1523,15 +1545,6 @@ void CBaseEntity::FireBullets( ULONG cShots, Vector vecSrc, Vector vecDirShootin
|
|||
DecalGunshot( &tr, iBulletType );
|
||||
}
|
||||
break;
|
||||
|
||||
case BULLET_PLAYER_357:
|
||||
pEntity->TraceAttack(pevAttacker, gSkillData.plrDmg357, vecDir, &tr, DMG_BULLET);
|
||||
if ( !tracer )
|
||||
{
|
||||
TEXTURETYPE_PlaySound(&tr, vecSrc, vecEnd, iBulletType);
|
||||
DecalGunshot( &tr, iBulletType );
|
||||
}
|
||||
break;
|
||||
|
||||
case BULLET_NONE: // FIX
|
||||
pEntity->TraceAttack( pevAttacker, 50, vecDir, &tr, DMG_CLUB );
|
||||
|
@ -1612,13 +1625,6 @@ Vector CBaseEntity::FireBulletsPlayer( ULONG cShots, Vector vecSrc, Vector vecDi
|
|||
case BULLET_PLAYER_MP5:
|
||||
pEntity->TraceAttack( pevAttacker, gSkillData.plrDmgMP5, vecDir, &tr, DMG_BULLET );
|
||||
break;
|
||||
case BULLET_PLAYER_BUCKSHOT:
|
||||
// make distance based!
|
||||
pEntity->TraceAttack( pevAttacker, gSkillData.plrDmgBuckshot, vecDir, &tr, DMG_BULLET );
|
||||
break;
|
||||
case BULLET_PLAYER_357:
|
||||
pEntity->TraceAttack( pevAttacker, gSkillData.plrDmg357, vecDir, &tr, DMG_BULLET );
|
||||
break;
|
||||
case BULLET_NONE: // FIX
|
||||
pEntity->TraceAttack( pevAttacker, 50, vecDir, &tr, DMG_CLUB );
|
||||
TEXTURETYPE_PlaySound( &tr, vecSrc, vecEnd, iBulletType );
|
||||
|
|
|
@ -65,7 +65,7 @@ public:
|
|||
|
||||
void Stop( void );
|
||||
void Move( float flInterval );
|
||||
int CheckLocalMove( const Vector &vecStart, const Vector &vecEnd, CBaseEntity *pTarget, float *pflDist );
|
||||
virtual int CheckLocalMove( const Vector &vecStart, const Vector &vecEnd, CBaseEntity *pTarget, float *pflDist );
|
||||
void MoveExecute( CBaseEntity *pTargetEnt, const Vector &vecDir, float flInterval );
|
||||
void SetActivity( Activity NewActivity );
|
||||
BOOL ShouldAdvanceRoute( float flWaypointDist );
|
||||
|
|
|
@ -448,9 +448,9 @@ void CCrossbow::FireBolt()
|
|||
pBolt->pev->avelocity.z = 10;
|
||||
#endif
|
||||
|
||||
if( !m_iClip && m_pPlayer->m_rgAmmo[m_iPrimaryAmmoType] <= 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_pPlayer->SetSuitUpdate( "!HEV_AMO0", FALSE, 0 );
|
||||
|
||||
m_flNextPrimaryAttack = GetNextAttackDelay( 0.75 );
|
||||
|
||||
|
|
|
@ -21,6 +21,7 @@
|
|||
#include "nodes.h"
|
||||
#include "player.h"
|
||||
#include "gamerules.h"
|
||||
#include "crowbar.h"
|
||||
|
||||
#define CROWBAR_BODYHIT_VOLUME 128
|
||||
#define CROWBAR_WALLHIT_VOLUME 512
|
||||
|
|
|
@ -0,0 +1,423 @@
|
|||
/***
|
||||
*
|
||||
* Copyright (c) 1999, 2000 Valve LLC. All rights reserved.
|
||||
*
|
||||
* This product contains software technology licensed from Id
|
||||
* Software, Inc. ("Id Technology"). Id Technology (c) 1996 Id Software, Inc.
|
||||
* All Rights Reserved.
|
||||
*
|
||||
* This source code contains proprietary and confidential information of
|
||||
* Valve LLC and its suppliers. Access to this code is restricted to
|
||||
* persons who have executed a written SDK license with Valve. Any access,
|
||||
* use or distribution of this code by or to any unlicensed person is illegal.
|
||||
*
|
||||
****/
|
||||
//=========================================================
|
||||
// Chicken
|
||||
//=========================================================
|
||||
|
||||
#include "extdll.h"
|
||||
#include "util.h"
|
||||
#include "cbase.h"
|
||||
#include "monsters.h"
|
||||
#include "schedule.h"
|
||||
#include "soundent.h"
|
||||
#include "decals.h"
|
||||
|
||||
#define CHICKEN_IDLE 0
|
||||
#define CHICKEN_BORED 1
|
||||
#define CHICKEN_SCARED_BY_ENT 2
|
||||
#define CHICKEN_EAT 3
|
||||
#define CHICKEN_CROW 4
|
||||
|
||||
//=========================================================
|
||||
// Monster's Anim Events Go Here
|
||||
//=========================================================
|
||||
|
||||
#include "Chicken.h"
|
||||
|
||||
//=========================================================
|
||||
// Furniture - this is the cool comment I cut-and-pasted
|
||||
//=========================================================
|
||||
|
||||
LINK_ENTITY_TO_CLASS( monster_chicken_feathers, CChickenFeathers );
|
||||
|
||||
|
||||
//=========================================================
|
||||
// Furniture is killed
|
||||
//=========================================================
|
||||
void CChickenFeathers :: Die ( void )
|
||||
{
|
||||
SetThink ( SUB_Remove );
|
||||
SetNextThink(0);
|
||||
}
|
||||
|
||||
//=========================================================
|
||||
// This used to have something to do with bees flying, but
|
||||
// now it only initializes moving furniture in scripted sequences
|
||||
//=========================================================
|
||||
void CChickenFeathers :: Spawn( )
|
||||
{
|
||||
if (pev->model)
|
||||
PRECACHE_MODEL((char*)STRING(pev->model)); //LRC
|
||||
else
|
||||
PRECACHE_MODEL("models/feathers.mdl");
|
||||
|
||||
if (pev->model)
|
||||
SET_MODEL(ENT(pev), STRING(pev->model)); //LRC
|
||||
else
|
||||
SET_MODEL(ENT(pev), "models/feathers.mdl");
|
||||
|
||||
pev->movetype = MOVETYPE_NONE;
|
||||
pev->solid = SOLID_NOT;
|
||||
pev->health = 80000;
|
||||
pev->takedamage = DAMAGE_AIM;
|
||||
pev->effects = 0;
|
||||
pev->yaw_speed = 0;
|
||||
pev->sequence = 0;
|
||||
pev->frame = 0;
|
||||
|
||||
// pev->nextthink += 1.0;
|
||||
// SetThink (WalkMonsterDelay);
|
||||
|
||||
ResetSequenceInfo( );
|
||||
pev->frame = 0;
|
||||
MonsterInit();
|
||||
// so it doesn't get affected by things...
|
||||
pev->deadflag = DEAD_DEAD;
|
||||
}
|
||||
|
||||
//=========================================================
|
||||
// ID's Furniture as neutral (noone will attack it)
|
||||
//=========================================================
|
||||
int CChickenFeathers::Classify ( void )
|
||||
{
|
||||
return m_iClass?m_iClass:CLASS_NONE;
|
||||
}
|
||||
|
||||
|
||||
//////////////////////////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
|
||||
LINK_ENTITY_TO_CLASS( monster_chicken, CChicken );
|
||||
|
||||
TYPEDESCRIPTION CChicken::m_SaveData[] =
|
||||
{
|
||||
DEFINE_FIELD( CChicken, m_iszGibModel, FIELD_STRING ),
|
||||
};
|
||||
|
||||
IMPLEMENT_SAVERESTORE( CChicken, CBaseMonster );
|
||||
|
||||
//=========================================================
|
||||
// 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 CChicken :: ISoundMask ( void )
|
||||
{
|
||||
return bits_SOUND_COMBAT | bits_SOUND_DANGER;
|
||||
}
|
||||
|
||||
//=========================================================
|
||||
// Classify - indicates this monster's place in the
|
||||
// relationship table.
|
||||
//=========================================================
|
||||
int CChicken :: Classify ( void )
|
||||
{
|
||||
return m_iClass?m_iClass:CLASS_MUNDANE_PREY;
|
||||
}
|
||||
|
||||
//=========================================================
|
||||
// SetYawSpeed - allows each sequence to have a different
|
||||
// turn rate associated with it.
|
||||
//=========================================================
|
||||
void CChicken :: SetYawSpeed ( void )
|
||||
{
|
||||
int ys;
|
||||
|
||||
ys = 120;
|
||||
|
||||
pev->yaw_speed = ys;
|
||||
}
|
||||
|
||||
//=========================================================
|
||||
// Spawn
|
||||
//=========================================================
|
||||
void CChicken :: Spawn()
|
||||
{
|
||||
Precache( );
|
||||
|
||||
if (pev->model)
|
||||
SET_MODEL(ENT(pev), STRING(pev->model)); //LRC
|
||||
else
|
||||
SET_MODEL(ENT(pev), "models/chicken.mdl");
|
||||
UTIL_SetSize( pev, Vector( -8, -8, 0 ), Vector( 8, 8, 8 ) );
|
||||
|
||||
pev->solid = SOLID_SLIDEBOX;
|
||||
pev->movetype = MOVETYPE_STEP;
|
||||
m_bloodColor = BLOOD_COLOR_RED;
|
||||
pev->effects = 0;
|
||||
pev->health = 4;
|
||||
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_iMode = CHICKEN_IDLE;
|
||||
m_flNextSmellTime = gpGlobals->time;
|
||||
}
|
||||
|
||||
//=========================================================
|
||||
// Precache - precaches all resources this monster needs
|
||||
//=========================================================
|
||||
void CChicken :: Precache()
|
||||
{
|
||||
if (pev->model)
|
||||
PRECACHE_MODEL((char*)STRING(pev->model)); //LRC
|
||||
else
|
||||
PRECACHE_MODEL("models/chicken.mdl");
|
||||
|
||||
PRECACHE_SOUND("chicken/chick_cluck1.wav");
|
||||
PRECACHE_SOUND("chicken/chick_cluck2.wav");
|
||||
PRECACHE_SOUND("chicken/chick_cluck3.wav");
|
||||
PRECACHE_SOUND("chicken/chick_scream.wav");
|
||||
|
||||
m_iszGibModel = ALLOC_STRING("models/chickengibs.mdl");
|
||||
PRECACHE_MODEL( "models/chickengibs.mdl" ); //LRC
|
||||
PRECACHE_MODEL( "models/feathers.mdl" ); //LRC
|
||||
}
|
||||
|
||||
|
||||
//=========================================================
|
||||
// Killed.
|
||||
//=========================================================
|
||||
void CChicken :: Killed( entvars_t *pevAttacker, int iGib )
|
||||
{
|
||||
pev->solid = SOLID_NOT;
|
||||
|
||||
CSoundEnt::InsertSound ( bits_SOUND_WORLD, pev->origin, 128, 1 );
|
||||
|
||||
CBaseEntity *pOwner = CBaseEntity::Instance(pev->owner);
|
||||
if ( pOwner )
|
||||
{
|
||||
pOwner->DeathNotice( pev );
|
||||
}
|
||||
CBaseMonster::Killed( pevAttacker, GIB_ALWAYS );
|
||||
|
||||
// send some faethers flying...
|
||||
CBaseEntity *pNew = Create( "monster_chicken_feathers", pev->origin, pev->angles );
|
||||
CBaseMonster *pNewMonster = pNew->MyMonsterPointer( );
|
||||
pNew->pev->spawnflags |= 1;
|
||||
|
||||
UTIL_Remove( this );
|
||||
}
|
||||
|
||||
//=========================================================
|
||||
// MonsterThink, overridden for chickens.
|
||||
//=========================================================
|
||||
void CChicken :: MonsterThink( void )
|
||||
{
|
||||
if ( FNullEnt( FIND_CLIENT_IN_PVS( edict() ) ) )
|
||||
SetNextThink(RANDOM_FLOAT(1,1.5));
|
||||
else
|
||||
{
|
||||
SetNextThink(0.l);// keep monster thinking
|
||||
}
|
||||
|
||||
float flInterval = StudioFrameAdvance( ); // animate
|
||||
|
||||
switch ( m_iMode )
|
||||
{
|
||||
case CHICKEN_CROW:
|
||||
if (m_fSequenceFinished)
|
||||
{
|
||||
SetActivity ( ACT_IDLE );
|
||||
m_iMode = CHICKEN_IDLE;
|
||||
}
|
||||
break;
|
||||
case CHICKEN_BORED:
|
||||
Look( 100 );
|
||||
if (HasConditions(bits_COND_SEE_FEAR | bits_COND_SEE_CLIENT))
|
||||
{
|
||||
// if see something scary
|
||||
//ALERT ( at_aiconsole, "Scared\n" );
|
||||
Eat( 30 + ( RANDOM_LONG(0,14) ) );// chicken will ignore food for 30 to 45 seconds
|
||||
PickNewDest( CHICKEN_SCARED_BY_ENT );
|
||||
SetActivity ( ACT_RUN );
|
||||
EMIT_SOUND_DYN(ENT(pev), CHAN_VOICE, "chicken/chick_scream.wav", 0.8, ATTN_NORM, 0, 80 + RANDOM_LONG(0,39) );
|
||||
}
|
||||
break;
|
||||
case CHICKEN_IDLE:
|
||||
case CHICKEN_EAT:
|
||||
{
|
||||
// Cower when you hear something scary
|
||||
if ( HasConditions( bits_COND_HEAR_SOUND ) )
|
||||
{
|
||||
CSound *pSound;
|
||||
pSound = PBestSound();
|
||||
|
||||
ASSERT( pSound != NULL );
|
||||
if ( pSound )
|
||||
{
|
||||
if ( pSound->m_iType & bits_SOUND_COMBAT )
|
||||
{
|
||||
PickNewDest( CHICKEN_SCARED_BY_ENT );
|
||||
SetActivity ( ACT_RUN );
|
||||
EMIT_SOUND_DYN(ENT(pev), CHAN_VOICE, "chicken/chick_scream.wav", 0.8, ATTN_NORM, 0, 80 + RANDOM_LONG(0,39) );
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
// if not moving, sample environment to see if anything scary is around. Do a radius search 'look' at random.
|
||||
if ( RANDOM_LONG(0,3) < 5 )
|
||||
{
|
||||
Look( 150 );
|
||||
if (HasConditions(bits_COND_SEE_FEAR | bits_COND_SEE_CLIENT))
|
||||
{
|
||||
// if see something scary
|
||||
//ALERT ( at_aiconsole, "Scared\n" );
|
||||
Eat( 30 + ( RANDOM_LONG(0,14) ) );// chicken will ignore food for 30 to 45 seconds
|
||||
PickNewDest( CHICKEN_SCARED_BY_ENT );
|
||||
SetActivity ( ACT_RUN );
|
||||
EMIT_SOUND_DYN(ENT(pev), CHAN_VOICE, "chicken/chick_scream.wav", 0.8, ATTN_NORM, 0, 80 + RANDOM_LONG(0,39) );
|
||||
}
|
||||
else if ( RANDOM_LONG(0,100) < 5 )
|
||||
{
|
||||
// if chicken doesn't see anything, there's still a good chance that it will move. (boredom)
|
||||
//ALERT ( at_aiconsole, "Bored\n" );
|
||||
PickNewDest( CHICKEN_BORED );
|
||||
SetActivity ( ACT_WALK );
|
||||
|
||||
if ( m_iMode == CHICKEN_EAT )
|
||||
{
|
||||
// chicken 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!
|
||||
else if ( m_iMode == CHICKEN_IDLE )
|
||||
{
|
||||
float f = RANDOM_FLOAT(0,1.0);
|
||||
if ( f < 0.1 )
|
||||
{
|
||||
// crow
|
||||
SetActivity ( ACT_EXCITED );
|
||||
m_iMode = CHICKEN_CROW;
|
||||
}
|
||||
else if ( f < 0.4 )
|
||||
{
|
||||
// peck at ground...
|
||||
SetActivity ( ACT_EAT );
|
||||
m_iMode = CHICKEN_EAT;
|
||||
}
|
||||
else
|
||||
{
|
||||
SetActivity ( ACT_IDLE );
|
||||
}
|
||||
}
|
||||
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if (( m_iMode != CHICKEN_SCARED_BY_ENT ) && ( m_iMode != CHICKEN_CROW ))
|
||||
{
|
||||
if ( RANDOM_FLOAT(0,1.0) < 0.05 )
|
||||
{
|
||||
switch (RANDOM_LONG(0,2))
|
||||
{
|
||||
case 0:
|
||||
EMIT_SOUND_DYN(ENT(pev), CHAN_VOICE, "chicken/chick_cluck1.wav", 0.4, ATTN_NORM, 0, 80 + RANDOM_LONG(0,39) );
|
||||
break;
|
||||
case 1:
|
||||
EMIT_SOUND_DYN(ENT(pev), CHAN_VOICE, "chicken/chick_cluck2.wav", 0.4, ATTN_NORM, 0, 80 + RANDOM_LONG(0,39) );
|
||||
break;
|
||||
default:
|
||||
EMIT_SOUND_DYN(ENT(pev), CHAN_VOICE, "chicken/chick_cluck3.wav", 0.4, ATTN_NORM, 0, 80 + RANDOM_LONG(0,39) );
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if ( m_flGroundSpeed != 0 )
|
||||
{
|
||||
Move( flInterval );
|
||||
}
|
||||
}
|
||||
|
||||
//=========================================================
|
||||
// Picks a new spot for chicken to run to.(
|
||||
//=========================================================
|
||||
void CChicken :: PickNewDest ( int iCondition )
|
||||
{
|
||||
Vector vecNewDir;
|
||||
Vector vecDest;
|
||||
float flDist;
|
||||
|
||||
m_iMode = iCondition;
|
||||
|
||||
do
|
||||
{
|
||||
// picks a random spot, requiring that it be at least 128 units away
|
||||
// else, the chicken 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() < 64 );
|
||||
|
||||
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 );
|
||||
}
|
||||
|
||||
//=========================================================
|
||||
// chickens's move function
|
||||
//=========================================================
|
||||
void CChicken :: 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 chicken to overshoot)
|
||||
if ( flWaypointDist <= m_flGroundSpeed * flInterval )
|
||||
{
|
||||
// take truncated step and stop
|
||||
|
||||
SetActivity ( ACT_IDLE );
|
||||
|
||||
m_iMode = CHICKEN_IDLE;
|
||||
}
|
||||
}
|
||||
|
||||
//=========================================================
|
||||
// AI Schedules Specific to this monster
|
||||
//=========================================================
|
||||
|
|
@ -0,0 +1,48 @@
|
|||
|
||||
#ifndef CHICKEN_H
|
||||
#define CHICKEN_H
|
||||
|
||||
|
||||
class CChickenFeathers : public CBaseMonster
|
||||
{
|
||||
public:
|
||||
void Spawn ( void );
|
||||
void Die( void );
|
||||
int Classify ( void );
|
||||
virtual int ObjectCaps( void ) { return (CBaseMonster :: ObjectCaps() & ~FCAP_ACROSS_TRANSITION); }
|
||||
};
|
||||
|
||||
|
||||
|
||||
|
||||
class CChicken : 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 Killed( entvars_t *pevAttacker, int iGib );
|
||||
|
||||
float m_flNextSmellTime;
|
||||
int Classify ( void );
|
||||
int ISoundMask ( void );
|
||||
|
||||
virtual int HasCustomGibs( void ) { return m_iszGibModel; }
|
||||
|
||||
int m_iszGibModel;
|
||||
|
||||
virtual int Save( CSave &save );
|
||||
virtual int Restore( CRestore &restore );
|
||||
static TYPEDESCRIPTION m_SaveData[];
|
||||
|
||||
// UNDONE: These don't necessarily need to be save/restored, but if we add more data, it may
|
||||
int m_iMode;
|
||||
// -----------------------------
|
||||
};
|
||||
|
||||
|
||||
#endif
|
||||
|
|
@ -0,0 +1,216 @@
|
|||
|
||||
#include "extdll.h"
|
||||
#include "util.h"
|
||||
#include "cbase.h"
|
||||
#include "monsters.h"
|
||||
#include "schedule.h"
|
||||
#include "climbingmonster.h"
|
||||
|
||||
|
||||
float CClimbingMonster :: ChangeYaw( int speed )
|
||||
{
|
||||
if (mbIsClimbing)
|
||||
{
|
||||
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 );
|
||||
}
|
||||
|
||||
BOOL CClimbingMonster::FGetNodeRoute ( Vector vecDest )
|
||||
{
|
||||
// if we are using the nodes, then we may be climbing
|
||||
mbIsClimbing = TRUE;
|
||||
|
||||
// while calculating the node route, assume the monster can fly.
|
||||
// this will allow it to use the info_node_air nodes.
|
||||
SetBits( pev->flags, FL_FLY );
|
||||
mOldType = pev->movetype;
|
||||
pev->movetype = MOVETYPE_FLY;
|
||||
m_flGroundSpeed = 100;
|
||||
m_afCapability |= bits_CAP_FLY;
|
||||
|
||||
BOOL b = CBaseMonster::FGetNodeRoute( vecDest );
|
||||
|
||||
return b;
|
||||
}
|
||||
|
||||
//
|
||||
// We may need to override Move, in order to reset the FL_FLY flag again.
|
||||
//
|
||||
|
||||
BOOL CClimbingMonster::BuildRoute ( const Vector &vecGoal, int iMoveFlag, CBaseEntity *pTarget )
|
||||
{
|
||||
// by default, we are not climbing
|
||||
mbIsClimbing = FALSE;
|
||||
pev->movetype = mOldType;
|
||||
ClearBits( pev->flags, FL_FLY );
|
||||
m_velocity = Vector(0,0,0);
|
||||
m_afCapability &= ~bits_CAP_FLY;
|
||||
|
||||
return CBaseMonster::BuildRoute ( vecGoal, iMoveFlag, pTarget );
|
||||
}
|
||||
|
||||
BOOL CClimbingMonster::FRefreshRoute( void )
|
||||
{
|
||||
// by default, we are not climbing
|
||||
mbIsClimbing = FALSE;
|
||||
pev->movetype = mOldType;
|
||||
ClearBits( pev->flags, FL_FLY );
|
||||
m_afCapability &= ~bits_CAP_FLY;
|
||||
|
||||
return CBaseMonster::FRefreshRoute();
|
||||
}
|
||||
|
||||
int CClimbingMonster :: CheckLocalMove ( const Vector &vecStart, const Vector &vecEnd, CBaseEntity *pTarget, float *pflDist )
|
||||
{
|
||||
// if we are climbing, then we are effectively flying (like the controller)
|
||||
if (mbIsClimbing)
|
||||
{
|
||||
TraceResult tr;
|
||||
|
||||
UTIL_TraceHull( vecStart + Vector( 0, 0, 32), vecEnd + Vector( 0, 0, 32), dont_ignore_monsters, human_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 );
|
||||
// (tr.fStartSolid || tr.flFraction < 1.0)
|
||||
if (tr.flFraction < 1.0)
|
||||
{
|
||||
if ( pTarget && pTarget->edict() == gpGlobals->trace_ent )
|
||||
return LOCALMOVE_VALID;
|
||||
return LOCALMOVE_INVALID;
|
||||
}
|
||||
|
||||
return LOCALMOVE_VALID;
|
||||
}
|
||||
else // otherwise we walk, just like anyone else
|
||||
{
|
||||
return CBaseMonster::CheckLocalMove( vecStart, vecEnd, pTarget, pflDist );
|
||||
}
|
||||
}
|
||||
|
||||
BOOL CClimbingMonster:: ShouldAdvanceRoute( float flWaypointDist )
|
||||
{
|
||||
if (mbIsClimbing)
|
||||
{
|
||||
// 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;
|
||||
}
|
||||
else
|
||||
{
|
||||
return CBaseMonster::ShouldAdvanceRoute(flWaypointDist);
|
||||
}
|
||||
}
|
||||
|
||||
void CClimbingMonster :: Move( float flInterval )
|
||||
{
|
||||
if (mbIsClimbing)
|
||||
{
|
||||
if ( pev->movetype == MOVETYPE_FLY )
|
||||
m_flGroundSpeed = m_climbSpeed;
|
||||
}
|
||||
CBaseMonster::Move( flInterval );
|
||||
}
|
||||
|
||||
void CClimbingMonster::MoveExecute( CBaseEntity *pTargetEnt, const Vector &vecDir, float flInterval )
|
||||
{
|
||||
if (mbIsClimbing)
|
||||
{
|
||||
if ( pev->movetype == MOVETYPE_FLY )
|
||||
{
|
||||
if ( gpGlobals->time - m_stopTime > 1.0 )
|
||||
{
|
||||
if ( m_IdealActivity != m_movementActivity )
|
||||
{
|
||||
m_IdealActivity = m_movementActivity;
|
||||
m_flGroundSpeed = m_climbSpeed = 200;
|
||||
}
|
||||
}
|
||||
// hardcoded momentum
|
||||
Vector vecMove = pev->origin + (( vecDir + (m_vecTravel * 2.5) ).Normalize() * (m_flGroundSpeed * flInterval));
|
||||
|
||||
if ( m_IdealActivity != m_movementActivity )
|
||||
{
|
||||
m_climbSpeed = UTIL_Approach( 100, m_climbSpeed, 75 * gpGlobals->frametime );
|
||||
if ( m_climbSpeed < 100 )
|
||||
m_stopTime = gpGlobals->time;
|
||||
}
|
||||
else
|
||||
m_climbSpeed = UTIL_Approach( 20, m_climbSpeed, 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 );
|
||||
}
|
||||
}
|
||||
|
||||
void CClimbingMonster :: Stop( void )
|
||||
{
|
||||
if (mbIsClimbing)
|
||||
{
|
||||
Activity stopped = GetStoppedActivity();
|
||||
if ( m_IdealActivity != stopped )
|
||||
{
|
||||
m_climbSpeed = 0;
|
||||
m_IdealActivity = stopped;
|
||||
}
|
||||
pev->angles.z = 0;
|
||||
pev->angles.x = 0;
|
||||
m_vecTravel = g_vecZero;
|
||||
}
|
||||
else
|
||||
{
|
||||
CBaseMonster::Stop();
|
||||
}
|
||||
}
|
||||
|
||||
void CClimbingMonster :: Killed( entvars_t *pevAttacker, int iGib )
|
||||
{
|
||||
if (mbIsClimbing)
|
||||
{
|
||||
pev->movetype = MOVETYPE_STEP;
|
||||
ClearBits( pev->flags, FL_ONGROUND );
|
||||
pev->angles.z = 0;
|
||||
pev->angles.x = 0;
|
||||
}
|
||||
CBaseMonster::Killed( pevAttacker, iGib );
|
||||
}
|
|
@ -0,0 +1,33 @@
|
|||
|
||||
#ifndef CLIMBINGMONSTER_H
|
||||
#define CLIMBINGMONSTER_H
|
||||
|
||||
class CClimbingMonster : public CBaseMonster
|
||||
{
|
||||
public:
|
||||
float ChangeYaw( int speed );
|
||||
virtual BOOL FGetNodeRoute ( Vector vecDest );
|
||||
virtual BOOL BuildRoute ( const Vector &vecGoal, int iMoveFlag, CBaseEntity *pTarget );
|
||||
BOOL FRefreshRoute( void );
|
||||
BOOL ShouldAdvanceRoute( float flWaypointDist );
|
||||
virtual int CheckLocalMove ( const Vector &vecStart, const Vector &vecEnd, CBaseEntity *pTarget, float *pflDist );// check validity of a straight move through space
|
||||
void Move( float flInterval = 0.1 );
|
||||
virtual void MoveExecute( CBaseEntity *pTargetEnt, const Vector &vecDir, float flInterval );
|
||||
void Stop( void );
|
||||
void Killed( entvars_t *pevAttacker, int iGib );
|
||||
|
||||
protected:
|
||||
BOOL mbIsClimbing;
|
||||
|
||||
int mOldType;
|
||||
|
||||
Vector m_vecTravel; // Current direction
|
||||
Vector m_velocity;
|
||||
float m_climbSpeed; // Current climb speed
|
||||
float m_stopTime; // Last time we stopped (to avoid switching states too soon)
|
||||
};
|
||||
|
||||
|
||||
#endif
|
||||
|
||||
|
|
@ -0,0 +1,133 @@
|
|||
/***
|
||||
*
|
||||
* Copyright (c) 1999, 2000 Valve LLC. All rights reserved.
|
||||
*
|
||||
* This product contains software technology licensed from Id
|
||||
* Software, Inc. ("Id Technology"). Id Technology (c) 1996 Id Software, Inc.
|
||||
* All Rights Reserved.
|
||||
*
|
||||
* This source code contains proprietary and confidential information of
|
||||
* Valve LLC and its suppliers. Access to this code is restricted to
|
||||
* persons who have executed a written SDK license with Valve. Any access,
|
||||
* use or distribution of this code by or to any unlicensed person is illegal.
|
||||
*
|
||||
****/
|
||||
//=========================================================
|
||||
// Chicken
|
||||
//=========================================================
|
||||
|
||||
#include "extdll.h"
|
||||
#include "util.h"
|
||||
#include "cbase.h"
|
||||
#include "monsters.h"
|
||||
#include "schedule.h"
|
||||
#include "soundent.h"
|
||||
#include "decals.h"
|
||||
|
||||
//=========================================================
|
||||
// Monster's Anim Events Go Here
|
||||
//=========================================================
|
||||
|
||||
#include "Cow.h"
|
||||
|
||||
|
||||
LINK_ENTITY_TO_CLASS( monster_cow, CCow );
|
||||
|
||||
TYPEDESCRIPTION CCow::m_SaveData[] =
|
||||
{
|
||||
DEFINE_FIELD( CCow, m_iszGibModel, FIELD_STRING ),
|
||||
};
|
||||
|
||||
IMPLEMENT_SAVERESTORE( CCow, CBaseMonster );
|
||||
|
||||
//=========================================================
|
||||
// Classify - indicates this monster's place in the
|
||||
// relationship table.
|
||||
//=========================================================
|
||||
int CCow :: Classify ( void )
|
||||
{
|
||||
return CLASS_NONE;
|
||||
}
|
||||
|
||||
//=========================================================
|
||||
// Spawn
|
||||
//=========================================================
|
||||
void CCow :: Spawn()
|
||||
{
|
||||
Precache( );
|
||||
|
||||
if (pev->model)
|
||||
SET_MODEL(ENT(pev), STRING(pev->model)); //LRC
|
||||
else
|
||||
SET_MODEL(ENT(pev), "models/cow.mdl");
|
||||
UTIL_SetSize( pev, Vector( -48, -16, 0 ), Vector( 48, 16, 72 ) );
|
||||
|
||||
pev->solid = SOLID_SLIDEBOX;
|
||||
pev->movetype = MOVETYPE_STEP;
|
||||
m_bloodColor = BLOOD_COLOR_RED;
|
||||
pev->effects = 0;
|
||||
if (pev->health == 0)
|
||||
pev->health = 20;
|
||||
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;
|
||||
}
|
||||
|
||||
//=========================================================
|
||||
// Precache - precaches all resources this monster needs
|
||||
//=========================================================
|
||||
void CCow :: Precache()
|
||||
{
|
||||
if (pev->model)
|
||||
PRECACHE_MODEL((char*)STRING(pev->model)); //LRC
|
||||
else
|
||||
PRECACHE_MODEL("models/cow.mdl");
|
||||
|
||||
m_iszGibModel = ALLOC_STRING("models/horsegibs.mdl");
|
||||
PRECACHE_MODEL( "models/horsegibs.mdl" );
|
||||
|
||||
PRECACHE_SOUND("cow/cowmoo.wav");
|
||||
PRECACHE_SOUND("cow/cowdie.wav");
|
||||
}
|
||||
|
||||
//=========================================================
|
||||
// Killed.
|
||||
//=========================================================
|
||||
void CCow :: IdleSound( void )
|
||||
{
|
||||
if (RANDOM_LONG(0,5) < 2)
|
||||
{
|
||||
// Play a random idle sound
|
||||
EMIT_SOUND_DYN ( ENT(pev), CHAN_VOICE, "cow/cowmoo.wav", 1.0, ATTN_NORM, 0, 100 );
|
||||
}
|
||||
}
|
||||
|
||||
//=========================================================
|
||||
// Killed.
|
||||
//=========================================================
|
||||
void CCow :: Killed( entvars_t *pevAttacker, int iGib )
|
||||
{
|
||||
// pev->solid = SOLID_NOT;
|
||||
|
||||
// CSoundEnt::InsertSound ( bits_SOUND_WORLD, pev->origin, 128, 1 );
|
||||
|
||||
EMIT_SOUND_DYN(ENT(pev), CHAN_VOICE, "cow/cowdie.wav", 0.8, ATTN_NORM, 0, 80 + RANDOM_LONG(0,39) );
|
||||
|
||||
// CBaseEntity *pOwner = CBaseEntity::Instance(pev->owner);
|
||||
// if ( pOwner )
|
||||
// {
|
||||
// pOwner->DeathNotice( pev );
|
||||
// }
|
||||
// UTIL_Remove( this );
|
||||
CBaseMonster::Killed(pevAttacker, GIB_ALWAYS);
|
||||
}
|
||||
|
||||
//=========================================================
|
||||
// AI Schedules Specific to this monster
|
||||
//=========================================================
|
||||
|
|
@ -0,0 +1,26 @@
|
|||
|
||||
#ifndef COW_H
|
||||
#define COW_H
|
||||
|
||||
class CCow : public CBaseMonster
|
||||
{
|
||||
public:
|
||||
void Spawn( void );
|
||||
void Precache( void );
|
||||
void Killed( entvars_t *pevAttacker, int iGib );
|
||||
void IdleSound( void );
|
||||
|
||||
int Classify ( void );
|
||||
|
||||
virtual int HasCustomGibs( void ) { return m_iszGibModel; }
|
||||
|
||||
int m_iszGibModel;
|
||||
|
||||
virtual int Save( CSave &save );
|
||||
virtual int Restore( CRestore &restore );
|
||||
static TYPEDESCRIPTION m_SaveData[];
|
||||
};
|
||||
|
||||
|
||||
#endif
|
||||
|
File diff suppressed because it is too large
Load Diff
|
@ -0,0 +1,62 @@
|
|||
|
||||
#ifndef CTHONIAN_H
|
||||
#define CTHONIAN_H
|
||||
|
||||
class CCthonianSpit : public CBaseEntity
|
||||
{
|
||||
public:
|
||||
void Spawn( void );
|
||||
|
||||
static void Shoot( entvars_t *pevOwner, Vector vecStart, Vector vecVelocity );
|
||||
void Touch( CBaseEntity *pOther );
|
||||
void EXPORT Animate( void );
|
||||
|
||||
virtual int Save( CSave &save );
|
||||
virtual int Restore( CRestore &restore );
|
||||
static TYPEDESCRIPTION m_SaveData[];
|
||||
|
||||
int m_maxFrame;
|
||||
};
|
||||
|
||||
/////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
class CCthonian : public CBaseMonster
|
||||
{
|
||||
public:
|
||||
void Spawn( void );
|
||||
void Precache( void );
|
||||
void SetYawSpeed( void );
|
||||
int ISoundMask( void );
|
||||
int Classify ( void );
|
||||
void HandleAnimEvent( MonsterEvent_t *pEvent );
|
||||
void IdleSound( void );
|
||||
void PainSound( void );
|
||||
void DeathSound( void );
|
||||
void AlertSound ( void );
|
||||
void AttackSound( void );
|
||||
void StartTask ( Task_t *pTask );
|
||||
void RunTask ( Task_t *pTask );
|
||||
BOOL CheckMeleeAttack1 ( float flDot, float flDist );
|
||||
BOOL CheckMeleeAttack2 ( float flDot, float flDist );
|
||||
BOOL CheckRangeAttack1 ( float flDot, float flDist );
|
||||
void RunAI( void );
|
||||
BOOL FValidateHintType ( short sHint );
|
||||
Schedule_t *GetSchedule( void );
|
||||
Schedule_t *GetScheduleOfType ( int Type );
|
||||
int TakeDamage( entvars_t *pevInflictor, entvars_t *pevAttacker, float flDamage, int bitsDamageType );
|
||||
int IRelationship ( CBaseEntity *pTarget );
|
||||
int IgnoreConditions ( void );
|
||||
MONSTERSTATE GetIdealState ( void );
|
||||
|
||||
int Save( CSave &save );
|
||||
int Restore( CRestore &restore );
|
||||
|
||||
CUSTOM_SCHEDULES;
|
||||
static TYPEDESCRIPTION m_SaveData[];
|
||||
|
||||
float m_flLastHurtTime;// we keep track of this, because if something hurts a squid, it will forget about its love of headcrabs for a while.
|
||||
float m_flNextSpitTime;// last time the bullsquid used the spit attack.
|
||||
};
|
||||
|
||||
|
||||
#endif
|
|
@ -0,0 +1,328 @@
|
|||
/***
|
||||
*
|
||||
* Copyright (c) 1999, 2000 Valve LLC. All rights reserved.
|
||||
*
|
||||
* This product contains software technology licensed from Id
|
||||
* Software, Inc. ("Id Technology"). Id Technology (c) 1996 Id Software, Inc.
|
||||
* All Rights Reserved.
|
||||
*
|
||||
* Use, distribution, and modification of this source code and/or resulting
|
||||
* object code is restricted to non-commercial enhancements to products from
|
||||
* Valve LLC. All other use, distribution, or modification is prohibited
|
||||
* without written permission from Valve LLC.
|
||||
*
|
||||
****/
|
||||
|
||||
#include "extdll.h"
|
||||
#include "util.h"
|
||||
#include "cbase.h"
|
||||
#include "monsters.h"
|
||||
#include "weapons.h"
|
||||
#include "nodes.h"
|
||||
#include "player.h"
|
||||
#include "gamerules.h"
|
||||
|
||||
|
||||
#define KNIFE_BODYHIT_VOLUME 128
|
||||
#define KNIFE_WALLHIT_VOLUME 512
|
||||
|
||||
#include "Dagger.h"
|
||||
|
||||
LINK_ENTITY_TO_CLASS( weapon_knife, CKnife );
|
||||
|
||||
|
||||
|
||||
enum knife_e {
|
||||
KNIFE_IDLE = 0,
|
||||
KNIFE_DRAW,
|
||||
KNIFE_HOLSTER,
|
||||
KNIFE_ATTACK1,
|
||||
KNIFE_ATTACK2
|
||||
};
|
||||
|
||||
|
||||
void CKnife::Spawn( )
|
||||
{
|
||||
Precache( );
|
||||
m_iId = WEAPON_KNIFE;
|
||||
SET_MODEL(ENT(pev), "models/w_knife.mdl");
|
||||
m_iClip = -1;
|
||||
|
||||
FallInit();// get ready to fall down.
|
||||
}
|
||||
|
||||
|
||||
void CKnife::Precache( void )
|
||||
{
|
||||
PRECACHE_MODEL("models/v_knife.mdl");
|
||||
PRECACHE_MODEL("models/w_knife.mdl");
|
||||
// PRECACHE_MODEL("models/p_knife.mdl");
|
||||
PRECACHE_SOUND("weapons/cbar_hit1.wav");
|
||||
PRECACHE_SOUND("weapons/cbar_hit2.wav");
|
||||
PRECACHE_SOUND("weapons/cbar_hitbod1.wav");
|
||||
PRECACHE_SOUND("weapons/cbar_hitbod2.wav");
|
||||
PRECACHE_SOUND("weapons/cbar_hitbod3.wav");
|
||||
PRECACHE_SOUND("weapons/cbar_miss1.wav");
|
||||
}
|
||||
|
||||
int CKnife::GetItemInfo(ItemInfo *p)
|
||||
{
|
||||
p->pszName = STRING(pev->classname);
|
||||
p->pszAmmo1 = NULL;
|
||||
p->iMaxAmmo1 = -1;
|
||||
p->pszAmmo2 = NULL;
|
||||
p->iMaxAmmo2 = -1;
|
||||
p->iMaxClip = WEAPON_NOCLIP;
|
||||
p->iSlot = 0;
|
||||
p->iPosition = 0;
|
||||
p->iId = WEAPON_KNIFE;
|
||||
p->iWeight = KNIFE_WEIGHT;
|
||||
return 1;
|
||||
}
|
||||
|
||||
|
||||
BOOL CKnife::Deploy( )
|
||||
{
|
||||
return DefaultDeploy( "models/v_knife.mdl", "", KNIFE_DRAW, "knife" );
|
||||
}
|
||||
|
||||
void CKnife::Holster( int skiplocal /* = 0 */ )
|
||||
{
|
||||
m_pPlayer->m_flNextAttack = UTIL_WeaponTimeBase() + 0.5;
|
||||
SendWeaponAnim( KNIFE_HOLSTER );
|
||||
}
|
||||
|
||||
int CKnife::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 CKnife::FindHullIntersection( const Vector &vecSrc, TraceResult &tr, float *mins, float *maxs, edict_t *pEntity )
|
||||
{
|
||||
int i, j, k;
|
||||
float distance;
|
||||
float *minmaxs[2] = {mins, maxs};
|
||||
TraceResult tmpTrace;
|
||||
Vector vecHullEnd = tr.vecEndPos;
|
||||
Vector vecEnd;
|
||||
|
||||
distance = 1e6f;
|
||||
|
||||
vecHullEnd = vecSrc + ((vecHullEnd - vecSrc)*2);
|
||||
UTIL_TraceLine( vecSrc, vecHullEnd, dont_ignore_monsters, pEntity, &tmpTrace );
|
||||
if ( tmpTrace.flFraction < 1.0 )
|
||||
{
|
||||
tr = tmpTrace;
|
||||
return;
|
||||
}
|
||||
|
||||
for ( i = 0; i < 2; i++ )
|
||||
{
|
||||
for ( j = 0; j < 2; j++ )
|
||||
{
|
||||
for ( k = 0; k < 2; k++ )
|
||||
{
|
||||
vecEnd.x = vecHullEnd.x + minmaxs[i][0];
|
||||
vecEnd.y = vecHullEnd.y + minmaxs[j][1];
|
||||
vecEnd.z = vecHullEnd.z + minmaxs[k][2];
|
||||
|
||||
UTIL_TraceLine( vecSrc, vecEnd, dont_ignore_monsters, pEntity, &tmpTrace );
|
||||
if ( tmpTrace.flFraction < 1.0 )
|
||||
{
|
||||
float thisDistance = (tmpTrace.vecEndPos - vecSrc).Length();
|
||||
if ( thisDistance < distance )
|
||||
{
|
||||
tr = tmpTrace;
|
||||
distance = thisDistance;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
void CKnife::PrimaryAttack()
|
||||
{
|
||||
if (! Swing( 1 ))
|
||||
{
|
||||
SetThink( SwingAgain );
|
||||
SetNextThink( 0.5 );
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
void CKnife::SwingAgain( void )
|
||||
{
|
||||
Swing( 0 );
|
||||
DontThink();
|
||||
}
|
||||
|
||||
|
||||
int CKnife::Swing( int fFirst )
|
||||
{
|
||||
int fDidHit = FALSE;
|
||||
|
||||
TraceResult tr;
|
||||
|
||||
UTIL_MakeVectors (m_pPlayer->pev->v_angle);
|
||||
Vector vecSrc = m_pPlayer->GetGunPosition( );
|
||||
Vector vecEnd = vecSrc + gpGlobals->v_forward * 32;
|
||||
|
||||
UTIL_TraceLine( vecSrc, vecEnd, dont_ignore_monsters, ENT( m_pPlayer->pev ), &tr );
|
||||
|
||||
if ( tr.flFraction >= 1.0 )
|
||||
{
|
||||
UTIL_TraceHull( vecSrc, vecEnd, dont_ignore_monsters, head_hull, ENT( m_pPlayer->pev ), &tr );
|
||||
if ( tr.flFraction < 1.0 )
|
||||
{
|
||||
// Calculate the point of intersection of the line (or hull) and the object we hit
|
||||
// This is and approximation of the "best" intersection
|
||||
CBaseEntity *pHit = CBaseEntity::Instance( tr.pHit );
|
||||
if ( !pHit || pHit->IsBSPModel() )
|
||||
FindHullIntersection( vecSrc, tr, VEC_DUCK_HULL_MIN, VEC_DUCK_HULL_MAX, m_pPlayer->edict() );
|
||||
vecEnd = tr.vecEndPos; // This is the point on the actual surface (the hull could have hit space)
|
||||
}
|
||||
}
|
||||
|
||||
if ( tr.flFraction >= 1.0 )
|
||||
{
|
||||
if (fFirst)
|
||||
{
|
||||
// miss
|
||||
switch( (m_iSwing++) % 3 )
|
||||
{
|
||||
case 0:
|
||||
SendWeaponAnim( KNIFE_ATTACK1 ); break;
|
||||
case 1:
|
||||
SendWeaponAnim( KNIFE_ATTACK2 ); break;
|
||||
default:
|
||||
SendWeaponAnim( KNIFE_ATTACK1 ); break;
|
||||
}
|
||||
m_flNextPrimaryAttack = gpGlobals->time + 0.5;
|
||||
// play wiff or swish sound
|
||||
EMIT_SOUND_DYN(ENT(m_pPlayer->pev), CHAN_WEAPON, "weapons/cbar_miss1.wav", 1, ATTN_NORM, 0, 94 + RANDOM_LONG(0,0xF));
|
||||
|
||||
// player "shoot" animation
|
||||
m_pPlayer->SetAnimation( PLAYER_ATTACK1 );
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
// hit
|
||||
fDidHit = TRUE;
|
||||
|
||||
CBaseEntity *pEntity = CBaseEntity::Instance(tr.pHit);
|
||||
|
||||
switch( ((m_iSwing++) % 2) + 1 )
|
||||
{
|
||||
case 0:
|
||||
SendWeaponAnim( KNIFE_ATTACK1); break;
|
||||
case 1:
|
||||
SendWeaponAnim( KNIFE_ATTACK2 ); break;
|
||||
default:
|
||||
SendWeaponAnim( KNIFE_ATTACK1 ); break;
|
||||
}
|
||||
|
||||
// player "shoot" animation
|
||||
m_pPlayer->SetAnimation( PLAYER_ATTACK1 );
|
||||
|
||||
ClearMultiDamage( );
|
||||
if ( (m_flNextPrimaryAttack + 1 < gpGlobals->time) || g_pGameRules->IsMultiplayer() )
|
||||
{
|
||||
// first swing does full damage
|
||||
pEntity->TraceAttack(m_pPlayer->pev, gSkillData.plrDmgKnife, gpGlobals->v_forward, &tr, DMG_SLASH );
|
||||
}
|
||||
else
|
||||
{
|
||||
// subsequent swings do half
|
||||
pEntity->TraceAttack(m_pPlayer->pev, gSkillData.plrDmgKnife / 2, gpGlobals->v_forward, &tr, DMG_SLASH );
|
||||
}
|
||||
ApplyMultiDamage( m_pPlayer->pev, m_pPlayer->pev );
|
||||
|
||||
m_flNextPrimaryAttack = gpGlobals->time + 0.5;
|
||||
|
||||
// play thwack, or dong sound
|
||||
float flVol = 1.0;
|
||||
int fHitWorld = TRUE;
|
||||
|
||||
if (pEntity)
|
||||
{
|
||||
if (pEntity->Classify() != CLASS_NONE && pEntity->Classify() != CLASS_MACHINE)
|
||||
{
|
||||
// play thwack sound
|
||||
switch( RANDOM_LONG(0,2) )
|
||||
{
|
||||
case 0:
|
||||
EMIT_SOUND(ENT(m_pPlayer->pev), CHAN_WEAPON, "weapons/cbar_hitbod1.wav", 1, ATTN_NORM); break;
|
||||
case 1:
|
||||
EMIT_SOUND(ENT(m_pPlayer->pev), CHAN_WEAPON, "weapons/cbar_hitbod2.wav", 1, ATTN_NORM); break;
|
||||
case 2:
|
||||
EMIT_SOUND(ENT(m_pPlayer->pev), CHAN_WEAPON, "weapons/cbar_hitbod3.wav", 1, ATTN_NORM); break;
|
||||
}
|
||||
m_pPlayer->m_iWeaponVolume = KNIFE_BODYHIT_VOLUME;
|
||||
if (!pEntity->IsAlive() )
|
||||
return TRUE;
|
||||
else
|
||||
flVol = 0.1;
|
||||
|
||||
fHitWorld = FALSE;
|
||||
}
|
||||
}
|
||||
|
||||
// play texture hit sound
|
||||
// UNDONE: Calculate the correct point of intersection when we hit with the hull instead of the line
|
||||
|
||||
if (fHitWorld)
|
||||
{
|
||||
float fvolbar = TEXTURETYPE_PlaySound(&tr, vecSrc, vecSrc + (vecEnd-vecSrc)*2, BULLET_PLAYER_KNIFE);
|
||||
|
||||
if ( g_pGameRules->IsMultiplayer() )
|
||||
{
|
||||
// override the volume here, cause we don't play texture sounds in multiplayer,
|
||||
// and fvolbar is going to be 0 from the above call.
|
||||
|
||||
fvolbar = 1;
|
||||
}
|
||||
|
||||
// also play Knife strike
|
||||
switch( RANDOM_LONG(0,1) )
|
||||
{
|
||||
case 0:
|
||||
//UTIL_EmitAmbientSound(ENT(0), ptr->vecEndPos, "weapons/dagger_hit1.wav", fvolbar, ATTN_NORM, 0, 98 + RANDOM_LONG(0,3));
|
||||
EMIT_SOUND_DYN(ENT(m_pPlayer->pev), CHAN_ITEM, "weapons/cbar_hit1.wav", fvolbar, ATTN_NORM, 0, 98 + RANDOM_LONG(0,3));
|
||||
break;
|
||||
case 1:
|
||||
//UTIL_EmitAmbientSound(ENT(0), ptr->vecEndPos, "weapons/dagger_hit2.wav", fvolbar, ATTN_NORM, 0, 98 + RANDOM_LONG(0,3));
|
||||
EMIT_SOUND_DYN(ENT(m_pPlayer->pev), CHAN_ITEM, "weapons/cbar_hit2.wav", fvolbar, ATTN_NORM, 0, 98 + RANDOM_LONG(0,3));
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
// delay the decal a bit
|
||||
m_trHit = tr;
|
||||
SetNextThink( 0.2 );
|
||||
|
||||
m_pPlayer->m_iWeaponVolume = flVol * KNIFE_WALLHIT_VOLUME;
|
||||
}
|
||||
return fDidHit;
|
||||
}
|
||||
|
||||
void CKnife::WeaponIdle( void )
|
||||
{
|
||||
if ( m_flTimeWeaponIdle > UTIL_WeaponTimeBase() )
|
||||
return;
|
||||
|
||||
m_flTimeWeaponIdle = UTIL_WeaponTimeBase() + (30.0 / 15.0) + RANDOM_FLOAT ( 1, 3 );
|
||||
SendWeaponAnim( KNIFE_IDLE, 1 );
|
||||
}
|
||||
|
||||
|
|
@ -0,0 +1,36 @@
|
|||
|
||||
#ifndef DAGGER_H
|
||||
#define DAGGER_H
|
||||
|
||||
|
||||
class CKnife : public CBasePlayerWeapon
|
||||
{
|
||||
public:
|
||||
void Spawn( void );
|
||||
void Precache( void );
|
||||
int iItemSlot( void ) { return 0; }
|
||||
void EXPORT SwingAgain( void );
|
||||
int GetItemInfo(ItemInfo *p);
|
||||
int AddToPlayer( CBasePlayer *pPlayer );
|
||||
void FindHullIntersection( const Vector &vecSrc, TraceResult &tr, float *mins, float *maxs, edict_t *pEntity );
|
||||
|
||||
void PrimaryAttack( void );
|
||||
int Swing( int fFirst );
|
||||
BOOL Deploy( void );
|
||||
void Holster( int skiplocal = 0 );
|
||||
int m_iSwing;
|
||||
TraceResult m_trHit;
|
||||
void WeaponIdle( void );
|
||||
|
||||
virtual BOOL UseDecrement( void )
|
||||
{
|
||||
#if defined( CLIENT_WEAPONS )
|
||||
return TRUE;
|
||||
#else
|
||||
return FALSE;
|
||||
#endif
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
#endif
|
|
@ -0,0 +1,442 @@
|
|||
/***
|
||||
*
|
||||
* Copyright (c) 1999, 2000 Valve LLC. All rights reserved.
|
||||
*
|
||||
* This product contains software technology licensed from Id
|
||||
* Software, Inc. ("Id Technology"). Id Technology (c) 1996 Id Software, Inc.
|
||||
* All Rights Reserved.
|
||||
*
|
||||
* This source code contains proprietary and confidential information of
|
||||
* Valve LLC and its suppliers. Access to this code is restricted to
|
||||
* persons who have executed a written SDK license with Valve. Any access,
|
||||
* use or distribution of this code by or to any unlicensed person is illegal.
|
||||
*
|
||||
****/
|
||||
//=========================================================
|
||||
// DeepOne
|
||||
//=========================================================
|
||||
|
||||
// UNDONE: Don't flinch every time you get hit
|
||||
|
||||
#include "extdll.h"
|
||||
#include "util.h"
|
||||
#include "cbase.h"
|
||||
#include "monsters.h"
|
||||
#include "flyingmonster.h"
|
||||
#include "schedule.h"
|
||||
|
||||
|
||||
//=========================================================
|
||||
// Monster's Anim Events Go Here
|
||||
//=========================================================
|
||||
#define DEEP_ONE_AE_ATTACK_RIGHT 0x01
|
||||
#define DEEP_ONE_AE_ATTACK_LEFT 0x02
|
||||
#define DEEP_ONE_AE_ATTACK_BOTH 0x03
|
||||
|
||||
#define DEEP_ONE_FLINCH_DELAY 2 // at most one flinch every n secs
|
||||
|
||||
|
||||
#include "DeepOne.h"
|
||||
|
||||
|
||||
LINK_ENTITY_TO_CLASS( monster_deepone, CDeepOne );
|
||||
|
||||
const char *CDeepOne::pAttackHitSounds[] =
|
||||
{
|
||||
"zombie/claw_strike1.wav",
|
||||
"zombie/claw_strike2.wav",
|
||||
"zombie/claw_strike3.wav",
|
||||
};
|
||||
|
||||
const char *CDeepOne::pAttackMissSounds[] =
|
||||
{
|
||||
"zombie/claw_miss1.wav",
|
||||
"zombie/claw_miss2.wav",
|
||||
};
|
||||
|
||||
const char *CDeepOne::pAttackSounds[] =
|
||||
{
|
||||
"deepone/do_attack1.wav",
|
||||
"deepone/do_attack2.wav",
|
||||
};
|
||||
|
||||
const char *CDeepOne::pIdleSounds[] =
|
||||
{
|
||||
"deepone/do_idle1.wav",
|
||||
"deepone/do_idle2.wav",
|
||||
};
|
||||
|
||||
const char *CDeepOne::pAlertSounds[] =
|
||||
{
|
||||
"deepone/do_alert1.wav",
|
||||
"deepone/do_alert2.wav",
|
||||
};
|
||||
|
||||
const char *CDeepOne::pPainSounds[] =
|
||||
{
|
||||
"deepone/do_pain1.wav",
|
||||
"deepone/do_pain2.wav",
|
||||
};
|
||||
|
||||
//=========================================================
|
||||
// Classify - indicates this monster's place in the
|
||||
// relationship table.
|
||||
//=========================================================
|
||||
int CDeepOne :: Classify ( void )
|
||||
{
|
||||
return m_iClass?m_iClass:CLASS_ALIEN_MONSTER;
|
||||
}
|
||||
|
||||
//=========================================================
|
||||
// SetYawSpeed - allows each sequence to have a different
|
||||
// turn rate associated with it.
|
||||
//=========================================================
|
||||
void CDeepOne :: SetYawSpeed ( void )
|
||||
{
|
||||
int ys;
|
||||
|
||||
ys = 120;
|
||||
|
||||
pev->yaw_speed = ys;
|
||||
}
|
||||
|
||||
int CDeepOne :: 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 CDeepOne :: PainSound( void )
|
||||
{
|
||||
int pitch = GetVoicePitch() - 5 + RANDOM_LONG(0,10);
|
||||
|
||||
if (RANDOM_LONG(0,5) < 2)
|
||||
EMIT_SOUND_DYN ( ENT(pev), CHAN_VOICE, pPainSounds[ RANDOM_LONG(0,ARRAYSIZE(pPainSounds)-1) ], GetVolume(), ATTN_NORM, 0, pitch );
|
||||
}
|
||||
|
||||
void CDeepOne :: AlertSound( void )
|
||||
{
|
||||
int pitch = GetVoicePitch() - 5 + RANDOM_LONG(0,10);
|
||||
|
||||
EMIT_SOUND_DYN ( ENT(pev), CHAN_VOICE, pAlertSounds[ RANDOM_LONG(0,ARRAYSIZE(pAlertSounds)-1) ], GetVolume(), ATTN_NORM, 0, pitch );
|
||||
}
|
||||
|
||||
void CDeepOne :: IdleSound( void )
|
||||
{
|
||||
int pitch = GetVoicePitch() - 5 + RANDOM_LONG(0,10);
|
||||
|
||||
// Play a random idle sound
|
||||
EMIT_SOUND_DYN ( ENT(pev), CHAN_VOICE, pIdleSounds[ RANDOM_LONG(0,ARRAYSIZE(pIdleSounds)-1) ], GetVolume(), ATTN_NORM, 0, pitch );
|
||||
}
|
||||
|
||||
void CDeepOne :: AttackSound( void )
|
||||
{
|
||||
int pitch = GetVoicePitch() - 5 + RANDOM_LONG(0,10);
|
||||
|
||||
// Play a random attack sound
|
||||
EMIT_SOUND_DYN ( ENT(pev), CHAN_VOICE, pAttackSounds[ RANDOM_LONG(0,ARRAYSIZE(pAttackSounds)-1) ], GetVolume(), ATTN_NORM, 0, pitch );
|
||||
}
|
||||
|
||||
|
||||
//=========================================================
|
||||
// HandleAnimEvent - catches the monster-specific messages
|
||||
// that occur when tagged animation frames are played.
|
||||
//=========================================================
|
||||
|
||||
float CDeepOne :: GetAttackDist( void )
|
||||
{
|
||||
return 70.0;
|
||||
}
|
||||
|
||||
void CDeepOne :: HandleAnimEvent( MonsterEvent_t *pEvent )
|
||||
{
|
||||
double fldist = GetAttackDist();
|
||||
|
||||
switch( pEvent->event )
|
||||
{
|
||||
case DEEP_ONE_AE_ATTACK_RIGHT:
|
||||
{
|
||||
// do stuff for this event.
|
||||
// ALERT( at_console, "Slash right!\n" );
|
||||
CBaseEntity *pHurt = CheckTraceHullAttack( fldist, gSkillData.deeponeDmgOneSlash, 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 DEEP_ONE_AE_ATTACK_LEFT:
|
||||
{
|
||||
// do stuff for this event.
|
||||
// ALERT( at_console, "Slash left!\n" );
|
||||
CBaseEntity *pHurt = CheckTraceHullAttack( fldist, gSkillData.deeponeDmgOneSlash, 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 DEEP_ONE_AE_ATTACK_BOTH:
|
||||
{
|
||||
// do stuff for this event.
|
||||
CBaseEntity *pHurt = CheckTraceHullAttack( fldist, gSkillData.deeponeDmgBothSlash, 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 CDeepOne :: Spawn()
|
||||
{
|
||||
Precache( );
|
||||
|
||||
if (pev->model)
|
||||
SET_MODEL(ENT(pev), STRING(pev->model)); //LRC
|
||||
else
|
||||
SET_MODEL(ENT(pev), "models/deepone.mdl");
|
||||
UTIL_SetSize( pev, VEC_HUMAN_HULL_MIN, VEC_HUMAN_HULL_MAX );
|
||||
|
||||
pev->solid = SOLID_SLIDEBOX;
|
||||
pev->movetype = MOVETYPE_STEP;
|
||||
m_bloodColor = BLOOD_COLOR_GREEN;
|
||||
if (pev->health == 0)
|
||||
pev->health = gSkillData.deeponeHealth;
|
||||
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 | bits_CAP_SWIM | bits_CAP_CLIMB | bits_CAP_JUMP;
|
||||
|
||||
MonsterInit();
|
||||
|
||||
}
|
||||
|
||||
//=========================================================
|
||||
// Precache - precaches all resources this monster needs
|
||||
//=========================================================
|
||||
void CDeepOne :: Precache()
|
||||
{
|
||||
int i;
|
||||
|
||||
if (pev->model)
|
||||
PRECACHE_MODEL((char*)STRING(pev->model)); //LRC
|
||||
else
|
||||
PRECACHE_MODEL("models/deepone.mdl");
|
||||
|
||||
for ( i = 0; i < ARRAYSIZE( pAttackHitSounds ); i++ )
|
||||
PRECACHE_SOUND((char *)pAttackHitSounds[i]);
|
||||
|
||||
for ( i = 0; i < ARRAYSIZE( pAttackMissSounds ); i++ )
|
||||
PRECACHE_SOUND((char *)pAttackMissSounds[i]);
|
||||
|
||||
for ( i = 0; i < ARRAYSIZE( pAttackSounds ); i++ )
|
||||
PRECACHE_SOUND((char *)pAttackSounds[i]);
|
||||
|
||||
for ( i = 0; i < ARRAYSIZE( pIdleSounds ); i++ )
|
||||
PRECACHE_SOUND((char *)pIdleSounds[i]);
|
||||
|
||||
for ( i = 0; i < ARRAYSIZE( pAlertSounds ); i++ )
|
||||
PRECACHE_SOUND((char *)pAlertSounds[i]);
|
||||
|
||||
for ( i = 0; i < ARRAYSIZE( pPainSounds ); i++ )
|
||||
PRECACHE_SOUND((char *)pPainSounds[i]);
|
||||
}
|
||||
|
||||
//=========================================================
|
||||
// AI Schedules Specific to this monster
|
||||
//=========================================================
|
||||
|
||||
|
||||
|
||||
int CDeepOne::IgnoreConditions ( void )
|
||||
{
|
||||
int iIgnore = CBaseMonster::IgnoreConditions();
|
||||
|
||||
if ((m_Activity == ACT_MELEE_ATTACK1) || (m_Activity == ACT_MELEE_ATTACK2))
|
||||
{
|
||||
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 + DEEP_ONE_FLINCH_DELAY;
|
||||
}
|
||||
|
||||
return iIgnore;
|
||||
|
||||
}
|
||||
|
||||
//////////////////////////////////////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
LINK_ENTITY_TO_CLASS( monster_dagon, CDagon );
|
||||
|
||||
void CDagon :: Spawn( void )
|
||||
{
|
||||
Precache( );
|
||||
|
||||
if (pev->model)
|
||||
SET_MODEL(ENT(pev), STRING(pev->model)); //LRC
|
||||
else
|
||||
SET_MODEL(ENT(pev), "models/dagon.mdl");
|
||||
|
||||
UTIL_SetSize(pev, Vector(-32, -32, 0), Vector(32, 32, 216));
|
||||
|
||||
if (pev->health == 0)
|
||||
pev->health = gSkillData.deeponeHealth * 9;
|
||||
|
||||
pev->solid = SOLID_SLIDEBOX;
|
||||
pev->movetype = MOVETYPE_STEP;
|
||||
m_bloodColor = BLOOD_COLOR_GREEN;
|
||||
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 | bits_CAP_SWIM | bits_CAP_CLIMB | bits_CAP_JUMP;
|
||||
|
||||
MonsterInit();
|
||||
}
|
||||
|
||||
void CDagon :: Precache( void )
|
||||
{
|
||||
if (pev->model)
|
||||
PRECACHE_MODEL((char*)STRING(pev->model)); //LRC
|
||||
else
|
||||
PRECACHE_MODEL( "models/dagon.mdl" );
|
||||
CDeepOne::Precache();
|
||||
}
|
||||
|
||||
//=========================================================
|
||||
// CheckMeleeAttack1
|
||||
//=========================================================
|
||||
BOOL CDagon :: CheckMeleeAttack1 ( float flDot, float flDist )
|
||||
{
|
||||
// Decent fix to keep folks from kicking/punching hornets and snarks is to check the onground flag(sjb)
|
||||
// if ( flDist <= 128 && flDot >= 0.7 && m_hEnemy != NULL && FBitSet ( m_hEnemy->pev->flags, FL_ONGROUND ) )
|
||||
if ( flDist <= 160 && flDot >= 0.7 && m_hEnemy != NULL && FBitSet ( m_hEnemy->pev->flags, FL_ONGROUND ) )
|
||||
{
|
||||
return TRUE;
|
||||
}
|
||||
return FALSE;
|
||||
}
|
||||
|
||||
//=========================================================
|
||||
// HandleAnimEvent - catches the monster-specific messages
|
||||
// that occur when tagged animation frames are played.
|
||||
//=========================================================
|
||||
float CDagon :: GetAttackDist( void )
|
||||
{
|
||||
return 160.0;
|
||||
}
|
||||
|
||||
void CDagon :: HandleAnimEvent( MonsterEvent_t *pEvent )
|
||||
{
|
||||
switch( pEvent->event )
|
||||
{
|
||||
case DEEP_ONE_AE_ATTACK_RIGHT:
|
||||
case DEEP_ONE_AE_ATTACK_LEFT:
|
||||
case DEEP_ONE_AE_ATTACK_BOTH:
|
||||
{
|
||||
pev->size.z *= 0.25;
|
||||
}
|
||||
break;
|
||||
|
||||
default:
|
||||
break;
|
||||
}
|
||||
|
||||
CDeepOne::HandleAnimEvent( pEvent );
|
||||
|
||||
pev->origin.z += 64;
|
||||
|
||||
CDeepOne::HandleAnimEvent( pEvent );
|
||||
|
||||
pev->origin.z += 64;
|
||||
|
||||
CDeepOne::HandleAnimEvent( pEvent );
|
||||
|
||||
pev->origin.z -= 128;
|
||||
|
||||
switch( pEvent->event )
|
||||
{
|
||||
case DEEP_ONE_AE_ATTACK_RIGHT:
|
||||
case DEEP_ONE_AE_ATTACK_LEFT:
|
||||
case DEEP_ONE_AE_ATTACK_BOTH:
|
||||
{
|
||||
pev->size.z *= 4.0;
|
||||
}
|
||||
break;
|
||||
|
||||
default:
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
int CDagon :: TakeDamage( entvars_t *pevInflictor, entvars_t *pevAttacker, float flDamage, int bitsDamageType )
|
||||
{
|
||||
if ( bitsDamageType & DMG_CRUSH ) return 0;
|
||||
if ( bitsDamageType & DMG_SLASH ) return 0;
|
||||
if ( bitsDamageType & DMG_BULLET ) return 0;
|
||||
if ( bitsDamageType & DMG_CLUB ) return 0;
|
||||
|
||||
return CDeepOne::TakeDamage( pevInflictor, pevAttacker, flDamage, bitsDamageType );
|
||||
}
|
||||
|
||||
|
||||
|
|
@ -0,0 +1,52 @@
|
|||
|
||||
#ifndef DEEP_ONE_H
|
||||
#define DEEP_ONE_H
|
||||
|
||||
class CDeepOne : public CBaseMonster
|
||||
{
|
||||
public:
|
||||
void Spawn( void );
|
||||
void Precache( void );
|
||||
void SetYawSpeed( void );
|
||||
int Classify ( void );
|
||||
virtual float GetAttackDist( 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[];
|
||||
|
||||
virtual int GetVoicePitch( void ) { return PITCH_NORM; };
|
||||
virtual float GetVolume (void) { return 0.5; };
|
||||
|
||||
// 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 );
|
||||
};
|
||||
|
||||
class CDagon : public CDeepOne
|
||||
{
|
||||
public:
|
||||
void Spawn( void );
|
||||
void Precache( void );
|
||||
virtual float GetAttackDist( void );
|
||||
void HandleAnimEvent( MonsterEvent_t *pEvent );
|
||||
virtual BOOL CheckMeleeAttack1( float flDot, float flDist );
|
||||
int TakeDamage( entvars_t *pevInflictor, entvars_t *pevAttacker, float flDamage, int bitsDamageType );
|
||||
virtual int GetVoicePitch( void ) { return RANDOM_LONG(40,50); };
|
||||
virtual float GetVolume (void) { return 1.0; };
|
||||
};
|
||||
|
||||
#endif
|
|
@ -0,0 +1,418 @@
|
|||
/***
|
||||
*
|
||||
* Copyright (c) 1999, 2000 Valve LLC. All rights reserved.
|
||||
*
|
||||
* This product contains software technology licensed from Id
|
||||
* Software, Inc. ("Id Technology"). Id Technology (c) 1996 Id Software, Inc.
|
||||
* All Rights Reserved.
|
||||
*
|
||||
* This source code contains proprietary and confidential information of
|
||||
* Valve LLC and its suppliers. Access to this code is restricted to
|
||||
* persons who have executed a written SDK license with Valve. Any access,
|
||||
* use or distribution of this code by or to any unlicensed person is illegal.
|
||||
*
|
||||
****/
|
||||
//=========================================================
|
||||
// SHAMBLER
|
||||
//=========================================================
|
||||
|
||||
// UNDONE: Don't flinch every time you get hit
|
||||
|
||||
#include "extdll.h"
|
||||
#include "util.h"
|
||||
#include "cbase.h"
|
||||
#include "monsters.h"
|
||||
#include "schedule.h"
|
||||
#include "effects.h"
|
||||
|
||||
const float PI = 3.14159;
|
||||
|
||||
//=========================================================
|
||||
// Monster's Anim Events Go Here
|
||||
//=========================================================
|
||||
#define SHAMBLER_AE_ATTACK_RIGHT 0x01
|
||||
#define SHAMBLER_AE_ATTACK_LEFT 0x02
|
||||
#define SHAMBLER_AE_ATTACK_BOTH 0x03
|
||||
|
||||
#define SHAMBLER_FLINCH_DELAY 2 // at most one flinch every n secs
|
||||
|
||||
|
||||
#include "DimensionalShambler.h"
|
||||
|
||||
|
||||
LINK_ENTITY_TO_CLASS( monster_dimensionalshambler, CDimensionalShambler );
|
||||
|
||||
const char *CDimensionalShambler::pAttackHitSounds[] =
|
||||
{
|
||||
"zombie/claw_strike1.wav",
|
||||
"zombie/claw_strike2.wav",
|
||||
"zombie/claw_strike3.wav",
|
||||
};
|
||||
|
||||
const char *CDimensionalShambler::pAttackMissSounds[] =
|
||||
{
|
||||
"zombie/claw_miss1.wav",
|
||||
"zombie/claw_miss2.wav",
|
||||
};
|
||||
|
||||
const char *CDimensionalShambler::pAttackSounds[] =
|
||||
{
|
||||
"shambler/ds_attack1.wav",
|
||||
"shambler/ds_attack2.wav",
|
||||
};
|
||||
|
||||
const char *CDimensionalShambler::pIdleSounds[] =
|
||||
{
|
||||
"shambler/ds_idle1.wav",
|
||||
"shambler/ds_idle2.wav",
|
||||
};
|
||||
|
||||
const char *CDimensionalShambler::pAlertSounds[] =
|
||||
{
|
||||
"shambler/ds_alert1.wav",
|
||||
"shambler/ds_alert2.wav",
|
||||
};
|
||||
|
||||
const char *CDimensionalShambler::pPainSounds[] =
|
||||
{
|
||||
"shambler/ds_pain1.wav",
|
||||
"shambler/ds_pain2.wav",
|
||||
"shambler/ds_pain3.wav",
|
||||
"shambler/ds_pain4.wav",
|
||||
};
|
||||
|
||||
//=========================================================
|
||||
// Classify - indicates this monster's place in the
|
||||
// relationship table.
|
||||
//=========================================================
|
||||
int CDimensionalShambler :: Classify ( void )
|
||||
{
|
||||
return m_iClass?m_iClass:CLASS_ALIEN_MONSTER;
|
||||
}
|
||||
|
||||
//=========================================================
|
||||
// SetYawSpeed - allows each sequence to have a different
|
||||
// turn rate associated with it.
|
||||
//=========================================================
|
||||
void CDimensionalShambler :: SetYawSpeed ( void )
|
||||
{
|
||||
int ys;
|
||||
|
||||
ys = 120;
|
||||
|
||||
pev->yaw_speed = ys;
|
||||
}
|
||||
|
||||
int CDimensionalShambler :: 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 CDimensionalShambler :: 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 CDimensionalShambler :: 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 CDimensionalShambler :: IdleSound( void )
|
||||
{
|
||||
int pitch = 95 + RANDOM_LONG(0,9);
|
||||
|
||||
// Play a random idle sound
|
||||
EMIT_SOUND_DYN ( ENT(pev), CHAN_VOICE, pIdleSounds[ RANDOM_LONG(0,ARRAYSIZE(pIdleSounds)-1) ], 1.0, ATTN_NORM, 0, 100 + RANDOM_LONG(-5,5) );
|
||||
}
|
||||
|
||||
void CDimensionalShambler :: AttackSound( void )
|
||||
{
|
||||
// Play a random attack sound
|
||||
EMIT_SOUND_DYN ( ENT(pev), CHAN_VOICE, pAttackSounds[ RANDOM_LONG(0,ARRAYSIZE(pAttackSounds)-1) ], 1.0, ATTN_NORM, 0, 100 + RANDOM_LONG(-5,5) );
|
||||
}
|
||||
|
||||
|
||||
//=========================================================
|
||||
// HandleAnimEvent - catches the monster-specific messages
|
||||
// that occur when tagged animation frames are played.
|
||||
//=========================================================
|
||||
void CDimensionalShambler :: HandleAnimEvent( MonsterEvent_t *pEvent )
|
||||
{
|
||||
switch( pEvent->event )
|
||||
{
|
||||
case SHAMBLER_AE_ATTACK_RIGHT:
|
||||
{
|
||||
// do stuff for this event.
|
||||
// ALERT( at_console, "Slash right!\n" );
|
||||
CBaseEntity *pHurt = CheckTraceHullAttack( 70, gSkillData.shamblerDmgOneSlash, 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 SHAMBLER_AE_ATTACK_LEFT:
|
||||
{
|
||||
// do stuff for this event.
|
||||
// ALERT( at_console, "Slash left!\n" );
|
||||
CBaseEntity *pHurt = CheckTraceHullAttack( 70, gSkillData.shamblerDmgOneSlash, 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 SHAMBLER_AE_ATTACK_BOTH:
|
||||
{
|
||||
// do stuff for this event.
|
||||
CBaseEntity *pHurt = CheckTraceHullAttack( 70, gSkillData.shamblerDmgBothSlash, 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 CDimensionalShambler :: Spawn()
|
||||
{
|
||||
Precache( );
|
||||
|
||||
if (pev->model)
|
||||
SET_MODEL(ENT(pev), STRING(pev->model)); //LRC
|
||||
else
|
||||
SET_MODEL(ENT(pev), "models/shambler.mdl");
|
||||
UTIL_SetSize( pev, VEC_HUMAN_HULL_MIN, VEC_HUMAN_HULL_MAX );
|
||||
|
||||
pev->solid = SOLID_SLIDEBOX;
|
||||
pev->movetype = MOVETYPE_STEP;
|
||||
m_bloodColor = BLOOD_COLOR_GREEN;
|
||||
if (pev->health == 0)
|
||||
pev->health = gSkillData.shamblerHealth;
|
||||
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;
|
||||
m_flNextTeleport = gpGlobals->time + 5.0;
|
||||
|
||||
MonsterInit();
|
||||
}
|
||||
|
||||
//=========================================================
|
||||
// Precache - precaches all resources this monster needs
|
||||
//=========================================================
|
||||
void CDimensionalShambler :: Precache()
|
||||
{
|
||||
int i;
|
||||
|
||||
if (pev->model)
|
||||
PRECACHE_MODEL((char*)STRING(pev->model)); //LRC
|
||||
else
|
||||
PRECACHE_MODEL("models/shambler.mdl");
|
||||
|
||||
PRECACHE_SOUND("debris/beamstart6.wav");
|
||||
|
||||
for ( i = 0; i < ARRAYSIZE( pAttackHitSounds ); i++ )
|
||||
PRECACHE_SOUND((char *)pAttackHitSounds[i]);
|
||||
|
||||
for ( i = 0; i < ARRAYSIZE( pAttackMissSounds ); i++ )
|
||||
PRECACHE_SOUND((char *)pAttackMissSounds[i]);
|
||||
|
||||
for ( i = 0; i < ARRAYSIZE( pAttackSounds ); i++ )
|
||||
PRECACHE_SOUND((char *)pAttackSounds[i]);
|
||||
|
||||
for ( i = 0; i < ARRAYSIZE( pIdleSounds ); i++ )
|
||||
PRECACHE_SOUND((char *)pIdleSounds[i]);
|
||||
|
||||
for ( i = 0; i < ARRAYSIZE( pAlertSounds ); i++ )
|
||||
PRECACHE_SOUND((char *)pAlertSounds[i]);
|
||||
|
||||
for ( i = 0; i < ARRAYSIZE( pPainSounds ); i++ )
|
||||
PRECACHE_SOUND((char *)pPainSounds[i]);
|
||||
|
||||
// sprites
|
||||
PRECACHE_MODEL("sprites/c-tele1.spr");
|
||||
PRECACHE_MODEL("sprites/d-tele1.spr");
|
||||
// for the warpball
|
||||
PRECACHE_MODEL( "sprites/Fexplo1.spr" );
|
||||
PRECACHE_MODEL( "sprites/XFlare1.spr" );
|
||||
PRECACHE_SOUND( "debris/beamstart2.wav" );
|
||||
PRECACHE_SOUND( "debris/beamstart7.wav" );
|
||||
}
|
||||
|
||||
//=========================================================
|
||||
// AI Schedules Specific to this monster
|
||||
//=========================================================
|
||||
|
||||
|
||||
|
||||
int CDimensionalShambler::IgnoreConditions ( void )
|
||||
{
|
||||
int iIgnore = CBaseMonster::IgnoreConditions();
|
||||
|
||||
if ((m_Activity == ACT_MELEE_ATTACK1) || (m_Activity == ACT_MELEE_ATTACK2))
|
||||
{
|
||||
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 + SHAMBLER_FLINCH_DELAY;
|
||||
}
|
||||
|
||||
return iIgnore;
|
||||
|
||||
}
|
||||
|
||||
//=========================================================
|
||||
// Look
|
||||
//=========================================================
|
||||
|
||||
void CDimensionalShambler :: Look ( int iDistance )
|
||||
{
|
||||
CBaseMonster::Look ( iDistance );
|
||||
|
||||
float yaw, y;
|
||||
BOOL bDown, bUp;
|
||||
Vector vOffset;
|
||||
|
||||
// if we are within teleport time
|
||||
if (m_flNextTeleport < gpGlobals->time)
|
||||
{
|
||||
// if we can see the enemy
|
||||
if (HasConditions(bits_COND_SEE_ENEMY))
|
||||
{
|
||||
CBaseEntity* pEnemy = m_hEnemy;
|
||||
|
||||
// and we can find a place to teleport to
|
||||
yaw = RANDOM_FLOAT(-PI,PI);
|
||||
|
||||
Vector vecTele;
|
||||
|
||||
// Store original origin
|
||||
Vector myOrig = pev->origin;
|
||||
|
||||
for (y =-PI; y < PI; y += PI/6)
|
||||
{
|
||||
// if vecGoal+yaw*36 (close enough) is ok.
|
||||
vOffset = Vector(36*cos(y+yaw),36*sin(y+yaw),pev->mins.z - pEnemy->pev->mins.z);
|
||||
|
||||
// check that we are over some solid floor - i.e. if we move down 8 bits, we are in a brush!
|
||||
// now check we are not stuck in a brush anyway
|
||||
pev->origin = m_hEnemy->pev->origin + vOffset;
|
||||
|
||||
pev->origin.z += 1;
|
||||
DROP_TO_FLOOR ( ENT(pev) );
|
||||
|
||||
bDown = (fabs((pev->origin.z + pev->mins.z) - (pEnemy->pev->origin.z + pEnemy->pev->mins.z)) < 16.0);
|
||||
bUp = WALK_MOVE ( ENT(pev), 0,1, WALKMOVE_NORMAL );
|
||||
|
||||
// this (hopefully) ensures that the dimensional shambler will not teleport to empty air (e.g. if the player is on a ledge)
|
||||
// and will account for slight slopes or steps.
|
||||
//if ( bDown && WALK_MOVE ( ENT(pev), 0,1, WALKMOVE_NORMAL ) )
|
||||
if ( bUp && bDown )
|
||||
{
|
||||
// yes, we can teleport there
|
||||
CEnvWarpBall* pWarpOut = (CEnvWarpBall*)CBaseEntity::Create("env_warpball",myOrig+Vector(0,0,36),Vector(0,0,0),edict());
|
||||
pWarpOut->pev->frags = 8; // num of beams
|
||||
pWarpOut->pev->health = 128; // max length of beam
|
||||
pWarpOut->Use(NULL,NULL,USE_TOGGLE,0);
|
||||
// make a flash here
|
||||
//CSprite *pHereSprite = CSprite::SpriteCreate( "sprites/d-tele1.spr", myOrig+Vector(0,0,36), TRUE );
|
||||
//pHereSprite->pev->scale = 1.0;
|
||||
//pHereSprite->SetTransparency( kRenderGlow, 255, 255, 255, 255, kRenderFxNoDissipation );
|
||||
//pHereSprite->AnimateAndDie( 20 );
|
||||
CEnvWarpBall* pWarpIn = (CEnvWarpBall*)CBaseEntity::Create("env_warpball",pev->origin+Vector(0,0,36),Vector(0,0,0),edict());
|
||||
pWarpIn->pev->frags = 8; // num of beams
|
||||
pWarpIn->pev->health = 128; // max length of beam
|
||||
pWarpIn->Use(NULL,NULL,USE_TOGGLE,0);
|
||||
// make a flash there
|
||||
//CSprite *pThereSprite = CSprite::SpriteCreate( "sprites/c-tele1.spr", pev->origin+Vector(0,0,36), TRUE );
|
||||
//pThereSprite->pev->scale = 1.0;
|
||||
//pThereSprite->SetTransparency( kRenderGlow, 255, 255, 255, 255, kRenderFxNoDissipation );
|
||||
//pThereSprite->AnimateAndDie( 20 );
|
||||
// now the sound
|
||||
//EMIT_SOUND_DYN ( ENT(pev), CHAN_WEAPON, "debris/beamstart6.wav", 1.0, ATTN_NORM, 0, 100 + RANDOM_LONG(-5,5) );
|
||||
// set ideal facing
|
||||
MakeIdealYaw(m_hEnemy->pev->origin);
|
||||
pev->angles.y = pev->ideal_yaw;
|
||||
// clear the route
|
||||
RouteClear();
|
||||
// set the next teleport time (10 secs)
|
||||
m_flNextTeleport = gpGlobals->time + RANDOM_LONG(8.0,12.0);
|
||||
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
// we failed to find a teleport spot
|
||||
// restore the old origin
|
||||
pev->origin = myOrig;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,183 @@
|
|||
/***
|
||||
*
|
||||
* Copyright (c) 1999, 2000 Valve LLC. All rights reserved.
|
||||
*
|
||||
* This product contains software technology licensed from Id
|
||||
* Software, Inc. ("Id Technology"). Id Technology (c) 1996 Id Software, Inc.
|
||||
* All Rights Reserved.
|
||||
*
|
||||
* Use, distribution, and modification of this source code and/or resulting
|
||||
* object code is restricted to non-commercial enhancements to products from
|
||||
* Valve LLC. All other use, distribution, or modification is prohibited
|
||||
* without written permission from Valve LLC.
|
||||
*
|
||||
****/
|
||||
|
||||
#include "extdll.h"
|
||||
#include "util.h"
|
||||
#include "cbase.h"
|
||||
#include "monsters.h"
|
||||
#include "weapons.h"
|
||||
#include "nodes.h"
|
||||
#include "player.h"
|
||||
#include "gamerules.h"
|
||||
|
||||
|
||||
#define DREAD_NAME_SPEAK_VOLUME 512
|
||||
|
||||
#include "DreadName.h"
|
||||
|
||||
LINK_ENTITY_TO_CLASS( weapon_dread_name, CDreadName );
|
||||
|
||||
|
||||
|
||||
enum dread_name_e {
|
||||
DREAD_NAME_OPEN = 0,
|
||||
DREAD_NAME_IDLE1,
|
||||
DREAD_NAME_IDLE2,
|
||||
DREAD_NAME_IDLE3,
|
||||
DREAD_NAME_CAST,
|
||||
DREAD_NAME_CLOSE
|
||||
};
|
||||
|
||||
|
||||
void CDreadName::Spawn( )
|
||||
{
|
||||
Precache( );
|
||||
m_iId = WEAPON_DREAD_NAME;
|
||||
SET_MODEL(ENT(pev), "models/w_dread_name.mdl");
|
||||
m_iClip = -1;
|
||||
m_iIsCasting = 0;
|
||||
|
||||
FallInit();// get ready to fall down.
|
||||
}
|
||||
|
||||
|
||||
void CDreadName::Precache( void )
|
||||
{
|
||||
PRECACHE_MODEL("models/v_dread_name.mdl");
|
||||
PRECACHE_MODEL("models/w_dread_name.mdl");
|
||||
PRECACHE_MODEL("models/p_dread_name.mdl");
|
||||
PRECACHE_SOUND("weapons/dreadname.wav");
|
||||
}
|
||||
|
||||
int CDreadName::GetItemInfo(ItemInfo *p)
|
||||
{
|
||||
p->pszName = STRING(pev->classname);
|
||||
p->pszAmmo1 = NULL;
|
||||
p->iMaxAmmo1 = -1;
|
||||
p->pszAmmo2 = NULL;
|
||||
p->iMaxAmmo2 = -1;
|
||||
p->iMaxClip = WEAPON_NOCLIP;
|
||||
p->iSlot = 4;
|
||||
p->iPosition = 0;
|
||||
p->iId = WEAPON_DREAD_NAME;
|
||||
p->iWeight = DREAD_NAME_WEIGHT;
|
||||
return 1;
|
||||
}
|
||||
|
||||
|
||||
|
||||
BOOL CDreadName::Deploy( )
|
||||
{
|
||||
return DefaultDeploy( "models/v_dread_name.mdl", "models/p_dread_name.mdl", DREAD_NAME_OPEN, "dread name" );
|
||||
}
|
||||
|
||||
void CDreadName::Holster( int skiplocal /* = 0 */ )
|
||||
{
|
||||
m_pPlayer->m_flNextAttack = UTIL_WeaponTimeBase() + 1.0;
|
||||
SendWeaponAnim( DREAD_NAME_CLOSE );
|
||||
}
|
||||
|
||||
int CDreadName::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 CDreadName::PrimaryAttack()
|
||||
{
|
||||
// send the weapon animation
|
||||
SendWeaponAnim( DREAD_NAME_CAST );
|
||||
m_iIsCasting = 1;
|
||||
m_flNextPrimaryAttack = gpGlobals->time + (42.0/30.0);
|
||||
m_flTimeWeaponIdle = gpGlobals->time + 1.0;
|
||||
}
|
||||
|
||||
void CDreadName::WeaponIdle( void )
|
||||
{
|
||||
ResetEmptySound( );
|
||||
|
||||
if ( m_flTimeWeaponIdle > gpGlobals->time )
|
||||
return;
|
||||
|
||||
if ( m_iIsCasting )
|
||||
{
|
||||
// find all monster in a sphere
|
||||
const int MAX_MONSTER = 1000;
|
||||
CBaseEntity* pEntInSphere[MAX_MONSTER];
|
||||
int iMonsters = UTIL_MonstersInSphere(pEntInSphere, MAX_MONSTER, pev->origin, 384);
|
||||
|
||||
// send them the panic message
|
||||
int iClass;
|
||||
for (int i = 0; i < iMonsters; i++)
|
||||
{
|
||||
CBaseEntity* pEnt = pEntInSphere[i];
|
||||
|
||||
// is this a monster?
|
||||
if (!(pEnt->pev->flags & FL_MONSTER)) continue;
|
||||
|
||||
CBaseMonster* pMon = (CBaseMonster*)pEnt;
|
||||
|
||||
// is it an alien monster (as opposed to a cultist, chicken, etc)?
|
||||
iClass = pMon->Classify();
|
||||
|
||||
if (iClass != CLASS_ALIEN_MILITARY
|
||||
&& iClass != CLASS_ALIEN_MONSTER
|
||||
&& iClass != CLASS_ALIEN_PREY
|
||||
&& iClass != CLASS_ALIEN_PREDATOR)
|
||||
continue;
|
||||
|
||||
pMon->Panic(m_pPlayer->pev);
|
||||
}
|
||||
|
||||
m_iIsCasting = 0;
|
||||
m_pPlayer->LoseSanity(5);
|
||||
m_flTimeWeaponIdle = gpGlobals->time + 0.5;
|
||||
return;
|
||||
}
|
||||
|
||||
int iAnim;
|
||||
|
||||
float flRand = RANDOM_FLOAT(0,1);
|
||||
|
||||
if ( flRand <= 0.4 )
|
||||
{
|
||||
iAnim = DREAD_NAME_IDLE1;
|
||||
m_flTimeWeaponIdle = gpGlobals->time + RANDOM_FLOAT(2,3);
|
||||
}
|
||||
else if ( flRand <= 0.75 )
|
||||
{
|
||||
iAnim = DREAD_NAME_IDLE2;
|
||||
m_flTimeWeaponIdle = gpGlobals->time + 3;
|
||||
}
|
||||
else
|
||||
{
|
||||
iAnim = DREAD_NAME_IDLE3;
|
||||
m_flTimeWeaponIdle = gpGlobals->time + 3;
|
||||
}
|
||||
|
||||
SendWeaponAnim( iAnim );
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
|
@ -0,0 +1,32 @@
|
|||
|
||||
#ifndef CROWBAR_H
|
||||
#define CROWBAR_H
|
||||
|
||||
|
||||
class CDreadName : public CBasePlayerWeapon
|
||||
{
|
||||
public:
|
||||
void Spawn( void );
|
||||
void Precache( void );
|
||||
int iItemSlot( void ) { return 4; }
|
||||
int GetItemInfo(ItemInfo *p);
|
||||
int AddToPlayer( CBasePlayer *pPlayer );
|
||||
|
||||
void PrimaryAttack( void );
|
||||
BOOL Deploy( void );
|
||||
void Holster( int skiplocal = 0 );
|
||||
void WeaponIdle( void );
|
||||
int m_iIsCasting;
|
||||
|
||||
virtual BOOL UseDecrement( void )
|
||||
{
|
||||
#if defined( CLIENT_WEAPONS )
|
||||
return TRUE;
|
||||
#else
|
||||
return FALSE;
|
||||
#endif
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
#endif
|
|
@ -0,0 +1,990 @@
|
|||
/***
|
||||
*
|
||||
* Copyright (c) 1999, 2000 Valve LLC. All rights reserved.
|
||||
*
|
||||
* This product contains software technology licensed from Id
|
||||
* Software, Inc. ("Id Technology"). Id Technology (c) 1996 Id Software, Inc.
|
||||
* All Rights Reserved.
|
||||
*
|
||||
* This source code contains proprietary and confidential information of
|
||||
* Valve LLC and its suppliers. Access to this code is restricted to
|
||||
* persons who have executed a written SDK license with Valve. Any access,
|
||||
* use or distribution of this code by or to any unlicensed person is illegal.
|
||||
*
|
||||
****/
|
||||
#if !defined( OEM_BUILD ) && !defined( HLDEMO_BUILD )
|
||||
|
||||
//=========================================================
|
||||
// monster template
|
||||
//=========================================================
|
||||
#include "extdll.h"
|
||||
#include "util.h"
|
||||
#include "cbase.h"
|
||||
#include "monsters.h"
|
||||
#include "schedule.h"
|
||||
#include "decals.h"
|
||||
#include "weapons.h"
|
||||
#include "game.h"
|
||||
|
||||
#include "bm.h"
|
||||
|
||||
//=========================================================
|
||||
// Monster's Anim Events Go Here
|
||||
//=========================================================
|
||||
#define DH_AE_STEP1 1 // Footstep left
|
||||
#define DH_AE_STEP2 2 // Footstep right
|
||||
#define DH_AE_STEP3 3 // Footstep back left
|
||||
#define DH_AE_STEP4 4 // Footstep back right
|
||||
#define DH_AE_SACK 5 // Sack slosh
|
||||
#define DH_AE_DEATHSOUND 6 // Death sound
|
||||
|
||||
#define DH_AE_MELEE_ATTACKBR 8 // Leg attack
|
||||
#define DH_AE_MELEE_ATTACKBL 9 // Leg attack
|
||||
#define DH_AE_MELEE_ATTACK1 10 // Leg attack
|
||||
//#define DH_AE_MORTAR_ATTACK1 11 // Launch a mortar
|
||||
//#define DH_AE_LAY_CRAB 12 // Lay a headcrab
|
||||
#define DH_AE_JUMP_FORWARD 13 // Jump up and forward
|
||||
#define DH_AE_SCREAM 14 // alert sound
|
||||
#define DH_AE_PAIN_SOUND 15 // pain sound
|
||||
#define DH_AE_ATTACK_SOUND 16 // attack sound
|
||||
//#define DH_AE_BIRTH_SOUND 17 // birth sound
|
||||
#define DH_AE_EARLY_TARGET 50 // Fire target early
|
||||
|
||||
|
||||
|
||||
// User defined conditions
|
||||
#define bits_COND_NODE_SEQUENCE ( bits_COND_SPECIAL1 ) // pev->netname contains the name of a sequence to play
|
||||
|
||||
// Attack distance constants
|
||||
#define DH_ATTACKDIST 170
|
||||
//#define DH_MORTARDIST 800
|
||||
//#define DH_MAXCHILDREN 20 // Max # of live headcrab children
|
||||
|
||||
|
||||
//#define bits_MEMORY_CHILDPAIR (bits_MEMORY_CUSTOM1)
|
||||
#define bits_MEMORY_ADVANCE_NODE (bits_MEMORY_CUSTOM2)
|
||||
#define bits_MEMORY_COMPLETED_NODE (bits_MEMORY_CUSTOM3)
|
||||
#define bits_MEMORY_FIRED_NODE (bits_MEMORY_CUSTOM4)
|
||||
|
||||
int gDHSpitSprite, gDHSpitDebrisSprite;
|
||||
|
||||
|
||||
class CDunwichHorror : public CBaseMonster
|
||||
{
|
||||
public:
|
||||
void Spawn( void );
|
||||
void Precache( void );
|
||||
void KeyValue( KeyValueData *pkvd );
|
||||
void Activate( void );
|
||||
int TakeDamage( entvars_t *pevInflictor, entvars_t *pevAttacker, float flDamage, int bitsDamageType );
|
||||
|
||||
void RunTask( Task_t *pTask );
|
||||
void StartTask( Task_t *pTask );
|
||||
Schedule_t *GetSchedule( void );
|
||||
Schedule_t *GetScheduleOfType( int Type );
|
||||
void TraceAttack( entvars_t *pevAttacker, float flDamage, Vector vecDir, TraceResult *ptr, int bitsDamageType );
|
||||
void SetActivity ( Activity NewActivity );
|
||||
virtual void MonsterThink( void );
|
||||
|
||||
void NodeStart( int iszNextNode );
|
||||
void NodeReach( void );
|
||||
BOOL ShouldGoToNode( void );
|
||||
|
||||
void SetYawSpeed( void );
|
||||
int Classify ( void );
|
||||
void HandleAnimEvent( MonsterEvent_t *pEvent );
|
||||
|
||||
int GetNodeSequence( void )
|
||||
{
|
||||
CBaseEntity *pTarget = m_hTargetEnt;
|
||||
if ( pTarget )
|
||||
{
|
||||
return pTarget->pev->netname; // netname holds node sequence
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
||||
int GetNodePresequence( void )
|
||||
{
|
||||
CInfoBM *pTarget = (CInfoBM *)(CBaseEntity *)m_hTargetEnt;
|
||||
if ( pTarget )
|
||||
{
|
||||
return pTarget->m_preSequence;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
float GetNodeDelay( void )
|
||||
{
|
||||
CBaseEntity *pTarget = m_hTargetEnt;
|
||||
if ( pTarget )
|
||||
{
|
||||
return pTarget->pev->speed; // Speed holds node delay
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
float GetNodeRange( void )
|
||||
{
|
||||
CBaseEntity *pTarget = m_hTargetEnt;
|
||||
if ( pTarget )
|
||||
{
|
||||
return pTarget->pev->scale; // Scale holds node delay
|
||||
}
|
||||
return 1e6;
|
||||
}
|
||||
|
||||
float GetNodeYaw( void )
|
||||
{
|
||||
CBaseEntity *pTarget = m_hTargetEnt;
|
||||
if ( pTarget )
|
||||
{
|
||||
if ( pTarget->pev->angles.y != 0 )
|
||||
return pTarget->pev->angles.y;
|
||||
}
|
||||
return pev->angles.y;
|
||||
}
|
||||
|
||||
//void LaunchMortar( void );
|
||||
|
||||
void SetObjectCollisionBox( void )
|
||||
{
|
||||
pev->absmin = pev->origin + Vector( -95, -95, 0 );
|
||||
pev->absmax = pev->origin + Vector( 95, 95, 190 );
|
||||
}
|
||||
|
||||
BOOL CheckMeleeAttack1( float flDot, float flDist ); // Slash
|
||||
BOOL CheckMeleeAttack2( float flDot, float flDist ); // Lay a crab
|
||||
BOOL CheckRangeAttack1( float flDot, float flDist ); // Mortar launch
|
||||
|
||||
virtual int Save( CSave &save );
|
||||
virtual int Restore( CRestore &restore );
|
||||
static TYPEDESCRIPTION m_SaveData[];
|
||||
|
||||
static const char *pSackSounds[];
|
||||
static const char *pDeathSounds[];
|
||||
static const char *pAttackSounds[];
|
||||
static const char *pAttackHitSounds[];
|
||||
static const char *pAlertSounds[];
|
||||
static const char *pPainSounds[];
|
||||
static const char *pFootSounds[];
|
||||
|
||||
CUSTOM_SCHEDULES;
|
||||
|
||||
private:
|
||||
float m_nodeTime;
|
||||
float m_mortarTime;
|
||||
float m_painSoundTime;
|
||||
|
||||
BOOL m_bVisible;
|
||||
BOOL m_bBecomingVisible;
|
||||
};
|
||||
LINK_ENTITY_TO_CLASS( monster_dunwichhorror, CDunwichHorror );
|
||||
|
||||
TYPEDESCRIPTION CDunwichHorror::m_SaveData[] =
|
||||
{
|
||||
DEFINE_FIELD( CDunwichHorror, m_nodeTime, FIELD_TIME ),
|
||||
DEFINE_FIELD( CDunwichHorror, m_mortarTime, FIELD_TIME ),
|
||||
DEFINE_FIELD( CDunwichHorror, m_painSoundTime, FIELD_TIME ),
|
||||
DEFINE_FIELD( CDunwichHorror, m_bVisible, FIELD_BOOLEAN ),
|
||||
DEFINE_FIELD( CDunwichHorror, m_bBecomingVisible, FIELD_BOOLEAN ),
|
||||
};
|
||||
|
||||
IMPLEMENT_SAVERESTORE( CDunwichHorror, CBaseMonster );
|
||||
|
||||
const char *CDunwichHorror::pSackSounds[] =
|
||||
{
|
||||
"gonarch/gon_sack1.wav",
|
||||
"gonarch/gon_sack2.wav",
|
||||
"gonarch/gon_sack3.wav",
|
||||
};
|
||||
|
||||
const char *CDunwichHorror::pDeathSounds[] =
|
||||
{
|
||||
"gonarch/gon_die1.wav",
|
||||
};
|
||||
|
||||
const char *CDunwichHorror::pAttackSounds[] =
|
||||
{
|
||||
"gonarch/gon_attack1.wav",
|
||||
"gonarch/gon_attack2.wav",
|
||||
"gonarch/gon_attack3.wav",
|
||||
};
|
||||
const char *CDunwichHorror::pAttackHitSounds[] =
|
||||
{
|
||||
"zombie/claw_strike1.wav",
|
||||
"zombie/claw_strike2.wav",
|
||||
"zombie/claw_strike3.wav",
|
||||
};
|
||||
|
||||
const char *CDunwichHorror::pAlertSounds[] =
|
||||
{
|
||||
"gonarch/gon_alert1.wav",
|
||||
"gonarch/gon_alert2.wav",
|
||||
"gonarch/gon_alert3.wav",
|
||||
};
|
||||
|
||||
const char *CDunwichHorror::pPainSounds[] =
|
||||
{
|
||||
"gonarch/gon_pain2.wav",
|
||||
"gonarch/gon_pain4.wav",
|
||||
"gonarch/gon_pain5.wav",
|
||||
};
|
||||
|
||||
const char *CDunwichHorror::pFootSounds[] =
|
||||
{
|
||||
"gonarch/gon_step1.wav",
|
||||
"gonarch/gon_step2.wav",
|
||||
"gonarch/gon_step3.wav",
|
||||
};
|
||||
|
||||
|
||||
|
||||
void CDunwichHorror :: KeyValue( KeyValueData *pkvd )
|
||||
{
|
||||
#if 0
|
||||
if (FStrEq(pkvd->szKeyName, "volume"))
|
||||
{
|
||||
m_volume = atof(pkvd->szValue);
|
||||
pkvd->fHandled = TRUE;
|
||||
}
|
||||
else
|
||||
#endif
|
||||
if (FStrEq(pkvd->szKeyName, "visible"))
|
||||
{
|
||||
m_bVisible = atoi(pkvd->szValue);
|
||||
pkvd->fHandled = TRUE;
|
||||
}
|
||||
else
|
||||
CBaseMonster::KeyValue( pkvd );
|
||||
}
|
||||
|
||||
//=========================================================
|
||||
// Classify - indicates this monster's place in the
|
||||
// relationship table.
|
||||
//=========================================================
|
||||
int CDunwichHorror :: Classify ( void )
|
||||
{
|
||||
return m_iClass?m_iClass:CLASS_ALIEN_MONSTER;
|
||||
}
|
||||
|
||||
//=========================================================
|
||||
// SetYawSpeed - allows each sequence to have a different
|
||||
// turn rate associated with it.
|
||||
//=========================================================
|
||||
void CDunwichHorror :: SetYawSpeed ( void )
|
||||
{
|
||||
int ys;
|
||||
|
||||
switch ( m_Activity )
|
||||
{
|
||||
case ACT_IDLE:
|
||||
ys = 100;
|
||||
break;
|
||||
default:
|
||||
ys = 90;
|
||||
}
|
||||
pev->yaw_speed = ys;
|
||||
}
|
||||
|
||||
void CDunwichHorror :: MonsterThink ( void )
|
||||
{
|
||||
// if we are becoming visible, then update the fx amount...
|
||||
if (m_bBecomingVisible)
|
||||
{
|
||||
// update the fx amount
|
||||
pev->renderamt += 5;
|
||||
// don't go above 255
|
||||
if (pev->renderamt > 255) pev->renderamt = 255;
|
||||
// if we are at max fx amount, then
|
||||
if (pev->renderamt == 255)
|
||||
{
|
||||
// stop updating the fx amount
|
||||
m_bBecomingVisible = FALSE;
|
||||
}
|
||||
}
|
||||
|
||||
CBaseMonster :: MonsterThink ();
|
||||
}
|
||||
|
||||
//=========================================================
|
||||
// HandleAnimEvent - catches the monster-specific messages
|
||||
// that occur when tagged animation frames are played.
|
||||
//
|
||||
// Returns number of events handled, 0 if none.
|
||||
//=========================================================
|
||||
void CDunwichHorror :: HandleAnimEvent( MonsterEvent_t *pEvent )
|
||||
{
|
||||
switch( pEvent->event )
|
||||
{
|
||||
case DH_AE_MELEE_ATTACKBR:
|
||||
case DH_AE_MELEE_ATTACKBL:
|
||||
case DH_AE_MELEE_ATTACK1:
|
||||
{
|
||||
Vector forward, right;
|
||||
|
||||
UTIL_MakeVectorsPrivate( pev->angles, forward, right, NULL );
|
||||
|
||||
Vector center = pev->origin + forward * 128;
|
||||
Vector mins = center - Vector( 64, 64, 0 );
|
||||
Vector maxs = center + Vector( 64, 64, 64 );
|
||||
|
||||
CBaseEntity *pList[8];
|
||||
int count = UTIL_EntitiesInBox( pList, 8, mins, maxs, FL_MONSTER|FL_CLIENT );
|
||||
CBaseEntity *pHurt = NULL;
|
||||
|
||||
for ( int i = 0; i < count && !pHurt; i++ )
|
||||
{
|
||||
if ( pList[i] != this )
|
||||
{
|
||||
if ( pList[i]->pev->owner != edict() )
|
||||
pHurt = pList[i];
|
||||
}
|
||||
}
|
||||
|
||||
if ( pHurt )
|
||||
{
|
||||
pHurt->TakeDamage( pev, pev, gSkillData.bigmommaDmgSlash, DMG_CRUSH | DMG_SLASH );
|
||||
pHurt->pev->punchangle.x = 15;
|
||||
switch( pEvent->event )
|
||||
{
|
||||
case DH_AE_MELEE_ATTACKBR:
|
||||
pHurt->pev->velocity = pHurt->pev->velocity + (forward * 150) + Vector(0,0,250) - (right * 200);
|
||||
break;
|
||||
|
||||
case DH_AE_MELEE_ATTACKBL:
|
||||
pHurt->pev->velocity = pHurt->pev->velocity + (forward * 150) + Vector(0,0,250) + (right * 200);
|
||||
break;
|
||||
|
||||
case DH_AE_MELEE_ATTACK1:
|
||||
pHurt->pev->velocity = pHurt->pev->velocity + (forward * 220) + Vector(0,0,200);
|
||||
break;
|
||||
}
|
||||
|
||||
pHurt->pev->flags &= ~FL_ONGROUND;
|
||||
EMIT_SOUND_DYN( edict(), CHAN_WEAPON, RANDOM_SOUND_ARRAY(pAttackHitSounds), 1.0, ATTN_NORM, 0, 100 + RANDOM_LONG(-5,5) );
|
||||
}
|
||||
}
|
||||
break;
|
||||
|
||||
case DH_AE_SCREAM:
|
||||
EMIT_SOUND_ARRAY_DYN( CHAN_VOICE, pAlertSounds );
|
||||
break;
|
||||
|
||||
case DH_AE_PAIN_SOUND:
|
||||
EMIT_SOUND_ARRAY_DYN( CHAN_VOICE, pPainSounds );
|
||||
break;
|
||||
|
||||
case DH_AE_ATTACK_SOUND:
|
||||
EMIT_SOUND_ARRAY_DYN( CHAN_WEAPON, pAttackSounds );
|
||||
break;
|
||||
|
||||
case DH_AE_SACK:
|
||||
if ( RANDOM_LONG(0,100) < 30 )
|
||||
EMIT_SOUND_ARRAY_DYN( CHAN_BODY, pSackSounds );
|
||||
break;
|
||||
|
||||
case DH_AE_DEATHSOUND:
|
||||
EMIT_SOUND_ARRAY_DYN( CHAN_VOICE, pDeathSounds );
|
||||
break;
|
||||
|
||||
case DH_AE_STEP1: // Footstep left
|
||||
case DH_AE_STEP3: // Footstep back left
|
||||
EMIT_SOUND_ARRAY_DYN( CHAN_ITEM, pFootSounds );
|
||||
break;
|
||||
|
||||
case DH_AE_STEP4: // Footstep back right
|
||||
case DH_AE_STEP2: // Footstep right
|
||||
EMIT_SOUND_ARRAY_DYN( CHAN_BODY, pFootSounds );
|
||||
break;
|
||||
|
||||
// case DH_AE_MORTAR_ATTACK1:
|
||||
// LaunchMortar();
|
||||
// break;
|
||||
|
||||
case DH_AE_JUMP_FORWARD:
|
||||
ClearBits( pev->flags, FL_ONGROUND );
|
||||
|
||||
UTIL_SetOrigin (this, pev->origin + Vector ( 0 , 0 , 1) );// take her off ground so engine doesn't instantly reset onground
|
||||
UTIL_MakeVectors ( pev->angles );
|
||||
|
||||
pev->velocity = (gpGlobals->v_forward * 200) + gpGlobals->v_up * 500;
|
||||
break;
|
||||
|
||||
case DH_AE_EARLY_TARGET:
|
||||
{
|
||||
CBaseEntity *pTarget = m_hTargetEnt;
|
||||
if ( pTarget && pTarget->pev->message )
|
||||
FireTargets( STRING(pTarget->pev->message), this, this, USE_TOGGLE, 0 );
|
||||
Remember( bits_MEMORY_FIRED_NODE );
|
||||
}
|
||||
break;
|
||||
|
||||
default:
|
||||
CBaseMonster::HandleAnimEvent( pEvent );
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
void CDunwichHorror :: TraceAttack( entvars_t *pevAttacker, float flDamage, Vector vecDir, TraceResult *ptr, int bitsDamageType )
|
||||
{
|
||||
if ( ptr->iHitgroup != 1 )
|
||||
{
|
||||
// didn't hit the sack?
|
||||
|
||||
if ( pev->dmgtime != gpGlobals->time || (RANDOM_LONG(0,10) < 1) )
|
||||
{
|
||||
UTIL_Ricochet( ptr->vecEndPos, RANDOM_FLOAT( 1, 2) );
|
||||
pev->dmgtime = gpGlobals->time;
|
||||
}
|
||||
|
||||
flDamage = 0.1;// don't hurt the monster much, but allow bits_COND_LIGHT_DAMAGE to be generated
|
||||
}
|
||||
else if ( gpGlobals->time > m_painSoundTime )
|
||||
{
|
||||
m_painSoundTime = gpGlobals->time + RANDOM_LONG(1, 3);
|
||||
EMIT_SOUND_ARRAY_DYN( CHAN_VOICE, pPainSounds );
|
||||
}
|
||||
|
||||
|
||||
CBaseMonster::TraceAttack( pevAttacker, flDamage, vecDir, ptr, bitsDamageType );
|
||||
}
|
||||
|
||||
|
||||
int CDunwichHorror :: TakeDamage( entvars_t *pevInflictor, entvars_t *pevAttacker, float flDamage, int bitsDamageType )
|
||||
{
|
||||
if ( bitsDamageType & DMG_POWDER_IBN && !m_bVisible)
|
||||
{
|
||||
m_bVisible = TRUE;
|
||||
m_bBecomingVisible = TRUE;
|
||||
return 0;
|
||||
}
|
||||
|
||||
// if we are invisible, we are invulnerable
|
||||
if (!m_bVisible) return 0;
|
||||
|
||||
// Don't take any acid damage -- BigMomma's mortar is acid
|
||||
if ( bitsDamageType & DMG_ACID )
|
||||
flDamage = 0;
|
||||
|
||||
if ( !HasMemory(bits_MEMORY_PATH_FINISHED) )
|
||||
{
|
||||
if ( pev->health <= flDamage )
|
||||
{
|
||||
pev->health = flDamage + 1;
|
||||
Remember( bits_MEMORY_ADVANCE_NODE | bits_MEMORY_COMPLETED_NODE );
|
||||
ALERT( at_aiconsole, "BM: Finished node health!!!\n" );
|
||||
}
|
||||
}
|
||||
|
||||
return CBaseMonster::TakeDamage( pevInflictor, pevAttacker, flDamage, bitsDamageType );
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
void CDunwichHorror::LaunchMortar( void )
|
||||
{
|
||||
m_mortarTime = gpGlobals->time + RANDOM_FLOAT( 2, 15 );
|
||||
|
||||
Vector startPos = pev->origin;
|
||||
startPos.z += 180;
|
||||
Vector vecLaunch = g_vecZero;
|
||||
|
||||
if (m_pCine) // is a scripted_action making me shoot?
|
||||
{
|
||||
if (m_hTargetEnt != NULL) // don't check m_fTurnType- bigmomma can fire in any direction.
|
||||
{
|
||||
vecLaunch = VecCheckSplatToss( pev, startPos, m_hTargetEnt->pev->origin, RANDOM_FLOAT( 150, 500 ) );
|
||||
}
|
||||
if (vecLaunch == g_vecZero)
|
||||
{
|
||||
vecLaunch = pev->movedir;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
vecLaunch = pev->movedir;
|
||||
}
|
||||
|
||||
EMIT_SOUND_DYN( edict(), CHAN_WEAPON, RANDOM_SOUND_ARRAY(pSackSounds), 1.0, ATTN_NORM, 0, 100 + RANDOM_LONG(-5,5) );
|
||||
CBMortar *pBomb = CBMortar::Shoot( edict(), startPos, vecLaunch );
|
||||
pBomb->pev->gravity = 1.0;
|
||||
MortarSpray( startPos, Vector(0,0,1), gDHSpitSprite, 24 );
|
||||
}
|
||||
*/
|
||||
|
||||
//=========================================================
|
||||
// Spawn
|
||||
//=========================================================
|
||||
void CDunwichHorror :: Spawn()
|
||||
{
|
||||
Precache( );
|
||||
|
||||
if (pev->model)
|
||||
SET_MODEL(ENT(pev), STRING(pev->model)); //LRC
|
||||
else
|
||||
SET_MODEL(ENT(pev), "models/big_mom.mdl");
|
||||
UTIL_SetSize( pev, Vector( -32, -32, 0 ), Vector( 32, 32, 64 ) );
|
||||
|
||||
pev->solid = SOLID_SLIDEBOX;
|
||||
pev->movetype = MOVETYPE_STEP;
|
||||
m_bloodColor = BLOOD_COLOR_GREEN;
|
||||
if (pev->health == 0)
|
||||
pev->health = 150 * gSkillData.bigmommaHealthFactor;
|
||||
pev->view_ofs = Vector ( 0, 0, 128 );// position of the eyes relative to monster's origin.
|
||||
m_flFieldOfView = 0.3;// indicates the width of this monster's forward view cone ( as a dotproduct result )
|
||||
m_MonsterState = MONSTERSTATE_NONE;
|
||||
m_bBecomingVisible = FALSE;
|
||||
MonsterInit();
|
||||
}
|
||||
|
||||
//=========================================================
|
||||
// Precache - precaches all resources this monster needs
|
||||
//=========================================================
|
||||
void CDunwichHorror :: Precache()
|
||||
{
|
||||
if (pev->model)
|
||||
PRECACHE_MODEL((char*)STRING(pev->model)); //LRC
|
||||
else
|
||||
PRECACHE_MODEL("models/big_mom.mdl");
|
||||
|
||||
PRECACHE_SOUND_ARRAY( pSackSounds );
|
||||
PRECACHE_SOUND_ARRAY( pDeathSounds );
|
||||
PRECACHE_SOUND_ARRAY( pAttackSounds );
|
||||
PRECACHE_SOUND_ARRAY( pAttackHitSounds );
|
||||
PRECACHE_SOUND_ARRAY( pAlertSounds );
|
||||
PRECACHE_SOUND_ARRAY( pPainSounds );
|
||||
PRECACHE_SOUND_ARRAY( pFootSounds );
|
||||
|
||||
// TEMP: Squid
|
||||
PRECACHE_MODEL("sprites/mommaspit.spr");// spit projectile.
|
||||
gDHSpitSprite = PRECACHE_MODEL("sprites/mommaspout.spr");// client side spittle.
|
||||
gDHSpitDebrisSprite = PRECACHE_MODEL("sprites/mommablob.spr" );
|
||||
|
||||
PRECACHE_SOUND( "bullchicken/bc_acid1.wav" );
|
||||
PRECACHE_SOUND( "bullchicken/bc_spithit1.wav" );
|
||||
PRECACHE_SOUND( "bullchicken/bc_spithit2.wav" );
|
||||
}
|
||||
|
||||
|
||||
void CDunwichHorror::Activate( void )
|
||||
{
|
||||
if ( m_hTargetEnt == NULL )
|
||||
Remember( bits_MEMORY_ADVANCE_NODE ); // Start 'er up
|
||||
|
||||
CBaseMonster::Activate();
|
||||
}
|
||||
|
||||
|
||||
void CDunwichHorror::NodeStart( int iszNextNode )
|
||||
{
|
||||
pev->netname = iszNextNode;
|
||||
|
||||
CBaseEntity *pTarget = NULL;
|
||||
|
||||
if ( pev->netname )
|
||||
{
|
||||
pTarget = UTIL_FindEntityByTargetname( NULL, STRING(pev->netname) );
|
||||
}
|
||||
|
||||
|
||||
if ( !pTarget )
|
||||
{
|
||||
ALERT( at_aiconsole, "BM: Finished the path!!\n" );
|
||||
Remember( bits_MEMORY_PATH_FINISHED );
|
||||
return;
|
||||
}
|
||||
Remember( bits_MEMORY_ON_PATH );
|
||||
m_hTargetEnt = pTarget;
|
||||
}
|
||||
|
||||
|
||||
void CDunwichHorror::NodeReach( void )
|
||||
{
|
||||
CBaseEntity *pTarget = m_hTargetEnt;
|
||||
|
||||
Forget( bits_MEMORY_ADVANCE_NODE );
|
||||
|
||||
if ( !pTarget )
|
||||
return;
|
||||
|
||||
if ( pTarget->pev->health )
|
||||
pev->max_health = pev->health = pTarget->pev->health * gSkillData.bigmommaHealthFactor;
|
||||
|
||||
if ( !HasMemory( bits_MEMORY_FIRED_NODE ) )
|
||||
{
|
||||
if ( pTarget->pev->message )
|
||||
FireTargets( STRING(pTarget->pev->message), this, this, USE_TOGGLE, 0 );
|
||||
}
|
||||
Forget( bits_MEMORY_FIRED_NODE );
|
||||
|
||||
pev->netname = pTarget->pev->target;
|
||||
if ( pTarget->pev->health == 0 )
|
||||
Remember( bits_MEMORY_ADVANCE_NODE ); // Move on if no health at this node
|
||||
}
|
||||
|
||||
|
||||
// Slash
|
||||
BOOL CDunwichHorror::CheckMeleeAttack1( float flDot, float flDist )
|
||||
{
|
||||
if (flDot >= 0.7)
|
||||
{
|
||||
if ( flDist <= DH_ATTACKDIST )
|
||||
return TRUE;
|
||||
}
|
||||
return FALSE;
|
||||
}
|
||||
|
||||
|
||||
// Lay a crab
|
||||
BOOL CDunwichHorror::CheckMeleeAttack2( float flDot, float flDist )
|
||||
{
|
||||
return FALSE;
|
||||
}
|
||||
|
||||
|
||||
// Mortar launch
|
||||
BOOL CDunwichHorror::CheckRangeAttack1( float flDot, float flDist )
|
||||
{
|
||||
/*
|
||||
if ( flDist <= DH_MORTARDIST && m_mortarTime < gpGlobals->time )
|
||||
{
|
||||
CBaseEntity *pEnemy = m_hEnemy;
|
||||
|
||||
if ( pEnemy )
|
||||
{
|
||||
Vector startPos = pev->origin;
|
||||
startPos.z += 180;
|
||||
pev->movedir = VecCheckSplatToss( pev, startPos, pEnemy->BodyTarget( pev->origin ), RANDOM_FLOAT( 150, 500 ) );
|
||||
if ( pev->movedir != g_vecZero )
|
||||
return TRUE;
|
||||
}
|
||||
}
|
||||
*/
|
||||
return FALSE;
|
||||
}
|
||||
|
||||
//=========================================================
|
||||
// AI Schedules Specific to this monster
|
||||
//=========================================================
|
||||
|
||||
enum
|
||||
{
|
||||
SCHED_DH_NODE = LAST_COMMON_SCHEDULE + 1,
|
||||
SCHED_NODE_FAIL,
|
||||
};
|
||||
|
||||
enum
|
||||
{
|
||||
TASK_MOVE_TO_NODE_RANGE = LAST_COMMON_TASK + 1, // Move within node range
|
||||
TASK_FIND_NODE, // Find my next node
|
||||
TASK_PLAY_NODE_PRESEQUENCE, // Play node pre-script
|
||||
TASK_PLAY_NODE_SEQUENCE, // Play node script
|
||||
TASK_PROCESS_NODE, // Fire targets, etc.
|
||||
TASK_WAIT_NODE, // Wait at the node
|
||||
TASK_NODE_DELAY, // Delay walking toward node for a bit. You've failed to get there
|
||||
TASK_NODE_YAW, // Get the best facing direction for this node
|
||||
};
|
||||
|
||||
|
||||
Task_t tlDHBigNode[] =
|
||||
{
|
||||
{ TASK_SET_FAIL_SCHEDULE, (float)SCHED_NODE_FAIL },
|
||||
{ TASK_STOP_MOVING, (float)0 },
|
||||
{ TASK_FIND_NODE, (float)0 }, // Find my next node
|
||||
{ TASK_PLAY_NODE_PRESEQUENCE,(float)0 }, // Play the pre-approach sequence if any
|
||||
{ TASK_MOVE_TO_NODE_RANGE, (float)0 }, // Move within node range
|
||||
{ TASK_STOP_MOVING, (float)0 },
|
||||
{ TASK_NODE_YAW, (float)0 },
|
||||
{ TASK_FACE_IDEAL, (float)0 },
|
||||
{ TASK_WAIT_NODE, (float)0 }, // Wait for node delay
|
||||
{ TASK_PLAY_NODE_SEQUENCE, (float)0 }, // Play the sequence if one exists
|
||||
{ TASK_PROCESS_NODE, (float)0 }, // Fire targets, etc.
|
||||
{ TASK_SET_ACTIVITY, (float)ACT_IDLE },
|
||||
};
|
||||
|
||||
Schedule_t slDHBigNode[] =
|
||||
{
|
||||
{
|
||||
tlDHBigNode,
|
||||
ARRAYSIZE ( tlDHBigNode ),
|
||||
0,
|
||||
0,
|
||||
"DH Big Node"
|
||||
},
|
||||
};
|
||||
|
||||
|
||||
Task_t tlDHNodeFail[] =
|
||||
{
|
||||
{ TASK_NODE_DELAY, (float)10 }, // Try to do something else for 10 seconds
|
||||
{ TASK_SET_ACTIVITY, (float)ACT_IDLE },
|
||||
};
|
||||
|
||||
Schedule_t slDHNodeFail[] =
|
||||
{
|
||||
{
|
||||
tlDHNodeFail,
|
||||
ARRAYSIZE ( tlDHNodeFail ),
|
||||
0,
|
||||
0,
|
||||
"DH NodeFail"
|
||||
},
|
||||
};
|
||||
|
||||
|
||||
DEFINE_CUSTOM_SCHEDULES( CDunwichHorror )
|
||||
{
|
||||
slDHBigNode,
|
||||
slDHNodeFail,
|
||||
};
|
||||
|
||||
IMPLEMENT_CUSTOM_SCHEDULES( CDunwichHorror, CBaseMonster );
|
||||
|
||||
|
||||
|
||||
|
||||
Schedule_t *CDunwichHorror::GetScheduleOfType( int Type )
|
||||
{
|
||||
switch( Type )
|
||||
{
|
||||
case SCHED_DH_NODE:
|
||||
return slDHBigNode;
|
||||
break;
|
||||
|
||||
case SCHED_NODE_FAIL:
|
||||
return slDHNodeFail;
|
||||
break;
|
||||
}
|
||||
|
||||
return CBaseMonster::GetScheduleOfType( Type );
|
||||
}
|
||||
|
||||
|
||||
BOOL CDunwichHorror::ShouldGoToNode( void )
|
||||
{
|
||||
if ( HasMemory( bits_MEMORY_ADVANCE_NODE ) )
|
||||
{
|
||||
if ( m_nodeTime < gpGlobals->time )
|
||||
return TRUE;
|
||||
}
|
||||
return FALSE;
|
||||
}
|
||||
|
||||
// Overridden to make Dunwich Horror jump on command; the model doesn't support it otherwise.
|
||||
void CDunwichHorror :: SetActivity ( Activity NewActivity )
|
||||
{
|
||||
int iSequence;
|
||||
|
||||
if (NewActivity == ACT_HOP)
|
||||
{
|
||||
iSequence = LookupSequence( "jump" );
|
||||
}
|
||||
else
|
||||
{
|
||||
iSequence = LookupActivity ( NewActivity );
|
||||
}
|
||||
|
||||
// Set to the desired anim, or default anim if the desired is not present
|
||||
if ( iSequence > ACTIVITY_NOT_AVAILABLE )
|
||||
{
|
||||
if ( pev->sequence != iSequence || !m_fSequenceLoops )
|
||||
{
|
||||
// don't reset frame between walk and run
|
||||
if ( !(m_Activity == ACT_WALK || m_Activity == ACT_RUN) || !(NewActivity == ACT_WALK || NewActivity == ACT_RUN))
|
||||
pev->frame = 0;
|
||||
}
|
||||
|
||||
pev->sequence = iSequence; // Set to the reset anim (if it's there)
|
||||
ResetSequenceInfo( );
|
||||
SetYawSpeed();
|
||||
}
|
||||
else
|
||||
{
|
||||
// Not available try to get default anim
|
||||
ALERT ( at_aiconsole, "%s has no sequence for act:%d\n", STRING(pev->classname), NewActivity );
|
||||
pev->sequence = 0; // Set to the reset anim (if it's there)
|
||||
}
|
||||
|
||||
m_Activity = NewActivity; // Go ahead and set this so it doesn't keep trying when the anim is not present
|
||||
|
||||
// In case someone calls this with something other than the ideal activity
|
||||
m_IdealActivity = m_Activity;
|
||||
}
|
||||
|
||||
Schedule_t *CDunwichHorror::GetSchedule( void )
|
||||
{
|
||||
if ( ShouldGoToNode() )
|
||||
{
|
||||
return GetScheduleOfType( SCHED_DH_NODE );
|
||||
}
|
||||
|
||||
return CBaseMonster::GetSchedule();
|
||||
}
|
||||
|
||||
|
||||
void CDunwichHorror::StartTask( Task_t *pTask )
|
||||
{
|
||||
switch ( pTask->iTask )
|
||||
{
|
||||
case TASK_FIND_NODE:
|
||||
{
|
||||
CBaseEntity *pTarget = m_hTargetEnt;
|
||||
if ( !HasMemory( bits_MEMORY_ADVANCE_NODE ) )
|
||||
{
|
||||
if ( pTarget )
|
||||
pev->netname = m_hTargetEnt->pev->target;
|
||||
}
|
||||
NodeStart( pev->netname );
|
||||
TaskComplete();
|
||||
ALERT( at_aiconsole, "BM: Found node %s\n", STRING(pev->netname) );
|
||||
}
|
||||
break;
|
||||
|
||||
case TASK_NODE_DELAY:
|
||||
m_nodeTime = gpGlobals->time + pTask->flData;
|
||||
TaskComplete();
|
||||
ALERT( at_aiconsole, "BM: FAIL! Delay %.2f\n", pTask->flData );
|
||||
break;
|
||||
|
||||
case TASK_PROCESS_NODE:
|
||||
ALERT( at_aiconsole, "BM: Reached node %s\n", STRING(pev->netname) );
|
||||
NodeReach();
|
||||
TaskComplete();
|
||||
break;
|
||||
|
||||
case TASK_PLAY_NODE_PRESEQUENCE:
|
||||
case TASK_PLAY_NODE_SEQUENCE:
|
||||
{
|
||||
int sequence;
|
||||
if ( pTask->iTask == TASK_PLAY_NODE_SEQUENCE )
|
||||
sequence = GetNodeSequence();
|
||||
else
|
||||
sequence = GetNodePresequence();
|
||||
|
||||
ALERT( at_aiconsole, "BM: Playing node sequence %s\n", STRING(sequence) );
|
||||
if ( sequence )
|
||||
{
|
||||
sequence = LookupSequence( STRING( sequence ) );
|
||||
if ( sequence != -1 )
|
||||
{
|
||||
pev->sequence = sequence;
|
||||
pev->frame = 0;
|
||||
ResetSequenceInfo( );
|
||||
ALERT( at_aiconsole, "BM: Sequence %s\n", STRING(GetNodeSequence()) );
|
||||
return;
|
||||
}
|
||||
}
|
||||
TaskComplete();
|
||||
}
|
||||
break;
|
||||
|
||||
case TASK_NODE_YAW:
|
||||
pev->ideal_yaw = GetNodeYaw();
|
||||
TaskComplete();
|
||||
break;
|
||||
|
||||
case TASK_WAIT_NODE:
|
||||
m_flWait = gpGlobals->time + GetNodeDelay();
|
||||
if ( m_hTargetEnt->pev->spawnflags & SF_INFOBM_WAIT )
|
||||
ALERT( at_aiconsole, "BM: Wait at node %s forever\n", STRING(pev->netname) );
|
||||
else
|
||||
ALERT( at_aiconsole, "BM: Wait at node %s for %.2f\n", STRING(pev->netname), GetNodeDelay() );
|
||||
break;
|
||||
|
||||
|
||||
case TASK_MOVE_TO_NODE_RANGE:
|
||||
{
|
||||
CBaseEntity *pTarget = m_hTargetEnt;
|
||||
if ( !pTarget )
|
||||
TaskFail();
|
||||
else
|
||||
{
|
||||
if ( (pTarget->pev->origin - pev->origin).Length() < GetNodeRange() )
|
||||
TaskComplete();
|
||||
else
|
||||
{
|
||||
Activity act = ACT_WALK;
|
||||
if ( pTarget->pev->spawnflags & SF_INFOBM_RUN )
|
||||
act = ACT_RUN;
|
||||
|
||||
m_vecMoveGoal = pTarget->pev->origin;
|
||||
if ( !MoveToTarget( act, 2 ) )
|
||||
{
|
||||
TaskFail();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
ALERT( at_aiconsole, "BM: Moving to node %s\n", STRING(pev->netname) );
|
||||
|
||||
break;
|
||||
|
||||
case TASK_MELEE_ATTACK1:
|
||||
// Play an attack sound here
|
||||
EMIT_SOUND_DYN( ENT(pev), CHAN_VOICE, RANDOM_SOUND_ARRAY(pAttackSounds), 1.0, ATTN_NORM, 0, PITCH_NORM );
|
||||
CBaseMonster::StartTask( pTask );
|
||||
break;
|
||||
|
||||
default:
|
||||
CBaseMonster::StartTask( pTask );
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
//=========================================================
|
||||
// RunTask
|
||||
//=========================================================
|
||||
void CDunwichHorror::RunTask( Task_t *pTask )
|
||||
{
|
||||
switch ( pTask->iTask )
|
||||
{
|
||||
case TASK_MOVE_TO_NODE_RANGE:
|
||||
{
|
||||
float distance;
|
||||
|
||||
if ( m_hTargetEnt == NULL )
|
||||
TaskFail();
|
||||
else
|
||||
{
|
||||
distance = ( m_vecMoveGoal - pev->origin ).Length2D();
|
||||
// Set the appropriate activity based on an overlapping range
|
||||
// overlap the range to prevent oscillation
|
||||
if ( (distance < GetNodeRange()) || MovementIsComplete() )
|
||||
{
|
||||
ALERT( at_aiconsole, "BM: Reached node!\n" );
|
||||
TaskComplete();
|
||||
RouteClear(); // Stop moving
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
break;
|
||||
|
||||
case TASK_WAIT_NODE:
|
||||
if ( m_hTargetEnt != NULL && (m_hTargetEnt->pev->spawnflags & SF_INFOBM_WAIT) )
|
||||
return;
|
||||
|
||||
if ( gpGlobals->time > m_flWaitFinished )
|
||||
TaskComplete();
|
||||
ALERT( at_aiconsole, "BM: The WAIT is over!\n" );
|
||||
break;
|
||||
|
||||
case TASK_PLAY_NODE_PRESEQUENCE:
|
||||
case TASK_PLAY_NODE_SEQUENCE:
|
||||
if ( m_fSequenceFinished )
|
||||
{
|
||||
m_Activity = ACT_RESET;
|
||||
TaskComplete();
|
||||
}
|
||||
break;
|
||||
|
||||
default:
|
||||
CBaseMonster::RunTask( pTask );
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
#endif
|
|
@ -0,0 +1,493 @@
|
|||
/***
|
||||
*
|
||||
* Copyright (c) 1999, 2000 Valve LLC. All rights reserved.
|
||||
*
|
||||
* This product contains software technology licensed from Id
|
||||
* Software, Inc. ("Id Technology"). Id Technology (c) 1996 Id Software, Inc.
|
||||
* All Rights Reserved.
|
||||
*
|
||||
* This source code contains proprietary and confidential information of
|
||||
* Valve LLC and its suppliers. Access to this code is restricted to
|
||||
* persons who have executed a written SDK license with Valve. Any access,
|
||||
* use or distribution of this code by or to any unlicensed person is illegal.
|
||||
*
|
||||
****/
|
||||
//=========================================================
|
||||
// Formless Spawn
|
||||
//=========================================================
|
||||
|
||||
// UNDONE: Don't flinch every time you get hit
|
||||
|
||||
#include "extdll.h"
|
||||
#include "util.h"
|
||||
#include "cbase.h"
|
||||
#include "monsters.h"
|
||||
#include "schedule.h"
|
||||
#include "game.h"
|
||||
#include "soundent.h"
|
||||
|
||||
|
||||
//=========================================================
|
||||
// Monster's Anim Events Go Here
|
||||
//=========================================================
|
||||
#define FS_AE_ATTACK ( 1 )
|
||||
#define FS_AE_JUMPATTACK ( 2 )
|
||||
|
||||
#define FORMLESS_SPAWN_FLINCH_DELAY 3 // at most one flinch every n secs
|
||||
|
||||
|
||||
Task_t tlFSRangeAttack1[] =
|
||||
{
|
||||
{ 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 slFSRangeAttack1[] =
|
||||
{
|
||||
{
|
||||
tlFSRangeAttack1,
|
||||
ARRAYSIZE ( tlFSRangeAttack1 ),
|
||||
bits_COND_ENEMY_OCCLUDED |
|
||||
bits_COND_NO_AMMO_LOADED,
|
||||
0,
|
||||
"FSRangeAttack1"
|
||||
},
|
||||
};
|
||||
|
||||
|
||||
#include "FormlessSpawn.h"
|
||||
|
||||
|
||||
LINK_ENTITY_TO_CLASS( monster_formless_spawn, CFormlessSpawn );
|
||||
|
||||
DEFINE_CUSTOM_SCHEDULES( CFormlessSpawn )
|
||||
{
|
||||
slFSRangeAttack1,
|
||||
};
|
||||
|
||||
IMPLEMENT_CUSTOM_SCHEDULES( CFormlessSpawn, CBaseMonster );
|
||||
|
||||
const char *CFormlessSpawn::pAttackHitSounds[] =
|
||||
{
|
||||
"zombie/claw_strike1.wav",
|
||||
"zombie/claw_strike2.wav",
|
||||
"zombie/claw_strike3.wav",
|
||||
};
|
||||
|
||||
const char *CFormlessSpawn::pAttackMissSounds[] =
|
||||
{
|
||||
"zombie/claw_miss1.wav",
|
||||
"zombie/claw_miss2.wav",
|
||||
};
|
||||
|
||||
const char *CFormlessSpawn::pAttackSounds[] =
|
||||
{
|
||||
"formless_spawn/fs_attack1.wav",
|
||||
"formless_spawn/fs_attack2.wav",
|
||||
};
|
||||
|
||||
const char *CFormlessSpawn::pIdleSounds[] =
|
||||
{
|
||||
"formless_spawn/fs_idle1.wav",
|
||||
"formless_spawn/fs_idle2.wav",
|
||||
};
|
||||
|
||||
const char *CFormlessSpawn::pAlertSounds[] =
|
||||
{
|
||||
"formless_spawn/fs_alert1.wav",
|
||||
"formless_spawn/fs_alert2.wav",
|
||||
};
|
||||
|
||||
const char *CFormlessSpawn::pPainSounds[] =
|
||||
{
|
||||
"formless_spawn/fs_pain1.wav",
|
||||
"formless_spawn/fs_pain2.wav",
|
||||
};
|
||||
|
||||
//=========================================================
|
||||
// Classify - indicates this monster's place in the
|
||||
// relationship table.
|
||||
//=========================================================
|
||||
int CFormlessSpawn :: Classify ( void )
|
||||
{
|
||||
return m_iClass?m_iClass:CLASS_ALIEN_MONSTER;
|
||||
}
|
||||
|
||||
//=========================================================
|
||||
// SetYawSpeed - allows each sequence to have a different
|
||||
// turn rate associated with it.
|
||||
//=========================================================
|
||||
void CFormlessSpawn :: SetYawSpeed ( void )
|
||||
{
|
||||
int ys;
|
||||
|
||||
switch ( m_Activity )
|
||||
{
|
||||
case ACT_IDLE:
|
||||
ys = 120;
|
||||
break;
|
||||
case ACT_RUN:
|
||||
case ACT_WALK:
|
||||
ys = 120;
|
||||
break;
|
||||
case ACT_TURN_LEFT:
|
||||
case ACT_TURN_RIGHT:
|
||||
ys = 120;
|
||||
break;
|
||||
case ACT_RANGE_ATTACK1:
|
||||
ys = 120;
|
||||
break;
|
||||
default:
|
||||
ys = 120;
|
||||
break;
|
||||
}
|
||||
|
||||
pev->yaw_speed = ys;
|
||||
}
|
||||
|
||||
int CFormlessSpawn :: TakeDamage( entvars_t *pevInflictor, entvars_t *pevAttacker, float flDamage, int bitsDamageType )
|
||||
{
|
||||
// Take 20% damage from bullets
|
||||
if ( bitsDamageType & DMG_CRUSH ) return 0;
|
||||
if ( bitsDamageType & DMG_SLASH ) return 0;
|
||||
if ( bitsDamageType & DMG_FALL ) return 0;
|
||||
if ( bitsDamageType & DMG_CLUB ) return 0;
|
||||
if ( bitsDamageType & DMG_DROWN ) return 0;
|
||||
|
||||
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.1;
|
||||
}
|
||||
|
||||
// HACK HACK -- until we fix this.
|
||||
if ( IsAlive() )
|
||||
PainSound();
|
||||
return CBaseMonster::TakeDamage( pevInflictor, pevAttacker, flDamage, bitsDamageType );
|
||||
}
|
||||
|
||||
void CFormlessSpawn :: 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 CFormlessSpawn :: 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 CFormlessSpawn :: IdleSound( void )
|
||||
{
|
||||
int pitch = 95 + RANDOM_LONG(0,9);
|
||||
|
||||
// Play a random idle sound
|
||||
EMIT_SOUND_DYN ( ENT(pev), CHAN_VOICE, pIdleSounds[ RANDOM_LONG(0,ARRAYSIZE(pIdleSounds)-1) ], 1.0, ATTN_NORM, 0, 100 + RANDOM_LONG(-5,5) );
|
||||
}
|
||||
|
||||
void CFormlessSpawn :: AttackSound( void )
|
||||
{
|
||||
// Play a random attack sound
|
||||
EMIT_SOUND_DYN ( ENT(pev), CHAN_VOICE, pAttackSounds[ RANDOM_LONG(0,ARRAYSIZE(pAttackSounds)-1) ], 1.0, ATTN_NORM, 0, 100 + RANDOM_LONG(-5,5) );
|
||||
}
|
||||
|
||||
|
||||
//=========================================================
|
||||
// HandleAnimEvent - catches the monster-specific messages
|
||||
// that occur when tagged animation frames are played.
|
||||
//=========================================================
|
||||
void CFormlessSpawn :: HandleAnimEvent( MonsterEvent_t *pEvent )
|
||||
{
|
||||
switch( pEvent->event )
|
||||
{
|
||||
case FS_AE_ATTACK:
|
||||
{
|
||||
// do stuff for this event.
|
||||
// ALERT( at_console, "Slash right!\n" );
|
||||
CBaseEntity *pHurt = CheckTraceHullAttack( 70, gSkillData.formless_spawnDmgAttack, 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 FS_AE_JUMPATTACK:
|
||||
{
|
||||
ClearBits( pev->flags, FL_ONGROUND );
|
||||
|
||||
UTIL_SetOrigin (this, pev->origin + Vector ( 0 , 0 , 1) );// take him off ground so engine doesn't instantly reset onground
|
||||
UTIL_MakeVectors ( pev->angles );
|
||||
|
||||
Vector vdir = (pev->angles).Normalize();
|
||||
UTIL_SetOrigin (this, pev->origin - (8*vdir) );// move him back a bit
|
||||
BOOL bOK = WALK_MOVE ( ENT(pev), 0,1, WALKMOVE_NORMAL );
|
||||
if (!bOK)
|
||||
UTIL_SetOrigin (this, pev->origin + (8*vdir) );// move him forward a bit
|
||||
UTIL_MakeVectors ( pev->angles );
|
||||
|
||||
Vector vecJumpDir;
|
||||
if (m_hEnemy != NULL)
|
||||
{
|
||||
float gravity = g_psv_gravity->value;
|
||||
if (gravity <= 1)
|
||||
gravity = 1;
|
||||
|
||||
// How fast does the headcrab need to travel to reach that height given gravity?
|
||||
float height = (m_hEnemy->pev->origin.z + m_hEnemy->pev->view_ofs.z - pev->origin.z);
|
||||
if (height < 16)
|
||||
height = 16;
|
||||
float speed = sqrt( 2 * gravity * height );
|
||||
float time = speed / gravity;
|
||||
|
||||
// Scale the sideways velocity to get there at the right time
|
||||
vecJumpDir = (m_hEnemy->pev->origin + m_hEnemy->pev->view_ofs - pev->origin);
|
||||
vecJumpDir = vecJumpDir * ( 1.0 / time );
|
||||
|
||||
// Speed to offset gravity at the desired height
|
||||
vecJumpDir.z = speed;
|
||||
|
||||
// Don't jump too far/fast
|
||||
float distance = vecJumpDir.Length();
|
||||
|
||||
if (distance > 650)
|
||||
{
|
||||
vecJumpDir = vecJumpDir * ( 650.0 / distance );
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
// jump hop, don't care where
|
||||
vecJumpDir = Vector( gpGlobals->v_forward.x, gpGlobals->v_forward.y, gpGlobals->v_up.z ) * 350;
|
||||
}
|
||||
|
||||
int iSound = RANDOM_LONG(0,2);
|
||||
if ( iSound != 0 )
|
||||
EMIT_SOUND_DYN( edict(), CHAN_VOICE, pAttackSounds[iSound], 1.0, ATTN_IDLE, 0, 100 );
|
||||
|
||||
pev->velocity = vecJumpDir;
|
||||
m_flNextAttack = gpGlobals->time + 2;
|
||||
}
|
||||
break;
|
||||
|
||||
default:
|
||||
CBaseMonster::HandleAnimEvent( pEvent );
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
//=========================================================
|
||||
// Spawn
|
||||
//=========================================================
|
||||
void CFormlessSpawn :: Spawn()
|
||||
{
|
||||
Precache( );
|
||||
|
||||
if (pev->model)
|
||||
SET_MODEL(ENT(pev), STRING(pev->model)); //LRC
|
||||
else
|
||||
SET_MODEL(ENT(pev), "models/formless_spawn.mdl");
|
||||
UTIL_SetSize( pev, VEC_HUMAN_HULL_MIN, VEC_HUMAN_HULL_MAX );
|
||||
|
||||
pev->solid = SOLID_SLIDEBOX;
|
||||
pev->movetype = MOVETYPE_STEP;
|
||||
m_bloodColor = BLOOD_COLOR_GREEN;
|
||||
if (pev->health == 0)
|
||||
pev->health = gSkillData.formless_spawnHealth;
|
||||
pev->view_ofs = VEC_VIEW;// 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;
|
||||
m_afCapability = bits_CAP_DOORS_GROUP;
|
||||
|
||||
MonsterInit();
|
||||
}
|
||||
|
||||
//=========================================================
|
||||
// Precache - precaches all resources this monster needs
|
||||
//=========================================================
|
||||
void CFormlessSpawn :: Precache()
|
||||
{
|
||||
int i;
|
||||
|
||||
if (pev->model)
|
||||
PRECACHE_MODEL((char*)STRING(pev->model)); //LRC
|
||||
else
|
||||
PRECACHE_MODEL("models/formless_spawn.mdl");
|
||||
|
||||
for ( i = 0; i < ARRAYSIZE( pAttackHitSounds ); i++ )
|
||||
PRECACHE_SOUND((char *)pAttackHitSounds[i]);
|
||||
|
||||
for ( i = 0; i < ARRAYSIZE( pAttackMissSounds ); i++ )
|
||||
PRECACHE_SOUND((char *)pAttackMissSounds[i]);
|
||||
|
||||
for ( i = 0; i < ARRAYSIZE( pAttackSounds ); i++ )
|
||||
PRECACHE_SOUND((char *)pAttackSounds[i]);
|
||||
|
||||
for ( i = 0; i < ARRAYSIZE( pIdleSounds ); i++ )
|
||||
PRECACHE_SOUND((char *)pIdleSounds[i]);
|
||||
|
||||
for ( i = 0; i < ARRAYSIZE( pAlertSounds ); i++ )
|
||||
PRECACHE_SOUND((char *)pAlertSounds[i]);
|
||||
|
||||
for ( i = 0; i < ARRAYSIZE( pPainSounds ); i++ )
|
||||
PRECACHE_SOUND((char *)pPainSounds[i]);
|
||||
}
|
||||
|
||||
//=========================================================
|
||||
// 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 CFormlessSpawn :: ISoundMask ( void )
|
||||
{
|
||||
return bits_SOUND_WORLD |
|
||||
bits_SOUND_COMBAT |
|
||||
bits_SOUND_PLAYER;
|
||||
}
|
||||
|
||||
//=========================================================
|
||||
// AI Schedules Specific to this monster
|
||||
//=========================================================
|
||||
|
||||
|
||||
int CFormlessSpawn::IgnoreConditions ( void )
|
||||
{
|
||||
int iIgnore = CBaseMonster::IgnoreConditions();
|
||||
|
||||
if ((m_Activity == ACT_MELEE_ATTACK1) || (m_Activity == ACT_MELEE_ATTACK2))
|
||||
{
|
||||
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 + FORMLESS_SPAWN_FLINCH_DELAY;
|
||||
}
|
||||
|
||||
return iIgnore;
|
||||
|
||||
}
|
||||
|
||||
//=========================================================
|
||||
// RunTask
|
||||
//=========================================================
|
||||
void CFormlessSpawn :: 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);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void CFormlessSpawn :: StartTask ( Task_t *pTask )
|
||||
{
|
||||
m_iTaskStatus = TASKSTATUS_RUNNING;
|
||||
|
||||
switch ( pTask->iTask )
|
||||
{
|
||||
case TASK_RANGE_ATTACK1:
|
||||
{
|
||||
EMIT_SOUND_DYN( edict(), CHAN_WEAPON, pAttackSounds[0], 1.0, ATTN_IDLE, 0, 100 );
|
||||
SetTouch ( LeapTouch );
|
||||
m_IdealActivity = ACT_RANGE_ATTACK1;
|
||||
break;
|
||||
}
|
||||
default:
|
||||
{
|
||||
CBaseMonster :: StartTask( pTask );
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
//=========================================================
|
||||
// LeapTouch - this is the headcrab's touch function when it
|
||||
// is in the air
|
||||
//=========================================================
|
||||
void CFormlessSpawn :: 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(pAttackSounds), 1.0, ATTN_IDLE, 0, 100 );
|
||||
|
||||
pOther->TakeDamage( pev, pev, gSkillData.formless_spawnDmgAttack, DMG_SLASH );
|
||||
}
|
||||
|
||||
SetTouch( NULL );
|
||||
}
|
||||
|
||||
//=========================================================
|
||||
// CheckRangeAttack1
|
||||
//=========================================================
|
||||
BOOL CFormlessSpawn :: CheckRangeAttack1 ( float flDot, float flDist )
|
||||
{
|
||||
if ( FBitSet( pev->flags, FL_ONGROUND ) && flDist > 64 && flDist <= 512 && flDot >= 0.65 )
|
||||
{
|
||||
return TRUE;
|
||||
}
|
||||
return FALSE;
|
||||
}
|
||||
|
||||
Schedule_t* CFormlessSpawn :: GetScheduleOfType ( int Type )
|
||||
{
|
||||
switch ( Type )
|
||||
{
|
||||
case SCHED_RANGE_ATTACK1:
|
||||
{
|
||||
return &slFSRangeAttack1[ 0 ];
|
||||
}
|
||||
break;
|
||||
}
|
||||
|
||||
return CBaseMonster::GetScheduleOfType( Type );
|
||||
}
|
|
@ -0,0 +1,45 @@
|
|||
|
||||
#ifndef FORMLESSSPAWN_H
|
||||
#define FORMLESSSPAWN_H
|
||||
|
||||
class CFormlessSpawn : public CBaseMonster
|
||||
{
|
||||
public:
|
||||
void Spawn( void );
|
||||
void Precache( void );
|
||||
void RunTask ( Task_t *pTask );
|
||||
void StartTask ( Task_t *pTask );
|
||||
void SetYawSpeed( void );
|
||||
int Classify ( void );
|
||||
void HandleAnimEvent( MonsterEvent_t *pEvent );
|
||||
int IgnoreConditions ( void );
|
||||
void EXPORT LeapTouch ( CBaseEntity *pOther );
|
||||
virtual int ISoundMask( 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 );
|
||||
BOOL CheckRangeAttack2 ( float flDot, float flDist ) { return FALSE; };
|
||||
int TakeDamage( entvars_t *pevInflictor, entvars_t *pevAttacker, float flDamage, int bitsDamageType );
|
||||
|
||||
Schedule_t* GetScheduleOfType ( int Type );
|
||||
|
||||
CUSTOM_SCHEDULES;
|
||||
};
|
||||
|
||||
|
||||
#endif
|
||||
|
File diff suppressed because it is too large
Load Diff
|
@ -0,0 +1,88 @@
|
|||
|
||||
#ifndef GANGSTER_H
|
||||
#define GANGSTER_H
|
||||
|
||||
class CGangster : public CSquadMonster
|
||||
{
|
||||
public:
|
||||
void Spawn( void );
|
||||
void Precache( void );
|
||||
void SetYawSpeed ( void );
|
||||
int Classify ( void );
|
||||
int ISoundMask ( void );
|
||||
void HandleAnimEvent( MonsterEvent_t *pEvent );
|
||||
BOOL FCanCheckAttacks ( void );
|
||||
BOOL CheckMeleeAttack1 ( float flDot, float flDist );
|
||||
BOOL CheckRangeAttack1 ( float flDot, float flDist );
|
||||
BOOL CheckRangeAttack2 ( float flDot, float flDist );
|
||||
void CheckAmmo ( void );
|
||||
virtual void SetActivity ( Activity NewActivity );
|
||||
void StartTask ( Task_t *pTask );
|
||||
void RunTask ( Task_t *pTask );
|
||||
void DeathSound( void );
|
||||
void PainSound( void );
|
||||
void IdleSound ( void );
|
||||
Vector GetGunPosition( void );
|
||||
void Shoot ( void );
|
||||
void Shotgun ( void );
|
||||
void Revolver ( void );
|
||||
void PrescheduleThink ( void );
|
||||
void GibMonster( void );
|
||||
void SpeakSentence( void );
|
||||
|
||||
int Save( CSave &save );
|
||||
int Restore( CRestore &restore );
|
||||
|
||||
CBaseEntity *Kick( void );
|
||||
Schedule_t *GetSchedule( void );
|
||||
Schedule_t *GetScheduleOfType ( int Type );
|
||||
void TraceAttack( entvars_t *pevAttacker, float flDamage, Vector vecDir, TraceResult *ptr, int bitsDamageType);
|
||||
int TakeDamage( entvars_t *pevInflictor, entvars_t *pevAttacker, float flDamage, int bitsDamageType );
|
||||
|
||||
int IRelationship ( CBaseEntity *pTarget );
|
||||
|
||||
BOOL FOkToSpeak( void );
|
||||
void JustSpoke( void );
|
||||
|
||||
CUSTOM_SCHEDULES;
|
||||
static TYPEDESCRIPTION m_SaveData[];
|
||||
|
||||
float m_flNextPainTime;
|
||||
float m_flLastEnemySightTime;
|
||||
|
||||
BOOL m_fStanding;
|
||||
BOOL m_fFirstEncounter;// only put on the handsign show in the squad's first encounter.
|
||||
int m_cClipSize;
|
||||
|
||||
int m_voicePitch;
|
||||
|
||||
int m_iBrassShell;
|
||||
int m_iShotgunShell;
|
||||
|
||||
int m_iSentence;
|
||||
|
||||
static const char *pGangsterSentences[];
|
||||
};
|
||||
|
||||
|
||||
/////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
|
||||
class CDeadGangster : public CBaseMonster
|
||||
{
|
||||
public:
|
||||
void Spawn( void );
|
||||
int Classify ( void ) { return CLASS_HUMAN_MILITARY; }
|
||||
|
||||
void KeyValue( KeyValueData *pkvd );
|
||||
|
||||
int m_iPose;// which sequence to display -- temporary, don't need to save
|
||||
static char *m_szPoses[3];
|
||||
};
|
||||
|
||||
|
||||
|
||||
#endif
|
||||
|
||||
|
||||
|
|
@ -0,0 +1,330 @@
|
|||
/***
|
||||
*
|
||||
* Copyright (c) 1999, 2000 Valve LLC. All rights reserved.
|
||||
*
|
||||
* This product contains software technology licensed from Id
|
||||
* Software, Inc. ("Id Technology"). Id Technology (c) 1996 Id Software, Inc.
|
||||
* All Rights Reserved.
|
||||
*
|
||||
* This source code contains proprietary and confidential information of
|
||||
* Valve LLC and its suppliers. Access to this code is restricted to
|
||||
* persons who have executed a written SDK license with Valve. Any access,
|
||||
* use or distribution of this code by or to any unlicensed person is illegal.
|
||||
*
|
||||
****/
|
||||
//=========================================================
|
||||
// Ghoul
|
||||
//=========================================================
|
||||
|
||||
// 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 GHOUL_AE_ATTACK_RIGHT 0x01
|
||||
#define GHOUL_AE_ATTACK_LEFT 0x02
|
||||
#define GHOUL_AE_ATTACK_BOTH 0x03
|
||||
|
||||
#define GHOUL_FLINCH_DELAY 2 // at most one flinch every n secs
|
||||
|
||||
|
||||
#include "Ghoul.h"
|
||||
|
||||
|
||||
LINK_ENTITY_TO_CLASS( monster_ghoul, CGhoul );
|
||||
|
||||
const char *CGhoul::pAttackHitSounds[] =
|
||||
{
|
||||
"zombie/claw_strike1.wav",
|
||||
"zombie/claw_strike2.wav",
|
||||
"zombie/claw_strike3.wav",
|
||||
};
|
||||
|
||||
const char *CGhoul::pAttackMissSounds[] =
|
||||
{
|
||||
"zombie/claw_miss1.wav",
|
||||
"zombie/claw_miss2.wav",
|
||||
};
|
||||
|
||||
const char *CGhoul::pAttackSounds[] =
|
||||
{
|
||||
"ghoul/gh_attack1.wav",
|
||||
// "ghoul/gh_attack2.wav",
|
||||
};
|
||||
|
||||
const char *CGhoul::pIdleSounds[] =
|
||||
{
|
||||
"ghoul/gh_idle1.wav",
|
||||
"ghoul/gh_idle2.wav",
|
||||
"ghoul/gh_idle3.wav",
|
||||
// "ghoul/gh_idle4.wav",
|
||||
};
|
||||
|
||||
const char *CGhoul::pAlertSounds[] =
|
||||
{
|
||||
"ghoul/gh_alert1.wav",
|
||||
// "ghoul/gh_alert20.wav",
|
||||
// "ghoul/gh_alert30.wav",
|
||||
};
|
||||
|
||||
const char *CGhoul::pPainSounds[] =
|
||||
{
|
||||
"ghoul/gh_pain1.wav",
|
||||
// "ghoul/gh_pain2.wav",
|
||||
};
|
||||
|
||||
//=========================================================
|
||||
// Classify - indicates this monster's place in the
|
||||
// relationship table.
|
||||
//=========================================================
|
||||
int CGhoul :: Classify ( void )
|
||||
{
|
||||
return m_iClass?m_iClass:CLASS_ALIEN_MONSTER;
|
||||
}
|
||||
|
||||
//=========================================================
|
||||
// SetYawSpeed - allows each sequence to have a different
|
||||
// turn rate associated with it.
|
||||
//=========================================================
|
||||
void CGhoul :: SetYawSpeed ( void )
|
||||
{
|
||||
int ys;
|
||||
|
||||
ys = 120;
|
||||
|
||||
#if 0
|
||||
switch ( m_Activity )
|
||||
{
|
||||
}
|
||||
#endif
|
||||
|
||||
pev->yaw_speed = ys;
|
||||
}
|
||||
|
||||
int CGhoul :: 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 CGhoul :: PainSound( void )
|
||||
{
|
||||
//int pitch = 95 + RANDOM_LONG(0,9);
|
||||
int pitch = 125 + 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 CGhoul :: AlertSound( void )
|
||||
{
|
||||
//int pitch = 95 + RANDOM_LONG(0,9);
|
||||
int pitch = 125 + 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 CGhoul :: IdleSound( void )
|
||||
{
|
||||
//int pitch = 95 + RANDOM_LONG(0,9);
|
||||
int pitch = 125 + RANDOM_LONG(0,9);
|
||||
|
||||
// Play a random idle sound
|
||||
EMIT_SOUND_DYN ( ENT(pev), CHAN_VOICE, pIdleSounds[ RANDOM_LONG(0,ARRAYSIZE(pIdleSounds)-1) ], 0.2, ATTN_NORM, 0, pitch );
|
||||
}
|
||||
|
||||
void CGhoul :: AttackSound( void )
|
||||
{
|
||||
int pitch = 125 + RANDOM_LONG(0,9);
|
||||
|
||||
// Play a random attack sound
|
||||
EMIT_SOUND_DYN ( ENT(pev), CHAN_VOICE, pAttackSounds[ RANDOM_LONG(0,ARRAYSIZE(pAttackSounds)-1) ], 0.5, ATTN_NORM, 0, pitch );
|
||||
}
|
||||
|
||||
|
||||
//=========================================================
|
||||
// HandleAnimEvent - catches the monster-specific messages
|
||||
// that occur when tagged animation frames are played.
|
||||
//=========================================================
|
||||
void CGhoul :: HandleAnimEvent( MonsterEvent_t *pEvent )
|
||||
{
|
||||
switch( pEvent->event )
|
||||
{
|
||||
case GHOUL_AE_ATTACK_RIGHT:
|
||||
{
|
||||
// do stuff for this event.
|
||||
// ALERT( at_console, "Slash right!\n" );
|
||||
CBaseEntity *pHurt = CheckTraceHullAttack( 70, gSkillData.ghoulDmgOneSlash, 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 GHOUL_AE_ATTACK_LEFT:
|
||||
{
|
||||
// do stuff for this event.
|
||||
// ALERT( at_console, "Slash left!\n" );
|
||||
CBaseEntity *pHurt = CheckTraceHullAttack( 70, gSkillData.ghoulDmgOneSlash, 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 GHOUL_AE_ATTACK_BOTH:
|
||||
{
|
||||
// do stuff for this event.
|
||||
CBaseEntity *pHurt = CheckTraceHullAttack( 70, gSkillData.ghoulDmgBothSlash, 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 CGhoul :: Spawn()
|
||||
{
|
||||
Precache( );
|
||||
|
||||
if (pev->model)
|
||||
SET_MODEL(ENT(pev), STRING(pev->model)); //LRC
|
||||
else
|
||||
SET_MODEL(ENT(pev), "models/ghoul.mdl");
|
||||
UTIL_SetSize( pev, VEC_HUMAN_HULL_MIN, VEC_HUMAN_HULL_MAX );
|
||||
|
||||
pev->solid = SOLID_SLIDEBOX;
|
||||
pev->movetype = MOVETYPE_STEP;
|
||||
m_bloodColor = BLOOD_COLOR_RED;
|
||||
if (pev->health == 0)
|
||||
pev->health = gSkillData.ghoulHealth;
|
||||
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 CGhoul :: Precache()
|
||||
{
|
||||
int i;
|
||||
|
||||
if (pev->model)
|
||||
PRECACHE_MODEL((char*)STRING(pev->model)); //LRC
|
||||
else
|
||||
PRECACHE_MODEL("models/ghoul.mdl");
|
||||
|
||||
for ( i = 0; i < ARRAYSIZE( pAttackHitSounds ); i++ )
|
||||
PRECACHE_SOUND((char *)pAttackHitSounds[i]);
|
||||
|
||||
for ( i = 0; i < ARRAYSIZE( pAttackMissSounds ); i++ )
|
||||
PRECACHE_SOUND((char *)pAttackMissSounds[i]);
|
||||
|
||||
for ( i = 0; i < ARRAYSIZE( pAttackSounds ); i++ )
|
||||
PRECACHE_SOUND((char *)pAttackSounds[i]);
|
||||
|
||||
for ( i = 0; i < ARRAYSIZE( pIdleSounds ); i++ )
|
||||
PRECACHE_SOUND((char *)pIdleSounds[i]);
|
||||
|
||||
for ( i = 0; i < ARRAYSIZE( pAlertSounds ); i++ )
|
||||
PRECACHE_SOUND((char *)pAlertSounds[i]);
|
||||
|
||||
for ( i = 0; i < ARRAYSIZE( pPainSounds ); i++ )
|
||||
PRECACHE_SOUND((char *)pPainSounds[i]);
|
||||
}
|
||||
|
||||
//=========================================================
|
||||
// AI Schedules Specific to this monster
|
||||
//=========================================================
|
||||
|
||||
int CGhoul::IgnoreConditions ( void )
|
||||
{
|
||||
int iIgnore = CBaseMonster::IgnoreConditions();
|
||||
|
||||
if ((m_Activity == ACT_MELEE_ATTACK1) || (m_Activity == ACT_MELEE_ATTACK2))
|
||||
{
|
||||
#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 + GHOUL_FLINCH_DELAY;
|
||||
}
|
||||
|
||||
return iIgnore;
|
||||
|
||||
}
|
|
@ -0,0 +1,39 @@
|
|||
|
||||
#ifndef GHOUL_H
|
||||
#define GHOUL_H
|
||||
|
||||
class CGhoul : 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 );
|
||||
};
|
||||
|
||||
|
||||
|
||||
#endif
|
||||
|
||||
|
|
@ -0,0 +1,780 @@
|
|||
/***
|
||||
*
|
||||
* Copyright (c) 1999, 2000 Valve LLC. All rights reserved.
|
||||
*
|
||||
* This product contains software technology licensed from Id
|
||||
* Software, Inc. ("Id Technology"). Id Technology (c) 1996 Id Software, Inc.
|
||||
* All Rights Reserved.
|
||||
*
|
||||
* This source code contains proprietary and confidential information of
|
||||
* Valve LLC and its suppliers. Access to this code is restricted to
|
||||
* persons who have executed a written SDK license with Valve. Any access,
|
||||
* use or distribution of this code by or to any unlicensed person is illegal.
|
||||
*
|
||||
****/
|
||||
//=========================================================
|
||||
// Great Race of Yith 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 GREAT_RACE_AE_CLAW ( 1 )
|
||||
#define GREAT_RACE_AE_CLAWRAKE ( 2 )
|
||||
#define GREAT_RACE_AE_ZAP_POWERUP ( 3 )
|
||||
#define GREAT_RACE_AE_ZAP_SHOOT ( 4 )
|
||||
#define GREAT_RACE_AE_ZAP_DONE ( 5 )
|
||||
#define GREAT_RACE_DROP_GUN ( 6 )
|
||||
|
||||
#define GREAT_RACE_MAX_BEAMS 8
|
||||
|
||||
#define GREAT_RACE_LIGHTNING_GUN ( 1 << 0)
|
||||
#define GREAT_RACE_NONE ( 1 << 1)
|
||||
|
||||
#define GUN_GROUP 1
|
||||
#define GUN_LIGHTNING 0
|
||||
#define GUN_NONE 1
|
||||
|
||||
#include "GreatRace.h"
|
||||
|
||||
|
||||
LINK_ENTITY_TO_CLASS( monster_greatrace, CGreatRace );
|
||||
|
||||
|
||||
TYPEDESCRIPTION CGreatRace::m_SaveData[] =
|
||||
{
|
||||
DEFINE_ARRAY( CGreatRace, m_pBeam, FIELD_CLASSPTR, GREAT_RACE_MAX_BEAMS ),
|
||||
DEFINE_FIELD( CGreatRace, m_iBeams, FIELD_INTEGER ),
|
||||
DEFINE_FIELD( CGreatRace, m_flNextAttack, FIELD_TIME ),
|
||||
|
||||
DEFINE_FIELD( CGreatRace, m_voicePitch, FIELD_INTEGER ),
|
||||
};
|
||||
|
||||
IMPLEMENT_SAVERESTORE( CGreatRace, CSquadMonster );
|
||||
|
||||
|
||||
|
||||
|
||||
const char *CGreatRace::pAttackHitSounds[] =
|
||||
{
|
||||
"zombie/claw_strike1.wav",
|
||||
"zombie/claw_strike2.wav",
|
||||
"zombie/claw_strike3.wav",
|
||||
};
|
||||
|
||||
const char *CGreatRace::pAttackMissSounds[] =
|
||||
{
|
||||
"zombie/claw_miss1.wav",
|
||||
"zombie/claw_miss2.wav",
|
||||
};
|
||||
|
||||
const char *CGreatRace::pPainSounds[] =
|
||||
{
|
||||
"greatrace/gr_pain1.wav",
|
||||
"greatrace/gr_pain2.wav",
|
||||
};
|
||||
|
||||
const char *CGreatRace::pDeathSounds[] =
|
||||
{
|
||||
"greatrace/gr_die1.wav",
|
||||
"greatrace/gr_die2.wav",
|
||||
};
|
||||
|
||||
//=========================================================
|
||||
// Classify - indicates this monster's place in the
|
||||
// relationship table.
|
||||
//=========================================================
|
||||
int CGreatRace :: Classify ( void )
|
||||
{
|
||||
return m_iClass?m_iClass:CLASS_ALIEN_MILITARY;
|
||||
}
|
||||
|
||||
|
||||
int CGreatRace::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 CGreatRace :: CallForHelp( char *szClassname, float flDist, EHANDLE hEnemy, Vector &vecLocation )
|
||||
{
|
||||
// ALERT( at_aiconsole, "help " );
|
||||
|
||||
// skip ones not on my netname
|
||||
if ( FStringNull( pev->netname ))
|
||||
return;
|
||||
|
||||
CBaseEntity *pEntity = NULL;
|
||||
|
||||
while ((pEntity = UTIL_FindEntityByString( pEntity, "netname", STRING( pev->netname ))) != NULL)
|
||||
{
|
||||
float d = (pev->origin - pEntity->pev->origin).Length();
|
||||
if (d < flDist)
|
||||
{
|
||||
CBaseMonster *pMonster = pEntity->MyMonsterPointer( );
|
||||
if (pMonster)
|
||||
{
|
||||
pMonster->m_afMemory |= bits_MEMORY_PROVOKED;
|
||||
pMonster->PushEnemy( hEnemy, vecLocation );
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
//=========================================================
|
||||
// ALertSound - scream
|
||||
//=========================================================
|
||||
void CGreatRace :: AlertSound( void )
|
||||
{
|
||||
if ( m_hEnemy != NULL )
|
||||
{
|
||||
SENTENCEG_PlayRndSz(ENT(pev), "GRACE_ALERT", 0.85, ATTN_NORM, 0, m_voicePitch);
|
||||
|
||||
CallForHelp( "monster_greatrace", 512, m_hEnemy, m_vecEnemyLKP );
|
||||
CallForHelp( "monster_yodan", 512, m_hEnemy, m_vecEnemyLKP );
|
||||
}
|
||||
}
|
||||
|
||||
//=========================================================
|
||||
// IdleSound
|
||||
//=========================================================
|
||||
void CGreatRace :: IdleSound( void )
|
||||
{
|
||||
//if (RANDOM_LONG( 0, 1 ) == 0)
|
||||
//{
|
||||
SENTENCEG_PlayRndSz(ENT(pev), "GRACE_IDLE", 0.85, ATTN_NORM, 0, m_voicePitch);
|
||||
//}
|
||||
|
||||
}
|
||||
|
||||
//=========================================================
|
||||
// PainSound
|
||||
//=========================================================
|
||||
void CGreatRace :: PainSound( void )
|
||||
{
|
||||
if (RANDOM_LONG( 0, 1 ) == 0)
|
||||
{
|
||||
EMIT_SOUND_DYN ( ENT(pev), CHAN_WEAPON, pPainSounds[ RANDOM_LONG(0,ARRAYSIZE(pPainSounds)-1) ], 1.0, ATTN_NORM, 0, m_voicePitch );
|
||||
}
|
||||
}
|
||||
|
||||
//=========================================================
|
||||
// DieSound
|
||||
//=========================================================
|
||||
|
||||
void CGreatRace :: 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 CGreatRace :: ISoundMask ( void)
|
||||
{
|
||||
return bits_SOUND_WORLD |
|
||||
bits_SOUND_COMBAT |
|
||||
bits_SOUND_DANGER |
|
||||
bits_SOUND_PLAYER;
|
||||
}
|
||||
|
||||
|
||||
void CGreatRace::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 CGreatRace :: 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 CGreatRace :: HandleAnimEvent( MonsterEvent_t *pEvent )
|
||||
{
|
||||
// ALERT( at_console, "event %d : %f\n", pEvent->event, pev->frame );
|
||||
switch( pEvent->event )
|
||||
{
|
||||
case GREAT_RACE_AE_CLAW:
|
||||
{
|
||||
// SOUND HERE!
|
||||
CBaseEntity *pHurt = CheckTraceHullAttack( 70, gSkillData.greatraceDmgClaw, 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 GREAT_RACE_AE_CLAWRAKE:
|
||||
{
|
||||
CBaseEntity *pHurt = CheckTraceHullAttack( 70, gSkillData.greatraceDmgClawrake, 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 GREAT_RACE_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( );
|
||||
|
||||
}
|
||||
//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 GREAT_RACE_AE_ZAP_SHOOT:
|
||||
{
|
||||
ClearBeams( );
|
||||
|
||||
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( 2.0, 5.0 );
|
||||
}
|
||||
break;
|
||||
|
||||
case GREAT_RACE_AE_ZAP_DONE:
|
||||
{
|
||||
ClearBeams( );
|
||||
}
|
||||
break;
|
||||
|
||||
case GREAT_RACE_DROP_GUN:
|
||||
{
|
||||
if (GetBodygroup(GUN_GROUP) != GUN_LIGHTNING) break;
|
||||
if (pev->spawnflags & SF_MONSTER_NO_WPN_DROP) break;
|
||||
|
||||
Vector vecGunPos;
|
||||
Vector vecGunAngles;
|
||||
|
||||
GetAttachment( 0, vecGunPos, vecGunAngles );
|
||||
|
||||
// switch to body group with no gun.
|
||||
SetBodygroup( GUN_GROUP, GUN_NONE );
|
||||
|
||||
// now spawn a gun.
|
||||
DropItem( "weapon_lightninggun", vecGunPos, vecGunAngles );
|
||||
|
||||
}
|
||||
break;
|
||||
|
||||
default:
|
||||
CSquadMonster::HandleAnimEvent( pEvent );
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
//=========================================================
|
||||
// CheckRangeAttack1 - normal beam attack
|
||||
//=========================================================
|
||||
BOOL CGreatRace :: CheckRangeAttack1 ( float flDot, float flDist )
|
||||
{
|
||||
if (GetBodygroup(GUN_GROUP) == GUN_NONE)
|
||||
return FALSE;
|
||||
|
||||
if (m_flNextAttack > gpGlobals->time)
|
||||
{
|
||||
return FALSE;
|
||||
}
|
||||
|
||||
return CSquadMonster::CheckRangeAttack1( flDot, flDist );
|
||||
}
|
||||
|
||||
//=========================================================
|
||||
// CheckRangeAttack2 - check bravery and try to resurect dead comrades
|
||||
//=========================================================
|
||||
BOOL CGreatRace :: CheckRangeAttack2 ( float flDot, float flDist )
|
||||
{
|
||||
return FALSE;
|
||||
}
|
||||
|
||||
|
||||
//=========================================================
|
||||
// StartTask
|
||||
//=========================================================
|
||||
void CGreatRace :: StartTask ( Task_t *pTask )
|
||||
{
|
||||
ClearBeams( );
|
||||
|
||||
CSquadMonster :: StartTask ( pTask );
|
||||
}
|
||||
|
||||
|
||||
//=========================================================
|
||||
// Spawn
|
||||
//=========================================================
|
||||
void CGreatRace :: Spawn()
|
||||
{
|
||||
Precache( );
|
||||
|
||||
if (pev->model)
|
||||
SET_MODEL(ENT(pev), STRING(pev->model)); //LRC
|
||||
else
|
||||
SET_MODEL(ENT(pev), "models/greatrace.mdl");
|
||||
UTIL_SetSize(pev, VEC_HUMAN_HULL_MIN, VEC_HUMAN_HULL_MAX);
|
||||
|
||||
pev->solid = SOLID_SLIDEBOX;
|
||||
pev->movetype = MOVETYPE_STEP;
|
||||
m_bloodColor = BLOOD_COLOR_GREEN;
|
||||
pev->effects = 0;
|
||||
if (pev->health == 0)
|
||||
pev->health = gSkillData.greatraceHealth;
|
||||
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 );
|
||||
|
||||
if (FBitSet( pev->weapons, GREAT_RACE_LIGHTNING_GUN ))
|
||||
{
|
||||
SetBodygroup( GUN_GROUP, GUN_LIGHTNING );
|
||||
}
|
||||
else // none
|
||||
{
|
||||
SetBodygroup( GUN_GROUP, GUN_NONE );
|
||||
}
|
||||
|
||||
MonsterInit();
|
||||
}
|
||||
|
||||
//=========================================================
|
||||
// Precache - precaches all resources this monster needs
|
||||
//=========================================================
|
||||
void CGreatRace :: Precache()
|
||||
{
|
||||
int i;
|
||||
|
||||
if (pev->model)
|
||||
PRECACHE_MODEL((char*)STRING(pev->model)); //LRC
|
||||
else
|
||||
PRECACHE_MODEL("models/greatrace.mdl");
|
||||
PRECACHE_MODEL("sprites/lgtning.spr");
|
||||
PRECACHE_SOUND("debris/zap1.wav");
|
||||
PRECACHE_SOUND("debris/zap4.wav");
|
||||
PRECACHE_SOUND("weapons/electro4.wav");
|
||||
PRECACHE_SOUND("hassault/hw_shoot1.wav");
|
||||
PRECACHE_SOUND("zombie/zo_pain2.wav");
|
||||
PRECACHE_SOUND("headcrab/hc_headbite.wav");
|
||||
PRECACHE_SOUND("weapons/cbar_miss1.wav");
|
||||
|
||||
for ( i = 0; i < ARRAYSIZE( pAttackHitSounds ); i++ )
|
||||
PRECACHE_SOUND((char *)pAttackHitSounds[i]);
|
||||
|
||||
for ( i = 0; i < ARRAYSIZE( pAttackMissSounds ); i++ )
|
||||
PRECACHE_SOUND((char *)pAttackMissSounds[i]);
|
||||
|
||||
for ( i = 0; i < ARRAYSIZE( pPainSounds ); i++ )
|
||||
PRECACHE_SOUND((char *)pPainSounds[i]);
|
||||
|
||||
for ( i = 0; i < ARRAYSIZE( pDeathSounds ); i++ )
|
||||
PRECACHE_SOUND((char *)pDeathSounds[i]);
|
||||
|
||||
UTIL_PrecacheOther( "test_effect" );
|
||||
}
|
||||
|
||||
|
||||
//=========================================================
|
||||
// TakeDamage - get provoked when injured
|
||||
//=========================================================
|
||||
|
||||
int CGreatRace :: 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);
|
||||
}
|
||||
|
||||
//=========================================================
|
||||
// GibMonster - make gun fly through the air.
|
||||
//=========================================================
|
||||
void CGreatRace :: GibMonster ( void )
|
||||
{
|
||||
Vector vecGunPos;
|
||||
Vector vecGunAngles;
|
||||
|
||||
if (( GetBodygroup( GUN_GROUP ) != GUN_NONE ) && !(pev->spawnflags & SF_MONSTER_NO_WPN_DROP))
|
||||
{// throw a gun if the yodan has one
|
||||
GetAttachment( 0, vecGunPos, vecGunAngles );
|
||||
|
||||
CBaseEntity *pGun;
|
||||
pGun = DropItem( "weapon_lightninggun", vecGunPos, vecGunAngles );
|
||||
|
||||
if ( pGun )
|
||||
{
|
||||
pGun->pev->velocity = Vector (RANDOM_FLOAT(-100,100), RANDOM_FLOAT(-100,100), RANDOM_FLOAT(200,300));
|
||||
pGun->pev->avelocity = Vector ( 0, RANDOM_FLOAT( 200, 400 ), 0 );
|
||||
}
|
||||
}
|
||||
|
||||
CSquadMonster :: GibMonster();
|
||||
}
|
||||
|
||||
//=========================================================
|
||||
// AI Schedules Specific to this monster
|
||||
//=========================================================
|
||||
|
||||
|
||||
|
||||
// primary range attack
|
||||
Task_t tlGreatRaceAttack1[] =
|
||||
{
|
||||
{ TASK_STOP_MOVING, 0 },
|
||||
{ TASK_FACE_IDEAL, (float)0 },
|
||||
{ TASK_RANGE_ATTACK1, (float)0 },
|
||||
};
|
||||
|
||||
Schedule_t slGreatRaceAttack1[] =
|
||||
{
|
||||
{
|
||||
tlGreatRaceAttack1,
|
||||
ARRAYSIZE ( tlGreatRaceAttack1 ),
|
||||
bits_COND_CAN_MELEE_ATTACK1 |
|
||||
bits_COND_HEAR_SOUND |
|
||||
bits_COND_HEAVY_DAMAGE,
|
||||
|
||||
bits_SOUND_DANGER,
|
||||
"GreatRace Range Attack1"
|
||||
},
|
||||
};
|
||||
|
||||
|
||||
DEFINE_CUSTOM_SCHEDULES( CGreatRace )
|
||||
{
|
||||
slGreatRaceAttack1,
|
||||
};
|
||||
|
||||
IMPLEMENT_CUSTOM_SCHEDULES( CGreatRace, CSquadMonster );
|
||||
|
||||
|
||||
//=========================================================
|
||||
//=========================================================
|
||||
Schedule_t *CGreatRace :: 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)
|
||||
{
|
||||
if (!HasConditions( bits_COND_CAN_MELEE_ATTACK1 ))
|
||||
{
|
||||
m_failSchedule = SCHED_CHASE_ENEMY;
|
||||
if (HasConditions( bits_COND_LIGHT_DAMAGE | bits_COND_HEAVY_DAMAGE))
|
||||
{
|
||||
return GetScheduleOfType( SCHED_TAKE_COVER_FROM_ENEMY );
|
||||
}
|
||||
if ( HasConditions ( bits_COND_SEE_ENEMY ) && HasConditions ( bits_COND_ENEMY_FACING_ME ) )
|
||||
{
|
||||
// ALERT( at_console, "exposed\n");
|
||||
return GetScheduleOfType( SCHED_TAKE_COVER_FROM_ENEMY );
|
||||
}
|
||||
}
|
||||
}
|
||||
break;
|
||||
}
|
||||
return CSquadMonster::GetSchedule( );
|
||||
}
|
||||
|
||||
|
||||
Schedule_t *CGreatRace :: 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 slGreatRaceAttack1;
|
||||
case SCHED_RANGE_ATTACK2:
|
||||
return slGreatRaceAttack1;
|
||||
}
|
||||
return CSquadMonster :: GetScheduleOfType( Type );
|
||||
}
|
||||
|
||||
|
||||
//=========================================================
|
||||
// ArmBeam - small beam from arm to nearby geometry
|
||||
//=========================================================
|
||||
|
||||
void CGreatRace :: ArmBeam( int side )
|
||||
{
|
||||
TraceResult tr;
|
||||
float flDist = 1.0;
|
||||
|
||||
if (m_iBeams >= GREAT_RACE_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 CGreatRace :: 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 CGreatRace :: WackBeam( int side, CBaseEntity *pEntity )
|
||||
{
|
||||
Vector vecDest;
|
||||
float flDist = 1.0;
|
||||
|
||||
if (m_iBeams >= GREAT_RACE_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 CGreatRace :: ZapBeam( int side )
|
||||
{
|
||||
Vector vecSrc, vecAim;
|
||||
TraceResult tr;
|
||||
CBaseEntity *pEntity;
|
||||
|
||||
if (m_iBeams >= GREAT_RACE_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.greatraceDmgZap, 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 CGreatRace :: ClearBeams( )
|
||||
{
|
||||
for (int i = 0; i < GREAT_RACE_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" );
|
||||
}
|
|
@ -0,0 +1,59 @@
|
|||
|
||||
#ifndef GREAT_RACE_H
|
||||
#define GREAT_RACE_H
|
||||
|
||||
class CGreatRace : public CSquadMonster
|
||||
{
|
||||
public:
|
||||
void Spawn( void );
|
||||
void Precache( void );
|
||||
void SetYawSpeed( void );
|
||||
int ISoundMask( void );
|
||||
int Classify ( void );
|
||||
int IRelationship( CBaseEntity *pTarget );
|
||||
void HandleAnimEvent( MonsterEvent_t *pEvent );
|
||||
BOOL CheckRangeAttack1 ( float flDot, float flDist );
|
||||
BOOL CheckRangeAttack2 ( float flDot, float flDist );
|
||||
void CallForHelp( char *szClassname, float flDist, EHANDLE hEnemy, Vector &vecLocation );
|
||||
int TakeDamage( entvars_t* pevInflictor, entvars_t* pevAttacker, float flDamage, int bitsDamageType);
|
||||
void GibMonster( void );
|
||||
|
||||
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 );
|
||||
|
||||
CBeam *m_pBeam[GREAT_RACE_MAX_BEAMS];
|
||||
|
||||
int m_iBeams;
|
||||
float m_flNextAttack;
|
||||
|
||||
int m_voicePitch;
|
||||
|
||||
static const char *pAttackHitSounds[];
|
||||
static const char *pAttackMissSounds[];
|
||||
static const char *pPainSounds[];
|
||||
static const char *pDeathSounds[];
|
||||
};
|
||||
|
||||
|
||||
|
||||
|
||||
#endif
|
|
@ -0,0 +1,986 @@
|
|||
/***
|
||||
*
|
||||
* Copyright (c) 1999, 2000 Valve LLC. All rights reserved.
|
||||
*
|
||||
* This product contains software technology licensed from Id
|
||||
* Software, Inc. ("Id Technology"). Id Technology (c) 1996 Id Software, Inc.
|
||||
* All Rights Reserved.
|
||||
*
|
||||
* This source code contains proprietary and confidential information of
|
||||
* Valve LLC and its suppliers. Access to this code is restricted to
|
||||
* persons who have executed a written SDK license with Valve. Any access,
|
||||
* use or distribution of this code by or to any unlicensed person is illegal.
|
||||
*
|
||||
****/
|
||||
#if !defined( OEM_BUILD ) && !defined( HLDEMO_BUILD )
|
||||
|
||||
//=========================================================
|
||||
// hunting_horror
|
||||
//=========================================================
|
||||
|
||||
#include "extdll.h"
|
||||
#include "util.h"
|
||||
#include "cbase.h"
|
||||
#include "monsters.h"
|
||||
#include "schedule.h"
|
||||
#include "flyingmonster.h"
|
||||
#include "nodes.h"
|
||||
#include "soundent.h"
|
||||
#include "animation.h"
|
||||
#include "effects.h"
|
||||
#include "weapons.h"
|
||||
|
||||
#define SEARCH_RETRY 16
|
||||
|
||||
#define HUNTING_HORROR_SPEED 150
|
||||
|
||||
extern CGraph WorldGraph;
|
||||
|
||||
#define EYE_MAD 0
|
||||
#define EYE_BASE 1
|
||||
#define EYE_CLOSED 2
|
||||
#define EYE_BACK 3
|
||||
#define EYE_LOOK 4
|
||||
|
||||
|
||||
|
||||
//=========================================================
|
||||
// Monster's Anim Events Go Here
|
||||
//=========================================================
|
||||
|
||||
|
||||
#include "HuntingHorror.h"
|
||||
|
||||
|
||||
LINK_ENTITY_TO_CLASS( monster_huntinghorror, CHuntingHorror );
|
||||
|
||||
TYPEDESCRIPTION CHuntingHorror::m_SaveData[] =
|
||||
{
|
||||
DEFINE_FIELD( CHuntingHorror, m_SaveVelocity, FIELD_VECTOR ),
|
||||
DEFINE_FIELD( CHuntingHorror, m_idealDist, FIELD_FLOAT ),
|
||||
DEFINE_FIELD( CHuntingHorror, m_flEnemyTouched, FIELD_FLOAT ),
|
||||
DEFINE_FIELD( CHuntingHorror, m_bOnAttack, FIELD_BOOLEAN ),
|
||||
DEFINE_FIELD( CHuntingHorror, m_flMaxSpeed, FIELD_FLOAT ),
|
||||
DEFINE_FIELD( CHuntingHorror, m_flMinSpeed, FIELD_FLOAT ),
|
||||
DEFINE_FIELD( CHuntingHorror, m_flMaxDist, FIELD_FLOAT ),
|
||||
DEFINE_FIELD( CHuntingHorror, m_flNextAlert, FIELD_TIME ),
|
||||
};
|
||||
|
||||
IMPLEMENT_SAVERESTORE( CHuntingHorror, CFlyingMonster );
|
||||
|
||||
|
||||
const char *CHuntingHorror::pIdleSounds[] =
|
||||
{
|
||||
"hhorror/hh_idle1.wav",
|
||||
"hhorror/hh_idle2.wav",
|
||||
"hhorror/hh_idle3.wav",
|
||||
};
|
||||
|
||||
const char *CHuntingHorror::pAlertSounds[] =
|
||||
{
|
||||
"hhorror/hh_alert1.wav",
|
||||
"hhorror/hh_alert2.wav",
|
||||
"hhorror/hh_alert3.wav",
|
||||
};
|
||||
|
||||
const char *CHuntingHorror::pAttackSounds[] =
|
||||
{
|
||||
"hhorror/hh_attack1.wav",
|
||||
"hhorror/hh_attack2.wav",
|
||||
"hhorror/hh_attack3.wav",
|
||||
};
|
||||
|
||||
const char *CHuntingHorror::pBiteSounds[] =
|
||||
{
|
||||
"ichy/ichy_bite1.wav",
|
||||
"ichy/ichy_bite2.wav",
|
||||
};
|
||||
|
||||
const char *CHuntingHorror::pPainSounds[] =
|
||||
{
|
||||
"hhorror/hh_pain1.wav",
|
||||
"hhorror/hh_pain2.wav",
|
||||
"hhorror/hh_pain3.wav",
|
||||
};
|
||||
|
||||
const char *CHuntingHorror::pDieSounds[] =
|
||||
{
|
||||
"hhorror/hh_die1.wav",
|
||||
"hhorror/hh_die2.wav",
|
||||
"hhorror/hh_die3.wav",
|
||||
};
|
||||
|
||||
#define EMIT_HUNT_HORR_SOUND( chan, array ) \
|
||||
EMIT_SOUND_DYN ( ENT(pev), chan , array [ RANDOM_LONG(0,ARRAYSIZE( array )-1) ], 1.0, 0.6, 0, RANDOM_LONG(95,105) );
|
||||
|
||||
|
||||
void CHuntingHorror :: IdleSound( void )
|
||||
{
|
||||
EMIT_HUNT_HORR_SOUND( CHAN_VOICE, pIdleSounds );
|
||||
}
|
||||
|
||||
void CHuntingHorror :: AlertSound( void )
|
||||
{
|
||||
EMIT_HUNT_HORR_SOUND( CHAN_VOICE, pAlertSounds );
|
||||
}
|
||||
|
||||
void CHuntingHorror :: AttackSound( void )
|
||||
{
|
||||
EMIT_HUNT_HORR_SOUND( CHAN_VOICE, pAttackSounds );
|
||||
}
|
||||
|
||||
void CHuntingHorror :: BiteSound( void )
|
||||
{
|
||||
EMIT_HUNT_HORR_SOUND( CHAN_WEAPON, pBiteSounds );
|
||||
}
|
||||
|
||||
void CHuntingHorror :: DeathSound( void )
|
||||
{
|
||||
EMIT_HUNT_HORR_SOUND( CHAN_VOICE, pDieSounds );
|
||||
}
|
||||
|
||||
void CHuntingHorror :: PainSound( void )
|
||||
{
|
||||
EMIT_HUNT_HORR_SOUND( CHAN_VOICE, pPainSounds );
|
||||
}
|
||||
|
||||
//=========================================================
|
||||
// monster-specific tasks and states
|
||||
//=========================================================
|
||||
enum
|
||||
{
|
||||
TASK_HUNTING_HORROR_CIRCLE_ENEMY = LAST_COMMON_TASK + 1,
|
||||
TASK_HUNTING_HORROR_FLY,
|
||||
};
|
||||
|
||||
//=========================================================
|
||||
// AI Schedules Specific to this monster
|
||||
//=========================================================
|
||||
|
||||
static Task_t tlFlyAround[] =
|
||||
{
|
||||
{ TASK_SET_ACTIVITY, (float)ACT_WALK },
|
||||
{ TASK_HUNTING_HORROR_FLY, 0.0 },
|
||||
};
|
||||
|
||||
static Schedule_t slFlyAround[] =
|
||||
{
|
||||
{
|
||||
tlFlyAround,
|
||||
ARRAYSIZE(tlFlyAround),
|
||||
bits_COND_LIGHT_DAMAGE |
|
||||
bits_COND_HEAVY_DAMAGE |
|
||||
bits_COND_SEE_ENEMY |
|
||||
bits_COND_NEW_ENEMY |
|
||||
bits_COND_HEAR_SOUND,
|
||||
bits_SOUND_PLAYER |
|
||||
bits_SOUND_COMBAT,
|
||||
"FlyAround"
|
||||
},
|
||||
};
|
||||
|
||||
static Task_t tlFlyAgitated[] =
|
||||
{
|
||||
{ TASK_STOP_MOVING, (float) 0 },
|
||||
{ TASK_SET_ACTIVITY, (float)ACT_RUN },
|
||||
{ TASK_WAIT, (float)2.0 },
|
||||
};
|
||||
|
||||
static Schedule_t slFlyAgitated[] =
|
||||
{
|
||||
{
|
||||
tlFlyAgitated,
|
||||
ARRAYSIZE(tlFlyAgitated),
|
||||
0,
|
||||
0,
|
||||
"FlyAgitated"
|
||||
},
|
||||
};
|
||||
|
||||
|
||||
static Task_t tlCircleEnemy[] =
|
||||
{
|
||||
{ TASK_SET_ACTIVITY, (float)ACT_WALK },
|
||||
{ TASK_HUNTING_HORROR_CIRCLE_ENEMY, 0.0 },
|
||||
};
|
||||
|
||||
static Schedule_t slCircleEnemy[] =
|
||||
{
|
||||
{
|
||||
tlCircleEnemy,
|
||||
ARRAYSIZE(tlCircleEnemy),
|
||||
bits_COND_NEW_ENEMY |
|
||||
bits_COND_LIGHT_DAMAGE |
|
||||
bits_COND_HEAVY_DAMAGE |
|
||||
bits_COND_CAN_MELEE_ATTACK1 |
|
||||
bits_COND_CAN_RANGE_ATTACK1,
|
||||
0,
|
||||
"CircleEnemy"
|
||||
},
|
||||
};
|
||||
|
||||
|
||||
Task_t tlHuntHorrTwitchDie[] =
|
||||
{
|
||||
{ TASK_STOP_MOVING, 0 },
|
||||
{ TASK_SOUND_DIE, (float)0 },
|
||||
{ TASK_DIE, (float)0 },
|
||||
};
|
||||
|
||||
Schedule_t slHuntHorrTwitchDie[] =
|
||||
{
|
||||
{
|
||||
tlHuntHorrTwitchDie,
|
||||
ARRAYSIZE( tlHuntHorrTwitchDie ),
|
||||
0,
|
||||
0,
|
||||
"Die"
|
||||
},
|
||||
};
|
||||
|
||||
|
||||
DEFINE_CUSTOM_SCHEDULES(CHuntingHorror)
|
||||
{
|
||||
slFlyAround,
|
||||
slFlyAgitated,
|
||||
slCircleEnemy,
|
||||
slHuntHorrTwitchDie,
|
||||
};
|
||||
IMPLEMENT_CUSTOM_SCHEDULES(CHuntingHorror, CFlyingMonster);
|
||||
|
||||
//=========================================================
|
||||
// Classify - indicates this monster's place in the
|
||||
// relationship table.
|
||||
//=========================================================
|
||||
int CHuntingHorror :: Classify ( void )
|
||||
{
|
||||
return m_iClass?m_iClass:CLASS_ALIEN_MONSTER;
|
||||
}
|
||||
|
||||
|
||||
//=========================================================
|
||||
// CheckMeleeAttack1
|
||||
//=========================================================
|
||||
BOOL CHuntingHorror :: CheckMeleeAttack1 ( float flDot, float flDist )
|
||||
{
|
||||
if ( flDot >= 0.7 && m_flEnemyTouched > gpGlobals->time - 0.2 && flDist <= 64.0 )
|
||||
{
|
||||
return TRUE;
|
||||
}
|
||||
return FALSE;
|
||||
}
|
||||
|
||||
void CHuntingHorror::BiteTouch( CBaseEntity *pOther )
|
||||
{
|
||||
// bite if we hit who we want to eat
|
||||
if ( pOther == m_hEnemy )
|
||||
{
|
||||
m_flEnemyTouched = gpGlobals->time;
|
||||
m_bOnAttack = TRUE;
|
||||
}
|
||||
}
|
||||
|
||||
void CHuntingHorror::CombatUse( CBaseEntity *pActivator, CBaseEntity *pCaller, USE_TYPE useType, float value )
|
||||
{
|
||||
if ( !ShouldToggle( useType, m_bOnAttack ) )
|
||||
return;
|
||||
|
||||
if (m_bOnAttack)
|
||||
{
|
||||
m_bOnAttack = 0;
|
||||
}
|
||||
else
|
||||
{
|
||||
m_bOnAttack = 1;
|
||||
}
|
||||
}
|
||||
|
||||
//=========================================================
|
||||
// CheckRangeAttack1 - Fly in for a chomp
|
||||
//
|
||||
//=========================================================
|
||||
BOOL CHuntingHorror :: CheckRangeAttack1 ( float flDot, float flDist )
|
||||
{
|
||||
if ( flDot > -0.7 && (m_bOnAttack || ( flDist <= 384 && m_idealDist <= 384)))
|
||||
{
|
||||
return TRUE;
|
||||
}
|
||||
|
||||
return FALSE;
|
||||
}
|
||||
|
||||
//=========================================================
|
||||
// SetYawSpeed - allows each sequence to have a different
|
||||
// turn rate associated with it.
|
||||
//=========================================================
|
||||
void CHuntingHorror :: SetYawSpeed ( void )
|
||||
{
|
||||
pev->yaw_speed = 100;
|
||||
}
|
||||
|
||||
|
||||
|
||||
//=========================================================
|
||||
// Killed - overrides CFlyingMonster.
|
||||
//
|
||||
void CHuntingHorror :: Killed( entvars_t *pevAttacker, int iGib )
|
||||
{
|
||||
pev->velocity = Vector(0,0,-100);
|
||||
pev->gravity = 1.0;
|
||||
pev->angles.x = 0;
|
||||
|
||||
CFlyingMonster::Killed( pevAttacker, iGib );
|
||||
//CBaseMonster::Killed( pevAttacker, iGib );
|
||||
}
|
||||
|
||||
void CHuntingHorror::BecomeDead( void )
|
||||
{
|
||||
pev->takedamage = DAMAGE_YES;// don't let autoaim aim at corpses.
|
||||
|
||||
// give the corpse half of the monster's original maximum health.
|
||||
pev->health = pev->max_health / 2;
|
||||
pev->max_health = 5; // max_health now becomes a counter for how many blood decals the corpse can place.
|
||||
}
|
||||
|
||||
#define HUNTING_HORROR_AE_SHAKE_RIGHT 1
|
||||
#define HUNTING_HORROR_AE_SHAKE_LEFT 2
|
||||
|
||||
|
||||
//=========================================================
|
||||
// HandleAnimEvent - catches the monster-specific messages
|
||||
// that occur when tagged animation frames are played.
|
||||
//=========================================================
|
||||
void CHuntingHorror :: HandleAnimEvent( MonsterEvent_t *pEvent )
|
||||
{
|
||||
switch( pEvent->event )
|
||||
{
|
||||
case HUNTING_HORROR_AE_SHAKE_RIGHT:
|
||||
case HUNTING_HORROR_AE_SHAKE_LEFT:
|
||||
{
|
||||
if (m_hEnemy != NULL && FVisible( m_hEnemy ))
|
||||
{
|
||||
CBaseEntity *pHurt = m_hEnemy;
|
||||
|
||||
if (m_flEnemyTouched < gpGlobals->time - 0.2 && (m_hEnemy->BodyTarget( pev->origin ) - pev->origin).Length() > (32+16+32))
|
||||
break;
|
||||
|
||||
Vector vecShootDir = ShootAtEnemy( pev->origin );
|
||||
UTIL_MakeAimVectors ( pev->angles );
|
||||
|
||||
if (DotProduct( vecShootDir, gpGlobals->v_forward ) > 0.707)
|
||||
{
|
||||
m_bOnAttack = TRUE;
|
||||
pHurt->pev->punchangle.z = -18;
|
||||
pHurt->pev->punchangle.x = 5;
|
||||
pHurt->pev->velocity = pHurt->pev->velocity - gpGlobals->v_right * 300;
|
||||
if (pHurt->IsPlayer())
|
||||
{
|
||||
pHurt->pev->angles.x += RANDOM_FLOAT( -35, 35 );
|
||||
pHurt->pev->angles.y += RANDOM_FLOAT( -90, 90 );
|
||||
pHurt->pev->angles.z = 0;
|
||||
pHurt->pev->fixangle = TRUE;
|
||||
}
|
||||
pHurt->TakeDamage( pev, pev, gSkillData.huntinghorrorDmgBite, DMG_SLASH );
|
||||
}
|
||||
}
|
||||
BiteSound();
|
||||
}
|
||||
break;
|
||||
default:
|
||||
CFlyingMonster::HandleAnimEvent( pEvent );
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
//=========================================================
|
||||
// Spawn
|
||||
//=========================================================
|
||||
void CHuntingHorror :: Spawn()
|
||||
{
|
||||
Precache( );
|
||||
|
||||
if (pev->model)
|
||||
SET_MODEL(ENT(pev), STRING(pev->model)); //LRC
|
||||
else
|
||||
SET_MODEL(ENT(pev), "models/huntinghorror.mdl");
|
||||
UTIL_SetSize( pev, Vector( -15, -15, 5 ), Vector( 15, 15, 17 ) );
|
||||
|
||||
pev->solid = SOLID_BBOX;
|
||||
pev->movetype = MOVETYPE_FLY;
|
||||
m_bloodColor = BLOOD_COLOR_GREEN;
|
||||
if (pev->health == 0)
|
||||
pev->health = gSkillData.huntinghorrorHealth;
|
||||
pev->view_ofs = Vector ( 0, 0, 16 );
|
||||
m_flFieldOfView = VIEW_FIELD_WIDE;
|
||||
m_MonsterState = MONSTERSTATE_NONE;
|
||||
SetBits(pev->flags, FL_FLY);
|
||||
SetFlyingSpeed( HUNTING_HORROR_SPEED );
|
||||
SetFlyingMomentum( 2.5 ); // Set momentum constant
|
||||
|
||||
m_afCapability = bits_CAP_RANGE_ATTACK1 | bits_CAP_FLY;
|
||||
|
||||
MonsterInit();
|
||||
|
||||
SetTouch( BiteTouch );
|
||||
SetUse( CombatUse );
|
||||
|
||||
m_idealDist = 384;
|
||||
m_flMinSpeed = 80;
|
||||
m_flMaxSpeed = 300;
|
||||
m_flMaxDist = 384;
|
||||
|
||||
Vector Forward;
|
||||
UTIL_MakeVectorsPrivate(pev->angles, Forward, 0, 0);
|
||||
pev->velocity = m_flightSpeed * Forward.Normalize();
|
||||
m_SaveVelocity = pev->velocity;
|
||||
}
|
||||
|
||||
//=========================================================
|
||||
// Precache - precaches all resources this monster needs
|
||||
//=========================================================
|
||||
void CHuntingHorror :: Precache()
|
||||
{
|
||||
if (pev->model)
|
||||
PRECACHE_MODEL((char*)STRING(pev->model)); //LRC
|
||||
else
|
||||
PRECACHE_MODEL("models/huntinghorror.mdl");
|
||||
|
||||
PRECACHE_SOUND_ARRAY( pIdleSounds );
|
||||
PRECACHE_SOUND_ARRAY( pAlertSounds );
|
||||
PRECACHE_SOUND_ARRAY( pAttackSounds );
|
||||
PRECACHE_SOUND_ARRAY( pBiteSounds );
|
||||
PRECACHE_SOUND_ARRAY( pDieSounds );
|
||||
PRECACHE_SOUND_ARRAY( pPainSounds );
|
||||
}
|
||||
|
||||
//=========================================================
|
||||
// GetSchedule
|
||||
//=========================================================
|
||||
Schedule_t* CHuntingHorror::GetSchedule()
|
||||
{
|
||||
// ALERT( at_console, "GetSchedule( )\n" );
|
||||
switch(m_MonsterState)
|
||||
{
|
||||
case MONSTERSTATE_IDLE:
|
||||
m_flightSpeed = 80;
|
||||
return GetScheduleOfType( SCHED_IDLE_WALK );
|
||||
|
||||
case MONSTERSTATE_ALERT:
|
||||
m_flightSpeed = 150;
|
||||
return GetScheduleOfType( SCHED_IDLE_WALK );
|
||||
|
||||
case MONSTERSTATE_COMBAT:
|
||||
m_flMaxSpeed = 400;
|
||||
// eat them
|
||||
if ( HasConditions( bits_COND_CAN_MELEE_ATTACK1 ) )
|
||||
{
|
||||
return GetScheduleOfType( SCHED_MELEE_ATTACK1 );
|
||||
}
|
||||
// chase them down and eat them
|
||||
if ( HasConditions( bits_COND_CAN_RANGE_ATTACK1 ) )
|
||||
{
|
||||
return GetScheduleOfType( SCHED_CHASE_ENEMY );
|
||||
}
|
||||
if ( HasConditions( bits_COND_HEAVY_DAMAGE ) )
|
||||
{
|
||||
m_bOnAttack = TRUE;
|
||||
}
|
||||
if ( pev->health < pev->max_health - 20 )
|
||||
{
|
||||
m_bOnAttack = TRUE;
|
||||
}
|
||||
|
||||
return GetScheduleOfType( SCHED_STANDOFF );
|
||||
}
|
||||
|
||||
return CFlyingMonster :: GetSchedule();
|
||||
}
|
||||
|
||||
|
||||
//=========================================================
|
||||
//=========================================================
|
||||
Schedule_t* CHuntingHorror :: GetScheduleOfType ( int Type )
|
||||
{
|
||||
// ALERT( at_console, "GetScheduleOfType( %d ) %d\n", Type, m_bOnAttack );
|
||||
switch ( Type )
|
||||
{
|
||||
case SCHED_IDLE_WALK:
|
||||
return slFlyAround;
|
||||
case SCHED_STANDOFF:
|
||||
return slCircleEnemy;
|
||||
case SCHED_FAIL:
|
||||
return slFlyAgitated;
|
||||
case SCHED_DIE:
|
||||
return slHuntHorrTwitchDie;
|
||||
case SCHED_CHASE_ENEMY:
|
||||
AttackSound( );
|
||||
}
|
||||
|
||||
return CBaseMonster :: GetScheduleOfType( Type );
|
||||
}
|
||||
|
||||
|
||||
|
||||
//=========================================================
|
||||
// Start task - selects the correct activity and performs
|
||||
// any necessary calculations to start the next task on the
|
||||
// schedule.
|
||||
//=========================================================
|
||||
void CHuntingHorror::StartTask(Task_t *pTask)
|
||||
{
|
||||
switch (pTask->iTask)
|
||||
{
|
||||
case TASK_HUNTING_HORROR_CIRCLE_ENEMY:
|
||||
break;
|
||||
case TASK_HUNTING_HORROR_FLY:
|
||||
break;
|
||||
case TASK_SMALL_FLINCH:
|
||||
if (m_idealDist > 128)
|
||||
{
|
||||
m_flMaxDist = 512;
|
||||
m_idealDist = 512;
|
||||
}
|
||||
else
|
||||
{
|
||||
m_bOnAttack = TRUE;
|
||||
}
|
||||
CFlyingMonster::StartTask(pTask);
|
||||
break;
|
||||
|
||||
default:
|
||||
CFlyingMonster::StartTask(pTask);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
void CHuntingHorror :: RunTask ( Task_t *pTask )
|
||||
{
|
||||
switch ( pTask->iTask )
|
||||
{
|
||||
case TASK_HUNTING_HORROR_CIRCLE_ENEMY:
|
||||
if (m_hEnemy == NULL)
|
||||
{
|
||||
TaskComplete( );
|
||||
}
|
||||
else if (FVisible( m_hEnemy ))
|
||||
{
|
||||
Vector vecFrom = m_hEnemy->EyePosition( );
|
||||
|
||||
Vector vecDelta = (pev->origin - vecFrom).Normalize( );
|
||||
Vector vecFly = CrossProduct( vecDelta, Vector( 0, 0, 1 ) ).Normalize( );
|
||||
|
||||
if (DotProduct( vecFly, m_SaveVelocity ) < 0)
|
||||
vecFly = vecFly * -1.0;
|
||||
|
||||
Vector vecPos = vecFrom + vecDelta * m_idealDist + vecFly * 32;
|
||||
|
||||
// ALERT( at_console, "vecPos %.0f %.0f %.0f\n", vecPos.x, vecPos.y, vecPos.z );
|
||||
|
||||
TraceResult tr;
|
||||
|
||||
UTIL_TraceHull( vecFrom, vecPos, ignore_monsters, large_hull, m_hEnemy->edict(), &tr );
|
||||
|
||||
if (tr.flFraction > 0.5)
|
||||
vecPos = tr.vecEndPos;
|
||||
|
||||
m_SaveVelocity = m_SaveVelocity * 0.8 + 0.2 * (vecPos - pev->origin).Normalize() * m_flightSpeed;
|
||||
|
||||
// ALERT( at_console, "m_SaveVelocity %.2f %.2f %.2f\n", m_SaveVelocity.x, m_SaveVelocity.y, m_SaveVelocity.z );
|
||||
|
||||
if (HasConditions( bits_COND_ENEMY_FACING_ME ) && m_hEnemy->FVisible( this ))
|
||||
{
|
||||
m_flNextAlert -= 0.1;
|
||||
|
||||
if (m_idealDist < m_flMaxDist)
|
||||
{
|
||||
m_idealDist += 4;
|
||||
}
|
||||
if (m_flightSpeed > m_flMinSpeed)
|
||||
{
|
||||
m_flightSpeed -= 2;
|
||||
}
|
||||
else if (m_flightSpeed < m_flMinSpeed)
|
||||
{
|
||||
m_flightSpeed += 2;
|
||||
}
|
||||
if (m_flMinSpeed < m_flMaxSpeed)
|
||||
{
|
||||
m_flMinSpeed += 0.5;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
m_flNextAlert += 0.1;
|
||||
|
||||
if (m_idealDist > 128)
|
||||
{
|
||||
m_idealDist -= 4;
|
||||
}
|
||||
if (m_flightSpeed < m_flMaxSpeed)
|
||||
{
|
||||
m_flightSpeed += 4;
|
||||
}
|
||||
}
|
||||
// ALERT( at_console, "%.0f\n", m_idealDist );
|
||||
}
|
||||
else
|
||||
{
|
||||
m_flNextAlert = gpGlobals->time + 0.2;
|
||||
}
|
||||
|
||||
if (m_flNextAlert < gpGlobals->time)
|
||||
{
|
||||
// ALERT( at_console, "AlertSound()\n");
|
||||
AlertSound( );
|
||||
m_flNextAlert = gpGlobals->time + RANDOM_FLOAT( 3, 5 );
|
||||
}
|
||||
|
||||
break;
|
||||
case TASK_HUNTING_HORROR_FLY:
|
||||
if (m_fSequenceFinished)
|
||||
{
|
||||
TaskComplete( );
|
||||
}
|
||||
break;
|
||||
case TASK_DIE:
|
||||
if ( m_fSequenceFinished )
|
||||
{
|
||||
CFlyingMonster :: RunTask ( pTask );
|
||||
|
||||
pev->deadflag = DEAD_DEAD;
|
||||
|
||||
TaskComplete( );
|
||||
}
|
||||
break;
|
||||
|
||||
default:
|
||||
CFlyingMonster :: RunTask ( pTask );
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
float CHuntingHorror::VectorToPitch( const Vector &vec )
|
||||
{
|
||||
float pitch;
|
||||
if (vec.z == 0 && vec.x == 0)
|
||||
pitch = 0;
|
||||
else
|
||||
{
|
||||
pitch = (int) (atan2(vec.z, sqrt(vec.x*vec.x+vec.y*vec.y)) * 180 / M_PI);
|
||||
if (pitch < 0)
|
||||
pitch += 360;
|
||||
}
|
||||
return pitch;
|
||||
}
|
||||
|
||||
//=========================================================
|
||||
void CHuntingHorror::Move(float flInterval)
|
||||
{
|
||||
CFlyingMonster::Move( flInterval );
|
||||
}
|
||||
|
||||
float CHuntingHorror::FlPitchDiff( void )
|
||||
{
|
||||
float flPitchDiff;
|
||||
float flCurrentPitch;
|
||||
|
||||
flCurrentPitch = UTIL_AngleMod( pev->angles.z );
|
||||
|
||||
if ( flCurrentPitch == pev->idealpitch )
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
|
||||
flPitchDiff = pev->idealpitch - flCurrentPitch;
|
||||
|
||||
if ( pev->idealpitch > flCurrentPitch )
|
||||
{
|
||||
if (flPitchDiff >= 180)
|
||||
flPitchDiff = flPitchDiff - 360;
|
||||
}
|
||||
else
|
||||
{
|
||||
if (flPitchDiff <= -180)
|
||||
flPitchDiff = flPitchDiff + 360;
|
||||
}
|
||||
return flPitchDiff;
|
||||
}
|
||||
|
||||
float CHuntingHorror :: ChangePitch( int speed )
|
||||
{
|
||||
if ( pev->movetype == MOVETYPE_FLY )
|
||||
{
|
||||
float diff = FlPitchDiff();
|
||||
float target = 0;
|
||||
if ( m_IdealActivity != GetStoppedActivity() )
|
||||
{
|
||||
if (diff < -20)
|
||||
target = 45;
|
||||
else if (diff > 20)
|
||||
target = -45;
|
||||
}
|
||||
pev->angles.x = UTIL_Approach(target, pev->angles.x, 220.0 * 0.1 );
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
float CHuntingHorror::ChangeYaw( int speed )
|
||||
{
|
||||
if ( pev->movetype == MOVETYPE_FLY )
|
||||
{
|
||||
float diff = FlYawDiff();
|
||||
float target = 0;
|
||||
|
||||
if ( m_IdealActivity != GetStoppedActivity() )
|
||||
{
|
||||
if ( diff < -20 )
|
||||
target = 20;
|
||||
else if ( diff > 20 )
|
||||
target = -20;
|
||||
}
|
||||
pev->angles.z = UTIL_Approach( target, pev->angles.z, 220.0 * 0.1 );
|
||||
}
|
||||
return CFlyingMonster::ChangeYaw( speed );
|
||||
}
|
||||
|
||||
|
||||
Activity CHuntingHorror:: GetStoppedActivity( void )
|
||||
{
|
||||
if ( pev->movetype != MOVETYPE_FLY ) // UNDONE: Ground idle here, IDLE may be something else
|
||||
return ACT_IDLE;
|
||||
return ACT_WALK;
|
||||
}
|
||||
|
||||
void CHuntingHorror::MoveExecute( CBaseEntity *pTargetEnt, const Vector &vecDir, float flInterval )
|
||||
{
|
||||
m_SaveVelocity = vecDir * m_flightSpeed;
|
||||
}
|
||||
|
||||
|
||||
void CHuntingHorror::MonsterThink ( void )
|
||||
{
|
||||
CFlyingMonster::MonsterThink( );
|
||||
|
||||
if (pev->deadflag == DEAD_NO)
|
||||
{
|
||||
if (m_MonsterState != MONSTERSTATE_SCRIPT)
|
||||
{
|
||||
Fly( );
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void CHuntingHorror :: Stop( void )
|
||||
{
|
||||
if (!m_bOnAttack)
|
||||
m_flightSpeed = 80.0;
|
||||
}
|
||||
|
||||
void CHuntingHorror::Fly( )
|
||||
{
|
||||
int retValue = 0;
|
||||
|
||||
Vector start = pev->origin;
|
||||
|
||||
Vector Angles;
|
||||
Vector Forward, Right, Up;
|
||||
|
||||
if (FBitSet( pev->flags, FL_ONGROUND))
|
||||
{
|
||||
pev->angles.x = 0;
|
||||
pev->angles.y += RANDOM_FLOAT( -45, 45 );
|
||||
ClearBits( pev->flags, FL_ONGROUND );
|
||||
|
||||
Angles = Vector( -pev->angles.x, pev->angles.y, pev->angles.z );
|
||||
UTIL_MakeVectorsPrivate(Angles, Forward, Right, Up);
|
||||
|
||||
pev->velocity = Forward * 200 + Up * 200;
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
if (m_bOnAttack && m_flightSpeed < m_flMaxSpeed)
|
||||
{
|
||||
m_flightSpeed += 40;
|
||||
}
|
||||
if (m_flightSpeed < 180)
|
||||
{
|
||||
if (m_IdealActivity == ACT_RUN)
|
||||
SetActivity( ACT_WALK );
|
||||
if (m_IdealActivity == ACT_WALK)
|
||||
pev->framerate = m_flightSpeed / 150.0;
|
||||
// ALERT( at_console, "walk %.2f\n", pev->framerate );
|
||||
}
|
||||
else
|
||||
{
|
||||
if (m_IdealActivity == ACT_WALK)
|
||||
SetActivity( ACT_RUN );
|
||||
if (m_IdealActivity == ACT_RUN)
|
||||
pev->framerate = m_flightSpeed / 150.0;
|
||||
// ALERT( at_console, "run %.2f\n", pev->framerate );
|
||||
}
|
||||
|
||||
#define PROBE_LENGTH 150
|
||||
|
||||
Angles = UTIL_VecToAngles( m_SaveVelocity );
|
||||
Angles.x = -Angles.x;
|
||||
UTIL_MakeVectorsPrivate(Angles, Forward, Right, Up);
|
||||
|
||||
Vector f, u, l, r, d;
|
||||
f = DoProbe(start + PROBE_LENGTH * Forward);
|
||||
r = DoProbe(start + PROBE_LENGTH/3 * Forward+Right);
|
||||
l = DoProbe(start + PROBE_LENGTH/3 * Forward-Right);
|
||||
u = DoProbe(start + PROBE_LENGTH/3 * Forward+Up);
|
||||
d = DoProbe(start + PROBE_LENGTH/3 * Forward-Up);
|
||||
|
||||
Vector SteeringVector = f+r+l+u+d;
|
||||
m_SaveVelocity = (m_SaveVelocity + SteeringVector/2).Normalize();
|
||||
|
||||
Angles = Vector( -pev->angles.x, pev->angles.y, pev->angles.z );
|
||||
UTIL_MakeVectorsPrivate(Angles, Forward, Right, Up);
|
||||
// ALERT( at_console, "%f : %f\n", Angles.x, Forward.z );
|
||||
|
||||
float flDot = DotProduct( Forward, m_SaveVelocity );
|
||||
if (flDot > 0.5)
|
||||
pev->velocity = m_SaveVelocity = m_SaveVelocity * m_flightSpeed;
|
||||
else if (flDot > 0)
|
||||
pev->velocity = m_SaveVelocity = m_SaveVelocity * m_flightSpeed * (flDot + 0.5);
|
||||
else
|
||||
pev->velocity = m_SaveVelocity = m_SaveVelocity * 80;
|
||||
|
||||
|
||||
Angles = UTIL_VecToAngles( m_SaveVelocity );
|
||||
|
||||
// Smooth Pitch
|
||||
//
|
||||
if (Angles.x > 180)
|
||||
Angles.x = Angles.x - 360;
|
||||
pev->angles.x = UTIL_Approach(Angles.x, pev->angles.x, 50 * 0.1 );
|
||||
if (pev->angles.x < -80) pev->angles.x = -80;
|
||||
if (pev->angles.x > 80) pev->angles.x = 80;
|
||||
|
||||
// Smooth Yaw and generate Roll
|
||||
//
|
||||
float turn = 360;
|
||||
// ALERT( at_console, "Y %.0f %.0f\n", Angles.y, pev->angles.y );
|
||||
|
||||
if (fabs(Angles.y - pev->angles.y) < fabs(turn))
|
||||
{
|
||||
turn = Angles.y - pev->angles.y;
|
||||
}
|
||||
if (fabs(Angles.y - pev->angles.y + 360) < fabs(turn))
|
||||
{
|
||||
turn = Angles.y - pev->angles.y + 360;
|
||||
}
|
||||
if (fabs(Angles.y - pev->angles.y - 360) < fabs(turn))
|
||||
{
|
||||
turn = Angles.y - pev->angles.y - 360;
|
||||
}
|
||||
|
||||
float speed = m_flightSpeed * 0.1;
|
||||
|
||||
// ALERT( at_console, "speed %.0f %f\n", turn, speed );
|
||||
if (fabs(turn) > speed)
|
||||
{
|
||||
if (turn < 0.0)
|
||||
{
|
||||
turn = -speed;
|
||||
}
|
||||
else
|
||||
{
|
||||
turn = speed;
|
||||
}
|
||||
}
|
||||
pev->angles.y += turn;
|
||||
pev->angles.z -= turn;
|
||||
pev->angles.y = fmod((pev->angles.y + 360.0), 360.0);
|
||||
|
||||
static float yaw_adj;
|
||||
|
||||
yaw_adj = yaw_adj * 0.8 + turn;
|
||||
|
||||
// ALERT( at_console, "yaw %f : %f\n", turn, yaw_adj );
|
||||
|
||||
SetBoneController( 0, -yaw_adj / 4.0 );
|
||||
|
||||
// Roll Smoothing
|
||||
//
|
||||
turn = 360;
|
||||
if (fabs(Angles.z - pev->angles.z) < fabs(turn))
|
||||
{
|
||||
turn = Angles.z - pev->angles.z;
|
||||
}
|
||||
if (fabs(Angles.z - pev->angles.z + 360) < fabs(turn))
|
||||
{
|
||||
turn = Angles.z - pev->angles.z + 360;
|
||||
}
|
||||
if (fabs(Angles.z - pev->angles.z - 360) < fabs(turn))
|
||||
{
|
||||
turn = Angles.z - pev->angles.z - 360;
|
||||
}
|
||||
speed = m_flightSpeed/2 * 0.1;
|
||||
if (fabs(turn) < speed)
|
||||
{
|
||||
pev->angles.z += turn;
|
||||
}
|
||||
else
|
||||
{
|
||||
if (turn < 0.0)
|
||||
{
|
||||
pev->angles.z -= speed;
|
||||
}
|
||||
else
|
||||
{
|
||||
pev->angles.z += speed;
|
||||
}
|
||||
}
|
||||
if (pev->angles.z < -20) pev->angles.z = -20;
|
||||
if (pev->angles.z > 20) pev->angles.z = 20;
|
||||
|
||||
UTIL_MakeVectorsPrivate( Vector( -Angles.x, Angles.y, Angles.z ), Forward, Right, Up);
|
||||
|
||||
}
|
||||
|
||||
|
||||
Vector CHuntingHorror::DoProbe(const Vector &Probe)
|
||||
{
|
||||
Vector WallNormal = Vector(0,0,-1); // AIR normal is Straight Down for flying thing.
|
||||
float frac;
|
||||
BOOL bBumpedSomething = ProbeZ(pev->origin, Probe, &frac);
|
||||
|
||||
TraceResult tr;
|
||||
TRACE_MONSTER_HULL(edict(), pev->origin, Probe, dont_ignore_monsters, edict(), &tr);
|
||||
if ( tr.fAllSolid || tr.flFraction < 0.99 )
|
||||
{
|
||||
if (tr.flFraction < 0.0) tr.flFraction = 0.0;
|
||||
if (tr.flFraction > 1.0) tr.flFraction = 1.0;
|
||||
if (tr.flFraction < frac)
|
||||
{
|
||||
frac = tr.flFraction;
|
||||
bBumpedSomething = TRUE;
|
||||
WallNormal = tr.vecPlaneNormal;
|
||||
}
|
||||
}
|
||||
|
||||
if (bBumpedSomething && (m_hEnemy == NULL || tr.pHit != m_hEnemy->edict()))
|
||||
{
|
||||
Vector ProbeDir = Probe - pev->origin;
|
||||
|
||||
Vector NormalToProbeAndWallNormal = CrossProduct(ProbeDir, WallNormal);
|
||||
Vector SteeringVector = CrossProduct( NormalToProbeAndWallNormal, ProbeDir);
|
||||
|
||||
float SteeringForce = m_flightSpeed * (1-frac) * (DotProduct(WallNormal.Normalize(), m_SaveVelocity.Normalize()));
|
||||
if (SteeringForce < 0.0)
|
||||
{
|
||||
SteeringForce = -SteeringForce;
|
||||
}
|
||||
SteeringVector = SteeringForce * SteeringVector.Normalize();
|
||||
|
||||
return SteeringVector;
|
||||
}
|
||||
return Vector(0, 0, 0);
|
||||
}
|
||||
|
||||
#endif
|
|
@ -0,0 +1,80 @@
|
|||
|
||||
#ifndef HUNTING_HORROR_H
|
||||
#define HUNTING_HORROR_H
|
||||
|
||||
class CHuntingHorror : public CFlyingMonster
|
||||
{
|
||||
public:
|
||||
void Spawn( void );
|
||||
void Precache( void );
|
||||
void SetYawSpeed( void );
|
||||
int Classify( void );
|
||||
void HandleAnimEvent( MonsterEvent_t *pEvent );
|
||||
CUSTOM_SCHEDULES;
|
||||
|
||||
int Save( CSave &save );
|
||||
int Restore( CRestore &restore );
|
||||
static TYPEDESCRIPTION m_SaveData[];
|
||||
|
||||
Schedule_t *GetSchedule( void );
|
||||
Schedule_t *GetScheduleOfType ( int Type );
|
||||
|
||||
void Killed( entvars_t *pevAttacker, int iGib );
|
||||
void BecomeDead( void );
|
||||
|
||||
void EXPORT CombatUse( CBaseEntity *pActivator, CBaseEntity *pCaller, USE_TYPE useType, float value );
|
||||
void EXPORT BiteTouch( CBaseEntity *pOther );
|
||||
|
||||
void StartTask( Task_t *pTask );
|
||||
void RunTask( Task_t *pTask );
|
||||
|
||||
BOOL CheckMeleeAttack1 ( float flDot, float flDist );
|
||||
BOOL CheckRangeAttack1 ( float flDot, float flDist );
|
||||
|
||||
float ChangeYaw( int speed );
|
||||
Activity GetStoppedActivity( void );
|
||||
|
||||
void Move( float flInterval );
|
||||
void MoveExecute( CBaseEntity *pTargetEnt, const Vector &vecDir, float flInterval );
|
||||
void MonsterThink( void );
|
||||
void Stop( void );
|
||||
void Fly( void );
|
||||
Vector DoProbe(const Vector &Probe);
|
||||
|
||||
float VectorToPitch( const Vector &vec);
|
||||
float FlPitchDiff( void );
|
||||
float ChangePitch( int speed );
|
||||
|
||||
Vector m_SaveVelocity;
|
||||
float m_idealDist;
|
||||
|
||||
float m_flEnemyTouched;
|
||||
BOOL m_bOnAttack;
|
||||
|
||||
float m_flMaxSpeed;
|
||||
float m_flMinSpeed;
|
||||
float m_flMaxDist;
|
||||
|
||||
float m_flNextAlert;
|
||||
|
||||
static const char *pIdleSounds[];
|
||||
static const char *pAlertSounds[];
|
||||
static const char *pAttackSounds[];
|
||||
static const char *pBiteSounds[];
|
||||
static const char *pDieSounds[];
|
||||
static const char *pPainSounds[];
|
||||
|
||||
void IdleSound( void );
|
||||
void AlertSound( void );
|
||||
void AttackSound( void );
|
||||
void BiteSound( void );
|
||||
void DeathSound( void );
|
||||
void PainSound( void );
|
||||
};
|
||||
|
||||
|
||||
|
||||
|
||||
#endif
|
||||
|
||||
|
|
@ -0,0 +1,980 @@
|
|||
/***
|
||||
*
|
||||
* Copyright (c) 1999, 2000 Valve LLC. All rights reserved.
|
||||
*
|
||||
* This product contains software technology licensed from Id
|
||||
* Software, Inc. ("Id Technology"). Id Technology (c) 1996 Id Software, Inc.
|
||||
* All Rights Reserved.
|
||||
*
|
||||
* This source code contains proprietary and confidential information of
|
||||
* Valve LLC and its suppliers. Access to this code is restricted to
|
||||
* persons who have executed a written SDK license with Valve. Any access,
|
||||
* use or distribution of this code by or to any unlicensed person is illegal.
|
||||
*
|
||||
****/
|
||||
#if !defined( OEM_BUILD ) && !defined( HLDEMO_BUILD )
|
||||
|
||||
//=========================================================
|
||||
// night_gaunt
|
||||
//=========================================================
|
||||
|
||||
#include "extdll.h"
|
||||
#include "util.h"
|
||||
#include "cbase.h"
|
||||
#include "monsters.h"
|
||||
#include "schedule.h"
|
||||
#include "flyingmonster.h"
|
||||
#include "nodes.h"
|
||||
#include "soundent.h"
|
||||
#include "animation.h"
|
||||
#include "effects.h"
|
||||
#include "weapons.h"
|
||||
|
||||
#define SEARCH_RETRY 16
|
||||
|
||||
#define NIGHT_GAUNT_SPEED 200
|
||||
|
||||
extern CGraph WorldGraph;
|
||||
|
||||
|
||||
#define NGAUNT_AE_SLASH 1
|
||||
|
||||
//=========================================================
|
||||
// Monster's Anim Events Go Here
|
||||
//=========================================================
|
||||
|
||||
|
||||
#include "NightGaunt.h"
|
||||
|
||||
|
||||
LINK_ENTITY_TO_CLASS( monster_nightgaunt, CNightGaunt );
|
||||
|
||||
TYPEDESCRIPTION CNightGaunt::m_SaveData[] =
|
||||
{
|
||||
DEFINE_FIELD( CNightGaunt, m_SaveVelocity, FIELD_VECTOR ),
|
||||
DEFINE_FIELD( CNightGaunt, m_idealDist, FIELD_FLOAT ),
|
||||
DEFINE_FIELD( CNightGaunt, m_flEnemyTouched, FIELD_FLOAT ),
|
||||
DEFINE_FIELD( CNightGaunt, m_bOnAttack, FIELD_BOOLEAN ),
|
||||
DEFINE_FIELD( CNightGaunt, m_flMaxSpeed, FIELD_FLOAT ),
|
||||
DEFINE_FIELD( CNightGaunt, m_flMinSpeed, FIELD_FLOAT ),
|
||||
DEFINE_FIELD( CNightGaunt, m_flMaxDist, FIELD_FLOAT ),
|
||||
DEFINE_FIELD( CNightGaunt, m_flNextAlert, FIELD_TIME ),
|
||||
};
|
||||
|
||||
IMPLEMENT_SAVERESTORE( CNightGaunt, CFlyingMonster );
|
||||
|
||||
|
||||
/* nightgaunt does not make idle sounds
|
||||
const char *CNightGaunt::pIdleSounds[] =
|
||||
{
|
||||
"nightgaunt/nightgaunt_idle1.wav",
|
||||
"nightgaunt/nightgaunt_idle2.wav",
|
||||
"nightgaunt/nightgaunt_idle3.wav",
|
||||
"nightgaunt/nightgaunt_idle4.wav",
|
||||
};
|
||||
*/
|
||||
|
||||
const char *CNightGaunt::pAlertSounds[] =
|
||||
{
|
||||
"nightgaunt/ng_alert1.wav",
|
||||
};
|
||||
|
||||
const char *CNightGaunt::pAttackSounds[] =
|
||||
{
|
||||
"nightgaunt/ng_attack1.wav",
|
||||
};
|
||||
|
||||
const char *CNightGaunt::pSlashSounds[] =
|
||||
{
|
||||
"zombie/claw_strike1.wav",
|
||||
"zombie/claw_strike2.wav",
|
||||
"zombie/claw_strike3.wav",
|
||||
};
|
||||
|
||||
const char *CNightGaunt::pPainSounds[] =
|
||||
{
|
||||
"nightgaunt/ng_pain1.wav",
|
||||
"nightgaunt/ng_pain2.wav",
|
||||
"nightgaunt/ng_pain3.wav",
|
||||
};
|
||||
|
||||
const char *CNightGaunt::pDieSounds[] =
|
||||
{
|
||||
"nightgaunt/ng_die1.wav",
|
||||
"nightgaunt/ng_die2.wav",
|
||||
};
|
||||
|
||||
#define EMIT_NIGHT_GAUNT_SOUND( chan, array ) \
|
||||
EMIT_SOUND_DYN ( ENT(pev), chan , array [ RANDOM_LONG(0,ARRAYSIZE( array )-1) ], 1.0, 0.6, 0, RANDOM_LONG(95,105) );
|
||||
|
||||
|
||||
void CNightGaunt :: IdleSound( void )
|
||||
{
|
||||
// EMIT_NIGHT_GAUNT_SOUND( CHAN_VOICE, pIdleSounds );
|
||||
}
|
||||
|
||||
void CNightGaunt :: AlertSound( void )
|
||||
{
|
||||
EMIT_NIGHT_GAUNT_SOUND( CHAN_VOICE, pAlertSounds );
|
||||
}
|
||||
|
||||
void CNightGaunt :: AttackSound( void )
|
||||
{
|
||||
EMIT_NIGHT_GAUNT_SOUND( CHAN_VOICE, pAttackSounds );
|
||||
}
|
||||
|
||||
void CNightGaunt :: SlashSound( void )
|
||||
{
|
||||
EMIT_NIGHT_GAUNT_SOUND( CHAN_WEAPON, pSlashSounds );
|
||||
}
|
||||
|
||||
void CNightGaunt :: DeathSound( void )
|
||||
{
|
||||
EMIT_NIGHT_GAUNT_SOUND( CHAN_VOICE, pDieSounds );
|
||||
}
|
||||
|
||||
void CNightGaunt :: PainSound( void )
|
||||
{
|
||||
EMIT_NIGHT_GAUNT_SOUND( CHAN_VOICE, pPainSounds );
|
||||
}
|
||||
|
||||
//=========================================================
|
||||
// monster-specific tasks and states
|
||||
//=========================================================
|
||||
enum
|
||||
{
|
||||
TASK_NIGHT_GAUNT_CIRCLE_ENEMY = LAST_COMMON_TASK + 1,
|
||||
TASK_NIGHT_GAUNT_FLY,
|
||||
};
|
||||
|
||||
//=========================================================
|
||||
// AI Schedules Specific to this monster
|
||||
//=========================================================
|
||||
|
||||
static Task_t tlFlyAround[] =
|
||||
{
|
||||
{ TASK_SET_ACTIVITY, (float)ACT_WALK },
|
||||
{ TASK_NIGHT_GAUNT_FLY, 0.0 },
|
||||
};
|
||||
|
||||
static Schedule_t slFlyAround[] =
|
||||
{
|
||||
{
|
||||
tlFlyAround,
|
||||
ARRAYSIZE(tlFlyAround),
|
||||
bits_COND_LIGHT_DAMAGE |
|
||||
bits_COND_HEAVY_DAMAGE |
|
||||
bits_COND_SEE_ENEMY |
|
||||
bits_COND_NEW_ENEMY |
|
||||
bits_COND_HEAR_SOUND,
|
||||
bits_SOUND_PLAYER |
|
||||
bits_SOUND_COMBAT,
|
||||
"FlyAround"
|
||||
},
|
||||
};
|
||||
|
||||
static Task_t tlFlyAgitated[] =
|
||||
{
|
||||
{ TASK_STOP_MOVING, (float) 0 },
|
||||
{ TASK_SET_ACTIVITY, (float)ACT_RUN },
|
||||
{ TASK_WAIT, (float)2.0 },
|
||||
};
|
||||
|
||||
static Schedule_t slFlyAgitated[] =
|
||||
{
|
||||
{
|
||||
tlFlyAgitated,
|
||||
ARRAYSIZE(tlFlyAgitated),
|
||||
0,
|
||||
0,
|
||||
"FlyAgitated"
|
||||
},
|
||||
};
|
||||
|
||||
|
||||
static Task_t tlCircleEnemy[] =
|
||||
{
|
||||
{ TASK_SET_ACTIVITY, (float)ACT_WALK },
|
||||
{ TASK_NIGHT_GAUNT_CIRCLE_ENEMY, 0.0 },
|
||||
};
|
||||
|
||||
static Schedule_t slCircleEnemy[] =
|
||||
{
|
||||
{
|
||||
tlCircleEnemy,
|
||||
ARRAYSIZE(tlCircleEnemy),
|
||||
bits_COND_NEW_ENEMY |
|
||||
bits_COND_LIGHT_DAMAGE |
|
||||
bits_COND_HEAVY_DAMAGE |
|
||||
bits_COND_CAN_MELEE_ATTACK1 |
|
||||
bits_COND_CAN_RANGE_ATTACK1,
|
||||
0,
|
||||
"CircleEnemy"
|
||||
},
|
||||
};
|
||||
|
||||
|
||||
Task_t tlNightGauntTwitchDie[] =
|
||||
{
|
||||
{ TASK_STOP_MOVING, 0 },
|
||||
{ TASK_SOUND_DIE, (float)0 },
|
||||
{ TASK_DIE, (float)0 },
|
||||
};
|
||||
|
||||
Schedule_t slNightGauntTwitchDie[] =
|
||||
{
|
||||
{
|
||||
tlNightGauntTwitchDie,
|
||||
ARRAYSIZE( tlNightGauntTwitchDie ),
|
||||
0,
|
||||
0,
|
||||
"Die"
|
||||
},
|
||||
};
|
||||
|
||||
|
||||
DEFINE_CUSTOM_SCHEDULES(CNightGaunt)
|
||||
{
|
||||
slFlyAround,
|
||||
slFlyAgitated,
|
||||
slCircleEnemy,
|
||||
slNightGauntTwitchDie,
|
||||
};
|
||||
IMPLEMENT_CUSTOM_SCHEDULES(CNightGaunt, CFlyingMonster);
|
||||
|
||||
//=========================================================
|
||||
// Classify - indicates this monster's place in the
|
||||
// relationship table.
|
||||
//=========================================================
|
||||
int CNightGaunt :: Classify ( void )
|
||||
{
|
||||
return m_iClass?m_iClass:CLASS_ALIEN_MONSTER;
|
||||
}
|
||||
|
||||
|
||||
Vector CNightGaunt :: Center ( void )
|
||||
{
|
||||
return Vector( pev->origin.x, pev->origin.y, pev->origin.z + 64 );
|
||||
}
|
||||
|
||||
//=========================================================
|
||||
// CheckMeleeAttack1
|
||||
//=========================================================
|
||||
BOOL CNightGaunt :: CheckMeleeAttack1 ( float flDot, float flDist )
|
||||
{
|
||||
if ( flDot >= 0.7 && m_flEnemyTouched > gpGlobals->time - 0.2 )
|
||||
{
|
||||
return TRUE;
|
||||
}
|
||||
return FALSE;
|
||||
}
|
||||
|
||||
void CNightGaunt::SlashTouch( CBaseEntity *pOther )
|
||||
{
|
||||
// slash if we hit who we want to eat
|
||||
if ( pOther == m_hEnemy )
|
||||
{
|
||||
m_flEnemyTouched = gpGlobals->time;
|
||||
m_bOnAttack = TRUE;
|
||||
}
|
||||
}
|
||||
|
||||
void CNightGaunt::CombatUse( CBaseEntity *pActivator, CBaseEntity *pCaller, USE_TYPE useType, float value )
|
||||
{
|
||||
if ( !ShouldToggle( useType, m_bOnAttack ) )
|
||||
return;
|
||||
|
||||
if (m_bOnAttack)
|
||||
{
|
||||
m_bOnAttack = 0;
|
||||
}
|
||||
else
|
||||
{
|
||||
m_bOnAttack = 1;
|
||||
}
|
||||
}
|
||||
|
||||
//=========================================================
|
||||
// CheckRangeAttack1 - Fly in for a chomp
|
||||
//
|
||||
//=========================================================
|
||||
BOOL CNightGaunt :: CheckRangeAttack1 ( float flDot, float flDist )
|
||||
{
|
||||
if ( flDot > -0.7 && (m_bOnAttack || ( flDist <= 384 && m_idealDist <= 384)))
|
||||
{
|
||||
return TRUE;
|
||||
}
|
||||
|
||||
return FALSE;
|
||||
}
|
||||
|
||||
//=========================================================
|
||||
// SetYawSpeed - allows each sequence to have a different
|
||||
// turn rate associated with it.
|
||||
//=========================================================
|
||||
void CNightGaunt :: SetYawSpeed ( void )
|
||||
{
|
||||
pev->yaw_speed = 100;
|
||||
}
|
||||
|
||||
|
||||
|
||||
//=========================================================
|
||||
// Killed - overrides CFlyingMonster.
|
||||
//
|
||||
void CNightGaunt :: Killed( entvars_t *pevAttacker, int iGib )
|
||||
{
|
||||
pev->velocity = Vector(0,0,-100);
|
||||
pev->gravity = 1.0;
|
||||
pev->angles.x = 0;
|
||||
|
||||
//CBaseMonster::Killed( pevAttacker, iGib );
|
||||
CFlyingMonster::Killed( pevAttacker, iGib );
|
||||
}
|
||||
|
||||
void CNightGaunt::BecomeDead( void )
|
||||
{
|
||||
pev->takedamage = DAMAGE_YES;// don't let autoaim aim at corpses.
|
||||
|
||||
// give the corpse half of the monster's original maximum health.
|
||||
pev->health = pev->max_health / 2;
|
||||
pev->max_health = 5; // max_health now becomes a counter for how many blood decals the corpse can place.
|
||||
}
|
||||
|
||||
//=========================================================
|
||||
// HandleAnimEvent - catches the monster-specific messages
|
||||
// that occur when tagged animation frames are played.
|
||||
//=========================================================
|
||||
void CNightGaunt :: HandleAnimEvent( MonsterEvent_t *pEvent )
|
||||
{
|
||||
switch( pEvent->event )
|
||||
{
|
||||
case NGAUNT_AE_SLASH:
|
||||
{
|
||||
if (m_hEnemy != NULL && FVisible( m_hEnemy ))
|
||||
{
|
||||
CBaseEntity *pHurt = m_hEnemy;
|
||||
|
||||
if (m_flEnemyTouched < gpGlobals->time - 0.2 && (m_hEnemy->BodyTarget( pev->origin ) - pev->origin).Length() > (32+16+32))
|
||||
break;
|
||||
|
||||
Vector vecShootDir = ShootAtEnemy( pev->origin );
|
||||
UTIL_MakeAimVectors ( pev->angles );
|
||||
|
||||
if (DotProduct( vecShootDir, gpGlobals->v_forward ) > 0.707)
|
||||
{
|
||||
m_bOnAttack = TRUE;
|
||||
pHurt->pev->punchangle.z = -18;
|
||||
pHurt->pev->punchangle.x = 5;
|
||||
pHurt->pev->velocity = pHurt->pev->velocity - gpGlobals->v_right * 100;
|
||||
// if (pHurt->IsPlayer())
|
||||
// {
|
||||
// pHurt->pev->angles.x += RANDOM_FLOAT( -35, 35 );
|
||||
// pHurt->pev->angles.y += RANDOM_FLOAT( -90, 90 );
|
||||
// pHurt->pev->angles.z = 0;
|
||||
// pHurt->pev->fixangle = TRUE;
|
||||
// }
|
||||
pHurt->TakeDamage( pev, pev, gSkillData.nightgauntDmgSlash, DMG_SLASH );
|
||||
}
|
||||
}
|
||||
SlashSound();
|
||||
}
|
||||
break;
|
||||
default:
|
||||
CFlyingMonster::HandleAnimEvent( pEvent );
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
//=========================================================
|
||||
// Spawn
|
||||
//=========================================================
|
||||
void CNightGaunt :: Spawn()
|
||||
{
|
||||
Precache( );
|
||||
|
||||
if (pev->model)
|
||||
SET_MODEL(ENT(pev), STRING(pev->model)); //LRC
|
||||
else
|
||||
SET_MODEL(ENT(pev), "models/monsters/ngaunt.mdl");
|
||||
UTIL_SetSize( pev, Vector( -16, -16, 0 ), Vector( 16, 16, 72 ) );
|
||||
|
||||
pev->solid = SOLID_BBOX;
|
||||
pev->movetype = MOVETYPE_FLY;
|
||||
m_bloodColor = BLOOD_COLOR_GREEN;
|
||||
if (pev->health == 0)
|
||||
pev->health = gSkillData.nightgauntHealth;
|
||||
pev->view_ofs = Vector ( 0, 0, 64 );
|
||||
m_flFieldOfView = VIEW_FIELD_WIDE;
|
||||
m_MonsterState = MONSTERSTATE_NONE;
|
||||
SetBits(pev->flags, FL_FLY);
|
||||
SetFlyingSpeed( NIGHT_GAUNT_SPEED );
|
||||
SetFlyingMomentum( 2.5 ); // Set momentum constant
|
||||
|
||||
m_afCapability = bits_CAP_RANGE_ATTACK1 | bits_CAP_FLY;
|
||||
|
||||
MonsterInit();
|
||||
|
||||
SetTouch( SlashTouch );
|
||||
SetUse( CombatUse );
|
||||
|
||||
m_idealDist = 384;
|
||||
m_flMinSpeed = 80;
|
||||
m_flMaxSpeed = 300;
|
||||
m_flMaxDist = 384;
|
||||
|
||||
Vector Forward;
|
||||
UTIL_MakeVectorsPrivate(pev->angles, Forward, 0, 0);
|
||||
pev->velocity = m_flightSpeed * Forward.Normalize();
|
||||
m_SaveVelocity = pev->velocity;
|
||||
}
|
||||
|
||||
//=========================================================
|
||||
// Precache - precaches all resources this monster needs
|
||||
//=========================================================
|
||||
void CNightGaunt :: Precache()
|
||||
{
|
||||
if (pev->model)
|
||||
PRECACHE_MODEL((char*)STRING(pev->model)); //LRC
|
||||
else
|
||||
PRECACHE_MODEL("models/monsters/ngaunt.mdl");
|
||||
|
||||
// PRECACHE_SOUND_ARRAY( pIdleSounds );
|
||||
PRECACHE_SOUND_ARRAY( pAlertSounds );
|
||||
PRECACHE_SOUND_ARRAY( pAttackSounds );
|
||||
PRECACHE_SOUND_ARRAY( pSlashSounds );
|
||||
PRECACHE_SOUND_ARRAY( pDieSounds );
|
||||
PRECACHE_SOUND_ARRAY( pPainSounds );
|
||||
}
|
||||
|
||||
//=========================================================
|
||||
// GetSchedule
|
||||
//=========================================================
|
||||
Schedule_t* CNightGaunt::GetSchedule()
|
||||
{
|
||||
// ALERT( at_console, "GetSchedule( )\n" );
|
||||
switch(m_MonsterState)
|
||||
{
|
||||
case MONSTERSTATE_IDLE:
|
||||
m_flightSpeed = NIGHT_GAUNT_SPEED / 2;
|
||||
return GetScheduleOfType( SCHED_IDLE_WALK );
|
||||
|
||||
case MONSTERSTATE_ALERT:
|
||||
m_flightSpeed = NIGHT_GAUNT_SPEED - 50;
|
||||
return GetScheduleOfType( SCHED_IDLE_WALK );
|
||||
|
||||
case MONSTERSTATE_COMBAT:
|
||||
m_flMaxSpeed = NIGHT_GAUNT_SPEED + 50;
|
||||
// eat them
|
||||
if ( HasConditions( bits_COND_CAN_MELEE_ATTACK1 ) )
|
||||
{
|
||||
return GetScheduleOfType( SCHED_MELEE_ATTACK1 );
|
||||
}
|
||||
// chase them down and eat them
|
||||
if ( HasConditions( bits_COND_CAN_RANGE_ATTACK1 ) )
|
||||
{
|
||||
return GetScheduleOfType( SCHED_CHASE_ENEMY );
|
||||
}
|
||||
if ( HasConditions( bits_COND_HEAVY_DAMAGE ) )
|
||||
{
|
||||
m_bOnAttack = TRUE;
|
||||
}
|
||||
if ( pev->health < pev->max_health - 20 )
|
||||
{
|
||||
m_bOnAttack = TRUE;
|
||||
}
|
||||
|
||||
return GetScheduleOfType( SCHED_STANDOFF );
|
||||
}
|
||||
|
||||
return CFlyingMonster :: GetSchedule();
|
||||
}
|
||||
|
||||
|
||||
//=========================================================
|
||||
//=========================================================
|
||||
Schedule_t* CNightGaunt :: GetScheduleOfType ( int Type )
|
||||
{
|
||||
// ALERT( at_console, "GetScheduleOfType( %d ) %d\n", Type, m_bOnAttack );
|
||||
switch ( Type )
|
||||
{
|
||||
case SCHED_IDLE_WALK:
|
||||
return slFlyAround;
|
||||
case SCHED_STANDOFF:
|
||||
return slCircleEnemy;
|
||||
case SCHED_FAIL:
|
||||
return slFlyAgitated;
|
||||
case SCHED_DIE:
|
||||
return slNightGauntTwitchDie;
|
||||
case SCHED_CHASE_ENEMY:
|
||||
AttackSound( );
|
||||
}
|
||||
|
||||
return CBaseMonster :: GetScheduleOfType( Type );
|
||||
}
|
||||
|
||||
|
||||
|
||||
//=========================================================
|
||||
// Start task - selects the correct activity and performs
|
||||
// any necessary calculations to start the next task on the
|
||||
// schedule.
|
||||
//=========================================================
|
||||
void CNightGaunt::StartTask(Task_t *pTask)
|
||||
{
|
||||
switch (pTask->iTask)
|
||||
{
|
||||
case TASK_NIGHT_GAUNT_CIRCLE_ENEMY:
|
||||
break;
|
||||
case TASK_NIGHT_GAUNT_FLY:
|
||||
break;
|
||||
case TASK_SMALL_FLINCH:
|
||||
if (m_idealDist > 128)
|
||||
{
|
||||
m_flMaxDist = 512;
|
||||
m_idealDist = 512;
|
||||
}
|
||||
else
|
||||
{
|
||||
m_bOnAttack = TRUE;
|
||||
}
|
||||
CFlyingMonster::StartTask(pTask);
|
||||
break;
|
||||
|
||||
default:
|
||||
CFlyingMonster::StartTask(pTask);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
void CNightGaunt :: RunTask ( Task_t *pTask )
|
||||
{
|
||||
switch ( pTask->iTask )
|
||||
{
|
||||
case TASK_NIGHT_GAUNT_CIRCLE_ENEMY:
|
||||
if (m_hEnemy == NULL)
|
||||
{
|
||||
TaskComplete( );
|
||||
}
|
||||
else if (FVisible( m_hEnemy ))
|
||||
{
|
||||
Vector vecFrom = m_hEnemy->EyePosition( );
|
||||
|
||||
Vector vecDelta = (pev->origin - vecFrom).Normalize( );
|
||||
Vector vecFly = CrossProduct( vecDelta, Vector( 0, 0, 1 ) ).Normalize( );
|
||||
|
||||
if (DotProduct( vecFly, m_SaveVelocity ) < 0)
|
||||
vecFly = vecFly * -1.0;
|
||||
|
||||
Vector vecPos = vecFrom + vecDelta * m_idealDist + vecFly * 32;
|
||||
|
||||
// ALERT( at_console, "vecPos %.0f %.0f %.0f\n", vecPos.x, vecPos.y, vecPos.z );
|
||||
|
||||
TraceResult tr;
|
||||
|
||||
UTIL_TraceHull( vecFrom, vecPos, ignore_monsters, large_hull, m_hEnemy->edict(), &tr );
|
||||
|
||||
if (tr.flFraction > 0.5)
|
||||
vecPos = tr.vecEndPos;
|
||||
|
||||
m_SaveVelocity = m_SaveVelocity * 0.8 + 0.2 * (vecPos - pev->origin).Normalize() * m_flightSpeed;
|
||||
|
||||
// ALERT( at_console, "m_SaveVelocity %.2f %.2f %.2f\n", m_SaveVelocity.x, m_SaveVelocity.y, m_SaveVelocity.z );
|
||||
|
||||
if (HasConditions( bits_COND_ENEMY_FACING_ME ) && m_hEnemy->FVisible( this ))
|
||||
{
|
||||
m_flNextAlert -= 0.1;
|
||||
|
||||
if (m_idealDist < m_flMaxDist)
|
||||
{
|
||||
m_idealDist += 4;
|
||||
}
|
||||
if (m_flightSpeed > m_flMinSpeed)
|
||||
{
|
||||
m_flightSpeed -= 2;
|
||||
}
|
||||
else if (m_flightSpeed < m_flMinSpeed)
|
||||
{
|
||||
m_flightSpeed += 2;
|
||||
}
|
||||
if (m_flMinSpeed < m_flMaxSpeed)
|
||||
{
|
||||
m_flMinSpeed += 0.5;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
m_flNextAlert += 0.1;
|
||||
|
||||
if (m_idealDist > 128)
|
||||
{
|
||||
m_idealDist -= 4;
|
||||
}
|
||||
if (m_flightSpeed < m_flMaxSpeed)
|
||||
{
|
||||
m_flightSpeed += 4;
|
||||
}
|
||||
}
|
||||
// ALERT( at_console, "%.0f\n", m_idealDist );
|
||||
}
|
||||
else
|
||||
{
|
||||
m_flNextAlert = gpGlobals->time + 0.2;
|
||||
}
|
||||
|
||||
if (m_flNextAlert < gpGlobals->time)
|
||||
{
|
||||
// ALERT( at_console, "AlertSound()\n");
|
||||
AlertSound( );
|
||||
m_flNextAlert = gpGlobals->time + RANDOM_FLOAT( 3, 5 );
|
||||
}
|
||||
|
||||
break;
|
||||
case TASK_NIGHT_GAUNT_FLY:
|
||||
if (m_fSequenceFinished)
|
||||
{
|
||||
TaskComplete( );
|
||||
}
|
||||
break;
|
||||
case TASK_DIE:
|
||||
if ( m_fSequenceFinished )
|
||||
{
|
||||
CFlyingMonster :: RunTask ( pTask );
|
||||
|
||||
pev->deadflag = DEAD_DEAD;
|
||||
|
||||
TaskComplete( );
|
||||
}
|
||||
break;
|
||||
|
||||
default:
|
||||
CFlyingMonster :: RunTask ( pTask );
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
float CNightGaunt::VectorToPitch( const Vector &vec )
|
||||
{
|
||||
float pitch;
|
||||
if (vec.z == 0 && vec.x == 0)
|
||||
pitch = 0;
|
||||
else
|
||||
{
|
||||
pitch = (int) (atan2(vec.z, sqrt(vec.x*vec.x+vec.y*vec.y)) * 180 / M_PI);
|
||||
if (pitch < 0)
|
||||
pitch += 360;
|
||||
}
|
||||
return pitch;
|
||||
}
|
||||
|
||||
//=========================================================
|
||||
void CNightGaunt::Move(float flInterval)
|
||||
{
|
||||
CFlyingMonster::Move( flInterval );
|
||||
}
|
||||
|
||||
float CNightGaunt::FlPitchDiff( void )
|
||||
{
|
||||
float flPitchDiff;
|
||||
float flCurrentPitch;
|
||||
|
||||
flCurrentPitch = UTIL_AngleMod( pev->angles.z );
|
||||
|
||||
if ( flCurrentPitch == pev->idealpitch )
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
|
||||
flPitchDiff = pev->idealpitch - flCurrentPitch;
|
||||
|
||||
if ( pev->idealpitch > flCurrentPitch )
|
||||
{
|
||||
if (flPitchDiff >= 180)
|
||||
flPitchDiff = flPitchDiff - 360;
|
||||
}
|
||||
else
|
||||
{
|
||||
if (flPitchDiff <= -180)
|
||||
flPitchDiff = flPitchDiff + 360;
|
||||
}
|
||||
return flPitchDiff;
|
||||
}
|
||||
|
||||
float CNightGaunt :: ChangePitch( int speed )
|
||||
{
|
||||
if ( pev->movetype == MOVETYPE_FLY )
|
||||
{
|
||||
float diff = FlPitchDiff();
|
||||
float target = 0;
|
||||
if ( m_IdealActivity != GetStoppedActivity() )
|
||||
{
|
||||
if (diff < -20)
|
||||
target = 45;
|
||||
else if (diff > 20)
|
||||
target = -45;
|
||||
}
|
||||
pev->angles.x = UTIL_Approach(target, pev->angles.x, 220.0 * 0.1 );
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
float CNightGaunt::ChangeYaw( int speed )
|
||||
{
|
||||
if ( pev->movetype == MOVETYPE_FLY )
|
||||
{
|
||||
float diff = FlYawDiff();
|
||||
float target = 0;
|
||||
|
||||
if ( m_IdealActivity != GetStoppedActivity() )
|
||||
{
|
||||
if ( diff < -20 )
|
||||
target = 20;
|
||||
else if ( diff > 20 )
|
||||
target = -20;
|
||||
}
|
||||
pev->angles.z = UTIL_Approach( target, pev->angles.z, 220.0 * 0.1 );
|
||||
}
|
||||
return CFlyingMonster::ChangeYaw( speed );
|
||||
}
|
||||
|
||||
|
||||
Activity CNightGaunt:: GetStoppedActivity( void )
|
||||
{
|
||||
if ( pev->movetype != MOVETYPE_FLY ) // UNDONE: Ground idle here, IDLE may be something else
|
||||
return ACT_IDLE;
|
||||
return ACT_WALK;
|
||||
}
|
||||
|
||||
void CNightGaunt::MoveExecute( CBaseEntity *pTargetEnt, const Vector &vecDir, float flInterval )
|
||||
{
|
||||
m_SaveVelocity = vecDir * m_flightSpeed;
|
||||
}
|
||||
|
||||
|
||||
void CNightGaunt::MonsterThink ( void )
|
||||
{
|
||||
CFlyingMonster::MonsterThink( );
|
||||
|
||||
if (pev->deadflag == DEAD_NO)
|
||||
{
|
||||
if (m_MonsterState != MONSTERSTATE_SCRIPT)
|
||||
{
|
||||
Fly( );
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void CNightGaunt :: Stop( void )
|
||||
{
|
||||
if (!m_bOnAttack)
|
||||
m_flightSpeed = 80.0;
|
||||
}
|
||||
|
||||
void CNightGaunt::Fly( )
|
||||
{
|
||||
int retValue = 0;
|
||||
|
||||
Vector start = pev->origin;
|
||||
|
||||
Vector Angles;
|
||||
Vector Forward, Right, Up;
|
||||
|
||||
if (FBitSet( pev->flags, FL_ONGROUND))
|
||||
{
|
||||
pev->angles.x = 0;
|
||||
pev->angles.y += RANDOM_FLOAT( -45, 45 );
|
||||
ClearBits( pev->flags, FL_ONGROUND );
|
||||
|
||||
Angles = Vector( -pev->angles.x, pev->angles.y, pev->angles.z );
|
||||
UTIL_MakeVectorsPrivate(Angles, Forward, Right, Up);
|
||||
|
||||
pev->velocity = Forward * 200 + Up * 200;
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
if (m_bOnAttack && m_flightSpeed < m_flMaxSpeed)
|
||||
{
|
||||
m_flightSpeed += 40;
|
||||
}
|
||||
if (m_flightSpeed < 180)
|
||||
{
|
||||
if (m_IdealActivity == ACT_RUN)
|
||||
SetActivity( ACT_WALK );
|
||||
if (m_IdealActivity == ACT_WALK)
|
||||
pev->framerate = m_flightSpeed / 150.0;
|
||||
// ALERT( at_console, "walk %.2f\n", pev->framerate );
|
||||
}
|
||||
else
|
||||
{
|
||||
if (m_IdealActivity == ACT_WALK)
|
||||
SetActivity( ACT_RUN );
|
||||
if (m_IdealActivity == ACT_RUN)
|
||||
pev->framerate = m_flightSpeed / 150.0;
|
||||
// ALERT( at_console, "run %.2f\n", pev->framerate );
|
||||
}
|
||||
|
||||
#define PROBE_LENGTH 150
|
||||
|
||||
Angles = UTIL_VecToAngles( m_SaveVelocity );
|
||||
Angles.x = -Angles.x;
|
||||
UTIL_MakeVectorsPrivate(Angles, Forward, Right, Up);
|
||||
|
||||
Vector f, u, l, r, d;
|
||||
f = DoProbe(start + PROBE_LENGTH * Forward);
|
||||
r = DoProbe(start + PROBE_LENGTH/3 * Forward+Right);
|
||||
l = DoProbe(start + PROBE_LENGTH/3 * Forward-Right);
|
||||
u = DoProbe(start + PROBE_LENGTH/3 * Forward+Up);
|
||||
d = DoProbe(start + PROBE_LENGTH/3 * Forward-Up);
|
||||
|
||||
Vector SteeringVector = f+r+l+u+d;
|
||||
m_SaveVelocity = (m_SaveVelocity + SteeringVector/2).Normalize();
|
||||
|
||||
Angles = Vector( -pev->angles.x, pev->angles.y, pev->angles.z );
|
||||
UTIL_MakeVectorsPrivate(Angles, Forward, Right, Up);
|
||||
// ALERT( at_console, "%f : %f\n", Angles.x, Forward.z );
|
||||
|
||||
float flDot = DotProduct( Forward, m_SaveVelocity );
|
||||
if (flDot > 0.5)
|
||||
pev->velocity = m_SaveVelocity = m_SaveVelocity * m_flightSpeed;
|
||||
else if (flDot > 0)
|
||||
pev->velocity = m_SaveVelocity = m_SaveVelocity * m_flightSpeed * (flDot + 0.5);
|
||||
else
|
||||
pev->velocity = m_SaveVelocity = m_SaveVelocity * 80;
|
||||
|
||||
|
||||
Angles = UTIL_VecToAngles( m_SaveVelocity );
|
||||
|
||||
// Smooth Pitch
|
||||
//
|
||||
if (Angles.x > 180)
|
||||
Angles.x = Angles.x - 360;
|
||||
pev->angles.x = UTIL_Approach(Angles.x, pev->angles.x, 50 * 0.1 );
|
||||
if (pev->angles.x < -80) pev->angles.x = -80;
|
||||
if (pev->angles.x > 80) pev->angles.x = 80;
|
||||
|
||||
// Smooth Yaw and generate Roll
|
||||
//
|
||||
float turn = 360;
|
||||
// ALERT( at_console, "Y %.0f %.0f\n", Angles.y, pev->angles.y );
|
||||
|
||||
if (fabs(Angles.y - pev->angles.y) < fabs(turn))
|
||||
{
|
||||
turn = Angles.y - pev->angles.y;
|
||||
}
|
||||
if (fabs(Angles.y - pev->angles.y + 360) < fabs(turn))
|
||||
{
|
||||
turn = Angles.y - pev->angles.y + 360;
|
||||
}
|
||||
if (fabs(Angles.y - pev->angles.y - 360) < fabs(turn))
|
||||
{
|
||||
turn = Angles.y - pev->angles.y - 360;
|
||||
}
|
||||
|
||||
float speed = m_flightSpeed * 0.1;
|
||||
|
||||
// ALERT( at_console, "speed %.0f %f\n", turn, speed );
|
||||
if (fabs(turn) > speed)
|
||||
{
|
||||
if (turn < 0.0)
|
||||
{
|
||||
turn = -speed;
|
||||
}
|
||||
else
|
||||
{
|
||||
turn = speed;
|
||||
}
|
||||
}
|
||||
pev->angles.y += turn;
|
||||
pev->angles.z -= turn;
|
||||
pev->angles.y = fmod((pev->angles.y + 360.0), 360.0);
|
||||
|
||||
static float yaw_adj;
|
||||
|
||||
yaw_adj = yaw_adj * 0.8 + turn;
|
||||
|
||||
// ALERT( at_console, "yaw %f : %f\n", turn, yaw_adj );
|
||||
|
||||
SetBoneController( 0, -yaw_adj / 4.0 );
|
||||
|
||||
// Roll Smoothing
|
||||
//
|
||||
turn = 360;
|
||||
if (fabs(Angles.z - pev->angles.z) < fabs(turn))
|
||||
{
|
||||
turn = Angles.z - pev->angles.z;
|
||||
}
|
||||
if (fabs(Angles.z - pev->angles.z + 360) < fabs(turn))
|
||||
{
|
||||
turn = Angles.z - pev->angles.z + 360;
|
||||
}
|
||||
if (fabs(Angles.z - pev->angles.z - 360) < fabs(turn))
|
||||
{
|
||||
turn = Angles.z - pev->angles.z - 360;
|
||||
}
|
||||
speed = m_flightSpeed/2 * 0.1;
|
||||
if (fabs(turn) < speed)
|
||||
{
|
||||
pev->angles.z += turn;
|
||||
}
|
||||
else
|
||||
{
|
||||
if (turn < 0.0)
|
||||
{
|
||||
pev->angles.z -= speed;
|
||||
}
|
||||
else
|
||||
{
|
||||
pev->angles.z += speed;
|
||||
}
|
||||
}
|
||||
if (pev->angles.z < -20) pev->angles.z = -20;
|
||||
if (pev->angles.z > 20) pev->angles.z = 20;
|
||||
|
||||
UTIL_MakeVectorsPrivate( Vector( -Angles.x, Angles.y, Angles.z ), Forward, Right, Up);
|
||||
|
||||
}
|
||||
|
||||
|
||||
Vector CNightGaunt::DoProbe(const Vector &Probe)
|
||||
{
|
||||
Vector WallNormal = Vector(0,0,-1); // AIR normal is Straight Down for flying thing.
|
||||
float frac;
|
||||
BOOL bBumpedSomething = ProbeZ(pev->origin, Probe, &frac);
|
||||
|
||||
TraceResult tr;
|
||||
TRACE_MONSTER_HULL(edict(), pev->origin, Probe, dont_ignore_monsters, edict(), &tr);
|
||||
if ( tr.fAllSolid || tr.flFraction < 0.99 )
|
||||
{
|
||||
if (tr.flFraction < 0.0) tr.flFraction = 0.0;
|
||||
if (tr.flFraction > 1.0) tr.flFraction = 1.0;
|
||||
if (tr.flFraction < frac)
|
||||
{
|
||||
frac = tr.flFraction;
|
||||
bBumpedSomething = TRUE;
|
||||
WallNormal = tr.vecPlaneNormal;
|
||||
}
|
||||
}
|
||||
|
||||
if (bBumpedSomething && (m_hEnemy == NULL || tr.pHit != m_hEnemy->edict()))
|
||||
{
|
||||
Vector ProbeDir = Probe - pev->origin;
|
||||
|
||||
Vector NormalToProbeAndWallNormal = CrossProduct(ProbeDir, WallNormal);
|
||||
Vector SteeringVector = CrossProduct( NormalToProbeAndWallNormal, ProbeDir);
|
||||
|
||||
float SteeringForce = m_flightSpeed * (1-frac) * (DotProduct(WallNormal.Normalize(), m_SaveVelocity.Normalize()));
|
||||
if (SteeringForce < 0.0)
|
||||
{
|
||||
SteeringForce = -SteeringForce;
|
||||
}
|
||||
SteeringVector = SteeringForce * SteeringVector.Normalize();
|
||||
|
||||
return SteeringVector;
|
||||
}
|
||||
return Vector(0, 0, 0);
|
||||
}
|
||||
|
||||
#endif
|
|
@ -0,0 +1,79 @@
|
|||
|
||||
#ifndef NIGHT_GAUNT_H
|
||||
#define NIGHT_GAUNT_H
|
||||
|
||||
class CNightGaunt : public CFlyingMonster
|
||||
{
|
||||
public:
|
||||
void Spawn( void );
|
||||
void Precache( void );
|
||||
void SetYawSpeed( void );
|
||||
int Classify( void );
|
||||
Vector Center( void );
|
||||
void HandleAnimEvent( MonsterEvent_t *pEvent );
|
||||
CUSTOM_SCHEDULES;
|
||||
|
||||
int Save( CSave &save );
|
||||
int Restore( CRestore &restore );
|
||||
static TYPEDESCRIPTION m_SaveData[];
|
||||
|
||||
Schedule_t *GetSchedule( void );
|
||||
Schedule_t *GetScheduleOfType ( int Type );
|
||||
|
||||
void Killed( entvars_t *pevAttacker, int iGib );
|
||||
void BecomeDead( void );
|
||||
|
||||
void EXPORT CombatUse( CBaseEntity *pActivator, CBaseEntity *pCaller, USE_TYPE useType, float value );
|
||||
void EXPORT SlashTouch( CBaseEntity *pOther );
|
||||
|
||||
void StartTask( Task_t *pTask );
|
||||
void RunTask( Task_t *pTask );
|
||||
|
||||
BOOL CheckMeleeAttack1 ( float flDot, float flDist );
|
||||
BOOL CheckRangeAttack1 ( float flDot, float flDist );
|
||||
|
||||
float ChangeYaw( int speed );
|
||||
Activity GetStoppedActivity( void );
|
||||
|
||||
void Move( float flInterval );
|
||||
void MoveExecute( CBaseEntity *pTargetEnt, const Vector &vecDir, float flInterval );
|
||||
void MonsterThink( void );
|
||||
void Stop( void );
|
||||
void Fly( void );
|
||||
Vector DoProbe(const Vector &Probe);
|
||||
|
||||
float VectorToPitch( const Vector &vec);
|
||||
float FlPitchDiff( void );
|
||||
float ChangePitch( int speed );
|
||||
|
||||
Vector m_SaveVelocity;
|
||||
float m_idealDist;
|
||||
|
||||
float m_flEnemyTouched;
|
||||
BOOL m_bOnAttack;
|
||||
|
||||
float m_flMaxSpeed;
|
||||
float m_flMinSpeed;
|
||||
float m_flMaxDist;
|
||||
|
||||
float m_flNextAlert;
|
||||
|
||||
//static const char *pIdleSounds[];
|
||||
static const char *pAlertSounds[];
|
||||
static const char *pAttackSounds[];
|
||||
static const char *pSlashSounds[];
|
||||
static const char *pDieSounds[];
|
||||
static const char *pPainSounds[];
|
||||
|
||||
void IdleSound( void );
|
||||
void AlertSound( void );
|
||||
void AttackSound( void );
|
||||
void SlashSound( void );
|
||||
void DeathSound( void );
|
||||
void PainSound( void );
|
||||
};
|
||||
|
||||
|
||||
#endif
|
||||
|
||||
|
|
@ -0,0 +1,196 @@
|
|||
|
||||
#include "extdll.h"
|
||||
#include "util.h"
|
||||
#include "cbase.h"
|
||||
#include "monsters.h"
|
||||
#include "weapons.h"
|
||||
#include "nodes.h"
|
||||
#include "player.h"
|
||||
|
||||
#include "PowderOfIbn.h"
|
||||
#include "monster_PowderOfIbn.h"
|
||||
|
||||
LINK_ENTITY_TO_CLASS( weapon_powderofibn, CPowderOfIbn );
|
||||
|
||||
|
||||
void CPowderOfIbn::Spawn( )
|
||||
{
|
||||
Precache( );
|
||||
m_iId = WEAPON_POWDER_IBN;
|
||||
SET_MODEL(ENT(pev), "models/w_powderofibn.mdl");
|
||||
|
||||
pev->dmg = 0;
|
||||
|
||||
m_iDefaultAmmo = POWDER_IBN_DEFAULT_GIVE;
|
||||
|
||||
FallInit();// get ready to fall down.
|
||||
}
|
||||
|
||||
void CPowderOfIbn::Precache( void )
|
||||
{
|
||||
PRECACHE_MODEL("models/w_powderofibn.mdl");
|
||||
PRECACHE_MODEL("models/v_powderofibn.mdl");
|
||||
// PRECACHE_MODEL("models/p_powderofibn.mdl");
|
||||
}
|
||||
|
||||
int CPowderOfIbn::GetItemInfo(ItemInfo *p)
|
||||
{
|
||||
p->pszName = STRING(pev->classname);
|
||||
p->pszAmmo1 = "Powder Of Ibn";
|
||||
p->iMaxAmmo1 = POWDER_IBN_MAX_CARRY;
|
||||
p->pszAmmo2 = NULL;
|
||||
p->iMaxAmmo2 = -1;
|
||||
p->iMaxClip = WEAPON_NOCLIP;
|
||||
p->iSlot = 4;
|
||||
p->iPosition = 3;
|
||||
p->iId = m_iId = WEAPON_POWDER_IBN;
|
||||
p->iWeight = POWDER_IBN_WEIGHT;
|
||||
p->iFlags = ITEM_FLAG_LIMITINWORLD | ITEM_FLAG_EXHAUSTIBLE;
|
||||
|
||||
return 1;
|
||||
}
|
||||
|
||||
|
||||
BOOL CPowderOfIbn::Deploy( )
|
||||
{
|
||||
m_flReleaseThrow = -1;
|
||||
return DefaultDeploy( "models/v_powderofibn.mdl", "", IBN_DRAW, "powderofibn" );
|
||||
}
|
||||
|
||||
BOOL CPowderOfIbn::CanHolster( void )
|
||||
{
|
||||
// can only holster powder when not lit!
|
||||
return ( m_flStartThrow == 0 );
|
||||
}
|
||||
|
||||
void CPowderOfIbn::Holster( int skiplocal /* = 0 */ )
|
||||
{
|
||||
m_pPlayer->m_flNextAttack = UTIL_WeaponTimeBase() + 0.5;
|
||||
if (m_pPlayer->m_rgAmmo[m_iPrimaryAmmoType])
|
||||
{
|
||||
SendWeaponAnim( IBN_HOLSTER );
|
||||
}
|
||||
else
|
||||
{
|
||||
// no more powders!
|
||||
m_pPlayer->pev->weapons &= ~(1<<WEAPON_POWDER_IBN);
|
||||
SetThink( DestroyItem );
|
||||
SetNextThink( 0.1 );
|
||||
}
|
||||
|
||||
EMIT_SOUND(ENT(m_pPlayer->pev), CHAN_WEAPON, "common/null.wav", 1.0, ATTN_NORM);
|
||||
}
|
||||
|
||||
int CPowderOfIbn::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 CPowderOfIbn::PrimaryAttack()
|
||||
{
|
||||
if (!m_flStartThrow && m_pPlayer->m_rgAmmo[m_iPrimaryAmmoType] > 0)
|
||||
{
|
||||
m_flStartThrow = gpGlobals->time;
|
||||
m_flReleaseThrow = gpGlobals->time;
|
||||
|
||||
SendWeaponAnim( IBN_THROW );
|
||||
m_flTimeWeaponIdle = gpGlobals->time + 0.5;
|
||||
}
|
||||
}
|
||||
|
||||
void CPowderOfIbn::WeaponIdle( void )
|
||||
{
|
||||
//if (m_flReleaseThrow == 0)
|
||||
// m_flReleaseThrow = gpGlobals->time;
|
||||
|
||||
if (m_flTimeWeaponIdle > gpGlobals->time)
|
||||
return;
|
||||
|
||||
if (m_flStartThrow > 0)
|
||||
{
|
||||
Vector angThrow = m_pPlayer->pev->v_angle + m_pPlayer->pev->punchangle;
|
||||
|
||||
if (angThrow.x < 0)
|
||||
angThrow.x = -10 + angThrow.x * ((90 - 10) / 90.0);
|
||||
else
|
||||
angThrow.x = -10 + angThrow.x * ((90 + 10) / 90.0);
|
||||
|
||||
float flVel = (90 - angThrow.x) * 4;
|
||||
if (flVel > 500)
|
||||
flVel = 500;
|
||||
|
||||
UTIL_MakeVectors( angThrow );
|
||||
|
||||
Vector vecSrc = m_pPlayer->pev->origin + m_pPlayer->pev->view_ofs + gpGlobals->v_forward * 16;
|
||||
|
||||
Vector vecThrow = gpGlobals->v_forward * flVel + m_pPlayer->pev->velocity;
|
||||
|
||||
CMonsterPowderOfIbn::ShootContact( m_pPlayer->pev, vecSrc, vecThrow );
|
||||
|
||||
// player "shoot" animation
|
||||
m_pPlayer->SetAnimation( PLAYER_ATTACK1 );
|
||||
|
||||
m_flStartThrow = 0;
|
||||
m_flNextPrimaryAttack = gpGlobals->time + 0.5;
|
||||
m_flTimeWeaponIdle = gpGlobals->time + 0.5;
|
||||
|
||||
m_pPlayer->m_rgAmmo[m_iPrimaryAmmoType]--;
|
||||
|
||||
if ( !m_pPlayer->m_rgAmmo[m_iPrimaryAmmoType] )
|
||||
{
|
||||
// just threw last bottle of powder
|
||||
// set attack times in the future, and weapon idle in the future so we can see the whole throw
|
||||
// animation, weapon idle will automatically retire the weapon for us.
|
||||
m_flTimeWeaponIdle = m_flNextSecondaryAttack = m_flNextPrimaryAttack = gpGlobals->time + 0.5;// ensure that the animation can finish playing
|
||||
}
|
||||
return;
|
||||
}
|
||||
else if (m_flReleaseThrow > 0)
|
||||
{
|
||||
// we've finished the throw, restart.
|
||||
m_flStartThrow = 0;
|
||||
m_flReleaseThrow = -1;
|
||||
m_flTimeWeaponIdle = gpGlobals->time + RANDOM_FLOAT ( 10, 15 );
|
||||
|
||||
if (m_pPlayer->m_rgAmmo[m_iPrimaryAmmoType])
|
||||
{
|
||||
SendWeaponAnim( IBN_DRAW );
|
||||
}
|
||||
else
|
||||
{
|
||||
RetireWeapon();
|
||||
return;
|
||||
}
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
if (m_pPlayer->m_rgAmmo[m_iPrimaryAmmoType])
|
||||
{
|
||||
int iAnim;
|
||||
float flRand = RANDOM_FLOAT(0, 1);
|
||||
if (flRand <= 0.75)
|
||||
{
|
||||
iAnim = IBN_IDLE;
|
||||
m_flTimeWeaponIdle = gpGlobals->time + RANDOM_FLOAT ( 10, 15 );// how long till we do this again.
|
||||
}
|
||||
else
|
||||
{
|
||||
iAnim = IBN_FIDGET;
|
||||
m_flTimeWeaponIdle = gpGlobals->time + 75.0 / 30.0;
|
||||
}
|
||||
|
||||
SendWeaponAnim( iAnim );
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
|
@ -0,0 +1,59 @@
|
|||
|
||||
#ifndef POWDER_IBN_H
|
||||
#define POWDER_IBN_H
|
||||
|
||||
/***
|
||||
*
|
||||
* Copyright (c) 1999, 2000 Valve LLC. All rights reserved.
|
||||
*
|
||||
* This product contains software technology licensed from Id
|
||||
* Software, Inc. ("Id Technology"). Id Technology (c) 1996 Id Software, Inc.
|
||||
* All Rights Reserved.
|
||||
*
|
||||
* Use, distribution, and modification of this source code and/or resulting
|
||||
* object code is restricted to non-commercial enhancements to products from
|
||||
* Valve LLC. All other use, distribution, or modification is prohibited
|
||||
* without written permission from Valve LLC.
|
||||
*
|
||||
****/
|
||||
|
||||
#define IBN_PRIMARY_VOLUME 450
|
||||
|
||||
enum ibn_e {
|
||||
IBN_IDLE = 0,
|
||||
IBN_FIDGET,
|
||||
IBN_THROW, // toss
|
||||
IBN_HOLSTER,
|
||||
IBN_DRAW
|
||||
};
|
||||
|
||||
|
||||
|
||||
class CPowderOfIbn : public CBasePlayerWeapon
|
||||
{
|
||||
public:
|
||||
void Spawn( void );
|
||||
void Precache( void );
|
||||
int iItemSlot( void ) { return 4; }
|
||||
int GetItemInfo(ItemInfo *p);
|
||||
int AddToPlayer( CBasePlayer *pPlayer );
|
||||
|
||||
void PrimaryAttack( void );
|
||||
BOOL Deploy( void );
|
||||
BOOL CanHolster( void );
|
||||
void Holster( int skiplocal = 0 );
|
||||
void WeaponIdle( void );
|
||||
|
||||
virtual BOOL UseDecrement( void )
|
||||
{
|
||||
#if defined( CLIENT_WEAPONS )
|
||||
return TRUE;
|
||||
#else
|
||||
return FALSE;
|
||||
#endif
|
||||
}
|
||||
};
|
||||
|
||||
#endif
|
||||
|
||||
|
File diff suppressed because it is too large
Load Diff
|
@ -0,0 +1,72 @@
|
|||
|
||||
#ifndef PRIEST_H
|
||||
#define PRIEST_H
|
||||
|
||||
class CPriest : public CSquadMonster
|
||||
{
|
||||
public:
|
||||
void Spawn( void );
|
||||
void Precache( void );
|
||||
void SetYawSpeed( void );
|
||||
int ISoundMask( void );
|
||||
int Classify ( void );
|
||||
int IRelationship( CBaseEntity *pTarget );
|
||||
void HandleAnimEvent( MonsterEvent_t *pEvent );
|
||||
BOOL CheckMeleeAttack1 ( float flDot, float flDist );
|
||||
BOOL CheckRangeAttack1 ( float flDot, float flDist );
|
||||
BOOL CheckRangeAttack2 ( float flDot, float flDist );
|
||||
void CallForHelp( char *szClassname, float flDist, EHANDLE hEnemy, Vector &vecLocation );
|
||||
void TraceAttack( entvars_t *pevAttacker, float flDamage, Vector vecDir, TraceResult *ptr, int bitsDamageType);
|
||||
int TakeDamage( entvars_t* pevInflictor, entvars_t* pevAttacker, float flDamage, int bitsDamageType);
|
||||
Vector GetGunPosition( void );
|
||||
|
||||
void DeathSound( void );
|
||||
void PainSound( void );
|
||||
void AlertSound( void );
|
||||
void IdleSound( void );
|
||||
|
||||
void Killed( entvars_t *pevAttacker, int iGib );
|
||||
void GibMonster ( void );
|
||||
|
||||
void SetActivity ( Activity NewActivity );
|
||||
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 BeamGlow( void );
|
||||
void ZapBeam( int side );
|
||||
|
||||
CBeam *m_pBeam[PRIEST_MAX_BEAMS];
|
||||
|
||||
int m_iBeams;
|
||||
float m_flNextAttack;
|
||||
|
||||
BOOL m_fStanding;
|
||||
int m_voicePitch;
|
||||
|
||||
BOOL FOkToSpeak( void );
|
||||
void JustSpoke( void );
|
||||
void SpeakSentence( void );
|
||||
|
||||
static const char *pAttackHitSounds[];
|
||||
static const char *pAttackMissSounds[];
|
||||
static const char *pPainSounds[];
|
||||
static const char *pDeathSounds[];
|
||||
|
||||
int m_iSentence;
|
||||
static const char *pPriestSentences[];
|
||||
};
|
||||
|
||||
|
||||
|
||||
|
||||
#endif
|
||||
|
|
@ -0,0 +1,342 @@
|
|||
/***
|
||||
*
|
||||
* Copyright (c) 1999, 2000 Valve LLC. All rights reserved.
|
||||
*
|
||||
* This product contains software technology licensed from Id
|
||||
* Software, Inc. ("Id Technology"). Id Technology (c) 1996 Id Software, Inc.
|
||||
* All Rights Reserved.
|
||||
*
|
||||
* Use, distribution, and modification of this source code and/or resulting
|
||||
* object code is restricted to non-commercial enhancements to products from
|
||||
* Valve LLC. All other use, distribution, or modification is prohibited
|
||||
* without written permission from Valve LLC.
|
||||
*
|
||||
****/
|
||||
#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 rifle_e {
|
||||
RIFLE_IDLE1 = 0,
|
||||
RIFLE_FIRE1,
|
||||
RIFLE_RELOAD,
|
||||
RIFLE_CLOSEBREAK,
|
||||
RIFLE_BREAK,
|
||||
RIFLE_DRAW,
|
||||
RIFLE_HOLSTER
|
||||
};
|
||||
|
||||
|
||||
#include "Rifle.h"
|
||||
|
||||
|
||||
LINK_ENTITY_TO_CLASS( weapon_rifle, CRifle );
|
||||
|
||||
int CRifle::GetItemInfo(ItemInfo *p)
|
||||
{
|
||||
p->pszName = STRING(pev->classname);
|
||||
p->pszAmmo1 = "Rifle";
|
||||
p->iMaxAmmo1 = RIFLE_MAX_CARRY;
|
||||
p->pszAmmo2 = NULL;
|
||||
p->iMaxAmmo2 = -1;
|
||||
p->iMaxClip = RIFLE_MAX_CLIP;
|
||||
p->iFlags = 0;
|
||||
p->iSlot = 1;
|
||||
p->iPosition = 3;
|
||||
p->iId = m_iId = WEAPON_RIFLE;
|
||||
p->iWeight = RIFLE_WEIGHT;
|
||||
|
||||
return 1;
|
||||
}
|
||||
|
||||
int CRifle::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 CRifle::Spawn( )
|
||||
{
|
||||
pev->classname = MAKE_STRING("weapon_rifle"); // hack to allow for old names
|
||||
Precache( );
|
||||
m_iId = WEAPON_RIFLE;
|
||||
SET_MODEL(ENT(pev), "models/w_rifle.mdl");
|
||||
|
||||
m_iDefaultAmmo = RIFLE_DEFAULT_GIVE;
|
||||
|
||||
m_fInZoom = FALSE;
|
||||
|
||||
FallInit();// get ready to fall down.
|
||||
}
|
||||
|
||||
|
||||
void CRifle::Precache( void )
|
||||
{
|
||||
PRECACHE_MODEL("models/v_rifle.mdl");
|
||||
PRECACHE_MODEL("models/w_rifle.mdl");
|
||||
// PRECACHE_MODEL("models/p_rifle.mdl");
|
||||
|
||||
PRECACHE_MODEL("models/w_rifleammo.mdl");
|
||||
|
||||
PRECACHE_SOUND("items/9mmclip1.wav");
|
||||
|
||||
PRECACHE_SOUND ("weapons/rifle_reload.wav");
|
||||
PRECACHE_SOUND ("weapons/rifle_break.wav");
|
||||
//PRECACHE_SOUND ("weapons/rifle_closebreak.wav");
|
||||
PRECACHE_SOUND ("weapons/rifle_fire.wav");
|
||||
PRECACHE_SOUND ("weapons/rifle_dryfire.wav");
|
||||
|
||||
m_usFireRifle = PRECACHE_EVENT( 1, "events/rifle.sc" );
|
||||
}
|
||||
|
||||
BOOL CRifle::Deploy( )
|
||||
{
|
||||
if ( g_pGameRules->IsMultiplayer() )
|
||||
{
|
||||
// enable laser sight geometry.
|
||||
pev->body = 1;
|
||||
}
|
||||
else
|
||||
{
|
||||
pev->body = 0;
|
||||
}
|
||||
|
||||
return DefaultDeploy( "models/v_rifle.mdl", "", RIFLE_DRAW, "rifle" );
|
||||
}
|
||||
|
||||
|
||||
void CRifle::Holster( int skiplocal /* = 0 */ )
|
||||
{
|
||||
m_fInReload = FALSE;// cancel any reload in progress.
|
||||
|
||||
if ( m_fInZoom )
|
||||
{
|
||||
m_fInZoom = FALSE;
|
||||
m_pPlayer->m_iFOV = 0; // 0 means reset to default fov
|
||||
}
|
||||
|
||||
m_pPlayer->m_flNextAttack = UTIL_WeaponTimeBase() + 1.0;
|
||||
m_flTimeWeaponIdle = gpGlobals->time + 10 + RANDOM_FLOAT ( 0, 5 );
|
||||
SendWeaponAnim( RIFLE_HOLSTER );
|
||||
}
|
||||
|
||||
void CRifle::SecondaryAttack( void )
|
||||
{
|
||||
if (m_iClip <= 0) return;
|
||||
|
||||
if ( m_fInZoom )
|
||||
{
|
||||
m_fInZoom = FALSE;
|
||||
m_pPlayer->m_iFOV = 0; // 0 means reset to default fov
|
||||
}
|
||||
else
|
||||
{
|
||||
m_fInZoom = TRUE;
|
||||
m_pPlayer->m_iFOV = 40;
|
||||
}
|
||||
|
||||
m_flNextSecondaryAttack = gpGlobals->time + 0.5;
|
||||
}
|
||||
|
||||
void CRifle::PrimaryAttack()
|
||||
{
|
||||
// don't fire underwater
|
||||
if (m_pPlayer->pev->waterlevel == 3)
|
||||
{
|
||||
PlayEmptySound( );
|
||||
m_flNextPrimaryAttack = gpGlobals->time + 0.15;
|
||||
return;
|
||||
}
|
||||
|
||||
if (m_iClip <= 0)
|
||||
{
|
||||
if (m_fInZoom)
|
||||
SecondaryAttack();
|
||||
|
||||
if (!m_fFireOnEmpty)
|
||||
Reload( );
|
||||
else
|
||||
{
|
||||
EMIT_SOUND(ENT(m_pPlayer->pev), CHAN_WEAPON, "weapons/rifle_dryfire.wav", 0.8, ATTN_NORM);
|
||||
m_flNextPrimaryAttack = gpGlobals->time + 0.15;
|
||||
}
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
PLAYBACK_EVENT( 0, m_pPlayer->edict(), m_usFireRifle );
|
||||
|
||||
m_pPlayer->m_iWeaponVolume = LOUD_GUN_VOLUME;
|
||||
m_pPlayer->m_iWeaponFlash = BRIGHT_GUN_FLASH;
|
||||
|
||||
m_iClip--;
|
||||
|
||||
if (m_iClip <= 0)
|
||||
{
|
||||
if (m_fInZoom)
|
||||
SecondaryAttack();
|
||||
}
|
||||
|
||||
// player "shoot" animation
|
||||
m_pPlayer->SetAnimation( PLAYER_ATTACK1 );
|
||||
m_pPlayer->pev->effects = (int)(m_pPlayer->pev->effects) | EF_MUZZLEFLASH;
|
||||
|
||||
UTIL_MakeVectors( m_pPlayer->pev->v_angle + m_pPlayer->pev->punchangle );
|
||||
|
||||
Vector vecSrc = m_pPlayer->GetGunPosition( );
|
||||
Vector vecAiming = m_pPlayer->GetAutoaimVector( AUTOAIM_2DEGREES );
|
||||
m_pPlayer->FireBullets( 1, vecSrc, vecAiming, VECTOR_CONE_1DEGREES, 8192, BULLET_PLAYER_RIFLE, 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 = gpGlobals->time + 1.0;
|
||||
m_flTimeWeaponIdle = gpGlobals->time + RANDOM_FLOAT ( 10, 15 );
|
||||
}
|
||||
|
||||
|
||||
void CRifle::Reload( void )
|
||||
{
|
||||
if ( m_fInZoom )
|
||||
{
|
||||
m_fInZoom = FALSE;
|
||||
m_pPlayer->m_iFOV = 0; // 0 means reset to default fov
|
||||
}
|
||||
|
||||
if (m_pPlayer->m_rgAmmo[m_iPrimaryAmmoType] <= 0 || m_iClip == RIFLE_MAX_CLIP)
|
||||
return;
|
||||
|
||||
if (m_flNextReload > gpGlobals->time)
|
||||
return;
|
||||
|
||||
// don't reload until recoil is done
|
||||
if (m_flNextPrimaryAttack > gpGlobals->time)
|
||||
return;
|
||||
|
||||
// check to see if we're ready to reload
|
||||
if (m_fInReload == 0)
|
||||
{
|
||||
SendWeaponAnim( RIFLE_BREAK );
|
||||
EMIT_SOUND(ENT(m_pPlayer->pev), CHAN_WEAPON, "weapons/rifle_break.wav", 0.8, ATTN_NORM);
|
||||
m_fInReload = 1;
|
||||
m_pPlayer->m_flNextAttack = UTIL_WeaponTimeBase() + 0.75;
|
||||
m_flTimeWeaponIdle = gpGlobals->time + 0.75;
|
||||
m_flNextPrimaryAttack = gpGlobals->time + 1.0;
|
||||
m_flNextSecondaryAttack = gpGlobals->time + 1.0;
|
||||
return;
|
||||
}
|
||||
else if (m_fInReload == 1)
|
||||
{
|
||||
if (m_flTimeWeaponIdle > gpGlobals->time)
|
||||
return;
|
||||
// was waiting for gun to move to side
|
||||
m_fInReload = 2;
|
||||
|
||||
EMIT_SOUND_DYN(ENT(m_pPlayer->pev), CHAN_ITEM, "weapons/rifle_reload.wav", 1, ATTN_NORM, 0, 85 + RANDOM_LONG(0,0x1f));
|
||||
//if (RANDOM_LONG(0,1))
|
||||
// EMIT_SOUND_DYN(ENT(m_pPlayer->pev), CHAN_ITEM, "weapons/rifle_reload1.wav", 1, ATTN_NORM, 0, 85 + RANDOM_LONG(0,0x1f));
|
||||
//else
|
||||
// EMIT_SOUND_DYN(ENT(m_pPlayer->pev), CHAN_ITEM, "weapons/rifle_reload2.wav", 1, ATTN_NORM, 0, 85 + RANDOM_LONG(0,0x1f));
|
||||
|
||||
SendWeaponAnim( RIFLE_RELOAD );
|
||||
|
||||
m_flNextReload = gpGlobals->time + 0.5;
|
||||
m_flTimeWeaponIdle = gpGlobals->time + 0.5;
|
||||
}
|
||||
else
|
||||
{
|
||||
// Add them to the clip
|
||||
m_iClip += 1;
|
||||
m_pPlayer->m_rgAmmo[m_iPrimaryAmmoType] -= 1;
|
||||
m_fInReload = 1;
|
||||
}
|
||||
|
||||
// like the HL shotgun reload...
|
||||
//if (DefaultReload( 6, RIFLE_RELOAD, 2.0 ))
|
||||
//{
|
||||
// m_flSoundDelay = gpGlobals->time + 1.5;
|
||||
//}
|
||||
}
|
||||
|
||||
|
||||
void CRifle::WeaponIdle( void )
|
||||
{
|
||||
ResetEmptySound( );
|
||||
|
||||
m_pPlayer->GetAutoaimVector( AUTOAIM_10DEGREES );
|
||||
|
||||
if (m_flTimeWeaponIdle > gpGlobals->time)
|
||||
return;
|
||||
|
||||
if (m_iClip == 0 && m_fInReload == 0 && m_pPlayer->m_rgAmmo[m_iPrimaryAmmoType])
|
||||
{
|
||||
Reload( );
|
||||
}
|
||||
else if (m_fInReload != 0)
|
||||
{
|
||||
if (m_iClip != RIFLE_MAX_CLIP && m_pPlayer->m_rgAmmo[m_iPrimaryAmmoType])
|
||||
{
|
||||
Reload( );
|
||||
}
|
||||
else
|
||||
{
|
||||
// reload debounce has timed out
|
||||
SendWeaponAnim( RIFLE_CLOSEBREAK );
|
||||
|
||||
// play cocking sound
|
||||
//EMIT_SOUND_DYN(ENT(m_pPlayer->pev), CHAN_ITEM, "weapons/rifle_closebreak.wav", 1, ATTN_NORM, 0, 95 + RANDOM_LONG(0,0x1f));
|
||||
EMIT_SOUND_DYN(ENT(m_pPlayer->pev), CHAN_ITEM, "weapons/rifle_break.wav", 1, ATTN_NORM, 0, 95 + RANDOM_LONG(0,0x1f));
|
||||
m_fInReload = 0;
|
||||
m_flTimeWeaponIdle = gpGlobals->time + 0.75;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
SendWeaponAnim( RIFLE_IDLE1 );
|
||||
m_flTimeWeaponIdle = gpGlobals->time + (70.0/30.0);// * RANDOM_LONG(2, 5);
|
||||
}
|
||||
}
|
||||
|
||||
//////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
void CRifleAmmo::Spawn( void )
|
||||
{
|
||||
Precache( );
|
||||
SET_MODEL(ENT(pev), "models/w_rifleammo.mdl");
|
||||
CBasePlayerAmmo::Spawn( );
|
||||
}
|
||||
|
||||
void CRifleAmmo::Precache( void )
|
||||
{
|
||||
PRECACHE_MODEL ("models/w_rifleammo.mdl");
|
||||
PRECACHE_SOUND("items/9mmclip1.wav");
|
||||
}
|
||||
|
||||
BOOL CRifleAmmo::AddAmmo( CBaseEntity *pOther )
|
||||
{
|
||||
if (pOther->GiveAmmo( AMMO_RIFLE_GIVE, "Rifle", RIFLE_MAX_CARRY ) != -1)
|
||||
{
|
||||
EMIT_SOUND(ENT(pev), CHAN_ITEM, "items/9mmclip1.wav", 1, ATTN_NORM);
|
||||
return TRUE;
|
||||
}
|
||||
return FALSE;
|
||||
}
|
||||
|
||||
LINK_ENTITY_TO_CLASS( ammo_rifle, CRifleAmmo );
|
||||
|
||||
|
||||
#endif
|
|
@ -0,0 +1,53 @@
|
|||
|
||||
#ifndef RIFLE_H
|
||||
#define RIFLE_H
|
||||
|
||||
class CRifle : public CBasePlayerWeapon
|
||||
{
|
||||
public:
|
||||
void Spawn( void );
|
||||
void Precache( void );
|
||||
int iItemSlot( void ) { return 1; }
|
||||
int GetItemInfo(ItemInfo *p);
|
||||
int AddToPlayer( CBasePlayer *pPlayer );
|
||||
void PrimaryAttack( void );
|
||||
void SecondaryAttack( void );
|
||||
BOOL Deploy( void );
|
||||
void Holster( int skiplocal = 0 );
|
||||
void Reload( void );
|
||||
void WeaponIdle( void );
|
||||
int m_fInReload;
|
||||
float m_flNextReload;
|
||||
|
||||
BOOL m_fInZoom;// don't save this.
|
||||
|
||||
virtual BOOL UseDecrement( void )
|
||||
{
|
||||
#if defined( CLIENT_WEAPONS )
|
||||
return TRUE;
|
||||
#else
|
||||
return FALSE;
|
||||
#endif
|
||||
}
|
||||
|
||||
private:
|
||||
unsigned short m_usFireRifle;
|
||||
|
||||
};
|
||||
|
||||
|
||||
//////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
class CRifleAmmo : public CBasePlayerAmmo
|
||||
{
|
||||
void Spawn( void );
|
||||
void Precache( void );
|
||||
BOOL AddAmmo( CBaseEntity *pOther );
|
||||
};
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
#endif
|
||||
|
|
@ -0,0 +1,739 @@
|
|||
/***
|
||||
*
|
||||
* Copyright (c) 1999, 2000 Valve LLC. All rights reserved.
|
||||
*
|
||||
* This product contains software technology licensed from Id
|
||||
* Software, Inc. ("Id Technology"). Id Technology (c) 1996 Id Software, Inc.
|
||||
* All Rights Reserved.
|
||||
*
|
||||
* This source code contains proprietary and confidential information of
|
||||
* Valve LLC and its suppliers. Access to this code is restricted to
|
||||
* persons who have executed a written SDK license with Valve. Any access,
|
||||
* use or distribution of this code by or to any unlicensed person is illegal.
|
||||
*
|
||||
****/
|
||||
//=========================================================
|
||||
// Serpent Man 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"
|
||||
#include "animation.h"
|
||||
#include "snake.h"
|
||||
|
||||
extern DLL_GLOBAL int g_iSkillLevel;
|
||||
|
||||
// TO DO:
|
||||
// cast serpent staff
|
||||
// melee attack
|
||||
|
||||
#define GUN_GROUP 2
|
||||
#define GUN_STAFF 0
|
||||
#define GUN_NONE 1
|
||||
|
||||
#define MAX_SERPENTS 12
|
||||
|
||||
//=========================================================
|
||||
// Monster's Anim Events Go Here
|
||||
//=========================================================
|
||||
#define SERPENT_MAN_AE_CAST ( 1 )
|
||||
#define SERPENT_MAN_AE_CLAW ( 2 )
|
||||
#define SERPENT_MAN_AE_DROP_STAFF ( 3 )
|
||||
|
||||
#include "SerpentMan.h"
|
||||
|
||||
|
||||
LINK_ENTITY_TO_CLASS( monster_serpentman, CSerpentMan );
|
||||
|
||||
|
||||
TYPEDESCRIPTION CSerpentMan::m_SaveData[] =
|
||||
{
|
||||
DEFINE_FIELD( CSerpentMan, m_flNextAttack, FIELD_TIME ),
|
||||
DEFINE_FIELD( CSerpentMan, m_fStanding, FIELD_BOOLEAN ),
|
||||
DEFINE_FIELD( CSerpentMan, m_voicePitch, FIELD_INTEGER ),
|
||||
DEFINE_FIELD( CSerpentMan, m_iNumSerpents, FIELD_INTEGER ),
|
||||
};
|
||||
|
||||
IMPLEMENT_SAVERESTORE( CSerpentMan, CSquadMonster );
|
||||
|
||||
|
||||
|
||||
|
||||
const char *CSerpentMan::pAttackHitSounds[] =
|
||||
{
|
||||
"zombie/claw_strike1.wav",
|
||||
"zombie/claw_strike2.wav",
|
||||
"zombie/claw_strike3.wav",
|
||||
};
|
||||
|
||||
const char *CSerpentMan::pAttackMissSounds[] =
|
||||
{
|
||||
"zombie/claw_miss1.wav",
|
||||
"zombie/claw_miss2.wav",
|
||||
};
|
||||
|
||||
const char *CSerpentMan::pAttackSounds[] =
|
||||
{
|
||||
"serpentman/sm_attack1.wav",
|
||||
"serpentman/sm_attack2.wav",
|
||||
"serpentman/sm_attack3.wav",
|
||||
};
|
||||
|
||||
const char *CSerpentMan::pPainSounds[] =
|
||||
{
|
||||
"serpentman/sm_pain1.wav",
|
||||
"serpentman/sm_pain2.wav",
|
||||
"serpentman/sm_pain3.wav",
|
||||
};
|
||||
|
||||
const char *CSerpentMan::pDeathSounds[] =
|
||||
{
|
||||
"serpentman/sm_die1.wav",
|
||||
"serpentman/sm_die2.wav",
|
||||
};
|
||||
|
||||
//=========================================================
|
||||
// Classify - indicates this monster's place in the
|
||||
// relationship table.
|
||||
//=========================================================
|
||||
int CSerpentMan :: Classify ( void )
|
||||
{
|
||||
return m_iClass?m_iClass:CLASS_ALIEN_MILITARY;
|
||||
}
|
||||
|
||||
|
||||
int CSerpentMan::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 CSerpentMan :: CallForHelp( char *szClassname, float flDist, EHANDLE hEnemy, Vector &vecLocation )
|
||||
{
|
||||
// ALERT( at_aiconsole, "help " );
|
||||
|
||||
// skip ones not on my netname
|
||||
if ( FStringNull( pev->netname ))
|
||||
return;
|
||||
|
||||
CBaseEntity *pEntity = NULL;
|
||||
|
||||
while ((pEntity = UTIL_FindEntityByString( pEntity, "netname", STRING( pev->netname ))) != NULL)
|
||||
{
|
||||
float d = (pev->origin - pEntity->pev->origin).Length();
|
||||
if (d < flDist)
|
||||
{
|
||||
CBaseMonster *pMonster = pEntity->MyMonsterPointer( );
|
||||
if (pMonster)
|
||||
{
|
||||
pMonster->m_afMemory |= bits_MEMORY_PROVOKED;
|
||||
pMonster->PushEnemy( hEnemy, vecLocation );
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
//=========================================================
|
||||
// ALertSound - scream
|
||||
//=========================================================
|
||||
void CSerpentMan :: AlertSound( void )
|
||||
{
|
||||
if ( m_hEnemy != NULL )
|
||||
{
|
||||
SENTENCEG_PlayRndSz(ENT(pev), "SM_ALERT", 0.85, ATTN_NORM, 0, m_voicePitch);
|
||||
|
||||
CallForHelp( "monster_serpent_man", 512, m_hEnemy, m_vecEnemyLKP );
|
||||
CallForHelp( "monster_human_cultist", 512, m_hEnemy, m_vecEnemyLKP );
|
||||
}
|
||||
}
|
||||
|
||||
//=========================================================
|
||||
// IdleSound
|
||||
//=========================================================
|
||||
void CSerpentMan :: IdleSound( void )
|
||||
{
|
||||
if (RANDOM_LONG( 0, 2 ) == 0)
|
||||
{
|
||||
SENTENCEG_PlayRndSz(ENT(pev), "SM_IDLE", 0.85, ATTN_NORM, 0, m_voicePitch);
|
||||
}
|
||||
}
|
||||
|
||||
//=========================================================
|
||||
// AttackSound
|
||||
//=========================================================
|
||||
void CSerpentMan :: AttackSound( void )
|
||||
{
|
||||
if (RANDOM_LONG( 0, 2 ) == 0)
|
||||
{
|
||||
EMIT_SOUND_DYN ( ENT(pev), CHAN_WEAPON, pAttackSounds[ RANDOM_LONG(0,ARRAYSIZE(pAttackSounds)-1) ], 1.0, ATTN_NORM, 0, m_voicePitch );
|
||||
}
|
||||
}
|
||||
|
||||
//=========================================================
|
||||
// PainSound
|
||||
//=========================================================
|
||||
void CSerpentMan :: 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 CSerpentMan :: 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 CSerpentMan :: ISoundMask ( void)
|
||||
{
|
||||
return bits_SOUND_WORLD |
|
||||
bits_SOUND_COMBAT |
|
||||
bits_SOUND_DANGER |
|
||||
bits_SOUND_PLAYER;
|
||||
}
|
||||
|
||||
|
||||
void CSerpentMan::Killed( entvars_t *pevAttacker, int iGib )
|
||||
{
|
||||
CSquadMonster::Killed( pevAttacker, iGib );
|
||||
}
|
||||
|
||||
//=========================================================
|
||||
// SetYawSpeed - allows each sequence to have a different
|
||||
// turn rate associated with it.
|
||||
//=========================================================
|
||||
void CSerpentMan :: 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;
|
||||
}
|
||||
|
||||
//=========================================================
|
||||
// GetGunPosition return the end of the barrel
|
||||
//=========================================================
|
||||
|
||||
Vector CSerpentMan :: GetGunPosition( )
|
||||
{
|
||||
// because origin is at the centre of the box, not at the bottom
|
||||
if (m_fStanding )
|
||||
{
|
||||
return pev->origin + Vector( 0, 0, 24 );
|
||||
// return pev->origin + Vector( 0, 0, 60 );
|
||||
}
|
||||
else
|
||||
{
|
||||
return pev->origin + Vector( 0, 0, 12 );
|
||||
// return pev->origin + Vector( 0, 0, 48 );
|
||||
}
|
||||
}
|
||||
//=========================================================
|
||||
// GibMonster - make gun fly through the air.
|
||||
//=========================================================
|
||||
void CSerpentMan :: GibMonster ( void )
|
||||
{
|
||||
Vector vecGunPos;
|
||||
Vector vecGunAngles;
|
||||
|
||||
if ( GetBodygroup( GUN_GROUP ) != GUN_NONE )
|
||||
{// throw a serpent staff if the serpent man has one
|
||||
GetAttachment( 0, vecGunPos, vecGunAngles );
|
||||
|
||||
CBaseEntity* pGun = DropItem( "weapon_serpentstaff", vecGunPos, vecGunAngles );
|
||||
|
||||
if ( pGun )
|
||||
{
|
||||
pGun->pev->velocity = Vector (RANDOM_FLOAT(-100,100), RANDOM_FLOAT(-100,100), RANDOM_FLOAT(200,300));
|
||||
pGun->pev->avelocity = Vector ( 0, RANDOM_FLOAT( 200, 400 ), 0 );
|
||||
}
|
||||
}
|
||||
|
||||
CBaseMonster :: GibMonster();
|
||||
}
|
||||
|
||||
//=========================================================
|
||||
// HandleAnimEvent - catches the monster-specific messages
|
||||
// that occur when tagged animation frames are played.
|
||||
//
|
||||
// Returns number of events handled, 0 if none.
|
||||
//=========================================================
|
||||
void CSerpentMan :: HandleAnimEvent( MonsterEvent_t *pEvent )
|
||||
{
|
||||
int box = 24;
|
||||
int spawndist = 64;
|
||||
TraceResult tr;
|
||||
Vector trace_origin;
|
||||
Vector vecShootOrigin;
|
||||
Vector vecShootDir;
|
||||
|
||||
// ALERT( at_console, "event %d : %f\n", pEvent->event, pev->frame );
|
||||
switch( pEvent->event )
|
||||
{
|
||||
case SERPENT_MAN_AE_CAST:
|
||||
UTIL_MakeVectors( pev->v_angle );
|
||||
|
||||
// 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 = pev->origin;
|
||||
if ( !m_fStanding )
|
||||
{
|
||||
trace_origin = trace_origin - ( VEC_HULL_MIN - VEC_DUCK_HULL_MIN );
|
||||
}
|
||||
|
||||
// find place to toss monster - this is some horrible code I have written here...
|
||||
// UTIL_TraceLine( trace_origin + angle * 20, trace_origin + angle * 64, dont_ignore_monsters, NULL, &tr );
|
||||
// firstly check all areas within the box to be created
|
||||
BOOL b1, b2, b3, b4;
|
||||
vecShootOrigin = pev->origin + Vector( 0, 0, 55 );
|
||||
vecShootDir = ShootAtEnemy( vecShootOrigin );
|
||||
|
||||
UTIL_TraceLine( trace_origin + vecShootDir * 20, trace_origin + vecShootDir * spawndist + Vector(box,box,0), dont_ignore_monsters, NULL, &tr );
|
||||
b1 = (tr.fAllSolid == 0 && tr.fStartSolid == 0 && tr.flFraction > 0.99);
|
||||
UTIL_TraceLine( trace_origin + vecShootDir * 20, trace_origin + vecShootDir * spawndist + Vector(-box,-box,0), dont_ignore_monsters, NULL, &tr );
|
||||
b2 = (tr.fAllSolid == 0 && tr.fStartSolid == 0 && tr.flFraction > 0.99);
|
||||
UTIL_TraceLine( trace_origin + vecShootDir * 20, trace_origin + vecShootDir * spawndist + Vector(-box,box,0), dont_ignore_monsters, NULL, &tr );
|
||||
b3 = (tr.fAllSolid == 0 && tr.fStartSolid == 0 && tr.flFraction > 0.99);
|
||||
UTIL_TraceLine( trace_origin + vecShootDir * 20, trace_origin + vecShootDir * spawndist + Vector(box,-box,0), dont_ignore_monsters, NULL, &tr );
|
||||
b4 = (tr.fAllSolid == 0 && tr.fStartSolid == 0 && tr.flFraction > 0.99);
|
||||
UTIL_TraceLine( trace_origin + vecShootDir * 20, trace_origin + vecShootDir * spawndist, dont_ignore_monsters, NULL, &tr );
|
||||
|
||||
// if we are clear at all corners of the box the snake is going to be created in
|
||||
if (b1 && b2 && b3 && b4)
|
||||
{
|
||||
Vector temp;
|
||||
temp.x = temp.y = temp.z = 0.0;
|
||||
temp.y = pev->v_angle.y;
|
||||
|
||||
//CBaseEntity *pSnake = CBaseEntity::Create( "monster_snake", tr.vecEndPos, temp, m_pPlayer->edict() );
|
||||
CBaseEntity *pSnake = CBaseEntity::Create( "monster_snake", tr.vecEndPos, pev->angles );
|
||||
((CSnake*)pSnake)->SetOwner(this);
|
||||
pSnake->pev->spawnflags |= 1;
|
||||
SetBits(pSnake->pev->spawnflags,SF_MONSTER_NO_YELLOW_BLOBS);
|
||||
// drop to floor, to check if we have landed on top of an entity...
|
||||
DROP_TO_FLOOR( ENT( pSnake->pev ) );
|
||||
|
||||
CBaseEntity *pList[2];
|
||||
int buf = 24;
|
||||
// if there are any no other entities nearby (particularly under it!), then...
|
||||
int count = UTIL_EntitiesInBox( pList, 2, pSnake->pev->origin - Vector(buf,buf,buf), pSnake->pev->origin + Vector(buf,buf,buf), FL_CLIENT|FL_MONSTER );
|
||||
//...put the snake there and decrement ammo
|
||||
if ( count <= 1 )
|
||||
{
|
||||
pSnake->pev->velocity = vecShootDir * 100 + pev->velocity;
|
||||
|
||||
m_iNumSerpents--;
|
||||
}
|
||||
// otherwise we do not want this snake...as it will cause yellow blobs!
|
||||
else
|
||||
{
|
||||
UTIL_Remove(pSnake);
|
||||
}
|
||||
|
||||
m_flNextAttack = gpGlobals->time + 2.0;
|
||||
}
|
||||
break;
|
||||
case SERPENT_MAN_AE_DROP_STAFF:
|
||||
{
|
||||
Vector vecGunPos;
|
||||
Vector vecGunAngles;
|
||||
|
||||
GetAttachment( 0, vecGunPos, vecGunAngles );
|
||||
|
||||
// switch to body group with no gun.
|
||||
SetBodygroup( GUN_GROUP, GUN_NONE );
|
||||
|
||||
// now spawn a serpent staff.
|
||||
DropItem( "weapon_serpentstaff", vecGunPos, vecGunAngles );
|
||||
|
||||
}
|
||||
break;
|
||||
|
||||
case SERPENT_MAN_AE_CLAW:
|
||||
{
|
||||
Vector oldorig = pev->origin;
|
||||
CBaseEntity *pHurt = NULL;
|
||||
// check down below in stages for snakes...
|
||||
for (int dz = 0; dz >=-3; dz--)
|
||||
{
|
||||
pev->origin = oldorig;
|
||||
pev->origin.z += dz * 12;
|
||||
pHurt = CheckTraceHullAttack( 70, gSkillData.serpentmanDmgStaff, DMG_POISON );
|
||||
if (pHurt)
|
||||
{
|
||||
break;
|
||||
}
|
||||
}
|
||||
pev->origin = oldorig;
|
||||
|
||||
if ( pHurt )
|
||||
{
|
||||
// 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 );
|
||||
}
|
||||
|
||||
// also play an attack sound
|
||||
AttackSound();
|
||||
}
|
||||
break;
|
||||
|
||||
default:
|
||||
CSquadMonster::HandleAnimEvent( pEvent );
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
//=========================================================
|
||||
// CheckMeleeAttack1
|
||||
//=========================================================
|
||||
BOOL CSerpentMan :: CheckMeleeAttack1 ( float flDot, float flDist )
|
||||
{
|
||||
CBaseMonster *pEnemy;
|
||||
|
||||
if ( m_hEnemy != NULL )
|
||||
{
|
||||
pEnemy = m_hEnemy->MyMonsterPointer();
|
||||
|
||||
if ( !pEnemy )
|
||||
{
|
||||
return FALSE;
|
||||
}
|
||||
}
|
||||
|
||||
if ( flDist <= 64 && flDot >= 0.7 &&
|
||||
pEnemy->Classify() != CLASS_ALIEN_BIOWEAPON &&
|
||||
pEnemy->Classify() != CLASS_PLAYER_BIOWEAPON )
|
||||
{
|
||||
return TRUE;
|
||||
}
|
||||
return FALSE;
|
||||
}
|
||||
|
||||
//=========================================================
|
||||
// CheckRangeAttack1 - normal beam attack
|
||||
//=========================================================
|
||||
BOOL CSerpentMan :: CheckRangeAttack1 ( float flDot, float flDist )
|
||||
{
|
||||
if (m_flNextAttack > gpGlobals->time || m_iNumSerpents <= 0)
|
||||
{
|
||||
return FALSE;
|
||||
}
|
||||
|
||||
return CSquadMonster::CheckRangeAttack1( flDot, flDist );
|
||||
}
|
||||
|
||||
//=========================================================
|
||||
// CheckRangeAttack2 - check bravery and try to resurect dead comrades
|
||||
//=========================================================
|
||||
BOOL CSerpentMan :: CheckRangeAttack2 ( float flDot, float flDist )
|
||||
{
|
||||
return FALSE;
|
||||
}
|
||||
|
||||
|
||||
//=========================================================
|
||||
// StartTask
|
||||
//=========================================================
|
||||
void CSerpentMan :: StartTask ( Task_t *pTask )
|
||||
{
|
||||
CSquadMonster :: StartTask ( pTask );
|
||||
}
|
||||
|
||||
|
||||
//=========================================================
|
||||
// Spawn
|
||||
//=========================================================
|
||||
void CSerpentMan :: Spawn()
|
||||
{
|
||||
Precache( );
|
||||
|
||||
if (pev->model)
|
||||
SET_MODEL(ENT(pev), STRING(pev->model)); //LRC
|
||||
else
|
||||
SET_MODEL(ENT(pev), "models/serpentman.mdl");
|
||||
//UTIL_SetSize(pev, VEC_HUMAN_HULL_MIN, VEC_HUMAN_HULL_MAX);
|
||||
UTIL_SetSize(pev, Vector(-16,-16,-36), Vector(16,16,36));
|
||||
|
||||
pev->solid = SOLID_SLIDEBOX;
|
||||
pev->movetype = MOVETYPE_STEP;
|
||||
m_bloodColor = BLOOD_COLOR_GREEN;
|
||||
pev->effects = 0;
|
||||
if (pev->health == 0)
|
||||
pev->health = gSkillData.serpentmanHealth;
|
||||
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 );
|
||||
|
||||
m_iNumSerpents = MAX_SERPENTS;
|
||||
|
||||
MonsterInit();
|
||||
}
|
||||
|
||||
//=========================================================
|
||||
// Precache - precaches all resources this monster needs
|
||||
//=========================================================
|
||||
void CSerpentMan :: Precache()
|
||||
{
|
||||
int i;
|
||||
|
||||
if (pev->model)
|
||||
PRECACHE_MODEL((char*)STRING(pev->model)); //LRC
|
||||
else
|
||||
PRECACHE_MODEL("models/serpentman.mdl");
|
||||
PRECACHE_SOUND("zombie/zo_pain2.wav");
|
||||
PRECACHE_SOUND("weapons/cbar_miss1.wav");
|
||||
PRECACHE_SOUND("serpentman/sm_summon.wav");
|
||||
|
||||
for ( i = 0; i < ARRAYSIZE( pAttackSounds ); i++ )
|
||||
PRECACHE_SOUND((char *)pAttackSounds[i]);
|
||||
|
||||
for ( i = 0; i < ARRAYSIZE( pAttackHitSounds ); i++ )
|
||||
PRECACHE_SOUND((char *)pAttackHitSounds[i]);
|
||||
|
||||
for ( i = 0; i < ARRAYSIZE( pAttackMissSounds ); i++ )
|
||||
PRECACHE_SOUND((char *)pAttackMissSounds[i]);
|
||||
|
||||
for ( i = 0; i < ARRAYSIZE( pPainSounds ); i++ )
|
||||
PRECACHE_SOUND((char *)pPainSounds[i]);
|
||||
|
||||
for ( i = 0; i < ARRAYSIZE( pDeathSounds ); i++ )
|
||||
PRECACHE_SOUND((char *)pDeathSounds[i]);
|
||||
|
||||
//UTIL_PrecacheOther( "test_effect" );
|
||||
}
|
||||
|
||||
|
||||
//=========================================================
|
||||
// TakeDamage - get provoked when injured
|
||||
//=========================================================
|
||||
|
||||
int CSerpentMan :: TakeDamage( entvars_t* pevInflictor, entvars_t* pevAttacker, float flDamage, int bitsDamageType)
|
||||
{
|
||||
// don't slash one of your own
|
||||
if ((bitsDamageType & DMG_POISON) || (bitsDamageType & DMG_NERVEGAS) || (bitsDamageType & DMG_PARALYZE))
|
||||
return 0;
|
||||
|
||||
m_afMemory |= bits_MEMORY_PROVOKED;
|
||||
return CSquadMonster::TakeDamage(pevInflictor, pevAttacker, flDamage, bitsDamageType);
|
||||
}
|
||||
|
||||
|
||||
void CSerpentMan::TraceAttack( entvars_t *pevAttacker, float flDamage, Vector vecDir, TraceResult *ptr, int bitsDamageType)
|
||||
{
|
||||
if ((bitsDamageType & DMG_PARALYZE) || (bitsDamageType & DMG_NERVEGAS) || (bitsDamageType & DMG_POISON))
|
||||
return;
|
||||
|
||||
CSquadMonster::TraceAttack( pevAttacker, flDamage, vecDir, ptr, bitsDamageType );
|
||||
}
|
||||
|
||||
|
||||
//=========================================================
|
||||
// AI Schedules Specific to this monster
|
||||
//=========================================================
|
||||
|
||||
|
||||
|
||||
// primary range attack
|
||||
Task_t tlSerpentManAttack1[] =
|
||||
{
|
||||
{ TASK_STOP_MOVING, 0 },
|
||||
{ TASK_FACE_IDEAL, (float)0 },
|
||||
{ TASK_RANGE_ATTACK1, (float)0 },
|
||||
};
|
||||
|
||||
Schedule_t slSerpentManAttack1[] =
|
||||
{
|
||||
{
|
||||
tlSerpentManAttack1,
|
||||
ARRAYSIZE ( tlSerpentManAttack1 ),
|
||||
bits_COND_CAN_MELEE_ATTACK1 |
|
||||
bits_COND_HEAR_SOUND |
|
||||
bits_COND_HEAVY_DAMAGE,
|
||||
|
||||
bits_SOUND_DANGER,
|
||||
"SerpentMan Range Attack1"
|
||||
},
|
||||
};
|
||||
|
||||
|
||||
DEFINE_CUSTOM_SCHEDULES( CSerpentMan )
|
||||
{
|
||||
slSerpentManAttack1,
|
||||
};
|
||||
|
||||
IMPLEMENT_CUSTOM_SCHEDULES( CSerpentMan, CSquadMonster );
|
||||
|
||||
|
||||
//=========================================================
|
||||
//=========================================================
|
||||
// SetActivity
|
||||
//=========================================================
|
||||
void CSerpentMan :: SetActivity ( Activity NewActivity )
|
||||
{
|
||||
int iSequence = ACTIVITY_NOT_AVAILABLE;
|
||||
void *pmodel = GET_MODEL_PTR( ENT(pev) );
|
||||
|
||||
switch ( NewActivity)
|
||||
{
|
||||
case ACT_MELEE_ATTACK1:
|
||||
|
||||
// randomly stand or crouch
|
||||
if (RANDOM_LONG(0,99) == 0)
|
||||
m_fStanding = 0;
|
||||
else
|
||||
m_fStanding = 1;
|
||||
|
||||
// a short enemy...probably a snake...
|
||||
if ((m_hEnemy != NULL) && (m_hEnemy->pev->maxs.z < 36))
|
||||
{
|
||||
m_fStanding = 0;
|
||||
}
|
||||
|
||||
if ( m_fStanding )
|
||||
{
|
||||
// get aimable sequence
|
||||
iSequence = LookupSequence( "ref_shoot_serpentstaff" );
|
||||
}
|
||||
else
|
||||
{
|
||||
// get crouching shoot
|
||||
iSequence = LookupSequence( "crouch_shoot_serpentstaff" );
|
||||
}
|
||||
break;
|
||||
default:
|
||||
iSequence = LookupActivity ( NewActivity );
|
||||
break;
|
||||
}
|
||||
|
||||
m_Activity = NewActivity; // Go ahead and set this so it doesn't keep trying when the anim is not present
|
||||
|
||||
// Set to the desired anim, or default anim if the desired is not present
|
||||
if ( iSequence > ACTIVITY_NOT_AVAILABLE )
|
||||
{
|
||||
if ( pev->sequence != iSequence || !m_fSequenceLoops )
|
||||
{
|
||||
pev->frame = 0;
|
||||
}
|
||||
|
||||
pev->sequence = iSequence; // Set to the reset anim (if it's there)
|
||||
ResetSequenceInfo( );
|
||||
SetYawSpeed();
|
||||
}
|
||||
else
|
||||
{
|
||||
// Not available try to get default anim
|
||||
ALERT ( at_console, "%s has no sequence for act:%d\n", STRING(pev->classname), NewActivity );
|
||||
pev->sequence = 0; // Set to the reset anim (if it's there)
|
||||
}
|
||||
}
|
||||
//=========================================================
|
||||
Schedule_t *CSerpentMan :: 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 ( 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)
|
||||
{
|
||||
if (!HasConditions( bits_COND_CAN_MELEE_ATTACK1 ))
|
||||
{
|
||||
m_failSchedule = SCHED_CHASE_ENEMY;
|
||||
if (HasConditions( bits_COND_LIGHT_DAMAGE | bits_COND_HEAVY_DAMAGE))
|
||||
{
|
||||
return GetScheduleOfType( SCHED_TAKE_COVER_FROM_ENEMY );
|
||||
}
|
||||
if ( HasConditions ( bits_COND_SEE_ENEMY ) && HasConditions ( bits_COND_ENEMY_FACING_ME ) )
|
||||
{
|
||||
// ALERT( at_console, "exposed\n");
|
||||
return GetScheduleOfType( SCHED_TAKE_COVER_FROM_ENEMY );
|
||||
}
|
||||
}
|
||||
}
|
||||
break;
|
||||
}
|
||||
return CSquadMonster::GetSchedule( );
|
||||
}
|
||||
|
||||
|
||||
Schedule_t *CSerpentMan :: 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 slSerpentManAttack1;
|
||||
case SCHED_RANGE_ATTACK2:
|
||||
return slSerpentManAttack1;
|
||||
}
|
||||
return CSquadMonster :: GetScheduleOfType( Type );
|
||||
}
|
||||
|
||||
|
|
@ -0,0 +1,62 @@
|
|||
|
||||
#ifndef SERPENT_MAN_H
|
||||
#define SERPENT_MAN_H
|
||||
|
||||
class CSerpentMan : public CSquadMonster
|
||||
{
|
||||
public:
|
||||
void Spawn( void );
|
||||
void Precache( void );
|
||||
void SetYawSpeed( void );
|
||||
int ISoundMask( void );
|
||||
int Classify ( void );
|
||||
int IRelationship( CBaseEntity *pTarget );
|
||||
void HandleAnimEvent( MonsterEvent_t *pEvent );
|
||||
Vector GetGunPosition( void );
|
||||
BOOL CheckMeleeAttack1 ( float flDot, float flDist );
|
||||
BOOL CheckRangeAttack1 ( float flDot, float flDist );
|
||||
BOOL CheckRangeAttack2 ( float flDot, float flDist );
|
||||
void CallForHelp( char *szClassname, float flDist, EHANDLE hEnemy, Vector &vecLocation );
|
||||
void TraceAttack( entvars_t *pevAttacker, float flDamage, Vector vecDir, TraceResult *ptr, int bitsDamageType);
|
||||
int TakeDamage( entvars_t* pevInflictor, entvars_t* pevAttacker, float flDamage, int bitsDamageType);
|
||||
|
||||
void DeathSound( void );
|
||||
void PainSound( void );
|
||||
void AlertSound( void );
|
||||
void IdleSound( void );
|
||||
void AttackSound( void );
|
||||
|
||||
void GibMonster( void );
|
||||
void Killed( entvars_t *pevAttacker, int iGib );
|
||||
|
||||
void SetActivity ( Activity NewActivity );
|
||||
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[];
|
||||
|
||||
BOOL m_fStanding;
|
||||
float m_flNextAttack;
|
||||
|
||||
int m_voicePitch;
|
||||
|
||||
int m_iNumSerpents;
|
||||
|
||||
static const char *pAttackSounds[];
|
||||
static const char *pAttackHitSounds[];
|
||||
static const char *pAttackMissSounds[];
|
||||
static const char *pPainSounds[];
|
||||
static const char *pDeathSounds[];
|
||||
};
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
#endif
|
|
@ -0,0 +1,247 @@
|
|||
/***
|
||||
*
|
||||
* Copyright (c) 1999, 2000 Valve LLC. All rights reserved.
|
||||
*
|
||||
* This product contains software technology licensed from Id
|
||||
* Software, Inc. ("Id Technology"). Id Technology (c) 1996 Id Software, Inc.
|
||||
* All Rights Reserved.
|
||||
*
|
||||
* Use, distribution, and modification of this source code and/or resulting
|
||||
* object code is restricted to non-commercial enhancements to products from
|
||||
* Valve LLC. All other use, distribution, or modification is prohibited
|
||||
* without written permission from Valve LLC.
|
||||
*
|
||||
****/
|
||||
#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"
|
||||
|
||||
#include "snake.h"
|
||||
|
||||
enum squeak_e {
|
||||
SERPENT_STAFF_DRAW = 0,
|
||||
SERPENT_STAFF_IDLE1,
|
||||
SERPENT_STAFF_IDLE2,
|
||||
SERPENT_STAFF_CAST,
|
||||
SERPENT_STAFF_HOLSTER
|
||||
};
|
||||
|
||||
|
||||
#include "SerpentStaff.h"
|
||||
|
||||
|
||||
LINK_ENTITY_TO_CLASS( weapon_serpentstaff, CSerpentStaff );
|
||||
|
||||
|
||||
void CSerpentStaff::Spawn( )
|
||||
{
|
||||
Precache( );
|
||||
m_iId = WEAPON_SERPENT_STAFF;
|
||||
SET_MODEL(ENT(pev), "models/w_serpentstaff.mdl");
|
||||
|
||||
FallInit();//get ready to fall down.
|
||||
|
||||
m_iDefaultAmmo = SERPENT_STAFF_DEFAULT_GIVE;
|
||||
|
||||
pev->sequence = 1;
|
||||
pev->animtime = gpGlobals->time;
|
||||
pev->framerate = 1.0;
|
||||
}
|
||||
|
||||
|
||||
void CSerpentStaff::Precache( void )
|
||||
{
|
||||
PRECACHE_MODEL("models/w_serpentstaff.mdl");
|
||||
PRECACHE_MODEL("models/v_serpentstaff.mdl");
|
||||
// PRECACHE_MODEL("models/p_serpentstaff.mdl");
|
||||
UTIL_PrecacheOther("monster_snake");
|
||||
}
|
||||
|
||||
|
||||
int CSerpentStaff::GetItemInfo(ItemInfo *p)
|
||||
{
|
||||
p->pszName = STRING(pev->classname);
|
||||
p->pszAmmo1 = "Serpent Staff";
|
||||
p->iMaxAmmo1 = SERPENT_STAFF_MAX_CARRY;
|
||||
p->pszAmmo2 = NULL;
|
||||
p->iMaxAmmo2 = -1;
|
||||
p->iMaxClip = WEAPON_NOCLIP;
|
||||
p->iSlot = 3;
|
||||
p->iPosition = 2;
|
||||
p->iId = m_iId = WEAPON_SERPENT_STAFF;
|
||||
p->iWeight = SERPENT_STAFF_WEIGHT;
|
||||
p->iFlags = 0;
|
||||
|
||||
return 1;
|
||||
}
|
||||
|
||||
|
||||
|
||||
BOOL CSerpentStaff::Deploy( )
|
||||
{
|
||||
m_pPlayer->m_iWeaponVolume = QUIET_GUN_VOLUME;
|
||||
|
||||
return DefaultDeploy( "models/v_serpentstaff.mdl", "", SERPENT_STAFF_DRAW, "serpent_staff" );
|
||||
}
|
||||
|
||||
|
||||
void CSerpentStaff::Holster( int skiplocal /* = 0 */ )
|
||||
{
|
||||
m_pPlayer->m_flNextAttack = gpGlobals->time + 0.5;
|
||||
|
||||
if (!m_pPlayer->m_rgAmmo[m_iPrimaryAmmoType])
|
||||
{
|
||||
m_pPlayer->pev->weapons &= ~(1<<WEAPON_SERPENT_STAFF);
|
||||
SetThink( DestroyItem );
|
||||
SetNextThink( 0.1 );
|
||||
return;
|
||||
}
|
||||
|
||||
SendWeaponAnim( SERPENT_STAFF_HOLSTER );
|
||||
EMIT_SOUND(ENT(m_pPlayer->pev), CHAN_WEAPON, "common/null.wav", 1.0, ATTN_NORM);
|
||||
}
|
||||
|
||||
int CSerpentStaff::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 CSerpentStaff::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 - this is some horrible code I have written here...
|
||||
// UTIL_TraceLine( trace_origin + gpGlobals->v_forward * 20, trace_origin + gpGlobas->v_forward * 64, dont_ignore_monsters, NULL, &tr );
|
||||
// firstly check all areas within the box to be created
|
||||
int box = 24;
|
||||
int spawndist = 64;
|
||||
BOOL b1, b2, b3, b4;
|
||||
UTIL_TraceLine( trace_origin + gpGlobals->v_forward * 20, trace_origin + gpGlobals->v_forward * spawndist + Vector(box,box,0), dont_ignore_monsters, NULL, &tr );
|
||||
b1 = (tr.fAllSolid == 0 && tr.fStartSolid == 0 && tr.flFraction > 0.99);
|
||||
UTIL_TraceLine( trace_origin + gpGlobals->v_forward * 20, trace_origin + gpGlobals->v_forward * spawndist + Vector(-box,-box,0), dont_ignore_monsters, NULL, &tr );
|
||||
b2 = (tr.fAllSolid == 0 && tr.fStartSolid == 0 && tr.flFraction > 0.99);
|
||||
UTIL_TraceLine( trace_origin + gpGlobals->v_forward * 20, trace_origin + gpGlobals->v_forward * spawndist + Vector(-box,box,0), dont_ignore_monsters, NULL, &tr );
|
||||
b3 = (tr.fAllSolid == 0 && tr.fStartSolid == 0 && tr.flFraction > 0.99);
|
||||
UTIL_TraceLine( trace_origin + gpGlobals->v_forward * 20, trace_origin + gpGlobals->v_forward * spawndist + Vector(box,-box,0), dont_ignore_monsters, NULL, &tr );
|
||||
b4 = (tr.fAllSolid == 0 && tr.fStartSolid == 0 && tr.flFraction > 0.99);
|
||||
UTIL_TraceLine( trace_origin + gpGlobals->v_forward * 20, trace_origin + gpGlobals->v_forward * spawndist, dont_ignore_monsters, NULL, &tr );
|
||||
|
||||
// if we are clear at all corners of the box the snake is going to be created in
|
||||
if (b1 && b2 && b3 && b4)
|
||||
{
|
||||
SendWeaponAnim( SERPENT_STAFF_CAST );
|
||||
|
||||
// player "shoot" animation
|
||||
m_pPlayer->SetAnimation( PLAYER_ATTACK1 );
|
||||
|
||||
Vector temp;
|
||||
temp.x = temp.y = temp.z = 0.0;
|
||||
temp.y = m_pPlayer->pev->v_angle.y;
|
||||
|
||||
//CBaseEntity *pSnake = CBaseEntity::Create( "monster_snake", tr.vecEndPos, temp, m_pPlayer->edict() );
|
||||
CBaseEntity *pSnake = CBaseEntity::Create( "monster_snake", tr.vecEndPos, temp );
|
||||
((CSnake*)pSnake)->SetOwner(m_pPlayer);
|
||||
pSnake->pev->spawnflags |= 1;
|
||||
pSnake->pev->spawnflags |= SF_MONSTER_NO_YELLOW_BLOBS;
|
||||
// drop to floor, to check if we have landed on top of an entity...
|
||||
DROP_TO_FLOOR( ENT( pSnake->pev ) );
|
||||
|
||||
CBaseEntity *pList[2];
|
||||
int buf = 24;
|
||||
// if there are any no other entities nearby (particularly under it!), then...
|
||||
int count = UTIL_EntitiesInBox( pList, 2, pSnake->pev->origin - Vector(buf,buf,buf), pSnake->pev->origin + Vector(buf,buf,buf), FL_CLIENT|FL_MONSTER );
|
||||
//...put the snake there and decrement ammo
|
||||
if ( count <= 1 )
|
||||
{
|
||||
pSnake->pev->velocity = gpGlobals->v_forward * 100 + m_pPlayer->pev->velocity;
|
||||
|
||||
m_pPlayer->m_iWeaponVolume = QUIET_GUN_VOLUME;
|
||||
|
||||
m_pPlayer->m_rgAmmo[m_iPrimaryAmmoType]--;
|
||||
}
|
||||
// otherwise we do not want this snake...as it will cause yellow blobs!
|
||||
else
|
||||
{
|
||||
UTIL_Remove(pSnake);
|
||||
}
|
||||
|
||||
m_fJustThrown = 1;
|
||||
|
||||
m_flNextPrimaryAttack = gpGlobals->time + 0.5;
|
||||
m_flTimeWeaponIdle = gpGlobals->time + 0.5;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
void CSerpentStaff::SecondaryAttack( void )
|
||||
{
|
||||
|
||||
}
|
||||
|
||||
|
||||
void CSerpentStaff::WeaponIdle( void )
|
||||
{
|
||||
if (m_flTimeWeaponIdle > gpGlobals->time)
|
||||
return;
|
||||
|
||||
if (m_fJustThrown)
|
||||
{
|
||||
m_fJustThrown = 0;
|
||||
|
||||
if ( !m_pPlayer->m_rgAmmo[PrimaryAmmoIndex()] )
|
||||
{
|
||||
RetireWeapon();
|
||||
return;
|
||||
}
|
||||
|
||||
//SendWeaponAnim( SERPENT_STAFF_DRAW );
|
||||
m_flTimeWeaponIdle = gpGlobals->time + RANDOM_FLOAT ( 3,5 );
|
||||
return;
|
||||
}
|
||||
|
||||
int iAnim;
|
||||
|
||||
float flRand = RANDOM_FLOAT(0, 1);
|
||||
if (flRand <= 0.75)
|
||||
{
|
||||
iAnim = SERPENT_STAFF_IDLE1;
|
||||
m_flTimeWeaponIdle = gpGlobals->time + 30.0 / 16 * (2);
|
||||
}
|
||||
else
|
||||
{
|
||||
iAnim = SERPENT_STAFF_IDLE2;
|
||||
m_flTimeWeaponIdle = gpGlobals->time + 70.0 / 16.0;
|
||||
}
|
||||
|
||||
SendWeaponAnim( iAnim );
|
||||
}
|
||||
|
||||
#endif
|
|
@ -0,0 +1,35 @@
|
|||
|
||||
#ifndef SERPENT_STAFF_H
|
||||
#define SERPENT_STAFF_H
|
||||
|
||||
class CSerpentStaff : public CBasePlayerWeapon
|
||||
{
|
||||
public:
|
||||
void Spawn( void );
|
||||
void Precache( void );
|
||||
int iItemSlot( void ) { return 3; }
|
||||
int GetItemInfo(ItemInfo *p);
|
||||
int AddToPlayer( CBasePlayer *pPlayer );
|
||||
|
||||
void PrimaryAttack( void );
|
||||
void SecondaryAttack( void );
|
||||
BOOL Deploy( void );
|
||||
void Holster( int skiplocal = 0 );
|
||||
void WeaponIdle( void );
|
||||
int m_fJustThrown;
|
||||
|
||||
virtual BOOL UseDecrement( void )
|
||||
{
|
||||
#if defined( CLIENT_WEAPONS )
|
||||
return TRUE;
|
||||
#else
|
||||
return FALSE;
|
||||
#endif
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
|
||||
|
||||
#endif
|
||||
|
|
@ -0,0 +1,63 @@
|
|||
|
||||
#ifndef SHRIVELLING_H
|
||||
#define SHRIVELLING_H
|
||||
|
||||
class CShrivelling : public CBasePlayerWeapon
|
||||
{
|
||||
public:
|
||||
int Save( CSave &save );
|
||||
int Restore( CRestore &restore );
|
||||
static TYPEDESCRIPTION m_SaveData[];
|
||||
|
||||
void Spawn( void );
|
||||
void Precache( void );
|
||||
int iItemSlot( void ) { return 4; }
|
||||
int GetItemInfo(ItemInfo *p);
|
||||
int AddToPlayer( CBasePlayer *pPlayer );
|
||||
|
||||
BOOL Deploy( void );
|
||||
void Holster( int skiplocal = 0 );
|
||||
|
||||
void CreateEffect( void );
|
||||
void UpdateEffect( const Vector &startPoint, const Vector &endPoint, float timeBlend );
|
||||
void DestroyEffect( void );
|
||||
|
||||
void EndAttack( void );
|
||||
void Attack( void );
|
||||
void PrimaryAttack( void );
|
||||
void WeaponIdle( void );
|
||||
static int g_fireAnims1[];
|
||||
static int g_fireAnims2[];
|
||||
|
||||
float m_flAmmoUseTime;// since we use < 1 point of ammo per update, we subtract ammo on a timer.
|
||||
|
||||
float GetPulseInterval( void );
|
||||
float GetDischargeInterval( void );
|
||||
|
||||
void Fire( const Vector &vecOrigSrc, const Vector &vecDir );
|
||||
|
||||
enum SHRIVELLING_FIRESTATE { FIRE_OFF, FIRE_CHARGE };
|
||||
enum SHRIVELLING_FIREMODE { FIRE_NARROW, FIRE_WIDE};
|
||||
|
||||
virtual BOOL UseDecrement( void )
|
||||
{
|
||||
#if defined( CLIENT_WEAPONS )
|
||||
return TRUE;
|
||||
#else
|
||||
return FALSE;
|
||||
#endif
|
||||
}
|
||||
|
||||
private:
|
||||
float m_shootTime;
|
||||
CBeam *m_pBeam;
|
||||
CBeam *m_pNoise;
|
||||
CSprite *m_pSprite;
|
||||
SHRIVELLING_FIRESTATE m_fireState;
|
||||
SHRIVELLING_FIREMODE m_fireMode;
|
||||
float m_shakeTime;
|
||||
BOOL m_deployed;
|
||||
};
|
||||
|
||||
#endif
|
||||
|
File diff suppressed because it is too large
Load Diff
|
@ -0,0 +1,249 @@
|
|||
/***
|
||||
*
|
||||
* Copyright (c) 1999, 2000 Valve LLC. All rights reserved.
|
||||
*
|
||||
* This product contains software technology licensed from Id
|
||||
* Software, Inc. ("Id Technology"). Id Technology (c) 1996 Id Software, Inc.
|
||||
* All Rights Reserved.
|
||||
*
|
||||
* This source code contains proprietary and confidential information of
|
||||
* Valve LLC and its suppliers. Access to this code is restricted to
|
||||
* persons who have executed a written SDK license with Valve. Any access,
|
||||
* use or distribution of this code by or to any unlicensed person is illegal.
|
||||
*
|
||||
****/
|
||||
//=========================================================
|
||||
// Snake
|
||||
//=========================================================
|
||||
|
||||
// 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 SNAKE_AE_ATTACK 0x01
|
||||
|
||||
#define SNAKE_FLINCH_DELAY 2 // at most one flinch every n secs
|
||||
|
||||
|
||||
#include "Snake.h"
|
||||
|
||||
|
||||
LINK_ENTITY_TO_CLASS( monster_snake, CSnake );
|
||||
|
||||
TYPEDESCRIPTION CSnake::m_SaveData[] =
|
||||
{
|
||||
DEFINE_FIELD( CSnake, m_iszGibModel, FIELD_STRING ),
|
||||
};
|
||||
|
||||
IMPLEMENT_SAVERESTORE( CSnake, CBaseMonster );
|
||||
|
||||
const char *CSnake::pAttackSounds[] =
|
||||
{
|
||||
"snake/snake_attack1.wav",
|
||||
"snake/snake_attack2.wav",
|
||||
};
|
||||
|
||||
const char *CSnake::pIdleSounds[] =
|
||||
{
|
||||
"snake/snake_idle1.wav",
|
||||
"snake/snake_idle2.wav",
|
||||
};
|
||||
|
||||
const char *CSnake::pPainSounds[] =
|
||||
{
|
||||
"snake/snake_pain1.wav",
|
||||
"snake/snake_pain2.wav",
|
||||
};
|
||||
|
||||
//=========================================================
|
||||
// Classify - indicates this monster's place in the
|
||||
// relationship table.
|
||||
//=========================================================
|
||||
int CSnake :: Classify ( void )
|
||||
{
|
||||
// snakes are servants of great old ones...so treat them like aliens....if they are not "owned" by anyone else...
|
||||
if (mpOwner == NULL)
|
||||
return m_iClass?m_iClass:CLASS_ALIEN_MONSTER;
|
||||
|
||||
return mpOwner->Classify();
|
||||
}
|
||||
|
||||
int CSnake::IRelationship( CBaseEntity *pTarget )
|
||||
{
|
||||
return CBaseMonster::IRelationship( pTarget );
|
||||
}
|
||||
|
||||
//=========================================================
|
||||
// SetYawSpeed - allows each sequence to have a different
|
||||
// turn rate associated with it.
|
||||
//=========================================================
|
||||
void CSnake :: SetYawSpeed ( void )
|
||||
{
|
||||
int ys;
|
||||
|
||||
ys = 120;
|
||||
|
||||
pev->yaw_speed = ys;
|
||||
}
|
||||
|
||||
int CSnake :: TakeDamage( entvars_t *pevInflictor, entvars_t *pevAttacker, float flDamage, int bitsDamageType )
|
||||
{
|
||||
// HACK HACK -- until we fix this.
|
||||
if ( IsAlive() )
|
||||
PainSound();
|
||||
return CBaseMonster::TakeDamage( pevInflictor, pevAttacker, flDamage, bitsDamageType );
|
||||
}
|
||||
|
||||
void CSnake :: 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 CSnake :: IdleSound( void )
|
||||
{
|
||||
int pitch = 95 + RANDOM_LONG(0,9);
|
||||
|
||||
// Play a random idle sound
|
||||
EMIT_SOUND_DYN ( ENT(pev), CHAN_VOICE, pIdleSounds[ RANDOM_LONG(0,ARRAYSIZE(pIdleSounds)-1) ], 1.0, ATTN_NORM, 0, 100 + RANDOM_LONG(-5,5) );
|
||||
}
|
||||
|
||||
void CSnake :: AttackSound( void )
|
||||
{
|
||||
// Play a random attack sound
|
||||
EMIT_SOUND_DYN ( ENT(pev), CHAN_VOICE, pAttackSounds[ RANDOM_LONG(0,ARRAYSIZE(pAttackSounds)-1) ], 1.0, ATTN_NORM, 0, 100 + RANDOM_LONG(-5,5) );
|
||||
}
|
||||
|
||||
|
||||
//=========================================================
|
||||
// HandleAnimEvent - catches the monster-specific messages
|
||||
// that occur when tagged animation frames are played.
|
||||
//=========================================================
|
||||
void CSnake :: HandleAnimEvent( MonsterEvent_t *pEvent )
|
||||
{
|
||||
switch( pEvent->event )
|
||||
{
|
||||
case SNAKE_AE_ATTACK:
|
||||
{
|
||||
pev->size.z += 48;
|
||||
CBaseEntity *pHurt = CheckTraceHullAttack( 70, gSkillData.snakeDmgBite, DMG_POISON );
|
||||
pev->size.z -= 48;
|
||||
|
||||
if (RANDOM_LONG(0,1))
|
||||
AttackSound();
|
||||
|
||||
// can only bite once per second
|
||||
m_fNextBiteTime = gpGlobals->time + 1.0;
|
||||
}
|
||||
break;
|
||||
|
||||
default:
|
||||
CBaseMonster::HandleAnimEvent( pEvent );
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
//=========================================================
|
||||
// Spawn
|
||||
//=========================================================
|
||||
void CSnake :: Spawn()
|
||||
{
|
||||
Precache( );
|
||||
|
||||
if (pev->model)
|
||||
SET_MODEL(ENT(pev), STRING(pev->model)); //LRC
|
||||
else
|
||||
SET_MODEL(ENT(pev), "models/snake.mdl");
|
||||
UTIL_SetSize( pev, Vector(-16,-16,0), Vector(16,16,20) );
|
||||
|
||||
pev->solid = SOLID_SLIDEBOX;
|
||||
pev->movetype = MOVETYPE_STEP;
|
||||
m_bloodColor = BLOOD_COLOR_RED;
|
||||
if (pev->health == 0)
|
||||
pev->health = gSkillData.snakeHealth;
|
||||
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_MELEE_ATTACK1 | bits_CAP_SWIM;
|
||||
m_fNextBiteTime = 0.0;
|
||||
mpOwner = NULL;
|
||||
|
||||
MonsterInit();
|
||||
}
|
||||
|
||||
void CSnake :: SetOwner ( CBaseEntity* pOwner )
|
||||
{
|
||||
mpOwner = pOwner;
|
||||
}
|
||||
|
||||
//=========================================================
|
||||
// Precache - precaches all resources this monster needs
|
||||
//=========================================================
|
||||
void CSnake :: Precache()
|
||||
{
|
||||
int i;
|
||||
|
||||
if (pev->model)
|
||||
PRECACHE_MODEL((char*)STRING(pev->model)); //LRC
|
||||
else
|
||||
PRECACHE_MODEL("models/snake.mdl");
|
||||
m_iszGibModel = ALLOC_STRING("models/snakegibs.mdl");
|
||||
PRECACHE_MODEL( "models/snakegibs.mdl" ); //LRC
|
||||
|
||||
for ( i = 0; i < ARRAYSIZE( pAttackSounds ); i++ )
|
||||
PRECACHE_SOUND((char *)pAttackSounds[i]);
|
||||
|
||||
for ( i = 0; i < ARRAYSIZE( pIdleSounds ); i++ )
|
||||
PRECACHE_SOUND((char *)pIdleSounds[i]);
|
||||
|
||||
for ( i = 0; i < ARRAYSIZE( pPainSounds ); i++ )
|
||||
PRECACHE_SOUND((char *)pPainSounds[i]);
|
||||
}
|
||||
|
||||
//=========================================================
|
||||
// AI Schedules Specific to this monster
|
||||
//=========================================================
|
||||
|
||||
BOOL CSnake::CheckMeleeAttack1 ( float flDot, float flDist )
|
||||
{
|
||||
// are we in range and other conditions, plus have we waited long enough
|
||||
// since the last bite
|
||||
return (CBaseMonster::CheckMeleeAttack1(flDot,flDist) && (m_fNextBiteTime <= gpGlobals->time));
|
||||
}
|
||||
|
||||
BOOL CSnake::CheckMeleeAttack2 ( float flDot, float flDist )
|
||||
{
|
||||
// are we in range and other conditions, plus have we waited long enough
|
||||
// since the last bite
|
||||
return (CBaseMonster::CheckMeleeAttack2(flDot,flDist) && (m_fNextBiteTime <= gpGlobals->time));
|
||||
}
|
||||
|
||||
int CSnake::IgnoreConditions ( void )
|
||||
{
|
||||
int iIgnore = CBaseMonster::IgnoreConditions();
|
||||
|
||||
if ((m_Activity == ACT_MELEE_ATTACK1) || (m_Activity == ACT_MELEE_ATTACK2))
|
||||
{
|
||||
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 + SNAKE_FLINCH_DELAY;
|
||||
}
|
||||
|
||||
return iIgnore;
|
||||
|
||||
}
|
|
@ -0,0 +1,49 @@
|
|||
|
||||
#ifndef SNAKE_H
|
||||
#define SNAKE_H
|
||||
|
||||
class CSnake : public CBaseMonster
|
||||
{
|
||||
public:
|
||||
void Spawn( void );
|
||||
void Precache( void );
|
||||
void SetYawSpeed( void );
|
||||
int Classify ( void );
|
||||
void HandleAnimEvent( MonsterEvent_t *pEvent );
|
||||
int IgnoreConditions ( void );
|
||||
int IRelationship( CBaseEntity *pTarget );
|
||||
void SetOwner( CBaseEntity* pOwner );
|
||||
|
||||
float m_flNextFlinch;
|
||||
|
||||
void AttackSound( void );
|
||||
void IdleSound( void );
|
||||
void PainSound( void );
|
||||
|
||||
static const char *pAttackSounds[];
|
||||
static const char *pIdleSounds[];
|
||||
static const char *pPainSounds[];
|
||||
|
||||
// No range attacks
|
||||
BOOL CheckRangeAttack1 ( float flDot, float flDist ) { return FALSE; }
|
||||
BOOL CheckRangeAttack2 ( float flDot, float flDist ) { return FALSE; }
|
||||
BOOL CheckMeleeAttack1 ( float flDot, float flDist );
|
||||
BOOL CheckMeleeAttack2 ( float flDot, float flDist );
|
||||
int TakeDamage( entvars_t *pevInflictor, entvars_t *pevAttacker, float flDamage, int bitsDamageType );
|
||||
|
||||
virtual int HasCustomGibs( void ) { return m_iszGibModel; }
|
||||
|
||||
int m_iszGibModel;
|
||||
|
||||
virtual int Save( CSave &save );
|
||||
virtual int Restore( CRestore &restore );
|
||||
static TYPEDESCRIPTION m_SaveData[];
|
||||
|
||||
protected:
|
||||
|
||||
float m_fNextBiteTime;
|
||||
CBaseEntity* mpOwner;
|
||||
};
|
||||
|
||||
|
||||
#endif
|
|
@ -0,0 +1,105 @@
|
|||
|
||||
#include "extdll.h"
|
||||
#include "util.h"
|
||||
#include "cbase.h"
|
||||
#include "nodes.h"
|
||||
#include "monsters.h"
|
||||
#include "schedule.h"
|
||||
#include "customentity.h"
|
||||
#include "weapons.h"
|
||||
#include "effects.h"
|
||||
#include "soundent.h"
|
||||
#include "decals.h"
|
||||
#include "explode.h"
|
||||
#include "func_break.h"
|
||||
#include "scripted.h"
|
||||
|
||||
#include "spiral.h"
|
||||
|
||||
void SpiralStreakSplash( const Vector &origin, const Vector &direction, int color, int count, int speed, int velocityRange )
|
||||
{
|
||||
MESSAGE_BEGIN( MSG_PVS, SVC_TEMPENTITY, origin );
|
||||
WRITE_BYTE( TE_STREAK_SPLASH );
|
||||
WRITE_COORD( origin.x ); // origin
|
||||
WRITE_COORD( origin.y );
|
||||
WRITE_COORD( origin.z );
|
||||
WRITE_COORD( direction.x ); // direction
|
||||
WRITE_COORD( direction.y );
|
||||
WRITE_COORD( direction.z );
|
||||
WRITE_BYTE( color ); // Streak color 6
|
||||
WRITE_SHORT( count ); // count
|
||||
WRITE_SHORT( speed );
|
||||
WRITE_SHORT( velocityRange ); // Random velocity modifier
|
||||
MESSAGE_END();
|
||||
}
|
||||
|
||||
|
||||
|
||||
LINK_ENTITY_TO_CLASS( streak_spiral, CSpiral );
|
||||
|
||||
void CSpiral::Spawn( void )
|
||||
{
|
||||
pev->movetype = MOVETYPE_NONE;
|
||||
SetNextThink( 0 );
|
||||
pev->solid = SOLID_NOT;
|
||||
UTIL_SetSize(pev, g_vecZero, g_vecZero );
|
||||
pev->effects |= EF_NODRAW;
|
||||
pev->angles = g_vecZero;
|
||||
}
|
||||
|
||||
|
||||
CSpiral *CSpiral::Create( const Vector &origin, float height, float radius, float duration )
|
||||
{
|
||||
if ( duration <= 0 )
|
||||
return NULL;
|
||||
|
||||
CSpiral *pSpiral = GetClassPtr( (CSpiral *)NULL );
|
||||
pSpiral->Spawn();
|
||||
pSpiral->pev->dmgtime = pSpiral->m_fNextThink;
|
||||
pSpiral->pev->origin = origin;
|
||||
pSpiral->pev->scale = radius;
|
||||
pSpiral->pev->dmg = height;
|
||||
pSpiral->pev->speed = duration;
|
||||
pSpiral->pev->health = 0;
|
||||
pSpiral->pev->angles = g_vecZero;
|
||||
|
||||
return pSpiral;
|
||||
}
|
||||
|
||||
#define SPIRAL_INTERVAL 0.1 //025
|
||||
|
||||
void CSpiral::Think( void )
|
||||
{
|
||||
float time = gpGlobals->time - pev->dmgtime;
|
||||
|
||||
while ( time > SPIRAL_INTERVAL )
|
||||
{
|
||||
Vector position = pev->origin;
|
||||
Vector direction = Vector(0,0,1);
|
||||
|
||||
float fraction = 1.0 / pev->speed;
|
||||
|
||||
float radius = (pev->scale * pev->health) * fraction;
|
||||
|
||||
position.z += (pev->health * pev->dmg) * fraction;
|
||||
pev->angles.y = (pev->health * 360 * 8) * fraction;
|
||||
UTIL_MakeVectors( pev->angles );
|
||||
position = position + gpGlobals->v_forward * radius;
|
||||
direction = (direction + gpGlobals->v_forward).Normalize();
|
||||
|
||||
SpiralStreakSplash( position, Vector(0,0,1), RANDOM_LONG(8,11), 20, RANDOM_LONG(50,150), 400 );
|
||||
|
||||
// Jeez, how many counters should this take ? :)
|
||||
pev->dmgtime += SPIRAL_INTERVAL;
|
||||
pev->health += SPIRAL_INTERVAL;
|
||||
time -= SPIRAL_INTERVAL;
|
||||
}
|
||||
|
||||
SetNextThink( 0 );
|
||||
|
||||
if ( pev->health >= pev->speed )
|
||||
UTIL_Remove( this );
|
||||
}
|
||||
|
||||
|
||||
|
|
@ -0,0 +1,16 @@
|
|||
|
||||
#ifndef SPIRAL_H
|
||||
#define SPIRAL_H
|
||||
|
||||
// Spiral Effect
|
||||
class CSpiral : public CBaseEntity
|
||||
{
|
||||
public:
|
||||
void Spawn( void );
|
||||
void Think( void );
|
||||
int ObjectCaps( void ) { return FCAP_DONT_SAVE; }
|
||||
static CSpiral *Create( const Vector &origin, float height, float radius, float duration );
|
||||
};
|
||||
|
||||
#endif
|
||||
|
File diff suppressed because it is too large
Load Diff
|
@ -0,0 +1,133 @@
|
|||
|
||||
#ifndef STUKABAT_H
|
||||
#define STUKABAT_H
|
||||
|
||||
#define STUKABAT_IDLE 0
|
||||
#define STUKABAT_BORED 1
|
||||
#define STUKABAT_SCARED_BY_ENT 2
|
||||
#define STUKABAT_SMELL_FOOD 3
|
||||
#define STUKABAT_EAT 4
|
||||
#define STUKABAT_DEAD 5
|
||||
|
||||
//=========================================================
|
||||
// Nodes at which Stukabats may land.
|
||||
//=========================================================
|
||||
class CStukabatNode : public CBaseEntity
|
||||
{
|
||||
public:
|
||||
void Spawn( void );
|
||||
void KeyValue( KeyValueData *pkvd );
|
||||
|
||||
bool mbInUse;
|
||||
|
||||
int Save( CSave &save );
|
||||
int Restore( CRestore &restore );
|
||||
static TYPEDESCRIPTION m_SaveData[];
|
||||
};
|
||||
|
||||
//=========================================================
|
||||
// The Stukabat monster.
|
||||
//=========================================================
|
||||
class CStukabat : public CFlyingMonster
|
||||
{
|
||||
public:
|
||||
enum SB_Medium
|
||||
{
|
||||
SB_ONGROUND,
|
||||
SB_INAIR,
|
||||
SB_ONCEILING
|
||||
};
|
||||
|
||||
void Spawn( void );
|
||||
void Precache( void );
|
||||
void SetYawSpeed( void );
|
||||
int Classify( void );
|
||||
void HandleAnimEvent( MonsterEvent_t *pEvent );
|
||||
CUSTOM_SCHEDULES;
|
||||
|
||||
int Save( CSave &save );
|
||||
int Restore( CRestore &restore );
|
||||
static TYPEDESCRIPTION m_SaveData[];
|
||||
|
||||
virtual void SetActivity ( Activity NewActivity );
|
||||
|
||||
Schedule_t *GetSchedule( void );
|
||||
Schedule_t *GetScheduleOfType ( int Type );
|
||||
|
||||
void Killed( entvars_t *pevAttacker, int iGib );
|
||||
void BecomeDead( void );
|
||||
|
||||
void EXPORT CombatUse( CBaseEntity *pActivator, CBaseEntity *pCaller, USE_TYPE useType, float value );
|
||||
//void EXPORT SlashTouch( CBaseEntity *pOther );
|
||||
void Slash(void);
|
||||
|
||||
void StartTask( Task_t *pTask );
|
||||
void RunTask( Task_t *pTask );
|
||||
|
||||
BOOL CheckMeleeAttack1 ( float flDot, float flDist );
|
||||
BOOL CheckRangeAttack1 ( float flDot, float flDist );
|
||||
BOOL CheckRangeAttack2 ( float flDot, float flDist );
|
||||
|
||||
float ChangeYaw( int speed );
|
||||
Activity GetStoppedActivity( void );
|
||||
|
||||
virtual int CheckLocalMove ( const Vector &vecStart, const Vector &vecEnd, CBaseEntity *pTarget, float *pflDist );// check validity of a straight move through space
|
||||
virtual void Move( float flInterval );
|
||||
virtual void MoveExecute( CBaseEntity *pTargetEnt, const Vector &vecDir, float flInterval );
|
||||
void MonsterThink( void );
|
||||
void MonsterThinkInAir( void );
|
||||
void MonsterThinkOnGround( void );
|
||||
void MonsterThinkOnCeiling( void );
|
||||
void Stop( void );
|
||||
void Fly( void );
|
||||
Vector DoProbe(const Vector &Probe);
|
||||
|
||||
void LandGround (void);
|
||||
void LandCeiling (void);
|
||||
void TakeOffGround (void);
|
||||
void FindNode (void);
|
||||
|
||||
float VectorToPitch( const Vector &vec);
|
||||
float FlPitchDiff( void );
|
||||
float ChangePitch( int speed );
|
||||
|
||||
Vector m_SaveVelocity;
|
||||
float m_idealDist;
|
||||
|
||||
float m_flNextRangedAttack;
|
||||
float m_flNextMeleeAttack;
|
||||
//BOOL m_bOnAttack;
|
||||
|
||||
float m_flMaxSpeed;
|
||||
float m_flMinSpeed;
|
||||
float m_flMaxDist;
|
||||
|
||||
float m_flNextAlert;
|
||||
float m_flLandTime;
|
||||
|
||||
void PickNewDest ( int iCondition );
|
||||
|
||||
SB_Medium meMedium;
|
||||
int m_iMode;
|
||||
|
||||
CStukabatNode* mpCeilingNode;
|
||||
|
||||
static const char *pIdleSounds[];
|
||||
static const char *pAlertSounds[];
|
||||
static const char *pAttackSounds[];
|
||||
static const char *pSlashSounds[];
|
||||
static const char *pDieSounds[];
|
||||
static const char *pPainSounds[];
|
||||
|
||||
void IdleSound( void );
|
||||
void AlertSound( void );
|
||||
void AttackSound( void );
|
||||
void SlashSound( void );
|
||||
void DeathSound( void );
|
||||
void PainSound( void );
|
||||
};
|
||||
|
||||
|
||||
#endif
|
||||
|
||||
|
|
@ -0,0 +1,336 @@
|
|||
/***
|
||||
*
|
||||
* Copyright (c) 1999, 2000 Valve LLC. All rights reserved.
|
||||
*
|
||||
* This product contains software technology licensed from Id
|
||||
* Software, Inc. ("Id Technology"). Id Technology (c) 1996 Id Software, Inc.
|
||||
* All Rights Reserved.
|
||||
*
|
||||
* Use, distribution, and modification of this source code and/or resulting
|
||||
* object code is restricted to non-commercial enhancements to products from
|
||||
* Valve LLC. All other use, distribution, or modification is prohibited
|
||||
* without written permission from Valve LLC.
|
||||
*
|
||||
****/
|
||||
|
||||
#include "extdll.h"
|
||||
#include "util.h"
|
||||
#include "cbase.h"
|
||||
#include "monsters.h"
|
||||
#include "weapons.h"
|
||||
#include "nodes.h"
|
||||
#include "player.h"
|
||||
#include "gamerules.h"
|
||||
|
||||
|
||||
#define SWORDCANE_BODYHIT_VOLUME 128
|
||||
#define SWORDCANE_WALLHIT_VOLUME 512
|
||||
|
||||
#include "swordcane.h"
|
||||
|
||||
LINK_ENTITY_TO_CLASS( weapon_swordcane, CSwordcane );
|
||||
|
||||
|
||||
|
||||
enum swordcane_e {
|
||||
SWORDCANE_IDLE = 0,
|
||||
SWORDCANE_DRAW,
|
||||
SWORDCANE_HOLSTER,
|
||||
SWORDCANE_ATTACK1,
|
||||
SWORDCANE_ATTACK2,
|
||||
};
|
||||
|
||||
|
||||
void CSwordcane::Spawn( )
|
||||
{
|
||||
Precache( );
|
||||
m_iId = WEAPON_SWORDCANE;
|
||||
SET_MODEL(ENT(pev), "models/w_swordcane.mdl");
|
||||
m_iClip = -1;
|
||||
|
||||
FallInit();// get ready to fall down.
|
||||
}
|
||||
|
||||
|
||||
void CSwordcane::Precache( void )
|
||||
{
|
||||
PRECACHE_MODEL("models/v_swordcane.mdl");
|
||||
PRECACHE_MODEL("models/w_swordcane.mdl");
|
||||
// PRECACHE_MODEL("models/p_swordcane.mdl");
|
||||
|
||||
PRECACHE_SOUND("weapons/cbar_hit1.wav");
|
||||
PRECACHE_SOUND("weapons/cbar_hit2.wav");
|
||||
PRECACHE_SOUND("weapons/cbar_hitbod1.wav");
|
||||
PRECACHE_SOUND("weapons/cbar_hitbod2.wav");
|
||||
PRECACHE_SOUND("weapons/cbar_hitbod3.wav");
|
||||
PRECACHE_SOUND("weapons/cbar_miss1.wav");
|
||||
}
|
||||
|
||||
int CSwordcane::GetItemInfo(ItemInfo *p)
|
||||
{
|
||||
p->pszName = STRING(pev->classname);
|
||||
p->pszAmmo1 = NULL;
|
||||
p->iMaxAmmo1 = -1;
|
||||
p->pszAmmo2 = NULL;
|
||||
p->iMaxAmmo2 = -1;
|
||||
p->iMaxClip = WEAPON_NOCLIP;
|
||||
p->iSlot = 0;
|
||||
p->iPosition = 1;
|
||||
p->iId = WEAPON_SWORDCANE;
|
||||
p->iWeight = SWORDCANE_WEIGHT;
|
||||
return 1;
|
||||
}
|
||||
|
||||
|
||||
|
||||
BOOL CSwordcane::Deploy( )
|
||||
{
|
||||
return DefaultDeploy( "models/v_swordcane.mdl", "", SWORDCANE_DRAW, "swordcane" );
|
||||
}
|
||||
|
||||
void CSwordcane::Holster( int skiplocal /* = 0 */ )
|
||||
{
|
||||
m_pPlayer->m_flNextAttack = UTIL_WeaponTimeBase() + 0.5;
|
||||
SendWeaponAnim( SWORDCANE_HOLSTER );
|
||||
}
|
||||
|
||||
int CSwordcane::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 CSwordcane::FindHullIntersection( const Vector &vecSrc, TraceResult &tr, float *mins, float *maxs, edict_t *pEntity )
|
||||
{
|
||||
int i, j, k;
|
||||
float distance;
|
||||
float *minmaxs[2] = {mins, maxs};
|
||||
TraceResult tmpTrace;
|
||||
Vector vecHullEnd = tr.vecEndPos;
|
||||
Vector vecEnd;
|
||||
|
||||
distance = 1e6f;
|
||||
|
||||
vecHullEnd = vecSrc + ((vecHullEnd - vecSrc)*2);
|
||||
UTIL_TraceLine( vecSrc, vecHullEnd, dont_ignore_monsters, pEntity, &tmpTrace );
|
||||
if ( tmpTrace.flFraction < 1.0 )
|
||||
{
|
||||
tr = tmpTrace;
|
||||
return;
|
||||
}
|
||||
|
||||
for ( i = 0; i < 2; i++ )
|
||||
{
|
||||
for ( j = 0; j < 2; j++ )
|
||||
{
|
||||
for ( k = 0; k < 2; k++ )
|
||||
{
|
||||
vecEnd.x = vecHullEnd.x + minmaxs[i][0];
|
||||
vecEnd.y = vecHullEnd.y + minmaxs[j][1];
|
||||
vecEnd.z = vecHullEnd.z + minmaxs[k][2];
|
||||
|
||||
UTIL_TraceLine( vecSrc, vecEnd, dont_ignore_monsters, pEntity, &tmpTrace );
|
||||
if ( tmpTrace.flFraction < 1.0 )
|
||||
{
|
||||
float thisDistance = (tmpTrace.vecEndPos - vecSrc).Length();
|
||||
if ( thisDistance < distance )
|
||||
{
|
||||
tr = tmpTrace;
|
||||
distance = thisDistance;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
void CSwordcane::PrimaryAttack()
|
||||
{
|
||||
if (! Swing( 1 ))
|
||||
{
|
||||
SetThink( SwingAgain );
|
||||
SetNextThink( 0.5 );
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
void CSwordcane::Smack( )
|
||||
{
|
||||
DecalGunshot( &m_trHit, BULLET_PLAYER_SWORDCANE );
|
||||
}
|
||||
|
||||
|
||||
void CSwordcane::SwingAgain( void )
|
||||
{
|
||||
Swing( 0 );
|
||||
DontThink();
|
||||
}
|
||||
|
||||
|
||||
int CSwordcane::Swing( int fFirst )
|
||||
{
|
||||
int fDidHit = FALSE;
|
||||
|
||||
TraceResult tr;
|
||||
|
||||
UTIL_MakeVectors (m_pPlayer->pev->v_angle);
|
||||
Vector vecSrc = m_pPlayer->GetGunPosition( );
|
||||
Vector vecEnd = vecSrc + gpGlobals->v_forward * 32;
|
||||
|
||||
UTIL_TraceLine( vecSrc, vecEnd, dont_ignore_monsters, ENT( m_pPlayer->pev ), &tr );
|
||||
|
||||
if ( tr.flFraction >= 1.0 )
|
||||
{
|
||||
UTIL_TraceHull( vecSrc, vecEnd, dont_ignore_monsters, head_hull, ENT( m_pPlayer->pev ), &tr );
|
||||
if ( tr.flFraction < 1.0 )
|
||||
{
|
||||
// Calculate the point of intersection of the line (or hull) and the object we hit
|
||||
// This is and approximation of the "best" intersection
|
||||
CBaseEntity *pHit = CBaseEntity::Instance( tr.pHit );
|
||||
if ( !pHit || pHit->IsBSPModel() )
|
||||
FindHullIntersection( vecSrc, tr, VEC_DUCK_HULL_MIN, VEC_DUCK_HULL_MAX, m_pPlayer->edict() );
|
||||
vecEnd = tr.vecEndPos; // This is the point on the actual surface (the hull could have hit space)
|
||||
}
|
||||
}
|
||||
|
||||
if ( tr.flFraction >= 1.0 )
|
||||
{
|
||||
if (fFirst)
|
||||
{
|
||||
// miss
|
||||
switch( (m_iSwing++) % 3 )
|
||||
{
|
||||
case 0:
|
||||
SendWeaponAnim( SWORDCANE_ATTACK1 ); break;
|
||||
case 1:
|
||||
SendWeaponAnim( SWORDCANE_ATTACK2 ); break;
|
||||
default:
|
||||
SendWeaponAnim( SWORDCANE_ATTACK1 ); break;
|
||||
}
|
||||
m_flNextPrimaryAttack = gpGlobals->time + 0.5;
|
||||
// play wiff or swish sound
|
||||
EMIT_SOUND_DYN(ENT(m_pPlayer->pev), CHAN_WEAPON, "weapons/cbar_miss1.wav", 1, ATTN_NORM, 0, 94 + RANDOM_LONG(0,0xF));
|
||||
|
||||
// player "shoot" animation
|
||||
m_pPlayer->SetAnimation( PLAYER_ATTACK1 );
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
// hit
|
||||
fDidHit = TRUE;
|
||||
|
||||
CBaseEntity *pEntity = CBaseEntity::Instance(tr.pHit);
|
||||
|
||||
switch( ((m_iSwing++) % 2) + 1 )
|
||||
{
|
||||
case 0:
|
||||
SendWeaponAnim( SWORDCANE_ATTACK1 ); break;
|
||||
case 1:
|
||||
SendWeaponAnim( SWORDCANE_ATTACK2 ); break;
|
||||
default:
|
||||
SendWeaponAnim( SWORDCANE_ATTACK1 ); break;
|
||||
}
|
||||
|
||||
// player "shoot" animation
|
||||
m_pPlayer->SetAnimation( PLAYER_ATTACK1 );
|
||||
|
||||
ClearMultiDamage( );
|
||||
if ( (m_flNextPrimaryAttack + 1 < gpGlobals->time) || g_pGameRules->IsMultiplayer() )
|
||||
{
|
||||
// first swing does full damage
|
||||
pEntity->TraceAttack(m_pPlayer->pev, gSkillData.plrDmgSwordCane, gpGlobals->v_forward, &tr, DMG_ENERGYBEAM );
|
||||
}
|
||||
else
|
||||
{
|
||||
// subsequent swings do half
|
||||
pEntity->TraceAttack(m_pPlayer->pev, gSkillData.plrDmgSwordCane / 2, gpGlobals->v_forward, &tr, DMG_ENERGYBEAM );
|
||||
}
|
||||
ApplyMultiDamage( m_pPlayer->pev, m_pPlayer->pev );
|
||||
|
||||
m_flNextPrimaryAttack = gpGlobals->time + 0.5;
|
||||
|
||||
// play thwack, smack, or dong sound
|
||||
float flVol = 1.0;
|
||||
int fHitWorld = TRUE;
|
||||
|
||||
if (pEntity)
|
||||
{
|
||||
if (pEntity->Classify() != CLASS_NONE && pEntity->Classify() != CLASS_MACHINE)
|
||||
{
|
||||
// play thwack or smack sound
|
||||
switch( RANDOM_LONG(0,2) )
|
||||
{
|
||||
case 0:
|
||||
EMIT_SOUND(ENT(m_pPlayer->pev), CHAN_WEAPON, "weapons/cbar_hitbod1.wav", 1, ATTN_NORM); break;
|
||||
case 1:
|
||||
EMIT_SOUND(ENT(m_pPlayer->pev), CHAN_WEAPON, "weapons/cbar_hitbod2.wav", 1, ATTN_NORM); break;
|
||||
case 2:
|
||||
EMIT_SOUND(ENT(m_pPlayer->pev), CHAN_WEAPON, "weapons/cbar_hitbod3.wav", 1, ATTN_NORM); break;
|
||||
}
|
||||
m_pPlayer->m_iWeaponVolume = SWORDCANE_BODYHIT_VOLUME;
|
||||
if (!pEntity->IsAlive() )
|
||||
return TRUE;
|
||||
else
|
||||
flVol = 0.1;
|
||||
|
||||
fHitWorld = FALSE;
|
||||
}
|
||||
}
|
||||
|
||||
// play texture hit sound
|
||||
// UNDONE: Calculate the correct point of intersection when we hit with the hull instead of the line
|
||||
|
||||
if (fHitWorld)
|
||||
{
|
||||
float fvolbar = TEXTURETYPE_PlaySound(&tr, vecSrc, vecSrc + (vecEnd-vecSrc)*2, BULLET_PLAYER_SWORDCANE);
|
||||
|
||||
if ( g_pGameRules->IsMultiplayer() )
|
||||
{
|
||||
// override the volume here, cause we don't play texture sounds in multiplayer,
|
||||
// and fvolbar is going to be 0 from the above call.
|
||||
|
||||
fvolbar = 1;
|
||||
}
|
||||
|
||||
// also play swordcane strike
|
||||
switch( RANDOM_LONG(0,1) )
|
||||
{
|
||||
case 0:
|
||||
//UTIL_EmitAmbientSound(ENT(0), ptr->vecEndPos, "weapons/swordcane_hit1.wav", fvolbar, ATTN_NORM, 0, 98 + RANDOM_LONG(0,3));
|
||||
EMIT_SOUND_DYN(ENT(m_pPlayer->pev), CHAN_ITEM, "weapons/cbar_hit1.wav", fvolbar, ATTN_NORM, 0, 98 + RANDOM_LONG(0,3));
|
||||
break;
|
||||
case 1:
|
||||
//UTIL_EmitAmbientSound(ENT(0), ptr->vecEndPos, "weapons/swordcane_hit2.wav", fvolbar, ATTN_NORM, 0, 98 + RANDOM_LONG(0,3));
|
||||
EMIT_SOUND_DYN(ENT(m_pPlayer->pev), CHAN_ITEM, "weapons/cbar_hit2.wav", fvolbar, ATTN_NORM, 0, 98 + RANDOM_LONG(0,3));
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
// delay the decal a bit
|
||||
m_trHit = tr;
|
||||
SetThink( Smack );
|
||||
SetNextThink( 0.2 );
|
||||
|
||||
m_pPlayer->m_iWeaponVolume = flVol * SWORDCANE_WALLHIT_VOLUME;
|
||||
}
|
||||
return fDidHit;
|
||||
}
|
||||
|
||||
void CSwordcane::WeaponIdle( void )
|
||||
{
|
||||
if ( m_flTimeWeaponIdle > UTIL_WeaponTimeBase() )
|
||||
return;
|
||||
|
||||
m_flTimeWeaponIdle = UTIL_WeaponTimeBase() + (30.0 / 15.0) + RANDOM_FLOAT ( 1, 3 );
|
||||
SendWeaponAnim( SWORDCANE_IDLE, 1 );
|
||||
}
|
||||
|
||||
|
|
@ -0,0 +1,37 @@
|
|||
|
||||
#ifndef SWORDCANE_H
|
||||
#define SWORDCANE_H
|
||||
|
||||
|
||||
class CSwordcane : public CBasePlayerWeapon
|
||||
{
|
||||
public:
|
||||
void Spawn( void );
|
||||
void Precache( void );
|
||||
int iItemSlot( void ) { return 0; }
|
||||
void EXPORT SwingAgain( void );
|
||||
void EXPORT Smack( void );
|
||||
int GetItemInfo(ItemInfo *p);
|
||||
int AddToPlayer( CBasePlayer *pPlayer );
|
||||
void FindHullIntersection( const Vector &vecSrc, TraceResult &tr, float *mins, float *maxs, edict_t *pEntity );
|
||||
|
||||
void PrimaryAttack( void );
|
||||
int Swing( int fFirst );
|
||||
BOOL Deploy( void );
|
||||
void Holster( int skiplocal = 0 );
|
||||
int m_iSwing;
|
||||
TraceResult m_trHit;
|
||||
void WeaponIdle( void );
|
||||
|
||||
virtual BOOL UseDecrement( void )
|
||||
{
|
||||
#if defined( CLIENT_WEAPONS )
|
||||
return TRUE;
|
||||
#else
|
||||
return FALSE;
|
||||
#endif
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
#endif
|
File diff suppressed because it is too large
Load Diff
|
@ -0,0 +1,86 @@
|
|||
|
||||
#ifndef TREX_H
|
||||
#define TREX_H
|
||||
|
||||
class CTRexStomp : public CBaseEntity
|
||||
{
|
||||
public:
|
||||
void Spawn( void );
|
||||
void Think( void );
|
||||
static CTRexStomp *StompCreate( const Vector &origin, const Vector &end, float speed );
|
||||
|
||||
private:
|
||||
// UNDONE: re-use this sprite list instead of creating new ones all the time
|
||||
// CSprite *m_pSprites[ STOMP_SPRITE_COUNT ];
|
||||
};
|
||||
|
||||
class CTRex : public CBaseMonster
|
||||
{
|
||||
public:
|
||||
void Spawn( void );
|
||||
void Precache( void );
|
||||
void SetYawSpeed( void );
|
||||
int Classify ( void );
|
||||
int TakeDamage( entvars_t *pevInflictor, entvars_t *pevAttacker, float flDamage, int bitsDamageType );
|
||||
void Killed( entvars_t *pevAttacker, int iGib );
|
||||
void TraceAttack( entvars_t *pevAttacker, float flDamage, Vector vecDir, TraceResult *ptr, int bitsDamageType );
|
||||
void HandleAnimEvent( MonsterEvent_t *pEvent );
|
||||
|
||||
BOOL CheckMeleeAttack1( float flDot, float flDist ); // Swipe
|
||||
BOOL CheckMeleeAttack2( float flDot, float flDist ); // Flames
|
||||
BOOL CheckRangeAttack1( float flDot, float flDist ); // Stomp attack
|
||||
void SetObjectCollisionBox( void )
|
||||
{
|
||||
pev->absmin = pev->origin + Vector( -80, -80, 0 );
|
||||
pev->absmax = pev->origin + Vector( 80, 80, 214 );
|
||||
}
|
||||
|
||||
Schedule_t *GetScheduleOfType( int Type );
|
||||
void StartTask( Task_t *pTask );
|
||||
void RunTask( Task_t *pTask );
|
||||
|
||||
void Leap( void );
|
||||
void StompAttack( void );
|
||||
void FlameCreate( void );
|
||||
void FlameUpdate( void );
|
||||
void FlameControls( float angleX, float angleY );
|
||||
void FlameDestroy( void );
|
||||
inline BOOL FlameIsOn( void ) { return m_pFlame[0] != NULL; }
|
||||
|
||||
void FlameDamage( Vector vecStart, Vector vecEnd, entvars_t *pevInflictor, entvars_t *pevAttacker, float flDamage, int iClassIgnore, int bitsDamageType );
|
||||
|
||||
virtual int Save( CSave &save );
|
||||
virtual int Restore( CRestore &restore );
|
||||
static TYPEDESCRIPTION m_SaveData[];
|
||||
|
||||
CUSTOM_SCHEDULES;
|
||||
|
||||
private:
|
||||
static const char *pAttackHitSounds[];
|
||||
static const char *pBeamAttackSounds[];
|
||||
static const char *pAttackMissSounds[];
|
||||
static const char *pRicSounds[];
|
||||
static const char *pFootSounds[];
|
||||
static const char *pIdleSounds[];
|
||||
static const char *pAlertSounds[];
|
||||
static const char *pPainSounds[];
|
||||
static const char *pAttackSounds[];
|
||||
static const char *pStompSounds[];
|
||||
static const char *pBreatheSounds[];
|
||||
static const char *pRoarSounds[];
|
||||
|
||||
CBaseEntity* TRexCheckTraceHullAttack(float flDist, int iDamage, int iDmgType);
|
||||
|
||||
CBeam *m_pFlame[2]; // Flame beams
|
||||
|
||||
float m_seeTime; // Time to attack (when I see the enemy, I set this)
|
||||
float m_flameTime; // Time of next flame attack
|
||||
float m_painSoundTime; // Time of next pain sound
|
||||
float m_streakTime; // streak timer (don't send too many)
|
||||
float m_flameX; // Flame thrower aim
|
||||
float m_flameY;
|
||||
};
|
||||
|
||||
|
||||
#endif
|
||||
|
|
@ -0,0 +1,219 @@
|
|||
/***
|
||||
*
|
||||
* Copyright (c) 1999, 2000 Valve LLC. All rights reserved.
|
||||
*
|
||||
* This product contains software technology licensed from Id
|
||||
* Software, Inc. ("Id Technology"). Id Technology (c) 1996 Id Software, Inc.
|
||||
* All Rights Reserved.
|
||||
*
|
||||
* Use, distribution, and modification of this source code and/or resulting
|
||||
* object code is restricted to non-commercial enhancements to products from
|
||||
* Valve LLC. All other use, distribution, or modification is prohibited
|
||||
* without written permission from Valve LLC.
|
||||
*
|
||||
****/
|
||||
|
||||
#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 tommygun_e
|
||||
{
|
||||
TOMMYGUN_IDLE = 0,
|
||||
TOMMYGUN_RELOAD,
|
||||
TOMMYGUN_DRAW,
|
||||
TOMMYGUN_FIRE1,
|
||||
TOMMYGUN_FIRE2,
|
||||
TOMMYGUN_EMPTY_IDLE,
|
||||
};
|
||||
|
||||
#include "TommyGun.h"
|
||||
|
||||
LINK_ENTITY_TO_CLASS( weapon_tommygun, CTommyGun );
|
||||
|
||||
|
||||
//=========================================================
|
||||
//=========================================================
|
||||
|
||||
void CTommyGun::Spawn( )
|
||||
{
|
||||
pev->classname = MAKE_STRING("weapon_tommygun"); // hack to allow for old names
|
||||
Precache( );
|
||||
SET_MODEL(ENT(pev), "models/w_tommygun.mdl");
|
||||
m_iId = WEAPON_TOMMYGUN;
|
||||
|
||||
m_iDefaultAmmo = TOMMYGUN_DEFAULT_GIVE;
|
||||
|
||||
FallInit();// get ready to fall down.
|
||||
}
|
||||
|
||||
|
||||
void CTommyGun::Precache( void )
|
||||
{
|
||||
PRECACHE_MODEL("models/v_tommygun.mdl");
|
||||
PRECACHE_MODEL("models/w_tommygun.mdl");
|
||||
PRECACHE_MODEL("models/p_tommygun.mdl");
|
||||
|
||||
m_iShell = PRECACHE_MODEL ("models/shell.mdl");// brass shellTE_MODEL
|
||||
|
||||
PRECACHE_MODEL("models/tommy_ammo.mdl");
|
||||
|
||||
PRECACHE_SOUND("weapons/tommy_draw_slideback.wav");
|
||||
PRECACHE_SOUND("weapons/tommy_reload_clipin.wav");
|
||||
PRECACHE_SOUND("weapons/tommy_reload_clipout.wav");
|
||||
PRECACHE_SOUND("weapons/tommy_shoot1.wav");
|
||||
PRECACHE_SOUND("weapons/tommy_shoot2.wav");
|
||||
|
||||
// PRECACHE_SOUND ("weapons/357_cock1.wav");
|
||||
|
||||
m_usTommyGun = PRECACHE_EVENT( 1, "events/tommygun.sc" );
|
||||
}
|
||||
|
||||
int CTommyGun::GetItemInfo(ItemInfo *p)
|
||||
{
|
||||
p->pszName = STRING(pev->classname);
|
||||
p->pszAmmo1 = "Tommy Gun";
|
||||
p->iMaxAmmo1 = TOMMYGUN_MAX_CARRY;
|
||||
p->pszAmmo2 = NULL;
|
||||
p->iMaxAmmo2 = -1;
|
||||
p->iMaxClip = TOMMYGUN_MAX_CLIP;
|
||||
p->iSlot = 1;
|
||||
p->iPosition = 2;
|
||||
p->iFlags = 0;
|
||||
p->iId = m_iId = WEAPON_TOMMYGUN;
|
||||
p->iWeight = TOMMYGUN_WEIGHT;
|
||||
|
||||
return 1;
|
||||
}
|
||||
|
||||
int CTommyGun::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 CTommyGun::Deploy( )
|
||||
{
|
||||
return DefaultDeploy( "models/v_tommygun.mdl", "models/p_tommygun.mdl", TOMMYGUN_DRAW, "tommygun" );
|
||||
}
|
||||
|
||||
void CTommyGun::PrimaryAttack()
|
||||
{
|
||||
// don't fire underwater
|
||||
if (m_pPlayer->pev->waterlevel == 3)
|
||||
{
|
||||
PlayEmptySound( );
|
||||
m_flNextPrimaryAttack = gpGlobals->time + 0.15;
|
||||
return;
|
||||
}
|
||||
|
||||
if (m_iClip <= 0)
|
||||
{
|
||||
PlayEmptySound();
|
||||
m_flNextPrimaryAttack = gpGlobals->time + 0.15;
|
||||
return;
|
||||
}
|
||||
|
||||
PLAYBACK_EVENT( 0, m_pPlayer->edict(), m_usTommyGun );
|
||||
|
||||
m_pPlayer->m_iWeaponVolume = NORMAL_GUN_VOLUME;
|
||||
m_pPlayer->m_iWeaponFlash = NORMAL_GUN_FLASH;
|
||||
|
||||
m_iClip--;
|
||||
|
||||
// player "shoot" animation
|
||||
m_pPlayer->SetAnimation( PLAYER_ATTACK1 );
|
||||
|
||||
Vector vecSrc = m_pPlayer->GetGunPosition( );
|
||||
Vector vecAiming = m_pPlayer->GetAutoaimVector( AUTOAIM_5DEGREES );
|
||||
|
||||
if ( g_pGameRules->IsDeathmatch() )
|
||||
{
|
||||
// optimized multiplayer. Widened to make it easier to hit a moving player
|
||||
m_pPlayer->FireBullets( 1, vecSrc, vecAiming, VECTOR_CONE_6DEGREES, 8192, BULLET_PLAYER_TOMMYGUN, 2 );
|
||||
}
|
||||
else
|
||||
{
|
||||
// single player spread
|
||||
m_pPlayer->FireBullets( 1, vecSrc, vecAiming, VECTOR_CONE_3DEGREES, 8192, BULLET_PLAYER_TOMMYGUN, 2 );
|
||||
}
|
||||
|
||||
//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 = m_flNextPrimaryAttack + 0.1;
|
||||
if (m_flNextPrimaryAttack < gpGlobals->time)
|
||||
m_flNextPrimaryAttack = gpGlobals->time + 0.1;
|
||||
|
||||
m_flTimeWeaponIdle = gpGlobals->time + RANDOM_FLOAT ( 10, 15 );
|
||||
}
|
||||
|
||||
void CTommyGun::SecondaryAttack( void )
|
||||
{
|
||||
PrimaryAttack();
|
||||
}
|
||||
|
||||
void CTommyGun::Reload( void )
|
||||
{
|
||||
DefaultReload( TOMMYGUN_MAX_CLIP, TOMMYGUN_RELOAD, 177.0/38.0 );
|
||||
}
|
||||
|
||||
void CTommyGun::WeaponIdle( void )
|
||||
{
|
||||
ResetEmptySound( );
|
||||
|
||||
m_pPlayer->GetAutoaimVector( AUTOAIM_5DEGREES );
|
||||
|
||||
if (m_flTimeWeaponIdle > gpGlobals->time)
|
||||
return;
|
||||
|
||||
SendWeaponAnim( TOMMYGUN_IDLE );
|
||||
|
||||
//m_flTimeWeaponIdle = gpGlobals->time + RANDOM_FLOAT ( 10, 15 );// how long till we do this again.
|
||||
m_flTimeWeaponIdle = gpGlobals->time + 40.0/12.0 + RANDOM_FLOAT ( 1, 4 );
|
||||
}
|
||||
|
||||
////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
void CTommyGunAmmoClip::Spawn( void )
|
||||
{
|
||||
Precache( );
|
||||
SET_MODEL(ENT(pev), "models/tommy_ammo.mdl");
|
||||
CBasePlayerAmmo::Spawn( );
|
||||
}
|
||||
|
||||
void CTommyGunAmmoClip::Precache( void )
|
||||
{
|
||||
PRECACHE_MODEL ("models/tommy_ammo.mdl");
|
||||
PRECACHE_SOUND("items/9mmclip1.wav");
|
||||
}
|
||||
|
||||
BOOL CTommyGunAmmoClip::AddAmmo( CBaseEntity *pOther )
|
||||
{
|
||||
int bResult = (pOther->GiveAmmo( AMMO_TOMMYGUNCLIP_GIVE, "Tommy Gun", TOMMYGUN_MAX_CARRY) != -1);
|
||||
if (bResult)
|
||||
{
|
||||
EMIT_SOUND(ENT(pev), CHAN_ITEM, "items/9mmclip1.wav", 1, ATTN_NORM);
|
||||
}
|
||||
return bResult;
|
||||
}
|
||||
|
||||
|
||||
LINK_ENTITY_TO_CLASS( ammo_tommygun, CTommyGunAmmoClip );
|
||||
|
||||
|
||||
|
||||
|
|
@ -0,0 +1,46 @@
|
|||
|
||||
#ifndef TOMMYGUN_H
|
||||
#define TOMMYGUN_H
|
||||
|
||||
class CTommyGun : public CBasePlayerWeapon
|
||||
{
|
||||
public:
|
||||
void Spawn( void );
|
||||
void Precache( void );
|
||||
int iItemSlot( void ) { return 1; }
|
||||
int GetItemInfo(ItemInfo *p);
|
||||
int AddToPlayer( CBasePlayer *pPlayer );
|
||||
|
||||
void PrimaryAttack( void );
|
||||
void SecondaryAttack( void );
|
||||
BOOL Deploy( void );
|
||||
void Reload( void );
|
||||
void WeaponIdle( void );
|
||||
float m_flNextAnimTime;
|
||||
int m_iShell;
|
||||
|
||||
virtual BOOL UseDecrement( void )
|
||||
{
|
||||
#if defined( CLIENT_WEAPONS )
|
||||
return TRUE;
|
||||
#else
|
||||
return FALSE;
|
||||
#endif
|
||||
}
|
||||
|
||||
private:
|
||||
unsigned short m_usTommyGun;
|
||||
};
|
||||
|
||||
class CTommyGunAmmoClip : public CBasePlayerAmmo
|
||||
{
|
||||
public:
|
||||
void Spawn( void );
|
||||
void Precache( void );
|
||||
BOOL AddAmmo( CBaseEntity *pOther );
|
||||
};
|
||||
|
||||
|
||||
|
||||
#endif
|
||||
|
|
@ -0,0 +1,882 @@
|
|||
/***
|
||||
*
|
||||
* Copyright (c) 1999, 2000 Valve LLC. All rights reserved.
|
||||
*
|
||||
* This product contains software technology licensed from Id
|
||||
* Software, Inc. ("Id Technology"). Id Technology (c) 1996 Id Software, Inc.
|
||||
* All Rights Reserved.
|
||||
*
|
||||
* This source code contains proprietary and confidential information of
|
||||
* Valve LLC and its suppliers. Access to this code is restricted to
|
||||
* persons who have executed a written SDK license with Valve. Any access,
|
||||
* use or distribution of this code by or to any unlicensed person is illegal.
|
||||
*
|
||||
****/
|
||||
//=========================================================
|
||||
// Great Race of Yith servant - the Yodan 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 "animation.h"
|
||||
#include "soundent.h"
|
||||
|
||||
extern DLL_GLOBAL int g_iSkillLevel;
|
||||
|
||||
#define YODAN_LIGHTNING_GUN ( 1 << 0)
|
||||
#define YODAN_NONE ( 1 << 1)
|
||||
|
||||
#define GUN_GROUP 1
|
||||
#define GUN_LIGHTNING 0
|
||||
#define GUN_NONE 1
|
||||
|
||||
//=========================================================
|
||||
// Monster's Anim Events Go Here
|
||||
//=========================================================
|
||||
#define YODAN_AE_CLAW ( 1 )
|
||||
#define YODAN_AE_CLAWRAKE ( 2 )
|
||||
#define YODAN_AE_ZAP_POWERUP ( 3 )
|
||||
#define YODAN_AE_ZAP_SHOOT ( 4 )
|
||||
#define YODAN_AE_ZAP_DONE ( 5 )
|
||||
#define YODAN_AE_DROP_GUN ( 6 )
|
||||
|
||||
#define YODAN_MAX_BEAMS 8
|
||||
|
||||
|
||||
#include "Yodan.h"
|
||||
|
||||
|
||||
LINK_ENTITY_TO_CLASS( monster_yodan, CYodan );
|
||||
|
||||
|
||||
TYPEDESCRIPTION CYodan::m_SaveData[] =
|
||||
{
|
||||
DEFINE_ARRAY( CYodan, m_pBeam, FIELD_CLASSPTR, YODAN_MAX_BEAMS ),
|
||||
DEFINE_FIELD( CYodan, m_iBeams, FIELD_INTEGER ),
|
||||
DEFINE_FIELD( CYodan, m_flNextAttack, FIELD_TIME ),
|
||||
DEFINE_FIELD( CYodan, m_fStanding, FIELD_BOOLEAN ),
|
||||
DEFINE_FIELD( CYodan, m_voicePitch, FIELD_INTEGER ),
|
||||
};
|
||||
|
||||
IMPLEMENT_SAVERESTORE( CYodan, CSquadMonster );
|
||||
|
||||
|
||||
|
||||
|
||||
const char *CYodan::pAttackHitSounds[] =
|
||||
{
|
||||
"zombie/claw_strike1.wav",
|
||||
"zombie/claw_strike2.wav",
|
||||
"zombie/claw_strike3.wav",
|
||||
};
|
||||
|
||||
const char *CYodan::pAttackMissSounds[] =
|
||||
{
|
||||
"zombie/claw_miss1.wav",
|
||||
"zombie/claw_miss2.wav",
|
||||
};
|
||||
|
||||
const char *CYodan::pPainSounds[] =
|
||||
{
|
||||
"yodan/yo_pain1.wav",
|
||||
"yodan/yo_pain2.wav",
|
||||
"yodan/yo_pain3.wav",
|
||||
};
|
||||
|
||||
const char *CYodan::pDeathSounds[] =
|
||||
{
|
||||
"yodan/yo_die1.wav",
|
||||
"yodan/yo_die2.wav",
|
||||
};
|
||||
|
||||
//=========================================================
|
||||
// Classify - indicates this monster's place in the
|
||||
// relationship table.
|
||||
//=========================================================
|
||||
int CYodan :: Classify ( void )
|
||||
{
|
||||
return m_iClass?m_iClass:CLASS_ALIEN_MILITARY;
|
||||
}
|
||||
|
||||
|
||||
int CYodan::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 CYodan :: CallForHelp( char *szClassname, float flDist, EHANDLE hEnemy, Vector &vecLocation )
|
||||
{
|
||||
// ALERT( at_aiconsole, "help " );
|
||||
|
||||
// skip ones not on my netname
|
||||
if ( FStringNull( pev->netname ))
|
||||
return;
|
||||
|
||||
CBaseEntity *pEntity = NULL;
|
||||
|
||||
while ((pEntity = UTIL_FindEntityByString( pEntity, "netname", STRING( pev->netname ))) != NULL)
|
||||
{
|
||||
float d = (pev->origin - pEntity->pev->origin).Length();
|
||||
if (d < flDist)
|
||||
{
|
||||
CBaseMonster *pMonster = pEntity->MyMonsterPointer( );
|
||||
if (pMonster)
|
||||
{
|
||||
pMonster->m_afMemory |= bits_MEMORY_PROVOKED;
|
||||
pMonster->PushEnemy( hEnemy, vecLocation );
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
//=========================================================
|
||||
// ALertSound - scream
|
||||
//=========================================================
|
||||
void CYodan :: AlertSound( void )
|
||||
{
|
||||
if ( m_hEnemy != NULL )
|
||||
{
|
||||
SENTENCEG_PlayRndSz(ENT(pev), "YODAN_ALERT", 0.85, ATTN_NORM, 0, m_voicePitch);
|
||||
|
||||
CallForHelp( "monster_greatrace", 512, m_hEnemy, m_vecEnemyLKP );
|
||||
CallForHelp( "monster_yodan", 512, m_hEnemy, m_vecEnemyLKP );
|
||||
}
|
||||
}
|
||||
|
||||
//=========================================================
|
||||
// IdleSound
|
||||
//=========================================================
|
||||
void CYodan :: IdleSound( void )
|
||||
{
|
||||
//if (RANDOM_LONG( 0, 2 ) == 0)
|
||||
//{
|
||||
SENTENCEG_PlayRndSz(ENT(pev), "YODAN_IDLE", 0.85, ATTN_NORM, 0, m_voicePitch);
|
||||
//}
|
||||
|
||||
}
|
||||
|
||||
//=========================================================
|
||||
// PainSound
|
||||
//=========================================================
|
||||
void CYodan :: 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 CYodan :: 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 CYodan :: ISoundMask ( void)
|
||||
{
|
||||
return bits_SOUND_WORLD |
|
||||
bits_SOUND_COMBAT |
|
||||
bits_SOUND_DANGER |
|
||||
bits_SOUND_PLAYER;
|
||||
}
|
||||
|
||||
|
||||
void CYodan::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 CYodan :: SetYawSpeed ( void )
|
||||
{
|
||||
int ys;
|
||||
|
||||
switch ( m_Activity )
|
||||
{
|
||||
case ACT_WALK:
|
||||
ys = 90;
|
||||
break;
|
||||
case ACT_RUN:
|
||||
ys = 120;
|
||||
break;
|
||||
case ACT_IDLE:
|
||||
ys = 90;
|
||||
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 CYodan :: HandleAnimEvent( MonsterEvent_t *pEvent )
|
||||
{
|
||||
// ALERT( at_console, "event %d : %f\n", pEvent->event, pev->frame );
|
||||
switch( pEvent->event )
|
||||
{
|
||||
case YODAN_AE_DROP_GUN:
|
||||
{
|
||||
if (GetBodygroup(GUN_GROUP) != GUN_LIGHTNING) break;
|
||||
if (pev->spawnflags & SF_MONSTER_NO_WPN_DROP) break;
|
||||
|
||||
Vector vecGunPos;
|
||||
Vector vecGunAngles;
|
||||
|
||||
GetAttachment( 0, vecGunPos, vecGunAngles );
|
||||
|
||||
// switch to body group with no gun.
|
||||
SetBodygroup( GUN_GROUP, GUN_NONE );
|
||||
|
||||
// now spawn a gun.
|
||||
DropItem( "weapon_lightninggun", vecGunPos, vecGunAngles );
|
||||
|
||||
}
|
||||
break;
|
||||
|
||||
case YODAN_AE_CLAW:
|
||||
{
|
||||
// SOUND HERE!
|
||||
CBaseEntity *pHurt = CheckTraceHullAttack( 70, gSkillData.yodanDmgClaw, 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 YODAN_AE_CLAWRAKE:
|
||||
{
|
||||
CBaseEntity *pHurt = CheckTraceHullAttack( 70, gSkillData.yodanDmgClawrake, 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 YODAN_AE_ZAP_POWERUP:
|
||||
{
|
||||
if (m_flNextAttack > gpGlobals->time) break;
|
||||
|
||||
// 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( );
|
||||
|
||||
}
|
||||
//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 YODAN_AE_ZAP_SHOOT:
|
||||
{
|
||||
ClearBeams( );
|
||||
|
||||
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( 2.0, 5.0 );
|
||||
}
|
||||
break;
|
||||
|
||||
case YODAN_AE_ZAP_DONE:
|
||||
{
|
||||
ClearBeams( );
|
||||
}
|
||||
break;
|
||||
|
||||
default:
|
||||
CSquadMonster::HandleAnimEvent( pEvent );
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
//=========================================================
|
||||
// CheckRangeAttack1 - normal beam attack
|
||||
//=========================================================
|
||||
BOOL CYodan :: CheckRangeAttack1 ( float flDot, float flDist )
|
||||
{
|
||||
if (GetBodygroup(GUN_GROUP) == GUN_NONE)
|
||||
return FALSE;
|
||||
|
||||
if (m_flNextAttack > gpGlobals->time)
|
||||
{
|
||||
return FALSE;
|
||||
}
|
||||
|
||||
return CSquadMonster::CheckRangeAttack1( flDot, flDist );
|
||||
}
|
||||
|
||||
//=========================================================
|
||||
// CheckRangeAttack2 - check bravery and try to resurect dead comrades
|
||||
//=========================================================
|
||||
BOOL CYodan :: CheckRangeAttack2 ( float flDot, float flDist )
|
||||
{
|
||||
return FALSE;
|
||||
}
|
||||
|
||||
|
||||
//=========================================================
|
||||
// StartTask
|
||||
//=========================================================
|
||||
void CYodan :: StartTask ( Task_t *pTask )
|
||||
{
|
||||
ClearBeams( );
|
||||
|
||||
CSquadMonster :: StartTask ( pTask );
|
||||
}
|
||||
|
||||
|
||||
//=========================================================
|
||||
// Spawn
|
||||
//=========================================================
|
||||
void CYodan :: Spawn()
|
||||
{
|
||||
Precache( );
|
||||
|
||||
if (pev->model)
|
||||
SET_MODEL(ENT(pev), STRING(pev->model)); //LRC
|
||||
else
|
||||
SET_MODEL(ENT(pev), "models/yodan.mdl");
|
||||
// UTIL_SetSize(pev, VEC_HUMAN_HULL_MIN, VEC_HUMAN_HULL_MAX);
|
||||
UTIL_SetSize(pev, VEC_HULL_MIN, VEC_HULL_MAX);
|
||||
|
||||
pev->solid = SOLID_SLIDEBOX;
|
||||
pev->movetype = MOVETYPE_STEP;
|
||||
m_bloodColor = BLOOD_COLOR_GREEN;
|
||||
pev->effects = 0;
|
||||
if (pev->health == 0)
|
||||
pev->health = gSkillData.yodanHealth;
|
||||
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_DOORS_GROUP;
|
||||
|
||||
m_voicePitch = RANDOM_LONG( 85, 110 );
|
||||
m_fStanding = 1;
|
||||
|
||||
if (FBitSet( pev->weapons, YODAN_LIGHTNING_GUN ))
|
||||
{
|
||||
SetBodygroup( GUN_GROUP, GUN_LIGHTNING );
|
||||
}
|
||||
else // none
|
||||
{
|
||||
SetBodygroup( GUN_GROUP, GUN_NONE );
|
||||
}
|
||||
|
||||
MonsterInit();
|
||||
|
||||
m_iBeams = 0;
|
||||
}
|
||||
|
||||
//=========================================================
|
||||
// Precache - precaches all resources this monster needs
|
||||
//=========================================================
|
||||
void CYodan :: Precache()
|
||||
{
|
||||
int i;
|
||||
|
||||
if (pev->model)
|
||||
PRECACHE_MODEL((char*)STRING(pev->model)); //LRC
|
||||
else
|
||||
PRECACHE_MODEL("models/yodan.mdl");
|
||||
PRECACHE_MODEL("sprites/lgtning.spr");
|
||||
PRECACHE_SOUND("debris/zap1.wav");
|
||||
PRECACHE_SOUND("debris/zap4.wav");
|
||||
PRECACHE_SOUND("weapons/electro4.wav");
|
||||
PRECACHE_SOUND("hassault/hw_shoot1.wav");
|
||||
PRECACHE_SOUND("zombie/zo_pain2.wav");
|
||||
PRECACHE_SOUND("headcrab/hc_headbite.wav");
|
||||
PRECACHE_SOUND("weapons/cbar_miss1.wav");
|
||||
|
||||
for ( i = 0; i < ARRAYSIZE( pAttackHitSounds ); i++ )
|
||||
PRECACHE_SOUND((char *)pAttackHitSounds[i]);
|
||||
|
||||
for ( i = 0; i < ARRAYSIZE( pAttackMissSounds ); i++ )
|
||||
PRECACHE_SOUND((char *)pAttackMissSounds[i]);
|
||||
|
||||
for ( i = 0; i < ARRAYSIZE( pPainSounds ); i++ )
|
||||
PRECACHE_SOUND((char *)pPainSounds[i]);
|
||||
|
||||
for ( i = 0; i < ARRAYSIZE( pDeathSounds ); i++ )
|
||||
PRECACHE_SOUND((char *)pDeathSounds[i]);
|
||||
|
||||
UTIL_PrecacheOther( "test_effect" );
|
||||
}
|
||||
|
||||
|
||||
//=========================================================
|
||||
// TakeDamage - get provoked when injured
|
||||
//=========================================================
|
||||
|
||||
int CYodan :: 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 CYodan::TraceAttack( entvars_t *pevAttacker, float flDamage, Vector vecDir, TraceResult *ptr, int bitsDamageType)
|
||||
{
|
||||
if (bitsDamageType & DMG_SHOCK)
|
||||
if (FClassnameIs(pev, STRING(pevAttacker->classname)))
|
||||
return;
|
||||
|
||||
CSquadMonster::TraceAttack( pevAttacker, flDamage, vecDir, ptr, bitsDamageType );
|
||||
}
|
||||
|
||||
|
||||
//=========================================================
|
||||
// GibMonster - make gun fly through the air.
|
||||
//=========================================================
|
||||
void CYodan :: GibMonster ( void )
|
||||
{
|
||||
Vector vecGunPos;
|
||||
Vector vecGunAngles;
|
||||
|
||||
if (( GetBodygroup( GUN_GROUP ) != GUN_NONE ) && !(pev->spawnflags & SF_MONSTER_NO_WPN_DROP))
|
||||
{// throw a gun if the yodan has one
|
||||
GetAttachment( 0, vecGunPos, vecGunAngles );
|
||||
|
||||
CBaseEntity *pGun;
|
||||
pGun = DropItem( "weapon_lightninggun", vecGunPos, vecGunAngles );
|
||||
|
||||
if ( pGun )
|
||||
{
|
||||
pGun->pev->velocity = Vector (RANDOM_FLOAT(-100,100), RANDOM_FLOAT(-100,100), RANDOM_FLOAT(200,300));
|
||||
pGun->pev->avelocity = Vector ( 0, RANDOM_FLOAT( 200, 400 ), 0 );
|
||||
}
|
||||
}
|
||||
|
||||
CSquadMonster :: GibMonster();
|
||||
}
|
||||
|
||||
//=========================================================
|
||||
// AI Schedules Specific to this monster
|
||||
//=========================================================
|
||||
|
||||
|
||||
|
||||
// primary range attack
|
||||
Task_t tlYodanAttack1[] =
|
||||
{
|
||||
{ TASK_STOP_MOVING, 0 },
|
||||
{ TASK_FACE_IDEAL, (float)0 },
|
||||
{ TASK_RANGE_ATTACK1, (float)0 },
|
||||
};
|
||||
|
||||
Schedule_t slYodanAttack1[] =
|
||||
{
|
||||
{
|
||||
tlYodanAttack1,
|
||||
ARRAYSIZE ( tlYodanAttack1 ),
|
||||
bits_COND_CAN_MELEE_ATTACK1 |
|
||||
bits_COND_HEAR_SOUND |
|
||||
bits_COND_HEAVY_DAMAGE,
|
||||
|
||||
bits_SOUND_DANGER,
|
||||
"Yodan Range Attack1"
|
||||
},
|
||||
};
|
||||
|
||||
|
||||
DEFINE_CUSTOM_SCHEDULES( CYodan )
|
||||
{
|
||||
slYodanAttack1,
|
||||
};
|
||||
|
||||
IMPLEMENT_CUSTOM_SCHEDULES( CYodan, CSquadMonster );
|
||||
|
||||
|
||||
//=========================================================
|
||||
//=========================================================
|
||||
Schedule_t *CYodan :: 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)
|
||||
{
|
||||
if (!HasConditions( bits_COND_CAN_MELEE_ATTACK1 ))
|
||||
{
|
||||
m_failSchedule = SCHED_CHASE_ENEMY;
|
||||
if (HasConditions( bits_COND_LIGHT_DAMAGE | bits_COND_HEAVY_DAMAGE))
|
||||
{
|
||||
return GetScheduleOfType( SCHED_TAKE_COVER_FROM_ENEMY );
|
||||
}
|
||||
if ( HasConditions ( bits_COND_SEE_ENEMY ) && HasConditions ( bits_COND_ENEMY_FACING_ME ) )
|
||||
{
|
||||
// ALERT( at_console, "exposed\n");
|
||||
return GetScheduleOfType( SCHED_TAKE_COVER_FROM_ENEMY );
|
||||
}
|
||||
}
|
||||
}
|
||||
break;
|
||||
}
|
||||
return CSquadMonster::GetSchedule( );
|
||||
}
|
||||
|
||||
|
||||
Schedule_t *CYodan :: 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 slYodanAttack1;
|
||||
case SCHED_RANGE_ATTACK2:
|
||||
return slYodanAttack1;
|
||||
}
|
||||
return CSquadMonster :: GetScheduleOfType( Type );
|
||||
}
|
||||
|
||||
//=========================================================
|
||||
// SetActivity
|
||||
//=========================================================
|
||||
void CYodan :: SetActivity ( Activity NewActivity )
|
||||
{
|
||||
// if we were crouching, move up again
|
||||
if (m_fStanding == 0)
|
||||
{
|
||||
m_fStanding = 1;
|
||||
pev->origin.z += 18;
|
||||
UTIL_SetSize(pev, VEC_HULL_MIN, VEC_HULL_MAX);
|
||||
}
|
||||
|
||||
|
||||
int iSequence = ACTIVITY_NOT_AVAILABLE;
|
||||
void *pmodel = GET_MODEL_PTR( ENT(pev) );
|
||||
|
||||
switch ( NewActivity)
|
||||
{
|
||||
case ACT_MELEE_ATTACK1:
|
||||
|
||||
// randomly stand or crouch
|
||||
if (RANDOM_LONG(0,99) == 0)
|
||||
m_fStanding = 0;
|
||||
else
|
||||
m_fStanding = 1;
|
||||
|
||||
// a short enemy...probably a snake...
|
||||
if ((m_hEnemy != NULL) && (m_hEnemy->pev->maxs.z < 36))
|
||||
{
|
||||
m_fStanding = 0;
|
||||
}
|
||||
|
||||
if (m_fStanding == 0)
|
||||
{
|
||||
UTIL_SetSize(pev, VEC_DUCK_HULL_MIN, VEC_DUCK_HULL_MAX);
|
||||
pev->origin.z -= 18;
|
||||
}
|
||||
|
||||
if ( m_fStanding )
|
||||
{
|
||||
// get aimable sequence
|
||||
iSequence = LookupSequence( "ref_shoot_crowbar" );
|
||||
}
|
||||
else
|
||||
{
|
||||
// get crouching shoot
|
||||
iSequence = LookupSequence( "crouch_shoot_crowbar" );
|
||||
}
|
||||
break;
|
||||
default:
|
||||
iSequence = LookupActivity ( NewActivity );
|
||||
break;
|
||||
}
|
||||
|
||||
m_Activity = NewActivity; // Go ahead and set this so it doesn't keep trying when the anim is not present
|
||||
|
||||
// Set to the desired anim, or default anim if the desired is not present
|
||||
if ( iSequence > ACTIVITY_NOT_AVAILABLE )
|
||||
{
|
||||
if ( pev->sequence != iSequence || !m_fSequenceLoops )
|
||||
{
|
||||
pev->frame = 0;
|
||||
}
|
||||
|
||||
pev->sequence = iSequence; // Set to the reset anim (if it's there)
|
||||
ResetSequenceInfo( );
|
||||
SetYawSpeed();
|
||||
}
|
||||
else
|
||||
{
|
||||
// Not available try to get default anim
|
||||
ALERT ( at_console, "%s has no sequence for act:%d\n", STRING(pev->classname), NewActivity );
|
||||
pev->sequence = 0; // Set to the reset anim (if it's there)
|
||||
}
|
||||
}
|
||||
|
||||
//=========================================================
|
||||
// ArmBeam - small beam from arm to nearby geometry
|
||||
//=========================================================
|
||||
|
||||
/*
|
||||
void CYodan :: ArmBeam( int side )
|
||||
{
|
||||
TraceResult tr;
|
||||
float flDist = 1.0;
|
||||
|
||||
if (m_iBeams >= YODAN_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 );
|
||||
|
||||
// Cthulhu: check m_pBeam
|
||||
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 CYodan :: 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 CYodan :: WackBeam( int side, CBaseEntity *pEntity )
|
||||
{
|
||||
Vector vecDest;
|
||||
float flDist = 1.0;
|
||||
|
||||
if (m_iBeams >= YODAN_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 CYodan :: ZapBeam( int side )
|
||||
{
|
||||
Vector vecSrc, vecAim;
|
||||
TraceResult tr;
|
||||
CBaseEntity *pEntity;
|
||||
|
||||
if (m_iBeams >= YODAN_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.yodanDmgZap, 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 CYodan :: ClearBeams( )
|
||||
{
|
||||
for (int i = 0; i < YODAN_MAX_BEAMS; i++)
|
||||
{
|
||||
if (m_pBeam[i])
|
||||
{
|
||||
UTIL_Remove( m_pBeam[i] );
|
||||
m_pBeam[i] = NULL;
|
||||
m_iBeams--;
|
||||
}
|
||||
}
|
||||
m_iBeams = 0;
|
||||
pev->skin = 0;
|
||||
|
||||
STOP_SOUND( ENT(pev), CHAN_WEAPON, "debris/zap4.wav" );
|
||||
}
|
|
@ -0,0 +1,63 @@
|
|||
|
||||
#ifndef YODAN_H
|
||||
#define YODAN_H
|
||||
|
||||
class CYodan : public CSquadMonster
|
||||
{
|
||||
public:
|
||||
void Spawn( void );
|
||||
void Precache( void );
|
||||
void SetYawSpeed( void );
|
||||
int ISoundMask( void );
|
||||
int Classify ( void );
|
||||
int IRelationship( CBaseEntity *pTarget );
|
||||
void HandleAnimEvent( MonsterEvent_t *pEvent );
|
||||
BOOL CheckRangeAttack1 ( float flDot, float flDist );
|
||||
BOOL CheckRangeAttack2 ( float flDot, float flDist );
|
||||
void CallForHelp( char *szClassname, float flDist, EHANDLE hEnemy, Vector &vecLocation );
|
||||
void TraceAttack( entvars_t *pevAttacker, float flDamage, Vector vecDir, TraceResult *ptr, int bitsDamageType);
|
||||
int TakeDamage( entvars_t* pevInflictor, entvars_t* pevAttacker, float flDamage, int bitsDamageType);
|
||||
void GibMonster( void );
|
||||
|
||||
void DeathSound( void );
|
||||
void PainSound( void );
|
||||
void AlertSound( void );
|
||||
void IdleSound( void );
|
||||
|
||||
void Killed( entvars_t *pevAttacker, int iGib );
|
||||
|
||||
BOOL m_fStanding;
|
||||
|
||||
void SetActivity ( Activity NewActivity );
|
||||
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 );
|
||||
|
||||
CBeam *m_pBeam[YODAN_MAX_BEAMS];
|
||||
|
||||
int m_iBeams;
|
||||
float m_flNextAttack;
|
||||
|
||||
int m_voicePitch;
|
||||
|
||||
static const char *pAttackHitSounds[];
|
||||
static const char *pAttackMissSounds[];
|
||||
static const char *pPainSounds[];
|
||||
static const char *pDeathSounds[];
|
||||
};
|
||||
|
||||
|
||||
|
||||
|
||||
#endif
|
|
@ -0,0 +1,313 @@
|
|||
/***
|
||||
*
|
||||
* Copyright (c) 1999, 2000 Valve LLC. All rights reserved.
|
||||
*
|
||||
* This product contains software technology licensed from Id
|
||||
* Software, Inc. ("Id Technology"). Id Technology (c) 1996 Id Software, Inc.
|
||||
* All Rights Reserved.
|
||||
*
|
||||
* This source code contains proprietary and confidential information of
|
||||
* Valve LLC and its suppliers. Access to this code is restricted to
|
||||
* persons who have executed a written SDK license with Valve. Any access,
|
||||
* use or distribution of this code by or to any unlicensed person is illegal.
|
||||
*
|
||||
****/
|
||||
//=========================================================
|
||||
// alien_egg.cpp - spawns headcrabs
|
||||
//=========================================================
|
||||
|
||||
#include "extdll.h"
|
||||
#include "util.h"
|
||||
#include "cbase.h"
|
||||
#include "monsters.h"
|
||||
#include "schedule.h"
|
||||
#include "game.h"
|
||||
#include "defaultai.h"
|
||||
#include "scripted.h"
|
||||
#include "weapons.h"
|
||||
#include "soundent.h"
|
||||
|
||||
#include "alien_egg.h"
|
||||
|
||||
const int DEAD_EGG_Z = 8;
|
||||
|
||||
LINK_ENTITY_TO_CLASS( monster_alienegg, CAlienEgg );
|
||||
|
||||
TYPEDESCRIPTION CAlienEgg::m_SaveData[] =
|
||||
{
|
||||
DEFINE_FIELD( CAlienEgg, m_iOrientation, FIELD_INTEGER ),
|
||||
};
|
||||
|
||||
IMPLEMENT_SAVERESTORE( CAlienEgg, CBaseMonster );
|
||||
|
||||
//=========================================================
|
||||
// Classify - indicates this monster's place in the
|
||||
// relationship table.
|
||||
//=========================================================
|
||||
int CAlienEgg :: Classify ( void )
|
||||
{
|
||||
return m_iClass?m_iClass:CLASS_ALIEN_PREY;
|
||||
}
|
||||
|
||||
//=========================================================
|
||||
// Center - returns the real center of the headcrab. The
|
||||
// bounding box is much larger than the actual creature so
|
||||
// this is needed for targeting
|
||||
//=========================================================
|
||||
Vector CAlienEgg :: Center ( void )
|
||||
{
|
||||
if (m_iOrientation == 0)
|
||||
{
|
||||
return Vector( pev->origin.x, pev->origin.y, pev->origin.z + 8 );
|
||||
}
|
||||
else
|
||||
{
|
||||
return Vector( pev->origin.x, pev->origin.y, pev->origin.z - 8 );
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
Vector CAlienEgg :: BodyTarget( const Vector &posSrc )
|
||||
{
|
||||
return Center( );
|
||||
}
|
||||
|
||||
//=========================================================
|
||||
// SetYawSpeed - allows each sequence to have a different
|
||||
// turn rate associated with it.
|
||||
//=========================================================
|
||||
void CAlienEgg :: SetYawSpeed ( void )
|
||||
{
|
||||
}
|
||||
|
||||
void CAlienEgg::KeyValue( KeyValueData *pkvd )
|
||||
{
|
||||
if (FStrEq(pkvd->szKeyName, "orientation"))
|
||||
{
|
||||
m_iOrientation = atoi(pkvd->szValue);
|
||||
pkvd->fHandled = TRUE;
|
||||
|
||||
}
|
||||
else
|
||||
CBaseMonster::KeyValue( pkvd );
|
||||
}
|
||||
|
||||
//=========================================================
|
||||
// Spawn
|
||||
//=========================================================
|
||||
void CAlienEgg :: Spawn()
|
||||
{
|
||||
Precache( );
|
||||
|
||||
if (pev->model)
|
||||
SET_MODEL(ENT(pev), STRING(pev->model)); //LRC
|
||||
else
|
||||
SET_MODEL(ENT(pev), "models/alien_egg.mdl");
|
||||
UTIL_SetSize(pev, Vector(-16, -16, 0), Vector(16, 16, 48));
|
||||
|
||||
pev->solid = SOLID_SLIDEBOX;
|
||||
pev->movetype = MOVETYPE_NONE;
|
||||
m_bloodColor = BLOOD_COLOR_GREEN;
|
||||
pev->effects = 0;
|
||||
if (pev->health == 0)
|
||||
pev->health = gSkillData.headcrabHealth;
|
||||
pev->yaw_speed = 0;//!!! should we put this in the monster's changeanim function since turn rates may vary with state/anim?
|
||||
m_flFieldOfView = VIEW_FIELD_FULL;// indicates the width of this monster's forward view cone ( as a dotproduct result )
|
||||
m_MonsterState = MONSTERSTATE_NONE;
|
||||
|
||||
if (m_iOrientation == 1)
|
||||
{
|
||||
UTIL_SetSize(pev, Vector(-16, -16, -48), Vector(16, 16, 0));
|
||||
|
||||
pev->idealpitch = 180;
|
||||
pev->flags |= FL_FLY;
|
||||
SetBits( pev->spawnflags, SF_MONSTER_FALL_TO_GROUND);
|
||||
pev->effects |= EF_INVLIGHT;
|
||||
|
||||
pev->angles.x = 180;
|
||||
pev->angles.y = pev->angles.y + 180;
|
||||
if (pev->angles.y > 360)
|
||||
pev->angles.y = pev->angles.y - 360;
|
||||
}
|
||||
|
||||
MonsterInit();
|
||||
|
||||
SetUse(EggUse);
|
||||
|
||||
if (m_iOrientation == 0)
|
||||
{
|
||||
pev->view_ofs = Vector ( 0, 0, 32 );// position of the eyes relative to monster's origin.
|
||||
}
|
||||
else
|
||||
{
|
||||
pev->view_ofs = Vector ( 0, 0, -32 );// position of the eyes relative to monster's origin.
|
||||
}
|
||||
}
|
||||
|
||||
//=========================================================
|
||||
// Precache - precaches all resources this monster needs
|
||||
//=========================================================
|
||||
void CAlienEgg :: Precache()
|
||||
{
|
||||
PRECACHE_SOUND("gonarch/gon_birth3.wav");
|
||||
PRECACHE_SOUND("common/bodysplat.wav");
|
||||
|
||||
if (pev->model)
|
||||
PRECACHE_MODEL((char*)STRING(pev->model)); //LRC
|
||||
else
|
||||
PRECACHE_MODEL("models/alien_egg.mdl");
|
||||
|
||||
UTIL_PrecacheOther( "monster_headcrab" );
|
||||
}
|
||||
|
||||
|
||||
//=========================================================
|
||||
// RunTask
|
||||
//=========================================================
|
||||
void CAlienEgg :: RunTask ( Task_t *pTask )
|
||||
{
|
||||
switch ( pTask->iTask )
|
||||
{
|
||||
case TASK_WAIT_FOR_MOVEMENT:
|
||||
{
|
||||
TaskComplete();
|
||||
break;
|
||||
}
|
||||
case TASK_RANGE_ATTACK1:
|
||||
case TASK_RANGE_ATTACK2:
|
||||
{
|
||||
if ( m_fSequenceFinished )
|
||||
{
|
||||
TaskComplete();
|
||||
|
||||
Burst();
|
||||
|
||||
m_IdealActivity = ACT_IDLE;
|
||||
}
|
||||
break;
|
||||
}
|
||||
default:
|
||||
{
|
||||
CBaseMonster :: RunTask(pTask);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
void CAlienEgg :: StartTask ( Task_t *pTask )
|
||||
{
|
||||
m_iTaskStatus = TASKSTATUS_RUNNING;
|
||||
|
||||
switch ( pTask->iTask )
|
||||
{
|
||||
case TASK_FACE_ENEMY:
|
||||
{
|
||||
TaskComplete();
|
||||
break;
|
||||
}
|
||||
case TASK_RANGE_ATTACK1:
|
||||
{
|
||||
m_IdealActivity = ACT_RANGE_ATTACK1;
|
||||
CBaseMonster :: StartTask( pTask );
|
||||
break;
|
||||
}
|
||||
default:
|
||||
{
|
||||
CBaseMonster :: StartTask( pTask );
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
//=========================================================
|
||||
// CheckRangeAttack1
|
||||
//=========================================================
|
||||
BOOL CAlienEgg :: CheckRangeAttack1 ( float flDot, float flDist )
|
||||
{
|
||||
if ( flDist <= 128 + (128 * m_iOrientation) )
|
||||
{
|
||||
return TRUE;
|
||||
}
|
||||
return FALSE;
|
||||
}
|
||||
|
||||
int CAlienEgg :: TakeDamage( entvars_t *pevInflictor, entvars_t *pevAttacker, float flDamage, int bitsDamageType )
|
||||
{
|
||||
return CBaseMonster::TakeDamage( pevInflictor, pevAttacker, flDamage, bitsDamageType );
|
||||
}
|
||||
|
||||
void CAlienEgg :: Killed( entvars_t *pevAttacker, int iGib )
|
||||
{
|
||||
// change body
|
||||
pev->body = 1;
|
||||
// spray gibs
|
||||
CGib::SpawnRandomGibs( pev, 6, 0 ); // throw some human gibs.
|
||||
// make sound
|
||||
EMIT_SOUND_DYN( edict(), CHAN_BODY, "common/bodysplat.wav", 1.0, ATTN_IDLE, 0, 100 );
|
||||
// cannot use now
|
||||
SetUse(NULL);
|
||||
|
||||
pev->deadflag = DEAD_DEAD;
|
||||
SetThink(NULL);
|
||||
StopAnimation();
|
||||
if (m_iOrientation == 0)
|
||||
{
|
||||
UTIL_SetSize ( pev, Vector ( pev->mins.x, pev->mins.y, pev->mins.z ), Vector ( pev->maxs.x, pev->maxs.y, pev->mins.z + DEAD_EGG_Z ) );
|
||||
}
|
||||
else
|
||||
{
|
||||
UTIL_SetSize ( pev, Vector ( pev->mins.x, pev->mins.y, pev->maxs.z - DEAD_EGG_Z), Vector ( pev->maxs.x, pev->maxs.y, pev->maxs.z ) );
|
||||
}
|
||||
pev->solid = SOLID_NOT;
|
||||
}
|
||||
|
||||
void CAlienEgg::Burst()
|
||||
{
|
||||
pev->health = 0;
|
||||
Killed(NULL, GIB_NEVER);
|
||||
|
||||
// spawn a headcrab
|
||||
edict_t* pent = CREATE_NAMED_ENTITY( ALLOC_STRING("monster_headcrab") );
|
||||
|
||||
if ( FNullEnt( pent ) )
|
||||
{
|
||||
ALERT ( at_debug, "NULL Ent in Alien Egg!\n" );
|
||||
return;
|
||||
}
|
||||
|
||||
entvars_t* pevCreate = VARS( pent );
|
||||
pevCreate->origin = Center();
|
||||
if (m_iOrientation == 0)
|
||||
{
|
||||
}
|
||||
else
|
||||
{
|
||||
pevCreate->origin.z -= 32;
|
||||
}
|
||||
pevCreate->angles.y = pev->angles.y;
|
||||
pevCreate->angles.z = pev->angles.z;
|
||||
SetBits( pevCreate->spawnflags, SF_MONSTER_FALL_TO_GROUND );
|
||||
|
||||
DispatchSpawn( ENT( pevCreate ) );
|
||||
}
|
||||
|
||||
void CAlienEgg::EggUse( CBaseEntity *pActivator, CBaseEntity *pCaller, USE_TYPE useType, float value )
|
||||
{
|
||||
if (IsAlive())
|
||||
{
|
||||
Burst();
|
||||
}
|
||||
}
|
||||
|
||||
Schedule_t *CAlienEgg :: GetSchedule ( void )
|
||||
{
|
||||
// we can see the enemy
|
||||
if ( HasConditions(bits_COND_CAN_RANGE_ATTACK1) )
|
||||
{
|
||||
return GetScheduleOfType( SCHED_RANGE_ATTACK1 );
|
||||
}
|
||||
|
||||
return GetScheduleOfType ( SCHED_IDLE_STAND );
|
||||
}
|
||||
|
||||
|
|
@ -0,0 +1,34 @@
|
|||
|
||||
#ifndef ALIEN_EGG
|
||||
#define ALIEN_EGG
|
||||
|
||||
|
||||
class CAlienEgg : public CBaseMonster
|
||||
{
|
||||
public:
|
||||
void Spawn( void );
|
||||
void Precache( void );
|
||||
void KeyValue( KeyValueData *pkvd );
|
||||
void RunTask ( Task_t *pTask );
|
||||
void StartTask ( Task_t *pTask );
|
||||
void SetYawSpeed ( void );
|
||||
Vector Center( void );
|
||||
Vector BodyTarget( const Vector &posSrc );
|
||||
int Classify ( void );
|
||||
BOOL CheckRangeAttack1 ( float flDot, float flDist );
|
||||
int TakeDamage( entvars_t *pevInflictor, entvars_t *pevAttacker, float flDamage, int bitsDamageType );
|
||||
virtual void Killed( entvars_t *pevAttacker, int iGib );
|
||||
void Burst();
|
||||
void EXPORT EggUse( CBaseEntity *pActivator, CBaseEntity *pCaller, USE_TYPE useType, float value );
|
||||
virtual Schedule_t *GetSchedule( void );
|
||||
|
||||
virtual int Save( CSave &save );
|
||||
virtual int Restore( CRestore &restore );
|
||||
|
||||
static TYPEDESCRIPTION m_SaveData[];
|
||||
|
||||
int m_iOrientation; // 0 = floor, 1 = ceiling
|
||||
};
|
||||
|
||||
|
||||
#endif
|
|
@ -0,0 +1,188 @@
|
|||
|
||||
#if !defined( OEM_BUILD ) && !defined( HLDEMO_BUILD )
|
||||
|
||||
//=========================================================
|
||||
// monster template
|
||||
//=========================================================
|
||||
#include "extdll.h"
|
||||
#include "util.h"
|
||||
#include "cbase.h"
|
||||
#include "monsters.h"
|
||||
#include "schedule.h"
|
||||
#include "decals.h"
|
||||
#include "weapons.h"
|
||||
#include "game.h"
|
||||
|
||||
|
||||
#include "bm.h"
|
||||
|
||||
extern int gSpitSprite;
|
||||
|
||||
Vector VecCheckSplatToss( entvars_t *pev, const Vector &vecSpot1, Vector vecSpot2, float maxHeight )
|
||||
{
|
||||
TraceResult tr;
|
||||
Vector vecMidPoint;// halfway point between Spot1 and Spot2
|
||||
Vector vecApex;// highest point
|
||||
Vector vecScale;
|
||||
Vector vecGrenadeVel;
|
||||
Vector vecTemp;
|
||||
float flGravity = g_psv_gravity->value;
|
||||
|
||||
// calculate the midpoint and apex of the 'triangle'
|
||||
vecMidPoint = vecSpot1 + (vecSpot2 - vecSpot1) * 0.5;
|
||||
UTIL_TraceLine(vecMidPoint, vecMidPoint + Vector(0,0,maxHeight), ignore_monsters, ENT(pev), &tr);
|
||||
vecApex = tr.vecEndPos;
|
||||
|
||||
UTIL_TraceLine(vecSpot1, vecApex, dont_ignore_monsters, ENT(pev), &tr);
|
||||
if (tr.flFraction != 1.0)
|
||||
{
|
||||
// fail!
|
||||
return g_vecZero;
|
||||
}
|
||||
|
||||
// Don't worry about actually hitting the target, this won't hurt us!
|
||||
|
||||
// How high should the grenade travel (subtract 15 so the grenade doesn't hit the ceiling)?
|
||||
float height = (vecApex.z - vecSpot1.z) - 15;
|
||||
// How fast does the grenade need to travel to reach that height given gravity?
|
||||
float speed = sqrt( 2 * flGravity * height );
|
||||
|
||||
// How much time does it take to get there?
|
||||
float time = speed / flGravity;
|
||||
vecGrenadeVel = (vecSpot2 - vecSpot1);
|
||||
vecGrenadeVel.z = 0;
|
||||
float distance = vecGrenadeVel.Length();
|
||||
|
||||
// Travel half the distance to the target in that time (apex is at the midpoint)
|
||||
vecGrenadeVel = vecGrenadeVel * ( 0.5 / time );
|
||||
// Speed to offset gravity at the desired height
|
||||
vecGrenadeVel.z = speed;
|
||||
|
||||
return vecGrenadeVel;
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
// ---------------------------------
|
||||
//
|
||||
// Mortar
|
||||
//
|
||||
// ---------------------------------
|
||||
void MortarSpray( const Vector &position, const Vector &direction, int spriteModel, int count )
|
||||
{
|
||||
MESSAGE_BEGIN( MSG_PVS, SVC_TEMPENTITY, position );
|
||||
WRITE_BYTE( TE_SPRITE_SPRAY );
|
||||
WRITE_COORD( position.x); // pos
|
||||
WRITE_COORD( position.y);
|
||||
WRITE_COORD( position.z);
|
||||
WRITE_COORD( direction.x); // dir
|
||||
WRITE_COORD( direction.y);
|
||||
WRITE_COORD( direction.z);
|
||||
WRITE_SHORT( spriteModel ); // model
|
||||
WRITE_BYTE ( count ); // count
|
||||
WRITE_BYTE ( 130 ); // speed
|
||||
WRITE_BYTE ( 80 ); // noise ( client will divide by 100 )
|
||||
MESSAGE_END();
|
||||
}
|
||||
|
||||
// UNDONE: right now this is pretty much a copy of the squid spit with minor changes to the way it does damage
|
||||
void CBMortar:: Spawn( void )
|
||||
{
|
||||
pev->movetype = MOVETYPE_TOSS;
|
||||
pev->classname = MAKE_STRING( "bmortar" );
|
||||
|
||||
pev->solid = SOLID_BBOX;
|
||||
pev->rendermode = kRenderTransAlpha;
|
||||
pev->renderamt = 255;
|
||||
|
||||
SET_MODEL(ENT(pev), "sprites/mommaspit.spr");
|
||||
pev->frame = 0;
|
||||
pev->scale = 0.5;
|
||||
|
||||
UTIL_SetSize( pev, Vector( 0, 0, 0), Vector(0, 0, 0) );
|
||||
|
||||
m_maxFrame = (float) MODEL_FRAMES( pev->modelindex ) - 1;
|
||||
pev->dmgtime = gpGlobals->time + 0.4;
|
||||
}
|
||||
|
||||
void CBMortar::Animate( void )
|
||||
{
|
||||
SetNextThink( 0.1 );
|
||||
|
||||
if ( gpGlobals->time > pev->dmgtime )
|
||||
{
|
||||
pev->dmgtime = gpGlobals->time + 0.2;
|
||||
MortarSpray( pev->origin, -pev->velocity.Normalize(), gSpitSprite, 3 );
|
||||
}
|
||||
if ( pev->frame++ )
|
||||
{
|
||||
if ( pev->frame > m_maxFrame )
|
||||
{
|
||||
pev->frame = 0;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
CBMortar *CBMortar::Shoot( edict_t *pOwner, Vector vecStart, Vector vecVelocity )
|
||||
{
|
||||
CBMortar *pSpit = GetClassPtr( (CBMortar *)NULL );
|
||||
pSpit->Spawn();
|
||||
|
||||
UTIL_SetOrigin( pSpit, vecStart );
|
||||
pSpit->pev->velocity = vecVelocity;
|
||||
pSpit->pev->owner = pOwner;
|
||||
pSpit->pev->scale = 2.5;
|
||||
pSpit->SetThink ( Animate );
|
||||
pSpit->SetNextThink( 0.1 );
|
||||
|
||||
return pSpit;
|
||||
}
|
||||
|
||||
|
||||
void CBMortar::Touch( CBaseEntity *pOther )
|
||||
{
|
||||
TraceResult tr;
|
||||
int iPitch;
|
||||
|
||||
// splat sound
|
||||
iPitch = RANDOM_FLOAT( 90, 110 );
|
||||
|
||||
EMIT_SOUND_DYN( ENT(pev), CHAN_VOICE, "bullchicken/bc_acid1.wav", 1, ATTN_NORM, 0, iPitch );
|
||||
|
||||
switch ( RANDOM_LONG( 0, 1 ) )
|
||||
{
|
||||
case 0:
|
||||
EMIT_SOUND_DYN( ENT(pev), CHAN_WEAPON, "bullchicken/bc_spithit1.wav", 1, ATTN_NORM, 0, iPitch );
|
||||
break;
|
||||
case 1:
|
||||
EMIT_SOUND_DYN( ENT(pev), CHAN_WEAPON, "bullchicken/bc_spithit2.wav", 1, ATTN_NORM, 0, iPitch );
|
||||
break;
|
||||
}
|
||||
|
||||
if ( pOther->IsBSPModel() )
|
||||
{
|
||||
|
||||
// make a splat on the wall
|
||||
UTIL_TraceLine( pev->origin, pev->origin + pev->velocity * 10, dont_ignore_monsters, ENT( pev ), &tr );
|
||||
UTIL_DecalTrace(&tr, DECAL_MOMMASPLAT);
|
||||
}
|
||||
else
|
||||
{
|
||||
tr.vecEndPos = pev->origin;
|
||||
tr.vecPlaneNormal = -1 * pev->velocity.Normalize();
|
||||
}
|
||||
// make some flecks
|
||||
MortarSpray( tr.vecEndPos, tr.vecPlaneNormal, gSpitSprite, 24 );
|
||||
|
||||
entvars_t *pevOwner = NULL;
|
||||
if ( pev->owner )
|
||||
pevOwner = VARS(pev->owner);
|
||||
|
||||
RadiusDamage( pev->origin, pev, pevOwner, gSkillData.bigmommaDmgBlast, gSkillData.bigmommaRadiusBlast, CLASS_NONE, DMG_ACID );
|
||||
UTIL_Remove( this );
|
||||
}
|
||||
|
||||
|
||||
|
||||
#endif
|
|
@ -0,0 +1,55 @@
|
|||
|
||||
#ifndef BM_H
|
||||
#define BM_H
|
||||
|
||||
//LRC brought in from animation.h
|
||||
#define ACTIVITY_NOT_AVAILABLE -1
|
||||
|
||||
#define SF_INFOBM_RUN 0x0001
|
||||
#define SF_INFOBM_WAIT 0x0002
|
||||
|
||||
// AI Nodes for Big Momma
|
||||
class CInfoBM : public CPointEntity
|
||||
{
|
||||
public:
|
||||
void Spawn( void );
|
||||
void KeyValue( KeyValueData* pkvd );
|
||||
|
||||
// name in pev->targetname
|
||||
// next in pev->target
|
||||
// radius in pev->scale
|
||||
// health in pev->health
|
||||
// Reach target in pev->message
|
||||
// Reach delay in pev->speed
|
||||
// Reach sequence in pev->netname
|
||||
|
||||
virtual int Save( CSave &save );
|
||||
virtual int Restore( CRestore &restore );
|
||||
static TYPEDESCRIPTION m_SaveData[];
|
||||
|
||||
int m_preSequence;
|
||||
};
|
||||
|
||||
|
||||
class CBMortar : public CBaseEntity
|
||||
{
|
||||
public:
|
||||
void Spawn( void );
|
||||
|
||||
static CBMortar *Shoot( edict_t *pOwner, Vector vecStart, Vector vecVelocity );
|
||||
void Touch( CBaseEntity *pOther );
|
||||
void EXPORT Animate( void );
|
||||
|
||||
virtual int Save( CSave &save );
|
||||
virtual int Restore( CRestore &restore );
|
||||
static TYPEDESCRIPTION m_SaveData[];
|
||||
|
||||
int m_maxFrame;
|
||||
};
|
||||
|
||||
Vector VecCheckSplatToss( entvars_t *pev, const Vector &vecSpot1, Vector vecSpot2, float maxHeight );
|
||||
void MortarSpray( const Vector &position, const Vector &direction, int spriteModel, int count );
|
||||
|
||||
|
||||
|
||||
#endif
|
|
@ -0,0 +1,173 @@
|
|||
/***
|
||||
*
|
||||
* Copyright (c) 1999, 2000 Valve LLC. All rights reserved.
|
||||
*
|
||||
* This product contains software technology licensed from Id
|
||||
* Software, Inc. ("Id Technology"). Id Technology (c) 1996 Id Software, Inc.
|
||||
* All Rights Reserved.
|
||||
*
|
||||
* Use, distribution, and modification of this source code and/or resulting
|
||||
* object code is restricted to non-commercial enhancements to products from
|
||||
* Valve LLC. All other use, distribution, or modification is prohibited
|
||||
* without written permission from Valve LLC.
|
||||
*
|
||||
****/
|
||||
#include "extdll.h"
|
||||
#include "util.h"
|
||||
#include "cbase.h"
|
||||
#include "monsters.h"
|
||||
#include "weapons.h"
|
||||
#include "nodes.h"
|
||||
#include "player.h"
|
||||
#include "items.h"
|
||||
#include "gamerules.h"
|
||||
|
||||
extern int gmsgItemPickup;
|
||||
|
||||
#define SF_BOOK_FIREONCE 0x00000001
|
||||
|
||||
#define NUM_BOOK_PAGES 3
|
||||
|
||||
class CBook : public CBaseToggle
|
||||
{
|
||||
public:
|
||||
void Spawn( void );
|
||||
void Precache( void );
|
||||
void Use( CBaseEntity *pActivator, CBaseEntity *pCaller, USE_TYPE useType, float value );
|
||||
virtual int ObjectCaps( void ) { return (CBaseToggle :: ObjectCaps() | FCAP_ONOFF_USE) & ~FCAP_ACROSS_TRANSITION; }
|
||||
void EXPORT ReadThink( void );
|
||||
|
||||
virtual int Save( CSave &save );
|
||||
virtual int Restore( CRestore &restore );
|
||||
|
||||
static TYPEDESCRIPTION m_SaveData[];
|
||||
|
||||
void KeyValue( KeyValueData *pkvd );
|
||||
|
||||
int m_iszImage[NUM_BOOK_PAGES];
|
||||
float m_fImageDuration;
|
||||
|
||||
int m_iImage;
|
||||
|
||||
BOOL m_bHasFired;
|
||||
};
|
||||
|
||||
|
||||
LINK_ENTITY_TO_CLASS( item_book, CBook );
|
||||
|
||||
TYPEDESCRIPTION CBook::m_SaveData[] =
|
||||
{
|
||||
DEFINE_FIELD( CBook, m_iImage, FIELD_INTEGER ),
|
||||
DEFINE_FIELD( CBook, m_iszImage[0], FIELD_STRING ),
|
||||
DEFINE_FIELD( CBook, m_iszImage[1], FIELD_STRING ),
|
||||
DEFINE_FIELD( CBook, m_iszImage[2], FIELD_STRING ),
|
||||
DEFINE_FIELD( CBook, m_fImageDuration, FIELD_FLOAT ),
|
||||
DEFINE_FIELD( CBook, m_bHasFired, FIELD_BOOLEAN ),
|
||||
};
|
||||
|
||||
|
||||
IMPLEMENT_SAVERESTORE( CBook, CBaseToggle);
|
||||
|
||||
void CBook :: Spawn( void )
|
||||
{
|
||||
Precache( );
|
||||
pev->solid = SOLID_BBOX;
|
||||
pev->movetype = MOVETYPE_NONE;
|
||||
pev->takedamage = DAMAGE_NO;
|
||||
pev->sequence = 0;
|
||||
pev->frame = 0;
|
||||
|
||||
// initialise the book pages
|
||||
m_iImage = -1;
|
||||
|
||||
// we haven't fired yet
|
||||
m_bHasFired = FALSE;
|
||||
|
||||
UTIL_SetOrigin(this, pev->origin); // set size and link into world
|
||||
// UTIL_SetSize(pev, Vector(-16,-16,0), Vector(16,16,16));
|
||||
SET_MODEL(ENT(pev), STRING(pev->model));
|
||||
|
||||
ResetSequenceInfo( );
|
||||
pev->frame = 0;
|
||||
// NB: FallInit is a weapons function, so make sure the book is on a surface in Worldcraft,
|
||||
// since it will not fall (i.e. it is not affected by gravity!).
|
||||
if (DROP_TO_FLOOR(ENT(pev)) == 0)
|
||||
{
|
||||
ALERT(at_error, "Book %s fell out of level at %f,%f,%f", STRING( pev->classname ), pev->origin.x, pev->origin.y, pev->origin.z);
|
||||
UTIL_Remove( this );
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
void CBook::Precache( void )
|
||||
{
|
||||
char* sz = (char*)STRING(pev->model);
|
||||
PRECACHE_MODEL(sz);
|
||||
}
|
||||
|
||||
void CBook::Use( CBaseEntity *pActivator, CBaseEntity *pCaller, USE_TYPE useType, float value )
|
||||
{
|
||||
// we are already reading this book
|
||||
if (m_iImage >= 0) return;
|
||||
|
||||
// write a message to the client to display the picture
|
||||
m_iImage = 0;
|
||||
UTIL_ReadBook(STRING(m_iszImage[m_iImage]));
|
||||
SetThink(ReadThink);
|
||||
SetNextThink(m_fImageDuration);
|
||||
|
||||
// if we haven't fired or we are allowed to fire more than once, fire target
|
||||
if (!m_bHasFired || !(pev->spawnflags & SF_BOOK_FIREONCE))
|
||||
{
|
||||
SUB_UseTargets( NULL, USE_TOGGLE, 0 );
|
||||
m_bHasFired = TRUE;
|
||||
}
|
||||
}
|
||||
|
||||
void CBook::KeyValue( KeyValueData *pkvd )
|
||||
{
|
||||
if (FStrEq(pkvd->szKeyName, "image1"))
|
||||
{
|
||||
m_iszImage[0] = ALLOC_STRING( pkvd->szValue );
|
||||
pkvd->fHandled = TRUE;
|
||||
}
|
||||
else if (FStrEq(pkvd->szKeyName, "image2"))
|
||||
{
|
||||
m_iszImage[1] = ALLOC_STRING( pkvd->szValue );
|
||||
pkvd->fHandled = TRUE;
|
||||
}
|
||||
else if (FStrEq(pkvd->szKeyName, "image3"))
|
||||
{
|
||||
m_iszImage[2] = ALLOC_STRING( pkvd->szValue );
|
||||
pkvd->fHandled = TRUE;
|
||||
}
|
||||
else if (FStrEq(pkvd->szKeyName, "imageduration"))
|
||||
{
|
||||
m_fImageDuration = atof(pkvd->szValue);
|
||||
pkvd->fHandled = TRUE;
|
||||
}
|
||||
else
|
||||
CBaseToggle::KeyValue( pkvd );
|
||||
}
|
||||
|
||||
void CBook::ReadThink( )
|
||||
{
|
||||
// turn the page
|
||||
m_iImage++;
|
||||
|
||||
if (m_iImage == NUM_BOOK_PAGES || FStringNull(m_iszImage[m_iImage]))
|
||||
{
|
||||
// we have finished the book
|
||||
UTIL_ReadBook("");
|
||||
DontThink();
|
||||
m_iImage = -1;
|
||||
}
|
||||
else
|
||||
{
|
||||
// clear the page
|
||||
UTIL_ReadBook("");
|
||||
// show the next page
|
||||
UTIL_ReadBook(STRING(m_iszImage[m_iImage]));
|
||||
SetNextThink(m_fImageDuration);
|
||||
}
|
||||
}
|
File diff suppressed because it is too large
Load Diff
|
@ -0,0 +1,88 @@
|
|||
|
||||
#ifndef BUTLER_H
|
||||
#define BUTLER_H
|
||||
|
||||
class CButler : public CTalkMonster
|
||||
{
|
||||
public:
|
||||
void Spawn( void );
|
||||
void Precache( void );
|
||||
|
||||
void SetYawSpeed( void );
|
||||
int Classify ( void );
|
||||
void HandleAnimEvent( MonsterEvent_t *pEvent );
|
||||
void RunTask( Task_t *pTask );
|
||||
void StartTask( Task_t *pTask );
|
||||
int ObjectCaps( void ) { return CTalkMonster :: ObjectCaps() | FCAP_IMPULSE_USE; }
|
||||
int TakeDamage( entvars_t* pevInflictor, entvars_t* pevAttacker, float flDamage, int bitsDamageType);
|
||||
virtual int FriendNumber( int arrayNumber );
|
||||
void SetActivity ( Activity newActivity );
|
||||
Activity GetStoppedActivity( void );
|
||||
int ISoundMask( void );
|
||||
void DeclineFollowing( void );
|
||||
|
||||
float CoverRadius( void ) { return 1200; } // Need more room for cover because Butlers want to get far away!
|
||||
BOOL DisregardEnemy( CBaseEntity *pEnemy ) { return !pEnemy->IsAlive() || (gpGlobals->time - m_fearTime) > 15; }
|
||||
|
||||
void Scream( 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 Killed( entvars_t *pevAttacker, int iGib );
|
||||
|
||||
virtual int Save( CSave &save );
|
||||
virtual int Restore( CRestore &restore );
|
||||
static TYPEDESCRIPTION m_SaveData[];
|
||||
|
||||
CUSTOM_SCHEDULES;
|
||||
|
||||
private:
|
||||
float m_painTime;
|
||||
float m_fearTime;
|
||||
};
|
||||
|
||||
|
||||
class CDeadButler : public CBaseMonster
|
||||
{
|
||||
public:
|
||||
void Spawn( void );
|
||||
int Classify ( void ) { return CLASS_HUMAN_PASSIVE; }
|
||||
|
||||
void KeyValue( KeyValueData *pkvd );
|
||||
int m_iPose;// which sequence to display
|
||||
static char *m_szPoses[7];
|
||||
};
|
||||
|
||||
|
||||
class CSittingButler : public CButler // kdb: changed from public CBaseMonster so he can speak
|
||||
{
|
||||
public:
|
||||
void Spawn( void );
|
||||
void Precache( void );
|
||||
|
||||
void EXPORT SittingThink( void );
|
||||
int Classify ( void );
|
||||
virtual int Save( CSave &save );
|
||||
virtual int Restore( CRestore &restore );
|
||||
static TYPEDESCRIPTION m_SaveData[];
|
||||
|
||||
virtual void SetAnswerQuestion( CTalkMonster *pSpeaker );
|
||||
int FriendNumber( int arrayNumber );
|
||||
|
||||
int FIdleSpeak ( void );
|
||||
int m_baseSequence;
|
||||
int m_headTurn;
|
||||
float m_flResponseDelay;
|
||||
};
|
||||
|
||||
|
||||
|
||||
#endif
|
|
@ -0,0 +1,422 @@
|
|||
/***
|
||||
*
|
||||
* Copyright (c) 1996-2001, Valve LLC. All rights reserved.
|
||||
*
|
||||
* This product contains software technology licensed from Id
|
||||
* Software, Inc. ("Id Technology"). Id Technology (c) 1996 Id Software, Inc.
|
||||
* All Rights Reserved.
|
||||
*
|
||||
* Use, distribution, and modification of this source code and/or resulting
|
||||
* object code is restricted to non-commercial enhancements to products from
|
||||
* Valve LLC. All other use, distribution, or modification is prohibited
|
||||
* without written permission from Valve LLC.
|
||||
*
|
||||
****/
|
||||
#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"
|
||||
|
||||
|
||||
// set the weapon sounds
|
||||
// set the weapon slot
|
||||
// set the weapon crosshairs
|
||||
|
||||
#define CHARM_PRIMARY_FIRE_VOLUME 450// how loud charm is when cast
|
||||
|
||||
class CCharmedMonster : public CBaseEntity
|
||||
{
|
||||
public:
|
||||
void Spawn( void );
|
||||
|
||||
virtual int Save( CSave &save );
|
||||
virtual int Restore( CRestore &restore );
|
||||
static TYPEDESCRIPTION m_SaveData[];
|
||||
|
||||
void Initialise(CBaseMonster* pCharmedMonster);
|
||||
void EXPORT CharmThink(void);
|
||||
|
||||
int m_iOriginalClass;
|
||||
CBaseMonster* m_pCharmedMonster; // this is the thing being charmed
|
||||
};
|
||||
|
||||
TYPEDESCRIPTION CCharmedMonster::m_SaveData[] =
|
||||
{
|
||||
DEFINE_FIELD( CCharmedMonster, m_iOriginalClass, FIELD_INTEGER),
|
||||
DEFINE_FIELD( CCharmedMonster, m_pCharmedMonster, FIELD_CLASSPTR),
|
||||
};
|
||||
|
||||
IMPLEMENT_SAVERESTORE( CCharmedMonster, CBaseEntity );
|
||||
|
||||
LINK_ENTITY_TO_CLASS(charmedmonster, CCharmedMonster);
|
||||
|
||||
void CCharmedMonster::Spawn()
|
||||
{
|
||||
pev->movetype = MOVETYPE_NONE;
|
||||
pev->classname = MAKE_STRING( "charmedmonster" );
|
||||
|
||||
pev->solid = SOLID_NOT;
|
||||
|
||||
UTIL_SetSize( pev, Vector( 0, 0, 0), Vector(0, 0, 0) );
|
||||
|
||||
DontThink();
|
||||
}
|
||||
|
||||
void CCharmedMonster::Initialise(CBaseMonster* pCharmedMonster)
|
||||
{
|
||||
m_pCharmedMonster = pCharmedMonster;
|
||||
|
||||
m_iOriginalClass = m_pCharmedMonster->Classify();
|
||||
m_pCharmedMonster->m_iClass = CLASS_PLAYER_ALLY;
|
||||
|
||||
m_pCharmedMonster->m_hEnemy = NULL;
|
||||
|
||||
SetThink(CharmThink);
|
||||
|
||||
SetNextThink(3000.0 / (float)m_pCharmedMonster->pev->max_health);
|
||||
}
|
||||
|
||||
void CCharmedMonster::CharmThink()
|
||||
{
|
||||
m_pCharmedMonster->m_iClass = m_iOriginalClass;
|
||||
|
||||
UTIL_Remove(this);
|
||||
}
|
||||
|
||||
|
||||
/////////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
enum charm_e {
|
||||
CHARM_OPEN = 0,
|
||||
CHARM_IDLE1,
|
||||
CHARM_IDLE2,
|
||||
CHARM_IDLE3,
|
||||
CHARM_CAST,
|
||||
CHARM_CLOSE
|
||||
};
|
||||
|
||||
class CCharm : public CBasePlayerWeapon
|
||||
{
|
||||
public:
|
||||
|
||||
int Save( CSave &save );
|
||||
int Restore( CRestore &restore );
|
||||
static TYPEDESCRIPTION m_SaveData[];
|
||||
|
||||
void Spawn( void );
|
||||
void Precache( void );
|
||||
int iItemSlot( void ) { return 4; }
|
||||
int GetItemInfo(ItemInfo *p);
|
||||
int AddToPlayer( CBasePlayer *pPlayer );
|
||||
|
||||
BOOL Deploy( void );
|
||||
void Holster( int skiplocal = 0 );
|
||||
|
||||
void PrimaryAttack( void );
|
||||
void SecondaryAttack( void );
|
||||
void WeaponIdle( void );
|
||||
void ZapPowerUp( void );
|
||||
void ZapShoot( void );
|
||||
void ZapDone( void );
|
||||
|
||||
int m_fInAttack;
|
||||
|
||||
void ClearBeam( void );
|
||||
void ZapBeam();
|
||||
|
||||
BOOL IsMonsterCharmed (CBaseEntity* pEntity);
|
||||
|
||||
virtual BOOL UseDecrement( void )
|
||||
{
|
||||
#if defined( CLIENT_WEAPONS )
|
||||
return TRUE;
|
||||
#else
|
||||
return FALSE;
|
||||
#endif
|
||||
}
|
||||
|
||||
private:
|
||||
|
||||
// the beam effect
|
||||
CBeam* m_pBeam;
|
||||
};
|
||||
|
||||
LINK_ENTITY_TO_CLASS( weapon_charm, CCharm );
|
||||
|
||||
TYPEDESCRIPTION CCharm::m_SaveData[] =
|
||||
{
|
||||
DEFINE_FIELD( CCharm, m_fInAttack, FIELD_INTEGER ),
|
||||
};
|
||||
|
||||
IMPLEMENT_SAVERESTORE( CCharm, CBasePlayerWeapon );
|
||||
|
||||
void CCharm::Spawn( )
|
||||
{
|
||||
Precache( );
|
||||
m_iId = WEAPON_CHARM;
|
||||
SET_MODEL(ENT(pev), "models/w_charm.mdl");
|
||||
m_iClip = -1;
|
||||
|
||||
FallInit();// get ready to fall down.
|
||||
}
|
||||
|
||||
|
||||
void CCharm::Precache( void )
|
||||
{
|
||||
PRECACHE_MODEL("models/w_charm.mdl");
|
||||
PRECACHE_MODEL("models/v_charm.mdl");
|
||||
//PRECACHE_MODEL("models/p_charm.mdl");
|
||||
|
||||
PRECACHE_MODEL( "sprites/xbeam3.spr" );
|
||||
|
||||
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");
|
||||
}
|
||||
|
||||
int CCharm::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 CCharm::GetItemInfo(ItemInfo *p)
|
||||
{
|
||||
p->pszName = STRING(pev->classname);
|
||||
p->pszAmmo1 = NULL;
|
||||
p->iMaxAmmo1 = -1;
|
||||
p->pszAmmo2 = NULL;
|
||||
p->iMaxAmmo2 = -1;
|
||||
p->iMaxClip = WEAPON_NOCLIP;
|
||||
p->iSlot = 4;
|
||||
p->iPosition = 5;
|
||||
p->iId = m_iId = WEAPON_CHARM;
|
||||
p->iFlags = 0;
|
||||
p->iWeight = CHARM_WEIGHT;
|
||||
|
||||
return 1;
|
||||
}
|
||||
|
||||
BOOL CCharm::Deploy( )
|
||||
{
|
||||
return DefaultDeploy( "models/v_charm.mdl", "", CHARM_OPEN, "charm" );
|
||||
}
|
||||
|
||||
void CCharm::Holster( int skiplocal /* = 0 */ )
|
||||
{
|
||||
m_pPlayer->m_flNextAttack = UTIL_WeaponTimeBase() + 0.5;
|
||||
|
||||
SendWeaponAnim( CHARM_CLOSE );
|
||||
m_fInAttack = 0;
|
||||
}
|
||||
|
||||
|
||||
void CCharm::PrimaryAttack()
|
||||
{
|
||||
if (m_fInAttack != 0)
|
||||
{
|
||||
if ( m_fInAttack == 1 )
|
||||
{
|
||||
ZapShoot();
|
||||
}
|
||||
else // == 2
|
||||
{
|
||||
ZapDone();
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
||||
// don't fire underwater
|
||||
if (m_pPlayer->pev->waterlevel == 3)
|
||||
{
|
||||
PlayEmptySound( );
|
||||
m_flNextSecondaryAttack = m_flNextPrimaryAttack = gpGlobals->time + 0.15;
|
||||
return;
|
||||
}
|
||||
|
||||
/*
|
||||
if (m_pPlayer->m_rgAmmo[m_iPrimaryAmmoType] < 1)
|
||||
{
|
||||
PlayEmptySound( );
|
||||
m_pPlayer->m_flNextAttack = UTIL_WeaponTimeBase() + 0.5;
|
||||
return;
|
||||
}
|
||||
*/
|
||||
|
||||
ZapPowerUp();
|
||||
}
|
||||
|
||||
void CCharm::SecondaryAttack()
|
||||
{
|
||||
}
|
||||
|
||||
void CCharm::ZapPowerUp()
|
||||
{
|
||||
m_fInAttack = 1;
|
||||
|
||||
EMIT_SOUND_DYN( ENT(pev), CHAN_WEAPON, "debris/zap4.wav", 1, ATTN_NORM, 0, 150 );
|
||||
|
||||
m_flTimeWeaponIdle = gpGlobals->time + 0.2;
|
||||
}
|
||||
|
||||
|
||||
void CCharm::ZapShoot()
|
||||
{
|
||||
ClearBeam( );
|
||||
|
||||
UTIL_MakeAimVectors( pev->angles );
|
||||
|
||||
ZapBeam();
|
||||
|
||||
EMIT_SOUND_DYN( ENT(pev), CHAN_WEAPON, "hassault/hw_shoot1.wav", 1, ATTN_NORM, 0, RANDOM_LONG( 130, 160 ) );
|
||||
|
||||
m_flTimeWeaponIdle = gpGlobals->time + 0.2;
|
||||
m_flNextPrimaryAttack = gpGlobals->time + 0.2;
|
||||
|
||||
m_fInAttack = 2;
|
||||
}
|
||||
|
||||
void CCharm::ZapDone()
|
||||
{
|
||||
ClearBeam( );
|
||||
|
||||
m_fInAttack = 0;
|
||||
m_flTimeWeaponIdle = gpGlobals->time + 0.4;
|
||||
m_flNextPrimaryAttack = gpGlobals->time + 0.4;
|
||||
}
|
||||
|
||||
void CCharm::WeaponIdle( void )
|
||||
{
|
||||
ResetEmptySound( );
|
||||
|
||||
if ( m_flTimeWeaponIdle > gpGlobals->time )
|
||||
return;
|
||||
|
||||
if (m_fInAttack != 0)
|
||||
{
|
||||
if ( m_fInAttack == 1 )
|
||||
{
|
||||
ZapShoot();
|
||||
}
|
||||
else // == 2
|
||||
{
|
||||
ZapDone();
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
int iAnim;
|
||||
float flRand = RANDOM_FLOAT(0, 1);
|
||||
if (flRand <= 0.4)
|
||||
{
|
||||
iAnim = CHARM_IDLE1;
|
||||
m_flTimeWeaponIdle = gpGlobals->time + RANDOM_FLOAT(2,3);
|
||||
}
|
||||
else if (flRand <= 0.75)
|
||||
{
|
||||
iAnim = CHARM_IDLE2;
|
||||
m_flTimeWeaponIdle = gpGlobals->time + 3;
|
||||
}
|
||||
else
|
||||
{
|
||||
iAnim = CHARM_IDLE3;
|
||||
m_flTimeWeaponIdle = gpGlobals->time + 3;
|
||||
}
|
||||
|
||||
return;
|
||||
SendWeaponAnim( iAnim );
|
||||
}
|
||||
}
|
||||
|
||||
//=========================================================
|
||||
// ZapBeam - heavy damage directly forward
|
||||
//=========================================================
|
||||
void CCharm :: ZapBeam()
|
||||
{
|
||||
TraceResult tr;
|
||||
CBaseEntity *pEntity;
|
||||
|
||||
// this should be attachment 0 (???)
|
||||
UTIL_MakeVectors( m_pPlayer->pev->v_angle + m_pPlayer->pev->punchangle );
|
||||
Vector vecAiming = gpGlobals->v_forward;
|
||||
Vector vecSrc = m_pPlayer->GetGunPosition( );
|
||||
Vector vecDest = vecSrc + vecAiming * 2048;
|
||||
UTIL_TraceLine( vecSrc, vecDest, dont_ignore_monsters, m_pPlayer->edict(), &tr );
|
||||
|
||||
m_pBeam = CBeam::BeamCreate( "sprites/xbeam3.spr", 20 );
|
||||
if (!m_pBeam)
|
||||
return;
|
||||
|
||||
m_pBeam->PointEntInit( tr.vecEndPos, m_pPlayer->entindex( ) );
|
||||
m_pBeam->SetEndAttachment( 1 );
|
||||
m_pBeam->SetColor( 255, 180, 96 );
|
||||
m_pBeam->SetBrightness( 255 );
|
||||
m_pBeam->SetNoise( 0 );
|
||||
m_pBeam->SetScrollRate( 20 );
|
||||
m_pBeam->pev->spawnflags |= SF_BEAM_TEMPORARY; // Flag these to be destroyed on save/restore or level transition
|
||||
|
||||
pEntity = CBaseEntity::Instance(tr.pHit);
|
||||
if (pEntity != NULL)
|
||||
{
|
||||
// is it a monster
|
||||
if (pEntity->pev->flags & FL_MONSTER)
|
||||
{
|
||||
// we cannot charm a monster who is already charmed
|
||||
if (!IsMonsterCharmed(pEntity))
|
||||
{
|
||||
// create a charmed monster entity
|
||||
CCharmedMonster* pCharm = (CCharmedMonster*)CBaseEntity::Create( "charmedmonster", pEntity->pev->origin, pEntity->pev->angles, pEntity->edict() );
|
||||
|
||||
// set the monster
|
||||
pCharm->Initialise((CBaseMonster*)pEntity);
|
||||
}
|
||||
}
|
||||
}
|
||||
UTIL_EmitAmbientSound( ENT(pev), tr.vecEndPos, "weapons/electro4.wav", 0.5, ATTN_NORM, 0, RANDOM_LONG( 140, 160 ) );
|
||||
}
|
||||
|
||||
BOOL CCharm :: IsMonsterCharmed(CBaseEntity* pEntity)
|
||||
{
|
||||
CCharmedMonster* pCharm = NULL;
|
||||
pCharm = (CCharmedMonster*)UTIL_FindEntityByClassname(pCharm, "charmedmonster");
|
||||
while (pCharm != NULL)
|
||||
{
|
||||
if (pCharm->m_pCharmedMonster == pEntity) return TRUE;
|
||||
|
||||
pCharm = (CCharmedMonster*)UTIL_FindEntityByClassname(pCharm, "charmedmonster");
|
||||
}
|
||||
|
||||
return FALSE;
|
||||
}
|
||||
|
||||
void CCharm :: ClearBeam( )
|
||||
{
|
||||
if (m_pBeam)
|
||||
{
|
||||
UTIL_Remove( m_pBeam );
|
||||
m_pBeam = NULL;
|
||||
}
|
||||
|
||||
STOP_SOUND( ENT(pev), CHAN_WEAPON, "debris/zap4.wav" );
|
||||
}
|
||||
|
||||
|
||||
#endif
|
File diff suppressed because it is too large
Load Diff
|
@ -0,0 +1,26 @@
|
|||
|
||||
#ifndef CROWBAR_H
|
||||
#define CROWBAR_H
|
||||
|
||||
|
||||
class CCrowbar : public CBasePlayerWeapon
|
||||
{
|
||||
public:
|
||||
void Spawn( void );
|
||||
void Precache( void );
|
||||
int iItemSlot( void ) { return 1; }
|
||||
void EXPORT SwingAgain( void );
|
||||
void EXPORT Smack( void );
|
||||
int GetItemInfo(ItemInfo *p);
|
||||
void FindHullIntersection( const Vector &vecSrc, TraceResult &tr, float *mins, float *maxs, edict_t *pEntity );
|
||||
|
||||
void PrimaryAttack( void );
|
||||
int Swing( int fFirst );
|
||||
BOOL Deploy( void );
|
||||
void Holster( int skiplocal = 0 );
|
||||
int m_iSwing;
|
||||
TraceResult m_trHit;
|
||||
};
|
||||
|
||||
|
||||
#endif
|
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
|
@ -0,0 +1,79 @@
|
|||
|
||||
#ifndef CULTIST_H
|
||||
#define CULTIST_H
|
||||
|
||||
class CCultist : public CSquadMonster
|
||||
{
|
||||
public:
|
||||
void Spawn( void );
|
||||
void Precache( void );
|
||||
void SetYawSpeed ( void );
|
||||
int Classify ( void );
|
||||
int ISoundMask ( void );
|
||||
void HandleAnimEvent( MonsterEvent_t *pEvent );
|
||||
BOOL FCanCheckAttacks ( void );
|
||||
BOOL CheckMeleeAttack1 ( float flDot, float flDist );
|
||||
BOOL CheckRangeAttack1 ( float flDot, float flDist );
|
||||
BOOL CheckRangeAttack2 ( float flDot, float flDist );
|
||||
void CheckAmmo ( void );
|
||||
void SetActivity ( Activity NewActivity );
|
||||
void StartTask ( Task_t *pTask );
|
||||
void RunTask ( Task_t *pTask );
|
||||
void DeathSound( void );
|
||||
void PainSound( void );
|
||||
void IdleSound ( void );
|
||||
Vector GetGunPosition( void );
|
||||
void Shoot ( void );
|
||||
void Shotgun ( void );
|
||||
void PrescheduleThink ( void );
|
||||
void GibMonster( void );
|
||||
void SpeakSentence( void );
|
||||
|
||||
int Save( CSave &save );
|
||||
int Restore( CRestore &restore );
|
||||
|
||||
CBaseEntity *Kick( void );
|
||||
Schedule_t *GetSchedule( void );
|
||||
Schedule_t *GetScheduleOfType ( int Type );
|
||||
void TraceAttack( entvars_t *pevAttacker, float flDamage, Vector vecDir, TraceResult *ptr, int bitsDamageType);
|
||||
int TakeDamage( entvars_t *pevInflictor, entvars_t *pevAttacker, float flDamage, int bitsDamageType );
|
||||
|
||||
BOOL FOkToSpeak( void );
|
||||
void JustSpoke( void );
|
||||
|
||||
CUSTOM_SCHEDULES;
|
||||
static TYPEDESCRIPTION m_SaveData[];
|
||||
|
||||
float m_flNextPainTime;
|
||||
float m_flLastEnemySightTime;
|
||||
|
||||
BOOL m_fStanding;
|
||||
BOOL m_fFirstEncounter;// only put on the handsign show in the squad's first encounter.
|
||||
int m_cClipSize;
|
||||
|
||||
int m_voicePitch;
|
||||
|
||||
int m_iBrassShell;
|
||||
int m_iShotgunShell;
|
||||
|
||||
int m_iSentence;
|
||||
|
||||
static const char *pCultistSentences[];
|
||||
};
|
||||
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
class CDeadCultist : public CBaseMonster
|
||||
{
|
||||
public:
|
||||
void Spawn( void );
|
||||
int Classify ( void ) { return CLASS_HUMAN_MILITARY; }
|
||||
|
||||
void KeyValue( KeyValueData *pkvd );
|
||||
|
||||
int m_iPose;// which sequence to display -- temporary, don't need to save
|
||||
static char *m_szPoses[3];
|
||||
};
|
||||
|
||||
|
||||
#endif
|
|
@ -0,0 +1,99 @@
|
|||
/***
|
||||
*
|
||||
* Copyright (c) 1999, 2000 Valve LLC. All rights reserved.
|
||||
*
|
||||
* This product contains software technology licensed from Id
|
||||
* Software, Inc. ("Id Technology"). Id Technology (c) 1996 Id Software, Inc.
|
||||
* All Rights Reserved.
|
||||
*
|
||||
* This source code contains proprietary and confidential information of
|
||||
* Valve LLC and its suppliers. Access to this code is restricted to
|
||||
* persons who have executed a written SDK license with Valve. Any access,
|
||||
* use or distribution of this code by or to any unlicensed person is illegal.
|
||||
*
|
||||
****/
|
||||
#ifndef DEFAULTAI_H
|
||||
#define DEFAULTAI_H
|
||||
|
||||
//=========================================================
|
||||
// Failed
|
||||
//=========================================================
|
||||
extern Schedule_t slFail[];
|
||||
|
||||
//=========================================================
|
||||
// Idle Schedules
|
||||
//=========================================================
|
||||
extern Schedule_t slIdleStand[];
|
||||
extern Schedule_t slIdleTrigger[];
|
||||
extern Schedule_t slIdleWalk[];
|
||||
|
||||
//=========================================================
|
||||
// Wake Schedules
|
||||
//=========================================================
|
||||
extern Schedule_t slWakeAngry[];
|
||||
|
||||
//=========================================================
|
||||
// AlertTurn Schedules
|
||||
//=========================================================
|
||||
extern Schedule_t slAlertFace[];
|
||||
|
||||
//=========================================================
|
||||
// AlertIdle Schedules
|
||||
//=========================================================
|
||||
extern Schedule_t slAlertStand[];
|
||||
|
||||
//=========================================================
|
||||
// CombatIdle Schedule
|
||||
//=========================================================
|
||||
extern Schedule_t slCombatStand[];
|
||||
|
||||
//=========================================================
|
||||
// CombatFace Schedule
|
||||
//=========================================================
|
||||
extern Schedule_t slCombatFace[];
|
||||
|
||||
//=========================================================
|
||||
// reload schedule
|
||||
//=========================================================
|
||||
extern Schedule_t slReload[];
|
||||
|
||||
//=========================================================
|
||||
// Attack Schedules
|
||||
//=========================================================
|
||||
|
||||
extern Schedule_t slRangeAttack1[];
|
||||
extern Schedule_t slRangeAttack2[];
|
||||
|
||||
extern Schedule_t slTakeCoverFromBestSound[];
|
||||
|
||||
// primary melee attack
|
||||
extern Schedule_t slMeleeAttack[];
|
||||
|
||||
// Chase enemy schedule
|
||||
extern Schedule_t slChaseEnemy[];
|
||||
extern Schedule_t slChaseEnemyLKP[];
|
||||
|
||||
//=========================================================
|
||||
// small flinch, used when a relatively minor bit of damage
|
||||
// is inflicted.
|
||||
//=========================================================
|
||||
extern Schedule_t slSmallFlinch[];
|
||||
|
||||
//=========================================================
|
||||
// Die!
|
||||
//=========================================================
|
||||
extern Schedule_t slDie[];
|
||||
|
||||
//=========================================================
|
||||
// Universal Error Schedule
|
||||
//=========================================================
|
||||
extern Schedule_t slError[];
|
||||
|
||||
//=========================================================
|
||||
// Scripted sequences
|
||||
//=========================================================
|
||||
extern Schedule_t slWalkToScript[];
|
||||
extern Schedule_t slRunToScript[];
|
||||
extern Schedule_t slWaitScript[];
|
||||
|
||||
#endif // DEFAULTAI_H
|
|
@ -0,0 +1,411 @@
|
|||
/***
|
||||
*
|
||||
* Copyright (c) 1999, 2000 Valve LLC. All rights reserved.
|
||||
*
|
||||
* This product contains software technology licensed from Id
|
||||
* Software, Inc. ("Id Technology"). Id Technology (c) 1996 Id Software, Inc.
|
||||
* All Rights Reserved.
|
||||
*
|
||||
* Use, distribution, and modification of this source code and/or resulting
|
||||
* object code is restricted to non-commercial enhancements to products from
|
||||
* Valve LLC. All other use, distribution, or modification is prohibited
|
||||
* without written permission from Valve LLC.
|
||||
*
|
||||
****/
|
||||
#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"
|
||||
|
||||
#include "drainlife.h"
|
||||
|
||||
#define DRAINLIFE_PRIMARY_VOLUME 250
|
||||
#define DRAINLIFE_BEAM_SPRITE "sprites/xbeam1.spr"
|
||||
#define DRAINLIFE_FLARE_SPRITE "sprites/XSpark1.spr"
|
||||
#define DRAINLIFE_SOUND_OFF "weapons/egon_off1.wav"
|
||||
#define DRAINLIFE_SOUND_RUN "weapons/egon_run3.wav"
|
||||
#define DRAINLIFE_SOUND_STARTUP "weapons/egon_windup2.wav"
|
||||
|
||||
|
||||
enum DrainLife_e {
|
||||
DRAINLIFE_OPEN = 0,
|
||||
DRAINLIFE_IDLE1,
|
||||
DRAINLIFE_IDLE2,
|
||||
DRAINLIFE_IDLE3,
|
||||
DRAINLIFE_CAST,
|
||||
DRAINLIFE_CLOSE
|
||||
};
|
||||
|
||||
|
||||
LINK_ENTITY_TO_CLASS( weapon_drainlife, CDrainLife );
|
||||
|
||||
TYPEDESCRIPTION CDrainLife::m_SaveData[] =
|
||||
{
|
||||
DEFINE_FIELD( CDrainLife, m_pBeam, FIELD_CLASSPTR ),
|
||||
DEFINE_FIELD( CDrainLife, m_pNoise, FIELD_CLASSPTR ),
|
||||
DEFINE_FIELD( CDrainLife, m_pSprite, FIELD_CLASSPTR ),
|
||||
DEFINE_FIELD( CDrainLife, m_shootTime, FIELD_TIME ),
|
||||
DEFINE_FIELD( CDrainLife, m_fireState, FIELD_INTEGER ),
|
||||
DEFINE_FIELD( CDrainLife, m_flAmmoUseTime, FIELD_TIME ),
|
||||
};
|
||||
IMPLEMENT_SAVERESTORE( CDrainLife, CBasePlayerWeapon );
|
||||
|
||||
|
||||
void CDrainLife::Spawn( )
|
||||
{
|
||||
Precache( );
|
||||
m_iId = WEAPON_DRAINLIFE;
|
||||
SET_MODEL(ENT(pev), "models/w_drainlife.mdl");
|
||||
m_iClip = -1;
|
||||
|
||||
FallInit();// get ready to fall down.
|
||||
}
|
||||
|
||||
|
||||
void CDrainLife::Precache( void )
|
||||
{
|
||||
PRECACHE_MODEL("models/w_drainlife.mdl");
|
||||
PRECACHE_MODEL("models/v_drainlife.mdl");
|
||||
// PRECACHE_MODEL("models/p_drainlife.mdl");
|
||||
|
||||
PRECACHE_MODEL("models/w_9mmclip.mdl");
|
||||
PRECACHE_SOUND("items/9mmclip1.wav");
|
||||
|
||||
PRECACHE_SOUND( DRAINLIFE_SOUND_OFF );
|
||||
PRECACHE_SOUND( DRAINLIFE_SOUND_RUN );
|
||||
PRECACHE_SOUND( DRAINLIFE_SOUND_STARTUP );
|
||||
|
||||
PRECACHE_MODEL( DRAINLIFE_BEAM_SPRITE );
|
||||
PRECACHE_MODEL( DRAINLIFE_FLARE_SPRITE );
|
||||
|
||||
PRECACHE_SOUND ("weapons/357_cock1.wav");
|
||||
}
|
||||
|
||||
|
||||
BOOL CDrainLife::Deploy( void )
|
||||
{
|
||||
m_deployed = FALSE;
|
||||
return DefaultDeploy( "models/v_drainlife.mdl", "", DRAINLIFE_OPEN, "shrivelling" );
|
||||
}
|
||||
|
||||
int CDrainLife::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 CDrainLife::Holster( int skiplocal /* = 0 */ )
|
||||
{
|
||||
m_pPlayer->m_flNextAttack = UTIL_WeaponTimeBase() + 1.0;
|
||||
// m_flTimeWeaponIdle = gpGlobals->time + UTIL_RandomFloat ( 10, 15 );
|
||||
SendWeaponAnim( DRAINLIFE_CLOSE );
|
||||
|
||||
if ( m_fireState != FIRE_OFF )
|
||||
EndAttack();
|
||||
}
|
||||
|
||||
int CDrainLife::GetItemInfo(ItemInfo *p)
|
||||
{
|
||||
p->pszName = STRING(pev->classname);
|
||||
p->pszAmmo1 = NULL;
|
||||
p->iMaxAmmo1 = -1;
|
||||
p->pszAmmo2 = NULL;
|
||||
p->iMaxAmmo2 = -1;
|
||||
p->iMaxClip = WEAPON_NOCLIP;
|
||||
p->iSlot = 4;
|
||||
p->iPosition = 2;
|
||||
p->iId = m_iId = WEAPON_DRAINLIFE;
|
||||
p->iFlags = 0;
|
||||
p->iWeight = DRAINLIFE_WEIGHT;
|
||||
|
||||
return 1;
|
||||
}
|
||||
|
||||
|
||||
//#define DRAINLIFE_PULSE_INTERVAL 0.25
|
||||
//#define DRAINLIFE_DISCHARGE_INTERVAL 0.5
|
||||
|
||||
#define DRAINLIFE_PULSE_INTERVAL 0.1
|
||||
#define DRAINLIFE_DISCHARGE_INTERVAL 0.1
|
||||
|
||||
float CDrainLife::GetPulseInterval( void )
|
||||
{
|
||||
if ( g_pGameRules->IsMultiplayer() )
|
||||
{
|
||||
return 0.1;
|
||||
}
|
||||
|
||||
return DRAINLIFE_PULSE_INTERVAL;
|
||||
}
|
||||
|
||||
float CDrainLife::GetDischargeInterval( void )
|
||||
{
|
||||
if ( g_pGameRules->IsMultiplayer() )
|
||||
{
|
||||
return 0.1;
|
||||
}
|
||||
|
||||
return DRAINLIFE_DISCHARGE_INTERVAL;
|
||||
}
|
||||
|
||||
void CDrainLife::Attack( void )
|
||||
{
|
||||
UTIL_MakeVectors( m_pPlayer->pev->v_angle + m_pPlayer->pev->punchangle );
|
||||
Vector vecAiming = gpGlobals->v_forward;
|
||||
Vector vecSrc = m_pPlayer->GetGunPosition( );
|
||||
|
||||
switch( m_fireState )
|
||||
{
|
||||
case FIRE_OFF:
|
||||
{
|
||||
m_flAmmoUseTime = gpGlobals->time;// start using ammo ASAP.
|
||||
|
||||
SendWeaponAnim( DRAINLIFE_CAST );
|
||||
|
||||
m_pPlayer->m_iWeaponVolume = DRAINLIFE_PRIMARY_VOLUME;
|
||||
m_flTimeWeaponIdle = gpGlobals->time + 0.1;
|
||||
m_shootTime = gpGlobals->time + 2;
|
||||
|
||||
EMIT_SOUND_DYN(ENT(m_pPlayer->pev), CHAN_WEAPON, DRAINLIFE_SOUND_STARTUP, 0.2, ATTN_NORM, 0, 100 );
|
||||
|
||||
pev->dmgtime = gpGlobals->time + GetPulseInterval();
|
||||
m_fireState = FIRE_CHARGE;
|
||||
}
|
||||
break;
|
||||
|
||||
case FIRE_CHARGE:
|
||||
{
|
||||
Fire( vecSrc, vecAiming );
|
||||
m_pPlayer->m_iWeaponVolume = DRAINLIFE_PRIMARY_VOLUME;
|
||||
|
||||
if ( m_shootTime != 0 && gpGlobals->time > m_shootTime )
|
||||
{
|
||||
EMIT_SOUND_DYN(ENT(m_pPlayer->pev), CHAN_STATIC, DRAINLIFE_SOUND_RUN, 0.2, ATTN_NORM, 0, 100 );
|
||||
|
||||
m_shootTime = 0;
|
||||
}
|
||||
}
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
void CDrainLife::PrimaryAttack( void )
|
||||
{
|
||||
Attack();
|
||||
}
|
||||
|
||||
void CDrainLife::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;
|
||||
|
||||
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;
|
||||
}
|
||||
}
|
||||
|
||||
float timedist;
|
||||
|
||||
if ( pev->dmgtime < gpGlobals->time )
|
||||
{
|
||||
bool bHuman = false;
|
||||
|
||||
if (FClassnameIs(pEntity->pev, "player")) bHuman = true;
|
||||
else if (FClassnameIs(pEntity->pev, "monster_cultist")) bHuman = true;
|
||||
else if (FClassnameIs(pEntity->pev, "monster_priest")) bHuman = true;
|
||||
else if (FClassnameIs(pEntity->pev, "monster_policeman")) bHuman = true;
|
||||
else if (FClassnameIs(pEntity->pev, "monster_civilian")) bHuman = true;
|
||||
else if (FClassnameIs(pEntity->pev, "monster_butler")) bHuman = true;
|
||||
else if (FClassnameIs(pEntity->pev, "monster_sirhenry")) bHuman = true;
|
||||
else if (FClassnameIs(pEntity->pev, "monster_ranulf")) bHuman = true;
|
||||
else if (FClassnameIs(pEntity->pev, "monster_gangster")) bHuman = true;
|
||||
else if (FClassnameIs(pEntity->pev, "monster_sitting_scientist")) bHuman = true;
|
||||
else if (FClassnameIs(pEntity->pev, "monster_scientist")) bHuman = true;
|
||||
else if (FClassnameIs(pEntity->pev, "monster_sitting_butler")) bHuman = true;
|
||||
else if (FClassnameIs(pEntity->pev, "monster_sitting_civilian")) bHuman = true;
|
||||
else if (FClassnameIs(pEntity->pev, "monster_sitting_sirhenry")) bHuman = true;
|
||||
|
||||
if (bHuman)
|
||||
{
|
||||
float flOldTgtHealth = pEntity->pev->health;
|
||||
pEntity->TakeDamage(m_pPlayer->pev,m_pPlayer->pev, gSkillData.plrDmgDrainLife, DMG_ENERGYBEAM);
|
||||
float flTgtDamage = flOldTgtHealth - max(0,pEntity->pev->health);
|
||||
|
||||
// lose one point of san for every 5 health taken
|
||||
m_pPlayer->LoseSanity(flTgtDamage/5.0);
|
||||
|
||||
float flOldHealth = m_pPlayer->pev->health;
|
||||
m_pPlayer->TakeHealth(flTgtDamage, DMG_ENERGYBEAM);
|
||||
}
|
||||
|
||||
pev->dmgtime = gpGlobals->time + GetPulseInterval();
|
||||
}
|
||||
timedist = ( pev->dmgtime - gpGlobals->time ) / GetPulseInterval();
|
||||
|
||||
if ( timedist < 0 )
|
||||
timedist = 0;
|
||||
else if ( timedist > 1 )
|
||||
timedist = 1;
|
||||
timedist = 1-timedist;
|
||||
|
||||
UpdateEffect( tmpSrc, tr.vecEndPos, timedist );
|
||||
}
|
||||
|
||||
|
||||
void CDrainLife::UpdateEffect( const Vector &startPoint, const Vector &endPoint, float timeBlend )
|
||||
{
|
||||
if ( !m_pBeam )
|
||||
{
|
||||
CreateEffect();
|
||||
}
|
||||
|
||||
m_pBeam->SetStartPos( endPoint );
|
||||
m_pBeam->SetBrightness( 255 - (timeBlend*180) );
|
||||
m_pBeam->SetWidth( 40 - (timeBlend*20) );
|
||||
|
||||
m_pBeam->SetColor( 120 + (25*timeBlend), 60 + (30*timeBlend), 64 + 80*fabs(sin(gpGlobals->time*10)) );
|
||||
|
||||
UTIL_SetOrigin( m_pSprite, endPoint );
|
||||
m_pSprite->pev->frame += 8 * gpGlobals->frametime;
|
||||
if ( m_pSprite->pev->frame > m_pSprite->Frames() )
|
||||
m_pSprite->pev->frame = 0;
|
||||
|
||||
m_pNoise->SetStartPos( endPoint );
|
||||
}
|
||||
|
||||
|
||||
void CDrainLife::CreateEffect( void )
|
||||
{
|
||||
DestroyEffect();
|
||||
|
||||
m_pBeam = CBeam::BeamCreate( DRAINLIFE_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_pNoise = CBeam::BeamCreate( DRAINLIFE_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_pSprite = CSprite::SpriteCreate( DRAINLIFE_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_pBeam->SetScrollRate( 110 );
|
||||
m_pBeam->SetNoise( 5 );
|
||||
m_pNoise->SetColor( 255, 80, 120 );
|
||||
m_pNoise->SetNoise( 2 );
|
||||
}
|
||||
|
||||
|
||||
void CDrainLife::DestroyEffect( void )
|
||||
{
|
||||
if ( m_pBeam )
|
||||
{
|
||||
UTIL_Remove( m_pBeam );
|
||||
m_pBeam = NULL;
|
||||
}
|
||||
if ( m_pNoise )
|
||||
{
|
||||
UTIL_Remove( m_pNoise );
|
||||
m_pNoise = NULL;
|
||||
}
|
||||
if ( m_pSprite )
|
||||
{
|
||||
UTIL_Remove( m_pSprite );
|
||||
m_pSprite = NULL;
|
||||
}
|
||||
}
|
||||
|
||||
void CDrainLife::WeaponIdle( void )
|
||||
{
|
||||
ResetEmptySound( );
|
||||
|
||||
if ( m_flTimeWeaponIdle > gpGlobals->time )
|
||||
return;
|
||||
|
||||
if ( m_fireState != FIRE_OFF )
|
||||
EndAttack();
|
||||
|
||||
|
||||
int iAnim;
|
||||
|
||||
float flRand = RANDOM_FLOAT(0,1);
|
||||
|
||||
if ( flRand <= 0.4 )
|
||||
{
|
||||
iAnim = DRAINLIFE_IDLE1;
|
||||
m_flTimeWeaponIdle = gpGlobals->time + RANDOM_FLOAT(2,3);
|
||||
}
|
||||
else if ( flRand <= 0.75 )
|
||||
{
|
||||
iAnim = DRAINLIFE_IDLE2;
|
||||
m_flTimeWeaponIdle = gpGlobals->time + 3;
|
||||
}
|
||||
else
|
||||
{
|
||||
iAnim = DRAINLIFE_IDLE3;
|
||||
m_flTimeWeaponIdle = gpGlobals->time + 3;
|
||||
}
|
||||
|
||||
SendWeaponAnim( iAnim );
|
||||
m_deployed = TRUE;
|
||||
}
|
||||
|
||||
void CDrainLife::EndAttack( void )
|
||||
{
|
||||
STOP_SOUND( ENT(m_pPlayer->pev), CHAN_STATIC, DRAINLIFE_SOUND_RUN );
|
||||
EMIT_SOUND_DYN(ENT(m_pPlayer->pev), CHAN_WEAPON, DRAINLIFE_SOUND_OFF, 0.2, ATTN_NORM, 0, 100);
|
||||
m_fireState = FIRE_OFF;
|
||||
m_flTimeWeaponIdle = gpGlobals->time + 2.0;
|
||||
m_flNextPrimaryAttack = m_flNextSecondaryAttack = gpGlobals->time + 0.5;
|
||||
DestroyEffect();
|
||||
}
|
||||
|
||||
|
||||
|
||||
#endif
|
|
@ -0,0 +1,60 @@
|
|||
|
||||
#ifndef DRAINLIFE_H
|
||||
#define DRAINLIFE_H
|
||||
|
||||
class CDrainLife : public CBasePlayerWeapon
|
||||
{
|
||||
public:
|
||||
int Save( CSave &save );
|
||||
int Restore( CRestore &restore );
|
||||
static TYPEDESCRIPTION m_SaveData[];
|
||||
|
||||
void Spawn( void );
|
||||
void Precache( void );
|
||||
int iItemSlot( void ) { return 4; }
|
||||
int GetItemInfo(ItemInfo *p);
|
||||
int AddToPlayer( CBasePlayer *pPlayer );
|
||||
|
||||
BOOL Deploy( void );
|
||||
void Holster( int skiplocal = 0 );
|
||||
|
||||
void CreateEffect( void );
|
||||
void UpdateEffect( const Vector &startPoint, const Vector &endPoint, float timeBlend );
|
||||
void DestroyEffect( void );
|
||||
|
||||
void EndAttack( void );
|
||||
void Attack( void );
|
||||
void PrimaryAttack( void );
|
||||
void WeaponIdle( void );
|
||||
static int g_fireAnims1[];
|
||||
static int g_fireAnims2[];
|
||||
|
||||
float m_flAmmoUseTime;// since we use < 1 point of ammo per update, we subtract ammo on a timer.
|
||||
|
||||
float GetPulseInterval( void );
|
||||
float GetDischargeInterval( void );
|
||||
|
||||
void Fire( const Vector &vecOrigSrc, const Vector &vecDir );
|
||||
|
||||
enum DRAINLIFE_FIRESTATE { FIRE_OFF, FIRE_CHARGE };
|
||||
|
||||
virtual BOOL UseDecrement( void )
|
||||
{
|
||||
#if defined( CLIENT_WEAPONS )
|
||||
return TRUE;
|
||||
#else
|
||||
return FALSE;
|
||||
#endif
|
||||
}
|
||||
|
||||
private:
|
||||
float m_shootTime;
|
||||
CBeam *m_pBeam;
|
||||
CBeam *m_pNoise;
|
||||
CSprite *m_pSprite;
|
||||
DRAINLIFE_FIRESTATE m_fireState;
|
||||
BOOL m_deployed;
|
||||
};
|
||||
|
||||
#endif
|
||||
|
|
@ -0,0 +1,225 @@
|
|||
|
||||
#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 "dynamite.h"
|
||||
#include "monster_dynamite.h"
|
||||
|
||||
//#define DYNAMITE_FUSE_SPRITE "sprites/fire.spr"
|
||||
|
||||
|
||||
LINK_ENTITY_TO_CLASS( weapon_dynamite, CDynamite );
|
||||
|
||||
|
||||
void CDynamite::Spawn( )
|
||||
{
|
||||
Precache( );
|
||||
m_iId = WEAPON_DYNAMITE;
|
||||
SET_MODEL(ENT(pev), "models/w_dynamite.mdl");
|
||||
|
||||
pev->dmg = gSkillData.plrDmgDynamite;
|
||||
|
||||
m_iDefaultAmmo = DYNAMITE_DEFAULT_GIVE;
|
||||
|
||||
FallInit();// get ready to fall down.
|
||||
}
|
||||
|
||||
void CDynamite::Precache( void )
|
||||
{
|
||||
PRECACHE_MODEL("models/w_dynamite.mdl");
|
||||
PRECACHE_MODEL("models/v_dynamite.mdl");
|
||||
// PRECACHE_MODEL("models/p_dynamite.mdl");
|
||||
|
||||
// PRECACHE_MODEL( DYNAMITE_FUSE_SPRITE );
|
||||
}
|
||||
|
||||
int CDynamite::GetItemInfo(ItemInfo *p)
|
||||
{
|
||||
p->pszName = STRING(pev->classname);
|
||||
p->pszAmmo1 = "Dynamite";
|
||||
p->iMaxAmmo1 = DYNAMITE_MAX_CARRY;
|
||||
p->pszAmmo2 = NULL;
|
||||
p->iMaxAmmo2 = -1;
|
||||
p->iMaxClip = WEAPON_NOCLIP;
|
||||
p->iSlot = 2;
|
||||
p->iPosition = 1;
|
||||
p->iId = m_iId = WEAPON_DYNAMITE;
|
||||
p->iWeight = DYNAMITE_WEIGHT;
|
||||
p->iFlags = ITEM_FLAG_LIMITINWORLD | ITEM_FLAG_EXHAUSTIBLE;
|
||||
|
||||
return 1;
|
||||
}
|
||||
|
||||
|
||||
BOOL CDynamite::Deploy( )
|
||||
{
|
||||
m_flReleaseThrow = -1;
|
||||
m_flFinishThrow = -1;
|
||||
return DefaultDeploy( "models/v_dynamite.mdl", "", DYNAMITE_DRAW, "dynamite" );
|
||||
}
|
||||
|
||||
BOOL CDynamite::CanHolster( void )
|
||||
{
|
||||
// can only holster dynamite when not lit!
|
||||
return ( m_flStartThrow == 0 );
|
||||
}
|
||||
|
||||
void CDynamite::Holster( int skiplocal /* = 0 */ )
|
||||
{
|
||||
m_pPlayer->m_flNextAttack = UTIL_WeaponTimeBase() + 0.5;
|
||||
if (m_pPlayer->m_rgAmmo[m_iPrimaryAmmoType])
|
||||
{
|
||||
SendWeaponAnim( DYNAMITE_HOLSTER );
|
||||
}
|
||||
else
|
||||
{
|
||||
// no more dynamites!
|
||||
m_pPlayer->pev->weapons &= ~(1<<WEAPON_DYNAMITE);
|
||||
SetThink( DestroyItem );
|
||||
SetNextThink( 0.1 );
|
||||
}
|
||||
|
||||
EMIT_SOUND(ENT(m_pPlayer->pev), CHAN_WEAPON, "common/null.wav", 1.0, ATTN_NORM);
|
||||
}
|
||||
|
||||
int CDynamite::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 CDynamite::PrimaryAttack()
|
||||
{
|
||||
if (!m_flStartThrow && m_pPlayer->m_rgAmmo[m_iPrimaryAmmoType] > 0)
|
||||
{
|
||||
m_flStartThrow = gpGlobals->time;
|
||||
m_flReleaseThrow = gpGlobals->time;
|
||||
m_flFinishThrow = gpGlobals->time;
|
||||
|
||||
SendWeaponAnim( DYNAMITE_LIGHT );
|
||||
|
||||
m_flTimeWeaponIdle = gpGlobals->time + 1.5;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
void CDynamite::WeaponIdle( void )
|
||||
{
|
||||
//if (m_flReleaseThrow == 0)
|
||||
// m_flReleaseThrow = gpGlobals->time;
|
||||
//else if (m_flFinishThrow == 0)
|
||||
// m_flFinishThrow = gpGlobals->time;
|
||||
|
||||
if (m_flTimeWeaponIdle > gpGlobals->time)
|
||||
return;
|
||||
|
||||
if (m_flStartThrow > 0)
|
||||
{
|
||||
// alway explode 5 seconds after the fuse was lit
|
||||
SendWeaponAnim( DYNAMITE_THROW );
|
||||
|
||||
// player "shoot" animation
|
||||
m_pPlayer->SetAnimation( PLAYER_ATTACK1 );
|
||||
|
||||
m_flStartThrow = 0;
|
||||
m_flNextPrimaryAttack = gpGlobals->time + 1.0;
|
||||
m_flTimeWeaponIdle = gpGlobals->time + 0.5;
|
||||
|
||||
return;
|
||||
}
|
||||
else if (m_flReleaseThrow > 0)
|
||||
{
|
||||
Vector angThrow = m_pPlayer->pev->v_angle + m_pPlayer->pev->punchangle;
|
||||
|
||||
if (angThrow.x < 0)
|
||||
angThrow.x = -10 + angThrow.x * ((90 - 10) / 90.0);
|
||||
else
|
||||
angThrow.x = -10 + angThrow.x * ((90 + 10) / 90.0);
|
||||
|
||||
float flVel = (90 - angThrow.x) * 4;
|
||||
if (flVel > 500)
|
||||
flVel = 500;
|
||||
|
||||
UTIL_MakeVectors( angThrow );
|
||||
|
||||
Vector vecSrc = m_pPlayer->pev->origin + m_pPlayer->pev->view_ofs + gpGlobals->v_forward * 16;
|
||||
|
||||
Vector vecThrow = gpGlobals->v_forward * flVel + m_pPlayer->pev->velocity;
|
||||
|
||||
float time = m_flReleaseThrow - gpGlobals->time + 5.0 - 0.5; // because it has been 0.5 seconds since we lit the fuse...
|
||||
if (time < 0)
|
||||
time = 0;
|
||||
|
||||
CMonsterDynamite::Shoot( m_pPlayer->pev, vecSrc, vecThrow, time );
|
||||
|
||||
m_flTimeWeaponIdle = gpGlobals->time + 0.5;
|
||||
|
||||
m_flReleaseThrow = -1;
|
||||
|
||||
m_pPlayer->m_rgAmmo[m_iPrimaryAmmoType]--;
|
||||
|
||||
if ( !m_pPlayer->m_rgAmmo[m_iPrimaryAmmoType] )
|
||||
{
|
||||
// just threw last stick of dynamite
|
||||
// set attack times in the future, and weapon idle in the future so we can see the whole throw
|
||||
// animation, weapon idle will automatically retire the weapon for us.
|
||||
m_flTimeWeaponIdle = m_flNextSecondaryAttack = m_flNextPrimaryAttack = gpGlobals->time + 1.0;// ensure that the animation can finish playing
|
||||
}
|
||||
return;
|
||||
}
|
||||
else if (m_flFinishThrow > 0)
|
||||
{
|
||||
// we've finished the throw, restart.
|
||||
m_flStartThrow = 0;
|
||||
m_flReleaseThrow = -1;
|
||||
m_flFinishThrow = -1;
|
||||
m_flTimeWeaponIdle = gpGlobals->time + RANDOM_FLOAT ( 10, 15 );
|
||||
|
||||
if (m_pPlayer->m_rgAmmo[m_iPrimaryAmmoType])
|
||||
{
|
||||
SendWeaponAnim( DYNAMITE_DRAW );
|
||||
}
|
||||
else
|
||||
{
|
||||
RetireWeapon();
|
||||
return;
|
||||
}
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
if (m_pPlayer->m_rgAmmo[m_iPrimaryAmmoType])
|
||||
{
|
||||
int iAnim;
|
||||
float flRand = RANDOM_FLOAT(0, 1);
|
||||
if (flRand <= 0.75)
|
||||
{
|
||||
iAnim = DYNAMITE_IDLE1;
|
||||
m_flTimeWeaponIdle = gpGlobals->time + RANDOM_FLOAT ( 10, 15 );// how long till we do this again.
|
||||
}
|
||||
else
|
||||
{
|
||||
iAnim = DYNAMITE_IDLE2;
|
||||
m_flTimeWeaponIdle = gpGlobals->time + 75.0 / 30.0;
|
||||
}
|
||||
|
||||
SendWeaponAnim( iAnim );
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
|
@ -0,0 +1,61 @@
|
|||
|
||||
#ifndef DYNAMITE_H
|
||||
#define DYNAMITE_H
|
||||
|
||||
/***
|
||||
*
|
||||
* Copyright (c) 1999, 2000 Valve LLC. All rights reserved.
|
||||
*
|
||||
* This product contains software technology licensed from Id
|
||||
* Software, Inc. ("Id Technology"). Id Technology (c) 1996 Id Software, Inc.
|
||||
* All Rights Reserved.
|
||||
*
|
||||
* Use, distribution, and modification of this source code and/or resulting
|
||||
* object code is restricted to non-commercial enhancements to products from
|
||||
* Valve LLC. All other use, distribution, or modification is prohibited
|
||||
* without written permission from Valve LLC.
|
||||
*
|
||||
****/
|
||||
|
||||
#define DYNAMITE_PRIMARY_VOLUME 450
|
||||
|
||||
enum dynamite_e {
|
||||
DYNAMITE_DRAW = 0,
|
||||
DYNAMITE_IDLE1,
|
||||
DYNAMITE_IDLE2,
|
||||
DYNAMITE_LIGHT,
|
||||
DYNAMITE_THROW, // toss
|
||||
DYNAMITE_HOLSTER
|
||||
};
|
||||
|
||||
|
||||
|
||||
class CDynamite : public CBasePlayerWeapon
|
||||
{
|
||||
public:
|
||||
void Spawn( void );
|
||||
void Precache( void );
|
||||
int iItemSlot( void ) { return 2; }
|
||||
int GetItemInfo(ItemInfo *p);
|
||||
int AddToPlayer( CBasePlayer *pPlayer );
|
||||
|
||||
void PrimaryAttack( void );
|
||||
BOOL Deploy( void );
|
||||
BOOL CanHolster( void );
|
||||
void Holster( int skiplocal = 0 );
|
||||
void WeaponIdle( void );
|
||||
float m_flFinishThrow;
|
||||
|
||||
virtual BOOL UseDecrement( void )
|
||||
{
|
||||
#if defined( CLIENT_WEAPONS )
|
||||
return TRUE;
|
||||
#else
|
||||
return FALSE;
|
||||
#endif
|
||||
}
|
||||
};
|
||||
|
||||
#endif
|
||||
|
||||
|
|
@ -0,0 +1,295 @@
|
|||
/***
|
||||
*
|
||||
* Copyright (c) 1999, 2000 Valve LLC. All rights reserved.
|
||||
*
|
||||
* This product contains software technology licensed from Id
|
||||
* Software, Inc. ("Id Technology"). Id Technology (c) 1996 Id Software, Inc.
|
||||
* All Rights Reserved.
|
||||
*
|
||||
* This source code contains proprietary and confidential information of
|
||||
* Valve LLC and its suppliers. Access to this code is restricted to
|
||||
* persons who have executed a written SDK license with Valve. Any access,
|
||||
* use or distribution of this code by or to any unlicensed person is illegal.
|
||||
*
|
||||
****/
|
||||
//=========================================================
|
||||
// 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 EihortVictim dying for scripted sequences?
|
||||
#define EIHORTVICTIM_AE_BURST ( 2 )
|
||||
#define EIHORTVICTIM_AE_PAIN ( 3 )
|
||||
|
||||
#include "EihortVictim.h"
|
||||
|
||||
LINK_ENTITY_TO_CLASS( monster_eihortvictim, CEihortVictim );
|
||||
|
||||
TYPEDESCRIPTION CEihortVictim::m_SaveData[] =
|
||||
{
|
||||
DEFINE_FIELD( CEihortVictim, m_painTime, FIELD_TIME ),
|
||||
};
|
||||
|
||||
IMPLEMENT_SAVERESTORE( CEihortVictim, CTalkMonster );
|
||||
|
||||
|
||||
//=========================================================
|
||||
// ISoundMask - returns a bit mask indicating which types
|
||||
// of sounds this monster regards.
|
||||
//=========================================================
|
||||
int CEihortVictim :: 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 CEihortVictim :: Classify ( void )
|
||||
{
|
||||
return m_iClass?m_iClass:CLASS_NONE;
|
||||
}
|
||||
|
||||
//=========================================================
|
||||
// SetYawSpeed - allows each sequence to have a different
|
||||
// turn rate associated with it.
|
||||
//=========================================================
|
||||
void CEihortVictim :: SetYawSpeed ( void )
|
||||
{
|
||||
pev->yaw_speed = 0;
|
||||
}
|
||||
|
||||
//=========================================================
|
||||
// HandleAnimEvent - catches the monster-specific messages
|
||||
// that occur when tagged animation frames are played.
|
||||
//
|
||||
// Returns number of events handled, 0 if none.
|
||||
//=========================================================
|
||||
void CEihortVictim :: HandleAnimEvent( MonsterEvent_t *pEvent )
|
||||
{
|
||||
switch( pEvent->event )
|
||||
{
|
||||
case EIHORTVICTIM_AE_BURST:
|
||||
Burst();
|
||||
break;
|
||||
|
||||
case EIHORTVICTIM_AE_PAIN:
|
||||
if (IsTalking()) return;
|
||||
if (RANDOM_LONG(0,2) == 0)
|
||||
{
|
||||
//play scientist pain sound 1, 4, 5, 7 or 10
|
||||
PainSound();
|
||||
}
|
||||
break;
|
||||
|
||||
default:
|
||||
CTalkMonster::HandleAnimEvent( pEvent );
|
||||
}
|
||||
}
|
||||
|
||||
void CEihortVictim :: StartTask( Task_t *pTask )
|
||||
{
|
||||
switch ( pTask->iTask )
|
||||
{
|
||||
// victims do not turn
|
||||
case TASK_TLK_IDEALYAW:
|
||||
TaskComplete();
|
||||
break;
|
||||
default:
|
||||
CTalkMonster::StartTask( pTask );
|
||||
}
|
||||
}
|
||||
|
||||
//=========================================================
|
||||
// Spawn
|
||||
//=========================================================
|
||||
void CEihortVictim :: Spawn()
|
||||
{
|
||||
Precache( );
|
||||
|
||||
if (pev->model)
|
||||
SET_MODEL(ENT(pev), STRING(pev->model)); //LRC
|
||||
else
|
||||
SET_MODEL(ENT(pev), "models/eihort_victim.mdl");
|
||||
//UTIL_SetSize(pev, VEC_HUMAN_HULL_MIN, VEC_HUMAN_HULL_MAX);
|
||||
UTIL_SetSize(pev, Vector(-16,-16,0), Vector(16,16,8));
|
||||
|
||||
pev->solid = SOLID_SLIDEBOX;
|
||||
pev->movetype = MOVETYPE_STEP;
|
||||
m_bloodColor = BLOOD_COLOR_RED;
|
||||
if (pev->health == 0)
|
||||
pev->health = 100;
|
||||
pev->view_ofs = Vector ( 0, 0, 50 );// position of the eyes relative to monster's origin.
|
||||
m_flFieldOfView = VIEW_FIELD_WIDE; // NOTE: we need a wide field of view so npc will notice player and say hello
|
||||
m_MonsterState = MONSTERSTATE_NONE;
|
||||
|
||||
m_afCapability = bits_CAP_HEAR | bits_CAP_TURN_HEAD | bits_CAP_DOORS_GROUP;
|
||||
|
||||
MonsterInit();
|
||||
SetUse( NULL );
|
||||
// this monster cannot turn
|
||||
SetYawSpeed();
|
||||
}
|
||||
|
||||
//=========================================================
|
||||
// Precache - precaches all resources this monster needs
|
||||
//=========================================================
|
||||
void CEihortVictim :: Precache()
|
||||
{
|
||||
if (pev->model)
|
||||
PRECACHE_MODEL((char*)STRING(pev->model)); //LRC
|
||||
else
|
||||
PRECACHE_MODEL("models/eihort_victim.mdl");
|
||||
|
||||
PRECACHE_SOUND("scientist/sci_pain1.wav");
|
||||
PRECACHE_SOUND("scientist/sci_pain4.wav");
|
||||
PRECACHE_SOUND("scientist/sci_pain5.wav");
|
||||
PRECACHE_SOUND("scientist/sci_pain7.wav");
|
||||
PRECACHE_SOUND("scientist/sci_pain10.wav");
|
||||
|
||||
UTIL_PrecacheOther( "monster_babycrab" );
|
||||
|
||||
// every new EihortVictim must call this, otherwise
|
||||
// when a level is loaded, nobody will talk (time is reset to 0)
|
||||
TalkInit();
|
||||
CTalkMonster::Precache();
|
||||
}
|
||||
|
||||
// Init talk data
|
||||
void CEihortVictim :: TalkInit()
|
||||
{
|
||||
CTalkMonster::TalkInit();
|
||||
|
||||
// EihortVictim speech group names (group names are in sentences.txt)
|
||||
|
||||
if (!m_iszSpeakAs)
|
||||
{
|
||||
//m_szGrp[TLK_ANSWER] = "BA_ANSWER";
|
||||
//m_szGrp[TLK_QUESTION] = "BA_QUESTION";
|
||||
//m_szGrp[TLK_IDLE] = "BA_IDLE";
|
||||
//m_szGrp[TLK_STARE] = "BA_STARE";
|
||||
m_szGrp[TLK_STOP] = "BA_STOP";
|
||||
|
||||
m_szGrp[TLK_NOSHOOT] = "BA_SCARED";
|
||||
//m_szGrp[TLK_HELLO] = "BA_HELLO";
|
||||
|
||||
//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 EihortVictim voice for now
|
||||
m_voicePitch = 100;
|
||||
}
|
||||
|
||||
int CEihortVictim :: TakeDamage( entvars_t* pevInflictor, entvars_t* pevAttacker, float flDamage, int bitsDamageType)
|
||||
{
|
||||
return 0;
|
||||
|
||||
// make sure friends talk about it if player hurts talkmonsters...
|
||||
int ret = CTalkMonster::TakeDamage(pevInflictor, pevAttacker, flDamage, bitsDamageType);
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
|
||||
//=========================================================
|
||||
// PainSound
|
||||
//=========================================================
|
||||
void CEihortVictim :: PainSound ( void )
|
||||
{
|
||||
if (gpGlobals->time < m_painTime)
|
||||
return;
|
||||
|
||||
m_painTime = gpGlobals->time + RANDOM_FLOAT(0.5, 0.75);
|
||||
|
||||
switch ( RANDOM_LONG(0,4) )
|
||||
{
|
||||
case 0:
|
||||
EMIT_SOUND_DYN( ENT(pev), CHAN_VOICE, "scientist/sci_pain1.wav", 1, ATTN_NORM, 0, m_voicePitch );
|
||||
break;
|
||||
case 1:
|
||||
EMIT_SOUND_DYN( ENT(pev), CHAN_VOICE, "scientist/sci_pain4.wav", 1, ATTN_NORM, 0, m_voicePitch );
|
||||
break;
|
||||
case 2:
|
||||
EMIT_SOUND_DYN( ENT(pev), CHAN_VOICE, "scientist/sci_pain5.wav", 1, ATTN_NORM, 0, m_voicePitch );
|
||||
break;
|
||||
case 3:
|
||||
EMIT_SOUND_DYN( ENT(pev), CHAN_VOICE, "scientist/sci_pain7.wav", 1, ATTN_NORM, 0, m_voicePitch );
|
||||
break;
|
||||
case 4:
|
||||
EMIT_SOUND_DYN( ENT(pev), CHAN_VOICE, "scientist/sci_pain10.wav", 1, ATTN_NORM, 0, m_voicePitch );
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
//=========================================================
|
||||
// DeathSound
|
||||
//=========================================================
|
||||
void CEihortVictim :: DeathSound ( void )
|
||||
{
|
||||
switch (RANDOM_LONG(0,2))
|
||||
{
|
||||
case 0: EMIT_SOUND_DYN( ENT(pev), CHAN_VOICE, "EihortVictim/ba_die1.wav", 1, ATTN_NORM, 0, GetVoicePitch()); break;
|
||||
case 1: EMIT_SOUND_DYN( ENT(pev), CHAN_VOICE, "EihortVictim/ba_die2.wav", 1, ATTN_NORM, 0, GetVoicePitch()); break;
|
||||
case 2: EMIT_SOUND_DYN( ENT(pev), CHAN_VOICE, "EihortVictim/ba_die3.wav", 1, ATTN_NORM, 0, GetVoicePitch()); break;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
void CEihortVictim::Killed( entvars_t *pevAttacker, int iGib )
|
||||
{
|
||||
SetUse( NULL );
|
||||
Burst();
|
||||
CTalkMonster::Killed( pevAttacker, iGib );
|
||||
}
|
||||
|
||||
void CEihortVictim::Burst()
|
||||
{
|
||||
pev->body = 1;
|
||||
EMIT_SOUND(ENT(pev), CHAN_WEAPON, "common/bodysplat.wav", 1, ATTN_NORM);
|
||||
CGib::SpawnRandomGibs( pev, 4, 1 ); // throw some human gibs.
|
||||
|
||||
// now make some baby headcrabs
|
||||
for (int x = 0; x <= 1; x++)
|
||||
{
|
||||
for (int y = 0; y <= 1; y++)
|
||||
{
|
||||
//CBaseEntity *pChild = CBaseEntity::Create( "monster_babycrab", pev->origin + 24*Vector(x-0.5,y-0.5,0) + Vector(0,0,1), pev->angles, edict() );
|
||||
CBaseEntity *pChild = CBaseEntity::Create( "monster_babycrab", pev->origin + 24*Vector(x-0.5,y-0.5,0) + Vector(0,0,8), pev->angles, edict() );
|
||||
pChild->pev->spawnflags |= SF_MONSTER_FALL_TO_GROUND;
|
||||
pChild->pev->spawnflags |= SF_MONSTER_NO_YELLOW_BLOBS;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
|
|
@ -0,0 +1,32 @@
|
|||
|
||||
class CEihortVictim : public CTalkMonster
|
||||
{
|
||||
public:
|
||||
void Spawn( void );
|
||||
void Precache( void );
|
||||
void SetYawSpeed( void );
|
||||
int ISoundMask( void );
|
||||
void HandleAnimEvent( MonsterEvent_t *pEvent );
|
||||
int Classify ( void );
|
||||
|
||||
void StartTask( Task_t *pTask );
|
||||
|
||||
int TakeDamage( entvars_t* pevInflictor, entvars_t* pevAttacker, float flDamage, int bitsDamageType);
|
||||
|
||||
void DeathSound( void );
|
||||
void PainSound( void );
|
||||
|
||||
void TalkInit( void );
|
||||
|
||||
void Killed( entvars_t *pevAttacker, int iGib );
|
||||
|
||||
void Burst ( void );
|
||||
|
||||
virtual int Save( CSave &save );
|
||||
virtual int Restore( CRestore &restore );
|
||||
static TYPEDESCRIPTION m_SaveData[];
|
||||
|
||||
// BOOL m_fGunDrawn;
|
||||
float m_painTime;
|
||||
};
|
||||
|
|
@ -0,0 +1,377 @@
|
|||
|
||||
#include "elder_sign.h"
|
||||
|
||||
enum elder_sign_e {
|
||||
ELDER_SIGN_DRAW = 0,
|
||||
ELDER_SIGN_IDLE1,
|
||||
ELDER_SIGN_IDLE2,
|
||||
ELDER_SIGN_IDLE3,
|
||||
ELDER_SIGN_CAST,
|
||||
ELDER_SIGN_HOLSTER,
|
||||
ELDER_SIGN_WORLD,
|
||||
ELDER_SIGN_GROUND
|
||||
};
|
||||
|
||||
|
||||
LINK_ENTITY_TO_CLASS( monster_elder_sign, CElderSignArea );
|
||||
|
||||
//TYPEDESCRIPTION CElderSignArea::m_SaveData[] =
|
||||
//{
|
||||
//};
|
||||
|
||||
//IMPLEMENT_SAVERESTORE( CElderSignArea, CBaseMonster );
|
||||
|
||||
|
||||
void CElderSignArea :: Spawn( void )
|
||||
{
|
||||
Precache( );
|
||||
|
||||
m_afCapability = bits_CAP_RANGE_ATTACK1;
|
||||
|
||||
// motor
|
||||
pev->movetype = MOVETYPE_FLY;
|
||||
pev->solid = SOLID_NOT;
|
||||
|
||||
SET_MODEL(ENT(pev), "models/v_elder_sign.mdl");
|
||||
pev->frame = 0;
|
||||
pev->body = 3;
|
||||
//pev->sequence = ELDER_SIGN_IDLE1;
|
||||
pev->sequence = ELDER_SIGN_WORLD;
|
||||
ResetSequenceInfo( );
|
||||
pev->framerate = 0;
|
||||
|
||||
UTIL_SetSize(pev, Vector( -8, 0, -8), Vector(8, 4, 8));
|
||||
UTIL_SetOrigin( this, pev->origin );
|
||||
|
||||
//pev->solid = SOLID_BBOX;
|
||||
SetThink ( ElderSignThink );
|
||||
SetNextThink( 0.2 );
|
||||
|
||||
pev->takedamage = DAMAGE_YES;
|
||||
pev->health = 5; // don't let die normally
|
||||
|
||||
//MonsterInit();
|
||||
SetBits (pev->flags, FL_MONSTER);
|
||||
|
||||
// make solid
|
||||
//pev->solid = SOLID_BBOX;
|
||||
UTIL_SetOrigin( this, pev->origin );
|
||||
m_bloodColor = DONT_BLEED;
|
||||
}
|
||||
|
||||
void CElderSignArea :: Precache( void )
|
||||
{
|
||||
PRECACHE_MODEL("models/v_elder_sign.mdl");
|
||||
}
|
||||
|
||||
int CElderSignArea :: TakeDamage( entvars_t *pevInflictor, entvars_t *pevAttacker, float flDamage, int bitsDamageType )
|
||||
{
|
||||
if (flDamage < pev->health)
|
||||
{
|
||||
SetThink( SUB_Remove );
|
||||
SetNextThink( 0.1 );
|
||||
return FALSE;
|
||||
}
|
||||
return CBaseMonster :: TakeDamage ( pevInflictor, pevAttacker, flDamage, bitsDamageType );
|
||||
}
|
||||
|
||||
BOOL CElderSignArea :: CheckRangeAttack1 ( float flDot, float flDist )
|
||||
{
|
||||
return FALSE;
|
||||
}
|
||||
|
||||
void CElderSignArea :: ElderSignThink ( void )
|
||||
{
|
||||
int iMonsters;
|
||||
|
||||
// wait until we are free of monsters (incl. players) in our box and then become solid
|
||||
if (pev->solid == SOLID_NOT)
|
||||
{
|
||||
iMonsters = UTIL_EntitiesInBox(mpEntInSphere, MAX_MONSTER, pev->origin - Vector(8,2,8), pev->origin + Vector(8,2,8), (FL_CLIENT|FL_MONSTER));
|
||||
|
||||
// are we (the elder sign) the only monster in the area
|
||||
if (iMonsters <= 1)
|
||||
{
|
||||
pev->solid = SOLID_BBOX;
|
||||
}
|
||||
}
|
||||
|
||||
iMonsters = UTIL_MonstersInSphere(mpEntInSphere, MAX_MONSTER, pev->origin, 96);
|
||||
|
||||
for (int i = 0; i < iMonsters; i++)
|
||||
{
|
||||
if (!(mpEntInSphere[i]->pev->flags & FL_MONSTER)) continue;
|
||||
|
||||
Repel((CBaseMonster*)mpEntInSphere[i]);
|
||||
}
|
||||
|
||||
SetNextThink( 0.1 );
|
||||
}
|
||||
|
||||
void CElderSignArea :: Killed( entvars_t *pevAttacker, int iGib )
|
||||
{
|
||||
MESSAGE_BEGIN( MSG_PAS, SVC_TEMPENTITY, pev->origin );
|
||||
WRITE_BYTE( TE_EXPLOSION ); // This makes a dynamic light and the explosion sprites/sound
|
||||
WRITE_COORD( pev->origin.x ); // Send to PAS because of the sound
|
||||
WRITE_COORD( pev->origin.y );
|
||||
WRITE_COORD( pev->origin.z );
|
||||
WRITE_SHORT( g_sModelIndexFireball );
|
||||
WRITE_BYTE( 5 );
|
||||
WRITE_BYTE( 15 ); // framerate
|
||||
WRITE_BYTE( TE_EXPLFLAG_NONE );
|
||||
MESSAGE_END();
|
||||
|
||||
UTIL_Remove(this);
|
||||
}
|
||||
|
||||
int CElderSignArea :: Classify ( void )
|
||||
{
|
||||
return CLASS_ALIEN_PASSIVE;
|
||||
}
|
||||
|
||||
void CElderSignArea::Repel( CBaseMonster* pEnt )
|
||||
{
|
||||
int iClass = pEnt->Classify();
|
||||
|
||||
if (iClass != CLASS_ALIEN_MILITARY
|
||||
&& iClass != CLASS_ALIEN_MONSTER
|
||||
&& iClass != CLASS_ALIEN_PREY
|
||||
&& iClass != CLASS_ALIEN_PREDATOR)
|
||||
return;
|
||||
|
||||
entvars_t* pevEnt = pEnt->pev;
|
||||
|
||||
if ( !FBitSet ( pevEnt->flags , FL_MONSTER ) )
|
||||
{// touched by a non-monster.
|
||||
return;
|
||||
}
|
||||
|
||||
pevEnt->origin.z += 1;
|
||||
|
||||
if ( FBitSet ( pevEnt->flags, FL_ONGROUND ) )
|
||||
{// clear the onground so physics don't bitch
|
||||
pevEnt->flags &= ~FL_ONGROUND;
|
||||
}
|
||||
|
||||
// flying monsters do not work the same way, so we panic them instead.
|
||||
if (pEnt->pev->movetype == MOVETYPE_FLY)
|
||||
{
|
||||
CBaseEntity* pPlayer = UTIL_FindEntityByClassname( NULL, "player" );
|
||||
pEnt->Panic(pPlayer->pev);
|
||||
return;
|
||||
}
|
||||
|
||||
// calculate the opposite direction, and push in that direction
|
||||
Vector vecDirToEnemy = ( pevEnt->origin - pev->origin );
|
||||
vecDirToEnemy.z = 0;
|
||||
//Vector angDir = UTIL_VecToAngles( vecDirToEnemy );
|
||||
vecDirToEnemy = vecDirToEnemy.Normalize();
|
||||
|
||||
pevEnt->velocity = vecDirToEnemy * 256;
|
||||
pevEnt->velocity.z += 128;
|
||||
}
|
||||
|
||||
//////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
LINK_ENTITY_TO_CLASS( weapon_eldersign, CElderSign );
|
||||
|
||||
|
||||
void CElderSign::Spawn( )
|
||||
{
|
||||
Precache( );
|
||||
m_iId = WEAPON_ELDER_SIGN;
|
||||
SET_MODEL(ENT(pev), "models/v_elder_sign.mdl");
|
||||
pev->frame = 0;
|
||||
pev->body = 3;
|
||||
pev->sequence = ELDER_SIGN_GROUND;
|
||||
//ResetSequenceInfo( );
|
||||
pev->framerate = 0;
|
||||
|
||||
FallInit();// get ready to fall down
|
||||
|
||||
m_iDefaultAmmo = ELDER_SIGN_DEFAULT_GIVE;
|
||||
|
||||
if ( !g_pGameRules->IsDeathmatch() )
|
||||
{
|
||||
UTIL_SetSize(pev, Vector(-16, -16, 0), Vector(16, 16, 28) );
|
||||
}
|
||||
}
|
||||
|
||||
void CElderSign::Precache( void )
|
||||
{
|
||||
PRECACHE_MODEL ("models/v_elder_sign.mdl");
|
||||
// PRECACHE_MODEL ("models/p_elder_sign.mdl");
|
||||
UTIL_PrecacheOther( "monster_elder_sign" );
|
||||
}
|
||||
|
||||
int CElderSign::GetItemInfo(ItemInfo *p)
|
||||
{
|
||||
p->pszName = STRING(pev->classname);
|
||||
p->pszAmmo1 = "Elder Sign";
|
||||
p->iMaxAmmo1 = ELDER_SIGN_MAX_CARRY;
|
||||
p->pszAmmo2 = NULL;
|
||||
p->iMaxAmmo2 = -1;
|
||||
p->iMaxClip = WEAPON_NOCLIP;
|
||||
p->iSlot = 3;
|
||||
p->iPosition = 0;
|
||||
p->iId = m_iId = WEAPON_ELDER_SIGN;
|
||||
p->iWeight = ELDER_SIGN_WEIGHT;
|
||||
p->iFlags = ITEM_FLAG_LIMITINWORLD | ITEM_FLAG_EXHAUSTIBLE;
|
||||
|
||||
return 1;
|
||||
}
|
||||
|
||||
BOOL CElderSign::Deploy( )
|
||||
{
|
||||
pev->body = 0;
|
||||
return DefaultDeploy( "models/v_elder_sign.mdl", "", ELDER_SIGN_DRAW, "eldersign" );
|
||||
}
|
||||
|
||||
|
||||
void CElderSign::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_ELDER_SIGN);
|
||||
SetThink( DestroyItem );
|
||||
SetNextThink( 0.1 );
|
||||
}
|
||||
|
||||
SendWeaponAnim( ELDER_SIGN_HOLSTER );
|
||||
EMIT_SOUND(ENT(m_pPlayer->pev), CHAN_WEAPON, "common/null.wav", 1.0, ATTN_NORM);
|
||||
}
|
||||
|
||||
int CElderSign::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 CElderSign::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 );
|
||||
|
||||
if (tr.flFraction < 1.0)
|
||||
{
|
||||
// ALERT( at_console, "hit %f\n", tr.flFraction );
|
||||
|
||||
CBaseEntity *pEntity = CBaseEntity::Instance( tr.pHit );
|
||||
if (pEntity && !(pEntity->pev->flags & FL_CONVEYOR))
|
||||
{
|
||||
Vector angles = UTIL_VecToAngles( tr.vecPlaneNormal );
|
||||
|
||||
edict_t *pentity;
|
||||
entvars_t *pevCreate;
|
||||
|
||||
// ALERT(at_console,"Making Monster NOW\n");
|
||||
|
||||
pentity = CREATE_NAMED_ENTITY( MAKE_STRING("monster_elder_sign") );
|
||||
|
||||
pevCreate = VARS( pentity );
|
||||
//pevCreate->origin = tr.vecEndPos + tr.vecPlaneNormal * 8;
|
||||
pevCreate->origin = tr.vecEndPos + tr.vecPlaneNormal * 0.1;
|
||||
pevCreate->angles = angles;
|
||||
|
||||
DispatchSpawn( ENT( pevCreate ) );
|
||||
pevCreate->owner = edict();
|
||||
|
||||
//LRC - custom monster behaviour
|
||||
CBaseEntity *pEnt = CBaseEntity::Instance( pevCreate );
|
||||
|
||||
|
||||
|
||||
|
||||
//CBaseEntity *pEnt = CBaseEntity::Create( "monster_elder_sign", tr.vecEndPos + tr.vecPlaneNormal * 8, angles, m_pPlayer->edict() );
|
||||
//CBaseMonster *pNewMonster = pEnt->MyMonsterPointer( );
|
||||
//pEnt->pev->spawnflags |= 1;
|
||||
|
||||
//CElderSignArea *pElderSignArea = (CElderSignArea *)pEnt;
|
||||
|
||||
m_pPlayer->m_rgAmmo[m_iPrimaryAmmoType]--;
|
||||
|
||||
// player "shoot" animation
|
||||
m_pPlayer->SetAnimation( PLAYER_ATTACK1 );
|
||||
|
||||
if (m_pPlayer->m_rgAmmo[m_iPrimaryAmmoType] > 0)
|
||||
{
|
||||
SendWeaponAnim( ELDER_SIGN_DRAW );
|
||||
}
|
||||
else
|
||||
{
|
||||
// no more mines!
|
||||
RetireWeapon();
|
||||
return;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
// ALERT( at_console, "no deploy\n" );
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
|
||||
}
|
||||
|
||||
m_flNextPrimaryAttack = gpGlobals->time + 0.3;
|
||||
m_flTimeWeaponIdle = gpGlobals->time + RANDOM_FLOAT ( 10, 15 );
|
||||
}
|
||||
|
||||
void CElderSign::WeaponIdle( void )
|
||||
{
|
||||
if (m_flTimeWeaponIdle > gpGlobals->time)
|
||||
return;
|
||||
|
||||
if (m_pPlayer->m_rgAmmo[m_iPrimaryAmmoType] > 0)
|
||||
{
|
||||
SendWeaponAnim( ELDER_SIGN_DRAW );
|
||||
}
|
||||
else
|
||||
{
|
||||
RetireWeapon();
|
||||
return;
|
||||
}
|
||||
|
||||
int iAnim;
|
||||
float flRand = RANDOM_FLOAT(0, 1);
|
||||
if (flRand <= 0.25)
|
||||
{
|
||||
iAnim = ELDER_SIGN_IDLE1;
|
||||
m_flTimeWeaponIdle = gpGlobals->time + 90.0 / 30.0;
|
||||
}
|
||||
else if (flRand <= 0.75)
|
||||
{
|
||||
iAnim = ELDER_SIGN_IDLE2;
|
||||
m_flTimeWeaponIdle = gpGlobals->time + 60.0 / 30.0;
|
||||
}
|
||||
else
|
||||
{
|
||||
iAnim = ELDER_SIGN_IDLE3;
|
||||
m_flTimeWeaponIdle = gpGlobals->time + 100.0 / 30.0;
|
||||
}
|
||||
|
||||
SendWeaponAnim( iAnim );
|
||||
|
||||
}
|
||||
|
||||
|
||||
|
|
@ -0,0 +1,106 @@
|
|||
|
||||
#ifndef ELDERSIGN_H
|
||||
#define ELDERSIGN_H
|
||||
|
||||
/***
|
||||
*
|
||||
* Copyright (c) 1999, 2000 Valve LLC. All rights reserved.
|
||||
*
|
||||
* This product contains software technology licensed from Id
|
||||
* Software, Inc. ("Id Technology"). Id Technology (c) 1996 Id Software, Inc.
|
||||
* All Rights Reserved.
|
||||
*
|
||||
* Use, distribution, and modification of this source code and/or resulting
|
||||
* object code is restricted to non-commercial enhancements to products from
|
||||
* Valve LLC. All other use, distribution, or modification is prohibited
|
||||
* without written permission from Valve LLC.
|
||||
*
|
||||
****/
|
||||
#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"
|
||||
|
||||
#include "triggers.h"
|
||||
|
||||
#define ELDER_SIGN_PRIMARY_VOLUME 450
|
||||
|
||||
|
||||
const int MAX_MONSTER = 1000;
|
||||
|
||||
|
||||
// The elder sign is supposed to push all monsters (not humans, like cultists)
|
||||
// away from it, to prevent them passing...
|
||||
|
||||
|
||||
class CElderSignArea : public CBaseMonster
|
||||
{
|
||||
public:
|
||||
void Spawn( void );
|
||||
void Precache( void );
|
||||
int Classify ( void );
|
||||
|
||||
// no melee attacks
|
||||
BOOL CheckMeleeAttack1 ( float flDot, float flDist ) { return FALSE; };
|
||||
BOOL CheckMeleeAttack2 ( float flDot, float flDist ) { return FALSE; };
|
||||
BOOL CheckRangeAttack1 ( float flDot, float flDist );
|
||||
|
||||
void EXPORT ElderSignThink ( void );
|
||||
void Killed( entvars_t *pevAttacker, int iGib );
|
||||
|
||||
int TakeDamage( entvars_t *pevInflictor, entvars_t *pevAttacker, float flDamage, int bitsDamageType );
|
||||
|
||||
void Repel ( CBaseMonster* pEnt );
|
||||
|
||||
//int Save( CSave &save );
|
||||
//int Restore( CRestore &restore );
|
||||
|
||||
static TYPEDESCRIPTION m_SaveData[];
|
||||
|
||||
protected:
|
||||
CBaseEntity* mpEntInSphere[MAX_MONSTER];
|
||||
|
||||
};
|
||||
|
||||
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
class CElderSign : public CBasePlayerWeapon
|
||||
{
|
||||
public:
|
||||
void Spawn( void );
|
||||
void Precache( void );
|
||||
int iItemSlot( void ) { return 3; }
|
||||
int GetItemInfo(ItemInfo *p);
|
||||
int AddToPlayer( CBasePlayer *pPlayer );
|
||||
void SetObjectCollisionBox( void )
|
||||
{
|
||||
//!!!BUGBUG - fix the model!
|
||||
pev->absmin = pev->origin + Vector(-16, -16, -5);
|
||||
pev->absmax = pev->origin + Vector(16, 16, 28);
|
||||
}
|
||||
|
||||
void PrimaryAttack( void );
|
||||
BOOL Deploy( void );
|
||||
void Holster( int skiplocal = 0 );
|
||||
void WeaponIdle( void );
|
||||
|
||||
virtual BOOL UseDecrement( void )
|
||||
{
|
||||
#if defined( CLIENT_WEAPONS )
|
||||
return TRUE;
|
||||
#else
|
||||
return FALSE;
|
||||
#endif
|
||||
}
|
||||
};
|
||||
|
||||
#endif
|
||||
|
||||
|
||||
|
Some files were not shown because too many files have changed in this diff Show More
Loading…
Reference in New Issue