Merge original "Cthulhu" source code.

This commit is contained in:
Andrey Akhmichin 2019-11-04 23:40:16 +05:00
parent 2905950cb0
commit 8720c4518c
185 changed files with 46704 additions and 3173 deletions

32
cl_dll/cthulhu/ImageLabel.h Executable file
View File

@ -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

90
cl_dll/cthulhu/readbook.cpp Executable file
View File

@ -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;
}

146
cl_dll/cthulhu/sanity.cpp Executable file
View File

@ -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;
}

File diff suppressed because it is too large Load Diff

View File

@ -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 );

View File

@ -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() { }

View File

@ -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 );
}

View File

@ -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();
}

View File

@ -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 );
}

View File

@ -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();

View File

@ -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;

View File

@ -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

View File

@ -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;

View File

@ -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;

View File

@ -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.

View File

@ -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

View File

@ -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 )

View File

@ -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;

View File

@ -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)

View File

@ -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;

View File

@ -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 );

View File

@ -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 );

View File

@ -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 );

View File

@ -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

423
dlls/cthulhu/Chicken.cpp Executable file
View File

@ -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
//=========================================================

48
dlls/cthulhu/Chicken.h Executable file
View File

@ -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

216
dlls/cthulhu/ClimbingMonster.cpp Executable file
View File

@ -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 );
}

33
dlls/cthulhu/ClimbingMonster.h Executable file
View File

@ -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

133
dlls/cthulhu/Cow.cpp Executable file
View File

@ -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
//=========================================================

26
dlls/cthulhu/Cow.h Executable file
View File

@ -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

1154
dlls/cthulhu/Cthonian.cpp Executable file

File diff suppressed because it is too large Load Diff

62
dlls/cthulhu/Cthonian.h Executable file
View File

@ -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

328
dlls/cthulhu/Dagger.cpp Executable file
View File

@ -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 );
}

36
dlls/cthulhu/Dagger.h Executable file
View File

@ -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

442
dlls/cthulhu/DeepOne.cpp Executable file
View File

@ -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 );
}

52
dlls/cthulhu/DeepOne.h Executable file
View File

@ -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

View File

@ -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;
}
}
}

183
dlls/cthulhu/DreadName.cpp Executable file
View File

@ -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 );
}

32
dlls/cthulhu/DreadName.h Executable file
View File

@ -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

990
dlls/cthulhu/DunwichHorror.cpp Executable file
View File

@ -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

493
dlls/cthulhu/FormlessSpawn.cpp Executable file
View File

@ -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 );
}

45
dlls/cthulhu/FormlessSpawn.h Executable file
View File

@ -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

1963
dlls/cthulhu/Gangster.cpp Executable file

File diff suppressed because it is too large Load Diff

88
dlls/cthulhu/Gangster.h Executable file
View File

@ -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

330
dlls/cthulhu/Ghoul.cpp Executable file
View File

@ -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;
}

39
dlls/cthulhu/Ghoul.h Executable file
View File

@ -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

780
dlls/cthulhu/GreatRace.cpp Executable file
View File

@ -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" );
}

59
dlls/cthulhu/GreatRace.h Executable file
View File

@ -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

986
dlls/cthulhu/HuntingHorror.cpp Executable file
View File

@ -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

80
dlls/cthulhu/HuntingHorror.h Executable file
View File

@ -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

980
dlls/cthulhu/NightGaunt.cpp Executable file
View File

@ -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

79
dlls/cthulhu/NightGaunt.h Executable file
View File

@ -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

196
dlls/cthulhu/PowderOfIbn.cpp Executable file
View File

@ -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 );
}
}

59
dlls/cthulhu/PowderOfIbn.h Executable file
View File

@ -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

1131
dlls/cthulhu/Priest.cpp Executable file

File diff suppressed because it is too large Load Diff

72
dlls/cthulhu/Priest.h Executable file
View File

@ -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

342
dlls/cthulhu/Rifle.cpp Executable file
View File

@ -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

53
dlls/cthulhu/Rifle.h Executable file
View File

@ -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

739
dlls/cthulhu/SerpentMan.cpp Executable file
View File

@ -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 );
}

62
dlls/cthulhu/SerpentMan.h Executable file
View File

@ -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

247
dlls/cthulhu/SerpentStaff.cpp Executable file
View File

@ -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

35
dlls/cthulhu/SerpentStaff.h Executable file
View File

@ -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

63
dlls/cthulhu/Shrivelling.h Executable file
View File

@ -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

1764
dlls/cthulhu/SirHenry.cpp Executable file

File diff suppressed because it is too large Load Diff

249
dlls/cthulhu/Snake.cpp Executable file
View File

@ -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;
}

49
dlls/cthulhu/Snake.h Executable file
View File

@ -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

105
dlls/cthulhu/Spiral.cpp Executable file
View File

@ -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 );
}

16
dlls/cthulhu/Spiral.h Executable file
View File

@ -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

1952
dlls/cthulhu/Stukabat.cpp Executable file

File diff suppressed because it is too large Load Diff

133
dlls/cthulhu/Stukabat.h Executable file
View File

@ -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

336
dlls/cthulhu/SwordCane.cpp Executable file
View File

@ -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 );
}

37
dlls/cthulhu/SwordCane.h Executable file
View File

@ -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

1115
dlls/cthulhu/TRex.cpp Executable file

File diff suppressed because it is too large Load Diff

86
dlls/cthulhu/TRex.h Executable file
View File

@ -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

219
dlls/cthulhu/TommyGun.cpp Executable file
View File

@ -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 );

46
dlls/cthulhu/TommyGun.h Executable file
View File

@ -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

882
dlls/cthulhu/Yodan.cpp Executable file
View File

@ -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" );
}

63
dlls/cthulhu/Yodan.h Executable file
View File

@ -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

313
dlls/cthulhu/alien_egg.cpp Executable file
View File

@ -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 );
}

34
dlls/cthulhu/alien_egg.h Executable file
View File

@ -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

188
dlls/cthulhu/bm.cpp Executable file
View File

@ -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

55
dlls/cthulhu/bm.h Executable file
View File

@ -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

173
dlls/cthulhu/book.cpp Executable file
View File

@ -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);
}
}

1280
dlls/cthulhu/butler.cpp Executable file

File diff suppressed because it is too large Load Diff

88
dlls/cthulhu/butler.h Executable file
View File

@ -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

422
dlls/cthulhu/charm.cpp Executable file
View File

@ -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

1468
dlls/cthulhu/civilian.cpp Executable file

File diff suppressed because it is too large Load Diff

26
dlls/cthulhu/crowbar.h Executable file
View File

@ -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

1142
dlls/cthulhu/cthulhu.cpp Executable file

File diff suppressed because it is too large Load Diff

1996
dlls/cthulhu/cultist.cpp Executable file

File diff suppressed because it is too large Load Diff

79
dlls/cthulhu/cultist.h Executable file
View File

@ -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

99
dlls/cthulhu/defaultai.h Executable file
View File

@ -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

411
dlls/cthulhu/drainlife.cpp Executable file
View File

@ -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

60
dlls/cthulhu/drainlife.h Executable file
View File

@ -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

225
dlls/cthulhu/dynamite.cpp Executable file
View File

@ -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 );
}
}

61
dlls/cthulhu/dynamite.h Executable file
View File

@ -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

295
dlls/cthulhu/eihortvictim.cpp Executable file
View File

@ -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;
}
}
}

32
dlls/cthulhu/eihortvictim.h Executable file
View File

@ -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;
};

377
dlls/cthulhu/elder_sign.cpp Executable file
View File

@ -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 );
}

106
dlls/cthulhu/elder_sign.h Executable file
View File

@ -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