Files
monstermod-redo-repo-copy/src/dlls/rgrunt.cpp
2023-02-05 17:13:39 -03:00

424 lines
12 KiB
C++

/***
*
* Copyright (c) 1996-2001, Valve LLC. All rights reserved.
*
* This product contains software technology licensed from Id
* Software, Inc. ("Id Technology"). Id Technology (c) 1996 Id Software, Inc.
* All Rights Reserved.
*
* 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.
*
****/
//=========================================================
// Robo Grunt
//=========================================================
#include "extdll.h"
#include "plane.h"
#include "util.h"
#include "cmbase.h"
#include "cmbasemonster.h"
#include "monsters.h"
#include "schedule.h"
#include "animation.h"
#include "weapons.h"
#include "cmtalkmonster.h"
#include "effects.h"
#include "explode.h"
#include "customentity.h"
//=========================================================
// monster-specific DEFINE's
//=========================================================
#define RGRUNT_CLIP_SIZE 36 // how many bullets in a clip? - NOTE: 3 round burst sound, so keep as 3 * x!
// Weapon flags
#define RGRUNT_9MMAR (1 << 0)
#define RGRUNT_HANDGRENADE (1 << 1)
#define RGRUNT_GRENADELAUNCHER (1 << 2)
#define RGRUNT_SHOTGUN (1 << 3)
// Body groups
#define GUN_GROUP 2
// Gun values
#define GUN_MP5 0
#define GUN_SHOTGUN 1
#define GUN_NONE 2
// How many sparks to emit when low on health
#define RGRUNT_MAX_SPARKS 5
//=========================================================
// These sounds are muted for Robo Grunts
//=========================================================
BOOL CMRGrunt::FOkToSpeak(void)
{
return FALSE;
}
void CMRGrunt::IdleSound(void)
{
}
void CMRGrunt::PainSound(void)
{
}
//=========================================================
// DeathSound
//=========================================================
void CMRGrunt::DeathSound(void)
{
switch ( RANDOM_LONG(0,1) )
{
case 0:
EMIT_SOUND( ENT(pev), CHAN_VOICE, "turret/tu_die.wav", 1, ATTN_IDLE );
break;
case 1:
EMIT_SOUND( ENT(pev), CHAN_VOICE, "turret/tu_die2.wav", 1, ATTN_IDLE );
break;
}
// Use this spot to activate the explosion
int duration = RANDOM_LONG( 3, 9 );
/* Smoke effect */
// variable smoke balls
// 1.7X normal size
// 0 radial distribution
// instant start
SmokeCreate( pev->origin, duration * 7, 17, 0, 0 );
// "Gib"
pev->nextthink = gpGlobals->time + duration;
SetThink( &CMRGrunt::StartGib );
}
//=========================================================
// Classify - indicates this monster's place in the
// relationship table.
//=========================================================
int CMRGrunt::Classify(void)
{
if ( m_iClassifyOverride == -1 ) // helper
return CLASS_NONE;
else if ( m_iClassifyOverride > 0 )
return m_iClassifyOverride; // override
return CLASS_HUMAN_MILITARY;
}
//=========================================================
// Killed - Explode a few seconds after death
//=========================================================
void CMRGrunt::Killed(entvars_t *pevAttacker, int iGib)
{
// Turn off electricity
if ( m_flActiveDischarge != 0 )
{
pev->renderfx = kRenderFxNone;
m_flActiveDischarge = 0;
}
// Disallow this monster to fade away, need to keep it around for the explosion
pev->owner = 0;
pev->spawnflags &= ~SF_MONSTER_FADECORPSE;
pev->solid = SOLID_NOT; // stop interacting with the world
CMBaseMonster::Killed(pevAttacker, iGib);
}
void CMRGrunt::StartGib(void)
{
// derp
GibMonster();
}
//=========================================================
// GibMonster - Boom!
//=========================================================
void CMRGrunt::GibMonster()
{
// Don't call this more times than needed
if ( pev->iuser1 != 0 )
return;
pev->iuser1 = 1;
Vector vecSpot = pev->origin + (pev->mins + pev->maxs) * 0.5;
// Explosion
ExplosionCreate( vecSpot, g_vecZero, ENT(pev), 128, 0, 0 );
// Wreckage
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( 96 );
WRITE_COORD( 96 );
WRITE_COORD( 16 );
// velocity
WRITE_COORD( 0 );
WRITE_COORD( 0 );
WRITE_COORD( 30 );
// randomization
WRITE_BYTE( 15 );
// Model
WRITE_SHORT( m_iBodyGibs ); //model id#
// # of shards
WRITE_BYTE( 35 );
// duration
WRITE_BYTE( 100 );// 5.0 seconds
// flags
WRITE_BYTE( BREAK_METAL );
MESSAGE_END();
SetThink( &CMBaseEntity::SUB_Remove );
pev->nextthink = gpGlobals->time;
}
//=========================================================
// RunAI - Robo Grunt emits sparks when its low on health.
//=========================================================
void CMRGrunt::RunAI(void)
{
CMBaseMonster::RunAI();
if ( pev->health <= ( pev->max_health / 10 ) ) // below 10% health
{
// Spark ON
if ( gpGlobals->time > m_flNextSpark )
{
// Code looks familiar? It's CBaseButton::DoSpark
for ( int spark_num = 0; spark_num < RGRUNT_MAX_SPARKS; spark_num++ )
{
Vector tmp = pev->origin + (pev->mins + pev->maxs) * 0.5; // grab center
tmp.x += RANDOM_FLOAT( -( pev->size.x / 2 ), pev->size.x / 2); // then randomize
tmp.y += RANDOM_FLOAT( -( pev->size.y / 2 ), pev->size.y / 2);
tmp.z += RANDOM_FLOAT( -( pev->size.z / 2 ), pev->size.z / 2);
UTIL_Sparks( tmp );
}
switch ( (int)(RANDOM_FLOAT(0,1) * 6) )
{
case 0: EMIT_SOUND(ENT(pev), CHAN_VOICE, "buttons/spark1.wav", 0.6, ATTN_NORM); break;
case 1: EMIT_SOUND(ENT(pev), CHAN_VOICE, "buttons/spark2.wav", 0.6, ATTN_NORM); break;
case 2: EMIT_SOUND(ENT(pev), CHAN_VOICE, "buttons/spark3.wav", 0.6, ATTN_NORM); break;
case 3: EMIT_SOUND(ENT(pev), CHAN_VOICE, "buttons/spark4.wav", 0.6, ATTN_NORM); break;
case 4: EMIT_SOUND(ENT(pev), CHAN_VOICE, "buttons/spark5.wav", 0.6, ATTN_NORM); break;
case 5: EMIT_SOUND(ENT(pev), CHAN_VOICE, "buttons/spark6.wav", 0.6, ATTN_NORM); break;
}
m_flNextSpark = gpGlobals->time + 0.5;
}
// Glow/Hurt ON
if ( gpGlobals->time > m_flNextDischarge )
{
// Turn on the electric glow
pev->renderfx = kRenderFxGlowShell;
pev->rendercolor = Vector( 100, 150, 250 ); // r, g, b
EMIT_SOUND(ENT(pev), CHAN_BODY, "debris/beamstart14.wav", 0.8, ATTN_NORM);
// Sustain the electricity for this long
m_flActiveDischarge = gpGlobals->time + RANDOM_FLOAT( 0.3, 0.6 );
// Discharge again in...
m_flNextDischarge = gpGlobals->time + RANDOM_FLOAT( 0.9, 2.7 );
}
// Glow/Hurt OFF
if ( gpGlobals->time > m_flActiveDischarge )
{
// Turn off electricity
pev->renderfx = kRenderFxNone;
m_flActiveDischarge = 0;
}
}
}
//=========================================================
// SparkTouch - Hurt players who come too close to it
//=========================================================
void CMRGrunt::SparkTouch( edict_t *pOther )
{
// No electricity, no harm
if ( m_flActiveDischarge == 0 )
return;
// Only affect players
if ( UTIL_IsPlayer( pOther ) )
{
// Because of Touch(), players are going to be hurt every server frame.
// Don't be bullshit like Sven Co-op, set the damage REALLY LOW.
UTIL_TakeDamage( pOther, pev, pev, 1, DMG_SHOCK );
}
}
//=========================================================
// TraceAttack - Override for robo grunt
// Emit ricochet sparks if getting hurt from bullets
//=========================================================
void CMRGrunt::TraceAttack( entvars_t *pevAttacker, float flDamage, Vector vecDir, TraceResult *ptr, int bitsDamageType )
{
// Absorb damage and emit ricochet if bullets or melee attacks are used
if ( bitsDamageType & ( DMG_BULLET | DMG_SLASH | DMG_CLUB ) )
{
if ( RANDOM_LONG( 0, 100 ) < 20 )
{
UTIL_Ricochet( ptr->vecEndPos, RANDOM_FLOAT( 0.5, 1.5 ) );
// EMIT_SOUND_DYN( ENT(pev), CHAN_BODY, pRicSounds[ RANDOM_LONG(0,ARRAYSIZE(pRicSounds)-1) ], 1.0, ATTN_NORM, 0, PITCH_NORM );
}
flDamage *= (1.00f - gSkillData.rgruntArmor); // cut damage
}
// Lower protection against explosions
else if ( bitsDamageType & DMG_BLAST )
flDamage *= (1.00f - (gSkillData.rgruntArmor / 2.00f)); // 50% less protection
// No protection at all against other types of damages
CMBaseMonster::TraceAttack( pevAttacker, flDamage, vecDir, ptr, bitsDamageType );
}
//=========================================================
// TakeDamage - Robo Grunts should not take cover as soon
// as they take damage.
//=========================================================
int CMRGrunt::TakeDamage( entvars_t *pevInflictor, entvars_t *pevAttacker, float flDamage, int bitsDamageType )
{
return CMBaseMonster::TakeDamage( pevInflictor, pevAttacker, flDamage, bitsDamageType );
}
//=========================================================
// Spawn
//=========================================================
void CMRGrunt::Spawn()
{
Precache();
SET_MODEL(ENT(pev), "models/rgrunt.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->effects = 0;
pev->health = gSkillData.rgruntHealth;
pev->max_health = pev->health; // to determine when sparks should be emitted
m_flFieldOfView = 0.2;// indicates the width of this monster's forward view cone ( as a dotproduct result )
m_MonsterState = MONSTERSTATE_NONE;
m_flNextGrenadeCheck = gpGlobals->time + 1;
m_flNextPainTime = gpGlobals->time;
m_flNextSpark = gpGlobals->time; // when to emit sparks again
m_flNextDischarge = gpGlobals->time; // when electric shell should activate
m_flActiveDischarge = 0; // how long to sustain the electricity
m_iSentence = -1;
//m_afCapability = bits_CAP_SQUAD | bits_CAP_TURN_HEAD | bits_CAP_DOORS_GROUP;
m_afCapability = bits_CAP_TURN_HEAD | bits_CAP_DOORS_GROUP;
//m_fEnemyEluded = FALSE;
m_fFirstEncounter = TRUE;// this is true when the grunt spawns, because he hasn't encountered an enemy yet.
m_HackedGunPos = Vector(0, 0, 55);
if (pev->weapons == 0)
{
// weapons not specified, randomize
switch(RANDOM_LONG(0, 2))
{
case 0:
pev->weapons = RGRUNT_9MMAR | RGRUNT_HANDGRENADE;
break;
case 1:
pev->weapons = RGRUNT_SHOTGUN;
break;
case 2:
pev->weapons = RGRUNT_9MMAR | RGRUNT_GRENADELAUNCHER;
break;
}
}
if (FBitSet(pev->weapons, RGRUNT_SHOTGUN))
{
SetBodygroup(GUN_GROUP, GUN_SHOTGUN);
m_cClipSize = 8;
}
else
{
m_cClipSize = RGRUNT_CLIP_SIZE;
}
m_cAmmoLoaded = m_cClipSize;
CMTalkMonster::g_talkWaitTime = 0;
MonsterInit();
SetTouch( &CMRGrunt::SparkTouch );
pev->classname = MAKE_STRING( "monster_robogrunt" );
if ( strlen( STRING( m_szMonsterName ) ) == 0 )
{
// default name
m_szMonsterName = MAKE_STRING( "Robo Grunt" );
}
}
//=========================================================
// Precache - precaches all resources this monster needs
//=========================================================
void CMRGrunt::Precache()
{
PRECACHE_MODEL("models/rgrunt.mdl");
m_iBodyGibs = PRECACHE_MODEL( "models/metalplategibs_green.mdl" );
PRECACHE_SOUND("hgrunt/gr_mgun1.wav");
PRECACHE_SOUND("hgrunt/gr_mgun2.wav");
PRECACHE_SOUND("turret/tu_die.wav");
PRECACHE_SOUND("turret/tu_die2.wav");
PRECACHE_SOUND("buttons/spark1.wav");
PRECACHE_SOUND("buttons/spark2.wav");
PRECACHE_SOUND("buttons/spark3.wav");
PRECACHE_SOUND("buttons/spark4.wav");
PRECACHE_SOUND("buttons/spark5.wav");
PRECACHE_SOUND("buttons/spark6.wav");
PRECACHE_SOUND("debris/beamstart14.wav");
PRECACHE_SOUND("hgrunt/gr_reload1.wav");
PRECACHE_SOUND("weapons/glauncher.wav");
PRECACHE_SOUND("weapons/sbarrel1.wav");
PRECACHE_SOUND("zombie/claw_miss2.wav");// because we use the basemonster SWIPE animation event
/*
// get voice pitch
if (RANDOM_LONG(0, 1))
m_voicePitch = 109 + RANDOM_LONG(0, 7);
else
m_voicePitch = 100;
*/
m_iBrassShell = PRECACHE_MODEL("models/shell.mdl");// brass shell
}