10 Apr 2010
This commit is contained in:
parent
3571b54223
commit
406f0d5f9a
|
@ -0,0 +1,109 @@
|
|||
/***
|
||||
*
|
||||
* 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.
|
||||
*
|
||||
****/
|
||||
|
||||
#ifndef ACTIVITY_H
|
||||
#define ACTIVITY_H
|
||||
|
||||
|
||||
typedef enum {
|
||||
ACT_RESET = 0, // Set m_Activity to this invalid value to force a reset to m_IdealActivity
|
||||
ACT_IDLE = 1,
|
||||
ACT_GUARD,
|
||||
ACT_WALK,
|
||||
ACT_RUN,
|
||||
ACT_FLY, // Fly (and flap if appropriate)
|
||||
ACT_SWIM,
|
||||
ACT_HOP, // vertical jump
|
||||
ACT_LEAP, // long forward jump
|
||||
ACT_FALL,
|
||||
ACT_LAND,
|
||||
ACT_STRAFE_LEFT,
|
||||
ACT_STRAFE_RIGHT,
|
||||
ACT_ROLL_LEFT, // tuck and roll, left
|
||||
ACT_ROLL_RIGHT, // tuck and roll, right
|
||||
ACT_TURN_LEFT, // turn quickly left (stationary)
|
||||
ACT_TURN_RIGHT, // turn quickly right (stationary)
|
||||
ACT_CROUCH, // the act of crouching down from a standing position
|
||||
ACT_CROUCHIDLE, // holding body in crouched position (loops)
|
||||
ACT_STAND, // the act of standing from a crouched position
|
||||
ACT_USE,
|
||||
ACT_SIGNAL1,
|
||||
ACT_SIGNAL2,
|
||||
ACT_SIGNAL3,
|
||||
ACT_TWITCH,
|
||||
ACT_COWER,
|
||||
ACT_SMALL_FLINCH,
|
||||
ACT_BIG_FLINCH,
|
||||
ACT_RANGE_ATTACK1,
|
||||
ACT_RANGE_ATTACK2,
|
||||
ACT_MELEE_ATTACK1,
|
||||
ACT_MELEE_ATTACK2,
|
||||
ACT_RELOAD,
|
||||
ACT_ARM, // pull out gun, for instance
|
||||
ACT_DISARM, // reholster gun
|
||||
ACT_EAT, // monster chowing on a large food item (loop)
|
||||
ACT_DIESIMPLE,
|
||||
ACT_DIEBACKWARD,
|
||||
ACT_DIEFORWARD,
|
||||
ACT_DIEVIOLENT,
|
||||
ACT_BARNACLE_HIT, // barnacle tongue hits a monster
|
||||
ACT_BARNACLE_PULL, // barnacle is lifting the monster ( loop )
|
||||
ACT_BARNACLE_CHOMP, // barnacle latches on to the monster
|
||||
ACT_BARNACLE_CHEW, // barnacle is holding the monster in its mouth ( loop )
|
||||
ACT_SLEEP,
|
||||
ACT_INSPECT_FLOOR, // for active idles, look at something on or near the floor
|
||||
ACT_INSPECT_WALL, // for active idles, look at something directly ahead of you ( doesn't HAVE to be a wall or on a wall )
|
||||
ACT_IDLE_ANGRY, // alternate idle animation in which the monster is clearly agitated. (loop)
|
||||
ACT_WALK_HURT, // limp (loop)
|
||||
ACT_RUN_HURT, // limp (loop)
|
||||
ACT_HOVER, // Idle while in flight
|
||||
ACT_GLIDE, // Fly (don't flap)
|
||||
ACT_FLY_LEFT, // Turn left in flight
|
||||
ACT_FLY_RIGHT, // Turn right in flight
|
||||
ACT_DETECT_SCENT, // this means the monster smells a scent carried by the air
|
||||
ACT_SNIFF, // this is the act of actually sniffing an item in front of the monster
|
||||
ACT_BITE, // some large monsters can eat small things in one bite. This plays one time, EAT loops.
|
||||
ACT_THREAT_DISPLAY, // without attacking, monster demonstrates that it is angry. (Yell, stick out chest, etc )
|
||||
ACT_FEAR_DISPLAY, // monster just saw something that it is afraid of
|
||||
ACT_EXCITED, // for some reason, monster is excited. Sees something he really likes to eat, or whatever.
|
||||
ACT_SPECIAL_ATTACK1, // very monster specific special attacks.
|
||||
ACT_SPECIAL_ATTACK2,
|
||||
ACT_COMBAT_IDLE, // agitated idle.
|
||||
ACT_WALK_SCARED,
|
||||
ACT_RUN_SCARED,
|
||||
ACT_VICTORY_DANCE, // killed a player, do a victory dance.
|
||||
ACT_DIE_HEADSHOT, // die, hit in head.
|
||||
ACT_DIE_CHESTSHOT, // die, hit in chest
|
||||
ACT_DIE_GUTSHOT, // die, hit in gut
|
||||
ACT_DIE_BACKSHOT, // die, hit in back
|
||||
ACT_FLINCH_HEAD,
|
||||
ACT_FLINCH_CHEST,
|
||||
ACT_FLINCH_STOMACH,
|
||||
ACT_FLINCH_LEFTARM,
|
||||
ACT_FLINCH_RIGHTARM,
|
||||
ACT_FLINCH_LEFTLEG,
|
||||
ACT_FLINCH_RIGHTLEG,
|
||||
} Activity;
|
||||
|
||||
|
||||
typedef struct {
|
||||
int type;
|
||||
char *name;
|
||||
} activity_map_t;
|
||||
|
||||
extern activity_map_t activity_map[];
|
||||
|
||||
|
||||
#endif //ACTIVITY_H
|
|
@ -0,0 +1,97 @@
|
|||
/***
|
||||
*
|
||||
* 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 _A( a ) { a, #a }
|
||||
|
||||
activity_map_t activity_map[] =
|
||||
{
|
||||
_A( ACT_IDLE ),
|
||||
_A( ACT_GUARD ),
|
||||
_A( ACT_WALK ),
|
||||
_A( ACT_RUN ),
|
||||
_A( ACT_FLY ),
|
||||
_A( ACT_SWIM ),
|
||||
_A( ACT_HOP ),
|
||||
_A( ACT_LEAP ),
|
||||
_A( ACT_FALL ),
|
||||
_A( ACT_LAND ),
|
||||
_A( ACT_STRAFE_LEFT ),
|
||||
_A( ACT_STRAFE_RIGHT ),
|
||||
_A( ACT_ROLL_LEFT ),
|
||||
_A( ACT_ROLL_RIGHT ),
|
||||
_A( ACT_TURN_LEFT ),
|
||||
_A( ACT_TURN_RIGHT ),
|
||||
_A( ACT_CROUCH ),
|
||||
_A( ACT_CROUCHIDLE ),
|
||||
_A( ACT_STAND ),
|
||||
_A( ACT_USE ),
|
||||
_A( ACT_SIGNAL1 ),
|
||||
_A( ACT_SIGNAL2 ),
|
||||
_A( ACT_SIGNAL3 ),
|
||||
_A( ACT_TWITCH ),
|
||||
_A( ACT_COWER ),
|
||||
_A( ACT_SMALL_FLINCH ),
|
||||
_A( ACT_BIG_FLINCH ),
|
||||
_A( ACT_RANGE_ATTACK1 ),
|
||||
_A( ACT_RANGE_ATTACK2 ),
|
||||
_A( ACT_MELEE_ATTACK1 ),
|
||||
_A( ACT_MELEE_ATTACK2 ),
|
||||
_A( ACT_RELOAD ),
|
||||
_A( ACT_ARM ),
|
||||
_A( ACT_DISARM ),
|
||||
_A( ACT_EAT ),
|
||||
_A( ACT_DIESIMPLE ),
|
||||
_A( ACT_DIEBACKWARD ),
|
||||
_A( ACT_DIEFORWARD ),
|
||||
_A( ACT_DIEVIOLENT ),
|
||||
_A( ACT_BARNACLE_HIT ),
|
||||
_A( ACT_BARNACLE_PULL ),
|
||||
_A( ACT_BARNACLE_CHOMP ),
|
||||
_A( ACT_BARNACLE_CHEW ),
|
||||
_A( ACT_SLEEP ),
|
||||
_A( ACT_INSPECT_FLOOR ),
|
||||
_A( ACT_INSPECT_WALL ),
|
||||
_A( ACT_IDLE_ANGRY ),
|
||||
_A( ACT_WALK_HURT ),
|
||||
_A( ACT_RUN_HURT ),
|
||||
_A( ACT_HOVER ),
|
||||
_A( ACT_GLIDE ),
|
||||
_A( ACT_FLY_LEFT ),
|
||||
_A( ACT_FLY_RIGHT ),
|
||||
_A( ACT_DETECT_SCENT ),
|
||||
_A( ACT_SNIFF ),
|
||||
_A( ACT_BITE ),
|
||||
_A( ACT_THREAT_DISPLAY ),
|
||||
_A( ACT_FEAR_DISPLAY ),
|
||||
_A( ACT_EXCITED ),
|
||||
_A( ACT_SPECIAL_ATTACK1 ),
|
||||
_A( ACT_SPECIAL_ATTACK2 ),
|
||||
_A( ACT_COMBAT_IDLE ),
|
||||
_A( ACT_WALK_SCARED ),
|
||||
_A( ACT_RUN_SCARED ),
|
||||
_A( ACT_VICTORY_DANCE ),
|
||||
_A( ACT_DIE_HEADSHOT ),
|
||||
_A( ACT_DIE_CHESTSHOT ),
|
||||
_A( ACT_DIE_GUTSHOT ),
|
||||
_A( ACT_DIE_BACKSHOT ),
|
||||
_A( ACT_FLINCH_HEAD ),
|
||||
_A( ACT_FLINCH_CHEST ),
|
||||
_A( ACT_FLINCH_STOMACH ),
|
||||
_A( ACT_FLINCH_LEFTARM ),
|
||||
_A( ACT_FLINCH_RIGHTARM ),
|
||||
_A( ACT_FLINCH_LEFTLEG ),
|
||||
_A( ACT_FLINCH_RIGHTLEG ),
|
||||
0, NULL
|
||||
};
|
|
@ -0,0 +1,910 @@
|
|||
/***
|
||||
*
|
||||
* 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.
|
||||
*
|
||||
****/
|
||||
//=========================================================
|
||||
//=========================================================
|
||||
#include "extdll.h"
|
||||
#include "util.h"
|
||||
#include "cbase.h"
|
||||
#include "monsters.h"
|
||||
#include "squadmonster.h"
|
||||
|
||||
#define AFLOCK_MAX_RECRUIT_RADIUS 1024
|
||||
#define AFLOCK_FLY_SPEED 125
|
||||
#define AFLOCK_TURN_RATE 75
|
||||
#define AFLOCK_ACCELERATE 10
|
||||
#define AFLOCK_CHECK_DIST 192
|
||||
#define AFLOCK_TOO_CLOSE 100
|
||||
#define AFLOCK_TOO_FAR 256
|
||||
|
||||
//=========================================================
|
||||
//=========================================================
|
||||
class CFlockingFlyerFlock : public CBaseMonster
|
||||
{
|
||||
public:
|
||||
void Spawn( void );
|
||||
void Precache( void );
|
||||
void KeyValue( KeyValueData *pkvd );
|
||||
void SpawnFlock( void );
|
||||
|
||||
virtual int Save( CSave &save );
|
||||
virtual int Restore( CRestore &restore );
|
||||
static TYPEDESCRIPTION m_SaveData[];
|
||||
|
||||
// Sounds are shared by the flock
|
||||
static void PrecacheFlockSounds( void );
|
||||
|
||||
int m_cFlockSize;
|
||||
float m_flFlockRadius;
|
||||
};
|
||||
|
||||
TYPEDESCRIPTION CFlockingFlyerFlock::m_SaveData[] =
|
||||
{
|
||||
DEFINE_FIELD( CFlockingFlyerFlock, m_cFlockSize, FIELD_INTEGER ),
|
||||
DEFINE_FIELD( CFlockingFlyerFlock, m_flFlockRadius, FIELD_FLOAT ),
|
||||
};
|
||||
|
||||
IMPLEMENT_SAVERESTORE( CFlockingFlyerFlock, CBaseMonster );
|
||||
|
||||
//=========================================================
|
||||
//=========================================================
|
||||
class CFlockingFlyer : public CBaseMonster
|
||||
{
|
||||
public:
|
||||
void Spawn( void );
|
||||
void Precache( void );
|
||||
void SpawnCommonCode( void );
|
||||
void EXPORT IdleThink( void );
|
||||
void BoidAdvanceFrame( void );
|
||||
void EXPORT FormFlock( void );
|
||||
void EXPORT Start( void );
|
||||
void EXPORT FlockLeaderThink( void );
|
||||
void EXPORT FlockFollowerThink( void );
|
||||
void EXPORT FallHack( void );
|
||||
void MakeSound( void );
|
||||
void AlertFlock( void );
|
||||
void SpreadFlock( void );
|
||||
void SpreadFlock2( void );
|
||||
void Killed( entvars_t *pevAttacker, int iGib );
|
||||
void Poop ( void );
|
||||
BOOL FPathBlocked( void );
|
||||
//void KeyValue( KeyValueData *pkvd );
|
||||
|
||||
virtual int Save( CSave &save );
|
||||
virtual int Restore( CRestore &restore );
|
||||
static TYPEDESCRIPTION m_SaveData[];
|
||||
|
||||
int IsLeader( void ) { return m_pSquadLeader == this; }
|
||||
int InSquad( void ) { return m_pSquadLeader != NULL; }
|
||||
int SquadCount( void );
|
||||
void SquadRemove( CFlockingFlyer *pRemove );
|
||||
void SquadUnlink( void );
|
||||
void SquadAdd( CFlockingFlyer *pAdd );
|
||||
void SquadDisband( void );
|
||||
|
||||
CFlockingFlyer *m_pSquadLeader;
|
||||
CFlockingFlyer *m_pSquadNext;
|
||||
BOOL m_fTurning;// is this boid turning?
|
||||
BOOL m_fCourseAdjust;// followers set this flag TRUE to override flocking while they avoid something
|
||||
BOOL m_fPathBlocked;// TRUE if there is an obstacle ahead
|
||||
Vector m_vecReferencePoint;// last place we saw leader
|
||||
Vector m_vecAdjustedVelocity;// adjusted velocity (used when fCourseAdjust is TRUE)
|
||||
float m_flGoalSpeed;
|
||||
float m_flLastBlockedTime;
|
||||
float m_flFakeBlockedTime;
|
||||
float m_flAlertTime;
|
||||
float m_flFlockNextSoundTime;
|
||||
};
|
||||
LINK_ENTITY_TO_CLASS( monster_flyer, CFlockingFlyer );
|
||||
LINK_ENTITY_TO_CLASS( monster_flyer_flock, CFlockingFlyerFlock );
|
||||
|
||||
TYPEDESCRIPTION CFlockingFlyer::m_SaveData[] =
|
||||
{
|
||||
DEFINE_FIELD( CFlockingFlyer, m_pSquadLeader, FIELD_CLASSPTR ),
|
||||
DEFINE_FIELD( CFlockingFlyer, m_pSquadNext, FIELD_CLASSPTR ),
|
||||
DEFINE_FIELD( CFlockingFlyer, m_fTurning, FIELD_BOOLEAN ),
|
||||
DEFINE_FIELD( CFlockingFlyer, m_fCourseAdjust, FIELD_BOOLEAN ),
|
||||
DEFINE_FIELD( CFlockingFlyer, m_fPathBlocked, FIELD_BOOLEAN ),
|
||||
DEFINE_FIELD( CFlockingFlyer, m_vecReferencePoint, FIELD_POSITION_VECTOR ),
|
||||
DEFINE_FIELD( CFlockingFlyer, m_vecAdjustedVelocity, FIELD_VECTOR ),
|
||||
DEFINE_FIELD( CFlockingFlyer, m_flGoalSpeed, FIELD_FLOAT ),
|
||||
DEFINE_FIELD( CFlockingFlyer, m_flLastBlockedTime, FIELD_TIME ),
|
||||
DEFINE_FIELD( CFlockingFlyer, m_flFakeBlockedTime, FIELD_TIME ),
|
||||
DEFINE_FIELD( CFlockingFlyer, m_flAlertTime, FIELD_TIME ),
|
||||
// DEFINE_FIELD( CFlockingFlyer, m_flFlockNextSoundTime, FIELD_TIME ), // don't need to save
|
||||
};
|
||||
|
||||
IMPLEMENT_SAVERESTORE( CFlockingFlyer, CBaseMonster );
|
||||
|
||||
//=========================================================
|
||||
//=========================================================
|
||||
void CFlockingFlyerFlock :: KeyValue( KeyValueData *pkvd )
|
||||
{
|
||||
if (FStrEq(pkvd->szKeyName, "iFlockSize"))
|
||||
{
|
||||
m_cFlockSize = atoi(pkvd->szValue);
|
||||
pkvd->fHandled = TRUE;
|
||||
}
|
||||
else if (FStrEq(pkvd->szKeyName, "flFlockRadius"))
|
||||
{
|
||||
m_flFlockRadius = atof(pkvd->szValue);
|
||||
pkvd->fHandled = TRUE;
|
||||
}
|
||||
}
|
||||
|
||||
//=========================================================
|
||||
//=========================================================
|
||||
void CFlockingFlyerFlock :: Spawn( )
|
||||
{
|
||||
Precache( );
|
||||
SpawnFlock();
|
||||
|
||||
REMOVE_ENTITY(ENT(pev)); // dump the spawn ent
|
||||
}
|
||||
|
||||
//=========================================================
|
||||
//=========================================================
|
||||
void CFlockingFlyerFlock :: Precache( )
|
||||
{
|
||||
//PRECACHE_MODEL("models/aflock.mdl");
|
||||
PRECACHE_MODEL("models/boid.mdl");
|
||||
|
||||
PrecacheFlockSounds();
|
||||
}
|
||||
|
||||
|
||||
void CFlockingFlyerFlock :: PrecacheFlockSounds( void )
|
||||
{
|
||||
PRECACHE_SOUND("boid/boid_alert1.wav" );
|
||||
PRECACHE_SOUND("boid/boid_alert2.wav" );
|
||||
|
||||
PRECACHE_SOUND("boid/boid_idle1.wav" );
|
||||
PRECACHE_SOUND("boid/boid_idle2.wav" );
|
||||
}
|
||||
|
||||
//=========================================================
|
||||
//=========================================================
|
||||
void CFlockingFlyerFlock :: SpawnFlock( void )
|
||||
{
|
||||
float R = m_flFlockRadius;
|
||||
int iCount;
|
||||
Vector vecSpot;
|
||||
CFlockingFlyer *pBoid, *pLeader;
|
||||
|
||||
pLeader = pBoid = NULL;
|
||||
|
||||
for ( iCount = 0 ; iCount < m_cFlockSize ; iCount++ )
|
||||
{
|
||||
pBoid = GetClassPtr( (CFlockingFlyer *)NULL );
|
||||
|
||||
if ( !pLeader )
|
||||
{
|
||||
// make this guy the leader.
|
||||
pLeader = pBoid;
|
||||
|
||||
pLeader->m_pSquadLeader = pLeader;
|
||||
pLeader->m_pSquadNext = NULL;
|
||||
}
|
||||
|
||||
vecSpot.x = RANDOM_FLOAT( -R, R );
|
||||
vecSpot.y = RANDOM_FLOAT( -R, R );
|
||||
vecSpot.z = RANDOM_FLOAT( 0, 16 );
|
||||
vecSpot = pev->origin + vecSpot;
|
||||
|
||||
UTIL_SetOrigin(pBoid->pev, vecSpot);
|
||||
pBoid->pev->movetype = MOVETYPE_FLY;
|
||||
pBoid->SpawnCommonCode();
|
||||
pBoid->pev->flags &= ~FL_ONGROUND;
|
||||
pBoid->pev->velocity = g_vecZero;
|
||||
pBoid->pev->angles = pev->angles;
|
||||
|
||||
pBoid->pev->frame = 0;
|
||||
pBoid->pev->nextthink = gpGlobals->time + 0.2;
|
||||
pBoid->SetThink( CFlockingFlyer :: IdleThink );
|
||||
|
||||
if ( pBoid != pLeader )
|
||||
{
|
||||
pLeader->SquadAdd( pBoid );
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
//=========================================================
|
||||
//=========================================================
|
||||
void CFlockingFlyer :: Spawn( )
|
||||
{
|
||||
Precache( );
|
||||
SpawnCommonCode();
|
||||
|
||||
pev->frame = 0;
|
||||
pev->nextthink = gpGlobals->time + 0.1;
|
||||
SetThink( IdleThink );
|
||||
}
|
||||
|
||||
//=========================================================
|
||||
//=========================================================
|
||||
void CFlockingFlyer :: Precache( )
|
||||
{
|
||||
//PRECACHE_MODEL("models/aflock.mdl");
|
||||
PRECACHE_MODEL("models/boid.mdl");
|
||||
CFlockingFlyerFlock::PrecacheFlockSounds();
|
||||
}
|
||||
|
||||
//=========================================================
|
||||
//=========================================================
|
||||
void CFlockingFlyer :: MakeSound( void )
|
||||
{
|
||||
if ( m_flAlertTime > gpGlobals->time )
|
||||
{
|
||||
// make agitated sounds
|
||||
switch ( RANDOM_LONG( 0, 1 ) )
|
||||
{
|
||||
case 0: EMIT_SOUND( ENT(pev), CHAN_WEAPON, "boid/boid_alert1.wav", 1, ATTN_NORM ); break;
|
||||
case 1: EMIT_SOUND( ENT(pev), CHAN_WEAPON, "boid/boid_alert2.wav", 1, ATTN_NORM ); break;
|
||||
}
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
// make normal sound
|
||||
switch ( RANDOM_LONG( 0, 1 ) )
|
||||
{
|
||||
case 0: EMIT_SOUND( ENT(pev), CHAN_WEAPON, "boid/boid_idle1.wav", 1, ATTN_NORM ); break;
|
||||
case 1: EMIT_SOUND( ENT(pev), CHAN_WEAPON, "boid/boid_idle2.wav", 1, ATTN_NORM ); break;
|
||||
}
|
||||
}
|
||||
|
||||
//=========================================================
|
||||
//=========================================================
|
||||
void CFlockingFlyer :: Killed( entvars_t *pevAttacker, int iGib )
|
||||
{
|
||||
CFlockingFlyer *pSquad;
|
||||
|
||||
pSquad = (CFlockingFlyer *)m_pSquadLeader;
|
||||
|
||||
while ( pSquad )
|
||||
{
|
||||
pSquad->m_flAlertTime = gpGlobals->time + 15;
|
||||
pSquad = (CFlockingFlyer *)pSquad->m_pSquadNext;
|
||||
}
|
||||
|
||||
if ( m_pSquadLeader )
|
||||
{
|
||||
m_pSquadLeader->SquadRemove( this );
|
||||
}
|
||||
|
||||
pev->deadflag = DEAD_DEAD;
|
||||
|
||||
pev->framerate = 0;
|
||||
pev->effects = EF_NOINTERP;
|
||||
|
||||
UTIL_SetSize( pev, Vector(0,0,0), Vector(0,0,0) );
|
||||
pev->movetype = MOVETYPE_TOSS;
|
||||
|
||||
SetThink ( FallHack );
|
||||
pev->nextthink = gpGlobals->time + 0.1;
|
||||
}
|
||||
|
||||
void CFlockingFlyer :: FallHack( void )
|
||||
{
|
||||
if ( pev->flags & FL_ONGROUND )
|
||||
{
|
||||
if ( !FClassnameIs ( pev->groundentity, "worldspawn" ) )
|
||||
{
|
||||
pev->flags &= ~FL_ONGROUND;
|
||||
pev->nextthink = gpGlobals->time + 0.1;
|
||||
}
|
||||
else
|
||||
{
|
||||
pev->velocity = g_vecZero;
|
||||
SetThink( NULL );
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
//=========================================================
|
||||
//=========================================================
|
||||
void CFlockingFlyer :: SpawnCommonCode( )
|
||||
{
|
||||
pev->deadflag = DEAD_NO;
|
||||
pev->classname = MAKE_STRING("monster_flyer");
|
||||
pev->solid = SOLID_SLIDEBOX;
|
||||
pev->movetype = MOVETYPE_FLY;
|
||||
pev->takedamage = DAMAGE_NO;
|
||||
pev->health = 1;
|
||||
|
||||
m_fPathBlocked = FALSE;// obstacles will be detected
|
||||
m_flFieldOfView = 0.2;
|
||||
|
||||
//SET_MODEL(ENT(pev), "models/aflock.mdl");
|
||||
SET_MODEL(ENT(pev), "models/boid.mdl");
|
||||
|
||||
// UTIL_SetSize(pev, Vector(0,0,0), Vector(0,0,0));
|
||||
UTIL_SetSize(pev, Vector(-5,-5,0), Vector(5,5,2));
|
||||
}
|
||||
|
||||
//=========================================================
|
||||
//=========================================================
|
||||
void CFlockingFlyer :: BoidAdvanceFrame ( )
|
||||
{
|
||||
float flapspeed = (pev->speed - pev->armorvalue) / AFLOCK_ACCELERATE;
|
||||
pev->armorvalue = pev->armorvalue * .8 + pev->speed * .2;
|
||||
|
||||
if (flapspeed < 0) flapspeed = -flapspeed;
|
||||
if (flapspeed < 0.25) flapspeed = 0.25;
|
||||
if (flapspeed > 1.9) flapspeed = 1.9;
|
||||
|
||||
pev->framerate = flapspeed;
|
||||
|
||||
// lean
|
||||
pev->avelocity.x = - (pev->angles.x + flapspeed * 5);
|
||||
|
||||
// bank
|
||||
pev->avelocity.z = - (pev->angles.z + pev->avelocity.y);
|
||||
|
||||
// pev->framerate = flapspeed;
|
||||
StudioFrameAdvance( 0.1 );
|
||||
}
|
||||
|
||||
//=========================================================
|
||||
//=========================================================
|
||||
void CFlockingFlyer :: IdleThink( void )
|
||||
{
|
||||
pev->nextthink = gpGlobals->time + 0.2;
|
||||
|
||||
// see if there's a client in the same pvs as the monster
|
||||
if ( !FNullEnt( FIND_CLIENT_IN_PVS( edict() ) ) )
|
||||
{
|
||||
SetThink( Start );
|
||||
pev->nextthink = gpGlobals->time + 0.1;
|
||||
}
|
||||
}
|
||||
|
||||
//=========================================================
|
||||
// Start - player enters the pvs, so get things going.
|
||||
//=========================================================
|
||||
void CFlockingFlyer :: Start( void )
|
||||
{
|
||||
pev->nextthink = gpGlobals->time + 0.1;
|
||||
|
||||
if ( IsLeader() )
|
||||
{
|
||||
SetThink( FlockLeaderThink );
|
||||
}
|
||||
else
|
||||
{
|
||||
SetThink( FlockFollowerThink );
|
||||
}
|
||||
|
||||
/*
|
||||
Vector vecTakeOff;
|
||||
vecTakeOff = Vector ( 0 , 0 , 0 );
|
||||
|
||||
vecTakeOff.z = 50 + RANDOM_FLOAT ( 0, 100 );
|
||||
vecTakeOff.x = 20 - RANDOM_FLOAT ( 0, 40);
|
||||
vecTakeOff.y = 20 - RANDOM_FLOAT ( 0, 40);
|
||||
|
||||
pev->velocity = vecTakeOff;
|
||||
|
||||
|
||||
pev->speed = pev->velocity.Length();
|
||||
pev->sequence = 0;
|
||||
*/
|
||||
SetActivity ( ACT_FLY );
|
||||
ResetSequenceInfo( );
|
||||
BoidAdvanceFrame( );
|
||||
|
||||
pev->speed = AFLOCK_FLY_SPEED;// no delay!
|
||||
}
|
||||
|
||||
//=========================================================
|
||||
// Leader boid calls this to form a flock from surrounding boids
|
||||
//=========================================================
|
||||
void CFlockingFlyer :: FormFlock( void )
|
||||
{
|
||||
if ( !InSquad() )
|
||||
{
|
||||
// I am my own leader
|
||||
m_pSquadLeader = this;
|
||||
m_pSquadNext = NULL;
|
||||
int squadCount = 1;
|
||||
|
||||
CBaseEntity *pEntity = NULL;
|
||||
|
||||
while ((pEntity = UTIL_FindEntityInSphere( pEntity, pev->origin, AFLOCK_MAX_RECRUIT_RADIUS )) != NULL)
|
||||
{
|
||||
CBaseMonster *pRecruit = pEntity->MyMonsterPointer( );
|
||||
|
||||
if ( pRecruit && pRecruit != this && pRecruit->IsAlive() && !pRecruit->m_pCine )
|
||||
{
|
||||
// Can we recruit this guy?
|
||||
if ( FClassnameIs ( pRecruit->pev, "monster_flyer" ) )
|
||||
{
|
||||
squadCount++;
|
||||
SquadAdd( (CFlockingFlyer *)pRecruit );
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
SetThink( IdleThink );// now that flock is formed, go to idle and wait for a player to come along.
|
||||
pev->nextthink = gpGlobals->time;
|
||||
}
|
||||
|
||||
//=========================================================
|
||||
// Searches for boids that are too close and pushes them away
|
||||
//=========================================================
|
||||
void CFlockingFlyer :: SpreadFlock( )
|
||||
{
|
||||
Vector vecDir;
|
||||
float flSpeed;// holds vector magnitude while we fiddle with the direction
|
||||
|
||||
CFlockingFlyer *pList = m_pSquadLeader;
|
||||
while ( pList )
|
||||
{
|
||||
if ( pList != this && ( pev->origin - pList->pev->origin ).Length() <= AFLOCK_TOO_CLOSE )
|
||||
{
|
||||
// push the other away
|
||||
vecDir = ( pList->pev->origin - pev->origin );
|
||||
vecDir = vecDir.Normalize();
|
||||
|
||||
// store the magnitude of the other boid's velocity, and normalize it so we
|
||||
// can average in a course that points away from the leader.
|
||||
flSpeed = pList->pev->velocity.Length();
|
||||
pList->pev->velocity = pList->pev->velocity.Normalize();
|
||||
pList->pev->velocity = ( pList->pev->velocity + vecDir ) * 0.5;
|
||||
pList->pev->velocity = pList->pev->velocity * flSpeed;
|
||||
}
|
||||
|
||||
pList = pList->m_pSquadNext;
|
||||
}
|
||||
}
|
||||
|
||||
//=========================================================
|
||||
// Alters the caller's course if he's too close to others
|
||||
//
|
||||
// This function should **ONLY** be called when Caller's velocity is normalized!!
|
||||
//=========================================================
|
||||
void CFlockingFlyer :: SpreadFlock2 ( )
|
||||
{
|
||||
Vector vecDir;
|
||||
|
||||
CFlockingFlyer *pList = m_pSquadLeader;
|
||||
while ( pList )
|
||||
{
|
||||
if ( pList != this && ( pev->origin - pList->pev->origin ).Length() <= AFLOCK_TOO_CLOSE )
|
||||
{
|
||||
vecDir = ( pev->origin - pList->pev->origin );
|
||||
vecDir = vecDir.Normalize();
|
||||
|
||||
pev->velocity = (pev->velocity + vecDir);
|
||||
}
|
||||
|
||||
pList = pList->m_pSquadNext;
|
||||
}
|
||||
}
|
||||
|
||||
//=========================================================
|
||||
// FBoidPathBlocked - returns TRUE if there is an obstacle ahead
|
||||
//=========================================================
|
||||
BOOL CFlockingFlyer :: FPathBlocked( )
|
||||
{
|
||||
TraceResult tr;
|
||||
Vector vecDist;// used for general measurements
|
||||
Vector vecDir;// used for general measurements
|
||||
BOOL fBlocked;
|
||||
|
||||
if ( m_flFakeBlockedTime > gpGlobals->time )
|
||||
{
|
||||
m_flLastBlockedTime = gpGlobals->time;
|
||||
return TRUE;
|
||||
}
|
||||
|
||||
// use VELOCITY, not angles, not all boids point the direction they are flying
|
||||
//vecDir = UTIL_VecToAngles( pevBoid->velocity );
|
||||
UTIL_MakeVectors ( pev->angles );
|
||||
|
||||
fBlocked = FALSE;// assume the way ahead is clear
|
||||
|
||||
// check for obstacle ahead
|
||||
UTIL_TraceLine(pev->origin, pev->origin + gpGlobals->v_forward * AFLOCK_CHECK_DIST, ignore_monsters, ENT(pev), &tr);
|
||||
if (tr.flFraction != 1.0)
|
||||
{
|
||||
m_flLastBlockedTime = gpGlobals->time;
|
||||
fBlocked = TRUE;
|
||||
}
|
||||
|
||||
// extra wide checks
|
||||
UTIL_TraceLine(pev->origin + gpGlobals->v_right * 12, pev->origin + gpGlobals->v_right * 12 + gpGlobals->v_forward * AFLOCK_CHECK_DIST, ignore_monsters, ENT(pev), &tr);
|
||||
if (tr.flFraction != 1.0)
|
||||
{
|
||||
m_flLastBlockedTime = gpGlobals->time;
|
||||
fBlocked = TRUE;
|
||||
}
|
||||
|
||||
UTIL_TraceLine(pev->origin - gpGlobals->v_right * 12, pev->origin - gpGlobals->v_right * 12 + gpGlobals->v_forward * AFLOCK_CHECK_DIST, ignore_monsters, ENT(pev), &tr);
|
||||
if (tr.flFraction != 1.0)
|
||||
{
|
||||
m_flLastBlockedTime = gpGlobals->time;
|
||||
fBlocked = TRUE;
|
||||
}
|
||||
|
||||
if ( !fBlocked && gpGlobals->time - m_flLastBlockedTime > 6 )
|
||||
{
|
||||
// not blocked, and it's been a few seconds since we've actually been blocked.
|
||||
m_flFakeBlockedTime = gpGlobals->time + RANDOM_LONG(1, 3);
|
||||
}
|
||||
|
||||
return fBlocked;
|
||||
}
|
||||
|
||||
|
||||
//=========================================================
|
||||
// Leader boids use this think every tenth
|
||||
//=========================================================
|
||||
void CFlockingFlyer :: FlockLeaderThink( void )
|
||||
{
|
||||
TraceResult tr;
|
||||
Vector vecDist;// used for general measurements
|
||||
Vector vecDir;// used for general measurements
|
||||
int cProcessed = 0;// keep track of how many other boids we've processed
|
||||
float flLeftSide;
|
||||
float flRightSide;
|
||||
|
||||
|
||||
pev->nextthink = gpGlobals->time + 0.1;
|
||||
|
||||
UTIL_MakeVectors ( pev->angles );
|
||||
|
||||
// is the way ahead clear?
|
||||
if ( !FPathBlocked () )
|
||||
{
|
||||
// if the boid is turning, stop the trend.
|
||||
if ( m_fTurning )
|
||||
{
|
||||
m_fTurning = FALSE;
|
||||
pev->avelocity.y = 0;
|
||||
}
|
||||
|
||||
m_fPathBlocked = FALSE;
|
||||
|
||||
if (pev->speed <= AFLOCK_FLY_SPEED )
|
||||
pev->speed+= 5;
|
||||
|
||||
pev->velocity = gpGlobals->v_forward * pev->speed;
|
||||
|
||||
BoidAdvanceFrame( );
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
// IF we get this far in the function, the leader's path is blocked!
|
||||
m_fPathBlocked = TRUE;
|
||||
|
||||
if ( !m_fTurning)// something in the way and boid is not already turning to avoid
|
||||
{
|
||||
// measure clearance on left and right to pick the best dir to turn
|
||||
UTIL_TraceLine(pev->origin, pev->origin + gpGlobals->v_right * AFLOCK_CHECK_DIST, ignore_monsters, ENT(pev), &tr);
|
||||
vecDist = (tr.vecEndPos - pev->origin);
|
||||
flRightSide = vecDist.Length();
|
||||
|
||||
UTIL_TraceLine(pev->origin, pev->origin - gpGlobals->v_right * AFLOCK_CHECK_DIST, ignore_monsters, ENT(pev), &tr);
|
||||
vecDist = (tr.vecEndPos - pev->origin);
|
||||
flLeftSide = vecDist.Length();
|
||||
|
||||
// turn right if more clearance on right side
|
||||
if ( flRightSide > flLeftSide )
|
||||
{
|
||||
pev->avelocity.y = -AFLOCK_TURN_RATE;
|
||||
m_fTurning = TRUE;
|
||||
}
|
||||
// default to left turn :)
|
||||
else if ( flLeftSide > flRightSide )
|
||||
{
|
||||
pev->avelocity.y = AFLOCK_TURN_RATE;
|
||||
m_fTurning = TRUE;
|
||||
}
|
||||
else
|
||||
{
|
||||
// equidistant. Pick randomly between left and right.
|
||||
m_fTurning = TRUE;
|
||||
|
||||
if ( RANDOM_LONG( 0, 1 ) == 0 )
|
||||
{
|
||||
pev->avelocity.y = AFLOCK_TURN_RATE;
|
||||
}
|
||||
else
|
||||
{
|
||||
pev->avelocity.y = -AFLOCK_TURN_RATE;
|
||||
}
|
||||
}
|
||||
}
|
||||
SpreadFlock( );
|
||||
|
||||
pev->velocity = gpGlobals->v_forward * pev->speed;
|
||||
|
||||
// check and make sure we aren't about to plow into the ground, don't let it happen
|
||||
UTIL_TraceLine(pev->origin, pev->origin - gpGlobals->v_up * 16, ignore_monsters, ENT(pev), &tr);
|
||||
if (tr.flFraction != 1.0 && pev->velocity.z < 0 )
|
||||
pev->velocity.z = 0;
|
||||
|
||||
// maybe it did, though.
|
||||
if ( FBitSet (pev->flags, FL_ONGROUND) )
|
||||
{
|
||||
UTIL_SetOrigin (pev, pev->origin + Vector ( 0 , 0 , 1 ) );
|
||||
pev->velocity.z = 0;
|
||||
}
|
||||
|
||||
if ( m_flFlockNextSoundTime < gpGlobals->time )
|
||||
{
|
||||
MakeSound();
|
||||
m_flFlockNextSoundTime = gpGlobals->time + RANDOM_FLOAT( 1, 3 );
|
||||
}
|
||||
|
||||
BoidAdvanceFrame( );
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
//=========================================================
|
||||
// follower boids execute this code when flocking
|
||||
//=========================================================
|
||||
void CFlockingFlyer :: FlockFollowerThink( void )
|
||||
{
|
||||
TraceResult tr;
|
||||
Vector vecDist;
|
||||
Vector vecDir;
|
||||
Vector vecDirToLeader;
|
||||
float flDistToLeader;
|
||||
|
||||
pev->nextthink = gpGlobals->time + 0.1;
|
||||
|
||||
if ( IsLeader() || !InSquad() )
|
||||
{
|
||||
// the leader has been killed and this flyer suddenly finds himself the leader.
|
||||
SetThink ( FlockLeaderThink );
|
||||
return;
|
||||
}
|
||||
|
||||
vecDirToLeader = ( m_pSquadLeader->pev->origin - pev->origin );
|
||||
flDistToLeader = vecDirToLeader.Length();
|
||||
|
||||
// match heading with leader
|
||||
pev->angles = m_pSquadLeader->pev->angles;
|
||||
|
||||
//
|
||||
// We can see the leader, so try to catch up to it
|
||||
//
|
||||
if ( FInViewCone ( m_pSquadLeader ) )
|
||||
{
|
||||
// if we're too far away, speed up
|
||||
if ( flDistToLeader > AFLOCK_TOO_FAR )
|
||||
{
|
||||
m_flGoalSpeed = m_pSquadLeader->pev->velocity.Length() * 1.5;
|
||||
}
|
||||
|
||||
// if we're too close, slow down
|
||||
else if ( flDistToLeader < AFLOCK_TOO_CLOSE )
|
||||
{
|
||||
m_flGoalSpeed = m_pSquadLeader->pev->velocity.Length() * 0.5;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
// wait up! the leader isn't out in front, so we slow down to let him pass
|
||||
m_flGoalSpeed = m_pSquadLeader->pev->velocity.Length() * 0.5;
|
||||
}
|
||||
|
||||
SpreadFlock2();
|
||||
|
||||
pev->speed = pev->velocity.Length();
|
||||
pev->velocity = pev->velocity.Normalize();
|
||||
|
||||
// if we are too far from leader, average a vector towards it into our current velocity
|
||||
if ( flDistToLeader > AFLOCK_TOO_FAR )
|
||||
{
|
||||
vecDirToLeader = vecDirToLeader.Normalize();
|
||||
pev->velocity = (pev->velocity + vecDirToLeader) * 0.5;
|
||||
}
|
||||
|
||||
// clamp speeds and handle acceleration
|
||||
if ( m_flGoalSpeed > AFLOCK_FLY_SPEED * 2 )
|
||||
{
|
||||
m_flGoalSpeed = AFLOCK_FLY_SPEED * 2;
|
||||
}
|
||||
|
||||
if ( pev->speed < m_flGoalSpeed )
|
||||
{
|
||||
pev->speed += AFLOCK_ACCELERATE;
|
||||
}
|
||||
else if ( pev->speed > m_flGoalSpeed )
|
||||
{
|
||||
pev->speed -= AFLOCK_ACCELERATE;
|
||||
}
|
||||
|
||||
pev->velocity = pev->velocity * pev->speed;
|
||||
|
||||
BoidAdvanceFrame( );
|
||||
}
|
||||
|
||||
/*
|
||||
// Is this boid's course blocked?
|
||||
if ( FBoidPathBlocked (pev) )
|
||||
{
|
||||
// course is still blocked from last time. Just keep flying along adjusted
|
||||
// velocity
|
||||
if ( m_fCourseAdjust )
|
||||
{
|
||||
pev->velocity = m_vecAdjustedVelocity * pev->speed;
|
||||
return;
|
||||
}
|
||||
else // set course adjust flag and calculate adjusted velocity
|
||||
{
|
||||
m_fCourseAdjust = TRUE;
|
||||
|
||||
// use VELOCITY, not angles, not all boids point the direction they are flying
|
||||
//vecDir = UTIL_VecToAngles( pev->velocity );
|
||||
//UTIL_MakeVectors ( vecDir );
|
||||
|
||||
UTIL_MakeVectors ( pev->angles );
|
||||
|
||||
// measure clearance on left and right to pick the best dir to turn
|
||||
UTIL_TraceLine(pev->origin, pev->origin + gpGlobals->v_right * AFLOCK_CHECK_DIST, ignore_monsters, ENT(pev), &tr);
|
||||
vecDist = (tr.vecEndPos - pev->origin);
|
||||
flRightSide = vecDist.Length();
|
||||
|
||||
UTIL_TraceLine(pev->origin, pev->origin - gpGlobals->v_right * AFLOCK_CHECK_DIST, ignore_monsters, ENT(pev), &tr);
|
||||
vecDist = (tr.vecEndPos - pev->origin);
|
||||
flLeftSide = vecDist.Length();
|
||||
|
||||
// slide right if more clearance on right side
|
||||
if ( flRightSide > flLeftSide )
|
||||
{
|
||||
m_vecAdjustedVelocity = gpGlobals->v_right;
|
||||
}
|
||||
// else slide left
|
||||
else
|
||||
{
|
||||
m_vecAdjustedVelocity = gpGlobals->v_right * -1;
|
||||
}
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
||||
// if we make it this far, boids path is CLEAR!
|
||||
m_fCourseAdjust = FALSE;
|
||||
*/
|
||||
|
||||
|
||||
//=========================================================
|
||||
//
|
||||
// SquadUnlink(), Unlink the squad pointers.
|
||||
//
|
||||
//=========================================================
|
||||
void CFlockingFlyer :: SquadUnlink( void )
|
||||
{
|
||||
m_pSquadLeader = NULL;
|
||||
m_pSquadNext = NULL;
|
||||
}
|
||||
|
||||
//=========================================================
|
||||
//
|
||||
// SquadAdd(), add pAdd to my squad
|
||||
//
|
||||
//=========================================================
|
||||
void CFlockingFlyer :: SquadAdd( CFlockingFlyer *pAdd )
|
||||
{
|
||||
ASSERT( pAdd!=NULL );
|
||||
ASSERT( !pAdd->InSquad() );
|
||||
ASSERT( this->IsLeader() );
|
||||
|
||||
pAdd->m_pSquadNext = m_pSquadNext;
|
||||
m_pSquadNext = pAdd;
|
||||
pAdd->m_pSquadLeader = this;
|
||||
}
|
||||
//=========================================================
|
||||
//
|
||||
// SquadRemove(), remove pRemove from my squad.
|
||||
// If I am pRemove, promote m_pSquadNext to leader
|
||||
//
|
||||
//=========================================================
|
||||
void CFlockingFlyer :: SquadRemove( CFlockingFlyer *pRemove )
|
||||
{
|
||||
ASSERT( pRemove!=NULL );
|
||||
ASSERT( this->IsLeader() );
|
||||
ASSERT( pRemove->m_pSquadLeader == this );
|
||||
|
||||
if ( SquadCount() > 2 )
|
||||
{
|
||||
// Removing the leader, promote m_pSquadNext to leader
|
||||
if ( pRemove == this )
|
||||
{
|
||||
CFlockingFlyer *pLeader = m_pSquadNext;
|
||||
|
||||
// copy the enemy LKP to the new leader
|
||||
pLeader->m_vecEnemyLKP = m_vecEnemyLKP;
|
||||
|
||||
if ( pLeader )
|
||||
{
|
||||
CFlockingFlyer *pList = pLeader;
|
||||
|
||||
while ( pList )
|
||||
{
|
||||
pList->m_pSquadLeader = pLeader;
|
||||
pList = pList->m_pSquadNext;
|
||||
}
|
||||
|
||||
}
|
||||
SquadUnlink();
|
||||
}
|
||||
else // removing a node
|
||||
{
|
||||
CFlockingFlyer *pList = this;
|
||||
|
||||
// Find the node before pRemove
|
||||
while ( pList->m_pSquadNext != pRemove )
|
||||
{
|
||||
// assert to test valid list construction
|
||||
ASSERT( pList->m_pSquadNext != NULL );
|
||||
pList = pList->m_pSquadNext;
|
||||
}
|
||||
// List validity
|
||||
ASSERT( pList->m_pSquadNext == pRemove );
|
||||
|
||||
// Relink without pRemove
|
||||
pList->m_pSquadNext = pRemove->m_pSquadNext;
|
||||
|
||||
// Unlink pRemove
|
||||
pRemove->SquadUnlink();
|
||||
}
|
||||
}
|
||||
else
|
||||
SquadDisband();
|
||||
}
|
||||
//=========================================================
|
||||
//
|
||||
// SquadCount(), return the number of members of this squad
|
||||
// callable from leaders & followers
|
||||
//
|
||||
//=========================================================
|
||||
int CFlockingFlyer :: SquadCount( void )
|
||||
{
|
||||
CFlockingFlyer *pList = m_pSquadLeader;
|
||||
int squadCount = 0;
|
||||
while ( pList )
|
||||
{
|
||||
squadCount++;
|
||||
pList = pList->m_pSquadNext;
|
||||
}
|
||||
|
||||
return squadCount;
|
||||
}
|
||||
|
||||
//=========================================================
|
||||
//
|
||||
// SquadDisband(), Unlink all squad members
|
||||
//
|
||||
//=========================================================
|
||||
void CFlockingFlyer :: SquadDisband( void )
|
||||
{
|
||||
CFlockingFlyer *pList = m_pSquadLeader;
|
||||
CFlockingFlyer *pNext;
|
||||
|
||||
while ( pList )
|
||||
{
|
||||
pNext = pList->m_pSquadNext;
|
||||
pList->SquadUnlink();
|
||||
pList = pNext;
|
||||
}
|
||||
}
|
File diff suppressed because it is too large
Load Diff
|
@ -0,0 +1,121 @@
|
|||
/***
|
||||
*
|
||||
* 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"
|
||||
|
||||
class CAirtank : public CGrenade
|
||||
{
|
||||
void Spawn( void );
|
||||
void Precache( void );
|
||||
void EXPORT TankThink( void );
|
||||
void EXPORT TankTouch( CBaseEntity *pOther );
|
||||
int BloodColor( void ) { return DONT_BLEED; };
|
||||
void Killed( entvars_t *pevAttacker, int iGib );
|
||||
|
||||
virtual int Save( CSave &save );
|
||||
virtual int Restore( CRestore &restore );
|
||||
|
||||
static TYPEDESCRIPTION m_SaveData[];
|
||||
|
||||
int m_state;
|
||||
};
|
||||
|
||||
|
||||
LINK_ENTITY_TO_CLASS( item_airtank, CAirtank );
|
||||
TYPEDESCRIPTION CAirtank::m_SaveData[] =
|
||||
{
|
||||
DEFINE_FIELD( CAirtank, m_state, FIELD_INTEGER ),
|
||||
};
|
||||
|
||||
IMPLEMENT_SAVERESTORE( CAirtank, CGrenade );
|
||||
|
||||
|
||||
void CAirtank :: Spawn( void )
|
||||
{
|
||||
Precache( );
|
||||
// motor
|
||||
pev->movetype = MOVETYPE_FLY;
|
||||
pev->solid = SOLID_BBOX;
|
||||
|
||||
SET_MODEL(ENT(pev), "models/w_oxygen.mdl");
|
||||
UTIL_SetSize(pev, Vector( -16, -16, 0), Vector(16, 16, 36));
|
||||
UTIL_SetOrigin( pev, pev->origin );
|
||||
|
||||
SetTouch( TankTouch );
|
||||
SetThink( TankThink );
|
||||
|
||||
pev->flags |= FL_MONSTER;
|
||||
pev->takedamage = DAMAGE_YES;
|
||||
pev->health = 20;
|
||||
pev->dmg = 50;
|
||||
m_state = 1;
|
||||
}
|
||||
|
||||
void CAirtank::Precache( void )
|
||||
{
|
||||
PRECACHE_MODEL("models/w_oxygen.mdl");
|
||||
PRECACHE_SOUND("doors/aliendoor3.wav");
|
||||
}
|
||||
|
||||
|
||||
void CAirtank :: Killed( entvars_t *pevAttacker, int iGib )
|
||||
{
|
||||
pev->owner = ENT( pevAttacker );
|
||||
|
||||
// UNDONE: this should make a big bubble cloud, not an explosion
|
||||
|
||||
Explode( pev->origin, Vector( 0, 0, -1 ) );
|
||||
}
|
||||
|
||||
|
||||
void CAirtank::TankThink( void )
|
||||
{
|
||||
// Fire trigger
|
||||
m_state = 1;
|
||||
SUB_UseTargets( this, USE_TOGGLE, 0 );
|
||||
}
|
||||
|
||||
|
||||
void CAirtank::TankTouch( CBaseEntity *pOther )
|
||||
{
|
||||
if ( !pOther->IsPlayer() )
|
||||
return;
|
||||
|
||||
if (!m_state)
|
||||
{
|
||||
// "no oxygen" sound
|
||||
EMIT_SOUND( ENT(pev), CHAN_BODY, "player/pl_swim2.wav", 1.0, ATTN_NORM );
|
||||
return;
|
||||
}
|
||||
|
||||
CBasePlayer *pPlayer = (CBasePlayer *)CBasePlayer::Instance( pOther->pev );
|
||||
if( !pOther ) return;
|
||||
|
||||
// give player 12 more seconds of air
|
||||
pPlayer->m_fAirFinished = gpGlobals->time + 12;
|
||||
|
||||
// suit recharge sound
|
||||
EMIT_SOUND( ENT(pev), CHAN_VOICE, "doors/aliendoor3.wav", 1.0, ATTN_NORM );
|
||||
|
||||
// recharge airtank in 30 seconds
|
||||
pev->nextthink = gpGlobals->time + 30;
|
||||
m_state = 0;
|
||||
SUB_UseTargets( this, USE_TOGGLE, 1 );
|
||||
}
|
|
@ -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.
|
||||
*
|
||||
* 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.
|
||||
*
|
||||
****/
|
||||
/*
|
||||
|
||||
===== monsters.cpp ========================================================
|
||||
|
||||
Monster-related utility code
|
||||
|
||||
*/
|
||||
|
||||
#include "extdll.h"
|
||||
#include "util.h"
|
||||
#include "cbase.h"
|
||||
#include "animation.h"
|
||||
#include "saverestore.h"
|
||||
|
||||
TYPEDESCRIPTION CBaseAnimating::m_SaveData[] =
|
||||
{
|
||||
DEFINE_FIELD( CBaseMonster, m_flFrameRate, FIELD_FLOAT ),
|
||||
DEFINE_FIELD( CBaseMonster, m_flGroundSpeed, FIELD_FLOAT ),
|
||||
DEFINE_FIELD( CBaseMonster, m_flLastEventCheck, FIELD_TIME ),
|
||||
DEFINE_FIELD( CBaseMonster, m_fSequenceFinished, FIELD_BOOLEAN ),
|
||||
DEFINE_FIELD( CBaseMonster, m_fSequenceLoops, FIELD_BOOLEAN ),
|
||||
};
|
||||
|
||||
IMPLEMENT_SAVERESTORE( CBaseAnimating, CBaseDelay );
|
||||
|
||||
|
||||
//=========================================================
|
||||
// StudioFrameAdvance - advance the animation frame up to the current time
|
||||
// if an flInterval is passed in, only advance animation that number of seconds
|
||||
//=========================================================
|
||||
float CBaseAnimating :: StudioFrameAdvance ( float flInterval )
|
||||
{
|
||||
if (flInterval == 0.0)
|
||||
{
|
||||
flInterval = (gpGlobals->time - pev->animtime);
|
||||
if (flInterval <= 0.001)
|
||||
{
|
||||
pev->animtime = gpGlobals->time;
|
||||
return 0.0;
|
||||
}
|
||||
}
|
||||
if (! pev->animtime)
|
||||
flInterval = 0.0;
|
||||
|
||||
pev->frame += flInterval * m_flFrameRate * pev->framerate;
|
||||
pev->animtime = gpGlobals->time;
|
||||
|
||||
if (pev->frame < 0.0 || pev->frame >= 256.0)
|
||||
{
|
||||
if (m_fSequenceLoops)
|
||||
pev->frame -= (int)(pev->frame / 256.0) * 256.0;
|
||||
else
|
||||
pev->frame = (pev->frame < 0.0) ? 0 : 255;
|
||||
m_fSequenceFinished = TRUE; // just in case it wasn't caught in GetEvents
|
||||
}
|
||||
|
||||
return flInterval;
|
||||
}
|
||||
|
||||
//=========================================================
|
||||
// LookupActivity
|
||||
//=========================================================
|
||||
int CBaseAnimating :: LookupActivity ( int activity )
|
||||
{
|
||||
ASSERT( activity != 0 );
|
||||
void *pmodel = GET_MODEL_PTR( ENT(pev) );
|
||||
|
||||
return ::LookupActivity( pmodel, pev, activity );
|
||||
}
|
||||
|
||||
//=========================================================
|
||||
// LookupActivityHeaviest
|
||||
//
|
||||
// Get activity with highest 'weight'
|
||||
//
|
||||
//=========================================================
|
||||
int CBaseAnimating :: LookupActivityHeaviest ( int activity )
|
||||
{
|
||||
void *pmodel = GET_MODEL_PTR( ENT(pev) );
|
||||
|
||||
return ::LookupActivityHeaviest( pmodel, pev, activity );
|
||||
}
|
||||
|
||||
//=========================================================
|
||||
//=========================================================
|
||||
int CBaseAnimating :: LookupSequence ( const char *label )
|
||||
{
|
||||
void *pmodel = GET_MODEL_PTR( ENT(pev) );
|
||||
|
||||
return ::LookupSequence( pmodel, label );
|
||||
}
|
||||
|
||||
|
||||
//=========================================================
|
||||
//=========================================================
|
||||
void CBaseAnimating :: ResetSequenceInfo ( )
|
||||
{
|
||||
void *pmodel = GET_MODEL_PTR( ENT(pev) );
|
||||
|
||||
GetSequenceInfo( pmodel, pev, &m_flFrameRate, &m_flGroundSpeed );
|
||||
m_fSequenceLoops = ((GetSequenceFlags() & STUDIO_LOOPING) != 0);
|
||||
pev->animtime = gpGlobals->time;
|
||||
pev->framerate = 1.0;
|
||||
m_fSequenceFinished = FALSE;
|
||||
m_flLastEventCheck = gpGlobals->time;
|
||||
}
|
||||
|
||||
|
||||
|
||||
//=========================================================
|
||||
//=========================================================
|
||||
BOOL CBaseAnimating :: GetSequenceFlags( )
|
||||
{
|
||||
void *pmodel = GET_MODEL_PTR( ENT(pev) );
|
||||
|
||||
return ::GetSequenceFlags( pmodel, pev );
|
||||
}
|
||||
|
||||
//=========================================================
|
||||
// DispatchAnimEvents
|
||||
//=========================================================
|
||||
void CBaseAnimating :: DispatchAnimEvents ( float flInterval )
|
||||
{
|
||||
MonsterEvent_t event;
|
||||
|
||||
void *pmodel = GET_MODEL_PTR( ENT(pev) );
|
||||
|
||||
if ( !pmodel )
|
||||
{
|
||||
ALERT( at_aiconsole, "Gibbed monster is thinking!\n" );
|
||||
return;
|
||||
}
|
||||
|
||||
// FIXME: I have to do this or some events get missed, and this is probably causing the problem below
|
||||
flInterval = 0.1;
|
||||
|
||||
// FIX: this still sometimes hits events twice
|
||||
float flStart = pev->frame + (m_flLastEventCheck - pev->animtime) * m_flFrameRate * pev->framerate;
|
||||
float flEnd = pev->frame + flInterval * m_flFrameRate * pev->framerate;
|
||||
m_flLastEventCheck = pev->animtime + flInterval;
|
||||
|
||||
m_fSequenceFinished = FALSE;
|
||||
if (flEnd >= 256 || flEnd <= 0.0)
|
||||
m_fSequenceFinished = TRUE;
|
||||
|
||||
int index = 0;
|
||||
|
||||
while ( (index = GetAnimationEvent( pmodel, pev, &event, flStart, flEnd, index ) ) != 0 )
|
||||
{
|
||||
HandleAnimEvent( &event );
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
//=========================================================
|
||||
//=========================================================
|
||||
float CBaseAnimating :: SetBoneController ( int iController, float flValue )
|
||||
{
|
||||
void *pmodel = GET_MODEL_PTR( ENT(pev) );
|
||||
|
||||
return SetController( pmodel, pev, iController, flValue );
|
||||
}
|
||||
|
||||
//=========================================================
|
||||
//=========================================================
|
||||
void CBaseAnimating :: InitBoneControllers ( void )
|
||||
{
|
||||
void *pmodel = GET_MODEL_PTR( ENT(pev) );
|
||||
|
||||
SetController( pmodel, pev, 0, 0.0 );
|
||||
SetController( pmodel, pev, 1, 0.0 );
|
||||
SetController( pmodel, pev, 2, 0.0 );
|
||||
SetController( pmodel, pev, 3, 0.0 );
|
||||
}
|
||||
|
||||
//=========================================================
|
||||
//=========================================================
|
||||
float CBaseAnimating :: SetBlending ( int iBlender, float flValue )
|
||||
{
|
||||
void *pmodel = GET_MODEL_PTR( ENT(pev) );
|
||||
|
||||
return ::SetBlending( pmodel, pev, iBlender, flValue );
|
||||
}
|
||||
|
||||
//=========================================================
|
||||
//=========================================================
|
||||
void CBaseAnimating :: GetBonePosition ( int iBone, Vector &origin, Vector &angles )
|
||||
{
|
||||
GET_BONE_POSITION( ENT(pev), iBone, origin, angles );
|
||||
}
|
||||
|
||||
//=========================================================
|
||||
//=========================================================
|
||||
void CBaseAnimating :: GetAttachment ( int iAttachment, Vector &origin, Vector &angles )
|
||||
{
|
||||
GET_ATTACHMENT( ENT(pev), iAttachment, origin, angles );
|
||||
}
|
||||
|
||||
//=========================================================
|
||||
//=========================================================
|
||||
int CBaseAnimating :: FindTransition( int iEndingSequence, int iGoalSequence, int *piDir )
|
||||
{
|
||||
void *pmodel = GET_MODEL_PTR( ENT(pev) );
|
||||
|
||||
if (piDir == NULL)
|
||||
{
|
||||
int iDir;
|
||||
int sequence = ::FindTransition( pmodel, iEndingSequence, iGoalSequence, &iDir );
|
||||
if (iDir != 1)
|
||||
return -1;
|
||||
else
|
||||
return sequence;
|
||||
}
|
||||
|
||||
return ::FindTransition( pmodel, iEndingSequence, iGoalSequence, piDir );
|
||||
}
|
||||
|
||||
//=========================================================
|
||||
//=========================================================
|
||||
void CBaseAnimating :: GetAutomovement( Vector &origin, Vector &angles, float flInterval )
|
||||
{
|
||||
|
||||
}
|
||||
|
||||
void CBaseAnimating :: SetBodygroup( int iGroup, int iValue )
|
||||
{
|
||||
::SetBodygroup( GET_MODEL_PTR( ENT(pev) ), pev, iGroup, iValue );
|
||||
}
|
||||
|
||||
int CBaseAnimating :: GetBodygroup( int iGroup )
|
||||
{
|
||||
return ::GetBodygroup( GET_MODEL_PTR( ENT(pev) ), pev, iGroup );
|
||||
}
|
||||
|
||||
|
||||
int CBaseAnimating :: ExtractBbox( int sequence, Vector &mins, Vector &maxs )
|
||||
{
|
||||
return ::ExtractBbox( GET_MODEL_PTR( ENT(pev) ), sequence, mins, maxs );
|
||||
}
|
||||
|
||||
//=========================================================
|
||||
//=========================================================
|
||||
|
||||
void CBaseAnimating :: SetSequenceBox( void )
|
||||
{
|
||||
Vector mins, maxs;
|
||||
|
||||
// Get sequence bbox
|
||||
if ( ExtractBbox( pev->sequence, mins, maxs ) )
|
||||
{
|
||||
// expand box for rotation
|
||||
// find min / max for rotations
|
||||
float yaw = pev->angles.y * (M_PI / 180.0);
|
||||
|
||||
Vector xvector, yvector;
|
||||
xvector.x = cos(yaw);
|
||||
xvector.y = sin(yaw);
|
||||
yvector.x = -sin(yaw);
|
||||
yvector.y = cos(yaw);
|
||||
Vector bounds[2];
|
||||
|
||||
bounds[0] = mins;
|
||||
bounds[1] = maxs;
|
||||
|
||||
Vector rmin( 9999, 9999, 9999 );
|
||||
Vector rmax( -9999, -9999, -9999 );
|
||||
Vector base, transformed;
|
||||
|
||||
for (int i = 0; i <= 1; i++ )
|
||||
{
|
||||
base.x = bounds[i].x;
|
||||
for ( int j = 0; j <= 1; j++ )
|
||||
{
|
||||
base.y = bounds[j].y;
|
||||
for ( int k = 0; k <= 1; k++ )
|
||||
{
|
||||
base.z = bounds[k].z;
|
||||
|
||||
// transform the point
|
||||
transformed.x = xvector.x*base.x + yvector.x*base.y;
|
||||
transformed.y = xvector.y*base.x + yvector.y*base.y;
|
||||
transformed.z = base.z;
|
||||
|
||||
for ( int l = 0; l < 3; l++ )
|
||||
{
|
||||
if (transformed[l] < rmin[l])
|
||||
rmin[l] = transformed[l];
|
||||
if (transformed[l] > rmax[l])
|
||||
rmax[l] = transformed[l];
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
rmin.z = 0;
|
||||
rmax.z = rmin.z + 1;
|
||||
UTIL_SetSize( pev, rmin, rmax );
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,501 @@
|
|||
/***
|
||||
*
|
||||
* 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 <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
|
||||
#include "extdll.h"
|
||||
#include "const.h"
|
||||
#include "studio_ref.h"
|
||||
#include "util.h"
|
||||
|
||||
#include "activitymap.h"
|
||||
|
||||
#ifndef ANIMATION_H
|
||||
#include "animation.h"
|
||||
#endif
|
||||
|
||||
#ifndef SCRIPTEVENT_H
|
||||
#include "scriptevent.h"
|
||||
#endif
|
||||
|
||||
int ExtractBbox( void *pmodel, int sequence, Vector &mins, Vector &maxs )
|
||||
{
|
||||
dstudiohdr_t *pstudiohdr;
|
||||
|
||||
pstudiohdr = (dstudiohdr_t *)pmodel;
|
||||
if (! pstudiohdr)
|
||||
return 0;
|
||||
|
||||
dstudioseqdesc_t *pseqdesc;
|
||||
|
||||
pseqdesc = (dstudioseqdesc_t *)((byte *)pstudiohdr + pstudiohdr->seqindex);
|
||||
|
||||
mins[0] = pseqdesc[ sequence ].bbmin[0];
|
||||
mins[1] = pseqdesc[ sequence ].bbmin[1];
|
||||
mins[2] = pseqdesc[ sequence ].bbmin[2];
|
||||
|
||||
maxs[0] = pseqdesc[ sequence ].bbmax[0];
|
||||
maxs[1] = pseqdesc[ sequence ].bbmax[1];
|
||||
maxs[2] = pseqdesc[ sequence ].bbmax[2];
|
||||
|
||||
return 1;
|
||||
}
|
||||
|
||||
|
||||
int LookupActivity( void *pmodel, entvars_t *pev, int activity )
|
||||
{
|
||||
dstudiohdr_t *pstudiohdr;
|
||||
|
||||
pstudiohdr = (dstudiohdr_t *)pmodel;
|
||||
if (! pstudiohdr)
|
||||
return 0;
|
||||
|
||||
dstudioseqdesc_t *pseqdesc;
|
||||
|
||||
pseqdesc = (dstudioseqdesc_t *)((byte *)pstudiohdr + pstudiohdr->seqindex);
|
||||
|
||||
int weighttotal = 0;
|
||||
int seq = ACTIVITY_NOT_AVAILABLE;
|
||||
for (int i = 0; i < pstudiohdr->numseq; i++)
|
||||
{
|
||||
if (pseqdesc[i].activity == activity)
|
||||
{
|
||||
weighttotal += pseqdesc[i].actweight;
|
||||
if (!weighttotal || RANDOM_LONG(0,weighttotal-1) < pseqdesc[i].actweight)
|
||||
seq = i;
|
||||
}
|
||||
}
|
||||
|
||||
return seq;
|
||||
}
|
||||
|
||||
|
||||
int LookupActivityHeaviest( void *pmodel, entvars_t *pev, int activity )
|
||||
{
|
||||
dstudiohdr_t *pstudiohdr;
|
||||
|
||||
pstudiohdr = (dstudiohdr_t *)pmodel;
|
||||
if ( !pstudiohdr )
|
||||
return 0;
|
||||
|
||||
dstudioseqdesc_t *pseqdesc;
|
||||
|
||||
pseqdesc = (dstudioseqdesc_t *)((byte *)pstudiohdr + pstudiohdr->seqindex);
|
||||
|
||||
int weight = 0;
|
||||
int seq = ACTIVITY_NOT_AVAILABLE;
|
||||
for (int i = 0; i < pstudiohdr->numseq; i++)
|
||||
{
|
||||
if (pseqdesc[i].activity == activity)
|
||||
{
|
||||
if ( pseqdesc[i].actweight > weight )
|
||||
{
|
||||
weight = pseqdesc[i].actweight;
|
||||
seq = i;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return seq;
|
||||
}
|
||||
|
||||
void GetEyePosition ( void *pmodel, Vector &vecEyePosition )
|
||||
{
|
||||
dstudiohdr_t *pstudiohdr;
|
||||
|
||||
pstudiohdr = (dstudiohdr_t *)pmodel;
|
||||
|
||||
if ( !pstudiohdr )
|
||||
{
|
||||
ALERT ( at_error, "GetEyePosition() Can't get pstudiohdr ptr!\n" );
|
||||
return;
|
||||
}
|
||||
|
||||
vecEyePosition = pstudiohdr->eyeposition;
|
||||
}
|
||||
|
||||
int LookupSequence( void *pmodel, const char *label )
|
||||
{
|
||||
dstudiohdr_t *pstudiohdr;
|
||||
|
||||
pstudiohdr = (dstudiohdr_t *)pmodel;
|
||||
if (! pstudiohdr)
|
||||
return 0;
|
||||
|
||||
dstudioseqdesc_t *pseqdesc;
|
||||
|
||||
pseqdesc = (dstudioseqdesc_t *)((byte *)pstudiohdr + pstudiohdr->seqindex);
|
||||
|
||||
for (int i = 0; i < pstudiohdr->numseq; i++)
|
||||
{
|
||||
if (stricmp( pseqdesc[i].label, label ) == 0)
|
||||
return i;
|
||||
}
|
||||
|
||||
return -1;
|
||||
}
|
||||
|
||||
|
||||
int IsSoundEvent( int eventNumber )
|
||||
{
|
||||
if ( eventNumber == SCRIPT_EVENT_SOUND || eventNumber == SCRIPT_EVENT_SOUND_VOICE )
|
||||
return 1;
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
||||
void SequencePrecache( void *pmodel, const char *pSequenceName )
|
||||
{
|
||||
int index = LookupSequence( pmodel, pSequenceName );
|
||||
if ( index >= 0 )
|
||||
{
|
||||
dstudiohdr_t *pstudiohdr;
|
||||
|
||||
pstudiohdr = (dstudiohdr_t *)pmodel;
|
||||
if ( !pstudiohdr || index >= pstudiohdr->numseq )
|
||||
return;
|
||||
|
||||
dstudioseqdesc_t *pseqdesc;
|
||||
dstudioevent_t *pevent;
|
||||
|
||||
pseqdesc = (dstudioseqdesc_t *)((byte *)pstudiohdr + pstudiohdr->seqindex) + index;
|
||||
pevent = (dstudioevent_t *)((byte *)pstudiohdr + pseqdesc->eventindex);
|
||||
|
||||
for (int i = 0; i < pseqdesc->numevents; i++)
|
||||
{
|
||||
// Don't send client-side events to the server AI
|
||||
if ( pevent[i].event >= EVENT_CLIENT )
|
||||
continue;
|
||||
|
||||
// UNDONE: Add a callback to check to see if a sound is precached yet and don't allocate a copy
|
||||
// of it's name if it is.
|
||||
if ( IsSoundEvent( pevent[i].event ) )
|
||||
{
|
||||
if ( !strlen(pevent[i].options) )
|
||||
{
|
||||
ALERT( at_error, "Bad sound event %d in sequence %s :: %s (sound is \"%s\")\n", pevent[i].event, pstudiohdr->name, pSequenceName, pevent[i].options );
|
||||
}
|
||||
|
||||
PRECACHE_SOUND( (char *)STRING( ALLOC_STRING( pevent[i].options )) );
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
void GetSequenceInfo( void *pmodel, entvars_t *pev, float *pflFrameRate, float *pflGroundSpeed )
|
||||
{
|
||||
dstudiohdr_t *pstudiohdr;
|
||||
|
||||
pstudiohdr = (dstudiohdr_t *)pmodel;
|
||||
if (! pstudiohdr)
|
||||
return;
|
||||
|
||||
dstudioseqdesc_t *pseqdesc;
|
||||
|
||||
if (pev->sequence >= pstudiohdr->numseq)
|
||||
{
|
||||
*pflFrameRate = 0.0;
|
||||
*pflGroundSpeed = 0.0;
|
||||
return;
|
||||
}
|
||||
|
||||
pseqdesc = (dstudioseqdesc_t *)((byte *)pstudiohdr + pstudiohdr->seqindex) + (int)pev->sequence;
|
||||
|
||||
if (pseqdesc->numframes > 1)
|
||||
{
|
||||
*pflFrameRate = 256 * pseqdesc->fps / (pseqdesc->numframes - 1);
|
||||
*pflGroundSpeed = sqrt( pseqdesc->linearmovement[0]*pseqdesc->linearmovement[0]+ pseqdesc->linearmovement[1]*pseqdesc->linearmovement[1]+ pseqdesc->linearmovement[2]*pseqdesc->linearmovement[2] );
|
||||
*pflGroundSpeed = *pflGroundSpeed * pseqdesc->fps / (pseqdesc->numframes - 1);
|
||||
}
|
||||
else
|
||||
{
|
||||
*pflFrameRate = 256.0;
|
||||
*pflGroundSpeed = 0.0;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
int GetSequenceFlags( void *pmodel, entvars_t *pev )
|
||||
{
|
||||
dstudiohdr_t *pstudiohdr;
|
||||
|
||||
pstudiohdr = (dstudiohdr_t *)pmodel;
|
||||
if ( !pstudiohdr || pev->sequence >= pstudiohdr->numseq )
|
||||
return 0;
|
||||
|
||||
dstudioseqdesc_t *pseqdesc;
|
||||
pseqdesc = (dstudioseqdesc_t *)((byte *)pstudiohdr + pstudiohdr->seqindex) + (int)pev->sequence;
|
||||
|
||||
return pseqdesc->flags;
|
||||
}
|
||||
|
||||
|
||||
int GetAnimationEvent( void *pmodel, entvars_t *pev, MonsterEvent_t *pMonsterEvent, float flStart, float flEnd, int index )
|
||||
{
|
||||
dstudiohdr_t *pstudiohdr;
|
||||
|
||||
pstudiohdr = (dstudiohdr_t *)pmodel;
|
||||
if ( !pstudiohdr || pev->sequence >= pstudiohdr->numseq || !pMonsterEvent )
|
||||
return 0;
|
||||
|
||||
int events = 0;
|
||||
|
||||
dstudioseqdesc_t *pseqdesc;
|
||||
dstudioevent_t *pevent;
|
||||
|
||||
pseqdesc = (dstudioseqdesc_t *)((byte *)pstudiohdr + pstudiohdr->seqindex) + (int)pev->sequence;
|
||||
pevent = (dstudioevent_t *)((byte *)pstudiohdr + pseqdesc->eventindex);
|
||||
|
||||
if (pseqdesc->numevents == 0 || index > pseqdesc->numevents )
|
||||
return 0;
|
||||
|
||||
if (pseqdesc->numframes > 1)
|
||||
{
|
||||
flStart *= (pseqdesc->numframes - 1) / 256.0;
|
||||
flEnd *= (pseqdesc->numframes - 1) / 256.0;
|
||||
}
|
||||
else
|
||||
{
|
||||
flStart = 0;
|
||||
flEnd = 1.0;
|
||||
}
|
||||
|
||||
for (; index < pseqdesc->numevents; index++)
|
||||
{
|
||||
// Don't send client-side events to the server AI
|
||||
if ( pevent[index].event >= EVENT_CLIENT )
|
||||
continue;
|
||||
|
||||
if ( (pevent[index].frame >= flStart && pevent[index].frame < flEnd) ||
|
||||
((pseqdesc->flags & STUDIO_LOOPING) && flEnd >= pseqdesc->numframes - 1 && pevent[index].frame < flEnd - pseqdesc->numframes + 1) )
|
||||
{
|
||||
pMonsterEvent->event = pevent[index].event;
|
||||
pMonsterEvent->options = pevent[index].options;
|
||||
return index + 1;
|
||||
}
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
float SetController( void *pmodel, entvars_t *pev, int iController, float flValue )
|
||||
{
|
||||
dstudiohdr_t *pstudiohdr;
|
||||
|
||||
pstudiohdr = (dstudiohdr_t *)pmodel;
|
||||
if (! pstudiohdr)
|
||||
return flValue;
|
||||
|
||||
dstudiobonecontroller_t *pbonecontroller = (dstudiobonecontroller_t *)((byte *)pstudiohdr + pstudiohdr->bonecontrollerindex);
|
||||
|
||||
// find first controller that matches the index
|
||||
for (int i = 0; i < pstudiohdr->numbonecontrollers; i++, pbonecontroller++)
|
||||
{
|
||||
if (pbonecontroller->index == iController)
|
||||
break;
|
||||
}
|
||||
if (i >= pstudiohdr->numbonecontrollers)
|
||||
return flValue;
|
||||
|
||||
// wrap 0..360 if it's a rotational controller
|
||||
|
||||
if (pbonecontroller->type & (STUDIO_XR | STUDIO_YR | STUDIO_ZR))
|
||||
{
|
||||
// ugly hack, invert value if end < start
|
||||
if (pbonecontroller->end < pbonecontroller->start)
|
||||
flValue = -flValue;
|
||||
|
||||
// does the controller not wrap?
|
||||
if (pbonecontroller->start + 359.0 >= pbonecontroller->end)
|
||||
{
|
||||
if (flValue > ((pbonecontroller->start + pbonecontroller->end) / 2.0) + 180)
|
||||
flValue = flValue - 360;
|
||||
if (flValue < ((pbonecontroller->start + pbonecontroller->end) / 2.0) - 180)
|
||||
flValue = flValue + 360;
|
||||
}
|
||||
else
|
||||
{
|
||||
if (flValue > 360)
|
||||
flValue = flValue - (int)(flValue / 360.0) * 360.0;
|
||||
else if (flValue < 0)
|
||||
flValue = flValue + (int)((flValue / -360.0) + 1) * 360.0;
|
||||
}
|
||||
}
|
||||
|
||||
int setting = 255 * (flValue - pbonecontroller->start) / (pbonecontroller->end - pbonecontroller->start);
|
||||
|
||||
if (setting < 0) setting = 0;
|
||||
if (setting > 255) setting = 255;
|
||||
pev->controller[iController] = setting;
|
||||
|
||||
return setting * (1.0 / 255.0) * (pbonecontroller->end - pbonecontroller->start) + pbonecontroller->start;
|
||||
}
|
||||
|
||||
|
||||
float SetBlending( void *pmodel, entvars_t *pev, int iBlender, float flValue )
|
||||
{
|
||||
dstudiohdr_t *pstudiohdr;
|
||||
|
||||
pstudiohdr = (dstudiohdr_t *)pmodel;
|
||||
if (! pstudiohdr)
|
||||
return flValue;
|
||||
|
||||
dstudioseqdesc_t *pseqdesc;
|
||||
|
||||
pseqdesc = (dstudioseqdesc_t *)((byte *)pstudiohdr + pstudiohdr->seqindex) + (int)pev->sequence;
|
||||
|
||||
if (pseqdesc->blendtype[iBlender] == 0)
|
||||
return flValue;
|
||||
|
||||
if (pseqdesc->blendtype[iBlender] & (STUDIO_XR | STUDIO_YR | STUDIO_ZR))
|
||||
{
|
||||
// ugly hack, invert value if end < start
|
||||
if (pseqdesc->blendend[iBlender] < pseqdesc->blendstart[iBlender])
|
||||
flValue = -flValue;
|
||||
|
||||
// does the controller not wrap?
|
||||
if (pseqdesc->blendstart[iBlender] + 359.0 >= pseqdesc->blendend[iBlender])
|
||||
{
|
||||
if (flValue > ((pseqdesc->blendstart[iBlender] + pseqdesc->blendend[iBlender]) / 2.0) + 180)
|
||||
flValue = flValue - 360;
|
||||
if (flValue < ((pseqdesc->blendstart[iBlender] + pseqdesc->blendend[iBlender]) / 2.0) - 180)
|
||||
flValue = flValue + 360;
|
||||
}
|
||||
}
|
||||
|
||||
int setting = 255 * (flValue - pseqdesc->blendstart[iBlender]) / (pseqdesc->blendend[iBlender] - pseqdesc->blendstart[iBlender]);
|
||||
|
||||
if (setting < 0) setting = 0;
|
||||
if (setting > 255) setting = 255;
|
||||
|
||||
pev->blending[iBlender] = setting;
|
||||
|
||||
return setting * (1.0 / 255.0) * (pseqdesc->blendend[iBlender] - pseqdesc->blendstart[iBlender]) + pseqdesc->blendstart[iBlender];
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
int FindTransition( void *pmodel, int iEndingAnim, int iGoalAnim, int *piDir )
|
||||
{
|
||||
dstudiohdr_t *pstudiohdr;
|
||||
|
||||
pstudiohdr = (dstudiohdr_t *)pmodel;
|
||||
if (! pstudiohdr)
|
||||
return iGoalAnim;
|
||||
|
||||
dstudioseqdesc_t *pseqdesc;
|
||||
pseqdesc = (dstudioseqdesc_t *)((byte *)pstudiohdr + pstudiohdr->seqindex);
|
||||
|
||||
// bail if we're going to or from a node 0
|
||||
if (pseqdesc[iEndingAnim].entrynode == 0 || pseqdesc[iGoalAnim].entrynode == 0)
|
||||
{
|
||||
return iGoalAnim;
|
||||
}
|
||||
|
||||
int iEndNode;
|
||||
|
||||
// ALERT( at_console, "from %d to %d: ", pEndNode->iEndNode, pGoalNode->iStartNode );
|
||||
|
||||
if (*piDir > 0)
|
||||
{
|
||||
iEndNode = pseqdesc[iEndingAnim].exitnode;
|
||||
}
|
||||
else
|
||||
{
|
||||
iEndNode = pseqdesc[iEndingAnim].entrynode;
|
||||
}
|
||||
|
||||
if (iEndNode == pseqdesc[iGoalAnim].entrynode)
|
||||
{
|
||||
*piDir = 1;
|
||||
return iGoalAnim;
|
||||
}
|
||||
|
||||
byte *pTransition = ((byte *)pstudiohdr + pstudiohdr->transitionindex);
|
||||
|
||||
int iInternNode = pTransition[(iEndNode-1)*pstudiohdr->numtransitions + (pseqdesc[iGoalAnim].entrynode-1)];
|
||||
|
||||
if (iInternNode == 0)
|
||||
return iGoalAnim;
|
||||
|
||||
int i;
|
||||
|
||||
// look for someone going
|
||||
for (i = 0; i < pstudiohdr->numseq; i++)
|
||||
{
|
||||
if (pseqdesc[i].entrynode == iEndNode && pseqdesc[i].exitnode == iInternNode)
|
||||
{
|
||||
*piDir = 1;
|
||||
return i;
|
||||
}
|
||||
if (pseqdesc[i].nodeflags)
|
||||
{
|
||||
if (pseqdesc[i].exitnode == iEndNode && pseqdesc[i].entrynode == iInternNode)
|
||||
{
|
||||
*piDir = -1;
|
||||
return i;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
ALERT( at_console, "error in transition graph" );
|
||||
return iGoalAnim;
|
||||
}
|
||||
|
||||
void SetBodygroup( void *pmodel, entvars_t *pev, int iGroup, int iValue )
|
||||
{
|
||||
dstudiohdr_t *pstudiohdr;
|
||||
|
||||
pstudiohdr = (dstudiohdr_t *)pmodel;
|
||||
if (! pstudiohdr)
|
||||
return;
|
||||
|
||||
if (iGroup > pstudiohdr->numbodyparts)
|
||||
return;
|
||||
|
||||
dstudiobodyparts_t *pbodypart = (dstudiobodyparts_t *)((byte *)pstudiohdr + pstudiohdr->bodypartindex) + iGroup;
|
||||
|
||||
if (iValue >= pbodypart->nummodels)
|
||||
return;
|
||||
|
||||
int iCurrent = (pev->body / pbodypart->base) % pbodypart->nummodels;
|
||||
|
||||
pev->body = (pev->body - (iCurrent * pbodypart->base) + (iValue * pbodypart->base));
|
||||
}
|
||||
|
||||
|
||||
int GetBodygroup( void *pmodel, entvars_t *pev, int iGroup )
|
||||
{
|
||||
dstudiohdr_t *pstudiohdr;
|
||||
|
||||
pstudiohdr = (dstudiohdr_t *)pmodel;
|
||||
if (! pstudiohdr)
|
||||
return 0;
|
||||
|
||||
if (iGroup > pstudiohdr->numbodyparts)
|
||||
return 0;
|
||||
|
||||
dstudiobodyparts_t *pbodypart = (dstudiobodyparts_t *)((byte *)pstudiohdr + pstudiohdr->bodypartindex) + iGroup;
|
||||
|
||||
if (pbodypart->nummodels <= 1)
|
||||
return 0;
|
||||
|
||||
int iCurrent = (pev->body / pbodypart->base) % pbodypart->nummodels;
|
||||
|
||||
return iCurrent;
|
||||
}
|
|
@ -0,0 +1,45 @@
|
|||
/***
|
||||
*
|
||||
* 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.
|
||||
*
|
||||
****/
|
||||
#ifndef ANIMATION_H
|
||||
#define ANIMATION_H
|
||||
|
||||
#define ACTIVITY_NOT_AVAILABLE -1
|
||||
|
||||
#include "monsterevent.h"
|
||||
|
||||
extern int IsSoundEvent( int eventNumber );
|
||||
|
||||
int LookupActivity( void *pmodel, entvars_t *pev, int activity );
|
||||
int LookupActivityHeaviest( void *pmodel, entvars_t *pev, int activity );
|
||||
int LookupSequence( void *pmodel, const char *label );
|
||||
void GetSequenceInfo( void *pmodel, entvars_t *pev, float *pflFrameRate, float *pflGroundSpeed );
|
||||
int GetSequenceFlags( void *pmodel, entvars_t *pev );
|
||||
int LookupAnimationEvents( void *pmodel, entvars_t *pev, float flStart, float flEnd );
|
||||
float SetController( void *pmodel, entvars_t *pev, int iController, float flValue );
|
||||
float SetBlending( void *pmodel, entvars_t *pev, int iBlender, float flValue );
|
||||
void GetEyePosition( void *pmodel, Vector &vecEyePosition );
|
||||
void SequencePrecache( void *pmodel, const char *pSequenceName );
|
||||
int FindTransition( void *pmodel, int iEndingAnim, int iGoalAnim, int *piDir );
|
||||
void SetBodygroup( void *pmodel, entvars_t *pev, int iGroup, int iValue );
|
||||
int GetBodygroup( void *pmodel, entvars_t *pev, int iGroup );
|
||||
|
||||
int GetAnimationEvent( void *pmodel, entvars_t *pev, MonsterEvent_t *pMonsterEvent, float flStart, float flEnd, int index );
|
||||
int ExtractBbox( void *pmodel, int sequence, Vector &mins, Vector &maxs );
|
||||
|
||||
// From /common/ref_studio.h
|
||||
#define STUDIO_LOOPING 0x0001
|
||||
|
||||
|
||||
#endif //ANIMATION_H
|
File diff suppressed because it is too large
Load Diff
|
@ -0,0 +1,428 @@
|
|||
/***
|
||||
*
|
||||
* 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.
|
||||
*
|
||||
****/
|
||||
//=========================================================
|
||||
// barnacle - stationary ceiling mounted 'fishing' monster
|
||||
//=========================================================
|
||||
|
||||
#include "extdll.h"
|
||||
#include "util.h"
|
||||
#include "cbase.h"
|
||||
#include "monsters.h"
|
||||
#include "schedule.h"
|
||||
|
||||
#define BARNACLE_BODY_HEIGHT 44 // how 'tall' the barnacle's model is.
|
||||
#define BARNACLE_PULL_SPEED 8
|
||||
#define BARNACLE_KILL_VICTIM_DELAY 5 // how many seconds after pulling prey in to gib them.
|
||||
|
||||
//=========================================================
|
||||
// Monster's Anim Events Go Here
|
||||
//=========================================================
|
||||
#define BARNACLE_AE_PUKEGIB 2
|
||||
|
||||
class CBarnacle : public CBaseMonster
|
||||
{
|
||||
public:
|
||||
void Spawn( void );
|
||||
void Precache( void );
|
||||
CBaseEntity *TongueTouchEnt ( float *pflLength );
|
||||
int Classify ( void );
|
||||
void HandleAnimEvent( MonsterEvent_t *pEvent );
|
||||
void EXPORT BarnacleThink ( void );
|
||||
void EXPORT WaitTillDead ( void );
|
||||
void Killed( entvars_t *pevAttacker, int iGib );
|
||||
int TakeDamage( entvars_t *pevInflictor, entvars_t *pevAttacker, float flDamage, int bitsDamageType );
|
||||
virtual int Save( CSave &save );
|
||||
virtual int Restore( CRestore &restore );
|
||||
static TYPEDESCRIPTION m_SaveData[];
|
||||
|
||||
float m_flAltitude;
|
||||
float m_flKillVictimTime;
|
||||
int m_cGibs;// barnacle loads up on gibs each time it kills something.
|
||||
BOOL m_fTongueExtended;
|
||||
BOOL m_fLiftingPrey;
|
||||
float m_flTongueAdj;
|
||||
};
|
||||
LINK_ENTITY_TO_CLASS( monster_barnacle, CBarnacle );
|
||||
|
||||
TYPEDESCRIPTION CBarnacle::m_SaveData[] =
|
||||
{
|
||||
DEFINE_FIELD( CBarnacle, m_flAltitude, FIELD_FLOAT ),
|
||||
DEFINE_FIELD( CBarnacle, m_flKillVictimTime, FIELD_TIME ),
|
||||
DEFINE_FIELD( CBarnacle, m_cGibs, FIELD_INTEGER ),// barnacle loads up on gibs each time it kills something.
|
||||
DEFINE_FIELD( CBarnacle, m_fTongueExtended, FIELD_BOOLEAN ),
|
||||
DEFINE_FIELD( CBarnacle, m_fLiftingPrey, FIELD_BOOLEAN ),
|
||||
DEFINE_FIELD( CBarnacle, m_flTongueAdj, FIELD_FLOAT ),
|
||||
};
|
||||
|
||||
IMPLEMENT_SAVERESTORE( CBarnacle, CBaseMonster );
|
||||
|
||||
|
||||
//=========================================================
|
||||
// Classify - indicates this monster's place in the
|
||||
// relationship table.
|
||||
//=========================================================
|
||||
int CBarnacle :: Classify ( void )
|
||||
{
|
||||
return CLASS_ALIEN_MONSTER;
|
||||
}
|
||||
|
||||
//=========================================================
|
||||
// HandleAnimEvent - catches the monster-specific messages
|
||||
// that occur when tagged animation frames are played.
|
||||
//
|
||||
// Returns number of events handled, 0 if none.
|
||||
//=========================================================
|
||||
void CBarnacle :: HandleAnimEvent( MonsterEvent_t *pEvent )
|
||||
{
|
||||
switch( pEvent->event )
|
||||
{
|
||||
case BARNACLE_AE_PUKEGIB:
|
||||
CGib::SpawnRandomGibs( pev, 1, 1 );
|
||||
break;
|
||||
default:
|
||||
CBaseMonster::HandleAnimEvent( pEvent );
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
//=========================================================
|
||||
// Spawn
|
||||
//=========================================================
|
||||
void CBarnacle :: Spawn()
|
||||
{
|
||||
Precache( );
|
||||
|
||||
SET_MODEL(ENT(pev), "models/barnacle.mdl");
|
||||
UTIL_SetSize( pev, Vector(-16, -16, -32), Vector(16, 16, 0) );
|
||||
|
||||
pev->solid = SOLID_SLIDEBOX;
|
||||
pev->movetype = MOVETYPE_NONE;
|
||||
pev->takedamage = DAMAGE_AIM;
|
||||
m_bloodColor = BLOOD_COLOR_RED;
|
||||
pev->effects = EF_INVLIGHT; // take light from the ceiling
|
||||
pev->health = 25;
|
||||
m_flFieldOfView = 0.5;// indicates the width of this monster's forward view cone ( as a dotproduct result )
|
||||
m_MonsterState = MONSTERSTATE_NONE;
|
||||
m_flKillVictimTime = 0;
|
||||
m_cGibs = 0;
|
||||
m_fLiftingPrey = FALSE;
|
||||
m_flTongueAdj = -100;
|
||||
|
||||
InitBoneControllers();
|
||||
|
||||
SetActivity ( ACT_IDLE );
|
||||
|
||||
SetThink ( BarnacleThink );
|
||||
pev->nextthink = gpGlobals->time + 0.5;
|
||||
|
||||
UTIL_SetOrigin ( pev, pev->origin );
|
||||
}
|
||||
|
||||
int CBarnacle::TakeDamage( entvars_t *pevInflictor, entvars_t *pevAttacker, float flDamage, int bitsDamageType )
|
||||
{
|
||||
if ( bitsDamageType & DMG_CLUB )
|
||||
{
|
||||
flDamage = pev->health;
|
||||
}
|
||||
|
||||
return CBaseMonster::TakeDamage( pevInflictor, pevAttacker, flDamage, bitsDamageType );
|
||||
}
|
||||
|
||||
//=========================================================
|
||||
//=========================================================
|
||||
void CBarnacle :: BarnacleThink ( void )
|
||||
{
|
||||
CBaseEntity *pTouchEnt;
|
||||
CBaseMonster *pVictim;
|
||||
float flLength;
|
||||
|
||||
pev->nextthink = gpGlobals->time + 0.1;
|
||||
|
||||
if ( m_hEnemy != NULL )
|
||||
{
|
||||
// barnacle has prey.
|
||||
|
||||
if ( !m_hEnemy->IsAlive() )
|
||||
{
|
||||
// someone (maybe even the barnacle) killed the prey. Reset barnacle.
|
||||
m_fLiftingPrey = FALSE;// indicate that we're not lifting prey.
|
||||
m_hEnemy = NULL;
|
||||
return;
|
||||
}
|
||||
|
||||
if ( m_fLiftingPrey )
|
||||
{
|
||||
if ( m_hEnemy != NULL && m_hEnemy->pev->deadflag != DEAD_NO )
|
||||
{
|
||||
// crap, someone killed the prey on the way up.
|
||||
m_hEnemy = NULL;
|
||||
m_fLiftingPrey = FALSE;
|
||||
return;
|
||||
}
|
||||
|
||||
// still pulling prey.
|
||||
Vector vecNewEnemyOrigin = m_hEnemy->pev->origin;
|
||||
vecNewEnemyOrigin.x = pev->origin.x;
|
||||
vecNewEnemyOrigin.y = pev->origin.y;
|
||||
|
||||
// guess as to where their neck is
|
||||
vecNewEnemyOrigin.x -= 6 * cos(m_hEnemy->pev->angles.y * M_PI/180.0);
|
||||
vecNewEnemyOrigin.y -= 6 * sin(m_hEnemy->pev->angles.y * M_PI/180.0);
|
||||
|
||||
m_flAltitude -= BARNACLE_PULL_SPEED;
|
||||
vecNewEnemyOrigin.z += BARNACLE_PULL_SPEED;
|
||||
|
||||
if ( fabs( pev->origin.z - ( vecNewEnemyOrigin.z + m_hEnemy->pev->view_ofs.z - 8 ) ) < BARNACLE_BODY_HEIGHT )
|
||||
{
|
||||
// prey has just been lifted into position ( if the victim origin + eye height + 8 is higher than the bottom of the barnacle, it is assumed that the head is within barnacle's body )
|
||||
m_fLiftingPrey = FALSE;
|
||||
|
||||
EMIT_SOUND( ENT(pev), CHAN_WEAPON, "barnacle/bcl_bite3.wav", 1, ATTN_NORM );
|
||||
|
||||
pVictim = m_hEnemy->MyMonsterPointer();
|
||||
|
||||
m_flKillVictimTime = gpGlobals->time + 10;// now that the victim is in place, the killing bite will be administered in 10 seconds.
|
||||
|
||||
if ( pVictim )
|
||||
{
|
||||
pVictim->BarnacleVictimBitten( pev );
|
||||
SetActivity ( ACT_EAT );
|
||||
}
|
||||
}
|
||||
|
||||
UTIL_SetOrigin ( m_hEnemy->pev, vecNewEnemyOrigin );
|
||||
}
|
||||
else
|
||||
{
|
||||
// prey is lifted fully into feeding position and is dangling there.
|
||||
|
||||
pVictim = m_hEnemy->MyMonsterPointer();
|
||||
|
||||
if ( m_flKillVictimTime != -1 && gpGlobals->time > m_flKillVictimTime )
|
||||
{
|
||||
// kill!
|
||||
if ( pVictim )
|
||||
{
|
||||
pVictim->TakeDamage ( pev, pev, pVictim->pev->health, DMG_SLASH | DMG_ALWAYSGIB );
|
||||
m_cGibs = 3;
|
||||
}
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
// bite prey every once in a while
|
||||
if ( pVictim && ( RANDOM_LONG(0,49) == 0 ) )
|
||||
{
|
||||
switch ( RANDOM_LONG(0,2) )
|
||||
{
|
||||
case 0: EMIT_SOUND( ENT(pev), CHAN_WEAPON, "barnacle/bcl_chew1.wav", 1, ATTN_NORM ); break;
|
||||
case 1: EMIT_SOUND( ENT(pev), CHAN_WEAPON, "barnacle/bcl_chew2.wav", 1, ATTN_NORM ); break;
|
||||
case 2: EMIT_SOUND( ENT(pev), CHAN_WEAPON, "barnacle/bcl_chew3.wav", 1, ATTN_NORM ); break;
|
||||
}
|
||||
|
||||
pVictim->BarnacleVictimBitten( pev );
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
// barnacle has no prey right now, so just idle and check to see if anything is touching the tongue.
|
||||
|
||||
// If idle and no nearby client, don't think so often
|
||||
if ( FNullEnt( FIND_CLIENT_IN_PVS( edict() ) ) )
|
||||
pev->nextthink = gpGlobals->time + RANDOM_FLOAT(1,1.5); // Stagger a bit to keep barnacles from thinking on the same frame
|
||||
|
||||
if ( m_fSequenceFinished )
|
||||
{// this is done so barnacle will fidget.
|
||||
SetActivity ( ACT_IDLE );
|
||||
m_flTongueAdj = -100;
|
||||
}
|
||||
|
||||
if ( m_cGibs && RANDOM_LONG(0,99) == 1 )
|
||||
{
|
||||
// cough up a gib.
|
||||
CGib::SpawnRandomGibs( pev, 1, 1 );
|
||||
m_cGibs--;
|
||||
|
||||
switch ( RANDOM_LONG(0,2) )
|
||||
{
|
||||
case 0: EMIT_SOUND( ENT(pev), CHAN_WEAPON, "barnacle/bcl_chew1.wav", 1, ATTN_NORM ); break;
|
||||
case 1: EMIT_SOUND( ENT(pev), CHAN_WEAPON, "barnacle/bcl_chew2.wav", 1, ATTN_NORM ); break;
|
||||
case 2: EMIT_SOUND( ENT(pev), CHAN_WEAPON, "barnacle/bcl_chew3.wav", 1, ATTN_NORM ); break;
|
||||
}
|
||||
}
|
||||
|
||||
pTouchEnt = TongueTouchEnt( &flLength );
|
||||
|
||||
if ( pTouchEnt != NULL && m_fTongueExtended )
|
||||
{
|
||||
// tongue is fully extended, and is touching someone.
|
||||
if ( pTouchEnt->FBecomeProne() )
|
||||
{
|
||||
EMIT_SOUND( ENT(pev), CHAN_WEAPON, "barnacle/bcl_alert2.wav", 1, ATTN_NORM );
|
||||
|
||||
SetSequenceByName ( "attack1" );
|
||||
m_flTongueAdj = -20;
|
||||
|
||||
m_hEnemy = pTouchEnt;
|
||||
|
||||
pTouchEnt->pev->movetype = MOVETYPE_FLY;
|
||||
pTouchEnt->pev->velocity = g_vecZero;
|
||||
pTouchEnt->pev->basevelocity = g_vecZero;
|
||||
pTouchEnt->pev->origin.x = pev->origin.x;
|
||||
pTouchEnt->pev->origin.y = pev->origin.y;
|
||||
|
||||
m_fLiftingPrey = TRUE;// indicate that we should be lifting prey.
|
||||
m_flKillVictimTime = -1;// set this to a bogus time while the victim is lifted.
|
||||
|
||||
m_flAltitude = (pev->origin.z - pTouchEnt->EyePosition().z);
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
// calculate a new length for the tongue to be clear of anything else that moves under it.
|
||||
if ( m_flAltitude < flLength )
|
||||
{
|
||||
// if tongue is higher than is should be, lower it kind of slowly.
|
||||
m_flAltitude += BARNACLE_PULL_SPEED;
|
||||
m_fTongueExtended = FALSE;
|
||||
}
|
||||
else
|
||||
{
|
||||
m_flAltitude = flLength;
|
||||
m_fTongueExtended = TRUE;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
// ALERT( at_console, "tounge %f\n", m_flAltitude + m_flTongueAdj );
|
||||
SetBoneController( 0, -(m_flAltitude + m_flTongueAdj) );
|
||||
StudioFrameAdvance( 0.1 );
|
||||
}
|
||||
|
||||
//=========================================================
|
||||
// Killed.
|
||||
//=========================================================
|
||||
void CBarnacle :: Killed( entvars_t *pevAttacker, int iGib )
|
||||
{
|
||||
CBaseMonster *pVictim;
|
||||
|
||||
pev->solid = SOLID_NOT;
|
||||
pev->takedamage = DAMAGE_NO;
|
||||
|
||||
if ( m_hEnemy != NULL )
|
||||
{
|
||||
pVictim = m_hEnemy->MyMonsterPointer();
|
||||
|
||||
if ( pVictim )
|
||||
{
|
||||
pVictim->BarnacleVictimReleased();
|
||||
}
|
||||
}
|
||||
|
||||
// CGib::SpawnRandomGibs( pev, 4, 1 );
|
||||
|
||||
switch ( RANDOM_LONG ( 0, 1 ) )
|
||||
{
|
||||
case 0: EMIT_SOUND( ENT(pev), CHAN_WEAPON, "barnacle/bcl_die1.wav", 1, ATTN_NORM ); break;
|
||||
case 1: EMIT_SOUND( ENT(pev), CHAN_WEAPON, "barnacle/bcl_die3.wav", 1, ATTN_NORM ); break;
|
||||
}
|
||||
|
||||
SetActivity ( ACT_DIESIMPLE );
|
||||
SetBoneController( 0, 0 );
|
||||
|
||||
StudioFrameAdvance( 0.1 );
|
||||
|
||||
pev->nextthink = gpGlobals->time + 0.1;
|
||||
SetThink ( WaitTillDead );
|
||||
}
|
||||
|
||||
//=========================================================
|
||||
//=========================================================
|
||||
void CBarnacle :: WaitTillDead ( void )
|
||||
{
|
||||
pev->nextthink = gpGlobals->time + 0.1;
|
||||
|
||||
float flInterval = StudioFrameAdvance( 0.1 );
|
||||
DispatchAnimEvents ( flInterval );
|
||||
|
||||
if ( m_fSequenceFinished )
|
||||
{
|
||||
// death anim finished.
|
||||
StopAnimation();
|
||||
SetThink ( NULL );
|
||||
}
|
||||
}
|
||||
|
||||
//=========================================================
|
||||
// Precache - precaches all resources this monster needs
|
||||
//=========================================================
|
||||
void CBarnacle :: Precache()
|
||||
{
|
||||
PRECACHE_MODEL("models/barnacle.mdl");
|
||||
|
||||
PRECACHE_SOUND("barnacle/bcl_alert2.wav");//happy, lifting food up
|
||||
PRECACHE_SOUND("barnacle/bcl_bite3.wav");//just got food to mouth
|
||||
PRECACHE_SOUND("barnacle/bcl_chew1.wav");
|
||||
PRECACHE_SOUND("barnacle/bcl_chew2.wav");
|
||||
PRECACHE_SOUND("barnacle/bcl_chew3.wav");
|
||||
PRECACHE_SOUND("barnacle/bcl_die1.wav" );
|
||||
PRECACHE_SOUND("barnacle/bcl_die3.wav" );
|
||||
}
|
||||
|
||||
//=========================================================
|
||||
// TongueTouchEnt - does a trace along the barnacle's tongue
|
||||
// to see if any entity is touching it. Also stores the length
|
||||
// of the trace in the int pointer provided.
|
||||
//=========================================================
|
||||
#define BARNACLE_CHECK_SPACING 8
|
||||
CBaseEntity *CBarnacle :: TongueTouchEnt ( float *pflLength )
|
||||
{
|
||||
TraceResult tr;
|
||||
float length;
|
||||
|
||||
// trace once to hit architecture and see if the tongue needs to change position.
|
||||
UTIL_TraceLine ( pev->origin, pev->origin - Vector ( 0 , 0 , 2048 ), ignore_monsters, ENT(pev), &tr );
|
||||
length = fabs( pev->origin.z - tr.vecEndPos.z );
|
||||
if ( pflLength )
|
||||
{
|
||||
*pflLength = length;
|
||||
}
|
||||
|
||||
Vector delta = Vector( BARNACLE_CHECK_SPACING, BARNACLE_CHECK_SPACING, 0 );
|
||||
Vector mins = pev->origin - delta;
|
||||
Vector maxs = pev->origin + delta;
|
||||
maxs.z = pev->origin.z;
|
||||
mins.z -= length;
|
||||
|
||||
CBaseEntity *pList[10];
|
||||
int count = UTIL_EntitiesInBox( pList, 10, mins, maxs, (FL_CLIENT|FL_MONSTER) );
|
||||
if ( count )
|
||||
{
|
||||
for ( int i = 0; i < count; i++ )
|
||||
{
|
||||
// only clients and monsters
|
||||
if ( pList[i] != this && IRelationship( pList[i] ) > R_NO && pList[ i ]->pev->deadflag == DEAD_NO ) // this ent is one of our enemies. Barnacle tries to eat it.
|
||||
{
|
||||
return pList[i];
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return NULL;
|
||||
}
|
|
@ -0,0 +1,841 @@
|
|||
/***
|
||||
*
|
||||
* 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 barney dying for scripted sequences?
|
||||
#define BARNEY_AE_DRAW ( 2 )
|
||||
#define BARNEY_AE_SHOOT ( 3 )
|
||||
#define BARNEY_AE_HOLSTER ( 4 )
|
||||
|
||||
#define BARNEY_BODY_GUNHOLSTERED 0
|
||||
#define BARNEY_BODY_GUNDRAWN 1
|
||||
#define BARNEY_BODY_GUNGONE 2
|
||||
|
||||
class CBarney : public CTalkMonster
|
||||
{
|
||||
public:
|
||||
void Spawn( void );
|
||||
void Precache( void );
|
||||
void SetYawSpeed( void );
|
||||
int ISoundMask( void );
|
||||
void BarneyFirePistol( void );
|
||||
void AlertSound( void );
|
||||
int Classify ( void );
|
||||
void HandleAnimEvent( MonsterEvent_t *pEvent );
|
||||
|
||||
void RunTask( Task_t *pTask );
|
||||
void StartTask( Task_t *pTask );
|
||||
virtual int ObjectCaps( void ) { return CTalkMonster :: ObjectCaps() | FCAP_IMPULSE_USE; }
|
||||
int TakeDamage( entvars_t* pevInflictor, entvars_t* pevAttacker, float flDamage, int bitsDamageType);
|
||||
BOOL CheckRangeAttack1 ( float flDot, float flDist );
|
||||
|
||||
void DeclineFollowing( void );
|
||||
|
||||
// Override these to set behavior
|
||||
Schedule_t *GetScheduleOfType ( int Type );
|
||||
Schedule_t *GetSchedule ( void );
|
||||
MONSTERSTATE GetIdealState ( void );
|
||||
|
||||
void DeathSound( void );
|
||||
void PainSound( void );
|
||||
|
||||
void TalkInit( void );
|
||||
|
||||
void TraceAttack( entvars_t *pevAttacker, float flDamage, Vector vecDir, TraceResult *ptr, int bitsDamageType);
|
||||
void Killed( entvars_t *pevAttacker, int iGib );
|
||||
|
||||
virtual int Save( CSave &save );
|
||||
virtual int Restore( CRestore &restore );
|
||||
static TYPEDESCRIPTION m_SaveData[];
|
||||
|
||||
BOOL m_fGunDrawn;
|
||||
float m_painTime;
|
||||
float m_checkAttackTime;
|
||||
BOOL m_lastAttackCheck;
|
||||
|
||||
// UNDONE: What is this for? It isn't used?
|
||||
float m_flPlayerDamage;// how much pain has the player inflicted on me?
|
||||
|
||||
CUSTOM_SCHEDULES;
|
||||
};
|
||||
|
||||
LINK_ENTITY_TO_CLASS( monster_barney, CBarney );
|
||||
|
||||
TYPEDESCRIPTION CBarney::m_SaveData[] =
|
||||
{
|
||||
DEFINE_FIELD( CBarney, m_fGunDrawn, FIELD_BOOLEAN ),
|
||||
DEFINE_FIELD( CBarney, m_painTime, FIELD_TIME ),
|
||||
DEFINE_FIELD( CBarney, m_checkAttackTime, FIELD_TIME ),
|
||||
DEFINE_FIELD( CBarney, m_lastAttackCheck, FIELD_BOOLEAN ),
|
||||
DEFINE_FIELD( CBarney, m_flPlayerDamage, FIELD_FLOAT ),
|
||||
};
|
||||
|
||||
IMPLEMENT_SAVERESTORE( CBarney, CTalkMonster );
|
||||
|
||||
//=========================================================
|
||||
// AI Schedules Specific to this monster
|
||||
//=========================================================
|
||||
Task_t tlBaFollow[] =
|
||||
{
|
||||
{ TASK_MOVE_TO_TARGET_RANGE,(float)128 }, // Move within 128 of target ent (client)
|
||||
{ TASK_SET_SCHEDULE, (float)SCHED_TARGET_FACE },
|
||||
};
|
||||
|
||||
Schedule_t slBaFollow[] =
|
||||
{
|
||||
{
|
||||
tlBaFollow,
|
||||
ARRAYSIZE ( tlBaFollow ),
|
||||
bits_COND_NEW_ENEMY |
|
||||
bits_COND_LIGHT_DAMAGE |
|
||||
bits_COND_HEAVY_DAMAGE |
|
||||
bits_COND_HEAR_SOUND |
|
||||
bits_COND_PROVOKED,
|
||||
bits_SOUND_DANGER,
|
||||
"Follow"
|
||||
},
|
||||
};
|
||||
|
||||
//=========================================================
|
||||
// BarneyDraw- much better looking draw schedule for when
|
||||
// barney knows who he's gonna attack.
|
||||
//=========================================================
|
||||
Task_t tlBarneyEnemyDraw[] =
|
||||
{
|
||||
{ TASK_STOP_MOVING, 0 },
|
||||
{ TASK_FACE_ENEMY, 0 },
|
||||
{ TASK_PLAY_SEQUENCE_FACE_ENEMY, (float) ACT_ARM },
|
||||
};
|
||||
|
||||
Schedule_t slBarneyEnemyDraw[] =
|
||||
{
|
||||
{
|
||||
tlBarneyEnemyDraw,
|
||||
ARRAYSIZE ( tlBarneyEnemyDraw ),
|
||||
0,
|
||||
0,
|
||||
"Barney Enemy Draw"
|
||||
}
|
||||
};
|
||||
|
||||
Task_t tlBaFaceTarget[] =
|
||||
{
|
||||
{ TASK_SET_ACTIVITY, (float)ACT_IDLE },
|
||||
{ TASK_FACE_TARGET, (float)0 },
|
||||
{ TASK_SET_ACTIVITY, (float)ACT_IDLE },
|
||||
{ TASK_SET_SCHEDULE, (float)SCHED_TARGET_CHASE },
|
||||
};
|
||||
|
||||
Schedule_t slBaFaceTarget[] =
|
||||
{
|
||||
{
|
||||
tlBaFaceTarget,
|
||||
ARRAYSIZE ( tlBaFaceTarget ),
|
||||
bits_COND_CLIENT_PUSH |
|
||||
bits_COND_NEW_ENEMY |
|
||||
bits_COND_LIGHT_DAMAGE |
|
||||
bits_COND_HEAVY_DAMAGE |
|
||||
bits_COND_HEAR_SOUND |
|
||||
bits_COND_PROVOKED,
|
||||
bits_SOUND_DANGER,
|
||||
"FaceTarget"
|
||||
},
|
||||
};
|
||||
|
||||
|
||||
Task_t tlIdleBaStand[] =
|
||||
{
|
||||
{ TASK_STOP_MOVING, 0 },
|
||||
{ TASK_SET_ACTIVITY, (float)ACT_IDLE },
|
||||
{ TASK_WAIT, (float)2 }, // repick IDLESTAND every two seconds.
|
||||
{ TASK_TLK_HEADRESET, (float)0 }, // reset head position
|
||||
};
|
||||
|
||||
Schedule_t slIdleBaStand[] =
|
||||
{
|
||||
{
|
||||
tlIdleBaStand,
|
||||
ARRAYSIZE ( tlIdleBaStand ),
|
||||
bits_COND_NEW_ENEMY |
|
||||
bits_COND_LIGHT_DAMAGE |
|
||||
bits_COND_HEAVY_DAMAGE |
|
||||
bits_COND_HEAR_SOUND |
|
||||
bits_COND_SMELL |
|
||||
bits_COND_PROVOKED,
|
||||
|
||||
bits_SOUND_COMBAT |// sound flags - change these, and you'll break the talking code.
|
||||
//bits_SOUND_PLAYER |
|
||||
//bits_SOUND_WORLD |
|
||||
|
||||
bits_SOUND_DANGER |
|
||||
bits_SOUND_MEAT |// scents
|
||||
bits_SOUND_CARCASS |
|
||||
bits_SOUND_GARBAGE,
|
||||
"IdleStand"
|
||||
},
|
||||
};
|
||||
|
||||
DEFINE_CUSTOM_SCHEDULES( CBarney )
|
||||
{
|
||||
slBaFollow,
|
||||
slBarneyEnemyDraw,
|
||||
slBaFaceTarget,
|
||||
slIdleBaStand,
|
||||
};
|
||||
|
||||
|
||||
IMPLEMENT_CUSTOM_SCHEDULES( CBarney, CTalkMonster );
|
||||
|
||||
void CBarney :: StartTask( Task_t *pTask )
|
||||
{
|
||||
CTalkMonster::StartTask( pTask );
|
||||
}
|
||||
|
||||
void CBarney :: RunTask( Task_t *pTask )
|
||||
{
|
||||
switch ( pTask->iTask )
|
||||
{
|
||||
case TASK_RANGE_ATTACK1:
|
||||
if (m_hEnemy != NULL && (m_hEnemy->IsPlayer()))
|
||||
{
|
||||
pev->framerate = 1.5;
|
||||
}
|
||||
CTalkMonster::RunTask( pTask );
|
||||
break;
|
||||
default:
|
||||
CTalkMonster::RunTask( pTask );
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
//=========================================================
|
||||
// ISoundMask - returns a bit mask indicating which types
|
||||
// of sounds this monster regards.
|
||||
//=========================================================
|
||||
int CBarney :: ISoundMask ( void)
|
||||
{
|
||||
return bits_SOUND_WORLD |
|
||||
bits_SOUND_COMBAT |
|
||||
bits_SOUND_CARCASS |
|
||||
bits_SOUND_MEAT |
|
||||
bits_SOUND_GARBAGE |
|
||||
bits_SOUND_DANGER |
|
||||
bits_SOUND_PLAYER;
|
||||
}
|
||||
|
||||
//=========================================================
|
||||
// Classify - indicates this monster's place in the
|
||||
// relationship table.
|
||||
//=========================================================
|
||||
int CBarney :: Classify ( void )
|
||||
{
|
||||
return CLASS_PLAYER_ALLY;
|
||||
}
|
||||
|
||||
//=========================================================
|
||||
// ALertSound - barney says "Freeze!"
|
||||
//=========================================================
|
||||
void CBarney :: AlertSound( void )
|
||||
{
|
||||
if ( m_hEnemy != NULL )
|
||||
{
|
||||
if ( FOkToSpeak() )
|
||||
{
|
||||
PlaySentence( "BA_ATTACK", RANDOM_FLOAT(2.8, 3.2), VOL_NORM, ATTN_IDLE );
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
//=========================================================
|
||||
// SetYawSpeed - allows each sequence to have a different
|
||||
// turn rate associated with it.
|
||||
//=========================================================
|
||||
void CBarney :: SetYawSpeed ( void )
|
||||
{
|
||||
int ys;
|
||||
|
||||
ys = 0;
|
||||
|
||||
switch ( m_Activity )
|
||||
{
|
||||
case ACT_IDLE:
|
||||
ys = 70;
|
||||
break;
|
||||
case ACT_WALK:
|
||||
ys = 70;
|
||||
break;
|
||||
case ACT_RUN:
|
||||
ys = 90;
|
||||
break;
|
||||
default:
|
||||
ys = 70;
|
||||
break;
|
||||
}
|
||||
|
||||
pev->yaw_speed = ys;
|
||||
}
|
||||
|
||||
|
||||
//=========================================================
|
||||
// CheckRangeAttack1
|
||||
//=========================================================
|
||||
BOOL CBarney :: CheckRangeAttack1 ( float flDot, float flDist )
|
||||
{
|
||||
if ( flDist <= 1024 && flDot >= 0.5 )
|
||||
{
|
||||
if ( gpGlobals->time > m_checkAttackTime )
|
||||
{
|
||||
TraceResult tr;
|
||||
|
||||
Vector shootOrigin = pev->origin + Vector( 0, 0, 55 );
|
||||
CBaseEntity *pEnemy = m_hEnemy;
|
||||
Vector shootTarget = ( (pEnemy->BodyTarget( shootOrigin ) - pEnemy->pev->origin) + m_vecEnemyLKP );
|
||||
UTIL_TraceLine( shootOrigin, shootTarget, dont_ignore_monsters, ENT(pev), &tr );
|
||||
m_checkAttackTime = gpGlobals->time + 1;
|
||||
if ( tr.flFraction == 1.0 || (tr.pHit != NULL && CBaseEntity::Instance(tr.pHit) == pEnemy) )
|
||||
m_lastAttackCheck = TRUE;
|
||||
else
|
||||
m_lastAttackCheck = FALSE;
|
||||
m_checkAttackTime = gpGlobals->time + 1.5;
|
||||
}
|
||||
return m_lastAttackCheck;
|
||||
}
|
||||
return FALSE;
|
||||
}
|
||||
|
||||
|
||||
//=========================================================
|
||||
// BarneyFirePistol - shoots one round from the pistol at
|
||||
// the enemy barney is facing.
|
||||
//=========================================================
|
||||
void CBarney :: BarneyFirePistol ( void )
|
||||
{
|
||||
Vector vecShootOrigin;
|
||||
|
||||
UTIL_MakeVectors(pev->angles);
|
||||
vecShootOrigin = pev->origin + Vector( 0, 0, 55 );
|
||||
Vector vecShootDir = ShootAtEnemy( vecShootOrigin );
|
||||
|
||||
Vector angDir = UTIL_VecToAngles( vecShootDir );
|
||||
SetBlending( 0, angDir.x );
|
||||
pev->effects = EF_MUZZLEFLASH;
|
||||
|
||||
FireBullets(1, vecShootOrigin, vecShootDir, VECTOR_CONE_2DEGREES, 1024, BULLET_MONSTER_9MM );
|
||||
|
||||
int pitchShift = RANDOM_LONG( 0, 20 );
|
||||
|
||||
// Only shift about half the time
|
||||
if ( pitchShift > 10 )
|
||||
pitchShift = 0;
|
||||
else
|
||||
pitchShift -= 5;
|
||||
EMIT_SOUND_DYN( ENT(pev), CHAN_WEAPON, "barney/ba_attack2.wav", 1, ATTN_NORM, 0, 100 + pitchShift );
|
||||
|
||||
CSoundEnt::InsertSound ( bits_SOUND_COMBAT, pev->origin, 384, 0.3 );
|
||||
|
||||
// UNDONE: Reload?
|
||||
m_cAmmoLoaded--;// take away a bullet!
|
||||
}
|
||||
|
||||
//=========================================================
|
||||
// HandleAnimEvent - catches the monster-specific messages
|
||||
// that occur when tagged animation frames are played.
|
||||
//
|
||||
// Returns number of events handled, 0 if none.
|
||||
//=========================================================
|
||||
void CBarney :: HandleAnimEvent( MonsterEvent_t *pEvent )
|
||||
{
|
||||
switch( pEvent->event )
|
||||
{
|
||||
case BARNEY_AE_SHOOT:
|
||||
BarneyFirePistol();
|
||||
break;
|
||||
|
||||
case BARNEY_AE_DRAW:
|
||||
// barney's bodygroup switches here so he can pull gun from holster
|
||||
pev->body = BARNEY_BODY_GUNDRAWN;
|
||||
m_fGunDrawn = TRUE;
|
||||
break;
|
||||
|
||||
case BARNEY_AE_HOLSTER:
|
||||
// change bodygroup to replace gun in holster
|
||||
pev->body = BARNEY_BODY_GUNHOLSTERED;
|
||||
m_fGunDrawn = FALSE;
|
||||
break;
|
||||
|
||||
default:
|
||||
CTalkMonster::HandleAnimEvent( pEvent );
|
||||
}
|
||||
}
|
||||
|
||||
//=========================================================
|
||||
// Spawn
|
||||
//=========================================================
|
||||
void CBarney :: Spawn()
|
||||
{
|
||||
Precache( );
|
||||
|
||||
SET_MODEL(ENT(pev), "models/barney.mdl");
|
||||
UTIL_SetSize(pev, VEC_HUMAN_HULL_MIN, VEC_HUMAN_HULL_MAX);
|
||||
|
||||
pev->solid = SOLID_SLIDEBOX;
|
||||
pev->movetype = MOVETYPE_STEP;
|
||||
m_bloodColor = BLOOD_COLOR_RED;
|
||||
pev->health = gSkillData.barneyHealth;
|
||||
pev->view_ofs = Vector ( 0, 0, 50 );// position of the eyes relative to monster's origin.
|
||||
m_flFieldOfView = VIEW_FIELD_WIDE; // NOTE: we need a wide field of view so npc will notice player and say hello
|
||||
m_MonsterState = MONSTERSTATE_NONE;
|
||||
|
||||
pev->body = 0; // gun in holster
|
||||
m_fGunDrawn = FALSE;
|
||||
|
||||
m_afCapability = bits_CAP_HEAR | bits_CAP_TURN_HEAD | bits_CAP_DOORS_GROUP;
|
||||
|
||||
MonsterInit();
|
||||
SetUse( FollowerUse );
|
||||
}
|
||||
|
||||
//=========================================================
|
||||
// Precache - precaches all resources this monster needs
|
||||
//=========================================================
|
||||
void CBarney :: Precache()
|
||||
{
|
||||
PRECACHE_MODEL("models/barney.mdl");
|
||||
|
||||
PRECACHE_SOUND("barney/ba_attack1.wav" );
|
||||
PRECACHE_SOUND("barney/ba_attack2.wav" );
|
||||
|
||||
PRECACHE_SOUND("barney/ba_pain1.wav");
|
||||
PRECACHE_SOUND("barney/ba_pain2.wav");
|
||||
PRECACHE_SOUND("barney/ba_pain3.wav");
|
||||
|
||||
PRECACHE_SOUND("barney/ba_die1.wav");
|
||||
PRECACHE_SOUND("barney/ba_die2.wav");
|
||||
PRECACHE_SOUND("barney/ba_die3.wav");
|
||||
|
||||
// every new barney must call this, otherwise
|
||||
// when a level is loaded, nobody will talk (time is reset to 0)
|
||||
TalkInit();
|
||||
CTalkMonster::Precache();
|
||||
}
|
||||
|
||||
// Init talk data
|
||||
void CBarney :: TalkInit()
|
||||
{
|
||||
|
||||
CTalkMonster::TalkInit();
|
||||
|
||||
// scientists speach group names (group names are in sentences.txt)
|
||||
|
||||
m_szGrp[TLK_ANSWER] = "BA_ANSWER";
|
||||
m_szGrp[TLK_QUESTION] = "BA_QUESTION";
|
||||
m_szGrp[TLK_IDLE] = "BA_IDLE";
|
||||
m_szGrp[TLK_STARE] = "BA_STARE";
|
||||
m_szGrp[TLK_USE] = "BA_OK";
|
||||
m_szGrp[TLK_UNUSE] = "BA_WAIT";
|
||||
m_szGrp[TLK_STOP] = "BA_STOP";
|
||||
|
||||
m_szGrp[TLK_NOSHOOT] = "BA_SCARED";
|
||||
m_szGrp[TLK_HELLO] = "BA_HELLO";
|
||||
|
||||
m_szGrp[TLK_PLHURT1] = "!BA_CUREA";
|
||||
m_szGrp[TLK_PLHURT2] = "!BA_CUREB";
|
||||
m_szGrp[TLK_PLHURT3] = "!BA_CUREC";
|
||||
|
||||
m_szGrp[TLK_PHELLO] = NULL; //"BA_PHELLO"; // UNDONE
|
||||
m_szGrp[TLK_PIDLE] = NULL; //"BA_PIDLE"; // UNDONE
|
||||
m_szGrp[TLK_PQUESTION] = "BA_PQUEST"; // UNDONE
|
||||
|
||||
m_szGrp[TLK_SMELL] = "BA_SMELL";
|
||||
|
||||
m_szGrp[TLK_WOUND] = "BA_WOUND";
|
||||
m_szGrp[TLK_MORTAL] = "BA_MORTAL";
|
||||
|
||||
// get voice for head - just one barney voice for now
|
||||
m_voicePitch = 100;
|
||||
}
|
||||
|
||||
|
||||
static BOOL IsFacing( entvars_t *pevTest, const Vector &reference )
|
||||
{
|
||||
Vector vecDir = (reference - pevTest->origin);
|
||||
vecDir.z = 0;
|
||||
vecDir = vecDir.Normalize();
|
||||
Vector forward, angle;
|
||||
angle = pevTest->viewangles;
|
||||
angle.x = 0;
|
||||
UTIL_MakeVectorsPrivate( angle, forward, NULL, NULL );
|
||||
// He's facing me, he meant it
|
||||
if ( DotProduct( forward, vecDir ) > 0.96 ) // +/- 15 degrees or so
|
||||
{
|
||||
return TRUE;
|
||||
}
|
||||
return FALSE;
|
||||
}
|
||||
|
||||
|
||||
int CBarney :: TakeDamage( entvars_t* pevInflictor, entvars_t* pevAttacker, float flDamage, int bitsDamageType)
|
||||
{
|
||||
// make sure friends talk about it if player hurts talkmonsters...
|
||||
int ret = CTalkMonster::TakeDamage(pevInflictor, pevAttacker, flDamage, bitsDamageType);
|
||||
if ( !IsAlive() || pev->deadflag == DEAD_DYING )
|
||||
return ret;
|
||||
|
||||
if ( m_MonsterState != MONSTERSTATE_PRONE && (pevAttacker->flags & FL_CLIENT) )
|
||||
{
|
||||
m_flPlayerDamage += flDamage;
|
||||
|
||||
// This is a heurstic to determine if the player intended to harm me
|
||||
// If I have an enemy, we can't establish intent (may just be crossfire)
|
||||
if ( m_hEnemy == NULL )
|
||||
{
|
||||
// If the player was facing directly at me, or I'm already suspicious, get mad
|
||||
if ( (m_afMemory & bits_MEMORY_SUSPICIOUS) || IsFacing( pevAttacker, pev->origin ) )
|
||||
{
|
||||
// Alright, now I'm pissed!
|
||||
PlaySentence( "BA_MAD", 4, VOL_NORM, ATTN_NORM );
|
||||
|
||||
Remember( bits_MEMORY_PROVOKED );
|
||||
StopFollowing( TRUE );
|
||||
}
|
||||
else
|
||||
{
|
||||
// Hey, be careful with that
|
||||
PlaySentence( "BA_SHOT", 4, VOL_NORM, ATTN_NORM );
|
||||
Remember( bits_MEMORY_SUSPICIOUS );
|
||||
}
|
||||
}
|
||||
else if ( !(m_hEnemy->IsPlayer()) && pev->deadflag == DEAD_NO )
|
||||
{
|
||||
PlaySentence( "BA_SHOT", 4, VOL_NORM, ATTN_NORM );
|
||||
}
|
||||
}
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
|
||||
//=========================================================
|
||||
// PainSound
|
||||
//=========================================================
|
||||
void CBarney :: PainSound ( void )
|
||||
{
|
||||
if (gpGlobals->time < m_painTime)
|
||||
return;
|
||||
|
||||
m_painTime = gpGlobals->time + RANDOM_FLOAT(0.5, 0.75);
|
||||
|
||||
switch (RANDOM_LONG(0,2))
|
||||
{
|
||||
case 0: EMIT_SOUND_DYN( ENT(pev), CHAN_VOICE, "barney/ba_pain1.wav", 1, ATTN_NORM, 0, GetVoicePitch()); break;
|
||||
case 1: EMIT_SOUND_DYN( ENT(pev), CHAN_VOICE, "barney/ba_pain2.wav", 1, ATTN_NORM, 0, GetVoicePitch()); break;
|
||||
case 2: EMIT_SOUND_DYN( ENT(pev), CHAN_VOICE, "barney/ba_pain3.wav", 1, ATTN_NORM, 0, GetVoicePitch()); break;
|
||||
}
|
||||
}
|
||||
|
||||
//=========================================================
|
||||
// DeathSound
|
||||
//=========================================================
|
||||
void CBarney :: DeathSound ( void )
|
||||
{
|
||||
switch (RANDOM_LONG(0,2))
|
||||
{
|
||||
case 0: EMIT_SOUND_DYN( ENT(pev), CHAN_VOICE, "barney/ba_die1.wav", 1, ATTN_NORM, 0, GetVoicePitch()); break;
|
||||
case 1: EMIT_SOUND_DYN( ENT(pev), CHAN_VOICE, "barney/ba_die2.wav", 1, ATTN_NORM, 0, GetVoicePitch()); break;
|
||||
case 2: EMIT_SOUND_DYN( ENT(pev), CHAN_VOICE, "barney/ba_die3.wav", 1, ATTN_NORM, 0, GetVoicePitch()); break;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
void CBarney::TraceAttack( entvars_t *pevAttacker, float flDamage, Vector vecDir, TraceResult *ptr, int bitsDamageType)
|
||||
{
|
||||
switch( ptr->iHitgroup)
|
||||
{
|
||||
case HITGROUP_CHEST:
|
||||
case HITGROUP_STOMACH:
|
||||
if (bitsDamageType & (DMG_BULLET | DMG_SLASH | DMG_BLAST))
|
||||
{
|
||||
flDamage = flDamage / 2;
|
||||
}
|
||||
break;
|
||||
case 10:
|
||||
if (bitsDamageType & (DMG_BULLET | DMG_SLASH | DMG_CLUB))
|
||||
{
|
||||
flDamage -= 20;
|
||||
if (flDamage <= 0)
|
||||
{
|
||||
UTIL_Ricochet( ptr->vecEndPos, 1.0 );
|
||||
flDamage = 0.01;
|
||||
}
|
||||
}
|
||||
// always a head shot
|
||||
ptr->iHitgroup = HITGROUP_HEAD;
|
||||
break;
|
||||
}
|
||||
|
||||
CTalkMonster::TraceAttack( pevAttacker, flDamage, vecDir, ptr, bitsDamageType );
|
||||
}
|
||||
|
||||
|
||||
void CBarney::Killed( entvars_t *pevAttacker, int iGib )
|
||||
{
|
||||
if ( pev->body < BARNEY_BODY_GUNGONE )
|
||||
{// drop the gun!
|
||||
Vector vecGunPos;
|
||||
Vector vecGunAngles;
|
||||
|
||||
pev->body = BARNEY_BODY_GUNGONE;
|
||||
|
||||
GetAttachment( 0, vecGunPos, vecGunAngles );
|
||||
|
||||
CBaseEntity *pGun = DropItem( "weapon_9mmhandgun", vecGunPos, vecGunAngles );
|
||||
}
|
||||
|
||||
SetUse( NULL );
|
||||
CTalkMonster::Killed( pevAttacker, iGib );
|
||||
}
|
||||
|
||||
//=========================================================
|
||||
// AI Schedules Specific to this monster
|
||||
//=========================================================
|
||||
|
||||
Schedule_t* CBarney :: GetScheduleOfType ( int Type )
|
||||
{
|
||||
Schedule_t *psched;
|
||||
|
||||
switch( Type )
|
||||
{
|
||||
case SCHED_ARM_WEAPON:
|
||||
if ( m_hEnemy != NULL )
|
||||
{
|
||||
// face enemy, then draw.
|
||||
return slBarneyEnemyDraw;
|
||||
}
|
||||
break;
|
||||
|
||||
// Hook these to make a looping schedule
|
||||
case SCHED_TARGET_FACE:
|
||||
// call base class default so that barney will talk
|
||||
// when 'used'
|
||||
psched = CTalkMonster::GetScheduleOfType(Type);
|
||||
|
||||
if (psched == slIdleStand)
|
||||
return slBaFaceTarget; // override this for different target face behavior
|
||||
else
|
||||
return psched;
|
||||
|
||||
case SCHED_TARGET_CHASE:
|
||||
return slBaFollow;
|
||||
|
||||
case SCHED_IDLE_STAND:
|
||||
// call base class default so that scientist will talk
|
||||
// when standing during idle
|
||||
psched = CTalkMonster::GetScheduleOfType(Type);
|
||||
|
||||
if (psched == slIdleStand)
|
||||
{
|
||||
// just look straight ahead.
|
||||
return slIdleBaStand;
|
||||
}
|
||||
else
|
||||
return psched;
|
||||
}
|
||||
|
||||
return CTalkMonster::GetScheduleOfType( Type );
|
||||
}
|
||||
|
||||
//=========================================================
|
||||
// GetSchedule - Decides which type of schedule best suits
|
||||
// the monster's current state and conditions. Then calls
|
||||
// monster's member function to get a pointer to a schedule
|
||||
// of the proper type.
|
||||
//=========================================================
|
||||
Schedule_t *CBarney :: GetSchedule ( void )
|
||||
{
|
||||
if ( HasConditions( bits_COND_HEAR_SOUND ) )
|
||||
{
|
||||
CSound *pSound;
|
||||
pSound = PBestSound();
|
||||
|
||||
ASSERT( pSound != NULL );
|
||||
if ( pSound && (pSound->m_iType & bits_SOUND_DANGER) )
|
||||
return GetScheduleOfType( SCHED_TAKE_COVER_FROM_BEST_SOUND );
|
||||
}
|
||||
if ( HasConditions( bits_COND_ENEMY_DEAD ) && FOkToSpeak() )
|
||||
{
|
||||
PlaySentence( "BA_KILL", 4, VOL_NORM, ATTN_NORM );
|
||||
}
|
||||
|
||||
switch( m_MonsterState )
|
||||
{
|
||||
case MONSTERSTATE_COMBAT:
|
||||
{
|
||||
// dead enemy
|
||||
if ( HasConditions( bits_COND_ENEMY_DEAD ) )
|
||||
{
|
||||
// call base class, all code to handle dead enemies is centralized there.
|
||||
return CBaseMonster :: GetSchedule();
|
||||
}
|
||||
|
||||
// always act surprized with a new enemy
|
||||
if ( HasConditions( bits_COND_NEW_ENEMY ) && HasConditions( bits_COND_LIGHT_DAMAGE) )
|
||||
return GetScheduleOfType( SCHED_SMALL_FLINCH );
|
||||
|
||||
// wait for one schedule to draw gun
|
||||
if (!m_fGunDrawn )
|
||||
return GetScheduleOfType( SCHED_ARM_WEAPON );
|
||||
|
||||
if ( HasConditions( bits_COND_HEAVY_DAMAGE ) )
|
||||
return GetScheduleOfType( SCHED_TAKE_COVER_FROM_ENEMY );
|
||||
}
|
||||
break;
|
||||
|
||||
case MONSTERSTATE_ALERT:
|
||||
case MONSTERSTATE_IDLE:
|
||||
if ( HasConditions(bits_COND_LIGHT_DAMAGE | bits_COND_HEAVY_DAMAGE))
|
||||
{
|
||||
// flinch if hurt
|
||||
return GetScheduleOfType( SCHED_SMALL_FLINCH );
|
||||
}
|
||||
|
||||
if ( m_hEnemy == NULL && IsFollowing() )
|
||||
{
|
||||
if ( !m_hTargetEnt->IsAlive() )
|
||||
{
|
||||
// UNDONE: Comment about the recently dead player here?
|
||||
StopFollowing( FALSE );
|
||||
break;
|
||||
}
|
||||
else
|
||||
{
|
||||
if ( HasConditions( bits_COND_CLIENT_PUSH ) )
|
||||
{
|
||||
return GetScheduleOfType( SCHED_MOVE_AWAY_FOLLOW );
|
||||
}
|
||||
return GetScheduleOfType( SCHED_TARGET_FACE );
|
||||
}
|
||||
}
|
||||
|
||||
if ( HasConditions( bits_COND_CLIENT_PUSH ) )
|
||||
{
|
||||
return GetScheduleOfType( SCHED_MOVE_AWAY );
|
||||
}
|
||||
|
||||
// try to say something about smells
|
||||
TrySmellTalk();
|
||||
break;
|
||||
}
|
||||
|
||||
return CTalkMonster::GetSchedule();
|
||||
}
|
||||
|
||||
MONSTERSTATE CBarney :: GetIdealState ( void )
|
||||
{
|
||||
return CTalkMonster::GetIdealState();
|
||||
}
|
||||
|
||||
|
||||
|
||||
void CBarney::DeclineFollowing( void )
|
||||
{
|
||||
PlaySentence( "BA_POK", 2, VOL_NORM, ATTN_NORM );
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
//=========================================================
|
||||
// DEAD BARNEY PROP
|
||||
//
|
||||
// Designer selects a pose in worldcraft, 0 through num_poses-1
|
||||
// this value is added to what is selected as the 'first dead pose'
|
||||
// among the monster's normal animations. All dead poses must
|
||||
// appear sequentially in the model file. Be sure and set
|
||||
// the m_iFirstPose properly!
|
||||
//
|
||||
//=========================================================
|
||||
class CDeadBarney : public CBaseMonster
|
||||
{
|
||||
public:
|
||||
void Spawn( void );
|
||||
int Classify ( void ) { return CLASS_PLAYER_ALLY; }
|
||||
|
||||
void KeyValue( KeyValueData *pkvd );
|
||||
|
||||
int m_iPose;// which sequence to display -- temporary, don't need to save
|
||||
static char *m_szPoses[3];
|
||||
};
|
||||
|
||||
char *CDeadBarney::m_szPoses[] = { "lying_on_back", "lying_on_side", "lying_on_stomach" };
|
||||
|
||||
void CDeadBarney::KeyValue( KeyValueData *pkvd )
|
||||
{
|
||||
if (FStrEq(pkvd->szKeyName, "pose"))
|
||||
{
|
||||
m_iPose = atoi(pkvd->szValue);
|
||||
pkvd->fHandled = TRUE;
|
||||
}
|
||||
else
|
||||
CBaseMonster::KeyValue( pkvd );
|
||||
}
|
||||
|
||||
LINK_ENTITY_TO_CLASS( monster_barney_dead, CDeadBarney );
|
||||
|
||||
//=========================================================
|
||||
// ********** DeadBarney SPAWN **********
|
||||
//=========================================================
|
||||
void CDeadBarney :: Spawn( )
|
||||
{
|
||||
PRECACHE_MODEL("models/barney.mdl");
|
||||
SET_MODEL(ENT(pev), "models/barney.mdl");
|
||||
|
||||
pev->effects = 0;
|
||||
pev->yaw_speed = 8;
|
||||
pev->sequence = 0;
|
||||
m_bloodColor = BLOOD_COLOR_RED;
|
||||
|
||||
pev->sequence = LookupSequence( m_szPoses[m_iPose] );
|
||||
if (pev->sequence == -1)
|
||||
{
|
||||
ALERT ( at_console, "Dead barney with bad pose\n" );
|
||||
}
|
||||
// Corpses have less health
|
||||
pev->health = 8;//gSkillData.barneyHealth;
|
||||
|
||||
MonsterInitDead();
|
||||
}
|
||||
|
||||
|
|
@ -0,0 +1,339 @@
|
|||
/***
|
||||
*
|
||||
* 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 BASEMONSTER_H
|
||||
#define BASEMONSTER_H
|
||||
|
||||
//
|
||||
// generic Monster
|
||||
//
|
||||
class CBaseMonster : public CBaseToggle
|
||||
{
|
||||
private:
|
||||
int m_afConditions;
|
||||
|
||||
public:
|
||||
typedef enum
|
||||
{
|
||||
SCRIPT_PLAYING = 0, // Playing the sequence
|
||||
SCRIPT_WAIT, // Waiting on everyone in the script to be ready
|
||||
SCRIPT_CLEANUP, // Cancelling the script / cleaning up
|
||||
SCRIPT_WALK_TO_MARK,
|
||||
SCRIPT_RUN_TO_MARK,
|
||||
} SCRIPTSTATE;
|
||||
|
||||
|
||||
|
||||
// these fields have been added in the process of reworking the state machine. (sjb)
|
||||
EHANDLE m_hEnemy; // the entity that the monster is fighting.
|
||||
EHANDLE m_hTargetEnt; // the entity that the monster is trying to reach
|
||||
EHANDLE m_hOldEnemy[ MAX_OLD_ENEMIES ];
|
||||
Vector m_vecOldEnemy[ MAX_OLD_ENEMIES ];
|
||||
|
||||
float m_flFieldOfView;// width of monster's field of view ( dot product )
|
||||
float m_flWaitFinished;// if we're told to wait, this is the time that the wait will be over.
|
||||
float m_flMoveWaitFinished;
|
||||
|
||||
Activity m_Activity;// what the monster is doing (animation)
|
||||
Activity m_IdealActivity;// monster should switch to this activity
|
||||
|
||||
int m_LastHitGroup; // the last body region that took damage
|
||||
|
||||
MONSTERSTATE m_MonsterState;// monster's current state
|
||||
MONSTERSTATE m_IdealMonsterState;// monster should change to this state
|
||||
|
||||
int m_iTaskStatus;
|
||||
Schedule_t *m_pSchedule;
|
||||
int m_iScheduleIndex;
|
||||
|
||||
WayPoint_t m_Route[ ROUTE_SIZE ]; // Positions of movement
|
||||
int m_movementGoal; // Goal that defines route
|
||||
int m_iRouteIndex; // index into m_Route[]
|
||||
float m_moveWaitTime; // How long I should wait for something to move
|
||||
|
||||
Vector m_vecMoveGoal; // kept around for node graph moves, so we know our ultimate goal
|
||||
Activity m_movementActivity; // When moving, set this activity
|
||||
|
||||
int m_iAudibleList; // first index of a linked list of sounds that the monster can hear.
|
||||
int m_afSoundTypes;
|
||||
|
||||
Vector m_vecLastPosition;// monster sometimes wants to return to where it started after an operation.
|
||||
|
||||
int m_iHintNode; // this is the hint node that the monster is moving towards or performing active idle on.
|
||||
|
||||
int m_afMemory;
|
||||
|
||||
int m_iMaxHealth;// keeps track of monster's maximum health value (for re-healing, etc)
|
||||
|
||||
Vector m_vecEnemyLKP;// last known position of enemy. (enemy's origin)
|
||||
|
||||
int m_cAmmoLoaded; // how much ammo is in the weapon (used to trigger reload anim sequences)
|
||||
|
||||
int m_afCapability;// tells us what a monster can/can't do.
|
||||
|
||||
float m_flNextAttack; // cannot attack again until this time
|
||||
|
||||
int m_bitsDamageType; // what types of damage has monster (player) taken
|
||||
BYTE m_rgbTimeBasedDamage[CDMG_TIMEBASED];
|
||||
|
||||
int m_lastDamageAmount;// how much damage did monster (player) last take
|
||||
// time based damage counters, decr. 1 per 2 seconds
|
||||
int m_bloodColor; // color of blood particless
|
||||
|
||||
int m_failSchedule; // Schedule type to choose if current schedule fails
|
||||
|
||||
float m_flHungryTime;// set this is a future time to stop the monster from eating for a while.
|
||||
|
||||
float m_flDistTooFar; // if enemy farther away than this, bits_COND_ENEMY_TOOFAR set in CheckEnemy
|
||||
float m_flDistLook; // distance monster sees (Default 2048)
|
||||
|
||||
int m_iTriggerCondition;// for scripted AI, this is the condition that will cause the activation of the monster's TriggerTarget
|
||||
string_t m_iszTriggerTarget;// name of target that should be fired.
|
||||
|
||||
Vector m_HackedGunPos; // HACK until we can query end of gun
|
||||
|
||||
// Scripted sequence Info
|
||||
SCRIPTSTATE m_scriptState; // internal cinematic state
|
||||
CCineMonster *m_pCine;
|
||||
|
||||
virtual int Save( CSave &save );
|
||||
virtual int Restore( CRestore &restore );
|
||||
|
||||
static TYPEDESCRIPTION m_SaveData[];
|
||||
|
||||
void KeyValue( KeyValueData *pkvd );
|
||||
|
||||
// monster use function
|
||||
void EXPORT MonsterUse( CBaseEntity *pActivator, CBaseEntity *pCaller, USE_TYPE useType, float value );
|
||||
void EXPORT CorpseUse( CBaseEntity *pActivator, CBaseEntity *pCaller, USE_TYPE useType, float value );
|
||||
|
||||
// overrideable Monster member functions
|
||||
|
||||
virtual int BloodColor( void ) { return m_bloodColor; }
|
||||
|
||||
virtual CBaseMonster *MyMonsterPointer( void ) { return this; }
|
||||
virtual void Look ( int iDistance );// basic sight function for monsters
|
||||
virtual void RunAI ( void );// core ai function!
|
||||
void Listen ( void );
|
||||
|
||||
virtual BOOL IsAlive( void ) { return (pev->deadflag != DEAD_DEAD); }
|
||||
virtual BOOL ShouldFadeOnDeath( void );
|
||||
|
||||
// Basic Monster AI functions
|
||||
virtual float ChangeYaw ( int speed );
|
||||
float VecToYaw( Vector vecDir );
|
||||
float FlYawDiff ( void );
|
||||
|
||||
float DamageForce( float damage );
|
||||
|
||||
// stuff written for new state machine
|
||||
virtual void MonsterThink( void );
|
||||
void EXPORT CallMonsterThink( void ) { this->MonsterThink(); }
|
||||
virtual int IRelationship ( CBaseEntity *pTarget );
|
||||
virtual void MonsterInit ( void );
|
||||
virtual void MonsterInitDead( void ); // Call after animation/pose is set up
|
||||
virtual void BecomeDead( void );
|
||||
void EXPORT CorpseFallThink( void );
|
||||
|
||||
void EXPORT MonsterInitThink ( void );
|
||||
virtual void StartMonster ( void );
|
||||
virtual CBaseEntity* BestVisibleEnemy ( void );// finds best visible enemy for attack
|
||||
virtual BOOL FInViewCone ( CBaseEntity *pEntity );// see if pEntity is in monster's view cone
|
||||
virtual BOOL FInViewCone ( Vector *pOrigin );// see if given location is in monster's view cone
|
||||
virtual void HandleAnimEvent( MonsterEvent_t *pEvent );
|
||||
|
||||
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 = 0.1 );
|
||||
virtual void MoveExecute( CBaseEntity *pTargetEnt, const Vector &vecDir, float flInterval );
|
||||
virtual BOOL ShouldAdvanceRoute( float flWaypointDist );
|
||||
|
||||
virtual Activity GetStoppedActivity( void ) { return ACT_IDLE; }
|
||||
virtual void Stop( void ) { m_IdealActivity = GetStoppedActivity(); }
|
||||
|
||||
// This will stop animation until you call ResetSequenceInfo() at some point in the future
|
||||
inline void StopAnimation( void ) { pev->framerate = 0; }
|
||||
|
||||
// these functions will survey conditions and set appropriate conditions bits for attack types.
|
||||
virtual BOOL CheckRangeAttack1( float flDot, float flDist );
|
||||
virtual BOOL CheckRangeAttack2( float flDot, float flDist );
|
||||
virtual BOOL CheckMeleeAttack1( float flDot, float flDist );
|
||||
virtual BOOL CheckMeleeAttack2( float flDot, float flDist );
|
||||
|
||||
BOOL FHaveSchedule( void );
|
||||
BOOL FScheduleValid ( void );
|
||||
void ClearSchedule( void );
|
||||
BOOL FScheduleDone ( void );
|
||||
void ChangeSchedule ( Schedule_t *pNewSchedule );
|
||||
void NextScheduledTask ( void );
|
||||
Schedule_t *ScheduleInList( const char *pName, Schedule_t **pList, int listCount );
|
||||
|
||||
virtual Schedule_t *ScheduleFromName( const char *pName );
|
||||
static Schedule_t *m_scheduleList[];
|
||||
|
||||
void MaintainSchedule ( void );
|
||||
virtual void StartTask ( Task_t *pTask );
|
||||
virtual void RunTask ( Task_t *pTask );
|
||||
virtual Schedule_t *GetScheduleOfType( int Type );
|
||||
virtual Schedule_t *GetSchedule( void );
|
||||
virtual void ScheduleChange( void ) {}
|
||||
// virtual int CanPlaySequence( void ) { return ((m_pCine == NULL) && (m_MonsterState == MONSTERSTATE_NONE || m_MonsterState == MONSTERSTATE_IDLE || m_IdealMonsterState == MONSTERSTATE_IDLE)); }
|
||||
virtual int CanPlaySequence( BOOL fDisregardState, int interruptLevel );
|
||||
virtual int CanPlaySentence( BOOL fDisregardState ) { return IsAlive(); }
|
||||
virtual void PlaySentence( const char *pszSentence, float duration, float volume, float attenuation );
|
||||
virtual void PlayScriptedSentence( const char *pszSentence, float duration, float volume, float attenuation, BOOL bConcurrent, CBaseEntity *pListener );
|
||||
|
||||
virtual void SentenceStop( void );
|
||||
|
||||
Task_t *GetTask ( void );
|
||||
virtual MONSTERSTATE GetIdealState ( void );
|
||||
virtual void SetActivity ( Activity NewActivity );
|
||||
void SetSequenceByName ( char *szSequence );
|
||||
void SetState ( MONSTERSTATE State );
|
||||
virtual void ReportAIState( void );
|
||||
|
||||
void CheckAttacks ( CBaseEntity *pTarget, float flDist );
|
||||
virtual int CheckEnemy ( CBaseEntity *pEnemy );
|
||||
void PushEnemy( CBaseEntity *pEnemy, Vector &vecLastKnownPos );
|
||||
BOOL PopEnemy( void );
|
||||
|
||||
BOOL FGetNodeRoute ( Vector vecDest );
|
||||
|
||||
inline void TaskComplete( void ) { if ( !HasConditions(bits_COND_TASK_FAILED) ) m_iTaskStatus = TASKSTATUS_COMPLETE; }
|
||||
void MovementComplete( void );
|
||||
inline void TaskFail( void ) { SetConditions(bits_COND_TASK_FAILED); }
|
||||
inline void TaskBegin( void ) { m_iTaskStatus = TASKSTATUS_RUNNING; }
|
||||
int TaskIsRunning( void );
|
||||
inline int TaskIsComplete( void ) { return (m_iTaskStatus == TASKSTATUS_COMPLETE); }
|
||||
inline int MovementIsComplete( void ) { return (m_movementGoal == MOVEGOAL_NONE); }
|
||||
|
||||
int IScheduleFlags ( void );
|
||||
BOOL FRefreshRoute( void );
|
||||
BOOL FRouteClear ( void );
|
||||
void RouteSimplify( CBaseEntity *pTargetEnt );
|
||||
void AdvanceRoute ( float distance );
|
||||
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 BuildNearestRoute ( Vector vecThreat, Vector vecViewOffset, float flMinDist, float flMaxDist );
|
||||
int RouteClassify( int iMoveFlag );
|
||||
void InsertWaypoint ( Vector vecLocation, int afMoveFlags );
|
||||
|
||||
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; };
|
||||
virtual float CoverRadius( void ) { return 784; } // Default cover radius
|
||||
|
||||
virtual BOOL FCanCheckAttacks ( void );
|
||||
virtual void CheckAmmo( void ) { return; };
|
||||
virtual int IgnoreConditions ( void );
|
||||
|
||||
inline void SetConditions( int iConditions ) { m_afConditions |= iConditions; }
|
||||
inline void ClearConditions( int iConditions ) { m_afConditions &= ~iConditions; }
|
||||
inline BOOL HasConditions( int iConditions ) { if ( m_afConditions & iConditions ) return TRUE; return FALSE; }
|
||||
inline BOOL HasAllConditions( int iConditions ) { if ( (m_afConditions & iConditions) == iConditions ) return TRUE; return FALSE; }
|
||||
|
||||
virtual BOOL FValidateHintType( short sHint );
|
||||
int FindHintNode ( void );
|
||||
virtual BOOL FCanActiveIdle ( void );
|
||||
void SetTurnActivity ( void );
|
||||
float FLSoundVolume ( CSound *pSound );
|
||||
|
||||
BOOL MoveToNode( Activity movementAct, float waitTime, const Vector &goal );
|
||||
BOOL MoveToTarget( Activity movementAct, float waitTime );
|
||||
BOOL MoveToLocation( Activity movementAct, float waitTime, const Vector &goal );
|
||||
BOOL MoveToEnemy( Activity movementAct, float waitTime );
|
||||
|
||||
// Returns the time when the door will be open
|
||||
float OpenDoorAndWait( entvars_t *pevDoor );
|
||||
|
||||
virtual int ISoundMask( void );
|
||||
virtual CSound* PBestSound ( void );
|
||||
virtual CSound* PBestScent ( void );
|
||||
virtual float HearingSensitivity( void ) { return 1.0; };
|
||||
|
||||
BOOL FBecomeProne ( void );
|
||||
virtual void BarnacleVictimBitten( entvars_t *pevBarnacle );
|
||||
virtual void BarnacleVictimReleased( void );
|
||||
|
||||
void SetEyePosition ( void );
|
||||
|
||||
BOOL FShouldEat( void );// see if a monster is 'hungry'
|
||||
void Eat ( float flFullDuration );// make the monster 'full' for a while.
|
||||
|
||||
CBaseEntity *CheckTraceHullAttack( float flDist, int iDamage, int iDmgType );
|
||||
BOOL FacingIdeal( void );
|
||||
|
||||
BOOL FCheckAITrigger( void );// checks and, if necessary, fires the monster's trigger target.
|
||||
BOOL NoFriendlyFire( void );
|
||||
|
||||
BOOL BBoxFlat( void );
|
||||
|
||||
// PrescheduleThink
|
||||
virtual void PrescheduleThink( void ) { return; };
|
||||
|
||||
BOOL GetEnemy ( void );
|
||||
void MakeDamageBloodDecal ( int cCount, float flNoise, TraceResult *ptr, const Vector &vecDir );
|
||||
void TraceAttack( entvars_t *pevAttacker, float flDamage, Vector vecDir, TraceResult *ptr, int bitsDamageType);
|
||||
|
||||
// combat functions
|
||||
float UpdateTarget ( entvars_t *pevTarget );
|
||||
virtual Activity GetDeathActivity ( void );
|
||||
Activity GetSmallFlinchActivity( void );
|
||||
virtual void Killed( entvars_t *pevAttacker, int iGib );
|
||||
virtual void GibMonster( void );
|
||||
BOOL ShouldGibMonster( int iGib );
|
||||
void CallGibMonster( void );
|
||||
virtual BOOL HasHumanGibs( void );
|
||||
virtual BOOL HasAlienGibs( void );
|
||||
virtual void FadeMonster( void ); // Called instead of GibMonster() when gibs are disabled
|
||||
|
||||
Vector ShootAtEnemy( const Vector &shootOrigin );
|
||||
virtual Vector BodyTarget( const Vector &posSrc ) { return Center( ) * 0.75 + EyePosition() * 0.25; }; // position to shoot at
|
||||
|
||||
virtual Vector GetGunPosition( void );
|
||||
|
||||
virtual int TakeHealth( float flHealth, int bitsDamageType );
|
||||
virtual int TakeDamage( entvars_t* pevInflictor, entvars_t* pevAttacker, float flDamage, int bitsDamageType);
|
||||
int DeadTakeDamage( entvars_t *pevInflictor, entvars_t *pevAttacker, float flDamage, int bitsDamageType );
|
||||
|
||||
void RadiusDamage(entvars_t *pevInflictor, entvars_t *pevAttacker, float flDamage, int iClassIgnore, int bitsDamageType );
|
||||
void RadiusDamage(Vector vecSrc, entvars_t *pevInflictor, entvars_t *pevAttacker, float flDamage, int iClassIgnore, int bitsDamageType );
|
||||
virtual int IsMoving( void ) { return m_movementGoal != MOVEGOAL_NONE; }
|
||||
|
||||
void RouteClear( void );
|
||||
void RouteNew( void );
|
||||
|
||||
virtual void DeathSound ( void ) { return; };
|
||||
virtual void AlertSound ( void ) { return; };
|
||||
virtual void IdleSound ( void ) { return; };
|
||||
virtual void PainSound ( void ) { return; };
|
||||
|
||||
virtual void StopFollowing( BOOL clearSchedule ) {}
|
||||
|
||||
inline void Remember( int iMemory ) { m_afMemory |= iMemory; }
|
||||
inline void Forget( int iMemory ) { m_afMemory &= ~iMemory; }
|
||||
inline BOOL HasMemory( int iMemory ) { if ( m_afMemory & iMemory ) return TRUE; return FALSE; }
|
||||
inline BOOL HasAllMemories( int iMemory ) { if ( (m_afMemory & iMemory) == iMemory ) return TRUE; return FALSE; }
|
||||
|
||||
BOOL ExitScriptedSequence( );
|
||||
BOOL CineCleanup( );
|
||||
|
||||
CBaseEntity* DropItem ( char *pszItemName, const Vector &vecPos, const Vector &vecAng );// drop an item.
|
||||
};
|
||||
|
||||
|
||||
|
||||
#endif // BASEMONSTER_H
|
File diff suppressed because it is too large
Load Diff
|
@ -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.
|
||||
*
|
||||
* This source code contains proprietary and confidential information of
|
||||
* Valve LLC and its suppliers. Access to this code is restricted to
|
||||
* persons who have executed a written SDK license with Valve. Any access,
|
||||
* use or distribution of this code by or to any unlicensed person is illegal.
|
||||
*
|
||||
****/
|
||||
//=========================================================
|
||||
// Bloater
|
||||
//=========================================================
|
||||
|
||||
#include "extdll.h"
|
||||
#include "util.h"
|
||||
#include "cbase.h"
|
||||
#include "monsters.h"
|
||||
#include "schedule.h"
|
||||
|
||||
|
||||
//=========================================================
|
||||
// Monster's Anim Events Go Here
|
||||
//=========================================================
|
||||
#define BLOATER_AE_ATTACK_MELEE1 0x01
|
||||
|
||||
|
||||
class CBloater : public CBaseMonster
|
||||
{
|
||||
public:
|
||||
void Spawn( void );
|
||||
void Precache( void );
|
||||
void SetYawSpeed( void );
|
||||
int Classify ( void );
|
||||
void HandleAnimEvent( MonsterEvent_t *pEvent );
|
||||
|
||||
void PainSound( void );
|
||||
void AlertSound( void );
|
||||
void IdleSound( void );
|
||||
void AttackSnd( void );
|
||||
|
||||
// No range attacks
|
||||
BOOL CheckRangeAttack1 ( float flDot, float flDist ) { return FALSE; }
|
||||
BOOL CheckRangeAttack2 ( float flDot, float flDist ) { return FALSE; }
|
||||
int TakeDamage( entvars_t *pevInflictor, entvars_t *pevAttacker, float flDamage, int bitsDamageType );
|
||||
};
|
||||
|
||||
LINK_ENTITY_TO_CLASS( monster_bloater, CBloater );
|
||||
|
||||
//=========================================================
|
||||
// Classify - indicates this monster's place in the
|
||||
// relationship table.
|
||||
//=========================================================
|
||||
int CBloater :: Classify ( void )
|
||||
{
|
||||
return CLASS_ALIEN_MONSTER;
|
||||
}
|
||||
|
||||
//=========================================================
|
||||
// SetYawSpeed - allows each sequence to have a different
|
||||
// turn rate associated with it.
|
||||
//=========================================================
|
||||
void CBloater :: SetYawSpeed ( void )
|
||||
{
|
||||
int ys;
|
||||
|
||||
ys = 120;
|
||||
|
||||
#if 0
|
||||
switch ( m_Activity )
|
||||
{
|
||||
}
|
||||
#endif
|
||||
|
||||
pev->yaw_speed = ys;
|
||||
}
|
||||
|
||||
int CBloater :: TakeDamage( entvars_t *pevInflictor, entvars_t *pevAttacker, float flDamage, int bitsDamageType )
|
||||
{
|
||||
PainSound();
|
||||
return CBaseMonster::TakeDamage( pevInflictor, pevAttacker, flDamage, bitsDamageType );
|
||||
}
|
||||
|
||||
void CBloater :: PainSound( void )
|
||||
{
|
||||
#if 0
|
||||
int pitch = 95 + RANDOM_LONG(0,9);
|
||||
|
||||
switch (RANDOM_LONG(0,5))
|
||||
{
|
||||
case 0:
|
||||
EMIT_SOUND_DYN(ENT(pev), CHAN_VOICE, "zombie/zo_pain1.wav", 1.0, ATTN_NORM, 0, pitch);
|
||||
break;
|
||||
case 1:
|
||||
EMIT_SOUND_DYN(ENT(pev), CHAN_VOICE, "zombie/zo_pain2.wav", 1.0, ATTN_NORM, 0, pitch);
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
#endif
|
||||
}
|
||||
|
||||
void CBloater :: AlertSound( void )
|
||||
{
|
||||
#if 0
|
||||
int pitch = 95 + RANDOM_LONG(0,9);
|
||||
|
||||
switch (RANDOM_LONG(0,2))
|
||||
{
|
||||
case 0:
|
||||
EMIT_SOUND_DYN(ENT(pev), CHAN_VOICE, "zombie/zo_alert10.wav", 1.0, ATTN_NORM, 0, pitch);
|
||||
break;
|
||||
case 1:
|
||||
EMIT_SOUND_DYN(ENT(pev), CHAN_VOICE, "zombie/zo_alert20.wav", 1.0, ATTN_NORM, 0, pitch);
|
||||
break;
|
||||
case 2:
|
||||
EMIT_SOUND_DYN(ENT(pev), CHAN_VOICE, "zombie/zo_alert30.wav", 1.0, ATTN_NORM, 0, pitch);
|
||||
break;
|
||||
}
|
||||
#endif
|
||||
}
|
||||
|
||||
void CBloater :: IdleSound( void )
|
||||
{
|
||||
#if 0
|
||||
int pitch = 95 + RANDOM_LONG(0,9);
|
||||
|
||||
switch (RANDOM_LONG(0,2))
|
||||
{
|
||||
case 0:
|
||||
EMIT_SOUND_DYN(ENT(pev), CHAN_VOICE, "zombie/zo_idle1.wav", 1.0, ATTN_NORM, 0, pitch);
|
||||
break;
|
||||
case 1:
|
||||
EMIT_SOUND_DYN(ENT(pev), CHAN_VOICE, "zombie/zo_idle2.wav", 1.0, ATTN_NORM, 0, pitch);
|
||||
break;
|
||||
case 2:
|
||||
EMIT_SOUND_DYN(ENT(pev), CHAN_VOICE, "zombie/zo_idle3.wav", 1.0, ATTN_NORM, 0, pitch);
|
||||
break;
|
||||
}
|
||||
#endif
|
||||
}
|
||||
|
||||
void CBloater :: AttackSnd( void )
|
||||
{
|
||||
#if 0
|
||||
int pitch = 95 + RANDOM_LONG(0,9);
|
||||
|
||||
switch (RANDOM_LONG(0,1))
|
||||
{
|
||||
case 0:
|
||||
EMIT_SOUND_DYN(ENT(pev), CHAN_VOICE, "zombie/zo_attack1.wav", 1.0, ATTN_NORM, 0, pitch);
|
||||
break;
|
||||
case 1:
|
||||
EMIT_SOUND_DYN(ENT(pev), CHAN_VOICE, "zombie/zo_attack2.wav", 1.0, ATTN_NORM, 0, pitch);
|
||||
break;
|
||||
}
|
||||
#endif
|
||||
}
|
||||
|
||||
|
||||
//=========================================================
|
||||
// HandleAnimEvent - catches the monster-specific messages
|
||||
// that occur when tagged animation frames are played.
|
||||
//=========================================================
|
||||
void CBloater :: HandleAnimEvent( MonsterEvent_t *pEvent )
|
||||
{
|
||||
switch( pEvent->event )
|
||||
{
|
||||
case BLOATER_AE_ATTACK_MELEE1:
|
||||
{
|
||||
// do stuff for this event.
|
||||
AttackSnd();
|
||||
}
|
||||
break;
|
||||
|
||||
default:
|
||||
CBaseMonster::HandleAnimEvent( pEvent );
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
//=========================================================
|
||||
// Spawn
|
||||
//=========================================================
|
||||
void CBloater :: Spawn()
|
||||
{
|
||||
Precache( );
|
||||
|
||||
SET_MODEL(ENT(pev), "models/floater.mdl");
|
||||
UTIL_SetSize( pev, VEC_HUMAN_HULL_MIN, VEC_HUMAN_HULL_MAX );
|
||||
|
||||
pev->solid = SOLID_SLIDEBOX;
|
||||
pev->movetype = MOVETYPE_FLY;
|
||||
pev->spawnflags |= FL_FLY;
|
||||
m_bloodColor = BLOOD_COLOR_GREEN;
|
||||
pev->health = 40;
|
||||
pev->view_ofs = VEC_VIEW;// position of the eyes relative to monster's origin.
|
||||
m_flFieldOfView = 0.5;// indicates the width of this monster's forward view cone ( as a dotproduct result )
|
||||
m_MonsterState = MONSTERSTATE_NONE;
|
||||
|
||||
MonsterInit();
|
||||
}
|
||||
|
||||
//=========================================================
|
||||
// Precache - precaches all resources this monster needs
|
||||
//=========================================================
|
||||
void CBloater :: Precache()
|
||||
{
|
||||
PRECACHE_MODEL("models/floater.mdl");
|
||||
}
|
||||
|
||||
//=========================================================
|
||||
// AI Schedules Specific to this monster
|
||||
//=========================================================
|
||||
|
|
@ -0,0 +1,958 @@
|
|||
/***
|
||||
*
|
||||
* 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.
|
||||
*
|
||||
****/
|
||||
/*
|
||||
|
||||
===== bmodels.cpp ========================================================
|
||||
|
||||
spawn, think, and use functions for entities that use brush models
|
||||
|
||||
*/
|
||||
|
||||
#include "extdll.h"
|
||||
#include "util.h"
|
||||
#include "cbase.h"
|
||||
#include "doors.h"
|
||||
|
||||
extern DLL_GLOBAL Vector g_vecAttackDir;
|
||||
|
||||
#define SF_BRUSH_ACCDCC 16// brush should accelerate and decelerate when toggled
|
||||
#define SF_BRUSH_HURT 32// rotating brush that inflicts pain based on rotation speed
|
||||
#define SF_ROTATING_NOT_SOLID 64 // some special rotating objects are not solid.
|
||||
|
||||
// covering cheesy noise1, noise2, & noise3 fields so they make more sense (for rotating fans)
|
||||
#define noiseStart noise1
|
||||
#define noiseStop noise2
|
||||
#define noiseRunning noise3
|
||||
|
||||
#define SF_PENDULUM_SWING 2 // spawnflag that makes a pendulum a rope swing.
|
||||
//
|
||||
// BModelOrigin - calculates origin of a bmodel from absmin/size because all bmodel origins are 0 0 0
|
||||
//
|
||||
Vector VecBModelOrigin( entvars_t* pevBModel )
|
||||
{
|
||||
return pevBModel->absmin + ( pevBModel->size * 0.5 );
|
||||
}
|
||||
|
||||
// =================== FUNC_WALL ==============================================
|
||||
|
||||
/*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 );
|
||||
|
||||
// Bmodels don't go across transitions
|
||||
virtual int ObjectCaps( void ) { return CBaseEntity :: ObjectCaps() & ~FCAP_ACROSS_TRANSITION; }
|
||||
};
|
||||
|
||||
LINK_ENTITY_TO_CLASS( func_wall, CFuncWall );
|
||||
|
||||
void CFuncWall :: Spawn( void )
|
||||
{
|
||||
pev->angles = g_vecZero;
|
||||
pev->movetype = MOVETYPE_PUSH; // so it doesn't get pushed by anything
|
||||
pev->solid = SOLID_BSP;
|
||||
SET_MODEL( ENT(pev), STRING(pev->model) );
|
||||
|
||||
// If it can't move/go away, it's really part of the world
|
||||
pev->flags |= FL_WORLDBRUSH;
|
||||
}
|
||||
|
||||
|
||||
void CFuncWall :: Use( CBaseEntity *pActivator, CBaseEntity *pCaller, USE_TYPE useType, float value )
|
||||
{
|
||||
if ( ShouldToggle( useType, (int)(pev->frame)) )
|
||||
pev->frame = 1 - pev->frame;
|
||||
}
|
||||
|
||||
|
||||
#define SF_WALL_START_OFF 0x0001
|
||||
|
||||
class CFuncWallToggle : public CFuncWall
|
||||
{
|
||||
public:
|
||||
void Spawn( void );
|
||||
void Use( CBaseEntity *pActivator, CBaseEntity *pCaller, USE_TYPE useType, float value );
|
||||
void TurnOff( void );
|
||||
void TurnOn( void );
|
||||
BOOL IsOn( void );
|
||||
};
|
||||
|
||||
LINK_ENTITY_TO_CLASS( func_wall_toggle, CFuncWallToggle );
|
||||
|
||||
void CFuncWallToggle :: Spawn( void )
|
||||
{
|
||||
CFuncWall::Spawn();
|
||||
if ( pev->spawnflags & SF_WALL_START_OFF )
|
||||
TurnOff();
|
||||
}
|
||||
|
||||
|
||||
void CFuncWallToggle :: TurnOff( void )
|
||||
{
|
||||
pev->solid = SOLID_NOT;
|
||||
pev->effects |= EF_NODRAW;
|
||||
UTIL_SetOrigin( pev, pev->origin );
|
||||
}
|
||||
|
||||
|
||||
void CFuncWallToggle :: TurnOn( void )
|
||||
{
|
||||
pev->solid = SOLID_BSP;
|
||||
pev->effects &= ~EF_NODRAW;
|
||||
UTIL_SetOrigin( pev, pev->origin );
|
||||
}
|
||||
|
||||
|
||||
BOOL CFuncWallToggle :: IsOn( void )
|
||||
{
|
||||
if ( pev->solid == SOLID_NOT )
|
||||
return FALSE;
|
||||
return TRUE;
|
||||
}
|
||||
|
||||
|
||||
void CFuncWallToggle :: Use( CBaseEntity *pActivator, CBaseEntity *pCaller, USE_TYPE useType, float value )
|
||||
{
|
||||
int status = IsOn();
|
||||
|
||||
if ( ShouldToggle( useType, status ) )
|
||||
{
|
||||
if ( status )
|
||||
TurnOff();
|
||||
else
|
||||
TurnOn();
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
#define SF_CONVEYOR_VISUAL 0x0001
|
||||
#define SF_CONVEYOR_NOTSOLID 0x0002
|
||||
|
||||
class CFuncConveyor : public CFuncWall
|
||||
{
|
||||
public:
|
||||
void Spawn( void );
|
||||
void Use( CBaseEntity *pActivator, CBaseEntity *pCaller, USE_TYPE useType, float value );
|
||||
void UpdateSpeed( float speed );
|
||||
};
|
||||
|
||||
LINK_ENTITY_TO_CLASS( func_conveyor, CFuncConveyor );
|
||||
void CFuncConveyor :: Spawn( void )
|
||||
{
|
||||
SetMovedir( pev );
|
||||
CFuncWall::Spawn();
|
||||
|
||||
if ( !(pev->spawnflags & SF_CONVEYOR_VISUAL) )
|
||||
SetBits( pev->flags, FL_CONVEYOR );
|
||||
|
||||
// HACKHACK - This is to allow for some special effects
|
||||
if ( pev->spawnflags & SF_CONVEYOR_NOTSOLID )
|
||||
{
|
||||
pev->solid = SOLID_NOT;
|
||||
pev->skin = 0; // Don't want the engine thinking we've got special contents on this brush
|
||||
}
|
||||
|
||||
if ( pev->speed == 0 )
|
||||
pev->speed = 100;
|
||||
|
||||
UpdateSpeed( pev->speed );
|
||||
}
|
||||
|
||||
|
||||
// HACKHACK -- This is ugly, but encode the speed in the rendercolor to avoid adding more data to the network stream
|
||||
void CFuncConveyor :: UpdateSpeed( float speed )
|
||||
{
|
||||
// Encode it as an integer with 4 fractional bits
|
||||
int speedCode = (int)(fabs(speed) * 16.0);
|
||||
|
||||
if ( speed < 0 )
|
||||
pev->rendercolor.x = 1;
|
||||
else
|
||||
pev->rendercolor.x = 0;
|
||||
|
||||
pev->rendercolor.y = (speedCode >> 8);
|
||||
pev->rendercolor.z = (speedCode & 0xFF);
|
||||
}
|
||||
|
||||
|
||||
void CFuncConveyor :: Use( CBaseEntity *pActivator, CBaseEntity *pCaller, USE_TYPE useType, float value )
|
||||
{
|
||||
pev->speed = -pev->speed;
|
||||
UpdateSpeed( pev->speed );
|
||||
}
|
||||
|
||||
|
||||
|
||||
// =================== FUNC_ILLUSIONARY ==============================================
|
||||
|
||||
|
||||
/*QUAKED func_illusionary (0 .5 .8) ?
|
||||
A simple entity that looks solid but lets you walk through it.
|
||||
*/
|
||||
class CFuncIllusionary : public CBaseToggle
|
||||
{
|
||||
public:
|
||||
void Spawn( void );
|
||||
void EXPORT SloshTouch( CBaseEntity *pOther );
|
||||
void KeyValue( KeyValueData *pkvd );
|
||||
virtual int ObjectCaps( void ) { return CBaseEntity :: ObjectCaps() & ~FCAP_ACROSS_TRANSITION; }
|
||||
};
|
||||
|
||||
LINK_ENTITY_TO_CLASS( func_illusionary, CFuncIllusionary );
|
||||
|
||||
void CFuncIllusionary :: KeyValue( KeyValueData *pkvd )
|
||||
{
|
||||
if (FStrEq(pkvd->szKeyName, "skin"))//skin is used for content type
|
||||
{
|
||||
pev->skin = atof(pkvd->szValue);
|
||||
pkvd->fHandled = TRUE;
|
||||
}
|
||||
else
|
||||
CBaseToggle::KeyValue( pkvd );
|
||||
}
|
||||
|
||||
void CFuncIllusionary :: Spawn( void )
|
||||
{
|
||||
pev->angles = g_vecZero;
|
||||
pev->movetype = MOVETYPE_NONE;
|
||||
pev->solid = SOLID_NOT;// always solid_not
|
||||
SET_MODEL( ENT(pev), STRING(pev->model) );
|
||||
|
||||
// I'd rather eat the network bandwidth of this than figure out how to save/restore
|
||||
// these entities after they have been moved to the client, or respawn them ala Quake
|
||||
// Perhaps we can do this in deathmatch only.
|
||||
// MAKE_STATIC(ENT(pev));
|
||||
}
|
||||
|
||||
|
||||
// -------------------------------------------------------------------------------
|
||||
//
|
||||
// Monster only clip brush
|
||||
//
|
||||
// This brush will be solid for any entity who has the FL_MONSTERCLIP 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
|
||||
//
|
||||
// -------------------------------------------------------------------------------
|
||||
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 );
|
||||
|
||||
void CFuncMonsterClip::Spawn( void )
|
||||
{
|
||||
CFuncWall::Spawn();
|
||||
if ( CVAR_GET_FLOAT("showtriggers") == 0 )
|
||||
pev->effects = EF_NODRAW;
|
||||
pev->flags |= FL_MONSTERCLIP;
|
||||
}
|
||||
|
||||
|
||||
// =================== FUNC_ROTATING ==============================================
|
||||
class CFuncRotating : public CBaseEntity
|
||||
{
|
||||
public:
|
||||
// basic functions
|
||||
void Spawn( void );
|
||||
void Precache( void );
|
||||
void EXPORT SpinUp ( void );
|
||||
void EXPORT SpinDown ( void );
|
||||
void KeyValue( KeyValueData* pkvd);
|
||||
void EXPORT HurtTouch ( CBaseEntity *pOther );
|
||||
void EXPORT RotatingUse( CBaseEntity *pActivator, CBaseEntity *pCaller, USE_TYPE useType, float value );
|
||||
void EXPORT Rotate( void );
|
||||
void RampPitchVol (int fUp );
|
||||
void Blocked( CBaseEntity *pOther );
|
||||
virtual int ObjectCaps( void ) { return CBaseEntity :: ObjectCaps() & ~FCAP_ACROSS_TRANSITION; }
|
||||
virtual int Save( CSave &save );
|
||||
virtual int Restore( CRestore &restore );
|
||||
|
||||
static TYPEDESCRIPTION m_SaveData[];
|
||||
|
||||
float m_flFanFriction;
|
||||
float m_flAttenuation;
|
||||
float m_flVolume;
|
||||
float m_pitch;
|
||||
int m_sounds;
|
||||
};
|
||||
|
||||
TYPEDESCRIPTION CFuncRotating::m_SaveData[] =
|
||||
{
|
||||
DEFINE_FIELD( CFuncRotating, m_flFanFriction, FIELD_FLOAT ),
|
||||
DEFINE_FIELD( CFuncRotating, m_flAttenuation, FIELD_FLOAT ),
|
||||
DEFINE_FIELD( CFuncRotating, m_flVolume, FIELD_FLOAT ),
|
||||
DEFINE_FIELD( CFuncRotating, m_pitch, FIELD_FLOAT ),
|
||||
DEFINE_FIELD( CFuncRotating, m_sounds, FIELD_INTEGER )
|
||||
};
|
||||
|
||||
IMPLEMENT_SAVERESTORE( CFuncRotating, CBaseEntity );
|
||||
|
||||
|
||||
LINK_ENTITY_TO_CLASS( func_rotating, CFuncRotating );
|
||||
|
||||
void CFuncRotating :: KeyValue( KeyValueData* pkvd)
|
||||
{
|
||||
if (FStrEq(pkvd->szKeyName, "fanfriction"))
|
||||
{
|
||||
m_flFanFriction = atof(pkvd->szValue)/100;
|
||||
pkvd->fHandled = TRUE;
|
||||
}
|
||||
else if (FStrEq(pkvd->szKeyName, "Volume"))
|
||||
{
|
||||
m_flVolume = atof(pkvd->szValue)/10.0;
|
||||
|
||||
if (m_flVolume > 1.0)
|
||||
m_flVolume = 1.0;
|
||||
if (m_flVolume < 0.0)
|
||||
m_flVolume = 0.0;
|
||||
pkvd->fHandled = TRUE;
|
||||
}
|
||||
else if (FStrEq(pkvd->szKeyName, "spawnorigin"))
|
||||
{
|
||||
Vector tmp;
|
||||
UTIL_StringToVector( (float *)tmp, pkvd->szValue );
|
||||
if ( tmp != g_vecZero )
|
||||
pev->origin = tmp;
|
||||
}
|
||||
else if (FStrEq(pkvd->szKeyName, "sounds"))
|
||||
{
|
||||
m_sounds = atoi(pkvd->szValue);
|
||||
pkvd->fHandled = TRUE;
|
||||
}
|
||||
else
|
||||
CBaseEntity::KeyValue( pkvd );
|
||||
}
|
||||
|
||||
/*QUAKED func_rotating (0 .5 .8) ? START_ON REVERSE X_AXIS Y_AXIS
|
||||
You need to have an origin brush as part of this entity. The
|
||||
center of that brush will be
|
||||
the point around which it is rotated. It will rotate around the Z
|
||||
axis by default. You can
|
||||
check either the X_AXIS or Y_AXIS box to change that.
|
||||
|
||||
"speed" determines how fast it moves; default value is 100.
|
||||
"dmg" damage to inflict when blocked (2 default)
|
||||
|
||||
REVERSE will cause the it to rotate in the opposite direction.
|
||||
*/
|
||||
|
||||
|
||||
void CFuncRotating :: Spawn( )
|
||||
{
|
||||
// set final pitch. Must not be PITCH_NORM, since we
|
||||
// plan on pitch shifting later.
|
||||
|
||||
m_pitch = PITCH_NORM - 1;
|
||||
|
||||
// maintain compatibility with previous maps
|
||||
if (m_flVolume == 0.0)
|
||||
m_flVolume = 1.0;
|
||||
|
||||
// if the designer didn't set a sound attenuation, default to one.
|
||||
m_flAttenuation = ATTN_NORM;
|
||||
|
||||
if ( FBitSet ( pev->spawnflags, SF_BRUSH_ROTATE_SMALLRADIUS) )
|
||||
{
|
||||
m_flAttenuation = ATTN_IDLE;
|
||||
}
|
||||
else if ( FBitSet ( pev->spawnflags, SF_BRUSH_ROTATE_MEDIUMRADIUS) )
|
||||
{
|
||||
m_flAttenuation = ATTN_STATIC;
|
||||
}
|
||||
else if ( FBitSet ( pev->spawnflags, SF_BRUSH_ROTATE_LARGERADIUS) )
|
||||
{
|
||||
m_flAttenuation = ATTN_NORM;
|
||||
}
|
||||
|
||||
// prevent divide by zero if level designer forgets friction!
|
||||
if ( m_flFanFriction == 0 )
|
||||
{
|
||||
m_flFanFriction = 1;
|
||||
}
|
||||
|
||||
if ( FBitSet(pev->spawnflags, SF_BRUSH_ROTATE_Z_AXIS) )
|
||||
pev->movedir = Vector(0,0,1);
|
||||
else if ( FBitSet(pev->spawnflags, SF_BRUSH_ROTATE_X_AXIS) )
|
||||
pev->movedir = Vector(1,0,0);
|
||||
else
|
||||
pev->movedir = Vector(0,1,0); // y-axis
|
||||
|
||||
// check for reverse rotation
|
||||
if ( FBitSet(pev->spawnflags, SF_BRUSH_ROTATE_BACKWARDS) )
|
||||
pev->movedir = pev->movedir * -1;
|
||||
|
||||
// some rotating objects like fake volumetric lights will not be solid.
|
||||
if ( FBitSet(pev->spawnflags, SF_ROTATING_NOT_SOLID) )
|
||||
{
|
||||
pev->solid = SOLID_NOT;
|
||||
pev->skin = CONTENTS_EMPTY;
|
||||
pev->movetype = MOVETYPE_PUSH;
|
||||
}
|
||||
else
|
||||
{
|
||||
pev->solid = SOLID_BSP;
|
||||
pev->movetype = MOVETYPE_PUSH;
|
||||
}
|
||||
|
||||
UTIL_SetOrigin(pev, pev->origin);
|
||||
SET_MODEL( ENT(pev), STRING(pev->model) );
|
||||
|
||||
SetUse( RotatingUse );
|
||||
// did level designer forget to assign speed?
|
||||
if (pev->speed <= 0)
|
||||
pev->speed = 0;
|
||||
|
||||
// Removed this per level designers request. -- JAY
|
||||
// if (pev->dmg == 0)
|
||||
// pev->dmg = 2;
|
||||
|
||||
// instant-use brush?
|
||||
if ( FBitSet( pev->spawnflags, SF_BRUSH_ROTATE_INSTANT) )
|
||||
{
|
||||
SetThink( SUB_CallUseToggle );
|
||||
pev->nextthink = pev->ltime + 1.5; // leave a magic delay for client to start up
|
||||
}
|
||||
// can this brush inflict pain?
|
||||
if ( FBitSet (pev->spawnflags, SF_BRUSH_HURT) )
|
||||
{
|
||||
SetTouch( HurtTouch );
|
||||
}
|
||||
|
||||
Precache( );
|
||||
}
|
||||
|
||||
|
||||
void CFuncRotating :: Precache( void )
|
||||
{
|
||||
char* szSoundFile = (char*) STRING(pev->message);
|
||||
|
||||
// set up fan sounds
|
||||
|
||||
if (!FStringNull( pev->message ) && strlen( szSoundFile ) > 0)
|
||||
{
|
||||
// if a path is set for a wave, use it
|
||||
|
||||
PRECACHE_SOUND(szSoundFile);
|
||||
|
||||
pev->noiseRunning = ALLOC_STRING(szSoundFile);
|
||||
} else
|
||||
{
|
||||
// otherwise use preset sound
|
||||
switch (m_sounds)
|
||||
{
|
||||
case 1:
|
||||
PRECACHE_SOUND ("fans/fan1.wav");
|
||||
pev->noiseRunning = ALLOC_STRING("fans/fan1.wav");
|
||||
break;
|
||||
case 2:
|
||||
PRECACHE_SOUND ("fans/fan2.wav");
|
||||
pev->noiseRunning = ALLOC_STRING("fans/fan2.wav");
|
||||
break;
|
||||
case 3:
|
||||
PRECACHE_SOUND ("fans/fan3.wav");
|
||||
pev->noiseRunning = ALLOC_STRING("fans/fan3.wav");
|
||||
break;
|
||||
case 4:
|
||||
PRECACHE_SOUND ("fans/fan4.wav");
|
||||
pev->noiseRunning = ALLOC_STRING("fans/fan4.wav");
|
||||
break;
|
||||
case 5:
|
||||
PRECACHE_SOUND ("fans/fan5.wav");
|
||||
pev->noiseRunning = ALLOC_STRING("fans/fan5.wav");
|
||||
break;
|
||||
|
||||
case 0:
|
||||
default:
|
||||
if (!FStringNull( pev->message ) && strlen( szSoundFile ) > 0)
|
||||
{
|
||||
PRECACHE_SOUND(szSoundFile);
|
||||
|
||||
pev->noiseRunning = ALLOC_STRING(szSoundFile);
|
||||
break;
|
||||
} else
|
||||
{
|
||||
pev->noiseRunning = ALLOC_STRING("common/null.wav");
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (pev->avelocity != g_vecZero )
|
||||
{
|
||||
// if fan was spinning, and we went through transition or save/restore,
|
||||
// make sure we restart the sound. 1.5 sec delay is magic number. KDB
|
||||
|
||||
SetThink ( SpinUp );
|
||||
pev->nextthink = pev->ltime + 1.5;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
//
|
||||
// Touch - will hurt others based on how fast the brush is spinning
|
||||
//
|
||||
void CFuncRotating :: HurtTouch ( CBaseEntity *pOther )
|
||||
{
|
||||
entvars_t *pevOther = pOther->pev;
|
||||
|
||||
// we can't hurt this thing, so we're not concerned with it
|
||||
if ( !pevOther->takedamage )
|
||||
return;
|
||||
|
||||
// calculate damage based on rotation speed
|
||||
pev->dmg = pev->avelocity.Length() / 10;
|
||||
|
||||
pOther->TakeDamage( pev, pev, pev->dmg, DMG_CRUSH);
|
||||
|
||||
pevOther->velocity = (pevOther->origin - VecBModelOrigin(pev) ).Normalize() * pev->dmg;
|
||||
}
|
||||
|
||||
//
|
||||
// RampPitchVol - ramp pitch and volume up to final values, based on difference
|
||||
// between how fast we're going vs how fast we plan to go
|
||||
//
|
||||
#define FANPITCHMIN 30
|
||||
#define FANPITCHMAX 100
|
||||
|
||||
void CFuncRotating :: RampPitchVol (int fUp)
|
||||
{
|
||||
|
||||
Vector vecAVel = pev->avelocity;
|
||||
vec_t vecCur;
|
||||
vec_t vecFinal;
|
||||
float fpct;
|
||||
float fvol;
|
||||
float fpitch;
|
||||
int pitch;
|
||||
|
||||
// get current angular velocity
|
||||
|
||||
vecCur = abs(vecAVel.x != 0 ? vecAVel.x : (vecAVel.y != 0 ? vecAVel.y : vecAVel.z));
|
||||
|
||||
// get target angular velocity
|
||||
|
||||
vecFinal = (pev->movedir.x != 0 ? pev->movedir.x : (pev->movedir.y != 0 ? pev->movedir.y : pev->movedir.z));
|
||||
vecFinal *= pev->speed;
|
||||
vecFinal = abs(vecFinal);
|
||||
|
||||
// calc volume and pitch as % of final vol and pitch
|
||||
|
||||
fpct = vecCur / vecFinal;
|
||||
// if (fUp)
|
||||
// fvol = m_flVolume * (0.5 + fpct/2.0); // spinup volume ramps up from 50% max vol
|
||||
// else
|
||||
fvol = m_flVolume * fpct; // slowdown volume ramps down to 0
|
||||
|
||||
fpitch = FANPITCHMIN + (FANPITCHMAX - FANPITCHMIN) * fpct;
|
||||
|
||||
pitch = (int) fpitch;
|
||||
if (pitch == PITCH_NORM)
|
||||
pitch = PITCH_NORM-1;
|
||||
|
||||
// change the fan's vol and pitch
|
||||
|
||||
EMIT_SOUND_DYN(ENT(pev), CHAN_STATIC, (char *)STRING(pev->noiseRunning),
|
||||
fvol, m_flAttenuation, SND_CHANGE_PITCH | SND_CHANGE_VOL, pitch);
|
||||
|
||||
}
|
||||
|
||||
//
|
||||
// SpinUp - accelerates a non-moving func_rotating up to it's speed
|
||||
//
|
||||
void CFuncRotating :: SpinUp( void )
|
||||
{
|
||||
Vector vecAVel;//rotational velocity
|
||||
|
||||
pev->nextthink = pev->ltime + 0.1;
|
||||
pev->avelocity = pev->avelocity + ( pev->movedir * ( pev->speed * m_flFanFriction ) );
|
||||
|
||||
vecAVel = pev->avelocity;// cache entity's rotational velocity
|
||||
|
||||
// if we've met or exceeded target speed, set target speed and stop thinking
|
||||
if ( abs(vecAVel.x) >= abs(pev->movedir.x * pev->speed) &&
|
||||
abs(vecAVel.y) >= abs(pev->movedir.y * pev->speed) &&
|
||||
abs(vecAVel.z) >= abs(pev->movedir.z * pev->speed) )
|
||||
{
|
||||
pev->avelocity = pev->movedir * pev->speed;// set speed in case we overshot
|
||||
EMIT_SOUND_DYN(ENT(pev), CHAN_STATIC, (char *)STRING(pev->noiseRunning),
|
||||
m_flVolume, m_flAttenuation, SND_CHANGE_PITCH | SND_CHANGE_VOL, FANPITCHMAX);
|
||||
|
||||
SetThink( Rotate );
|
||||
Rotate();
|
||||
}
|
||||
else
|
||||
{
|
||||
RampPitchVol(TRUE);
|
||||
}
|
||||
}
|
||||
|
||||
//
|
||||
// SpinDown - decelerates a moving func_rotating to a standstill.
|
||||
//
|
||||
void CFuncRotating :: SpinDown( void )
|
||||
{
|
||||
Vector vecAVel;//rotational velocity
|
||||
vec_t vecdir;
|
||||
|
||||
pev->nextthink = pev->ltime + 0.1;
|
||||
|
||||
pev->avelocity = pev->avelocity - ( pev->movedir * ( pev->speed * m_flFanFriction ) );//spin down slower than spinup
|
||||
|
||||
vecAVel = pev->avelocity;// cache entity's rotational velocity
|
||||
|
||||
if (pev->movedir.x != 0)
|
||||
vecdir = pev->movedir.x;
|
||||
else if (pev->movedir.y != 0)
|
||||
vecdir = pev->movedir.y;
|
||||
else
|
||||
vecdir = pev->movedir.z;
|
||||
|
||||
// if we've met or exceeded target speed, set target speed and stop thinking
|
||||
// (note: must check for movedir > 0 or < 0)
|
||||
if (((vecdir > 0) && (vecAVel.x <= 0 && vecAVel.y <= 0 && vecAVel.z <= 0)) ||
|
||||
((vecdir < 0) && (vecAVel.x >= 0 && vecAVel.y >= 0 && vecAVel.z >= 0)))
|
||||
{
|
||||
pev->avelocity = g_vecZero;// set speed in case we overshot
|
||||
|
||||
// stop sound, we're done
|
||||
EMIT_SOUND_DYN(ENT(pev), CHAN_STATIC, (char *)STRING(pev->noiseRunning /* Stop */),
|
||||
0, 0, SND_STOP, m_pitch);
|
||||
|
||||
SetThink( Rotate );
|
||||
Rotate();
|
||||
}
|
||||
else
|
||||
{
|
||||
RampPitchVol(FALSE);
|
||||
}
|
||||
}
|
||||
|
||||
void CFuncRotating :: Rotate( void )
|
||||
{
|
||||
pev->nextthink = pev->ltime + 10;
|
||||
}
|
||||
|
||||
//=========================================================
|
||||
// Rotating Use - when a rotating brush is triggered
|
||||
//=========================================================
|
||||
void CFuncRotating :: RotatingUse( CBaseEntity *pActivator, CBaseEntity *pCaller, USE_TYPE useType, float value )
|
||||
{
|
||||
// is this a brush that should accelerate and decelerate when turned on/off (fan)?
|
||||
if ( FBitSet ( pev->spawnflags, SF_BRUSH_ACCDCC ) )
|
||||
{
|
||||
// fan is spinning, so stop it.
|
||||
if ( pev->avelocity != g_vecZero )
|
||||
{
|
||||
SetThink ( SpinDown );
|
||||
//EMIT_SOUND_DYN(ENT(pev), CHAN_WEAPON, (char *)STRING(pev->noiseStop),
|
||||
// m_flVolume, m_flAttenuation, 0, m_pitch);
|
||||
|
||||
pev->nextthink = pev->ltime + 0.1;
|
||||
}
|
||||
else// fan is not moving, so start it
|
||||
{
|
||||
SetThink ( SpinUp );
|
||||
EMIT_SOUND_DYN(ENT(pev), CHAN_STATIC, (char *)STRING(pev->noiseRunning),
|
||||
0.01, m_flAttenuation, 0, FANPITCHMIN);
|
||||
|
||||
pev->nextthink = pev->ltime + 0.1;
|
||||
}
|
||||
}
|
||||
else if ( !FBitSet ( pev->spawnflags, SF_BRUSH_ACCDCC ) )//this is a normal start/stop brush.
|
||||
{
|
||||
if ( pev->avelocity != g_vecZero )
|
||||
{
|
||||
// play stopping sound here
|
||||
SetThink ( SpinDown );
|
||||
|
||||
// EMIT_SOUND_DYN(ENT(pev), CHAN_WEAPON, (char *)STRING(pev->noiseStop),
|
||||
// m_flVolume, m_flAttenuation, 0, m_pitch);
|
||||
|
||||
pev->nextthink = pev->ltime + 0.1;
|
||||
// pev->avelocity = g_vecZero;
|
||||
}
|
||||
else
|
||||
{
|
||||
EMIT_SOUND_DYN(ENT(pev), CHAN_STATIC, (char *)STRING(pev->noiseRunning),
|
||||
m_flVolume, m_flAttenuation, 0, FANPITCHMAX);
|
||||
pev->avelocity = pev->movedir * pev->speed;
|
||||
|
||||
SetThink( Rotate );
|
||||
Rotate();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
//
|
||||
// RotatingBlocked - An entity has blocked the brush
|
||||
//
|
||||
void CFuncRotating :: Blocked( CBaseEntity *pOther )
|
||||
|
||||
{
|
||||
pOther->TakeDamage( pev, pev, pev->dmg, DMG_CRUSH);
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
//#endif
|
||||
|
||||
|
||||
class CPendulum : public CBaseEntity
|
||||
{
|
||||
public:
|
||||
void Spawn ( void );
|
||||
void KeyValue( KeyValueData *pkvd );
|
||||
void EXPORT Swing( void );
|
||||
void EXPORT PendulumUse( CBaseEntity *pActivator, CBaseEntity *pCaller, USE_TYPE useType, float value );
|
||||
void EXPORT Stop( void );
|
||||
void Touch( CBaseEntity *pOther );
|
||||
void EXPORT RopeTouch ( CBaseEntity *pOther );// this touch func makes the pendulum a rope
|
||||
virtual int ObjectCaps( void ) { return CBaseEntity :: ObjectCaps() & ~FCAP_ACROSS_TRANSITION; }
|
||||
virtual int Save( CSave &save );
|
||||
virtual int Restore( CRestore &restore );
|
||||
void Blocked( CBaseEntity *pOther );
|
||||
|
||||
static TYPEDESCRIPTION m_SaveData[];
|
||||
|
||||
float m_accel; // Acceleration
|
||||
float m_distance; //
|
||||
float m_time;
|
||||
float m_damp;
|
||||
float m_maxSpeed;
|
||||
float m_dampSpeed;
|
||||
vec3_t m_center;
|
||||
vec3_t m_start;
|
||||
};
|
||||
|
||||
LINK_ENTITY_TO_CLASS( func_pendulum, CPendulum );
|
||||
|
||||
TYPEDESCRIPTION CPendulum::m_SaveData[] =
|
||||
{
|
||||
DEFINE_FIELD( CPendulum, m_accel, FIELD_FLOAT ),
|
||||
DEFINE_FIELD( CPendulum, m_distance, FIELD_FLOAT ),
|
||||
DEFINE_FIELD( CPendulum, m_time, FIELD_TIME ),
|
||||
DEFINE_FIELD( CPendulum, m_damp, FIELD_FLOAT ),
|
||||
DEFINE_FIELD( CPendulum, m_maxSpeed, FIELD_FLOAT ),
|
||||
DEFINE_FIELD( CPendulum, m_dampSpeed, FIELD_FLOAT ),
|
||||
DEFINE_FIELD( CPendulum, m_center, FIELD_VECTOR ),
|
||||
DEFINE_FIELD( CPendulum, m_start, FIELD_VECTOR ),
|
||||
};
|
||||
|
||||
IMPLEMENT_SAVERESTORE( CPendulum, CBaseEntity );
|
||||
|
||||
|
||||
|
||||
void CPendulum :: KeyValue( KeyValueData *pkvd )
|
||||
{
|
||||
if (FStrEq(pkvd->szKeyName, "distance"))
|
||||
{
|
||||
m_distance = atof(pkvd->szValue);
|
||||
pkvd->fHandled = TRUE;
|
||||
}
|
||||
else if (FStrEq(pkvd->szKeyName, "damp"))
|
||||
{
|
||||
m_damp = atof(pkvd->szValue) * 0.001;
|
||||
pkvd->fHandled = TRUE;
|
||||
}
|
||||
else
|
||||
CBaseEntity::KeyValue( pkvd );
|
||||
}
|
||||
|
||||
|
||||
void CPendulum :: Spawn( void )
|
||||
{
|
||||
// set the axis of rotation
|
||||
CBaseToggle :: AxisDir( pev );
|
||||
|
||||
if ( FBitSet (pev->spawnflags, SF_DOOR_PASSABLE) )
|
||||
pev->solid = SOLID_NOT;
|
||||
else
|
||||
pev->solid = SOLID_BSP;
|
||||
pev->movetype = MOVETYPE_PUSH;
|
||||
UTIL_SetOrigin(pev, pev->origin);
|
||||
SET_MODEL(ENT(pev), STRING(pev->model) );
|
||||
|
||||
if ( m_distance == 0 )
|
||||
return;
|
||||
|
||||
if (pev->speed == 0)
|
||||
pev->speed = 100;
|
||||
|
||||
m_accel = (pev->speed * pev->speed) / (2 * fabs(m_distance)); // Calculate constant acceleration from speed and distance
|
||||
m_maxSpeed = pev->speed;
|
||||
m_start = pev->angles;
|
||||
m_center = pev->angles + (m_distance * 0.5) * pev->movedir;
|
||||
|
||||
if ( FBitSet( pev->spawnflags, SF_BRUSH_ROTATE_INSTANT) )
|
||||
{
|
||||
SetThink( SUB_CallUseToggle );
|
||||
pev->nextthink = gpGlobals->time + 0.1;
|
||||
}
|
||||
pev->speed = 0;
|
||||
SetUse( PendulumUse );
|
||||
|
||||
if ( FBitSet( pev->spawnflags, SF_PENDULUM_SWING ) )
|
||||
{
|
||||
SetTouch ( RopeTouch );
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
void CPendulum :: PendulumUse( CBaseEntity *pActivator, CBaseEntity *pCaller, USE_TYPE useType, float value )
|
||||
{
|
||||
if ( pev->speed ) // Pendulum is moving, stop it and auto-return if necessary
|
||||
{
|
||||
if ( FBitSet( pev->spawnflags, SF_PENDULUM_AUTO_RETURN ) )
|
||||
{
|
||||
float delta;
|
||||
|
||||
delta = CBaseToggle :: AxisDelta( pev->spawnflags, pev->angles, m_start );
|
||||
|
||||
pev->avelocity = m_maxSpeed * pev->movedir;
|
||||
pev->nextthink = pev->ltime + (delta / m_maxSpeed);
|
||||
SetThink( Stop );
|
||||
}
|
||||
else
|
||||
{
|
||||
pev->speed = 0; // Dead stop
|
||||
SetThink( NULL );
|
||||
pev->avelocity = g_vecZero;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
pev->nextthink = pev->ltime + 0.1; // Start the pendulum moving
|
||||
m_time = gpGlobals->time; // Save time to calculate dt
|
||||
SetThink( Swing );
|
||||
m_dampSpeed = m_maxSpeed;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
void CPendulum :: Stop( void )
|
||||
{
|
||||
pev->angles = m_start;
|
||||
pev->speed = 0;
|
||||
SetThink( NULL );
|
||||
pev->avelocity = g_vecZero;
|
||||
}
|
||||
|
||||
|
||||
void CPendulum::Blocked( CBaseEntity *pOther )
|
||||
{
|
||||
m_time = gpGlobals->time;
|
||||
}
|
||||
|
||||
|
||||
void CPendulum :: Swing( void )
|
||||
{
|
||||
float delta, dt;
|
||||
|
||||
delta = CBaseToggle :: AxisDelta( pev->spawnflags, pev->angles, m_center );
|
||||
dt = gpGlobals->time - m_time; // How much time has passed?
|
||||
m_time = gpGlobals->time; // Remember the last time called
|
||||
|
||||
if ( delta > 0 && m_accel > 0 )
|
||||
pev->speed -= m_accel * dt; // Integrate velocity
|
||||
else
|
||||
pev->speed += m_accel * dt;
|
||||
|
||||
if ( pev->speed > m_maxSpeed )
|
||||
pev->speed = m_maxSpeed;
|
||||
else if ( pev->speed < -m_maxSpeed )
|
||||
pev->speed = -m_maxSpeed;
|
||||
// scale the destdelta vector by the time spent traveling to get velocity
|
||||
pev->avelocity = pev->speed * pev->movedir;
|
||||
|
||||
// Call this again
|
||||
pev->nextthink = pev->ltime + 0.1;
|
||||
|
||||
if ( m_damp )
|
||||
{
|
||||
m_dampSpeed -= m_damp * m_dampSpeed * dt;
|
||||
if ( m_dampSpeed < 30.0 )
|
||||
{
|
||||
pev->angles = m_center;
|
||||
pev->speed = 0;
|
||||
SetThink( NULL );
|
||||
pev->avelocity = g_vecZero;
|
||||
}
|
||||
else if ( pev->speed > m_dampSpeed )
|
||||
pev->speed = m_dampSpeed;
|
||||
else if ( pev->speed < -m_dampSpeed )
|
||||
pev->speed = -m_dampSpeed;
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
void CPendulum :: Touch ( CBaseEntity *pOther )
|
||||
{
|
||||
entvars_t *pevOther = pOther->pev;
|
||||
|
||||
if ( pev->dmg <= 0 )
|
||||
return;
|
||||
|
||||
// we can't hurt this thing, so we're not concerned with it
|
||||
if ( !pevOther->takedamage )
|
||||
return;
|
||||
|
||||
// calculate damage based on rotation speed
|
||||
float damage = pev->dmg * pev->speed * 0.01;
|
||||
|
||||
if ( damage < 0 )
|
||||
damage = -damage;
|
||||
|
||||
pOther->TakeDamage( pev, pev, damage, DMG_CRUSH );
|
||||
|
||||
pevOther->velocity = (pevOther->origin - VecBModelOrigin(pev) ).Normalize() * damage;
|
||||
}
|
||||
|
||||
void CPendulum :: RopeTouch ( CBaseEntity *pOther )
|
||||
{
|
||||
entvars_t *pevOther = pOther->pev;
|
||||
|
||||
if ( !pOther->IsPlayer() )
|
||||
{// not a player!
|
||||
ALERT ( at_console, "Not a client\n" );
|
||||
return;
|
||||
}
|
||||
|
||||
if ( ENT(pevOther) == pev->enemy )
|
||||
{// this player already on the rope.
|
||||
return;
|
||||
}
|
||||
|
||||
pev->enemy = pOther->edict();
|
||||
pevOther->velocity = g_vecZero;
|
||||
pevOther->movetype = MOVETYPE_NONE;
|
||||
}
|
||||
|
||||
|
|
@ -0,0 +1,5 @@
|
|||
LIBRARY server
|
||||
EXPORTS
|
||||
CreateAPI @1
|
||||
SECTIONS
|
||||
.data READ WRITE
|
|
@ -0,0 +1,696 @@
|
|||
# Microsoft Developer Studio Project File - Name="bshift" - Package Owner=<4>
|
||||
# Microsoft Developer Studio Generated Build File, Format Version 6.00
|
||||
# ** DO NOT EDIT **
|
||||
|
||||
# TARGTYPE "Win32 (x86) Dynamic-Link Library" 0x0102
|
||||
|
||||
CFG=bshift - Win32 Release
|
||||
!MESSAGE This is not a valid makefile. To build this project using NMAKE,
|
||||
!MESSAGE use the Export Makefile command and run
|
||||
!MESSAGE
|
||||
!MESSAGE NMAKE /f "bshift.mak".
|
||||
!MESSAGE
|
||||
!MESSAGE You can specify a configuration when running NMAKE
|
||||
!MESSAGE by defining the macro CFG on the command line. For example:
|
||||
!MESSAGE
|
||||
!MESSAGE NMAKE /f "bshift.mak" CFG="bshift - Win32 Release"
|
||||
!MESSAGE
|
||||
!MESSAGE Possible choices for configuration are:
|
||||
!MESSAGE
|
||||
!MESSAGE "bshift - Win32 Release" (based on "Win32 (x86) Dynamic-Link Library")
|
||||
!MESSAGE "bshift - Win32 Debug" (based on "Win32 (x86) Dynamic-Link Library")
|
||||
!MESSAGE
|
||||
|
||||
# Begin Project
|
||||
# PROP AllowPerConfigDependencies 0
|
||||
# PROP Scc_ProjName ""$/GoldSrc/dlls", ELEBAAAA"
|
||||
# PROP Scc_LocalPath "."
|
||||
CPP=cl.exe
|
||||
MTL=midl.exe
|
||||
RSC=rc.exe
|
||||
|
||||
!IF "$(CFG)" == "bshift - Win32 Release"
|
||||
|
||||
# PROP BASE Use_MFC 0
|
||||
# PROP BASE Use_Debug_Libraries 0
|
||||
# PROP BASE Output_Dir ".\Release"
|
||||
# PROP BASE Intermediate_Dir ".\Release"
|
||||
# PROP BASE Target_Dir ""
|
||||
# PROP Use_MFC 0
|
||||
# PROP Use_Debug_Libraries 0
|
||||
# PROP Output_Dir "..\temp\bshift\!release"
|
||||
# PROP Intermediate_Dir "..\temp\bshift\!release"
|
||||
# PROP Ignore_Export_Lib 1
|
||||
# PROP Target_Dir ""
|
||||
# ADD BASE CPP /nologo /MT /W3 /GX /O2 /D "WIN32" /D "NDEBUG" /D "_WINDOWS" /YX /c
|
||||
# ADD CPP /nologo /G5 /MT /W3 /O2 /I "..\bshift" /I "..\common" /I "..\game_shared" /D "NDEBUG" /D "WIN32" /D "_WINDOWS" /FD /c
|
||||
# SUBTRACT CPP /Fr /YX
|
||||
# ADD BASE MTL /nologo /D "NDEBUG" /win32
|
||||
# ADD MTL /nologo /D "NDEBUG" /mktyplib203 /win32
|
||||
# ADD BASE RSC /l 0x409 /d "NDEBUG"
|
||||
# ADD RSC /l 0x409 /d "NDEBUG"
|
||||
BSC32=bscmake.exe
|
||||
# ADD BASE BSC32 /nologo
|
||||
# ADD BSC32 /nologo /o"..\temp\bshift\!release/server.bsc"
|
||||
LINK32=link.exe
|
||||
# ADD BASE LINK32 kernel32.lib user32.lib gdi32.lib winspool.lib comdlg32.lib advapi32.lib shell32.lib ole32.lib oleaut32.lib uuid.lib odbc32.lib odbccp32.lib /nologo /subsystem:windows /dll /machine:I386
|
||||
# ADD LINK32 msvcrt.lib /nologo /subsystem:windows /dll /pdb:none /machine:I386 /nodefaultlib:"libcmt.lib" /def:".\bshift.def" /out:"..\temp\bshift\!release/server.dll"
|
||||
# SUBTRACT LINK32 /profile /map /debug
|
||||
# Begin Custom Build
|
||||
TargetDir=\Xash3D\src_main\temp\bshift\!release
|
||||
InputPath=\Xash3D\src_main\temp\bshift\!release\server.dll
|
||||
SOURCE="$(InputPath)"
|
||||
|
||||
"D:\Xash3D\valve\bin\server.dll" : $(SOURCE) "$(INTDIR)" "$(OUTDIR)"
|
||||
copy $(TargetDir)\server.dll "D:\Xash3D\valve\bin\server.dll"
|
||||
|
||||
# End Custom Build
|
||||
|
||||
!ELSEIF "$(CFG)" == "bshift - Win32 Debug"
|
||||
|
||||
# PROP BASE Use_MFC 0
|
||||
# PROP BASE Use_Debug_Libraries 0
|
||||
# PROP BASE Output_Dir "spirit___Win32_Debug"
|
||||
# PROP BASE Intermediate_Dir "spirit___Win32_Debug"
|
||||
# PROP BASE Ignore_Export_Lib 0
|
||||
# PROP BASE Target_Dir ""
|
||||
# PROP Use_MFC 0
|
||||
# PROP Use_Debug_Libraries 0
|
||||
# PROP Output_Dir "..\temp\bshift\!debug"
|
||||
# PROP Intermediate_Dir "..\temp\bshift\!debug"
|
||||
# PROP Ignore_Export_Lib 1
|
||||
# PROP Target_Dir ""
|
||||
# ADD BASE CPP /nologo /G5 /MT /W3 /O1 /I "..\dlls" /I "..\engine" /I "..\common" /I "..\game_shared" /D "NDEBUG" /D "WIN32" /D "_WINDOWS" /D "QUIVER" /D "VOXEL" /D "QUAKE2" /D "VALVE_DLL" /YX /FD /c
|
||||
# SUBTRACT BASE CPP /Fr
|
||||
# ADD CPP /nologo /MDd /W3 /Gm /GX /ZI /Od /I "..\bshift" /I "..\common" /I "..\game_shared" /D "DEBUG" /D "WIN32" /D "_WINDOWS" /FR /FD /c
|
||||
# SUBTRACT CPP /WX /YX
|
||||
# ADD BASE MTL /nologo /D "NDEBUG" /mktyplib203 /win32
|
||||
# ADD MTL /nologo /D "NDEBUG" /mktyplib203 /win32
|
||||
# ADD BASE RSC /l 0x409 /d "NDEBUG"
|
||||
# ADD RSC /l 0x409 /d "NDEBUG"
|
||||
BSC32=bscmake.exe
|
||||
# ADD BASE BSC32 /nologo /o".\Release/server.bsc"
|
||||
# ADD BSC32 /nologo /o"..\temp\bshift\!debug/server.bsc"
|
||||
LINK32=link.exe
|
||||
# ADD BASE LINK32 kernel32.lib user32.lib gdi32.lib winspool.lib comdlg32.lib advapi32.lib shell32.lib ole32.lib oleaut32.lib uuid.lib odbc32.lib odbccp32.lib /nologo /subsystem:windows /dll /machine:I386
|
||||
# SUBTRACT BASE LINK32 /profile /map /debug
|
||||
# ADD LINK32 msvcrtd.lib /nologo /subsystem:windows /dll /incremental:yes /debug /machine:I386 /nodefaultlib:"libc.lib" /def:".\bshift.def" /out:"..\temp\bshift\!debug/server.dll" /pdbtype:sept
|
||||
# SUBTRACT LINK32 /profile /map
|
||||
# Begin Custom Build
|
||||
TargetDir=\Xash3D\src_main\temp\bshift\!debug
|
||||
InputPath=\Xash3D\src_main\temp\bshift\!debug\server.dll
|
||||
SOURCE="$(InputPath)"
|
||||
|
||||
"D:\Xash3D\valve\bin\server.dll" : $(SOURCE) "$(INTDIR)" "$(OUTDIR)"
|
||||
copy $(TargetDir)\server.dll "D:\Xash3D\valve\bin\server.dll"
|
||||
|
||||
# End Custom Build
|
||||
|
||||
!ENDIF
|
||||
|
||||
# Begin Target
|
||||
|
||||
# Name "bshift - Win32 Release"
|
||||
# Name "bshift - Win32 Debug"
|
||||
# Begin Group "Source Files"
|
||||
|
||||
# PROP Default_Filter "cpp;c;cxx;rc;def;r;odl;idl;hpj;bat;for;f90"
|
||||
# Begin Source File
|
||||
|
||||
SOURCE=.\aflock.cpp
|
||||
# End Source File
|
||||
# Begin Source File
|
||||
|
||||
SOURCE=.\agrunt.cpp
|
||||
# End Source File
|
||||
# Begin Source File
|
||||
|
||||
SOURCE=.\airtank.cpp
|
||||
# End Source File
|
||||
# Begin Source File
|
||||
|
||||
SOURCE=.\animating.cpp
|
||||
# End Source File
|
||||
# Begin Source File
|
||||
|
||||
SOURCE=.\animation.cpp
|
||||
# End Source File
|
||||
# Begin Source File
|
||||
|
||||
SOURCE=.\apache.cpp
|
||||
# End Source File
|
||||
# Begin Source File
|
||||
|
||||
SOURCE=.\barnacle.cpp
|
||||
# End Source File
|
||||
# Begin Source File
|
||||
|
||||
SOURCE=.\barney.cpp
|
||||
# End Source File
|
||||
# Begin Source File
|
||||
|
||||
SOURCE=.\bigmomma.cpp
|
||||
# End Source File
|
||||
# Begin Source File
|
||||
|
||||
SOURCE=.\bloater.cpp
|
||||
# End Source File
|
||||
# Begin Source File
|
||||
|
||||
SOURCE=.\bmodels.cpp
|
||||
# End Source File
|
||||
# Begin Source File
|
||||
|
||||
SOURCE=.\bullsquid.cpp
|
||||
# End Source File
|
||||
# Begin Source File
|
||||
|
||||
SOURCE=.\buttons.cpp
|
||||
# End Source File
|
||||
# Begin Source File
|
||||
|
||||
SOURCE=.\cbase.cpp
|
||||
# End Source File
|
||||
# Begin Source File
|
||||
|
||||
SOURCE=.\client.cpp
|
||||
# End Source File
|
||||
# Begin Source File
|
||||
|
||||
SOURCE=.\combat.cpp
|
||||
# End Source File
|
||||
# Begin Source File
|
||||
|
||||
SOURCE=.\controller.cpp
|
||||
# End Source File
|
||||
# Begin Source File
|
||||
|
||||
SOURCE=.\crossbow.cpp
|
||||
# End Source File
|
||||
# Begin Source File
|
||||
|
||||
SOURCE=.\crowbar.cpp
|
||||
# End Source File
|
||||
# Begin Source File
|
||||
|
||||
SOURCE=.\defaultai.cpp
|
||||
# End Source File
|
||||
# Begin Source File
|
||||
|
||||
SOURCE=.\doors.cpp
|
||||
# End Source File
|
||||
# Begin Source File
|
||||
|
||||
SOURCE=.\effects.cpp
|
||||
# End Source File
|
||||
# Begin Source File
|
||||
|
||||
SOURCE=.\egon.cpp
|
||||
# End Source File
|
||||
# Begin Source File
|
||||
|
||||
SOURCE=.\explode.cpp
|
||||
# End Source File
|
||||
# Begin Source File
|
||||
|
||||
SOURCE=.\flyingmonster.cpp
|
||||
# End Source File
|
||||
# Begin Source File
|
||||
|
||||
SOURCE=.\func_break.cpp
|
||||
# End Source File
|
||||
# Begin Source File
|
||||
|
||||
SOURCE=.\func_tank.cpp
|
||||
# End Source File
|
||||
# Begin Source File
|
||||
|
||||
SOURCE=.\game.cpp
|
||||
# End Source File
|
||||
# Begin Source File
|
||||
|
||||
SOURCE=.\gamerules.cpp
|
||||
# End Source File
|
||||
# Begin Source File
|
||||
|
||||
SOURCE=.\gargantua.cpp
|
||||
# End Source File
|
||||
# Begin Source File
|
||||
|
||||
SOURCE=.\gauss.cpp
|
||||
# End Source File
|
||||
# Begin Source File
|
||||
|
||||
SOURCE=.\genericmonster.cpp
|
||||
# End Source File
|
||||
# Begin Source File
|
||||
|
||||
SOURCE=.\ggrenade.cpp
|
||||
# End Source File
|
||||
# Begin Source File
|
||||
|
||||
SOURCE=.\globals.cpp
|
||||
# End Source File
|
||||
# Begin Source File
|
||||
|
||||
SOURCE=.\glock.cpp
|
||||
# End Source File
|
||||
# Begin Source File
|
||||
|
||||
SOURCE=.\gman.cpp
|
||||
# End Source File
|
||||
# Begin Source File
|
||||
|
||||
SOURCE=.\h_ai.cpp
|
||||
# End Source File
|
||||
# Begin Source File
|
||||
|
||||
SOURCE=.\h_battery.cpp
|
||||
# End Source File
|
||||
# Begin Source File
|
||||
|
||||
SOURCE=.\h_cine.cpp
|
||||
# End Source File
|
||||
# Begin Source File
|
||||
|
||||
SOURCE=.\h_cycler.cpp
|
||||
# End Source File
|
||||
# Begin Source File
|
||||
|
||||
SOURCE=.\h_export.cpp
|
||||
# End Source File
|
||||
# Begin Source File
|
||||
|
||||
SOURCE=.\handgrenade.cpp
|
||||
# End Source File
|
||||
# Begin Source File
|
||||
|
||||
SOURCE=.\hassassin.cpp
|
||||
# End Source File
|
||||
# Begin Source File
|
||||
|
||||
SOURCE=.\headcrab.cpp
|
||||
# End Source File
|
||||
# Begin Source File
|
||||
|
||||
SOURCE=.\healthkit.cpp
|
||||
# End Source File
|
||||
# Begin Source File
|
||||
|
||||
SOURCE=.\hgrunt.cpp
|
||||
# End Source File
|
||||
# Begin Source File
|
||||
|
||||
SOURCE=.\hornet.cpp
|
||||
# End Source File
|
||||
# Begin Source File
|
||||
|
||||
SOURCE=.\hornetgun.cpp
|
||||
# End Source File
|
||||
# Begin Source File
|
||||
|
||||
SOURCE=.\houndeye.cpp
|
||||
# End Source File
|
||||
# Begin Source File
|
||||
|
||||
SOURCE=.\ichthyosaur.cpp
|
||||
# End Source File
|
||||
# Begin Source File
|
||||
|
||||
SOURCE=.\islave.cpp
|
||||
# End Source File
|
||||
# Begin Source File
|
||||
|
||||
SOURCE=.\items.cpp
|
||||
# End Source File
|
||||
# Begin Source File
|
||||
|
||||
SOURCE=.\leech.cpp
|
||||
# End Source File
|
||||
# Begin Source File
|
||||
|
||||
SOURCE=.\lights.cpp
|
||||
# End Source File
|
||||
# Begin Source File
|
||||
|
||||
SOURCE=.\maprules.cpp
|
||||
# End Source File
|
||||
# Begin Source File
|
||||
|
||||
SOURCE=.\monstermaker.cpp
|
||||
# End Source File
|
||||
# Begin Source File
|
||||
|
||||
SOURCE=.\monsters.cpp
|
||||
# End Source File
|
||||
# Begin Source File
|
||||
|
||||
SOURCE=.\monsters.h
|
||||
# End Source File
|
||||
# Begin Source File
|
||||
|
||||
SOURCE=.\monsterstate.cpp
|
||||
# End Source File
|
||||
# Begin Source File
|
||||
|
||||
SOURCE=.\mortar.cpp
|
||||
# End Source File
|
||||
# Begin Source File
|
||||
|
||||
SOURCE=.\mp5.cpp
|
||||
# End Source File
|
||||
# Begin Source File
|
||||
|
||||
SOURCE=.\multiplay_gamerules.cpp
|
||||
# End Source File
|
||||
# Begin Source File
|
||||
|
||||
SOURCE=.\nihilanth.cpp
|
||||
# End Source File
|
||||
# Begin Source File
|
||||
|
||||
SOURCE=.\nodes.cpp
|
||||
# End Source File
|
||||
# Begin Source File
|
||||
|
||||
SOURCE=.\osprey.cpp
|
||||
# End Source File
|
||||
# Begin Source File
|
||||
|
||||
SOURCE=.\pathcorner.cpp
|
||||
# End Source File
|
||||
# Begin Source File
|
||||
|
||||
SOURCE=.\plane.cpp
|
||||
# End Source File
|
||||
# Begin Source File
|
||||
|
||||
SOURCE=.\plats.cpp
|
||||
# End Source File
|
||||
# Begin Source File
|
||||
|
||||
SOURCE=.\player.cpp
|
||||
# End Source File
|
||||
# Begin Source File
|
||||
|
||||
SOURCE=..\game_shared\pm_shared.cpp
|
||||
# End Source File
|
||||
# Begin Source File
|
||||
|
||||
SOURCE=.\python.cpp
|
||||
# End Source File
|
||||
# Begin Source File
|
||||
|
||||
SOURCE=.\rat.cpp
|
||||
# End Source File
|
||||
# Begin Source File
|
||||
|
||||
SOURCE=.\roach.cpp
|
||||
# End Source File
|
||||
# Begin Source File
|
||||
|
||||
SOURCE=.\rpg.cpp
|
||||
# End Source File
|
||||
# Begin Source File
|
||||
|
||||
SOURCE=.\satchel.cpp
|
||||
# End Source File
|
||||
# Begin Source File
|
||||
|
||||
SOURCE=.\schedule.cpp
|
||||
# End Source File
|
||||
# Begin Source File
|
||||
|
||||
SOURCE=.\scientist.cpp
|
||||
# End Source File
|
||||
# Begin Source File
|
||||
|
||||
SOURCE=.\scripted.cpp
|
||||
# End Source File
|
||||
# Begin Source File
|
||||
|
||||
SOURCE=.\shotgun.cpp
|
||||
# End Source File
|
||||
# Begin Source File
|
||||
|
||||
SOURCE=.\singleplay_gamerules.cpp
|
||||
# End Source File
|
||||
# Begin Source File
|
||||
|
||||
SOURCE=.\skill.cpp
|
||||
# End Source File
|
||||
# Begin Source File
|
||||
|
||||
SOURCE=.\sound.cpp
|
||||
# End Source File
|
||||
# Begin Source File
|
||||
|
||||
SOURCE=.\soundent.cpp
|
||||
# End Source File
|
||||
# Begin Source File
|
||||
|
||||
SOURCE=.\spectator.cpp
|
||||
# End Source File
|
||||
# Begin Source File
|
||||
|
||||
SOURCE=.\squadmonster.cpp
|
||||
# End Source File
|
||||
# Begin Source File
|
||||
|
||||
SOURCE=.\squeakgrenade.cpp
|
||||
# End Source File
|
||||
# Begin Source File
|
||||
|
||||
SOURCE=.\subs.cpp
|
||||
# End Source File
|
||||
# Begin Source File
|
||||
|
||||
SOURCE=.\talkmonster.cpp
|
||||
# End Source File
|
||||
# Begin Source File
|
||||
|
||||
SOURCE=.\teamplay_gamerules.cpp
|
||||
# End Source File
|
||||
# Begin Source File
|
||||
|
||||
SOURCE=.\tempmonster.cpp
|
||||
# End Source File
|
||||
# Begin Source File
|
||||
|
||||
SOURCE=.\tentacle.cpp
|
||||
# End Source File
|
||||
# Begin Source File
|
||||
|
||||
SOURCE=.\triggers.cpp
|
||||
# End Source File
|
||||
# Begin Source File
|
||||
|
||||
SOURCE=.\tripmine.cpp
|
||||
# End Source File
|
||||
# Begin Source File
|
||||
|
||||
SOURCE=.\turret.cpp
|
||||
# End Source File
|
||||
# Begin Source File
|
||||
|
||||
SOURCE=.\util.cpp
|
||||
# End Source File
|
||||
# Begin Source File
|
||||
|
||||
SOURCE=.\util.h
|
||||
# End Source File
|
||||
# Begin Source File
|
||||
|
||||
SOURCE=.\weapons.cpp
|
||||
# End Source File
|
||||
# Begin Source File
|
||||
|
||||
SOURCE=.\world.cpp
|
||||
# End Source File
|
||||
# Begin Source File
|
||||
|
||||
SOURCE=.\xen.cpp
|
||||
# End Source File
|
||||
# Begin Source File
|
||||
|
||||
SOURCE=.\zombie.cpp
|
||||
# End Source File
|
||||
# End Group
|
||||
# Begin Group "Header Files"
|
||||
|
||||
# PROP Default_Filter "h;hpp;hxx;hm;inl;fi;fd"
|
||||
# Begin Source File
|
||||
|
||||
SOURCE=.\activity.h
|
||||
# End Source File
|
||||
# Begin Source File
|
||||
|
||||
SOURCE=.\activitymap.h
|
||||
# End Source File
|
||||
# Begin Source File
|
||||
|
||||
SOURCE=.\animation.h
|
||||
# End Source File
|
||||
# Begin Source File
|
||||
|
||||
SOURCE=.\basemonster.h
|
||||
# End Source File
|
||||
# Begin Source File
|
||||
|
||||
SOURCE=.\cbase.h
|
||||
# End Source File
|
||||
# Begin Source File
|
||||
|
||||
SOURCE=.\cdll_dll.h
|
||||
# End Source File
|
||||
# Begin Source File
|
||||
|
||||
SOURCE=.\client.h
|
||||
# End Source File
|
||||
# Begin Source File
|
||||
|
||||
SOURCE=.\decals.h
|
||||
# End Source File
|
||||
# Begin Source File
|
||||
|
||||
SOURCE=.\defaultai.h
|
||||
# End Source File
|
||||
# Begin Source File
|
||||
|
||||
SOURCE=.\doors.h
|
||||
# End Source File
|
||||
# Begin Source File
|
||||
|
||||
SOURCE=.\effects.h
|
||||
# End Source File
|
||||
# Begin Source File
|
||||
|
||||
SOURCE=.\enginecallback.h
|
||||
# End Source File
|
||||
# Begin Source File
|
||||
|
||||
SOURCE=.\explode.h
|
||||
# End Source File
|
||||
# Begin Source File
|
||||
|
||||
SOURCE=.\extdll.h
|
||||
# End Source File
|
||||
# Begin Source File
|
||||
|
||||
SOURCE=.\flyingmonster.h
|
||||
# End Source File
|
||||
# Begin Source File
|
||||
|
||||
SOURCE=.\func_break.h
|
||||
# End Source File
|
||||
# Begin Source File
|
||||
|
||||
SOURCE=.\gamerules.h
|
||||
# End Source File
|
||||
# Begin Source File
|
||||
|
||||
SOURCE=.\hornet.h
|
||||
# End Source File
|
||||
# Begin Source File
|
||||
|
||||
SOURCE=.\items.h
|
||||
# End Source File
|
||||
# Begin Source File
|
||||
|
||||
SOURCE=.\monsterevent.h
|
||||
# End Source File
|
||||
# Begin Source File
|
||||
|
||||
SOURCE=.\nodes.h
|
||||
# End Source File
|
||||
# Begin Source File
|
||||
|
||||
SOURCE=.\plane.h
|
||||
# End Source File
|
||||
# Begin Source File
|
||||
|
||||
SOURCE=.\player.h
|
||||
# End Source File
|
||||
# Begin Source File
|
||||
|
||||
SOURCE=..\pm_shared\pm_debug.h
|
||||
# End Source File
|
||||
# Begin Source File
|
||||
|
||||
SOURCE=..\pm_shared\pm_defs.h
|
||||
# End Source File
|
||||
# Begin Source File
|
||||
|
||||
SOURCE=..\pm_shared\pm_info.h
|
||||
# End Source File
|
||||
# Begin Source File
|
||||
|
||||
SOURCE=..\pm_shared\pm_materials.h
|
||||
# End Source File
|
||||
# Begin Source File
|
||||
|
||||
SOURCE=..\pm_shared\pm_movevars.h
|
||||
# End Source File
|
||||
# Begin Source File
|
||||
|
||||
SOURCE=..\pm_shared\pm_shared.h
|
||||
# End Source File
|
||||
# Begin Source File
|
||||
|
||||
SOURCE=.\saverestore.h
|
||||
# End Source File
|
||||
# Begin Source File
|
||||
|
||||
SOURCE=.\schedule.h
|
||||
# End Source File
|
||||
# Begin Source File
|
||||
|
||||
SOURCE=.\scripted.h
|
||||
# End Source File
|
||||
# Begin Source File
|
||||
|
||||
SOURCE=.\scriptevent.h
|
||||
# End Source File
|
||||
# Begin Source File
|
||||
|
||||
SOURCE=.\skill.h
|
||||
# End Source File
|
||||
# Begin Source File
|
||||
|
||||
SOURCE=.\soundent.h
|
||||
# End Source File
|
||||
# Begin Source File
|
||||
|
||||
SOURCE=.\spectator.h
|
||||
# End Source File
|
||||
# Begin Source File
|
||||
|
||||
SOURCE=.\squadmonster.h
|
||||
# End Source File
|
||||
# Begin Source File
|
||||
|
||||
SOURCE=.\talkmonster.h
|
||||
# End Source File
|
||||
# Begin Source File
|
||||
|
||||
SOURCE=.\teamplay_gamerules.h
|
||||
# End Source File
|
||||
# Begin Source File
|
||||
|
||||
SOURCE=.\trains.h
|
||||
# End Source File
|
||||
# Begin Source File
|
||||
|
||||
SOURCE=.\vector.h
|
||||
# End Source File
|
||||
# Begin Source File
|
||||
|
||||
SOURCE=.\weapons.h
|
||||
# End Source File
|
||||
# End Group
|
||||
# Begin Group "Resource Files"
|
||||
|
||||
# PROP Default_Filter "ico;cur;bmp;dlg;rc2;rct;bin;cnt;rtf;gif;jpg;jpeg;jpe"
|
||||
# End Group
|
||||
# End Target
|
||||
# End Project
|
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
|
@ -0,0 +1,790 @@
|
|||
/***
|
||||
*
|
||||
* 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 "saverestore.h"
|
||||
#include "client.h"
|
||||
#include "decals.h"
|
||||
#include "gamerules.h"
|
||||
#include "game.h"
|
||||
|
||||
void EntvarsKeyvalue( entvars_t *pev, KeyValueData *pkvd );
|
||||
|
||||
extern Vector VecBModelOrigin( entvars_t* pevBModel );
|
||||
extern DLL_GLOBAL Vector g_vecAttackDir;
|
||||
extern DLL_GLOBAL int g_iSkillLevel;
|
||||
|
||||
static DLL_FUNCTIONS gFunctionTable =
|
||||
{
|
||||
sizeof( DLL_FUNCTIONS ), // Xash3D requires this
|
||||
GameDLLInit, //pfnGameInit
|
||||
DispatchSpawn, //pfnSpawn
|
||||
DispatchThink, //pfnThink
|
||||
DispatchUse, //pfnUse
|
||||
DispatchTouch, //pfnTouch
|
||||
DispatchBlocked, //pfnBlocked
|
||||
DispatchKeyValue, //pfnKeyValue
|
||||
DispatchSave, //pfnSave
|
||||
DispatchRestore, //pfnRestore
|
||||
DispatchObjectCollsionBox, //pfnAbsBox
|
||||
|
||||
SaveWriteFields, //pfnSaveWriteFields
|
||||
SaveReadFields, //pfnSaveReadFields
|
||||
|
||||
SaveGlobalState, //pfnSaveGlobalState
|
||||
RestoreGlobalState, //pfnRestoreGlobalState
|
||||
ResetGlobalState, //pfnResetGlobalState
|
||||
|
||||
ClientConnect, //pfnClientConnect
|
||||
ClientDisconnect, //pfnClientDisconnect
|
||||
ClientKill, //pfnClientKill
|
||||
ClientPutInServer, //pfnClientPutInServer
|
||||
ClientCommand, //pfnClientCommand
|
||||
ClientUserInfoChanged, //pfnClientUserInfoChanged
|
||||
|
||||
ServerActivate, //pfnServerActivate
|
||||
ServerDeactivate, //pfnServerDeactivate
|
||||
|
||||
PlayerPreThink, //pfnPlayerPreThink
|
||||
PlayerPostThink, //pfnPlayerPostThink
|
||||
|
||||
StartFrame, //pfnStartFrame
|
||||
DispatchCreate, //pfnCreate
|
||||
ParmsChangeLevel, //pfnParmsChangeLevel
|
||||
|
||||
GetGameDescription, //pfnGetGameDescription Returns string describing current .dll game.
|
||||
GetEntvarsDescirption, // pfnGetEntvarsDescirption engine uses this to lookup entvars table
|
||||
|
||||
SpectatorConnect, //pfnSpectatorConnect Called when spectator joins server
|
||||
SpectatorDisconnect, //pfnSpectatorDisconnect Called when spectator leaves the server
|
||||
SpectatorThink, //pfnSpectatorThink Called when spectator sends a command packet (usercmd_t)
|
||||
|
||||
ServerClassifyEdict, // pfnClassifyEdict
|
||||
|
||||
PM_Move, // pfnPM_Move
|
||||
PM_Init, // pfnPM_Init Server version of player movement initialization
|
||||
PM_FindTextureType, // pfnPM_FindTextureType
|
||||
|
||||
SetupVisibility, // pfnSetupVisibility
|
||||
DispatchFrame, // pfnPhysicsEntity
|
||||
AddToFullPack, // pfnAddtoFullPack
|
||||
EndFrame, // pfnEndFrame
|
||||
|
||||
ShouldCollide, // pfnShouldCollide
|
||||
UpdateEntityState, // pfnUpdateEntityState
|
||||
OnFreeEntPrivateData, // pfnOnFreeEntPrivateData
|
||||
CmdStart, // pfnCmdStart
|
||||
CmdEnd, // pfnCmdEnd
|
||||
|
||||
GameDLLShutdown, // pfnGameShutdown
|
||||
};
|
||||
|
||||
static void SetObjectCollisionBox( entvars_t *pev );
|
||||
|
||||
//=======================================================================
|
||||
// General API entering point
|
||||
//=======================================================================
|
||||
|
||||
int CreateAPI( DLL_FUNCTIONS *pFunctionTable, enginefuncs_t* pengfuncsFromEngine, globalvars_t *pGlobals )
|
||||
{
|
||||
if( !pFunctionTable || !pengfuncsFromEngine || !pGlobals )
|
||||
{
|
||||
return FALSE;
|
||||
}
|
||||
|
||||
memcpy( pFunctionTable, &gFunctionTable, sizeof( DLL_FUNCTIONS ));
|
||||
memcpy(&g_engfuncs, pengfuncsFromEngine, sizeof( enginefuncs_t ));
|
||||
gpGlobals = pGlobals;
|
||||
|
||||
return TRUE;
|
||||
}
|
||||
|
||||
|
||||
|
||||
int DispatchSpawn( edict_t *pent )
|
||||
{
|
||||
CBaseEntity *pEntity = (CBaseEntity *)GET_PRIVATE(pent);
|
||||
|
||||
if (pEntity)
|
||||
{
|
||||
// Initialize these or entities who don't link to the world won't have anything in here
|
||||
pEntity->pev->absmin = pEntity->pev->origin - Vector(1,1,1);
|
||||
pEntity->pev->absmax = pEntity->pev->origin + Vector(1,1,1);
|
||||
|
||||
pEntity->Spawn();
|
||||
|
||||
// Try to get the pointer again, in case the spawn function deleted the entity.
|
||||
// UNDONE: Spawn() should really return a code to ask that the entity be deleted, but
|
||||
// that would touch too much code for me to do that right now.
|
||||
pEntity = (CBaseEntity *)GET_PRIVATE(pent);
|
||||
|
||||
if ( pEntity )
|
||||
{
|
||||
if ( g_pGameRules && !g_pGameRules->IsAllowedToSpawn( pEntity ) )
|
||||
return -1; // return that this entity should be deleted
|
||||
if ( pEntity->pev->flags & FL_KILLME )
|
||||
return -1;
|
||||
}
|
||||
|
||||
|
||||
// Handle global stuff here
|
||||
if ( pEntity && pEntity->pev->globalname )
|
||||
{
|
||||
const globalentity_t *pGlobal = gGlobalState.EntityFromTable( pEntity->pev->globalname );
|
||||
if ( pGlobal )
|
||||
{
|
||||
// Already dead? delete
|
||||
if ( pGlobal->state == GLOBAL_DEAD )
|
||||
return -1;
|
||||
else if ( !FStrEq( STRING(gpGlobals->mapname), pGlobal->levelName ) )
|
||||
pEntity->MakeDormant(); // Hasn't been moved to this level yet, wait but stay alive
|
||||
// In this level & not dead, continue on as normal
|
||||
}
|
||||
else
|
||||
{
|
||||
// Spawned entities default to 'On'
|
||||
gGlobalState.EntityAdd( pEntity->pev->globalname, gpGlobals->mapname, GLOBAL_ON );
|
||||
// ALERT( at_console, "Added global entity %s (%s)\n", STRING(pEntity->pev->classname), STRING(pEntity->pev->globalname) );
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
int DispatchCreate( edict_t *pent, const char *szName )
|
||||
{
|
||||
// Xash3D extension
|
||||
// handle virtual entities here
|
||||
return -1;
|
||||
}
|
||||
|
||||
void DispatchKeyValue( edict_t *pentKeyvalue, KeyValueData *pkvd )
|
||||
{
|
||||
if ( !pkvd || !pentKeyvalue )
|
||||
return;
|
||||
|
||||
EntvarsKeyvalue( VARS(pentKeyvalue), pkvd );
|
||||
|
||||
// If the key was an entity variable, or there's no class set yet, don't look for the object, it may
|
||||
// not exist yet.
|
||||
if ( pkvd->fHandled || pkvd->szClassName == NULL )
|
||||
return;
|
||||
|
||||
// Get the actualy entity object
|
||||
CBaseEntity *pEntity = (CBaseEntity *)GET_PRIVATE(pentKeyvalue);
|
||||
|
||||
if ( !pEntity )
|
||||
return;
|
||||
|
||||
pEntity->KeyValue( pkvd );
|
||||
}
|
||||
|
||||
/*
|
||||
-----------------
|
||||
DispatchFrame
|
||||
|
||||
this function can override any physics movement
|
||||
and let user use custom physic.
|
||||
e.g. you can replace MOVETYPE_PUSH for new movewith system
|
||||
and many many other things.
|
||||
-----------------
|
||||
*/
|
||||
int DispatchFrame( edict_t *pent )
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
|
||||
// HACKHACK -- this is a hack to keep the node graph entity from "touching" things (like triggers)
|
||||
// while it builds the graph
|
||||
BOOL gTouchDisabled = FALSE;
|
||||
void DispatchTouch( edict_t *pentTouched, edict_t *pentOther )
|
||||
{
|
||||
if ( gTouchDisabled )
|
||||
return;
|
||||
|
||||
CBaseEntity *pEntity = (CBaseEntity *)GET_PRIVATE(pentTouched);
|
||||
CBaseEntity *pOther = (CBaseEntity *)GET_PRIVATE( pentOther );
|
||||
|
||||
if ( pEntity && pOther && ! ((pEntity->pev->flags | pOther->pev->flags) & FL_KILLME) )
|
||||
pEntity->Touch( pOther );
|
||||
}
|
||||
|
||||
|
||||
void DispatchUse( edict_t *pentUsed, edict_t *pentOther )
|
||||
{
|
||||
CBaseEntity *pEntity = (CBaseEntity *)GET_PRIVATE(pentUsed);
|
||||
CBaseEntity *pOther = (CBaseEntity *)GET_PRIVATE(pentOther);
|
||||
|
||||
if (pEntity && !(pEntity->pev->flags & FL_KILLME) )
|
||||
pEntity->Use( pOther, pOther, USE_TOGGLE, 0 );
|
||||
}
|
||||
|
||||
void DispatchThink( edict_t *pent )
|
||||
{
|
||||
CBaseEntity *pEntity = (CBaseEntity *)GET_PRIVATE(pent);
|
||||
if (pEntity)
|
||||
{
|
||||
if ( FBitSet( pEntity->pev->flags, FL_DORMANT ) )
|
||||
ALERT( at_error, "Dormant entity %s is thinking!!\n", STRING(pEntity->pev->classname) );
|
||||
|
||||
pEntity->Think();
|
||||
}
|
||||
}
|
||||
|
||||
void DispatchBlocked( edict_t *pentBlocked, edict_t *pentOther )
|
||||
{
|
||||
CBaseEntity *pEntity = (CBaseEntity *)GET_PRIVATE( pentBlocked );
|
||||
CBaseEntity *pOther = (CBaseEntity *)GET_PRIVATE( pentOther );
|
||||
|
||||
if (pEntity)
|
||||
pEntity->Blocked( pOther );
|
||||
}
|
||||
|
||||
void DispatchSave( edict_t *pent, SAVERESTOREDATA *pSaveData )
|
||||
{
|
||||
CBaseEntity *pEntity = (CBaseEntity *)GET_PRIVATE(pent);
|
||||
|
||||
if ( pEntity && pSaveData )
|
||||
{
|
||||
ENTITYTABLE *pTable = &pSaveData->pTable[ pSaveData->currentIndex ];
|
||||
|
||||
if ( pTable->pent != pent )
|
||||
ALERT( at_error, "ENTITY TABLE OR INDEX IS WRONG!!!!\n" );
|
||||
|
||||
if ( pEntity->ObjectCaps() & FCAP_DONT_SAVE )
|
||||
return;
|
||||
|
||||
// These don't use ltime & nextthink as times really, but we'll fudge around it.
|
||||
if ( pEntity->pev->movetype == MOVETYPE_PUSH )
|
||||
{
|
||||
float delta = pEntity->pev->nextthink - pEntity->pev->ltime;
|
||||
pEntity->pev->ltime = gpGlobals->time;
|
||||
pEntity->pev->nextthink = pEntity->pev->ltime + delta;
|
||||
}
|
||||
|
||||
pTable->location = pSaveData->size; // Remember entity position for file I/O
|
||||
pTable->classname = pEntity->pev->classname; // Remember entity class for respawn
|
||||
|
||||
CSave saveHelper( pSaveData );
|
||||
pEntity->Save( saveHelper );
|
||||
|
||||
pTable->size = pSaveData->size - pTable->location; // Size of entity block is data size written to block
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
// Find the matching global entity. Spit out an error if the designer made entities of
|
||||
// different classes with the same global name
|
||||
CBaseEntity *FindGlobalEntity( string_t classname, string_t globalname )
|
||||
{
|
||||
edict_t *pent = FIND_ENTITY_BY_STRING( NULL, "globalname", STRING(globalname) );
|
||||
CBaseEntity *pReturn = CBaseEntity::Instance( pent );
|
||||
if ( pReturn )
|
||||
{
|
||||
if ( !FClassnameIs( pReturn->pev, STRING(classname) ) )
|
||||
{
|
||||
ALERT( at_console, "Global entity found %s, wrong class %s\n", STRING(globalname), STRING(pReturn->pev->classname) );
|
||||
pReturn = NULL;
|
||||
}
|
||||
}
|
||||
|
||||
return pReturn;
|
||||
}
|
||||
|
||||
|
||||
int DispatchRestore( edict_t *pent, SAVERESTOREDATA *pSaveData, int globalEntity )
|
||||
{
|
||||
CBaseEntity *pEntity = (CBaseEntity *)GET_PRIVATE(pent);
|
||||
|
||||
if ( pEntity && pSaveData )
|
||||
{
|
||||
entvars_t tmpVars;
|
||||
Vector oldOffset;
|
||||
|
||||
CRestore restoreHelper( pSaveData );
|
||||
if ( globalEntity )
|
||||
{
|
||||
CRestore tmpRestore( pSaveData );
|
||||
tmpRestore.PrecacheMode( 0 );
|
||||
tmpRestore.ReadEntVars( "ENTVARS", &tmpVars );
|
||||
|
||||
// HACKHACK - reset the save pointers, we're going to restore for real this time
|
||||
pSaveData->size = pSaveData->pTable[pSaveData->currentIndex].location;
|
||||
pSaveData->pCurrentData = pSaveData->pBaseData + pSaveData->size;
|
||||
// -------------------
|
||||
|
||||
|
||||
const globalentity_t *pGlobal = gGlobalState.EntityFromTable( tmpVars.globalname );
|
||||
|
||||
// Don't overlay any instance of the global that isn't the latest
|
||||
// pSaveData->szCurrentMapName is the level this entity is coming from
|
||||
// pGlobla->levelName is the last level the global entity was active in.
|
||||
// If they aren't the same, then this global update is out of date.
|
||||
if ( !FStrEq( pSaveData->szCurrentMapName, pGlobal->levelName ) )
|
||||
return 0;
|
||||
|
||||
// Compute the new global offset
|
||||
oldOffset = pSaveData->vecLandmarkOffset;
|
||||
CBaseEntity *pNewEntity = FindGlobalEntity( tmpVars.classname, tmpVars.globalname );
|
||||
if ( pNewEntity )
|
||||
{
|
||||
// ALERT( at_console, "Overlay %s with %s\n", STRING(pNewEntity->pev->classname), STRING(tmpVars.classname) );
|
||||
// Tell the restore code we're overlaying a global entity from another level
|
||||
restoreHelper.SetGlobalMode( 1 ); // Don't overwrite global fields
|
||||
pSaveData->vecLandmarkOffset = (pSaveData->vecLandmarkOffset - pNewEntity->pev->mins) + tmpVars.mins;
|
||||
pEntity = pNewEntity;// we're going to restore this data OVER the old entity
|
||||
pent = ENT( pEntity->pev );
|
||||
// Update the global table to say that the global definition of this entity should come from this level
|
||||
gGlobalState.EntityUpdate( pEntity->pev->globalname, gpGlobals->mapname );
|
||||
}
|
||||
else
|
||||
{
|
||||
// This entity will be freed automatically by the engine. If we don't do a restore on a matching entity (below)
|
||||
// or call EntityUpdate() to move it to this level, we haven't changed global state at all.
|
||||
return 0;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
if ( pEntity->ObjectCaps() & FCAP_MUST_SPAWN )
|
||||
{
|
||||
pEntity->Restore( restoreHelper );
|
||||
pEntity->Spawn();
|
||||
}
|
||||
else
|
||||
{
|
||||
pEntity->Restore( restoreHelper );
|
||||
pEntity->Precache( );
|
||||
}
|
||||
|
||||
// Again, could be deleted, get the pointer again.
|
||||
pEntity = (CBaseEntity *)GET_PRIVATE(pent);
|
||||
|
||||
#if 0
|
||||
if ( pEntity && pEntity->pev->globalname && globalEntity )
|
||||
{
|
||||
ALERT( at_console, "Global %s is %s\n", STRING(pEntity->pev->globalname), STRING(pEntity->pev->model) );
|
||||
}
|
||||
#endif
|
||||
|
||||
// Is this an overriding global entity (coming over the transition), or one restoring in a level
|
||||
if ( globalEntity )
|
||||
{
|
||||
// ALERT( at_console, "After: %f %f %f %s\n", pEntity->pev->origin.x, pEntity->pev->origin.y, pEntity->pev->origin.z, STRING(pEntity->pev->model) );
|
||||
pSaveData->vecLandmarkOffset = oldOffset;
|
||||
if ( pEntity )
|
||||
{
|
||||
UTIL_SetOrigin( pEntity->pev, pEntity->pev->origin );
|
||||
pEntity->OverrideReset();
|
||||
}
|
||||
}
|
||||
else if ( pEntity && pEntity->pev->globalname )
|
||||
{
|
||||
const globalentity_t *pGlobal = gGlobalState.EntityFromTable( pEntity->pev->globalname );
|
||||
if ( pGlobal )
|
||||
{
|
||||
// Already dead? delete
|
||||
if ( pGlobal->state == GLOBAL_DEAD )
|
||||
return -1;
|
||||
else if ( !FStrEq( STRING(gpGlobals->mapname), pGlobal->levelName ) )
|
||||
{
|
||||
pEntity->MakeDormant(); // Hasn't been moved to this level yet, wait but stay alive
|
||||
}
|
||||
// In this level & not dead, continue on as normal
|
||||
}
|
||||
else
|
||||
{
|
||||
ALERT( at_error, "Global Entity %s (%s) not in table!!!\n", STRING(pEntity->pev->globalname), STRING(pEntity->pev->classname) );
|
||||
// Spawned entities default to 'On'
|
||||
gGlobalState.EntityAdd( pEntity->pev->globalname, gpGlobals->mapname, GLOBAL_ON );
|
||||
}
|
||||
}
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
||||
void DispatchObjectCollsionBox( edict_t *pent )
|
||||
{
|
||||
CBaseEntity *pEntity = (CBaseEntity *)GET_PRIVATE(pent);
|
||||
if (pEntity)
|
||||
{
|
||||
pEntity->SetObjectCollisionBox();
|
||||
}
|
||||
else
|
||||
SetObjectCollisionBox( &pent->v );
|
||||
}
|
||||
|
||||
|
||||
void SaveWriteFields( SAVERESTOREDATA *pSaveData, const char *pname, void *pBaseData, TYPEDESCRIPTION *pFields, int fieldCount )
|
||||
{
|
||||
CSave saveHelper( pSaveData );
|
||||
saveHelper.WriteFields( pname, pBaseData, pFields, fieldCount );
|
||||
}
|
||||
|
||||
|
||||
void SaveReadFields( SAVERESTOREDATA *pSaveData, const char *pname, void *pBaseData, TYPEDESCRIPTION *pFields, int fieldCount )
|
||||
{
|
||||
CRestore restoreHelper( pSaveData );
|
||||
restoreHelper.ReadFields( pname, pBaseData, pFields, fieldCount );
|
||||
}
|
||||
|
||||
void OnFreeEntPrivateData( edict_t *pEdict )
|
||||
{
|
||||
if( pEdict && pEdict->pvPrivateData )
|
||||
{
|
||||
((CBaseEntity*)pEdict->pvPrivateData)->~CBaseEntity();
|
||||
}
|
||||
}
|
||||
|
||||
edict_t * EHANDLE::Get( void )
|
||||
{
|
||||
if (m_pent)
|
||||
{
|
||||
if (m_pent->serialnumber == m_serialnumber)
|
||||
return m_pent;
|
||||
else
|
||||
return NULL;
|
||||
}
|
||||
return NULL;
|
||||
};
|
||||
|
||||
edict_t * EHANDLE::Set( edict_t *pent )
|
||||
{
|
||||
m_pent = pent;
|
||||
if (pent)
|
||||
m_serialnumber = m_pent->serialnumber;
|
||||
return pent;
|
||||
};
|
||||
|
||||
|
||||
EHANDLE :: operator CBaseEntity *()
|
||||
{
|
||||
return (CBaseEntity *)GET_PRIVATE( Get( ) );
|
||||
};
|
||||
|
||||
|
||||
CBaseEntity * EHANDLE :: operator = (CBaseEntity *pEntity)
|
||||
{
|
||||
if (pEntity)
|
||||
{
|
||||
m_pent = ENT( pEntity->pev );
|
||||
if (m_pent)
|
||||
m_serialnumber = m_pent->serialnumber;
|
||||
}
|
||||
else
|
||||
{
|
||||
m_pent = NULL;
|
||||
m_serialnumber = 0;
|
||||
}
|
||||
return pEntity;
|
||||
}
|
||||
|
||||
EHANDLE :: operator int ()
|
||||
{
|
||||
return Get() != NULL;
|
||||
}
|
||||
|
||||
CBaseEntity * EHANDLE :: operator -> ()
|
||||
{
|
||||
return (CBaseEntity *)GET_PRIVATE( Get( ) );
|
||||
}
|
||||
|
||||
|
||||
// give health
|
||||
int CBaseEntity :: TakeHealth( float flHealth, int bitsDamageType )
|
||||
{
|
||||
if (!pev->takedamage)
|
||||
return 0;
|
||||
|
||||
// heal
|
||||
if ( pev->health >= pev->max_health )
|
||||
return 0;
|
||||
|
||||
pev->health += flHealth;
|
||||
|
||||
if (pev->health > pev->max_health)
|
||||
pev->health = pev->max_health;
|
||||
|
||||
return 1;
|
||||
}
|
||||
|
||||
// inflict damage on this entity. bitsDamageType indicates type of damage inflicted, ie: DMG_CRUSH
|
||||
|
||||
int CBaseEntity :: TakeDamage( entvars_t* pevInflictor, entvars_t* pevAttacker, float flDamage, int bitsDamageType )
|
||||
{
|
||||
Vector vecTemp;
|
||||
|
||||
if (!pev->takedamage)
|
||||
return 0;
|
||||
|
||||
// UNDONE: some entity types may be immune or resistant to some bitsDamageType
|
||||
|
||||
// if Attacker == Inflictor, the attack was a melee or other instant-hit attack.
|
||||
// (that is, no actual entity projectile was involved in the attack so use the shooter's origin).
|
||||
if ( pevAttacker == pevInflictor )
|
||||
{
|
||||
vecTemp = pevInflictor->origin - ( VecBModelOrigin(pev) );
|
||||
}
|
||||
else
|
||||
// an actual missile was involved.
|
||||
{
|
||||
vecTemp = pevInflictor->origin - ( VecBModelOrigin(pev) );
|
||||
}
|
||||
|
||||
// this global is still used for glass and other non-monster killables, along with decals.
|
||||
g_vecAttackDir = vecTemp.Normalize();
|
||||
|
||||
// save damage based on the target's armor level
|
||||
|
||||
// figure momentum add (don't let hurt brushes or other triggers move player)
|
||||
if ((!FNullEnt(pevInflictor)) && (pev->movetype == MOVETYPE_WALK || pev->movetype == MOVETYPE_STEP) && (pevAttacker->solid != SOLID_TRIGGER) )
|
||||
{
|
||||
Vector vecDir = pev->origin - (pevInflictor->absmin + pevInflictor->absmax) * 0.5;
|
||||
vecDir = vecDir.Normalize();
|
||||
|
||||
float flForce = flDamage * ((32 * 32 * 72.0) / (pev->size.x * pev->size.y * pev->size.z)) * 5;
|
||||
|
||||
if (flForce > 1000.0)
|
||||
flForce = 1000.0;
|
||||
pev->velocity = pev->velocity + vecDir * flForce;
|
||||
}
|
||||
|
||||
// do the damage
|
||||
pev->health -= flDamage;
|
||||
if (pev->health <= 0)
|
||||
{
|
||||
Killed( pevAttacker, GIB_NORMAL );
|
||||
return 0;
|
||||
}
|
||||
|
||||
return 1;
|
||||
}
|
||||
|
||||
|
||||
void CBaseEntity :: Killed( entvars_t *pevAttacker, int iGib )
|
||||
{
|
||||
pev->takedamage = DAMAGE_NO;
|
||||
pev->deadflag = DEAD_DEAD;
|
||||
UTIL_Remove( this );
|
||||
}
|
||||
|
||||
|
||||
CBaseEntity *CBaseEntity::GetNextTarget( void )
|
||||
{
|
||||
if ( FStringNull( pev->target ) )
|
||||
return NULL;
|
||||
edict_t *pTarget = FIND_ENTITY_BY_TARGETNAME ( NULL, STRING(pev->target) );
|
||||
if ( FNullEnt(pTarget) )
|
||||
return NULL;
|
||||
|
||||
return Instance( pTarget );
|
||||
}
|
||||
|
||||
// Global Savedata for Delay
|
||||
TYPEDESCRIPTION CBaseEntity::m_SaveData[] =
|
||||
{
|
||||
DEFINE_FIELD( CBaseEntity, m_pGoalEnt, FIELD_CLASSPTR ),
|
||||
DEFINE_FIELD( CBaseEntity, m_iClassType, FIELD_INTEGER ),
|
||||
DEFINE_FIELD( CBaseEntity, m_pfnThink, FIELD_FUNCTION ), // UNDONE: Build table of these!!!
|
||||
DEFINE_FIELD( CBaseEntity, m_pfnTouch, FIELD_FUNCTION ),
|
||||
DEFINE_FIELD( CBaseEntity, m_pfnUse, FIELD_FUNCTION ),
|
||||
DEFINE_FIELD( CBaseEntity, m_pfnBlocked, FIELD_FUNCTION ),
|
||||
};
|
||||
|
||||
|
||||
int CBaseEntity::Save( CSave &save )
|
||||
{
|
||||
if ( save.WriteEntVars( "ENTVARS", pev ) )
|
||||
return save.WriteFields( "BASE", this, m_SaveData, ARRAYSIZE(m_SaveData) );
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
int CBaseEntity::Restore( CRestore &restore )
|
||||
{
|
||||
int status;
|
||||
|
||||
status = restore.ReadEntVars( "ENTVARS", pev );
|
||||
if ( status )
|
||||
status = restore.ReadFields( "BASE", this, m_SaveData, ARRAYSIZE(m_SaveData) );
|
||||
|
||||
// restore edict class here
|
||||
SetObjectClass( m_iClassType );
|
||||
|
||||
if ( pev->modelindex != 0 && !FStringNull(pev->model) )
|
||||
{
|
||||
Vector mins, maxs;
|
||||
mins = pev->mins; // Set model is about to destroy these
|
||||
maxs = pev->maxs;
|
||||
|
||||
|
||||
PRECACHE_MODEL( (char *)STRING(pev->model) );
|
||||
SET_MODEL(ENT(pev), STRING(pev->model));
|
||||
UTIL_SetSize(pev, mins, maxs); // Reset them
|
||||
}
|
||||
|
||||
return status;
|
||||
}
|
||||
|
||||
|
||||
// Initialize absmin & absmax to the appropriate box
|
||||
void SetObjectCollisionBox( entvars_t *pev )
|
||||
{
|
||||
if ( (pev->solid == SOLID_BSP) &&
|
||||
(pev->angles.x || pev->angles.y|| pev->angles.z) )
|
||||
{ // expand for rotation
|
||||
float max, v;
|
||||
int i;
|
||||
|
||||
max = 0;
|
||||
for (i=0 ; i<3 ; i++)
|
||||
{
|
||||
v = fabs( pev->mins[i]);
|
||||
if (v > max)
|
||||
max = v;
|
||||
v = fabs( pev->maxs[i]);
|
||||
if (v > max)
|
||||
max = v;
|
||||
}
|
||||
for (i=0 ; i<3 ; i++)
|
||||
{
|
||||
pev->absmin[i] = pev->origin[i] - max;
|
||||
pev->absmax[i] = pev->origin[i] + max;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
pev->absmin = pev->origin + pev->mins;
|
||||
pev->absmax = pev->origin + pev->maxs;
|
||||
}
|
||||
|
||||
pev->absmin.x -= 1;
|
||||
pev->absmin.y -= 1;
|
||||
pev->absmin.z -= 1;
|
||||
pev->absmax.x += 1;
|
||||
pev->absmax.y += 1;
|
||||
pev->absmax.z += 1;
|
||||
}
|
||||
|
||||
|
||||
void CBaseEntity::SetObjectCollisionBox( void )
|
||||
{
|
||||
::SetObjectCollisionBox( pev );
|
||||
}
|
||||
|
||||
|
||||
int CBaseEntity :: Intersects( CBaseEntity *pOther )
|
||||
{
|
||||
if ( pOther->pev->absmin.x > pev->absmax.x ||
|
||||
pOther->pev->absmin.y > pev->absmax.y ||
|
||||
pOther->pev->absmin.z > pev->absmax.z ||
|
||||
pOther->pev->absmax.x < pev->absmin.x ||
|
||||
pOther->pev->absmax.y < pev->absmin.y ||
|
||||
pOther->pev->absmax.z < pev->absmin.z )
|
||||
return 0;
|
||||
return 1;
|
||||
}
|
||||
|
||||
void CBaseEntity :: MakeDormant( void )
|
||||
{
|
||||
SetBits( pev->flags, FL_DORMANT );
|
||||
|
||||
// Don't touch
|
||||
pev->solid = SOLID_NOT;
|
||||
// Don't move
|
||||
pev->movetype = MOVETYPE_NONE;
|
||||
// Don't draw
|
||||
SetBits( pev->effects, EF_NODRAW );
|
||||
// Don't think
|
||||
pev->nextthink = 0;
|
||||
// Relink
|
||||
UTIL_SetOrigin( pev, pev->origin );
|
||||
}
|
||||
|
||||
int CBaseEntity :: IsDormant( void )
|
||||
{
|
||||
return FBitSet( pev->flags, FL_DORMANT );
|
||||
}
|
||||
|
||||
BOOL CBaseEntity :: IsInWorld( void )
|
||||
{
|
||||
// position
|
||||
if (pev->origin.x >= 4096) return FALSE;
|
||||
if (pev->origin.y >= 4096) return FALSE;
|
||||
if (pev->origin.z >= 4096) return FALSE;
|
||||
if (pev->origin.x <= -4096) return FALSE;
|
||||
if (pev->origin.y <= -4096) return FALSE;
|
||||
if (pev->origin.z <= -4096) return FALSE;
|
||||
// speed
|
||||
if (pev->velocity.x >= 2000) return FALSE;
|
||||
if (pev->velocity.y >= 2000) return FALSE;
|
||||
if (pev->velocity.z >= 2000) return FALSE;
|
||||
if (pev->velocity.x <= -2000) return FALSE;
|
||||
if (pev->velocity.y <= -2000) return FALSE;
|
||||
if (pev->velocity.z <= -2000) return FALSE;
|
||||
|
||||
return TRUE;
|
||||
}
|
||||
|
||||
int CBaseEntity::ShouldToggle( USE_TYPE useType, BOOL currentState )
|
||||
{
|
||||
if ( useType != USE_TOGGLE && useType != USE_SET )
|
||||
{
|
||||
if ( (currentState && useType == USE_ON) || (!currentState && useType == USE_OFF) )
|
||||
return 0;
|
||||
}
|
||||
return 1;
|
||||
}
|
||||
|
||||
|
||||
int CBaseEntity :: DamageDecal( int bitsDamageType )
|
||||
{
|
||||
if ( pev->rendermode == kRenderTransAlpha )
|
||||
return -1;
|
||||
|
||||
if ( pev->rendermode != kRenderNormal )
|
||||
return DECAL_BPROOF1;
|
||||
|
||||
return DECAL_GUNSHOT1 + RANDOM_LONG(0,4);
|
||||
}
|
||||
|
||||
|
||||
|
||||
// NOTE: szName must be a pointer to constant memory, e.g. "monster_class" because the entity
|
||||
// will keep a pointer to it after this call.
|
||||
CBaseEntity * CBaseEntity::Create( char *szName, const Vector &vecOrigin, const Vector &vecAngles, edict_t *pentOwner )
|
||||
{
|
||||
edict_t *pent;
|
||||
CBaseEntity *pEntity;
|
||||
|
||||
pent = CREATE_NAMED_ENTITY( MAKE_STRING( szName ));
|
||||
if ( FNullEnt( pent ) )
|
||||
{
|
||||
ALERT ( at_console, "NULL Ent in Create!\n" );
|
||||
return NULL;
|
||||
}
|
||||
pEntity = Instance( pent );
|
||||
pEntity->pev->owner = pentOwner;
|
||||
pEntity->pev->origin = vecOrigin;
|
||||
pEntity->pev->angles = vecAngles;
|
||||
DispatchSpawn( pEntity->edict() );
|
||||
return pEntity;
|
||||
}
|
||||
|
||||
|
|
@ -0,0 +1,805 @@
|
|||
/***
|
||||
*
|
||||
* 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.
|
||||
*
|
||||
****/
|
||||
/*
|
||||
|
||||
Class Hierachy
|
||||
|
||||
CBaseEntity
|
||||
CBaseDelay
|
||||
CBaseToggle
|
||||
CBaseItem
|
||||
CBaseMonster
|
||||
CBaseCycler
|
||||
CBasePlayer
|
||||
CBaseGroup
|
||||
*/
|
||||
|
||||
#include "entity_state.h"
|
||||
|
||||
#define MAX_PATH_SIZE 10 // max number of nodes available for a path.
|
||||
|
||||
// These are caps bits to indicate what an object's capabilities (currently used for save/restore and level transitions)
|
||||
#define FCAP_CUSTOMSAVE 0x00000001
|
||||
#define FCAP_ACROSS_TRANSITION 0x00000002 // should transfer between transitions
|
||||
#define FCAP_MUST_SPAWN 0x00000004 // Spawn after restore
|
||||
#define FCAP_DONT_SAVE 0x80000000 // Don't save this
|
||||
#define FCAP_IMPULSE_USE 0x00000008 // can be used by the player
|
||||
#define FCAP_CONTINUOUS_USE 0x00000010 // can be used by the player
|
||||
#define FCAP_ONOFF_USE 0x00000020 // can be used by the player
|
||||
#define FCAP_DIRECTIONAL_USE 0x00000040 // Player sends +/- 1 when using (currently only tracktrains)
|
||||
#define FCAP_MASTER 0x00000080 // Can be used to "master" other entities (like multisource)
|
||||
|
||||
// UNDONE: This will ignore transition volumes (trigger_transition), but not the PVS!!!
|
||||
#define FCAP_FORCE_TRANSITION 0x00000080 // ALWAYS goes across transitions
|
||||
|
||||
#include "saverestore.h"
|
||||
#include "schedule.h"
|
||||
#include "studio_ref.h"
|
||||
|
||||
#ifndef MONSTEREVENT_H
|
||||
#include "monsterevent.h"
|
||||
#endif
|
||||
|
||||
// C functions for external declarations that call the appropriate C++ methods
|
||||
|
||||
#ifdef _WIN32
|
||||
#define EXPORT _declspec( dllexport )
|
||||
#else
|
||||
#define EXPORT
|
||||
#endif
|
||||
|
||||
extern int DispatchSpawn( edict_t *pent );
|
||||
extern int DispatchCreate( edict_t *pent, const char *szName );
|
||||
extern void DispatchKeyValue( edict_t *pentKeyvalue, KeyValueData *pkvd );
|
||||
extern void DispatchTouch( edict_t *pentTouched, edict_t *pentOther );
|
||||
extern void DispatchUse( edict_t *pentUsed, edict_t *pentOther );
|
||||
extern void DispatchThink( edict_t *pent );
|
||||
extern int DispatchFrame( edict_t *pent );
|
||||
extern void DispatchBlocked( edict_t *pentBlocked, edict_t *pentOther );
|
||||
extern void DispatchSave( edict_t *pent, SAVERESTOREDATA *pSaveData );
|
||||
extern int DispatchRestore( edict_t *pent, SAVERESTOREDATA *pSaveData, int globalEntity );
|
||||
extern void DispatchObjectCollsionBox( edict_t *pent );
|
||||
extern void SaveWriteFields( SAVERESTOREDATA *pSaveData, const char *pname, void *pBaseData, TYPEDESCRIPTION *pFields, int fieldCount );
|
||||
extern void SaveReadFields( SAVERESTOREDATA *pSaveData, const char *pname, void *pBaseData, TYPEDESCRIPTION *pFields, int fieldCount );
|
||||
extern void SaveGlobalState( SAVERESTOREDATA *pSaveData );
|
||||
extern void RestoreGlobalState( SAVERESTOREDATA *pSaveData );
|
||||
extern void ResetGlobalState( void );
|
||||
extern TYPEDESCRIPTION *GetEntvarsDescirption( int number );
|
||||
extern int ServerClassifyEdict( edict_t *pentToClassify );
|
||||
extern void UpdateEntityState( struct entity_state_s *to, edict_t *from, int baseline );
|
||||
extern void OnFreeEntPrivateData( edict_s *pEdict );
|
||||
extern int ShouldCollide( edict_t *pentTouched, edict_t *pentOther );
|
||||
|
||||
typedef enum { USE_OFF = 0, USE_ON = 1, USE_SET = 2, USE_TOGGLE = 3 } USE_TYPE;
|
||||
|
||||
extern void FireTargets( const char *targetName, CBaseEntity *pActivator, CBaseEntity *pCaller, USE_TYPE useType, float value );
|
||||
|
||||
typedef void (CBaseEntity::*BASEPTR)(void);
|
||||
typedef void (CBaseEntity::*ENTITYFUNCPTR)(CBaseEntity *pOther );
|
||||
typedef void (CBaseEntity::*USEPTR)( CBaseEntity *pActivator, CBaseEntity *pCaller, USE_TYPE useType, float value );
|
||||
|
||||
// For CLASSIFY
|
||||
#define CLASS_NONE 0
|
||||
#define CLASS_MACHINE 1
|
||||
#define CLASS_PLAYER 2
|
||||
#define CLASS_HUMAN_PASSIVE 3
|
||||
#define CLASS_HUMAN_MILITARY 4
|
||||
#define CLASS_ALIEN_MILITARY 5
|
||||
#define CLASS_ALIEN_PASSIVE 6
|
||||
#define CLASS_ALIEN_MONSTER 7
|
||||
#define CLASS_ALIEN_PREY 8
|
||||
#define CLASS_ALIEN_PREDATOR 9
|
||||
#define CLASS_INSECT 10
|
||||
#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_BARNACLE 99 // special because no one pays attention to it, and it eats a wide cross-section of creatures.
|
||||
|
||||
class CBaseEntity;
|
||||
class CBaseMonster;
|
||||
class CBasePlayerItem;
|
||||
class CSquadMonster;
|
||||
|
||||
|
||||
#define SF_NORESPAWN ( 1 << 30 )// !!!set this bit on guns and stuff that should never respawn.
|
||||
|
||||
//
|
||||
// EHANDLE. Safe way to point to CBaseEntities who may die between frames
|
||||
//
|
||||
class EHANDLE
|
||||
{
|
||||
private:
|
||||
edict_t *m_pent;
|
||||
int m_serialnumber;
|
||||
public:
|
||||
edict_t *Get( void );
|
||||
edict_t *Set( edict_t *pent );
|
||||
|
||||
operator int ();
|
||||
|
||||
operator CBaseEntity *();
|
||||
|
||||
CBaseEntity * operator = (CBaseEntity *pEntity);
|
||||
CBaseEntity * operator ->();
|
||||
};
|
||||
|
||||
|
||||
//
|
||||
// Base Entity. All entity types derive from this
|
||||
//
|
||||
class CBaseEntity
|
||||
{
|
||||
public:
|
||||
// Constructor. Set engine to use C/C++ callback functions
|
||||
// pointers to engine data
|
||||
entvars_t *pev; // Don't need to save/restore this pointer, the engine resets it
|
||||
|
||||
// path corners
|
||||
CBaseEntity *m_pGoalEnt;// path corner we are heading towards
|
||||
CBaseEntity *m_pLink;// used for temporary link-list operations.
|
||||
|
||||
int m_iClassType; // edict classtype
|
||||
|
||||
virtual void SetObjectClass( int iClassType = ED_SPAWNED )
|
||||
{
|
||||
m_iClassType = iClassType;
|
||||
}
|
||||
|
||||
// initialization functions
|
||||
virtual void Spawn( void ) { return; }
|
||||
virtual void Precache( void ) { return; }
|
||||
virtual void KeyValue( KeyValueData* pkvd) { pkvd->fHandled = FALSE; }
|
||||
virtual int Save( CSave &save );
|
||||
virtual int Restore( CRestore &restore );
|
||||
virtual int ObjectCaps( void ) { return FCAP_ACROSS_TRANSITION; }
|
||||
virtual void Activate( void ) {}
|
||||
|
||||
// Setup the object->object collision box (pev->mins / pev->maxs is the object->world collision box)
|
||||
virtual void SetObjectCollisionBox( void );
|
||||
|
||||
// Classify - returns the type of group (i.e, "houndeye", or "human military" so that monsters with different classnames
|
||||
// still realize that they are teammates. (overridden for monsters that form groups)
|
||||
virtual int Classify ( void ) { return CLASS_NONE; };
|
||||
virtual void DeathNotice ( entvars_t *pevChild ) {}// monster maker children use this to tell the monster maker that they have died.
|
||||
|
||||
|
||||
static TYPEDESCRIPTION m_SaveData[];
|
||||
|
||||
virtual void TraceAttack( entvars_t *pevAttacker, float flDamage, Vector vecDir, TraceResult *ptr, int bitsDamageType);
|
||||
virtual int TakeDamage( entvars_t* pevInflictor, entvars_t* pevAttacker, float flDamage, int bitsDamageType );
|
||||
virtual int TakeHealth( float flHealth, int bitsDamageType );
|
||||
virtual void Killed( entvars_t *pevAttacker, int iGib );
|
||||
virtual int BloodColor( void ) { return DONT_BLEED; }
|
||||
virtual void TraceBleed( float flDamage, Vector vecDir, TraceResult *ptr, int bitsDamageType );
|
||||
virtual BOOL IsTriggered( CBaseEntity *pActivator ) {return TRUE;}
|
||||
virtual CBaseMonster *MyMonsterPointer( void ) { return NULL;}
|
||||
virtual CSquadMonster *MySquadMonsterPointer( void ) { return NULL;}
|
||||
virtual int GetToggleState( void ) { return TS_AT_TOP; }
|
||||
virtual void AddPoints( int score, BOOL bAllowNegativeScore ) {}
|
||||
virtual void AddPointsToTeam( int score, BOOL bAllowNegativeScore ) {}
|
||||
virtual BOOL AddPlayerItem( CBasePlayerItem *pItem ) { return 0; }
|
||||
virtual BOOL RemovePlayerItem( CBasePlayerItem *pItem ) { return 0; }
|
||||
virtual int GiveAmmo( int iAmount, char *szName, int iMax ) { return -1; };
|
||||
virtual float GetDelay( void ) { return 0; }
|
||||
virtual int IsMoving( void ) { return pev->velocity != g_vecZero; }
|
||||
virtual void OverrideReset( void ) {}
|
||||
virtual int DamageDecal( int bitsDamageType );
|
||||
// This is ONLY used by the node graph to test movement through a door
|
||||
virtual void SetToggleState( int state ) {}
|
||||
virtual void StartSneaking( void ) {}
|
||||
virtual void StopSneaking( void ) {}
|
||||
virtual BOOL OnControls( entvars_t *pev ) { return FALSE; }
|
||||
virtual BOOL IsSneaking( void ) { return FALSE; }
|
||||
virtual BOOL IsAlive( void ) { return (pev->deadflag == DEAD_NO) && pev->health > 0; }
|
||||
virtual BOOL IsBSPModel( void ) { return pev->solid == SOLID_BSP || pev->movetype == MOVETYPE_PUSHSTEP; }
|
||||
virtual BOOL ReflectGauss( void ) { return ( IsBSPModel() && !pev->takedamage ); }
|
||||
virtual BOOL HasTarget( string_t targetname ) { return FStrEq(STRING(targetname), STRING(pev->targetname) ); }
|
||||
virtual BOOL IsInWorld( void );
|
||||
virtual BOOL IsPlayer( void ) { return FALSE; }
|
||||
virtual BOOL IsNetClient( void ) { return FALSE; }
|
||||
virtual const char *TeamID( void ) { return ""; }
|
||||
|
||||
|
||||
// virtual void SetActivator( CBaseEntity *pActivator ) {}
|
||||
virtual CBaseEntity *GetNextTarget( void );
|
||||
|
||||
// fundamental callbacks
|
||||
void (CBaseEntity ::*m_pfnThink)(void);
|
||||
void (CBaseEntity ::*m_pfnTouch)( CBaseEntity *pOther );
|
||||
void (CBaseEntity ::*m_pfnUse)( CBaseEntity *pActivator, CBaseEntity *pCaller, USE_TYPE useType, float value );
|
||||
void (CBaseEntity ::*m_pfnBlocked)( CBaseEntity *pOther );
|
||||
|
||||
virtual void Think( void ) { if (m_pfnThink) (this->*m_pfnThink)(); };
|
||||
virtual void Touch( CBaseEntity *pOther ) { if (m_pfnTouch) (this->*m_pfnTouch)( pOther ); };
|
||||
virtual void Use( CBaseEntity *pActivator, CBaseEntity *pCaller, USE_TYPE useType, float value )
|
||||
{
|
||||
if (m_pfnUse)
|
||||
(this->*m_pfnUse)( pActivator, pCaller, useType, value );
|
||||
}
|
||||
virtual void Blocked( CBaseEntity *pOther ) { if (m_pfnBlocked) (this->*m_pfnBlocked)( pOther ); };
|
||||
|
||||
// allow engine to allocate instance data
|
||||
void *operator new( size_t stAllocateBlock, entvars_t *pev )
|
||||
{
|
||||
return (void *)ALLOC_PRIVATE(ENT(pev), stAllocateBlock);
|
||||
};
|
||||
|
||||
// don't use this.
|
||||
#if _MSC_VER >= 1200 // only build this code if MSVC++ 6.0 or higher
|
||||
void operator delete(void *pMem, entvars_t *pev)
|
||||
{
|
||||
pev->flags |= FL_KILLME;
|
||||
};
|
||||
#endif
|
||||
|
||||
void UpdateOnRemove( void );
|
||||
|
||||
// common member functions
|
||||
void EXPORT SUB_Remove( void );
|
||||
void EXPORT SUB_DoNothing( void );
|
||||
void EXPORT SUB_StartFadeOut ( void );
|
||||
void EXPORT SUB_FadeOut ( void );
|
||||
void EXPORT SUB_CallUseToggle( void ) { this->Use( this, this, USE_TOGGLE, 0 ); }
|
||||
int ShouldToggle( USE_TYPE useType, BOOL currentState );
|
||||
void FireBullets( ULONG cShots, Vector vecSrc, Vector vecDirShooting, Vector vecSpread, float flDistance, int iBulletType, int iTracerFreq = 4, int iDamage = 0, entvars_t *pevAttacker = NULL );
|
||||
|
||||
virtual CBaseEntity *Respawn( void ) { return NULL; }
|
||||
|
||||
void SUB_UseTargets( CBaseEntity *pActivator, USE_TYPE useType, float value );
|
||||
// Do the bounding boxes of these two intersect?
|
||||
int Intersects( CBaseEntity *pOther );
|
||||
void MakeDormant( void );
|
||||
int IsDormant( void );
|
||||
BOOL IsLockedByMaster( void ) { return FALSE; }
|
||||
|
||||
#ifdef _DEBUG
|
||||
static CBaseEntity *Instance( edict_t *pent )
|
||||
{
|
||||
if ( !pent )
|
||||
pent = ENT(0);
|
||||
CBaseEntity *pEnt = (CBaseEntity *)GET_PRIVATE(pent);
|
||||
ASSERT(pEnt!=NULL);
|
||||
return pEnt;
|
||||
}
|
||||
#else
|
||||
static CBaseEntity *Instance( edict_t *pent )
|
||||
{
|
||||
if ( !pent )
|
||||
pent = ENT(0);
|
||||
CBaseEntity *pEnt = (CBaseEntity *)GET_PRIVATE(pent);
|
||||
return pEnt;
|
||||
}
|
||||
#endif
|
||||
|
||||
static CBaseEntity *Instance( entvars_t *pev ) { return Instance( ENT( pev ) ); }
|
||||
static CBaseEntity *Instance( int eoffset) { return Instance( ENT( eoffset) ); }
|
||||
|
||||
CBaseMonster *GetMonsterPointer( entvars_t *pevMonster )
|
||||
{
|
||||
CBaseEntity *pEntity = Instance( pevMonster );
|
||||
if ( pEntity )
|
||||
return pEntity->MyMonsterPointer();
|
||||
return NULL;
|
||||
}
|
||||
CBaseMonster *GetMonsterPointer( edict_t *pentMonster )
|
||||
{
|
||||
CBaseEntity *pEntity = Instance( pentMonster );
|
||||
if ( pEntity )
|
||||
return pEntity->MyMonsterPointer();
|
||||
return NULL;
|
||||
}
|
||||
|
||||
|
||||
// Ugly code to lookup all functions to make sure they are exported when set.
|
||||
#ifdef _DEBUG
|
||||
void FunctionCheck( void *pFunction, char *name )
|
||||
{
|
||||
#ifdef _WIN32
|
||||
if (pFunction && !NAME_FOR_FUNCTION((unsigned long)(pFunction)) )
|
||||
ALERT( at_error, "No EXPORT: %s:%s (%08lx)\n", STRING(pev->classname), name, (unsigned long)pFunction );
|
||||
#endif // _WIN32
|
||||
}
|
||||
|
||||
BASEPTR ThinkSet( BASEPTR func, char *name )
|
||||
{
|
||||
m_pfnThink = func;
|
||||
FunctionCheck( (void *)*((int *)((char *)this + ( offsetof(CBaseEntity,m_pfnThink)))), name );
|
||||
return func;
|
||||
}
|
||||
ENTITYFUNCPTR TouchSet( ENTITYFUNCPTR func, char *name )
|
||||
{
|
||||
m_pfnTouch = func;
|
||||
FunctionCheck( (void *)*((int *)((char *)this + ( offsetof(CBaseEntity,m_pfnTouch)))), name );
|
||||
return func;
|
||||
}
|
||||
USEPTR UseSet( USEPTR func, char *name )
|
||||
{
|
||||
m_pfnUse = func;
|
||||
FunctionCheck( (void *)*((int *)((char *)this + ( offsetof(CBaseEntity,m_pfnUse)))), name );
|
||||
return func;
|
||||
}
|
||||
ENTITYFUNCPTR BlockedSet( ENTITYFUNCPTR func, char *name )
|
||||
{
|
||||
m_pfnBlocked = func;
|
||||
FunctionCheck( (void *)*((int *)((char *)this + ( offsetof(CBaseEntity,m_pfnBlocked)))), name );
|
||||
return func;
|
||||
}
|
||||
|
||||
#endif
|
||||
|
||||
|
||||
// virtual functions used by a few classes
|
||||
|
||||
// used by monsters that are created by the MonsterMaker
|
||||
virtual void UpdateOwner( void ) { return; };
|
||||
|
||||
|
||||
//
|
||||
static CBaseEntity *Create( char *szName, const Vector &vecOrigin, const Vector &vecAngles, edict_t *pentOwner = NULL );
|
||||
|
||||
virtual BOOL FBecomeProne( void ) {return FALSE;};
|
||||
edict_t *edict() { return ENT( pev ); };
|
||||
EOFFSET eoffset( ) { return OFFSET( pev ); };
|
||||
int entindex( ) { return ENTINDEX( edict() ); };
|
||||
|
||||
virtual Vector Center( ) { return (pev->absmax + pev->absmin) * 0.5; }; // center point of entity
|
||||
virtual Vector EyePosition( ) { return pev->origin + pev->view_ofs; }; // position of eyes
|
||||
virtual Vector EarPosition( ) { return pev->origin + pev->view_ofs; }; // position of ears
|
||||
virtual Vector BodyTarget( const Vector &posSrc ) { return Center( ); }; // position to shoot at
|
||||
|
||||
virtual int Illumination( ) { return GETENTITYILLUM( ENT( pev ) ); };
|
||||
|
||||
virtual BOOL FVisible ( CBaseEntity *pEntity );
|
||||
virtual BOOL FVisible ( const Vector &vecOrigin );
|
||||
};
|
||||
|
||||
|
||||
|
||||
// Ugly technique to override base member functions
|
||||
// Normally it's illegal to cast a pointer to a member function of a derived class to a pointer to a
|
||||
// member function of a base class. static_cast is a sleezy way around that problem.
|
||||
|
||||
#ifdef _DEBUG
|
||||
|
||||
#define SetThink( a ) ThinkSet( static_cast <void (CBaseEntity::*)(void)> (a), #a )
|
||||
#define SetTouch( a ) TouchSet( static_cast <void (CBaseEntity::*)(CBaseEntity *)> (a), #a )
|
||||
#define SetUse( a ) UseSet( static_cast <void (CBaseEntity::*)( CBaseEntity *pActivator, CBaseEntity *pCaller, USE_TYPE useType, float value )> (a), #a )
|
||||
#define SetBlocked( a ) BlockedSet( static_cast <void (CBaseEntity::*)(CBaseEntity *)> (a), #a )
|
||||
|
||||
#else
|
||||
|
||||
#define SetThink( a ) m_pfnThink = static_cast <void (CBaseEntity::*)(void)> (a)
|
||||
#define SetTouch( a ) m_pfnTouch = static_cast <void (CBaseEntity::*)(CBaseEntity *)> (a)
|
||||
#define SetUse( a ) m_pfnUse = static_cast <void (CBaseEntity::*)( CBaseEntity *pActivator, CBaseEntity *pCaller, USE_TYPE useType, float value )> (a)
|
||||
#define SetBlocked( a ) m_pfnBlocked = static_cast <void (CBaseEntity::*)(CBaseEntity *)> (a)
|
||||
|
||||
#endif
|
||||
|
||||
|
||||
class CPointEntity : public CBaseEntity
|
||||
{
|
||||
public:
|
||||
void Spawn( void );
|
||||
virtual int ObjectCaps( void ) { return CBaseEntity :: ObjectCaps() & ~FCAP_ACROSS_TRANSITION; }
|
||||
private:
|
||||
};
|
||||
|
||||
|
||||
typedef struct locksounds // sounds that doors and buttons make when locked/unlocked
|
||||
{
|
||||
string_t sLockedSound; // sound a door makes when it's locked
|
||||
string_t sLockedSentence; // sentence group played when door is locked
|
||||
string_t sUnlockedSound; // sound a door makes when it's unlocked
|
||||
string_t sUnlockedSentence; // sentence group played when door is unlocked
|
||||
|
||||
int iLockedSentence; // which sentence in sentence group to play next
|
||||
int iUnlockedSentence; // which sentence in sentence group to play next
|
||||
|
||||
float flwaitSound; // time delay between playing consecutive 'locked/unlocked' sounds
|
||||
float flwaitSentence; // time delay between playing consecutive sentences
|
||||
BYTE bEOFLocked; // true if hit end of list of locked sentences
|
||||
BYTE bEOFUnlocked; // true if hit end of list of unlocked sentences
|
||||
} locksound_t;
|
||||
|
||||
void PlayLockSounds(entvars_t *pev, locksound_t *pls, int flocked, int fbutton);
|
||||
|
||||
//
|
||||
// MultiSouce
|
||||
//
|
||||
|
||||
#define MAX_MULTI_TARGETS 16 // maximum number of targets a single multi_manager entity may be assigned.
|
||||
#define MS_MAX_TARGETS 32
|
||||
|
||||
class CMultiSource : public CPointEntity
|
||||
{
|
||||
public:
|
||||
void Spawn( );
|
||||
void KeyValue( KeyValueData *pkvd );
|
||||
void Use( CBaseEntity *pActivator, CBaseEntity *pCaller, USE_TYPE useType, float value );
|
||||
int ObjectCaps( void ) { return (CPointEntity::ObjectCaps() | FCAP_MASTER); }
|
||||
BOOL IsTriggered( CBaseEntity *pActivator );
|
||||
void EXPORT Register( void );
|
||||
virtual int Save( CSave &save );
|
||||
virtual int Restore( CRestore &restore );
|
||||
|
||||
static TYPEDESCRIPTION m_SaveData[];
|
||||
|
||||
EHANDLE m_rgEntities[MS_MAX_TARGETS];
|
||||
int m_rgTriggered[MS_MAX_TARGETS];
|
||||
|
||||
int m_iTotal;
|
||||
string_t m_globalstate;
|
||||
};
|
||||
|
||||
|
||||
//
|
||||
// generic Delay entity.
|
||||
//
|
||||
class CBaseDelay : public CBaseEntity
|
||||
{
|
||||
public:
|
||||
float m_flDelay;
|
||||
int m_iszKillTarget;
|
||||
|
||||
virtual void KeyValue( KeyValueData* pkvd);
|
||||
virtual int Save( CSave &save );
|
||||
virtual int Restore( CRestore &restore );
|
||||
|
||||
static TYPEDESCRIPTION m_SaveData[];
|
||||
// common member functions
|
||||
void SUB_UseTargets( CBaseEntity *pActivator, USE_TYPE useType, float value );
|
||||
void EXPORT DelayThink( void );
|
||||
};
|
||||
|
||||
|
||||
class CBaseAnimating : public CBaseDelay
|
||||
{
|
||||
public:
|
||||
virtual int Save( CSave &save );
|
||||
virtual int Restore( CRestore &restore );
|
||||
|
||||
static TYPEDESCRIPTION m_SaveData[];
|
||||
|
||||
// Basic Monster Animation functions
|
||||
float StudioFrameAdvance( float flInterval = 0.0 ); // accumulate animation frame time from last time called until now
|
||||
int GetSequenceFlags( void );
|
||||
int LookupActivity ( int activity );
|
||||
int LookupActivityHeaviest ( int activity );
|
||||
int LookupSequence ( const char *label );
|
||||
void ResetSequenceInfo ( );
|
||||
void DispatchAnimEvents ( float flFutureInterval = 0.1 ); // Handle events that have happend since last time called up until X seconds into the future
|
||||
virtual void HandleAnimEvent( MonsterEvent_t *pEvent ) { return; };
|
||||
float SetBoneController ( int iController, float flValue );
|
||||
void InitBoneControllers ( void );
|
||||
float SetBlending ( int iBlender, float flValue );
|
||||
void GetBonePosition ( int iBone, Vector &origin, Vector &angles );
|
||||
void GetAutomovement( Vector &origin, Vector &angles, float flInterval = 0.1 );
|
||||
int FindTransition( int iEndingSequence, int iGoalSequence, int *piDir );
|
||||
void GetAttachment ( int iAttachment, Vector &origin, Vector &angles );
|
||||
void SetBodygroup( int iGroup, int iValue );
|
||||
int GetBodygroup( int iGroup );
|
||||
int ExtractBbox( int sequence, Vector &mins, Vector &maxs );
|
||||
void SetSequenceBox( void );
|
||||
|
||||
// animation needs
|
||||
float m_flFrameRate; // computed FPS for current sequence
|
||||
float m_flGroundSpeed; // computed linear movement rate for current sequence
|
||||
float m_flLastEventCheck; // last time the event list was checked
|
||||
BOOL m_fSequenceFinished;// flag set when StudioAdvanceFrame moves across a frame boundry
|
||||
BOOL m_fSequenceLoops; // true if the sequence loops
|
||||
};
|
||||
|
||||
|
||||
//
|
||||
// generic Toggle entity.
|
||||
//
|
||||
#define SF_ITEM_USE_ONLY 256 // ITEM_USE_ONLY = BUTTON_USE_ONLY = DOOR_USE_ONLY!!!
|
||||
|
||||
class CBaseToggle : public CBaseAnimating
|
||||
{
|
||||
public:
|
||||
void KeyValue( KeyValueData *pkvd );
|
||||
|
||||
TOGGLE_STATE m_toggle_state;
|
||||
float m_flActivateFinished;//like attack_finished, but for doors
|
||||
float m_flMoveDistance;// how far a door should slide or rotate
|
||||
float m_flWait;
|
||||
float m_flLip;
|
||||
float m_flTWidth;// for plats
|
||||
float m_flTLength;// for plats
|
||||
|
||||
Vector m_vecPosition1;
|
||||
Vector m_vecPosition2;
|
||||
Vector m_vecAngle1;
|
||||
Vector m_vecAngle2;
|
||||
|
||||
int m_cTriggersLeft; // trigger_counter only, # of activations remaining
|
||||
float m_flHeight;
|
||||
EHANDLE m_hActivator;
|
||||
void (CBaseToggle::*m_pfnCallWhenMoveDone)(void);
|
||||
Vector m_vecFinalDest;
|
||||
Vector m_vecFinalAngle;
|
||||
|
||||
int m_bitsDamageInflict; // DMG_ damage type that the door or tigger does
|
||||
|
||||
virtual int Save( CSave &save );
|
||||
virtual int Restore( CRestore &restore );
|
||||
|
||||
static TYPEDESCRIPTION m_SaveData[];
|
||||
|
||||
virtual int GetToggleState( void ) { return m_toggle_state; }
|
||||
virtual float GetDelay( void ) { return m_flWait; }
|
||||
|
||||
// common member functions
|
||||
void LinearMove( Vector vecDest, float flSpeed );
|
||||
void EXPORT LinearMoveDone( void );
|
||||
void AngularMove( Vector vecDestAngle, float flSpeed );
|
||||
void EXPORT AngularMoveDone( void );
|
||||
BOOL IsLockedByMaster( void );
|
||||
|
||||
static float AxisValue( int flags, const Vector &angles );
|
||||
static void AxisDir( entvars_t *pev );
|
||||
static float AxisDelta( int flags, const Vector &angle1, const Vector &angle2 );
|
||||
|
||||
string_t m_sMaster; // If this button has a master switch, this is the targetname.
|
||||
// A master switch must be of the multisource type. If all
|
||||
// of the switches in the multisource have been triggered, then
|
||||
// the button will be allowed to operate. Otherwise, it will be
|
||||
// deactivated.
|
||||
};
|
||||
#define SetMoveDone( a ) m_pfnCallWhenMoveDone = static_cast <void (CBaseToggle::*)(void)> (a)
|
||||
|
||||
|
||||
// people gib if their health is <= this at the time of death
|
||||
#define GIB_HEALTH_VALUE -30
|
||||
|
||||
#define ROUTE_SIZE 8 // how many waypoints a monster can store at one time
|
||||
#define MAX_OLD_ENEMIES 4 // how many old enemies to remember
|
||||
|
||||
#define bits_CAP_DUCK ( 1 << 0 )// crouch
|
||||
#define bits_CAP_JUMP ( 1 << 1 )// jump/leap
|
||||
#define bits_CAP_STRAFE ( 1 << 2 )// strafe ( walk/run sideways)
|
||||
#define bits_CAP_SQUAD ( 1 << 3 )// can form squads
|
||||
#define bits_CAP_SWIM ( 1 << 4 )// proficiently navigate in water
|
||||
#define bits_CAP_CLIMB ( 1 << 5 )// climb ladders/ropes
|
||||
#define bits_CAP_USE ( 1 << 6 )// open doors/push buttons/pull levers
|
||||
#define bits_CAP_HEAR ( 1 << 7 )// can hear forced sounds
|
||||
#define bits_CAP_AUTO_DOORS ( 1 << 8 )// can trigger auto doors
|
||||
#define bits_CAP_OPEN_DOORS ( 1 << 9 )// can open manual doors
|
||||
#define bits_CAP_TURN_HEAD ( 1 << 10)// can turn head, always bone controller 0
|
||||
|
||||
#define bits_CAP_RANGE_ATTACK1 ( 1 << 11)// can do a range attack 1
|
||||
#define bits_CAP_RANGE_ATTACK2 ( 1 << 12)// can do a range attack 2
|
||||
#define bits_CAP_MELEE_ATTACK1 ( 1 << 13)// can do a melee attack 1
|
||||
#define bits_CAP_MELEE_ATTACK2 ( 1 << 14)// can do a melee attack 2
|
||||
|
||||
#define bits_CAP_FLY ( 1 << 15)// can fly, move all around
|
||||
|
||||
#define bits_CAP_DOORS_GROUP (bits_CAP_USE | bits_CAP_AUTO_DOORS | bits_CAP_OPEN_DOORS)
|
||||
|
||||
// used by suit voice to indicate damage sustained and repaired type to player
|
||||
|
||||
// instant damage
|
||||
|
||||
#define DMG_GENERIC 0 // generic damage was done
|
||||
#define DMG_CRUSH (1 << 0) // crushed by falling or moving object
|
||||
#define DMG_BULLET (1 << 1) // shot
|
||||
#define DMG_SLASH (1 << 2) // cut, clawed, stabbed
|
||||
#define DMG_BURN (1 << 3) // heat burned
|
||||
#define DMG_FREEZE (1 << 4) // frozen
|
||||
#define DMG_FALL (1 << 5) // fell too far
|
||||
#define DMG_BLAST (1 << 6) // explosive blast damage
|
||||
#define DMG_CLUB (1 << 7) // crowbar, punch, headbutt
|
||||
#define DMG_SHOCK (1 << 8) // electric shock
|
||||
#define DMG_SONIC (1 << 9) // sound pulse shockwave
|
||||
#define DMG_ENERGYBEAM (1 << 10) // laser or other high energy beam
|
||||
#define DMG_NEVERGIB (1 << 12) // with this bit OR'd in, no damage type will be able to gib victims upon death
|
||||
#define DMG_ALWAYSGIB (1 << 13) // with this bit OR'd in, any damage type can be made to gib victims upon death.
|
||||
#define DMG_DROWN (1 << 14) // Drowning
|
||||
// time-based damage
|
||||
#define DMG_TIMEBASED (~(0x3fff)) // mask for time-based damage
|
||||
|
||||
#define DMG_PARALYZE (1 << 15) // slows affected creature down
|
||||
#define DMG_NERVEGAS (1 << 16) // nerve toxins, very bad
|
||||
#define DMG_POISON (1 << 17) // blood poisioning
|
||||
#define DMG_RADIATION (1 << 18) // radiation exposure
|
||||
#define DMG_DROWNRECOVER (1 << 19) // drowning recovery
|
||||
#define DMG_ACID (1 << 20) // toxic chemicals or acid burns
|
||||
#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)
|
||||
|
||||
// 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 )
|
||||
|
||||
// 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)
|
||||
|
||||
// NOTE: tweak these values based on gameplay feedback:
|
||||
|
||||
#define PARALYZE_DURATION 2 // number of 2 second intervals to take damage
|
||||
#define PARALYZE_DAMAGE 1.0 // damage to take each 2 second interval
|
||||
|
||||
#define NERVEGAS_DURATION 2
|
||||
#define NERVEGAS_DAMAGE 5.0
|
||||
|
||||
#define POISON_DURATION 5
|
||||
#define POISON_DAMAGE 2.0
|
||||
|
||||
#define RADIATION_DURATION 2
|
||||
#define RADIATION_DAMAGE 1.0
|
||||
|
||||
#define ACID_DURATION 2
|
||||
#define ACID_DAMAGE 5.0
|
||||
|
||||
#define SLOWBURN_DURATION 2
|
||||
#define SLOWBURN_DAMAGE 1.0
|
||||
|
||||
#define SLOWFREEZE_DURATION 2
|
||||
#define SLOWFREEZE_DAMAGE 1.0
|
||||
|
||||
|
||||
#define itbd_Paralyze 0
|
||||
#define itbd_NerveGas 1
|
||||
#define itbd_Poison 2
|
||||
#define itbd_Radiation 3
|
||||
#define itbd_DrownRecover 4
|
||||
#define itbd_Acid 5
|
||||
#define itbd_SlowBurn 6
|
||||
#define itbd_SlowFreeze 7
|
||||
#define CDMG_TIMEBASED 8
|
||||
|
||||
// when calling KILLED(), a value that governs gib behavior is expected to be
|
||||
// one of these three values
|
||||
#define GIB_NORMAL 0// gib if entity was overkilled
|
||||
#define GIB_NEVER 1// never gib, no matter how much death damage is done ( freezing, etc )
|
||||
#define GIB_ALWAYS 2// always gib ( Houndeye Shock, Barnacle Bite )
|
||||
|
||||
class CBaseMonster;
|
||||
class CCineMonster;
|
||||
class CSound;
|
||||
|
||||
#include "basemonster.h"
|
||||
|
||||
|
||||
char *ButtonSound( int sound ); // get string of button sound number
|
||||
|
||||
|
||||
//
|
||||
// Generic Button
|
||||
//
|
||||
class CBaseButton : public CBaseToggle
|
||||
{
|
||||
public:
|
||||
void Spawn( void );
|
||||
virtual void Precache( void );
|
||||
void RotSpawn( void );
|
||||
virtual void KeyValue( KeyValueData* pkvd);
|
||||
|
||||
void ButtonActivate( );
|
||||
void SparkSoundCache( void );
|
||||
|
||||
void EXPORT ButtonShot( void );
|
||||
void EXPORT ButtonTouch( CBaseEntity *pOther );
|
||||
void EXPORT ButtonSpark ( void );
|
||||
void EXPORT TriggerAndWait( void );
|
||||
void EXPORT ButtonReturn( void );
|
||||
void EXPORT ButtonBackHome( void );
|
||||
void EXPORT ButtonUse ( CBaseEntity *pActivator, CBaseEntity *pCaller, USE_TYPE useType, float value );
|
||||
virtual int TakeDamage( entvars_t* pevInflictor, entvars_t* pevAttacker, float flDamage, int bitsDamageType );
|
||||
virtual int Save( CSave &save );
|
||||
virtual int Restore( CRestore &restore );
|
||||
|
||||
enum BUTTON_CODE { BUTTON_NOTHING, BUTTON_ACTIVATE, BUTTON_RETURN };
|
||||
BUTTON_CODE ButtonResponseToTouch( void );
|
||||
|
||||
static TYPEDESCRIPTION m_SaveData[];
|
||||
// Buttons that don't take damage can be IMPULSE used
|
||||
virtual int ObjectCaps( void ) { return (CBaseToggle:: ObjectCaps() & ~FCAP_ACROSS_TRANSITION) | (pev->takedamage?0:FCAP_IMPULSE_USE); }
|
||||
|
||||
BOOL m_fStayPushed; // button stays pushed in until touched again?
|
||||
BOOL m_fRotating; // a rotating button? default is a sliding button.
|
||||
|
||||
string_t m_strChangeTarget; // if this field is not null, this is an index into the engine string array.
|
||||
// when this button is touched, it's target entity's TARGET field will be set
|
||||
// to the button's ChangeTarget. This allows you to make a func_train switch paths, etc.
|
||||
|
||||
locksound_t m_ls; // door lock sounds
|
||||
|
||||
BYTE m_bLockedSound; // ordinals from entity selection
|
||||
BYTE m_bLockedSentence;
|
||||
BYTE m_bUnlockedSound;
|
||||
BYTE m_bUnlockedSentence;
|
||||
int m_sounds;
|
||||
};
|
||||
|
||||
//
|
||||
// Weapons
|
||||
//
|
||||
|
||||
#define BAD_WEAPON 0x00007FFF
|
||||
|
||||
//
|
||||
// Converts a entvars_t * to a class pointer
|
||||
// It will allocate the class and entity if necessary
|
||||
//
|
||||
template <class T> T * GetClassPtr( T *a )
|
||||
{
|
||||
entvars_t *pev = (entvars_t *)a;
|
||||
|
||||
// allocate entity if necessary
|
||||
if (pev == NULL)
|
||||
pev = VARS(CREATE_ENTITY());
|
||||
|
||||
// get the private data
|
||||
a = (T *)GET_PRIVATE(ENT(pev));
|
||||
|
||||
if (a == NULL)
|
||||
{
|
||||
// allocate private data
|
||||
a = new(pev) T;
|
||||
a->pev = pev;
|
||||
}
|
||||
return a;
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
bit_PUSHBRUSH_DATA | bit_TOGGLE_DATA
|
||||
bit_MONSTER_DATA
|
||||
bit_DELAY_DATA
|
||||
bit_TOGGLE_DATA | bit_DELAY_DATA | bit_MONSTER_DATA
|
||||
bit_PLAYER_DATA | bit_MONSTER_DATA
|
||||
bit_MONSTER_DATA | CYCLER_DATA
|
||||
bit_LIGHT_DATA
|
||||
path_corner_data
|
||||
bit_MONSTER_DATA | wildcard_data
|
||||
bit_MONSTER_DATA | bit_GROUP_DATA
|
||||
boid_flock_data
|
||||
boid_data
|
||||
CYCLER_DATA
|
||||
bit_ITEM_DATA
|
||||
bit_ITEM_DATA | func_hud_data
|
||||
bit_TOGGLE_DATA | bit_ITEM_DATA
|
||||
EOFFSET
|
||||
env_sound_data
|
||||
env_sound_data
|
||||
push_trigger_data
|
||||
*/
|
||||
|
||||
#define TRACER_FREQ 4 // Tracers fire every 4 bullets
|
||||
|
||||
typedef struct _SelAmmo
|
||||
{
|
||||
BYTE Ammo1Type;
|
||||
BYTE Ammo1;
|
||||
BYTE Ammo2Type;
|
||||
BYTE Ammo2;
|
||||
} SelAmmo;
|
||||
|
||||
|
||||
// this moved here from world.cpp, to allow classes to be derived from it
|
||||
//=======================
|
||||
// CWorld
|
||||
//
|
||||
// This spawns first when each level begins.
|
||||
//=======================
|
||||
class CWorld : public CBaseEntity
|
||||
{
|
||||
public:
|
||||
void Spawn( void );
|
||||
void Precache( void );
|
||||
void KeyValue( KeyValueData *pkvd );
|
||||
};
|
|
@ -0,0 +1,28 @@
|
|||
/***
|
||||
*
|
||||
* 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.
|
||||
*
|
||||
****/
|
||||
//
|
||||
// cdll_dll.h
|
||||
|
||||
// this file is included by both the game-dll and the client-dll,
|
||||
|
||||
#ifndef CDLL_DLL_H
|
||||
#define CDLL_DLL_H
|
||||
|
||||
#define MAX_WEAPON_SLOTS 5 // hud item selection slots
|
||||
#define MAX_ITEM_TYPES 6 // hud item selection slots
|
||||
|
||||
#define MAX_ITEMS 5 // hard coded item types
|
||||
|
||||
#endif
|
File diff suppressed because it is too large
Load Diff
|
@ -0,0 +1,50 @@
|
|||
/***
|
||||
*
|
||||
* 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.
|
||||
*
|
||||
****/
|
||||
#ifndef CLIENT_H
|
||||
#define CLIENT_H
|
||||
|
||||
extern void respawn( entvars_t* pev, BOOL fCopyCorpse );
|
||||
extern BOOL ClientConnect( edict_t *pEntity, const char *userinfo );
|
||||
extern void ClientDisconnect( edict_t *pEntity );
|
||||
extern void ClientKill( edict_t *pEntity );
|
||||
extern void ClientPutInServer( edict_t *pEntity );
|
||||
extern void ClientCommand( edict_t *pEntity );
|
||||
extern void ClientUserInfoChanged( edict_t *pEntity, char *infobuffer );
|
||||
extern void ServerActivate( edict_t *pEdictList, int edictCount, int clientMax );
|
||||
extern void ServerDeactivate( void );
|
||||
extern void StartFrame( void );
|
||||
extern void EndFrame( void );
|
||||
extern void PlayerPostThink( edict_t *pEntity );
|
||||
extern void PlayerPreThink( edict_t *pEntity );
|
||||
extern void ParmsNewLevel( void );
|
||||
extern void ParmsChangeLevel( void );
|
||||
|
||||
extern void ClientPrecache( void );
|
||||
|
||||
extern const char *GetGameDescription( void );
|
||||
|
||||
extern void SpectatorConnect ( edict_t *pEntity );
|
||||
extern void SpectatorDisconnect ( edict_t *pEntity );
|
||||
extern void SpectatorThink ( edict_t *pEntity );
|
||||
|
||||
extern void Sys_Error( const char *error_string );
|
||||
|
||||
extern int SetupVisibility( edict_t *pViewEntity, edict_t *pClient, int portal, float *rgflViewOrg );
|
||||
extern int AddToFullPack( edict_t *pView, edict_t *pHost, edict_t *pEdict, int hostflags, int hostarea, byte *pSet );
|
||||
|
||||
extern void CmdStart( const edict_t *player, const struct usercmd_s *cmd, unsigned int random_seed );
|
||||
extern void CmdEnd ( const edict_t *player );
|
||||
|
||||
#endif // CLIENT_H
|
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
|
@ -0,0 +1,600 @@
|
|||
/***
|
||||
*
|
||||
* 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 "gamerules.h"
|
||||
|
||||
#define BOLT_AIR_VELOCITY 2000
|
||||
#define BOLT_WATER_VELOCITY 1000
|
||||
|
||||
// UNDONE: Save/restore this? Don't forget to set classname and LINK_ENTITY_TO_CLASS()
|
||||
//
|
||||
// OVERLOADS SOME ENTVARS:
|
||||
//
|
||||
// speed - the ideal magnitude of my velocity
|
||||
class CCrossbowBolt : public CBaseEntity
|
||||
{
|
||||
void Spawn( void );
|
||||
void Precache( void );
|
||||
int Classify ( void );
|
||||
void EXPORT BubbleThink( void );
|
||||
void EXPORT BoltTouch( CBaseEntity *pOther );
|
||||
void EXPORT ExplodeThink( void );
|
||||
|
||||
int m_iTrail;
|
||||
|
||||
public:
|
||||
static CCrossbowBolt *BoltCreate( void );
|
||||
};
|
||||
LINK_ENTITY_TO_CLASS( crossbow_bolt, CCrossbowBolt );
|
||||
|
||||
CCrossbowBolt *CCrossbowBolt::BoltCreate( void )
|
||||
{
|
||||
// Create a new entity with CCrossbowBolt private data
|
||||
CCrossbowBolt *pBolt = GetClassPtr( (CCrossbowBolt *)NULL );
|
||||
pBolt->pev->classname = MAKE_STRING("bolt");
|
||||
pBolt->Spawn();
|
||||
|
||||
return pBolt;
|
||||
}
|
||||
|
||||
void CCrossbowBolt::Spawn( )
|
||||
{
|
||||
Precache( );
|
||||
pev->movetype = MOVETYPE_FLY;
|
||||
pev->solid = SOLID_BBOX;
|
||||
|
||||
pev->gravity = 0.5;
|
||||
|
||||
SET_MODEL(ENT(pev), "models/crossbow_bolt.mdl");
|
||||
|
||||
UTIL_SetOrigin( pev, pev->origin );
|
||||
UTIL_SetSize(pev, Vector(0, 0, 0), Vector(0, 0, 0));
|
||||
|
||||
SetTouch( BoltTouch );
|
||||
SetThink( BubbleThink );
|
||||
pev->nextthink = gpGlobals->time + 0.2;
|
||||
}
|
||||
|
||||
|
||||
void CCrossbowBolt::Precache( )
|
||||
{
|
||||
PRECACHE_MODEL ("models/crossbow_bolt.mdl");
|
||||
PRECACHE_SOUND("weapons/xbow_hitbod1.wav");
|
||||
PRECACHE_SOUND("weapons/xbow_hitbod2.wav");
|
||||
PRECACHE_SOUND("weapons/xbow_fly1.wav");
|
||||
PRECACHE_SOUND("weapons/xbow_hit1.wav");
|
||||
PRECACHE_SOUND("fvox/beep.wav");
|
||||
m_iTrail = PRECACHE_MODEL("sprites/streak.spr");
|
||||
}
|
||||
|
||||
|
||||
int CCrossbowBolt :: Classify ( void )
|
||||
{
|
||||
return CLASS_NONE;
|
||||
}
|
||||
|
||||
void CCrossbowBolt::BoltTouch( CBaseEntity *pOther )
|
||||
{
|
||||
SetTouch( NULL );
|
||||
SetThink( NULL );
|
||||
|
||||
if (pOther->pev->takedamage)
|
||||
{
|
||||
TraceResult tr = UTIL_GetGlobalTrace( );
|
||||
entvars_t *pevOwner;
|
||||
|
||||
pevOwner = VARS( pev->owner );
|
||||
|
||||
// UNDONE: this needs to call TraceAttack instead
|
||||
ClearMultiDamage( );
|
||||
|
||||
if ( pOther->IsPlayer() )
|
||||
{
|
||||
pOther->TraceAttack(pevOwner, gSkillData.plrDmgCrossbowClient, pev->velocity.Normalize(), &tr, DMG_NEVERGIB );
|
||||
}
|
||||
else
|
||||
{
|
||||
pOther->TraceAttack(pevOwner, gSkillData.plrDmgCrossbowMonster, pev->velocity.Normalize(), &tr, DMG_BULLET | DMG_NEVERGIB );
|
||||
}
|
||||
|
||||
ApplyMultiDamage( pev, pevOwner );
|
||||
|
||||
pev->velocity = Vector( 0, 0, 0 );
|
||||
// play body "thwack" sound
|
||||
switch( RANDOM_LONG(0,1) )
|
||||
{
|
||||
case 0:
|
||||
EMIT_SOUND(ENT(pev), CHAN_WEAPON, "weapons/xbow_hitbod1.wav", 1, ATTN_NORM); break;
|
||||
case 1:
|
||||
EMIT_SOUND(ENT(pev), CHAN_WEAPON, "weapons/xbow_hitbod2.wav", 1, ATTN_NORM); break;
|
||||
}
|
||||
|
||||
if ( !g_pGameRules->IsMultiplayer() )
|
||||
{
|
||||
Killed( pev, GIB_NEVER );
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
EMIT_SOUND_DYN(ENT(pev), CHAN_WEAPON, "weapons/xbow_hit1.wav", RANDOM_FLOAT(0.95, 1.0), ATTN_NORM, 0, 98 + RANDOM_LONG(0,7));
|
||||
|
||||
SetThink( SUB_Remove );
|
||||
pev->nextthink = gpGlobals->time;// this will get changed below if the bolt is allowed to stick in what it hit.
|
||||
|
||||
if ( FClassnameIs( pOther->pev, "worldspawn" ) )
|
||||
{
|
||||
// if what we hit is static architecture, can stay around for a while.
|
||||
Vector vecDir = pev->velocity.Normalize( );
|
||||
UTIL_SetOrigin( pev, pev->origin - vecDir * 12 );
|
||||
pev->angles = UTIL_VecToAngles( vecDir );
|
||||
pev->solid = SOLID_NOT;
|
||||
pev->movetype = MOVETYPE_FLY;
|
||||
pev->velocity = Vector( 0, 0, 0 );
|
||||
pev->avelocity.z = 0;
|
||||
pev->angles.z = RANDOM_LONG(0,360);
|
||||
pev->nextthink = gpGlobals->time + 10.0;
|
||||
}
|
||||
|
||||
if (UTIL_PointContents(pev->origin) != CONTENTS_WATER)
|
||||
{
|
||||
UTIL_Sparks( pev->origin );
|
||||
}
|
||||
}
|
||||
|
||||
if ( g_pGameRules->IsMultiplayer() )
|
||||
{
|
||||
SetThink( ExplodeThink );
|
||||
pev->nextthink = gpGlobals->time + 0.1;
|
||||
}
|
||||
}
|
||||
|
||||
void CCrossbowBolt::BubbleThink( void )
|
||||
{
|
||||
pev->nextthink = gpGlobals->time + 0.1;
|
||||
|
||||
if (pev->waterlevel == 0)
|
||||
return;
|
||||
|
||||
UTIL_BubbleTrail( pev->origin - pev->velocity * 0.1, pev->origin, 1 );
|
||||
}
|
||||
|
||||
void CCrossbowBolt::ExplodeThink( void )
|
||||
{
|
||||
int iContents = UTIL_PointContents ( pev->origin );
|
||||
int iScale;
|
||||
|
||||
pev->dmg = 40;
|
||||
iScale = 10;
|
||||
|
||||
MESSAGE_BEGIN( MSG_PVS, SVC_TEMPENTITY, pev->origin );
|
||||
WRITE_BYTE( TE_EXPLOSION);
|
||||
WRITE_COORD( pev->origin.x );
|
||||
WRITE_COORD( pev->origin.y );
|
||||
WRITE_COORD( pev->origin.z );
|
||||
if (iContents != CONTENTS_WATER)
|
||||
{
|
||||
WRITE_SHORT( g_sModelIndexFireball );
|
||||
}
|
||||
else
|
||||
{
|
||||
WRITE_SHORT( g_sModelIndexWExplosion );
|
||||
}
|
||||
WRITE_BYTE( iScale ); // scale * 10
|
||||
WRITE_BYTE( 15 ); // framerate
|
||||
WRITE_BYTE( TE_EXPLFLAG_NONE );
|
||||
MESSAGE_END();
|
||||
|
||||
entvars_t *pevOwner;
|
||||
|
||||
if ( pev->owner )
|
||||
pevOwner = VARS( pev->owner );
|
||||
else
|
||||
pevOwner = NULL;
|
||||
|
||||
pev->owner = NULL; // can't traceline attack owner if this is set
|
||||
|
||||
::RadiusDamage( pev->origin, pev, pevOwner, pev->dmg, 128, CLASS_NONE, DMG_BLAST | DMG_ALWAYSGIB );
|
||||
|
||||
UTIL_Remove(this);
|
||||
}
|
||||
|
||||
|
||||
enum crossbow_e {
|
||||
CROSSBOW_IDLE1 = 0, // full
|
||||
CROSSBOW_IDLE2, // empty
|
||||
CROSSBOW_FIDGET1, // full
|
||||
CROSSBOW_FIDGET2, // empty
|
||||
CROSSBOW_FIRE1, // full
|
||||
CROSSBOW_FIRE2, // reload
|
||||
CROSSBOW_FIRE3, // empty
|
||||
CROSSBOW_RELOAD, // from empty
|
||||
CROSSBOW_DRAW1, // full
|
||||
CROSSBOW_DRAW2, // empty
|
||||
CROSSBOW_HOLSTER1, // full
|
||||
CROSSBOW_HOLSTER2, // empty
|
||||
};
|
||||
|
||||
|
||||
class CCrossbow : public CBasePlayerWeapon
|
||||
{
|
||||
public:
|
||||
void Spawn( void );
|
||||
void Precache( void );
|
||||
int iItemSlot( ) { return 3; }
|
||||
int GetItemInfo(ItemInfo *p);
|
||||
|
||||
void FireBolt( void );
|
||||
void FireSniperBolt( void );
|
||||
void PrimaryAttack( void );
|
||||
void SecondaryAttack( void );
|
||||
int AddToPlayer( CBasePlayer *pPlayer );
|
||||
BOOL Deploy( );
|
||||
void Holster( int skiplocal = 0 );
|
||||
void Reload( void );
|
||||
void WeaponIdle( void );
|
||||
|
||||
int m_fInZoom; // don't save this
|
||||
};
|
||||
LINK_ENTITY_TO_CLASS( weapon_crossbow, CCrossbow );
|
||||
|
||||
void CCrossbow::Spawn( )
|
||||
{
|
||||
Precache( );
|
||||
m_iId = WEAPON_CROSSBOW;
|
||||
SET_MODEL(ENT(pev), "models/w_crossbow.mdl");
|
||||
|
||||
m_iDefaultAmmo = CROSSBOW_DEFAULT_GIVE;
|
||||
|
||||
FallInit();// get ready to fall down.
|
||||
}
|
||||
|
||||
int CCrossbow::AddToPlayer( CBasePlayer *pPlayer )
|
||||
{
|
||||
if ( CBasePlayerWeapon::AddToPlayer( pPlayer ) )
|
||||
{
|
||||
MESSAGE_BEGIN( MSG_ONE, gmsgWeapPickup, NULL, pPlayer->pev );
|
||||
WRITE_BYTE( m_iId );
|
||||
MESSAGE_END();
|
||||
return TRUE;
|
||||
}
|
||||
return FALSE;
|
||||
}
|
||||
|
||||
void CCrossbow::Precache( void )
|
||||
{
|
||||
PRECACHE_MODEL("models/w_crossbow.mdl");
|
||||
PRECACHE_MODEL("models/v_crossbow.mdl");
|
||||
PRECACHE_MODEL("models/p_crossbow.mdl");
|
||||
|
||||
PRECACHE_SOUND("weapons/xbow_fire1.wav");
|
||||
PRECACHE_SOUND("weapons/xbow_reload1.wav");
|
||||
|
||||
UTIL_PrecacheOther( "crossbow_bolt" );
|
||||
}
|
||||
|
||||
|
||||
int CCrossbow::GetItemInfo(ItemInfo *p)
|
||||
{
|
||||
p->pszName = STRING(pev->classname);
|
||||
p->pszAmmo1 = "bolts";
|
||||
p->iMaxAmmo1 = BOLT_MAX_CARRY;
|
||||
p->pszAmmo2 = NULL;
|
||||
p->iMaxAmmo2 = -1;
|
||||
p->iMaxClip = CROSSBOW_MAX_CLIP;
|
||||
p->iSlot = 2;
|
||||
p->iPosition = 2;
|
||||
p->iId = WEAPON_CROSSBOW;
|
||||
p->iFlags = 0;
|
||||
p->iWeight = CROSSBOW_WEIGHT;
|
||||
return 1;
|
||||
}
|
||||
|
||||
|
||||
BOOL CCrossbow::Deploy( )
|
||||
{
|
||||
if (m_iClip)
|
||||
return DefaultDeploy( "models/v_crossbow.mdl", "models/p_crossbow.mdl", CROSSBOW_DRAW1, "bow" );
|
||||
return DefaultDeploy( "models/v_crossbow.mdl", "models/p_crossbow.mdl", CROSSBOW_DRAW2, "bow" );
|
||||
}
|
||||
|
||||
void CCrossbow::Holster( int skiplocal /* = 0 */ )
|
||||
{
|
||||
m_fInReload = FALSE;// cancel any reload in progress.
|
||||
|
||||
if ( m_fInZoom )
|
||||
{
|
||||
SecondaryAttack( );
|
||||
}
|
||||
|
||||
m_pPlayer->m_flNextAttack = UTIL_WeaponTimeBase() + 0.5;
|
||||
if (m_iClip)
|
||||
SendWeaponAnim( CROSSBOW_HOLSTER1 );
|
||||
else
|
||||
SendWeaponAnim( CROSSBOW_HOLSTER2 );
|
||||
}
|
||||
|
||||
void CCrossbow::PrimaryAttack( void )
|
||||
{
|
||||
if ( m_fInZoom && g_pGameRules->IsMultiplayer() )
|
||||
{
|
||||
FireSniperBolt();
|
||||
return;
|
||||
}
|
||||
|
||||
FireBolt();
|
||||
}
|
||||
|
||||
// this function only gets called in multiplayer
|
||||
void CCrossbow::FireSniperBolt()
|
||||
{
|
||||
m_flNextPrimaryAttack = gpGlobals->time + 0.75;
|
||||
|
||||
if (m_iClip == 0)
|
||||
{
|
||||
PlayEmptySound( );
|
||||
return;
|
||||
}
|
||||
|
||||
TraceResult tr;
|
||||
|
||||
m_pPlayer->m_iWeaponVolume = QUIET_GUN_VOLUME;
|
||||
m_iClip--;
|
||||
|
||||
// make twang sound
|
||||
EMIT_SOUND_DYN(ENT(m_pPlayer->pev), CHAN_WEAPON, "weapons/xbow_fire1.wav", RANDOM_FLOAT(0.95, 1.0), ATTN_NORM, 0, 93 + RANDOM_LONG(0,0xF));
|
||||
|
||||
if (m_iClip)
|
||||
{
|
||||
EMIT_SOUND_DYN(ENT(m_pPlayer->pev), CHAN_ITEM, "weapons/xbow_reload1.wav", RANDOM_FLOAT(0.95, 1.0), ATTN_NORM, 0, 93 + RANDOM_LONG(0,0xF));
|
||||
SendWeaponAnim( CROSSBOW_FIRE1 );
|
||||
}
|
||||
else if (m_pPlayer->m_rgAmmo[m_iPrimaryAmmoType] == 0)
|
||||
{
|
||||
SendWeaponAnim( CROSSBOW_FIRE3 );
|
||||
}
|
||||
|
||||
// player "shoot" animation
|
||||
m_pPlayer->SetAnimation( PLAYER_ATTACK1 );
|
||||
|
||||
Vector anglesAim = m_pPlayer->pev->viewangles + m_pPlayer->pev->punchangle;
|
||||
UTIL_MakeVectors( anglesAim );
|
||||
Vector vecSrc = m_pPlayer->GetGunPosition( ) - gpGlobals->v_up * 2;
|
||||
Vector vecDir = gpGlobals->v_forward;
|
||||
|
||||
UTIL_TraceLine(vecSrc, vecSrc + vecDir * 8192, dont_ignore_monsters, m_pPlayer->edict(), &tr);
|
||||
|
||||
if ( tr.pHit->v.takedamage )
|
||||
{
|
||||
switch( RANDOM_LONG(0,1) )
|
||||
{
|
||||
case 0:
|
||||
EMIT_SOUND( tr.pHit, CHAN_BODY, "weapons/xbow_hitbod1.wav", 1, ATTN_NORM); break;
|
||||
case 1:
|
||||
EMIT_SOUND( tr.pHit, CHAN_BODY, "weapons/xbow_hitbod2.wav", 1, ATTN_NORM); break;
|
||||
}
|
||||
|
||||
ClearMultiDamage( );
|
||||
CBaseEntity::Instance(tr.pHit)->TraceAttack(m_pPlayer->pev, 120, vecDir, &tr, DMG_BULLET | DMG_NEVERGIB );
|
||||
ApplyMultiDamage( pev, m_pPlayer->pev );
|
||||
}
|
||||
else
|
||||
{
|
||||
// create a bolt
|
||||
CCrossbowBolt *pBolt = CCrossbowBolt::BoltCreate();
|
||||
pBolt->pev->origin = tr.vecEndPos - vecDir * 10;
|
||||
pBolt->pev->angles = UTIL_VecToAngles( vecDir );
|
||||
pBolt->pev->solid = SOLID_NOT;
|
||||
pBolt->SetTouch( NULL );
|
||||
pBolt->SetThink( SUB_Remove );
|
||||
|
||||
EMIT_SOUND( pBolt->edict(), CHAN_WEAPON, "weapons/xbow_hit1.wav", RANDOM_FLOAT(0.95, 1.0), ATTN_NORM );
|
||||
|
||||
if (UTIL_PointContents(tr.vecEndPos) != CONTENTS_WATER)
|
||||
{
|
||||
UTIL_Sparks( tr.vecEndPos );
|
||||
}
|
||||
|
||||
if ( FClassnameIs( tr.pHit, "worldspawn" ) )
|
||||
{
|
||||
// let the bolt sit around for a while if it hit static architecture
|
||||
pBolt->pev->nextthink = gpGlobals->time + 5.0;
|
||||
}
|
||||
else
|
||||
{
|
||||
pBolt->pev->nextthink = gpGlobals->time;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void CCrossbow::FireBolt()
|
||||
{
|
||||
TraceResult tr;
|
||||
|
||||
if (m_iClip == 0)
|
||||
{
|
||||
PlayEmptySound( );
|
||||
return;
|
||||
}
|
||||
|
||||
m_pPlayer->m_iWeaponVolume = QUIET_GUN_VOLUME;
|
||||
|
||||
m_iClip--;
|
||||
|
||||
// make twang sound
|
||||
EMIT_SOUND_DYN(ENT(m_pPlayer->pev), CHAN_WEAPON, "weapons/xbow_fire1.wav", RANDOM_FLOAT(0.95, 1.0), ATTN_NORM, 0, 93 + RANDOM_LONG(0,0xF));
|
||||
|
||||
if (m_iClip)
|
||||
{
|
||||
|
||||
EMIT_SOUND_DYN(ENT(m_pPlayer->pev), CHAN_ITEM, "weapons/xbow_reload1.wav", RANDOM_FLOAT(0.95, 1.0), ATTN_NORM, 0, 93 + RANDOM_LONG(0,0xF));
|
||||
SendWeaponAnim( CROSSBOW_FIRE1 );
|
||||
}
|
||||
else if (m_pPlayer->m_rgAmmo[m_iPrimaryAmmoType] == 0)
|
||||
{
|
||||
SendWeaponAnim( CROSSBOW_FIRE3 );
|
||||
}
|
||||
|
||||
// player "shoot" animation
|
||||
m_pPlayer->SetAnimation( PLAYER_ATTACK1 );
|
||||
|
||||
Vector anglesAim = m_pPlayer->pev->viewangles + m_pPlayer->pev->punchangle;
|
||||
UTIL_MakeVectors( anglesAim );
|
||||
|
||||
// Vector vecSrc = pev->origin + gpGlobals->v_up * 16 + gpGlobals->v_forward * 20 + gpGlobals->v_right * 4;
|
||||
anglesAim.x = -anglesAim.x;
|
||||
Vector vecSrc = m_pPlayer->GetGunPosition( ) - gpGlobals->v_up * 2;
|
||||
Vector vecDir = gpGlobals->v_forward;
|
||||
|
||||
//CBaseEntity *pBolt = CBaseEntity::Create( "crossbow_bolt", vecSrc, anglesAim, m_pPlayer->edict() );
|
||||
CCrossbowBolt *pBolt = CCrossbowBolt::BoltCreate();
|
||||
pBolt->pev->origin = vecSrc;
|
||||
pBolt->pev->angles = anglesAim;
|
||||
pBolt->pev->owner = m_pPlayer->edict();
|
||||
|
||||
if (m_pPlayer->pev->waterlevel == 3)
|
||||
{
|
||||
pBolt->pev->velocity = vecDir * BOLT_WATER_VELOCITY;
|
||||
pBolt->pev->speed = BOLT_WATER_VELOCITY;
|
||||
}
|
||||
else
|
||||
{
|
||||
pBolt->pev->velocity = vecDir * BOLT_AIR_VELOCITY;
|
||||
pBolt->pev->speed = BOLT_AIR_VELOCITY;
|
||||
}
|
||||
pBolt->pev->avelocity.z = 10;
|
||||
|
||||
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 + 0.75;
|
||||
|
||||
m_flNextSecondaryAttack = gpGlobals->time + 0.75;
|
||||
if (m_iClip != 0)
|
||||
m_flTimeWeaponIdle = gpGlobals->time + 5.0;
|
||||
else
|
||||
m_flTimeWeaponIdle = 0.75;
|
||||
|
||||
m_pPlayer->pev->punchangle.x -= 2;
|
||||
}
|
||||
|
||||
|
||||
void CCrossbow::SecondaryAttack()
|
||||
{
|
||||
if (m_fInZoom)
|
||||
{
|
||||
m_pPlayer->pev->fov = 90; // 90 means reset to default fov
|
||||
m_fInZoom = 0;
|
||||
}
|
||||
else
|
||||
{
|
||||
m_pPlayer->pev->fov = 20;
|
||||
m_fInZoom = 1;
|
||||
}
|
||||
|
||||
pev->nextthink = gpGlobals->time + 0.1;
|
||||
m_flNextSecondaryAttack = gpGlobals->time + 1.0;
|
||||
}
|
||||
|
||||
|
||||
void CCrossbow::Reload( void )
|
||||
{
|
||||
if ( m_fInZoom )
|
||||
{
|
||||
SecondaryAttack();
|
||||
}
|
||||
|
||||
if (DefaultReload( 5, CROSSBOW_RELOAD, 4.5 ))
|
||||
{
|
||||
EMIT_SOUND_DYN(ENT(m_pPlayer->pev), CHAN_ITEM, "weapons/xbow_reload1.wav", RANDOM_FLOAT(0.95, 1.0), ATTN_NORM, 0, 93 + RANDOM_LONG(0,0xF));
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
void CCrossbow::WeaponIdle( void )
|
||||
{
|
||||
m_pPlayer->GetAutoaimVector( AUTOAIM_2DEGREES ); // get the autoaim vector but ignore it; used for autoaim crosshair in DM
|
||||
|
||||
ResetEmptySound( );
|
||||
|
||||
if (m_flTimeWeaponIdle < gpGlobals->time)
|
||||
{
|
||||
float flRand = RANDOM_FLOAT(0, 1);
|
||||
if (flRand <= 0.75)
|
||||
{
|
||||
if (m_iClip)
|
||||
{
|
||||
SendWeaponAnim( CROSSBOW_IDLE1 );
|
||||
}
|
||||
else
|
||||
{
|
||||
SendWeaponAnim( CROSSBOW_IDLE2 );
|
||||
}
|
||||
m_flTimeWeaponIdle = gpGlobals->time + RANDOM_FLOAT ( 10, 15 );
|
||||
}
|
||||
else
|
||||
{
|
||||
if (m_iClip)
|
||||
{
|
||||
SendWeaponAnim( CROSSBOW_FIDGET1 );
|
||||
m_flTimeWeaponIdle = gpGlobals->time + 90.0 / 30.0;
|
||||
}
|
||||
else
|
||||
{
|
||||
SendWeaponAnim( CROSSBOW_FIDGET2 );
|
||||
m_flTimeWeaponIdle = gpGlobals->time + 80.0 / 30.0;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
class CCrossbowAmmo : public CBasePlayerAmmo
|
||||
{
|
||||
void Spawn( void )
|
||||
{
|
||||
Precache( );
|
||||
SET_MODEL(ENT(pev), "models/w_crossbow_clip.mdl");
|
||||
CBasePlayerAmmo::Spawn( );
|
||||
}
|
||||
void Precache( void )
|
||||
{
|
||||
PRECACHE_MODEL ("models/w_crossbow_clip.mdl");
|
||||
PRECACHE_SOUND("items/9mmclip1.wav");
|
||||
}
|
||||
BOOL AddAmmo( CBaseEntity *pOther )
|
||||
{
|
||||
if (pOther->GiveAmmo( AMMO_CROSSBOWCLIP_GIVE, "bolts", BOLT_MAX_CARRY ) != -1)
|
||||
{
|
||||
EMIT_SOUND(ENT(pev), CHAN_ITEM, "items/9mmclip1.wav", 1, ATTN_NORM);
|
||||
return TRUE;
|
||||
}
|
||||
return FALSE;
|
||||
}
|
||||
};
|
||||
LINK_ENTITY_TO_CLASS( ammo_crossbow, CCrossbowAmmo );
|
||||
|
||||
|
||||
|
||||
#endif
|
|
@ -0,0 +1,334 @@
|
|||
/***
|
||||
*
|
||||
* 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 CROWBAR_BODYHIT_VOLUME 128
|
||||
#define CROWBAR_WALLHIT_VOLUME 512
|
||||
|
||||
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 PrimaryAttack( void );
|
||||
int Swing( int fFirst );
|
||||
BOOL Deploy( void );
|
||||
void Holster( int skiplocal = 0 );
|
||||
int m_iSwing;
|
||||
TraceResult m_trHit;
|
||||
};
|
||||
LINK_ENTITY_TO_CLASS( weapon_crowbar, CCrowbar );
|
||||
|
||||
|
||||
|
||||
enum gauss_e {
|
||||
CROWBAR_IDLE = 0,
|
||||
CROWBAR_DRAW,
|
||||
CROWBAR_HOLSTER,
|
||||
CROWBAR_ATTACK1HIT,
|
||||
CROWBAR_ATTACK1MISS,
|
||||
CROWBAR_ATTACK2MISS,
|
||||
CROWBAR_ATTACK2HIT,
|
||||
CROWBAR_ATTACK3MISS,
|
||||
CROWBAR_ATTACK3HIT
|
||||
};
|
||||
|
||||
|
||||
void CCrowbar::Spawn( )
|
||||
{
|
||||
Precache( );
|
||||
m_iId = WEAPON_CROWBAR;
|
||||
SET_MODEL(ENT(pev), "models/w_crowbar.mdl");
|
||||
m_iClip = -1;
|
||||
|
||||
FallInit();// get ready to fall down.
|
||||
}
|
||||
|
||||
|
||||
void CCrowbar::Precache( void )
|
||||
{
|
||||
PRECACHE_MODEL("models/v_crowbar.mdl");
|
||||
PRECACHE_MODEL("models/w_crowbar.mdl");
|
||||
PRECACHE_MODEL("models/p_crowbar.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 CCrowbar::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_CROWBAR;
|
||||
p->iWeight = CROWBAR_WEIGHT;
|
||||
return 1;
|
||||
}
|
||||
|
||||
|
||||
|
||||
BOOL CCrowbar::Deploy( )
|
||||
{
|
||||
return DefaultDeploy( "models/v_crowbar.mdl", "models/p_crowbar.mdl", CROWBAR_DRAW, "crowbar" );
|
||||
}
|
||||
|
||||
void CCrowbar::Holster( int skiplocal /* = 0 */ )
|
||||
{
|
||||
m_pPlayer->m_flNextAttack = UTIL_WeaponTimeBase() + 0.5;
|
||||
SendWeaponAnim( CROWBAR_HOLSTER );
|
||||
}
|
||||
|
||||
|
||||
void 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 CCrowbar::PrimaryAttack()
|
||||
{
|
||||
if (! Swing( 1 ))
|
||||
{
|
||||
SetThink( SwingAgain );
|
||||
pev->nextthink = gpGlobals->time + 0.1;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
void CCrowbar::Smack( )
|
||||
{
|
||||
DecalGunshot( &m_trHit, BULLET_PLAYER_CROWBAR );
|
||||
}
|
||||
|
||||
|
||||
void CCrowbar::SwingAgain( void )
|
||||
{
|
||||
Swing( 0 );
|
||||
}
|
||||
|
||||
|
||||
int CCrowbar::Swing( int fFirst )
|
||||
{
|
||||
int fDidHit = FALSE;
|
||||
|
||||
TraceResult tr;
|
||||
|
||||
UTIL_MakeVectors (m_pPlayer->pev->viewangles);
|
||||
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( CROWBAR_ATTACK1MISS ); break;
|
||||
case 1:
|
||||
SendWeaponAnim( CROWBAR_ATTACK2MISS ); break;
|
||||
case 2:
|
||||
SendWeaponAnim( CROWBAR_ATTACK3MISS ); 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( CROWBAR_ATTACK1HIT ); break;
|
||||
case 1:
|
||||
SendWeaponAnim( CROWBAR_ATTACK2HIT ); break;
|
||||
case 2:
|
||||
SendWeaponAnim( CROWBAR_ATTACK3HIT ); 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.plrDmgCrowbar, gpGlobals->v_forward, &tr, DMG_CLUB );
|
||||
}
|
||||
else
|
||||
{
|
||||
// subsequent swings do half
|
||||
pEntity->TraceAttack(m_pPlayer->pev, gSkillData.plrDmgCrowbar / 2, gpGlobals->v_forward, &tr, DMG_CLUB );
|
||||
}
|
||||
ApplyMultiDamage( m_pPlayer->pev, m_pPlayer->pev );
|
||||
|
||||
m_flNextPrimaryAttack = gpGlobals->time + 0.25;
|
||||
|
||||
// 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 = CROWBAR_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_CROWBAR);
|
||||
|
||||
if ( g_pGameRules->IsMultiplayer() )
|
||||
{
|
||||
// override the volume here, cause we don't play texture sounds in multiplayer,
|
||||
// and fvolbar is going to be 0 from the above call.
|
||||
|
||||
fvolbar = 1;
|
||||
}
|
||||
|
||||
// also play crowbar strike
|
||||
switch( RANDOM_LONG(0,1) )
|
||||
{
|
||||
case 0:
|
||||
//UTIL_EmitAmbientSound(ENT(0), ptr->vecEndPos, "weapons/cbar_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/cbar_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 );
|
||||
pev->nextthink = gpGlobals->time + 0.2;
|
||||
|
||||
m_pPlayer->m_iWeaponVolume = flVol * CROWBAR_WALLHIT_VOLUME;
|
||||
}
|
||||
return fDidHit;
|
||||
}
|
||||
|
||||
|
||||
|
|
@ -0,0 +1,75 @@
|
|||
/***
|
||||
*
|
||||
* 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.
|
||||
*
|
||||
****/
|
||||
#ifndef DECALS_H
|
||||
#define DECALS_H
|
||||
|
||||
//
|
||||
// Dynamic Decals
|
||||
//
|
||||
enum decal_e
|
||||
{
|
||||
DECAL_GUNSHOT1 = 0,
|
||||
DECAL_GUNSHOT2,
|
||||
DECAL_GUNSHOT3,
|
||||
DECAL_GUNSHOT4,
|
||||
DECAL_GUNSHOT5,
|
||||
DECAL_LAMBDA1,
|
||||
DECAL_LAMBDA2,
|
||||
DECAL_LAMBDA3,
|
||||
DECAL_LAMBDA4,
|
||||
DECAL_LAMBDA5,
|
||||
DECAL_LAMBDA6,
|
||||
DECAL_SCORCH1,
|
||||
DECAL_SCORCH2,
|
||||
DECAL_BLOOD1,
|
||||
DECAL_BLOOD2,
|
||||
DECAL_BLOOD3,
|
||||
DECAL_BLOOD4,
|
||||
DECAL_BLOOD5,
|
||||
DECAL_BLOOD6,
|
||||
DECAL_YBLOOD1,
|
||||
DECAL_YBLOOD2,
|
||||
DECAL_YBLOOD3,
|
||||
DECAL_YBLOOD4,
|
||||
DECAL_YBLOOD5,
|
||||
DECAL_YBLOOD6,
|
||||
DECAL_GLASSBREAK1,
|
||||
DECAL_GLASSBREAK2,
|
||||
DECAL_GLASSBREAK3,
|
||||
DECAL_BIGSHOT1,
|
||||
DECAL_BIGSHOT2,
|
||||
DECAL_BIGSHOT3,
|
||||
DECAL_BIGSHOT4,
|
||||
DECAL_BIGSHOT5,
|
||||
DECAL_SPIT1,
|
||||
DECAL_SPIT2,
|
||||
DECAL_BPROOF1, // Bulletproof glass decal
|
||||
DECAL_GARGSTOMP1, // Gargantua stomp crack
|
||||
DECAL_SMALLSCORCH1, // Small scorch mark
|
||||
DECAL_SMALLSCORCH2, // Small scorch mark
|
||||
DECAL_SMALLSCORCH3, // Small scorch mark
|
||||
DECAL_MOMMABIRTH, // Big momma birth splatter
|
||||
DECAL_MOMMASPLAT,
|
||||
};
|
||||
|
||||
typedef struct
|
||||
{
|
||||
char *name;
|
||||
int index;
|
||||
} DLL_DECALLIST;
|
||||
|
||||
extern DLL_DECALLIST gDecals[];
|
||||
|
||||
#endif // DECALS_H
|
File diff suppressed because it is too large
Load Diff
|
@ -0,0 +1,98 @@
|
|||
/***
|
||||
*
|
||||
* 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[];
|
||||
|
||||
//=========================================================
|
||||
// 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
|
File diff suppressed because it is too large
Load Diff
|
@ -0,0 +1,33 @@
|
|||
/***
|
||||
*
|
||||
* 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.
|
||||
*
|
||||
****/
|
||||
#ifndef DOORS_H
|
||||
#define DOORS_H
|
||||
|
||||
// doors
|
||||
#define SF_DOOR_ROTATE_Y 0
|
||||
#define SF_DOOR_START_OPEN 1
|
||||
#define SF_DOOR_ROTATE_BACKWARDS 2
|
||||
#define SF_DOOR_PASSABLE 8
|
||||
#define SF_DOOR_ONEWAY 16
|
||||
#define SF_DOOR_NO_AUTO_RETURN 32
|
||||
#define SF_DOOR_ROTATE_Z 64
|
||||
#define SF_DOOR_ROTATE_X 128
|
||||
#define SF_DOOR_USE_ONLY 256 // door must be opened by player's use button.
|
||||
#define SF_DOOR_NOMONSTERS 512 // Monster can't open
|
||||
#define SF_DOOR_SILENT 0x80000000
|
||||
|
||||
|
||||
|
||||
#endif //DOORS_H
|
File diff suppressed because it is too large
Load Diff
|
@ -0,0 +1,210 @@
|
|||
/***
|
||||
*
|
||||
* 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.
|
||||
*
|
||||
****/
|
||||
#ifndef EFFECTS_H
|
||||
#define EFFECTS_H
|
||||
|
||||
#include "beam_def.h"
|
||||
|
||||
#define SF_BEAM_STARTON 0x0001
|
||||
#define SF_BEAM_TOGGLE 0x0002
|
||||
#define SF_BEAM_RANDOM 0x0004
|
||||
#define SF_BEAM_RING 0x0008
|
||||
#define SF_BEAM_SPARKSTART 0x0010
|
||||
#define SF_BEAM_SPARKEND 0x0020
|
||||
#define SF_BEAM_DECALS 0x0040
|
||||
#define SF_BEAM_SHADEIN 0x0080
|
||||
#define SF_BEAM_SHADEOUT 0x0100
|
||||
#define SF_BEAM_TEMPORARY 0x8000
|
||||
|
||||
#define SF_SPRITE_STARTON 0x0001
|
||||
#define SF_SPRITE_ONCE 0x0002
|
||||
#define SF_SPRITE_TEMPORARY 0x8000
|
||||
|
||||
class CSprite : public CPointEntity
|
||||
{
|
||||
public:
|
||||
void Spawn( void );
|
||||
void Precache( void );
|
||||
|
||||
int ObjectCaps( void )
|
||||
{
|
||||
int flags = 0;
|
||||
if ( pev->spawnflags & SF_SPRITE_TEMPORARY )
|
||||
flags = FCAP_DONT_SAVE;
|
||||
return (CBaseEntity :: ObjectCaps() & ~FCAP_ACROSS_TRANSITION) | flags;
|
||||
}
|
||||
void EXPORT AnimateThink( void );
|
||||
void EXPORT ExpandThink( void );
|
||||
void Use( CBaseEntity *pActivator, CBaseEntity *pCaller, USE_TYPE useType, float value );
|
||||
void Animate( float frames );
|
||||
void Expand( float scaleSpeed, float fadeSpeed );
|
||||
void SpriteInit( const char *pSpriteName, const Vector &origin );
|
||||
|
||||
inline void SetAttachment( edict_t *pEntity, int attachment )
|
||||
{
|
||||
if ( pEntity )
|
||||
{
|
||||
pev->skin = ENTINDEX(pEntity);
|
||||
pev->body = attachment;
|
||||
pev->aiment = pEntity;
|
||||
pev->movetype = MOVETYPE_FOLLOW;
|
||||
}
|
||||
}
|
||||
void TurnOff( void );
|
||||
void TurnOn( void );
|
||||
inline float Frames( void ) { return m_maxFrame; }
|
||||
inline void SetTransparency( int rendermode, int r, int g, int b, int a, int fx )
|
||||
{
|
||||
pev->rendermode = rendermode;
|
||||
pev->rendercolor.x = r;
|
||||
pev->rendercolor.y = g;
|
||||
pev->rendercolor.z = b;
|
||||
pev->renderamt = a;
|
||||
pev->renderfx = fx;
|
||||
}
|
||||
inline void SetTexture( int spriteIndex ) { pev->modelindex = spriteIndex; }
|
||||
inline void SetScale( float scale ) { pev->scale = scale; }
|
||||
inline void SetColor( int r, int g, int b ) { pev->rendercolor.x = r; pev->rendercolor.y = g; pev->rendercolor.z = b; }
|
||||
inline void SetBrightness( int brightness ) { pev->renderamt = brightness; }
|
||||
|
||||
inline void AnimateAndDie( float framerate )
|
||||
{
|
||||
SetThink(AnimateUntilDead);
|
||||
pev->framerate = framerate;
|
||||
pev->dmgtime = gpGlobals->time + (m_maxFrame / framerate);
|
||||
pev->nextthink = gpGlobals->time;
|
||||
}
|
||||
|
||||
void EXPORT AnimateUntilDead( void );
|
||||
|
||||
virtual int Save( CSave &save );
|
||||
virtual int Restore( CRestore &restore );
|
||||
static TYPEDESCRIPTION m_SaveData[];
|
||||
static CSprite *SpriteCreate( const char *pSpriteName, const Vector &origin, BOOL animate );
|
||||
|
||||
private:
|
||||
|
||||
float m_lastTime;
|
||||
float m_maxFrame;
|
||||
};
|
||||
|
||||
|
||||
class CBeam : public CBaseEntity
|
||||
{
|
||||
public:
|
||||
void Spawn( void );
|
||||
int ObjectCaps( void )
|
||||
{
|
||||
int flags = 0;
|
||||
if ( pev->spawnflags & SF_BEAM_TEMPORARY )
|
||||
flags = FCAP_DONT_SAVE;
|
||||
return (CBaseEntity :: ObjectCaps() & ~FCAP_ACROSS_TRANSITION) | flags;
|
||||
}
|
||||
|
||||
void EXPORT TriggerTouch( CBaseEntity *pOther );
|
||||
|
||||
// These functions are here to show the way beams are encoded as entities.
|
||||
// Encoding beams as entities simplifies their management in the client/server architecture
|
||||
inline void SetType( int type ) { pev->rendermode = type; }
|
||||
inline void SetFlags( int flags ) { pev->renderfx |= flags; }
|
||||
inline void SetStartPos( const Vector& pos ) { pev->origin = pos; }
|
||||
inline void SetEndPos( const Vector& pos ) { pev->oldorigin = pos; }
|
||||
inline void SetStartEntity( edict_t *pEnt ) { pev->aiment = pEnt; }
|
||||
inline void SetEndEntity( edict_t *pEnt ) { pev->owner = pEnt; }
|
||||
|
||||
inline void SetStartAttachment( int attachment ) { pev->colormap = (pev->colormap & 0xFF00)>>8 | attachment; }
|
||||
inline void SetEndAttachment( int attachment ) { pev->colormap = (pev->colormap & 0xFF) | (attachment<<8); }
|
||||
|
||||
inline void SetTexture( int spriteIndex ) { pev->modelindex = spriteIndex; }
|
||||
inline void SetWidth( int width ) { pev->scale = width; }
|
||||
inline void SetNoise( int amplitude ) { pev->body = amplitude; }
|
||||
inline void SetColor( int r, int g, int b ) { pev->rendercolor.x = r; pev->rendercolor.y = g; pev->rendercolor.z = b; }
|
||||
inline void SetBrightness( int brightness ) { pev->renderamt = brightness; }
|
||||
inline void SetFrame( float frame ) { pev->frame = frame; }
|
||||
inline void SetScrollRate( int speed ) { pev->animtime = speed; }
|
||||
|
||||
inline int GetType( void ) { return pev->rendermode; }
|
||||
inline int GetFlags( void ) { return pev->renderfx; }
|
||||
inline edict_t *GetStartEntity( void ) { return pev->owner; }
|
||||
inline edict_t *GetEndEntity( void ) { return pev->aiment; }
|
||||
|
||||
const Vector &GetStartPos( void );
|
||||
const Vector &GetEndPos( void );
|
||||
|
||||
Vector Center( void ) { return (GetStartPos() + GetEndPos()) * 0.5; }; // center point of beam
|
||||
|
||||
inline int GetTexture( void ) { return pev->modelindex; }
|
||||
inline int GetWidth( void ) { return pev->scale; }
|
||||
inline int GetNoise( void ) { return pev->body; }
|
||||
// inline void GetColor( int r, int g, int b ) { pev->rendercolor.x = r; pev->rendercolor.y = g; pev->rendercolor.z = b; }
|
||||
inline int GetBrightness( void ) { return pev->renderamt; }
|
||||
inline int GetFrame( void ) { return pev->frame; }
|
||||
inline int GetScrollRate( void ) { return pev->animtime; }
|
||||
|
||||
// Call after you change start/end positions
|
||||
void RelinkBeam( void );
|
||||
// void SetObjectCollisionBox( void );
|
||||
|
||||
void DoSparks( const Vector &start, const Vector &end );
|
||||
CBaseEntity *RandomTargetname( const char *szName );
|
||||
void BeamDamage( TraceResult *ptr );
|
||||
// Init after BeamCreate()
|
||||
void BeamInit( const char *pSpriteName, int width );
|
||||
void PointsInit( const Vector &start, const Vector &end );
|
||||
void PointEntInit( const Vector &start, edict_t *pEnt );
|
||||
void EntsInit( edict_t *pStart, edict_t *pEnd );
|
||||
void HoseInit( const Vector &start, const Vector &direction );
|
||||
|
||||
static CBeam *BeamCreate( const char *pSpriteName, int width );
|
||||
|
||||
inline void LiveForTime( float time ) { SetThink(SUB_Remove); pev->nextthink = gpGlobals->time + time; }
|
||||
inline void BeamDamageInstant( TraceResult *ptr, float damage )
|
||||
{
|
||||
pev->dmg = damage;
|
||||
pev->dmgtime = gpGlobals->time - 1;
|
||||
BeamDamage(ptr);
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
#define SF_MESSAGE_ONCE 0x0001 // Fade in, not out
|
||||
#define SF_MESSAGE_ALL 0x0002 // Send to all clients
|
||||
|
||||
|
||||
class CLaser : public CBeam
|
||||
{
|
||||
public:
|
||||
void Spawn( void );
|
||||
void Precache( void );
|
||||
void KeyValue( KeyValueData *pkvd );
|
||||
|
||||
void TurnOn( void );
|
||||
void TurnOff( void );
|
||||
int IsOn( void );
|
||||
|
||||
void FireAtPoint( TraceResult &point );
|
||||
|
||||
void EXPORT StrikeThink( void );
|
||||
void Use( CBaseEntity *pActivator, CBaseEntity *pCaller, USE_TYPE useType, float value );
|
||||
virtual int Save( CSave &save );
|
||||
virtual int Restore( CRestore &restore );
|
||||
static TYPEDESCRIPTION m_SaveData[];
|
||||
|
||||
CSprite *m_pSprite;
|
||||
int m_iszSpriteName;
|
||||
Vector m_firePosition;
|
||||
};
|
||||
|
||||
#endif //EFFECTS_H
|
|
@ -0,0 +1,622 @@
|
|||
/***
|
||||
*
|
||||
* 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 "gamerules.h"
|
||||
|
||||
#define EGON_PRIMARY_VOLUME 450
|
||||
#define EGON_BEAM_SPRITE "sprites/xbeam1.spr"
|
||||
#define EGON_FLARE_SPRITE "sprites/XSpark1.spr"
|
||||
#define EGON_SOUND_OFF "weapons/egon_off1.wav"
|
||||
#define EGON_SOUND_RUN "weapons/egon_run3.wav"
|
||||
#define EGON_SOUND_STARTUP "weapons/egon_windup2.wav"
|
||||
|
||||
#define EGON_SWITCH_NARROW_TIME 0.75 // Time it takes to switch fire modes
|
||||
#define EGON_SWITCH_WIDE_TIME 1.5
|
||||
|
||||
|
||||
enum egon_e {
|
||||
EGON_IDLE1 = 0,
|
||||
EGON_FIDGET1,
|
||||
EGON_ALTFIREON,
|
||||
EGON_ALTFIRECYCLE,
|
||||
EGON_ALTFIREOFF,
|
||||
EGON_FIRE1,
|
||||
EGON_FIRE2,
|
||||
EGON_FIRE3,
|
||||
EGON_FIRE4,
|
||||
EGON_DRAW,
|
||||
EGON_HOLSTER
|
||||
};
|
||||
|
||||
|
||||
class CEgon : 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 );
|
||||
|
||||
BOOL HasAmmo( void )
|
||||
{
|
||||
if (m_pPlayer->m_rgAmmo[m_iPrimaryAmmoType] <= 0)
|
||||
return FALSE;
|
||||
return TRUE;
|
||||
}
|
||||
|
||||
void UseAmmo( int count )
|
||||
{
|
||||
if ( m_pPlayer->m_rgAmmo[m_iPrimaryAmmoType] >= count )
|
||||
m_pPlayer->m_rgAmmo[m_iPrimaryAmmoType] -= count;
|
||||
else
|
||||
m_pPlayer->m_rgAmmo[m_iPrimaryAmmoType] = 0;
|
||||
}
|
||||
|
||||
enum EGON_FIRESTATE { FIRE_OFF, FIRE_CHARGE };
|
||||
enum EGON_FIREMODE { FIRE_NARROW, FIRE_WIDE};
|
||||
|
||||
private:
|
||||
float m_shootTime;
|
||||
CBeam *m_pBeam;
|
||||
CBeam *m_pNoise;
|
||||
CSprite *m_pSprite;
|
||||
EGON_FIRESTATE m_fireState;
|
||||
EGON_FIREMODE m_fireMode;
|
||||
float m_shakeTime;
|
||||
BOOL m_deployed;
|
||||
};
|
||||
|
||||
LINK_ENTITY_TO_CLASS( weapon_egon, CEgon );
|
||||
|
||||
int CEgon::g_fireAnims1[] = { EGON_FIRE1, EGON_FIRE2, EGON_FIRE3, EGON_FIRE4 };
|
||||
int CEgon::g_fireAnims2[] = { EGON_ALTFIRECYCLE };
|
||||
|
||||
|
||||
TYPEDESCRIPTION CEgon::m_SaveData[] =
|
||||
{
|
||||
DEFINE_FIELD( CEgon, m_pBeam, FIELD_CLASSPTR ),
|
||||
DEFINE_FIELD( CEgon, m_pNoise, FIELD_CLASSPTR ),
|
||||
DEFINE_FIELD( CEgon, m_pSprite, FIELD_CLASSPTR ),
|
||||
DEFINE_FIELD( CEgon, m_shootTime, FIELD_TIME ),
|
||||
DEFINE_FIELD( CEgon, m_fireState, FIELD_INTEGER ),
|
||||
DEFINE_FIELD( CEgon, m_fireMode, FIELD_INTEGER ),
|
||||
DEFINE_FIELD( CEgon, m_shakeTime, FIELD_TIME ),
|
||||
DEFINE_FIELD( CEgon, m_flAmmoUseTime, FIELD_TIME ),
|
||||
};
|
||||
IMPLEMENT_SAVERESTORE( CEgon, CBasePlayerWeapon );
|
||||
|
||||
|
||||
void CEgon::Spawn( )
|
||||
{
|
||||
Precache( );
|
||||
m_iId = WEAPON_EGON;
|
||||
SET_MODEL(ENT(pev), "models/w_egon.mdl");
|
||||
|
||||
m_iDefaultAmmo = EGON_DEFAULT_GIVE;
|
||||
|
||||
FallInit();// get ready to fall down.
|
||||
}
|
||||
|
||||
|
||||
void CEgon::Precache( void )
|
||||
{
|
||||
PRECACHE_MODEL("models/w_egon.mdl");
|
||||
PRECACHE_MODEL("models/v_egon.mdl");
|
||||
PRECACHE_MODEL("models/p_egon.mdl");
|
||||
|
||||
PRECACHE_MODEL("models/w_9mmclip.mdl");
|
||||
PRECACHE_SOUND("items/9mmclip1.wav");
|
||||
|
||||
PRECACHE_SOUND( EGON_SOUND_OFF );
|
||||
PRECACHE_SOUND( EGON_SOUND_RUN );
|
||||
PRECACHE_SOUND( EGON_SOUND_STARTUP );
|
||||
|
||||
PRECACHE_MODEL( EGON_BEAM_SPRITE );
|
||||
PRECACHE_MODEL( EGON_FLARE_SPRITE );
|
||||
|
||||
PRECACHE_SOUND ("weapons/357_cock1.wav");
|
||||
}
|
||||
|
||||
|
||||
BOOL CEgon::Deploy( void )
|
||||
{
|
||||
m_deployed = FALSE;
|
||||
return DefaultDeploy( "models/v_egon.mdl", "models/p_egon.mdl", EGON_DRAW, "egon" );
|
||||
}
|
||||
|
||||
int CEgon::AddToPlayer( CBasePlayer *pPlayer )
|
||||
{
|
||||
if ( CBasePlayerWeapon::AddToPlayer( pPlayer ) )
|
||||
{
|
||||
MESSAGE_BEGIN( MSG_ONE, gmsgWeapPickup, NULL, pPlayer->pev );
|
||||
WRITE_BYTE( m_iId );
|
||||
MESSAGE_END();
|
||||
return TRUE;
|
||||
}
|
||||
return FALSE;
|
||||
}
|
||||
|
||||
|
||||
|
||||
void CEgon::Holster( int skiplocal /* = 0 */ )
|
||||
{
|
||||
m_pPlayer->m_flNextAttack = UTIL_WeaponTimeBase() + 0.5;
|
||||
// m_flTimeWeaponIdle = gpGlobals->time + UTIL_RandomFloat ( 10, 15 );
|
||||
SendWeaponAnim( EGON_HOLSTER );
|
||||
|
||||
if ( m_fireState != FIRE_OFF )
|
||||
EndAttack();
|
||||
}
|
||||
|
||||
int CEgon::GetItemInfo(ItemInfo *p)
|
||||
{
|
||||
p->pszName = STRING(pev->classname);
|
||||
p->pszAmmo1 = "uranium";
|
||||
p->iMaxAmmo1 = URANIUM_MAX_CARRY;
|
||||
p->pszAmmo2 = NULL;
|
||||
p->iMaxAmmo2 = -1;
|
||||
p->iMaxClip = WEAPON_NOCLIP;
|
||||
p->iSlot = 3;
|
||||
p->iPosition = 2;
|
||||
p->iId = m_iId = WEAPON_EGON;
|
||||
p->iFlags = 0;
|
||||
p->iWeight = EGON_WEIGHT;
|
||||
|
||||
return 1;
|
||||
}
|
||||
|
||||
|
||||
//#define EGON_PULSE_INTERVAL 0.25
|
||||
//#define EGON_DISCHARGE_INTERVAL 0.5
|
||||
|
||||
#define EGON_PULSE_INTERVAL 0.1
|
||||
#define EGON_DISCHARGE_INTERVAL 0.1
|
||||
|
||||
float CEgon::GetPulseInterval( void )
|
||||
{
|
||||
if ( g_pGameRules->IsMultiplayer() )
|
||||
{
|
||||
return 0.1;
|
||||
}
|
||||
|
||||
return EGON_PULSE_INTERVAL;
|
||||
}
|
||||
|
||||
float CEgon::GetDischargeInterval( void )
|
||||
{
|
||||
if ( g_pGameRules->IsMultiplayer() )
|
||||
{
|
||||
return 0.1;
|
||||
}
|
||||
|
||||
return EGON_DISCHARGE_INTERVAL;
|
||||
}
|
||||
|
||||
void CEgon::Attack( void )
|
||||
{
|
||||
// don't fire underwater
|
||||
if (m_pPlayer->pev->waterlevel == 3)
|
||||
{
|
||||
if ( m_pBeam )
|
||||
{
|
||||
EndAttack();
|
||||
}
|
||||
else
|
||||
{
|
||||
PlayEmptySound( );
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
||||
UTIL_MakeVectors( m_pPlayer->pev->viewangles + m_pPlayer->pev->punchangle );
|
||||
Vector vecAiming = gpGlobals->v_forward;
|
||||
Vector vecSrc = m_pPlayer->GetGunPosition( );
|
||||
|
||||
switch( m_fireState )
|
||||
{
|
||||
case FIRE_OFF:
|
||||
{
|
||||
if ( !HasAmmo() )
|
||||
{
|
||||
m_flNextPrimaryAttack = m_flNextSecondaryAttack = gpGlobals->time + 0.25;
|
||||
PlayEmptySound( );
|
||||
return;
|
||||
}
|
||||
|
||||
m_flAmmoUseTime = gpGlobals->time;// start using ammo ASAP.
|
||||
|
||||
SendWeaponAnim( g_fireAnims1[ RANDOM_LONG(0,ARRAYSIZE(g_fireAnims1)-1) ] );
|
||||
m_shakeTime = 0;
|
||||
|
||||
m_pPlayer->m_iWeaponVolume = EGON_PRIMARY_VOLUME;
|
||||
m_flTimeWeaponIdle = gpGlobals->time + 0.1;
|
||||
m_shootTime = gpGlobals->time + 2;
|
||||
|
||||
if ( m_fireMode == FIRE_WIDE )
|
||||
{
|
||||
EMIT_SOUND_DYN(ENT(m_pPlayer->pev), CHAN_WEAPON, EGON_SOUND_STARTUP, 0.98, ATTN_NORM, 0, 125 );
|
||||
}
|
||||
else
|
||||
{
|
||||
EMIT_SOUND_DYN(ENT(m_pPlayer->pev), CHAN_WEAPON, EGON_SOUND_STARTUP, 0.9, ATTN_NORM, 0, 100 );
|
||||
}
|
||||
|
||||
pev->dmgtime = gpGlobals->time + GetPulseInterval();
|
||||
m_fireState = FIRE_CHARGE;
|
||||
}
|
||||
break;
|
||||
|
||||
case FIRE_CHARGE:
|
||||
{
|
||||
Fire( vecSrc, vecAiming );
|
||||
m_pPlayer->m_iWeaponVolume = EGON_PRIMARY_VOLUME;
|
||||
|
||||
if ( m_shootTime != 0 && gpGlobals->time > m_shootTime )
|
||||
{
|
||||
if ( m_fireMode == FIRE_WIDE )
|
||||
{
|
||||
EMIT_SOUND_DYN(ENT(m_pPlayer->pev), CHAN_STATIC, EGON_SOUND_RUN, 0.98, ATTN_NORM, 0, 125 );
|
||||
}
|
||||
else
|
||||
{
|
||||
EMIT_SOUND_DYN(ENT(m_pPlayer->pev), CHAN_STATIC, EGON_SOUND_RUN, 0.9, ATTN_NORM, 0, 100 );
|
||||
}
|
||||
|
||||
m_shootTime = 0;
|
||||
}
|
||||
if ( !HasAmmo() )
|
||||
{
|
||||
EndAttack();
|
||||
m_fireState = FIRE_OFF;
|
||||
m_flNextPrimaryAttack = m_flNextSecondaryAttack = gpGlobals->time + 1.0;
|
||||
}
|
||||
|
||||
}
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
void CEgon::PrimaryAttack( void )
|
||||
{
|
||||
m_fireMode = FIRE_WIDE;
|
||||
Attack();
|
||||
|
||||
}
|
||||
|
||||
void CEgon::Fire( const Vector &vecOrigSrc, const Vector &vecDir )
|
||||
{
|
||||
Vector vecDest = vecOrigSrc + vecDir * 2048;
|
||||
edict_t *pentIgnore;
|
||||
TraceResult tr;
|
||||
|
||||
pentIgnore = m_pPlayer->edict();
|
||||
Vector tmpSrc = vecOrigSrc + gpGlobals->v_up * -8 + gpGlobals->v_right * 3;
|
||||
|
||||
// ALERT( at_console, "." );
|
||||
|
||||
UTIL_TraceLine( vecOrigSrc, vecDest, dont_ignore_monsters, pentIgnore, &tr );
|
||||
|
||||
if (tr.fAllSolid)
|
||||
return;
|
||||
|
||||
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;
|
||||
|
||||
switch ( m_fireMode )
|
||||
{
|
||||
case FIRE_NARROW:
|
||||
if ( pev->dmgtime < gpGlobals->time )
|
||||
{
|
||||
// Narrow mode only does damage to the entity it hits
|
||||
ClearMultiDamage();
|
||||
if (pEntity->pev->takedamage)
|
||||
{
|
||||
pEntity->TraceAttack( m_pPlayer->pev, gSkillData.plrDmgEgonNarrow, vecDir, &tr, DMG_ENERGYBEAM );
|
||||
}
|
||||
ApplyMultiDamage(m_pPlayer->pev, m_pPlayer->pev);
|
||||
|
||||
if ( g_pGameRules->IsMultiplayer() )
|
||||
{
|
||||
// multiplayer uses 1 ammo every 1/10th second
|
||||
if ( gpGlobals->time >= m_flAmmoUseTime )
|
||||
{
|
||||
UseAmmo( 1 );
|
||||
m_flAmmoUseTime = gpGlobals->time + 0.1;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
// single player, use 3 ammo/second
|
||||
if ( gpGlobals->time >= m_flAmmoUseTime )
|
||||
{
|
||||
UseAmmo( 1 );
|
||||
m_flAmmoUseTime = gpGlobals->time + 0.166;
|
||||
}
|
||||
}
|
||||
|
||||
pev->dmgtime = gpGlobals->time + GetPulseInterval();
|
||||
}
|
||||
timedist = ( pev->dmgtime - gpGlobals->time ) / GetPulseInterval();
|
||||
break;
|
||||
|
||||
case FIRE_WIDE:
|
||||
if ( pev->dmgtime < gpGlobals->time )
|
||||
{
|
||||
// wide mode does damage to the ent, and radius damage
|
||||
ClearMultiDamage();
|
||||
if (pEntity->pev->takedamage)
|
||||
{
|
||||
pEntity->TraceAttack( m_pPlayer->pev, gSkillData.plrDmgEgonWide, vecDir, &tr, DMG_ENERGYBEAM | DMG_ALWAYSGIB);
|
||||
}
|
||||
ApplyMultiDamage(m_pPlayer->pev, m_pPlayer->pev);
|
||||
|
||||
if ( g_pGameRules->IsMultiplayer() )
|
||||
{
|
||||
// radius damage a little more potent in multiplayer.
|
||||
::RadiusDamage( tr.vecEndPos, pev, m_pPlayer->pev, gSkillData.plrDmgEgonWide/4, 128, CLASS_NONE, DMG_ENERGYBEAM | DMG_BLAST | DMG_ALWAYSGIB );
|
||||
}
|
||||
|
||||
if ( !m_pPlayer->IsAlive() )
|
||||
return;
|
||||
|
||||
if ( g_pGameRules->IsMultiplayer() )
|
||||
{
|
||||
//multiplayer uses 5 ammo/second
|
||||
if ( gpGlobals->time >= m_flAmmoUseTime )
|
||||
{
|
||||
UseAmmo( 1 );
|
||||
m_flAmmoUseTime = gpGlobals->time + 0.2;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
// Wide mode uses 10 charges per second in single player
|
||||
if ( gpGlobals->time >= m_flAmmoUseTime )
|
||||
{
|
||||
UseAmmo( 1 );
|
||||
m_flAmmoUseTime = gpGlobals->time + 0.1;
|
||||
}
|
||||
}
|
||||
|
||||
pev->dmgtime = gpGlobals->time + GetDischargeInterval();
|
||||
if ( m_shakeTime < gpGlobals->time )
|
||||
{
|
||||
UTIL_ScreenShake( tr.vecEndPos, 5.0, 150.0, 0.75, 250.0 );
|
||||
m_shakeTime = gpGlobals->time + 1.5;
|
||||
}
|
||||
}
|
||||
timedist = ( pev->dmgtime - gpGlobals->time ) / GetDischargeInterval();
|
||||
break;
|
||||
}
|
||||
|
||||
if ( timedist < 0 )
|
||||
timedist = 0;
|
||||
else if ( timedist > 1 )
|
||||
timedist = 1;
|
||||
timedist = 1-timedist;
|
||||
|
||||
UpdateEffect( tmpSrc, tr.vecEndPos, timedist );
|
||||
}
|
||||
|
||||
|
||||
void CEgon::UpdateEffect( const Vector &startPoint, const Vector &endPoint, float timeBlend )
|
||||
{
|
||||
if ( !m_pBeam )
|
||||
{
|
||||
CreateEffect();
|
||||
}
|
||||
|
||||
m_pBeam->SetStartPos( endPoint );
|
||||
m_pBeam->SetBrightness( 255 - (timeBlend*180) );
|
||||
m_pBeam->SetWidth( 40 - (timeBlend*20) );
|
||||
|
||||
if ( m_fireMode == FIRE_WIDE )
|
||||
m_pBeam->SetColor( 30 + (25*timeBlend), 30 + (30*timeBlend), 64 + 80*fabs(sin(gpGlobals->time*10)) );
|
||||
else
|
||||
m_pBeam->SetColor( 60 + (25*timeBlend), 120 + (30*timeBlend), 64 + 80*fabs(sin(gpGlobals->time*10)) );
|
||||
|
||||
|
||||
UTIL_SetOrigin( m_pSprite->pev, endPoint );
|
||||
m_pSprite->pev->frame += 8 * gpGlobals->frametime;
|
||||
if ( m_pSprite->pev->frame > m_pSprite->Frames() )
|
||||
m_pSprite->pev->frame = 0;
|
||||
|
||||
m_pNoise->SetStartPos( endPoint );
|
||||
}
|
||||
|
||||
|
||||
void CEgon::CreateEffect( void )
|
||||
{
|
||||
DestroyEffect();
|
||||
|
||||
m_pBeam = CBeam::BeamCreate( EGON_BEAM_SPRITE, 40 );
|
||||
m_pBeam->PointEntInit( pev->origin, m_pPlayer->edict() );
|
||||
m_pBeam->SetFlags( FBEAM_SINENOISE );
|
||||
m_pBeam->SetEndAttachment( 1 );
|
||||
m_pBeam->pev->spawnflags |= SF_BEAM_TEMPORARY; // Flag these to be destroyed on save/restore or level transition
|
||||
|
||||
m_pNoise = CBeam::BeamCreate( EGON_BEAM_SPRITE, 55 );
|
||||
m_pNoise->PointEntInit( pev->origin, m_pPlayer->edict() );
|
||||
m_pNoise->SetScrollRate( 25 );
|
||||
m_pNoise->SetBrightness( 100 );
|
||||
m_pNoise->SetEndAttachment( 1 );
|
||||
m_pNoise->pev->spawnflags |= SF_BEAM_TEMPORARY;
|
||||
|
||||
m_pSprite = CSprite::SpriteCreate( EGON_FLARE_SPRITE, pev->origin, FALSE );
|
||||
m_pSprite->pev->scale = 1.0;
|
||||
m_pSprite->SetTransparency( kRenderGlow, 255, 255, 255, 255, kRenderFxNoDissipation );
|
||||
m_pSprite->pev->spawnflags |= SF_SPRITE_TEMPORARY;
|
||||
|
||||
if ( m_fireMode == FIRE_WIDE )
|
||||
{
|
||||
m_pBeam->SetScrollRate( 50 );
|
||||
m_pBeam->SetNoise( 20 );
|
||||
m_pNoise->SetColor( 50, 50, 255 );
|
||||
m_pNoise->SetNoise( 8 );
|
||||
}
|
||||
else
|
||||
{
|
||||
m_pBeam->SetScrollRate( 110 );
|
||||
m_pBeam->SetNoise( 5 );
|
||||
m_pNoise->SetColor( 80, 120, 255 );
|
||||
m_pNoise->SetNoise( 2 );
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
void CEgon::DestroyEffect( void )
|
||||
{
|
||||
if ( m_pBeam )
|
||||
{
|
||||
UTIL_Remove( m_pBeam );
|
||||
m_pBeam = NULL;
|
||||
}
|
||||
if ( m_pNoise )
|
||||
{
|
||||
UTIL_Remove( m_pNoise );
|
||||
m_pNoise = NULL;
|
||||
}
|
||||
if ( m_pSprite )
|
||||
{
|
||||
if ( m_fireMode == FIRE_WIDE )
|
||||
m_pSprite->Expand( 10, 500 );
|
||||
else
|
||||
UTIL_Remove( m_pSprite );
|
||||
m_pSprite = NULL;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
void CEgon::WeaponIdle( void )
|
||||
{
|
||||
ResetEmptySound( );
|
||||
|
||||
if ( m_flTimeWeaponIdle > gpGlobals->time )
|
||||
return;
|
||||
|
||||
if ( m_fireState != FIRE_OFF )
|
||||
EndAttack();
|
||||
|
||||
|
||||
int iAnim;
|
||||
|
||||
float flRand = RANDOM_FLOAT(0,1);
|
||||
|
||||
if ( flRand <= 0.5 )
|
||||
{
|
||||
iAnim = EGON_IDLE1;
|
||||
m_flTimeWeaponIdle = gpGlobals->time + RANDOM_FLOAT(10,15);
|
||||
}
|
||||
else
|
||||
{
|
||||
iAnim = EGON_FIDGET1;
|
||||
m_flTimeWeaponIdle = gpGlobals->time + 3;
|
||||
}
|
||||
|
||||
SendWeaponAnim( iAnim );
|
||||
m_deployed = TRUE;
|
||||
}
|
||||
|
||||
|
||||
|
||||
void CEgon::EndAttack( void )
|
||||
{
|
||||
STOP_SOUND( ENT(m_pPlayer->pev), CHAN_STATIC, EGON_SOUND_RUN );
|
||||
EMIT_SOUND_DYN(ENT(m_pPlayer->pev), CHAN_WEAPON, EGON_SOUND_OFF, 0.98, ATTN_NORM, 0, 100);
|
||||
m_fireState = FIRE_OFF;
|
||||
m_flTimeWeaponIdle = gpGlobals->time + 2.0;
|
||||
m_flNextPrimaryAttack = m_flNextSecondaryAttack = gpGlobals->time + 0.5;
|
||||
DestroyEffect();
|
||||
}
|
||||
|
||||
|
||||
|
||||
class CEgonAmmo : public CBasePlayerAmmo
|
||||
{
|
||||
void Spawn( void )
|
||||
{
|
||||
Precache( );
|
||||
SET_MODEL(ENT(pev), "models/w_chainammo.mdl");
|
||||
CBasePlayerAmmo::Spawn( );
|
||||
}
|
||||
void Precache( void )
|
||||
{
|
||||
PRECACHE_MODEL ("models/w_chainammo.mdl");
|
||||
PRECACHE_SOUND("items/9mmclip1.wav");
|
||||
}
|
||||
BOOL AddAmmo( CBaseEntity *pOther )
|
||||
{
|
||||
if (pOther->GiveAmmo( AMMO_URANIUMBOX_GIVE, "uranium", URANIUM_MAX_CARRY ) != -1)
|
||||
{
|
||||
EMIT_SOUND(ENT(pev), CHAN_ITEM, "items/9mmclip1.wav", 1, ATTN_NORM);
|
||||
return TRUE;
|
||||
}
|
||||
return FALSE;
|
||||
}
|
||||
};
|
||||
LINK_ENTITY_TO_CLASS( ammo_egonclip, CEgonAmmo );
|
||||
|
||||
#endif
|
|
@ -0,0 +1,158 @@
|
|||
/***
|
||||
*
|
||||
* 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.
|
||||
*
|
||||
****/
|
||||
#ifndef ENGINECALLBACK_H
|
||||
#define ENGINECALLBACK_H
|
||||
|
||||
// Must be provided by user of this code
|
||||
extern enginefuncs_t g_engfuncs;
|
||||
|
||||
// The actual engine callbacks
|
||||
#define MALLOC( x ) (*g_engfuncs.pfnMemAlloc)( x, __FILE__, __LINE__ )
|
||||
#define CALLOC( x, y ) (*g_engfuncs.pfnMemAlloc)((x) * (y), __FILE__, __LINE__ )
|
||||
#define FREE( x ) (*g_engfuncs.pfnMemFree)( x, __FILE__, __LINE__ )
|
||||
|
||||
// The actual engine callbacks
|
||||
#define GETPLAYERUSERID (*g_engfuncs.pfnGetPlayerUserId)
|
||||
#define PRECACHE_MODEL (*g_engfuncs.pfnPrecacheModel)
|
||||
#define PRECACHE_SOUND (*g_engfuncs.pfnPrecacheSound)
|
||||
#define PRECACHE_GENERIC (*g_engfuncs.pfnPrecacheGeneric)
|
||||
#define SET_MODEL (*g_engfuncs.pfnSetModel)
|
||||
#define MODEL_INDEX (*g_engfuncs.pfnModelIndex)
|
||||
#define MODEL_FRAMES (*g_engfuncs.pfnModelFrames)
|
||||
#define SET_SIZE (*g_engfuncs.pfnSetSize)
|
||||
#define CHANGE_LEVEL (*g_engfuncs.pfnChangeLevel)
|
||||
#define GET_SPAWN_PARMS (*g_engfuncs.pfnGetSpawnParms)
|
||||
#define SAVE_SPAWN_PARMS (*g_engfuncs.pfnSaveSpawnParms)
|
||||
#define VEC_TO_YAW (*g_engfuncs.pfnVecToYaw)
|
||||
#define VEC_TO_ANGLES (*g_engfuncs.pfnVecToAngles)
|
||||
#define MOVE_TO_ORIGIN (*g_engfuncs.pfnMoveToOrigin)
|
||||
#define oldCHANGE_YAW (*g_engfuncs.pfnChangeYaw)
|
||||
#define CHANGE_PITCH (*g_engfuncs.pfnChangePitch)
|
||||
#define MAKE_VECTORS (*g_engfuncs.pfnMakeVectors)
|
||||
#define CREATE_ENTITY (*g_engfuncs.pfnCreateEntity)
|
||||
#define REMOVE_ENTITY (*g_engfuncs.pfnRemoveEntity)
|
||||
#define CREATE_NAMED_ENTITY (*g_engfuncs.pfnCreateNamedEntity)
|
||||
#define MAKE_STATIC (*g_engfuncs.pfnMakeStatic)
|
||||
#define LINK_ENTITY (*g_engfuncs.pfnLinkEdict)
|
||||
#define DROP_TO_FLOOR (*g_engfuncs.pfnDropToFloor)
|
||||
#define WALK_MOVE (*g_engfuncs.pfnWalkMove)
|
||||
#define SET_ORIGIN (*g_engfuncs.pfnSetOrigin)
|
||||
#define EMIT_SOUND_DYN2 (*g_engfuncs.pfnEmitSound)
|
||||
#define BUILD_SOUND_MSG (*g_engfuncs.pfnBuildSoundMsg)
|
||||
#define TRACE_LINE (*g_engfuncs.pfnTraceLine)
|
||||
#define TRACE_TOSS (*g_engfuncs.pfnTraceToss)
|
||||
#define TRACE_MONSTER_HULL (*g_engfuncs.pfnTraceMonsterHull)
|
||||
#define TRACE_HULL (*g_engfuncs.pfnTraceHull)
|
||||
#define GET_AIM_VECTOR (*g_engfuncs.pfnGetAimVector)
|
||||
#define SERVER_COMMAND (*g_engfuncs.pfnServerCommand)
|
||||
#define CLIENT_COMMAND (*g_engfuncs.pfnClientCommand)
|
||||
#define PARTICLE_EFFECT (*g_engfuncs.pfnParticleEffect)
|
||||
#define LIGHT_STYLE (*g_engfuncs.pfnLightStyle)
|
||||
#define DECAL_INDEX (*g_engfuncs.pfnDecalIndex)
|
||||
#define POINT_CONTENTS (*g_engfuncs.pfnPointContents)
|
||||
#define CRC32_INIT (*g_engfuncs.pfnCRC_Init)
|
||||
#define CRC32_PROCESS_BUFFER (*g_engfuncs.pfnCRC_ProcessBuffer)
|
||||
#define CRC32_PROCESS_BYTE (*g_engfuncs.pfnCRC_ProcessByte)
|
||||
#define CRC32_FINAL (*g_engfuncs.pfnCRC_Final)
|
||||
#define RANDOM_LONG (*g_engfuncs.pfnRandomLong)
|
||||
#define RANDOM_FLOAT (*g_engfuncs.pfnRandomFloat)
|
||||
#define CLASSIFY_EDICT (*g_engfuncs.pfnClassifyEdict)
|
||||
#define SET_AREAPORTAL (*g_engfuncs.pfnAreaPortal)
|
||||
#define COM_Parse (*g_engfuncs.pfnParseToken)
|
||||
|
||||
inline void MESSAGE_BEGIN( int msg_dest, int msg_type, const float *pOrigin = NULL, edict_t *ed = NULL ) {
|
||||
(*g_engfuncs.pfnMessageBegin)(msg_dest, msg_type, pOrigin, ed);
|
||||
}
|
||||
#define MESSAGE_END (*g_engfuncs.pfnMessageEnd)
|
||||
#define WRITE_BYTE (*g_engfuncs.pfnWriteByte)
|
||||
#define WRITE_CHAR (*g_engfuncs.pfnWriteChar)
|
||||
#define WRITE_SHORT (*g_engfuncs.pfnWriteShort)
|
||||
#define WRITE_LONG (*g_engfuncs.pfnWriteLong)
|
||||
#define WRITE_ANGLE (*g_engfuncs.pfnWriteAngle)
|
||||
#define WRITE_COORD (*g_engfuncs.pfnWriteCoord)
|
||||
inline void WRITE_FLOAT( float flValue )
|
||||
{ union { float f; int l; } dat; dat.f = flValue; WRITE_LONG( dat.l ); }
|
||||
#define WRITE_STRING (*g_engfuncs.pfnWriteString)
|
||||
#define WRITE_ENTITY (*g_engfuncs.pfnWriteEntity)
|
||||
#define WRITE_DIR( dir ) WRITE_BYTE(DirToBits( dir ))
|
||||
#define CVAR_REGISTER (*g_engfuncs.pfnCVarRegister)
|
||||
#define CVAR_GET_FLOAT (*g_engfuncs.pfnCVarGetFloat)
|
||||
#define CVAR_GET_STRING (*g_engfuncs.pfnCVarGetString)
|
||||
#define CVAR_SET_FLOAT (*g_engfuncs.pfnCVarSetFloat)
|
||||
#define CVAR_SET_STRING (*g_engfuncs.pfnCVarSetString)
|
||||
#define ALERT (*g_engfuncs.pfnAlertMessage)
|
||||
#define ENGINE_FPRINTF (*g_engfuncs.pfnEngineFprintf)
|
||||
#define ALLOC_PRIVATE (*g_engfuncs.pfnPvAllocEntPrivateData)
|
||||
inline void *GET_PRIVATE( edict_t *pent )
|
||||
{
|
||||
if ( pent )
|
||||
return pent->pvPrivateData;
|
||||
return NULL;
|
||||
}
|
||||
|
||||
// NOTE: Xash3D using custom StringTable System that using safety methods for access to strings
|
||||
// and never make duplicated strings, so it make no differences between ALLOC_STRING and MAKE_STRING
|
||||
// leave macros as legacy
|
||||
// if you want using classical half-life strings for some reasons, please set sys_sharedstrings into "1" in vars.rc
|
||||
// and get back original strings code from gold source SDK.
|
||||
#define ALLOC_STRING (*g_engfuncs.pfnAllocString)
|
||||
#define MAKE_STRING (*g_engfuncs.pfnAllocString)
|
||||
#define STRING (*g_engfuncs.pfnSzFromIndex)
|
||||
|
||||
#define FREE_PRIVATE (*g_engfuncs.pfnFreeEntPrivateData)
|
||||
#define FIND_ENTITY_BY_STRING (*g_engfuncs.pfnFindEntityByString)
|
||||
#define GETENTITYILLUM (*g_engfuncs.pfnGetEntityIllum)
|
||||
#define FIND_ENTITY_IN_SPHERE (*g_engfuncs.pfnFindEntityInSphere)
|
||||
#define FIND_CLIENT_IN_PVS (*g_engfuncs.pfnFindClientInPVS)
|
||||
#define EMIT_AMBIENT_SOUND (*g_engfuncs.pfnEmitAmbientSound)
|
||||
#define GET_MODEL_PTR (*g_engfuncs.pfnGetModelPtr)
|
||||
#define REG_USER_MSG (*g_engfuncs.pfnRegUserMsg)
|
||||
#define GET_BONE_POSITION (*g_engfuncs.pfnGetBonePosition)
|
||||
#define FUNCTION_FROM_NAME (*g_engfuncs.pfnFunctionFromName)
|
||||
#define NAME_FOR_FUNCTION (*g_engfuncs.pfnNameForFunction)
|
||||
#define TRACE_TEXTURE (*g_engfuncs.pfnTraceTexture)
|
||||
#define CLIENT_PRINTF (*g_engfuncs.pfnClientPrintf)
|
||||
#define CMD_ARGS (*g_engfuncs.pfnCmd_Args)
|
||||
#define CMD_ARGC (*g_engfuncs.pfnCmd_Argc)
|
||||
#define CMD_ARGV (*g_engfuncs.pfnCmd_Argv)
|
||||
#define GET_ATTACHMENT (*g_engfuncs.pfnGetAttachment)
|
||||
#define SET_VIEW (*g_engfuncs.pfnSetView)
|
||||
#define SET_CROSSHAIRANGLE (*g_engfuncs.pfnCrosshairAngle)
|
||||
#define SET_SKYBOX (*g_engfuncs.pfnSetSkybox)
|
||||
#define LOAD_FILE_FOR_ME (*g_engfuncs.pfnLoadFile)
|
||||
#define FREE_FILE (*g_engfuncs.pfnFreeFile)
|
||||
#define COMPARE_FILE_TIME (*g_engfuncs.pfnCompareFileTime)
|
||||
#define FILE_EXISTS (*g_engfuncs.pfnFileExists)
|
||||
#define GET_GAME_DIR (*g_engfuncs.pfnGetGameDir)
|
||||
#define IS_MAP_VALID (*g_engfuncs.pfnIsMapValid)
|
||||
#define SET_BONE_POSITION (*g_engfuncs.pfnSetBonePos)
|
||||
#define ENGINE_CHECK_AREA (*g_engfuncs.pfnCheckArea)
|
||||
#define DROP_CLIENT (*g_engfuncs.pfnDropClient)
|
||||
#define ENGINE_CHECK_PVS (*g_engfuncs.pfnCheckVisibility)
|
||||
#define IS_DEDICATED_SERVER (*g_engfuncs.pfnIsDedicatedServer)
|
||||
|
||||
#define PRECACHE_EVENT (*g_engfuncs.pfnPrecacheEvent)
|
||||
#define ENGINE_CANSKIP ( *g_engfuncs.pfnCanSkipPlayer )
|
||||
|
||||
#define HOST_ENDGAME (*g_engfuncs.pfnEndGame)
|
||||
#define HOST_ERROR (*g_engfuncs.pfnHostError)
|
||||
|
||||
#define ENGINE_GETPHYSINFO ( *g_engfuncs.pfnGetPhysicsInfoString )
|
||||
|
||||
#define ENGINE_SETGROUPMASK ( *g_engfuncs.pfnSetGroupMask )
|
||||
|
||||
#define PLAYER_CNX_STATS ( *g_engfuncs.pfnGetPlayerStats )
|
||||
|
||||
#endif //ENGINECALLBACK_H
|
|
@ -0,0 +1,273 @@
|
|||
/***
|
||||
*
|
||||
* 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.
|
||||
*
|
||||
****/
|
||||
/*
|
||||
|
||||
===== explode.cpp ========================================================
|
||||
|
||||
Explosion-related code
|
||||
|
||||
*/
|
||||
#include "extdll.h"
|
||||
#include "util.h"
|
||||
#include "cbase.h"
|
||||
#include "decals.h"
|
||||
#include "explode.h"
|
||||
|
||||
// Spark Shower
|
||||
class CShower : public CBaseEntity
|
||||
{
|
||||
void Spawn( void );
|
||||
void Think( void );
|
||||
void Touch( CBaseEntity *pOther );
|
||||
int ObjectCaps( void ) { return FCAP_DONT_SAVE; }
|
||||
};
|
||||
|
||||
LINK_ENTITY_TO_CLASS( spark_shower, CShower );
|
||||
|
||||
void CShower::Spawn( void )
|
||||
{
|
||||
pev->velocity = RANDOM_FLOAT( 200, 300 ) * pev->angles;
|
||||
pev->velocity.x += RANDOM_FLOAT(-100.f,100.f);
|
||||
pev->velocity.y += RANDOM_FLOAT(-100.f,100.f);
|
||||
if ( pev->velocity.z >= 0 )
|
||||
pev->velocity.z += 200;
|
||||
else
|
||||
pev->velocity.z -= 200;
|
||||
pev->movetype = MOVETYPE_BOUNCE;
|
||||
pev->gravity = 0.5;
|
||||
pev->nextthink = gpGlobals->time + 0.1;
|
||||
pev->solid = SOLID_NOT;
|
||||
SET_MODEL( edict(), "models/grenade.mdl"); // Need a model, just use the grenade, we don't draw it anyway
|
||||
UTIL_SetSize(pev, g_vecZero, g_vecZero );
|
||||
pev->effects |= EF_NODRAW;
|
||||
pev->speed = RANDOM_FLOAT( 0.5, 1.5 );
|
||||
|
||||
pev->angles = g_vecZero;
|
||||
}
|
||||
|
||||
|
||||
void CShower::Think( void )
|
||||
{
|
||||
UTIL_Sparks( pev->origin );
|
||||
|
||||
pev->speed -= 0.1;
|
||||
if ( pev->speed > 0 )
|
||||
pev->nextthink = gpGlobals->time + 0.1;
|
||||
else
|
||||
UTIL_Remove( this );
|
||||
pev->flags &= ~FL_ONGROUND;
|
||||
}
|
||||
|
||||
void CShower::Touch( CBaseEntity *pOther )
|
||||
{
|
||||
if ( pev->flags & FL_ONGROUND )
|
||||
pev->velocity = pev->velocity * 0.1;
|
||||
else
|
||||
pev->velocity = pev->velocity * 0.6;
|
||||
|
||||
if ( (pev->velocity.x*pev->velocity.x+pev->velocity.y*pev->velocity.y) < 10.0 )
|
||||
pev->speed = 0;
|
||||
}
|
||||
|
||||
class CEnvExplosion : public CBaseMonster
|
||||
{
|
||||
public:
|
||||
void Spawn( );
|
||||
void EXPORT Smoke ( void );
|
||||
void KeyValue( KeyValueData *pkvd );
|
||||
void Use( CBaseEntity *pActivator, CBaseEntity *pCaller, USE_TYPE useType, float value );
|
||||
|
||||
virtual int Save( CSave &save );
|
||||
virtual int Restore( CRestore &restore );
|
||||
static TYPEDESCRIPTION m_SaveData[];
|
||||
|
||||
int m_iMagnitude;// how large is the fireball? how much damage?
|
||||
int m_spriteScale; // what's the exact fireball sprite scale?
|
||||
};
|
||||
|
||||
TYPEDESCRIPTION CEnvExplosion::m_SaveData[] =
|
||||
{
|
||||
DEFINE_FIELD( CEnvExplosion, m_iMagnitude, FIELD_INTEGER ),
|
||||
DEFINE_FIELD( CEnvExplosion, m_spriteScale, FIELD_INTEGER ),
|
||||
};
|
||||
|
||||
IMPLEMENT_SAVERESTORE( CEnvExplosion, CBaseMonster );
|
||||
LINK_ENTITY_TO_CLASS( env_explosion, CEnvExplosion );
|
||||
|
||||
void CEnvExplosion::KeyValue( KeyValueData *pkvd )
|
||||
{
|
||||
if (FStrEq(pkvd->szKeyName, "iMagnitude"))
|
||||
{
|
||||
m_iMagnitude = atoi(pkvd->szValue);
|
||||
pkvd->fHandled = TRUE;
|
||||
}
|
||||
else
|
||||
CBaseEntity::KeyValue( pkvd );
|
||||
}
|
||||
|
||||
void CEnvExplosion::Spawn( void )
|
||||
{
|
||||
pev->solid = SOLID_NOT;
|
||||
pev->effects = EF_NODRAW;
|
||||
|
||||
pev->movetype = MOVETYPE_NONE;
|
||||
/*
|
||||
if ( m_iMagnitude > 250 )
|
||||
{
|
||||
m_iMagnitude = 250;
|
||||
}
|
||||
*/
|
||||
|
||||
float flSpriteScale;
|
||||
flSpriteScale = ( m_iMagnitude - 50) * 0.6;
|
||||
|
||||
/*
|
||||
if ( flSpriteScale > 50 )
|
||||
{
|
||||
flSpriteScale = 50;
|
||||
}
|
||||
*/
|
||||
if ( flSpriteScale < 10 )
|
||||
{
|
||||
flSpriteScale = 10;
|
||||
}
|
||||
|
||||
m_spriteScale = (int)flSpriteScale;
|
||||
}
|
||||
|
||||
void CEnvExplosion::Use( CBaseEntity *pActivator, CBaseEntity *pCaller, USE_TYPE useType, float value )
|
||||
{
|
||||
TraceResult tr;
|
||||
|
||||
pev->model = iStringNull;//invisible
|
||||
pev->solid = SOLID_NOT;// intangible
|
||||
|
||||
Vector vecSpot;// trace starts here!
|
||||
|
||||
vecSpot = pev->origin + Vector ( 0 , 0 , 8 );
|
||||
|
||||
UTIL_TraceLine ( vecSpot, vecSpot + Vector ( 0, 0, -40 ), ignore_monsters, ENT(pev), & tr);
|
||||
|
||||
// Pull out of the wall a bit
|
||||
if ( tr.flFraction != 1.0 )
|
||||
{
|
||||
pev->origin = tr.vecEndPos + (tr.vecPlaneNormal * (m_iMagnitude - 24) * 0.6);
|
||||
}
|
||||
else
|
||||
{
|
||||
pev->origin = pev->origin;
|
||||
}
|
||||
|
||||
// draw decal
|
||||
if (! ( pev->spawnflags & SF_ENVEXPLOSION_NODECAL))
|
||||
{
|
||||
if ( RANDOM_FLOAT( 0 , 1 ) < 0.5 )
|
||||
{
|
||||
UTIL_DecalTrace( &tr, DECAL_SCORCH1 );
|
||||
}
|
||||
else
|
||||
{
|
||||
UTIL_DecalTrace( &tr, DECAL_SCORCH2 );
|
||||
}
|
||||
}
|
||||
|
||||
// draw fireball
|
||||
if ( !( pev->spawnflags & SF_ENVEXPLOSION_NOFIREBALL ) )
|
||||
{
|
||||
MESSAGE_BEGIN( MSG_PAS, SVC_TEMPENTITY, pev->origin );
|
||||
WRITE_BYTE( TE_EXPLOSION);
|
||||
WRITE_COORD( pev->origin.x );
|
||||
WRITE_COORD( pev->origin.y );
|
||||
WRITE_COORD( pev->origin.z );
|
||||
WRITE_SHORT( g_sModelIndexFireball );
|
||||
WRITE_BYTE( (BYTE)m_spriteScale ); // scale * 10
|
||||
WRITE_BYTE( 15 ); // framerate
|
||||
WRITE_BYTE( TE_EXPLFLAG_NONE );
|
||||
MESSAGE_END();
|
||||
}
|
||||
else
|
||||
{
|
||||
MESSAGE_BEGIN( MSG_PAS, SVC_TEMPENTITY, pev->origin );
|
||||
WRITE_BYTE( TE_EXPLOSION);
|
||||
WRITE_COORD( pev->origin.x );
|
||||
WRITE_COORD( pev->origin.y );
|
||||
WRITE_COORD( pev->origin.z );
|
||||
WRITE_SHORT( g_sModelIndexFireball );
|
||||
WRITE_BYTE( 0 ); // no sprite
|
||||
WRITE_BYTE( 15 ); // framerate
|
||||
WRITE_BYTE( TE_EXPLFLAG_NONE );
|
||||
MESSAGE_END();
|
||||
}
|
||||
|
||||
// do damage
|
||||
if ( !( pev->spawnflags & SF_ENVEXPLOSION_NODAMAGE ) )
|
||||
{
|
||||
RadiusDamage ( pev, pev, m_iMagnitude, CLASS_NONE, DMG_BLAST );
|
||||
}
|
||||
|
||||
SetThink( Smoke );
|
||||
pev->nextthink = gpGlobals->time + 0.3;
|
||||
|
||||
// draw sparks
|
||||
if ( !( pev->spawnflags & SF_ENVEXPLOSION_NOSPARKS ) )
|
||||
{
|
||||
int sparkCount = RANDOM_LONG(0,3);
|
||||
|
||||
for ( int i = 0; i < sparkCount; i++ )
|
||||
{
|
||||
Create( "spark_shower", pev->origin, tr.vecPlaneNormal, NULL );
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void CEnvExplosion::Smoke( void )
|
||||
{
|
||||
if ( !( pev->spawnflags & SF_ENVEXPLOSION_NOSMOKE ) )
|
||||
{
|
||||
MESSAGE_BEGIN( MSG_PAS, SVC_TEMPENTITY, pev->origin );
|
||||
WRITE_BYTE( TE_SMOKE );
|
||||
WRITE_COORD( pev->origin.x );
|
||||
WRITE_COORD( pev->origin.y );
|
||||
WRITE_COORD( pev->origin.z );
|
||||
WRITE_SHORT( g_sModelIndexSmoke );
|
||||
WRITE_BYTE( (BYTE)m_spriteScale ); // scale * 10
|
||||
WRITE_BYTE( 12 ); // framerate
|
||||
MESSAGE_END();
|
||||
}
|
||||
|
||||
if ( !(pev->spawnflags & SF_ENVEXPLOSION_REPEATABLE) )
|
||||
{
|
||||
UTIL_Remove( this );
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
// HACKHACK -- create one of these and fake a keyvalue to get the right explosion setup
|
||||
void ExplosionCreate( const Vector ¢er, const Vector &angles, edict_t *pOwner, int magnitude, BOOL doDamage )
|
||||
{
|
||||
KeyValueData kvd;
|
||||
char buf[128];
|
||||
|
||||
CBaseEntity *pExplosion = CBaseEntity::Create( "env_explosion", center, angles, pOwner );
|
||||
sprintf( buf, "%3d", magnitude );
|
||||
kvd.szKeyName = "iMagnitude";
|
||||
kvd.szValue = buf;
|
||||
pExplosion->KeyValue( &kvd );
|
||||
if ( !doDamage )
|
||||
pExplosion->pev->spawnflags |= SF_ENVEXPLOSION_NODAMAGE;
|
||||
|
||||
pExplosion->Spawn();
|
||||
pExplosion->Use( NULL, NULL, USE_TOGGLE, 0 );
|
||||
}
|
|
@ -0,0 +1,32 @@
|
|||
/***
|
||||
*
|
||||
* 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.
|
||||
*
|
||||
****/
|
||||
#ifndef EXPLODE_H
|
||||
#define EXPLODE_H
|
||||
|
||||
|
||||
#define SF_ENVEXPLOSION_NODAMAGE ( 1 << 0 ) // when set, ENV_EXPLOSION will not actually inflict damage
|
||||
#define SF_ENVEXPLOSION_REPEATABLE ( 1 << 1 ) // can this entity be refired?
|
||||
#define SF_ENVEXPLOSION_NOFIREBALL ( 1 << 2 ) // don't draw the fireball
|
||||
#define SF_ENVEXPLOSION_NOSMOKE ( 1 << 3 ) // don't draw the smoke
|
||||
#define SF_ENVEXPLOSION_NODECAL ( 1 << 4 ) // don't make a scorch mark
|
||||
#define SF_ENVEXPLOSION_NOSPARKS ( 1 << 5 ) // don't make a scorch mark
|
||||
|
||||
extern DLL_GLOBAL short g_sModelIndexFireball;
|
||||
extern DLL_GLOBAL short g_sModelIndexSmoke;
|
||||
|
||||
|
||||
extern void ExplosionCreate( const Vector ¢er, const Vector &angles, edict_t *pOwner, int magnitude, BOOL doDamage );
|
||||
|
||||
#endif //EXPLODE_H
|
|
@ -0,0 +1,74 @@
|
|||
/***
|
||||
*
|
||||
* 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.
|
||||
*
|
||||
****/
|
||||
#ifndef EXTDLL_H
|
||||
#define EXTDLL_H
|
||||
|
||||
|
||||
//
|
||||
// Global header file for extension DLLs
|
||||
//
|
||||
|
||||
// Allow "DEBUG" in addition to default "_DEBUG"
|
||||
#ifdef _DEBUG
|
||||
#define DEBUG 1
|
||||
#endif
|
||||
|
||||
// Silence certain warnings
|
||||
#pragma warning(disable : 4244) // int or float down-conversion
|
||||
#pragma warning(disable : 4305) // int or float data truncation
|
||||
#pragma warning(disable : 4201) // nameless struct/union
|
||||
#pragma warning(disable : 4514) // unreferenced inline function removed
|
||||
#pragma warning(disable : 4100) // unreferenced formal parameter
|
||||
|
||||
#include "windows.h"
|
||||
#include "basetypes.h"
|
||||
|
||||
#define FALSE 0
|
||||
#define TRUE 1
|
||||
|
||||
typedef unsigned long ULONG;
|
||||
|
||||
#include <limits.h>
|
||||
#include <stdarg.h>
|
||||
|
||||
#ifndef min
|
||||
#define min(a,b) (((a) < (b)) ? (a) : (b))
|
||||
#endif
|
||||
|
||||
#ifndef max
|
||||
#define max(a,b) (((a) > (b)) ? (a) : (b))
|
||||
#endif
|
||||
|
||||
#define _vsnprintf(a,b,c,d) vsnprintf(a,b,c,d)
|
||||
|
||||
// Misc C-runtime library headers
|
||||
#include "stdio.h"
|
||||
#include "stdlib.h"
|
||||
#include "math.h"
|
||||
|
||||
// Shared engine/DLL constants
|
||||
#include "const.h"
|
||||
|
||||
// Vector class
|
||||
#include "vector.h"
|
||||
|
||||
// Shared header describing protocol between engine and DLLs
|
||||
#include "entity_def.h"
|
||||
#include "svgame_api.h"
|
||||
|
||||
// Shared header between the client DLL and the game DLLs
|
||||
#include "cdll_dll.h"
|
||||
|
||||
#endif //EXTDLL_H
|
|
@ -0,0 +1,281 @@
|
|||
/***
|
||||
*
|
||||
* 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.
|
||||
*
|
||||
****/
|
||||
#include "extdll.h"
|
||||
#include "util.h"
|
||||
#include "cbase.h"
|
||||
#include "monsters.h"
|
||||
#include "schedule.h"
|
||||
#include "flyingmonster.h"
|
||||
|
||||
#define FLYING_AE_FLAP (8)
|
||||
#define FLYING_AE_FLAPSOUND (9)
|
||||
|
||||
|
||||
extern DLL_GLOBAL edict_t *g_pBodyQueueHead;
|
||||
|
||||
int CFlyingMonster :: CheckLocalMove ( const Vector &vecStart, const Vector &vecEnd, CBaseEntity *pTarget, float *pflDist )
|
||||
{
|
||||
// UNDONE: need to check more than the endpoint
|
||||
if (FBitSet(pev->flags, FL_SWIM) && (UTIL_PointContents(vecEnd) != CONTENTS_WATER))
|
||||
{
|
||||
// ALERT(at_aiconsole, "can't swim out of water\n");
|
||||
return FALSE;
|
||||
}
|
||||
|
||||
TraceResult tr;
|
||||
|
||||
UTIL_TraceHull( vecStart + Vector( 0, 0, 32 ), vecEnd + Vector( 0, 0, 32 ), dont_ignore_monsters, large_hull, edict(), &tr );
|
||||
|
||||
// ALERT( at_console, "%.0f %.0f %.0f : ", vecStart.x, vecStart.y, vecStart.z );
|
||||
// ALERT( at_console, "%.0f %.0f %.0f\n", vecEnd.x, vecEnd.y, vecEnd.z );
|
||||
|
||||
if (pflDist)
|
||||
{
|
||||
*pflDist = ( (tr.vecEndPos - Vector( 0, 0, 32 )) - vecStart ).Length();// get the distance.
|
||||
}
|
||||
|
||||
// ALERT( at_console, "check %d %d %f\n", tr.fStartSolid, tr.fAllSolid, tr.flFraction );
|
||||
if (tr.fStartSolid || tr.flFraction < 1.0)
|
||||
{
|
||||
if ( pTarget && pTarget->edict() == gpGlobals->trace_ent )
|
||||
return LOCALMOVE_VALID;
|
||||
return LOCALMOVE_INVALID;
|
||||
}
|
||||
|
||||
return LOCALMOVE_VALID;
|
||||
}
|
||||
|
||||
|
||||
BOOL CFlyingMonster :: FTriangulate ( const Vector &vecStart , const Vector &vecEnd, float flDist, CBaseEntity *pTargetEnt, Vector *pApex )
|
||||
{
|
||||
return CBaseMonster::FTriangulate( vecStart, vecEnd, flDist, pTargetEnt, pApex );
|
||||
}
|
||||
|
||||
|
||||
Activity CFlyingMonster :: GetStoppedActivity( void )
|
||||
{
|
||||
if ( pev->movetype != MOVETYPE_FLY ) // UNDONE: Ground idle here, IDLE may be something else
|
||||
return ACT_IDLE;
|
||||
|
||||
return ACT_HOVER;
|
||||
}
|
||||
|
||||
|
||||
void CFlyingMonster :: Stop( void )
|
||||
{
|
||||
Activity stopped = GetStoppedActivity();
|
||||
if ( m_IdealActivity != stopped )
|
||||
{
|
||||
m_flightSpeed = 0;
|
||||
m_IdealActivity = stopped;
|
||||
}
|
||||
pev->angles.z = 0;
|
||||
pev->angles.x = 0;
|
||||
m_vecTravel = g_vecZero;
|
||||
}
|
||||
|
||||
|
||||
float CFlyingMonster :: ChangeYaw( int speed )
|
||||
{
|
||||
if ( pev->movetype == MOVETYPE_FLY )
|
||||
{
|
||||
float diff = FlYawDiff();
|
||||
float target = 0;
|
||||
|
||||
if ( m_IdealActivity != GetStoppedActivity() )
|
||||
{
|
||||
if ( diff < -20 )
|
||||
target = 90;
|
||||
else if ( diff > 20 )
|
||||
target = -90;
|
||||
}
|
||||
pev->angles.z = UTIL_Approach( target, pev->angles.z, 220.0 * gpGlobals->frametime );
|
||||
}
|
||||
return CBaseMonster::ChangeYaw( speed );
|
||||
}
|
||||
|
||||
|
||||
void CFlyingMonster :: Killed( entvars_t *pevAttacker, int iGib )
|
||||
{
|
||||
pev->movetype = MOVETYPE_STEP;
|
||||
ClearBits( pev->flags, FL_ONGROUND );
|
||||
pev->angles.z = 0;
|
||||
pev->angles.x = 0;
|
||||
CBaseMonster::Killed( pevAttacker, iGib );
|
||||
}
|
||||
|
||||
|
||||
void CFlyingMonster :: HandleAnimEvent( MonsterEvent_t *pEvent )
|
||||
{
|
||||
switch( pEvent->event )
|
||||
{
|
||||
case FLYING_AE_FLAP:
|
||||
m_flightSpeed = 400;
|
||||
break;
|
||||
|
||||
case FLYING_AE_FLAPSOUND:
|
||||
if ( m_pFlapSound )
|
||||
EMIT_SOUND( edict(), CHAN_BODY, m_pFlapSound, 1, ATTN_NORM );
|
||||
break;
|
||||
|
||||
default:
|
||||
CBaseMonster::HandleAnimEvent( pEvent );
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
void CFlyingMonster :: Move( float flInterval )
|
||||
{
|
||||
if ( pev->movetype == MOVETYPE_FLY )
|
||||
m_flGroundSpeed = m_flightSpeed;
|
||||
CBaseMonster::Move( flInterval );
|
||||
}
|
||||
|
||||
|
||||
BOOL CFlyingMonster:: ShouldAdvanceRoute( float flWaypointDist )
|
||||
{
|
||||
// Get true 3D distance to the goal so we actually reach the correct height
|
||||
if ( m_Route[ m_iRouteIndex ].iType & bits_MF_IS_GOAL )
|
||||
flWaypointDist = ( m_Route[ m_iRouteIndex ].vecLocation - pev->origin ).Length();
|
||||
|
||||
if ( flWaypointDist <= 64 + (m_flGroundSpeed * gpGlobals->frametime) )
|
||||
return TRUE;
|
||||
|
||||
return FALSE;
|
||||
}
|
||||
|
||||
|
||||
void CFlyingMonster::MoveExecute( CBaseEntity *pTargetEnt, const Vector &vecDir, float flInterval )
|
||||
{
|
||||
if ( pev->movetype == MOVETYPE_FLY )
|
||||
{
|
||||
if ( gpGlobals->time - m_stopTime > 1.0 )
|
||||
{
|
||||
if ( m_IdealActivity != m_movementActivity )
|
||||
{
|
||||
m_IdealActivity = m_movementActivity;
|
||||
m_flGroundSpeed = m_flightSpeed = 200;
|
||||
}
|
||||
}
|
||||
Vector vecMove = pev->origin + (( vecDir + (m_vecTravel * m_momentum) ).Normalize() * (m_flGroundSpeed * flInterval));
|
||||
|
||||
if ( m_IdealActivity != m_movementActivity )
|
||||
{
|
||||
m_flightSpeed = UTIL_Approach( 100, m_flightSpeed, 75 * gpGlobals->frametime );
|
||||
if ( m_flightSpeed < 100 )
|
||||
m_stopTime = gpGlobals->time;
|
||||
}
|
||||
else
|
||||
m_flightSpeed = UTIL_Approach( 20, m_flightSpeed, 300 * gpGlobals->frametime );
|
||||
|
||||
if ( CheckLocalMove ( pev->origin, vecMove, pTargetEnt, NULL ) )
|
||||
{
|
||||
m_vecTravel = (vecMove - pev->origin);
|
||||
m_vecTravel = m_vecTravel.Normalize();
|
||||
UTIL_MoveToOrigin(ENT(pev), vecMove, (m_flGroundSpeed * flInterval), MOVE_STRAFE);
|
||||
}
|
||||
else
|
||||
{
|
||||
m_IdealActivity = GetStoppedActivity();
|
||||
m_stopTime = gpGlobals->time;
|
||||
m_vecTravel = g_vecZero;
|
||||
}
|
||||
}
|
||||
else
|
||||
CBaseMonster::MoveExecute( pTargetEnt, vecDir, flInterval );
|
||||
}
|
||||
|
||||
|
||||
float CFlyingMonster::CeilingZ( const Vector &position )
|
||||
{
|
||||
TraceResult tr;
|
||||
|
||||
Vector minUp = position;
|
||||
Vector maxUp = position;
|
||||
maxUp.z += 4096.0;
|
||||
|
||||
UTIL_TraceLine(position, maxUp, ignore_monsters, NULL, &tr);
|
||||
if (tr.flFraction != 1.0)
|
||||
maxUp.z = tr.vecEndPos.z;
|
||||
|
||||
if ((pev->flags) & FL_SWIM)
|
||||
{
|
||||
return UTIL_WaterLevel( position, minUp.z, maxUp.z );
|
||||
}
|
||||
return maxUp.z;
|
||||
}
|
||||
|
||||
BOOL CFlyingMonster::ProbeZ( const Vector &position, const Vector &probe, float *pFraction)
|
||||
{
|
||||
int conPosition = UTIL_PointContents(position);
|
||||
if ( (((pev->flags) & FL_SWIM) == FL_SWIM) ^ (conPosition == CONTENTS_WATER))
|
||||
{
|
||||
// SWIMING & !WATER
|
||||
// or FLYING & WATER
|
||||
//
|
||||
*pFraction = 0.0;
|
||||
return TRUE; // We hit a water boundary because we are where we don't belong.
|
||||
}
|
||||
int conProbe = UTIL_PointContents(probe);
|
||||
if (conProbe == conPosition)
|
||||
{
|
||||
// The probe is either entirely inside the water (for fish) or entirely
|
||||
// outside the water (for birds).
|
||||
//
|
||||
*pFraction = 1.0;
|
||||
return FALSE;
|
||||
}
|
||||
|
||||
Vector ProbeUnit = (probe-position).Normalize();
|
||||
float ProbeLength = (probe-position).Length();
|
||||
float maxProbeLength = ProbeLength;
|
||||
float minProbeLength = 0;
|
||||
|
||||
float diff = maxProbeLength - minProbeLength;
|
||||
while (diff > 1.0)
|
||||
{
|
||||
float midProbeLength = minProbeLength + diff/2.0;
|
||||
Vector midProbeVec = midProbeLength * ProbeUnit;
|
||||
if (UTIL_PointContents(position+midProbeVec) == conPosition)
|
||||
{
|
||||
minProbeLength = midProbeLength;
|
||||
}
|
||||
else
|
||||
{
|
||||
maxProbeLength = midProbeLength;
|
||||
}
|
||||
diff = maxProbeLength - minProbeLength;
|
||||
}
|
||||
*pFraction = minProbeLength/ProbeLength;
|
||||
|
||||
return TRUE;
|
||||
}
|
||||
|
||||
float CFlyingMonster::FloorZ( const Vector &position )
|
||||
{
|
||||
TraceResult tr;
|
||||
|
||||
Vector down = position;
|
||||
down.z -= 2048;
|
||||
|
||||
UTIL_TraceLine( position, down, ignore_monsters, NULL, &tr );
|
||||
|
||||
if ( tr.flFraction != 1.0 )
|
||||
return tr.vecEndPos.z;
|
||||
|
||||
return down.z;
|
||||
}
|
||||
|
|
@ -0,0 +1,53 @@
|
|||
/***
|
||||
*
|
||||
* 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.
|
||||
*
|
||||
****/
|
||||
// Base class for flying monsters. This overrides the movement test & execution code from CBaseMonster
|
||||
|
||||
#ifndef FLYINGMONSTER_H
|
||||
#define FLYINGMONSTER_H
|
||||
|
||||
class CFlyingMonster : public CBaseMonster
|
||||
{
|
||||
public:
|
||||
int CheckLocalMove ( const Vector &vecStart, const Vector &vecEnd, CBaseEntity *pTarget, float *pflDist );// check validity of a straight move through space
|
||||
BOOL FTriangulate ( const Vector &vecStart , const Vector &vecEnd, float flDist, CBaseEntity *pTargetEnt, Vector *pApex );
|
||||
Activity GetStoppedActivity( void );
|
||||
void Killed( entvars_t *pevAttacker, int iGib );
|
||||
void Stop( void );
|
||||
float ChangeYaw( int speed );
|
||||
void HandleAnimEvent( MonsterEvent_t *pEvent );
|
||||
void MoveExecute( CBaseEntity *pTargetEnt, const Vector &vecDir, float flInterval );
|
||||
void Move( float flInterval = 0.1 );
|
||||
BOOL ShouldAdvanceRoute( float flWaypointDist );
|
||||
|
||||
inline void SetFlyingMomentum( float momentum ) { m_momentum = momentum; }
|
||||
inline void SetFlyingFlapSound( const char *pFlapSound ) { m_pFlapSound = pFlapSound; }
|
||||
inline void SetFlyingSpeed( float speed ) { m_flightSpeed = speed; }
|
||||
float CeilingZ( const Vector &position );
|
||||
float FloorZ( const Vector &position );
|
||||
BOOL ProbeZ( const Vector &position, const Vector &probe, float *pFraction );
|
||||
|
||||
|
||||
// UNDONE: Save/restore this stuff!!!
|
||||
protected:
|
||||
Vector m_vecTravel; // Current direction
|
||||
float m_flightSpeed; // Current flight speed (decays when not flapping or gliding)
|
||||
float m_stopTime; // Last time we stopped (to avoid switching states too soon)
|
||||
float m_momentum; // Weight for desired vs. momentum velocity
|
||||
const char *m_pFlapSound;
|
||||
};
|
||||
|
||||
|
||||
#endif //FLYINGMONSTER_H
|
||||
|
|
@ -0,0 +1,997 @@
|
|||
/***
|
||||
*
|
||||
* 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.
|
||||
*
|
||||
****/
|
||||
/*
|
||||
|
||||
===== bmodels.cpp ========================================================
|
||||
|
||||
spawn, think, and use functions for entities that use brush models
|
||||
|
||||
*/
|
||||
#include "extdll.h"
|
||||
#include "util.h"
|
||||
#include "cbase.h"
|
||||
#include "saverestore.h"
|
||||
#include "func_break.h"
|
||||
#include "decals.h"
|
||||
#include "explode.h"
|
||||
|
||||
extern DLL_GLOBAL Vector g_vecAttackDir;
|
||||
|
||||
// =================== FUNC_Breakable ==============================================
|
||||
|
||||
// Just add more items to the bottom of this array and they will automagically be supported
|
||||
// This is done instead of just a classname in the FGD so we can control which entities can
|
||||
// be spawned, and still remain fairly flexible
|
||||
const char *CBreakable::pSpawnObjects[] =
|
||||
{
|
||||
NULL, // 0
|
||||
"item_battery", // 1
|
||||
"item_healthkit", // 2
|
||||
"weapon_9mmhandgun",// 3
|
||||
"ammo_9mmclip", // 4
|
||||
"weapon_9mmAR", // 5
|
||||
"ammo_9mmAR", // 6
|
||||
"ammo_ARgrenades", // 7
|
||||
"weapon_shotgun", // 8
|
||||
"ammo_buckshot", // 9
|
||||
"weapon_crossbow", // 10
|
||||
"ammo_crossbow", // 11
|
||||
"weapon_357", // 12
|
||||
"ammo_357", // 13
|
||||
"weapon_rpg", // 14
|
||||
"ammo_rpgclip", // 15
|
||||
"ammo_gaussclip", // 16
|
||||
"weapon_handgrenade",// 17
|
||||
"weapon_tripmine", // 18
|
||||
"weapon_satchel", // 19
|
||||
"weapon_snark", // 20
|
||||
"weapon_hornetgun", // 21
|
||||
};
|
||||
|
||||
void CBreakable::KeyValue( KeyValueData* pkvd )
|
||||
{
|
||||
// UNDONE_WC: explicitly ignoring these fields, but they shouldn't be in the map file!
|
||||
if (FStrEq(pkvd->szKeyName, "explosion"))
|
||||
{
|
||||
if (!stricmp(pkvd->szValue, "directed"))
|
||||
m_Explosion = expDirected;
|
||||
else if (!stricmp(pkvd->szValue, "random"))
|
||||
m_Explosion = expRandom;
|
||||
else
|
||||
m_Explosion = expRandom;
|
||||
|
||||
pkvd->fHandled = TRUE;
|
||||
}
|
||||
else if (FStrEq(pkvd->szKeyName, "material"))
|
||||
{
|
||||
int i = atoi( pkvd->szValue);
|
||||
|
||||
// 0:glass, 1:metal, 2:flesh, 3:wood
|
||||
|
||||
if ((i < 0) || (i >= matLastMaterial))
|
||||
m_Material = matWood;
|
||||
else
|
||||
m_Material = (Materials)i;
|
||||
|
||||
pkvd->fHandled = TRUE;
|
||||
}
|
||||
else if (FStrEq(pkvd->szKeyName, "deadmodel"))
|
||||
{
|
||||
pkvd->fHandled = TRUE;
|
||||
}
|
||||
else if (FStrEq(pkvd->szKeyName, "shards"))
|
||||
{
|
||||
// m_iShards = atof(pkvd->szValue);
|
||||
pkvd->fHandled = TRUE;
|
||||
}
|
||||
else if (FStrEq(pkvd->szKeyName, "gibmodel") )
|
||||
{
|
||||
m_iszGibModel = ALLOC_STRING(pkvd->szValue);
|
||||
pkvd->fHandled = TRUE;
|
||||
}
|
||||
else if (FStrEq(pkvd->szKeyName, "spawnobject") )
|
||||
{
|
||||
int object = atoi( pkvd->szValue );
|
||||
if ( object > 0 && object < ARRAYSIZE(pSpawnObjects) )
|
||||
m_iszSpawnObject = MAKE_STRING( pSpawnObjects[object] );
|
||||
pkvd->fHandled = TRUE;
|
||||
}
|
||||
else if (FStrEq(pkvd->szKeyName, "explodemagnitude") )
|
||||
{
|
||||
ExplosionSetMagnitude( atoi( pkvd->szValue ) );
|
||||
pkvd->fHandled = TRUE;
|
||||
}
|
||||
else if (FStrEq(pkvd->szKeyName, "lip") )
|
||||
pkvd->fHandled = TRUE;
|
||||
else
|
||||
CBaseDelay::KeyValue( pkvd );
|
||||
}
|
||||
|
||||
|
||||
//
|
||||
// func_breakable - bmodel that breaks into pieces after taking damage
|
||||
//
|
||||
LINK_ENTITY_TO_CLASS( func_breakable, CBreakable );
|
||||
TYPEDESCRIPTION CBreakable::m_SaveData[] =
|
||||
{
|
||||
DEFINE_FIELD( CBreakable, m_Material, FIELD_INTEGER ),
|
||||
DEFINE_FIELD( CBreakable, m_Explosion, FIELD_INTEGER ),
|
||||
|
||||
// Don't need to save/restore these because we precache after restore
|
||||
// DEFINE_FIELD( CBreakable, m_idShard, FIELD_INTEGER ),
|
||||
|
||||
DEFINE_FIELD( CBreakable, m_angle, FIELD_FLOAT ),
|
||||
DEFINE_FIELD( CBreakable, m_iszGibModel, FIELD_STRING ),
|
||||
DEFINE_FIELD( CBreakable, m_iszSpawnObject, FIELD_STRING ),
|
||||
|
||||
// Explosion magnitude is stored in pev->impulse
|
||||
};
|
||||
|
||||
IMPLEMENT_SAVERESTORE( CBreakable, CBaseEntity );
|
||||
|
||||
void CBreakable::Spawn( void )
|
||||
{
|
||||
Precache( );
|
||||
|
||||
if ( FBitSet( pev->spawnflags, SF_BREAK_TRIGGER_ONLY ) )
|
||||
pev->takedamage = DAMAGE_NO;
|
||||
else
|
||||
pev->takedamage = DAMAGE_YES;
|
||||
|
||||
pev->solid = SOLID_BSP;
|
||||
pev->movetype = MOVETYPE_PUSH;
|
||||
m_angle = pev->angles.y;
|
||||
pev->angles.y = 0;
|
||||
|
||||
SET_MODEL(ENT(pev), STRING(pev->model) );//set size and link into world.
|
||||
|
||||
SetTouch( BreakTouch );
|
||||
if ( FBitSet( pev->spawnflags, SF_BREAK_TRIGGER_ONLY ) ) // Only break on trigger
|
||||
SetTouch( NULL );
|
||||
|
||||
// Flag unbreakable glass as "worldbrush" so it will block ALL tracelines
|
||||
if ( !IsBreakable() && pev->rendermode != kRenderNormal )
|
||||
pev->flags |= FL_WORLDBRUSH;
|
||||
}
|
||||
|
||||
|
||||
const char *CBreakable::pSoundsWood[] =
|
||||
{
|
||||
"debris/wood1.wav",
|
||||
"debris/wood2.wav",
|
||||
"debris/wood3.wav",
|
||||
};
|
||||
|
||||
const char *CBreakable::pSoundsFlesh[] =
|
||||
{
|
||||
"debris/flesh1.wav",
|
||||
"debris/flesh2.wav",
|
||||
"debris/flesh3.wav",
|
||||
"debris/flesh5.wav",
|
||||
"debris/flesh6.wav",
|
||||
"debris/flesh7.wav",
|
||||
};
|
||||
|
||||
const char *CBreakable::pSoundsMetal[] =
|
||||
{
|
||||
"debris/metal1.wav",
|
||||
"debris/metal2.wav",
|
||||
"debris/metal3.wav",
|
||||
};
|
||||
|
||||
const char *CBreakable::pSoundsConcrete[] =
|
||||
{
|
||||
"debris/concrete1.wav",
|
||||
"debris/concrete2.wav",
|
||||
"debris/concrete3.wav",
|
||||
};
|
||||
|
||||
|
||||
const char *CBreakable::pSoundsGlass[] =
|
||||
{
|
||||
"debris/glass1.wav",
|
||||
"debris/glass2.wav",
|
||||
"debris/glass3.wav",
|
||||
};
|
||||
|
||||
const char **CBreakable::MaterialSoundList( Materials precacheMaterial, int &soundCount )
|
||||
{
|
||||
const char **pSoundList = NULL;
|
||||
|
||||
switch ( precacheMaterial )
|
||||
{
|
||||
case matWood:
|
||||
pSoundList = pSoundsWood;
|
||||
soundCount = ARRAYSIZE(pSoundsWood);
|
||||
break;
|
||||
case matFlesh:
|
||||
pSoundList = pSoundsFlesh;
|
||||
soundCount = ARRAYSIZE(pSoundsFlesh);
|
||||
break;
|
||||
case matComputer:
|
||||
case matUnbreakableGlass:
|
||||
case matGlass:
|
||||
pSoundList = pSoundsGlass;
|
||||
soundCount = ARRAYSIZE(pSoundsGlass);
|
||||
break;
|
||||
|
||||
case matMetal:
|
||||
pSoundList = pSoundsMetal;
|
||||
soundCount = ARRAYSIZE(pSoundsMetal);
|
||||
break;
|
||||
|
||||
case matCinderBlock:
|
||||
case matRocks:
|
||||
pSoundList = pSoundsConcrete;
|
||||
soundCount = ARRAYSIZE(pSoundsConcrete);
|
||||
break;
|
||||
|
||||
|
||||
case matCeilingTile:
|
||||
case matNone:
|
||||
default:
|
||||
soundCount = 0;
|
||||
break;
|
||||
}
|
||||
|
||||
return pSoundList;
|
||||
}
|
||||
|
||||
void CBreakable::MaterialSoundPrecache( Materials precacheMaterial )
|
||||
{
|
||||
const char **pSoundList;
|
||||
int i, soundCount = 0;
|
||||
|
||||
pSoundList = MaterialSoundList( precacheMaterial, soundCount );
|
||||
|
||||
for ( i = 0; i < soundCount; i++ )
|
||||
{
|
||||
PRECACHE_SOUND( (char *)pSoundList[i] );
|
||||
}
|
||||
}
|
||||
|
||||
void CBreakable::MaterialSoundRandom( edict_t *pEdict, Materials soundMaterial, float volume )
|
||||
{
|
||||
const char **pSoundList;
|
||||
int soundCount = 0;
|
||||
|
||||
pSoundList = MaterialSoundList( soundMaterial, soundCount );
|
||||
|
||||
if ( soundCount )
|
||||
EMIT_SOUND( pEdict, CHAN_BODY, pSoundList[ RANDOM_LONG(0,soundCount-1) ], volume, 1.0 );
|
||||
}
|
||||
|
||||
|
||||
void CBreakable::Precache( void )
|
||||
{
|
||||
const char *pGibName;
|
||||
|
||||
switch (m_Material)
|
||||
{
|
||||
case matWood:
|
||||
pGibName = "models/woodgibs.mdl";
|
||||
|
||||
PRECACHE_SOUND("debris/bustcrate1.wav");
|
||||
PRECACHE_SOUND("debris/bustcrate2.wav");
|
||||
break;
|
||||
case matFlesh:
|
||||
pGibName = "models/fleshgibs.mdl";
|
||||
|
||||
PRECACHE_SOUND("debris/bustflesh1.wav");
|
||||
PRECACHE_SOUND("debris/bustflesh2.wav");
|
||||
break;
|
||||
case matComputer:
|
||||
PRECACHE_SOUND("buttons/spark5.wav");
|
||||
PRECACHE_SOUND("buttons/spark6.wav");
|
||||
pGibName = "models/computergibs.mdl";
|
||||
|
||||
PRECACHE_SOUND("debris/bustmetal1.wav");
|
||||
PRECACHE_SOUND("debris/bustmetal2.wav");
|
||||
break;
|
||||
|
||||
case matUnbreakableGlass:
|
||||
case matGlass:
|
||||
pGibName = "models/glassgibs.mdl";
|
||||
|
||||
PRECACHE_SOUND("debris/bustglass1.wav");
|
||||
PRECACHE_SOUND("debris/bustglass2.wav");
|
||||
break;
|
||||
case matMetal:
|
||||
pGibName = "models/metalplategibs.mdl";
|
||||
|
||||
PRECACHE_SOUND("debris/bustmetal1.wav");
|
||||
PRECACHE_SOUND("debris/bustmetal2.wav");
|
||||
break;
|
||||
case matCinderBlock:
|
||||
pGibName = "models/cindergibs.mdl";
|
||||
|
||||
PRECACHE_SOUND("debris/bustconcrete1.wav");
|
||||
PRECACHE_SOUND("debris/bustconcrete2.wav");
|
||||
break;
|
||||
case matRocks:
|
||||
pGibName = "models/rockgibs.mdl";
|
||||
|
||||
PRECACHE_SOUND("debris/bustconcrete1.wav");
|
||||
PRECACHE_SOUND("debris/bustconcrete2.wav");
|
||||
break;
|
||||
case matCeilingTile:
|
||||
pGibName = "models/ceilinggibs.mdl";
|
||||
|
||||
PRECACHE_SOUND ("debris/bustceiling.wav");
|
||||
break;
|
||||
}
|
||||
MaterialSoundPrecache( m_Material );
|
||||
if ( m_iszGibModel )
|
||||
pGibName = STRING(m_iszGibModel);
|
||||
|
||||
m_idShard = PRECACHE_MODEL( (char *)pGibName );
|
||||
|
||||
// Precache the spawn item's data
|
||||
if ( m_iszSpawnObject )
|
||||
UTIL_PrecacheOther( (char *)STRING( m_iszSpawnObject ) );
|
||||
}
|
||||
|
||||
// play shard sound when func_breakable takes damage.
|
||||
// the more damage, the louder the shard sound.
|
||||
|
||||
|
||||
void CBreakable::DamageSound( void )
|
||||
{
|
||||
int pitch;
|
||||
float fvol;
|
||||
char *rgpsz[6];
|
||||
int i;
|
||||
int material = m_Material;
|
||||
|
||||
// if (RANDOM_LONG(0,1))
|
||||
// return;
|
||||
|
||||
if (RANDOM_LONG(0,2))
|
||||
pitch = PITCH_NORM;
|
||||
else
|
||||
pitch = 95 + RANDOM_LONG(0,34);
|
||||
|
||||
fvol = RANDOM_FLOAT(0.75, 1.0);
|
||||
|
||||
if (material == matComputer && RANDOM_LONG(0,1))
|
||||
material = matMetal;
|
||||
|
||||
switch (material)
|
||||
{
|
||||
case matComputer:
|
||||
case matGlass:
|
||||
case matUnbreakableGlass:
|
||||
rgpsz[0] = "debris/glass1.wav";
|
||||
rgpsz[1] = "debris/glass2.wav";
|
||||
rgpsz[2] = "debris/glass3.wav";
|
||||
i = 3;
|
||||
break;
|
||||
|
||||
case matWood:
|
||||
rgpsz[0] = "debris/wood1.wav";
|
||||
rgpsz[1] = "debris/wood2.wav";
|
||||
rgpsz[2] = "debris/wood3.wav";
|
||||
i = 3;
|
||||
break;
|
||||
|
||||
case matMetal:
|
||||
rgpsz[0] = "debris/metal1.wav";
|
||||
rgpsz[1] = "debris/metal3.wav";
|
||||
rgpsz[2] = "debris/metal2.wav";
|
||||
i = 2;
|
||||
break;
|
||||
|
||||
case matFlesh:
|
||||
rgpsz[0] = "debris/flesh1.wav";
|
||||
rgpsz[1] = "debris/flesh2.wav";
|
||||
rgpsz[2] = "debris/flesh3.wav";
|
||||
rgpsz[3] = "debris/flesh5.wav";
|
||||
rgpsz[4] = "debris/flesh6.wav";
|
||||
rgpsz[5] = "debris/flesh7.wav";
|
||||
i = 6;
|
||||
break;
|
||||
|
||||
case matRocks:
|
||||
case matCinderBlock:
|
||||
rgpsz[0] = "debris/concrete1.wav";
|
||||
rgpsz[1] = "debris/concrete2.wav";
|
||||
rgpsz[2] = "debris/concrete3.wav";
|
||||
i = 3;
|
||||
break;
|
||||
|
||||
case matCeilingTile:
|
||||
// UNDONE: no ceiling tile shard sound yet
|
||||
i = 0;
|
||||
break;
|
||||
}
|
||||
|
||||
if (i)
|
||||
EMIT_SOUND_DYN(ENT(pev), CHAN_VOICE, rgpsz[RANDOM_LONG(0,i-1)], fvol, ATTN_NORM, 0, pitch);
|
||||
}
|
||||
|
||||
void CBreakable::BreakTouch( CBaseEntity *pOther )
|
||||
{
|
||||
float flDamage;
|
||||
entvars_t* pevToucher = pOther->pev;
|
||||
|
||||
// only players can break these right now
|
||||
if ( !pOther->IsPlayer() || !IsBreakable() )
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
if ( FBitSet ( pev->spawnflags, SF_BREAK_TOUCH ) )
|
||||
{// can be broken when run into
|
||||
flDamage = pevToucher->velocity.Length() * 0.01;
|
||||
|
||||
if (flDamage >= pev->health)
|
||||
{
|
||||
SetTouch( NULL );
|
||||
TakeDamage(pevToucher, pevToucher, flDamage, DMG_CRUSH);
|
||||
|
||||
// do a little damage to player if we broke glass or computer
|
||||
pOther->TakeDamage( pev, pev, flDamage/4, DMG_SLASH );
|
||||
}
|
||||
}
|
||||
|
||||
if ( FBitSet ( pev->spawnflags, SF_BREAK_PRESSURE ) && pevToucher->absmin.z >= pev->maxs.z - 2 )
|
||||
{// can be broken when stood upon
|
||||
|
||||
// play creaking sound here.
|
||||
DamageSound();
|
||||
|
||||
SetThink ( Die );
|
||||
SetTouch( NULL );
|
||||
|
||||
if ( m_flDelay == 0 )
|
||||
{// !!!BUGBUG - why doesn't zero delay work?
|
||||
m_flDelay = 0.1;
|
||||
}
|
||||
|
||||
pev->nextthink = pev->ltime + m_flDelay;
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
|
||||
//
|
||||
// Smash the our breakable object
|
||||
//
|
||||
|
||||
// Break when triggered
|
||||
void CBreakable::Use( CBaseEntity *pActivator, CBaseEntity *pCaller, USE_TYPE useType, float value )
|
||||
{
|
||||
if ( IsBreakable() )
|
||||
{
|
||||
pev->angles.y = m_angle;
|
||||
UTIL_MakeVectors(pev->angles);
|
||||
g_vecAttackDir = gpGlobals->v_forward;
|
||||
|
||||
Die();
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
void CBreakable::TraceAttack( entvars_t *pevAttacker, float flDamage, Vector vecDir, TraceResult *ptr, int bitsDamageType )
|
||||
{
|
||||
// random spark if this is a 'computer' object
|
||||
if (RANDOM_LONG(0,1) )
|
||||
{
|
||||
switch( m_Material )
|
||||
{
|
||||
case matComputer:
|
||||
{
|
||||
UTIL_Sparks( ptr->vecEndPos );
|
||||
|
||||
float flVolume = RANDOM_FLOAT ( 0.7 , 1.0 );//random volume range
|
||||
switch ( RANDOM_LONG(0,1) )
|
||||
{
|
||||
case 0: EMIT_SOUND(ENT(pev), CHAN_VOICE, "buttons/spark5.wav", flVolume, ATTN_NORM); break;
|
||||
case 1: EMIT_SOUND(ENT(pev), CHAN_VOICE, "buttons/spark6.wav", flVolume, ATTN_NORM); break;
|
||||
}
|
||||
}
|
||||
break;
|
||||
|
||||
case matUnbreakableGlass:
|
||||
UTIL_Ricochet( ptr->vecEndPos, RANDOM_FLOAT(0.5,1.5) );
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
CBaseDelay::TraceAttack( pevAttacker, flDamage, vecDir, ptr, bitsDamageType );
|
||||
}
|
||||
|
||||
//=========================================================
|
||||
// Special takedamage for func_breakable. Allows us to make
|
||||
// exceptions that are breakable-specific
|
||||
// bitsDamageType indicates the type of damage sustained ie: DMG_CRUSH
|
||||
//=========================================================
|
||||
int CBreakable :: TakeDamage( entvars_t* pevInflictor, entvars_t* pevAttacker, float flDamage, int bitsDamageType )
|
||||
{
|
||||
Vector vecTemp;
|
||||
|
||||
// if Attacker == Inflictor, the attack was a melee or other instant-hit attack.
|
||||
// (that is, no actual entity projectile was involved in the attack so use the shooter's origin).
|
||||
if ( pevAttacker == pevInflictor )
|
||||
{
|
||||
vecTemp = pevInflictor->origin - ( pev->absmin + ( pev->size * 0.5 ) );
|
||||
|
||||
// if a client hit the breakable with a crowbar, and breakable is crowbar-sensitive, break it now.
|
||||
if ( FBitSet ( pevAttacker->flags, FL_CLIENT ) &&
|
||||
FBitSet ( pev->spawnflags, SF_BREAK_CROWBAR ) && (bitsDamageType & DMG_CLUB))
|
||||
flDamage = pev->health;
|
||||
}
|
||||
else
|
||||
// an actual missile was involved.
|
||||
{
|
||||
vecTemp = pevInflictor->origin - ( pev->absmin + ( pev->size * 0.5 ) );
|
||||
}
|
||||
|
||||
if (!IsBreakable())
|
||||
return 0;
|
||||
|
||||
// Breakables take double damage from the crowbar
|
||||
if ( bitsDamageType & DMG_CLUB )
|
||||
flDamage *= 2;
|
||||
|
||||
// Boxes / glass / etc. don't take much poison damage, just the impact of the dart - consider that 10%
|
||||
if ( bitsDamageType & DMG_POISON )
|
||||
flDamage *= 0.1;
|
||||
|
||||
// this global is still used for glass and other non-monster killables, along with decals.
|
||||
g_vecAttackDir = vecTemp.Normalize();
|
||||
|
||||
// do the damage
|
||||
pev->health -= flDamage;
|
||||
if (pev->health <= 0)
|
||||
{
|
||||
Killed( pevAttacker, GIB_NORMAL );
|
||||
Die();
|
||||
return 0;
|
||||
}
|
||||
|
||||
// Make a shard noise each time func breakable is hit.
|
||||
// Don't play shard noise if cbreakable actually died.
|
||||
|
||||
DamageSound();
|
||||
|
||||
return 1;
|
||||
}
|
||||
|
||||
|
||||
void CBreakable::Die( void )
|
||||
{
|
||||
Vector vecSpot;// shard origin
|
||||
Vector vecVelocity;// shard velocity
|
||||
CBaseEntity *pEntity = NULL;
|
||||
char cFlag = 0;
|
||||
int pitch;
|
||||
float fvol;
|
||||
|
||||
pitch = 95 + RANDOM_LONG(0,29);
|
||||
|
||||
if (pitch > 97 && pitch < 103)
|
||||
pitch = 100;
|
||||
|
||||
// The more negative pev->health, the louder
|
||||
// the sound should be.
|
||||
|
||||
fvol = RANDOM_FLOAT(0.85, 1.0) + (abs(pev->health) / 100.0);
|
||||
|
||||
if (fvol > 1.0)
|
||||
fvol = 1.0;
|
||||
|
||||
|
||||
switch (m_Material)
|
||||
{
|
||||
case matGlass:
|
||||
switch ( RANDOM_LONG(0,1) )
|
||||
{
|
||||
case 0: EMIT_SOUND_DYN(ENT(pev), CHAN_VOICE, "debris/bustglass1.wav", fvol, ATTN_NORM, 0, pitch);
|
||||
break;
|
||||
case 1: EMIT_SOUND_DYN(ENT(pev), CHAN_VOICE, "debris/bustglass2.wav", fvol, ATTN_NORM, 0, pitch);
|
||||
break;
|
||||
}
|
||||
cFlag = BREAK_GLASS;
|
||||
break;
|
||||
|
||||
case matWood:
|
||||
switch ( RANDOM_LONG(0,1) )
|
||||
{
|
||||
case 0: EMIT_SOUND_DYN(ENT(pev), CHAN_VOICE, "debris/bustcrate1.wav", fvol, ATTN_NORM, 0, pitch);
|
||||
break;
|
||||
case 1: EMIT_SOUND_DYN(ENT(pev), CHAN_VOICE, "debris/bustcrate2.wav", fvol, ATTN_NORM, 0, pitch);
|
||||
break;
|
||||
}
|
||||
cFlag = BREAK_WOOD;
|
||||
break;
|
||||
|
||||
case matComputer:
|
||||
case matMetal:
|
||||
switch ( RANDOM_LONG(0,1) )
|
||||
{
|
||||
case 0: EMIT_SOUND_DYN(ENT(pev), CHAN_VOICE, "debris/bustmetal1.wav", fvol, ATTN_NORM, 0, pitch);
|
||||
break;
|
||||
case 1: EMIT_SOUND_DYN(ENT(pev), CHAN_VOICE, "debris/bustmetal2.wav", fvol, ATTN_NORM, 0, pitch);
|
||||
break;
|
||||
}
|
||||
cFlag = BREAK_METAL;
|
||||
break;
|
||||
|
||||
case matFlesh:
|
||||
switch ( RANDOM_LONG(0,1) )
|
||||
{
|
||||
case 0: EMIT_SOUND_DYN(ENT(pev), CHAN_VOICE, "debris/bustflesh1.wav", fvol, ATTN_NORM, 0, pitch);
|
||||
break;
|
||||
case 1: EMIT_SOUND_DYN(ENT(pev), CHAN_VOICE, "debris/bustflesh2.wav", fvol, ATTN_NORM, 0, pitch);
|
||||
break;
|
||||
}
|
||||
cFlag = BREAK_FLESH;
|
||||
break;
|
||||
|
||||
case matRocks:
|
||||
case matCinderBlock:
|
||||
switch ( RANDOM_LONG(0,1) )
|
||||
{
|
||||
case 0: EMIT_SOUND_DYN(ENT(pev), CHAN_VOICE, "debris/bustconcrete1.wav", fvol, ATTN_NORM, 0, pitch);
|
||||
break;
|
||||
case 1: EMIT_SOUND_DYN(ENT(pev), CHAN_VOICE, "debris/bustconcrete2.wav", fvol, ATTN_NORM, 0, pitch);
|
||||
break;
|
||||
}
|
||||
cFlag = BREAK_CONCRETE;
|
||||
break;
|
||||
|
||||
case matCeilingTile:
|
||||
EMIT_SOUND_DYN(ENT(pev), CHAN_VOICE, "debris/bustceiling.wav", fvol, ATTN_NORM, 0, pitch);
|
||||
break;
|
||||
}
|
||||
|
||||
|
||||
if (m_Explosion == expDirected)
|
||||
vecVelocity = g_vecAttackDir * 200;
|
||||
else
|
||||
{
|
||||
vecVelocity.x = 0;
|
||||
vecVelocity.y = 0;
|
||||
vecVelocity.z = 0;
|
||||
}
|
||||
|
||||
vecSpot = pev->origin + (pev->mins + pev->maxs) * 0.5;
|
||||
MESSAGE_BEGIN( MSG_PVS, SVC_TEMPENTITY, vecSpot );
|
||||
WRITE_BYTE( TE_BREAKMODEL);
|
||||
|
||||
// position
|
||||
WRITE_COORD( vecSpot.x );
|
||||
WRITE_COORD( vecSpot.y );
|
||||
WRITE_COORD( vecSpot.z );
|
||||
|
||||
// size
|
||||
WRITE_COORD( pev->size.x);
|
||||
WRITE_COORD( pev->size.y);
|
||||
WRITE_COORD( pev->size.z);
|
||||
|
||||
// velocity
|
||||
WRITE_COORD( vecVelocity.x );
|
||||
WRITE_COORD( vecVelocity.y );
|
||||
WRITE_COORD( vecVelocity.z );
|
||||
|
||||
// randomization
|
||||
WRITE_BYTE( 10 );
|
||||
|
||||
// Model
|
||||
WRITE_SHORT( m_idShard ); //model id#
|
||||
|
||||
// # of shards
|
||||
WRITE_BYTE( 0 ); // let client decide
|
||||
|
||||
// duration
|
||||
WRITE_BYTE( 25 );// 2.5 seconds
|
||||
|
||||
// flags
|
||||
WRITE_BYTE( cFlag );
|
||||
MESSAGE_END();
|
||||
|
||||
float size = pev->size.x;
|
||||
if ( size < pev->size.y )
|
||||
size = pev->size.y;
|
||||
if ( size < pev->size.z )
|
||||
size = pev->size.z;
|
||||
|
||||
// !!! HACK This should work!
|
||||
// Build a box above the entity that looks like an 8 pixel high sheet
|
||||
Vector mins = pev->absmin;
|
||||
Vector maxs = pev->absmax;
|
||||
mins.z = pev->absmax.z;
|
||||
maxs.z += 8;
|
||||
|
||||
// BUGBUG -- can only find 256 entities on a breakable -- should be enough
|
||||
CBaseEntity *pList[256];
|
||||
int count = UTIL_EntitiesInBox( pList, 256, mins, maxs, FL_ONGROUND );
|
||||
if ( count )
|
||||
{
|
||||
for ( int i = 0; i < count; i++ )
|
||||
{
|
||||
ClearBits( pList[i]->pev->flags, FL_ONGROUND );
|
||||
pList[i]->pev->groundentity = NULL;
|
||||
}
|
||||
}
|
||||
|
||||
// Don't fire something that could fire myself
|
||||
pev->targetname = 0;
|
||||
|
||||
pev->solid = SOLID_NOT;
|
||||
// Fire targets on break
|
||||
SUB_UseTargets( NULL, USE_TOGGLE, 0 );
|
||||
|
||||
SetThink( SUB_Remove );
|
||||
pev->nextthink = pev->ltime + 0.1;
|
||||
if ( m_iszSpawnObject )
|
||||
CBaseEntity::Create( (char *)STRING(m_iszSpawnObject), VecBModelOrigin(pev), pev->angles, edict() );
|
||||
|
||||
|
||||
if ( Explodable() )
|
||||
{
|
||||
ExplosionCreate( Center(), pev->angles, edict(), ExplosionMagnitude(), TRUE );
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
BOOL CBreakable :: IsBreakable( void )
|
||||
{
|
||||
return m_Material != matUnbreakableGlass;
|
||||
}
|
||||
|
||||
|
||||
int CBreakable :: DamageDecal( int bitsDamageType )
|
||||
{
|
||||
if ( m_Material == matGlass )
|
||||
return DECAL_GLASSBREAK1 + RANDOM_LONG(0,2);
|
||||
|
||||
if ( m_Material == matUnbreakableGlass )
|
||||
return DECAL_BPROOF1;
|
||||
|
||||
return CBaseEntity::DamageDecal( bitsDamageType );
|
||||
}
|
||||
|
||||
|
||||
class CPushable : public CBreakable
|
||||
{
|
||||
public:
|
||||
void Spawn ( void );
|
||||
void Precache( void );
|
||||
void Touch ( CBaseEntity *pOther );
|
||||
void Move( CBaseEntity *pMover, int push );
|
||||
void KeyValue( KeyValueData *pkvd );
|
||||
void Use( CBaseEntity *pActivator, CBaseEntity *pCaller, USE_TYPE useType, float value );
|
||||
void EXPORT StopSound( void );
|
||||
// virtual void SetActivator( CBaseEntity *pActivator ) { m_pPusher = pActivator; }
|
||||
|
||||
virtual int ObjectCaps( void ) { return (CBaseEntity :: ObjectCaps() & ~FCAP_ACROSS_TRANSITION) | FCAP_CONTINUOUS_USE; }
|
||||
virtual int Save( CSave &save );
|
||||
virtual int Restore( CRestore &restore );
|
||||
|
||||
inline float MaxSpeed( void ) { return m_maxSpeed; }
|
||||
|
||||
// breakables use an overridden takedamage
|
||||
virtual int TakeDamage( entvars_t* pevInflictor, entvars_t* pevAttacker, float flDamage, int bitsDamageType );
|
||||
|
||||
static TYPEDESCRIPTION m_SaveData[];
|
||||
|
||||
static char *m_soundNames[3];
|
||||
int m_lastSound; // no need to save/restore, just keeps the same sound from playing twice in a row
|
||||
float m_maxSpeed;
|
||||
float m_soundTime;
|
||||
};
|
||||
|
||||
TYPEDESCRIPTION CPushable::m_SaveData[] =
|
||||
{
|
||||
DEFINE_FIELD( CPushable, m_maxSpeed, FIELD_FLOAT ),
|
||||
DEFINE_FIELD( CPushable, m_soundTime, FIELD_TIME ),
|
||||
};
|
||||
|
||||
IMPLEMENT_SAVERESTORE( CPushable, CBreakable );
|
||||
|
||||
LINK_ENTITY_TO_CLASS( func_pushable, CPushable );
|
||||
|
||||
char *CPushable :: m_soundNames[3] = { "debris/pushbox1.wav", "debris/pushbox2.wav", "debris/pushbox3.wav" };
|
||||
|
||||
|
||||
void CPushable :: Spawn( void )
|
||||
{
|
||||
if ( pev->spawnflags & SF_PUSH_BREAKABLE )
|
||||
CBreakable::Spawn();
|
||||
else
|
||||
Precache( );
|
||||
|
||||
pev->movetype = MOVETYPE_PUSHSTEP;
|
||||
pev->solid = SOLID_BBOX;
|
||||
SET_MODEL( ENT(pev), STRING(pev->model) );
|
||||
|
||||
if ( pev->friction > 399 )
|
||||
pev->friction = 399;
|
||||
|
||||
m_maxSpeed = 400 - pev->friction;
|
||||
SetBits( pev->flags, FL_FLOAT );
|
||||
pev->friction = 0;
|
||||
|
||||
pev->origin.z += 1; // Pick up off of the floor
|
||||
UTIL_SetOrigin( pev, pev->origin );
|
||||
|
||||
// Multiply by area of the box's cross-section (assume 1000 units^3 standard volume)
|
||||
pev->skin = ( pev->skin * (pev->maxs.x - pev->mins.x) * (pev->maxs.y - pev->mins.y) ) * 0.0005;
|
||||
m_soundTime = 0;
|
||||
}
|
||||
|
||||
|
||||
void CPushable :: Precache( void )
|
||||
{
|
||||
for ( int i = 0; i < 3; i++ )
|
||||
PRECACHE_SOUND( m_soundNames[i] );
|
||||
|
||||
if ( pev->spawnflags & SF_PUSH_BREAKABLE )
|
||||
CBreakable::Precache( );
|
||||
}
|
||||
|
||||
|
||||
void CPushable :: KeyValue( KeyValueData *pkvd )
|
||||
{
|
||||
if ( FStrEq(pkvd->szKeyName, "size") )
|
||||
{
|
||||
int bbox = atoi(pkvd->szValue);
|
||||
pkvd->fHandled = TRUE;
|
||||
|
||||
switch( bbox )
|
||||
{
|
||||
case 0: // Point
|
||||
UTIL_SetSize(pev, Vector(-8, -8, -8), Vector(8, 8, 8));
|
||||
break;
|
||||
|
||||
case 2: // Big Hull!?!? !!!BUGBUG Figure out what this hull really is
|
||||
UTIL_SetSize(pev, VEC_DUCK_HULL_MIN*2, VEC_DUCK_HULL_MAX*2);
|
||||
break;
|
||||
|
||||
case 3: // Player duck
|
||||
UTIL_SetSize(pev, VEC_DUCK_HULL_MIN, VEC_DUCK_HULL_MAX);
|
||||
break;
|
||||
|
||||
default:
|
||||
case 1: // Player
|
||||
UTIL_SetSize(pev, VEC_HULL_MIN, VEC_HULL_MAX);
|
||||
break;
|
||||
}
|
||||
|
||||
}
|
||||
else if ( FStrEq(pkvd->szKeyName, "buoyancy") )
|
||||
{
|
||||
pev->skin = atof(pkvd->szValue);
|
||||
pkvd->fHandled = TRUE;
|
||||
}
|
||||
else
|
||||
CBreakable::KeyValue( pkvd );
|
||||
}
|
||||
|
||||
|
||||
// Pull the func_pushable
|
||||
void CPushable :: Use( CBaseEntity *pActivator, CBaseEntity *pCaller, USE_TYPE useType, float value )
|
||||
{
|
||||
if ( !pActivator || !pActivator->IsPlayer() )
|
||||
{
|
||||
if ( pev->spawnflags & SF_PUSH_BREAKABLE )
|
||||
this->CBreakable::Use( pActivator, pCaller, useType, value );
|
||||
return;
|
||||
}
|
||||
|
||||
if ( pActivator->pev->velocity != g_vecZero )
|
||||
Move( pActivator, 0 );
|
||||
}
|
||||
|
||||
|
||||
void CPushable :: Touch( CBaseEntity *pOther )
|
||||
{
|
||||
if ( FClassnameIs( pOther->pev, "worldspawn" ) )
|
||||
return;
|
||||
|
||||
Move( pOther, 1 );
|
||||
}
|
||||
|
||||
|
||||
void CPushable :: Move( CBaseEntity *pOther, int push )
|
||||
{
|
||||
entvars_t* pevToucher = pOther->pev;
|
||||
int playerTouch = 0;
|
||||
|
||||
// Is entity standing on this pushable ?
|
||||
if ( FBitSet(pevToucher->flags,FL_ONGROUND) && pevToucher->groundentity && VARS(pevToucher->groundentity) == pev )
|
||||
{
|
||||
// Only push if floating
|
||||
if ( pev->waterlevel > 0 )
|
||||
pev->velocity.z += pevToucher->velocity.z * 0.1;
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
|
||||
if ( pOther->IsPlayer() )
|
||||
{
|
||||
if ( push && !(pevToucher->button & (IN_FORWARD|IN_USE)) ) // Don't push unless the player is pushing forward and NOT use (pull)
|
||||
return;
|
||||
playerTouch = 1;
|
||||
}
|
||||
|
||||
float factor;
|
||||
|
||||
if ( playerTouch )
|
||||
{
|
||||
if ( !(pevToucher->flags & FL_ONGROUND) ) // Don't push away from jumping/falling players unless in water
|
||||
{
|
||||
if ( pev->waterlevel < 1 )
|
||||
return;
|
||||
else
|
||||
factor = 0.1;
|
||||
}
|
||||
else
|
||||
factor = 1;
|
||||
}
|
||||
else
|
||||
factor = 0.25;
|
||||
|
||||
pev->velocity.x += pevToucher->velocity.x * factor;
|
||||
pev->velocity.y += pevToucher->velocity.y * factor;
|
||||
|
||||
float length = sqrt( pev->velocity.x * pev->velocity.x + pev->velocity.y * pev->velocity.y );
|
||||
if ( push && (length > MaxSpeed()) )
|
||||
{
|
||||
pev->velocity.x = (pev->velocity.x * MaxSpeed() / length );
|
||||
pev->velocity.y = (pev->velocity.y * MaxSpeed() / length );
|
||||
}
|
||||
if ( playerTouch )
|
||||
{
|
||||
pevToucher->velocity.x = pev->velocity.x;
|
||||
pevToucher->velocity.y = pev->velocity.y;
|
||||
if ( (gpGlobals->time - m_soundTime) > 0.7 )
|
||||
{
|
||||
m_soundTime = gpGlobals->time;
|
||||
if ( length > 0 && FBitSet(pev->flags,FL_ONGROUND) )
|
||||
{
|
||||
m_lastSound = RANDOM_LONG(0,2);
|
||||
EMIT_SOUND(ENT(pev), CHAN_WEAPON, m_soundNames[m_lastSound], 0.5, ATTN_NORM);
|
||||
// SetThink( StopSound );
|
||||
// pev->nextthink = pev->ltime + 0.1;
|
||||
}
|
||||
else
|
||||
STOP_SOUND( ENT(pev), CHAN_WEAPON, m_soundNames[m_lastSound] );
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#if 0
|
||||
void CPushable::StopSound( void )
|
||||
{
|
||||
Vector dist = pev->oldorigin - pev->origin;
|
||||
if ( dist.Length() <= 0 )
|
||||
STOP_SOUND( ENT(pev), CHAN_WEAPON, m_soundNames[m_lastSound] );
|
||||
}
|
||||
#endif
|
||||
|
||||
int CPushable::TakeDamage( entvars_t* pevInflictor, entvars_t* pevAttacker, float flDamage, int bitsDamageType )
|
||||
{
|
||||
if ( pev->spawnflags & SF_PUSH_BREAKABLE )
|
||||
return CBreakable::TakeDamage( pevInflictor, pevAttacker, flDamage, bitsDamageType );
|
||||
|
||||
return 1;
|
||||
}
|
||||
|
|
@ -0,0 +1,74 @@
|
|||
/***
|
||||
*
|
||||
* 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.
|
||||
*
|
||||
****/
|
||||
#ifndef FUNC_BREAK_H
|
||||
#define FUNC_BREAK_H
|
||||
|
||||
typedef enum { expRandom, expDirected} Explosions;
|
||||
typedef enum { matGlass = 0, matWood, matMetal, matFlesh, matCinderBlock, matCeilingTile, matComputer, matUnbreakableGlass, matRocks, matNone, matLastMaterial } Materials;
|
||||
|
||||
#define NUM_SHARDS 6 // this many shards spawned when breakable objects break;
|
||||
|
||||
class CBreakable : public CBaseDelay
|
||||
{
|
||||
public:
|
||||
// basic functions
|
||||
void Spawn( void );
|
||||
void Precache( void );
|
||||
void KeyValue( KeyValueData* pkvd);
|
||||
void EXPORT BreakTouch( CBaseEntity *pOther );
|
||||
void Use( CBaseEntity *pActivator, CBaseEntity *pCaller, USE_TYPE useType, float value );
|
||||
void DamageSound( void );
|
||||
|
||||
// breakables use an overridden takedamage
|
||||
virtual int TakeDamage( entvars_t* pevInflictor, entvars_t* pevAttacker, float flDamage, int bitsDamageType );
|
||||
// To spark when hit
|
||||
void TraceAttack( entvars_t *pevAttacker, float flDamage, Vector vecDir, TraceResult *ptr, int bitsDamageType );
|
||||
|
||||
BOOL IsBreakable( void );
|
||||
BOOL SparkWhenHit( void );
|
||||
|
||||
int DamageDecal( int bitsDamageType );
|
||||
|
||||
void EXPORT Die( void );
|
||||
virtual int ObjectCaps( void ) { return (CBaseEntity :: ObjectCaps() & ~FCAP_ACROSS_TRANSITION); }
|
||||
virtual int Save( CSave &save );
|
||||
virtual int Restore( CRestore &restore );
|
||||
|
||||
inline BOOL Explodable( void ) { return ExplosionMagnitude() > 0; }
|
||||
inline int ExplosionMagnitude( void ) { return pev->impulse; }
|
||||
inline void ExplosionSetMagnitude( int magnitude ) { pev->impulse = magnitude; }
|
||||
|
||||
static void MaterialSoundPrecache( Materials precacheMaterial );
|
||||
static void MaterialSoundRandom( edict_t *pEdict, Materials soundMaterial, float volume );
|
||||
static const char **MaterialSoundList( Materials precacheMaterial, int &soundCount );
|
||||
|
||||
static const char *pSoundsWood[];
|
||||
static const char *pSoundsFlesh[];
|
||||
static const char *pSoundsGlass[];
|
||||
static const char *pSoundsMetal[];
|
||||
static const char *pSoundsConcrete[];
|
||||
static const char *pSpawnObjects[];
|
||||
|
||||
static TYPEDESCRIPTION m_SaveData[];
|
||||
|
||||
Materials m_Material;
|
||||
Explosions m_Explosion;
|
||||
int m_idShard;
|
||||
float m_angle;
|
||||
int m_iszGibModel;
|
||||
int m_iszSpawnObject;
|
||||
};
|
||||
|
||||
#endif // FUNC_BREAK_H
|
File diff suppressed because it is too large
Load Diff
|
@ -0,0 +1,490 @@
|
|||
/***
|
||||
*
|
||||
* 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 "game.h"
|
||||
|
||||
// special macros for handle skill data
|
||||
#define CVAR_REGISTER_SKILL( x ) (*g_engfuncs.pfnCVarRegister)( #x, "0", 0, "skill config cvar" )
|
||||
|
||||
cvar_t *displaysoundlist;
|
||||
|
||||
// multiplayer server rules
|
||||
cvar_t *teamplay;
|
||||
cvar_t *fraglimit;
|
||||
cvar_t *timelimit;
|
||||
cvar_t *friendlyfire;
|
||||
cvar_t *falldamage;
|
||||
cvar_t *weaponstay;
|
||||
cvar_t *forcerespawn;
|
||||
cvar_t *flashlight;
|
||||
cvar_t *aimcrosshair;
|
||||
cvar_t *decalfrequency;
|
||||
cvar_t *teamlist;
|
||||
cvar_t *timeleft;
|
||||
cvar_t *teamoverride;
|
||||
cvar_t *defaultteam;
|
||||
cvar_t *allowmonsters;
|
||||
cvar_t *mp_chattime;
|
||||
|
||||
cvar_t *g_psv_gravity = NULL;
|
||||
cvar_t *g_psv_aim = NULL;
|
||||
cvar_t *g_psv_maxspeed = NULL;
|
||||
cvar_t *g_footsteps = NULL;
|
||||
|
||||
// Register your console variables here
|
||||
// This gets called one time when the game is initialied
|
||||
void GameDLLInit( void )
|
||||
{
|
||||
// Register cvars here:
|
||||
ALERT( at_aiconsole, "GameDLLInit();\n" );
|
||||
|
||||
g_psv_maxspeed = CVAR_REGISTER( "sv_maxspeed", "320", 0, "maximum speed a player can accelerate to when on ground" );
|
||||
g_psv_gravity = CVAR_REGISTER( "sv_gravity", "800", 0, "world gravity" );
|
||||
g_psv_aim = CVAR_REGISTER( "sv_aim", "1", 0, "enable auto-aiming" );
|
||||
g_footsteps = CVAR_REGISTER( "mp_footsteps", "0", FCVAR_SERVERINFO, "can hear footsteps from other players" );
|
||||
|
||||
displaysoundlist = CVAR_REGISTER( "displaysoundlist", "0", 0, "show monster sounds that actually playing" );
|
||||
|
||||
teamplay = CVAR_REGISTER( "mp_teamplay", "0", FCVAR_SERVERINFO, "sets to 1 to indicate teamplay" );
|
||||
fraglimit = CVAR_REGISTER( "mp_fraglimit", "0", FCVAR_SERVERINFO, "limit of frags for current server" );
|
||||
timelimit = CVAR_REGISTER( "mp_timelimit", "0", FCVAR_SERVERINFO, "server timelimit" );
|
||||
CVAR_REGISTER( "mp_footsteps", "0", FCVAR_SERVERINFO, "can hear footsteps from other players" );
|
||||
|
||||
CVAR_REGISTER( "mp_fragsleft", "0", FCVAR_SERVERINFO, "counter that indicated how many frags remaining" );
|
||||
timeleft = CVAR_REGISTER( "mp_timeleft", "0" , FCVAR_SERVERINFO, "counter that indicated how many time remaining" );
|
||||
|
||||
friendlyfire = CVAR_REGISTER( "mp_friendlyfire", "0", FCVAR_SERVERINFO, "enables firedlyfire for teamplay" );
|
||||
falldamage = CVAR_REGISTER( "mp_falldamage", "0", FCVAR_SERVERINFO, "falldamage multiplier" );
|
||||
weaponstay = CVAR_REGISTER( "mp_weaponstay", "0", FCVAR_SERVERINFO, "weapon leave stays on ground" );
|
||||
forcerespawn = CVAR_REGISTER( "mp_forcerespawn", "1", FCVAR_SERVERINFO, "force client respawn after his death" );
|
||||
flashlight = CVAR_REGISTER( "mp_flashlight", "0", FCVAR_SERVERINFO, "attempt to use flashlight in multiplayer" );
|
||||
aimcrosshair = CVAR_REGISTER( "mp_autocrosshair", "1", FCVAR_SERVERINFO, "enables auto-aim in multiplayer" );
|
||||
decalfrequency = CVAR_REGISTER( "decalfrequency", "30", FCVAR_SERVERINFO, "how many decals can be spawned" );
|
||||
teamlist = CVAR_REGISTER( "mp_teamlist", "hgrunt,scientist", FCVAR_SERVERINFO, "names of default teams" );
|
||||
teamoverride = CVAR_REGISTER( "mp_teamoverride", "1", 0, "can ovveride teams from map settings ?" );
|
||||
defaultteam = CVAR_REGISTER( "mp_defaultteam", "0", 0, "use default team instead ?" );
|
||||
allowmonsters = CVAR_REGISTER( "mp_allowmonsters", "0", FCVAR_SERVERINFO, "allow monsters in multiplayer" );
|
||||
mp_chattime = CVAR_REGISTER( "mp_chattime", "10", FCVAR_SERVERINFO, "time beetween messages" );;
|
||||
|
||||
// REGISTER CVARS FOR SKILL LEVEL STUFF
|
||||
// Agrunt
|
||||
CVAR_REGISTER_SKILL ( sk_agrunt_health1 );// {"sk_agrunt_health1","0"};
|
||||
CVAR_REGISTER_SKILL ( sk_agrunt_health2 );// {"sk_agrunt_health2","0"};
|
||||
CVAR_REGISTER_SKILL ( sk_agrunt_health3 );// {"sk_agrunt_health3","0"};
|
||||
|
||||
CVAR_REGISTER_SKILL ( sk_agrunt_dmg_punch1 );// {"sk_agrunt_dmg_punch1","0"};
|
||||
CVAR_REGISTER_SKILL ( sk_agrunt_dmg_punch2 );// {"sk_agrunt_dmg_punch2","0"};
|
||||
CVAR_REGISTER_SKILL ( sk_agrunt_dmg_punch3 );// {"sk_agrunt_dmg_punch3","0"};
|
||||
|
||||
// Apache
|
||||
CVAR_REGISTER_SKILL ( sk_apache_health1 );// {"sk_apache_health1","0"};
|
||||
CVAR_REGISTER_SKILL ( sk_apache_health2 );// {"sk_apache_health2","0"};
|
||||
CVAR_REGISTER_SKILL ( sk_apache_health3 );// {"sk_apache_health3","0"};
|
||||
|
||||
// Barney
|
||||
CVAR_REGISTER_SKILL ( sk_barney_health1 );// {"sk_barney_health1","0"};
|
||||
CVAR_REGISTER_SKILL ( sk_barney_health2 );// {"sk_barney_health2","0"};
|
||||
CVAR_REGISTER_SKILL ( sk_barney_health3 );// {"sk_barney_health3","0"};
|
||||
|
||||
// Bullsquid
|
||||
CVAR_REGISTER_SKILL ( sk_bullsquid_health1 );// {"sk_bullsquid_health1","0"};
|
||||
CVAR_REGISTER_SKILL ( sk_bullsquid_health2 );// {"sk_bullsquid_health2","0"};
|
||||
CVAR_REGISTER_SKILL ( sk_bullsquid_health3 );// {"sk_bullsquid_health3","0"};
|
||||
|
||||
CVAR_REGISTER_SKILL ( sk_bullsquid_dmg_bite1 );// {"sk_bullsquid_dmg_bite1","0"};
|
||||
CVAR_REGISTER_SKILL ( sk_bullsquid_dmg_bite2 );// {"sk_bullsquid_dmg_bite2","0"};
|
||||
CVAR_REGISTER_SKILL ( sk_bullsquid_dmg_bite3 );// {"sk_bullsquid_dmg_bite3","0"};
|
||||
|
||||
CVAR_REGISTER_SKILL ( sk_bullsquid_dmg_whip1 );// {"sk_bullsquid_dmg_whip1","0"};
|
||||
CVAR_REGISTER_SKILL ( sk_bullsquid_dmg_whip2 );// {"sk_bullsquid_dmg_whip2","0"};
|
||||
CVAR_REGISTER_SKILL ( sk_bullsquid_dmg_whip3 );// {"sk_bullsquid_dmg_whip3","0"};
|
||||
|
||||
CVAR_REGISTER_SKILL ( sk_bullsquid_dmg_spit1 );// {"sk_bullsquid_dmg_spit1","0"};
|
||||
CVAR_REGISTER_SKILL ( sk_bullsquid_dmg_spit2 );// {"sk_bullsquid_dmg_spit2","0"};
|
||||
CVAR_REGISTER_SKILL ( sk_bullsquid_dmg_spit3 );// {"sk_bullsquid_dmg_spit3","0"};
|
||||
|
||||
|
||||
CVAR_REGISTER_SKILL ( sk_bigmomma_health_factor1 );// {"sk_bigmomma_health_factor1","1.0"};
|
||||
CVAR_REGISTER_SKILL ( sk_bigmomma_health_factor2 );// {"sk_bigmomma_health_factor2","1.0"};
|
||||
CVAR_REGISTER_SKILL ( sk_bigmomma_health_factor3 );// {"sk_bigmomma_health_factor3","1.0"};
|
||||
|
||||
CVAR_REGISTER_SKILL ( sk_bigmomma_dmg_slash1 );// {"sk_bigmomma_dmg_slash1","50"};
|
||||
CVAR_REGISTER_SKILL ( sk_bigmomma_dmg_slash2 );// {"sk_bigmomma_dmg_slash2","50"};
|
||||
CVAR_REGISTER_SKILL ( sk_bigmomma_dmg_slash3 );// {"sk_bigmomma_dmg_slash3","50"};
|
||||
|
||||
CVAR_REGISTER_SKILL ( sk_bigmomma_dmg_blast1 );// {"sk_bigmomma_dmg_blast1","100"};
|
||||
CVAR_REGISTER_SKILL ( sk_bigmomma_dmg_blast2 );// {"sk_bigmomma_dmg_blast2","100"};
|
||||
CVAR_REGISTER_SKILL ( sk_bigmomma_dmg_blast3 );// {"sk_bigmomma_dmg_blast3","100"};
|
||||
|
||||
CVAR_REGISTER_SKILL ( sk_bigmomma_radius_blast1 );// {"sk_bigmomma_radius_blast1","250"};
|
||||
CVAR_REGISTER_SKILL ( sk_bigmomma_radius_blast2 );// {"sk_bigmomma_radius_blast2","250"};
|
||||
CVAR_REGISTER_SKILL ( sk_bigmomma_radius_blast3 );// {"sk_bigmomma_radius_blast3","250"};
|
||||
|
||||
// Gargantua
|
||||
CVAR_REGISTER_SKILL ( sk_gargantua_health1 );// {"sk_gargantua_health1","0"};
|
||||
CVAR_REGISTER_SKILL ( sk_gargantua_health2 );// {"sk_gargantua_health2","0"};
|
||||
CVAR_REGISTER_SKILL ( sk_gargantua_health3 );// {"sk_gargantua_health3","0"};
|
||||
|
||||
CVAR_REGISTER_SKILL ( sk_gargantua_dmg_slash1 );// {"sk_gargantua_dmg_slash1","0"};
|
||||
CVAR_REGISTER_SKILL ( sk_gargantua_dmg_slash2 );// {"sk_gargantua_dmg_slash2","0"};
|
||||
CVAR_REGISTER_SKILL ( sk_gargantua_dmg_slash3 );// {"sk_gargantua_dmg_slash3","0"};
|
||||
|
||||
CVAR_REGISTER_SKILL ( sk_gargantua_dmg_fire1 );// {"sk_gargantua_dmg_fire1","0"};
|
||||
CVAR_REGISTER_SKILL ( sk_gargantua_dmg_fire2 );// {"sk_gargantua_dmg_fire2","0"};
|
||||
CVAR_REGISTER_SKILL ( sk_gargantua_dmg_fire3 );// {"sk_gargantua_dmg_fire3","0"};
|
||||
|
||||
CVAR_REGISTER_SKILL ( sk_gargantua_dmg_stomp1 );// {"sk_gargantua_dmg_stomp1","0"};
|
||||
CVAR_REGISTER_SKILL ( sk_gargantua_dmg_stomp2 );// {"sk_gargantua_dmg_stomp2","0"};
|
||||
CVAR_REGISTER_SKILL ( sk_gargantua_dmg_stomp3 );// {"sk_gargantua_dmg_stomp3","0"};
|
||||
|
||||
|
||||
// Hassassin
|
||||
CVAR_REGISTER_SKILL ( sk_hassassin_health1 );// {"sk_hassassin_health1","0"};
|
||||
CVAR_REGISTER_SKILL ( sk_hassassin_health2 );// {"sk_hassassin_health2","0"};
|
||||
CVAR_REGISTER_SKILL ( sk_hassassin_health3 );// {"sk_hassassin_health3","0"};
|
||||
|
||||
|
||||
// Headcrab
|
||||
CVAR_REGISTER_SKILL ( sk_headcrab_health1 );// {"sk_headcrab_health1","0"};
|
||||
CVAR_REGISTER_SKILL ( sk_headcrab_health2 );// {"sk_headcrab_health2","0"};
|
||||
CVAR_REGISTER_SKILL ( sk_headcrab_health3 );// {"sk_headcrab_health3","0"};
|
||||
|
||||
CVAR_REGISTER_SKILL ( sk_headcrab_dmg_bite1 );// {"sk_headcrab_dmg_bite1","0"};
|
||||
CVAR_REGISTER_SKILL ( sk_headcrab_dmg_bite2 );// {"sk_headcrab_dmg_bite2","0"};
|
||||
CVAR_REGISTER_SKILL ( sk_headcrab_dmg_bite3 );// {"sk_headcrab_dmg_bite3","0"};
|
||||
|
||||
|
||||
// Hgrunt
|
||||
CVAR_REGISTER_SKILL ( sk_hgrunt_health1 );// {"sk_hgrunt_health1","0"};
|
||||
CVAR_REGISTER_SKILL ( sk_hgrunt_health2 );// {"sk_hgrunt_health2","0"};
|
||||
CVAR_REGISTER_SKILL ( sk_hgrunt_health3 );// {"sk_hgrunt_health3","0"};
|
||||
|
||||
CVAR_REGISTER_SKILL ( sk_hgrunt_kick1 );// {"sk_hgrunt_kick1","0"};
|
||||
CVAR_REGISTER_SKILL ( sk_hgrunt_kick2 );// {"sk_hgrunt_kick2","0"};
|
||||
CVAR_REGISTER_SKILL ( sk_hgrunt_kick3 );// {"sk_hgrunt_kick3","0"};
|
||||
|
||||
CVAR_REGISTER_SKILL ( sk_hgrunt_pellets1 );
|
||||
CVAR_REGISTER_SKILL ( sk_hgrunt_pellets2 );
|
||||
CVAR_REGISTER_SKILL ( sk_hgrunt_pellets3 );
|
||||
|
||||
CVAR_REGISTER_SKILL ( sk_hgrunt_gspeed1 );
|
||||
CVAR_REGISTER_SKILL ( sk_hgrunt_gspeed2 );
|
||||
CVAR_REGISTER_SKILL ( sk_hgrunt_gspeed3 );
|
||||
|
||||
// Houndeye
|
||||
CVAR_REGISTER_SKILL ( sk_houndeye_health1 );// {"sk_houndeye_health1","0"};
|
||||
CVAR_REGISTER_SKILL ( sk_houndeye_health2 );// {"sk_houndeye_health2","0"};
|
||||
CVAR_REGISTER_SKILL ( sk_houndeye_health3 );// {"sk_houndeye_health3","0"};
|
||||
|
||||
CVAR_REGISTER_SKILL ( sk_houndeye_dmg_blast1 );// {"sk_houndeye_dmg_blast1","0"};
|
||||
CVAR_REGISTER_SKILL ( sk_houndeye_dmg_blast2 );// {"sk_houndeye_dmg_blast2","0"};
|
||||
CVAR_REGISTER_SKILL ( sk_houndeye_dmg_blast3 );// {"sk_houndeye_dmg_blast3","0"};
|
||||
|
||||
|
||||
// ISlave
|
||||
CVAR_REGISTER_SKILL ( sk_islave_health1 );// {"sk_islave_health1","0"};
|
||||
CVAR_REGISTER_SKILL ( sk_islave_health2 );// {"sk_islave_health2","0"};
|
||||
CVAR_REGISTER_SKILL ( sk_islave_health3 );// {"sk_islave_health3","0"};
|
||||
|
||||
CVAR_REGISTER_SKILL ( sk_islave_dmg_claw1 );// {"sk_islave_dmg_claw1","0"};
|
||||
CVAR_REGISTER_SKILL ( sk_islave_dmg_claw2 );// {"sk_islave_dmg_claw2","0"};
|
||||
CVAR_REGISTER_SKILL ( sk_islave_dmg_claw3 );// {"sk_islave_dmg_claw3","0"};
|
||||
|
||||
CVAR_REGISTER_SKILL ( sk_islave_dmg_clawrake1 );// {"sk_islave_dmg_clawrake1","0"};
|
||||
CVAR_REGISTER_SKILL ( sk_islave_dmg_clawrake2 );// {"sk_islave_dmg_clawrake2","0"};
|
||||
CVAR_REGISTER_SKILL ( sk_islave_dmg_clawrake3 );// {"sk_islave_dmg_clawrake3","0"};
|
||||
|
||||
CVAR_REGISTER_SKILL ( sk_islave_dmg_zap1 );// {"sk_islave_dmg_zap1","0"};
|
||||
CVAR_REGISTER_SKILL ( sk_islave_dmg_zap2 );// {"sk_islave_dmg_zap2","0"};
|
||||
CVAR_REGISTER_SKILL ( sk_islave_dmg_zap3 );// {"sk_islave_dmg_zap3","0"};
|
||||
|
||||
|
||||
// Icthyosaur
|
||||
CVAR_REGISTER_SKILL ( sk_ichthyosaur_health1 );// {"sk_ichthyosaur_health1","0"};
|
||||
CVAR_REGISTER_SKILL ( sk_ichthyosaur_health2 );// {"sk_ichthyosaur_health2","0"};
|
||||
CVAR_REGISTER_SKILL ( sk_ichthyosaur_health3 );// {"sk_ichthyosaur_health3","0"};
|
||||
|
||||
CVAR_REGISTER_SKILL ( sk_ichthyosaur_shake1 );// {"sk_ichthyosaur_health3","0"};
|
||||
CVAR_REGISTER_SKILL ( sk_ichthyosaur_shake2 );// {"sk_ichthyosaur_health3","0"};
|
||||
CVAR_REGISTER_SKILL ( sk_ichthyosaur_shake3 );// {"sk_ichthyosaur_health3","0"};
|
||||
|
||||
|
||||
|
||||
// Leech
|
||||
CVAR_REGISTER_SKILL ( sk_leech_health1 );// {"sk_leech_health1","0"};
|
||||
CVAR_REGISTER_SKILL ( sk_leech_health2 );// {"sk_leech_health2","0"};
|
||||
CVAR_REGISTER_SKILL ( sk_leech_health3 );// {"sk_leech_health3","0"};
|
||||
|
||||
CVAR_REGISTER_SKILL ( sk_leech_dmg_bite1 );// {"sk_leech_dmg_bite1","0"};
|
||||
CVAR_REGISTER_SKILL ( sk_leech_dmg_bite2 );// {"sk_leech_dmg_bite2","0"};
|
||||
CVAR_REGISTER_SKILL ( sk_leech_dmg_bite3 );// {"sk_leech_dmg_bite3","0"};
|
||||
|
||||
|
||||
// Controller
|
||||
CVAR_REGISTER_SKILL ( sk_controller_health1 );
|
||||
CVAR_REGISTER_SKILL ( sk_controller_health2 );
|
||||
CVAR_REGISTER_SKILL ( sk_controller_health3 );
|
||||
|
||||
CVAR_REGISTER_SKILL ( sk_controller_dmgzap1 );
|
||||
CVAR_REGISTER_SKILL ( sk_controller_dmgzap2 );
|
||||
CVAR_REGISTER_SKILL ( sk_controller_dmgzap3 );
|
||||
|
||||
CVAR_REGISTER_SKILL ( sk_controller_speedball1 );
|
||||
CVAR_REGISTER_SKILL ( sk_controller_speedball2 );
|
||||
CVAR_REGISTER_SKILL ( sk_controller_speedball3 );
|
||||
|
||||
CVAR_REGISTER_SKILL ( sk_controller_dmgball1 );
|
||||
CVAR_REGISTER_SKILL ( sk_controller_dmgball2 );
|
||||
CVAR_REGISTER_SKILL ( sk_controller_dmgball3 );
|
||||
|
||||
// Nihilanth
|
||||
CVAR_REGISTER_SKILL ( sk_nihilanth_health1 );// {"sk_nihilanth_health1","0"};
|
||||
CVAR_REGISTER_SKILL ( sk_nihilanth_health2 );// {"sk_nihilanth_health2","0"};
|
||||
CVAR_REGISTER_SKILL ( sk_nihilanth_health3 );// {"sk_nihilanth_health3","0"};
|
||||
|
||||
CVAR_REGISTER_SKILL ( sk_nihilanth_zap1 );
|
||||
CVAR_REGISTER_SKILL ( sk_nihilanth_zap2 );
|
||||
CVAR_REGISTER_SKILL ( sk_nihilanth_zap3 );
|
||||
|
||||
// Scientist
|
||||
CVAR_REGISTER_SKILL ( sk_scientist_health1 );// {"sk_scientist_health1","0"};
|
||||
CVAR_REGISTER_SKILL ( sk_scientist_health2 );// {"sk_scientist_health2","0"};
|
||||
CVAR_REGISTER_SKILL ( sk_scientist_health3 );// {"sk_scientist_health3","0"};
|
||||
|
||||
|
||||
// Snark
|
||||
CVAR_REGISTER_SKILL ( sk_snark_health1 );// {"sk_snark_health1","0"};
|
||||
CVAR_REGISTER_SKILL ( sk_snark_health2 );// {"sk_snark_health2","0"};
|
||||
CVAR_REGISTER_SKILL ( sk_snark_health3 );// {"sk_snark_health3","0"};
|
||||
|
||||
CVAR_REGISTER_SKILL ( sk_snark_dmg_bite1 );// {"sk_snark_dmg_bite1","0"};
|
||||
CVAR_REGISTER_SKILL ( sk_snark_dmg_bite2 );// {"sk_snark_dmg_bite2","0"};
|
||||
CVAR_REGISTER_SKILL ( sk_snark_dmg_bite3 );// {"sk_snark_dmg_bite3","0"};
|
||||
|
||||
CVAR_REGISTER_SKILL ( sk_snark_dmg_pop1 );// {"sk_snark_dmg_pop1","0"};
|
||||
CVAR_REGISTER_SKILL ( sk_snark_dmg_pop2 );// {"sk_snark_dmg_pop2","0"};
|
||||
CVAR_REGISTER_SKILL ( sk_snark_dmg_pop3 );// {"sk_snark_dmg_pop3","0"};
|
||||
|
||||
|
||||
|
||||
// Zombie
|
||||
CVAR_REGISTER_SKILL ( sk_zombie_health1 );// {"sk_zombie_health1","0"};
|
||||
CVAR_REGISTER_SKILL ( sk_zombie_health2 );// {"sk_zombie_health3","0"};
|
||||
CVAR_REGISTER_SKILL ( sk_zombie_health3 );// {"sk_zombie_health3","0"};
|
||||
|
||||
CVAR_REGISTER_SKILL ( sk_zombie_dmg_one_slash1 );// {"sk_zombie_dmg_one_slash1","0"};
|
||||
CVAR_REGISTER_SKILL ( sk_zombie_dmg_one_slash2 );// {"sk_zombie_dmg_one_slash2","0"};
|
||||
CVAR_REGISTER_SKILL ( sk_zombie_dmg_one_slash3 );// {"sk_zombie_dmg_one_slash3","0"};
|
||||
|
||||
CVAR_REGISTER_SKILL ( sk_zombie_dmg_both_slash1 );// {"sk_zombie_dmg_both_slash1","0"};
|
||||
CVAR_REGISTER_SKILL ( sk_zombie_dmg_both_slash2 );// {"sk_zombie_dmg_both_slash2","0"};
|
||||
CVAR_REGISTER_SKILL ( sk_zombie_dmg_both_slash3 );// {"sk_zombie_dmg_both_slash3","0"};
|
||||
|
||||
|
||||
//Turret
|
||||
CVAR_REGISTER_SKILL ( sk_turret_health1 );// {"sk_turret_health1","0"};
|
||||
CVAR_REGISTER_SKILL ( sk_turret_health2 );// {"sk_turret_health2","0"};
|
||||
CVAR_REGISTER_SKILL ( sk_turret_health3 );// {"sk_turret_health3","0"};
|
||||
|
||||
|
||||
// MiniTurret
|
||||
CVAR_REGISTER_SKILL ( sk_miniturret_health1 );// {"sk_miniturret_health1","0"};
|
||||
CVAR_REGISTER_SKILL ( sk_miniturret_health2 );// {"sk_miniturret_health2","0"};
|
||||
CVAR_REGISTER_SKILL ( sk_miniturret_health3 );// {"sk_miniturret_health3","0"};
|
||||
|
||||
|
||||
// Sentry Turret
|
||||
CVAR_REGISTER_SKILL ( sk_sentry_health1 );// {"sk_sentry_health1","0"};
|
||||
CVAR_REGISTER_SKILL ( sk_sentry_health2 );// {"sk_sentry_health2","0"};
|
||||
CVAR_REGISTER_SKILL ( sk_sentry_health3 );// {"sk_sentry_health3","0"};
|
||||
|
||||
|
||||
// PLAYER WEAPONS
|
||||
|
||||
// Crowbar whack
|
||||
CVAR_REGISTER_SKILL ( sk_plr_crowbar1 );// {"sk_plr_crowbar1","0"};
|
||||
CVAR_REGISTER_SKILL ( sk_plr_crowbar2 );// {"sk_plr_crowbar2","0"};
|
||||
CVAR_REGISTER_SKILL ( sk_plr_crowbar3 );// {"sk_plr_crowbar3","0"};
|
||||
|
||||
// Glock Round
|
||||
CVAR_REGISTER_SKILL ( sk_plr_9mm_bullet1 );// {"sk_plr_9mm_bullet1","0"};
|
||||
CVAR_REGISTER_SKILL ( sk_plr_9mm_bullet2 );// {"sk_plr_9mm_bullet2","0"};
|
||||
CVAR_REGISTER_SKILL ( sk_plr_9mm_bullet3 );// {"sk_plr_9mm_bullet3","0"};
|
||||
|
||||
// 357 Round
|
||||
CVAR_REGISTER_SKILL ( sk_plr_357_bullet1 );// {"sk_plr_357_bullet1","0"};
|
||||
CVAR_REGISTER_SKILL ( sk_plr_357_bullet2 );// {"sk_plr_357_bullet2","0"};
|
||||
CVAR_REGISTER_SKILL ( sk_plr_357_bullet3 );// {"sk_plr_357_bullet3","0"};
|
||||
|
||||
// MP5 Round
|
||||
CVAR_REGISTER_SKILL ( sk_plr_9mmAR_bullet1 );// {"sk_plr_9mmAR_bullet1","0"};
|
||||
CVAR_REGISTER_SKILL ( sk_plr_9mmAR_bullet2 );// {"sk_plr_9mmAR_bullet2","0"};
|
||||
CVAR_REGISTER_SKILL ( sk_plr_9mmAR_bullet3 );// {"sk_plr_9mmAR_bullet3","0"};
|
||||
|
||||
|
||||
// M203 grenade
|
||||
CVAR_REGISTER_SKILL ( sk_plr_9mmAR_grenade1 );// {"sk_plr_9mmAR_grenade1","0"};
|
||||
CVAR_REGISTER_SKILL ( sk_plr_9mmAR_grenade2 );// {"sk_plr_9mmAR_grenade2","0"};
|
||||
CVAR_REGISTER_SKILL ( sk_plr_9mmAR_grenade3 );// {"sk_plr_9mmAR_grenade3","0"};
|
||||
|
||||
|
||||
// Shotgun buckshot
|
||||
CVAR_REGISTER_SKILL ( sk_plr_buckshot1 );// {"sk_plr_buckshot1","0"};
|
||||
CVAR_REGISTER_SKILL ( sk_plr_buckshot2 );// {"sk_plr_buckshot2","0"};
|
||||
CVAR_REGISTER_SKILL ( sk_plr_buckshot3 );// {"sk_plr_buckshot3","0"};
|
||||
|
||||
|
||||
// Crossbow
|
||||
CVAR_REGISTER_SKILL ( sk_plr_xbow_bolt_monster1 );// {"sk_plr_xbow_bolt1","0"};
|
||||
CVAR_REGISTER_SKILL ( sk_plr_xbow_bolt_monster2 );// {"sk_plr_xbow_bolt2","0"};
|
||||
CVAR_REGISTER_SKILL ( sk_plr_xbow_bolt_monster3 );// {"sk_plr_xbow_bolt3","0"};
|
||||
|
||||
CVAR_REGISTER_SKILL ( sk_plr_xbow_bolt_client1 );// {"sk_plr_xbow_bolt1","0"};
|
||||
CVAR_REGISTER_SKILL ( sk_plr_xbow_bolt_client2 );// {"sk_plr_xbow_bolt2","0"};
|
||||
CVAR_REGISTER_SKILL ( sk_plr_xbow_bolt_client3 );// {"sk_plr_xbow_bolt3","0"};
|
||||
|
||||
|
||||
// RPG
|
||||
CVAR_REGISTER_SKILL ( sk_plr_rpg1 );// {"sk_plr_rpg1","0"};
|
||||
CVAR_REGISTER_SKILL ( sk_plr_rpg2 );// {"sk_plr_rpg2","0"};
|
||||
CVAR_REGISTER_SKILL ( sk_plr_rpg3 );// {"sk_plr_rpg3","0"};
|
||||
|
||||
|
||||
// Gauss Gun
|
||||
CVAR_REGISTER_SKILL ( sk_plr_gauss1 );// {"sk_plr_gauss1","0"};
|
||||
CVAR_REGISTER_SKILL ( sk_plr_gauss2 );// {"sk_plr_gauss2","0"};
|
||||
CVAR_REGISTER_SKILL ( sk_plr_gauss3 );// {"sk_plr_gauss3","0"};
|
||||
|
||||
|
||||
// Egon Gun
|
||||
CVAR_REGISTER_SKILL ( sk_plr_egon_narrow1 );// {"sk_plr_egon_narrow1","0"};
|
||||
CVAR_REGISTER_SKILL ( sk_plr_egon_narrow2 );// {"sk_plr_egon_narrow2","0"};
|
||||
CVAR_REGISTER_SKILL ( sk_plr_egon_narrow3 );// {"sk_plr_egon_narrow3","0"};
|
||||
|
||||
CVAR_REGISTER_SKILL ( sk_plr_egon_wide1 );// {"sk_plr_egon_wide1","0"};
|
||||
CVAR_REGISTER_SKILL ( sk_plr_egon_wide2 );// {"sk_plr_egon_wide2","0"};
|
||||
CVAR_REGISTER_SKILL ( sk_plr_egon_wide3 );// {"sk_plr_egon_wide3","0"};
|
||||
|
||||
|
||||
// Hand Grendade
|
||||
CVAR_REGISTER_SKILL ( sk_plr_hand_grenade1 );// {"sk_plr_hand_grenade1","0"};
|
||||
CVAR_REGISTER_SKILL ( sk_plr_hand_grenade2 );// {"sk_plr_hand_grenade2","0"};
|
||||
CVAR_REGISTER_SKILL ( sk_plr_hand_grenade3 );// {"sk_plr_hand_grenade3","0"};
|
||||
|
||||
|
||||
// Satchel Charge
|
||||
CVAR_REGISTER_SKILL ( sk_plr_satchel1 );// {"sk_plr_satchel1","0"};
|
||||
CVAR_REGISTER_SKILL ( sk_plr_satchel2 );// {"sk_plr_satchel2","0"};
|
||||
CVAR_REGISTER_SKILL ( sk_plr_satchel3 );// {"sk_plr_satchel3","0"};
|
||||
|
||||
|
||||
// Tripmine
|
||||
CVAR_REGISTER_SKILL ( sk_plr_tripmine1 );// {"sk_plr_tripmine1","0"};
|
||||
CVAR_REGISTER_SKILL ( sk_plr_tripmine2 );// {"sk_plr_tripmine2","0"};
|
||||
CVAR_REGISTER_SKILL ( sk_plr_tripmine3 );// {"sk_plr_tripmine3","0"};
|
||||
|
||||
|
||||
// WORLD WEAPONS
|
||||
CVAR_REGISTER_SKILL ( sk_12mm_bullet1 );// {"sk_12mm_bullet1","0"};
|
||||
CVAR_REGISTER_SKILL ( sk_12mm_bullet2 );// {"sk_12mm_bullet2","0"};
|
||||
CVAR_REGISTER_SKILL ( sk_12mm_bullet3 );// {"sk_12mm_bullet3","0"};
|
||||
|
||||
CVAR_REGISTER_SKILL ( sk_9mmAR_bullet1 );// {"sk_9mm_bullet1","0"};
|
||||
CVAR_REGISTER_SKILL ( sk_9mmAR_bullet2 );// {"sk_9mm_bullet1","0"};
|
||||
CVAR_REGISTER_SKILL ( sk_9mmAR_bullet3 );// {"sk_9mm_bullet1","0"};
|
||||
|
||||
CVAR_REGISTER_SKILL ( sk_9mm_bullet1 );// {"sk_9mm_bullet1","0"};
|
||||
CVAR_REGISTER_SKILL ( sk_9mm_bullet2 );// {"sk_9mm_bullet2","0"};
|
||||
CVAR_REGISTER_SKILL ( sk_9mm_bullet3 );// {"sk_9mm_bullet3","0"};
|
||||
|
||||
|
||||
// HORNET
|
||||
CVAR_REGISTER_SKILL ( sk_hornet_dmg1 );// {"sk_hornet_dmg1","0"};
|
||||
CVAR_REGISTER_SKILL ( sk_hornet_dmg2 );// {"sk_hornet_dmg2","0"};
|
||||
CVAR_REGISTER_SKILL ( sk_hornet_dmg3 );// {"sk_hornet_dmg3","0"};
|
||||
|
||||
// HEALTH/SUIT CHARGE DISTRIBUTION
|
||||
CVAR_REGISTER_SKILL ( sk_suitcharger1 );
|
||||
CVAR_REGISTER_SKILL ( sk_suitcharger2 );
|
||||
CVAR_REGISTER_SKILL ( sk_suitcharger3 );
|
||||
|
||||
CVAR_REGISTER_SKILL ( sk_battery1 );
|
||||
CVAR_REGISTER_SKILL ( sk_battery2 );
|
||||
CVAR_REGISTER_SKILL ( sk_battery3 );
|
||||
|
||||
CVAR_REGISTER_SKILL ( sk_healthcharger1 );
|
||||
CVAR_REGISTER_SKILL ( sk_healthcharger2 );
|
||||
CVAR_REGISTER_SKILL ( sk_healthcharger3 );
|
||||
|
||||
CVAR_REGISTER_SKILL ( sk_healthkit1 );
|
||||
CVAR_REGISTER_SKILL ( sk_healthkit2 );
|
||||
CVAR_REGISTER_SKILL ( sk_healthkit3 );
|
||||
|
||||
CVAR_REGISTER_SKILL ( sk_scientist_heal1 );
|
||||
CVAR_REGISTER_SKILL ( sk_scientist_heal2 );
|
||||
CVAR_REGISTER_SKILL ( sk_scientist_heal3 );
|
||||
|
||||
|
||||
// monster damage adjusters
|
||||
CVAR_REGISTER_SKILL ( sk_monster_head1 );
|
||||
CVAR_REGISTER_SKILL ( sk_monster_head2 );
|
||||
CVAR_REGISTER_SKILL ( sk_monster_head3 );
|
||||
|
||||
CVAR_REGISTER_SKILL ( sk_monster_chest1 );
|
||||
CVAR_REGISTER_SKILL ( sk_monster_chest2 );
|
||||
CVAR_REGISTER_SKILL ( sk_monster_chest3 );
|
||||
|
||||
CVAR_REGISTER_SKILL ( sk_monster_stomach1 );
|
||||
CVAR_REGISTER_SKILL ( sk_monster_stomach2 );
|
||||
CVAR_REGISTER_SKILL ( sk_monster_stomach3 );
|
||||
|
||||
CVAR_REGISTER_SKILL ( sk_monster_arm1 );
|
||||
CVAR_REGISTER_SKILL ( sk_monster_arm2 );
|
||||
CVAR_REGISTER_SKILL ( sk_monster_arm3 );
|
||||
|
||||
CVAR_REGISTER_SKILL ( sk_monster_leg1 );
|
||||
CVAR_REGISTER_SKILL ( sk_monster_leg2 );
|
||||
CVAR_REGISTER_SKILL ( sk_monster_leg3 );
|
||||
|
||||
// player damage adjusters
|
||||
CVAR_REGISTER_SKILL ( sk_player_head1 );
|
||||
CVAR_REGISTER_SKILL ( sk_player_head2 );
|
||||
CVAR_REGISTER_SKILL ( sk_player_head3 );
|
||||
|
||||
CVAR_REGISTER_SKILL ( sk_player_chest1 );
|
||||
CVAR_REGISTER_SKILL ( sk_player_chest2 );
|
||||
CVAR_REGISTER_SKILL ( sk_player_chest3 );
|
||||
|
||||
CVAR_REGISTER_SKILL ( sk_player_stomach1 );
|
||||
CVAR_REGISTER_SKILL ( sk_player_stomach2 );
|
||||
CVAR_REGISTER_SKILL ( sk_player_stomach3 );
|
||||
|
||||
CVAR_REGISTER_SKILL ( sk_player_arm1 );
|
||||
CVAR_REGISTER_SKILL ( sk_player_arm2 );
|
||||
CVAR_REGISTER_SKILL ( sk_player_arm3 );
|
||||
|
||||
CVAR_REGISTER_SKILL ( sk_player_leg1 );
|
||||
CVAR_REGISTER_SKILL ( sk_player_leg2 );
|
||||
CVAR_REGISTER_SKILL ( sk_player_leg3 );
|
||||
// END REGISTER CVARS FOR SKILL LEVEL STUFF
|
||||
|
||||
SERVER_COMMAND( "exec skill.rc\n" );
|
||||
}
|
||||
|
||||
// perform any shutdown operations
|
||||
void GameDLLShutdown( void )
|
||||
{
|
||||
}
|
|
@ -0,0 +1,51 @@
|
|||
/***
|
||||
*
|
||||
* 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.
|
||||
*
|
||||
****/
|
||||
|
||||
#ifndef GAME_H
|
||||
#define GAME_H
|
||||
|
||||
extern void GameDLLInit( void );
|
||||
extern void GameDLLShutdown( void );
|
||||
|
||||
#include "cvardef.h"
|
||||
|
||||
extern cvar_t *displaysoundlist;
|
||||
extern cvar_t *mapcyclefile;
|
||||
extern cvar_t *servercfgfile;
|
||||
extern cvar_t *lservercfgfile;
|
||||
|
||||
// multiplayer server rules
|
||||
extern cvar_t *teamplay;
|
||||
extern cvar_t *fraglimit;
|
||||
extern cvar_t *timelimit;
|
||||
extern cvar_t *friendlyfir;
|
||||
extern cvar_t *falldamage;
|
||||
extern cvar_t *weaponstay;
|
||||
extern cvar_t *forcerespaw;
|
||||
extern cvar_t *flashlight;
|
||||
extern cvar_t *aimcrosshair;
|
||||
extern cvar_t *decalfrequency;
|
||||
extern cvar_t *teamlist;
|
||||
extern cvar_t *teamoverride;
|
||||
extern cvar_t *defaultteam;
|
||||
extern cvar_t *allowmonsters;
|
||||
|
||||
// Engine Cvars
|
||||
extern cvar_t *g_psv_gravity;
|
||||
extern cvar_t *g_psv_aim;
|
||||
extern cvar_t *g_footsteps;
|
||||
extern cvar_t *g_psv_maxspeed;
|
||||
|
||||
#endif // GAME_H
|
|
@ -0,0 +1,351 @@
|
|||
/***
|
||||
*
|
||||
* 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.
|
||||
*
|
||||
****/
|
||||
//=========================================================
|
||||
// GameRules.cpp
|
||||
//=========================================================
|
||||
|
||||
#include "extdll.h"
|
||||
#include "util.h"
|
||||
#include "cbase.h"
|
||||
#include "player.h"
|
||||
#include "weapons.h"
|
||||
#include "gamerules.h"
|
||||
#include "teamplay_gamerules.h"
|
||||
#include "skill.h"
|
||||
#include "game.h"
|
||||
|
||||
extern edict_t *EntSelectSpawnPoint( CBaseEntity *pPlayer );
|
||||
|
||||
DLL_GLOBAL CGameRules* g_pGameRules = NULL;
|
||||
extern DLL_GLOBAL BOOL g_fGameOver;
|
||||
extern int gmsgDeathMsg; // client dll messages
|
||||
extern int gmsgScoreInfo;
|
||||
extern int gmsgMOTD;
|
||||
|
||||
int g_teamplay = 0;
|
||||
|
||||
//=========================================================
|
||||
//=========================================================
|
||||
BOOL CGameRules::CanHaveAmmo( CBasePlayer *pPlayer, const char *pszAmmoName, int iMaxCarry )
|
||||
{
|
||||
int iAmmoIndex;
|
||||
|
||||
if ( pszAmmoName )
|
||||
{
|
||||
iAmmoIndex = pPlayer->GetAmmoIndex( pszAmmoName );
|
||||
|
||||
if ( iAmmoIndex > -1 )
|
||||
{
|
||||
if ( pPlayer->AmmoInventory( iAmmoIndex ) < iMaxCarry )
|
||||
{
|
||||
// player has room for more of this type of ammo
|
||||
return TRUE;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return FALSE;
|
||||
}
|
||||
|
||||
//=========================================================
|
||||
//=========================================================
|
||||
edict_t *CGameRules :: GetPlayerSpawnSpot( CBasePlayer *pPlayer )
|
||||
{
|
||||
edict_t *pentSpawnSpot = EntSelectSpawnPoint( pPlayer );
|
||||
|
||||
pPlayer->pev->origin = VARS(pentSpawnSpot)->origin + Vector(0,0,1);
|
||||
pPlayer->pev->viewangles = g_vecZero;
|
||||
pPlayer->pev->velocity = g_vecZero;
|
||||
pPlayer->pev->angles = VARS(pentSpawnSpot)->angles;
|
||||
pPlayer->pev->punchangle = g_vecZero;
|
||||
pPlayer->pev->fixangle = TRUE;
|
||||
|
||||
if (pentSpawnSpot->v.spawnflags & 1) // the START WITH SUIT flag
|
||||
{
|
||||
pPlayer->pev->weapons |= ITEM_SUIT;
|
||||
}
|
||||
|
||||
return pentSpawnSpot;
|
||||
}
|
||||
|
||||
//=========================================================
|
||||
//=========================================================
|
||||
BOOL CGameRules::CanHavePlayerItem( CBasePlayer *pPlayer, CBasePlayerItem *pWeapon )
|
||||
{
|
||||
// only living players can have items
|
||||
if ( pPlayer->pev->deadflag != DEAD_NO )
|
||||
return FALSE;
|
||||
|
||||
if ( pWeapon->pszAmmo1() )
|
||||
{
|
||||
if ( !CanHaveAmmo( pPlayer, pWeapon->pszAmmo1(), pWeapon->iMaxAmmo1() ) )
|
||||
{
|
||||
// we can't carry anymore ammo for this gun. We can only
|
||||
// have the gun if we aren't already carrying one of this type
|
||||
if ( pPlayer->HasPlayerItem( pWeapon ) )
|
||||
{
|
||||
return FALSE;
|
||||
}
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
// weapon doesn't use ammo, don't take another if you already have it.
|
||||
if ( pPlayer->HasPlayerItem( pWeapon ) )
|
||||
{
|
||||
return FALSE;
|
||||
}
|
||||
}
|
||||
|
||||
// note: will fall through to here if GetItemInfo doesn't fill the struct!
|
||||
return TRUE;
|
||||
}
|
||||
|
||||
//=========================================================
|
||||
// load the SkillData struct with the proper values based on the skill level.
|
||||
//=========================================================
|
||||
void CGameRules::RefreshSkillData ( void )
|
||||
{
|
||||
int iSkill;
|
||||
|
||||
iSkill = (int)CVAR_GET_FLOAT("skill");
|
||||
|
||||
if ( iSkill < 1 )
|
||||
{
|
||||
iSkill = 1;
|
||||
}
|
||||
else if ( iSkill > 3 )
|
||||
{
|
||||
iSkill = 3;
|
||||
}
|
||||
|
||||
gSkillData.iSkillLevel = iSkill;
|
||||
|
||||
ALERT ( at_console, "\nGAME SKILL LEVEL:%d\n",iSkill );
|
||||
|
||||
//Agrunt
|
||||
gSkillData.agruntHealth = GetSkillCvar( "sk_agrunt_health" );
|
||||
gSkillData.agruntDmgPunch = GetSkillCvar( "sk_agrunt_dmg_punch");
|
||||
|
||||
// Apache
|
||||
gSkillData.apacheHealth = GetSkillCvar( "sk_apache_health");
|
||||
|
||||
// Barney
|
||||
gSkillData.barneyHealth = GetSkillCvar( "sk_barney_health");
|
||||
|
||||
// Big Momma
|
||||
gSkillData.bigmommaHealthFactor = GetSkillCvar( "sk_bigmomma_health_factor" );
|
||||
gSkillData.bigmommaDmgSlash = GetSkillCvar( "sk_bigmomma_dmg_slash" );
|
||||
gSkillData.bigmommaDmgBlast = GetSkillCvar( "sk_bigmomma_dmg_blast" );
|
||||
gSkillData.bigmommaRadiusBlast = GetSkillCvar( "sk_bigmomma_radius_blast" );
|
||||
|
||||
// Bullsquid
|
||||
gSkillData.bullsquidHealth = GetSkillCvar( "sk_bullsquid_health");
|
||||
gSkillData.bullsquidDmgBite = GetSkillCvar( "sk_bullsquid_dmg_bite");
|
||||
gSkillData.bullsquidDmgWhip = GetSkillCvar( "sk_bullsquid_dmg_whip");
|
||||
gSkillData.bullsquidDmgSpit = GetSkillCvar( "sk_bullsquid_dmg_spit");
|
||||
|
||||
// Gargantua
|
||||
gSkillData.gargantuaHealth = GetSkillCvar( "sk_gargantua_health");
|
||||
gSkillData.gargantuaDmgSlash = GetSkillCvar( "sk_gargantua_dmg_slash");
|
||||
gSkillData.gargantuaDmgFire = GetSkillCvar( "sk_gargantua_dmg_fire");
|
||||
gSkillData.gargantuaDmgStomp = GetSkillCvar( "sk_gargantua_dmg_stomp");
|
||||
|
||||
// Hassassin
|
||||
gSkillData.hassassinHealth = GetSkillCvar( "sk_hassassin_health");
|
||||
|
||||
// Headcrab
|
||||
gSkillData.headcrabHealth = GetSkillCvar( "sk_headcrab_health");
|
||||
gSkillData.headcrabDmgBite = GetSkillCvar( "sk_headcrab_dmg_bite");
|
||||
|
||||
// Hgrunt
|
||||
gSkillData.hgruntHealth = GetSkillCvar( "sk_hgrunt_health");
|
||||
gSkillData.hgruntDmgKick = GetSkillCvar( "sk_hgrunt_kick");
|
||||
gSkillData.hgruntShotgunPellets = GetSkillCvar( "sk_hgrunt_pellets");
|
||||
gSkillData.hgruntGrenadeSpeed = GetSkillCvar( "sk_hgrunt_gspeed");
|
||||
|
||||
// Houndeye
|
||||
gSkillData.houndeyeHealth = GetSkillCvar( "sk_houndeye_health");
|
||||
gSkillData.houndeyeDmgBlast = GetSkillCvar( "sk_houndeye_dmg_blast");
|
||||
|
||||
// ISlave
|
||||
gSkillData.slaveHealth = GetSkillCvar( "sk_islave_health");
|
||||
gSkillData.slaveDmgClaw = GetSkillCvar( "sk_islave_dmg_claw");
|
||||
gSkillData.slaveDmgClawrake = GetSkillCvar( "sk_islave_dmg_clawrake");
|
||||
gSkillData.slaveDmgZap = GetSkillCvar( "sk_islave_dmg_zap");
|
||||
|
||||
// Icthyosaur
|
||||
gSkillData.ichthyosaurHealth = GetSkillCvar( "sk_ichthyosaur_health");
|
||||
gSkillData.ichthyosaurDmgShake = GetSkillCvar( "sk_ichthyosaur_shake");
|
||||
|
||||
// Leech
|
||||
gSkillData.leechHealth = GetSkillCvar( "sk_leech_health");
|
||||
|
||||
gSkillData.leechDmgBite = GetSkillCvar( "sk_leech_dmg_bite");
|
||||
|
||||
// Controller
|
||||
gSkillData.controllerHealth = GetSkillCvar( "sk_controller_health");
|
||||
gSkillData.controllerDmgZap = GetSkillCvar( "sk_controller_dmgzap");
|
||||
gSkillData.controllerSpeedBall = GetSkillCvar( "sk_controller_speedball");
|
||||
gSkillData.controllerDmgBall = GetSkillCvar( "sk_controller_dmgball");
|
||||
|
||||
// Nihilanth
|
||||
gSkillData.nihilanthHealth = GetSkillCvar( "sk_nihilanth_health");
|
||||
gSkillData.nihilanthZap = GetSkillCvar( "sk_nihilanth_zap");
|
||||
|
||||
// Scientist
|
||||
gSkillData.scientistHealth = GetSkillCvar( "sk_scientist_health");
|
||||
|
||||
// Snark
|
||||
gSkillData.snarkHealth = GetSkillCvar( "sk_snark_health");
|
||||
gSkillData.snarkDmgBite = GetSkillCvar( "sk_snark_dmg_bite");
|
||||
gSkillData.snarkDmgPop = GetSkillCvar( "sk_snark_dmg_pop");
|
||||
|
||||
// Zombie
|
||||
gSkillData.zombieHealth = GetSkillCvar( "sk_zombie_health");
|
||||
gSkillData.zombieDmgOneSlash = GetSkillCvar( "sk_zombie_dmg_one_slash");
|
||||
gSkillData.zombieDmgBothSlash = GetSkillCvar( "sk_zombie_dmg_both_slash");
|
||||
|
||||
//Turret
|
||||
gSkillData.turretHealth = GetSkillCvar( "sk_turret_health");
|
||||
|
||||
// MiniTurret
|
||||
gSkillData.miniturretHealth = GetSkillCvar( "sk_miniturret_health");
|
||||
|
||||
// Sentry Turret
|
||||
gSkillData.sentryHealth = GetSkillCvar( "sk_sentry_health");
|
||||
|
||||
// PLAYER WEAPONS
|
||||
|
||||
// Crowbar whack
|
||||
gSkillData.plrDmgCrowbar = GetSkillCvar( "sk_plr_crowbar");
|
||||
|
||||
// Glock Round
|
||||
gSkillData.plrDmg9MM = GetSkillCvar( "sk_plr_9mm_bullet");
|
||||
|
||||
// 357 Round
|
||||
gSkillData.plrDmg357 = GetSkillCvar( "sk_plr_357_bullet");
|
||||
|
||||
// MP5 Round
|
||||
gSkillData.plrDmgMP5 = GetSkillCvar( "sk_plr_9mmAR_bullet");
|
||||
|
||||
// M203 grenade
|
||||
gSkillData.plrDmgM203Grenade = GetSkillCvar( "sk_plr_9mmAR_grenade");
|
||||
|
||||
// Shotgun buckshot
|
||||
gSkillData.plrDmgBuckshot = GetSkillCvar( "sk_plr_buckshot");
|
||||
|
||||
// Crossbow
|
||||
gSkillData.plrDmgCrossbowClient = GetSkillCvar( "sk_plr_xbow_bolt_client");
|
||||
gSkillData.plrDmgCrossbowMonster = GetSkillCvar( "sk_plr_xbow_bolt_monster");
|
||||
|
||||
// RPG
|
||||
gSkillData.plrDmgRPG = GetSkillCvar( "sk_plr_rpg");
|
||||
|
||||
// Gauss gun
|
||||
gSkillData.plrDmgGauss = GetSkillCvar( "sk_plr_gauss");
|
||||
|
||||
// Egon Gun
|
||||
gSkillData.plrDmgEgonNarrow = GetSkillCvar( "sk_plr_egon_narrow");
|
||||
gSkillData.plrDmgEgonWide = GetSkillCvar( "sk_plr_egon_wide");
|
||||
|
||||
// Hand Grendade
|
||||
gSkillData.plrDmgHandGrenade = GetSkillCvar( "sk_plr_hand_grenade");
|
||||
|
||||
// Satchel Charge
|
||||
gSkillData.plrDmgSatchel = GetSkillCvar( "sk_plr_satchel");
|
||||
|
||||
// Tripmine
|
||||
gSkillData.plrDmgTripmine = GetSkillCvar( "sk_plr_tripmine");
|
||||
|
||||
// MONSTER WEAPONS
|
||||
gSkillData.monDmg12MM = GetSkillCvar( "sk_12mm_bullet");
|
||||
gSkillData.monDmgMP5 = GetSkillCvar ("sk_9mmAR_bullet" );
|
||||
gSkillData.monDmg9MM = GetSkillCvar( "sk_9mm_bullet");
|
||||
|
||||
// MONSTER HORNET
|
||||
gSkillData.monDmgHornet = GetSkillCvar( "sk_hornet_dmg");
|
||||
|
||||
// PLAYER HORNET
|
||||
// Up to this point, player hornet damage and monster hornet damage were both using
|
||||
// monDmgHornet to determine how much damage to do. In tuning the hivehand, we now need
|
||||
// to separate player damage and monster hivehand damage. Since it's so late in the project, we've
|
||||
// added plrDmgHornet to the SKILLDATA struct, but not to the engine CVar list, so it's inaccesible
|
||||
// via SKILLS.CFG. Any player hivehand tuning must take place in the code. (sjb)
|
||||
gSkillData.plrDmgHornet = 7;
|
||||
|
||||
|
||||
// HEALTH/CHARGE
|
||||
gSkillData.suitchargerCapacity = GetSkillCvar( "sk_suitcharger" );
|
||||
gSkillData.batteryCapacity = GetSkillCvar( "sk_battery" );
|
||||
gSkillData.healthchargerCapacity = GetSkillCvar ( "sk_healthcharger" );
|
||||
gSkillData.healthkitCapacity = GetSkillCvar ( "sk_healthkit" );
|
||||
gSkillData.scientistHeal = GetSkillCvar ( "sk_scientist_heal" );
|
||||
|
||||
// monster damage adj
|
||||
gSkillData.monHead = GetSkillCvar( "sk_monster_head" );
|
||||
gSkillData.monChest = GetSkillCvar( "sk_monster_chest" );
|
||||
gSkillData.monStomach = GetSkillCvar( "sk_monster_stomach" );
|
||||
gSkillData.monLeg = GetSkillCvar( "sk_monster_leg" );
|
||||
gSkillData.monArm = GetSkillCvar( "sk_monster_arm" );
|
||||
|
||||
// player damage adj
|
||||
gSkillData.plrHead = GetSkillCvar( "sk_player_head" );
|
||||
gSkillData.plrChest = GetSkillCvar( "sk_player_chest" );
|
||||
gSkillData.plrStomach = GetSkillCvar( "sk_player_stomach" );
|
||||
gSkillData.plrLeg = GetSkillCvar( "sk_player_leg" );
|
||||
gSkillData.plrArm = GetSkillCvar( "sk_player_arm" );
|
||||
}
|
||||
|
||||
//=========================================================
|
||||
// instantiate the proper game rules object
|
||||
//=========================================================
|
||||
|
||||
CGameRules *InstallGameRules( void )
|
||||
{
|
||||
SERVER_COMMAND( "exec game.rc\n" );
|
||||
g_engfuncs.pfnServerExecute(); // stupid fucking winspool.h AGRHHH!!!!
|
||||
|
||||
if ( !gpGlobals->deathmatch )
|
||||
{
|
||||
// generic half-life
|
||||
return new CHalfLifeRules;
|
||||
}
|
||||
else
|
||||
{
|
||||
if ( teamplay->value > 0 )
|
||||
{
|
||||
// teamplay
|
||||
|
||||
g_teamplay = 1;
|
||||
return new CHalfLifeTeamplay;
|
||||
}
|
||||
if ((int)gpGlobals->deathmatch == 1)
|
||||
{
|
||||
// vanilla deathmatch
|
||||
g_teamplay = 0;
|
||||
return new CHalfLifeMultiplay;
|
||||
}
|
||||
else
|
||||
{
|
||||
// vanilla deathmatch??
|
||||
g_teamplay = 0;
|
||||
return new CHalfLifeMultiplay;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
|
@ -0,0 +1,359 @@
|
|||
/***
|
||||
*
|
||||
* 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.
|
||||
*
|
||||
****/
|
||||
//=========================================================
|
||||
// GameRules
|
||||
//=========================================================
|
||||
|
||||
//#include "weapons.h"
|
||||
//#include "items.h"
|
||||
class CBasePlayerItem;
|
||||
class CBasePlayer;
|
||||
class CItem;
|
||||
class CBasePlayerAmmo;
|
||||
|
||||
// weapon respawning return codes
|
||||
enum
|
||||
{
|
||||
GR_NONE = 0,
|
||||
|
||||
GR_WEAPON_RESPAWN_YES,
|
||||
GR_WEAPON_RESPAWN_NO,
|
||||
|
||||
GR_AMMO_RESPAWN_YES,
|
||||
GR_AMMO_RESPAWN_NO,
|
||||
|
||||
GR_ITEM_RESPAWN_YES,
|
||||
GR_ITEM_RESPAWN_NO,
|
||||
|
||||
GR_PLR_DROP_GUN_ALL,
|
||||
GR_PLR_DROP_GUN_ACTIVE,
|
||||
GR_PLR_DROP_GUN_NO,
|
||||
|
||||
GR_PLR_DROP_AMMO_ALL,
|
||||
GR_PLR_DROP_AMMO_ACTIVE,
|
||||
GR_PLR_DROP_AMMO_NO,
|
||||
};
|
||||
|
||||
// Player relationship return codes
|
||||
enum
|
||||
{
|
||||
GR_NOTTEAMMATE = 0,
|
||||
GR_TEAMMATE,
|
||||
GR_ENEMY,
|
||||
GR_ALLY,
|
||||
GR_NEUTRAL,
|
||||
};
|
||||
|
||||
class CGameRules
|
||||
{
|
||||
public:
|
||||
virtual void RefreshSkillData( void );// fill skill data struct with proper values
|
||||
virtual void Think( void ) = 0;// GR_Think - runs every server frame, should handle any timer tasks, periodic events, etc.
|
||||
virtual BOOL IsAllowedToSpawn( CBaseEntity *pEntity ) = 0; // Can this item spawn (eg monsters don't spawn in deathmatch).
|
||||
|
||||
virtual BOOL FAllowFlashlight( void ) = 0;// Are players allowed to switch on their flashlight?
|
||||
virtual BOOL FShouldSwitchWeapon( CBasePlayer *pPlayer, CBasePlayerItem *pWeapon ) = 0;// should the player switch to this weapon?
|
||||
virtual BOOL GetNextBestWeapon( CBasePlayer *pPlayer, CBasePlayerItem *pCurrentWeapon ) = 0;// I can't use this weapon anymore, get me the next best one.
|
||||
|
||||
// Functions to verify the single/multiplayer status of a game
|
||||
virtual BOOL IsMultiplayer( void ) = 0;// is this a multiplayer game? (either coop or deathmatch)
|
||||
virtual BOOL IsDeathmatch( void ) = 0;//is this a deathmatch game?
|
||||
virtual BOOL IsTeamplay( void ) { return FALSE; };// is this deathmatch game being played with team rules?
|
||||
virtual BOOL IsCoOp( void ) = 0;// is this a coop game?
|
||||
virtual const char *GetGameDescription( void ) { return "Half-Life"; } // this is the game name that gets seen in the server browser
|
||||
|
||||
// Client connection/disconnection
|
||||
virtual BOOL ClientConnected( edict_t *pEntity, const char *userinfo ) = 0;// a client just connected to the server (player hasn't spawned yet)
|
||||
virtual void InitHUD( CBasePlayer *pl ) = 0; // the client dll is ready for updating
|
||||
virtual void ClientDisconnected( edict_t *pClient ) = 0;// a client just disconnected from the server
|
||||
virtual void UpdateGameMode( CBasePlayer *pPlayer ) {} // the client needs to be informed of the current game mode
|
||||
|
||||
// Client damage rules
|
||||
virtual float FlPlayerFallDamage( CBasePlayer *pPlayer ) = 0;// this client just hit the ground after a fall. How much damage?
|
||||
virtual BOOL FPlayerCanTakeDamage( CBasePlayer *pPlayer, CBaseEntity *pAttacker ) {return TRUE;};// can this player take damage from this attacker?
|
||||
virtual BOOL ShouldAutoAim( CBasePlayer *pPlayer, edict_t *target ) { return TRUE; }
|
||||
|
||||
// Client spawn/respawn control
|
||||
virtual void PlayerSpawn( CBasePlayer *pPlayer ) = 0;// called by CBasePlayer::Spawn just before releasing player into the game
|
||||
virtual void PlayerThink( CBasePlayer *pPlayer ) = 0; // called by CBasePlayer::PreThink every frame, before physics are run and after keys are accepted
|
||||
virtual BOOL FPlayerCanRespawn( CBasePlayer *pPlayer ) = 0;// is this player allowed to respawn now?
|
||||
virtual float FlPlayerSpawnTime( CBasePlayer *pPlayer ) = 0;// When in the future will this player be able to spawn?
|
||||
virtual edict_t *GetPlayerSpawnSpot( CBasePlayer *pPlayer );// Place this player on their spawnspot and face them the proper direction.
|
||||
|
||||
virtual BOOL AllowAutoTargetCrosshair( void ) { return TRUE; };
|
||||
virtual BOOL ClientCommand( CBasePlayer *pPlayer, const char *pcmd ) { return FALSE; }; // handles the user commands; returns TRUE if command handled properly
|
||||
virtual void ClientUserInfoChanged( CBasePlayer *pPlayer, char *infobuffer ) {} // the player has changed userinfo; can change it now
|
||||
|
||||
// Client kills/scoring
|
||||
virtual int IPointsForKill( CBasePlayer *pAttacker, CBasePlayer *pKilled ) = 0;// how many points do I award whoever kills this player?
|
||||
virtual void PlayerKilled( CBasePlayer *pVictim, entvars_t *pKiller, entvars_t *pInflictor ) = 0;// Called each time a player dies
|
||||
virtual void DeathNotice( CBasePlayer *pVictim, entvars_t *pKiller, entvars_t *pInflictor )= 0;// Call this from within a GameRules class to report an obituary.
|
||||
// Weapon retrieval
|
||||
virtual BOOL CanHavePlayerItem( CBasePlayer *pPlayer, CBasePlayerItem *pWeapon );// The player is touching an CBasePlayerItem, do I give it to him?
|
||||
virtual void PlayerGotWeapon( CBasePlayer *pPlayer, CBasePlayerItem *pWeapon ) = 0;// Called each time a player picks up a weapon from the ground
|
||||
|
||||
// Weapon spawn/respawn control
|
||||
virtual int WeaponShouldRespawn( CBasePlayerItem *pWeapon ) = 0;// should this weapon respawn?
|
||||
virtual float FlWeaponRespawnTime( CBasePlayerItem *pWeapon ) = 0;// when may this weapon respawn?
|
||||
virtual float FlWeaponTryRespawn( CBasePlayerItem *pWeapon ) = 0; // can i respawn now, and if not, when should i try again?
|
||||
virtual Vector VecWeaponRespawnSpot( CBasePlayerItem *pWeapon ) = 0;// where in the world should this weapon respawn?
|
||||
|
||||
// Item retrieval
|
||||
virtual BOOL CanHaveItem( CBasePlayer *pPlayer, CItem *pItem ) = 0;// is this player allowed to take this item?
|
||||
virtual void PlayerGotItem( CBasePlayer *pPlayer, CItem *pItem ) = 0;// call each time a player picks up an item (battery, healthkit, longjump)
|
||||
|
||||
// Item spawn/respawn control
|
||||
virtual int ItemShouldRespawn( CItem *pItem ) = 0;// Should this item respawn?
|
||||
virtual float FlItemRespawnTime( CItem *pItem ) = 0;// when may this item respawn?
|
||||
virtual Vector VecItemRespawnSpot( CItem *pItem ) = 0;// where in the world should this item respawn?
|
||||
|
||||
// Ammo retrieval
|
||||
virtual BOOL CanHaveAmmo( CBasePlayer *pPlayer, const char *pszAmmoName, int iMaxCarry );// can this player take more of this ammo?
|
||||
virtual void PlayerGotAmmo( CBasePlayer *pPlayer, char *szName, int iCount ) = 0;// called each time a player picks up some ammo in the world
|
||||
|
||||
// Ammo spawn/respawn control
|
||||
virtual int AmmoShouldRespawn( CBasePlayerAmmo *pAmmo ) = 0;// should this ammo item respawn?
|
||||
virtual float FlAmmoRespawnTime( CBasePlayerAmmo *pAmmo ) = 0;// when should this ammo item respawn?
|
||||
virtual Vector VecAmmoRespawnSpot( CBasePlayerAmmo *pAmmo ) = 0;// where in the world should this ammo item respawn?
|
||||
// by default, everything spawns
|
||||
|
||||
// Healthcharger respawn control
|
||||
virtual float FlHealthChargerRechargeTime( void ) = 0;// how long until a depleted HealthCharger recharges itself?
|
||||
virtual float FlHEVChargerRechargeTime( void ) { return 0; }// how long until a depleted HealthCharger recharges itself?
|
||||
|
||||
// What happens to a dead player's weapons
|
||||
virtual int DeadPlayerWeapons( CBasePlayer *pPlayer ) = 0;// what do I do with a player's weapons when he's killed?
|
||||
|
||||
// What happens to a dead player's ammo
|
||||
virtual int DeadPlayerAmmo( CBasePlayer *pPlayer ) = 0;// Do I drop ammo when the player dies? How much?
|
||||
|
||||
// Teamplay stuff
|
||||
virtual const char *GetTeamID( CBaseEntity *pEntity ) = 0;// what team is this entity on?
|
||||
virtual int PlayerRelationship( CBaseEntity *pPlayer, CBaseEntity *pTarget ) = 0;// What is the player's relationship with this entity?
|
||||
virtual int GetTeamIndex( const char *pTeamName ) { return -1; }
|
||||
virtual const char *GetIndexedTeamName( int teamIndex ) { return ""; }
|
||||
virtual BOOL IsValidTeam( const char *pTeamName ) { return TRUE; }
|
||||
virtual void ChangePlayerTeam( CBasePlayer *pPlayer, const char *pTeamName, BOOL bKill, BOOL bGib ) {}
|
||||
virtual const char *SetDefaultPlayerTeam( CBasePlayer *pPlayer ) { return ""; }
|
||||
|
||||
// Sounds
|
||||
virtual BOOL PlayTextureSounds( void ) { return TRUE; }
|
||||
virtual BOOL PlayFootstepSounds( CBasePlayer *pl, float fvol ) { return TRUE; }
|
||||
|
||||
// Monsters
|
||||
virtual BOOL FAllowMonsters( void ) = 0;//are monsters allowed
|
||||
|
||||
// Immediately end a multiplayer game
|
||||
virtual void EndMultiplayerGame( void ) {}
|
||||
};
|
||||
|
||||
extern CGameRules *InstallGameRules( void );
|
||||
|
||||
|
||||
//=========================================================
|
||||
// CHalfLifeRules - rules for the single player Half-Life
|
||||
// game.
|
||||
//=========================================================
|
||||
class CHalfLifeRules : public CGameRules
|
||||
{
|
||||
public:
|
||||
CHalfLifeRules ( void );
|
||||
|
||||
// GR_Think
|
||||
virtual void Think( void );
|
||||
virtual BOOL IsAllowedToSpawn( CBaseEntity *pEntity );
|
||||
virtual BOOL FAllowFlashlight( void ) { return TRUE; };
|
||||
|
||||
virtual BOOL FShouldSwitchWeapon( CBasePlayer *pPlayer, CBasePlayerItem *pWeapon );
|
||||
virtual BOOL GetNextBestWeapon( CBasePlayer *pPlayer, CBasePlayerItem *pCurrentWeapon );
|
||||
|
||||
// Functions to verify the single/multiplayer status of a game
|
||||
virtual BOOL IsMultiplayer( void );
|
||||
virtual BOOL IsDeathmatch( void );
|
||||
virtual BOOL IsCoOp( void );
|
||||
|
||||
// Client connection/disconnection
|
||||
virtual BOOL ClientConnected( edict_t *pEntity, const char *userinfo );
|
||||
virtual void InitHUD( CBasePlayer *pl ); // the client dll is ready for updating
|
||||
virtual void ClientDisconnected( edict_t *pClient );
|
||||
|
||||
// Client damage rules
|
||||
virtual float FlPlayerFallDamage( CBasePlayer *pPlayer );
|
||||
|
||||
// Client spawn/respawn control
|
||||
virtual void PlayerSpawn( CBasePlayer *pPlayer );
|
||||
virtual void PlayerThink( CBasePlayer *pPlayer );
|
||||
virtual BOOL FPlayerCanRespawn( CBasePlayer *pPlayer );
|
||||
virtual float FlPlayerSpawnTime( CBasePlayer *pPlayer );
|
||||
|
||||
virtual BOOL AllowAutoTargetCrosshair( void );
|
||||
|
||||
// Client kills/scoring
|
||||
virtual int IPointsForKill( CBasePlayer *pAttacker, CBasePlayer *pKilled );
|
||||
virtual void PlayerKilled( CBasePlayer *pVictim, entvars_t *pKiller, entvars_t *pInflictor );
|
||||
virtual void DeathNotice( CBasePlayer *pVictim, entvars_t *pKiller, entvars_t *pInflictor );
|
||||
|
||||
// Weapon retrieval
|
||||
virtual void PlayerGotWeapon( CBasePlayer *pPlayer, CBasePlayerItem *pWeapon );
|
||||
|
||||
// Weapon spawn/respawn control
|
||||
virtual int WeaponShouldRespawn( CBasePlayerItem *pWeapon );
|
||||
virtual float FlWeaponRespawnTime( CBasePlayerItem *pWeapon );
|
||||
virtual float FlWeaponTryRespawn( CBasePlayerItem *pWeapon );
|
||||
virtual Vector VecWeaponRespawnSpot( CBasePlayerItem *pWeapon );
|
||||
|
||||
// Item retrieval
|
||||
virtual BOOL CanHaveItem( CBasePlayer *pPlayer, CItem *pItem );
|
||||
virtual void PlayerGotItem( CBasePlayer *pPlayer, CItem *pItem );
|
||||
|
||||
// Item spawn/respawn control
|
||||
virtual int ItemShouldRespawn( CItem *pItem );
|
||||
virtual float FlItemRespawnTime( CItem *pItem );
|
||||
virtual Vector VecItemRespawnSpot( CItem *pItem );
|
||||
|
||||
// Ammo retrieval
|
||||
virtual void PlayerGotAmmo( CBasePlayer *pPlayer, char *szName, int iCount );
|
||||
|
||||
// Ammo spawn/respawn control
|
||||
virtual int AmmoShouldRespawn( CBasePlayerAmmo *pAmmo );
|
||||
virtual float FlAmmoRespawnTime( CBasePlayerAmmo *pAmmo );
|
||||
virtual Vector VecAmmoRespawnSpot( CBasePlayerAmmo *pAmmo );
|
||||
|
||||
// Healthcharger respawn control
|
||||
virtual float FlHealthChargerRechargeTime( void );
|
||||
|
||||
// What happens to a dead player's weapons
|
||||
virtual int DeadPlayerWeapons( CBasePlayer *pPlayer );
|
||||
|
||||
// What happens to a dead player's ammo
|
||||
virtual int DeadPlayerAmmo( CBasePlayer *pPlayer );
|
||||
|
||||
// Monsters
|
||||
virtual BOOL FAllowMonsters( void );
|
||||
|
||||
// Teamplay stuff
|
||||
virtual const char *GetTeamID( CBaseEntity *pEntity ) {return "";};
|
||||
virtual int PlayerRelationship( CBaseEntity *pPlayer, CBaseEntity *pTarget );
|
||||
};
|
||||
|
||||
//=========================================================
|
||||
// CHalfLifeMultiplay - rules for the basic half life multiplayer
|
||||
// competition
|
||||
//=========================================================
|
||||
class CHalfLifeMultiplay : public CGameRules
|
||||
{
|
||||
public:
|
||||
CHalfLifeMultiplay();
|
||||
|
||||
// GR_Think
|
||||
virtual void Think( void );
|
||||
virtual void RefreshSkillData( void );
|
||||
virtual BOOL IsAllowedToSpawn( CBaseEntity *pEntity );
|
||||
virtual BOOL FAllowFlashlight( void );
|
||||
|
||||
virtual BOOL FShouldSwitchWeapon( CBasePlayer *pPlayer, CBasePlayerItem *pWeapon );
|
||||
virtual BOOL GetNextBestWeapon( CBasePlayer *pPlayer, CBasePlayerItem *pCurrentWeapon );
|
||||
|
||||
// Functions to verify the single/multiplayer status of a game
|
||||
virtual BOOL IsMultiplayer( void );
|
||||
virtual BOOL IsDeathmatch( void );
|
||||
virtual BOOL IsCoOp( void );
|
||||
|
||||
// Client connection/disconnection
|
||||
// If ClientConnected returns FALSE, the connection is rejected and the user is provided the reason specified in
|
||||
// svRejectReason
|
||||
// Only the client's name and remote address are provided to the dll for verification.
|
||||
virtual BOOL ClientConnected( edict_t *pEntity, const char *userinfo );
|
||||
virtual void InitHUD( CBasePlayer *pl ); // the client dll is ready for updating
|
||||
virtual void ClientDisconnected( edict_t *pClient );
|
||||
virtual void UpdateGameMode( CBasePlayer *pPlayer ); // the client needs to be informed of the current game mode
|
||||
|
||||
// Client damage rules
|
||||
virtual float FlPlayerFallDamage( CBasePlayer *pPlayer );
|
||||
virtual BOOL FPlayerCanTakeDamage( CBasePlayer *pPlayer, CBaseEntity *pAttacker );
|
||||
|
||||
// Client spawn/respawn control
|
||||
virtual void PlayerSpawn( CBasePlayer *pPlayer );
|
||||
virtual void PlayerThink( CBasePlayer *pPlayer );
|
||||
virtual BOOL FPlayerCanRespawn( CBasePlayer *pPlayer );
|
||||
virtual float FlPlayerSpawnTime( CBasePlayer *pPlayer );
|
||||
virtual edict_t *GetPlayerSpawnSpot( CBasePlayer *pPlayer );
|
||||
|
||||
virtual BOOL AllowAutoTargetCrosshair( void );
|
||||
|
||||
// Client kills/scoring
|
||||
virtual int IPointsForKill( CBasePlayer *pAttacker, CBasePlayer *pKilled );
|
||||
virtual void PlayerKilled( CBasePlayer *pVictim, entvars_t *pKiller, entvars_t *pInflictor );
|
||||
virtual void DeathNotice( CBasePlayer *pVictim, entvars_t *pKiller, entvars_t *pInflictor );
|
||||
|
||||
// Weapon retrieval
|
||||
virtual void PlayerGotWeapon( CBasePlayer *pPlayer, CBasePlayerItem *pWeapon );
|
||||
virtual BOOL CanHavePlayerItem( CBasePlayer *pPlayer, CBasePlayerItem *pWeapon );// The player is touching an CBasePlayerItem, do I give it to him?
|
||||
|
||||
// Weapon spawn/respawn control
|
||||
virtual int WeaponShouldRespawn( CBasePlayerItem *pWeapon );
|
||||
virtual float FlWeaponRespawnTime( CBasePlayerItem *pWeapon );
|
||||
virtual float FlWeaponTryRespawn( CBasePlayerItem *pWeapon );
|
||||
virtual Vector VecWeaponRespawnSpot( CBasePlayerItem *pWeapon );
|
||||
|
||||
// Item retrieval
|
||||
virtual BOOL CanHaveItem( CBasePlayer *pPlayer, CItem *pItem );
|
||||
virtual void PlayerGotItem( CBasePlayer *pPlayer, CItem *pItem );
|
||||
|
||||
// Item spawn/respawn control
|
||||
virtual int ItemShouldRespawn( CItem *pItem );
|
||||
virtual float FlItemRespawnTime( CItem *pItem );
|
||||
virtual Vector VecItemRespawnSpot( CItem *pItem );
|
||||
|
||||
// Ammo retrieval
|
||||
virtual void PlayerGotAmmo( CBasePlayer *pPlayer, char *szName, int iCount );
|
||||
|
||||
// Ammo spawn/respawn control
|
||||
virtual int AmmoShouldRespawn( CBasePlayerAmmo *pAmmo );
|
||||
virtual float FlAmmoRespawnTime( CBasePlayerAmmo *pAmmo );
|
||||
virtual Vector VecAmmoRespawnSpot( CBasePlayerAmmo *pAmmo );
|
||||
|
||||
// Healthcharger respawn control
|
||||
virtual float FlHealthChargerRechargeTime( void );
|
||||
virtual float FlHEVChargerRechargeTime( void );
|
||||
|
||||
// What happens to a dead player's weapons
|
||||
virtual int DeadPlayerWeapons( CBasePlayer *pPlayer );
|
||||
|
||||
// What happens to a dead player's ammo
|
||||
virtual int DeadPlayerAmmo( CBasePlayer *pPlayer );
|
||||
|
||||
// Teamplay stuff
|
||||
virtual const char *GetTeamID( CBaseEntity *pEntity ) {return "";}
|
||||
virtual int PlayerRelationship( CBaseEntity *pPlayer, CBaseEntity *pTarget );
|
||||
|
||||
virtual BOOL PlayTextureSounds( void ) { return FALSE; }
|
||||
virtual BOOL PlayFootstepSounds( CBasePlayer *pl, float fvol );
|
||||
|
||||
// Monsters
|
||||
virtual BOOL FAllowMonsters( void );
|
||||
|
||||
// Immediately end a multiplayer game
|
||||
virtual void EndMultiplayerGame( void ) { GoToIntermission(); }
|
||||
|
||||
protected:
|
||||
virtual void ChangeLevel( void );
|
||||
virtual void GoToIntermission( void );
|
||||
float m_flIntermissionEndTime;
|
||||
BOOL m_iEndIntermissionButtonHit;
|
||||
void SendMOTDToClient( edict_t *client );
|
||||
};
|
||||
|
||||
extern DLL_GLOBAL CGameRules* g_pGameRules;
|
File diff suppressed because it is too large
Load Diff
|
@ -0,0 +1,638 @@
|
|||
/***
|
||||
*
|
||||
* 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 "shake.h"
|
||||
#include "gamerules.h"
|
||||
|
||||
|
||||
#define GAUSS_PRIMARY_CHARGE_VOLUME 256// how loud gauss is while charging
|
||||
#define GAUSS_PRIMARY_FIRE_VOLUME 450// how loud gauss is when discharged
|
||||
|
||||
enum gauss_e {
|
||||
GAUSS_IDLE = 0,
|
||||
GAUSS_IDLE2,
|
||||
GAUSS_FIDGET,
|
||||
GAUSS_SPINUP,
|
||||
GAUSS_SPIN,
|
||||
GAUSS_FIRE,
|
||||
GAUSS_FIRE2,
|
||||
GAUSS_HOLSTER,
|
||||
GAUSS_DRAW
|
||||
};
|
||||
|
||||
class CGauss : 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 );
|
||||
|
||||
int m_fInAttack;
|
||||
float m_flStartCharge;
|
||||
float m_flPlayAftershock;
|
||||
void StartFire( void );
|
||||
void Fire( Vector vecOrigSrc, Vector vecDirShooting, float flDamage );
|
||||
float GetFullChargeTime( void );
|
||||
int m_iBalls;
|
||||
int m_iGlow;
|
||||
int m_iBeam;
|
||||
int m_iSoundState; // don't save this
|
||||
|
||||
float m_flNextAmmoBurn;// while charging, when to absorb another unit of player's ammo?
|
||||
|
||||
// was this weapon just fired primary or secondary?
|
||||
// we need to know so we can pick the right set of effects.
|
||||
BOOL m_fPrimaryFire;
|
||||
|
||||
private:
|
||||
unsigned short m_usGaussFire;
|
||||
unsigned short m_usGaussSpin;
|
||||
};
|
||||
LINK_ENTITY_TO_CLASS( weapon_gauss, CGauss );
|
||||
|
||||
|
||||
TYPEDESCRIPTION CGauss::m_SaveData[] =
|
||||
{
|
||||
DEFINE_FIELD( CGauss, m_fInAttack, FIELD_INTEGER ),
|
||||
DEFINE_FIELD( CGauss, m_flStartCharge, FIELD_TIME ),
|
||||
DEFINE_FIELD( CGauss, m_flPlayAftershock, FIELD_TIME ),
|
||||
DEFINE_FIELD( CGauss, m_flNextAmmoBurn, FIELD_TIME ),
|
||||
DEFINE_FIELD( CGauss, m_fPrimaryFire, FIELD_BOOLEAN ),
|
||||
};
|
||||
IMPLEMENT_SAVERESTORE( CGauss, CBasePlayerWeapon );
|
||||
|
||||
|
||||
float CGauss::GetFullChargeTime( void )
|
||||
{
|
||||
if ( g_pGameRules->IsMultiplayer() )
|
||||
{
|
||||
return 1.5;
|
||||
}
|
||||
|
||||
return 4;
|
||||
}
|
||||
|
||||
void CGauss::Spawn( )
|
||||
{
|
||||
Precache( );
|
||||
m_iId = WEAPON_GAUSS;
|
||||
SET_MODEL(ENT(pev), "models/w_gauss.mdl");
|
||||
|
||||
m_iDefaultAmmo = GAUSS_DEFAULT_GIVE;
|
||||
|
||||
FallInit();// get ready to fall down.
|
||||
}
|
||||
|
||||
|
||||
void CGauss::Precache( void )
|
||||
{
|
||||
PRECACHE_MODEL("models/w_gauss.mdl");
|
||||
PRECACHE_MODEL("models/v_gauss.mdl");
|
||||
PRECACHE_MODEL("models/p_gauss.mdl");
|
||||
|
||||
PRECACHE_SOUND("items/9mmclip1.wav");
|
||||
|
||||
PRECACHE_SOUND("weapons/gauss2.wav");
|
||||
PRECACHE_SOUND("weapons/electro4.wav");
|
||||
PRECACHE_SOUND("weapons/electro5.wav");
|
||||
PRECACHE_SOUND("weapons/electro6.wav");
|
||||
PRECACHE_SOUND("ambience/pulsemachine.wav");
|
||||
|
||||
m_iGlow = PRECACHE_MODEL( "sprites/hotglow.spr" );
|
||||
m_iBalls = PRECACHE_MODEL( "sprites/hotglow.spr" );
|
||||
m_iBeam = PRECACHE_MODEL( "sprites/smoke.spr" );
|
||||
|
||||
m_usGaussFire = PRECACHE_EVENT( 1, "events/gauss.sc" );
|
||||
m_usGaussSpin = PRECACHE_EVENT( 1, "events/gaussspin.sc" );
|
||||
}
|
||||
|
||||
int CGauss::AddToPlayer( CBasePlayer *pPlayer )
|
||||
{
|
||||
if ( CBasePlayerWeapon::AddToPlayer( pPlayer ) )
|
||||
{
|
||||
MESSAGE_BEGIN( MSG_ONE, gmsgWeapPickup, NULL, pPlayer->pev );
|
||||
WRITE_BYTE( m_iId );
|
||||
MESSAGE_END();
|
||||
return TRUE;
|
||||
}
|
||||
return FALSE;
|
||||
}
|
||||
|
||||
int CGauss::GetItemInfo(ItemInfo *p)
|
||||
{
|
||||
p->pszName = STRING(pev->classname);
|
||||
p->pszAmmo1 = "uranium";
|
||||
p->iMaxAmmo1 = URANIUM_MAX_CARRY;
|
||||
p->pszAmmo2 = NULL;
|
||||
p->iMaxAmmo2 = -1;
|
||||
p->iMaxClip = WEAPON_NOCLIP;
|
||||
p->iSlot = 3;
|
||||
p->iPosition = 1;
|
||||
p->iId = m_iId = WEAPON_GAUSS;
|
||||
p->iFlags = 0;
|
||||
p->iWeight = GAUSS_WEIGHT;
|
||||
|
||||
return 1;
|
||||
}
|
||||
|
||||
|
||||
BOOL CGauss::Deploy( )
|
||||
{
|
||||
return DefaultDeploy( "models/v_gauss.mdl", "models/p_gauss.mdl", GAUSS_DRAW, "gauss" );
|
||||
}
|
||||
|
||||
|
||||
void CGauss::Holster( int skiplocal /* = 0 */ )
|
||||
{
|
||||
m_pPlayer->m_flNextAttack = UTIL_WeaponTimeBase() + 0.5;
|
||||
// m_flTimeWeaponIdle = gpGlobals->time + RANDOM_FLOAT ( 10, 15 );
|
||||
SendWeaponAnim( GAUSS_HOLSTER );
|
||||
m_fInAttack = 0;
|
||||
EMIT_SOUND(ENT(m_pPlayer->pev), CHAN_WEAPON, "common/null.wav", 1.0, ATTN_NORM);
|
||||
}
|
||||
|
||||
|
||||
void CGauss::PrimaryAttack()
|
||||
{
|
||||
// don't fire underwater
|
||||
if (m_pPlayer->pev->waterlevel == 3)
|
||||
{
|
||||
PlayEmptySound( );
|
||||
m_flNextSecondaryAttack = m_flNextPrimaryAttack = gpGlobals->time + 0.15;
|
||||
return;
|
||||
}
|
||||
|
||||
if (m_pPlayer->m_rgAmmo[m_iPrimaryAmmoType] < 2)
|
||||
{
|
||||
PlayEmptySound( );
|
||||
m_pPlayer->m_flNextAttack = UTIL_WeaponTimeBase() + 0.5;
|
||||
return;
|
||||
}
|
||||
|
||||
m_pPlayer->m_iWeaponVolume = GAUSS_PRIMARY_FIRE_VOLUME;
|
||||
|
||||
m_fPrimaryFire = TRUE;
|
||||
|
||||
m_pPlayer->m_rgAmmo[m_iPrimaryAmmoType] -= 2;
|
||||
|
||||
StartFire();
|
||||
m_fInAttack = 0;
|
||||
m_flTimeWeaponIdle = gpGlobals->time + 1.0;
|
||||
m_pPlayer->m_flNextAttack = UTIL_WeaponTimeBase() + 0.2;
|
||||
}
|
||||
|
||||
void CGauss::SecondaryAttack()
|
||||
{
|
||||
// don't fire underwater
|
||||
if (m_pPlayer->pev->waterlevel == 3)
|
||||
{
|
||||
if ( m_fInAttack != 0 )
|
||||
{
|
||||
EMIT_SOUND_DYN(ENT(m_pPlayer->pev), CHAN_WEAPON, "weapons/electro4.wav", 1.0, ATTN_NORM, 0, 80 + RANDOM_LONG(0,0x3f));
|
||||
SendWeaponAnim( GAUSS_IDLE );
|
||||
m_fInAttack = 0;
|
||||
}
|
||||
else
|
||||
{
|
||||
PlayEmptySound( );
|
||||
}
|
||||
|
||||
m_flNextSecondaryAttack = m_flNextPrimaryAttack = gpGlobals->time + 0.5;
|
||||
return;
|
||||
}
|
||||
|
||||
if (m_fInAttack == 0)
|
||||
{
|
||||
if (m_pPlayer->m_rgAmmo[m_iPrimaryAmmoType] <= 0)
|
||||
{
|
||||
EMIT_SOUND(ENT(m_pPlayer->pev), CHAN_WEAPON, "weapons/357_cock1.wav", 0.8, ATTN_NORM);
|
||||
m_pPlayer->m_flNextAttack = UTIL_WeaponTimeBase() + 0.5;
|
||||
return;
|
||||
}
|
||||
|
||||
m_fPrimaryFire = FALSE;
|
||||
|
||||
m_pPlayer->m_rgAmmo[m_iPrimaryAmmoType]--;// take one ammo just to start the spin
|
||||
m_flNextAmmoBurn = gpGlobals->time;
|
||||
|
||||
// spin up
|
||||
m_pPlayer->m_iWeaponVolume = GAUSS_PRIMARY_CHARGE_VOLUME;
|
||||
|
||||
SendWeaponAnim( GAUSS_SPINUP );
|
||||
m_fInAttack = 1;
|
||||
m_flTimeWeaponIdle = gpGlobals->time + 0.5;
|
||||
m_flStartCharge = gpGlobals->time;
|
||||
|
||||
PLAYBACK_EVENT_FULL( 0, m_pPlayer->edict(), m_usGaussSpin, 0.0, (float *)&g_vecZero, (float *)&g_vecZero, 0.0, 0.0, 110, 0, 0, 0 );
|
||||
|
||||
m_iSoundState = SND_CHANGE_PITCH;
|
||||
}
|
||||
else if (m_fInAttack == 1)
|
||||
{
|
||||
if (m_flTimeWeaponIdle < gpGlobals->time)
|
||||
{
|
||||
SendWeaponAnim( GAUSS_SPIN );
|
||||
m_fInAttack = 2;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
if ( m_pPlayer->m_rgAmmo[m_iPrimaryAmmoType] == 0 )
|
||||
{
|
||||
// out of ammo! force the gun to fire
|
||||
StartFire();
|
||||
m_fInAttack = 0;
|
||||
m_flTimeWeaponIdle = gpGlobals->time + 1.0;
|
||||
m_pPlayer->m_flNextAttack = UTIL_WeaponTimeBase() + 1;
|
||||
return;
|
||||
}
|
||||
|
||||
// during the charging process, eat one bit of ammo every once in a while
|
||||
if ( gpGlobals->time > m_flNextAmmoBurn && m_flNextAmmoBurn != -1 )
|
||||
{
|
||||
if ( g_pGameRules->IsMultiplayer() )
|
||||
{
|
||||
m_pPlayer->m_rgAmmo[m_iPrimaryAmmoType]--;
|
||||
m_flNextAmmoBurn = gpGlobals->time + 0.1;
|
||||
}
|
||||
else
|
||||
{
|
||||
m_pPlayer->m_rgAmmo[m_iPrimaryAmmoType]--;
|
||||
m_flNextAmmoBurn = gpGlobals->time + 0.3;
|
||||
}
|
||||
}
|
||||
|
||||
if ( gpGlobals->time - m_flStartCharge >= GetFullChargeTime() )
|
||||
{
|
||||
// don't eat any more ammo after gun is fully charged.
|
||||
m_flNextAmmoBurn = -1;
|
||||
}
|
||||
|
||||
int pitch = (gpGlobals->time - m_flStartCharge) * (150/GetFullChargeTime()) + 100;
|
||||
if (pitch > 250)
|
||||
pitch = 250;
|
||||
|
||||
// ALERT( at_console, "%d %d %d\n", m_fInAttack, m_iSoundState, pitch );
|
||||
|
||||
if (m_iSoundState == 0)
|
||||
ALERT( at_console, "sound state %d\n", m_iSoundState );
|
||||
|
||||
PLAYBACK_EVENT_FULL( 0, m_pPlayer->edict(), m_usGaussSpin, 0.0, (float *)&g_vecZero, (float *)&g_vecZero, 0.0, 0.0, pitch, 0, ( m_iSoundState == SND_CHANGE_PITCH ) ? 1 : 0, 0 );
|
||||
|
||||
m_iSoundState = SND_CHANGE_PITCH; // hack for going through level transitions
|
||||
|
||||
m_pPlayer->m_iWeaponVolume = GAUSS_PRIMARY_CHARGE_VOLUME;
|
||||
|
||||
// m_flTimeWeaponIdle = gpGlobals->time + 0.1;
|
||||
if (m_flStartCharge < gpGlobals->time - 10)
|
||||
{
|
||||
// Player charged up too long. Zap him.
|
||||
EMIT_SOUND_DYN(ENT(m_pPlayer->pev), CHAN_WEAPON, "weapons/electro4.wav", 1.0, ATTN_NORM, 0, 80 + RANDOM_LONG(0,0x3f));
|
||||
EMIT_SOUND_DYN(ENT(m_pPlayer->pev), CHAN_ITEM, "weapons/electro6.wav", 1.0, ATTN_NORM, 0, 75 + RANDOM_LONG(0,0x3f));
|
||||
|
||||
m_fInAttack = 0;
|
||||
m_flTimeWeaponIdle = gpGlobals->time + 1.0;
|
||||
m_pPlayer->m_flNextAttack = UTIL_WeaponTimeBase() + 1.0;
|
||||
m_pPlayer->TakeDamage( VARS(eoNullEntity), VARS(eoNullEntity), 50, DMG_SHOCK );
|
||||
|
||||
UTIL_ScreenFade( m_pPlayer, Vector(255,128,0), 2, 0.5, 128, FFADE_IN );
|
||||
SendWeaponAnim( GAUSS_IDLE );
|
||||
|
||||
// Player may have been killed and this weapon dropped, don't execute any more code after this!
|
||||
return;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
//=========================================================
|
||||
// StartFire- since all of this code has to run and then
|
||||
// call Fire(), it was easier at this point to rip it out
|
||||
// of weaponidle() and make its own function then to try to
|
||||
// merge this into Fire(), which has some identical variable names
|
||||
//=========================================================
|
||||
void CGauss::StartFire( void )
|
||||
{
|
||||
float flDamage;
|
||||
|
||||
UTIL_MakeVectors( m_pPlayer->pev->viewangles + m_pPlayer->pev->punchangle );
|
||||
Vector vecAiming = gpGlobals->v_forward;
|
||||
Vector vecSrc = m_pPlayer->GetGunPosition( ); // + gpGlobals->v_up * -8 + gpGlobals->v_right * 8;
|
||||
|
||||
if (gpGlobals->time - m_flStartCharge > GetFullChargeTime())
|
||||
{
|
||||
flDamage = 200;
|
||||
}
|
||||
else
|
||||
{
|
||||
flDamage = 200 * ((gpGlobals->time - m_flStartCharge) / GetFullChargeTime() );
|
||||
}
|
||||
|
||||
if ( m_fPrimaryFire )
|
||||
{
|
||||
// fixed damage on primary attack
|
||||
flDamage = gSkillData.plrDmgGauss;
|
||||
}
|
||||
|
||||
if (m_fInAttack != 3)
|
||||
{
|
||||
//ALERT ( at_console, "Time:%f Damage:%f\n", gpGlobals->time - m_flStartCharge, flDamage );
|
||||
|
||||
float flZVel = m_pPlayer->pev->velocity.z;
|
||||
|
||||
if ( !m_fPrimaryFire )
|
||||
{
|
||||
m_pPlayer->pev->velocity = m_pPlayer->pev->velocity - gpGlobals->v_forward * flDamage * 5;
|
||||
}
|
||||
|
||||
if ( !g_pGameRules->IsDeathmatch() )
|
||||
{
|
||||
// in deathmatch, gauss can pop you up into the air. Not in single play.
|
||||
m_pPlayer->pev->velocity.z = flZVel;
|
||||
}
|
||||
|
||||
// player "shoot" animation
|
||||
m_pPlayer->SetAnimation( PLAYER_ATTACK1 );
|
||||
}
|
||||
// time until aftershock 'static discharge' sound
|
||||
m_flPlayAftershock = gpGlobals->time + RANDOM_FLOAT(0.3, 0.8);
|
||||
|
||||
Fire( vecSrc, vecAiming, flDamage );
|
||||
}
|
||||
|
||||
void CGauss::Fire( Vector vecOrigSrc, Vector vecDir, float flDamage )
|
||||
{
|
||||
m_pPlayer->m_iWeaponVolume = GAUSS_PRIMARY_FIRE_VOLUME;
|
||||
|
||||
Vector vecSrc = vecOrigSrc;
|
||||
Vector vecDest = vecSrc + vecDir * 8192;
|
||||
edict_t *pentIgnore;
|
||||
TraceResult tr, beam_tr;
|
||||
float flMaxFrac = 1.0;
|
||||
int nTotal = 0;
|
||||
int fHasPunched = 0;
|
||||
int fFirstBeam = 1;
|
||||
int nMaxHits = 10;
|
||||
|
||||
pentIgnore = ENT( m_pPlayer->pev );
|
||||
|
||||
// The main firing event is sent unreliably so it won't be delayed.
|
||||
PLAYBACK_EVENT_FULL( 0, m_pPlayer->edict(), m_usGaussFire, 0.0, (float *)&g_vecZero, (float *)&g_vecZero, flDamage, 0.0, 0, 0, m_fPrimaryFire ? 1 : 0, 0 );
|
||||
|
||||
// This reliable event is used to stop the spinning sound
|
||||
// It's delayed by a fraction of second to make sure it is delayed by 1 frame on the client
|
||||
// It's sent reliably anyway, which could lead to other delays
|
||||
PLAYBACK_EVENT_FULL( FEV_RELIABLE, m_pPlayer->edict(), m_usGaussFire, 0.01, (float *)&g_vecZero, (float *)&g_vecZero, 0.0, 0.0, 0, 0, 0, 1 );
|
||||
|
||||
/*
|
||||
ALERT( at_console, "%f %f %f\n%f %f %f\n",
|
||||
vecSrc.x, vecSrc.y, vecSrc.z,
|
||||
vecDest.x, vecDest.y, vecDest.z );
|
||||
*/
|
||||
|
||||
// ALERT( at_console, "%f %f\n", tr.flFraction, flMaxFrac );
|
||||
|
||||
while (flDamage > 10 && nMaxHits > 0)
|
||||
{
|
||||
nMaxHits--;
|
||||
|
||||
// ALERT( at_console, "." );
|
||||
UTIL_TraceLine(vecSrc, vecDest, dont_ignore_monsters, pentIgnore, &tr);
|
||||
|
||||
if (tr.fAllSolid)
|
||||
break;
|
||||
|
||||
CBaseEntity *pEntity = CBaseEntity::Instance(tr.pHit);
|
||||
|
||||
if (pEntity == NULL)
|
||||
break;
|
||||
|
||||
if (fFirstBeam)
|
||||
{
|
||||
m_pPlayer->pev->effects |= EF_MUZZLEFLASH;
|
||||
fFirstBeam = 0;
|
||||
}
|
||||
else
|
||||
{
|
||||
}
|
||||
|
||||
if (pEntity->pev->takedamage)
|
||||
{
|
||||
ClearMultiDamage();
|
||||
pEntity->TraceAttack( m_pPlayer->pev, flDamage, vecDir, &tr, DMG_BULLET );
|
||||
ApplyMultiDamage(m_pPlayer->pev, m_pPlayer->pev);
|
||||
}
|
||||
|
||||
// ALERT( at_console, "%s\n", STRING( pEntity->pev->classname ));
|
||||
|
||||
if ( pEntity->ReflectGauss() )
|
||||
{
|
||||
float n;
|
||||
|
||||
pentIgnore = NULL;
|
||||
|
||||
n = -DotProduct(tr.vecPlaneNormal, vecDir);
|
||||
|
||||
if (n < 0.5) // 60 degrees
|
||||
{
|
||||
// ALERT( at_console, "reflect %f\n", n );
|
||||
// reflect
|
||||
Vector r;
|
||||
|
||||
r = 2.0 * tr.vecPlaneNormal * n + vecDir;
|
||||
flMaxFrac = flMaxFrac - tr.flFraction;
|
||||
vecDir = r;
|
||||
vecSrc = tr.vecEndPos + vecDir * 8;
|
||||
vecDest = vecSrc + vecDir * 8192;
|
||||
|
||||
// explode a bit
|
||||
m_pPlayer->RadiusDamage( tr.vecEndPos, pev, m_pPlayer->pev, flDamage * n, CLASS_NONE, DMG_BLAST );
|
||||
|
||||
// lose energy
|
||||
if (n == 0) n = 0.1;
|
||||
flDamage = flDamage * (1 - n);
|
||||
}
|
||||
else
|
||||
{
|
||||
// limit it to one hole punch
|
||||
if (fHasPunched)
|
||||
break;
|
||||
fHasPunched = 1;
|
||||
|
||||
// try punching through wall if secondary attack (primary is incapable of breaking through)
|
||||
if ( !m_fPrimaryFire )
|
||||
{
|
||||
UTIL_TraceLine( tr.vecEndPos + vecDir * 8, vecDest, dont_ignore_monsters, pentIgnore, &beam_tr);
|
||||
if (!beam_tr.fAllSolid)
|
||||
{
|
||||
// trace backwards to find exit point
|
||||
UTIL_TraceLine( beam_tr.vecEndPos, tr.vecEndPos, dont_ignore_monsters, pentIgnore, &beam_tr);
|
||||
|
||||
float n = (beam_tr.vecEndPos - tr.vecEndPos).Length( );
|
||||
|
||||
if (n < flDamage)
|
||||
{
|
||||
if (n == 0) n = 1;
|
||||
flDamage -= n;
|
||||
|
||||
// exit blast damage
|
||||
//m_pPlayer->RadiusDamage( beam_tr.vecEndPos + vecDir * 8, pev, m_pPlayer->pev, flDamage, CLASS_NONE, DMG_BLAST );
|
||||
float damage_radius;
|
||||
|
||||
if ( g_pGameRules->IsMultiplayer() )
|
||||
{
|
||||
damage_radius = flDamage * 1.75; // Old code == 2.5
|
||||
}
|
||||
else
|
||||
{
|
||||
damage_radius = flDamage * 2.5;
|
||||
}
|
||||
|
||||
::RadiusDamage( beam_tr.vecEndPos + vecDir * 8, pev, m_pPlayer->pev, flDamage, damage_radius, CLASS_NONE, DMG_BLAST );
|
||||
CSoundEnt::InsertSound ( bits_SOUND_COMBAT, pev->origin, NORMAL_EXPLOSION_VOLUME, 3.0 );
|
||||
|
||||
vecSrc = beam_tr.vecEndPos + vecDir;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
//ALERT( at_console, "blocked %f\n", n );
|
||||
flDamage = 0;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
//ALERT( at_console, "blocked solid\n" );
|
||||
flDamage = 0;
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
vecSrc = tr.vecEndPos + vecDir;
|
||||
pentIgnore = ENT( pEntity->pev );
|
||||
}
|
||||
}
|
||||
// ALERT( at_console, "%d bytes\n", nTotal );
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
void CGauss::WeaponIdle( void )
|
||||
{
|
||||
ResetEmptySound( );
|
||||
|
||||
// play aftershock static discharge
|
||||
if (m_flPlayAftershock && m_flPlayAftershock < gpGlobals->time)
|
||||
{
|
||||
switch (RANDOM_LONG(0,3))
|
||||
{
|
||||
case 0: EMIT_SOUND(ENT(m_pPlayer->pev), CHAN_WEAPON, "weapons/electro4.wav", RANDOM_FLOAT(0.7, 0.8), ATTN_NORM); break;
|
||||
case 1: EMIT_SOUND(ENT(m_pPlayer->pev), CHAN_WEAPON, "weapons/electro5.wav", RANDOM_FLOAT(0.7, 0.8), ATTN_NORM); break;
|
||||
case 2: EMIT_SOUND(ENT(m_pPlayer->pev), CHAN_WEAPON, "weapons/electro6.wav", RANDOM_FLOAT(0.7, 0.8), ATTN_NORM); break;
|
||||
case 3: break; // no sound
|
||||
}
|
||||
m_flPlayAftershock = 0.0;
|
||||
}
|
||||
|
||||
if (m_flTimeWeaponIdle > gpGlobals->time)
|
||||
return;
|
||||
|
||||
if (m_fInAttack != 0)
|
||||
{
|
||||
StartFire();
|
||||
m_fInAttack = 0;
|
||||
m_flTimeWeaponIdle = gpGlobals->time + 2.0;
|
||||
}
|
||||
else
|
||||
{
|
||||
int iAnim;
|
||||
float flRand = RANDOM_FLOAT(0, 1);
|
||||
if (flRand <= 0.5)
|
||||
{
|
||||
iAnim = GAUSS_IDLE;
|
||||
m_flTimeWeaponIdle = gpGlobals->time + RANDOM_FLOAT ( 10, 15 );
|
||||
}
|
||||
else if (flRand <= 0.75)
|
||||
{
|
||||
iAnim = GAUSS_IDLE2;
|
||||
m_flTimeWeaponIdle = gpGlobals->time + RANDOM_FLOAT ( 10, 15 );
|
||||
}
|
||||
else
|
||||
{
|
||||
iAnim = GAUSS_FIDGET;
|
||||
m_flTimeWeaponIdle = gpGlobals->time + 3;
|
||||
}
|
||||
|
||||
return;
|
||||
SendWeaponAnim( iAnim );
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
class CGaussAmmo : public CBasePlayerAmmo
|
||||
{
|
||||
void Spawn( void )
|
||||
{
|
||||
Precache( );
|
||||
SET_MODEL(ENT(pev), "models/w_gaussammo.mdl");
|
||||
CBasePlayerAmmo::Spawn( );
|
||||
}
|
||||
void Precache( void )
|
||||
{
|
||||
PRECACHE_MODEL ("models/w_gaussammo.mdl");
|
||||
PRECACHE_SOUND("items/9mmclip1.wav");
|
||||
}
|
||||
BOOL AddAmmo( CBaseEntity *pOther )
|
||||
{
|
||||
if (pOther->GiveAmmo( AMMO_URANIUMBOX_GIVE, "uranium", URANIUM_MAX_CARRY ) != -1)
|
||||
{
|
||||
EMIT_SOUND(ENT(pev), CHAN_ITEM, "items/9mmclip1.wav", 1, ATTN_NORM);
|
||||
return TRUE;
|
||||
}
|
||||
return FALSE;
|
||||
}
|
||||
};
|
||||
LINK_ENTITY_TO_CLASS( ammo_gaussclip, CGaussAmmo );
|
||||
|
||||
#endif
|
|
@ -0,0 +1,141 @@
|
|||
/***
|
||||
*
|
||||
* 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.
|
||||
*
|
||||
****/
|
||||
//=========================================================
|
||||
// Generic Monster - purely for scripted sequence work.
|
||||
//=========================================================
|
||||
#include "extdll.h"
|
||||
#include "util.h"
|
||||
#include "cbase.h"
|
||||
#include "monsters.h"
|
||||
#include "schedule.h"
|
||||
#include "talkmonster.h"
|
||||
|
||||
// For holograms, make them not solid so the player can walk through them
|
||||
#define SF_GENERICMONSTER_NOTSOLID 4
|
||||
#define SF_HEAD_CONTROLLER 8
|
||||
//=========================================================
|
||||
// Monster's Anim Events Go Here
|
||||
//=========================================================
|
||||
|
||||
class CGenericMonster : public CTalkMonster
|
||||
{
|
||||
public:
|
||||
void Spawn( void );
|
||||
void Precache( void );
|
||||
void SetYawSpeed( void );
|
||||
int Classify ( void );
|
||||
void HandleAnimEvent( MonsterEvent_t *pEvent );
|
||||
int ISoundMask ( void );
|
||||
};
|
||||
LINK_ENTITY_TO_CLASS( monster_generic, CGenericMonster );
|
||||
|
||||
//=========================================================
|
||||
// Classify - indicates this monster's place in the
|
||||
// relationship table.
|
||||
//=========================================================
|
||||
int CGenericMonster :: Classify ( void )
|
||||
{
|
||||
return CLASS_PLAYER_ALLY;
|
||||
}
|
||||
|
||||
//=========================================================
|
||||
// SetYawSpeed - allows each sequence to have a different
|
||||
// turn rate associated with it.
|
||||
//=========================================================
|
||||
void CGenericMonster :: SetYawSpeed ( void )
|
||||
{
|
||||
int ys;
|
||||
|
||||
switch ( m_Activity )
|
||||
{
|
||||
case ACT_IDLE:
|
||||
default:
|
||||
ys = 90;
|
||||
}
|
||||
|
||||
pev->yaw_speed = ys;
|
||||
}
|
||||
|
||||
//=========================================================
|
||||
// HandleAnimEvent - catches the monster-specific messages
|
||||
// that occur when tagged animation frames are played.
|
||||
//=========================================================
|
||||
void CGenericMonster :: HandleAnimEvent( MonsterEvent_t *pEvent )
|
||||
{
|
||||
switch( pEvent->event )
|
||||
{
|
||||
case 0:
|
||||
default:
|
||||
CBaseMonster::HandleAnimEvent( pEvent );
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
//=========================================================
|
||||
// ISoundMask - generic monster can't hear.
|
||||
//=========================================================
|
||||
int CGenericMonster :: ISoundMask ( void )
|
||||
{
|
||||
return NULL;
|
||||
}
|
||||
|
||||
//=========================================================
|
||||
// Spawn
|
||||
//=========================================================
|
||||
void CGenericMonster :: Spawn()
|
||||
{
|
||||
Precache();
|
||||
|
||||
SET_MODEL( ENT(pev), STRING(pev->model) );
|
||||
|
||||
if ( FStrEq( STRING(pev->model), "models/player.mdl" ) || FStrEq( STRING(pev->model), "models/holo.mdl" ) )
|
||||
UTIL_SetSize(pev, VEC_HULL_MIN, VEC_HULL_MAX);
|
||||
else
|
||||
UTIL_SetSize(pev, VEC_HUMAN_HULL_MIN, VEC_HUMAN_HULL_MAX);
|
||||
|
||||
pev->solid = SOLID_SLIDEBOX;
|
||||
pev->movetype = MOVETYPE_STEP;
|
||||
m_bloodColor = BLOOD_COLOR_RED;
|
||||
pev->health = 8;
|
||||
m_flFieldOfView = 0.5;// indicates the width of this monster's forward view cone ( as a dotproduct result )
|
||||
m_MonsterState = MONSTERSTATE_NONE;
|
||||
|
||||
MonsterInit();
|
||||
|
||||
if ( pev->spawnflags & SF_HEAD_CONTROLLER )
|
||||
{
|
||||
m_afCapability = bits_CAP_TURN_HEAD;
|
||||
}
|
||||
|
||||
if ( pev->spawnflags & SF_GENERICMONSTER_NOTSOLID )
|
||||
{
|
||||
pev->solid = SOLID_NOT;
|
||||
pev->takedamage = DAMAGE_NO;
|
||||
}
|
||||
}
|
||||
|
||||
//=========================================================
|
||||
// Precache - precaches all resources this monster needs
|
||||
//=========================================================
|
||||
void CGenericMonster :: Precache()
|
||||
{
|
||||
CTalkMonster::Precache();
|
||||
TalkInit();
|
||||
PRECACHE_MODEL( (char *)STRING(pev->model) );
|
||||
}
|
||||
|
||||
//=========================================================
|
||||
// AI Schedules Specific to this monster
|
||||
//=========================================================
|
|
@ -0,0 +1,488 @@
|
|||
/***
|
||||
*
|
||||
* 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.
|
||||
*
|
||||
****/
|
||||
/*
|
||||
|
||||
===== generic grenade.cpp ========================================================
|
||||
|
||||
*/
|
||||
|
||||
#include "extdll.h"
|
||||
#include "util.h"
|
||||
#include "cbase.h"
|
||||
#include "monsters.h"
|
||||
#include "weapons.h"
|
||||
#include "nodes.h"
|
||||
#include "soundent.h"
|
||||
#include "decals.h"
|
||||
|
||||
|
||||
//===================grenade
|
||||
|
||||
|
||||
LINK_ENTITY_TO_CLASS( grenade, CGrenade );
|
||||
|
||||
// Grenades flagged with this will be triggered when the owner calls detonateSatchelCharges
|
||||
#define SF_DETONATE 0x0001
|
||||
|
||||
//
|
||||
// Grenade Explode
|
||||
//
|
||||
void CGrenade::Explode( Vector vecSrc, Vector vecAim )
|
||||
{
|
||||
TraceResult tr;
|
||||
UTIL_TraceLine ( pev->origin, pev->origin + Vector ( 0, 0, -32 ), ignore_monsters, ENT(pev), & tr);
|
||||
|
||||
Explode( &tr, DMG_BLAST );
|
||||
}
|
||||
|
||||
// UNDONE: temporary scorching for PreAlpha - find a less sleazy permenant solution.
|
||||
void CGrenade::Explode( TraceResult *pTrace, int bitsDamageType )
|
||||
{
|
||||
float flRndSound;// sound randomizer
|
||||
|
||||
pev->model = iStringNull;//invisible
|
||||
pev->solid = SOLID_NOT;// intangible
|
||||
|
||||
pev->takedamage = DAMAGE_NO;
|
||||
|
||||
// Pull out of the wall a bit
|
||||
if ( pTrace->flFraction != 1.0 )
|
||||
{
|
||||
pev->origin = pTrace->vecEndPos + (pTrace->vecPlaneNormal * (pev->dmg - 24) * 0.6);
|
||||
}
|
||||
|
||||
int iContents = UTIL_PointContents ( pev->origin );
|
||||
|
||||
MESSAGE_BEGIN( MSG_PAS, SVC_TEMPENTITY, pev->origin );
|
||||
WRITE_BYTE( TE_EXPLOSION ); // This makes a dynamic light and the explosion sprites/sound
|
||||
WRITE_COORD( pev->origin.x ); // Send to PAS because of the sound
|
||||
WRITE_COORD( pev->origin.y );
|
||||
WRITE_COORD( pev->origin.z );
|
||||
if (iContents != CONTENTS_WATER)
|
||||
{
|
||||
WRITE_SHORT( g_sModelIndexFireball );
|
||||
}
|
||||
else
|
||||
{
|
||||
WRITE_SHORT( g_sModelIndexWExplosion );
|
||||
}
|
||||
WRITE_BYTE( (pev->dmg - 50) * .60 ); // scale * 10
|
||||
WRITE_BYTE( 15 ); // framerate
|
||||
WRITE_BYTE( TE_EXPLFLAG_NONE );
|
||||
MESSAGE_END();
|
||||
|
||||
CSoundEnt::InsertSound ( bits_SOUND_COMBAT, pev->origin, NORMAL_EXPLOSION_VOLUME, 3.0 );
|
||||
entvars_t *pevOwner;
|
||||
if ( pev->owner )
|
||||
pevOwner = VARS( pev->owner );
|
||||
else
|
||||
pevOwner = NULL;
|
||||
|
||||
pev->owner = NULL; // can't traceline attack owner if this is set
|
||||
|
||||
RadiusDamage ( pev, pevOwner, pev->dmg, CLASS_NONE, bitsDamageType );
|
||||
|
||||
if ( RANDOM_FLOAT( 0 , 1 ) < 0.5 )
|
||||
{
|
||||
UTIL_DecalTrace( pTrace, DECAL_SCORCH1 );
|
||||
}
|
||||
else
|
||||
{
|
||||
UTIL_DecalTrace( pTrace, DECAL_SCORCH2 );
|
||||
}
|
||||
|
||||
flRndSound = RANDOM_FLOAT( 0 , 1 );
|
||||
|
||||
switch ( RANDOM_LONG( 0, 2 ) )
|
||||
{
|
||||
case 0: EMIT_SOUND(ENT(pev), CHAN_VOICE, "weapons/debris1.wav", 0.55, ATTN_NORM); break;
|
||||
case 1: EMIT_SOUND(ENT(pev), CHAN_VOICE, "weapons/debris2.wav", 0.55, ATTN_NORM); break;
|
||||
case 2: EMIT_SOUND(ENT(pev), CHAN_VOICE, "weapons/debris3.wav", 0.55, ATTN_NORM); break;
|
||||
}
|
||||
|
||||
pev->effects |= EF_NODRAW;
|
||||
SetThink( Smoke );
|
||||
pev->velocity = g_vecZero;
|
||||
pev->nextthink = gpGlobals->time + 0.3;
|
||||
|
||||
if (iContents != CONTENTS_WATER)
|
||||
{
|
||||
int sparkCount = RANDOM_LONG(0,3);
|
||||
for ( int i = 0; i < sparkCount; i++ )
|
||||
Create( "spark_shower", pev->origin, pTrace->vecPlaneNormal, NULL );
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
void CGrenade::Smoke( void )
|
||||
{
|
||||
if (UTIL_PointContents ( pev->origin ) == CONTENTS_WATER)
|
||||
{
|
||||
UTIL_Bubbles( pev->origin - Vector( 64, 64, 64 ), pev->origin + Vector( 64, 64, 64 ), 100 );
|
||||
}
|
||||
else
|
||||
{
|
||||
MESSAGE_BEGIN( MSG_PVS, SVC_TEMPENTITY, pev->origin );
|
||||
WRITE_BYTE( TE_SMOKE );
|
||||
WRITE_COORD( pev->origin.x );
|
||||
WRITE_COORD( pev->origin.y );
|
||||
WRITE_COORD( pev->origin.z );
|
||||
WRITE_SHORT( g_sModelIndexSmoke );
|
||||
WRITE_BYTE( (pev->dmg - 50) * 0.80 ); // scale * 10
|
||||
WRITE_BYTE( 12 ); // framerate
|
||||
MESSAGE_END();
|
||||
}
|
||||
UTIL_Remove( this );
|
||||
}
|
||||
|
||||
void CGrenade::Killed( entvars_t *pevAttacker, int iGib )
|
||||
{
|
||||
Detonate( );
|
||||
}
|
||||
|
||||
|
||||
// Timed grenade, this think is called when time runs out.
|
||||
void CGrenade::DetonateUse( CBaseEntity *pActivator, CBaseEntity *pCaller, USE_TYPE useType, float value )
|
||||
{
|
||||
SetThink( Detonate );
|
||||
pev->nextthink = gpGlobals->time;
|
||||
}
|
||||
|
||||
void CGrenade::PreDetonate( void )
|
||||
{
|
||||
CSoundEnt::InsertSound ( bits_SOUND_DANGER, pev->origin, 400, 0.3 );
|
||||
|
||||
SetThink( Detonate );
|
||||
pev->nextthink = gpGlobals->time + 1;
|
||||
}
|
||||
|
||||
|
||||
void CGrenade::Detonate( void )
|
||||
{
|
||||
TraceResult tr;
|
||||
Vector vecSpot;// trace starts here!
|
||||
|
||||
vecSpot = pev->origin + Vector ( 0 , 0 , 8 );
|
||||
UTIL_TraceLine ( vecSpot, vecSpot + Vector ( 0, 0, -40 ), ignore_monsters, ENT(pev), & tr);
|
||||
|
||||
Explode( &tr, DMG_BLAST );
|
||||
}
|
||||
|
||||
|
||||
//
|
||||
// Contact grenade, explode when it touches something
|
||||
//
|
||||
void CGrenade::ExplodeTouch( CBaseEntity *pOther )
|
||||
{
|
||||
TraceResult tr;
|
||||
Vector vecSpot;// trace starts here!
|
||||
|
||||
pev->enemy = pOther->edict();
|
||||
|
||||
vecSpot = pev->origin - pev->velocity.Normalize() * 32;
|
||||
UTIL_TraceLine( vecSpot, vecSpot + pev->velocity.Normalize() * 64, ignore_monsters, ENT(pev), &tr );
|
||||
|
||||
Explode( &tr, DMG_BLAST );
|
||||
}
|
||||
|
||||
|
||||
void CGrenade::DangerSoundThink( void )
|
||||
{
|
||||
if (!IsInWorld())
|
||||
{
|
||||
UTIL_Remove( this );
|
||||
return;
|
||||
}
|
||||
|
||||
CSoundEnt::InsertSound ( bits_SOUND_DANGER, pev->origin + pev->velocity * 0.5, pev->velocity.Length( ), 0.2 );
|
||||
pev->nextthink = gpGlobals->time + 0.2;
|
||||
|
||||
if (pev->waterlevel != 0)
|
||||
{
|
||||
pev->velocity = pev->velocity * 0.5;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
void CGrenade::BounceTouch( CBaseEntity *pOther )
|
||||
{
|
||||
// don't hit the guy that launched this grenade
|
||||
if ( pOther->edict() == pev->owner )
|
||||
return;
|
||||
|
||||
// only do damage if we're moving fairly fast
|
||||
if (m_flNextAttack < gpGlobals->time && pev->velocity.Length() > 100)
|
||||
{
|
||||
entvars_t *pevOwner = VARS( pev->owner );
|
||||
if (pevOwner)
|
||||
{
|
||||
TraceResult tr = UTIL_GetGlobalTrace( );
|
||||
ClearMultiDamage( );
|
||||
pOther->TraceAttack(pevOwner, 1, gpGlobals->v_forward, &tr, DMG_CLUB );
|
||||
ApplyMultiDamage( pev, pevOwner);
|
||||
}
|
||||
m_flNextAttack = gpGlobals->time + 1.0; // debounce
|
||||
}
|
||||
|
||||
Vector vecTestVelocity;
|
||||
// pev->avelocity = Vector (300, 300, 300);
|
||||
|
||||
// this is my heuristic for modulating the grenade velocity because grenades dropped purely vertical
|
||||
// or thrown very far tend to slow down too quickly for me to always catch just by testing velocity.
|
||||
// trimming the Z velocity a bit seems to help quite a bit.
|
||||
vecTestVelocity = pev->velocity;
|
||||
vecTestVelocity.z *= 0.45;
|
||||
|
||||
if ( !m_fRegisteredSound && vecTestVelocity.Length() <= 60 )
|
||||
{
|
||||
//ALERT( at_console, "Grenade Registered!: %f\n", vecTestVelocity.Length() );
|
||||
|
||||
// grenade is moving really slow. It's probably very close to where it will ultimately stop moving.
|
||||
// go ahead and emit the danger sound.
|
||||
|
||||
// register a radius louder than the explosion, so we make sure everyone gets out of the way
|
||||
CSoundEnt::InsertSound ( bits_SOUND_DANGER, pev->origin, pev->dmg / 0.4, 0.3 );
|
||||
m_fRegisteredSound = TRUE;
|
||||
}
|
||||
|
||||
if (pev->flags & FL_ONGROUND)
|
||||
{
|
||||
// add a bit of static friction
|
||||
pev->velocity = pev->velocity * 0.8;
|
||||
|
||||
pev->sequence = RANDOM_LONG( 1, 1 );
|
||||
}
|
||||
else
|
||||
{
|
||||
// play bounce sound
|
||||
BounceSound();
|
||||
}
|
||||
pev->framerate = pev->velocity.Length() / 200.0;
|
||||
if (pev->framerate > 1.0)
|
||||
pev->framerate = 1;
|
||||
else if (pev->framerate < 0.5)
|
||||
pev->framerate = 0;
|
||||
|
||||
}
|
||||
|
||||
|
||||
|
||||
void CGrenade::SlideTouch( CBaseEntity *pOther )
|
||||
{
|
||||
// don't hit the guy that launched this grenade
|
||||
if ( pOther->edict() == pev->owner )
|
||||
return;
|
||||
|
||||
// pev->avelocity = Vector (300, 300, 300);
|
||||
|
||||
if (pev->flags & FL_ONGROUND)
|
||||
{
|
||||
// add a bit of static friction
|
||||
pev->velocity = pev->velocity * 0.95;
|
||||
|
||||
if (pev->velocity.x != 0 || pev->velocity.y != 0)
|
||||
{
|
||||
// maintain sliding sound
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
BounceSound();
|
||||
}
|
||||
}
|
||||
|
||||
void CGrenade :: BounceSound( void )
|
||||
{
|
||||
switch ( RANDOM_LONG( 0, 2 ) )
|
||||
{
|
||||
case 0: EMIT_SOUND(ENT(pev), CHAN_VOICE, "weapons/grenade_hit1.wav", 0.25, ATTN_NORM); break;
|
||||
case 1: EMIT_SOUND(ENT(pev), CHAN_VOICE, "weapons/grenade_hit2.wav", 0.25, ATTN_NORM); break;
|
||||
case 2: EMIT_SOUND(ENT(pev), CHAN_VOICE, "weapons/grenade_hit3.wav", 0.25, ATTN_NORM); break;
|
||||
}
|
||||
}
|
||||
|
||||
void CGrenade :: TumbleThink( void )
|
||||
{
|
||||
if (!IsInWorld())
|
||||
{
|
||||
UTIL_Remove( this );
|
||||
return;
|
||||
}
|
||||
|
||||
StudioFrameAdvance( );
|
||||
pev->nextthink = gpGlobals->time + 0.1;
|
||||
|
||||
if (pev->dmgtime - 1 < gpGlobals->time)
|
||||
{
|
||||
CSoundEnt::InsertSound ( bits_SOUND_DANGER, pev->origin + pev->velocity * (pev->dmgtime - gpGlobals->time), 400, 0.1 );
|
||||
}
|
||||
|
||||
if (pev->dmgtime <= gpGlobals->time)
|
||||
{
|
||||
SetThink( Detonate );
|
||||
}
|
||||
if (pev->waterlevel != 0)
|
||||
{
|
||||
pev->velocity = pev->velocity * 0.5;
|
||||
pev->framerate = 0.2;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
void CGrenade:: Spawn( void )
|
||||
{
|
||||
pev->movetype = MOVETYPE_BOUNCE;
|
||||
pev->classname = MAKE_STRING( "grenade" );
|
||||
|
||||
pev->solid = SOLID_BBOX;
|
||||
|
||||
SET_MODEL(ENT(pev), "models/grenade.mdl");
|
||||
UTIL_SetSize(pev, Vector( 0, 0, 0), Vector(0, 0, 0));
|
||||
|
||||
pev->dmg = 100;
|
||||
m_fRegisteredSound = FALSE;
|
||||
}
|
||||
|
||||
|
||||
CGrenade *CGrenade::ShootContact( entvars_t *pevOwner, Vector vecStart, Vector vecVelocity )
|
||||
{
|
||||
CGrenade *pGrenade = GetClassPtr( (CGrenade *)NULL );
|
||||
pGrenade->Spawn();
|
||||
// contact grenades arc lower
|
||||
pGrenade->pev->gravity = 0.5;// lower gravity since grenade is aerodynamic and engine doesn't know it.
|
||||
UTIL_SetOrigin( pGrenade->pev, vecStart );
|
||||
pGrenade->pev->velocity = vecVelocity;
|
||||
pGrenade->pev->angles = UTIL_VecToAngles (pGrenade->pev->velocity);
|
||||
pGrenade->pev->owner = ENT(pevOwner);
|
||||
|
||||
// make monsters afaid of it while in the air
|
||||
pGrenade->SetThink( DangerSoundThink );
|
||||
pGrenade->pev->nextthink = gpGlobals->time;
|
||||
|
||||
// Tumble in air
|
||||
pGrenade->pev->avelocity.x = RANDOM_FLOAT ( -100, -500 );
|
||||
|
||||
// Explode on contact
|
||||
pGrenade->SetTouch( ExplodeTouch );
|
||||
|
||||
pGrenade->pev->dmg = gSkillData.plrDmgM203Grenade;
|
||||
|
||||
return pGrenade;
|
||||
}
|
||||
|
||||
|
||||
CGrenade * CGrenade:: ShootTimed( entvars_t *pevOwner, Vector vecStart, Vector vecVelocity, float time )
|
||||
{
|
||||
CGrenade *pGrenade = GetClassPtr( (CGrenade *)NULL );
|
||||
pGrenade->Spawn();
|
||||
UTIL_SetOrigin( pGrenade->pev, vecStart );
|
||||
pGrenade->pev->velocity = vecVelocity;
|
||||
pGrenade->pev->angles = UTIL_VecToAngles(pGrenade->pev->velocity);
|
||||
pGrenade->pev->owner = ENT(pevOwner);
|
||||
|
||||
pGrenade->SetTouch( BounceTouch ); // Bounce if touched
|
||||
|
||||
// Take one second off of the desired detonation time and set the think to PreDetonate. PreDetonate
|
||||
// will insert a DANGER sound into the world sound list and delay detonation for one second so that
|
||||
// the grenade explodes after the exact amount of time specified in the call to ShootTimed().
|
||||
|
||||
pGrenade->pev->dmgtime = gpGlobals->time + time;
|
||||
pGrenade->SetThink( TumbleThink );
|
||||
pGrenade->pev->nextthink = gpGlobals->time + 0.1;
|
||||
if (time < 0.1)
|
||||
{
|
||||
pGrenade->pev->nextthink = gpGlobals->time;
|
||||
pGrenade->pev->velocity = Vector( 0, 0, 0 );
|
||||
}
|
||||
|
||||
pGrenade->pev->sequence = RANDOM_LONG( 3, 6 );
|
||||
pGrenade->pev->framerate = 1.0;
|
||||
|
||||
// Tumble through the air
|
||||
// pGrenade->pev->avelocity.x = -400;
|
||||
|
||||
pGrenade->pev->gravity = 0.5;
|
||||
pGrenade->pev->friction = 0.8;
|
||||
|
||||
SET_MODEL(ENT(pGrenade->pev), "models/w_grenade.mdl");
|
||||
pGrenade->pev->dmg = 100;
|
||||
|
||||
return pGrenade;
|
||||
}
|
||||
|
||||
|
||||
CGrenade * CGrenade :: ShootSatchelCharge( entvars_t *pevOwner, Vector vecStart, Vector vecVelocity )
|
||||
{
|
||||
CGrenade *pGrenade = GetClassPtr( (CGrenade *)NULL );
|
||||
pGrenade->pev->movetype = MOVETYPE_BOUNCE;
|
||||
pGrenade->pev->classname = MAKE_STRING( "grenade" );
|
||||
|
||||
pGrenade->pev->solid = SOLID_BBOX;
|
||||
|
||||
SET_MODEL(ENT(pGrenade->pev), "models/grenade.mdl"); // Change this to satchel charge model
|
||||
|
||||
UTIL_SetSize(pGrenade->pev, Vector( 0, 0, 0), Vector(0, 0, 0));
|
||||
|
||||
pGrenade->pev->dmg = 200;
|
||||
UTIL_SetOrigin( pGrenade->pev, vecStart );
|
||||
pGrenade->pev->velocity = vecVelocity;
|
||||
pGrenade->pev->angles = g_vecZero;
|
||||
pGrenade->pev->owner = ENT(pevOwner);
|
||||
|
||||
// Detonate in "time" seconds
|
||||
pGrenade->SetThink( SUB_DoNothing );
|
||||
pGrenade->SetUse( DetonateUse );
|
||||
pGrenade->SetTouch( SlideTouch );
|
||||
pGrenade->pev->spawnflags = SF_DETONATE;
|
||||
|
||||
pGrenade->pev->friction = 0.9;
|
||||
|
||||
return pGrenade;
|
||||
}
|
||||
|
||||
|
||||
|
||||
void CGrenade :: UseSatchelCharges( entvars_t *pevOwner, SATCHELCODE code )
|
||||
{
|
||||
edict_t *pentFind;
|
||||
edict_t *pentOwner;
|
||||
|
||||
if ( !pevOwner )
|
||||
return;
|
||||
|
||||
CBaseEntity *pOwner = CBaseEntity::Instance( pevOwner );
|
||||
|
||||
pentOwner = pOwner->edict();
|
||||
|
||||
pentFind = FIND_ENTITY_BY_CLASSNAME( NULL, "grenade" );
|
||||
while ( !FNullEnt( pentFind ) )
|
||||
{
|
||||
CBaseEntity *pEnt = Instance( pentFind );
|
||||
if ( pEnt )
|
||||
{
|
||||
if ( FBitSet( pEnt->pev->spawnflags, SF_DETONATE ) && pEnt->pev->owner == pentOwner )
|
||||
{
|
||||
if ( code == SATCHEL_DETONATE )
|
||||
pEnt->Use( pOwner, pOwner, USE_ON, 0 );
|
||||
else // SATCHEL_RELEASE
|
||||
pEnt->pev->owner = NULL;
|
||||
}
|
||||
}
|
||||
pentFind = FIND_ENTITY_BY_CLASSNAME( pentFind, "grenade" );
|
||||
}
|
||||
}
|
||||
|
||||
//======================end grenade
|
||||
|
|
@ -0,0 +1,39 @@
|
|||
/***
|
||||
*
|
||||
* 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.
|
||||
*
|
||||
****/
|
||||
/*
|
||||
|
||||
===== globals.cpp ========================================================
|
||||
|
||||
DLL-wide global variable definitions.
|
||||
They're all defined here, for convenient centralization.
|
||||
Source files that need them should "extern ..." declare each
|
||||
variable, to better document what globals they care about.
|
||||
|
||||
*/
|
||||
|
||||
#include "extdll.h"
|
||||
#include "util.h"
|
||||
#include "cbase.h"
|
||||
#include "soundent.h"
|
||||
|
||||
DLL_GLOBAL ULONG g_ulFrameCount;
|
||||
DLL_GLOBAL ULONG g_ulModelIndexEyes;
|
||||
DLL_GLOBAL ULONG g_ulModelIndexPlayer;
|
||||
DLL_GLOBAL Vector g_vecAttackDir;
|
||||
DLL_GLOBAL int g_iSkillLevel;
|
||||
DLL_GLOBAL int gDisplayTitle;
|
||||
DLL_GLOBAL BOOL g_fGameOver;
|
||||
DLL_GLOBAL const Vector g_vecZero = Vector(0,0,0);
|
||||
DLL_GLOBAL int g_Language;
|
|
@ -0,0 +1,297 @@
|
|||
/***
|
||||
*
|
||||
* 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.
|
||||
*
|
||||
****/
|
||||
|
||||
#include "extdll.h"
|
||||
#include "util.h"
|
||||
#include "cbase.h"
|
||||
#include "monsters.h"
|
||||
#include "weapons.h"
|
||||
#include "nodes.h"
|
||||
#include "player.h"
|
||||
|
||||
enum glock_e {
|
||||
GLOCK_IDLE1 = 0,
|
||||
GLOCK_IDLE2,
|
||||
GLOCK_IDLE3,
|
||||
GLOCK_SHOOT,
|
||||
GLOCK_SHOOT_EMPTY,
|
||||
GLOCK_RELOAD,
|
||||
GLOCK_RELOAD_NOT_EMPTY,
|
||||
GLOCK_DRAW,
|
||||
GLOCK_HOLSTER,
|
||||
GLOCK_ADD_SILENCER
|
||||
};
|
||||
|
||||
class CGlock : public CBasePlayerWeapon
|
||||
{
|
||||
public:
|
||||
void Spawn( void );
|
||||
void Precache( void );
|
||||
int iItemSlot( void ) { return 2; }
|
||||
int GetItemInfo(ItemInfo *p);
|
||||
|
||||
void PrimaryAttack( void );
|
||||
void SecondaryAttack( void );
|
||||
void GlockFire( float flSpread, float flCycleTime, BOOL fUseAutoAim );
|
||||
BOOL Deploy( void );
|
||||
void Reload( void );
|
||||
void WeaponIdle( void );
|
||||
int m_iShell;
|
||||
};
|
||||
LINK_ENTITY_TO_CLASS( weapon_glock, CGlock );
|
||||
LINK_ENTITY_TO_CLASS( weapon_9mmhandgun, CGlock );
|
||||
|
||||
|
||||
void CGlock::Spawn( )
|
||||
{
|
||||
pev->classname = MAKE_STRING("weapon_9mmhandgun"); // hack to allow for old names
|
||||
Precache( );
|
||||
m_iId = WEAPON_GLOCK;
|
||||
SET_MODEL(ENT(pev), "models/w_9mmhandgun.mdl");
|
||||
|
||||
m_iDefaultAmmo = GLOCK_DEFAULT_GIVE;
|
||||
|
||||
FallInit();// get ready to fall down.
|
||||
}
|
||||
|
||||
|
||||
void CGlock::Precache( void )
|
||||
{
|
||||
PRECACHE_MODEL("models/v_9mmhandgun.mdl");
|
||||
PRECACHE_MODEL("models/w_9mmhandgun.mdl");
|
||||
PRECACHE_MODEL("models/p_9mmhandgun.mdl");
|
||||
|
||||
m_iShell = PRECACHE_MODEL ("models/shell.mdl");// brass shell
|
||||
|
||||
PRECACHE_SOUND("items/9mmclip1.wav");
|
||||
PRECACHE_SOUND("items/9mmclip2.wav");
|
||||
|
||||
PRECACHE_SOUND ("weapons/pl_gun1.wav");//silenced handgun
|
||||
PRECACHE_SOUND ("weapons/pl_gun2.wav");//silenced handgun
|
||||
PRECACHE_SOUND ("weapons/pl_gun3.wav");//handgun
|
||||
}
|
||||
|
||||
int CGlock::GetItemInfo(ItemInfo *p)
|
||||
{
|
||||
p->pszName = STRING(pev->classname);
|
||||
p->pszAmmo1 = "9mm";
|
||||
p->iMaxAmmo1 = _9MM_MAX_CARRY;
|
||||
p->pszAmmo2 = NULL;
|
||||
p->iMaxAmmo2 = -1;
|
||||
p->iMaxClip = GLOCK_MAX_CLIP;
|
||||
p->iSlot = 1;
|
||||
p->iPosition = 0;
|
||||
p->iFlags = 0;
|
||||
p->iId = m_iId = WEAPON_GLOCK;
|
||||
p->iWeight = GLOCK_WEIGHT;
|
||||
|
||||
return 1;
|
||||
}
|
||||
|
||||
BOOL CGlock::Deploy( )
|
||||
{
|
||||
// pev->body = 1;
|
||||
return DefaultDeploy( "models/v_9mmhandgun.mdl", "models/p_9mmhandgun.mdl", GLOCK_DRAW, "onehanded" );
|
||||
}
|
||||
|
||||
void CGlock::SecondaryAttack( void )
|
||||
{
|
||||
GlockFire( 0.1, 0.2, FALSE );
|
||||
}
|
||||
|
||||
void CGlock::PrimaryAttack( void )
|
||||
{
|
||||
GlockFire( 0.01, 0.3, TRUE );
|
||||
}
|
||||
|
||||
void CGlock::GlockFire( float flSpread , float flCycleTime, BOOL fUseAutoAim )
|
||||
{
|
||||
if (m_iClip <= 0)
|
||||
{
|
||||
if (m_fFireOnEmpty)
|
||||
{
|
||||
PlayEmptySound();
|
||||
m_flNextPrimaryAttack = UTIL_WeaponTimeBase() + 0.2;
|
||||
}
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
m_iClip--;
|
||||
|
||||
m_pPlayer->pev->effects = (int)(m_pPlayer->pev->effects) | EF_MUZZLEFLASH;
|
||||
|
||||
if (m_iClip != 0)
|
||||
SendWeaponAnim( GLOCK_SHOOT );
|
||||
else
|
||||
SendWeaponAnim( GLOCK_SHOOT_EMPTY );
|
||||
|
||||
// player "shoot" animation
|
||||
m_pPlayer->SetAnimation( PLAYER_ATTACK1 );
|
||||
|
||||
UTIL_MakeVectors( m_pPlayer->pev->viewangles + m_pPlayer->pev->punchangle );
|
||||
|
||||
Vector vecShellVelocity = m_pPlayer->pev->velocity
|
||||
+ gpGlobals->v_right * RANDOM_FLOAT(50,70)
|
||||
+ gpGlobals->v_up * RANDOM_FLOAT(100,150)
|
||||
+ gpGlobals->v_forward * 25;
|
||||
EjectBrass ( pev->origin + m_pPlayer->pev->view_ofs + gpGlobals->v_up * -12 + gpGlobals->v_forward * 32 + gpGlobals->v_right * 6 , vecShellVelocity, pev->angles.y, m_iShell, TE_BOUNCE_SHELL );
|
||||
|
||||
// silenced
|
||||
if (pev->body == 1)
|
||||
{
|
||||
m_pPlayer->m_iWeaponVolume = QUIET_GUN_VOLUME;
|
||||
m_pPlayer->m_iWeaponFlash = DIM_GUN_FLASH;
|
||||
|
||||
switch(RANDOM_LONG(0,1))
|
||||
{
|
||||
case 0:
|
||||
EMIT_SOUND(ENT(m_pPlayer->pev), CHAN_WEAPON, "weapons/pl_gun1.wav", RANDOM_FLOAT(0.9, 1.0), ATTN_NORM);
|
||||
break;
|
||||
case 1:
|
||||
EMIT_SOUND(ENT(m_pPlayer->pev), CHAN_WEAPON, "weapons/pl_gun2.wav", RANDOM_FLOAT(0.9, 1.0), ATTN_NORM);
|
||||
break;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
// non-silenced
|
||||
m_pPlayer->m_iWeaponVolume = NORMAL_GUN_VOLUME;
|
||||
m_pPlayer->m_iWeaponFlash = NORMAL_GUN_FLASH;
|
||||
EMIT_SOUND_DYN(ENT(m_pPlayer->pev), CHAN_WEAPON, "weapons/pl_gun3.wav", RANDOM_FLOAT(0.92, 1.0), ATTN_NORM, 0, 98 + RANDOM_LONG(0,3));
|
||||
}
|
||||
|
||||
Vector vecSrc = m_pPlayer->GetGunPosition( );
|
||||
Vector vecAiming;
|
||||
|
||||
if ( fUseAutoAim )
|
||||
{
|
||||
vecAiming = m_pPlayer->GetAutoaimVector( AUTOAIM_10DEGREES );
|
||||
}
|
||||
else
|
||||
{
|
||||
vecAiming = gpGlobals->v_forward;
|
||||
}
|
||||
|
||||
m_pPlayer->FireBullets( 1, vecSrc, vecAiming, Vector( flSpread, flSpread, flSpread ), 8192, BULLET_PLAYER_9MM, 0 );
|
||||
m_flNextPrimaryAttack = m_flNextSecondaryAttack = UTIL_WeaponTimeBase() + flCycleTime;
|
||||
|
||||
if (!m_iClip && m_pPlayer->m_rgAmmo[m_iPrimaryAmmoType] <= 0)
|
||||
// HEV suit - indicate out of ammo condition
|
||||
m_pPlayer->SetSuitUpdate("!HEV_AMO0", FALSE, 0);
|
||||
|
||||
m_flTimeWeaponIdle = UTIL_WeaponTimeBase() + RANDOM_FLOAT ( 10, 15 );
|
||||
|
||||
m_pPlayer->pev->punchangle.x -= 2;
|
||||
}
|
||||
|
||||
|
||||
void CGlock::Reload( void )
|
||||
{
|
||||
int iResult;
|
||||
|
||||
if (m_iClip == 0)
|
||||
iResult = DefaultReload( 17, GLOCK_RELOAD, 1.5 );
|
||||
else
|
||||
iResult = DefaultReload( 18, GLOCK_RELOAD_NOT_EMPTY, 1.5 );
|
||||
|
||||
if (iResult)
|
||||
{
|
||||
m_flTimeWeaponIdle = UTIL_WeaponTimeBase() + RANDOM_FLOAT ( 10, 15 );
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
void CGlock::WeaponIdle( void )
|
||||
{
|
||||
ResetEmptySound( );
|
||||
|
||||
m_pPlayer->GetAutoaimVector( AUTOAIM_10DEGREES );
|
||||
|
||||
if ( m_flTimeWeaponIdle > UTIL_WeaponTimeBase() )
|
||||
return;
|
||||
|
||||
// only idle if the slid isn't back
|
||||
if (m_iClip != 0)
|
||||
{
|
||||
int iAnim;
|
||||
float flRand = RANDOM_FLOAT(0, 1);
|
||||
if (flRand <= 0.3 + 0 * 0.75)
|
||||
{
|
||||
iAnim = GLOCK_IDLE3;
|
||||
m_flTimeWeaponIdle = UTIL_WeaponTimeBase() + 49.0 / 16;
|
||||
}
|
||||
else if (flRand <= 0.6 + 0 * 0.875)
|
||||
{
|
||||
iAnim = GLOCK_IDLE1;
|
||||
m_flTimeWeaponIdle = UTIL_WeaponTimeBase() + 60.0 / 16.0;
|
||||
}
|
||||
else
|
||||
{
|
||||
iAnim = GLOCK_IDLE2;
|
||||
m_flTimeWeaponIdle = UTIL_WeaponTimeBase() + 40.0 / 16.0;
|
||||
}
|
||||
SendWeaponAnim( iAnim );
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
class CGlockAmmo : public CBasePlayerAmmo
|
||||
{
|
||||
void Spawn( void )
|
||||
{
|
||||
Precache( );
|
||||
SET_MODEL(ENT(pev), "models/w_9mmclip.mdl");
|
||||
CBasePlayerAmmo::Spawn( );
|
||||
}
|
||||
void Precache( void )
|
||||
{
|
||||
PRECACHE_MODEL ("models/w_9mmclip.mdl");
|
||||
PRECACHE_SOUND("items/9mmclip1.wav");
|
||||
}
|
||||
BOOL AddAmmo( CBaseEntity *pOther )
|
||||
{
|
||||
if (pOther->GiveAmmo( AMMO_GLOCKCLIP_GIVE, "9mm", _9MM_MAX_CARRY ) != -1)
|
||||
{
|
||||
EMIT_SOUND(ENT(pev), CHAN_ITEM, "items/9mmclip1.wav", 1, ATTN_NORM);
|
||||
return TRUE;
|
||||
}
|
||||
return FALSE;
|
||||
}
|
||||
};
|
||||
LINK_ENTITY_TO_CLASS( ammo_glockclip, CGlockAmmo );
|
||||
LINK_ENTITY_TO_CLASS( ammo_9mmclip, CGlockAmmo );
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
|
@ -0,0 +1,237 @@
|
|||
/***
|
||||
*
|
||||
* 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.
|
||||
*
|
||||
****/
|
||||
//=========================================================
|
||||
// GMan - misunderstood servant of the people
|
||||
//=========================================================
|
||||
#include "extdll.h"
|
||||
#include "util.h"
|
||||
#include "cbase.h"
|
||||
#include "monsters.h"
|
||||
#include "schedule.h"
|
||||
#include "weapons.h"
|
||||
|
||||
//=========================================================
|
||||
// Monster's Anim Events Go Here
|
||||
//=========================================================
|
||||
|
||||
class CGMan : public CBaseMonster
|
||||
{
|
||||
public:
|
||||
void Spawn( void );
|
||||
void Precache( void );
|
||||
void SetYawSpeed( void );
|
||||
int Classify ( void );
|
||||
void HandleAnimEvent( MonsterEvent_t *pEvent );
|
||||
int ISoundMask ( void );
|
||||
|
||||
int Save( CSave &save );
|
||||
int Restore( CRestore &restore );
|
||||
static TYPEDESCRIPTION m_SaveData[];
|
||||
|
||||
void StartTask( Task_t *pTask );
|
||||
void RunTask( Task_t *pTask );
|
||||
int TakeDamage( entvars_t* pevInflictor, entvars_t* pevAttacker, float flDamage, int bitsDamageType );
|
||||
void TraceAttack( entvars_t *pevAttacker, float flDamage, Vector vecDir, TraceResult *ptr, int bitsDamageType);
|
||||
|
||||
void PlayScriptedSentence( const char *pszSentence, float duration, float volume, float attenuation, BOOL bConcurrent, CBaseEntity *pListener );
|
||||
|
||||
EHANDLE m_hPlayer;
|
||||
EHANDLE m_hTalkTarget;
|
||||
float m_flTalkTime;
|
||||
};
|
||||
LINK_ENTITY_TO_CLASS( monster_gman, CGMan );
|
||||
|
||||
|
||||
TYPEDESCRIPTION CGMan::m_SaveData[] =
|
||||
{
|
||||
DEFINE_FIELD( CGMan, m_hTalkTarget, FIELD_EHANDLE ),
|
||||
DEFINE_FIELD( CGMan, m_flTalkTime, FIELD_TIME ),
|
||||
};
|
||||
IMPLEMENT_SAVERESTORE( CGMan, CBaseMonster );
|
||||
|
||||
|
||||
//=========================================================
|
||||
// Classify - indicates this monster's place in the
|
||||
// relationship table.
|
||||
//=========================================================
|
||||
int CGMan :: Classify ( void )
|
||||
{
|
||||
return CLASS_NONE;
|
||||
}
|
||||
|
||||
//=========================================================
|
||||
// SetYawSpeed - allows each sequence to have a different
|
||||
// turn rate associated with it.
|
||||
//=========================================================
|
||||
void CGMan :: SetYawSpeed ( void )
|
||||
{
|
||||
int ys;
|
||||
|
||||
switch ( m_Activity )
|
||||
{
|
||||
case ACT_IDLE:
|
||||
default:
|
||||
ys = 90;
|
||||
}
|
||||
|
||||
pev->yaw_speed = ys;
|
||||
}
|
||||
|
||||
//=========================================================
|
||||
// HandleAnimEvent - catches the monster-specific messages
|
||||
// that occur when tagged animation frames are played.
|
||||
//=========================================================
|
||||
void CGMan :: HandleAnimEvent( MonsterEvent_t *pEvent )
|
||||
{
|
||||
switch( pEvent->event )
|
||||
{
|
||||
case 0:
|
||||
default:
|
||||
CBaseMonster::HandleAnimEvent( pEvent );
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
//=========================================================
|
||||
// ISoundMask - generic monster can't hear.
|
||||
//=========================================================
|
||||
int CGMan :: ISoundMask ( void )
|
||||
{
|
||||
return NULL;
|
||||
}
|
||||
|
||||
//=========================================================
|
||||
// Spawn
|
||||
//=========================================================
|
||||
void CGMan :: Spawn()
|
||||
{
|
||||
Precache();
|
||||
|
||||
SET_MODEL( ENT(pev), "models/gman.mdl" );
|
||||
UTIL_SetSize(pev, VEC_HUMAN_HULL_MIN, VEC_HUMAN_HULL_MAX);
|
||||
|
||||
pev->solid = SOLID_SLIDEBOX;
|
||||
pev->movetype = MOVETYPE_STEP;
|
||||
m_bloodColor = DONT_BLEED;
|
||||
pev->health = 100;
|
||||
m_flFieldOfView = 0.5;// indicates the width of this monster's forward view cone ( as a dotproduct result )
|
||||
m_MonsterState = MONSTERSTATE_NONE;
|
||||
|
||||
MonsterInit();
|
||||
}
|
||||
|
||||
//=========================================================
|
||||
// Precache - precaches all resources this monster needs
|
||||
//=========================================================
|
||||
void CGMan :: Precache()
|
||||
{
|
||||
PRECACHE_MODEL( "models/gman.mdl" );
|
||||
}
|
||||
|
||||
|
||||
//=========================================================
|
||||
// AI Schedules Specific to this monster
|
||||
//=========================================================
|
||||
|
||||
|
||||
void CGMan :: StartTask( Task_t *pTask )
|
||||
{
|
||||
switch( pTask->iTask )
|
||||
{
|
||||
case TASK_WAIT:
|
||||
if (m_hPlayer == NULL)
|
||||
{
|
||||
m_hPlayer = UTIL_FindEntityByClassname( NULL, "player" );
|
||||
}
|
||||
break;
|
||||
}
|
||||
CBaseMonster::StartTask( pTask );
|
||||
}
|
||||
|
||||
void CGMan :: RunTask( Task_t *pTask )
|
||||
{
|
||||
switch( pTask->iTask )
|
||||
{
|
||||
case TASK_WAIT:
|
||||
// look at who I'm talking to
|
||||
if (m_flTalkTime > gpGlobals->time && m_hTalkTarget != NULL)
|
||||
{
|
||||
float yaw = VecToYaw(m_hTalkTarget->pev->origin - pev->origin) - pev->angles.y;
|
||||
|
||||
if (yaw > 180) yaw -= 360;
|
||||
if (yaw < -180) yaw += 360;
|
||||
|
||||
// turn towards vector
|
||||
SetBoneController( 0, yaw );
|
||||
}
|
||||
// look at player, but only if playing a "safe" idle animation
|
||||
else if (m_hPlayer != NULL && pev->sequence == 0)
|
||||
{
|
||||
float yaw = VecToYaw(m_hPlayer->pev->origin - pev->origin) - pev->angles.y;
|
||||
|
||||
if (yaw > 180) yaw -= 360;
|
||||
if (yaw < -180) yaw += 360;
|
||||
|
||||
// turn towards vector
|
||||
SetBoneController( 0, yaw );
|
||||
}
|
||||
else
|
||||
{
|
||||
SetBoneController( 0, 0 );
|
||||
}
|
||||
CBaseMonster::RunTask( pTask );
|
||||
break;
|
||||
default:
|
||||
SetBoneController( 0, 0 );
|
||||
CBaseMonster::RunTask( pTask );
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
//=========================================================
|
||||
// Override all damage
|
||||
//=========================================================
|
||||
int CGMan :: TakeDamage( entvars_t* pevInflictor, entvars_t* pevAttacker, float flDamage, int bitsDamageType )
|
||||
{
|
||||
pev->health = pev->max_health / 2; // always trigger the 50% damage aitrigger
|
||||
|
||||
if ( flDamage > 0 )
|
||||
{
|
||||
SetConditions(bits_COND_LIGHT_DAMAGE);
|
||||
}
|
||||
|
||||
if ( flDamage >= 20 )
|
||||
{
|
||||
SetConditions(bits_COND_HEAVY_DAMAGE);
|
||||
}
|
||||
return TRUE;
|
||||
}
|
||||
|
||||
|
||||
void CGMan::TraceAttack( entvars_t *pevAttacker, float flDamage, Vector vecDir, TraceResult *ptr, int bitsDamageType)
|
||||
{
|
||||
UTIL_Ricochet( ptr->vecEndPos, 1.0 );
|
||||
AddMultiDamage( pevAttacker, this, flDamage, bitsDamageType );
|
||||
}
|
||||
|
||||
|
||||
void CGMan::PlayScriptedSentence( const char *pszSentence, float duration, float volume, float attenuation, BOOL bConcurrent, CBaseEntity *pListener )
|
||||
{
|
||||
CBaseMonster::PlayScriptedSentence( pszSentence, duration, volume, attenuation, bConcurrent, pListener );
|
||||
|
||||
m_flTalkTime = gpGlobals->time + duration;
|
||||
m_hTalkTarget = pListener;
|
||||
}
|
|
@ -0,0 +1,198 @@
|
|||
/***
|
||||
*
|
||||
* 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.
|
||||
*
|
||||
****/
|
||||
/*
|
||||
|
||||
h_ai.cpp - halflife specific ai code
|
||||
|
||||
*/
|
||||
|
||||
|
||||
#include "extdll.h"
|
||||
#include "util.h"
|
||||
#include "cbase.h"
|
||||
#include "monsters.h"
|
||||
|
||||
#define NUM_LATERAL_CHECKS 13 // how many checks are made on each side of a monster looking for lateral cover
|
||||
#define NUM_LATERAL_LOS_CHECKS 6 // how many checks are made on each side of a monster looking for lateral cover
|
||||
|
||||
//float flRandom = RANDOM_FLOAT(0,1);
|
||||
|
||||
DLL_GLOBAL BOOL g_fDrawLines = FALSE;
|
||||
|
||||
//=========================================================
|
||||
//
|
||||
// AI UTILITY FUNCTIONS
|
||||
//
|
||||
// !!!UNDONE - move CBaseMonster functions to monsters.cpp
|
||||
//=========================================================
|
||||
|
||||
//=========================================================
|
||||
// FBoxVisible - a more accurate ( and slower ) version
|
||||
// of FVisible.
|
||||
//
|
||||
// !!!UNDONE - make this CBaseMonster?
|
||||
//=========================================================
|
||||
BOOL FBoxVisible ( entvars_t *pevLooker, entvars_t *pevTarget, Vector &vecTargetOrigin, float flSize )
|
||||
{
|
||||
// don't look through water
|
||||
if ((pevLooker->waterlevel != 3 && pevTarget->waterlevel == 3)
|
||||
|| (pevLooker->waterlevel == 3 && pevTarget->waterlevel == 0))
|
||||
return FALSE;
|
||||
|
||||
TraceResult tr;
|
||||
Vector vecLookerOrigin = pevLooker->origin + pevLooker->view_ofs;//look through the monster's 'eyes'
|
||||
for (int i = 0; i < 5; i++)
|
||||
{
|
||||
Vector vecTarget = pevTarget->origin;
|
||||
vecTarget.x += RANDOM_FLOAT( pevTarget->mins.x + flSize, pevTarget->maxs.x - flSize);
|
||||
vecTarget.y += RANDOM_FLOAT( pevTarget->mins.y + flSize, pevTarget->maxs.y - flSize);
|
||||
vecTarget.z += RANDOM_FLOAT( pevTarget->mins.z + flSize, pevTarget->maxs.z - flSize);
|
||||
|
||||
UTIL_TraceLine(vecLookerOrigin, vecTarget, ignore_monsters, ignore_glass, ENT(pevLooker)/*pentIgnore*/, &tr);
|
||||
|
||||
if (tr.flFraction == 1.0)
|
||||
{
|
||||
vecTargetOrigin = vecTarget;
|
||||
return TRUE;// line of sight is valid.
|
||||
}
|
||||
}
|
||||
return FALSE;// Line of sight is not established
|
||||
}
|
||||
|
||||
//
|
||||
// VecCheckToss - returns the velocity at which an object should be lobbed from vecspot1 to land near vecspot2.
|
||||
// returns g_vecZero if toss is not feasible.
|
||||
//
|
||||
Vector VecCheckToss ( entvars_t *pev, const Vector &vecSpot1, Vector vecSpot2, float flGravityAdj )
|
||||
{
|
||||
TraceResult tr;
|
||||
Vector vecMidPoint;// halfway point between Spot1 and Spot2
|
||||
Vector vecApex;// highest point
|
||||
Vector vecScale;
|
||||
Vector vecGrenadeVel;
|
||||
Vector vecTemp;
|
||||
float flGravity = CVAR_GET_FLOAT( "sv_gravity" ) * flGravityAdj;
|
||||
|
||||
if (vecSpot2.z - vecSpot1.z > 500)
|
||||
{
|
||||
// to high, fail
|
||||
return g_vecZero;
|
||||
}
|
||||
|
||||
UTIL_MakeVectors (pev->angles);
|
||||
|
||||
// toss a little bit to the left or right, not right down on the enemy's bean (head).
|
||||
vecSpot2 = vecSpot2 + gpGlobals->v_right * ( RANDOM_FLOAT(-8,8) + RANDOM_FLOAT(-16,16) );
|
||||
vecSpot2 = vecSpot2 + gpGlobals->v_forward * ( RANDOM_FLOAT(-8,8) + RANDOM_FLOAT(-16,16) );
|
||||
|
||||
// calculate the midpoint and apex of the 'triangle'
|
||||
// UNDONE: normalize any Z position differences between spot1 and spot2 so that triangle is always RIGHT
|
||||
|
||||
// How much time does it take to get there?
|
||||
|
||||
// get a rough idea of how high it can be thrown
|
||||
vecMidPoint = vecSpot1 + (vecSpot2 - vecSpot1) * 0.5;
|
||||
UTIL_TraceLine(vecMidPoint, vecMidPoint + Vector(0,0,500), ignore_monsters, ENT(pev), &tr);
|
||||
vecMidPoint = tr.vecEndPos;
|
||||
// (subtract 15 so the grenade doesn't hit the ceiling)
|
||||
vecMidPoint.z -= 15;
|
||||
|
||||
if (vecMidPoint.z < vecSpot1.z || vecMidPoint.z < vecSpot2.z)
|
||||
{
|
||||
// to not enough space, fail
|
||||
return g_vecZero;
|
||||
}
|
||||
|
||||
// How high should the grenade travel to reach the apex
|
||||
float distance1 = (vecMidPoint.z - vecSpot1.z);
|
||||
float distance2 = (vecMidPoint.z - vecSpot2.z);
|
||||
|
||||
// How long will it take for the grenade to travel this distance
|
||||
float time1 = sqrt( distance1 / (0.5 * flGravity) );
|
||||
float time2 = sqrt( distance2 / (0.5 * flGravity) );
|
||||
|
||||
if (time1 < 0.1)
|
||||
{
|
||||
// too close
|
||||
return g_vecZero;
|
||||
}
|
||||
|
||||
// how hard to throw sideways to get there in time.
|
||||
vecGrenadeVel = (vecSpot2 - vecSpot1) / (time1 + time2);
|
||||
// how hard upwards to reach the apex at the right time.
|
||||
vecGrenadeVel.z = flGravity * time1;
|
||||
|
||||
// find the apex
|
||||
vecApex = vecSpot1 + vecGrenadeVel * time1;
|
||||
vecApex.z = vecMidPoint.z;
|
||||
|
||||
UTIL_TraceLine(vecSpot1, vecApex, dont_ignore_monsters, ENT(pev), &tr);
|
||||
if (tr.flFraction != 1.0)
|
||||
{
|
||||
// fail!
|
||||
return g_vecZero;
|
||||
}
|
||||
|
||||
// UNDONE: either ignore monsters or change it to not care if we hit our enemy
|
||||
UTIL_TraceLine(vecSpot2, vecApex, ignore_monsters, ENT(pev), &tr);
|
||||
if (tr.flFraction != 1.0)
|
||||
{
|
||||
// fail!
|
||||
return g_vecZero;
|
||||
}
|
||||
|
||||
return vecGrenadeVel;
|
||||
}
|
||||
|
||||
|
||||
//
|
||||
// VecCheckThrow - returns the velocity vector at which an object should be thrown from vecspot1 to hit vecspot2.
|
||||
// returns g_vecZero if throw is not feasible.
|
||||
//
|
||||
Vector VecCheckThrow ( entvars_t *pev, const Vector &vecSpot1, Vector vecSpot2, float flSpeed, float flGravityAdj )
|
||||
{
|
||||
float flGravity = CVAR_GET_FLOAT( "sv_gravity" ) * flGravityAdj;
|
||||
|
||||
Vector vecGrenadeVel = (vecSpot2 - vecSpot1);
|
||||
|
||||
// throw at a constant time
|
||||
float time = vecGrenadeVel.Length( ) / flSpeed;
|
||||
vecGrenadeVel = vecGrenadeVel * (1.0 / time);
|
||||
|
||||
// adjust upward toss to compensate for gravity loss
|
||||
vecGrenadeVel.z += flGravity * time * 0.5;
|
||||
|
||||
Vector vecApex = vecSpot1 + (vecSpot2 - vecSpot1) * 0.5;
|
||||
vecApex.z += 0.5 * flGravity * (time * 0.5) * (time * 0.5);
|
||||
|
||||
TraceResult tr;
|
||||
UTIL_TraceLine(vecSpot1, vecApex, dont_ignore_monsters, ENT(pev), &tr);
|
||||
if (tr.flFraction != 1.0)
|
||||
{
|
||||
// fail!
|
||||
return g_vecZero;
|
||||
}
|
||||
|
||||
UTIL_TraceLine(vecSpot2, vecApex, ignore_monsters, ENT(pev), &tr);
|
||||
if (tr.flFraction != 1.0)
|
||||
{
|
||||
// fail!
|
||||
return g_vecZero;
|
||||
}
|
||||
|
||||
return vecGrenadeVel;
|
||||
}
|
||||
|
||||
|
|
@ -0,0 +1,200 @@
|
|||
/***
|
||||
*
|
||||
* 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.
|
||||
*
|
||||
****/
|
||||
/*
|
||||
|
||||
===== h_battery.cpp ========================================================
|
||||
|
||||
battery-related code
|
||||
|
||||
*/
|
||||
|
||||
#include "extdll.h"
|
||||
#include "util.h"
|
||||
#include "cbase.h"
|
||||
#include "saverestore.h"
|
||||
#include "skill.h"
|
||||
#include "gamerules.h"
|
||||
|
||||
class CRecharge : public CBaseToggle
|
||||
{
|
||||
public:
|
||||
void Spawn( );
|
||||
void Precache( void );
|
||||
void EXPORT Off(void);
|
||||
void EXPORT Recharge(void);
|
||||
void KeyValue( KeyValueData *pkvd );
|
||||
void Use( CBaseEntity *pActivator, CBaseEntity *pCaller, USE_TYPE useType, float value );
|
||||
virtual int ObjectCaps( void ) { return (CBaseToggle :: ObjectCaps() | FCAP_CONTINUOUS_USE) & ~FCAP_ACROSS_TRANSITION; }
|
||||
virtual int Save( CSave &save );
|
||||
virtual int Restore( CRestore &restore );
|
||||
|
||||
static TYPEDESCRIPTION m_SaveData[];
|
||||
|
||||
float m_flNextCharge;
|
||||
int m_iReactivate ; // DeathMatch Delay until reactvated
|
||||
int m_iJuice;
|
||||
int m_iOn; // 0 = off, 1 = startup, 2 = going
|
||||
float m_flSoundTime;
|
||||
};
|
||||
|
||||
TYPEDESCRIPTION CRecharge::m_SaveData[] =
|
||||
{
|
||||
DEFINE_FIELD( CRecharge, m_flNextCharge, FIELD_TIME ),
|
||||
DEFINE_FIELD( CRecharge, m_iReactivate, FIELD_INTEGER),
|
||||
DEFINE_FIELD( CRecharge, m_iJuice, FIELD_INTEGER),
|
||||
DEFINE_FIELD( CRecharge, m_iOn, FIELD_INTEGER),
|
||||
DEFINE_FIELD( CRecharge, m_flSoundTime, FIELD_TIME ),
|
||||
};
|
||||
|
||||
IMPLEMENT_SAVERESTORE( CRecharge, CBaseEntity );
|
||||
|
||||
LINK_ENTITY_TO_CLASS(func_recharge, CRecharge);
|
||||
|
||||
|
||||
void CRecharge::KeyValue( KeyValueData *pkvd )
|
||||
{
|
||||
if ( FStrEq(pkvd->szKeyName, "style") ||
|
||||
FStrEq(pkvd->szKeyName, "height") ||
|
||||
FStrEq(pkvd->szKeyName, "value1") ||
|
||||
FStrEq(pkvd->szKeyName, "value2") ||
|
||||
FStrEq(pkvd->szKeyName, "value3"))
|
||||
{
|
||||
pkvd->fHandled = TRUE;
|
||||
}
|
||||
else if (FStrEq(pkvd->szKeyName, "dmdelay"))
|
||||
{
|
||||
m_iReactivate = atoi(pkvd->szValue);
|
||||
pkvd->fHandled = TRUE;
|
||||
}
|
||||
else
|
||||
CBaseToggle::KeyValue( pkvd );
|
||||
}
|
||||
|
||||
void CRecharge::Spawn()
|
||||
{
|
||||
Precache( );
|
||||
|
||||
pev->solid = SOLID_BSP;
|
||||
pev->movetype = MOVETYPE_PUSH;
|
||||
|
||||
UTIL_SetOrigin(pev, pev->origin); // set size and link into world
|
||||
UTIL_SetSize(pev, pev->mins, pev->maxs);
|
||||
SET_MODEL(ENT(pev), STRING(pev->model) );
|
||||
m_iJuice = gSkillData.suitchargerCapacity;
|
||||
pev->frame = 0;
|
||||
}
|
||||
|
||||
void CRecharge::Precache()
|
||||
{
|
||||
PRECACHE_SOUND("items/suitcharge1.wav");
|
||||
PRECACHE_SOUND("items/suitchargeno1.wav");
|
||||
PRECACHE_SOUND("items/suitchargeok1.wav");
|
||||
}
|
||||
|
||||
|
||||
void CRecharge::Use( CBaseEntity *pActivator, CBaseEntity *pCaller, USE_TYPE useType, float value )
|
||||
{
|
||||
// if it's not a player, ignore
|
||||
if (!FClassnameIs(pActivator->pev, "player"))
|
||||
return;
|
||||
|
||||
// if there is no juice left, turn it off
|
||||
if (m_iJuice <= 0)
|
||||
{
|
||||
pev->frame = 1;
|
||||
Off();
|
||||
}
|
||||
|
||||
// if the player doesn't have the suit, or there is no juice left, make the deny noise
|
||||
if ((m_iJuice <= 0) || (!(pActivator->pev->weapons & (1<<WEAPON_SUIT))))
|
||||
{
|
||||
if (m_flSoundTime <= gpGlobals->time)
|
||||
{
|
||||
m_flSoundTime = gpGlobals->time + 0.62;
|
||||
EMIT_SOUND(ENT(pev), CHAN_ITEM, "items/suitchargeno1.wav", 0.85, ATTN_NORM );
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
||||
pev->nextthink = pev->ltime + 0.25;
|
||||
SetThink(Off);
|
||||
|
||||
// Time to recharge yet?
|
||||
|
||||
if (m_flNextCharge >= gpGlobals->time)
|
||||
return;
|
||||
|
||||
// Make sure that we have a caller
|
||||
if (!pActivator)
|
||||
return;
|
||||
|
||||
m_hActivator = pActivator;
|
||||
|
||||
//only recharge the player
|
||||
|
||||
if (!m_hActivator->IsPlayer() )
|
||||
return;
|
||||
|
||||
// Play the on sound or the looping charging sound
|
||||
if (!m_iOn)
|
||||
{
|
||||
m_iOn++;
|
||||
EMIT_SOUND(ENT(pev), CHAN_ITEM, "items/suitchargeok1.wav", 0.85, ATTN_NORM );
|
||||
m_flSoundTime = 0.56 + gpGlobals->time;
|
||||
}
|
||||
if ((m_iOn == 1) && (m_flSoundTime <= gpGlobals->time))
|
||||
{
|
||||
m_iOn++;
|
||||
EMIT_SOUND(ENT(pev), CHAN_STATIC, "items/suitcharge1.wav", 0.85, ATTN_NORM );
|
||||
}
|
||||
|
||||
|
||||
// charge the player
|
||||
if (m_hActivator->pev->armorvalue < 100)
|
||||
{
|
||||
m_iJuice--;
|
||||
m_hActivator->pev->armorvalue += 1;
|
||||
|
||||
if (m_hActivator->pev->armorvalue > 100)
|
||||
m_hActivator->pev->armorvalue = 100;
|
||||
}
|
||||
|
||||
// govern the rate of charge
|
||||
m_flNextCharge = gpGlobals->time + 0.1;
|
||||
}
|
||||
|
||||
void CRecharge::Recharge(void)
|
||||
{
|
||||
m_iJuice = gSkillData.suitchargerCapacity;
|
||||
pev->frame = 0;
|
||||
SetThink( SUB_DoNothing );
|
||||
}
|
||||
|
||||
void CRecharge::Off(void)
|
||||
{
|
||||
// Stop looping sound.
|
||||
if (m_iOn > 1)
|
||||
STOP_SOUND( ENT(pev), CHAN_STATIC, "items/suitcharge1.wav" );
|
||||
|
||||
m_iOn = 0;
|
||||
|
||||
if ((!m_iJuice) && ( ( m_iReactivate = g_pGameRules->FlHEVChargerRechargeTime() ) > 0) )
|
||||
{
|
||||
pev->nextthink = pev->ltime + m_iReactivate;
|
||||
SetThink(Recharge);
|
||||
}
|
||||
else
|
||||
SetThink( SUB_DoNothing );
|
||||
}
|
|
@ -0,0 +1,241 @@
|
|||
/***
|
||||
*
|
||||
* 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.
|
||||
*
|
||||
****/
|
||||
/*
|
||||
|
||||
===== h_cine.cpp ========================================================
|
||||
|
||||
The Halflife hard coded "scripted sequence".
|
||||
|
||||
I'm pretty sure all this code is obsolete
|
||||
|
||||
*/
|
||||
|
||||
#include "extdll.h"
|
||||
#include "util.h"
|
||||
#include "cbase.h"
|
||||
#include "monsters.h"
|
||||
#include "decals.h"
|
||||
|
||||
|
||||
class CLegacyCineMonster : public CBaseMonster
|
||||
{
|
||||
public:
|
||||
void CineSpawn( char *szModel );
|
||||
void Use( CBaseEntity *pActivator, CBaseEntity *pCaller, USE_TYPE useType, float value );
|
||||
void EXPORT CineThink( void );
|
||||
void Pain( void );
|
||||
void Die( void );
|
||||
};
|
||||
|
||||
class CCineScientist : public CLegacyCineMonster
|
||||
{
|
||||
public:
|
||||
void Spawn( void ) { CineSpawn("models/cine-scientist.mdl"); }
|
||||
};
|
||||
class CCine2Scientist : public CLegacyCineMonster
|
||||
{
|
||||
public:
|
||||
void Spawn( void ) { CineSpawn("models/cine2-scientist.mdl"); }
|
||||
};
|
||||
class CCinePanther : public CLegacyCineMonster
|
||||
{
|
||||
public:
|
||||
void Spawn( void ) { CineSpawn("models/cine-panther.mdl"); }
|
||||
};
|
||||
|
||||
class CCineBarney : public CLegacyCineMonster
|
||||
{
|
||||
public:
|
||||
void Spawn( void ) { CineSpawn("models/cine-barney.mdl"); }
|
||||
};
|
||||
|
||||
class CCine2HeavyWeapons : public CLegacyCineMonster
|
||||
{
|
||||
public:
|
||||
void Spawn( void ) { CineSpawn("models/cine2_hvyweapons.mdl"); }
|
||||
};
|
||||
|
||||
class CCine2Slave : public CLegacyCineMonster
|
||||
{
|
||||
public:
|
||||
void Spawn( void ) { CineSpawn("models/cine2_slave.mdl"); }
|
||||
};
|
||||
|
||||
class CCine3Scientist : public CLegacyCineMonster
|
||||
{
|
||||
public:
|
||||
void Spawn( void ) { CineSpawn("models/cine3-scientist.mdl"); }
|
||||
};
|
||||
|
||||
class CCine3Barney : public CLegacyCineMonster
|
||||
{
|
||||
public:
|
||||
void Spawn( void ) { CineSpawn("models/cine3-barney.mdl"); }
|
||||
};
|
||||
|
||||
//
|
||||
// ********** Scientist SPAWN **********
|
||||
//
|
||||
|
||||
LINK_ENTITY_TO_CLASS( monster_cine_scientist, CCineScientist );
|
||||
LINK_ENTITY_TO_CLASS( monster_cine_panther, CCinePanther );
|
||||
LINK_ENTITY_TO_CLASS( monster_cine_barney, CCineBarney );
|
||||
LINK_ENTITY_TO_CLASS( monster_cine2_scientist, CCine2Scientist );
|
||||
LINK_ENTITY_TO_CLASS( monster_cine2_hvyweapons, CCine2HeavyWeapons );
|
||||
LINK_ENTITY_TO_CLASS( monster_cine2_slave, CCine2Slave );
|
||||
LINK_ENTITY_TO_CLASS( monster_cine3_scientist, CCine3Scientist );
|
||||
LINK_ENTITY_TO_CLASS( monster_cine3_barney, CCine3Barney );
|
||||
|
||||
//
|
||||
// ********** Scientist SPAWN **********
|
||||
//
|
||||
|
||||
void CLegacyCineMonster :: CineSpawn( char *szModel )
|
||||
{
|
||||
PRECACHE_MODEL(szModel);
|
||||
SET_MODEL(ENT(pev), szModel);
|
||||
UTIL_SetSize(pev, Vector(-16, -16, 0), Vector(16, 16, 64));
|
||||
|
||||
pev->solid = SOLID_SLIDEBOX;
|
||||
pev->movetype = MOVETYPE_STEP;
|
||||
pev->effects = 0;
|
||||
pev->health = 1;
|
||||
pev->yaw_speed = 10;
|
||||
|
||||
// ugly alpha hack, can't set ints from the bsp.
|
||||
pev->sequence = (int)pev->impulse;
|
||||
ResetSequenceInfo( );
|
||||
pev->framerate = 0.0;
|
||||
|
||||
m_bloodColor = BLOOD_COLOR_RED;
|
||||
|
||||
// if no targetname, start now
|
||||
if ( FStringNull(pev->targetname) )
|
||||
{
|
||||
SetThink( CineThink );
|
||||
pev->nextthink += 1.0;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
//
|
||||
// CineStart
|
||||
//
|
||||
void CLegacyCineMonster :: Use( CBaseEntity *pActivator, CBaseEntity *pCaller, USE_TYPE useType, float value )
|
||||
{
|
||||
pev->animtime = 0; // reset the sequence
|
||||
SetThink( CineThink );
|
||||
pev->nextthink = gpGlobals->time;
|
||||
}
|
||||
|
||||
//
|
||||
// ********** Scientist DIE **********
|
||||
//
|
||||
void CLegacyCineMonster :: Die( void )
|
||||
{
|
||||
SetThink( SUB_Remove );
|
||||
}
|
||||
|
||||
//
|
||||
// ********** Scientist PAIN **********
|
||||
//
|
||||
void CLegacyCineMonster :: Pain( void )
|
||||
{
|
||||
EMIT_SOUND(ENT(pev), CHAN_VOICE, "player/pain3.wav", 1, ATTN_NORM);
|
||||
}
|
||||
|
||||
void CLegacyCineMonster :: CineThink( void )
|
||||
{
|
||||
// DBG_CheckMonsterData(pev);
|
||||
|
||||
// Emit particles from origin (double check animator's placement of model)
|
||||
// THIS is a test feature
|
||||
//UTIL_ParticleEffect(pev->origin, g_vecZero, 255, 20);
|
||||
|
||||
if (!pev->animtime)
|
||||
ResetSequenceInfo( );
|
||||
|
||||
pev->nextthink = gpGlobals->time + 1.0;
|
||||
|
||||
if (pev->spawnflags != 0 && m_fSequenceFinished)
|
||||
{
|
||||
Die();
|
||||
return;
|
||||
}
|
||||
|
||||
StudioFrameAdvance ( );
|
||||
}
|
||||
|
||||
//
|
||||
// cine_blood
|
||||
//
|
||||
// e3/prealpha only.
|
||||
class CCineBlood : public CBaseEntity
|
||||
{
|
||||
public:
|
||||
void Spawn( void );
|
||||
void EXPORT BloodStart ( CBaseEntity *pActivator, CBaseEntity *pCaller, USE_TYPE useType, float value );
|
||||
void EXPORT BloodGush ( void );
|
||||
};
|
||||
|
||||
LINK_ENTITY_TO_CLASS( cine_blood, CCineBlood );
|
||||
|
||||
|
||||
void CCineBlood :: BloodGush ( void )
|
||||
{
|
||||
Vector vecSplatDir;
|
||||
TraceResult tr;
|
||||
pev->nextthink = gpGlobals->time + 0.1;
|
||||
|
||||
UTIL_MakeVectors(pev->angles);
|
||||
if ( pev->health-- < 0 )
|
||||
REMOVE_ENTITY(ENT(pev));
|
||||
// CHANGE_METHOD ( ENT(pev), em_think, SUB_Remove );
|
||||
|
||||
if ( RANDOM_FLOAT ( 0 , 1 ) < 0.7 )// larger chance of globs
|
||||
{
|
||||
UTIL_BloodDrips( pev->origin, UTIL_RandomBloodVector(), BLOOD_COLOR_RED, 10 );
|
||||
}
|
||||
else// slim chance of geyser
|
||||
{
|
||||
UTIL_BloodStream( pev->origin, UTIL_RandomBloodVector(), BLOOD_COLOR_RED, RANDOM_LONG(50, 150) );
|
||||
}
|
||||
|
||||
if ( RANDOM_FLOAT ( 0, 1 ) < 0.75 )
|
||||
{// decals the floor with blood.
|
||||
vecSplatDir = Vector ( 0 , 0 , -1 );
|
||||
vecSplatDir = vecSplatDir + (RANDOM_FLOAT(-1,1) * 0.6 * gpGlobals->v_right) + (RANDOM_FLOAT(-1,1) * 0.6 * gpGlobals->v_forward);// randomize a bit
|
||||
UTIL_TraceLine( pev->origin + Vector ( 0, 0 , 64) , pev->origin + vecSplatDir * 256, ignore_monsters, ENT(pev), &tr);
|
||||
if ( tr.flFraction != 1.0 )
|
||||
{
|
||||
// Decal with a bloodsplat
|
||||
UTIL_BloodDecalTrace( &tr, BLOOD_COLOR_RED );
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void CCineBlood :: BloodStart ( CBaseEntity *pActivator, CBaseEntity *pCaller, USE_TYPE useType, float value )
|
||||
{
|
||||
SetThink( BloodGush );
|
||||
pev->nextthink = gpGlobals->time;// now!
|
||||
}
|
||||
|
||||
void CCineBlood :: Spawn ( void )
|
||||
{
|
||||
pev->solid = SOLID_NOT;
|
||||
SetUse ( BloodStart );
|
||||
pev->health = 20;//hacked health to count iterations
|
||||
}
|
||||
|
|
@ -0,0 +1,471 @@
|
|||
/***
|
||||
*
|
||||
* 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.
|
||||
*
|
||||
****/
|
||||
/*
|
||||
|
||||
===== h_cycler.cpp ========================================================
|
||||
|
||||
The Halflife Cycler Monsters
|
||||
|
||||
*/
|
||||
|
||||
#include "extdll.h"
|
||||
#include "util.h"
|
||||
#include "cbase.h"
|
||||
#include "monsters.h"
|
||||
#include "animation.h"
|
||||
#include "weapons.h"
|
||||
#include "player.h"
|
||||
|
||||
|
||||
#define TEMP_FOR_SCREEN_SHOTS
|
||||
#ifdef TEMP_FOR_SCREEN_SHOTS //===================================================
|
||||
|
||||
class CCycler : public CBaseMonster
|
||||
{
|
||||
public:
|
||||
void GenericCyclerSpawn(char *szModel, Vector vecMin, Vector vecMax);
|
||||
virtual int ObjectCaps( void ) { return (CBaseEntity :: ObjectCaps() | FCAP_IMPULSE_USE); }
|
||||
int TakeDamage( entvars_t* pevInflictor, entvars_t* pevAttacker, float flDamage, int bitsDamageType );
|
||||
void Spawn( void );
|
||||
void Think( void );
|
||||
//void Pain( float flDamage );
|
||||
void Use ( CBaseEntity *pActivator, CBaseEntity *pCaller, USE_TYPE useType, float value );
|
||||
|
||||
// Don't treat as a live target
|
||||
virtual BOOL IsAlive( void ) { return FALSE; }
|
||||
|
||||
virtual int Save( CSave &save );
|
||||
virtual int Restore( CRestore &restore );
|
||||
static TYPEDESCRIPTION m_SaveData[];
|
||||
|
||||
int m_animate;
|
||||
};
|
||||
|
||||
TYPEDESCRIPTION CCycler::m_SaveData[] =
|
||||
{
|
||||
DEFINE_FIELD( CCycler, m_animate, FIELD_INTEGER ),
|
||||
};
|
||||
|
||||
IMPLEMENT_SAVERESTORE( CCycler, CBaseMonster );
|
||||
|
||||
|
||||
//
|
||||
// we should get rid of all the other cyclers and replace them with this.
|
||||
//
|
||||
class CGenericCycler : public CCycler
|
||||
{
|
||||
public:
|
||||
void Spawn( void ) { GenericCyclerSpawn( (char *)STRING(pev->model), Vector(-16, -16, 0), Vector(16, 16, 72) ); }
|
||||
};
|
||||
LINK_ENTITY_TO_CLASS( cycler, CGenericCycler );
|
||||
|
||||
|
||||
|
||||
// Probe droid imported for tech demo compatibility
|
||||
//
|
||||
// PROBE DROID
|
||||
//
|
||||
class CCyclerProbe : public CCycler
|
||||
{
|
||||
public:
|
||||
void Spawn( void );
|
||||
};
|
||||
LINK_ENTITY_TO_CLASS( cycler_prdroid, CCyclerProbe );
|
||||
void CCyclerProbe :: Spawn( void )
|
||||
{
|
||||
pev->origin = pev->origin + Vector ( 0, 0, 16 );
|
||||
GenericCyclerSpawn( "models/prdroid.mdl", Vector(-16,-16,-16), Vector(16,16,16));
|
||||
}
|
||||
|
||||
|
||||
|
||||
// Cycler member functions
|
||||
|
||||
void CCycler :: GenericCyclerSpawn(char *szModel, Vector vecMin, Vector vecMax)
|
||||
{
|
||||
if (!szModel || !*szModel)
|
||||
{
|
||||
ALERT(at_error, "cycler at %.0f %.0f %0.f missing modelname", pev->origin.x, pev->origin.y, pev->origin.z );
|
||||
REMOVE_ENTITY(ENT(pev));
|
||||
return;
|
||||
}
|
||||
|
||||
pev->classname = MAKE_STRING("cycler");
|
||||
PRECACHE_MODEL( szModel );
|
||||
SET_MODEL(ENT(pev), szModel);
|
||||
|
||||
CCycler::Spawn( );
|
||||
|
||||
UTIL_SetSize(pev, vecMin, vecMax);
|
||||
}
|
||||
|
||||
|
||||
void CCycler :: Spawn( )
|
||||
{
|
||||
InitBoneControllers();
|
||||
pev->solid = SOLID_SLIDEBOX;
|
||||
pev->movetype = MOVETYPE_NONE;
|
||||
pev->takedamage = DAMAGE_YES;
|
||||
pev->effects = 0;
|
||||
pev->health = 80000;// no cycler should die
|
||||
pev->yaw_speed = 5;
|
||||
pev->ideal_yaw = pev->angles.y;
|
||||
ChangeYaw( 360 );
|
||||
|
||||
m_flFrameRate = 75;
|
||||
m_flGroundSpeed = 0;
|
||||
|
||||
pev->nextthink += 1.0;
|
||||
|
||||
ResetSequenceInfo( );
|
||||
|
||||
if (pev->sequence != 0 || pev->frame != 0)
|
||||
{
|
||||
m_animate = 0;
|
||||
pev->framerate = 0;
|
||||
}
|
||||
else
|
||||
{
|
||||
m_animate = 1;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
//
|
||||
// cycler think
|
||||
//
|
||||
void CCycler :: Think( void )
|
||||
{
|
||||
pev->nextthink = gpGlobals->time + 0.1;
|
||||
|
||||
if (m_animate)
|
||||
{
|
||||
StudioFrameAdvance ( );
|
||||
}
|
||||
if (m_fSequenceFinished && !m_fSequenceLoops)
|
||||
{
|
||||
// ResetSequenceInfo();
|
||||
// hack to avoid reloading model every frame
|
||||
pev->animtime = gpGlobals->time;
|
||||
pev->framerate = 1.0;
|
||||
m_fSequenceFinished = FALSE;
|
||||
m_flLastEventCheck = gpGlobals->time;
|
||||
pev->frame = 0;
|
||||
if (!m_animate)
|
||||
pev->framerate = 0.0; // FIX: don't reset framerate
|
||||
}
|
||||
}
|
||||
|
||||
//
|
||||
// CyclerUse - starts a rotation trend
|
||||
//
|
||||
void CCycler :: Use ( CBaseEntity *pActivator, CBaseEntity *pCaller, USE_TYPE useType, float value )
|
||||
{
|
||||
m_animate = !m_animate;
|
||||
if (m_animate)
|
||||
pev->framerate = 1.0;
|
||||
else
|
||||
pev->framerate = 0.0;
|
||||
}
|
||||
|
||||
//
|
||||
// CyclerPain , changes sequences when shot
|
||||
//
|
||||
//void CCycler :: Pain( float flDamage )
|
||||
int CCycler :: TakeDamage( entvars_t* pevInflictor, entvars_t* pevAttacker, float flDamage, int bitsDamageType )
|
||||
{
|
||||
if (m_animate)
|
||||
{
|
||||
pev->sequence++;
|
||||
|
||||
ResetSequenceInfo( );
|
||||
|
||||
if (m_flFrameRate == 0.0)
|
||||
{
|
||||
pev->sequence = 0;
|
||||
ResetSequenceInfo( );
|
||||
}
|
||||
pev->frame = 0;
|
||||
}
|
||||
else
|
||||
{
|
||||
pev->framerate = 1.0;
|
||||
StudioFrameAdvance ( 0.1 );
|
||||
pev->framerate = 0;
|
||||
ALERT( at_console, "sequence: %d, frame %.0f\n", pev->sequence, pev->frame );
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
#endif
|
||||
|
||||
|
||||
class CCyclerSprite : public CBaseEntity
|
||||
{
|
||||
public:
|
||||
void Spawn( void );
|
||||
void Think( void );
|
||||
void Use( CBaseEntity *pActivator, CBaseEntity *pCaller, USE_TYPE useType, float value );
|
||||
virtual int ObjectCaps( void ) { return (CBaseEntity :: ObjectCaps() | FCAP_DONT_SAVE | FCAP_IMPULSE_USE); }
|
||||
virtual int TakeDamage( entvars_t* pevInflictor, entvars_t* pevAttacker, float flDamage, int bitsDamageType );
|
||||
void Animate( float frames );
|
||||
|
||||
virtual int Save( CSave &save );
|
||||
virtual int Restore( CRestore &restore );
|
||||
static TYPEDESCRIPTION m_SaveData[];
|
||||
|
||||
inline int ShouldAnimate( void ) { return m_animate && m_maxFrame > 1.0; }
|
||||
int m_animate;
|
||||
float m_lastTime;
|
||||
float m_maxFrame;
|
||||
};
|
||||
|
||||
LINK_ENTITY_TO_CLASS( cycler_sprite, CCyclerSprite );
|
||||
|
||||
TYPEDESCRIPTION CCyclerSprite::m_SaveData[] =
|
||||
{
|
||||
DEFINE_FIELD( CCyclerSprite, m_animate, FIELD_INTEGER ),
|
||||
DEFINE_FIELD( CCyclerSprite, m_lastTime, FIELD_TIME ),
|
||||
DEFINE_FIELD( CCyclerSprite, m_maxFrame, FIELD_FLOAT ),
|
||||
};
|
||||
|
||||
IMPLEMENT_SAVERESTORE( CCyclerSprite, CBaseEntity );
|
||||
|
||||
|
||||
void CCyclerSprite::Spawn( void )
|
||||
{
|
||||
pev->solid = SOLID_SLIDEBOX;
|
||||
pev->movetype = MOVETYPE_NONE;
|
||||
pev->takedamage = DAMAGE_YES;
|
||||
pev->effects = 0;
|
||||
|
||||
pev->frame = 0;
|
||||
pev->nextthink = gpGlobals->time + 0.1;
|
||||
m_animate = 1;
|
||||
m_lastTime = gpGlobals->time;
|
||||
|
||||
PRECACHE_MODEL( (char *)STRING(pev->model) );
|
||||
SET_MODEL( ENT(pev), STRING(pev->model) );
|
||||
|
||||
m_maxFrame = (float) MODEL_FRAMES( pev->modelindex ) - 1;
|
||||
}
|
||||
|
||||
|
||||
void CCyclerSprite::Think( void )
|
||||
{
|
||||
if ( ShouldAnimate() )
|
||||
Animate( pev->framerate * (gpGlobals->time - m_lastTime) );
|
||||
|
||||
pev->nextthink = gpGlobals->time + 0.1;
|
||||
m_lastTime = gpGlobals->time;
|
||||
}
|
||||
|
||||
|
||||
void CCyclerSprite::Use( CBaseEntity *pActivator, CBaseEntity *pCaller, USE_TYPE useType, float value )
|
||||
{
|
||||
m_animate = !m_animate;
|
||||
ALERT( at_console, "Sprite: %s\n", STRING(pev->model) );
|
||||
}
|
||||
|
||||
|
||||
int CCyclerSprite::TakeDamage( entvars_t* pevInflictor, entvars_t* pevAttacker, float flDamage, int bitsDamageType )
|
||||
{
|
||||
if ( m_maxFrame > 1.0 )
|
||||
{
|
||||
Animate( 1.0 );
|
||||
}
|
||||
return 1;
|
||||
}
|
||||
|
||||
void CCyclerSprite::Animate( float frames )
|
||||
{
|
||||
pev->frame += frames;
|
||||
if ( m_maxFrame > 0 )
|
||||
pev->frame = fmod( pev->frame, m_maxFrame );
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
class CWeaponCycler : public CBasePlayerWeapon
|
||||
{
|
||||
public:
|
||||
void Spawn( void );
|
||||
int iItemSlot( void ) { return 1; }
|
||||
int GetItemInfo(ItemInfo *p) {return 0; }
|
||||
|
||||
void PrimaryAttack( void );
|
||||
void SecondaryAttack( void );
|
||||
BOOL Deploy( void );
|
||||
void Holster( int skiplocal = 0 );
|
||||
int m_iszModel;
|
||||
int m_iModel;
|
||||
};
|
||||
LINK_ENTITY_TO_CLASS( cycler_weapon, CWeaponCycler );
|
||||
|
||||
|
||||
void CWeaponCycler::Spawn( )
|
||||
{
|
||||
pev->solid = SOLID_SLIDEBOX;
|
||||
pev->movetype = MOVETYPE_NONE;
|
||||
|
||||
PRECACHE_MODEL( (char *)STRING(pev->model) );
|
||||
SET_MODEL( ENT(pev), STRING(pev->model) );
|
||||
m_iszModel = pev->model;
|
||||
m_iModel = pev->modelindex;
|
||||
|
||||
UTIL_SetOrigin( pev, pev->origin );
|
||||
UTIL_SetSize(pev, Vector(-16, -16, 0), Vector(16, 16, 16));
|
||||
SetTouch( DefaultTouch );
|
||||
}
|
||||
|
||||
|
||||
|
||||
BOOL CWeaponCycler::Deploy( )
|
||||
{
|
||||
m_pPlayer->pev->viewmodel = m_iszModel;
|
||||
m_pPlayer->m_flNextAttack = UTIL_WeaponTimeBase() + 1.0;
|
||||
SendWeaponAnim( 0 );
|
||||
m_iClip = 0;
|
||||
return TRUE;
|
||||
}
|
||||
|
||||
|
||||
void CWeaponCycler::Holster( int skiplocal /* = 0 */ )
|
||||
{
|
||||
m_pPlayer->m_flNextAttack = UTIL_WeaponTimeBase() + 0.5;
|
||||
}
|
||||
|
||||
|
||||
void CWeaponCycler::PrimaryAttack()
|
||||
{
|
||||
|
||||
SendWeaponAnim( pev->sequence );
|
||||
|
||||
m_flNextPrimaryAttack = gpGlobals->time + 0.3;
|
||||
}
|
||||
|
||||
|
||||
void CWeaponCycler::SecondaryAttack( void )
|
||||
{
|
||||
float flFrameRate, flGroundSpeed;
|
||||
|
||||
pev->sequence = (pev->sequence + 1) % 8;
|
||||
|
||||
pev->modelindex = m_iModel;
|
||||
void *pmodel = GET_MODEL_PTR( ENT(pev) );
|
||||
GetSequenceInfo( pmodel, pev, &flFrameRate, &flGroundSpeed );
|
||||
pev->modelindex = 0;
|
||||
|
||||
if (flFrameRate == 0.0)
|
||||
{
|
||||
pev->sequence = 0;
|
||||
}
|
||||
|
||||
SendWeaponAnim( pev->sequence );
|
||||
|
||||
m_flNextSecondaryAttack = gpGlobals->time + 0.3;
|
||||
}
|
||||
|
||||
|
||||
|
||||
// Flaming Wreakage
|
||||
class CWreckage : public CBaseMonster
|
||||
{
|
||||
int Save( CSave &save );
|
||||
int Restore( CRestore &restore );
|
||||
static TYPEDESCRIPTION m_SaveData[];
|
||||
|
||||
void Spawn( void );
|
||||
void Precache( void );
|
||||
void Think( void );
|
||||
|
||||
int m_flStartTime;
|
||||
};
|
||||
TYPEDESCRIPTION CWreckage::m_SaveData[] =
|
||||
{
|
||||
DEFINE_FIELD( CWreckage, m_flStartTime, FIELD_TIME ),
|
||||
};
|
||||
IMPLEMENT_SAVERESTORE( CWreckage, CBaseMonster );
|
||||
|
||||
|
||||
LINK_ENTITY_TO_CLASS( cycler_wreckage, CWreckage );
|
||||
|
||||
void CWreckage::Spawn( void )
|
||||
{
|
||||
pev->solid = SOLID_NOT;
|
||||
pev->movetype = MOVETYPE_NONE;
|
||||
pev->takedamage = 0;
|
||||
pev->effects = 0;
|
||||
|
||||
pev->frame = 0;
|
||||
pev->nextthink = gpGlobals->time + 0.1;
|
||||
|
||||
if (pev->model)
|
||||
{
|
||||
PRECACHE_MODEL( (char *)STRING(pev->model) );
|
||||
SET_MODEL( ENT(pev), STRING(pev->model) );
|
||||
}
|
||||
// pev->scale = 5.0;
|
||||
|
||||
m_flStartTime = gpGlobals->time;
|
||||
}
|
||||
|
||||
void CWreckage::Precache( )
|
||||
{
|
||||
if ( pev->model )
|
||||
PRECACHE_MODEL( (char *)STRING(pev->model) );
|
||||
}
|
||||
|
||||
void CWreckage::Think( void )
|
||||
{
|
||||
StudioFrameAdvance( );
|
||||
pev->nextthink = gpGlobals->time + 0.2;
|
||||
|
||||
if (pev->dmgtime)
|
||||
{
|
||||
if (pev->dmgtime < gpGlobals->time)
|
||||
{
|
||||
UTIL_Remove( this );
|
||||
return;
|
||||
}
|
||||
else if (RANDOM_FLOAT( 0, pev->dmgtime - m_flStartTime ) > pev->dmgtime - gpGlobals->time)
|
||||
{
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
Vector VecSrc;
|
||||
|
||||
VecSrc.x = RANDOM_FLOAT( pev->absmin.x, pev->absmax.x );
|
||||
VecSrc.y = RANDOM_FLOAT( pev->absmin.y, pev->absmax.y );
|
||||
VecSrc.z = RANDOM_FLOAT( pev->absmin.z, pev->absmax.z );
|
||||
|
||||
MESSAGE_BEGIN( MSG_PVS, SVC_TEMPENTITY, VecSrc );
|
||||
WRITE_BYTE( TE_SMOKE );
|
||||
WRITE_COORD( VecSrc.x );
|
||||
WRITE_COORD( VecSrc.y );
|
||||
WRITE_COORD( VecSrc.z );
|
||||
WRITE_SHORT( g_sModelIndexSmoke );
|
||||
WRITE_BYTE( RANDOM_LONG(0,49) + 50 ); // scale * 10
|
||||
WRITE_BYTE( RANDOM_LONG(0, 3) + 8 ); // framerate
|
||||
MESSAGE_END();
|
||||
}
|
|
@ -0,0 +1,61 @@
|
|||
/***
|
||||
*
|
||||
* 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.
|
||||
*
|
||||
****/
|
||||
/*
|
||||
|
||||
===== h_export.cpp ========================================================
|
||||
|
||||
Entity classes exported by Halflife.
|
||||
|
||||
*/
|
||||
|
||||
#include "extdll.h"
|
||||
#include "util.h"
|
||||
|
||||
#include "cbase.h"
|
||||
|
||||
|
||||
#ifdef _WIN32
|
||||
// Required DLL entry point
|
||||
BOOL WINAPI DllMain(
|
||||
HINSTANCE hinstDLL,
|
||||
DWORD fdwReason,
|
||||
LPVOID lpvReserved)
|
||||
{
|
||||
if (fdwReason == DLL_PROCESS_ATTACH)
|
||||
{
|
||||
}
|
||||
else if (fdwReason == DLL_PROCESS_DETACH)
|
||||
{
|
||||
}
|
||||
return TRUE;
|
||||
}
|
||||
#endif
|
||||
|
||||
// Holds engine functionality callbacks
|
||||
enginefuncs_t g_engfuncs;
|
||||
globalvars_t *gpGlobals;
|
||||
|
||||
#ifdef _WIN32
|
||||
void DLLEXPORT GiveFnptrsToDll( enginefuncs_t* pengfuncsFromEngine, globalvars_t *pGlobals )
|
||||
#else
|
||||
extern "C" void DLLEXPORT GiveFnptrsToDll( enginefuncs_t* pengfuncsFromEngine, globalvars_t *pGlobals )
|
||||
#endif
|
||||
{
|
||||
memcpy(&g_engfuncs, pengfuncsFromEngine, sizeof(enginefuncs_t));
|
||||
gpGlobals = pGlobals;
|
||||
}
|
||||
|
||||
|
||||
|
|
@ -0,0 +1,245 @@
|
|||
/***
|
||||
*
|
||||
* 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"
|
||||
|
||||
|
||||
#define HANDGRENADE_PRIMARY_VOLUME 450
|
||||
|
||||
enum handgrenade_e {
|
||||
HANDGRENADE_IDLE = 0,
|
||||
HANDGRENADE_FIDGET,
|
||||
HANDGRENADE_PINPULL,
|
||||
HANDGRENADE_THROW1, // toss
|
||||
HANDGRENADE_THROW2, // medium
|
||||
HANDGRENADE_THROW3, // hard
|
||||
HANDGRENADE_HOLSTER,
|
||||
HANDGRENADE_DRAW
|
||||
};
|
||||
|
||||
|
||||
class CHandGrenade : public CBasePlayerWeapon
|
||||
{
|
||||
public:
|
||||
void Spawn( void );
|
||||
void Precache( void );
|
||||
int iItemSlot( void ) { return 5; }
|
||||
int GetItemInfo(ItemInfo *p);
|
||||
|
||||
void PrimaryAttack( void );
|
||||
BOOL Deploy( void );
|
||||
BOOL CanHolster( void );
|
||||
void Holster( int skiplocal = 0 );
|
||||
void WeaponIdle( void );
|
||||
float m_flStartThrow;
|
||||
float m_flReleaseThrow;
|
||||
};
|
||||
LINK_ENTITY_TO_CLASS( weapon_handgrenade, CHandGrenade );
|
||||
|
||||
|
||||
void CHandGrenade::Spawn( )
|
||||
{
|
||||
Precache( );
|
||||
m_iId = WEAPON_HANDGRENADE;
|
||||
SET_MODEL(ENT(pev), "models/w_grenade.mdl");
|
||||
|
||||
pev->dmg = gSkillData.plrDmgHandGrenade;
|
||||
|
||||
m_iDefaultAmmo = HANDGRENADE_DEFAULT_GIVE;
|
||||
|
||||
FallInit();// get ready to fall down.
|
||||
}
|
||||
|
||||
|
||||
void CHandGrenade::Precache( void )
|
||||
{
|
||||
PRECACHE_MODEL("models/w_grenade.mdl");
|
||||
PRECACHE_MODEL("models/v_grenade.mdl");
|
||||
PRECACHE_MODEL("models/p_grenade.mdl");
|
||||
}
|
||||
|
||||
int CHandGrenade::GetItemInfo(ItemInfo *p)
|
||||
{
|
||||
p->pszName = STRING(pev->classname);
|
||||
p->pszAmmo1 = "Hand Grenade";
|
||||
p->iMaxAmmo1 = HANDGRENADE_MAX_CARRY;
|
||||
p->pszAmmo2 = NULL;
|
||||
p->iMaxAmmo2 = -1;
|
||||
p->iMaxClip = WEAPON_NOCLIP;
|
||||
p->iSlot = 4;
|
||||
p->iPosition = 0;
|
||||
p->iId = m_iId = WEAPON_HANDGRENADE;
|
||||
p->iWeight = HANDGRENADE_WEIGHT;
|
||||
p->iFlags = ITEM_FLAG_LIMITINWORLD | ITEM_FLAG_EXHAUSTIBLE;
|
||||
|
||||
return 1;
|
||||
}
|
||||
|
||||
|
||||
BOOL CHandGrenade::Deploy( )
|
||||
{
|
||||
m_flReleaseThrow = -1;
|
||||
return DefaultDeploy( "models/v_grenade.mdl", "models/p_grenade.mdl", HANDGRENADE_DRAW, "crowbar" );
|
||||
}
|
||||
|
||||
BOOL CHandGrenade::CanHolster( void )
|
||||
{
|
||||
// can only holster hand grenades when not primed!
|
||||
return ( m_flStartThrow == 0 );
|
||||
}
|
||||
|
||||
void CHandGrenade::Holster( int skiplocal /* = 0 */ )
|
||||
{
|
||||
m_pPlayer->m_flNextAttack = UTIL_WeaponTimeBase() + 0.5;
|
||||
if (m_pPlayer->m_rgAmmo[m_iPrimaryAmmoType])
|
||||
{
|
||||
SendWeaponAnim( HANDGRENADE_HOLSTER );
|
||||
}
|
||||
else
|
||||
{
|
||||
// no more grenades!
|
||||
m_pPlayer->pev->weapons &= ~(1<<WEAPON_HANDGRENADE);
|
||||
SetThink( DestroyItem );
|
||||
pev->nextthink = gpGlobals->time + 0.1;
|
||||
}
|
||||
|
||||
EMIT_SOUND(ENT(m_pPlayer->pev), CHAN_WEAPON, "common/null.wav", 1.0, ATTN_NORM);
|
||||
}
|
||||
|
||||
void CHandGrenade::PrimaryAttack()
|
||||
{
|
||||
if (!m_flStartThrow && m_pPlayer->m_rgAmmo[m_iPrimaryAmmoType] > 0)
|
||||
{
|
||||
m_flStartThrow = gpGlobals->time;
|
||||
m_flReleaseThrow = 0;
|
||||
|
||||
SendWeaponAnim( HANDGRENADE_PINPULL );
|
||||
m_flTimeWeaponIdle = gpGlobals->time + 0.5;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
void CHandGrenade::WeaponIdle( void )
|
||||
{
|
||||
if (m_flReleaseThrow == 0)
|
||||
m_flReleaseThrow = gpGlobals->time;
|
||||
|
||||
if (m_flTimeWeaponIdle > gpGlobals->time)
|
||||
return;
|
||||
|
||||
if (m_flStartThrow)
|
||||
{
|
||||
Vector angThrow = m_pPlayer->pev->viewangles + 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;
|
||||
|
||||
// alway explode 3 seconds after the pin was pulled
|
||||
float time = m_flStartThrow - gpGlobals->time + 3.0;
|
||||
if (time < 0)
|
||||
time = 0;
|
||||
|
||||
CGrenade::ShootTimed( m_pPlayer->pev, vecSrc, vecThrow, time );
|
||||
|
||||
if (flVel < 500)
|
||||
{
|
||||
SendWeaponAnim( HANDGRENADE_THROW1 );
|
||||
}
|
||||
else if (flVel < 1000)
|
||||
{
|
||||
SendWeaponAnim( HANDGRENADE_THROW2 );
|
||||
}
|
||||
else
|
||||
{
|
||||
SendWeaponAnim( HANDGRENADE_THROW3 );
|
||||
}
|
||||
|
||||
// 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 grenade
|
||||
// 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;
|
||||
|
||||
if (m_pPlayer->m_rgAmmo[m_iPrimaryAmmoType])
|
||||
{
|
||||
SendWeaponAnim( HANDGRENADE_DRAW );
|
||||
}
|
||||
else
|
||||
{
|
||||
RetireWeapon();
|
||||
return;
|
||||
}
|
||||
|
||||
m_flTimeWeaponIdle = gpGlobals->time + RANDOM_FLOAT ( 10, 15 );
|
||||
m_flReleaseThrow = -1;
|
||||
return;
|
||||
}
|
||||
|
||||
if (m_pPlayer->m_rgAmmo[m_iPrimaryAmmoType])
|
||||
{
|
||||
int iAnim;
|
||||
float flRand = RANDOM_FLOAT(0, 1);
|
||||
if (flRand <= 0.75)
|
||||
{
|
||||
iAnim = HANDGRENADE_IDLE;
|
||||
m_flTimeWeaponIdle = gpGlobals->time + RANDOM_FLOAT ( 10, 15 );// how long till we do this again.
|
||||
}
|
||||
else
|
||||
{
|
||||
iAnim = HANDGRENADE_FIDGET;
|
||||
m_flTimeWeaponIdle = gpGlobals->time + 75.0 / 30.0;
|
||||
}
|
||||
|
||||
SendWeaponAnim( iAnim );
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
File diff suppressed because it is too large
Load Diff
|
@ -0,0 +1,555 @@
|
|||
/***
|
||||
*
|
||||
* 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.
|
||||
*
|
||||
****/
|
||||
//=========================================================
|
||||
// headcrab.cpp - tiny, jumpy alien parasite
|
||||
//=========================================================
|
||||
|
||||
#include "extdll.h"
|
||||
#include "util.h"
|
||||
#include "cbase.h"
|
||||
#include "monsters.h"
|
||||
#include "schedule.h"
|
||||
|
||||
|
||||
//=========================================================
|
||||
// Monster's Anim Events Go Here
|
||||
//=========================================================
|
||||
#define HC_AE_JUMPATTACK ( 2 )
|
||||
|
||||
Task_t tlHCRangeAttack1[] =
|
||||
{
|
||||
{ TASK_STOP_MOVING, (float)0 },
|
||||
{ TASK_FACE_IDEAL, (float)0 },
|
||||
{ TASK_RANGE_ATTACK1, (float)0 },
|
||||
{ TASK_SET_ACTIVITY, (float)ACT_IDLE },
|
||||
{ TASK_FACE_IDEAL, (float)0 },
|
||||
{ TASK_WAIT_RANDOM, (float)0.5 },
|
||||
};
|
||||
|
||||
Schedule_t slHCRangeAttack1[] =
|
||||
{
|
||||
{
|
||||
tlHCRangeAttack1,
|
||||
ARRAYSIZE ( tlHCRangeAttack1 ),
|
||||
bits_COND_ENEMY_OCCLUDED |
|
||||
bits_COND_NO_AMMO_LOADED,
|
||||
0,
|
||||
"HCRangeAttack1"
|
||||
},
|
||||
};
|
||||
|
||||
Task_t tlHCRangeAttack1Fast[] =
|
||||
{
|
||||
{ TASK_STOP_MOVING, (float)0 },
|
||||
{ TASK_FACE_IDEAL, (float)0 },
|
||||
{ TASK_RANGE_ATTACK1, (float)0 },
|
||||
{ TASK_SET_ACTIVITY, (float)ACT_IDLE },
|
||||
};
|
||||
|
||||
Schedule_t slHCRangeAttack1Fast[] =
|
||||
{
|
||||
{
|
||||
tlHCRangeAttack1Fast,
|
||||
ARRAYSIZE ( tlHCRangeAttack1Fast ),
|
||||
bits_COND_ENEMY_OCCLUDED |
|
||||
bits_COND_NO_AMMO_LOADED,
|
||||
0,
|
||||
"HCRAFast"
|
||||
},
|
||||
};
|
||||
|
||||
class CHeadCrab : public CBaseMonster
|
||||
{
|
||||
public:
|
||||
void Spawn( void );
|
||||
void Precache( void );
|
||||
void RunTask ( Task_t *pTask );
|
||||
void StartTask ( Task_t *pTask );
|
||||
void SetYawSpeed ( void );
|
||||
void EXPORT LeapTouch ( CBaseEntity *pOther );
|
||||
Vector Center( void );
|
||||
Vector BodyTarget( const Vector &posSrc );
|
||||
void PainSound( void );
|
||||
void DeathSound( void );
|
||||
void IdleSound( void );
|
||||
void AlertSound( void );
|
||||
void PrescheduleThink( void );
|
||||
int Classify ( void );
|
||||
void HandleAnimEvent( MonsterEvent_t *pEvent );
|
||||
BOOL CheckRangeAttack1 ( float flDot, float flDist );
|
||||
BOOL CheckRangeAttack2 ( float flDot, float flDist );
|
||||
int TakeDamage( entvars_t *pevInflictor, entvars_t *pevAttacker, float flDamage, int bitsDamageType );
|
||||
|
||||
virtual float GetDamageAmount( void ) { return gSkillData.headcrabDmgBite; }
|
||||
virtual int GetVoicePitch( void ) { return 100; }
|
||||
virtual float GetSoundVolue( void ) { return 1.0; }
|
||||
Schedule_t* GetScheduleOfType ( int Type );
|
||||
|
||||
CUSTOM_SCHEDULES;
|
||||
|
||||
static const char *pIdleSounds[];
|
||||
static const char *pAlertSounds[];
|
||||
static const char *pPainSounds[];
|
||||
static const char *pAttackSounds[];
|
||||
static const char *pDeathSounds[];
|
||||
static const char *pBiteSounds[];
|
||||
};
|
||||
LINK_ENTITY_TO_CLASS( monster_headcrab, CHeadCrab );
|
||||
|
||||
DEFINE_CUSTOM_SCHEDULES( CHeadCrab )
|
||||
{
|
||||
slHCRangeAttack1,
|
||||
slHCRangeAttack1Fast,
|
||||
};
|
||||
|
||||
IMPLEMENT_CUSTOM_SCHEDULES( CHeadCrab, CBaseMonster );
|
||||
|
||||
const char *CHeadCrab::pIdleSounds[] =
|
||||
{
|
||||
"headcrab/hc_idle1.wav",
|
||||
"headcrab/hc_idle2.wav",
|
||||
"headcrab/hc_idle3.wav",
|
||||
};
|
||||
const char *CHeadCrab::pAlertSounds[] =
|
||||
{
|
||||
"headcrab/hc_alert1.wav",
|
||||
};
|
||||
const char *CHeadCrab::pPainSounds[] =
|
||||
{
|
||||
"headcrab/hc_pain1.wav",
|
||||
"headcrab/hc_pain2.wav",
|
||||
"headcrab/hc_pain3.wav",
|
||||
};
|
||||
const char *CHeadCrab::pAttackSounds[] =
|
||||
{
|
||||
"headcrab/hc_attack1.wav",
|
||||
"headcrab/hc_attack2.wav",
|
||||
"headcrab/hc_attack3.wav",
|
||||
};
|
||||
|
||||
const char *CHeadCrab::pDeathSounds[] =
|
||||
{
|
||||
"headcrab/hc_die1.wav",
|
||||
"headcrab/hc_die2.wav",
|
||||
};
|
||||
|
||||
const char *CHeadCrab::pBiteSounds[] =
|
||||
{
|
||||
"headcrab/hc_headbite.wav",
|
||||
};
|
||||
|
||||
//=========================================================
|
||||
// Classify - indicates this monster's place in the
|
||||
// relationship table.
|
||||
//=========================================================
|
||||
int CHeadCrab :: Classify ( void )
|
||||
{
|
||||
return CLASS_ALIEN_PREY;
|
||||
}
|
||||
|
||||
//=========================================================
|
||||
// Center - returns the real center of the headcrab. The
|
||||
// bounding box is much larger than the actual creature so
|
||||
// this is needed for targeting
|
||||
//=========================================================
|
||||
Vector CHeadCrab :: Center ( void )
|
||||
{
|
||||
return Vector( pev->origin.x, pev->origin.y, pev->origin.z + 6 );
|
||||
}
|
||||
|
||||
|
||||
Vector CHeadCrab :: BodyTarget( const Vector &posSrc )
|
||||
{
|
||||
return Center( );
|
||||
}
|
||||
|
||||
//=========================================================
|
||||
// SetYawSpeed - allows each sequence to have a different
|
||||
// turn rate associated with it.
|
||||
//=========================================================
|
||||
void CHeadCrab :: SetYawSpeed ( void )
|
||||
{
|
||||
int ys;
|
||||
|
||||
switch ( m_Activity )
|
||||
{
|
||||
case ACT_IDLE:
|
||||
ys = 30;
|
||||
break;
|
||||
case ACT_RUN:
|
||||
case ACT_WALK:
|
||||
ys = 20;
|
||||
break;
|
||||
case ACT_TURN_LEFT:
|
||||
case ACT_TURN_RIGHT:
|
||||
ys = 60;
|
||||
break;
|
||||
case ACT_RANGE_ATTACK1:
|
||||
ys = 30;
|
||||
break;
|
||||
default:
|
||||
ys = 30;
|
||||
break;
|
||||
}
|
||||
|
||||
pev->yaw_speed = ys;
|
||||
}
|
||||
|
||||
//=========================================================
|
||||
// HandleAnimEvent - catches the monster-specific messages
|
||||
// that occur when tagged animation frames are played.
|
||||
//=========================================================
|
||||
void CHeadCrab :: HandleAnimEvent( MonsterEvent_t *pEvent )
|
||||
{
|
||||
switch( pEvent->event )
|
||||
{
|
||||
case HC_AE_JUMPATTACK:
|
||||
{
|
||||
ClearBits( pev->flags, FL_ONGROUND );
|
||||
|
||||
UTIL_SetOrigin (pev, pev->origin + Vector ( 0 , 0 , 1) );// take him off ground so engine doesn't instantly reset onground
|
||||
UTIL_MakeVectors ( pev->angles );
|
||||
|
||||
Vector vecJumpDir;
|
||||
if (m_hEnemy != NULL)
|
||||
{
|
||||
float gravity = CVAR_GET_FLOAT( "sv_gravity" );
|
||||
if (gravity <= 1)
|
||||
gravity = 1;
|
||||
|
||||
// How fast does the headcrab need to travel to reach that height given gravity?
|
||||
float height = (m_hEnemy->pev->origin.z + m_hEnemy->pev->view_ofs.z - pev->origin.z);
|
||||
if (height < 16)
|
||||
height = 16;
|
||||
float speed = sqrt( 2 * gravity * height );
|
||||
float time = speed / gravity;
|
||||
|
||||
// Scale the sideways velocity to get there at the right time
|
||||
vecJumpDir = (m_hEnemy->pev->origin + m_hEnemy->pev->view_ofs - pev->origin);
|
||||
vecJumpDir = vecJumpDir * ( 1.0 / time );
|
||||
|
||||
// Speed to offset gravity at the desired height
|
||||
vecJumpDir.z = speed;
|
||||
|
||||
// Don't jump too far/fast
|
||||
float distance = vecJumpDir.Length();
|
||||
|
||||
if (distance > 650)
|
||||
{
|
||||
vecJumpDir = vecJumpDir * ( 650.0 / distance );
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
// jump hop, don't care where
|
||||
vecJumpDir = Vector( gpGlobals->v_forward.x, gpGlobals->v_forward.y, gpGlobals->v_up.z ) * 350;
|
||||
}
|
||||
|
||||
int iSound = RANDOM_LONG(0,2);
|
||||
if ( iSound != 0 )
|
||||
EMIT_SOUND_DYN( edict(), CHAN_VOICE, pAttackSounds[iSound], GetSoundVolue(), ATTN_IDLE, 0, GetVoicePitch() );
|
||||
|
||||
pev->velocity = vecJumpDir;
|
||||
m_flNextAttack = gpGlobals->time + 2;
|
||||
}
|
||||
break;
|
||||
|
||||
default:
|
||||
CBaseMonster::HandleAnimEvent( pEvent );
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
//=========================================================
|
||||
// Spawn
|
||||
//=========================================================
|
||||
void CHeadCrab :: Spawn()
|
||||
{
|
||||
Precache( );
|
||||
|
||||
SET_MODEL(ENT(pev), "models/headcrab.mdl");
|
||||
UTIL_SetSize(pev, Vector(-12, -12, 0), Vector(12, 12, 24));
|
||||
|
||||
pev->solid = SOLID_SLIDEBOX;
|
||||
pev->movetype = MOVETYPE_STEP;
|
||||
m_bloodColor = BLOOD_COLOR_GREEN;
|
||||
pev->effects = 0;
|
||||
pev->health = gSkillData.headcrabHealth;
|
||||
pev->view_ofs = Vector ( 0, 0, 20 );// position of the eyes relative to monster's origin.
|
||||
pev->yaw_speed = 5;//!!! should we put this in the monster's changeanim function since turn rates may vary with state/anim?
|
||||
m_flFieldOfView = 0.5;// indicates the width of this monster's forward view cone ( as a dotproduct result )
|
||||
m_MonsterState = MONSTERSTATE_NONE;
|
||||
|
||||
MonsterInit();
|
||||
}
|
||||
|
||||
//=========================================================
|
||||
// Precache - precaches all resources this monster needs
|
||||
//=========================================================
|
||||
void CHeadCrab :: Precache()
|
||||
{
|
||||
PRECACHE_SOUND_ARRAY(pIdleSounds);
|
||||
PRECACHE_SOUND_ARRAY(pAlertSounds);
|
||||
PRECACHE_SOUND_ARRAY(pPainSounds);
|
||||
PRECACHE_SOUND_ARRAY(pAttackSounds);
|
||||
PRECACHE_SOUND_ARRAY(pDeathSounds);
|
||||
PRECACHE_SOUND_ARRAY(pBiteSounds);
|
||||
|
||||
PRECACHE_MODEL("models/headcrab.mdl");
|
||||
}
|
||||
|
||||
|
||||
//=========================================================
|
||||
// RunTask
|
||||
//=========================================================
|
||||
void CHeadCrab :: RunTask ( Task_t *pTask )
|
||||
{
|
||||
switch ( pTask->iTask )
|
||||
{
|
||||
case TASK_RANGE_ATTACK1:
|
||||
case TASK_RANGE_ATTACK2:
|
||||
{
|
||||
if ( m_fSequenceFinished )
|
||||
{
|
||||
TaskComplete();
|
||||
SetTouch( NULL );
|
||||
m_IdealActivity = ACT_IDLE;
|
||||
}
|
||||
break;
|
||||
}
|
||||
default:
|
||||
{
|
||||
CBaseMonster :: RunTask(pTask);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
//=========================================================
|
||||
// LeapTouch - this is the headcrab's touch function when it
|
||||
// is in the air
|
||||
//=========================================================
|
||||
void CHeadCrab :: LeapTouch ( CBaseEntity *pOther )
|
||||
{
|
||||
if ( !pOther->pev->takedamage )
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
if ( pOther->Classify() == Classify() )
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
// Don't hit if back on ground
|
||||
if ( !FBitSet( pev->flags, FL_ONGROUND ) )
|
||||
{
|
||||
EMIT_SOUND_DYN( edict(), CHAN_WEAPON, RANDOM_SOUND_ARRAY(pBiteSounds), GetSoundVolue(), ATTN_IDLE, 0, GetVoicePitch() );
|
||||
|
||||
pOther->TakeDamage( pev, pev, GetDamageAmount(), DMG_SLASH );
|
||||
}
|
||||
|
||||
SetTouch( NULL );
|
||||
}
|
||||
|
||||
//=========================================================
|
||||
// PrescheduleThink
|
||||
//=========================================================
|
||||
void CHeadCrab :: PrescheduleThink ( void )
|
||||
{
|
||||
// make the crab coo a little bit in combat state
|
||||
if ( m_MonsterState == MONSTERSTATE_COMBAT && RANDOM_FLOAT( 0, 5 ) < 0.1 )
|
||||
{
|
||||
IdleSound();
|
||||
}
|
||||
}
|
||||
|
||||
void CHeadCrab :: StartTask ( Task_t *pTask )
|
||||
{
|
||||
m_iTaskStatus = TASKSTATUS_RUNNING;
|
||||
|
||||
switch ( pTask->iTask )
|
||||
{
|
||||
case TASK_RANGE_ATTACK1:
|
||||
{
|
||||
EMIT_SOUND_DYN( edict(), CHAN_WEAPON, pAttackSounds[0], GetSoundVolue(), ATTN_IDLE, 0, GetVoicePitch() );
|
||||
m_IdealActivity = ACT_RANGE_ATTACK1;
|
||||
SetTouch ( LeapTouch );
|
||||
break;
|
||||
}
|
||||
default:
|
||||
{
|
||||
CBaseMonster :: StartTask( pTask );
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
//=========================================================
|
||||
// CheckRangeAttack1
|
||||
//=========================================================
|
||||
BOOL CHeadCrab :: CheckRangeAttack1 ( float flDot, float flDist )
|
||||
{
|
||||
if ( FBitSet( pev->flags, FL_ONGROUND ) && flDist <= 256 && flDot >= 0.65 )
|
||||
{
|
||||
return TRUE;
|
||||
}
|
||||
return FALSE;
|
||||
}
|
||||
|
||||
//=========================================================
|
||||
// CheckRangeAttack2
|
||||
//=========================================================
|
||||
BOOL CHeadCrab :: CheckRangeAttack2 ( float flDot, float flDist )
|
||||
{
|
||||
return FALSE;
|
||||
// BUGBUG: Why is this code here? There is no ACT_RANGE_ATTACK2 animation. I've disabled it for now.
|
||||
#if 0
|
||||
if ( FBitSet( pev->flags, FL_ONGROUND ) && flDist > 64 && flDist <= 256 && flDot >= 0.5 )
|
||||
{
|
||||
return TRUE;
|
||||
}
|
||||
return FALSE;
|
||||
#endif
|
||||
}
|
||||
|
||||
int CHeadCrab :: TakeDamage( entvars_t *pevInflictor, entvars_t *pevAttacker, float flDamage, int bitsDamageType )
|
||||
{
|
||||
// Don't take any acid damage -- BigMomma's mortar is acid
|
||||
if ( bitsDamageType & DMG_ACID )
|
||||
flDamage = 0;
|
||||
|
||||
return CBaseMonster::TakeDamage( pevInflictor, pevAttacker, flDamage, bitsDamageType );
|
||||
}
|
||||
|
||||
//=========================================================
|
||||
// IdleSound
|
||||
//=========================================================
|
||||
#define CRAB_ATTN_IDLE (float)1.5
|
||||
void CHeadCrab :: IdleSound ( void )
|
||||
{
|
||||
EMIT_SOUND_DYN( edict(), CHAN_VOICE, RANDOM_SOUND_ARRAY(pIdleSounds), GetSoundVolue(), ATTN_IDLE, 0, GetVoicePitch() );
|
||||
}
|
||||
|
||||
//=========================================================
|
||||
// AlertSound
|
||||
//=========================================================
|
||||
void CHeadCrab :: AlertSound ( void )
|
||||
{
|
||||
EMIT_SOUND_DYN( edict(), CHAN_VOICE, RANDOM_SOUND_ARRAY(pAlertSounds), GetSoundVolue(), ATTN_IDLE, 0, GetVoicePitch() );
|
||||
}
|
||||
|
||||
//=========================================================
|
||||
// AlertSound
|
||||
//=========================================================
|
||||
void CHeadCrab :: PainSound ( void )
|
||||
{
|
||||
EMIT_SOUND_DYN( edict(), CHAN_VOICE, RANDOM_SOUND_ARRAY(pPainSounds), GetSoundVolue(), ATTN_IDLE, 0, GetVoicePitch() );
|
||||
}
|
||||
|
||||
//=========================================================
|
||||
// DeathSound
|
||||
//=========================================================
|
||||
void CHeadCrab :: DeathSound ( void )
|
||||
{
|
||||
EMIT_SOUND_DYN( edict(), CHAN_VOICE, RANDOM_SOUND_ARRAY(pDeathSounds), GetSoundVolue(), ATTN_IDLE, 0, GetVoicePitch() );
|
||||
}
|
||||
|
||||
Schedule_t* CHeadCrab :: GetScheduleOfType ( int Type )
|
||||
{
|
||||
switch ( Type )
|
||||
{
|
||||
case SCHED_RANGE_ATTACK1:
|
||||
{
|
||||
return &slHCRangeAttack1[ 0 ];
|
||||
}
|
||||
break;
|
||||
}
|
||||
|
||||
return CBaseMonster::GetScheduleOfType( Type );
|
||||
}
|
||||
|
||||
|
||||
class CBabyCrab : public CHeadCrab
|
||||
{
|
||||
public:
|
||||
void Spawn( void );
|
||||
void Precache( void );
|
||||
void SetYawSpeed ( void );
|
||||
float GetDamageAmount( void ) { return gSkillData.headcrabDmgBite * 0.3; }
|
||||
BOOL CheckRangeAttack1 ( float flDot, float flDist );
|
||||
Schedule_t* GetScheduleOfType ( int Type );
|
||||
virtual int GetVoicePitch( void ) { return PITCH_NORM + RANDOM_LONG(40,50); }
|
||||
virtual float GetSoundVolue( void ) { return 0.8; }
|
||||
};
|
||||
LINK_ENTITY_TO_CLASS( monster_babycrab, CBabyCrab );
|
||||
|
||||
void CBabyCrab :: Spawn( void )
|
||||
{
|
||||
CHeadCrab::Spawn();
|
||||
SET_MODEL(ENT(pev), "models/baby_headcrab.mdl");
|
||||
pev->rendermode = kRenderTransTexture;
|
||||
pev->renderamt = 192;
|
||||
UTIL_SetSize(pev, Vector(-12, -12, 0), Vector(12, 12, 24));
|
||||
|
||||
pev->health = gSkillData.headcrabHealth * 0.25; // less health than full grown
|
||||
}
|
||||
|
||||
void CBabyCrab :: Precache( void )
|
||||
{
|
||||
PRECACHE_MODEL( "models/baby_headcrab.mdl" );
|
||||
CHeadCrab::Precache();
|
||||
}
|
||||
|
||||
|
||||
void CBabyCrab :: SetYawSpeed ( void )
|
||||
{
|
||||
pev->yaw_speed = 120;
|
||||
}
|
||||
|
||||
|
||||
BOOL CBabyCrab :: CheckRangeAttack1( float flDot, float flDist )
|
||||
{
|
||||
if ( pev->flags & FL_ONGROUND )
|
||||
{
|
||||
if ( pev->groundentity && (pev->groundentity->v.flags & (FL_CLIENT|FL_MONSTER)) )
|
||||
return TRUE;
|
||||
|
||||
// A little less accurate, but jump from closer
|
||||
if ( flDist <= 180 && flDot >= 0.55 )
|
||||
return TRUE;
|
||||
}
|
||||
|
||||
return FALSE;
|
||||
}
|
||||
|
||||
|
||||
Schedule_t* CBabyCrab :: GetScheduleOfType ( int Type )
|
||||
{
|
||||
switch( Type )
|
||||
{
|
||||
case SCHED_FAIL: // If you fail, try to jump!
|
||||
if ( m_hEnemy != NULL )
|
||||
return slHCRangeAttack1Fast;
|
||||
break;
|
||||
|
||||
case SCHED_RANGE_ATTACK1:
|
||||
{
|
||||
return slHCRangeAttack1Fast;
|
||||
}
|
||||
break;
|
||||
}
|
||||
|
||||
return CHeadCrab::GetScheduleOfType( Type );
|
||||
}
|
|
@ -0,0 +1,259 @@
|
|||
/***
|
||||
*
|
||||
* 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;
|
||||
|
||||
class CHealthKit : public CItem
|
||||
{
|
||||
void Spawn( void );
|
||||
void Precache( void );
|
||||
BOOL MyTouch( CBasePlayer *pPlayer );
|
||||
|
||||
/*
|
||||
virtual int Save( CSave &save );
|
||||
virtual int Restore( CRestore &restore );
|
||||
|
||||
static TYPEDESCRIPTION m_SaveData[];
|
||||
*/
|
||||
|
||||
};
|
||||
|
||||
|
||||
LINK_ENTITY_TO_CLASS( item_healthkit, CHealthKit );
|
||||
|
||||
/*
|
||||
TYPEDESCRIPTION CHealthKit::m_SaveData[] =
|
||||
{
|
||||
|
||||
};
|
||||
|
||||
|
||||
IMPLEMENT_SAVERESTORE( CHealthKit, CItem);
|
||||
*/
|
||||
|
||||
void CHealthKit :: Spawn( void )
|
||||
{
|
||||
Precache( );
|
||||
SET_MODEL(ENT(pev), "models/w_medkit.mdl");
|
||||
|
||||
CItem::Spawn();
|
||||
}
|
||||
|
||||
void CHealthKit::Precache( void )
|
||||
{
|
||||
PRECACHE_MODEL("models/w_medkit.mdl");
|
||||
PRECACHE_SOUND("items/smallmedkit1.wav");
|
||||
}
|
||||
|
||||
BOOL CHealthKit::MyTouch( CBasePlayer *pPlayer )
|
||||
{
|
||||
if ( pPlayer->TakeHealth( gSkillData.healthkitCapacity, DMG_GENERIC ) )
|
||||
{
|
||||
MESSAGE_BEGIN( MSG_ONE, gmsgItemPickup, NULL, pPlayer->pev );
|
||||
WRITE_STRING( STRING(pev->classname) );
|
||||
MESSAGE_END();
|
||||
|
||||
EMIT_SOUND(ENT(pPlayer->pev), CHAN_ITEM, "items/smallmedkit1.wav", 1, ATTN_NORM);
|
||||
|
||||
if ( g_pGameRules->ItemShouldRespawn( this ) )
|
||||
{
|
||||
Respawn();
|
||||
}
|
||||
else
|
||||
{
|
||||
UTIL_Remove(this);
|
||||
}
|
||||
|
||||
return TRUE;
|
||||
}
|
||||
|
||||
return FALSE;
|
||||
}
|
||||
|
||||
|
||||
|
||||
//-------------------------------------------------------------
|
||||
// Wall mounted health kit
|
||||
//-------------------------------------------------------------
|
||||
class CWallHealth : public CBaseToggle
|
||||
{
|
||||
public:
|
||||
void Spawn( );
|
||||
void Precache( void );
|
||||
void EXPORT Off(void);
|
||||
void EXPORT Recharge(void);
|
||||
void KeyValue( KeyValueData *pkvd );
|
||||
void Use( CBaseEntity *pActivator, CBaseEntity *pCaller, USE_TYPE useType, float value );
|
||||
virtual int ObjectCaps( void ) { return (CBaseToggle :: ObjectCaps() | FCAP_CONTINUOUS_USE) & ~FCAP_ACROSS_TRANSITION; }
|
||||
virtual int Save( CSave &save );
|
||||
virtual int Restore( CRestore &restore );
|
||||
|
||||
static TYPEDESCRIPTION m_SaveData[];
|
||||
|
||||
float m_flNextCharge;
|
||||
int m_iReactivate ; // DeathMatch Delay until reactvated
|
||||
int m_iJuice;
|
||||
int m_iOn; // 0 = off, 1 = startup, 2 = going
|
||||
float m_flSoundTime;
|
||||
};
|
||||
|
||||
TYPEDESCRIPTION CWallHealth::m_SaveData[] =
|
||||
{
|
||||
DEFINE_FIELD( CWallHealth, m_flNextCharge, FIELD_TIME),
|
||||
DEFINE_FIELD( CWallHealth, m_iReactivate, FIELD_INTEGER),
|
||||
DEFINE_FIELD( CWallHealth, m_iJuice, FIELD_INTEGER),
|
||||
DEFINE_FIELD( CWallHealth, m_iOn, FIELD_INTEGER),
|
||||
DEFINE_FIELD( CWallHealth, m_flSoundTime, FIELD_TIME),
|
||||
};
|
||||
|
||||
IMPLEMENT_SAVERESTORE( CWallHealth, CBaseEntity );
|
||||
|
||||
LINK_ENTITY_TO_CLASS(func_healthcharger, CWallHealth);
|
||||
|
||||
|
||||
void CWallHealth::KeyValue( KeyValueData *pkvd )
|
||||
{
|
||||
if ( FStrEq(pkvd->szKeyName, "style") ||
|
||||
FStrEq(pkvd->szKeyName, "height") ||
|
||||
FStrEq(pkvd->szKeyName, "value1") ||
|
||||
FStrEq(pkvd->szKeyName, "value2") ||
|
||||
FStrEq(pkvd->szKeyName, "value3"))
|
||||
{
|
||||
pkvd->fHandled = TRUE;
|
||||
}
|
||||
else if (FStrEq(pkvd->szKeyName, "dmdelay"))
|
||||
{
|
||||
m_iReactivate = atoi(pkvd->szValue);
|
||||
pkvd->fHandled = TRUE;
|
||||
}
|
||||
else
|
||||
CBaseToggle::KeyValue( pkvd );
|
||||
}
|
||||
|
||||
void CWallHealth::Spawn()
|
||||
{
|
||||
Precache( );
|
||||
|
||||
pev->solid = SOLID_BSP;
|
||||
pev->movetype = MOVETYPE_PUSH;
|
||||
|
||||
UTIL_SetOrigin(pev, pev->origin); // set size and link into world
|
||||
UTIL_SetSize(pev, pev->mins, pev->maxs);
|
||||
SET_MODEL(ENT(pev), STRING(pev->model) );
|
||||
m_iJuice = gSkillData.healthchargerCapacity;
|
||||
pev->frame = 0;
|
||||
|
||||
}
|
||||
|
||||
void CWallHealth::Precache()
|
||||
{
|
||||
PRECACHE_SOUND("items/medshot4.wav");
|
||||
PRECACHE_SOUND("items/medshotno1.wav");
|
||||
PRECACHE_SOUND("items/medcharge4.wav");
|
||||
}
|
||||
|
||||
|
||||
void CWallHealth::Use( CBaseEntity *pActivator, CBaseEntity *pCaller, USE_TYPE useType, float value )
|
||||
{
|
||||
// Make sure that we have a caller
|
||||
if (!pActivator)
|
||||
return;
|
||||
// if it's not a player, ignore
|
||||
if ( !pActivator->IsPlayer() )
|
||||
return;
|
||||
|
||||
// if there is no juice left, turn it off
|
||||
if (m_iJuice <= 0)
|
||||
{
|
||||
pev->frame = 1;
|
||||
Off();
|
||||
}
|
||||
|
||||
// if the player doesn't have the suit, or there is no juice left, make the deny noise
|
||||
if ((m_iJuice <= 0) || (!(pActivator->pev->weapons & (1<<WEAPON_SUIT))))
|
||||
{
|
||||
if (m_flSoundTime <= gpGlobals->time)
|
||||
{
|
||||
m_flSoundTime = gpGlobals->time + 0.62;
|
||||
EMIT_SOUND(ENT(pev), CHAN_ITEM, "items/medshotno1.wav", 1.0, ATTN_NORM );
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
||||
pev->nextthink = pev->ltime + 0.25;
|
||||
SetThink(Off);
|
||||
|
||||
// Time to recharge yet?
|
||||
|
||||
if (m_flNextCharge >= gpGlobals->time)
|
||||
return;
|
||||
|
||||
// Play the on sound or the looping charging sound
|
||||
if (!m_iOn)
|
||||
{
|
||||
m_iOn++;
|
||||
EMIT_SOUND(ENT(pev), CHAN_ITEM, "items/medshot4.wav", 1.0, ATTN_NORM );
|
||||
m_flSoundTime = 0.56 + gpGlobals->time;
|
||||
}
|
||||
if ((m_iOn == 1) && (m_flSoundTime <= gpGlobals->time))
|
||||
{
|
||||
m_iOn++;
|
||||
EMIT_SOUND(ENT(pev), CHAN_STATIC, "items/medcharge4.wav", 1.0, ATTN_NORM );
|
||||
}
|
||||
|
||||
|
||||
// charge the player
|
||||
if ( pActivator->TakeHealth( 1, DMG_GENERIC ) )
|
||||
{
|
||||
m_iJuice--;
|
||||
}
|
||||
|
||||
// govern the rate of charge
|
||||
m_flNextCharge = gpGlobals->time + 0.1;
|
||||
}
|
||||
|
||||
void CWallHealth::Recharge(void)
|
||||
{
|
||||
EMIT_SOUND(ENT(pev), CHAN_ITEM, "items/medshot4.wav", 1.0, ATTN_NORM );
|
||||
m_iJuice = gSkillData.healthchargerCapacity;
|
||||
pev->frame = 0;
|
||||
SetThink( SUB_DoNothing );
|
||||
}
|
||||
|
||||
void CWallHealth::Off(void)
|
||||
{
|
||||
// Stop looping sound.
|
||||
if (m_iOn > 1)
|
||||
STOP_SOUND( ENT(pev), CHAN_STATIC, "items/medcharge4.wav" );
|
||||
|
||||
m_iOn = 0;
|
||||
|
||||
if ((!m_iJuice) && ( ( m_iReactivate = g_pGameRules->FlHealthChargerRechargeTime() ) > 0) )
|
||||
{
|
||||
pev->nextthink = pev->ltime + m_iReactivate;
|
||||
SetThink(Recharge);
|
||||
}
|
||||
else
|
||||
SetThink( SUB_DoNothing );
|
||||
}
|
File diff suppressed because it is too large
Load Diff
|
@ -0,0 +1,426 @@
|
|||
/***
|
||||
*
|
||||
* 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.
|
||||
*
|
||||
****/
|
||||
//=========================================================
|
||||
// Hornets
|
||||
//=========================================================
|
||||
|
||||
#include "extdll.h"
|
||||
#include "util.h"
|
||||
#include "cbase.h"
|
||||
#include "monsters.h"
|
||||
#include "weapons.h"
|
||||
#include "soundent.h"
|
||||
#include "hornet.h"
|
||||
#include "gamerules.h"
|
||||
|
||||
|
||||
int iHornetTrail;
|
||||
int iHornetPuff;
|
||||
|
||||
LINK_ENTITY_TO_CLASS( hornet, CHornet );
|
||||
|
||||
//=========================================================
|
||||
// Save/Restore
|
||||
//=========================================================
|
||||
TYPEDESCRIPTION CHornet::m_SaveData[] =
|
||||
{
|
||||
DEFINE_FIELD( CHornet, m_flStopAttack, FIELD_TIME ),
|
||||
DEFINE_FIELD( CHornet, m_iHornetType, FIELD_INTEGER ),
|
||||
DEFINE_FIELD( CHornet, m_flFlySpeed, FIELD_FLOAT ),
|
||||
};
|
||||
|
||||
IMPLEMENT_SAVERESTORE( CHornet, CBaseMonster );
|
||||
|
||||
//=========================================================
|
||||
// don't let hornets gib, ever.
|
||||
//=========================================================
|
||||
int CHornet :: TakeDamage( entvars_t *pevInflictor, entvars_t *pevAttacker, float flDamage, int bitsDamageType )
|
||||
{
|
||||
// filter these bits a little.
|
||||
bitsDamageType &= ~ ( DMG_ALWAYSGIB );
|
||||
bitsDamageType |= DMG_NEVERGIB;
|
||||
|
||||
return CBaseMonster :: TakeDamage ( pevInflictor, pevAttacker, flDamage, bitsDamageType );
|
||||
}
|
||||
|
||||
//=========================================================
|
||||
//=========================================================
|
||||
void CHornet :: Spawn( void )
|
||||
{
|
||||
Precache();
|
||||
|
||||
pev->movetype = MOVETYPE_FLY;
|
||||
pev->solid = SOLID_BBOX;
|
||||
pev->takedamage = DAMAGE_YES;
|
||||
pev->flags |= FL_MONSTER;
|
||||
pev->health = 1;// weak!
|
||||
|
||||
if ( g_pGameRules->IsMultiplayer() )
|
||||
{
|
||||
// hornets don't live as long in multiplayer
|
||||
m_flStopAttack = gpGlobals->time + 3.5;
|
||||
}
|
||||
else
|
||||
{
|
||||
m_flStopAttack = gpGlobals->time + 5.0;
|
||||
}
|
||||
|
||||
m_flFieldOfView = 0.9; // +- 25 degrees
|
||||
|
||||
if ( RANDOM_LONG ( 1, 5 ) <= 2 )
|
||||
{
|
||||
m_iHornetType = HORNET_TYPE_RED;
|
||||
m_flFlySpeed = HORNET_RED_SPEED;
|
||||
}
|
||||
else
|
||||
{
|
||||
m_iHornetType = HORNET_TYPE_ORANGE;
|
||||
m_flFlySpeed = HORNET_ORANGE_SPEED;
|
||||
}
|
||||
|
||||
SET_MODEL(ENT( pev ), "models/hornet.mdl");
|
||||
UTIL_SetSize( pev, Vector( -4, -4, -4 ), Vector( 4, 4, 4 ) );
|
||||
|
||||
SetTouch( DieTouch );
|
||||
SetThink( StartTrack );
|
||||
|
||||
edict_t *pSoundEnt = pev->owner;
|
||||
if ( !pSoundEnt )
|
||||
pSoundEnt = edict();
|
||||
|
||||
switch (RANDOM_LONG(0,2))
|
||||
{
|
||||
case 0: EMIT_SOUND( pSoundEnt, CHAN_WEAPON, "agrunt/ag_fire1.wav", 1, ATTN_NORM); break;
|
||||
case 1: EMIT_SOUND( pSoundEnt, CHAN_WEAPON, "agrunt/ag_fire2.wav", 1, ATTN_NORM); break;
|
||||
case 2: EMIT_SOUND( pSoundEnt, CHAN_WEAPON, "agrunt/ag_fire3.wav", 1, ATTN_NORM); break;
|
||||
}
|
||||
|
||||
if ( !FNullEnt(pev->owner) && (pev->owner->v.flags & FL_CLIENT) )
|
||||
{
|
||||
pev->dmg = gSkillData.plrDmgHornet;
|
||||
}
|
||||
else
|
||||
{
|
||||
// no real owner, or owner isn't a client.
|
||||
pev->dmg = gSkillData.monDmgHornet;
|
||||
}
|
||||
|
||||
pev->nextthink = gpGlobals->time + 0.1;
|
||||
ResetSequenceInfo( );
|
||||
}
|
||||
|
||||
|
||||
void CHornet :: Precache()
|
||||
{
|
||||
PRECACHE_MODEL("models/hornet.mdl");
|
||||
|
||||
PRECACHE_SOUND( "agrunt/ag_fire1.wav" );
|
||||
PRECACHE_SOUND( "agrunt/ag_fire2.wav" );
|
||||
PRECACHE_SOUND( "agrunt/ag_fire3.wav" );
|
||||
|
||||
PRECACHE_SOUND( "hornet/ag_buzz1.wav" );
|
||||
PRECACHE_SOUND( "hornet/ag_buzz2.wav" );
|
||||
PRECACHE_SOUND( "hornet/ag_buzz3.wav" );
|
||||
|
||||
PRECACHE_SOUND( "hornet/ag_hornethit1.wav" );
|
||||
PRECACHE_SOUND( "hornet/ag_hornethit2.wav" );
|
||||
PRECACHE_SOUND( "hornet/ag_hornethit3.wav" );
|
||||
|
||||
iHornetPuff = PRECACHE_MODEL( "sprites/muz1.spr" );
|
||||
iHornetTrail = PRECACHE_MODEL("sprites/laserbeam.spr");
|
||||
}
|
||||
|
||||
//=========================================================
|
||||
// hornets will never get mad at each other, no matter who the owner is.
|
||||
//=========================================================
|
||||
int CHornet::IRelationship ( CBaseEntity *pTarget )
|
||||
{
|
||||
if ( pTarget->pev->modelindex == pev->modelindex )
|
||||
{
|
||||
return R_NO;
|
||||
}
|
||||
|
||||
return CBaseMonster :: IRelationship( pTarget );
|
||||
}
|
||||
|
||||
//=========================================================
|
||||
// ID's Hornet as their owner
|
||||
//=========================================================
|
||||
int CHornet::Classify ( void )
|
||||
{
|
||||
|
||||
if ( pev->owner && pev->owner->v.flags & FL_CLIENT)
|
||||
{
|
||||
return CLASS_PLAYER_BIOWEAPON;
|
||||
}
|
||||
|
||||
return CLASS_ALIEN_BIOWEAPON;
|
||||
}
|
||||
|
||||
//=========================================================
|
||||
// StartTrack - starts a hornet out tracking its target
|
||||
//=========================================================
|
||||
void CHornet :: StartTrack ( void )
|
||||
{
|
||||
IgniteTrail();
|
||||
|
||||
SetTouch( TrackTouch );
|
||||
SetThink( TrackTarget );
|
||||
|
||||
pev->nextthink = gpGlobals->time + 0.1;
|
||||
}
|
||||
|
||||
//=========================================================
|
||||
// StartDart - starts a hornet out just flying straight.
|
||||
//=========================================================
|
||||
void CHornet :: StartDart ( void )
|
||||
{
|
||||
IgniteTrail();
|
||||
|
||||
SetTouch( DartTouch );
|
||||
|
||||
SetThink( SUB_Remove );
|
||||
pev->nextthink = gpGlobals->time + 4;
|
||||
}
|
||||
|
||||
void CHornet::IgniteTrail( void )
|
||||
{
|
||||
/*
|
||||
|
||||
ted's suggested trail colors:
|
||||
|
||||
r161
|
||||
g25
|
||||
b97
|
||||
|
||||
r173
|
||||
g39
|
||||
b14
|
||||
|
||||
old colors
|
||||
case HORNET_TYPE_RED:
|
||||
WRITE_BYTE( 255 ); // r, g, b
|
||||
WRITE_BYTE( 128 ); // r, g, b
|
||||
WRITE_BYTE( 0 ); // r, g, b
|
||||
break;
|
||||
case HORNET_TYPE_ORANGE:
|
||||
WRITE_BYTE( 0 ); // r, g, b
|
||||
WRITE_BYTE( 100 ); // r, g, b
|
||||
WRITE_BYTE( 255 ); // r, g, b
|
||||
break;
|
||||
|
||||
*/
|
||||
|
||||
// trail
|
||||
MESSAGE_BEGIN( MSG_BROADCAST, SVC_TEMPENTITY );
|
||||
WRITE_BYTE( TE_BEAMFOLLOW );
|
||||
WRITE_SHORT( entindex() ); // entity
|
||||
WRITE_SHORT( iHornetTrail ); // model
|
||||
WRITE_BYTE( 10 ); // life
|
||||
WRITE_BYTE( 2 ); // width
|
||||
|
||||
switch ( m_iHornetType )
|
||||
{
|
||||
case HORNET_TYPE_RED:
|
||||
WRITE_BYTE( 179 ); // r, g, b
|
||||
WRITE_BYTE( 39 ); // r, g, b
|
||||
WRITE_BYTE( 14 ); // r, g, b
|
||||
break;
|
||||
case HORNET_TYPE_ORANGE:
|
||||
WRITE_BYTE( 255 ); // r, g, b
|
||||
WRITE_BYTE( 128 ); // r, g, b
|
||||
WRITE_BYTE( 0 ); // r, g, b
|
||||
break;
|
||||
}
|
||||
|
||||
WRITE_BYTE( 128 ); // brightness
|
||||
|
||||
MESSAGE_END();
|
||||
}
|
||||
|
||||
//=========================================================
|
||||
// Hornet is flying, gently tracking target
|
||||
//=========================================================
|
||||
void CHornet :: TrackTarget ( void )
|
||||
{
|
||||
Vector vecFlightDir;
|
||||
Vector vecDirToEnemy;
|
||||
float flDelta;
|
||||
|
||||
StudioFrameAdvance( );
|
||||
|
||||
if (gpGlobals->time > m_flStopAttack)
|
||||
{
|
||||
SetTouch( NULL );
|
||||
SetThink( SUB_Remove );
|
||||
pev->nextthink = gpGlobals->time + 0.1;
|
||||
return;
|
||||
}
|
||||
|
||||
// UNDONE: The player pointer should come back after returning from another level
|
||||
if ( m_hEnemy == NULL )
|
||||
{// enemy is dead.
|
||||
Look( 512 );
|
||||
m_hEnemy = BestVisibleEnemy( );
|
||||
}
|
||||
|
||||
if ( m_hEnemy != NULL && FVisible( m_hEnemy ))
|
||||
{
|
||||
m_vecEnemyLKP = m_hEnemy->BodyTarget( pev->origin );
|
||||
}
|
||||
else
|
||||
{
|
||||
m_vecEnemyLKP = m_vecEnemyLKP + pev->velocity * m_flFlySpeed * 0.1;
|
||||
}
|
||||
|
||||
vecDirToEnemy = ( m_vecEnemyLKP - pev->origin ).Normalize();
|
||||
|
||||
if (pev->velocity.Length() < 0.1)
|
||||
vecFlightDir = vecDirToEnemy;
|
||||
else
|
||||
vecFlightDir = pev->velocity.Normalize();
|
||||
|
||||
// measure how far the turn is, the wider the turn, the slow we'll go this time.
|
||||
flDelta = DotProduct ( vecFlightDir, vecDirToEnemy );
|
||||
|
||||
if ( flDelta < 0.5 )
|
||||
{// hafta turn wide again. play sound
|
||||
switch (RANDOM_LONG(0,2))
|
||||
{
|
||||
case 0: EMIT_SOUND( ENT(pev), CHAN_VOICE, "hornet/ag_buzz1.wav", HORNET_BUZZ_VOLUME, ATTN_NORM); break;
|
||||
case 1: EMIT_SOUND( ENT(pev), CHAN_VOICE, "hornet/ag_buzz2.wav", HORNET_BUZZ_VOLUME, ATTN_NORM); break;
|
||||
case 2: EMIT_SOUND( ENT(pev), CHAN_VOICE, "hornet/ag_buzz3.wav", HORNET_BUZZ_VOLUME, ATTN_NORM); break;
|
||||
}
|
||||
}
|
||||
|
||||
if ( flDelta <= 0 && m_iHornetType == HORNET_TYPE_RED )
|
||||
{// no flying backwards, but we don't want to invert this, cause we'd go fast when we have to turn REAL far.
|
||||
flDelta = 0.25;
|
||||
}
|
||||
|
||||
pev->velocity = ( vecFlightDir + vecDirToEnemy).Normalize();
|
||||
|
||||
if ( pev->owner && (pev->owner->v.flags & FL_MONSTER) )
|
||||
{
|
||||
// random pattern only applies to hornets fired by monsters, not players.
|
||||
|
||||
pev->velocity.x += RANDOM_FLOAT ( -0.10, 0.10 );// scramble the flight dir a bit.
|
||||
pev->velocity.y += RANDOM_FLOAT ( -0.10, 0.10 );
|
||||
pev->velocity.z += RANDOM_FLOAT ( -0.10, 0.10 );
|
||||
}
|
||||
|
||||
switch ( m_iHornetType )
|
||||
{
|
||||
case HORNET_TYPE_RED:
|
||||
pev->velocity = pev->velocity * ( m_flFlySpeed * flDelta );// scale the dir by the ( speed * width of turn )
|
||||
pev->nextthink = gpGlobals->time + RANDOM_FLOAT( 0.1, 0.3 );
|
||||
break;
|
||||
case HORNET_TYPE_ORANGE:
|
||||
pev->velocity = pev->velocity * m_flFlySpeed;// do not have to slow down to turn.
|
||||
pev->nextthink = gpGlobals->time + 0.1;// fixed think time
|
||||
break;
|
||||
}
|
||||
|
||||
pev->angles = UTIL_VecToAngles (pev->velocity);
|
||||
|
||||
pev->solid = SOLID_BBOX;
|
||||
|
||||
// if hornet is close to the enemy, jet in a straight line for a half second.
|
||||
// (only in the single player game)
|
||||
if ( m_hEnemy != NULL && !g_pGameRules->IsMultiplayer() )
|
||||
{
|
||||
if ( flDelta >= 0.4 && ( pev->origin - m_vecEnemyLKP ).Length() <= 300 )
|
||||
{
|
||||
MESSAGE_BEGIN( MSG_PVS, SVC_TEMPENTITY, pev->origin );
|
||||
WRITE_BYTE( TE_SPRITE );
|
||||
WRITE_COORD( pev->origin.x); // pos
|
||||
WRITE_COORD( pev->origin.y);
|
||||
WRITE_COORD( pev->origin.z);
|
||||
WRITE_SHORT( iHornetPuff ); // model
|
||||
// WRITE_BYTE( 0 ); // life * 10
|
||||
WRITE_BYTE( 2 ); // size * 10
|
||||
WRITE_BYTE( 128 ); // brightness
|
||||
MESSAGE_END();
|
||||
|
||||
switch (RANDOM_LONG(0,2))
|
||||
{
|
||||
case 0: EMIT_SOUND( ENT(pev), CHAN_VOICE, "hornet/ag_buzz1.wav", HORNET_BUZZ_VOLUME, ATTN_NORM); break;
|
||||
case 1: EMIT_SOUND( ENT(pev), CHAN_VOICE, "hornet/ag_buzz2.wav", HORNET_BUZZ_VOLUME, ATTN_NORM); break;
|
||||
case 2: EMIT_SOUND( ENT(pev), CHAN_VOICE, "hornet/ag_buzz3.wav", HORNET_BUZZ_VOLUME, ATTN_NORM); break;
|
||||
}
|
||||
pev->velocity = pev->velocity * 2;
|
||||
pev->nextthink = gpGlobals->time + 1.0;
|
||||
// don't attack again
|
||||
m_flStopAttack = gpGlobals->time;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
//=========================================================
|
||||
// Tracking Hornet hit something
|
||||
//=========================================================
|
||||
void CHornet :: TrackTouch ( CBaseEntity *pOther )
|
||||
{
|
||||
if ( pOther->edict() == pev->owner || pOther->pev->modelindex == pev->modelindex )
|
||||
{// bumped into the guy that shot it.
|
||||
pev->solid = SOLID_NOT;
|
||||
return;
|
||||
}
|
||||
|
||||
if ( IRelationship( pOther ) <= R_NO )
|
||||
{
|
||||
// hit something we don't want to hurt, so turn around.
|
||||
|
||||
pev->velocity = pev->velocity.Normalize();
|
||||
|
||||
pev->velocity.x *= -1;
|
||||
pev->velocity.y *= -1;
|
||||
|
||||
pev->origin = pev->origin + pev->velocity * 4; // bounce the hornet off a bit.
|
||||
pev->velocity = pev->velocity * m_flFlySpeed;
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
DieTouch( pOther );
|
||||
}
|
||||
|
||||
void CHornet::DartTouch( CBaseEntity *pOther )
|
||||
{
|
||||
DieTouch( pOther );
|
||||
}
|
||||
|
||||
void CHornet::DieTouch ( CBaseEntity *pOther )
|
||||
{
|
||||
if ( pOther && pOther->pev->takedamage )
|
||||
{// do the damage
|
||||
|
||||
switch (RANDOM_LONG(0,2))
|
||||
{// buzz when you plug someone
|
||||
case 0: EMIT_SOUND( ENT(pev), CHAN_VOICE, "hornet/ag_hornethit1.wav", 1, ATTN_NORM); break;
|
||||
case 1: EMIT_SOUND( ENT(pev), CHAN_VOICE, "hornet/ag_hornethit2.wav", 1, ATTN_NORM); break;
|
||||
case 2: EMIT_SOUND( ENT(pev), CHAN_VOICE, "hornet/ag_hornethit3.wav", 1, ATTN_NORM); break;
|
||||
}
|
||||
|
||||
pOther->TakeDamage( pev, VARS( pev->owner ), pev->dmg, DMG_BULLET );
|
||||
}
|
||||
|
||||
pev->modelindex = 0;// so will disappear for the 0.1 secs we wait until NEXTTHINK gets rid
|
||||
pev->solid = SOLID_NOT;
|
||||
|
||||
SetThink ( SUB_Remove );
|
||||
pev->nextthink = gpGlobals->time + 1;// stick around long enough for the sound to finish!
|
||||
}
|
||||
|
|
@ -0,0 +1,58 @@
|
|||
/***
|
||||
*
|
||||
* 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.
|
||||
*
|
||||
****/
|
||||
//=========================================================
|
||||
// Hornets
|
||||
//=========================================================
|
||||
|
||||
//=========================================================
|
||||
// Hornet Defines
|
||||
//=========================================================
|
||||
#define HORNET_TYPE_RED 0
|
||||
#define HORNET_TYPE_ORANGE 1
|
||||
#define HORNET_RED_SPEED (float)600
|
||||
#define HORNET_ORANGE_SPEED (float)800
|
||||
#define HORNET_BUZZ_VOLUME (float)0.8
|
||||
|
||||
extern int iHornetPuff;
|
||||
|
||||
//=========================================================
|
||||
// Hornet - this is the projectile that the Alien Grunt fires.
|
||||
//=========================================================
|
||||
class CHornet : public CBaseMonster
|
||||
{
|
||||
public:
|
||||
void Spawn( void );
|
||||
void Precache( void );
|
||||
int Classify ( void );
|
||||
int IRelationship ( CBaseEntity *pTarget );
|
||||
virtual int Save( CSave &save );
|
||||
virtual int Restore( CRestore &restore );
|
||||
static TYPEDESCRIPTION m_SaveData[];
|
||||
|
||||
void IgniteTrail( void );
|
||||
void EXPORT StartTrack ( void );
|
||||
void EXPORT StartDart ( void );
|
||||
void EXPORT TrackTarget ( void );
|
||||
void EXPORT TrackTouch ( CBaseEntity *pOther );
|
||||
void EXPORT DartTouch( CBaseEntity *pOther );
|
||||
void EXPORT DieTouch ( CBaseEntity *pOther );
|
||||
|
||||
int TakeDamage( entvars_t *pevInflictor, entvars_t *pevAttacker, float flDamage, int bitsDamageType );
|
||||
|
||||
float m_flStopAttack;
|
||||
int m_iHornetType;
|
||||
float m_flFlySpeed;
|
||||
};
|
||||
|
|
@ -0,0 +1,293 @@
|
|||
/***
|
||||
*
|
||||
* 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 "hornet.h"
|
||||
#include "gamerules.h"
|
||||
|
||||
|
||||
enum hgun_e {
|
||||
HGUN_IDLE1 = 0,
|
||||
HGUN_FIDGETSWAY,
|
||||
HGUN_FIDGETSHAKE,
|
||||
HGUN_DOWN,
|
||||
HGUN_UP,
|
||||
HGUN_SHOOT
|
||||
};
|
||||
|
||||
class CHgun : 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 );
|
||||
void SecondaryAttack( void );
|
||||
BOOL Deploy( void );
|
||||
BOOL IsUseable( void );
|
||||
void Holster( int skiplocal = 0 );
|
||||
void Reload( void );
|
||||
void WeaponIdle( void );
|
||||
float m_flNextAnimTime;
|
||||
|
||||
float m_flRechargeTime;
|
||||
|
||||
int m_iFirePhase;// don't save me.
|
||||
};
|
||||
LINK_ENTITY_TO_CLASS( weapon_hornetgun, CHgun );
|
||||
|
||||
BOOL CHgun::IsUseable( void )
|
||||
{
|
||||
return TRUE;
|
||||
}
|
||||
|
||||
void CHgun::Spawn( )
|
||||
{
|
||||
Precache( );
|
||||
m_iId = WEAPON_HORNETGUN;
|
||||
SET_MODEL(ENT(pev), "models/w_hgun.mdl");
|
||||
|
||||
m_iDefaultAmmo = HIVEHAND_DEFAULT_GIVE;
|
||||
m_iFirePhase = 0;
|
||||
|
||||
FallInit();// get ready to fall down.
|
||||
}
|
||||
|
||||
|
||||
void CHgun::Precache( void )
|
||||
{
|
||||
PRECACHE_MODEL("models/v_hgun.mdl");
|
||||
PRECACHE_MODEL("models/w_hgun.mdl");
|
||||
PRECACHE_MODEL("models/p_hgun.mdl");
|
||||
|
||||
UTIL_PrecacheOther("hornet");
|
||||
}
|
||||
|
||||
int CHgun::AddToPlayer( CBasePlayer *pPlayer )
|
||||
{
|
||||
if ( CBasePlayerWeapon::AddToPlayer( pPlayer ) )
|
||||
{
|
||||
if ( g_pGameRules->IsMultiplayer() )
|
||||
{
|
||||
// in multiplayer, all hivehands come full.
|
||||
pPlayer->m_rgAmmo[ PrimaryAmmoIndex() ] = HORNET_MAX_CARRY;
|
||||
}
|
||||
|
||||
MESSAGE_BEGIN( MSG_ONE, gmsgWeapPickup, NULL, pPlayer->pev );
|
||||
WRITE_BYTE( m_iId );
|
||||
MESSAGE_END();
|
||||
return TRUE;
|
||||
}
|
||||
return FALSE;
|
||||
}
|
||||
|
||||
int CHgun::GetItemInfo(ItemInfo *p)
|
||||
{
|
||||
p->pszName = STRING(pev->classname);
|
||||
p->pszAmmo1 = "Hornets";
|
||||
p->iMaxAmmo1 = HORNET_MAX_CARRY;
|
||||
p->pszAmmo2 = NULL;
|
||||
p->iMaxAmmo2 = -1;
|
||||
p->iMaxClip = WEAPON_NOCLIP;
|
||||
p->iSlot = 3;
|
||||
p->iPosition = 3;
|
||||
p->iId = m_iId = WEAPON_HORNETGUN;
|
||||
p->iFlags = ITEM_FLAG_NOAUTOSWITCHEMPTY | ITEM_FLAG_NOAUTORELOAD;
|
||||
p->iWeight = HORNETGUN_WEIGHT;
|
||||
|
||||
return 1;
|
||||
}
|
||||
|
||||
|
||||
BOOL CHgun::Deploy( )
|
||||
{
|
||||
return DefaultDeploy( "models/v_hgun.mdl", "models/p_hgun.mdl", HGUN_UP, "hive" );
|
||||
}
|
||||
|
||||
void CHgun::Holster( int skiplocal /* = 0 */ )
|
||||
{
|
||||
m_pPlayer->m_flNextAttack = UTIL_WeaponTimeBase() + 0.5;
|
||||
// m_flTimeWeaponIdle = gpGlobals->time + RANDOM_FLOAT ( 10, 15 );
|
||||
SendWeaponAnim( HGUN_DOWN );
|
||||
|
||||
//!!!HACKHACK - can't select hornetgun if it's empty! no way to get ammo for it, either.
|
||||
if ( !m_pPlayer->m_rgAmmo[ PrimaryAmmoIndex() ] )
|
||||
{
|
||||
m_pPlayer->m_rgAmmo[ PrimaryAmmoIndex() ] = 1;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
void CHgun::PrimaryAttack()
|
||||
{
|
||||
Reload( );
|
||||
|
||||
if (m_pPlayer->m_rgAmmo[m_iPrimaryAmmoType] <= 0)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
UTIL_MakeVectors( m_pPlayer->pev->viewangles );
|
||||
|
||||
CBaseEntity *pHornet = CBaseEntity::Create( "hornet", m_pPlayer->GetGunPosition( ) + gpGlobals->v_forward * 16 + gpGlobals->v_right * 8 + gpGlobals->v_up * -12, m_pPlayer->pev->viewangles, m_pPlayer->edict() );
|
||||
pHornet->pev->velocity = gpGlobals->v_forward * 300;
|
||||
|
||||
m_pPlayer->m_rgAmmo[m_iPrimaryAmmoType]--;
|
||||
m_flRechargeTime = gpGlobals->time + 0.5;
|
||||
|
||||
m_pPlayer->m_iWeaponVolume = QUIET_GUN_VOLUME;
|
||||
m_pPlayer->m_iWeaponFlash = DIM_GUN_FLASH;
|
||||
|
||||
SendWeaponAnim( HGUN_SHOOT );
|
||||
|
||||
// player "shoot" animation
|
||||
m_pPlayer->SetAnimation( PLAYER_ATTACK1 );
|
||||
|
||||
m_flNextPrimaryAttack = m_flNextPrimaryAttack + 0.25;
|
||||
|
||||
if (m_flNextPrimaryAttack < gpGlobals->time)
|
||||
{
|
||||
m_flNextPrimaryAttack = gpGlobals->time + 0.25;
|
||||
}
|
||||
|
||||
m_flTimeWeaponIdle = gpGlobals->time + RANDOM_FLOAT ( 10, 15 );
|
||||
}
|
||||
|
||||
|
||||
|
||||
void CHgun::SecondaryAttack( void )
|
||||
{
|
||||
Reload();
|
||||
|
||||
if (m_pPlayer->m_rgAmmo[m_iPrimaryAmmoType] <= 0)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
CBaseEntity *pHornet;
|
||||
Vector vecSrc;
|
||||
|
||||
UTIL_MakeVectors( m_pPlayer->pev->viewangles );
|
||||
|
||||
vecSrc = m_pPlayer->GetGunPosition( ) + gpGlobals->v_forward * 16 + gpGlobals->v_right * 8 + gpGlobals->v_up * -12;
|
||||
|
||||
m_iFirePhase++;
|
||||
switch ( m_iFirePhase )
|
||||
{
|
||||
case 1:
|
||||
vecSrc = vecSrc + gpGlobals->v_up * 8;
|
||||
break;
|
||||
case 2:
|
||||
vecSrc = vecSrc + gpGlobals->v_up * 8;
|
||||
vecSrc = vecSrc + gpGlobals->v_right * 8;
|
||||
break;
|
||||
case 3:
|
||||
vecSrc = vecSrc + gpGlobals->v_right * 8;
|
||||
break;
|
||||
case 4:
|
||||
vecSrc = vecSrc + gpGlobals->v_up * -8;
|
||||
vecSrc = vecSrc + gpGlobals->v_right * 8;
|
||||
break;
|
||||
case 5:
|
||||
vecSrc = vecSrc + gpGlobals->v_up * -8;
|
||||
break;
|
||||
case 6:
|
||||
vecSrc = vecSrc + gpGlobals->v_up * -8;
|
||||
vecSrc = vecSrc + gpGlobals->v_right * -8;
|
||||
break;
|
||||
case 7:
|
||||
vecSrc = vecSrc + gpGlobals->v_right * -8;
|
||||
break;
|
||||
case 8:
|
||||
vecSrc = vecSrc + gpGlobals->v_up * 8;
|
||||
vecSrc = vecSrc + gpGlobals->v_right * -8;
|
||||
m_iFirePhase = 0;
|
||||
break;
|
||||
}
|
||||
|
||||
pHornet = CBaseEntity::Create( "hornet", vecSrc, m_pPlayer->pev->viewangles, m_pPlayer->edict() );
|
||||
pHornet->pev->velocity = gpGlobals->v_forward * 1200;
|
||||
pHornet->pev->angles = UTIL_VecToAngles( pHornet->pev->velocity );
|
||||
|
||||
pHornet->SetThink( CHornet::StartDart );
|
||||
|
||||
m_pPlayer->m_rgAmmo[m_iPrimaryAmmoType]--;
|
||||
m_pPlayer->m_iWeaponVolume = NORMAL_GUN_VOLUME;
|
||||
m_pPlayer->m_iWeaponFlash = DIM_GUN_FLASH;
|
||||
|
||||
m_flRechargeTime = gpGlobals->time + 0.5;
|
||||
|
||||
SendWeaponAnim( HGUN_SHOOT );
|
||||
|
||||
// player "shoot" animation
|
||||
m_pPlayer->SetAnimation( PLAYER_ATTACK1 );
|
||||
|
||||
m_flNextPrimaryAttack = m_flNextSecondaryAttack = gpGlobals->time + 0.1;
|
||||
m_flTimeWeaponIdle = gpGlobals->time + RANDOM_FLOAT ( 10, 15 );
|
||||
m_pPlayer->pev->punchangle.x = RANDOM_FLOAT( 0, 2 );
|
||||
}
|
||||
|
||||
|
||||
void CHgun::Reload( void )
|
||||
{
|
||||
if (m_pPlayer->m_rgAmmo[m_iPrimaryAmmoType] >= HORNET_MAX_CARRY)
|
||||
return;
|
||||
|
||||
while (m_pPlayer->m_rgAmmo[m_iPrimaryAmmoType] < HORNET_MAX_CARRY && m_flRechargeTime < gpGlobals->time)
|
||||
{
|
||||
m_pPlayer->m_rgAmmo[m_iPrimaryAmmoType]++;
|
||||
m_flRechargeTime += 0.5;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
void CHgun::WeaponIdle( void )
|
||||
{
|
||||
Reload( );
|
||||
|
||||
if (m_flTimeWeaponIdle > gpGlobals->time)
|
||||
return;
|
||||
|
||||
int iAnim;
|
||||
float flRand = RANDOM_FLOAT(0, 1);
|
||||
if (flRand <= 0.75)
|
||||
{
|
||||
iAnim = HGUN_IDLE1;
|
||||
m_flTimeWeaponIdle = gpGlobals->time + 30.0 / 16 * (2);
|
||||
}
|
||||
else if (flRand <= 0.875)
|
||||
{
|
||||
iAnim = HGUN_FIDGETSWAY;
|
||||
m_flTimeWeaponIdle = gpGlobals->time + 40.0 / 16.0;
|
||||
}
|
||||
else
|
||||
{
|
||||
iAnim = HGUN_FIDGETSHAKE;
|
||||
m_flTimeWeaponIdle = gpGlobals->time + 35.0 / 16.0;
|
||||
}
|
||||
SendWeaponAnim( iAnim );
|
||||
}
|
||||
|
||||
#endif
|
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
|
@ -0,0 +1,866 @@
|
|||
/***
|
||||
*
|
||||
* 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 slave monster
|
||||
//=========================================================
|
||||
|
||||
#include "extdll.h"
|
||||
#include "util.h"
|
||||
#include "cbase.h"
|
||||
#include "monsters.h"
|
||||
#include "squadmonster.h"
|
||||
#include "schedule.h"
|
||||
#include "effects.h"
|
||||
#include "weapons.h"
|
||||
#include "soundent.h"
|
||||
|
||||
extern DLL_GLOBAL int g_iSkillLevel;
|
||||
|
||||
//=========================================================
|
||||
// Monster's Anim Events Go Here
|
||||
//=========================================================
|
||||
#define ISLAVE_AE_CLAW ( 1 )
|
||||
#define ISLAVE_AE_CLAWRAKE ( 2 )
|
||||
#define ISLAVE_AE_ZAP_POWERUP ( 3 )
|
||||
#define ISLAVE_AE_ZAP_SHOOT ( 4 )
|
||||
#define ISLAVE_AE_ZAP_DONE ( 5 )
|
||||
|
||||
#define ISLAVE_MAX_BEAMS 8
|
||||
|
||||
class CISlave : public CSquadMonster
|
||||
{
|
||||
public:
|
||||
void Spawn( void );
|
||||
void Precache( void );
|
||||
void SetYawSpeed( void );
|
||||
int ISoundMask( void );
|
||||
int Classify ( void );
|
||||
int IRelationship( CBaseEntity *pTarget );
|
||||
void HandleAnimEvent( MonsterEvent_t *pEvent );
|
||||
BOOL CheckRangeAttack1 ( float flDot, float flDist );
|
||||
BOOL CheckRangeAttack2 ( float flDot, float flDist );
|
||||
void CallForHelp( char *szClassname, float flDist, EHANDLE hEnemy, Vector &vecLocation );
|
||||
void TraceAttack( entvars_t *pevAttacker, float flDamage, Vector vecDir, TraceResult *ptr, int bitsDamageType);
|
||||
int TakeDamage( entvars_t* pevInflictor, entvars_t* pevAttacker, float flDamage, int bitsDamageType);
|
||||
|
||||
void DeathSound( void );
|
||||
void PainSound( void );
|
||||
void AlertSound( void );
|
||||
void IdleSound( void );
|
||||
|
||||
void Killed( entvars_t *pevAttacker, int iGib );
|
||||
|
||||
void StartTask ( Task_t *pTask );
|
||||
Schedule_t *GetSchedule( void );
|
||||
Schedule_t *GetScheduleOfType ( int Type );
|
||||
CUSTOM_SCHEDULES;
|
||||
|
||||
int Save( CSave &save );
|
||||
int Restore( CRestore &restore );
|
||||
static TYPEDESCRIPTION m_SaveData[];
|
||||
|
||||
void ClearBeams( );
|
||||
void ArmBeam( int side );
|
||||
void WackBeam( int side, CBaseEntity *pEntity );
|
||||
void ZapBeam( int side );
|
||||
void BeamGlow( void );
|
||||
|
||||
int m_iBravery;
|
||||
|
||||
CBeam *m_pBeam[ISLAVE_MAX_BEAMS];
|
||||
|
||||
int m_iBeams;
|
||||
float m_flNextAttack;
|
||||
|
||||
int m_voicePitch;
|
||||
|
||||
EHANDLE m_hDead;
|
||||
|
||||
static const char *pAttackHitSounds[];
|
||||
static const char *pAttackMissSounds[];
|
||||
static const char *pPainSounds[];
|
||||
static const char *pDeathSounds[];
|
||||
};
|
||||
LINK_ENTITY_TO_CLASS( monster_alien_slave, CISlave );
|
||||
LINK_ENTITY_TO_CLASS( monster_vortigaunt, CISlave );
|
||||
|
||||
|
||||
TYPEDESCRIPTION CISlave::m_SaveData[] =
|
||||
{
|
||||
DEFINE_FIELD( CISlave, m_iBravery, FIELD_INTEGER ),
|
||||
|
||||
DEFINE_ARRAY( CISlave, m_pBeam, FIELD_CLASSPTR, ISLAVE_MAX_BEAMS ),
|
||||
DEFINE_FIELD( CISlave, m_iBeams, FIELD_INTEGER ),
|
||||
DEFINE_FIELD( CISlave, m_flNextAttack, FIELD_TIME ),
|
||||
|
||||
DEFINE_FIELD( CISlave, m_voicePitch, FIELD_INTEGER ),
|
||||
|
||||
DEFINE_FIELD( CISlave, m_hDead, FIELD_EHANDLE ),
|
||||
|
||||
};
|
||||
|
||||
IMPLEMENT_SAVERESTORE( CISlave, CSquadMonster );
|
||||
|
||||
|
||||
|
||||
|
||||
const char *CISlave::pAttackHitSounds[] =
|
||||
{
|
||||
"zombie/claw_strike1.wav",
|
||||
"zombie/claw_strike2.wav",
|
||||
"zombie/claw_strike3.wav",
|
||||
};
|
||||
|
||||
const char *CISlave::pAttackMissSounds[] =
|
||||
{
|
||||
"zombie/claw_miss1.wav",
|
||||
"zombie/claw_miss2.wav",
|
||||
};
|
||||
|
||||
const char *CISlave::pPainSounds[] =
|
||||
{
|
||||
"aslave/slv_pain1.wav",
|
||||
"aslave/slv_pain2.wav",
|
||||
};
|
||||
|
||||
const char *CISlave::pDeathSounds[] =
|
||||
{
|
||||
"aslave/slv_die1.wav",
|
||||
"aslave/slv_die2.wav",
|
||||
};
|
||||
|
||||
//=========================================================
|
||||
// Classify - indicates this monster's place in the
|
||||
// relationship table.
|
||||
//=========================================================
|
||||
int CISlave :: Classify ( void )
|
||||
{
|
||||
return CLASS_ALIEN_MILITARY;
|
||||
}
|
||||
|
||||
|
||||
int CISlave::IRelationship( CBaseEntity *pTarget )
|
||||
{
|
||||
if ( (pTarget->IsPlayer()) )
|
||||
if ( (pev->spawnflags & SF_MONSTER_WAIT_UNTIL_PROVOKED ) && ! (m_afMemory & bits_MEMORY_PROVOKED ))
|
||||
return R_NO;
|
||||
return CBaseMonster::IRelationship( pTarget );
|
||||
}
|
||||
|
||||
|
||||
void CISlave :: CallForHelp( char *szClassname, float flDist, EHANDLE hEnemy, Vector &vecLocation )
|
||||
{
|
||||
// ALERT( at_aiconsole, "help " );
|
||||
|
||||
// skip ones not on my netname
|
||||
if ( FStringNull( pev->netname ))
|
||||
return;
|
||||
|
||||
CBaseEntity *pEntity = NULL;
|
||||
|
||||
while ((pEntity = UTIL_FindEntityByString( pEntity, "netname", STRING( pev->netname ))) != NULL)
|
||||
{
|
||||
float d = (pev->origin - pEntity->pev->origin).Length();
|
||||
if (d < flDist)
|
||||
{
|
||||
CBaseMonster *pMonster = pEntity->MyMonsterPointer( );
|
||||
if (pMonster)
|
||||
{
|
||||
pMonster->m_afMemory |= bits_MEMORY_PROVOKED;
|
||||
pMonster->PushEnemy( hEnemy, vecLocation );
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
//=========================================================
|
||||
// ALertSound - scream
|
||||
//=========================================================
|
||||
void CISlave :: AlertSound( void )
|
||||
{
|
||||
if ( m_hEnemy != NULL )
|
||||
{
|
||||
SENTENCEG_PlayRndSz(ENT(pev), "SLV_ALERT", 0.85, ATTN_NORM, 0, m_voicePitch);
|
||||
|
||||
CallForHelp( "monster_alien_slave", 512, m_hEnemy, m_vecEnemyLKP );
|
||||
}
|
||||
}
|
||||
|
||||
//=========================================================
|
||||
// IdleSound
|
||||
//=========================================================
|
||||
void CISlave :: IdleSound( void )
|
||||
{
|
||||
if (RANDOM_LONG( 0, 2 ) == 0)
|
||||
{
|
||||
SENTENCEG_PlayRndSz(ENT(pev), "SLV_IDLE", 0.85, ATTN_NORM, 0, m_voicePitch);
|
||||
}
|
||||
|
||||
#if 0
|
||||
int side = RANDOM_LONG( 0, 1 ) * 2 - 1;
|
||||
|
||||
ClearBeams( );
|
||||
ArmBeam( side );
|
||||
|
||||
UTIL_MakeAimVectors( pev->angles );
|
||||
Vector vecSrc = pev->origin + gpGlobals->v_right * 2 * side;
|
||||
MESSAGE_BEGIN( MSG_PVS, SVC_TEMPENTITY, vecSrc );
|
||||
WRITE_BYTE(TE_DLIGHT);
|
||||
WRITE_COORD(vecSrc.x); // X
|
||||
WRITE_COORD(vecSrc.y); // Y
|
||||
WRITE_COORD(vecSrc.z); // Z
|
||||
WRITE_BYTE( 8 ); // radius * 0.1
|
||||
WRITE_BYTE( 255 ); // r
|
||||
WRITE_BYTE( 180 ); // g
|
||||
WRITE_BYTE( 96 ); // b
|
||||
WRITE_BYTE( 10 ); // time * 10
|
||||
WRITE_BYTE( 0 ); // decay * 0.1
|
||||
MESSAGE_END( );
|
||||
|
||||
EMIT_SOUND_DYN( ENT(pev), CHAN_WEAPON, "debris/zap1.wav", 1, ATTN_NORM, 0, 100 );
|
||||
#endif
|
||||
}
|
||||
|
||||
//=========================================================
|
||||
// PainSound
|
||||
//=========================================================
|
||||
void CISlave :: PainSound( void )
|
||||
{
|
||||
if (RANDOM_LONG( 0, 2 ) == 0)
|
||||
{
|
||||
EMIT_SOUND_DYN ( ENT(pev), CHAN_WEAPON, pPainSounds[ RANDOM_LONG(0,ARRAYSIZE(pPainSounds)-1) ], 1.0, ATTN_NORM, 0, m_voicePitch );
|
||||
}
|
||||
}
|
||||
|
||||
//=========================================================
|
||||
// DieSound
|
||||
//=========================================================
|
||||
|
||||
void CISlave :: DeathSound( void )
|
||||
{
|
||||
EMIT_SOUND_DYN ( ENT(pev), CHAN_WEAPON, pDeathSounds[ RANDOM_LONG(0,ARRAYSIZE(pDeathSounds)-1) ], 1.0, ATTN_NORM, 0, m_voicePitch );
|
||||
}
|
||||
|
||||
|
||||
//=========================================================
|
||||
// ISoundMask - returns a bit mask indicating which types
|
||||
// of sounds this monster regards.
|
||||
//=========================================================
|
||||
int CISlave :: ISoundMask ( void)
|
||||
{
|
||||
return bits_SOUND_WORLD |
|
||||
bits_SOUND_COMBAT |
|
||||
bits_SOUND_DANGER |
|
||||
bits_SOUND_PLAYER;
|
||||
}
|
||||
|
||||
|
||||
void CISlave::Killed( entvars_t *pevAttacker, int iGib )
|
||||
{
|
||||
ClearBeams( );
|
||||
CSquadMonster::Killed( pevAttacker, iGib );
|
||||
}
|
||||
|
||||
//=========================================================
|
||||
// SetYawSpeed - allows each sequence to have a different
|
||||
// turn rate associated with it.
|
||||
//=========================================================
|
||||
void CISlave :: SetYawSpeed ( void )
|
||||
{
|
||||
int ys;
|
||||
|
||||
switch ( m_Activity )
|
||||
{
|
||||
case ACT_WALK:
|
||||
ys = 50;
|
||||
break;
|
||||
case ACT_RUN:
|
||||
ys = 70;
|
||||
break;
|
||||
case ACT_IDLE:
|
||||
ys = 50;
|
||||
break;
|
||||
default:
|
||||
ys = 90;
|
||||
break;
|
||||
}
|
||||
|
||||
pev->yaw_speed = ys;
|
||||
}
|
||||
|
||||
//=========================================================
|
||||
// HandleAnimEvent - catches the monster-specific messages
|
||||
// that occur when tagged animation frames are played.
|
||||
//
|
||||
// Returns number of events handled, 0 if none.
|
||||
//=========================================================
|
||||
void CISlave :: HandleAnimEvent( MonsterEvent_t *pEvent )
|
||||
{
|
||||
// ALERT( at_console, "event %d : %f\n", pEvent->event, pev->frame );
|
||||
switch( pEvent->event )
|
||||
{
|
||||
case ISLAVE_AE_CLAW:
|
||||
{
|
||||
// SOUND HERE!
|
||||
CBaseEntity *pHurt = CheckTraceHullAttack( 70, gSkillData.slaveDmgClaw, DMG_SLASH );
|
||||
if ( pHurt )
|
||||
{
|
||||
if ( pHurt->pev->flags & (FL_MONSTER|FL_CLIENT) )
|
||||
{
|
||||
pHurt->pev->punchangle.z = -18;
|
||||
pHurt->pev->punchangle.x = 5;
|
||||
}
|
||||
// Play a random attack hit sound
|
||||
EMIT_SOUND_DYN ( ENT(pev), CHAN_WEAPON, pAttackHitSounds[ RANDOM_LONG(0,ARRAYSIZE(pAttackHitSounds)-1) ], 1.0, ATTN_NORM, 0, m_voicePitch );
|
||||
}
|
||||
else
|
||||
{
|
||||
// Play a random attack miss sound
|
||||
EMIT_SOUND_DYN ( ENT(pev), CHAN_WEAPON, pAttackMissSounds[ RANDOM_LONG(0,ARRAYSIZE(pAttackMissSounds)-1) ], 1.0, ATTN_NORM, 0, m_voicePitch );
|
||||
}
|
||||
}
|
||||
break;
|
||||
|
||||
case ISLAVE_AE_CLAWRAKE:
|
||||
{
|
||||
CBaseEntity *pHurt = CheckTraceHullAttack( 70, gSkillData.slaveDmgClawrake, DMG_SLASH );
|
||||
if ( pHurt )
|
||||
{
|
||||
if ( pHurt->pev->flags & (FL_MONSTER|FL_CLIENT) )
|
||||
{
|
||||
pHurt->pev->punchangle.z = -18;
|
||||
pHurt->pev->punchangle.x = 5;
|
||||
}
|
||||
EMIT_SOUND_DYN ( ENT(pev), CHAN_WEAPON, pAttackHitSounds[ RANDOM_LONG(0,ARRAYSIZE(pAttackHitSounds)-1) ], 1.0, ATTN_NORM, 0, m_voicePitch );
|
||||
}
|
||||
else
|
||||
{
|
||||
EMIT_SOUND_DYN ( ENT(pev), CHAN_WEAPON, pAttackMissSounds[ RANDOM_LONG(0,ARRAYSIZE(pAttackMissSounds)-1) ], 1.0, ATTN_NORM, 0, m_voicePitch );
|
||||
}
|
||||
}
|
||||
break;
|
||||
|
||||
case ISLAVE_AE_ZAP_POWERUP:
|
||||
{
|
||||
// speed up attack when on hard
|
||||
if (g_iSkillLevel == SKILL_HARD)
|
||||
pev->framerate = 1.5;
|
||||
|
||||
UTIL_MakeAimVectors( pev->angles );
|
||||
|
||||
if (m_iBeams == 0)
|
||||
{
|
||||
Vector vecSrc = pev->origin + gpGlobals->v_forward * 2;
|
||||
MESSAGE_BEGIN( MSG_PVS, SVC_TEMPENTITY, vecSrc );
|
||||
WRITE_BYTE(TE_DLIGHT);
|
||||
WRITE_COORD(vecSrc.x); // X
|
||||
WRITE_COORD(vecSrc.y); // Y
|
||||
WRITE_COORD(vecSrc.z); // Z
|
||||
WRITE_BYTE( 12 ); // radius * 0.1
|
||||
WRITE_BYTE( 255 ); // r
|
||||
WRITE_BYTE( 180 ); // g
|
||||
WRITE_BYTE( 96 ); // b
|
||||
WRITE_BYTE( 20 / pev->framerate ); // time * 10
|
||||
WRITE_BYTE( 0 ); // decay * 0.1
|
||||
MESSAGE_END( );
|
||||
|
||||
}
|
||||
if (m_hDead != NULL)
|
||||
{
|
||||
WackBeam( -1, m_hDead );
|
||||
WackBeam( 1, m_hDead );
|
||||
}
|
||||
else
|
||||
{
|
||||
ArmBeam( -1 );
|
||||
ArmBeam( 1 );
|
||||
BeamGlow( );
|
||||
}
|
||||
|
||||
EMIT_SOUND_DYN( ENT(pev), CHAN_WEAPON, "debris/zap4.wav", 1, ATTN_NORM, 0, 100 + m_iBeams * 10 );
|
||||
pev->skin = m_iBeams / 2;
|
||||
}
|
||||
break;
|
||||
|
||||
case ISLAVE_AE_ZAP_SHOOT:
|
||||
{
|
||||
ClearBeams( );
|
||||
|
||||
if (m_hDead != NULL)
|
||||
{
|
||||
Vector vecDest = m_hDead->pev->origin + Vector( 0, 0, 38 );
|
||||
TraceResult trace;
|
||||
UTIL_TraceHull( vecDest, vecDest, dont_ignore_monsters, human_hull, m_hDead->edict(), &trace );
|
||||
|
||||
if ( !trace.fStartSolid )
|
||||
{
|
||||
CBaseEntity *pNew = Create( "monster_alien_slave", m_hDead->pev->origin, m_hDead->pev->angles );
|
||||
CBaseMonster *pNewMonster = pNew->MyMonsterPointer( );
|
||||
pNew->pev->spawnflags |= 1;
|
||||
WackBeam( -1, pNew );
|
||||
WackBeam( 1, pNew );
|
||||
UTIL_Remove( m_hDead );
|
||||
EMIT_SOUND_DYN( ENT(pev), CHAN_WEAPON, "hassault/hw_shoot1.wav", 1, ATTN_NORM, 0, RANDOM_LONG( 130, 160 ) );
|
||||
|
||||
/*
|
||||
CBaseEntity *pEffect = Create( "test_effect", pNew->Center(), pev->angles );
|
||||
pEffect->Use( this, this, USE_ON, 1 );
|
||||
*/
|
||||
break;
|
||||
}
|
||||
}
|
||||
ClearMultiDamage();
|
||||
|
||||
UTIL_MakeAimVectors( pev->angles );
|
||||
|
||||
ZapBeam( -1 );
|
||||
ZapBeam( 1 );
|
||||
|
||||
EMIT_SOUND_DYN( ENT(pev), CHAN_WEAPON, "hassault/hw_shoot1.wav", 1, ATTN_NORM, 0, RANDOM_LONG( 130, 160 ) );
|
||||
// STOP_SOUND( ENT(pev), CHAN_WEAPON, "debris/zap4.wav" );
|
||||
ApplyMultiDamage(pev, pev);
|
||||
|
||||
m_flNextAttack = gpGlobals->time + RANDOM_FLOAT( 0.5, 4.0 );
|
||||
}
|
||||
break;
|
||||
|
||||
case ISLAVE_AE_ZAP_DONE:
|
||||
{
|
||||
ClearBeams( );
|
||||
}
|
||||
break;
|
||||
|
||||
default:
|
||||
CSquadMonster::HandleAnimEvent( pEvent );
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
//=========================================================
|
||||
// CheckRangeAttack1 - normal beam attack
|
||||
//=========================================================
|
||||
BOOL CISlave :: CheckRangeAttack1 ( float flDot, float flDist )
|
||||
{
|
||||
if (m_flNextAttack > gpGlobals->time)
|
||||
{
|
||||
return FALSE;
|
||||
}
|
||||
|
||||
return CSquadMonster::CheckRangeAttack1( flDot, flDist );
|
||||
}
|
||||
|
||||
//=========================================================
|
||||
// CheckRangeAttack2 - check bravery and try to resurect dead comrades
|
||||
//=========================================================
|
||||
BOOL CISlave :: CheckRangeAttack2 ( float flDot, float flDist )
|
||||
{
|
||||
return FALSE;
|
||||
|
||||
if (m_flNextAttack > gpGlobals->time)
|
||||
{
|
||||
return FALSE;
|
||||
}
|
||||
|
||||
m_hDead = NULL;
|
||||
m_iBravery = 0;
|
||||
|
||||
CBaseEntity *pEntity = NULL;
|
||||
while ((pEntity = UTIL_FindEntityByClassname( pEntity, "monster_alien_slave" )) != NULL)
|
||||
{
|
||||
TraceResult tr;
|
||||
|
||||
UTIL_TraceLine( EyePosition( ), pEntity->EyePosition( ), ignore_monsters, ENT(pev), &tr );
|
||||
if (tr.flFraction == 1.0 || tr.pHit == pEntity->edict())
|
||||
{
|
||||
if (pEntity->pev->deadflag == DEAD_DEAD)
|
||||
{
|
||||
float d = (pev->origin - pEntity->pev->origin).Length();
|
||||
if (d < flDist)
|
||||
{
|
||||
m_hDead = pEntity;
|
||||
flDist = d;
|
||||
}
|
||||
m_iBravery--;
|
||||
}
|
||||
else
|
||||
{
|
||||
m_iBravery++;
|
||||
}
|
||||
}
|
||||
}
|
||||
if (m_hDead != NULL)
|
||||
return TRUE;
|
||||
else
|
||||
return FALSE;
|
||||
}
|
||||
|
||||
|
||||
//=========================================================
|
||||
// StartTask
|
||||
//=========================================================
|
||||
void CISlave :: StartTask ( Task_t *pTask )
|
||||
{
|
||||
ClearBeams( );
|
||||
|
||||
CSquadMonster :: StartTask ( pTask );
|
||||
}
|
||||
|
||||
|
||||
//=========================================================
|
||||
// Spawn
|
||||
//=========================================================
|
||||
void CISlave :: Spawn()
|
||||
{
|
||||
Precache( );
|
||||
|
||||
SET_MODEL(ENT(pev), "models/islave.mdl");
|
||||
UTIL_SetSize(pev, VEC_HUMAN_HULL_MIN, VEC_HUMAN_HULL_MAX);
|
||||
|
||||
pev->solid = SOLID_SLIDEBOX;
|
||||
pev->movetype = MOVETYPE_STEP;
|
||||
m_bloodColor = BLOOD_COLOR_GREEN;
|
||||
pev->effects = 0;
|
||||
pev->health = gSkillData.slaveHealth;
|
||||
pev->view_ofs = Vector ( 0, 0, 64 );// position of the eyes relative to monster's origin.
|
||||
m_flFieldOfView = VIEW_FIELD_WIDE; // NOTE: we need a wide field of view so npc will notice player and say hello
|
||||
m_MonsterState = MONSTERSTATE_NONE;
|
||||
m_afCapability = bits_CAP_HEAR | bits_CAP_TURN_HEAD | bits_CAP_RANGE_ATTACK2 | bits_CAP_DOORS_GROUP;
|
||||
|
||||
m_voicePitch = RANDOM_LONG( 85, 110 );
|
||||
|
||||
MonsterInit();
|
||||
}
|
||||
|
||||
//=========================================================
|
||||
// Precache - precaches all resources this monster needs
|
||||
//=========================================================
|
||||
void CISlave :: Precache()
|
||||
{
|
||||
int i;
|
||||
|
||||
PRECACHE_MODEL("models/islave.mdl");
|
||||
PRECACHE_MODEL("sprites/lgtning.spr");
|
||||
PRECACHE_SOUND("debris/zap1.wav");
|
||||
PRECACHE_SOUND("debris/zap4.wav");
|
||||
PRECACHE_SOUND("weapons/electro4.wav");
|
||||
PRECACHE_SOUND("hassault/hw_shoot1.wav");
|
||||
PRECACHE_SOUND("zombie/zo_pain2.wav");
|
||||
PRECACHE_SOUND("headcrab/hc_headbite.wav");
|
||||
PRECACHE_SOUND("weapons/cbar_miss1.wav");
|
||||
|
||||
for ( i = 0; i < ARRAYSIZE( pAttackHitSounds ); i++ )
|
||||
PRECACHE_SOUND((char *)pAttackHitSounds[i]);
|
||||
|
||||
for ( i = 0; i < ARRAYSIZE( pAttackMissSounds ); i++ )
|
||||
PRECACHE_SOUND((char *)pAttackMissSounds[i]);
|
||||
|
||||
for ( i = 0; i < ARRAYSIZE( pPainSounds ); i++ )
|
||||
PRECACHE_SOUND((char *)pPainSounds[i]);
|
||||
|
||||
for ( i = 0; i < ARRAYSIZE( pDeathSounds ); i++ )
|
||||
PRECACHE_SOUND((char *)pDeathSounds[i]);
|
||||
|
||||
UTIL_PrecacheOther( "test_effect" );
|
||||
}
|
||||
|
||||
|
||||
//=========================================================
|
||||
// TakeDamage - get provoked when injured
|
||||
//=========================================================
|
||||
|
||||
int CISlave :: TakeDamage( entvars_t* pevInflictor, entvars_t* pevAttacker, float flDamage, int bitsDamageType)
|
||||
{
|
||||
// don't slash one of your own
|
||||
if ((bitsDamageType & DMG_SLASH) && pevAttacker && IRelationship( Instance(pevAttacker) ) < R_DL)
|
||||
return 0;
|
||||
|
||||
m_afMemory |= bits_MEMORY_PROVOKED;
|
||||
return CSquadMonster::TakeDamage(pevInflictor, pevAttacker, flDamage, bitsDamageType);
|
||||
}
|
||||
|
||||
|
||||
void CISlave::TraceAttack( entvars_t *pevAttacker, float flDamage, Vector vecDir, TraceResult *ptr, int bitsDamageType)
|
||||
{
|
||||
if (bitsDamageType & DMG_SHOCK)
|
||||
return;
|
||||
|
||||
CSquadMonster::TraceAttack( pevAttacker, flDamage, vecDir, ptr, bitsDamageType );
|
||||
}
|
||||
|
||||
|
||||
//=========================================================
|
||||
// AI Schedules Specific to this monster
|
||||
//=========================================================
|
||||
|
||||
|
||||
|
||||
// primary range attack
|
||||
Task_t tlSlaveAttack1[] =
|
||||
{
|
||||
{ TASK_STOP_MOVING, 0 },
|
||||
{ TASK_FACE_IDEAL, (float)0 },
|
||||
{ TASK_RANGE_ATTACK1, (float)0 },
|
||||
};
|
||||
|
||||
Schedule_t slSlaveAttack1[] =
|
||||
{
|
||||
{
|
||||
tlSlaveAttack1,
|
||||
ARRAYSIZE ( tlSlaveAttack1 ),
|
||||
bits_COND_CAN_MELEE_ATTACK1 |
|
||||
bits_COND_HEAR_SOUND |
|
||||
bits_COND_HEAVY_DAMAGE,
|
||||
|
||||
bits_SOUND_DANGER,
|
||||
"Slave Range Attack1"
|
||||
},
|
||||
};
|
||||
|
||||
|
||||
DEFINE_CUSTOM_SCHEDULES( CISlave )
|
||||
{
|
||||
slSlaveAttack1,
|
||||
};
|
||||
|
||||
IMPLEMENT_CUSTOM_SCHEDULES( CISlave, CSquadMonster );
|
||||
|
||||
|
||||
//=========================================================
|
||||
//=========================================================
|
||||
Schedule_t *CISlave :: GetSchedule( void )
|
||||
{
|
||||
ClearBeams( );
|
||||
|
||||
/*
|
||||
if (pev->spawnflags)
|
||||
{
|
||||
pev->spawnflags = 0;
|
||||
return GetScheduleOfType( SCHED_RELOAD );
|
||||
}
|
||||
*/
|
||||
|
||||
if ( HasConditions( bits_COND_HEAR_SOUND ) )
|
||||
{
|
||||
CSound *pSound;
|
||||
pSound = PBestSound();
|
||||
|
||||
ASSERT( pSound != NULL );
|
||||
|
||||
if ( pSound && (pSound->m_iType & bits_SOUND_DANGER) )
|
||||
return GetScheduleOfType( SCHED_TAKE_COVER_FROM_BEST_SOUND );
|
||||
if ( pSound->m_iType & bits_SOUND_COMBAT )
|
||||
m_afMemory |= bits_MEMORY_PROVOKED;
|
||||
}
|
||||
|
||||
switch (m_MonsterState)
|
||||
{
|
||||
case MONSTERSTATE_COMBAT:
|
||||
// dead enemy
|
||||
if ( HasConditions( bits_COND_ENEMY_DEAD ) )
|
||||
{
|
||||
// call base class, all code to handle dead enemies is centralized there.
|
||||
return CBaseMonster :: GetSchedule();
|
||||
}
|
||||
|
||||
if (pev->health < 20 || m_iBravery < 0)
|
||||
{
|
||||
if (!HasConditions( bits_COND_CAN_MELEE_ATTACK1 ))
|
||||
{
|
||||
m_failSchedule = SCHED_CHASE_ENEMY;
|
||||
if (HasConditions( bits_COND_LIGHT_DAMAGE | bits_COND_HEAVY_DAMAGE))
|
||||
{
|
||||
return GetScheduleOfType( SCHED_TAKE_COVER_FROM_ENEMY );
|
||||
}
|
||||
if ( HasConditions ( bits_COND_SEE_ENEMY ) && HasConditions ( bits_COND_ENEMY_FACING_ME ) )
|
||||
{
|
||||
// ALERT( at_console, "exposed\n");
|
||||
return GetScheduleOfType( SCHED_TAKE_COVER_FROM_ENEMY );
|
||||
}
|
||||
}
|
||||
}
|
||||
break;
|
||||
}
|
||||
return CSquadMonster::GetSchedule( );
|
||||
}
|
||||
|
||||
|
||||
Schedule_t *CISlave :: GetScheduleOfType ( int Type )
|
||||
{
|
||||
switch ( Type )
|
||||
{
|
||||
case SCHED_FAIL:
|
||||
if (HasConditions( bits_COND_CAN_MELEE_ATTACK1 ))
|
||||
{
|
||||
return CSquadMonster :: GetScheduleOfType( SCHED_MELEE_ATTACK1 ); ;
|
||||
}
|
||||
break;
|
||||
case SCHED_RANGE_ATTACK1:
|
||||
return slSlaveAttack1;
|
||||
case SCHED_RANGE_ATTACK2:
|
||||
return slSlaveAttack1;
|
||||
}
|
||||
return CSquadMonster :: GetScheduleOfType( Type );
|
||||
}
|
||||
|
||||
|
||||
//=========================================================
|
||||
// ArmBeam - small beam from arm to nearby geometry
|
||||
//=========================================================
|
||||
|
||||
void CISlave :: ArmBeam( int side )
|
||||
{
|
||||
TraceResult tr;
|
||||
float flDist = 1.0;
|
||||
|
||||
if (m_iBeams >= ISLAVE_MAX_BEAMS)
|
||||
return;
|
||||
|
||||
UTIL_MakeAimVectors( pev->angles );
|
||||
Vector vecSrc = pev->origin + gpGlobals->v_up * 36 + gpGlobals->v_right * side * 16 + gpGlobals->v_forward * 32;
|
||||
|
||||
for (int i = 0; i < 3; i++)
|
||||
{
|
||||
Vector vecAim = gpGlobals->v_right * side * RANDOM_FLOAT( 0, 1 ) + gpGlobals->v_up * RANDOM_FLOAT( -1, 1 );
|
||||
TraceResult tr1;
|
||||
UTIL_TraceLine ( vecSrc, vecSrc + vecAim * 512, dont_ignore_monsters, ENT( pev ), &tr1);
|
||||
if (flDist > tr1.flFraction)
|
||||
{
|
||||
tr = tr1;
|
||||
flDist = tr.flFraction;
|
||||
}
|
||||
}
|
||||
|
||||
// Couldn't find anything close enough
|
||||
if ( flDist == 1.0 )
|
||||
return;
|
||||
|
||||
DecalGunshot( &tr, BULLET_PLAYER_CROWBAR );
|
||||
|
||||
m_pBeam[m_iBeams] = CBeam::BeamCreate( "sprites/lgtning.spr", 30 );
|
||||
if (!m_pBeam[m_iBeams])
|
||||
return;
|
||||
|
||||
m_pBeam[m_iBeams]->PointEntInit( tr.vecEndPos, edict( ) );
|
||||
m_pBeam[m_iBeams]->SetEndAttachment( side < 0 ? 2 : 1 );
|
||||
// m_pBeam[m_iBeams]->SetColor( 180, 255, 96 );
|
||||
m_pBeam[m_iBeams]->SetColor( 96, 128, 16 );
|
||||
m_pBeam[m_iBeams]->SetBrightness( 64 );
|
||||
m_pBeam[m_iBeams]->SetNoise( 80 );
|
||||
m_iBeams++;
|
||||
}
|
||||
|
||||
|
||||
//=========================================================
|
||||
// BeamGlow - brighten all beams
|
||||
//=========================================================
|
||||
void CISlave :: BeamGlow( )
|
||||
{
|
||||
int b = m_iBeams * 32;
|
||||
if (b > 255)
|
||||
b = 255;
|
||||
|
||||
for (int i = 0; i < m_iBeams; i++)
|
||||
{
|
||||
if (m_pBeam[i]->GetBrightness() != 255)
|
||||
{
|
||||
m_pBeam[i]->SetBrightness( b );
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
//=========================================================
|
||||
// WackBeam - regenerate dead colleagues
|
||||
//=========================================================
|
||||
void CISlave :: WackBeam( int side, CBaseEntity *pEntity )
|
||||
{
|
||||
Vector vecDest;
|
||||
float flDist = 1.0;
|
||||
|
||||
if (m_iBeams >= ISLAVE_MAX_BEAMS)
|
||||
return;
|
||||
|
||||
if (pEntity == NULL)
|
||||
return;
|
||||
|
||||
m_pBeam[m_iBeams] = CBeam::BeamCreate( "sprites/lgtning.spr", 30 );
|
||||
if (!m_pBeam[m_iBeams])
|
||||
return;
|
||||
|
||||
m_pBeam[m_iBeams]->PointEntInit( pEntity->Center(), edict( ) );
|
||||
m_pBeam[m_iBeams]->SetEndAttachment( side < 0 ? 2 : 1 );
|
||||
m_pBeam[m_iBeams]->SetColor( 180, 255, 96 );
|
||||
m_pBeam[m_iBeams]->SetBrightness( 255 );
|
||||
m_pBeam[m_iBeams]->SetNoise( 80 );
|
||||
m_iBeams++;
|
||||
}
|
||||
|
||||
//=========================================================
|
||||
// ZapBeam - heavy damage directly forward
|
||||
//=========================================================
|
||||
void CISlave :: ZapBeam( int side )
|
||||
{
|
||||
Vector vecSrc, vecAim;
|
||||
TraceResult tr;
|
||||
CBaseEntity *pEntity;
|
||||
|
||||
if (m_iBeams >= ISLAVE_MAX_BEAMS)
|
||||
return;
|
||||
|
||||
vecSrc = pev->origin + gpGlobals->v_up * 36;
|
||||
vecAim = ShootAtEnemy( vecSrc );
|
||||
float deflection = 0.01;
|
||||
vecAim = vecAim + side * gpGlobals->v_right * RANDOM_FLOAT( 0, deflection ) + gpGlobals->v_up * RANDOM_FLOAT( -deflection, deflection );
|
||||
UTIL_TraceLine ( vecSrc, vecSrc + vecAim * 1024, dont_ignore_monsters, ENT( pev ), &tr);
|
||||
|
||||
m_pBeam[m_iBeams] = CBeam::BeamCreate( "sprites/lgtning.spr", 50 );
|
||||
if (!m_pBeam[m_iBeams])
|
||||
return;
|
||||
|
||||
m_pBeam[m_iBeams]->PointEntInit( tr.vecEndPos, edict( ) );
|
||||
m_pBeam[m_iBeams]->SetEndAttachment( side < 0 ? 2 : 1 );
|
||||
m_pBeam[m_iBeams]->SetColor( 180, 255, 96 );
|
||||
m_pBeam[m_iBeams]->SetBrightness( 255 );
|
||||
m_pBeam[m_iBeams]->SetNoise( 20 );
|
||||
m_iBeams++;
|
||||
|
||||
pEntity = CBaseEntity::Instance(tr.pHit);
|
||||
if (pEntity != NULL && pEntity->pev->takedamage)
|
||||
{
|
||||
pEntity->TraceAttack( pev, gSkillData.slaveDmgZap, vecAim, &tr, DMG_SHOCK );
|
||||
}
|
||||
UTIL_EmitAmbientSound( ENT(pev), tr.vecEndPos, "weapons/electro4.wav", 0.5, ATTN_NORM, 0, RANDOM_LONG( 140, 160 ) );
|
||||
}
|
||||
|
||||
|
||||
//=========================================================
|
||||
// ClearBeams - remove all beams
|
||||
//=========================================================
|
||||
void CISlave :: ClearBeams( )
|
||||
{
|
||||
for (int i = 0; i < ISLAVE_MAX_BEAMS; i++)
|
||||
{
|
||||
if (m_pBeam[i])
|
||||
{
|
||||
UTIL_Remove( m_pBeam[i] );
|
||||
m_pBeam[i] = NULL;
|
||||
}
|
||||
}
|
||||
m_iBeams = 0;
|
||||
pev->skin = 0;
|
||||
|
||||
STOP_SOUND( ENT(pev), CHAN_WEAPON, "debris/zap4.wav" );
|
||||
}
|
|
@ -0,0 +1,402 @@
|
|||
/***
|
||||
*
|
||||
* 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.
|
||||
*
|
||||
****/
|
||||
/*
|
||||
|
||||
===== items.cpp ========================================================
|
||||
|
||||
functions governing the selection/use of weapons for players
|
||||
|
||||
*/
|
||||
|
||||
#include "extdll.h"
|
||||
#include "util.h"
|
||||
#include "cbase.h"
|
||||
#include "weapons.h"
|
||||
#include "player.h"
|
||||
#include "skill.h"
|
||||
#include "items.h"
|
||||
#include "gamerules.h"
|
||||
|
||||
extern int gmsgItemPickup;
|
||||
|
||||
class CWorldItem : public CBaseEntity
|
||||
{
|
||||
public:
|
||||
void KeyValue(KeyValueData *pkvd );
|
||||
void Spawn( void );
|
||||
int m_iType;
|
||||
};
|
||||
|
||||
LINK_ENTITY_TO_CLASS(world_items, CWorldItem);
|
||||
|
||||
void CWorldItem::KeyValue(KeyValueData *pkvd)
|
||||
{
|
||||
if (FStrEq(pkvd->szKeyName, "type"))
|
||||
{
|
||||
m_iType = atoi(pkvd->szValue);
|
||||
pkvd->fHandled = TRUE;
|
||||
}
|
||||
else
|
||||
CBaseEntity::KeyValue( pkvd );
|
||||
}
|
||||
|
||||
void CWorldItem::Spawn( void )
|
||||
{
|
||||
CBaseEntity *pEntity = NULL;
|
||||
|
||||
switch (m_iType)
|
||||
{
|
||||
case 44: // ITEM_BATTERY:
|
||||
pEntity = CBaseEntity::Create( "item_battery", pev->origin, pev->angles );
|
||||
break;
|
||||
case 42: // ITEM_ANTIDOTE:
|
||||
pEntity = CBaseEntity::Create( "item_antidote", pev->origin, pev->angles );
|
||||
break;
|
||||
case 43: // ITEM_SECURITY:
|
||||
pEntity = CBaseEntity::Create( "item_security", pev->origin, pev->angles );
|
||||
break;
|
||||
case 45: // ITEM_SUIT:
|
||||
pEntity = CBaseEntity::Create( "item_suit", pev->origin, pev->angles );
|
||||
break;
|
||||
}
|
||||
|
||||
if (!pEntity)
|
||||
{
|
||||
ALERT( at_console, "unable to create world_item %d\n", m_iType );
|
||||
}
|
||||
else
|
||||
{
|
||||
pEntity->pev->target = pev->target;
|
||||
pEntity->pev->targetname = pev->targetname;
|
||||
pEntity->pev->spawnflags = pev->spawnflags;
|
||||
}
|
||||
|
||||
REMOVE_ENTITY(edict());
|
||||
}
|
||||
|
||||
|
||||
void CItem::Spawn( void )
|
||||
{
|
||||
pev->movetype = MOVETYPE_TOSS;
|
||||
pev->solid = SOLID_TRIGGER;
|
||||
UTIL_SetOrigin( pev, pev->origin );
|
||||
UTIL_SetSize(pev, Vector(-16, -16, 0), Vector(16, 16, 16));
|
||||
SetTouch(ItemTouch);
|
||||
|
||||
if (DROP_TO_FLOOR(ENT(pev)) == 0)
|
||||
{
|
||||
ALERT(at_error, "Item %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;
|
||||
}
|
||||
}
|
||||
|
||||
extern int gEvilImpulse101;
|
||||
|
||||
void CItem::ItemTouch( CBaseEntity *pOther )
|
||||
{
|
||||
// if it's not a player, ignore
|
||||
if ( !pOther->IsPlayer() )
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
CBasePlayer *pPlayer = (CBasePlayer *)pOther;
|
||||
|
||||
// ok, a player is touching this item, but can he have it?
|
||||
if ( !g_pGameRules->CanHaveItem( pPlayer, this ) )
|
||||
{
|
||||
// no? Ignore the touch.
|
||||
return;
|
||||
}
|
||||
|
||||
if (MyTouch( pPlayer ))
|
||||
{
|
||||
SUB_UseTargets( pOther, USE_TOGGLE, 0 );
|
||||
SetTouch( NULL );
|
||||
|
||||
// player grabbed the item.
|
||||
g_pGameRules->PlayerGotItem( pPlayer, this );
|
||||
if ( g_pGameRules->ItemShouldRespawn( this ) == GR_ITEM_RESPAWN_YES )
|
||||
{
|
||||
Respawn();
|
||||
}
|
||||
else
|
||||
{
|
||||
UTIL_Remove( this );
|
||||
}
|
||||
}
|
||||
else if (gEvilImpulse101)
|
||||
{
|
||||
UTIL_Remove( this );
|
||||
}
|
||||
}
|
||||
|
||||
CBaseEntity* CItem::Respawn( void )
|
||||
{
|
||||
SetTouch( NULL );
|
||||
pev->effects |= EF_NODRAW;
|
||||
|
||||
UTIL_SetOrigin( pev, g_pGameRules->VecItemRespawnSpot( this ) );// blip to whereever you should respawn.
|
||||
|
||||
SetThink ( Materialize );
|
||||
pev->nextthink = g_pGameRules->FlItemRespawnTime( this );
|
||||
return this;
|
||||
}
|
||||
|
||||
void CItem::Materialize( void )
|
||||
{
|
||||
if ( pev->effects & EF_NODRAW )
|
||||
{
|
||||
// changing from invisible state to visible.
|
||||
EMIT_SOUND_DYN( ENT(pev), CHAN_WEAPON, "items/suitchargeok1.wav", 1, ATTN_NORM, 0, 150 );
|
||||
pev->effects &= ~EF_NODRAW;
|
||||
pev->effects |= EF_MUZZLEFLASH;
|
||||
}
|
||||
|
||||
SetTouch( ItemTouch );
|
||||
}
|
||||
|
||||
#define SF_SUIT_SHORTLOGON 0x0001
|
||||
|
||||
class CItemSuit : public CItem
|
||||
{
|
||||
void Spawn( void )
|
||||
{
|
||||
Precache( );
|
||||
SET_MODEL(ENT(pev), "models/w_suit.mdl");
|
||||
CItem::Spawn( );
|
||||
}
|
||||
void Precache( void )
|
||||
{
|
||||
PRECACHE_MODEL ("models/w_suit.mdl");
|
||||
}
|
||||
BOOL MyTouch( CBasePlayer *pPlayer )
|
||||
{
|
||||
if ( pPlayer->pev->weapons & (1<<WEAPON_SUIT) )
|
||||
return FALSE;
|
||||
|
||||
if ( pev->spawnflags & SF_SUIT_SHORTLOGON )
|
||||
EMIT_SOUND_SUIT(pPlayer->edict(), "!HEV_A0"); // short version of suit logon,
|
||||
else
|
||||
EMIT_SOUND_SUIT(pPlayer->edict(), "!HEV_AAx"); // long version of suit logon
|
||||
|
||||
pPlayer->pev->weapons |= (1<<WEAPON_SUIT);
|
||||
return TRUE;
|
||||
}
|
||||
};
|
||||
|
||||
LINK_ENTITY_TO_CLASS(item_suit, CItemSuit);
|
||||
|
||||
class CItemArmorVest : public CItem
|
||||
{
|
||||
void Spawn( void )
|
||||
{
|
||||
Precache( );
|
||||
SET_MODEL(ENT(pev), "models/barney_vest.mdl");
|
||||
CItem::Spawn( );
|
||||
}
|
||||
void Precache( void )
|
||||
{
|
||||
PRECACHE_MODEL ("models/barney_vest.mdl");
|
||||
PRECACHE_SOUND( "items/gunpickup2.wav" );
|
||||
}
|
||||
BOOL MyTouch( CBasePlayer *pPlayer )
|
||||
{
|
||||
if ((pPlayer->pev->armorvalue < MAX_NORMAL_BATTERY) &&
|
||||
(pPlayer->pev->weapons & (1<<WEAPON_SUIT)))
|
||||
{
|
||||
pPlayer->pev->armorvalue += 60;
|
||||
pPlayer->pev->armorvalue = min(pPlayer->pev->armorvalue, MAX_NORMAL_BATTERY);
|
||||
|
||||
EMIT_SOUND( pPlayer->edict(), CHAN_ITEM, "items/gunpickup2.wav", 1, ATTN_NORM );
|
||||
|
||||
MESSAGE_BEGIN( MSG_ONE, gmsgItemPickup, NULL, pPlayer->pev );
|
||||
WRITE_STRING( STRING(pev->classname) );
|
||||
MESSAGE_END();
|
||||
return TRUE;
|
||||
}
|
||||
return FALSE;
|
||||
}
|
||||
};
|
||||
LINK_ENTITY_TO_CLASS(item_armorvest, CItemArmorVest);
|
||||
|
||||
|
||||
class CItemHelmet : public CItem
|
||||
{
|
||||
void Spawn( void )
|
||||
{
|
||||
Precache( );
|
||||
SET_MODEL(ENT(pev), "models/barney_helmet.mdl");
|
||||
CItem::Spawn( );
|
||||
}
|
||||
void Precache( void )
|
||||
{
|
||||
PRECACHE_MODEL ("models/barney_helmet.mdl");
|
||||
PRECACHE_SOUND( "items/gunpickup2.wav" );
|
||||
}
|
||||
BOOL MyTouch( CBasePlayer *pPlayer )
|
||||
{
|
||||
if ((pPlayer->pev->armorvalue < MAX_NORMAL_BATTERY) &&
|
||||
(pPlayer->pev->weapons & (1<<WEAPON_SUIT)))
|
||||
{
|
||||
pPlayer->pev->armorvalue += 40;
|
||||
pPlayer->pev->armorvalue = min(pPlayer->pev->armorvalue, MAX_NORMAL_BATTERY);
|
||||
|
||||
EMIT_SOUND( pPlayer->edict(), CHAN_ITEM, "items/gunpickup2.wav", 1, ATTN_NORM );
|
||||
|
||||
MESSAGE_BEGIN( MSG_ONE, gmsgItemPickup, NULL, pPlayer->pev );
|
||||
WRITE_STRING( STRING(pev->classname) );
|
||||
MESSAGE_END();
|
||||
return TRUE;
|
||||
}
|
||||
return FALSE;
|
||||
}
|
||||
};
|
||||
LINK_ENTITY_TO_CLASS(item_helmet, CItemHelmet);
|
||||
|
||||
class CItemBattery : public CItem
|
||||
{
|
||||
void Spawn( void )
|
||||
{
|
||||
Precache( );
|
||||
SET_MODEL(ENT(pev), "models/w_battery.mdl");
|
||||
CItem::Spawn( );
|
||||
}
|
||||
void Precache( void )
|
||||
{
|
||||
PRECACHE_MODEL ("models/w_battery.mdl");
|
||||
PRECACHE_SOUND( "items/gunpickup2.wav" );
|
||||
}
|
||||
BOOL MyTouch( CBasePlayer *pPlayer )
|
||||
{
|
||||
if ((pPlayer->pev->armorvalue < MAX_NORMAL_BATTERY) &&
|
||||
(pPlayer->pev->weapons & (1<<WEAPON_SUIT)))
|
||||
{
|
||||
int pct;
|
||||
char szcharge[64];
|
||||
|
||||
pPlayer->pev->armorvalue += gSkillData.batteryCapacity;
|
||||
pPlayer->pev->armorvalue = min(pPlayer->pev->armorvalue, MAX_NORMAL_BATTERY);
|
||||
|
||||
EMIT_SOUND( pPlayer->edict(), CHAN_ITEM, "items/gunpickup2.wav", 1, ATTN_NORM );
|
||||
|
||||
MESSAGE_BEGIN( MSG_ONE, gmsgItemPickup, NULL, pPlayer->pev );
|
||||
WRITE_STRING( STRING(pev->classname) );
|
||||
MESSAGE_END();
|
||||
|
||||
|
||||
// Suit reports new power level
|
||||
// For some reason this wasn't working in release build -- round it.
|
||||
pct = (int)( (float)(pPlayer->pev->armorvalue * 100.0) * (1.0/MAX_NORMAL_BATTERY) + 0.5);
|
||||
pct = (pct / 5);
|
||||
if (pct > 0)
|
||||
pct--;
|
||||
|
||||
sprintf( szcharge,"!HEV_%1dP", pct );
|
||||
|
||||
//EMIT_SOUND_SUIT(ENT(pev), szcharge);
|
||||
pPlayer->SetSuitUpdate(szcharge, FALSE, SUIT_NEXT_IN_30SEC);
|
||||
return TRUE;
|
||||
}
|
||||
return FALSE;
|
||||
}
|
||||
};
|
||||
|
||||
LINK_ENTITY_TO_CLASS(item_battery, CItemBattery);
|
||||
|
||||
|
||||
class CItemAntidote : public CItem
|
||||
{
|
||||
void Spawn( void )
|
||||
{
|
||||
Precache( );
|
||||
SET_MODEL(ENT(pev), "models/w_antidote.mdl");
|
||||
CItem::Spawn( );
|
||||
}
|
||||
void Precache( void )
|
||||
{
|
||||
PRECACHE_MODEL ("models/w_antidote.mdl");
|
||||
}
|
||||
BOOL MyTouch( CBasePlayer *pPlayer )
|
||||
{
|
||||
pPlayer->SetSuitUpdate("!HEV_DET4", FALSE, SUIT_NEXT_IN_1MIN);
|
||||
|
||||
pPlayer->m_rgItems[ITEM_ANTIDOTE] += 1;
|
||||
return TRUE;
|
||||
}
|
||||
};
|
||||
|
||||
LINK_ENTITY_TO_CLASS(item_antidote, CItemAntidote);
|
||||
|
||||
|
||||
class CItemSecurity : public CItem
|
||||
{
|
||||
void Spawn( void )
|
||||
{
|
||||
Precache( );
|
||||
SET_MODEL(ENT(pev), "models/w_security.mdl");
|
||||
CItem::Spawn( );
|
||||
}
|
||||
void Precache( void )
|
||||
{
|
||||
PRECACHE_MODEL ("models/w_security.mdl");
|
||||
}
|
||||
BOOL MyTouch( CBasePlayer *pPlayer )
|
||||
{
|
||||
pPlayer->m_rgItems[ITEM_SECURITY] += 1;
|
||||
return TRUE;
|
||||
}
|
||||
};
|
||||
|
||||
LINK_ENTITY_TO_CLASS(item_security, CItemSecurity);
|
||||
|
||||
class CItemLongJump : public CItem
|
||||
{
|
||||
void Spawn( void )
|
||||
{
|
||||
Precache( );
|
||||
SET_MODEL(ENT(pev), "models/w_longjump.mdl");
|
||||
CItem::Spawn( );
|
||||
}
|
||||
void Precache( void )
|
||||
{
|
||||
PRECACHE_MODEL ("models/w_longjump.mdl");
|
||||
}
|
||||
BOOL MyTouch( CBasePlayer *pPlayer )
|
||||
{
|
||||
if ( pPlayer->m_fLongJump )
|
||||
{
|
||||
return FALSE;
|
||||
}
|
||||
|
||||
if ( ( pPlayer->pev->weapons & (1<<WEAPON_SUIT) ) )
|
||||
{
|
||||
pPlayer->m_fLongJump = TRUE;// player now has longjump module
|
||||
|
||||
g_engfuncs.pfnSetPhysicsKeyValue( pPlayer->edict(), "slj", "1" );
|
||||
|
||||
MESSAGE_BEGIN( MSG_ONE, gmsgItemPickup, NULL, pPlayer->pev );
|
||||
WRITE_STRING( STRING(pev->classname) );
|
||||
MESSAGE_END();
|
||||
|
||||
EMIT_SOUND_SUIT( pPlayer->edict(), "!HEV_A1" ); // Play the longjump sound UNDONE: Kelly? correct sound?
|
||||
return TRUE;
|
||||
}
|
||||
return FALSE;
|
||||
}
|
||||
};
|
||||
|
||||
LINK_ENTITY_TO_CLASS( item_longjump, CItemLongJump );
|
|
@ -0,0 +1,29 @@
|
|||
/***
|
||||
*
|
||||
* 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.
|
||||
*
|
||||
****/
|
||||
#ifndef ITEMS_H
|
||||
#define ITEMS_H
|
||||
|
||||
|
||||
class CItem : public CBaseEntity
|
||||
{
|
||||
public:
|
||||
void Spawn( void );
|
||||
CBaseEntity* Respawn( void );
|
||||
void EXPORT ItemTouch( CBaseEntity *pOther );
|
||||
void EXPORT Materialize( void );
|
||||
virtual BOOL MyTouch( CBasePlayer *pPlayer ) { return FALSE; };
|
||||
};
|
||||
|
||||
#endif // ITEMS_H
|
|
@ -0,0 +1,723 @@
|
|||
/***
|
||||
*
|
||||
* 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.
|
||||
*
|
||||
****/
|
||||
//=========================================================
|
||||
// leech - basic little swimming monster
|
||||
//=========================================================
|
||||
//
|
||||
// UNDONE:
|
||||
// DONE:Steering force model for attack
|
||||
// DONE:Attack animation control / damage
|
||||
// DONE:Establish range of up/down motion and steer around vertical obstacles
|
||||
// DONE:Re-evaluate height periodically
|
||||
// DONE:Fall (MOVETYPE_TOSS) and play different anim if out of water
|
||||
// Test in complex room (c2a3?)
|
||||
// DONE:Sounds? - Kelly will fix
|
||||
// Blood cloud? Hurt effect?
|
||||
// Group behavior?
|
||||
// DONE:Save/restore
|
||||
// Flop animation - just bind to ACT_TWITCH
|
||||
// Fix fatal push into wall case
|
||||
//
|
||||
// Try this on a bird
|
||||
// Try this on a model with hulls/tracehull?
|
||||
//
|
||||
|
||||
|
||||
#include "float.h"
|
||||
#include "extdll.h"
|
||||
#include "util.h"
|
||||
#include "cbase.h"
|
||||
#include "monsters.h"
|
||||
|
||||
|
||||
|
||||
|
||||
// Animation events
|
||||
#define LEECH_AE_ATTACK 1
|
||||
#define LEECH_AE_FLOP 2
|
||||
|
||||
|
||||
// Movement constants
|
||||
|
||||
#define LEECH_ACCELERATE 10
|
||||
#define LEECH_CHECK_DIST 45
|
||||
#define LEECH_SWIM_SPEED 50
|
||||
#define LEECH_SWIM_ACCEL 80
|
||||
#define LEECH_SWIM_DECEL 10
|
||||
#define LEECH_TURN_RATE 90
|
||||
#define LEECH_SIZEX 10
|
||||
#define LEECH_FRAMETIME 0.1
|
||||
|
||||
|
||||
|
||||
#define DEBUG_BEAMS 0
|
||||
|
||||
#if DEBUG_BEAMS
|
||||
#include "effects.h"
|
||||
#endif
|
||||
|
||||
|
||||
class CLeech : public CBaseMonster
|
||||
{
|
||||
public:
|
||||
void Spawn( void );
|
||||
void Precache( void );
|
||||
|
||||
void EXPORT SwimThink( void );
|
||||
void EXPORT DeadThink( void );
|
||||
void Touch( CBaseEntity *pOther )
|
||||
{
|
||||
if ( pOther->IsPlayer() )
|
||||
{
|
||||
// If the client is pushing me, give me some base velocity
|
||||
if ( gpGlobals->trace_ent && gpGlobals->trace_ent == edict() )
|
||||
{
|
||||
pev->basevelocity = pOther->pev->velocity;
|
||||
pev->flags |= FL_BASEVELOCITY;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void SetObjectCollisionBox( void )
|
||||
{
|
||||
pev->absmin = pev->origin + Vector(-8,-8,0);
|
||||
pev->absmax = pev->origin + Vector(8,8,2);
|
||||
}
|
||||
|
||||
void AttackSound( void );
|
||||
void AlertSound( void );
|
||||
void UpdateMotion( void );
|
||||
float ObstacleDistance( CBaseEntity *pTarget );
|
||||
void MakeVectors( void );
|
||||
void RecalculateWaterlevel( void );
|
||||
void SwitchLeechState( void );
|
||||
|
||||
// Base entity functions
|
||||
void HandleAnimEvent( MonsterEvent_t *pEvent );
|
||||
int BloodColor( void ) { return DONT_BLEED; }
|
||||
void Killed( entvars_t *pevAttacker, int iGib );
|
||||
void Activate( void );
|
||||
int TakeDamage( entvars_t *pevInflictor, entvars_t *pevAttacker, float flDamage, int bitsDamageType );
|
||||
int Classify( void ) { return CLASS_INSECT; }
|
||||
int IRelationship( CBaseEntity *pTarget );
|
||||
|
||||
virtual int Save( CSave &save );
|
||||
virtual int Restore( CRestore &restore );
|
||||
static TYPEDESCRIPTION m_SaveData[];
|
||||
|
||||
static const char *pAttackSounds[];
|
||||
static const char *pAlertSounds[];
|
||||
|
||||
private:
|
||||
// UNDONE: Remove unused boid vars, do group behavior
|
||||
float m_flTurning;// is this boid turning?
|
||||
BOOL m_fPathBlocked;// TRUE if there is an obstacle ahead
|
||||
float m_flAccelerate;
|
||||
float m_obstacle;
|
||||
float m_top;
|
||||
float m_bottom;
|
||||
float m_height;
|
||||
float m_waterTime;
|
||||
float m_sideTime; // Timer to randomly check clearance on sides
|
||||
float m_zTime;
|
||||
float m_stateTime;
|
||||
float m_attackSoundTime;
|
||||
|
||||
#if DEBUG_BEAMS
|
||||
CBeam *m_pb;
|
||||
CBeam *m_pt;
|
||||
#endif
|
||||
};
|
||||
|
||||
|
||||
|
||||
LINK_ENTITY_TO_CLASS( monster_leech, CLeech );
|
||||
|
||||
TYPEDESCRIPTION CLeech::m_SaveData[] =
|
||||
{
|
||||
DEFINE_FIELD( CLeech, m_flTurning, FIELD_FLOAT ),
|
||||
DEFINE_FIELD( CLeech, m_fPathBlocked, FIELD_BOOLEAN ),
|
||||
DEFINE_FIELD( CLeech, m_flAccelerate, FIELD_FLOAT ),
|
||||
DEFINE_FIELD( CLeech, m_obstacle, FIELD_FLOAT ),
|
||||
DEFINE_FIELD( CLeech, m_top, FIELD_FLOAT ),
|
||||
DEFINE_FIELD( CLeech, m_bottom, FIELD_FLOAT ),
|
||||
DEFINE_FIELD( CLeech, m_height, FIELD_FLOAT ),
|
||||
DEFINE_FIELD( CLeech, m_waterTime, FIELD_TIME ),
|
||||
DEFINE_FIELD( CLeech, m_sideTime, FIELD_TIME ),
|
||||
DEFINE_FIELD( CLeech, m_zTime, FIELD_TIME ),
|
||||
DEFINE_FIELD( CLeech, m_stateTime, FIELD_TIME ),
|
||||
DEFINE_FIELD( CLeech, m_attackSoundTime, FIELD_TIME ),
|
||||
};
|
||||
|
||||
IMPLEMENT_SAVERESTORE( CLeech, CBaseMonster );
|
||||
|
||||
|
||||
const char *CLeech::pAttackSounds[] =
|
||||
{
|
||||
"leech/leech_bite1.wav",
|
||||
"leech/leech_bite2.wav",
|
||||
"leech/leech_bite3.wav",
|
||||
};
|
||||
|
||||
const char *CLeech::pAlertSounds[] =
|
||||
{
|
||||
"leech/leech_alert1.wav",
|
||||
"leech/leech_alert2.wav",
|
||||
};
|
||||
|
||||
|
||||
void CLeech::Spawn( void )
|
||||
{
|
||||
Precache();
|
||||
SET_MODEL(ENT(pev), "models/leech.mdl");
|
||||
// Just for fun
|
||||
// SET_MODEL(ENT(pev), "models/icky.mdl");
|
||||
|
||||
// UTIL_SetSize( pev, g_vecZero, g_vecZero );
|
||||
UTIL_SetSize( pev, Vector(-1,-1,0), Vector(1,1,2));
|
||||
// Don't push the minz down too much or the water check will fail because this entity is really point-sized
|
||||
pev->solid = SOLID_SLIDEBOX;
|
||||
pev->movetype = MOVETYPE_FLY;
|
||||
SetBits(pev->flags, FL_SWIM);
|
||||
pev->health = gSkillData.leechHealth;
|
||||
|
||||
m_flFieldOfView = -0.5; // 180 degree FOV
|
||||
m_flDistLook = 750;
|
||||
MonsterInit();
|
||||
SetThink( SwimThink );
|
||||
SetUse( NULL );
|
||||
SetTouch( NULL );
|
||||
pev->view_ofs = g_vecZero;
|
||||
|
||||
m_flTurning = 0;
|
||||
m_fPathBlocked = FALSE;
|
||||
SetActivity( ACT_SWIM );
|
||||
SetState( MONSTERSTATE_IDLE );
|
||||
m_stateTime = gpGlobals->time + RANDOM_FLOAT( 1, 5 );
|
||||
}
|
||||
|
||||
|
||||
void CLeech::Activate( void )
|
||||
{
|
||||
RecalculateWaterlevel();
|
||||
}
|
||||
|
||||
|
||||
|
||||
void CLeech::RecalculateWaterlevel( void )
|
||||
{
|
||||
// Calculate boundaries
|
||||
Vector vecTest = pev->origin - Vector(0,0,400);
|
||||
|
||||
TraceResult tr;
|
||||
|
||||
UTIL_TraceLine(pev->origin, vecTest, missile, edict(), &tr);
|
||||
if ( tr.flFraction != 1.0 )
|
||||
m_bottom = tr.vecEndPos.z + 1;
|
||||
else
|
||||
m_bottom = vecTest.z;
|
||||
|
||||
m_top = UTIL_WaterLevel( pev->origin, pev->origin.z, pev->origin.z + 400 ) - 1;
|
||||
|
||||
// Chop off 20% of the outside range
|
||||
float newBottom = m_bottom * 0.8 + m_top * 0.2;
|
||||
m_top = m_bottom * 0.2 + m_top * 0.8;
|
||||
m_bottom = newBottom;
|
||||
m_height = RANDOM_FLOAT( m_bottom, m_top );
|
||||
m_waterTime = gpGlobals->time + RANDOM_FLOAT( 5, 7 );
|
||||
}
|
||||
|
||||
|
||||
void CLeech::SwitchLeechState( void )
|
||||
{
|
||||
m_stateTime = gpGlobals->time + RANDOM_FLOAT( 3, 6 );
|
||||
if ( m_MonsterState == MONSTERSTATE_COMBAT )
|
||||
{
|
||||
m_hEnemy = NULL;
|
||||
SetState( MONSTERSTATE_IDLE );
|
||||
// We may be up against the player, so redo the side checks
|
||||
m_sideTime = 0;
|
||||
}
|
||||
else
|
||||
{
|
||||
Look( m_flDistLook );
|
||||
CBaseEntity *pEnemy = BestVisibleEnemy();
|
||||
if ( pEnemy && pEnemy->pev->waterlevel != 0 )
|
||||
{
|
||||
m_hEnemy = pEnemy;
|
||||
SetState( MONSTERSTATE_COMBAT );
|
||||
m_stateTime = gpGlobals->time + RANDOM_FLOAT( 18, 25 );
|
||||
AlertSound();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
int CLeech::IRelationship( CBaseEntity *pTarget )
|
||||
{
|
||||
if ( pTarget->IsPlayer() )
|
||||
return R_DL;
|
||||
return CBaseMonster::IRelationship( pTarget );
|
||||
}
|
||||
|
||||
|
||||
|
||||
void CLeech::AttackSound( void )
|
||||
{
|
||||
if ( gpGlobals->time > m_attackSoundTime )
|
||||
{
|
||||
EMIT_SOUND_DYN ( ENT(pev), CHAN_VOICE, pAttackSounds[ RANDOM_LONG(0,ARRAYSIZE(pAttackSounds)-1) ], 1.0, ATTN_NORM, 0, PITCH_NORM );
|
||||
m_attackSoundTime = gpGlobals->time + 0.5;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
void CLeech::AlertSound( void )
|
||||
{
|
||||
EMIT_SOUND_DYN ( ENT(pev), CHAN_VOICE, pAlertSounds[ RANDOM_LONG(0,ARRAYSIZE(pAlertSounds)-1) ], 1.0, ATTN_NORM * 0.5, 0, PITCH_NORM );
|
||||
}
|
||||
|
||||
|
||||
void CLeech::Precache( void )
|
||||
{
|
||||
int i;
|
||||
|
||||
//PRECACHE_MODEL("models/icky.mdl");
|
||||
PRECACHE_MODEL("models/leech.mdl");
|
||||
|
||||
for ( i = 0; i < ARRAYSIZE( pAttackSounds ); i++ )
|
||||
PRECACHE_SOUND((char *)pAttackSounds[i]);
|
||||
for ( i = 0; i < ARRAYSIZE( pAlertSounds ); i++ )
|
||||
PRECACHE_SOUND((char *)pAlertSounds[i]);
|
||||
}
|
||||
|
||||
|
||||
int CLeech::TakeDamage( entvars_t *pevInflictor, entvars_t *pevAttacker, float flDamage, int bitsDamageType )
|
||||
{
|
||||
pev->velocity = g_vecZero;
|
||||
|
||||
// Nudge the leech away from the damage
|
||||
if ( pevInflictor )
|
||||
{
|
||||
pev->velocity = (pev->origin - pevInflictor->origin).Normalize() * 25;
|
||||
}
|
||||
|
||||
return CBaseMonster::TakeDamage( pevInflictor, pevAttacker, flDamage, bitsDamageType );
|
||||
}
|
||||
|
||||
|
||||
void CLeech::HandleAnimEvent( MonsterEvent_t *pEvent )
|
||||
{
|
||||
switch( pEvent->event )
|
||||
{
|
||||
case LEECH_AE_ATTACK:
|
||||
AttackSound();
|
||||
CBaseEntity *pEnemy;
|
||||
|
||||
pEnemy = m_hEnemy;
|
||||
if ( pEnemy != NULL )
|
||||
{
|
||||
Vector dir, face;
|
||||
|
||||
UTIL_MakeVectorsPrivate( pev->angles, face, NULL, NULL );
|
||||
face.z = 0;
|
||||
dir = (pEnemy->pev->origin - pev->origin);
|
||||
dir.z = 0;
|
||||
dir = dir.Normalize();
|
||||
face = face.Normalize();
|
||||
|
||||
|
||||
if ( DotProduct(dir, face) > 0.9 ) // Only take damage if the leech is facing the prey
|
||||
pEnemy->TakeDamage( pev, pev, gSkillData.leechDmgBite, DMG_SLASH );
|
||||
}
|
||||
m_stateTime -= 2;
|
||||
break;
|
||||
|
||||
case LEECH_AE_FLOP:
|
||||
// Play flop sound
|
||||
break;
|
||||
|
||||
default:
|
||||
CBaseMonster::HandleAnimEvent( pEvent );
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
void CLeech::MakeVectors( void )
|
||||
{
|
||||
Vector tmp = pev->angles;
|
||||
tmp.x = -tmp.x;
|
||||
UTIL_MakeVectors ( tmp );
|
||||
}
|
||||
|
||||
|
||||
//
|
||||
// ObstacleDistance - returns normalized distance to obstacle
|
||||
//
|
||||
float CLeech::ObstacleDistance( CBaseEntity *pTarget )
|
||||
{
|
||||
TraceResult tr;
|
||||
Vector vecTest;
|
||||
|
||||
// use VELOCITY, not angles, not all boids point the direction they are flying
|
||||
//Vector vecDir = UTIL_VecToAngles( pev->velocity );
|
||||
MakeVectors();
|
||||
|
||||
// check for obstacle ahead
|
||||
vecTest = pev->origin + gpGlobals->v_forward * LEECH_CHECK_DIST;
|
||||
UTIL_TraceLine(pev->origin, vecTest, missile, edict(), &tr);
|
||||
|
||||
if ( tr.fStartSolid )
|
||||
{
|
||||
pev->speed = -LEECH_SWIM_SPEED * 0.5;
|
||||
// ALERT( at_console, "Stuck from (%f %f %f) to (%f %f %f)\n", pev->oldorigin.x, pev->oldorigin.y, pev->oldorigin.z, pev->origin.x, pev->origin.y, pev->origin.z );
|
||||
// UTIL_SetOrigin( pev, pev->oldorigin );
|
||||
}
|
||||
|
||||
if ( tr.flFraction != 1.0 )
|
||||
{
|
||||
if ( (pTarget == NULL || tr.pHit != pTarget->edict()) )
|
||||
{
|
||||
return tr.flFraction;
|
||||
}
|
||||
else
|
||||
{
|
||||
if ( fabs(m_height - pev->origin.z) > 10 )
|
||||
return tr.flFraction;
|
||||
}
|
||||
}
|
||||
|
||||
if ( m_sideTime < gpGlobals->time )
|
||||
{
|
||||
// extra wide checks
|
||||
vecTest = pev->origin + gpGlobals->v_right * LEECH_SIZEX * 2 + gpGlobals->v_forward * LEECH_CHECK_DIST;
|
||||
UTIL_TraceLine(pev->origin, vecTest, missile, edict(), &tr);
|
||||
if (tr.flFraction != 1.0)
|
||||
return tr.flFraction;
|
||||
|
||||
vecTest = pev->origin - gpGlobals->v_right * LEECH_SIZEX * 2 + gpGlobals->v_forward * LEECH_CHECK_DIST;
|
||||
UTIL_TraceLine(pev->origin, vecTest, missile, edict(), &tr);
|
||||
if (tr.flFraction != 1.0)
|
||||
return tr.flFraction;
|
||||
|
||||
// Didn't hit either side, so stop testing for another 0.5 - 1 seconds
|
||||
m_sideTime = gpGlobals->time + RANDOM_FLOAT(0.5,1);
|
||||
}
|
||||
return 1.0;
|
||||
}
|
||||
|
||||
|
||||
void CLeech::DeadThink( void )
|
||||
{
|
||||
if ( m_fSequenceFinished )
|
||||
{
|
||||
if ( m_Activity == ACT_DIEFORWARD )
|
||||
{
|
||||
SetThink( NULL );
|
||||
StopAnimation();
|
||||
return;
|
||||
}
|
||||
else if ( pev->flags & FL_ONGROUND )
|
||||
{
|
||||
pev->solid = SOLID_NOT;
|
||||
SetActivity(ACT_DIEFORWARD);
|
||||
}
|
||||
}
|
||||
StudioFrameAdvance();
|
||||
pev->nextthink = gpGlobals->time + 0.1;
|
||||
|
||||
// Apply damage velocity, but keep out of the walls
|
||||
if ( pev->velocity.x != 0 || pev->velocity.y != 0 )
|
||||
{
|
||||
TraceResult tr;
|
||||
|
||||
// Look 0.5 seconds ahead
|
||||
UTIL_TraceLine(pev->origin, pev->origin + pev->velocity * 0.5, missile, edict(), &tr);
|
||||
if (tr.flFraction != 1.0)
|
||||
{
|
||||
pev->velocity.x = 0;
|
||||
pev->velocity.y = 0;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
void CLeech::UpdateMotion( void )
|
||||
{
|
||||
float flapspeed = (pev->speed - m_flAccelerate) / LEECH_ACCELERATE;
|
||||
m_flAccelerate = m_flAccelerate * 0.8 + pev->speed * 0.2;
|
||||
|
||||
if (flapspeed < 0)
|
||||
flapspeed = -flapspeed;
|
||||
flapspeed += 1.0;
|
||||
if (flapspeed < 0.5)
|
||||
flapspeed = 0.5;
|
||||
if (flapspeed > 1.9)
|
||||
flapspeed = 1.9;
|
||||
|
||||
pev->framerate = flapspeed;
|
||||
|
||||
if ( !m_fPathBlocked )
|
||||
pev->avelocity.y = pev->ideal_yaw;
|
||||
else
|
||||
pev->avelocity.y = pev->ideal_yaw * m_obstacle;
|
||||
|
||||
if ( pev->avelocity.y > 150 )
|
||||
m_IdealActivity = ACT_TURN_LEFT;
|
||||
else if ( pev->avelocity.y < -150 )
|
||||
m_IdealActivity = ACT_TURN_RIGHT;
|
||||
else
|
||||
m_IdealActivity = ACT_SWIM;
|
||||
|
||||
// lean
|
||||
float targetPitch, delta;
|
||||
delta = m_height - pev->origin.z;
|
||||
|
||||
if ( delta < -10 )
|
||||
targetPitch = -30;
|
||||
else if ( delta > 10 )
|
||||
targetPitch = 30;
|
||||
else
|
||||
targetPitch = 0;
|
||||
|
||||
pev->angles.x = UTIL_Approach( targetPitch, pev->angles.x, 60 * LEECH_FRAMETIME );
|
||||
|
||||
// bank
|
||||
pev->avelocity.z = - (pev->angles.z + (pev->avelocity.y * 0.25));
|
||||
|
||||
if ( m_MonsterState == MONSTERSTATE_COMBAT && HasConditions( bits_COND_CAN_MELEE_ATTACK1 ) )
|
||||
m_IdealActivity = ACT_MELEE_ATTACK1;
|
||||
|
||||
// Out of water check
|
||||
if ( !pev->waterlevel )
|
||||
{
|
||||
pev->movetype = MOVETYPE_TOSS;
|
||||
m_IdealActivity = ACT_TWITCH;
|
||||
pev->velocity = g_vecZero;
|
||||
|
||||
// Animation will intersect the floor if either of these is non-zero
|
||||
pev->angles.z = 0;
|
||||
pev->angles.x = 0;
|
||||
|
||||
if ( pev->framerate < 1.0 )
|
||||
pev->framerate = 1.0;
|
||||
}
|
||||
else if ( pev->movetype == MOVETYPE_TOSS )
|
||||
{
|
||||
pev->movetype = MOVETYPE_FLY;
|
||||
pev->flags &= ~FL_ONGROUND;
|
||||
RecalculateWaterlevel();
|
||||
m_waterTime = gpGlobals->time + 2; // Recalc again soon, water may be rising
|
||||
}
|
||||
|
||||
if ( m_Activity != m_IdealActivity )
|
||||
{
|
||||
SetActivity ( m_IdealActivity );
|
||||
}
|
||||
float flInterval = StudioFrameAdvance();
|
||||
DispatchAnimEvents ( flInterval );
|
||||
|
||||
#if DEBUG_BEAMS
|
||||
if ( !m_pb )
|
||||
m_pb = CBeam::BeamCreate( "sprites/laserbeam.spr", 5 );
|
||||
if ( !m_pt )
|
||||
m_pt = CBeam::BeamCreate( "sprites/laserbeam.spr", 5 );
|
||||
m_pb->PointsInit( pev->origin, pev->origin + gpGlobals->v_forward * LEECH_CHECK_DIST );
|
||||
m_pt->PointsInit( pev->origin, pev->origin - gpGlobals->v_right * (pev->avelocity.y*0.25) );
|
||||
if ( m_fPathBlocked )
|
||||
{
|
||||
float color = m_obstacle * 30;
|
||||
if ( m_obstacle == 1.0 )
|
||||
color = 0;
|
||||
if ( color > 255 )
|
||||
color = 255;
|
||||
m_pb->SetColor( 255, (int)color, (int)color );
|
||||
}
|
||||
else
|
||||
m_pb->SetColor( 255, 255, 0 );
|
||||
m_pt->SetColor( 0, 0, 255 );
|
||||
#endif
|
||||
}
|
||||
|
||||
|
||||
void CLeech::SwimThink( void )
|
||||
{
|
||||
TraceResult tr;
|
||||
float flLeftSide;
|
||||
float flRightSide;
|
||||
float targetSpeed;
|
||||
float targetYaw = 0;
|
||||
CBaseEntity *pTarget;
|
||||
|
||||
if ( FNullEnt( FIND_CLIENT_IN_PVS( edict() ) ) )
|
||||
{
|
||||
pev->nextthink = gpGlobals->time + RANDOM_FLOAT(1,1.5);
|
||||
pev->velocity = g_vecZero;
|
||||
return;
|
||||
}
|
||||
else
|
||||
pev->nextthink = gpGlobals->time + 0.1;
|
||||
|
||||
targetSpeed = LEECH_SWIM_SPEED;
|
||||
|
||||
if ( m_waterTime < gpGlobals->time )
|
||||
RecalculateWaterlevel();
|
||||
|
||||
if ( m_stateTime < gpGlobals->time )
|
||||
SwitchLeechState();
|
||||
|
||||
ClearConditions( bits_COND_CAN_MELEE_ATTACK1 );
|
||||
switch( m_MonsterState )
|
||||
{
|
||||
case MONSTERSTATE_COMBAT:
|
||||
pTarget = m_hEnemy;
|
||||
if ( !pTarget )
|
||||
SwitchLeechState();
|
||||
else
|
||||
{
|
||||
// Chase the enemy's eyes
|
||||
m_height = pTarget->pev->origin.z + pTarget->pev->view_ofs.z - 5;
|
||||
// Clip to viable water area
|
||||
if ( m_height < m_bottom )
|
||||
m_height = m_bottom;
|
||||
else if ( m_height > m_top )
|
||||
m_height = m_top;
|
||||
Vector location = pTarget->pev->origin - pev->origin;
|
||||
location.z += (pTarget->pev->view_ofs.z);
|
||||
if ( location.Length() < 40 )
|
||||
SetConditions( bits_COND_CAN_MELEE_ATTACK1 );
|
||||
// Turn towards target ent
|
||||
targetYaw = UTIL_VecToYaw( location );
|
||||
|
||||
targetYaw = UTIL_AngleDiff( targetYaw, UTIL_AngleMod( pev->angles.y ) );
|
||||
|
||||
if ( targetYaw < (-LEECH_TURN_RATE*0.75) )
|
||||
targetYaw = (-LEECH_TURN_RATE*0.75);
|
||||
else if ( targetYaw > (LEECH_TURN_RATE*0.75) )
|
||||
targetYaw = (LEECH_TURN_RATE*0.75);
|
||||
else
|
||||
targetSpeed *= 2;
|
||||
}
|
||||
|
||||
break;
|
||||
|
||||
default:
|
||||
if ( m_zTime < gpGlobals->time )
|
||||
{
|
||||
float newHeight = RANDOM_FLOAT( m_bottom, m_top );
|
||||
m_height = 0.5 * m_height + 0.5 * newHeight;
|
||||
m_zTime = gpGlobals->time + RANDOM_FLOAT( 1, 4 );
|
||||
}
|
||||
if ( RANDOM_LONG( 0, 100 ) < 10 )
|
||||
targetYaw = RANDOM_LONG( -30, 30 );
|
||||
pTarget = NULL;
|
||||
// oldorigin test
|
||||
if ( (pev->origin - pev->oldorigin).Length() < 1 )
|
||||
{
|
||||
// If leech didn't move, there must be something blocking it, so try to turn
|
||||
m_sideTime = 0;
|
||||
}
|
||||
|
||||
break;
|
||||
}
|
||||
|
||||
m_obstacle = ObstacleDistance( pTarget );
|
||||
pev->oldorigin = pev->origin;
|
||||
if ( m_obstacle < 0.1 )
|
||||
m_obstacle = 0.1;
|
||||
|
||||
// is the way ahead clear?
|
||||
if ( m_obstacle == 1.0 )
|
||||
{
|
||||
// if the leech is turning, stop the trend.
|
||||
if ( m_flTurning != 0 )
|
||||
{
|
||||
m_flTurning = 0;
|
||||
}
|
||||
|
||||
m_fPathBlocked = FALSE;
|
||||
pev->speed = UTIL_Approach( targetSpeed, pev->speed, LEECH_SWIM_ACCEL * LEECH_FRAMETIME );
|
||||
pev->velocity = gpGlobals->v_forward * pev->speed;
|
||||
|
||||
}
|
||||
else
|
||||
{
|
||||
m_obstacle = 1.0 / m_obstacle;
|
||||
// IF we get this far in the function, the leader's path is blocked!
|
||||
m_fPathBlocked = TRUE;
|
||||
|
||||
if ( m_flTurning == 0 )// something in the way and leech is not already turning to avoid
|
||||
{
|
||||
Vector vecTest;
|
||||
// measure clearance on left and right to pick the best dir to turn
|
||||
vecTest = pev->origin + (gpGlobals->v_right * LEECH_SIZEX) + (gpGlobals->v_forward * LEECH_CHECK_DIST);
|
||||
UTIL_TraceLine(pev->origin, vecTest, missile, edict(), &tr);
|
||||
flRightSide = tr.flFraction;
|
||||
|
||||
vecTest = pev->origin + (gpGlobals->v_right * -LEECH_SIZEX) + (gpGlobals->v_forward * LEECH_CHECK_DIST);
|
||||
UTIL_TraceLine(pev->origin, vecTest, missile, edict(), &tr);
|
||||
flLeftSide = tr.flFraction;
|
||||
|
||||
// turn left, right or random depending on clearance ratio
|
||||
float delta = (flRightSide - flLeftSide);
|
||||
if ( delta > 0.1 || (delta > -0.1 && RANDOM_LONG(0,100)<50) )
|
||||
m_flTurning = -LEECH_TURN_RATE;
|
||||
else
|
||||
m_flTurning = LEECH_TURN_RATE;
|
||||
}
|
||||
pev->speed = UTIL_Approach( -(LEECH_SWIM_SPEED*0.5), pev->speed, LEECH_SWIM_DECEL * LEECH_FRAMETIME * m_obstacle );
|
||||
pev->velocity = gpGlobals->v_forward * pev->speed;
|
||||
}
|
||||
pev->ideal_yaw = m_flTurning + targetYaw;
|
||||
UpdateMotion();
|
||||
}
|
||||
|
||||
|
||||
void CLeech::Killed(entvars_t *pevAttacker, int iGib)
|
||||
{
|
||||
Vector vecSplatDir;
|
||||
TraceResult tr;
|
||||
|
||||
//ALERT(at_aiconsole, "Leech: killed\n");
|
||||
// tell owner ( if any ) that we're dead.This is mostly for MonsterMaker functionality.
|
||||
CBaseEntity *pOwner = CBaseEntity::Instance(pev->owner);
|
||||
if (pOwner)
|
||||
pOwner->DeathNotice(pev);
|
||||
|
||||
// When we hit the ground, play the "death_end" activity
|
||||
if ( pev->waterlevel )
|
||||
{
|
||||
pev->angles.z = 0;
|
||||
pev->angles.x = 0;
|
||||
pev->origin.z += 1;
|
||||
pev->avelocity = g_vecZero;
|
||||
if ( RANDOM_LONG( 0, 99 ) < 70 )
|
||||
pev->avelocity.y = RANDOM_LONG( -720, 720 );
|
||||
|
||||
pev->gravity = 0.02;
|
||||
ClearBits(pev->flags, FL_ONGROUND);
|
||||
SetActivity( ACT_DIESIMPLE );
|
||||
}
|
||||
else
|
||||
SetActivity( ACT_DIEFORWARD );
|
||||
|
||||
pev->movetype = MOVETYPE_TOSS;
|
||||
pev->takedamage = DAMAGE_NO;
|
||||
SetThink( DeadThink );
|
||||
}
|
||||
|
||||
|
|
@ -0,0 +1,199 @@
|
|||
/***
|
||||
*
|
||||
* 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.
|
||||
*
|
||||
****/
|
||||
/*
|
||||
|
||||
===== lights.cpp ========================================================
|
||||
|
||||
spawn and think functions for editor-placed lights
|
||||
|
||||
*/
|
||||
|
||||
#include "extdll.h"
|
||||
#include "util.h"
|
||||
#include "cbase.h"
|
||||
|
||||
|
||||
|
||||
class CLight : public CPointEntity
|
||||
{
|
||||
public:
|
||||
virtual void KeyValue( KeyValueData* pkvd );
|
||||
virtual void Spawn( void );
|
||||
void Use( CBaseEntity *pActivator, CBaseEntity *pCaller, USE_TYPE useType, float value );
|
||||
|
||||
virtual int Save( CSave &save );
|
||||
virtual int Restore( CRestore &restore );
|
||||
|
||||
static TYPEDESCRIPTION m_SaveData[];
|
||||
|
||||
private:
|
||||
int m_iStyle;
|
||||
int m_iszPattern;
|
||||
};
|
||||
LINK_ENTITY_TO_CLASS( light, CLight );
|
||||
|
||||
TYPEDESCRIPTION CLight::m_SaveData[] =
|
||||
{
|
||||
DEFINE_FIELD( CLight, m_iStyle, FIELD_INTEGER ),
|
||||
DEFINE_FIELD( CLight, m_iszPattern, FIELD_STRING ),
|
||||
};
|
||||
|
||||
IMPLEMENT_SAVERESTORE( CLight, CPointEntity );
|
||||
|
||||
|
||||
//
|
||||
// Cache user-entity-field values until spawn is called.
|
||||
//
|
||||
void CLight :: KeyValue( KeyValueData* pkvd)
|
||||
{
|
||||
if (FStrEq(pkvd->szKeyName, "style"))
|
||||
{
|
||||
m_iStyle = atoi(pkvd->szValue);
|
||||
pkvd->fHandled = TRUE;
|
||||
}
|
||||
else if (FStrEq(pkvd->szKeyName, "pitch"))
|
||||
{
|
||||
pev->angles.x = atof(pkvd->szValue);
|
||||
pkvd->fHandled = TRUE;
|
||||
}
|
||||
else if (FStrEq(pkvd->szKeyName, "pattern"))
|
||||
{
|
||||
m_iszPattern = ALLOC_STRING( pkvd->szValue );
|
||||
pkvd->fHandled = TRUE;
|
||||
}
|
||||
else
|
||||
{
|
||||
CPointEntity::KeyValue( pkvd );
|
||||
}
|
||||
}
|
||||
|
||||
/*QUAKED light (0 1 0) (-8 -8 -8) (8 8 8) LIGHT_START_OFF
|
||||
Non-displayed light.
|
||||
Default light value is 300
|
||||
Default style is 0
|
||||
If targeted, it will toggle between on or off.
|
||||
*/
|
||||
|
||||
void CLight :: Spawn( void )
|
||||
{
|
||||
if (FStringNull(pev->targetname))
|
||||
{ // inert light
|
||||
REMOVE_ENTITY(ENT(pev));
|
||||
return;
|
||||
}
|
||||
|
||||
if (m_iStyle >= 32)
|
||||
{
|
||||
// CHANGE_METHOD(ENT(pev), em_use, light_use);
|
||||
if (FBitSet(pev->spawnflags, SF_LIGHT_START_OFF))
|
||||
LIGHT_STYLE(m_iStyle, "a");
|
||||
else if (m_iszPattern)
|
||||
LIGHT_STYLE(m_iStyle, (char *)STRING( m_iszPattern ));
|
||||
else
|
||||
LIGHT_STYLE(m_iStyle, "m");
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
void CLight :: Use( CBaseEntity *pActivator, CBaseEntity *pCaller, USE_TYPE useType, float value )
|
||||
{
|
||||
if (m_iStyle >= 32)
|
||||
{
|
||||
if ( !ShouldToggle( useType, !FBitSet(pev->spawnflags, SF_LIGHT_START_OFF) ) )
|
||||
return;
|
||||
|
||||
if (FBitSet(pev->spawnflags, SF_LIGHT_START_OFF))
|
||||
{
|
||||
if (m_iszPattern)
|
||||
LIGHT_STYLE(m_iStyle, (char *)STRING( m_iszPattern ));
|
||||
else
|
||||
LIGHT_STYLE(m_iStyle, "m");
|
||||
ClearBits(pev->spawnflags, SF_LIGHT_START_OFF);
|
||||
}
|
||||
else
|
||||
{
|
||||
LIGHT_STYLE(m_iStyle, "a");
|
||||
SetBits(pev->spawnflags, SF_LIGHT_START_OFF);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
//
|
||||
// shut up spawn functions for new spotlights
|
||||
//
|
||||
LINK_ENTITY_TO_CLASS( light_spot, CLight );
|
||||
|
||||
|
||||
class CEnvLight : public CLight
|
||||
{
|
||||
public:
|
||||
void KeyValue( KeyValueData* pkvd );
|
||||
void Spawn( void );
|
||||
};
|
||||
|
||||
LINK_ENTITY_TO_CLASS( light_environment, CEnvLight );
|
||||
|
||||
void CEnvLight::KeyValue( KeyValueData* pkvd )
|
||||
{
|
||||
if (FStrEq(pkvd->szKeyName, "_light"))
|
||||
{
|
||||
int r, g, b, v, j;
|
||||
char szColor[64];
|
||||
j = sscanf( pkvd->szValue, "%d %d %d %d\n", &r, &g, &b, &v );
|
||||
if (j == 1)
|
||||
{
|
||||
g = b = r;
|
||||
}
|
||||
else if (j == 4)
|
||||
{
|
||||
r = r * (v / 255.0);
|
||||
g = g * (v / 255.0);
|
||||
b = b * (v / 255.0);
|
||||
}
|
||||
|
||||
// simulate qrad direct, ambient,and gamma adjustments, as well as engine scaling
|
||||
r = pow( r / 114.0, 0.6 ) * 264;
|
||||
g = pow( g / 114.0, 0.6 ) * 264;
|
||||
b = pow( b / 114.0, 0.6 ) * 264;
|
||||
|
||||
pkvd->fHandled = TRUE;
|
||||
sprintf( szColor, "%d", r );
|
||||
CVAR_SET_STRING( "sv_skycolor_r", szColor );
|
||||
sprintf( szColor, "%d", g );
|
||||
CVAR_SET_STRING( "sv_skycolor_g", szColor );
|
||||
sprintf( szColor, "%d", b );
|
||||
CVAR_SET_STRING( "sv_skycolor_b", szColor );
|
||||
}
|
||||
else
|
||||
{
|
||||
CLight::KeyValue( pkvd );
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
void CEnvLight :: Spawn( void )
|
||||
{
|
||||
char szVector[64];
|
||||
UTIL_MakeAimVectors( pev->angles );
|
||||
|
||||
sprintf( szVector, "%f", gpGlobals->v_forward.x );
|
||||
CVAR_SET_STRING( "sv_skyvec_x", szVector );
|
||||
sprintf( szVector, "%f", gpGlobals->v_forward.y );
|
||||
CVAR_SET_STRING( "sv_skyvec_y", szVector );
|
||||
sprintf( szVector, "%f", gpGlobals->v_forward.z );
|
||||
CVAR_SET_STRING( "sv_skyvec_z", szVector );
|
||||
|
||||
CLight::Spawn( );
|
||||
}
|
|
@ -0,0 +1,917 @@
|
|||
/***
|
||||
*
|
||||
* 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.
|
||||
*
|
||||
****/
|
||||
|
||||
// -------------------------------------------
|
||||
//
|
||||
// maprules.cpp
|
||||
//
|
||||
// This module contains entities for implementing/changing game
|
||||
// rules dynamically within each map (.BSP)
|
||||
//
|
||||
// -------------------------------------------
|
||||
|
||||
#include "extdll.h"
|
||||
#include "util.h"
|
||||
#include "gamerules.h"
|
||||
#include "maprules.h"
|
||||
#include "cbase.h"
|
||||
#include "player.h"
|
||||
|
||||
class CRuleEntity : public CBaseEntity
|
||||
{
|
||||
public:
|
||||
void Spawn( void );
|
||||
void KeyValue( KeyValueData *pkvd );
|
||||
virtual int Save( CSave &save );
|
||||
virtual int Restore( CRestore &restore );
|
||||
static TYPEDESCRIPTION m_SaveData[];
|
||||
|
||||
void SetMaster( int iszMaster ) { m_iszMaster = iszMaster; }
|
||||
|
||||
protected:
|
||||
BOOL CanFireForActivator( CBaseEntity *pActivator );
|
||||
|
||||
private:
|
||||
string_t m_iszMaster;
|
||||
};
|
||||
|
||||
TYPEDESCRIPTION CRuleEntity::m_SaveData[] =
|
||||
{
|
||||
DEFINE_FIELD( CRuleEntity, m_iszMaster, FIELD_STRING),
|
||||
};
|
||||
|
||||
IMPLEMENT_SAVERESTORE( CRuleEntity, CBaseEntity );
|
||||
|
||||
|
||||
void CRuleEntity::Spawn( void )
|
||||
{
|
||||
pev->solid = SOLID_NOT;
|
||||
pev->movetype = MOVETYPE_NONE;
|
||||
pev->effects = EF_NODRAW;
|
||||
}
|
||||
|
||||
|
||||
void CRuleEntity::KeyValue( KeyValueData *pkvd )
|
||||
{
|
||||
if (FStrEq(pkvd->szKeyName, "master"))
|
||||
{
|
||||
SetMaster( ALLOC_STRING(pkvd->szValue) );
|
||||
pkvd->fHandled = TRUE;
|
||||
}
|
||||
else
|
||||
CBaseEntity::KeyValue( pkvd );
|
||||
}
|
||||
|
||||
BOOL CRuleEntity::CanFireForActivator( CBaseEntity *pActivator )
|
||||
{
|
||||
if ( m_iszMaster )
|
||||
{
|
||||
if ( UTIL_IsMasterTriggered( m_iszMaster, pActivator ) )
|
||||
return TRUE;
|
||||
else
|
||||
return FALSE;
|
||||
}
|
||||
|
||||
return TRUE;
|
||||
}
|
||||
|
||||
//
|
||||
// CRulePointEntity -- base class for all rule "point" entities (not brushes)
|
||||
//
|
||||
class CRulePointEntity : public CRuleEntity
|
||||
{
|
||||
public:
|
||||
void Spawn( void );
|
||||
};
|
||||
|
||||
void CRulePointEntity::Spawn( void )
|
||||
{
|
||||
CRuleEntity::Spawn();
|
||||
pev->frame = 0;
|
||||
pev->model = 0;
|
||||
}
|
||||
|
||||
//
|
||||
// CRuleBrushEntity -- base class for all rule "brush" entities (not brushes)
|
||||
// Default behavior is to set up like a trigger, invisible, but keep the model for volume testing
|
||||
//
|
||||
class CRuleBrushEntity : public CRuleEntity
|
||||
{
|
||||
public:
|
||||
void Spawn( void );
|
||||
|
||||
private:
|
||||
};
|
||||
|
||||
void CRuleBrushEntity::Spawn( void )
|
||||
{
|
||||
SET_MODEL( edict(), STRING(pev->model) );
|
||||
CRuleEntity::Spawn();
|
||||
}
|
||||
|
||||
|
||||
// CGameScore / game_score -- award points to player / team
|
||||
// Points +/- total
|
||||
// Flag: Allow negative scores SF_SCORE_NEGATIVE
|
||||
// Flag: Award points to team in teamplay SF_SCORE_TEAM
|
||||
|
||||
#define SF_SCORE_NEGATIVE 0x0001
|
||||
#define SF_SCORE_TEAM 0x0002
|
||||
|
||||
class CGameScore : public CRulePointEntity
|
||||
{
|
||||
public:
|
||||
void Spawn( void );
|
||||
void Use( CBaseEntity *pActivator, CBaseEntity *pCaller, USE_TYPE useType, float value );
|
||||
void KeyValue( KeyValueData *pkvd );
|
||||
|
||||
inline int Points( void ) { return pev->frags; }
|
||||
inline BOOL AllowNegativeScore( void ) { return pev->spawnflags & SF_SCORE_NEGATIVE; }
|
||||
inline BOOL AwardToTeam( void ) { return pev->spawnflags & SF_SCORE_TEAM; }
|
||||
|
||||
inline void SetPoints( int points ) { pev->frags = points; }
|
||||
|
||||
private:
|
||||
};
|
||||
|
||||
LINK_ENTITY_TO_CLASS( game_score, CGameScore );
|
||||
|
||||
|
||||
void CGameScore::Spawn( void )
|
||||
{
|
||||
CRulePointEntity::Spawn();
|
||||
}
|
||||
|
||||
|
||||
void CGameScore::KeyValue( KeyValueData *pkvd )
|
||||
{
|
||||
if (FStrEq(pkvd->szKeyName, "points"))
|
||||
{
|
||||
SetPoints( atoi(pkvd->szValue) );
|
||||
pkvd->fHandled = TRUE;
|
||||
}
|
||||
else
|
||||
CRulePointEntity::KeyValue( pkvd );
|
||||
}
|
||||
|
||||
|
||||
|
||||
void CGameScore::Use( CBaseEntity *pActivator, CBaseEntity *pCaller, USE_TYPE useType, float value )
|
||||
{
|
||||
if ( !CanFireForActivator( pActivator ) )
|
||||
return;
|
||||
|
||||
// Only players can use this
|
||||
if ( pActivator->IsPlayer() )
|
||||
{
|
||||
if ( AwardToTeam() )
|
||||
{
|
||||
pActivator->AddPointsToTeam( Points(), AllowNegativeScore() );
|
||||
}
|
||||
else
|
||||
{
|
||||
pActivator->AddPoints( Points(), AllowNegativeScore() );
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
// CGameEnd / game_end -- Ends the game in MP
|
||||
|
||||
class CGameEnd : public CRulePointEntity
|
||||
{
|
||||
public:
|
||||
void Use( CBaseEntity *pActivator, CBaseEntity *pCaller, USE_TYPE useType, float value );
|
||||
private:
|
||||
};
|
||||
|
||||
LINK_ENTITY_TO_CLASS( game_end, CGameEnd );
|
||||
|
||||
|
||||
void CGameEnd::Use( CBaseEntity *pActivator, CBaseEntity *pCaller, USE_TYPE useType, float value )
|
||||
{
|
||||
if ( !CanFireForActivator( pActivator ) )
|
||||
return;
|
||||
|
||||
g_pGameRules->EndMultiplayerGame();
|
||||
}
|
||||
|
||||
|
||||
//
|
||||
// CGameText / game_text -- NON-Localized HUD Message (use env_message to display a titles.txt message)
|
||||
// Flag: All players SF_ENVTEXT_ALLPLAYERS
|
||||
//
|
||||
|
||||
|
||||
#define SF_ENVTEXT_ALLPLAYERS 0x0001
|
||||
|
||||
|
||||
class CGameText : public CRulePointEntity
|
||||
{
|
||||
public:
|
||||
void Use( CBaseEntity *pActivator, CBaseEntity *pCaller, USE_TYPE useType, float value );
|
||||
void KeyValue( KeyValueData *pkvd );
|
||||
|
||||
virtual int Save( CSave &save );
|
||||
virtual int Restore( CRestore &restore );
|
||||
static TYPEDESCRIPTION m_SaveData[];
|
||||
|
||||
inline BOOL MessageToAll( void ) { return (pev->spawnflags & SF_ENVTEXT_ALLPLAYERS); }
|
||||
inline void MessageSet( const char *pMessage ) { pev->message = ALLOC_STRING(pMessage); }
|
||||
inline const char *MessageGet( void ) { return STRING(pev->message); }
|
||||
|
||||
private:
|
||||
|
||||
hudtextparms_t m_textParms;
|
||||
};
|
||||
|
||||
LINK_ENTITY_TO_CLASS( game_text, CGameText );
|
||||
|
||||
// Save parms as a block. Will break save/restore if the structure changes, but this entity didn't ship with Half-Life, so
|
||||
// it can't impact saved Half-Life games.
|
||||
TYPEDESCRIPTION CGameText::m_SaveData[] =
|
||||
{
|
||||
DEFINE_ARRAY( CGameText, m_textParms, FIELD_CHARACTER, sizeof(hudtextparms_t) ),
|
||||
};
|
||||
|
||||
IMPLEMENT_SAVERESTORE( CGameText, CRulePointEntity );
|
||||
|
||||
|
||||
void CGameText::KeyValue( KeyValueData *pkvd )
|
||||
{
|
||||
if (FStrEq(pkvd->szKeyName, "channel"))
|
||||
{
|
||||
m_textParms.channel = atoi( pkvd->szValue );
|
||||
pkvd->fHandled = TRUE;
|
||||
}
|
||||
else if (FStrEq(pkvd->szKeyName, "x"))
|
||||
{
|
||||
m_textParms.x = atof( pkvd->szValue );
|
||||
pkvd->fHandled = TRUE;
|
||||
}
|
||||
else if (FStrEq(pkvd->szKeyName, "y"))
|
||||
{
|
||||
m_textParms.y = atof( pkvd->szValue );
|
||||
pkvd->fHandled = TRUE;
|
||||
}
|
||||
else if (FStrEq(pkvd->szKeyName, "effect"))
|
||||
{
|
||||
m_textParms.effect = atoi( pkvd->szValue );
|
||||
pkvd->fHandled = TRUE;
|
||||
}
|
||||
else if (FStrEq(pkvd->szKeyName, "color"))
|
||||
{
|
||||
int color[4];
|
||||
UTIL_StringToIntArray( color, 4, pkvd->szValue );
|
||||
m_textParms.r1 = color[0];
|
||||
m_textParms.g1 = color[1];
|
||||
m_textParms.b1 = color[2];
|
||||
m_textParms.a1 = color[3];
|
||||
pkvd->fHandled = TRUE;
|
||||
}
|
||||
else if (FStrEq(pkvd->szKeyName, "color2"))
|
||||
{
|
||||
int color[4];
|
||||
UTIL_StringToIntArray( color, 4, pkvd->szValue );
|
||||
m_textParms.r2 = color[0];
|
||||
m_textParms.g2 = color[1];
|
||||
m_textParms.b2 = color[2];
|
||||
m_textParms.a2 = color[3];
|
||||
pkvd->fHandled = TRUE;
|
||||
}
|
||||
else if (FStrEq(pkvd->szKeyName, "fadein"))
|
||||
{
|
||||
m_textParms.fadeinTime = atof( pkvd->szValue );
|
||||
pkvd->fHandled = TRUE;
|
||||
}
|
||||
else if (FStrEq(pkvd->szKeyName, "fadeout"))
|
||||
{
|
||||
m_textParms.fadeoutTime = atof( pkvd->szValue );
|
||||
pkvd->fHandled = TRUE;
|
||||
}
|
||||
else if (FStrEq(pkvd->szKeyName, "holdtime"))
|
||||
{
|
||||
m_textParms.holdTime = atof( pkvd->szValue );
|
||||
pkvd->fHandled = TRUE;
|
||||
}
|
||||
else if (FStrEq(pkvd->szKeyName, "fxtime"))
|
||||
{
|
||||
m_textParms.fxTime = atof( pkvd->szValue );
|
||||
pkvd->fHandled = TRUE;
|
||||
}
|
||||
else
|
||||
CRulePointEntity::KeyValue( pkvd );
|
||||
}
|
||||
|
||||
|
||||
void CGameText::Use( CBaseEntity *pActivator, CBaseEntity *pCaller, USE_TYPE useType, float value )
|
||||
{
|
||||
if ( !CanFireForActivator( pActivator ) )
|
||||
return;
|
||||
|
||||
if ( MessageToAll() )
|
||||
{
|
||||
UTIL_HudMessageAll( m_textParms, MessageGet() );
|
||||
}
|
||||
else
|
||||
{
|
||||
if ( pActivator->IsNetClient() )
|
||||
{
|
||||
UTIL_HudMessage( pActivator, m_textParms, MessageGet() );
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
//
|
||||
// CGameTeamMaster / game_team_master -- "Masters" like multisource, but based on the team of the activator
|
||||
// Only allows mastered entity to fire if the team matches my team
|
||||
//
|
||||
// team index (pulled from server team list "mp_teamlist"
|
||||
// Flag: Remove on Fire
|
||||
// Flag: Any team until set? -- Any team can use this until the team is set (otherwise no teams can use it)
|
||||
//
|
||||
|
||||
#define SF_TEAMMASTER_FIREONCE 0x0001
|
||||
#define SF_TEAMMASTER_ANYTEAM 0x0002
|
||||
|
||||
class CGameTeamMaster : public CRulePointEntity
|
||||
{
|
||||
public:
|
||||
void KeyValue( KeyValueData *pkvd );
|
||||
void Use( CBaseEntity *pActivator, CBaseEntity *pCaller, USE_TYPE useType, float value );
|
||||
int ObjectCaps( void ) { return CRulePointEntity:: ObjectCaps() | FCAP_MASTER; }
|
||||
|
||||
BOOL IsTriggered( CBaseEntity *pActivator );
|
||||
const char *TeamID( void );
|
||||
inline BOOL RemoveOnFire( void ) { return (pev->spawnflags & SF_TEAMMASTER_FIREONCE) ? TRUE : FALSE; }
|
||||
inline BOOL AnyTeam( void ) { return (pev->spawnflags & SF_TEAMMASTER_ANYTEAM) ? TRUE : FALSE; }
|
||||
|
||||
private:
|
||||
BOOL TeamMatch( CBaseEntity *pActivator );
|
||||
|
||||
int m_teamIndex;
|
||||
USE_TYPE triggerType;
|
||||
};
|
||||
|
||||
LINK_ENTITY_TO_CLASS( game_team_master, CGameTeamMaster );
|
||||
|
||||
void CGameTeamMaster::KeyValue( KeyValueData *pkvd )
|
||||
{
|
||||
if (FStrEq(pkvd->szKeyName, "teamindex"))
|
||||
{
|
||||
m_teamIndex = atoi( pkvd->szValue );
|
||||
pkvd->fHandled = TRUE;
|
||||
}
|
||||
else if (FStrEq(pkvd->szKeyName, "triggerstate"))
|
||||
{
|
||||
int type = atoi( pkvd->szValue );
|
||||
switch( type )
|
||||
{
|
||||
case 0:
|
||||
triggerType = USE_OFF;
|
||||
break;
|
||||
case 2:
|
||||
triggerType = USE_TOGGLE;
|
||||
break;
|
||||
default:
|
||||
triggerType = USE_ON;
|
||||
break;
|
||||
}
|
||||
pkvd->fHandled = TRUE;
|
||||
}
|
||||
else
|
||||
CRulePointEntity::KeyValue( pkvd );
|
||||
}
|
||||
|
||||
|
||||
void CGameTeamMaster::Use( CBaseEntity *pActivator, CBaseEntity *pCaller, USE_TYPE useType, float value )
|
||||
{
|
||||
if ( !CanFireForActivator( pActivator ) )
|
||||
return;
|
||||
|
||||
if ( useType == USE_SET )
|
||||
{
|
||||
if ( value < 0 )
|
||||
{
|
||||
m_teamIndex = -1;
|
||||
}
|
||||
else
|
||||
{
|
||||
m_teamIndex = g_pGameRules->GetTeamIndex( pActivator->TeamID() );
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
||||
if ( TeamMatch( pActivator ) )
|
||||
{
|
||||
SUB_UseTargets( pActivator, triggerType, value );
|
||||
if ( RemoveOnFire() )
|
||||
UTIL_Remove( this );
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
BOOL CGameTeamMaster::IsTriggered( CBaseEntity *pActivator )
|
||||
{
|
||||
return TeamMatch( pActivator );
|
||||
}
|
||||
|
||||
|
||||
const char *CGameTeamMaster::TeamID( void )
|
||||
{
|
||||
if ( m_teamIndex < 0 ) // Currently set to "no team"
|
||||
return "";
|
||||
|
||||
return g_pGameRules->GetIndexedTeamName( m_teamIndex ); // UNDONE: Fill this in with the team from the "teamlist"
|
||||
}
|
||||
|
||||
|
||||
BOOL CGameTeamMaster::TeamMatch( CBaseEntity *pActivator )
|
||||
{
|
||||
if ( m_teamIndex < 0 && AnyTeam() )
|
||||
return TRUE;
|
||||
|
||||
if ( !pActivator )
|
||||
return FALSE;
|
||||
|
||||
return UTIL_TeamsMatch( pActivator->TeamID(), TeamID() );
|
||||
}
|
||||
|
||||
|
||||
//
|
||||
// CGameTeamSet / game_team_set -- Changes the team of the entity it targets to the activator's team
|
||||
// Flag: Fire once
|
||||
// Flag: Clear team -- Sets the team to "NONE" instead of activator
|
||||
|
||||
#define SF_TEAMSET_FIREONCE 0x0001
|
||||
#define SF_TEAMSET_CLEARTEAM 0x0002
|
||||
|
||||
class CGameTeamSet : public CRulePointEntity
|
||||
{
|
||||
public:
|
||||
void Use( CBaseEntity *pActivator, CBaseEntity *pCaller, USE_TYPE useType, float value );
|
||||
inline BOOL RemoveOnFire( void ) { return (pev->spawnflags & SF_TEAMSET_FIREONCE) ? TRUE : FALSE; }
|
||||
inline BOOL ShouldClearTeam( void ) { return (pev->spawnflags & SF_TEAMSET_CLEARTEAM) ? TRUE : FALSE; }
|
||||
|
||||
private:
|
||||
};
|
||||
|
||||
LINK_ENTITY_TO_CLASS( game_team_set, CGameTeamSet );
|
||||
|
||||
|
||||
void CGameTeamSet::Use( CBaseEntity *pActivator, CBaseEntity *pCaller, USE_TYPE useType, float value )
|
||||
{
|
||||
if ( !CanFireForActivator( pActivator ) )
|
||||
return;
|
||||
|
||||
if ( ShouldClearTeam() )
|
||||
{
|
||||
SUB_UseTargets( pActivator, USE_SET, -1 );
|
||||
}
|
||||
else
|
||||
{
|
||||
SUB_UseTargets( pActivator, USE_SET, 0 );
|
||||
}
|
||||
|
||||
if ( RemoveOnFire() )
|
||||
{
|
||||
UTIL_Remove( this );
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
//
|
||||
// CGamePlayerZone / game_player_zone -- players in the zone fire my target when I'm fired
|
||||
//
|
||||
// Needs master?
|
||||
class CGamePlayerZone : public CRuleBrushEntity
|
||||
{
|
||||
public:
|
||||
void KeyValue( KeyValueData *pkvd );
|
||||
void Use( CBaseEntity *pActivator, CBaseEntity *pCaller, USE_TYPE useType, float value );
|
||||
|
||||
virtual int Save( CSave &save );
|
||||
virtual int Restore( CRestore &restore );
|
||||
static TYPEDESCRIPTION m_SaveData[];
|
||||
|
||||
private:
|
||||
string_t m_iszInTarget;
|
||||
string_t m_iszOutTarget;
|
||||
string_t m_iszInCount;
|
||||
string_t m_iszOutCount;
|
||||
};
|
||||
|
||||
LINK_ENTITY_TO_CLASS( game_zone_player, CGamePlayerZone );
|
||||
TYPEDESCRIPTION CGamePlayerZone::m_SaveData[] =
|
||||
{
|
||||
DEFINE_FIELD( CGamePlayerZone, m_iszInTarget, FIELD_STRING ),
|
||||
DEFINE_FIELD( CGamePlayerZone, m_iszOutTarget, FIELD_STRING ),
|
||||
DEFINE_FIELD( CGamePlayerZone, m_iszInCount, FIELD_STRING ),
|
||||
DEFINE_FIELD( CGamePlayerZone, m_iszOutCount, FIELD_STRING ),
|
||||
};
|
||||
|
||||
IMPLEMENT_SAVERESTORE( CGamePlayerZone, CRuleBrushEntity );
|
||||
|
||||
void CGamePlayerZone::KeyValue( KeyValueData *pkvd )
|
||||
{
|
||||
if (FStrEq(pkvd->szKeyName, "intarget"))
|
||||
{
|
||||
m_iszInTarget = ALLOC_STRING( pkvd->szValue );
|
||||
pkvd->fHandled = TRUE;
|
||||
}
|
||||
else if (FStrEq(pkvd->szKeyName, "outtarget"))
|
||||
{
|
||||
m_iszOutTarget = ALLOC_STRING( pkvd->szValue );
|
||||
pkvd->fHandled = TRUE;
|
||||
}
|
||||
else if (FStrEq(pkvd->szKeyName, "incount"))
|
||||
{
|
||||
m_iszInCount = ALLOC_STRING( pkvd->szValue );
|
||||
pkvd->fHandled = TRUE;
|
||||
}
|
||||
else if (FStrEq(pkvd->szKeyName, "outcount"))
|
||||
{
|
||||
m_iszOutCount = ALLOC_STRING( pkvd->szValue );
|
||||
pkvd->fHandled = TRUE;
|
||||
}
|
||||
else
|
||||
CRuleBrushEntity::KeyValue( pkvd );
|
||||
}
|
||||
|
||||
void CGamePlayerZone::Use( CBaseEntity *pActivator, CBaseEntity *pCaller, USE_TYPE useType, float value )
|
||||
{
|
||||
int playersInCount = 0;
|
||||
int playersOutCount = 0;
|
||||
|
||||
if ( !CanFireForActivator( pActivator ) )
|
||||
return;
|
||||
|
||||
CBaseEntity *pPlayer = NULL;
|
||||
|
||||
for ( int i = 1; i <= gpGlobals->maxClients; i++ )
|
||||
{
|
||||
pPlayer = UTIL_PlayerByIndex( i );
|
||||
if ( pPlayer )
|
||||
{
|
||||
TraceResult trace;
|
||||
int hullNumber;
|
||||
|
||||
hullNumber = human_hull;
|
||||
if ( pPlayer->pev->flags & FL_DUCKING )
|
||||
{
|
||||
hullNumber = head_hull;
|
||||
}
|
||||
|
||||
UTIL_TraceModel( pPlayer->pev->origin, pPlayer->pev->origin, hullNumber, edict(), &trace );
|
||||
|
||||
if ( trace.fStartSolid )
|
||||
{
|
||||
playersInCount++;
|
||||
if ( m_iszInTarget )
|
||||
{
|
||||
FireTargets( STRING(m_iszInTarget), pPlayer, pActivator, useType, value );
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
playersOutCount++;
|
||||
if ( m_iszOutTarget )
|
||||
{
|
||||
FireTargets( STRING(m_iszOutTarget), pPlayer, pActivator, useType, value );
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if ( m_iszInCount )
|
||||
{
|
||||
FireTargets( STRING(m_iszInCount), pActivator, this, USE_SET, playersInCount );
|
||||
}
|
||||
|
||||
if ( m_iszOutCount )
|
||||
{
|
||||
FireTargets( STRING(m_iszOutCount), pActivator, this, USE_SET, playersOutCount );
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
//
|
||||
// CGamePlayerHurt / game_player_hurt -- Damages the player who fires it
|
||||
// Flag: Fire once
|
||||
|
||||
#define SF_PKILL_FIREONCE 0x0001
|
||||
class CGamePlayerHurt : public CRulePointEntity
|
||||
{
|
||||
public:
|
||||
void Use( CBaseEntity *pActivator, CBaseEntity *pCaller, USE_TYPE useType, float value );
|
||||
inline BOOL RemoveOnFire( void ) { return (pev->spawnflags & SF_PKILL_FIREONCE) ? TRUE : FALSE; }
|
||||
|
||||
private:
|
||||
};
|
||||
|
||||
LINK_ENTITY_TO_CLASS( game_player_hurt, CGamePlayerHurt );
|
||||
|
||||
|
||||
void CGamePlayerHurt::Use( CBaseEntity *pActivator, CBaseEntity *pCaller, USE_TYPE useType, float value )
|
||||
{
|
||||
if ( !CanFireForActivator( pActivator ) )
|
||||
return;
|
||||
|
||||
if ( pActivator->IsPlayer() )
|
||||
{
|
||||
if ( pev->dmg < 0 )
|
||||
pActivator->TakeHealth( -pev->dmg, DMG_GENERIC );
|
||||
else
|
||||
pActivator->TakeDamage( pev, pev, pev->dmg, DMG_GENERIC );
|
||||
}
|
||||
|
||||
SUB_UseTargets( pActivator, useType, value );
|
||||
|
||||
if ( RemoveOnFire() )
|
||||
{
|
||||
UTIL_Remove( this );
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
//
|
||||
// CGameCounter / game_counter -- Counts events and fires target
|
||||
// Flag: Fire once
|
||||
// Flag: Reset on Fire
|
||||
|
||||
#define SF_GAMECOUNT_FIREONCE 0x0001
|
||||
#define SF_GAMECOUNT_RESET 0x0002
|
||||
|
||||
class CGameCounter : public CRulePointEntity
|
||||
{
|
||||
public:
|
||||
void Spawn( void );
|
||||
void Use( CBaseEntity *pActivator, CBaseEntity *pCaller, USE_TYPE useType, float value );
|
||||
inline BOOL RemoveOnFire( void ) { return (pev->spawnflags & SF_GAMECOUNT_FIREONCE) ? TRUE : FALSE; }
|
||||
inline BOOL ResetOnFire( void ) { return (pev->spawnflags & SF_GAMECOUNT_RESET) ? TRUE : FALSE; }
|
||||
|
||||
inline void CountUp( void ) { pev->frags++; }
|
||||
inline void CountDown( void ) { pev->frags--; }
|
||||
inline void ResetCount( void ) { pev->frags = pev->dmg; }
|
||||
inline int CountValue( void ) { return pev->frags; }
|
||||
inline int LimitValue( void ) { return pev->health; }
|
||||
|
||||
inline BOOL HitLimit( void ) { return CountValue() == LimitValue(); }
|
||||
|
||||
private:
|
||||
|
||||
inline void SetCountValue( int value ) { pev->frags = value; }
|
||||
inline void SetInitialValue( int value ) { pev->dmg = value; }
|
||||
};
|
||||
|
||||
LINK_ENTITY_TO_CLASS( game_counter, CGameCounter );
|
||||
|
||||
void CGameCounter::Spawn( void )
|
||||
{
|
||||
// Save off the initial count
|
||||
SetInitialValue( CountValue() );
|
||||
CRulePointEntity::Spawn();
|
||||
}
|
||||
|
||||
|
||||
void CGameCounter::Use( CBaseEntity *pActivator, CBaseEntity *pCaller, USE_TYPE useType, float value )
|
||||
{
|
||||
if ( !CanFireForActivator( pActivator ) )
|
||||
return;
|
||||
|
||||
switch( useType )
|
||||
{
|
||||
case USE_ON:
|
||||
case USE_TOGGLE:
|
||||
CountUp();
|
||||
break;
|
||||
|
||||
case USE_OFF:
|
||||
CountDown();
|
||||
break;
|
||||
|
||||
case USE_SET:
|
||||
SetCountValue( (int)value );
|
||||
break;
|
||||
}
|
||||
|
||||
if ( HitLimit() )
|
||||
{
|
||||
SUB_UseTargets( pActivator, USE_TOGGLE, 0 );
|
||||
if ( RemoveOnFire() )
|
||||
{
|
||||
UTIL_Remove( this );
|
||||
}
|
||||
|
||||
if ( ResetOnFire() )
|
||||
{
|
||||
ResetCount();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
//
|
||||
// CGameCounterSet / game_counter_set -- Sets the counter's value
|
||||
// Flag: Fire once
|
||||
|
||||
#define SF_GAMECOUNTSET_FIREONCE 0x0001
|
||||
|
||||
class CGameCounterSet : public CRulePointEntity
|
||||
{
|
||||
public:
|
||||
void Use( CBaseEntity *pActivator, CBaseEntity *pCaller, USE_TYPE useType, float value );
|
||||
inline BOOL RemoveOnFire( void ) { return (pev->spawnflags & SF_GAMECOUNTSET_FIREONCE) ? TRUE : FALSE; }
|
||||
|
||||
private:
|
||||
};
|
||||
|
||||
LINK_ENTITY_TO_CLASS( game_counter_set, CGameCounterSet );
|
||||
|
||||
|
||||
void CGameCounterSet::Use( CBaseEntity *pActivator, CBaseEntity *pCaller, USE_TYPE useType, float value )
|
||||
{
|
||||
if ( !CanFireForActivator( pActivator ) )
|
||||
return;
|
||||
|
||||
SUB_UseTargets( pActivator, USE_SET, pev->frags );
|
||||
|
||||
if ( RemoveOnFire() )
|
||||
{
|
||||
UTIL_Remove( this );
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
//
|
||||
// CGamePlayerEquip / game_playerequip -- Sets the default player equipment
|
||||
// Flag: USE Only
|
||||
|
||||
#define SF_PLAYEREQUIP_USEONLY 0x0001
|
||||
#define MAX_EQUIP 32
|
||||
|
||||
class CGamePlayerEquip : public CRulePointEntity
|
||||
{
|
||||
public:
|
||||
void KeyValue( KeyValueData *pkvd );
|
||||
void Touch( CBaseEntity *pOther );
|
||||
void Use( CBaseEntity *pActivator, CBaseEntity *pCaller, USE_TYPE useType, float value );
|
||||
|
||||
inline BOOL UseOnly( void ) { return (pev->spawnflags & SF_PLAYEREQUIP_USEONLY) ? TRUE : FALSE; }
|
||||
|
||||
private:
|
||||
|
||||
void EquipPlayer( CBaseEntity *pPlayer );
|
||||
|
||||
string_t m_weaponNames[MAX_EQUIP];
|
||||
int m_weaponCount[MAX_EQUIP];
|
||||
};
|
||||
|
||||
LINK_ENTITY_TO_CLASS( game_player_equip, CGamePlayerEquip );
|
||||
|
||||
|
||||
void CGamePlayerEquip::KeyValue( KeyValueData *pkvd )
|
||||
{
|
||||
CRulePointEntity::KeyValue( pkvd );
|
||||
|
||||
if ( !pkvd->fHandled )
|
||||
{
|
||||
for ( int i = 0; i < MAX_EQUIP; i++ )
|
||||
{
|
||||
if ( !m_weaponNames[i] )
|
||||
{
|
||||
char tmp[128];
|
||||
|
||||
UTIL_StripToken( pkvd->szKeyName, tmp );
|
||||
|
||||
m_weaponNames[i] = ALLOC_STRING(tmp);
|
||||
m_weaponCount[i] = atoi(pkvd->szValue);
|
||||
m_weaponCount[i] = max(1,m_weaponCount[i]);
|
||||
pkvd->fHandled = TRUE;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
void CGamePlayerEquip::Touch( CBaseEntity *pOther )
|
||||
{
|
||||
if ( !CanFireForActivator( pOther ) )
|
||||
return;
|
||||
|
||||
if ( UseOnly() )
|
||||
return;
|
||||
|
||||
EquipPlayer( pOther );
|
||||
}
|
||||
|
||||
void CGamePlayerEquip::EquipPlayer( CBaseEntity *pEntity )
|
||||
{
|
||||
CBasePlayer *pPlayer = NULL;
|
||||
|
||||
if ( pEntity->IsPlayer() )
|
||||
{
|
||||
pPlayer = (CBasePlayer *)pEntity;
|
||||
}
|
||||
|
||||
if ( !pPlayer )
|
||||
return;
|
||||
|
||||
for ( int i = 0; i < MAX_EQUIP; i++ )
|
||||
{
|
||||
if ( !m_weaponNames[i] )
|
||||
break;
|
||||
for ( int j = 0; j < m_weaponCount[i]; j++ )
|
||||
{
|
||||
pPlayer->GiveNamedItem( STRING(m_weaponNames[i]) );
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
void CGamePlayerEquip::Use( CBaseEntity *pActivator, CBaseEntity *pCaller, USE_TYPE useType, float value )
|
||||
{
|
||||
EquipPlayer( pActivator );
|
||||
}
|
||||
|
||||
|
||||
//
|
||||
// CGamePlayerTeam / game_player_team -- Changes the team of the player who fired it
|
||||
// Flag: Fire once
|
||||
// Flag: Kill Player
|
||||
// Flag: Gib Player
|
||||
|
||||
#define SF_PTEAM_FIREONCE 0x0001
|
||||
#define SF_PTEAM_KILL 0x0002
|
||||
#define SF_PTEAM_GIB 0x0004
|
||||
|
||||
class CGamePlayerTeam : public CRulePointEntity
|
||||
{
|
||||
public:
|
||||
void Use( CBaseEntity *pActivator, CBaseEntity *pCaller, USE_TYPE useType, float value );
|
||||
|
||||
private:
|
||||
|
||||
inline BOOL RemoveOnFire( void ) { return (pev->spawnflags & SF_PTEAM_FIREONCE) ? TRUE : FALSE; }
|
||||
inline BOOL ShouldKillPlayer( void ) { return (pev->spawnflags & SF_PTEAM_KILL) ? TRUE : FALSE; }
|
||||
inline BOOL ShouldGibPlayer( void ) { return (pev->spawnflags & SF_PTEAM_GIB) ? TRUE : FALSE; }
|
||||
|
||||
const char *TargetTeamName( const char *pszTargetName );
|
||||
};
|
||||
|
||||
LINK_ENTITY_TO_CLASS( game_player_team, CGamePlayerTeam );
|
||||
|
||||
|
||||
const char *CGamePlayerTeam::TargetTeamName( const char *pszTargetName )
|
||||
{
|
||||
CBaseEntity *pTeamEntity = NULL;
|
||||
|
||||
while ((pTeamEntity = UTIL_FindEntityByTargetname( pTeamEntity, pszTargetName )) != NULL)
|
||||
{
|
||||
if ( FClassnameIs( pTeamEntity->pev, "game_team_master" ) )
|
||||
return pTeamEntity->TeamID();
|
||||
}
|
||||
|
||||
return NULL;
|
||||
}
|
||||
|
||||
|
||||
void CGamePlayerTeam::Use( CBaseEntity *pActivator, CBaseEntity *pCaller, USE_TYPE useType, float value )
|
||||
{
|
||||
if ( !CanFireForActivator( pActivator ) )
|
||||
return;
|
||||
|
||||
if ( pActivator->IsPlayer() )
|
||||
{
|
||||
const char *pszTargetTeam = TargetTeamName( STRING(pev->target) );
|
||||
if ( pszTargetTeam )
|
||||
{
|
||||
CBasePlayer *pPlayer = (CBasePlayer *)pActivator;
|
||||
g_pGameRules->ChangePlayerTeam( pPlayer, pszTargetTeam, ShouldKillPlayer(), ShouldGibPlayer() );
|
||||
}
|
||||
}
|
||||
|
||||
if ( RemoveOnFire() )
|
||||
{
|
||||
UTIL_Remove( this );
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -0,0 +1,22 @@
|
|||
/***
|
||||
*
|
||||
* 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.
|
||||
*
|
||||
****/
|
||||
|
||||
#ifndef MAPRULES_H
|
||||
#define MAPRULES_H
|
||||
|
||||
|
||||
|
||||
#endif // MAPRULES_H
|
||||
|
|
@ -0,0 +1,34 @@
|
|||
/***
|
||||
*
|
||||
* 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.
|
||||
*
|
||||
****/
|
||||
#ifndef MONSTEREVENT_H
|
||||
#define MONSTEREVENT_H
|
||||
|
||||
typedef struct
|
||||
{
|
||||
int event;
|
||||
char *options;
|
||||
} MonsterEvent_t;
|
||||
|
||||
#define EVENT_SPECIFIC 0
|
||||
#define EVENT_SCRIPTED 1000
|
||||
#define EVENT_SHARED 2000
|
||||
#define EVENT_CLIENT 5000
|
||||
|
||||
#define MONSTER_EVENT_BODYDROP_LIGHT 2001
|
||||
#define MONSTER_EVENT_BODYDROP_HEAVY 2002
|
||||
|
||||
#define MONSTER_EVENT_SWISHSOUND 2010
|
||||
|
||||
#endif // MONSTEREVENT_H
|
|
@ -0,0 +1,292 @@
|
|||
/***
|
||||
*
|
||||
* 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.
|
||||
*
|
||||
****/
|
||||
//=========================================================
|
||||
// Monster Maker - this is an entity that creates monsters
|
||||
// in the game.
|
||||
//=========================================================
|
||||
|
||||
#include "extdll.h"
|
||||
#include "util.h"
|
||||
#include "cbase.h"
|
||||
#include "monsters.h"
|
||||
#include "saverestore.h"
|
||||
|
||||
// Monstermaker spawnflags
|
||||
#define SF_MONSTERMAKER_START_ON 1 // start active ( if has targetname )
|
||||
#define SF_MONSTERMAKER_CYCLIC 4 // drop one monster every time fired.
|
||||
#define SF_MONSTERMAKER_MONSTERCLIP 8 // Children are blocked by monsterclip
|
||||
|
||||
//=========================================================
|
||||
// MonsterMaker - this ent creates monsters during the game.
|
||||
//=========================================================
|
||||
class CMonsterMaker : public CBaseMonster
|
||||
{
|
||||
public:
|
||||
void Spawn( void );
|
||||
void Precache( void );
|
||||
void KeyValue( KeyValueData* pkvd);
|
||||
void EXPORT ToggleUse ( CBaseEntity *pActivator, CBaseEntity *pCaller, USE_TYPE useType, float value );
|
||||
void EXPORT CyclicUse ( CBaseEntity *pActivator, CBaseEntity *pCaller, USE_TYPE useType, float value );
|
||||
void EXPORT MakerThink ( void );
|
||||
void DeathNotice ( entvars_t *pevChild );// monster maker children use this to tell the monster maker that they have died.
|
||||
void MakeMonster( void );
|
||||
|
||||
virtual int Save( CSave &save );
|
||||
virtual int Restore( CRestore &restore );
|
||||
|
||||
static TYPEDESCRIPTION m_SaveData[];
|
||||
|
||||
string_t m_iszMonsterClassname;// classname of the monster(s) that will be created.
|
||||
|
||||
int m_cNumMonsters;// max number of monsters this ent can create
|
||||
|
||||
|
||||
int m_cLiveChildren;// how many monsters made by this monster maker that are currently alive
|
||||
int m_iMaxLiveChildren;// max number of monsters that this maker may have out at one time.
|
||||
|
||||
float m_flGround; // z coord of the ground under me, used to make sure no monsters are under the maker when it drops a new child
|
||||
|
||||
BOOL m_fActive;
|
||||
BOOL m_fFadeChildren;// should we make the children fadeout?
|
||||
};
|
||||
|
||||
LINK_ENTITY_TO_CLASS( monstermaker, CMonsterMaker );
|
||||
|
||||
TYPEDESCRIPTION CMonsterMaker::m_SaveData[] =
|
||||
{
|
||||
DEFINE_FIELD( CMonsterMaker, m_iszMonsterClassname, FIELD_STRING ),
|
||||
DEFINE_FIELD( CMonsterMaker, m_cNumMonsters, FIELD_INTEGER ),
|
||||
DEFINE_FIELD( CMonsterMaker, m_cLiveChildren, FIELD_INTEGER ),
|
||||
DEFINE_FIELD( CMonsterMaker, m_flGround, FIELD_FLOAT ),
|
||||
DEFINE_FIELD( CMonsterMaker, m_iMaxLiveChildren, FIELD_INTEGER ),
|
||||
DEFINE_FIELD( CMonsterMaker, m_fActive, FIELD_BOOLEAN ),
|
||||
DEFINE_FIELD( CMonsterMaker, m_fFadeChildren, FIELD_BOOLEAN ),
|
||||
};
|
||||
|
||||
|
||||
IMPLEMENT_SAVERESTORE( CMonsterMaker, CBaseMonster );
|
||||
|
||||
void CMonsterMaker :: KeyValue( KeyValueData *pkvd )
|
||||
{
|
||||
|
||||
if ( FStrEq(pkvd->szKeyName, "monstercount") )
|
||||
{
|
||||
m_cNumMonsters = atoi(pkvd->szValue);
|
||||
pkvd->fHandled = TRUE;
|
||||
}
|
||||
else if ( FStrEq(pkvd->szKeyName, "m_imaxlivechildren") )
|
||||
{
|
||||
m_iMaxLiveChildren = atoi(pkvd->szValue);
|
||||
pkvd->fHandled = TRUE;
|
||||
}
|
||||
else if ( FStrEq(pkvd->szKeyName, "monstertype") )
|
||||
{
|
||||
m_iszMonsterClassname = ALLOC_STRING( pkvd->szValue );
|
||||
pkvd->fHandled = TRUE;
|
||||
}
|
||||
else
|
||||
CBaseMonster::KeyValue( pkvd );
|
||||
}
|
||||
|
||||
|
||||
void CMonsterMaker :: Spawn( )
|
||||
{
|
||||
pev->solid = SOLID_NOT;
|
||||
|
||||
m_cLiveChildren = 0;
|
||||
Precache();
|
||||
if ( !FStringNull ( pev->targetname ) )
|
||||
{
|
||||
if ( pev->spawnflags & SF_MONSTERMAKER_CYCLIC )
|
||||
{
|
||||
SetUse ( CyclicUse );// drop one monster each time we fire
|
||||
}
|
||||
else
|
||||
{
|
||||
SetUse ( ToggleUse );// so can be turned on/off
|
||||
}
|
||||
|
||||
if ( FBitSet ( pev->spawnflags, SF_MONSTERMAKER_START_ON ) )
|
||||
{// start making monsters as soon as monstermaker spawns
|
||||
m_fActive = TRUE;
|
||||
SetThink ( MakerThink );
|
||||
}
|
||||
else
|
||||
{// wait to be activated.
|
||||
m_fActive = FALSE;
|
||||
SetThink ( SUB_DoNothing );
|
||||
}
|
||||
}
|
||||
else
|
||||
{// no targetname, just start.
|
||||
pev->nextthink = gpGlobals->time + m_flDelay;
|
||||
m_fActive = TRUE;
|
||||
SetThink ( MakerThink );
|
||||
}
|
||||
|
||||
if ( m_cNumMonsters == 1 )
|
||||
{
|
||||
m_fFadeChildren = FALSE;
|
||||
}
|
||||
else
|
||||
{
|
||||
m_fFadeChildren = TRUE;
|
||||
}
|
||||
|
||||
m_flGround = 0;
|
||||
}
|
||||
|
||||
void CMonsterMaker :: Precache( void )
|
||||
{
|
||||
CBaseMonster::Precache();
|
||||
|
||||
UTIL_PrecacheOther( STRING( m_iszMonsterClassname ) );
|
||||
}
|
||||
|
||||
//=========================================================
|
||||
// MakeMonster- this is the code that drops the monster
|
||||
//=========================================================
|
||||
void CMonsterMaker::MakeMonster( void )
|
||||
{
|
||||
edict_t *pent;
|
||||
entvars_t *pevCreate;
|
||||
|
||||
if ( m_iMaxLiveChildren > 0 && m_cLiveChildren >= m_iMaxLiveChildren )
|
||||
{// not allowed to make a new one yet. Too many live ones out right now.
|
||||
return;
|
||||
}
|
||||
|
||||
if ( !m_flGround )
|
||||
{
|
||||
// set altitude. Now that I'm activated, any breakables, etc should be out from under me.
|
||||
TraceResult tr;
|
||||
|
||||
UTIL_TraceLine ( pev->origin, pev->origin - Vector ( 0, 0, 2048 ), ignore_monsters, ENT(pev), &tr );
|
||||
m_flGround = tr.vecEndPos.z;
|
||||
}
|
||||
|
||||
Vector mins = pev->origin - Vector( 34, 34, 0 );
|
||||
Vector maxs = pev->origin + Vector( 34, 34, 0 );
|
||||
maxs.z = pev->origin.z;
|
||||
mins.z = m_flGround;
|
||||
|
||||
CBaseEntity *pList[2];
|
||||
int count = UTIL_EntitiesInBox( pList, 2, mins, maxs, FL_CLIENT|FL_MONSTER );
|
||||
if ( count )
|
||||
{
|
||||
// don't build a stack of monsters!
|
||||
return;
|
||||
}
|
||||
|
||||
pent = CREATE_NAMED_ENTITY( m_iszMonsterClassname );
|
||||
|
||||
if ( FNullEnt( pent ) )
|
||||
{
|
||||
ALERT ( at_console, "NULL Ent in MonsterMaker!\n" );
|
||||
return;
|
||||
}
|
||||
|
||||
// If I have a target, fire!
|
||||
if ( !FStringNull ( pev->target ) )
|
||||
{
|
||||
// delay already overloaded for this entity, so can't call SUB_UseTargets()
|
||||
FireTargets( STRING(pev->target), this, this, USE_TOGGLE, 0 );
|
||||
}
|
||||
|
||||
pevCreate = VARS( pent );
|
||||
pevCreate->origin = pev->origin;
|
||||
pevCreate->angles = pev->angles;
|
||||
SetBits( pevCreate->spawnflags, SF_MONSTER_FALL_TO_GROUND );
|
||||
|
||||
// Children hit monsterclip brushes
|
||||
if ( pev->spawnflags & SF_MONSTERMAKER_MONSTERCLIP )
|
||||
SetBits( pevCreate->spawnflags, SF_MONSTER_HITMONSTERCLIP );
|
||||
|
||||
DispatchSpawn( ENT( pevCreate ) );
|
||||
pevCreate->owner = edict();
|
||||
|
||||
if ( !FStringNull( pev->netname ) )
|
||||
{
|
||||
// if I have a netname (overloaded), give the child monster that name as a targetname
|
||||
pevCreate->targetname = pev->netname;
|
||||
}
|
||||
|
||||
m_cLiveChildren++;// count this monster
|
||||
m_cNumMonsters--;
|
||||
|
||||
if ( m_cNumMonsters == 0 )
|
||||
{
|
||||
// Disable this forever. Don't kill it because it still gets death notices
|
||||
SetThink( NULL );
|
||||
SetUse( NULL );
|
||||
}
|
||||
}
|
||||
|
||||
//=========================================================
|
||||
// CyclicUse - drops one monster from the monstermaker
|
||||
// each time we call this.
|
||||
//=========================================================
|
||||
void CMonsterMaker::CyclicUse ( CBaseEntity *pActivator, CBaseEntity *pCaller, USE_TYPE useType, float value )
|
||||
{
|
||||
MakeMonster();
|
||||
}
|
||||
|
||||
//=========================================================
|
||||
// ToggleUse - activates/deactivates the monster maker
|
||||
//=========================================================
|
||||
void CMonsterMaker :: ToggleUse ( CBaseEntity *pActivator, CBaseEntity *pCaller, USE_TYPE useType, float value )
|
||||
{
|
||||
if ( !ShouldToggle( useType, m_fActive ) )
|
||||
return;
|
||||
|
||||
if ( m_fActive )
|
||||
{
|
||||
m_fActive = FALSE;
|
||||
SetThink ( NULL );
|
||||
}
|
||||
else
|
||||
{
|
||||
m_fActive = TRUE;
|
||||
SetThink ( MakerThink );
|
||||
}
|
||||
|
||||
pev->nextthink = gpGlobals->time;
|
||||
}
|
||||
|
||||
//=========================================================
|
||||
// MakerThink - creates a new monster every so often
|
||||
//=========================================================
|
||||
void CMonsterMaker :: MakerThink ( void )
|
||||
{
|
||||
pev->nextthink = gpGlobals->time + m_flDelay;
|
||||
|
||||
MakeMonster();
|
||||
}
|
||||
|
||||
|
||||
//=========================================================
|
||||
//=========================================================
|
||||
void CMonsterMaker :: DeathNotice ( entvars_t *pevChild )
|
||||
{
|
||||
// ok, we've gotten the deathnotice from our child, now clear out its owner if we don't want it to fade.
|
||||
m_cLiveChildren--;
|
||||
|
||||
if ( !m_fFadeChildren )
|
||||
{
|
||||
pevChild->owner = NULL;
|
||||
}
|
||||
}
|
||||
|
||||
|
File diff suppressed because it is too large
Load Diff
|
@ -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.
|
||||
*
|
||||
* 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 MONSTERS_H
|
||||
#include "skill.h"
|
||||
#define MONSTERS_H
|
||||
|
||||
/*
|
||||
|
||||
===== monsters.h ========================================================
|
||||
|
||||
Header file for monster-related utility code
|
||||
|
||||
*/
|
||||
|
||||
// CHECKLOCALMOVE result types
|
||||
#define LOCALMOVE_INVALID 0 // move is not possible
|
||||
#define LOCALMOVE_INVALID_DONT_TRIANGULATE 1 // move is not possible, don't try to triangulate
|
||||
#define LOCALMOVE_VALID 2 // move is possible
|
||||
|
||||
// Hit Group standards
|
||||
#define HITGROUP_GENERIC 0
|
||||
#define HITGROUP_HEAD 1
|
||||
#define HITGROUP_CHEST 2
|
||||
#define HITGROUP_STOMACH 3
|
||||
#define HITGROUP_LEFTARM 4
|
||||
#define HITGROUP_RIGHTARM 5
|
||||
#define HITGROUP_LEFTLEG 6
|
||||
#define HITGROUP_RIGHTLEG 7
|
||||
|
||||
|
||||
// Monster Spawnflags
|
||||
#define SF_MONSTER_WAIT_TILL_SEEN 1// spawnflag that makes monsters wait until player can see them before attacking.
|
||||
#define SF_MONSTER_GAG 2 // no idle noises from this monster
|
||||
#define SF_MONSTER_HITMONSTERCLIP 4
|
||||
// 8
|
||||
#define SF_MONSTER_PRISONER 16 // monster won't attack anyone, no one will attacke him.
|
||||
// 32
|
||||
// 64
|
||||
#define SF_MONSTER_WAIT_FOR_SCRIPT 128 //spawnflag that makes monsters wait to check for attacking until the script is done or they've been attacked
|
||||
#define SF_MONSTER_PREDISASTER 256 //this is a predisaster scientist or barney. Influences how they speak.
|
||||
#define SF_MONSTER_FADECORPSE 512 // Fade out corpse after death
|
||||
#define SF_MONSTER_FALL_TO_GROUND 0x80000000
|
||||
|
||||
// specialty spawnflags
|
||||
#define SF_MONSTER_TURRET_AUTOACTIVATE 32
|
||||
#define SF_MONSTER_TURRET_STARTINACTIVE 64
|
||||
#define SF_MONSTER_WAIT_UNTIL_PROVOKED 64 // don't attack the player unless provoked
|
||||
|
||||
|
||||
|
||||
// MoveToOrigin stuff
|
||||
#define MOVE_START_TURN_DIST 64 // when this far away from moveGoal, start turning to face next goal
|
||||
#define MOVE_STUCK_DIST 32 // if a monster can't step this far, it is stuck.
|
||||
|
||||
|
||||
// MoveToOrigin stuff
|
||||
#define MOVE_NORMAL 0// normal move in the direction monster is facing
|
||||
#define MOVE_STRAFE 1// moves in direction specified, no matter which way monster is facing
|
||||
|
||||
// spawn flags 256 and above are already taken by the engine
|
||||
extern void UTIL_MoveToOrigin( edict_t* pent, const Vector &vecGoal, float flDist, int iMoveType );
|
||||
|
||||
Vector VecCheckToss ( entvars_t *pev, const Vector &vecSpot1, Vector vecSpot2, float flGravityAdj = 1.0 );
|
||||
Vector VecCheckThrow ( entvars_t *pev, const Vector &vecSpot1, Vector vecSpot2, float flSpeed, float flGravityAdj = 1.0 );
|
||||
extern DLL_GLOBAL Vector g_vecAttackDir;
|
||||
extern DLL_GLOBAL CONSTANT float g_flMeleeRange;
|
||||
extern DLL_GLOBAL CONSTANT float g_flMediumRange;
|
||||
extern DLL_GLOBAL CONSTANT float g_flLongRange;
|
||||
extern void EjectBrass (const Vector &vecOrigin, const Vector &vecVelocity, float rotation, int model, int soundtype );
|
||||
extern void ExplodeModel( const Vector &vecOrigin, float speed, int model, int count );
|
||||
|
||||
BOOL FBoxVisible ( entvars_t *pevLooker, entvars_t *pevTarget );
|
||||
BOOL FBoxVisible ( entvars_t *pevLooker, entvars_t *pevTarget, Vector &vecTargetOrigin, float flSize = 0.0 );
|
||||
|
||||
// monster to monster relationship types
|
||||
#define R_AL -2 // (ALLY) pals. Good alternative to R_NO when applicable.
|
||||
#define R_FR -1// (FEAR)will run
|
||||
#define R_NO 0// (NO RELATIONSHIP) disregard
|
||||
#define R_DL 1// (DISLIKE) will attack
|
||||
#define R_HT 2// (HATE)will attack this character instead of any visible DISLIKEd characters
|
||||
#define R_NM 3// (NEMESIS) A monster Will ALWAYS attack its nemsis, no matter what
|
||||
|
||||
|
||||
// these bits represent the monster's memory
|
||||
#define MEMORY_CLEAR 0
|
||||
#define bits_MEMORY_PROVOKED ( 1 << 0 )// right now only used for houndeyes.
|
||||
#define bits_MEMORY_INCOVER ( 1 << 1 )// monster knows it is in a covered position.
|
||||
#define bits_MEMORY_SUSPICIOUS ( 1 << 2 )// Ally is suspicious of the player, and will move to provoked more easily
|
||||
#define bits_MEMORY_PATH_FINISHED ( 1 << 3 )// Finished monster path (just used by big momma for now)
|
||||
#define bits_MEMORY_ON_PATH ( 1 << 4 )// Moving on a path
|
||||
#define bits_MEMORY_MOVE_FAILED ( 1 << 5 )// Movement has already failed
|
||||
#define bits_MEMORY_FLINCHED ( 1 << 6 )// Has already flinched
|
||||
#define bits_MEMORY_KILLED ( 1 << 7 )// HACKHACK -- remember that I've already called my Killed()
|
||||
#define bits_MEMORY_CUSTOM4 ( 1 << 28 ) // Monster-specific memory
|
||||
#define bits_MEMORY_CUSTOM3 ( 1 << 29 ) // Monster-specific memory
|
||||
#define bits_MEMORY_CUSTOM2 ( 1 << 30 ) // Monster-specific memory
|
||||
#define bits_MEMORY_CUSTOM1 ( 1 << 31 ) // Monster-specific memory
|
||||
|
||||
// trigger conditions for scripted AI
|
||||
// these MUST match the CHOICES interface in halflife.fgd for the base monster
|
||||
enum
|
||||
{
|
||||
AITRIGGER_NONE = 0,
|
||||
AITRIGGER_SEEPLAYER_ANGRY_AT_PLAYER,
|
||||
AITRIGGER_TAKEDAMAGE,
|
||||
AITRIGGER_HALFHEALTH,
|
||||
AITRIGGER_DEATH,
|
||||
AITRIGGER_SQUADMEMBERDIE,
|
||||
AITRIGGER_SQUADLEADERDIE,
|
||||
AITRIGGER_HEARWORLD,
|
||||
AITRIGGER_HEARPLAYER,
|
||||
AITRIGGER_HEARCOMBAT,
|
||||
AITRIGGER_SEEPLAYER_UNCONDITIONAL,
|
||||
AITRIGGER_SEEPLAYER_NOT_IN_COMBAT,
|
||||
};
|
||||
/*
|
||||
0 : "No Trigger"
|
||||
1 : "See Player"
|
||||
2 : "Take Damage"
|
||||
3 : "50% Health Remaining"
|
||||
4 : "Death"
|
||||
5 : "Squad Member Dead"
|
||||
6 : "Squad Leader Dead"
|
||||
7 : "Hear World"
|
||||
8 : "Hear Player"
|
||||
9 : "Hear Combat"
|
||||
*/
|
||||
|
||||
//
|
||||
// A gib is a chunk of a body, or a piece of wood/metal/rocks/etc.
|
||||
//
|
||||
class CGib : public CBaseEntity
|
||||
{
|
||||
public:
|
||||
void Spawn( const char *szGibModel );
|
||||
void EXPORT BounceGibTouch ( CBaseEntity *pOther );
|
||||
void EXPORT StickyGibTouch ( CBaseEntity *pOther );
|
||||
void EXPORT WaitTillLand( void );
|
||||
void LimitVelocity( void );
|
||||
|
||||
virtual int ObjectCaps( void ) { return (CBaseEntity :: ObjectCaps() & ~FCAP_ACROSS_TRANSITION) | FCAP_DONT_SAVE; }
|
||||
static void SpawnHeadGib( entvars_t *pevVictim );
|
||||
static void SpawnRandomGibs( entvars_t *pevVictim, int cGibs, int human );
|
||||
static void SpawnStickyGibs( entvars_t *pevVictim, Vector vecOrigin, int cGibs );
|
||||
|
||||
int m_bloodColor;
|
||||
int m_cBloodDecals;
|
||||
int m_material;
|
||||
float m_lifeTime;
|
||||
};
|
||||
|
||||
|
||||
#define CUSTOM_SCHEDULES\
|
||||
virtual Schedule_t *ScheduleFromName( const char *pName );\
|
||||
static Schedule_t *m_scheduleList[];
|
||||
|
||||
#define DEFINE_CUSTOM_SCHEDULES(derivedClass)\
|
||||
Schedule_t *derivedClass::m_scheduleList[] =
|
||||
|
||||
#define IMPLEMENT_CUSTOM_SCHEDULES(derivedClass, baseClass)\
|
||||
Schedule_t *derivedClass::ScheduleFromName( const char *pName )\
|
||||
{\
|
||||
Schedule_t *pSchedule = ScheduleInList( pName, m_scheduleList, ARRAYSIZE(m_scheduleList) );\
|
||||
if ( !pSchedule )\
|
||||
return baseClass::ScheduleFromName(pName);\
|
||||
return pSchedule;\
|
||||
}
|
||||
|
||||
|
||||
|
||||
#endif //MONSTERS_H
|
|
@ -0,0 +1,234 @@
|
|||
/***
|
||||
*
|
||||
* 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.
|
||||
*
|
||||
****/
|
||||
//=========================================================
|
||||
// monsterstate.cpp - base class monster functions for
|
||||
// controlling core AI.
|
||||
//=========================================================
|
||||
|
||||
#include "extdll.h"
|
||||
#include "util.h"
|
||||
#include "cbase.h"
|
||||
#include "nodes.h"
|
||||
#include "monsters.h"
|
||||
#include "animation.h"
|
||||
#include "saverestore.h"
|
||||
#include "soundent.h"
|
||||
|
||||
//=========================================================
|
||||
// SetState
|
||||
//=========================================================
|
||||
void CBaseMonster :: SetState ( MONSTERSTATE State )
|
||||
{
|
||||
/*
|
||||
if ( State != m_MonsterState )
|
||||
{
|
||||
ALERT ( at_aiconsole, "State Changed to %d\n", State );
|
||||
}
|
||||
*/
|
||||
|
||||
switch( State )
|
||||
{
|
||||
|
||||
// Drop enemy pointers when going to idle
|
||||
case MONSTERSTATE_IDLE:
|
||||
|
||||
if ( m_hEnemy != NULL )
|
||||
{
|
||||
m_hEnemy = NULL;// not allowed to have an enemy anymore.
|
||||
ALERT ( at_aiconsole, "Stripped\n" );
|
||||
}
|
||||
break;
|
||||
}
|
||||
|
||||
m_MonsterState = State;
|
||||
m_IdealMonsterState = State;
|
||||
}
|
||||
|
||||
//=========================================================
|
||||
// RunAI
|
||||
//=========================================================
|
||||
void CBaseMonster :: RunAI ( void )
|
||||
{
|
||||
// to test model's eye height
|
||||
//UTIL_ParticleEffect ( pev->origin + pev->view_ofs, g_vecZero, 255, 10 );
|
||||
|
||||
// IDLE sound permitted in ALERT state is because monsters were silent in ALERT state. Only play IDLE sound in IDLE state
|
||||
// once we have sounds for that state.
|
||||
if ( ( m_MonsterState == MONSTERSTATE_IDLE || m_MonsterState == MONSTERSTATE_ALERT ) && RANDOM_LONG(0,99) == 0 && !(pev->flags & SF_MONSTER_GAG) )
|
||||
{
|
||||
IdleSound();
|
||||
}
|
||||
|
||||
if ( m_MonsterState != MONSTERSTATE_NONE &&
|
||||
m_MonsterState != MONSTERSTATE_PRONE &&
|
||||
m_MonsterState != MONSTERSTATE_DEAD )// don't bother with this crap if monster is prone.
|
||||
{
|
||||
// collect some sensory Condition information.
|
||||
// don't let monsters outside of the player's PVS act up, or most of the interesting
|
||||
// things will happen before the player gets there!
|
||||
// UPDATE: We now let COMBAT state monsters think and act fully outside of player PVS. This allows the player to leave
|
||||
// an area where monsters are fighting, and the fight will continue.
|
||||
if ( !FNullEnt( FIND_CLIENT_IN_PVS( edict() ) ) || ( m_MonsterState == MONSTERSTATE_COMBAT ) )
|
||||
{
|
||||
Look( m_flDistLook );
|
||||
Listen();// check for audible sounds.
|
||||
|
||||
// now filter conditions.
|
||||
ClearConditions( IgnoreConditions() );
|
||||
|
||||
GetEnemy();
|
||||
}
|
||||
|
||||
// do these calculations if monster has an enemy.
|
||||
if ( m_hEnemy != NULL )
|
||||
{
|
||||
CheckEnemy( m_hEnemy );
|
||||
}
|
||||
|
||||
CheckAmmo();
|
||||
}
|
||||
|
||||
FCheckAITrigger();
|
||||
|
||||
PrescheduleThink();
|
||||
|
||||
MaintainSchedule();
|
||||
|
||||
// if the monster didn't use these conditions during the above call to MaintainSchedule() or CheckAITrigger()
|
||||
// we throw them out cause we don't want them sitting around through the lifespan of a schedule
|
||||
// that doesn't use them.
|
||||
m_afConditions &= ~( bits_COND_LIGHT_DAMAGE | bits_COND_HEAVY_DAMAGE );
|
||||
}
|
||||
|
||||
//=========================================================
|
||||
// GetIdealState - surveys the Conditions information available
|
||||
// and finds the best new state for a monster.
|
||||
//=========================================================
|
||||
MONSTERSTATE CBaseMonster :: GetIdealState ( void )
|
||||
{
|
||||
int iConditions;
|
||||
|
||||
iConditions = IScheduleFlags();
|
||||
|
||||
// If no schedule conditions, the new ideal state is probably the reason we're in here.
|
||||
switch ( m_MonsterState )
|
||||
{
|
||||
case MONSTERSTATE_IDLE:
|
||||
|
||||
/*
|
||||
IDLE goes to ALERT upon hearing a sound
|
||||
-IDLE goes to ALERT upon being injured
|
||||
IDLE goes to ALERT upon seeing food
|
||||
-IDLE goes to COMBAT upon sighting an enemy
|
||||
IDLE goes to HUNT upon smelling food
|
||||
*/
|
||||
{
|
||||
if ( iConditions & bits_COND_NEW_ENEMY )
|
||||
{
|
||||
// new enemy! This means an idle monster has seen someone it dislikes, or
|
||||
// that a monster in combat has found a more suitable target to attack
|
||||
m_IdealMonsterState = MONSTERSTATE_COMBAT;
|
||||
}
|
||||
else if ( iConditions & bits_COND_LIGHT_DAMAGE )
|
||||
{
|
||||
MakeIdealYaw ( m_vecEnemyLKP );
|
||||
m_IdealMonsterState = MONSTERSTATE_ALERT;
|
||||
}
|
||||
else if ( iConditions & bits_COND_HEAVY_DAMAGE )
|
||||
{
|
||||
MakeIdealYaw ( m_vecEnemyLKP );
|
||||
m_IdealMonsterState = MONSTERSTATE_ALERT;
|
||||
}
|
||||
else if ( iConditions & bits_COND_HEAR_SOUND )
|
||||
{
|
||||
CSound *pSound;
|
||||
|
||||
pSound = PBestSound();
|
||||
ASSERT( pSound != NULL );
|
||||
if ( pSound )
|
||||
{
|
||||
MakeIdealYaw ( pSound->m_vecOrigin );
|
||||
if ( pSound->m_iType & (bits_SOUND_COMBAT|bits_SOUND_DANGER) )
|
||||
m_IdealMonsterState = MONSTERSTATE_ALERT;
|
||||
}
|
||||
}
|
||||
else if ( iConditions & (bits_COND_SMELL | bits_COND_SMELL_FOOD) )
|
||||
{
|
||||
m_IdealMonsterState = MONSTERSTATE_ALERT;
|
||||
}
|
||||
|
||||
break;
|
||||
}
|
||||
case MONSTERSTATE_ALERT:
|
||||
/*
|
||||
ALERT goes to IDLE upon becoming bored
|
||||
-ALERT goes to COMBAT upon sighting an enemy
|
||||
ALERT goes to HUNT upon hearing a noise
|
||||
*/
|
||||
{
|
||||
if ( iConditions & (bits_COND_NEW_ENEMY|bits_COND_SEE_ENEMY) )
|
||||
{
|
||||
// see an enemy we MUST attack
|
||||
m_IdealMonsterState = MONSTERSTATE_COMBAT;
|
||||
}
|
||||
else if ( iConditions & bits_COND_HEAR_SOUND )
|
||||
{
|
||||
m_IdealMonsterState = MONSTERSTATE_ALERT;
|
||||
CSound *pSound = PBestSound();
|
||||
ASSERT( pSound != NULL );
|
||||
if ( pSound )
|
||||
MakeIdealYaw ( pSound->m_vecOrigin );
|
||||
}
|
||||
break;
|
||||
}
|
||||
case MONSTERSTATE_COMBAT:
|
||||
/*
|
||||
COMBAT goes to HUNT upon losing sight of enemy
|
||||
COMBAT goes to ALERT upon death of enemy
|
||||
*/
|
||||
{
|
||||
if ( m_hEnemy == NULL )
|
||||
{
|
||||
m_IdealMonsterState = MONSTERSTATE_ALERT;
|
||||
// pev->effects = EF_BRIGHTFIELD;
|
||||
ALERT ( at_aiconsole, "***Combat state with no enemy!\n" );
|
||||
}
|
||||
break;
|
||||
}
|
||||
case MONSTERSTATE_HUNT:
|
||||
/*
|
||||
HUNT goes to ALERT upon seeing food
|
||||
HUNT goes to ALERT upon being injured
|
||||
HUNT goes to IDLE if goal touched
|
||||
HUNT goes to COMBAT upon seeing enemy
|
||||
*/
|
||||
{
|
||||
break;
|
||||
}
|
||||
case MONSTERSTATE_SCRIPT:
|
||||
if ( iConditions & (bits_COND_TASK_FAILED|bits_COND_LIGHT_DAMAGE|bits_COND_HEAVY_DAMAGE) )
|
||||
{
|
||||
ExitScriptedSequence(); // This will set the ideal state
|
||||
}
|
||||
break;
|
||||
|
||||
case MONSTERSTATE_DEAD:
|
||||
m_IdealMonsterState = MONSTERSTATE_DEAD;
|
||||
break;
|
||||
}
|
||||
|
||||
return m_IdealMonsterState;
|
||||
}
|
||||
|
|
@ -0,0 +1,323 @@
|
|||
/***
|
||||
*
|
||||
* 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.
|
||||
*
|
||||
****/
|
||||
/*
|
||||
|
||||
===== mortar.cpp ========================================================
|
||||
|
||||
the "LaBuznik" mortar device
|
||||
|
||||
*/
|
||||
|
||||
#include "extdll.h"
|
||||
#include "util.h"
|
||||
#include "cbase.h"
|
||||
#include "saverestore.h"
|
||||
#include "weapons.h"
|
||||
#include "decals.h"
|
||||
#include "soundent.h"
|
||||
|
||||
class CFuncMortarField : public CBaseToggle
|
||||
{
|
||||
public:
|
||||
void Spawn( void );
|
||||
void Precache( void );
|
||||
void KeyValue( KeyValueData *pkvd );
|
||||
|
||||
// Bmodels don't go across transitions
|
||||
virtual int ObjectCaps( void ) { return CBaseToggle :: ObjectCaps() & ~FCAP_ACROSS_TRANSITION; }
|
||||
|
||||
virtual int Save( CSave &save );
|
||||
virtual int Restore( CRestore &restore );
|
||||
|
||||
static TYPEDESCRIPTION m_SaveData[];
|
||||
|
||||
void EXPORT FieldUse( CBaseEntity *pActivator, CBaseEntity *pCaller, USE_TYPE useType, float value );
|
||||
|
||||
int m_iszXController;
|
||||
int m_iszYController;
|
||||
float m_flSpread;
|
||||
float m_flDelay;
|
||||
int m_iCount;
|
||||
int m_fControl;
|
||||
};
|
||||
|
||||
LINK_ENTITY_TO_CLASS( func_mortar_field, CFuncMortarField );
|
||||
|
||||
TYPEDESCRIPTION CFuncMortarField::m_SaveData[] =
|
||||
{
|
||||
DEFINE_FIELD( CFuncMortarField, m_iszXController, FIELD_STRING ),
|
||||
DEFINE_FIELD( CFuncMortarField, m_iszYController, FIELD_STRING ),
|
||||
DEFINE_FIELD( CFuncMortarField, m_flSpread, FIELD_FLOAT ),
|
||||
DEFINE_FIELD( CFuncMortarField, m_flDelay, FIELD_FLOAT ),
|
||||
DEFINE_FIELD( CFuncMortarField, m_iCount, FIELD_INTEGER ),
|
||||
DEFINE_FIELD( CFuncMortarField, m_fControl, FIELD_INTEGER ),
|
||||
};
|
||||
|
||||
IMPLEMENT_SAVERESTORE( CFuncMortarField, CBaseToggle );
|
||||
|
||||
|
||||
void CFuncMortarField :: KeyValue( KeyValueData *pkvd )
|
||||
{
|
||||
if (FStrEq(pkvd->szKeyName, "m_iszXController"))
|
||||
{
|
||||
m_iszXController = ALLOC_STRING(pkvd->szValue);
|
||||
pkvd->fHandled = TRUE;
|
||||
}
|
||||
else if (FStrEq(pkvd->szKeyName, "m_iszYController"))
|
||||
{
|
||||
m_iszYController = ALLOC_STRING(pkvd->szValue);
|
||||
pkvd->fHandled = TRUE;
|
||||
}
|
||||
else if (FStrEq(pkvd->szKeyName, "m_flSpread"))
|
||||
{
|
||||
m_flSpread = atof(pkvd->szValue);
|
||||
pkvd->fHandled = TRUE;
|
||||
}
|
||||
else if (FStrEq(pkvd->szKeyName, "m_fControl"))
|
||||
{
|
||||
m_fControl = atoi(pkvd->szValue);
|
||||
pkvd->fHandled = TRUE;
|
||||
}
|
||||
else if (FStrEq(pkvd->szKeyName, "m_iCount"))
|
||||
{
|
||||
m_iCount = atoi(pkvd->szValue);
|
||||
pkvd->fHandled = TRUE;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
// Drop bombs from above
|
||||
void CFuncMortarField :: Spawn( void )
|
||||
{
|
||||
pev->solid = SOLID_NOT;
|
||||
SET_MODEL(ENT(pev), STRING(pev->model)); // set size and link into world
|
||||
pev->movetype = MOVETYPE_NONE;
|
||||
SetBits( pev->effects, EF_NODRAW );
|
||||
SetUse( FieldUse );
|
||||
Precache();
|
||||
}
|
||||
|
||||
|
||||
void CFuncMortarField :: Precache( void )
|
||||
{
|
||||
PRECACHE_SOUND ("weapons/mortar.wav");
|
||||
PRECACHE_SOUND ("weapons/mortarhit.wav");
|
||||
PRECACHE_MODEL( "sprites/lgtning.spr" );
|
||||
}
|
||||
|
||||
|
||||
// If connected to a table, then use the table controllers, else hit where the trigger is.
|
||||
void CFuncMortarField :: FieldUse( CBaseEntity *pActivator, CBaseEntity *pCaller, USE_TYPE useType, float value )
|
||||
{
|
||||
Vector vecStart;
|
||||
|
||||
vecStart.x = RANDOM_FLOAT( pev->mins.x, pev->maxs.x );
|
||||
vecStart.y = RANDOM_FLOAT( pev->mins.y, pev->maxs.y );
|
||||
vecStart.z = pev->maxs.z;
|
||||
|
||||
switch( m_fControl )
|
||||
{
|
||||
case 0: // random
|
||||
break;
|
||||
case 1: // Trigger Activator
|
||||
if (pActivator != NULL)
|
||||
{
|
||||
vecStart.x = pActivator->pev->origin.x;
|
||||
vecStart.y = pActivator->pev->origin.y;
|
||||
}
|
||||
break;
|
||||
case 2: // table
|
||||
{
|
||||
CBaseEntity *pController;
|
||||
|
||||
if (!FStringNull(m_iszXController))
|
||||
{
|
||||
pController = UTIL_FindEntityByTargetname( NULL, STRING(m_iszXController));
|
||||
if (pController != NULL)
|
||||
{
|
||||
vecStart.x = pev->mins.x + pController->pev->ideal_yaw * (pev->size.x);
|
||||
}
|
||||
}
|
||||
if (!FStringNull(m_iszYController))
|
||||
{
|
||||
pController = UTIL_FindEntityByTargetname( NULL, STRING(m_iszYController));
|
||||
if (pController != NULL)
|
||||
{
|
||||
vecStart.y = pev->mins.y + pController->pev->ideal_yaw * (pev->size.y);
|
||||
}
|
||||
}
|
||||
}
|
||||
break;
|
||||
}
|
||||
|
||||
int pitch = RANDOM_LONG(95,124);
|
||||
|
||||
EMIT_SOUND_DYN(ENT(pev), CHAN_VOICE, "weapons/mortar.wav", 1.0, ATTN_NONE, 0, pitch);
|
||||
|
||||
float t = 2.5;
|
||||
for (int i = 0; i < m_iCount; i++)
|
||||
{
|
||||
Vector vecSpot = vecStart;
|
||||
vecSpot.x += RANDOM_FLOAT( -m_flSpread, m_flSpread );
|
||||
vecSpot.y += RANDOM_FLOAT( -m_flSpread, m_flSpread );
|
||||
|
||||
TraceResult tr;
|
||||
UTIL_TraceLine( vecSpot, vecSpot + Vector( 0, 0, -1 ) * 4096, ignore_monsters, ENT(pev), &tr );
|
||||
|
||||
edict_t *pentOwner = NULL;
|
||||
if (pActivator) pentOwner = pActivator->edict();
|
||||
|
||||
CBaseEntity *pMortar = Create("monster_mortar", tr.vecEndPos, Vector( 0, 0, 0 ), pentOwner );
|
||||
pMortar->pev->nextthink = gpGlobals->time + t;
|
||||
t += RANDOM_FLOAT( 0.2, 0.5 );
|
||||
|
||||
if (i == 0)
|
||||
CSoundEnt::InsertSound ( bits_SOUND_DANGER, tr.vecEndPos, 400, 0.3 );
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
class CMortar : public CGrenade
|
||||
{
|
||||
public:
|
||||
void Spawn( void );
|
||||
void Precache( void );
|
||||
|
||||
void EXPORT MortarExplode( void );
|
||||
|
||||
int m_spriteTexture;
|
||||
};
|
||||
|
||||
LINK_ENTITY_TO_CLASS( monster_mortar, CMortar );
|
||||
|
||||
void CMortar::Spawn( )
|
||||
{
|
||||
pev->movetype = MOVETYPE_NONE;
|
||||
pev->solid = SOLID_NOT;
|
||||
|
||||
pev->dmg = 200;
|
||||
|
||||
SetThink( MortarExplode );
|
||||
pev->nextthink = 0;
|
||||
|
||||
Precache( );
|
||||
|
||||
|
||||
}
|
||||
|
||||
|
||||
void CMortar::Precache( )
|
||||
{
|
||||
m_spriteTexture = PRECACHE_MODEL( "sprites/lgtning.spr" );
|
||||
}
|
||||
|
||||
void CMortar::MortarExplode( void )
|
||||
{
|
||||
#if 1
|
||||
// mortar beam
|
||||
MESSAGE_BEGIN( MSG_BROADCAST, SVC_TEMPENTITY );
|
||||
WRITE_BYTE( TE_BEAMPOINTS );
|
||||
WRITE_COORD(pev->origin.x);
|
||||
WRITE_COORD(pev->origin.y);
|
||||
WRITE_COORD(pev->origin.z);
|
||||
WRITE_COORD(pev->origin.x);
|
||||
WRITE_COORD(pev->origin.y);
|
||||
WRITE_COORD(pev->origin.z + 1024);
|
||||
WRITE_SHORT(m_spriteTexture );
|
||||
WRITE_BYTE( 0 ); // framerate
|
||||
WRITE_BYTE( 0 ); // framerate
|
||||
WRITE_BYTE( 1 ); // life
|
||||
WRITE_BYTE( 40 ); // width
|
||||
WRITE_BYTE( 0 ); // noise
|
||||
WRITE_BYTE( 255 ); // r, g, b
|
||||
WRITE_BYTE( 160 ); // r, g, b
|
||||
WRITE_BYTE( 100 ); // r, g, b
|
||||
WRITE_BYTE( 128 ); // brightness
|
||||
WRITE_BYTE( 0 ); // speed
|
||||
MESSAGE_END();
|
||||
#endif
|
||||
|
||||
#if 0
|
||||
// blast circle
|
||||
MESSAGE_BEGIN( MSG_BROADCAST, SVC_TEMPENTITY );
|
||||
WRITE_BYTE( TE_BEAMTORUS);
|
||||
WRITE_COORD(pev->origin.x);
|
||||
WRITE_COORD(pev->origin.y);
|
||||
WRITE_COORD(pev->origin.z + 32);
|
||||
WRITE_COORD(pev->origin.x);
|
||||
WRITE_COORD(pev->origin.y);
|
||||
WRITE_COORD(pev->origin.z + 32 + pev->dmg * 2 / .2); // reach damage radius over .3 seconds
|
||||
WRITE_SHORT(m_spriteTexture );
|
||||
WRITE_BYTE( 0 ); // startframe
|
||||
WRITE_BYTE( 0 ); // framerate
|
||||
WRITE_BYTE( 2 ); // life
|
||||
WRITE_BYTE( 12 ); // width
|
||||
WRITE_BYTE( 0 ); // noise
|
||||
WRITE_BYTE( 255 ); // r, g, b
|
||||
WRITE_BYTE( 160 ); // r, g, b
|
||||
WRITE_BYTE( 100 ); // r, g, b
|
||||
WRITE_BYTE( 255 ); // brightness
|
||||
WRITE_BYTE( 0 ); // speed
|
||||
MESSAGE_END();
|
||||
#endif
|
||||
|
||||
TraceResult tr;
|
||||
UTIL_TraceLine( pev->origin + Vector( 0, 0, 1024 ), pev->origin - Vector( 0, 0, 1024 ), dont_ignore_monsters, ENT(pev), &tr );
|
||||
|
||||
Explode( &tr, DMG_BLAST | DMG_MORTAR );
|
||||
UTIL_ScreenShake( tr.vecEndPos, 25.0, 150.0, 1.0, 750 );
|
||||
|
||||
#if 0
|
||||
int pitch = RANDOM_LONG(95,124);
|
||||
EMIT_SOUND_DYN(ENT(pev), CHAN_VOICE, "weapons/mortarhit.wav", 1.0, 0.55, 0, pitch);
|
||||
|
||||
// ForceSound( SNDRADIUS_MP5, bits_SOUND_COMBAT );
|
||||
|
||||
// ExplodeModel( pev->origin, 400, g_sModelIndexShrapnel, 30 );
|
||||
|
||||
RadiusDamage ( pev, VARS(pev->owner), pev->dmg, CLASS_NONE, DMG_BLAST );
|
||||
|
||||
/*
|
||||
if ( RANDOM_FLOAT ( 0 , 1 ) < 0.5 )
|
||||
{
|
||||
UTIL_DecalTrace( pTrace, DECAL_SCORCH1 );
|
||||
}
|
||||
else
|
||||
{
|
||||
UTIL_DecalTrace( pTrace, DECAL_SCORCH2 );
|
||||
}
|
||||
*/
|
||||
|
||||
SetThink( SUB_Remove );
|
||||
pev->nextthink = gpGlobals->time + 0.1;
|
||||
#endif
|
||||
|
||||
}
|
||||
|
||||
|
||||
#if 0
|
||||
void CMortar::ShootTimed( EVARS *pevOwner, Vector vecStart, float time )
|
||||
{
|
||||
CMortar *pMortar = GetClassPtr( (CMortar *)NULL );
|
||||
pMortar->Spawn();
|
||||
|
||||
TraceResult tr;
|
||||
UTIL_TraceLine( vecStart, vecStart + Vector( 0, 0, -1 ) * 4096, ignore_monsters, ENT(pMortar->pev), &tr );
|
||||
|
||||
pMortar->pev->nextthink = gpGlobals->time + time;
|
||||
|
||||
UTIL_SetOrigin( pMortar->pev, tr.vecEndPos );
|
||||
}
|
||||
#endif
|
|
@ -0,0 +1,392 @@
|
|||
/***
|
||||
*
|
||||
* 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 mp5_e
|
||||
{
|
||||
MP5_LONGIDLE = 0,
|
||||
MP5_IDLE1,
|
||||
MP5_LAUNCH,
|
||||
MP5_RELOAD,
|
||||
MP5_DEPLOY,
|
||||
MP5_FIRE1,
|
||||
MP5_FIRE2,
|
||||
MP5_FIRE3,
|
||||
};
|
||||
|
||||
|
||||
class CMP5 : 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 );
|
||||
int SecondaryAmmoIndex( void );
|
||||
BOOL Deploy( void );
|
||||
void Reload( void );
|
||||
void WeaponIdle( void );
|
||||
float m_flNextAnimTime;
|
||||
int m_iShell;
|
||||
private:
|
||||
unsigned short m_usMP5;
|
||||
};
|
||||
LINK_ENTITY_TO_CLASS( weapon_mp5, CMP5 );
|
||||
LINK_ENTITY_TO_CLASS( weapon_9mmAR, CMP5 );
|
||||
|
||||
|
||||
//=========================================================
|
||||
//=========================================================
|
||||
int CMP5::SecondaryAmmoIndex( void )
|
||||
{
|
||||
return m_iSecondaryAmmoType;
|
||||
}
|
||||
|
||||
void CMP5::Spawn( )
|
||||
{
|
||||
pev->classname = MAKE_STRING("weapon_9mmAR"); // hack to allow for old names
|
||||
Precache( );
|
||||
SET_MODEL(ENT(pev), "models/w_9mmAR.mdl");
|
||||
m_iId = WEAPON_MP5;
|
||||
|
||||
m_iDefaultAmmo = MP5_DEFAULT_GIVE;
|
||||
|
||||
FallInit();// get ready to fall down.
|
||||
}
|
||||
|
||||
|
||||
void CMP5::Precache( void )
|
||||
{
|
||||
PRECACHE_MODEL("models/v_9mmAR.mdl");
|
||||
PRECACHE_MODEL("models/w_9mmAR.mdl");
|
||||
PRECACHE_MODEL("models/p_9mmAR.mdl");
|
||||
|
||||
m_iShell = PRECACHE_MODEL ("models/shell.mdl");// brass shellTE_MODEL
|
||||
|
||||
PRECACHE_MODEL("models/grenade.mdl"); // grenade
|
||||
|
||||
PRECACHE_MODEL("models/w_9mmARclip.mdl");
|
||||
PRECACHE_SOUND("items/9mmclip1.wav");
|
||||
|
||||
PRECACHE_SOUND("items/clipinsert1.wav");
|
||||
PRECACHE_SOUND("items/cliprelease1.wav");
|
||||
// PRECACHE_SOUND("items/guncock1.wav");
|
||||
|
||||
PRECACHE_SOUND ("weapons/hks1.wav");// H to the K
|
||||
PRECACHE_SOUND ("weapons/hks2.wav");// H to the K
|
||||
PRECACHE_SOUND ("weapons/hks3.wav");// H to the K
|
||||
|
||||
PRECACHE_SOUND( "weapons/glauncher.wav" );
|
||||
PRECACHE_SOUND( "weapons/glauncher2.wav" );
|
||||
|
||||
PRECACHE_SOUND ("weapons/357_cock1.wav");
|
||||
|
||||
m_usMP5 = PRECACHE_EVENT( 1, "events/mp5.sc" );
|
||||
}
|
||||
|
||||
int CMP5::GetItemInfo(ItemInfo *p)
|
||||
{
|
||||
p->pszName = STRING(pev->classname);
|
||||
p->pszAmmo1 = "9mm";
|
||||
p->iMaxAmmo1 = _9MM_MAX_CARRY;
|
||||
p->pszAmmo2 = "ARgrenades";
|
||||
p->iMaxAmmo2 = M203_GRENADE_MAX_CARRY;
|
||||
p->iMaxClip = MP5_MAX_CLIP;
|
||||
p->iSlot = 2;
|
||||
p->iPosition = 0;
|
||||
p->iFlags = 0;
|
||||
p->iId = m_iId = WEAPON_MP5;
|
||||
p->iWeight = MP5_WEIGHT;
|
||||
|
||||
return 1;
|
||||
}
|
||||
|
||||
int CMP5::AddToPlayer( CBasePlayer *pPlayer )
|
||||
{
|
||||
if ( CBasePlayerWeapon::AddToPlayer( pPlayer ) )
|
||||
{
|
||||
MESSAGE_BEGIN( MSG_ONE, gmsgWeapPickup, NULL, pPlayer->pev );
|
||||
WRITE_BYTE( m_iId );
|
||||
MESSAGE_END();
|
||||
return TRUE;
|
||||
}
|
||||
return FALSE;
|
||||
}
|
||||
|
||||
BOOL CMP5::Deploy( )
|
||||
{
|
||||
return DefaultDeploy( "models/v_9mmAR.mdl", "models/p_9mmAR.mdl", MP5_DEPLOY, "mp5" );
|
||||
}
|
||||
|
||||
|
||||
void CMP5::PrimaryAttack()
|
||||
{
|
||||
// don't fire underwater
|
||||
if (m_pPlayer->pev->waterlevel == 3)
|
||||
{
|
||||
PlayEmptySound( );
|
||||
m_flNextPrimaryAttack = gpGlobals->time + 0.15;
|
||||
return;
|
||||
}
|
||||
|
||||
if (m_iClip <= 0)
|
||||
{
|
||||
PlayEmptySound();
|
||||
m_flNextPrimaryAttack = gpGlobals->time + 0.15;
|
||||
return;
|
||||
}
|
||||
|
||||
PLAYBACK_EVENT( 0, m_pPlayer->edict(), m_usMP5 );
|
||||
|
||||
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_MP5, 2 );
|
||||
}
|
||||
else
|
||||
{
|
||||
// single player spread
|
||||
m_pPlayer->FireBullets( 1, vecSrc, vecAiming, VECTOR_CONE_3DEGREES, 8192, BULLET_PLAYER_MP5, 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 CMP5::SecondaryAttack( void )
|
||||
{
|
||||
// don't fire underwater
|
||||
if (m_pPlayer->pev->waterlevel == 3)
|
||||
{
|
||||
PlayEmptySound( );
|
||||
m_flNextPrimaryAttack = gpGlobals->time + 0.15;
|
||||
return;
|
||||
}
|
||||
|
||||
if (m_pPlayer->m_rgAmmo[m_iSecondaryAmmoType] == 0)
|
||||
{
|
||||
PlayEmptySound( );
|
||||
return;
|
||||
}
|
||||
|
||||
m_pPlayer->m_iWeaponVolume = NORMAL_GUN_VOLUME;
|
||||
m_pPlayer->m_iWeaponFlash = BRIGHT_GUN_FLASH;
|
||||
|
||||
m_pPlayer->m_iExtraSoundTypes = bits_SOUND_DANGER;
|
||||
m_pPlayer->m_flStopExtraSoundTime = gpGlobals->time + 0.2;
|
||||
|
||||
m_pPlayer->m_rgAmmo[m_iSecondaryAmmoType]--;
|
||||
|
||||
SendWeaponAnim( MP5_LAUNCH );
|
||||
|
||||
// player "shoot" animation
|
||||
m_pPlayer->SetAnimation( PLAYER_ATTACK1 );
|
||||
|
||||
if ( RANDOM_LONG(0,1) )
|
||||
{
|
||||
// play this sound through BODY channel so we can hear it if player didn't stop firing MP3
|
||||
EMIT_SOUND(ENT(m_pPlayer->pev), CHAN_WEAPON, "weapons/glauncher.wav", 0.8, ATTN_NORM);
|
||||
}
|
||||
else
|
||||
{
|
||||
// play this sound through BODY channel so we can hear it if player didn't stop firing MP3
|
||||
EMIT_SOUND(ENT(m_pPlayer->pev), CHAN_WEAPON, "weapons/glauncher2.wav", 0.8, ATTN_NORM);
|
||||
}
|
||||
|
||||
UTIL_MakeVectors( m_pPlayer->pev->viewangles + m_pPlayer->pev->punchangle );
|
||||
|
||||
// we don't add in player velocity anymore.
|
||||
CGrenade::ShootContact( m_pPlayer->pev,
|
||||
m_pPlayer->pev->origin + m_pPlayer->pev->view_ofs + gpGlobals->v_forward * 16,
|
||||
gpGlobals->v_forward * 800 );
|
||||
|
||||
m_flNextPrimaryAttack = gpGlobals->time + 1;
|
||||
m_flNextSecondaryAttack = gpGlobals->time + 1;
|
||||
m_flTimeWeaponIdle = gpGlobals->time + 5;// idle pretty soon after shooting.
|
||||
|
||||
if (!m_pPlayer->m_rgAmmo[m_iSecondaryAmmoType])
|
||||
// HEV suit - indicate out of ammo condition
|
||||
m_pPlayer->SetSuitUpdate("!HEV_AMO0", FALSE, 0);
|
||||
|
||||
m_pPlayer->pev->punchangle.x -= 10;
|
||||
}
|
||||
|
||||
void CMP5::Reload( void )
|
||||
{
|
||||
DefaultReload( MP5_MAX_CLIP, MP5_RELOAD, 1.5 );
|
||||
}
|
||||
|
||||
|
||||
|
||||
void CMP5::WeaponIdle( void )
|
||||
{
|
||||
ResetEmptySound( );
|
||||
|
||||
m_pPlayer->GetAutoaimVector( AUTOAIM_5DEGREES );
|
||||
|
||||
if (m_flTimeWeaponIdle > gpGlobals->time)
|
||||
return;
|
||||
|
||||
int iAnim;
|
||||
switch ( RANDOM_LONG( 0, 1 ) )
|
||||
{
|
||||
case 0:
|
||||
iAnim = MP5_LONGIDLE;
|
||||
break;
|
||||
|
||||
default:
|
||||
case 1:
|
||||
iAnim = MP5_IDLE1;
|
||||
break;
|
||||
}
|
||||
|
||||
SendWeaponAnim( iAnim );
|
||||
|
||||
m_flTimeWeaponIdle = gpGlobals->time + RANDOM_FLOAT ( 10, 15 );// how long till we do this again.
|
||||
}
|
||||
|
||||
|
||||
|
||||
class CMP5AmmoClip : public CBasePlayerAmmo
|
||||
{
|
||||
void Spawn( void )
|
||||
{
|
||||
Precache( );
|
||||
SET_MODEL(ENT(pev), "models/w_9mmARclip.mdl");
|
||||
CBasePlayerAmmo::Spawn( );
|
||||
}
|
||||
void Precache( void )
|
||||
{
|
||||
PRECACHE_MODEL ("models/w_9mmARclip.mdl");
|
||||
PRECACHE_SOUND("items/9mmclip1.wav");
|
||||
}
|
||||
BOOL AddAmmo( CBaseEntity *pOther )
|
||||
{
|
||||
int bResult = (pOther->GiveAmmo( AMMO_MP5CLIP_GIVE, "9mm", _9MM_MAX_CARRY) != -1);
|
||||
if (bResult)
|
||||
{
|
||||
EMIT_SOUND(ENT(pev), CHAN_ITEM, "items/9mmclip1.wav", 1, ATTN_NORM);
|
||||
}
|
||||
return bResult;
|
||||
}
|
||||
};
|
||||
LINK_ENTITY_TO_CLASS( ammo_mp5clip, CMP5AmmoClip );
|
||||
LINK_ENTITY_TO_CLASS( ammo_9mmAR, CMP5AmmoClip );
|
||||
|
||||
|
||||
|
||||
class CMP5Chainammo : public CBasePlayerAmmo
|
||||
{
|
||||
void Spawn( void )
|
||||
{
|
||||
Precache( );
|
||||
SET_MODEL(ENT(pev), "models/w_chainammo.mdl");
|
||||
CBasePlayerAmmo::Spawn( );
|
||||
}
|
||||
void Precache( void )
|
||||
{
|
||||
PRECACHE_MODEL ("models/w_chainammo.mdl");
|
||||
PRECACHE_SOUND("items/9mmclip1.wav");
|
||||
}
|
||||
BOOL AddAmmo( CBaseEntity *pOther )
|
||||
{
|
||||
int bResult = (pOther->GiveAmmo( AMMO_CHAINBOX_GIVE, "9mm", _9MM_MAX_CARRY) != -1);
|
||||
if (bResult)
|
||||
{
|
||||
EMIT_SOUND(ENT(pev), CHAN_ITEM, "items/9mmclip1.wav", 1, ATTN_NORM);
|
||||
}
|
||||
return bResult;
|
||||
}
|
||||
};
|
||||
LINK_ENTITY_TO_CLASS( ammo_9mmbox, CMP5Chainammo );
|
||||
|
||||
|
||||
class CMP5AmmoGrenade : public CBasePlayerAmmo
|
||||
{
|
||||
void Spawn( void )
|
||||
{
|
||||
Precache( );
|
||||
SET_MODEL(ENT(pev), "models/w_ARgrenade.mdl");
|
||||
CBasePlayerAmmo::Spawn( );
|
||||
}
|
||||
void Precache( void )
|
||||
{
|
||||
PRECACHE_MODEL ("models/w_ARgrenade.mdl");
|
||||
PRECACHE_SOUND("items/9mmclip1.wav");
|
||||
}
|
||||
BOOL AddAmmo( CBaseEntity *pOther )
|
||||
{
|
||||
int bResult = (pOther->GiveAmmo( AMMO_M203BOX_GIVE, "ARgrenades", M203_GRENADE_MAX_CARRY ) != -1);
|
||||
|
||||
if (bResult)
|
||||
{
|
||||
EMIT_SOUND(ENT(pev), CHAN_ITEM, "items/9mmclip1.wav", 1, ATTN_NORM);
|
||||
}
|
||||
return bResult;
|
||||
}
|
||||
};
|
||||
LINK_ENTITY_TO_CLASS( ammo_mp5grenades, CMP5AmmoGrenade );
|
||||
LINK_ENTITY_TO_CLASS( ammo_ARgrenades, CMP5AmmoGrenade );
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
|
@ -0,0 +1,374 @@
|
|||
/***
|
||||
*
|
||||
* 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.
|
||||
*
|
||||
****/
|
||||
//=========================================================
|
||||
// nodes.h
|
||||
//=========================================================
|
||||
|
||||
//=========================================================
|
||||
// DEFINE
|
||||
//=========================================================
|
||||
#define MAX_STACK_NODES 100
|
||||
#define NO_NODE -1
|
||||
#define MAX_NODE_HULLS 4
|
||||
|
||||
#define bits_NODE_LAND ( 1 << 0 ) // Land node, so nudge if necessary.
|
||||
#define bits_NODE_AIR ( 1 << 1 ) // Air node, don't nudge.
|
||||
#define bits_NODE_WATER ( 1 << 2 ) // Water node, don't nudge.
|
||||
#define bits_NODE_GROUP_REALM (bits_NODE_LAND | bits_NODE_AIR | bits_NODE_WATER)
|
||||
|
||||
//=========================================================
|
||||
// Instance of a node.
|
||||
//=========================================================
|
||||
class CNode
|
||||
{
|
||||
public:
|
||||
Vector m_vecOrigin;// location of this node in space
|
||||
Vector m_vecOriginPeek; // location of this node (LAND nodes are NODE_HEIGHT higher).
|
||||
BYTE m_Region[3]; // Which of 256 regions do each of the coordinate belong?
|
||||
int m_afNodeInfo;// bits that tell us more about this location
|
||||
|
||||
int m_cNumLinks; // how many links this node has
|
||||
int m_iFirstLink;// index of this node's first link in the link pool.
|
||||
|
||||
// Where to start looking in the compressed routing table (offset into m_pRouteInfo).
|
||||
// (4 hull sizes -- smallest to largest + fly/swim), and secondly, door capability.
|
||||
//
|
||||
int m_pNextBestNode[MAX_NODE_HULLS][2];
|
||||
|
||||
// Used in finding the shortest path. m_fClosestSoFar is -1 if not visited.
|
||||
// Then it is the distance to the source. If another path uses this node
|
||||
// and has a closer distance, then m_iPreviousNode is also updated.
|
||||
//
|
||||
float m_flClosestSoFar; // Used in finding the shortest path.
|
||||
int m_iPreviousNode;
|
||||
|
||||
short m_sHintType;// there is something interesting in the world at this node's position
|
||||
short m_sHintActivity;// there is something interesting in the world at this node's position
|
||||
float m_flHintYaw;// monster on this node should face this yaw to face the hint.
|
||||
};
|
||||
|
||||
//=========================================================
|
||||
// CLink - A link between 2 nodes
|
||||
//=========================================================
|
||||
#define bits_LINK_SMALL_HULL ( 1 << 0 )// headcrab box can fit through this connection
|
||||
#define bits_LINK_HUMAN_HULL ( 1 << 1 )// player box can fit through this connection
|
||||
#define bits_LINK_LARGE_HULL ( 1 << 2 )// big box can fit through this connection
|
||||
#define bits_LINK_FLY_HULL ( 1 << 3 )// a flying big box can fit through this connection
|
||||
#define bits_LINK_DISABLED ( 1 << 4 )// link is not valid when the set
|
||||
|
||||
#define NODE_SMALL_HULL 0
|
||||
#define NODE_HUMAN_HULL 1
|
||||
#define NODE_LARGE_HULL 2
|
||||
#define NODE_FLY_HULL 3
|
||||
|
||||
class CLink
|
||||
{
|
||||
public:
|
||||
int m_iSrcNode;// the node that 'owns' this link ( keeps us from having to make reverse lookups )
|
||||
int m_iDestNode;// the node on the other end of the link.
|
||||
|
||||
entvars_t *m_pLinkEnt;// the entity that blocks this connection (doors, etc)
|
||||
|
||||
// m_szLinkEntModelname is not necessarily NULL terminated (so we can store it in a more alignment-friendly 4 bytes)
|
||||
char m_szLinkEntModelname[ 4 ];// the unique name of the brush model that blocks the connection (this is kept for save/restore)
|
||||
|
||||
int m_afLinkInfo;// information about this link
|
||||
float m_flWeight;// length of the link line segment
|
||||
};
|
||||
|
||||
|
||||
typedef struct
|
||||
{
|
||||
int m_SortedBy[3];
|
||||
int m_CheckedEvent;
|
||||
} DIST_INFO;
|
||||
|
||||
typedef struct
|
||||
{
|
||||
Vector v;
|
||||
short n; // Nearest node or -1 if no node found.
|
||||
} CACHE_ENTRY;
|
||||
|
||||
//=========================================================
|
||||
// CGraph
|
||||
//=========================================================
|
||||
#define GRAPH_VERSION (int)16// !!!increment this whever graph/node/link classes change, to obsolesce older disk files.
|
||||
class CGraph
|
||||
{
|
||||
public:
|
||||
|
||||
// the graph has two flags, and should not be accessed unless both flags are TRUE!
|
||||
BOOL m_fGraphPresent;// is the graph in memory?
|
||||
BOOL m_fGraphPointersSet;// are the entity pointers for the graph all set?
|
||||
BOOL m_fRoutingComplete; // are the optimal routes computed, yet?
|
||||
|
||||
CNode *m_pNodes;// pointer to the memory block that contains all node info
|
||||
CLink *m_pLinkPool;// big list of all node connections
|
||||
char *m_pRouteInfo; // compressed routing information the nodes use.
|
||||
|
||||
int m_cNodes;// total number of nodes
|
||||
int m_cLinks;// total number of links
|
||||
int m_nRouteInfo; // size of m_pRouteInfo in bytes.
|
||||
|
||||
// Tables for making nearest node lookup faster. SortedBy provided nodes in a
|
||||
// order of a particular coordinate. Instead of doing a binary search, RangeStart
|
||||
// and RangeEnd let you get to the part of SortedBy that you are interested in.
|
||||
//
|
||||
// Once you have a point of interest, the only way you'll find a closer point is
|
||||
// if at least one of the coordinates is closer than the ones you have now. So we
|
||||
// search each range. After the search is exhausted, we know we have the closest
|
||||
// node.
|
||||
//
|
||||
#define CACHE_SIZE 128
|
||||
#define NUM_RANGES 256
|
||||
DIST_INFO *m_di; // This is m_cNodes long, but the entries don't correspond to CNode entries.
|
||||
int m_RangeStart[3][NUM_RANGES];
|
||||
int m_RangeEnd[3][NUM_RANGES];
|
||||
float m_flShortest;
|
||||
int m_iNearest;
|
||||
int m_minX, m_minY, m_minZ, m_maxX, m_maxY, m_maxZ;
|
||||
int m_minBoxX, m_minBoxY, m_minBoxZ, m_maxBoxX, m_maxBoxY, m_maxBoxZ;
|
||||
int m_CheckedCounter;
|
||||
float m_RegionMin[3], m_RegionMax[3]; // The range of nodes.
|
||||
CACHE_ENTRY m_Cache[CACHE_SIZE];
|
||||
|
||||
|
||||
int m_HashPrimes[16];
|
||||
short *m_pHashLinks;
|
||||
int m_nHashLinks;
|
||||
|
||||
|
||||
// kinda sleazy. In order to allow variety in active idles for monster groups in a room with more than one node,
|
||||
// we keep track of the last node we searched from and store it here. Subsequent searches by other monsters will pick
|
||||
// up where the last search stopped.
|
||||
int m_iLastActiveIdleSearch;
|
||||
|
||||
// another such system used to track the search for cover nodes, helps greatly with two monsters trying to get to the same node.
|
||||
int m_iLastCoverSearch;
|
||||
|
||||
// functions to create the graph
|
||||
int LinkVisibleNodes ( CLink *pLinkPool, FILE *file, int *piBadNode );
|
||||
int RejectInlineLinks ( CLink *pLinkPool, FILE *file );
|
||||
int FindShortestPath ( int *piPath, int iStart, int iDest, int iHull, int afCapMask);
|
||||
int FindNearestNode ( const Vector &vecOrigin, CBaseEntity *pEntity );
|
||||
int FindNearestNode ( const Vector &vecOrigin, int afNodeTypes );
|
||||
//int FindNearestLink ( const Vector &vecTestPoint, int *piNearestLink, BOOL *pfAlongLine );
|
||||
float PathLength( int iStart, int iDest, int iHull, int afCapMask );
|
||||
int NextNodeInRoute( int iCurrentNode, int iDest, int iHull, int iCap );
|
||||
|
||||
enum NODEQUERY { NODEGRAPH_DYNAMIC, NODEGRAPH_STATIC };
|
||||
// A static query means we're asking about the possiblity of handling this entity at ANY time
|
||||
// A dynamic query means we're asking about it RIGHT NOW. So we should query the current state
|
||||
int HandleLinkEnt ( int iNode, entvars_t *pevLinkEnt, int afCapMask, NODEQUERY queryType );
|
||||
entvars_t* LinkEntForLink ( CLink *pLink, CNode *pNode );
|
||||
void ShowNodeConnections ( int iNode );
|
||||
void InitGraph( void );
|
||||
int AllocNodes ( void );
|
||||
|
||||
int CheckNODFile(char *szMapName);
|
||||
int FLoadGraph(char *szMapName);
|
||||
int FSaveGraph(char *szMapName);
|
||||
int FSetGraphPointers(void);
|
||||
void CheckNode(Vector vecOrigin, int iNode);
|
||||
|
||||
void BuildRegionTables(void);
|
||||
void ComputeStaticRoutingTables(void);
|
||||
void TestRoutingTables(void);
|
||||
|
||||
void HashInsert(int iSrcNode, int iDestNode, int iKey);
|
||||
void HashSearch(int iSrcNode, int iDestNode, int &iKey);
|
||||
void HashChoosePrimes(int TableSize);
|
||||
void BuildLinkLookups(void);
|
||||
|
||||
void SortNodes(void);
|
||||
|
||||
int HullIndex( const CBaseEntity *pEntity ); // what hull the monster uses
|
||||
int NodeType( const CBaseEntity *pEntity ); // what node type the monster uses
|
||||
inline int CapIndex( int afCapMask )
|
||||
{
|
||||
if (afCapMask & (bits_CAP_OPEN_DOORS | bits_CAP_AUTO_DOORS | bits_CAP_USE))
|
||||
return 1;
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
||||
inline CNode &Node( int i )
|
||||
{
|
||||
#ifdef _DEBUG
|
||||
if ( !m_pNodes || i < 0 || i > m_cNodes )
|
||||
ALERT( at_error, "Bad Node!\n" );
|
||||
#endif
|
||||
return m_pNodes[i];
|
||||
}
|
||||
|
||||
inline CLink &Link( int i )
|
||||
{
|
||||
#ifdef _DEBUG
|
||||
if ( !m_pLinkPool || i < 0 || i > m_cLinks )
|
||||
ALERT( at_error, "Bad link!\n" );
|
||||
#endif
|
||||
return m_pLinkPool[i];
|
||||
}
|
||||
|
||||
inline CLink &NodeLink( int iNode, int iLink )
|
||||
{
|
||||
return Link( Node( iNode ).m_iFirstLink + iLink );
|
||||
}
|
||||
|
||||
inline CLink &NodeLink( const CNode &node, int iLink )
|
||||
{
|
||||
return Link( node.m_iFirstLink + iLink );
|
||||
}
|
||||
|
||||
inline int INodeLink ( int iNode, int iLink )
|
||||
{
|
||||
return NodeLink( iNode, iLink ).m_iDestNode;
|
||||
}
|
||||
|
||||
#if 0
|
||||
inline CNode &SourceNode( int iNode, int iLink )
|
||||
{
|
||||
return Node( NodeLink( iNode, iLink ).m_iSrcNode );
|
||||
}
|
||||
|
||||
inline CNode &DestNode( int iNode, int iLink )
|
||||
{
|
||||
return Node( NodeLink( iNode, iLink ).m_iDestNode );
|
||||
}
|
||||
|
||||
inline CNode *PNodeLink ( int iNode, int iLink )
|
||||
{
|
||||
return &DestNode( iNode, iLink );
|
||||
}
|
||||
#endif
|
||||
};
|
||||
|
||||
//=========================================================
|
||||
// Nodes start out as ents in the level. The node graph
|
||||
// is built, then these ents are discarded.
|
||||
//=========================================================
|
||||
class CNodeEnt : public CBaseEntity
|
||||
{
|
||||
void Spawn( void );
|
||||
void KeyValue( KeyValueData *pkvd );
|
||||
virtual int ObjectCaps( void ) { return CBaseEntity :: ObjectCaps() & ~FCAP_ACROSS_TRANSITION; }
|
||||
|
||||
short m_sHintType;
|
||||
short m_sHintActivity;
|
||||
};
|
||||
|
||||
|
||||
//=========================================================
|
||||
// CStack - last in, first out.
|
||||
//=========================================================
|
||||
class CStack
|
||||
{
|
||||
public:
|
||||
CStack( void );
|
||||
void Push( int value );
|
||||
int Pop( void );
|
||||
int Top( void );
|
||||
int Empty( void ) { return m_level==0; }
|
||||
int Size( void ) { return m_level; }
|
||||
void CopyToArray ( int *piArray );
|
||||
|
||||
private:
|
||||
int m_stack[ MAX_STACK_NODES ];
|
||||
int m_level;
|
||||
};
|
||||
|
||||
|
||||
//=========================================================
|
||||
// CQueue - first in, first out.
|
||||
//=========================================================
|
||||
class CQueue
|
||||
{
|
||||
public:
|
||||
|
||||
CQueue( void );// constructor
|
||||
inline int Full ( void ) { return ( m_cSize == MAX_STACK_NODES ); }
|
||||
inline int Empty ( void ) { return ( m_cSize == 0 ); }
|
||||
//inline int Tail ( void ) { return ( m_queue[ m_tail ] ); }
|
||||
inline int Size ( void ) { return ( m_cSize ); }
|
||||
void Insert( int, float );
|
||||
int Remove( float & );
|
||||
|
||||
private:
|
||||
int m_cSize;
|
||||
struct tag_QUEUE_NODE
|
||||
{
|
||||
int Id;
|
||||
float Priority;
|
||||
} m_queue[ MAX_STACK_NODES ];
|
||||
int m_head;
|
||||
int m_tail;
|
||||
};
|
||||
|
||||
//=========================================================
|
||||
// CQueuePriority - Priority queue (smallest item out first).
|
||||
//
|
||||
//=========================================================
|
||||
class CQueuePriority
|
||||
{
|
||||
public:
|
||||
|
||||
CQueuePriority( void );// constructor
|
||||
inline int Full ( void ) { return ( m_cSize == MAX_STACK_NODES ); }
|
||||
inline int Empty ( void ) { return ( m_cSize == 0 ); }
|
||||
//inline int Tail ( float & ) { return ( m_queue[ m_tail ].Id ); }
|
||||
inline int Size ( void ) { return ( m_cSize ); }
|
||||
void Insert( int, float );
|
||||
int Remove( float &);
|
||||
|
||||
private:
|
||||
int m_cSize;
|
||||
struct tag_HEAP_NODE
|
||||
{
|
||||
int Id;
|
||||
float Priority;
|
||||
} m_heap[ MAX_STACK_NODES ];
|
||||
void Heap_SiftDown(int);
|
||||
void Heap_SiftUp(void);
|
||||
|
||||
};
|
||||
|
||||
//=========================================================
|
||||
// hints - these MUST coincide with the HINTS listed under
|
||||
// info_node in the FGD file!
|
||||
//=========================================================
|
||||
enum
|
||||
{
|
||||
HINT_NONE = 0,
|
||||
HINT_WORLD_DOOR,
|
||||
HINT_WORLD_WINDOW,
|
||||
HINT_WORLD_BUTTON,
|
||||
HINT_WORLD_MACHINERY,
|
||||
HINT_WORLD_LEDGE,
|
||||
HINT_WORLD_LIGHT_SOURCE,
|
||||
HINT_WORLD_HEAT_SOURCE,
|
||||
HINT_WORLD_BLINKING_LIGHT,
|
||||
HINT_WORLD_BRIGHT_COLORS,
|
||||
HINT_WORLD_HUMAN_BLOOD,
|
||||
HINT_WORLD_ALIEN_BLOOD,
|
||||
|
||||
HINT_TACTICAL_EXIT = 100,
|
||||
HINT_TACTICAL_VANTAGE,
|
||||
HINT_TACTICAL_AMBUSH,
|
||||
|
||||
HINT_STUKA_PERCH = 300,
|
||||
HINT_STUKA_LANDING,
|
||||
};
|
||||
|
||||
extern CGraph WorldGraph;
|
|
@ -0,0 +1,804 @@
|
|||
/***
|
||||
*
|
||||
* 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.
|
||||
*
|
||||
****/
|
||||
#include "extdll.h"
|
||||
#include "util.h"
|
||||
#include "cbase.h"
|
||||
#include "monsters.h"
|
||||
#include "weapons.h"
|
||||
#include "nodes.h"
|
||||
#include "soundent.h"
|
||||
#include "effects.h"
|
||||
|
||||
typedef struct
|
||||
{
|
||||
int isValid;
|
||||
EHANDLE hGrunt;
|
||||
Vector vecOrigin;
|
||||
Vector vecAngles;
|
||||
} t_ospreygrunt;
|
||||
|
||||
|
||||
|
||||
#define SF_WAITFORTRIGGER 0x40
|
||||
|
||||
|
||||
#define MAX_CARRY 24
|
||||
|
||||
class COsprey : public CBaseMonster
|
||||
{
|
||||
public:
|
||||
int Save( CSave &save );
|
||||
int Restore( CRestore &restore );
|
||||
static TYPEDESCRIPTION m_SaveData[];
|
||||
int ObjectCaps( void ) { return CBaseMonster :: ObjectCaps() & ~FCAP_ACROSS_TRANSITION; }
|
||||
|
||||
void Spawn( void );
|
||||
void Precache( void );
|
||||
int Classify( void ) { return CLASS_MACHINE; };
|
||||
int BloodColor( void ) { return DONT_BLEED; }
|
||||
void Killed( entvars_t *pevAttacker, int iGib );
|
||||
|
||||
void UpdateGoal( void );
|
||||
BOOL HasDead( void );
|
||||
void EXPORT FlyThink( void );
|
||||
void EXPORT DeployThink( void );
|
||||
void Flight( void );
|
||||
void EXPORT HitTouch( CBaseEntity *pOther );
|
||||
void EXPORT FindAllThink( void );
|
||||
void EXPORT HoverThink( void );
|
||||
CBaseMonster *MakeGrunt( Vector vecSrc );
|
||||
void EXPORT CrashTouch( CBaseEntity *pOther );
|
||||
void EXPORT DyingThink( void );
|
||||
void EXPORT CommandUse( CBaseEntity *pActivator, CBaseEntity *pCaller, USE_TYPE useType, float value );
|
||||
|
||||
// int TakeDamage( entvars_t* pevInflictor, entvars_t* pevAttacker, float flDamage, int bitsDamageType );
|
||||
void TraceAttack( entvars_t *pevAttacker, float flDamage, Vector vecDir, TraceResult *ptr, int bitsDamageType);
|
||||
void ShowDamage( void );
|
||||
|
||||
CBaseEntity *m_pGoalEnt;
|
||||
Vector m_vel1;
|
||||
Vector m_vel2;
|
||||
Vector m_pos1;
|
||||
Vector m_pos2;
|
||||
Vector m_ang1;
|
||||
Vector m_ang2;
|
||||
float m_startTime;
|
||||
float m_dTime;
|
||||
|
||||
Vector m_velocity;
|
||||
|
||||
float m_flIdealtilt;
|
||||
float m_flRotortilt;
|
||||
|
||||
float m_flRightHealth;
|
||||
float m_flLeftHealth;
|
||||
|
||||
int m_iUnits;
|
||||
EHANDLE m_hGrunt[MAX_CARRY];
|
||||
Vector m_vecOrigin[MAX_CARRY];
|
||||
EHANDLE m_hRepel[4];
|
||||
|
||||
int m_iSoundState;
|
||||
int m_iSpriteTexture;
|
||||
|
||||
int m_iPitch;
|
||||
|
||||
int m_iExplode;
|
||||
int m_iTailGibs;
|
||||
int m_iBodyGibs;
|
||||
int m_iEngineGibs;
|
||||
|
||||
int m_iDoLeftSmokePuff;
|
||||
int m_iDoRightSmokePuff;
|
||||
};
|
||||
|
||||
LINK_ENTITY_TO_CLASS( monster_osprey, COsprey );
|
||||
|
||||
TYPEDESCRIPTION COsprey::m_SaveData[] =
|
||||
{
|
||||
DEFINE_FIELD( COsprey, m_pGoalEnt, FIELD_CLASSPTR ),
|
||||
DEFINE_FIELD( COsprey, m_vel1, FIELD_VECTOR ),
|
||||
DEFINE_FIELD( COsprey, m_vel2, FIELD_VECTOR ),
|
||||
DEFINE_FIELD( COsprey, m_pos1, FIELD_POSITION_VECTOR ),
|
||||
DEFINE_FIELD( COsprey, m_pos2, FIELD_POSITION_VECTOR ),
|
||||
DEFINE_FIELD( COsprey, m_ang1, FIELD_VECTOR ),
|
||||
DEFINE_FIELD( COsprey, m_ang2, FIELD_VECTOR ),
|
||||
|
||||
DEFINE_FIELD( COsprey, m_startTime, FIELD_TIME ),
|
||||
DEFINE_FIELD( COsprey, m_dTime, FIELD_FLOAT ),
|
||||
DEFINE_FIELD( COsprey, m_velocity, FIELD_VECTOR ),
|
||||
|
||||
DEFINE_FIELD( COsprey, m_flIdealtilt, FIELD_FLOAT ),
|
||||
DEFINE_FIELD( COsprey, m_flRotortilt, FIELD_FLOAT ),
|
||||
|
||||
DEFINE_FIELD( COsprey, m_flRightHealth, FIELD_FLOAT ),
|
||||
DEFINE_FIELD( COsprey, m_flLeftHealth, FIELD_FLOAT ),
|
||||
|
||||
DEFINE_FIELD( COsprey, m_iUnits, FIELD_INTEGER ),
|
||||
DEFINE_ARRAY( COsprey, m_hGrunt, FIELD_EHANDLE, MAX_CARRY ),
|
||||
DEFINE_ARRAY( COsprey, m_vecOrigin, FIELD_POSITION_VECTOR, MAX_CARRY ),
|
||||
DEFINE_ARRAY( COsprey, m_hRepel, FIELD_EHANDLE, 4 ),
|
||||
|
||||
// DEFINE_FIELD( COsprey, m_iSoundState, FIELD_INTEGER ),
|
||||
// DEFINE_FIELD( COsprey, m_iSpriteTexture, FIELD_INTEGER ),
|
||||
// DEFINE_FIELD( COsprey, m_iPitch, FIELD_INTEGER ),
|
||||
|
||||
DEFINE_FIELD( COsprey, m_iDoLeftSmokePuff, FIELD_INTEGER ),
|
||||
DEFINE_FIELD( COsprey, m_iDoRightSmokePuff, FIELD_INTEGER ),
|
||||
};
|
||||
IMPLEMENT_SAVERESTORE( COsprey, CBaseMonster );
|
||||
|
||||
|
||||
void COsprey :: Spawn( void )
|
||||
{
|
||||
Precache( );
|
||||
// motor
|
||||
pev->movetype = MOVETYPE_FLY;
|
||||
pev->solid = SOLID_BBOX;
|
||||
|
||||
SET_MODEL(ENT(pev), "models/osprey.mdl");
|
||||
UTIL_SetSize(pev, Vector( -400, -400, -100), Vector(400, 400, 32));
|
||||
UTIL_SetOrigin( pev, pev->origin );
|
||||
|
||||
pev->flags |= FL_MONSTER;
|
||||
pev->takedamage = DAMAGE_YES;
|
||||
m_flRightHealth = 200;
|
||||
m_flLeftHealth = 200;
|
||||
pev->health = 400;
|
||||
|
||||
m_flFieldOfView = 0; // 180 degrees
|
||||
|
||||
pev->sequence = 0;
|
||||
ResetSequenceInfo( );
|
||||
pev->frame = RANDOM_LONG(0,0xFF);
|
||||
|
||||
InitBoneControllers();
|
||||
|
||||
SetThink( FindAllThink );
|
||||
SetUse( CommandUse );
|
||||
|
||||
if (!(pev->spawnflags & SF_WAITFORTRIGGER))
|
||||
{
|
||||
pev->nextthink = gpGlobals->time + 1.0;
|
||||
}
|
||||
|
||||
m_pos2 = pev->origin;
|
||||
m_ang2 = pev->angles;
|
||||
m_vel2 = pev->velocity;
|
||||
}
|
||||
|
||||
|
||||
void COsprey::Precache( void )
|
||||
{
|
||||
UTIL_PrecacheOther( "monster_human_grunt" );
|
||||
|
||||
PRECACHE_MODEL("models/osprey.mdl");
|
||||
PRECACHE_MODEL("models/HVR.mdl");
|
||||
|
||||
PRECACHE_SOUND("apache/ap_rotor4.wav");
|
||||
PRECACHE_SOUND("weapons/mortarhit.wav");
|
||||
|
||||
m_iSpriteTexture = PRECACHE_MODEL( "sprites/rope.spr" );
|
||||
|
||||
m_iExplode = PRECACHE_MODEL( "sprites/fexplo.spr" );
|
||||
m_iTailGibs = PRECACHE_MODEL( "models/osprey_tailgibs.mdl" );
|
||||
m_iBodyGibs = PRECACHE_MODEL( "models/osprey_bodygibs.mdl" );
|
||||
m_iEngineGibs = PRECACHE_MODEL( "models/osprey_enginegibs.mdl" );
|
||||
}
|
||||
|
||||
void COsprey::CommandUse( CBaseEntity *pActivator, CBaseEntity *pCaller, USE_TYPE useType, float value )
|
||||
{
|
||||
pev->nextthink = gpGlobals->time + 0.1;
|
||||
}
|
||||
|
||||
void COsprey :: FindAllThink( void )
|
||||
{
|
||||
CBaseEntity *pEntity = NULL;
|
||||
|
||||
m_iUnits = 0;
|
||||
while (m_iUnits < MAX_CARRY && (pEntity = UTIL_FindEntityByClassname( pEntity, "monster_human_grunt" )) != NULL)
|
||||
{
|
||||
if (pEntity->IsAlive())
|
||||
{
|
||||
m_hGrunt[m_iUnits] = pEntity;
|
||||
m_vecOrigin[m_iUnits] = pEntity->pev->origin;
|
||||
m_iUnits++;
|
||||
}
|
||||
}
|
||||
|
||||
if (m_iUnits == 0)
|
||||
{
|
||||
ALERT( at_console, "osprey error: no grunts to resupply\n");
|
||||
UTIL_Remove( this );
|
||||
return;
|
||||
}
|
||||
SetThink( FlyThink );
|
||||
pev->nextthink = gpGlobals->time + 0.1;
|
||||
m_startTime = gpGlobals->time;
|
||||
}
|
||||
|
||||
|
||||
void COsprey :: DeployThink( void )
|
||||
{
|
||||
UTIL_MakeAimVectors( pev->angles );
|
||||
|
||||
Vector vecForward = gpGlobals->v_forward;
|
||||
Vector vecRight = gpGlobals->v_right;
|
||||
Vector vecUp = gpGlobals->v_up;
|
||||
|
||||
Vector vecSrc;
|
||||
|
||||
TraceResult tr;
|
||||
UTIL_TraceLine( pev->origin, pev->origin + Vector( 0, 0, -4096.0), ignore_monsters, ENT(pev), &tr);
|
||||
CSoundEnt::InsertSound ( bits_SOUND_DANGER, tr.vecEndPos, 400, 0.3 );
|
||||
|
||||
vecSrc = pev->origin + vecForward * 32 + vecRight * 100 + vecUp * -96;
|
||||
m_hRepel[0] = MakeGrunt( vecSrc );
|
||||
|
||||
vecSrc = pev->origin + vecForward * -64 + vecRight * 100 + vecUp * -96;
|
||||
m_hRepel[1] = MakeGrunt( vecSrc );
|
||||
|
||||
vecSrc = pev->origin + vecForward * 32 + vecRight * -100 + vecUp * -96;
|
||||
m_hRepel[2] = MakeGrunt( vecSrc );
|
||||
|
||||
vecSrc = pev->origin + vecForward * -64 + vecRight * -100 + vecUp * -96;
|
||||
m_hRepel[3] = MakeGrunt( vecSrc );
|
||||
|
||||
SetThink( HoverThink );
|
||||
pev->nextthink = gpGlobals->time + 0.1;
|
||||
}
|
||||
|
||||
|
||||
|
||||
BOOL COsprey :: HasDead( )
|
||||
{
|
||||
for (int i = 0; i < m_iUnits; i++)
|
||||
{
|
||||
if (m_hGrunt[i] == NULL || !m_hGrunt[i]->IsAlive())
|
||||
{
|
||||
return TRUE;
|
||||
}
|
||||
else
|
||||
{
|
||||
m_vecOrigin[i] = m_hGrunt[i]->pev->origin; // send them to where they died
|
||||
}
|
||||
}
|
||||
return FALSE;
|
||||
}
|
||||
|
||||
|
||||
CBaseMonster *COsprey :: MakeGrunt( Vector vecSrc )
|
||||
{
|
||||
CBaseEntity *pEntity;
|
||||
CBaseMonster *pGrunt;
|
||||
|
||||
TraceResult tr;
|
||||
UTIL_TraceLine( vecSrc, vecSrc + Vector( 0, 0, -4096.0), dont_ignore_monsters, ENT(pev), &tr);
|
||||
if ( tr.pHit && Instance( tr.pHit )->pev->solid != SOLID_BSP)
|
||||
return NULL;
|
||||
|
||||
for (int i = 0; i < m_iUnits; i++)
|
||||
{
|
||||
if (m_hGrunt[i] == NULL || !m_hGrunt[i]->IsAlive())
|
||||
{
|
||||
if (m_hGrunt[i] != NULL && m_hGrunt[i]->pev->rendermode == kRenderNormal)
|
||||
{
|
||||
m_hGrunt[i]->SUB_StartFadeOut( );
|
||||
}
|
||||
pEntity = Create( "monster_human_grunt", vecSrc, pev->angles );
|
||||
pGrunt = pEntity->MyMonsterPointer( );
|
||||
pGrunt->pev->movetype = MOVETYPE_FLY;
|
||||
pGrunt->pev->velocity = Vector( 0, 0, RANDOM_FLOAT( -196, -128 ) );
|
||||
pGrunt->SetActivity( ACT_GLIDE );
|
||||
|
||||
CBeam *pBeam = CBeam::BeamCreate( "sprites/rope.spr", 10 );
|
||||
pBeam->PointEntInit( vecSrc + Vector(0,0,112), pGrunt->edict() );
|
||||
pBeam->SetFlags( FBEAM_SOLID );
|
||||
pBeam->SetColor( 255, 255, 255 );
|
||||
pBeam->SetThink( SUB_Remove );
|
||||
pBeam->pev->nextthink = gpGlobals->time + -4096.0 * tr.flFraction / pGrunt->pev->velocity.z + 0.5;
|
||||
|
||||
// ALERT( at_console, "%d at %.0f %.0f %.0f\n", i, m_vecOrigin[i].x, m_vecOrigin[i].y, m_vecOrigin[i].z );
|
||||
pGrunt->m_vecLastPosition = m_vecOrigin[i];
|
||||
m_hGrunt[i] = pGrunt;
|
||||
return pGrunt;
|
||||
}
|
||||
}
|
||||
// ALERT( at_console, "none dead\n");
|
||||
return NULL;
|
||||
}
|
||||
|
||||
|
||||
void COsprey :: HoverThink( void )
|
||||
{
|
||||
int i;
|
||||
for (i = 0; i < 4; i++)
|
||||
{
|
||||
if (m_hRepel[i] != NULL && m_hRepel[i]->pev->health > 0 && !(m_hRepel[i]->pev->flags & FL_ONGROUND))
|
||||
{
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if (i == 4)
|
||||
{
|
||||
m_startTime = gpGlobals->time;
|
||||
SetThink( FlyThink );
|
||||
}
|
||||
|
||||
pev->nextthink = gpGlobals->time + 0.1;
|
||||
UTIL_MakeAimVectors( pev->angles );
|
||||
ShowDamage( );
|
||||
}
|
||||
|
||||
|
||||
void COsprey::UpdateGoal( )
|
||||
{
|
||||
if (m_pGoalEnt)
|
||||
{
|
||||
m_pos1 = m_pos2;
|
||||
m_ang1 = m_ang2;
|
||||
m_vel1 = m_vel2;
|
||||
m_pos2 = m_pGoalEnt->pev->origin;
|
||||
m_ang2 = m_pGoalEnt->pev->angles;
|
||||
UTIL_MakeAimVectors( Vector( 0, m_ang2.y, 0 ) );
|
||||
m_vel2 = gpGlobals->v_forward * m_pGoalEnt->pev->speed;
|
||||
|
||||
m_startTime = m_startTime + m_dTime;
|
||||
m_dTime = 2.0 * (m_pos1 - m_pos2).Length() / (m_vel1.Length() + m_pGoalEnt->pev->speed);
|
||||
|
||||
if (m_ang1.y - m_ang2.y < -180)
|
||||
{
|
||||
m_ang1.y += 360;
|
||||
}
|
||||
else if (m_ang1.y - m_ang2.y > 180)
|
||||
{
|
||||
m_ang1.y -= 360;
|
||||
}
|
||||
|
||||
if (m_pGoalEnt->pev->speed < 400)
|
||||
m_flIdealtilt = 0;
|
||||
else
|
||||
m_flIdealtilt = -90;
|
||||
}
|
||||
else
|
||||
{
|
||||
ALERT( at_console, "osprey missing target");
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
void COsprey::FlyThink( void )
|
||||
{
|
||||
StudioFrameAdvance( );
|
||||
pev->nextthink = gpGlobals->time + 0.1;
|
||||
|
||||
if ( m_pGoalEnt == NULL && !FStringNull(pev->target) )// this monster has a target
|
||||
{
|
||||
m_pGoalEnt = CBaseEntity::Instance( FIND_ENTITY_BY_TARGETNAME ( NULL, STRING( pev->target ) ) );
|
||||
UpdateGoal( );
|
||||
}
|
||||
|
||||
if (gpGlobals->time > m_startTime + m_dTime)
|
||||
{
|
||||
if (m_pGoalEnt->pev->speed == 0)
|
||||
{
|
||||
SetThink( DeployThink );
|
||||
}
|
||||
do {
|
||||
m_pGoalEnt = CBaseEntity::Instance( FIND_ENTITY_BY_TARGETNAME ( NULL, STRING( m_pGoalEnt->pev->target ) ) );
|
||||
} while (m_pGoalEnt->pev->speed < 400 && !HasDead());
|
||||
UpdateGoal( );
|
||||
}
|
||||
|
||||
Flight( );
|
||||
ShowDamage( );
|
||||
}
|
||||
|
||||
|
||||
void COsprey::Flight( )
|
||||
{
|
||||
float t = (gpGlobals->time - m_startTime);
|
||||
float scale = 1.0 / m_dTime;
|
||||
|
||||
float f = UTIL_SplineFraction( t * scale, 1.0 );
|
||||
|
||||
Vector pos = (m_pos1 + m_vel1 * t) * (1.0 - f) + (m_pos2 - m_vel2 * (m_dTime - t)) * f;
|
||||
Vector ang = (m_ang1) * (1.0 - f) + (m_ang2) * f;
|
||||
m_velocity = m_vel1 * (1.0 - f) + m_vel2 * f;
|
||||
|
||||
UTIL_SetOrigin( pev, pos );
|
||||
pev->angles = ang;
|
||||
UTIL_MakeAimVectors( pev->angles );
|
||||
float flSpeed = DotProduct( gpGlobals->v_forward, m_velocity );
|
||||
|
||||
// float flSpeed = DotProduct( gpGlobals->v_forward, pev->velocity );
|
||||
|
||||
float m_flIdealtilt = (160 - flSpeed) / 10.0;
|
||||
|
||||
// ALERT( at_console, "%f %f\n", flSpeed, flIdealtilt );
|
||||
if (m_flRotortilt < m_flIdealtilt)
|
||||
{
|
||||
m_flRotortilt += 0.5;
|
||||
if (m_flRotortilt > 0)
|
||||
m_flRotortilt = 0;
|
||||
}
|
||||
if (m_flRotortilt > m_flIdealtilt)
|
||||
{
|
||||
m_flRotortilt -= 0.5;
|
||||
if (m_flRotortilt < -90)
|
||||
m_flRotortilt = -90;
|
||||
}
|
||||
SetBoneController( 0, m_flRotortilt );
|
||||
|
||||
|
||||
if (m_iSoundState == 0)
|
||||
{
|
||||
EMIT_SOUND_DYN(ENT(pev), CHAN_STATIC, "apache/ap_rotor4.wav", 1.0, 0.15, 0, 110 );
|
||||
// EMIT_SOUND_DYN(ENT(pev), CHAN_STATIC, "apache/ap_whine1.wav", 0.5, 0.2, 0, 110 );
|
||||
|
||||
m_iSoundState = SND_CHANGE_PITCH; // hack for going through level transitions
|
||||
}
|
||||
else
|
||||
{
|
||||
CBaseEntity *pPlayer = NULL;
|
||||
|
||||
pPlayer = UTIL_FindEntityByClassname( NULL, "player" );
|
||||
// UNDONE: this needs to send different sounds to every player for multiplayer.
|
||||
if (pPlayer)
|
||||
{
|
||||
float pitch = DotProduct( m_velocity - pPlayer->pev->velocity, (pPlayer->pev->origin - pev->origin).Normalize() );
|
||||
|
||||
pitch = (int)(100 + pitch / 75.0);
|
||||
|
||||
if (pitch > 250)
|
||||
pitch = 250;
|
||||
if (pitch < 50)
|
||||
pitch = 50;
|
||||
|
||||
if (pitch == 100)
|
||||
pitch = 101;
|
||||
|
||||
if (pitch != m_iPitch)
|
||||
{
|
||||
m_iPitch = pitch;
|
||||
EMIT_SOUND_DYN(ENT(pev), CHAN_STATIC, "apache/ap_rotor4.wav", 1.0, 0.15, SND_CHANGE_PITCH | SND_CHANGE_VOL, pitch);
|
||||
// ALERT( at_console, "%.0f\n", pitch );
|
||||
}
|
||||
}
|
||||
// EMIT_SOUND_DYN(ENT(pev), CHAN_STATIC, "apache/ap_whine1.wav", flVol, 0.2, SND_CHANGE_PITCH | SND_CHANGE_VOL, pitch);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
|
||||
void COsprey::HitTouch( CBaseEntity *pOther )
|
||||
{
|
||||
pev->nextthink = gpGlobals->time + 2.0;
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
int COsprey::TakeDamage( entvars_t *pevInflictor, entvars_t *pevAttacker, float flDamage, int bitsDamageType )
|
||||
{
|
||||
if (m_flRotortilt <= -90)
|
||||
{
|
||||
m_flRotortilt = 0;
|
||||
}
|
||||
else
|
||||
{
|
||||
m_flRotortilt -= 45;
|
||||
}
|
||||
SetBoneController( 0, m_flRotortilt );
|
||||
return 0;
|
||||
}
|
||||
*/
|
||||
|
||||
|
||||
|
||||
void COsprey :: Killed( entvars_t *pevAttacker, int iGib )
|
||||
{
|
||||
pev->movetype = MOVETYPE_TOSS;
|
||||
pev->gravity = 0.3;
|
||||
pev->velocity = m_velocity;
|
||||
pev->avelocity = Vector( RANDOM_FLOAT( -20, 20 ), 0, RANDOM_FLOAT( -50, 50 ) );
|
||||
STOP_SOUND( ENT(pev), CHAN_STATIC, "apache/ap_rotor4.wav" );
|
||||
|
||||
UTIL_SetSize( pev, Vector( -32, -32, -64), Vector( 32, 32, 0) );
|
||||
SetThink( DyingThink );
|
||||
SetTouch( CrashTouch );
|
||||
pev->nextthink = gpGlobals->time + 0.1;
|
||||
pev->health = 0;
|
||||
pev->takedamage = DAMAGE_NO;
|
||||
|
||||
m_startTime = gpGlobals->time + 4.0;
|
||||
}
|
||||
|
||||
void COsprey::CrashTouch( CBaseEntity *pOther )
|
||||
{
|
||||
// only crash if we hit something solid
|
||||
if ( pOther->pev->solid == SOLID_BSP)
|
||||
{
|
||||
SetTouch( NULL );
|
||||
m_startTime = gpGlobals->time;
|
||||
pev->nextthink = gpGlobals->time;
|
||||
m_velocity = pev->velocity;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
void COsprey :: DyingThink( void )
|
||||
{
|
||||
StudioFrameAdvance( );
|
||||
pev->nextthink = gpGlobals->time + 0.1;
|
||||
|
||||
pev->avelocity = pev->avelocity * 1.02;
|
||||
|
||||
// still falling?
|
||||
if (m_startTime > gpGlobals->time )
|
||||
{
|
||||
UTIL_MakeAimVectors( pev->angles );
|
||||
ShowDamage( );
|
||||
|
||||
Vector vecSpot = pev->origin + pev->velocity * 0.2;
|
||||
|
||||
// random explosions
|
||||
MESSAGE_BEGIN( MSG_PVS, SVC_TEMPENTITY, vecSpot );
|
||||
WRITE_BYTE( TE_EXPLOSION); // This just makes a dynamic light now
|
||||
WRITE_COORD( vecSpot.x + RANDOM_FLOAT( -150, 150 ));
|
||||
WRITE_COORD( vecSpot.y + RANDOM_FLOAT( -150, 150 ));
|
||||
WRITE_COORD( vecSpot.z + RANDOM_FLOAT( -150, -50 ));
|
||||
WRITE_SHORT( g_sModelIndexFireball );
|
||||
WRITE_BYTE( RANDOM_LONG(0,29) + 30 ); // scale * 10
|
||||
WRITE_BYTE( 12 ); // framerate
|
||||
WRITE_BYTE( TE_EXPLFLAG_NONE );
|
||||
MESSAGE_END();
|
||||
|
||||
// lots of smoke
|
||||
MESSAGE_BEGIN( MSG_PVS, SVC_TEMPENTITY, vecSpot );
|
||||
WRITE_BYTE( TE_SMOKE );
|
||||
WRITE_COORD( vecSpot.x + RANDOM_FLOAT( -150, 150 ));
|
||||
WRITE_COORD( vecSpot.y + RANDOM_FLOAT( -150, 150 ));
|
||||
WRITE_COORD( vecSpot.z + RANDOM_FLOAT( -150, -50 ));
|
||||
WRITE_SHORT( g_sModelIndexSmoke );
|
||||
WRITE_BYTE( 100 ); // scale * 10
|
||||
WRITE_BYTE( 10 ); // framerate
|
||||
MESSAGE_END();
|
||||
|
||||
|
||||
vecSpot = pev->origin + (pev->mins + pev->maxs) * 0.5;
|
||||
MESSAGE_BEGIN( MSG_PVS, SVC_TEMPENTITY, vecSpot );
|
||||
WRITE_BYTE( TE_BREAKMODEL);
|
||||
|
||||
// position
|
||||
WRITE_COORD( vecSpot.x );
|
||||
WRITE_COORD( vecSpot.y );
|
||||
WRITE_COORD( vecSpot.z );
|
||||
|
||||
// size
|
||||
WRITE_COORD( 800 );
|
||||
WRITE_COORD( 800 );
|
||||
WRITE_COORD( 132 );
|
||||
|
||||
// velocity
|
||||
WRITE_COORD( pev->velocity.x );
|
||||
WRITE_COORD( pev->velocity.y );
|
||||
WRITE_COORD( pev->velocity.z );
|
||||
|
||||
// randomization
|
||||
WRITE_BYTE( 50 );
|
||||
|
||||
// Model
|
||||
WRITE_SHORT( m_iTailGibs ); //model id#
|
||||
|
||||
// # of shards
|
||||
WRITE_BYTE( 8 ); // let client decide
|
||||
|
||||
// duration
|
||||
WRITE_BYTE( 200 );// 10.0 seconds
|
||||
|
||||
// flags
|
||||
|
||||
WRITE_BYTE( BREAK_METAL );
|
||||
MESSAGE_END();
|
||||
|
||||
|
||||
|
||||
// don't stop it we touch a entity
|
||||
pev->flags &= ~FL_ONGROUND;
|
||||
pev->nextthink = gpGlobals->time + 0.2;
|
||||
return;
|
||||
}
|
||||
else
|
||||
{
|
||||
Vector vecSpot = pev->origin + (pev->mins + pev->maxs) * 0.5;
|
||||
|
||||
/*
|
||||
MESSAGE_BEGIN( MSG_BROADCAST, SVC_TEMPENTITY );
|
||||
WRITE_BYTE( TE_EXPLOSION); // This just makes a dynamic light now
|
||||
WRITE_COORD( vecSpot.x );
|
||||
WRITE_COORD( vecSpot.y );
|
||||
WRITE_COORD( vecSpot.z + 512 );
|
||||
WRITE_SHORT( m_iExplode );
|
||||
WRITE_BYTE( 250 ); // scale * 10
|
||||
WRITE_BYTE( 10 ); // framerate
|
||||
MESSAGE_END();
|
||||
*/
|
||||
|
||||
// gibs
|
||||
MESSAGE_BEGIN( MSG_PVS, SVC_TEMPENTITY, vecSpot );
|
||||
WRITE_BYTE( TE_SPRITE );
|
||||
WRITE_COORD( vecSpot.x );
|
||||
WRITE_COORD( vecSpot.y );
|
||||
WRITE_COORD( vecSpot.z + 512 );
|
||||
WRITE_SHORT( m_iExplode );
|
||||
WRITE_BYTE( 250 ); // scale * 10
|
||||
WRITE_BYTE( 255 ); // brightness
|
||||
MESSAGE_END();
|
||||
|
||||
/*
|
||||
MESSAGE_BEGIN( MSG_BROADCAST, SVC_TEMPENTITY );
|
||||
WRITE_BYTE( TE_SMOKE );
|
||||
WRITE_COORD( vecSpot.x );
|
||||
WRITE_COORD( vecSpot.y );
|
||||
WRITE_COORD( vecSpot.z + 300 );
|
||||
WRITE_SHORT( g_sModelIndexSmoke );
|
||||
WRITE_BYTE( 250 ); // scale * 10
|
||||
WRITE_BYTE( 6 ); // framerate
|
||||
MESSAGE_END();
|
||||
*/
|
||||
|
||||
// blast circle
|
||||
MESSAGE_BEGIN( MSG_PAS, SVC_TEMPENTITY, pev->origin );
|
||||
WRITE_BYTE( TE_BEAMCYLINDER );
|
||||
WRITE_COORD( pev->origin.x);
|
||||
WRITE_COORD( pev->origin.y);
|
||||
WRITE_COORD( pev->origin.z);
|
||||
WRITE_COORD( pev->origin.x);
|
||||
WRITE_COORD( pev->origin.y);
|
||||
WRITE_COORD( pev->origin.z + 2000 ); // reach damage radius over .2 seconds
|
||||
WRITE_SHORT( m_iSpriteTexture );
|
||||
WRITE_BYTE( 0 ); // startframe
|
||||
WRITE_BYTE( 0 ); // framerate
|
||||
WRITE_BYTE( 4 ); // life
|
||||
WRITE_BYTE( 32 ); // width
|
||||
WRITE_BYTE( 0 ); // noise
|
||||
WRITE_BYTE( 255 ); // r, g, b
|
||||
WRITE_BYTE( 255 ); // r, g, b
|
||||
WRITE_BYTE( 192 ); // r, g, b
|
||||
WRITE_BYTE( 128 ); // brightness
|
||||
WRITE_BYTE( 0 ); // speed
|
||||
MESSAGE_END();
|
||||
|
||||
EMIT_SOUND(ENT(pev), CHAN_STATIC, "weapons/mortarhit.wav", 1.0, 0.3);
|
||||
|
||||
RadiusDamage( pev->origin, pev, pev, 300, CLASS_NONE, DMG_BLAST );
|
||||
|
||||
// gibs
|
||||
vecSpot = pev->origin + (pev->mins + pev->maxs) * 0.5;
|
||||
MESSAGE_BEGIN( MSG_PAS, SVC_TEMPENTITY, vecSpot );
|
||||
WRITE_BYTE( TE_BREAKMODEL);
|
||||
|
||||
// position
|
||||
WRITE_COORD( vecSpot.x );
|
||||
WRITE_COORD( vecSpot.y );
|
||||
WRITE_COORD( vecSpot.z + 64);
|
||||
|
||||
// size
|
||||
WRITE_COORD( 800 );
|
||||
WRITE_COORD( 800 );
|
||||
WRITE_COORD( 128 );
|
||||
|
||||
// velocity
|
||||
WRITE_COORD( m_velocity.x );
|
||||
WRITE_COORD( m_velocity.y );
|
||||
WRITE_COORD( fabs( m_velocity.z ) * 0.25 );
|
||||
|
||||
// randomization
|
||||
WRITE_BYTE( 40 );
|
||||
|
||||
// Model
|
||||
WRITE_SHORT( m_iBodyGibs ); //model id#
|
||||
|
||||
// # of shards
|
||||
WRITE_BYTE( 128 );
|
||||
|
||||
// duration
|
||||
WRITE_BYTE( 200 );// 10.0 seconds
|
||||
|
||||
// flags
|
||||
|
||||
WRITE_BYTE( BREAK_METAL );
|
||||
MESSAGE_END();
|
||||
|
||||
UTIL_Remove( this );
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
void COsprey :: ShowDamage( void )
|
||||
{
|
||||
if (m_iDoLeftSmokePuff > 0 || RANDOM_LONG(0,99) > m_flLeftHealth)
|
||||
{
|
||||
Vector vecSrc = pev->origin + gpGlobals->v_right * -340;
|
||||
MESSAGE_BEGIN( MSG_PVS, SVC_TEMPENTITY, vecSrc );
|
||||
WRITE_BYTE( TE_SMOKE );
|
||||
WRITE_COORD( vecSrc.x );
|
||||
WRITE_COORD( vecSrc.y );
|
||||
WRITE_COORD( vecSrc.z );
|
||||
WRITE_SHORT( g_sModelIndexSmoke );
|
||||
WRITE_BYTE( RANDOM_LONG(0,9) + 20 ); // scale * 10
|
||||
WRITE_BYTE( 12 ); // framerate
|
||||
MESSAGE_END();
|
||||
if (m_iDoLeftSmokePuff > 0)
|
||||
m_iDoLeftSmokePuff--;
|
||||
}
|
||||
if (m_iDoRightSmokePuff > 0 || RANDOM_LONG(0,99) > m_flRightHealth)
|
||||
{
|
||||
Vector vecSrc = pev->origin + gpGlobals->v_right * 340;
|
||||
MESSAGE_BEGIN( MSG_PVS, SVC_TEMPENTITY, vecSrc );
|
||||
WRITE_BYTE( TE_SMOKE );
|
||||
WRITE_COORD( vecSrc.x );
|
||||
WRITE_COORD( vecSrc.y );
|
||||
WRITE_COORD( vecSrc.z );
|
||||
WRITE_SHORT( g_sModelIndexSmoke );
|
||||
WRITE_BYTE( RANDOM_LONG(0,9) + 20 ); // scale * 10
|
||||
WRITE_BYTE( 12 ); // framerate
|
||||
MESSAGE_END();
|
||||
if (m_iDoRightSmokePuff > 0)
|
||||
m_iDoRightSmokePuff--;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
void COsprey::TraceAttack( entvars_t *pevAttacker, float flDamage, Vector vecDir, TraceResult *ptr, int bitsDamageType)
|
||||
{
|
||||
// ALERT( at_console, "%d %.0f\n", ptr->iHitgroup, flDamage );
|
||||
|
||||
// only so much per engine
|
||||
if (ptr->iHitgroup == 3)
|
||||
{
|
||||
if (m_flRightHealth < 0)
|
||||
return;
|
||||
else
|
||||
m_flRightHealth -= flDamage;
|
||||
m_iDoLeftSmokePuff = 3 + (flDamage / 5.0);
|
||||
}
|
||||
|
||||
if (ptr->iHitgroup == 2)
|
||||
{
|
||||
if (m_flLeftHealth < 0)
|
||||
return;
|
||||
else
|
||||
m_flLeftHealth -= flDamage;
|
||||
m_iDoRightSmokePuff = 3 + (flDamage / 5.0);
|
||||
}
|
||||
|
||||
// hit hard, hits cockpit, hits engines
|
||||
if (flDamage > 50 || ptr->iHitgroup == 1 || ptr->iHitgroup == 2 || ptr->iHitgroup == 3)
|
||||
{
|
||||
// ALERT( at_console, "%.0f\n", flDamage );
|
||||
AddMultiDamage( pevAttacker, this, flDamage, bitsDamageType );
|
||||
}
|
||||
else
|
||||
{
|
||||
UTIL_Sparks( ptr->vecEndPos );
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
|
|
@ -0,0 +1,429 @@
|
|||
/***
|
||||
*
|
||||
* 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.
|
||||
*
|
||||
****/
|
||||
//
|
||||
// ========================== PATH_CORNER ===========================
|
||||
//
|
||||
|
||||
#include "extdll.h"
|
||||
#include "util.h"
|
||||
#include "cbase.h"
|
||||
#include "trains.h"
|
||||
#include "saverestore.h"
|
||||
|
||||
class CPathCorner : public CPointEntity
|
||||
{
|
||||
public:
|
||||
void Spawn( );
|
||||
void KeyValue( KeyValueData* pkvd );
|
||||
float GetDelay( void ) { return m_flWait; }
|
||||
// void Touch( CBaseEntity *pOther );
|
||||
virtual int Save( CSave &save );
|
||||
virtual int Restore( CRestore &restore );
|
||||
|
||||
static TYPEDESCRIPTION m_SaveData[];
|
||||
|
||||
private:
|
||||
float m_flWait;
|
||||
};
|
||||
|
||||
LINK_ENTITY_TO_CLASS( path_corner, CPathCorner );
|
||||
|
||||
// Global Savedata for Delay
|
||||
TYPEDESCRIPTION CPathCorner::m_SaveData[] =
|
||||
{
|
||||
DEFINE_FIELD( CPathCorner, m_flWait, FIELD_FLOAT ),
|
||||
};
|
||||
|
||||
IMPLEMENT_SAVERESTORE( CPathCorner, CPointEntity );
|
||||
|
||||
//
|
||||
// Cache user-entity-field values until spawn is called.
|
||||
//
|
||||
void CPathCorner :: KeyValue( KeyValueData *pkvd )
|
||||
{
|
||||
if (FStrEq(pkvd->szKeyName, "wait"))
|
||||
{
|
||||
m_flWait = atof(pkvd->szValue);
|
||||
pkvd->fHandled = TRUE;
|
||||
}
|
||||
else
|
||||
CPointEntity::KeyValue( pkvd );
|
||||
}
|
||||
|
||||
|
||||
void CPathCorner :: Spawn( )
|
||||
{
|
||||
ASSERTSZ(!FStringNull(pev->targetname), "path_corner without a targetname");
|
||||
}
|
||||
|
||||
#if 0
|
||||
void CPathCorner :: Touch( CBaseEntity *pOther )
|
||||
{
|
||||
entvars_t* pevToucher = pOther->pev;
|
||||
|
||||
if ( FBitSet ( pevToucher->flags, FL_MONSTER ) )
|
||||
{// monsters don't navigate path corners based on touch anymore
|
||||
return;
|
||||
}
|
||||
|
||||
// If OTHER isn't explicitly looking for this path_corner, bail out
|
||||
if ( pOther->m_pGoalEnt != this )
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
// If OTHER has an enemy, this touch is incidental, ignore
|
||||
if ( !FNullEnt(pevToucher->enemy) )
|
||||
{
|
||||
return; // fighting, not following a path
|
||||
}
|
||||
|
||||
// UNDONE: support non-zero flWait
|
||||
/*
|
||||
if (m_flWait != 0)
|
||||
ALERT(at_warning, "Non-zero path-cornder waits NYI");
|
||||
*/
|
||||
|
||||
// Find the next "stop" on the path, make it the goal of the "toucher".
|
||||
if (FStringNull(pev->target))
|
||||
{
|
||||
ALERT(at_warning, "PathCornerTouch: no next stop specified");
|
||||
}
|
||||
|
||||
pOther->m_pGoalEnt = CBaseEntity::Instance( FIND_ENTITY_BY_TARGETNAME ( NULL, STRING(pev->target) ) );
|
||||
|
||||
// If "next spot" was not found (does not exist - level design error)
|
||||
if ( !pOther->m_pGoalEnt )
|
||||
{
|
||||
ALERT(at_console, "PathCornerTouch--%s couldn't find next stop in path: %s", STRING(pev->classname), STRING(pev->target));
|
||||
return;
|
||||
}
|
||||
|
||||
// Turn towards the next stop in the path.
|
||||
pevToucher->ideal_yaw = UTIL_VecToYaw ( pOther->m_pGoalEnt->pev->origin - pevToucher->origin );
|
||||
}
|
||||
#endif
|
||||
|
||||
|
||||
|
||||
TYPEDESCRIPTION CPathTrack::m_SaveData[] =
|
||||
{
|
||||
DEFINE_FIELD( CPathTrack, m_length, FIELD_FLOAT ),
|
||||
DEFINE_FIELD( CPathTrack, m_pnext, FIELD_CLASSPTR ),
|
||||
DEFINE_FIELD( CPathTrack, m_paltpath, FIELD_CLASSPTR ),
|
||||
DEFINE_FIELD( CPathTrack, m_pprevious, FIELD_CLASSPTR ),
|
||||
DEFINE_FIELD( CPathTrack, m_altName, FIELD_STRING ),
|
||||
};
|
||||
|
||||
IMPLEMENT_SAVERESTORE( CPathTrack, CBaseEntity );
|
||||
LINK_ENTITY_TO_CLASS( path_track, CPathTrack );
|
||||
|
||||
//
|
||||
// Cache user-entity-field values until spawn is called.
|
||||
//
|
||||
void CPathTrack :: KeyValue( KeyValueData *pkvd )
|
||||
{
|
||||
if (FStrEq(pkvd->szKeyName, "altpath"))
|
||||
{
|
||||
m_altName = ALLOC_STRING(pkvd->szValue);
|
||||
pkvd->fHandled = TRUE;
|
||||
}
|
||||
else
|
||||
CPointEntity::KeyValue( pkvd );
|
||||
}
|
||||
|
||||
void CPathTrack :: Use( CBaseEntity *pActivator, CBaseEntity *pCaller, USE_TYPE useType, float value )
|
||||
{
|
||||
int on;
|
||||
|
||||
// Use toggles between two paths
|
||||
if ( m_paltpath )
|
||||
{
|
||||
on = !FBitSet( pev->spawnflags, SF_PATH_ALTERNATE );
|
||||
if ( ShouldToggle( useType, on ) )
|
||||
{
|
||||
if ( on )
|
||||
SetBits( pev->spawnflags, SF_PATH_ALTERNATE );
|
||||
else
|
||||
ClearBits( pev->spawnflags, SF_PATH_ALTERNATE );
|
||||
}
|
||||
}
|
||||
else // Use toggles between enabled/disabled
|
||||
{
|
||||
on = !FBitSet( pev->spawnflags, SF_PATH_DISABLED );
|
||||
|
||||
if ( ShouldToggle( useType, on ) )
|
||||
{
|
||||
if ( on )
|
||||
SetBits( pev->spawnflags, SF_PATH_DISABLED );
|
||||
else
|
||||
ClearBits( pev->spawnflags, SF_PATH_DISABLED );
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
void CPathTrack :: Link( void )
|
||||
{
|
||||
edict_t *pentTarget;
|
||||
|
||||
if ( !FStringNull(pev->target) )
|
||||
{
|
||||
pentTarget = FIND_ENTITY_BY_TARGETNAME( NULL, STRING(pev->target) );
|
||||
if ( !FNullEnt(pentTarget) )
|
||||
{
|
||||
m_pnext = CPathTrack::Instance( pentTarget );
|
||||
|
||||
if ( m_pnext ) // If no next pointer, this is the end of a path
|
||||
{
|
||||
m_pnext->SetPrevious( this );
|
||||
}
|
||||
}
|
||||
else
|
||||
ALERT( at_console, "Dead end link %s\n", STRING(pev->target) );
|
||||
}
|
||||
|
||||
// Find "alternate" path
|
||||
if ( m_altName )
|
||||
{
|
||||
pentTarget = FIND_ENTITY_BY_TARGETNAME( NULL, STRING(m_altName) );
|
||||
if ( !FNullEnt(pentTarget) )
|
||||
{
|
||||
m_paltpath = CPathTrack::Instance( pentTarget );
|
||||
|
||||
if ( m_paltpath ) // If no next pointer, this is the end of a path
|
||||
{
|
||||
m_paltpath->SetPrevious( this );
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
void CPathTrack :: Spawn( void )
|
||||
{
|
||||
pev->solid = SOLID_TRIGGER;
|
||||
UTIL_SetSize(pev, Vector(-8, -8, -8), Vector(8, 8, 8));
|
||||
|
||||
m_pnext = NULL;
|
||||
m_pprevious = NULL;
|
||||
// DEBUGGING CODE
|
||||
#if PATH_SPARKLE_DEBUG
|
||||
SetThink( Sparkle );
|
||||
pev->nextthink = gpGlobals->time + 0.5;
|
||||
#endif
|
||||
}
|
||||
|
||||
|
||||
void CPathTrack::Activate( void )
|
||||
{
|
||||
if ( !FStringNull( pev->targetname ) ) // Link to next, and back-link
|
||||
Link();
|
||||
}
|
||||
|
||||
CPathTrack *CPathTrack :: ValidPath( CPathTrack *ppath, int testFlag )
|
||||
{
|
||||
if ( !ppath )
|
||||
return NULL;
|
||||
|
||||
if ( testFlag && FBitSet( ppath->pev->spawnflags, SF_PATH_DISABLED ) )
|
||||
return NULL;
|
||||
|
||||
return ppath;
|
||||
}
|
||||
|
||||
|
||||
void CPathTrack :: Project( CPathTrack *pstart, CPathTrack *pend, Vector *origin, float dist )
|
||||
{
|
||||
if ( pstart && pend )
|
||||
{
|
||||
Vector dir = (pend->pev->origin - pstart->pev->origin);
|
||||
dir = dir.Normalize();
|
||||
*origin = pend->pev->origin + dir * dist;
|
||||
}
|
||||
}
|
||||
|
||||
CPathTrack *CPathTrack::GetNext( void )
|
||||
{
|
||||
if ( m_paltpath && FBitSet( pev->spawnflags, SF_PATH_ALTERNATE ) && !FBitSet( pev->spawnflags, SF_PATH_ALTREVERSE ) )
|
||||
return m_paltpath;
|
||||
|
||||
return m_pnext;
|
||||
}
|
||||
|
||||
|
||||
|
||||
CPathTrack *CPathTrack::GetPrevious( void )
|
||||
{
|
||||
if ( m_paltpath && FBitSet( pev->spawnflags, SF_PATH_ALTERNATE ) && FBitSet( pev->spawnflags, SF_PATH_ALTREVERSE ) )
|
||||
return m_paltpath;
|
||||
|
||||
return m_pprevious;
|
||||
}
|
||||
|
||||
|
||||
|
||||
void CPathTrack::SetPrevious( CPathTrack *pprev )
|
||||
{
|
||||
// Only set previous if this isn't my alternate path
|
||||
if ( pprev && !FStrEq( STRING(pprev->pev->targetname), STRING(m_altName) ) )
|
||||
m_pprevious = pprev;
|
||||
}
|
||||
|
||||
|
||||
// Assumes this is ALWAYS enabled
|
||||
CPathTrack *CPathTrack :: LookAhead( Vector *origin, float dist, int move )
|
||||
{
|
||||
CPathTrack *pcurrent;
|
||||
float originalDist = dist;
|
||||
|
||||
pcurrent = this;
|
||||
Vector currentPos = *origin;
|
||||
|
||||
if ( dist < 0 ) // Travelling backwards through path
|
||||
{
|
||||
dist = -dist;
|
||||
while ( dist > 0 )
|
||||
{
|
||||
Vector dir = pcurrent->pev->origin - currentPos;
|
||||
float length = dir.Length();
|
||||
if ( !length )
|
||||
{
|
||||
if ( !ValidPath(pcurrent->GetPrevious(), move) ) // If there is no previous node, or it's disabled, return now.
|
||||
{
|
||||
if ( !move )
|
||||
Project( pcurrent->GetNext(), pcurrent, origin, dist );
|
||||
return NULL;
|
||||
}
|
||||
pcurrent = pcurrent->GetPrevious();
|
||||
}
|
||||
else if ( length > dist ) // enough left in this path to move
|
||||
{
|
||||
*origin = currentPos + (dir * (dist / length));
|
||||
return pcurrent;
|
||||
}
|
||||
else
|
||||
{
|
||||
dist -= length;
|
||||
currentPos = pcurrent->pev->origin;
|
||||
*origin = currentPos;
|
||||
if ( !ValidPath(pcurrent->GetPrevious(), move) ) // If there is no previous node, or it's disabled, return now.
|
||||
return NULL;
|
||||
|
||||
pcurrent = pcurrent->GetPrevious();
|
||||
}
|
||||
}
|
||||
*origin = currentPos;
|
||||
return pcurrent;
|
||||
}
|
||||
else
|
||||
{
|
||||
while ( dist > 0 )
|
||||
{
|
||||
if ( !ValidPath(pcurrent->GetNext(), move) ) // If there is no next node, or it's disabled, return now.
|
||||
{
|
||||
if ( !move )
|
||||
Project( pcurrent->GetPrevious(), pcurrent, origin, dist );
|
||||
return NULL;
|
||||
}
|
||||
Vector dir = pcurrent->GetNext()->pev->origin - currentPos;
|
||||
float length = dir.Length();
|
||||
if ( !length && !ValidPath( pcurrent->GetNext()->GetNext(), move ) )
|
||||
{
|
||||
if ( dist == originalDist ) // HACK -- up against a dead end
|
||||
return NULL;
|
||||
return pcurrent;
|
||||
}
|
||||
if ( length > dist ) // enough left in this path to move
|
||||
{
|
||||
*origin = currentPos + (dir * (dist / length));
|
||||
return pcurrent;
|
||||
}
|
||||
else
|
||||
{
|
||||
dist -= length;
|
||||
currentPos = pcurrent->GetNext()->pev->origin;
|
||||
pcurrent = pcurrent->GetNext();
|
||||
*origin = currentPos;
|
||||
}
|
||||
}
|
||||
*origin = currentPos;
|
||||
}
|
||||
|
||||
return pcurrent;
|
||||
}
|
||||
|
||||
|
||||
// Assumes this is ALWAYS enabled
|
||||
CPathTrack *CPathTrack :: Nearest( Vector origin )
|
||||
{
|
||||
int deadCount;
|
||||
float minDist, dist;
|
||||
Vector delta;
|
||||
CPathTrack *ppath, *pnearest;
|
||||
|
||||
|
||||
delta = origin - pev->origin;
|
||||
delta.z = 0;
|
||||
minDist = delta.Length();
|
||||
pnearest = this;
|
||||
ppath = GetNext();
|
||||
|
||||
// Hey, I could use the old 2 racing pointers solution to this, but I'm lazy :)
|
||||
deadCount = 0;
|
||||
while ( ppath && ppath != this )
|
||||
{
|
||||
deadCount++;
|
||||
if ( deadCount > 9999 )
|
||||
{
|
||||
ALERT( at_error, "Bad sequence of path_tracks from %s", STRING(pev->targetname) );
|
||||
return NULL;
|
||||
}
|
||||
delta = origin - ppath->pev->origin;
|
||||
delta.z = 0;
|
||||
dist = delta.Length();
|
||||
if ( dist < minDist )
|
||||
{
|
||||
minDist = dist;
|
||||
pnearest = ppath;
|
||||
}
|
||||
ppath = ppath->GetNext();
|
||||
}
|
||||
return pnearest;
|
||||
}
|
||||
|
||||
|
||||
CPathTrack *CPathTrack::Instance( edict_t *pent )
|
||||
{
|
||||
if ( FNullEnt( pent )) return NULL;
|
||||
if ( FClassnameIs( pent, "path_track" ) )
|
||||
return (CPathTrack *)GET_PRIVATE(pent);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
|
||||
// DEBUGGING CODE
|
||||
#if PATH_SPARKLE_DEBUG
|
||||
void CPathTrack :: Sparkle( void )
|
||||
{
|
||||
|
||||
pev->nextthink = gpGlobals->time + 0.2;
|
||||
if ( FBitSet( pev->spawnflags, SF_PATH_DISABLED ) )
|
||||
UTIL_ParticleEffect(pev->origin, Vector(0,0,100), 210, 10);
|
||||
else
|
||||
UTIL_ParticleEffect(pev->origin, Vector(0,0,100), 84, 10);
|
||||
}
|
||||
#endif
|
||||
|
|
@ -0,0 +1,61 @@
|
|||
/***
|
||||
*
|
||||
* 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 "plane.h"
|
||||
|
||||
//=========================================================
|
||||
// Plane
|
||||
//=========================================================
|
||||
CPlane :: CPlane ( void )
|
||||
{
|
||||
m_fInitialized = FALSE;
|
||||
}
|
||||
|
||||
//=========================================================
|
||||
// InitializePlane - Takes a normal for the plane and a
|
||||
// point on the plane and
|
||||
//=========================================================
|
||||
void CPlane :: InitializePlane ( const Vector &vecNormal, const Vector &vecPoint )
|
||||
{
|
||||
m_vecNormal = vecNormal;
|
||||
m_flDist = DotProduct ( m_vecNormal, vecPoint );
|
||||
m_fInitialized = TRUE;
|
||||
}
|
||||
|
||||
|
||||
//=========================================================
|
||||
// PointInFront - determines whether the given vector is
|
||||
// in front of the plane.
|
||||
//=========================================================
|
||||
BOOL CPlane :: PointInFront ( const Vector &vecPoint )
|
||||
{
|
||||
float flFace;
|
||||
|
||||
if ( !m_fInitialized )
|
||||
{
|
||||
return FALSE;
|
||||
}
|
||||
|
||||
flFace = DotProduct ( m_vecNormal, vecPoint ) - m_flDist;
|
||||
|
||||
if ( flFace >= 0 )
|
||||
{
|
||||
return TRUE;
|
||||
}
|
||||
|
||||
return FALSE;
|
||||
}
|
||||
|
|
@ -0,0 +1,43 @@
|
|||
/***
|
||||
*
|
||||
* 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.
|
||||
*
|
||||
****/
|
||||
#ifndef PLANE_H
|
||||
#define PLANE_H
|
||||
|
||||
//=========================================================
|
||||
// Plane
|
||||
//=========================================================
|
||||
class CPlane
|
||||
{
|
||||
public:
|
||||
CPlane ( void );
|
||||
|
||||
//=========================================================
|
||||
// InitializePlane - Takes a normal for the plane and a
|
||||
// point on the plane and
|
||||
//=========================================================
|
||||
void InitializePlane ( const Vector &vecNormal, const Vector &vecPoint );
|
||||
|
||||
//=========================================================
|
||||
// PointInFront - determines whether the given vector is
|
||||
// in front of the plane.
|
||||
//=========================================================
|
||||
BOOL PointInFront ( const Vector &vecPoint );
|
||||
|
||||
Vector m_vecNormal;
|
||||
float m_flDist;
|
||||
BOOL m_fInitialized;
|
||||
};
|
||||
|
||||
#endif // PLANE_H
|
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
|
@ -0,0 +1,297 @@
|
|||
/***
|
||||
*
|
||||
* 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.
|
||||
*
|
||||
****/
|
||||
#ifndef PLAYER_H
|
||||
#define PLAYER_H
|
||||
|
||||
#define PLAYER_FATAL_FALL_SPEED 1024// approx 60 feet
|
||||
#define PLAYER_MAX_SAFE_FALL_SPEED 580// approx 20 feet
|
||||
#define DAMAGE_FOR_FALL_SPEED (float) 100 / ( PLAYER_FATAL_FALL_SPEED - PLAYER_MAX_SAFE_FALL_SPEED )// damage per unit per second.
|
||||
#define PLAYER_MIN_BOUNCE_SPEED 200
|
||||
#define PLAYER_FALL_PUNCH_THRESHHOLD (float)350 // won't punch player's screen/make scrape noise unless player falling at least this fast.
|
||||
|
||||
//
|
||||
// Player PHYSICS FLAGS bits
|
||||
//
|
||||
#define PFLAG_ONLADDER ( 1<<0 )
|
||||
#define PFLAG_ONSWING ( 1<<0 )
|
||||
#define PFLAG_ONTRAIN ( 1<<1 )
|
||||
#define PFLAG_ONBARNACLE ( 1<<2 )
|
||||
#define PFLAG_DUCKING ( 1<<3 ) // In the process of ducking, but totally squatted yet
|
||||
#define PFLAG_USING ( 1<<4 ) // Using a continuous entity
|
||||
#define PFLAG_OBSERVER ( 1<<5 ) // player is locked in stationary cam mode. Spectators can move, observers can't.
|
||||
|
||||
//
|
||||
// generic player
|
||||
//
|
||||
//-----------------------------------------------------
|
||||
//This is Half-Life player entity
|
||||
//-----------------------------------------------------
|
||||
#define CSUITPLAYLIST 4 // max of 4 suit sentences queued up at any time
|
||||
|
||||
#define SUIT_GROUP TRUE
|
||||
#define SUIT_SENTENCE FALSE
|
||||
|
||||
#define SUIT_REPEAT_OK 0
|
||||
#define SUIT_NEXT_IN_30SEC 30
|
||||
#define SUIT_NEXT_IN_1MIN 60
|
||||
#define SUIT_NEXT_IN_5MIN 300
|
||||
#define SUIT_NEXT_IN_10MIN 600
|
||||
#define SUIT_NEXT_IN_30MIN 1800
|
||||
#define SUIT_NEXT_IN_1HOUR 3600
|
||||
|
||||
#define CSUITNOREPEAT 32
|
||||
|
||||
#define SOUND_FLASHLIGHT_ON "items/flashlight1.wav"
|
||||
#define SOUND_FLASHLIGHT_OFF "items/flashlight1.wav"
|
||||
|
||||
#define TEAM_NAME_LENGTH 16
|
||||
|
||||
typedef enum
|
||||
{
|
||||
PLAYER_IDLE,
|
||||
PLAYER_WALK,
|
||||
PLAYER_JUMP,
|
||||
PLAYER_SUPERJUMP,
|
||||
PLAYER_DIE,
|
||||
PLAYER_ATTACK1,
|
||||
} PLAYER_ANIM;
|
||||
|
||||
class CBasePlayer : public CBaseMonster
|
||||
{
|
||||
public:
|
||||
int random_seed; // See that is shared between client & server for shared weapons code
|
||||
|
||||
int m_iPlayerSound;// the index of the sound list slot reserved for this player
|
||||
int m_iTargetVolume;// ideal sound volume.
|
||||
int m_iWeaponVolume;// how loud the player's weapon is right now.
|
||||
int m_iExtraSoundTypes;// additional classification for this weapon's sound
|
||||
int m_iWeaponFlash;// brightness of the weapon flash
|
||||
float m_flStopExtraSoundTime;
|
||||
|
||||
float m_flFlashLightTime; // Time until next battery draw/Recharge
|
||||
int m_iFlashBattery; // Flashlight Battery Draw
|
||||
|
||||
int m_afButtonLast;
|
||||
int m_afButtonPressed;
|
||||
int m_afButtonReleased;
|
||||
|
||||
edict_t *m_pentSndLast; // last sound entity to modify player room type
|
||||
float m_flSndRoomtype; // last roomtype set by sound entity
|
||||
float m_flSndRange; // dist from player to sound entity
|
||||
|
||||
float m_flFallVelocity;
|
||||
|
||||
int m_rgItems[MAX_ITEMS];
|
||||
int m_fKnownItem; // True when a new item needs to be added
|
||||
int m_fNewAmmo; // True when a new item has been added
|
||||
|
||||
unsigned int m_afPhysicsFlags; // physics flags - set when 'normal' physics should be revisited or overriden
|
||||
float m_fNextSuicideTime; // the time after which the player can next use the suicide command
|
||||
|
||||
|
||||
// these are time-sensitive things that we keep track of
|
||||
float m_flTimeStepSound; // when the last stepping sound was made
|
||||
float m_flTimeWeaponIdle; // when to play another weapon idle animation.
|
||||
float m_flSwimTime; // how long player has been underwater
|
||||
float m_flDuckTime; // how long we've been ducking
|
||||
float m_flWallJumpTime; // how long until next walljump
|
||||
|
||||
float m_flSuitUpdate; // when to play next suit update
|
||||
int m_rgSuitPlayList[CSUITPLAYLIST];// next sentencenum to play for suit update
|
||||
int m_iSuitPlayNext; // next sentence slot for queue storage;
|
||||
int m_rgiSuitNoRepeat[CSUITNOREPEAT]; // suit sentence no repeat list
|
||||
float m_rgflSuitNoRepeatTime[CSUITNOREPEAT]; // how long to wait before allowing repeat
|
||||
int m_lastDamageAmount; // Last damage taken
|
||||
float m_tbdPrev; // Time-based damage timer
|
||||
|
||||
float m_flgeigerRange; // range to nearest radiation source
|
||||
float m_flgeigerDelay; // delay per update of range msg to client
|
||||
int m_igeigerRangePrev;
|
||||
int m_iStepLeft; // alternate left/right foot stepping sound
|
||||
char m_szTextureName[CBTEXTURENAMEMAX]; // current texture name we're standing on
|
||||
char m_chTextureType; // current texture type
|
||||
|
||||
int m_idrowndmg; // track drowning damage taken
|
||||
int m_idrownrestored; // track drowning damage restored
|
||||
|
||||
int m_bitsHUDDamage; // Damage bits for the current fame. These get sent to
|
||||
// the hude via the DAMAGE message
|
||||
BOOL m_fInitHUD; // True when deferred HUD restart msg needs to be sent
|
||||
BOOL m_fGameHUDInitialized;
|
||||
int m_iTrain; // Train control position
|
||||
BOOL m_fWeapon; // Set this to FALSE to force a reset of the current weapon HUD info
|
||||
|
||||
EHANDLE m_pTank; // the tank which the player is currently controlling, NULL if no tank
|
||||
float m_fDeadTime; // the time at which the player died (used in PlayerDeathThink())
|
||||
float m_fAirFinished; // moved here from progdefs.h
|
||||
float m_fPainFinished; // moved here from progdefs.h
|
||||
float m_flViewHeight; // keep value from view_ofs.z that engine sets it when player first entering in multiplayer
|
||||
BOOL m_fNoPlayerSound; // a debugging feature. Player makes no sound if this is true.
|
||||
BOOL m_fLongJump; // does this player have the longjump module?
|
||||
|
||||
float m_tSneaking;
|
||||
int m_iUpdateTime; // stores the number of frame ticks before sending HUD update messages
|
||||
int m_iClientHealth; // the health currently known by the client. If this changes, send a new
|
||||
int m_iClientBattery; // the Battery currently known by the client. If this changes, send a new
|
||||
int m_iHideHUD; // the players hud weapon info is to be hidden
|
||||
int m_iClientHideHUD;
|
||||
// usable player items
|
||||
CBasePlayerItem *m_rgpPlayerItems[MAX_ITEM_TYPES];
|
||||
CBasePlayerItem *m_pActiveItem;
|
||||
CBasePlayerItem *m_pClientActiveItem; // client version of the active item
|
||||
CBasePlayerItem *m_pLastItem;
|
||||
// shared ammo slots
|
||||
int m_rgAmmo[MAX_AMMO_SLOTS];
|
||||
int m_rgAmmoLast[MAX_AMMO_SLOTS];
|
||||
|
||||
Vector m_vecAutoAim;
|
||||
BOOL m_fOnTarget;
|
||||
int m_iDeaths;
|
||||
float m_iRespawnFrames; // used in PlayerDeathThink() to make sure players can always respawn
|
||||
|
||||
int m_lastx, m_lasty; // These are the previous update's crosshair angles, DON"T SAVE/RESTORE
|
||||
|
||||
int m_nCustomSprayFrames;// Custom clan logo frames for this player
|
||||
float m_flNextDecalTime;// next time this player can spray a decal
|
||||
|
||||
float m_flNextChatTime;
|
||||
|
||||
char m_szTeamName[TEAM_NAME_LENGTH];
|
||||
|
||||
virtual void Spawn( void );
|
||||
void Pain( void );
|
||||
|
||||
// virtual void Think( void );
|
||||
virtual void Jump( void );
|
||||
virtual void Duck( void );
|
||||
virtual void PreThink( void );
|
||||
virtual void PostThink( void );
|
||||
virtual Vector GetGunPosition( void );
|
||||
virtual int TakeHealth( float flHealth, int bitsDamageType );
|
||||
virtual void TraceAttack( entvars_t *pevAttacker, float flDamage, Vector vecDir, TraceResult *ptr, int bitsDamageType);
|
||||
virtual int TakeDamage( entvars_t* pevInflictor, entvars_t* pevAttacker, float flDamage, int bitsDamageType);
|
||||
virtual void Killed( entvars_t *pevAttacker, int iGib );
|
||||
virtual Vector BodyTarget( const Vector &posSrc ) { return Center( ) + pev->view_ofs * RANDOM_FLOAT( 0.5, 1.1 ); }; // position to shoot at
|
||||
virtual void StartSneaking( void ) { m_tSneaking = gpGlobals->time - 1; }
|
||||
virtual void StopSneaking( void ) { m_tSneaking = gpGlobals->time + 30; }
|
||||
virtual BOOL IsSneaking( void ) { return m_tSneaking <= gpGlobals->time; }
|
||||
virtual BOOL IsAlive( void ) { return (pev->deadflag == DEAD_NO) && pev->health > 0; }
|
||||
virtual BOOL ShouldFadeOnDeath( void ) { return FALSE; }
|
||||
virtual BOOL IsPlayer( void ) { return TRUE; } // Spectators should return FALSE for this, they aren't "players" as far as game logic is concerned
|
||||
|
||||
virtual BOOL IsNetClient( void ) { return TRUE; } // Bots should return FALSE for this, they can't receive NET messages
|
||||
// Spectators should return TRUE for this
|
||||
virtual const char *TeamID( void );
|
||||
|
||||
virtual int Save( CSave &save );
|
||||
virtual int Restore( CRestore &restore );
|
||||
void RenewItems(void);
|
||||
void PackDeadPlayerItems( void );
|
||||
void RemoveAllItems( BOOL removeSuit );
|
||||
BOOL SwitchWeapon( CBasePlayerItem *pWeapon );
|
||||
|
||||
// JOHN: sends custom messages if player HUD data has changed (eg health, ammo)
|
||||
virtual void UpdateClientData( void );
|
||||
|
||||
static TYPEDESCRIPTION m_playerSaveData[];
|
||||
|
||||
// Player is moved across the transition by other means
|
||||
virtual int ObjectCaps( void ) { return CBaseMonster :: ObjectCaps() & ~FCAP_ACROSS_TRANSITION; }
|
||||
virtual void Precache( void );
|
||||
BOOL IsOnLadder( void );
|
||||
BOOL FlashlightIsOn( void );
|
||||
void FlashlightTurnOn( void );
|
||||
void FlashlightTurnOff( void );
|
||||
|
||||
void UpdatePlayerSound ( void );
|
||||
void DeathSound ( void );
|
||||
|
||||
int Classify ( void );
|
||||
void SetAnimation( PLAYER_ANIM playerAnim );
|
||||
void SetWeaponAnimType( const char *szExtention );
|
||||
char m_szAnimExtention[32];
|
||||
|
||||
// custom player functions
|
||||
virtual void ImpulseCommands( void );
|
||||
void CheatImpulseCommands( int iImpulse );
|
||||
|
||||
void StartDeathCam( void );
|
||||
void StartObserver( Vector vecPosition, Vector vecViewAngle );
|
||||
|
||||
void AddPoints( int score, BOOL bAllowNegativeScore );
|
||||
void AddPointsToTeam( int score, BOOL bAllowNegativeScore );
|
||||
BOOL AddPlayerItem( CBasePlayerItem *pItem );
|
||||
BOOL RemovePlayerItem( CBasePlayerItem *pItem );
|
||||
void DropPlayerItem ( char *pszItemName );
|
||||
BOOL HasPlayerItem( CBasePlayerItem *pCheckItem );
|
||||
BOOL HasNamedPlayerItem( const char *pszItemName );
|
||||
BOOL HasWeapons( void );// do I have ANY weapons?
|
||||
void SelectPrevItem( int iItem );
|
||||
void SelectNextItem( int iItem );
|
||||
void SelectLastItem(void);
|
||||
void SelectItem(const char *pstr);
|
||||
void ItemPreFrame( void );
|
||||
void ItemPostFrame( void );
|
||||
void GiveNamedItem( const char *szName );
|
||||
void EnableControl(BOOL fControl);
|
||||
|
||||
int GiveAmmo( int iAmount, char *szName, int iMax );
|
||||
void SendAmmoUpdate(void);
|
||||
|
||||
void WaterMove( void );
|
||||
void EXPORT PlayerDeathThink( void );
|
||||
void PlayerUse( void );
|
||||
|
||||
void CheckSuitUpdate();
|
||||
void SetSuitUpdate(char *name, int fgroup, int iNoRepeat);
|
||||
void UpdateGeigerCounter( void );
|
||||
void CheckTimeBasedDamage( void );
|
||||
void UpdateStepSound( void );
|
||||
void PlayStepSound(int step, float fvol);
|
||||
|
||||
BOOL FBecomeProne ( void );
|
||||
void BarnacleVictimBitten ( entvars_t *pevBarnacle );
|
||||
void BarnacleVictimReleased ( void );
|
||||
static int GetAmmoIndex(const char *psz);
|
||||
int AmmoInventory( int iAmmoIndex );
|
||||
int Illumination( void );
|
||||
|
||||
void ResetAutoaim( void );
|
||||
Vector GetAutoaimVector( float flDelta );
|
||||
Vector AutoaimDeflection( Vector &vecSrc, float flDist, float flDelta );
|
||||
|
||||
void ForceClientDllUpdate( void ); // Forces all client .dll specific data to be resent to client.
|
||||
|
||||
void DeathMessage( entvars_t *pevKiller );
|
||||
|
||||
void SetCustomDecalFrames( int nFrames );
|
||||
int GetCustomDecalFrames( void );
|
||||
};
|
||||
|
||||
#define AUTOAIM_2DEGREES 0.0348994967025
|
||||
#define AUTOAIM_5DEGREES 0.08715574274766
|
||||
#define AUTOAIM_8DEGREES 0.1391731009601
|
||||
#define AUTOAIM_10DEGREES 0.1736481776669
|
||||
|
||||
|
||||
extern int gmsgHudText;
|
||||
extern int gmsgShake;
|
||||
extern int gmsgFade;
|
||||
extern int gmsgWeaponAnim;
|
||||
extern int gmsgIntermission;
|
||||
extern int gmsgRoomType;
|
||||
extern BOOL gInitHUD;
|
||||
|
||||
#endif // PLAYER_H
|
|
@ -0,0 +1,303 @@
|
|||
/***
|
||||
*
|
||||
* 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 python_e {
|
||||
PYTHON_IDLE1 = 0,
|
||||
PYTHON_FIDGET,
|
||||
PYTHON_FIRE1,
|
||||
PYTHON_RELOAD,
|
||||
PYTHON_HOLSTER,
|
||||
PYTHON_DRAW,
|
||||
PYTHON_IDLE2,
|
||||
PYTHON_IDLE3
|
||||
};
|
||||
|
||||
class CPython : public CBasePlayerWeapon
|
||||
{
|
||||
public:
|
||||
void Spawn( void );
|
||||
void Precache( void );
|
||||
int iItemSlot( void ) { return 2; }
|
||||
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 );
|
||||
float m_flSoundDelay;
|
||||
|
||||
BOOL m_fInZoom;// don't save this.
|
||||
private:
|
||||
unsigned short m_usFirePython;
|
||||
};
|
||||
LINK_ENTITY_TO_CLASS( weapon_python, CPython );
|
||||
LINK_ENTITY_TO_CLASS( weapon_357, CPython );
|
||||
|
||||
int CPython::GetItemInfo(ItemInfo *p)
|
||||
{
|
||||
p->pszName = STRING(pev->classname);
|
||||
p->pszAmmo1 = "357";
|
||||
p->iMaxAmmo1 = _357_MAX_CARRY;
|
||||
p->pszAmmo2 = NULL;
|
||||
p->iMaxAmmo2 = -1;
|
||||
p->iMaxClip = PYTHON_MAX_CLIP;
|
||||
p->iFlags = 0;
|
||||
p->iSlot = 1;
|
||||
p->iPosition = 1;
|
||||
p->iId = m_iId = WEAPON_PYTHON;
|
||||
p->iWeight = PYTHON_WEIGHT;
|
||||
|
||||
return 1;
|
||||
}
|
||||
|
||||
int CPython::AddToPlayer( CBasePlayer *pPlayer )
|
||||
{
|
||||
if ( CBasePlayerWeapon::AddToPlayer( pPlayer ) )
|
||||
{
|
||||
MESSAGE_BEGIN( MSG_ONE, gmsgWeapPickup, NULL, pPlayer->pev );
|
||||
WRITE_BYTE( m_iId );
|
||||
MESSAGE_END();
|
||||
return TRUE;
|
||||
}
|
||||
return FALSE;
|
||||
}
|
||||
|
||||
void CPython::Spawn( )
|
||||
{
|
||||
pev->classname = MAKE_STRING("weapon_357"); // hack to allow for old names
|
||||
Precache( );
|
||||
m_iId = WEAPON_PYTHON;
|
||||
SET_MODEL(ENT(pev), "models/w_357.mdl");
|
||||
|
||||
m_iDefaultAmmo = PYTHON_DEFAULT_GIVE;
|
||||
|
||||
FallInit();// get ready to fall down.
|
||||
}
|
||||
|
||||
|
||||
void CPython::Precache( void )
|
||||
{
|
||||
PRECACHE_MODEL("models/v_357.mdl");
|
||||
PRECACHE_MODEL("models/w_357.mdl");
|
||||
PRECACHE_MODEL("models/p_357.mdl");
|
||||
|
||||
PRECACHE_MODEL("models/w_357ammobox.mdl");
|
||||
PRECACHE_SOUND("items/9mmclip1.wav");
|
||||
|
||||
PRECACHE_SOUND ("weapons/357_reload1.wav");
|
||||
PRECACHE_SOUND ("weapons/357_cock1.wav");
|
||||
PRECACHE_SOUND ("weapons/357_shot1.wav");
|
||||
PRECACHE_SOUND ("weapons/357_shot2.wav");
|
||||
|
||||
m_usFirePython = PRECACHE_EVENT( 1, "events/python.sc" );
|
||||
}
|
||||
|
||||
BOOL CPython::Deploy( )
|
||||
{
|
||||
if ( g_pGameRules->IsMultiplayer() )
|
||||
{
|
||||
// enable laser sight geometry.
|
||||
pev->body = 1;
|
||||
}
|
||||
else
|
||||
{
|
||||
pev->body = 0;
|
||||
}
|
||||
|
||||
return DefaultDeploy( "models/v_357.mdl", "models/p_357.mdl", PYTHON_DRAW, "python" );
|
||||
}
|
||||
|
||||
|
||||
void CPython::Holster( int skiplocal /* = 0 */ )
|
||||
{
|
||||
m_fInReload = FALSE;// cancel any reload in progress.
|
||||
|
||||
if ( m_fInZoom )
|
||||
{
|
||||
SecondaryAttack();
|
||||
}
|
||||
|
||||
m_pPlayer->m_flNextAttack = UTIL_WeaponTimeBase() + 1.0;
|
||||
m_flTimeWeaponIdle = gpGlobals->time + 10 + RANDOM_FLOAT ( 0, 5 );
|
||||
SendWeaponAnim( PYTHON_HOLSTER );
|
||||
}
|
||||
|
||||
void CPython::SecondaryAttack( void )
|
||||
{
|
||||
if ( !g_pGameRules->IsMultiplayer() )
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
if ( m_fInZoom )
|
||||
{
|
||||
m_fInZoom = FALSE;
|
||||
m_pPlayer->pev->fov = 90; // 0 means reset to default fov
|
||||
}
|
||||
else
|
||||
{
|
||||
m_fInZoom = TRUE;
|
||||
m_pPlayer->pev->fov = 40;
|
||||
}
|
||||
|
||||
m_flNextSecondaryAttack = gpGlobals->time + 0.5;
|
||||
}
|
||||
|
||||
void CPython::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_fFireOnEmpty)
|
||||
Reload( );
|
||||
else
|
||||
{
|
||||
EMIT_SOUND(ENT(m_pPlayer->pev), CHAN_WEAPON, "weapons/357_cock1.wav", 0.8, ATTN_NORM);
|
||||
m_flNextPrimaryAttack = gpGlobals->time + 0.15;
|
||||
}
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
PLAYBACK_EVENT( 0, m_pPlayer->edict(), m_usFirePython );
|
||||
|
||||
m_pPlayer->m_iWeaponVolume = LOUD_GUN_VOLUME;
|
||||
m_pPlayer->m_iWeaponFlash = BRIGHT_GUN_FLASH;
|
||||
|
||||
m_iClip--;
|
||||
|
||||
// player "shoot" animation
|
||||
m_pPlayer->SetAnimation( PLAYER_ATTACK1 );
|
||||
m_pPlayer->pev->effects = (int)(m_pPlayer->pev->effects) | EF_MUZZLEFLASH;
|
||||
|
||||
UTIL_MakeVectors( m_pPlayer->pev->viewangles + m_pPlayer->pev->punchangle );
|
||||
|
||||
Vector vecSrc = m_pPlayer->GetGunPosition( );
|
||||
Vector vecAiming = m_pPlayer->GetAutoaimVector( AUTOAIM_10DEGREES );
|
||||
m_pPlayer->FireBullets( 1, vecSrc, vecAiming, VECTOR_CONE_1DEGREES, 8192, BULLET_PLAYER_357, 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 + 0.75;
|
||||
m_flTimeWeaponIdle = gpGlobals->time + RANDOM_FLOAT ( 10, 15 );
|
||||
}
|
||||
|
||||
|
||||
void CPython::Reload( void )
|
||||
{
|
||||
if ( m_fInZoom )
|
||||
{
|
||||
m_fInZoom = FALSE;
|
||||
m_pPlayer->pev->fov = 90; // 0 means reset to default fov
|
||||
}
|
||||
|
||||
if (DefaultReload( 6, PYTHON_RELOAD, 2.0 ))
|
||||
{
|
||||
m_flSoundDelay = gpGlobals->time + 1.5;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
void CPython::WeaponIdle( void )
|
||||
{
|
||||
ResetEmptySound( );
|
||||
|
||||
m_pPlayer->GetAutoaimVector( AUTOAIM_10DEGREES );
|
||||
|
||||
// ALERT( at_console, "%.2f\n", gpGlobals->time - m_flSoundDelay );
|
||||
if (m_flSoundDelay != 0 && m_flSoundDelay <= gpGlobals->time)
|
||||
{
|
||||
EMIT_SOUND(ENT(m_pPlayer->pev), CHAN_WEAPON, "weapons/357_reload1.wav", RANDOM_FLOAT(0.8, 0.9), ATTN_NORM);
|
||||
m_flSoundDelay = 0;
|
||||
}
|
||||
|
||||
if (m_flTimeWeaponIdle > gpGlobals->time)
|
||||
return;
|
||||
|
||||
int iAnim;
|
||||
float flRand = RANDOM_FLOAT(0, 1);
|
||||
if (flRand <= 0.5)
|
||||
{
|
||||
iAnim = PYTHON_IDLE1;
|
||||
m_flTimeWeaponIdle = gpGlobals->time + (70.0/30.0);
|
||||
}
|
||||
else if (flRand <= 0.7)
|
||||
{
|
||||
iAnim = PYTHON_IDLE2;
|
||||
m_flTimeWeaponIdle = gpGlobals->time + (60.0/30.0);
|
||||
}
|
||||
else if (flRand <= 0.9)
|
||||
{
|
||||
iAnim = PYTHON_IDLE3;
|
||||
m_flTimeWeaponIdle = gpGlobals->time + (88.0/30.0);
|
||||
}
|
||||
else
|
||||
{
|
||||
iAnim = PYTHON_FIDGET;
|
||||
m_flTimeWeaponIdle = gpGlobals->time + (170.0/30.0);
|
||||
}
|
||||
SendWeaponAnim( iAnim );
|
||||
}
|
||||
|
||||
|
||||
|
||||
class CPythonAmmo : public CBasePlayerAmmo
|
||||
{
|
||||
void Spawn( void )
|
||||
{
|
||||
Precache( );
|
||||
SET_MODEL(ENT(pev), "models/w_357ammobox.mdl");
|
||||
CBasePlayerAmmo::Spawn( );
|
||||
}
|
||||
void Precache( void )
|
||||
{
|
||||
PRECACHE_MODEL ("models/w_357ammobox.mdl");
|
||||
PRECACHE_SOUND("items/9mmclip1.wav");
|
||||
}
|
||||
BOOL AddAmmo( CBaseEntity *pOther )
|
||||
{
|
||||
if (pOther->GiveAmmo( AMMO_357BOX_GIVE, "357", _357_MAX_CARRY ) != -1)
|
||||
{
|
||||
EMIT_SOUND(ENT(pev), CHAN_ITEM, "items/9mmclip1.wav", 1, ATTN_NORM);
|
||||
return TRUE;
|
||||
}
|
||||
return FALSE;
|
||||
}
|
||||
};
|
||||
LINK_ENTITY_TO_CLASS( ammo_357, CPythonAmmo );
|
||||
|
||||
|
||||
#endif
|
|
@ -0,0 +1,98 @@
|
|||
/***
|
||||
*
|
||||
* 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.
|
||||
*
|
||||
****/
|
||||
//=========================================================
|
||||
// rat - environmental monster
|
||||
//=========================================================
|
||||
|
||||
#include "extdll.h"
|
||||
#include "util.h"
|
||||
#include "cbase.h"
|
||||
#include "monsters.h"
|
||||
#include "schedule.h"
|
||||
|
||||
//=========================================================
|
||||
// Monster's Anim Events Go Here
|
||||
//=========================================================
|
||||
|
||||
class CRat : public CBaseMonster
|
||||
{
|
||||
public:
|
||||
void Spawn( void );
|
||||
void Precache( void );
|
||||
void SetYawSpeed( void );
|
||||
int Classify ( void );
|
||||
};
|
||||
LINK_ENTITY_TO_CLASS( monster_rat, CRat );
|
||||
|
||||
//=========================================================
|
||||
// Classify - indicates this monster's place in the
|
||||
// relationship table.
|
||||
//=========================================================
|
||||
int CRat :: Classify ( void )
|
||||
{
|
||||
return CLASS_INSECT;
|
||||
}
|
||||
|
||||
//=========================================================
|
||||
// SetYawSpeed - allows each sequence to have a different
|
||||
// turn rate associated with it.
|
||||
//=========================================================
|
||||
void CRat :: SetYawSpeed ( void )
|
||||
{
|
||||
int ys;
|
||||
|
||||
switch ( m_Activity )
|
||||
{
|
||||
case ACT_IDLE:
|
||||
default:
|
||||
ys = 45;
|
||||
break;
|
||||
}
|
||||
|
||||
pev->yaw_speed = ys;
|
||||
}
|
||||
|
||||
//=========================================================
|
||||
// Spawn
|
||||
//=========================================================
|
||||
void CRat :: Spawn()
|
||||
{
|
||||
Precache( );
|
||||
|
||||
SET_MODEL(ENT(pev), "models/bigrat.mdl");
|
||||
UTIL_SetSize( pev, Vector( 0, 0, 0 ), Vector( 0, 0, 0 ) );
|
||||
|
||||
pev->solid = SOLID_SLIDEBOX;
|
||||
pev->movetype = MOVETYPE_STEP;
|
||||
m_bloodColor = BLOOD_COLOR_RED;
|
||||
pev->health = 8;
|
||||
pev->view_ofs = Vector ( 0, 0, 6 );// position of the eyes relative to monster's origin.
|
||||
m_flFieldOfView = 0.5;// indicates the width of this monster's forward view cone ( as a dotproduct result )
|
||||
m_MonsterState = MONSTERSTATE_NONE;
|
||||
|
||||
MonsterInit();
|
||||
}
|
||||
|
||||
//=========================================================
|
||||
// Precache - precaches all resources this monster needs
|
||||
//=========================================================
|
||||
void CRat :: Precache()
|
||||
{
|
||||
PRECACHE_MODEL("models/bigrat.mdl");
|
||||
}
|
||||
|
||||
//=========================================================
|
||||
// AI Schedules Specific to this monster
|
||||
//=========================================================
|
|
@ -0,0 +1,460 @@
|
|||
/***
|
||||
*
|
||||
* 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.
|
||||
*
|
||||
****/
|
||||
//=========================================================
|
||||
// cockroach
|
||||
//=========================================================
|
||||
|
||||
#include "extdll.h"
|
||||
#include "util.h"
|
||||
#include "cbase.h"
|
||||
#include "monsters.h"
|
||||
#include "schedule.h"
|
||||
#include "soundent.h"
|
||||
#include "decals.h"
|
||||
|
||||
#define ROACH_IDLE 0
|
||||
#define ROACH_BORED 1
|
||||
#define ROACH_SCARED_BY_ENT 2
|
||||
#define ROACH_SCARED_BY_LIGHT 3
|
||||
#define ROACH_SMELL_FOOD 4
|
||||
#define ROACH_EAT 5
|
||||
|
||||
//=========================================================
|
||||
// Monster's Anim Events Go Here
|
||||
//=========================================================
|
||||
class CRoach : public CBaseMonster
|
||||
{
|
||||
public:
|
||||
void Spawn( void );
|
||||
void Precache( void );
|
||||
void SetYawSpeed( void );
|
||||
void EXPORT MonsterThink ( void );
|
||||
void Move ( float flInterval );
|
||||
void PickNewDest ( int iCondition );
|
||||
void EXPORT Touch ( CBaseEntity *pOther );
|
||||
void Killed( entvars_t *pevAttacker, int iGib );
|
||||
|
||||
float m_flLastLightLevel;
|
||||
float m_flNextSmellTime;
|
||||
int Classify ( void );
|
||||
void Look ( int iDistance );
|
||||
int ISoundMask ( void );
|
||||
|
||||
// UNDONE: These don't necessarily need to be save/restored, but if we add more data, it may
|
||||
BOOL m_fLightHacked;
|
||||
int m_iMode;
|
||||
// -----------------------------
|
||||
};
|
||||
LINK_ENTITY_TO_CLASS( monster_cockroach, CRoach );
|
||||
|
||||
//=========================================================
|
||||
// ISoundMask - returns a bit mask indicating which types
|
||||
// of sounds this monster regards. In the base class implementation,
|
||||
// monsters care about all sounds, but no scents.
|
||||
//=========================================================
|
||||
int CRoach :: ISoundMask ( void )
|
||||
{
|
||||
return bits_SOUND_CARCASS | bits_SOUND_MEAT;
|
||||
}
|
||||
|
||||
//=========================================================
|
||||
// Classify - indicates this monster's place in the
|
||||
// relationship table.
|
||||
//=========================================================
|
||||
int CRoach :: Classify ( void )
|
||||
{
|
||||
return CLASS_INSECT;
|
||||
}
|
||||
|
||||
//=========================================================
|
||||
// Touch
|
||||
//=========================================================
|
||||
void CRoach :: Touch ( CBaseEntity *pOther )
|
||||
{
|
||||
Vector vecSpot;
|
||||
TraceResult tr;
|
||||
|
||||
if ( pOther->pev->velocity == g_vecZero || !pOther->IsPlayer() )
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
vecSpot = pev->origin + Vector ( 0 , 0 , 8 );//move up a bit, and trace down.
|
||||
UTIL_TraceLine ( vecSpot, vecSpot + Vector ( 0, 0, -24 ), ignore_monsters, ENT(pev), & tr);
|
||||
|
||||
// This isn't really blood. So you don't have to screen it out based on violence levels (UTIL_ShouldShowBlood())
|
||||
UTIL_DecalTrace( &tr, DECAL_YBLOOD1 +RANDOM_LONG(0,5) );
|
||||
|
||||
TakeDamage( pOther->pev, pOther->pev, pev->health, DMG_CRUSH );
|
||||
}
|
||||
|
||||
//=========================================================
|
||||
// SetYawSpeed - allows each sequence to have a different
|
||||
// turn rate associated with it.
|
||||
//=========================================================
|
||||
void CRoach :: SetYawSpeed ( void )
|
||||
{
|
||||
int ys;
|
||||
|
||||
ys = 120;
|
||||
|
||||
pev->yaw_speed = ys;
|
||||
}
|
||||
|
||||
//=========================================================
|
||||
// Spawn
|
||||
//=========================================================
|
||||
void CRoach :: Spawn()
|
||||
{
|
||||
Precache( );
|
||||
|
||||
SET_MODEL(ENT(pev), "models/roach.mdl");
|
||||
UTIL_SetSize( pev, Vector( -1, -1, 0 ), Vector( 1, 1, 1 ) );
|
||||
|
||||
pev->solid = SOLID_SLIDEBOX;
|
||||
pev->movetype = MOVETYPE_STEP;
|
||||
m_bloodColor = BLOOD_COLOR_YELLOW;
|
||||
pev->effects = 0;
|
||||
pev->health = 1;
|
||||
m_flFieldOfView = 0.5;// indicates the width of this monster's forward view cone ( as a dotproduct result )
|
||||
m_MonsterState = MONSTERSTATE_NONE;
|
||||
|
||||
MonsterInit();
|
||||
SetActivity ( ACT_IDLE );
|
||||
|
||||
pev->view_ofs = Vector ( 0, 0, 1 );// position of the eyes relative to monster's origin.
|
||||
pev->takedamage = DAMAGE_YES;
|
||||
m_fLightHacked = FALSE;
|
||||
m_flLastLightLevel = -1;
|
||||
m_iMode = ROACH_IDLE;
|
||||
m_flNextSmellTime = gpGlobals->time;
|
||||
}
|
||||
|
||||
//=========================================================
|
||||
// Precache - precaches all resources this monster needs
|
||||
//=========================================================
|
||||
void CRoach :: Precache()
|
||||
{
|
||||
PRECACHE_MODEL("models/roach.mdl");
|
||||
|
||||
PRECACHE_SOUND("roach/rch_die.wav");
|
||||
PRECACHE_SOUND("roach/rch_walk.wav");
|
||||
PRECACHE_SOUND("roach/rch_smash.wav");
|
||||
}
|
||||
|
||||
|
||||
//=========================================================
|
||||
// Killed.
|
||||
//=========================================================
|
||||
void CRoach :: Killed( entvars_t *pevAttacker, int iGib )
|
||||
{
|
||||
pev->solid = SOLID_NOT;
|
||||
|
||||
//random sound
|
||||
if ( RANDOM_LONG(0,4) == 1 )
|
||||
{
|
||||
EMIT_SOUND_DYN(ENT(pev), CHAN_VOICE, "roach/rch_die.wav", 0.8, ATTN_NORM, 0, 80 + RANDOM_LONG(0,39) );
|
||||
}
|
||||
else
|
||||
{
|
||||
EMIT_SOUND_DYN(ENT(pev), CHAN_BODY, "roach/rch_smash.wav", 0.7, ATTN_NORM, 0, 80 + RANDOM_LONG(0,39) );
|
||||
}
|
||||
|
||||
CSoundEnt::InsertSound ( bits_SOUND_WORLD, pev->origin, 128, 1 );
|
||||
|
||||
CBaseEntity *pOwner = CBaseEntity::Instance(pev->owner);
|
||||
if ( pOwner )
|
||||
{
|
||||
pOwner->DeathNotice( pev );
|
||||
}
|
||||
UTIL_Remove( this );
|
||||
}
|
||||
|
||||
//=========================================================
|
||||
// MonsterThink, overridden for roaches.
|
||||
//=========================================================
|
||||
void CRoach :: MonsterThink( void )
|
||||
{
|
||||
if ( FNullEnt( FIND_CLIENT_IN_PVS( edict() ) ) )
|
||||
pev->nextthink = gpGlobals->time + RANDOM_FLOAT(1,1.5);
|
||||
else
|
||||
pev->nextthink = gpGlobals->time + 0.1;// keep monster thinking
|
||||
|
||||
float flInterval = StudioFrameAdvance( ); // animate
|
||||
|
||||
if ( !m_fLightHacked )
|
||||
{
|
||||
// if light value hasn't been collection for the first time yet,
|
||||
// suspend the creature for a second so the world finishes spawning, then we'll collect the light level.
|
||||
pev->nextthink = gpGlobals->time + 1;
|
||||
m_fLightHacked = TRUE;
|
||||
return;
|
||||
}
|
||||
else if ( m_flLastLightLevel < 0 )
|
||||
{
|
||||
// collect light level for the first time, now that all of the lightmaps in the roach's area have been calculated.
|
||||
m_flLastLightLevel = GETENTITYILLUM( ENT( pev ) );
|
||||
}
|
||||
|
||||
switch ( m_iMode )
|
||||
{
|
||||
case ROACH_IDLE:
|
||||
case ROACH_EAT:
|
||||
{
|
||||
// if not moving, sample environment to see if anything scary is around. Do a radius search 'look' at random.
|
||||
if ( RANDOM_LONG(0,3) == 1 )
|
||||
{
|
||||
Look( 150 );
|
||||
if (HasConditions(bits_COND_SEE_FEAR))
|
||||
{
|
||||
// if see something scary
|
||||
//ALERT ( at_aiconsole, "Scared\n" );
|
||||
Eat( 30 + ( RANDOM_LONG(0,14) ) );// roach will ignore food for 30 to 45 seconds
|
||||
PickNewDest( ROACH_SCARED_BY_ENT );
|
||||
SetActivity ( ACT_WALK );
|
||||
}
|
||||
else if ( RANDOM_LONG(0,149) == 1 )
|
||||
{
|
||||
// if roach doesn't see anything, there's still a chance that it will move. (boredom)
|
||||
//ALERT ( at_aiconsole, "Bored\n" );
|
||||
PickNewDest( ROACH_BORED );
|
||||
SetActivity ( ACT_WALK );
|
||||
|
||||
if ( m_iMode == ROACH_EAT )
|
||||
{
|
||||
// roach will ignore food for 30 to 45 seconds if it got bored while eating.
|
||||
Eat( 30 + ( RANDOM_LONG(0,14) ) );
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// don't do this stuff if eating!
|
||||
if ( m_iMode == ROACH_IDLE )
|
||||
{
|
||||
if ( FShouldEat() )
|
||||
{
|
||||
Listen();
|
||||
}
|
||||
|
||||
if ( GETENTITYILLUM( ENT(pev) ) > m_flLastLightLevel )
|
||||
{
|
||||
// someone turned on lights!
|
||||
//ALERT ( at_console, "Lights!\n" );
|
||||
PickNewDest( ROACH_SCARED_BY_LIGHT );
|
||||
SetActivity ( ACT_WALK );
|
||||
}
|
||||
else if ( HasConditions(bits_COND_SMELL_FOOD) )
|
||||
{
|
||||
CSound *pSound;
|
||||
|
||||
pSound = CSoundEnt::SoundPointerForIndex( m_iAudibleList );
|
||||
|
||||
// roach smells food and is just standing around. Go to food unless food isn't on same z-plane.
|
||||
if ( pSound && abs( pSound->m_vecOrigin.z - pev->origin.z ) <= 3 )
|
||||
{
|
||||
PickNewDest( ROACH_SMELL_FOOD );
|
||||
SetActivity ( ACT_WALK );
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
break;
|
||||
}
|
||||
case ROACH_SCARED_BY_LIGHT:
|
||||
{
|
||||
// if roach was scared by light, then stop if we're over a spot at least as dark as where we started!
|
||||
if ( GETENTITYILLUM( ENT( pev ) ) <= m_flLastLightLevel )
|
||||
{
|
||||
SetActivity ( ACT_IDLE );
|
||||
m_flLastLightLevel = GETENTITYILLUM( ENT ( pev ) );// make this our new light level.
|
||||
}
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if ( m_flGroundSpeed != 0 )
|
||||
{
|
||||
Move( flInterval );
|
||||
}
|
||||
}
|
||||
|
||||
//=========================================================
|
||||
// Picks a new spot for roach to run to.(
|
||||
//=========================================================
|
||||
void CRoach :: PickNewDest ( int iCondition )
|
||||
{
|
||||
Vector vecNewDir;
|
||||
Vector vecDest;
|
||||
float flDist;
|
||||
|
||||
m_iMode = iCondition;
|
||||
|
||||
if ( m_iMode == ROACH_SMELL_FOOD )
|
||||
{
|
||||
// find the food and go there.
|
||||
CSound *pSound;
|
||||
|
||||
pSound = CSoundEnt::SoundPointerForIndex( m_iAudibleList );
|
||||
|
||||
if ( pSound )
|
||||
{
|
||||
m_Route[ 0 ].vecLocation.x = pSound->m_vecOrigin.x + ( 3 - RANDOM_LONG(0,5) );
|
||||
m_Route[ 0 ].vecLocation.y = pSound->m_vecOrigin.y + ( 3 - RANDOM_LONG(0,5) );
|
||||
m_Route[ 0 ].vecLocation.z = pSound->m_vecOrigin.z;
|
||||
m_Route[ 0 ].iType = bits_MF_TO_LOCATION;
|
||||
m_movementGoal = RouteClassify( m_Route[ 0 ].iType );
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
do
|
||||
{
|
||||
// picks a random spot, requiring that it be at least 128 units away
|
||||
// else, the roach will pick a spot too close to itself and run in
|
||||
// circles. this is a hack but buys me time to work on the real monsters.
|
||||
vecNewDir.x = RANDOM_FLOAT( -1, 1 );
|
||||
vecNewDir.y = RANDOM_FLOAT( -1, 1 );
|
||||
flDist = 256 + ( RANDOM_LONG(0,255) );
|
||||
vecDest = pev->origin + vecNewDir * flDist;
|
||||
|
||||
} while ( ( vecDest - pev->origin ).Length2D() < 128 );
|
||||
|
||||
m_Route[ 0 ].vecLocation.x = vecDest.x;
|
||||
m_Route[ 0 ].vecLocation.y = vecDest.y;
|
||||
m_Route[ 0 ].vecLocation.z = pev->origin.z;
|
||||
m_Route[ 0 ].iType = bits_MF_TO_LOCATION;
|
||||
m_movementGoal = RouteClassify( m_Route[ 0 ].iType );
|
||||
|
||||
if ( RANDOM_LONG(0,9) == 1 )
|
||||
{
|
||||
// every once in a while, a roach will play a skitter sound when they decide to run
|
||||
EMIT_SOUND_DYN(ENT(pev), CHAN_BODY, "roach/rch_walk.wav", 1, ATTN_NORM, 0, 80 + RANDOM_LONG(0,39) );
|
||||
}
|
||||
}
|
||||
|
||||
//=========================================================
|
||||
// roach's move function
|
||||
//=========================================================
|
||||
void CRoach :: Move ( float flInterval )
|
||||
{
|
||||
float flWaypointDist;
|
||||
Vector vecApex;
|
||||
|
||||
// local move to waypoint.
|
||||
flWaypointDist = ( m_Route[ m_iRouteIndex ].vecLocation - pev->origin ).Length2D();
|
||||
MakeIdealYaw ( m_Route[ m_iRouteIndex ].vecLocation );
|
||||
|
||||
ChangeYaw ( pev->yaw_speed );
|
||||
UTIL_MakeVectors( pev->angles );
|
||||
|
||||
if ( RANDOM_LONG(0,7) == 1 )
|
||||
{
|
||||
// randomly check for blocked path.(more random load balancing)
|
||||
if ( !WALK_MOVE( ENT(pev), pev->ideal_yaw, 4, WALKMOVE_NORMAL ) )
|
||||
{
|
||||
// stuck, so just pick a new spot to run off to
|
||||
PickNewDest( m_iMode );
|
||||
}
|
||||
}
|
||||
|
||||
WALK_MOVE( ENT(pev), pev->ideal_yaw, m_flGroundSpeed * flInterval, WALKMOVE_NORMAL );
|
||||
|
||||
// if the waypoint is closer than step size, then stop after next step (ok for roach to overshoot)
|
||||
if ( flWaypointDist <= m_flGroundSpeed * flInterval )
|
||||
{
|
||||
// take truncated step and stop
|
||||
|
||||
SetActivity ( ACT_IDLE );
|
||||
m_flLastLightLevel = GETENTITYILLUM( ENT ( pev ) );// this is roach's new comfortable light level
|
||||
|
||||
if ( m_iMode == ROACH_SMELL_FOOD )
|
||||
{
|
||||
m_iMode = ROACH_EAT;
|
||||
}
|
||||
else
|
||||
{
|
||||
m_iMode = ROACH_IDLE;
|
||||
}
|
||||
}
|
||||
|
||||
if ( RANDOM_LONG(0,149) == 1 && m_iMode != ROACH_SCARED_BY_LIGHT && m_iMode != ROACH_SMELL_FOOD )
|
||||
{
|
||||
// random skitter while moving as long as not on a b-line to get out of light or going to food
|
||||
PickNewDest( FALSE );
|
||||
}
|
||||
}
|
||||
|
||||
//=========================================================
|
||||
// Look - overriden for the roach, which can virtually see
|
||||
// 360 degrees.
|
||||
//=========================================================
|
||||
void CRoach :: Look ( int iDistance )
|
||||
{
|
||||
CBaseEntity *pSightEnt = NULL;// the current visible entity that we're dealing with
|
||||
CBaseEntity *pPreviousEnt;// the last entity added to the link list
|
||||
int iSighted = 0;
|
||||
|
||||
// DON'T let visibility information from last frame sit around!
|
||||
ClearConditions( bits_COND_SEE_HATE |bits_COND_SEE_DISLIKE | bits_COND_SEE_ENEMY | bits_COND_SEE_FEAR );
|
||||
|
||||
// don't let monsters outside of the player's PVS act up, or most of the interesting
|
||||
// things will happen before the player gets there!
|
||||
if ( FNullEnt( FIND_CLIENT_IN_PVS( edict() ) ) )
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
m_pLink = NULL;
|
||||
pPreviousEnt = this;
|
||||
|
||||
// Does sphere also limit itself to PVS?
|
||||
// Examine all entities within a reasonable radius
|
||||
// !!!PERFORMANCE - let's trivially reject the ent list before radius searching!
|
||||
while ((pSightEnt = UTIL_FindEntityInSphere( pSightEnt, pev->origin, iDistance )) != NULL)
|
||||
{
|
||||
// only consider ents that can be damaged. !!!temporarily only considering other monsters and clients
|
||||
if ( pSightEnt->IsPlayer() || FBitSet ( pSightEnt->pev->flags, FL_MONSTER ) )
|
||||
{
|
||||
if ( /*FVisible( pSightEnt ) &&*/ !FBitSet( pSightEnt->pev->flags, FL_NOTARGET ) && pSightEnt->pev->health > 0 )
|
||||
{
|
||||
// NULL the Link pointer for each ent added to the link list. If other ents follow, the will overwrite
|
||||
// this value. If this ent happens to be the last, the list will be properly terminated.
|
||||
pPreviousEnt->m_pLink = pSightEnt;
|
||||
pSightEnt->m_pLink = NULL;
|
||||
pPreviousEnt = pSightEnt;
|
||||
|
||||
// don't add the Enemy's relationship to the conditions. We only want to worry about conditions when
|
||||
// we see monsters other than the Enemy.
|
||||
switch ( IRelationship ( pSightEnt ) )
|
||||
{
|
||||
case R_FR:
|
||||
iSighted |= bits_COND_SEE_FEAR;
|
||||
break;
|
||||
case R_NO:
|
||||
break;
|
||||
default:
|
||||
ALERT ( at_console, "%s can't asses %s\n", STRING(pev->classname), STRING(pSightEnt->pev->classname ) );
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
SetConditions( iSighted );
|
||||
}
|
||||
|
||||
//=========================================================
|
||||
// AI Schedules Specific to this monster
|
||||
//=========================================================
|
||||
|
|
@ -0,0 +1,695 @@
|
|||
/***
|
||||
*
|
||||
* 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 )
|
||||
|
||||
#include "extdll.h"
|
||||
#include "util.h"
|
||||
#include "cbase.h"
|
||||
#include "monsters.h"
|
||||
#include "weapons.h"
|
||||
#include "nodes.h"
|
||||
#include "player.h"
|
||||
#include "gamerules.h"
|
||||
|
||||
|
||||
enum rpg_e {
|
||||
RPG_IDLE = 0,
|
||||
RPG_FIDGET,
|
||||
RPG_RELOAD, // to reload
|
||||
RPG_FIRE2, // to empty
|
||||
RPG_HOLSTER1, // loaded
|
||||
RPG_DRAW1, // loaded
|
||||
RPG_HOLSTER2, // unloaded
|
||||
RPG_DRAW_UL, // unloaded
|
||||
RPG_IDLE_UL, // unloaded idle
|
||||
RPG_FIDGET_UL, // unloaded fidget
|
||||
};
|
||||
|
||||
|
||||
class CLaserSpot : public CBaseEntity
|
||||
{
|
||||
void Spawn( void );
|
||||
void Precache( void );
|
||||
|
||||
int ObjectCaps( void ) { return FCAP_DONT_SAVE; }
|
||||
|
||||
public:
|
||||
void Suspend( float flSuspendTime );
|
||||
void EXPORT Revive( void );
|
||||
|
||||
static CLaserSpot *CreateSpot( void );
|
||||
};
|
||||
LINK_ENTITY_TO_CLASS( laser_spot, CLaserSpot );
|
||||
|
||||
|
||||
class CRpg : public CBasePlayerWeapon
|
||||
{
|
||||
public:
|
||||
int Save( CSave &save );
|
||||
int Restore( CRestore &restore );
|
||||
static TYPEDESCRIPTION m_SaveData[];
|
||||
|
||||
void Spawn( void );
|
||||
void Precache( void );
|
||||
void Reload( void );
|
||||
int iItemSlot( void ) { return 4; }
|
||||
int GetItemInfo(ItemInfo *p);
|
||||
int AddToPlayer( CBasePlayer *pPlayer );
|
||||
|
||||
BOOL Deploy( void );
|
||||
BOOL CanHolster( void );
|
||||
void Holster( int skiplocal = 0 );
|
||||
|
||||
void PrimaryAttack( void );
|
||||
void SecondaryAttack( void );
|
||||
void WeaponIdle( void );
|
||||
|
||||
void UpdateSpot( void );
|
||||
BOOL ShouldWeaponIdle( void ) { return TRUE; };
|
||||
|
||||
CLaserSpot *m_pSpot;
|
||||
int m_fSpotActive;
|
||||
int m_cActiveRockets;// how many missiles in flight from this launcher right now?
|
||||
|
||||
};
|
||||
LINK_ENTITY_TO_CLASS( weapon_rpg, CRpg );
|
||||
|
||||
TYPEDESCRIPTION CRpg::m_SaveData[] =
|
||||
{
|
||||
DEFINE_FIELD( CRpg, m_fSpotActive, FIELD_INTEGER ),
|
||||
DEFINE_FIELD( CRpg, m_cActiveRockets, FIELD_INTEGER ),
|
||||
};
|
||||
IMPLEMENT_SAVERESTORE( CRpg, CBasePlayerWeapon );
|
||||
|
||||
//=========================================================
|
||||
//=========================================================
|
||||
CLaserSpot *CLaserSpot::CreateSpot( void )
|
||||
{
|
||||
CLaserSpot *pSpot = GetClassPtr( (CLaserSpot *)NULL );
|
||||
pSpot->Spawn();
|
||||
|
||||
pSpot->pev->classname = MAKE_STRING("laser_spot");
|
||||
|
||||
return pSpot;
|
||||
}
|
||||
|
||||
//=========================================================
|
||||
//=========================================================
|
||||
void CLaserSpot::Spawn( void )
|
||||
{
|
||||
Precache( );
|
||||
pev->movetype = MOVETYPE_NONE;
|
||||
pev->solid = SOLID_NOT;
|
||||
|
||||
pev->rendermode = kRenderGlow;
|
||||
pev->renderfx = kRenderFxNoDissipation;
|
||||
pev->renderamt = 255;
|
||||
|
||||
SET_MODEL(ENT(pev), "sprites/laserdot.spr");
|
||||
UTIL_SetOrigin( pev, pev->origin );
|
||||
};
|
||||
|
||||
//=========================================================
|
||||
// Suspend- make the laser sight invisible.
|
||||
//=========================================================
|
||||
void CLaserSpot::Suspend( float flSuspendTime )
|
||||
{
|
||||
pev->effects |= EF_NODRAW;
|
||||
|
||||
SetThink( Revive );
|
||||
pev->nextthink = gpGlobals->time + flSuspendTime;
|
||||
}
|
||||
|
||||
//=========================================================
|
||||
// Revive - bring a suspended laser sight back.
|
||||
//=========================================================
|
||||
void CLaserSpot::Revive( void )
|
||||
{
|
||||
pev->effects &= ~EF_NODRAW;
|
||||
|
||||
SetThink( NULL );
|
||||
}
|
||||
|
||||
void CLaserSpot::Precache( void )
|
||||
{
|
||||
PRECACHE_MODEL("sprites/laserdot.spr");
|
||||
};
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
class CRpgRocket : public CGrenade
|
||||
{
|
||||
public:
|
||||
int Save( CSave &save );
|
||||
int Restore( CRestore &restore );
|
||||
static TYPEDESCRIPTION m_SaveData[];
|
||||
void Spawn( void );
|
||||
void Precache( void );
|
||||
void EXPORT FollowThink( void );
|
||||
void EXPORT IgniteThink( void );
|
||||
void EXPORT RocketTouch( CBaseEntity *pOther );
|
||||
static CRpgRocket *CreateRpgRocket( Vector vecOrigin, Vector vecAngles, CBaseEntity *pOwner, CRpg *pLauncher );
|
||||
|
||||
int m_iTrail;
|
||||
float m_flIgniteTime;
|
||||
CRpg *m_pLauncher;// pointer back to the launcher that fired me.
|
||||
};
|
||||
LINK_ENTITY_TO_CLASS( rpg_rocket, CRpgRocket );
|
||||
|
||||
TYPEDESCRIPTION CRpgRocket::m_SaveData[] =
|
||||
{
|
||||
DEFINE_FIELD( CRpgRocket, m_flIgniteTime, FIELD_TIME ),
|
||||
DEFINE_FIELD( CRpgRocket, m_pLauncher, FIELD_CLASSPTR ),
|
||||
};
|
||||
IMPLEMENT_SAVERESTORE( CRpgRocket, CGrenade );
|
||||
|
||||
//=========================================================
|
||||
//=========================================================
|
||||
CRpgRocket *CRpgRocket::CreateRpgRocket( Vector vecOrigin, Vector vecAngles, CBaseEntity *pOwner, CRpg *pLauncher )
|
||||
{
|
||||
CRpgRocket *pRocket = GetClassPtr( (CRpgRocket *)NULL );
|
||||
|
||||
UTIL_SetOrigin( pRocket->pev, vecOrigin );
|
||||
pRocket->pev->angles = vecAngles;
|
||||
pRocket->Spawn();
|
||||
pRocket->SetTouch( CRpgRocket::RocketTouch );
|
||||
pRocket->m_pLauncher = pLauncher;// remember what RPG fired me.
|
||||
pRocket->m_pLauncher->m_cActiveRockets++;// register this missile as active for the launcher
|
||||
pRocket->pev->owner = pOwner->edict();
|
||||
|
||||
return pRocket;
|
||||
}
|
||||
|
||||
//=========================================================
|
||||
//=========================================================
|
||||
void CRpgRocket :: Spawn( void )
|
||||
{
|
||||
Precache( );
|
||||
// motor
|
||||
pev->movetype = MOVETYPE_BOUNCE;
|
||||
pev->solid = SOLID_BBOX;
|
||||
|
||||
SET_MODEL(ENT(pev), "models/rpgrocket.mdl");
|
||||
UTIL_SetSize(pev, Vector( 0, 0, 0), Vector(0, 0, 0));
|
||||
UTIL_SetOrigin( pev, pev->origin );
|
||||
|
||||
pev->classname = MAKE_STRING("rpg_rocket");
|
||||
|
||||
SetThink( IgniteThink );
|
||||
SetTouch( ExplodeTouch );
|
||||
|
||||
pev->angles.x -= 30;
|
||||
UTIL_MakeVectors( pev->angles );
|
||||
pev->angles.x = -(pev->angles.x + 30);
|
||||
|
||||
pev->velocity = gpGlobals->v_forward * 250;
|
||||
pev->gravity = 0.5;
|
||||
|
||||
pev->nextthink = gpGlobals->time + 0.4;
|
||||
|
||||
pev->dmg = gSkillData.plrDmgRPG;
|
||||
}
|
||||
|
||||
//=========================================================
|
||||
//=========================================================
|
||||
void CRpgRocket :: RocketTouch ( CBaseEntity *pOther )
|
||||
{
|
||||
if ( m_pLauncher )
|
||||
{
|
||||
// my launcher is still around, tell it I'm dead.
|
||||
m_pLauncher->m_cActiveRockets--;
|
||||
}
|
||||
|
||||
STOP_SOUND( edict(), CHAN_VOICE, "weapons/rocket1.wav" );
|
||||
ExplodeTouch( pOther );
|
||||
}
|
||||
|
||||
//=========================================================
|
||||
//=========================================================
|
||||
void CRpgRocket :: Precache( void )
|
||||
{
|
||||
PRECACHE_MODEL("models/rpgrocket.mdl");
|
||||
m_iTrail = PRECACHE_MODEL("sprites/smoke.spr");
|
||||
PRECACHE_SOUND ("weapons/rocket1.wav");
|
||||
}
|
||||
|
||||
|
||||
void CRpgRocket :: IgniteThink( void )
|
||||
{
|
||||
// pev->movetype = MOVETYPE_TOSS;
|
||||
|
||||
pev->movetype = MOVETYPE_FLY;
|
||||
pev->effects |= EF_LIGHT;
|
||||
|
||||
// make rocket sound
|
||||
EMIT_SOUND( ENT(pev), CHAN_VOICE, "weapons/rocket1.wav", 1, 0.5 );
|
||||
|
||||
// rocket trail
|
||||
MESSAGE_BEGIN( MSG_BROADCAST, SVC_TEMPENTITY );
|
||||
|
||||
WRITE_BYTE( TE_BEAMFOLLOW );
|
||||
WRITE_SHORT(entindex()); // entity
|
||||
WRITE_SHORT(m_iTrail ); // model
|
||||
WRITE_BYTE( 40 ); // life
|
||||
WRITE_BYTE( 5 ); // width
|
||||
WRITE_BYTE( 224 ); // r, g, b
|
||||
WRITE_BYTE( 224 ); // r, g, b
|
||||
WRITE_BYTE( 255 ); // r, g, b
|
||||
WRITE_BYTE( 255 ); // brightness
|
||||
|
||||
MESSAGE_END(); // move PHS/PVS data sending into here (SEND_ALL, SEND_PVS, SEND_PHS)
|
||||
|
||||
|
||||
|
||||
/*
|
||||
WRITE_BYTE( MSG_BROADCAST, SVC_TEMPENTITY );
|
||||
WRITE_BYTE( MSG_BROADCAST, TE_BEAMFOLLOW);
|
||||
WRITE_SHORT(entindex()); // entity
|
||||
WRITE_SHORT(MSG_BROADCAST, m_iTrail ); // model
|
||||
WRITE_BYTE( MSG_BROADCAST, 40 ); // life
|
||||
WRITE_BYTE( MSG_BROADCAST, 5 ); // width
|
||||
WRITE_BYTE( MSG_BROADCAST, 224 ); // r, g, b
|
||||
WRITE_BYTE( MSG_BROADCAST, 224 ); // r, g, b
|
||||
WRITE_BYTE( MSG_BROADCAST, 255 ); // r, g, b
|
||||
WRITE_BYTE( MSG_BROADCAST, 255 ); // brightness
|
||||
*/
|
||||
m_flIgniteTime = gpGlobals->time;
|
||||
|
||||
// set to follow laser spot
|
||||
SetThink( FollowThink );
|
||||
pev->nextthink = gpGlobals->time + 0.1;
|
||||
}
|
||||
|
||||
|
||||
void CRpgRocket :: FollowThink( void )
|
||||
{
|
||||
CBaseEntity *pOther = NULL;
|
||||
Vector vecTarget;
|
||||
Vector vecDir;
|
||||
float flDist, flMax, flDot;
|
||||
TraceResult tr;
|
||||
|
||||
UTIL_MakeAimVectors( pev->angles );
|
||||
|
||||
vecTarget = gpGlobals->v_forward;
|
||||
flMax = 4096;
|
||||
|
||||
// Examine all entities within a reasonable radius
|
||||
while ((pOther = UTIL_FindEntityByClassname( pOther, "laser_spot" )) != NULL)
|
||||
{
|
||||
UTIL_TraceLine ( pev->origin, pOther->pev->origin, dont_ignore_monsters, ENT(pev), &tr );
|
||||
// ALERT( at_console, "%f\n", tr.flFraction );
|
||||
if (tr.flFraction >= 0.90)
|
||||
{
|
||||
vecDir = pOther->pev->origin - pev->origin;
|
||||
flDist = vecDir.Length( );
|
||||
vecDir = vecDir.Normalize( );
|
||||
flDot = DotProduct( gpGlobals->v_forward, vecDir );
|
||||
if ((flDot > 0) && (flDist * (1 - flDot) < flMax))
|
||||
{
|
||||
flMax = flDist * (1 - flDot);
|
||||
vecTarget = vecDir;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
pev->angles = UTIL_VecToAngles( vecTarget );
|
||||
|
||||
// this acceleration and turning math is totally wrong, but it seems to respond well so don't change it.
|
||||
float flSpeed = pev->velocity.Length();
|
||||
if (gpGlobals->time - m_flIgniteTime < 1.0)
|
||||
{
|
||||
pev->velocity = pev->velocity * 0.2 + vecTarget * (flSpeed * 0.8 + 400);
|
||||
if (pev->waterlevel == 3)
|
||||
{
|
||||
// go slow underwater
|
||||
if (pev->velocity.Length() > 300)
|
||||
{
|
||||
pev->velocity = pev->velocity.Normalize() * 300;
|
||||
}
|
||||
UTIL_BubbleTrail( pev->origin - pev->velocity * 0.1, pev->origin, 4 );
|
||||
}
|
||||
else
|
||||
{
|
||||
if (pev->velocity.Length() > 2000)
|
||||
{
|
||||
pev->velocity = pev->velocity.Normalize() * 2000;
|
||||
}
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
if (pev->effects & EF_LIGHT)
|
||||
{
|
||||
pev->effects = 0;
|
||||
STOP_SOUND( ENT(pev), CHAN_VOICE, "weapons/rocket1.wav" );
|
||||
}
|
||||
pev->velocity = pev->velocity * 0.2 + vecTarget * flSpeed * 0.798;
|
||||
if (pev->waterlevel == 0 && pev->velocity.Length() < 1500)
|
||||
{
|
||||
Detonate( );
|
||||
}
|
||||
}
|
||||
// ALERT( at_console, "%.0f\n", flSpeed );
|
||||
|
||||
pev->nextthink = gpGlobals->time + 0.1;
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
void CRpg::Reload( void )
|
||||
{
|
||||
int iResult;
|
||||
|
||||
if ( m_iClip == 1 )
|
||||
{
|
||||
// don't bother with any of this if don't need to reload.
|
||||
return;
|
||||
}
|
||||
|
||||
// because the RPG waits to autoreload when no missiles are active while the LTD is on, the
|
||||
// weapons code is constantly calling into this function, but is often denied because
|
||||
// a) missiles are in flight, but the LTD is on
|
||||
// or
|
||||
// b) player is totally out of ammo and has nothing to switch to, and should be allowed to
|
||||
// shine the designator around
|
||||
//
|
||||
// Set the next attack time into the future so that WeaponIdle will get called more often
|
||||
// than reload, allowing the RPG LTD to be updated
|
||||
|
||||
m_flNextPrimaryAttack = gpGlobals->time + 0.5;
|
||||
|
||||
if ( m_cActiveRockets && m_fSpotActive )
|
||||
{
|
||||
// no reloading when there are active missiles tracking the designator.
|
||||
// ward off future autoreload attempts by setting next attack time into the future for a bit.
|
||||
return;
|
||||
}
|
||||
|
||||
if (m_pSpot && m_fSpotActive)
|
||||
{
|
||||
m_pSpot->Suspend( 2.1 );
|
||||
m_flNextSecondaryAttack = gpGlobals->time + 2.1;
|
||||
}
|
||||
|
||||
if (m_iClip == 0)
|
||||
{
|
||||
iResult = DefaultReload( RPG_MAX_CLIP, RPG_RELOAD, 2 );
|
||||
}
|
||||
|
||||
if (iResult)
|
||||
{
|
||||
m_flTimeWeaponIdle = gpGlobals->time + RANDOM_FLOAT ( 10, 15 );
|
||||
}
|
||||
}
|
||||
|
||||
void CRpg::Spawn( )
|
||||
{
|
||||
Precache( );
|
||||
m_iId = WEAPON_RPG;
|
||||
|
||||
SET_MODEL(ENT(pev), "models/w_rpg.mdl");
|
||||
m_fSpotActive = 1;
|
||||
|
||||
if ( g_pGameRules->IsMultiplayer() )
|
||||
{
|
||||
// more default ammo in multiplay.
|
||||
m_iDefaultAmmo = RPG_DEFAULT_GIVE * 2;
|
||||
}
|
||||
else
|
||||
{
|
||||
m_iDefaultAmmo = RPG_DEFAULT_GIVE;
|
||||
}
|
||||
|
||||
FallInit();// get ready to fall down.
|
||||
}
|
||||
|
||||
|
||||
void CRpg::Precache( void )
|
||||
{
|
||||
PRECACHE_MODEL("models/w_rpg.mdl");
|
||||
PRECACHE_MODEL("models/v_rpg.mdl");
|
||||
PRECACHE_MODEL("models/p_rpg.mdl");
|
||||
|
||||
PRECACHE_SOUND("items/9mmclip1.wav");
|
||||
|
||||
UTIL_PrecacheOther( "laser_spot" );
|
||||
UTIL_PrecacheOther( "rpg_rocket" );
|
||||
|
||||
PRECACHE_SOUND("weapons/rocketfire1.wav");
|
||||
PRECACHE_SOUND("weapons/glauncher.wav"); // alternative fire sound
|
||||
}
|
||||
|
||||
|
||||
int CRpg::GetItemInfo(ItemInfo *p)
|
||||
{
|
||||
p->pszName = STRING(pev->classname);
|
||||
p->pszAmmo1 = "rockets";
|
||||
p->iMaxAmmo1 = ROCKET_MAX_CARRY;
|
||||
p->pszAmmo2 = NULL;
|
||||
p->iMaxAmmo2 = -1;
|
||||
p->iMaxClip = RPG_MAX_CLIP;
|
||||
p->iSlot = 3;
|
||||
p->iPosition = 0;
|
||||
p->iId = m_iId = WEAPON_RPG;
|
||||
p->iFlags = 0;
|
||||
p->iWeight = RPG_WEIGHT;
|
||||
|
||||
return 1;
|
||||
}
|
||||
|
||||
int CRpg::AddToPlayer( CBasePlayer *pPlayer )
|
||||
{
|
||||
if ( CBasePlayerWeapon::AddToPlayer( pPlayer ) )
|
||||
{
|
||||
MESSAGE_BEGIN( MSG_ONE, gmsgWeapPickup, NULL, pPlayer->pev );
|
||||
WRITE_BYTE( m_iId );
|
||||
MESSAGE_END();
|
||||
return TRUE;
|
||||
}
|
||||
return FALSE;
|
||||
}
|
||||
|
||||
BOOL CRpg::Deploy( )
|
||||
{
|
||||
if ( m_iClip == 0 )
|
||||
{
|
||||
return DefaultDeploy( "models/v_rpg.mdl", "models/p_rpg.mdl", RPG_DRAW_UL, "rpg" );
|
||||
}
|
||||
|
||||
return DefaultDeploy( "models/v_rpg.mdl", "models/p_rpg.mdl", RPG_DRAW1, "rpg" );
|
||||
}
|
||||
|
||||
|
||||
BOOL CRpg::CanHolster( void )
|
||||
{
|
||||
if ( m_fSpotActive && m_cActiveRockets )
|
||||
{
|
||||
// can't put away while guiding a missile.
|
||||
return FALSE;
|
||||
}
|
||||
|
||||
return TRUE;
|
||||
}
|
||||
|
||||
void CRpg::Holster( int skiplocal /* = 0 */ )
|
||||
{
|
||||
m_fInReload = FALSE;// cancel any reload in progress.
|
||||
|
||||
m_pPlayer->m_flNextAttack = UTIL_WeaponTimeBase() + 0.5;
|
||||
// m_flTimeWeaponIdle = gpGlobals->time + RANDOM_FLOAT ( 10, 15 );
|
||||
SendWeaponAnim( RPG_HOLSTER1 );
|
||||
if (m_pSpot)
|
||||
{
|
||||
m_pSpot->Killed( NULL, GIB_NEVER );
|
||||
m_pSpot = NULL;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
void CRpg::PrimaryAttack()
|
||||
{
|
||||
if (m_iClip)
|
||||
{
|
||||
m_pPlayer->m_iWeaponVolume = LOUD_GUN_VOLUME;
|
||||
m_pPlayer->m_iWeaponFlash = BRIGHT_GUN_FLASH;
|
||||
|
||||
SendWeaponAnim( RPG_FIRE2 );
|
||||
|
||||
// player "shoot" animation
|
||||
m_pPlayer->SetAnimation( PLAYER_ATTACK1 );
|
||||
|
||||
UTIL_MakeVectors( m_pPlayer->pev->viewangles );
|
||||
Vector vecSrc = m_pPlayer->GetGunPosition( ) + gpGlobals->v_forward * 16 + gpGlobals->v_right * 8 + gpGlobals->v_up * -8;
|
||||
|
||||
CRpgRocket *pRocket = CRpgRocket::CreateRpgRocket( vecSrc, m_pPlayer->pev->viewangles, m_pPlayer, this );
|
||||
|
||||
UTIL_MakeVectors( m_pPlayer->pev->viewangles );// RpgRocket::Create stomps on globals, so remake.
|
||||
pRocket->pev->velocity = pRocket->pev->velocity + gpGlobals->v_forward * DotProduct( m_pPlayer->pev->velocity, gpGlobals->v_forward );
|
||||
|
||||
// firing RPG no longer turns on the designator. ALT fire is a toggle switch for the LTD.
|
||||
// Ken signed up for this as a global change (sjb)
|
||||
|
||||
|
||||
EMIT_SOUND(ENT(m_pPlayer->pev), CHAN_WEAPON, "weapons/rocketfire1.wav", 0.9, ATTN_NORM );
|
||||
EMIT_SOUND(ENT(m_pPlayer->pev), CHAN_ITEM, "weapons/glauncher.wav", 0.7, ATTN_NORM );
|
||||
|
||||
m_iClip--;
|
||||
//m_pPlayer->m_rgAmmo[m_iPrimaryAmmoType]--;
|
||||
|
||||
m_flNextPrimaryAttack = gpGlobals->time + 1.5;
|
||||
m_flTimeWeaponIdle = gpGlobals->time + 1.5;
|
||||
m_pPlayer->pev->punchangle.x -= 5;
|
||||
}
|
||||
else
|
||||
{
|
||||
PlayEmptySound( );
|
||||
}
|
||||
UpdateSpot( );
|
||||
}
|
||||
|
||||
|
||||
void CRpg::SecondaryAttack()
|
||||
{
|
||||
m_fSpotActive = ! m_fSpotActive;
|
||||
|
||||
if (!m_fSpotActive && m_pSpot)
|
||||
{
|
||||
m_pSpot->Killed( NULL, GIB_NORMAL );
|
||||
m_pSpot = NULL;
|
||||
}
|
||||
|
||||
m_flNextSecondaryAttack = gpGlobals->time + 0.2;
|
||||
}
|
||||
|
||||
|
||||
void CRpg::WeaponIdle( void )
|
||||
{
|
||||
UpdateSpot( );
|
||||
|
||||
ResetEmptySound( );
|
||||
|
||||
if (m_flTimeWeaponIdle > gpGlobals->time)
|
||||
return;
|
||||
|
||||
if (m_pPlayer->m_rgAmmo[m_iPrimaryAmmoType])
|
||||
{
|
||||
int iAnim;
|
||||
float flRand = RANDOM_FLOAT(0, 1);
|
||||
if (flRand <= 0.75 || m_fSpotActive)
|
||||
{
|
||||
if ( m_iClip == 0 )
|
||||
iAnim = RPG_IDLE_UL;
|
||||
else
|
||||
iAnim = RPG_IDLE;
|
||||
|
||||
m_flTimeWeaponIdle = gpGlobals->time + 90.0 / 15.0;
|
||||
}
|
||||
else
|
||||
{
|
||||
if ( m_iClip == 0 )
|
||||
iAnim = RPG_FIDGET_UL;
|
||||
else
|
||||
iAnim = RPG_FIDGET;
|
||||
|
||||
m_flTimeWeaponIdle = gpGlobals->time + 3.0;
|
||||
}
|
||||
|
||||
SendWeaponAnim( iAnim );
|
||||
}
|
||||
else
|
||||
{
|
||||
m_flTimeWeaponIdle = gpGlobals->time + 1;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
void CRpg::UpdateSpot( void )
|
||||
{
|
||||
if (m_fSpotActive)
|
||||
{
|
||||
if (!m_pSpot)
|
||||
{
|
||||
m_pSpot = CLaserSpot::CreateSpot();
|
||||
}
|
||||
|
||||
UTIL_MakeVectors( m_pPlayer->pev->viewangles );
|
||||
Vector vecSrc = m_pPlayer->GetGunPosition( );;
|
||||
Vector vecAiming = gpGlobals->v_forward;
|
||||
|
||||
TraceResult tr;
|
||||
UTIL_TraceLine ( vecSrc, vecSrc + vecAiming * 8192, dont_ignore_monsters, ENT(m_pPlayer->pev), &tr );
|
||||
|
||||
// ALERT( "%f %f\n", gpGlobals->v_forward.y, vecAiming.y );
|
||||
|
||||
/*
|
||||
float a = gpGlobals->v_forward.y * vecAiming.y + gpGlobals->v_forward.x * vecAiming.x;
|
||||
m_pPlayer->pev->punchangle.y = acos( a ) * (180 / M_PI);
|
||||
|
||||
ALERT( at_console, "%f\n", a );
|
||||
*/
|
||||
|
||||
UTIL_SetOrigin( m_pSpot->pev, tr.vecEndPos );
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
class CRpgAmmo : public CBasePlayerAmmo
|
||||
{
|
||||
void Spawn( void )
|
||||
{
|
||||
Precache( );
|
||||
SET_MODEL(ENT(pev), "models/w_rpgammo.mdl");
|
||||
CBasePlayerAmmo::Spawn( );
|
||||
}
|
||||
void Precache( void )
|
||||
{
|
||||
PRECACHE_MODEL ("models/w_rpgammo.mdl");
|
||||
PRECACHE_SOUND("items/9mmclip1.wav");
|
||||
}
|
||||
BOOL AddAmmo( CBaseEntity *pOther )
|
||||
{
|
||||
int iGive;
|
||||
|
||||
if ( g_pGameRules->IsMultiplayer() )
|
||||
{
|
||||
// hand out more ammo per rocket in multiplayer.
|
||||
iGive = AMMO_RPGCLIP_GIVE * 2;
|
||||
}
|
||||
else
|
||||
{
|
||||
iGive = AMMO_RPGCLIP_GIVE;
|
||||
}
|
||||
|
||||
if (pOther->GiveAmmo( iGive, "rockets", ROCKET_MAX_CARRY ) != -1)
|
||||
{
|
||||
EMIT_SOUND(ENT(pev), CHAN_ITEM, "items/9mmclip1.wav", 1, ATTN_NORM);
|
||||
return TRUE;
|
||||
}
|
||||
return FALSE;
|
||||
}
|
||||
};
|
||||
LINK_ENTITY_TO_CLASS( ammo_rpgclip, CRpgAmmo );
|
||||
|
||||
#endif
|
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue