Portable Half-Life SDK. GoldSource and Xash3D. Crossplatform.
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
 
 
 
 
 
 

631 lines
15 KiB

#include "extdll.h"
#include "util.h"
#include "cbase.h"
#include "effects.h"
#include "weapons.h"
#include "explode.h"
#include "monsters.h"
#include "player.h"
#include "soundent.h"
class CMortarShell : public CGrenade
{
public:
void Precache();
void BurnThink();
void MortarExplodeTouch(CBaseEntity *pOther);
void Spawn();
void FlyThink();
int Save(CSave &save);
int Restore(CRestore &restore);
static CMortarShell *CreateMortarShell(Vector p_VecOrigin, Vector p_VecAngles, CBaseEntity *pOwner, int velocity);
static TYPEDESCRIPTION m_SaveData[];
int m_iTrail;
BOOL m_iSoundedOff;
float m_flIgniteTime;
float m_velocity;
};
LINK_ENTITY_TO_CLASS(mortar_shell, CMortarShell)
TYPEDESCRIPTION CMortarShell::m_SaveData[] =
{
DEFINE_FIELD(CMortarShell, m_velocity, FIELD_FLOAT),
DEFINE_FIELD(CMortarShell, m_flIgniteTime, FIELD_FLOAT),
DEFINE_FIELD(CMortarShell, m_iSoundedOff, FIELD_BOOLEAN),
};
IMPLEMENT_SAVERESTORE(CMortarShell, CGrenade)
void CMortarShell::Precache()
{
PRECACHE_MODEL("models/mortarshell.mdl");
m_iTrail = PRECACHE_MODEL("sprites/wep_smoke_01.spr");
PRECACHE_SOUND("weapons/ofmortar.wav");
}
void CMortarShell::Spawn()
{
Precache();
SET_MODEL(ENT(pev), "models/mortarshell.mdl");
UTIL_SetSize(pev, g_vecZero, g_vecZero);
pev->solid = SOLID_BBOX;
pev->movetype = MOVETYPE_BOUNCE;
pev->classname = MAKE_STRING("mortar_shell");
SetThink(&CMortarShell::BurnThink);
SetTouch(&CMortarShell::MortarExplodeTouch);
pev->dmg = gSkillData.plrDmgRPG*2;
pev->nextthink = gpGlobals->time + 0.1;
m_flIgniteTime = gpGlobals->time;
m_iSoundedOff = FALSE;
}
void CMortarShell::MortarExplodeTouch(CBaseEntity *pOther)
{
TraceResult tr;
Vector vecSpot;
pev->model = iStringNull;//invisible
pev->solid = SOLID_NOT;// intangible
pev->takedamage = DAMAGE_NO;
vecSpot = pev->origin + Vector( 0, 0, 8 );
UTIL_TraceLine( vecSpot, vecSpot + Vector( 0, 0, -40 ), ignore_monsters, dont_ignore_glass, ENT(pev), &tr );
// Pull out of the wall a bit
if( tr.flFraction != 1.0 )
{
pev->origin = tr.vecEndPos + ( tr.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) * 5); // scale * 10
WRITE_BYTE( 10 ); // 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, DMG_BLAST );
if( RANDOM_FLOAT( 0, 1 ) < 0.5 )
{
UTIL_DecalTrace( &tr, 11 );
}
else
{
UTIL_DecalTrace( &tr, 12 );
}
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( &CGrenade::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, tr.vecPlaneNormal, NULL );
}
}
void CMortarShell::BurnThink()
{
UTIL_VecToAngles(pev->velocity);
pev->angles = pev->velocity;
MESSAGE_BEGIN(MSG_PVS, SVC_TEMPENTITY, pev->origin);
WRITE_BYTE(110);
WRITE_COORD(pev->origin.x);
WRITE_COORD(pev->origin.y);
WRITE_COORD(pev->origin.z);
WRITE_COORD(0);
WRITE_COORD(0);
WRITE_COORD(1);
WRITE_SHORT(m_iTrail);
WRITE_BYTE(1);
WRITE_BYTE(12);
WRITE_BYTE(120);
MESSAGE_END();
if(!((m_flIgniteTime + 0.2) >= gpGlobals->time))
SetThink(&CMortarShell::FlyThink);
pev->nextthink = gpGlobals->time + 0.01;
}
void CMortarShell::FlyThink()
{
pev->angles = UTIL_VecToAngles(pev->velocity);
pev->angles.x -= 90;
if(pev->velocity.z < 20 && !m_iSoundedOff)
{
m_iSoundedOff = TRUE;
EMIT_SOUND_DYN(ENT(pev), 2, "weapons/ofmortar.wav", 1, 0, 0, 100);
}
pev->nextthink = gpGlobals->time + 0.1;
}
CMortarShell *CMortarShell::CreateMortarShell(Vector p_VecOrigin, Vector p_VecAngles, CBaseEntity *pOwner, int velocity)
{
CMortarShell *rocket = GetClassPtr( (CMortarShell *)NULL );
rocket->Spawn();
rocket->pev->gravity = 1;
UTIL_SetOrigin( rocket->pev, p_VecOrigin );
rocket->pev->angles = UTIL_VecToAngles(p_VecAngles);
UTIL_MakeVectors(p_VecAngles);
rocket->pev->velocity = rocket->pev->velocity - gpGlobals->v_forward * velocity;
if (pOwner)
rocket->pev->owner = ENT(pOwner->pev);
return rocket;
}
#define SF_MORTAR_ACTIVE (1 << 0)
#define SF_MORTAR_LINE_OF_SIGHT (1 << 4)
#define SF_MORTAR_CAN_CONTROL (1 << 5)
class COp4Mortar : public CBaseMonster
{
public:
void Spawn();
virtual int TakeDamage(entvars_t *pevInflictor, entvars_t *pevAttacker, float flDamage, int bitsDamageType);
virtual void Use(CBaseEntity *pActivator, CBaseEntity *pCaller, USE_TYPE useType, float value);
void Precache();
void KeyValue(KeyValueData *pvkd);
void UpdatePosition(float direction, int controller);
void AIUpdatePosition();
virtual int Save(CSave &save);
virtual int Restore(CRestore &restore);
CBaseEntity *FindTarget();
void MortarThink();
static TYPEDESCRIPTION m_SaveData[];
int ObjectCaps() { return 0; }
float m_FireDelay;
BOOL m_tracking;
float m_minRange;
float m_maxRange;
float m_lastupdate;
float m_zeroYaw;
float m_lastFire;
float m_trackDelay;
float m_flExplodeTime;
int m_hmax;
int m_hmin;
int d_x;
int d_y;
int m_velocity;
int m_iEnemyType;
int m_iUpdateTime;
Vector m_vGunAngle;
Vector m_vIdealGunVector;
Vector m_vIdealGunAngle;
};
LINK_ENTITY_TO_CLASS(op4mortar, COp4Mortar)
TYPEDESCRIPTION COp4Mortar::m_SaveData[] =
{
DEFINE_FIELD( COp4Mortar, m_tracking, FIELD_BOOLEAN ),
DEFINE_FIELD( COp4Mortar, m_FireDelay, FIELD_FLOAT ),
DEFINE_FIELD( COp4Mortar, m_minRange, FIELD_FLOAT),
DEFINE_FIELD( COp4Mortar, m_maxRange, FIELD_FLOAT ),
DEFINE_FIELD( COp4Mortar, m_lastFire, FIELD_FLOAT),
DEFINE_FIELD( COp4Mortar, m_trackDelay, FIELD_FLOAT ),
DEFINE_FIELD( COp4Mortar, m_hmax, FIELD_INTEGER ),
DEFINE_FIELD( COp4Mortar, m_hmin, FIELD_INTEGER ),
DEFINE_FIELD( COp4Mortar, m_velocity, FIELD_INTEGER ),
DEFINE_FIELD( COp4Mortar, m_iEnemyType, FIELD_INTEGER ),
DEFINE_FIELD( COp4Mortar, m_vGunAngle, FIELD_VECTOR ),
DEFINE_FIELD(COp4Mortar, m_lastupdate, FIELD_FLOAT),
};
IMPLEMENT_SAVERESTORE( COp4Mortar, CBaseMonster )
void COp4Mortar::Precache()
{
PRECACHE_MODEL("models/mortar.mdl");
PRECACHE_SOUND("weapons/mortarhit.wav");
PRECACHE_SOUND("player/pl_grate1.wav");
UTIL_PrecacheOther("mortar_shell");
}
void COp4Mortar::Spawn()
{
float angle;
Precache();
UTIL_SetOrigin(pev, pev->origin);
SET_MODEL(ENT(pev), "models/mortar.mdl");
pev->sequence = LookupSequence("idle");
ResetSequenceInfo();
pev->frame = 0;
pev->framerate = 1;
m_tracking = FALSE;
m_lastupdate = gpGlobals->time + 0.5;
m_vGunAngle = Vector(0,0,0);
m_iUpdateTime = 0;
if(m_FireDelay < 0.5)
m_FireDelay = 5;
if(m_minRange == 0)
m_minRange = 128;
if(m_maxRange == 0)
m_maxRange = 3072;
InitBoneControllers();
angle = pev->angles.y + 180;
m_zeroYaw = UTIL_AngleMod(angle);
m_hEnemy = NULL;
if (pev->spawnflags & SF_MORTAR_ACTIVE)
SetThink(&COp4Mortar::MortarThink);
else
SetThink(NULL);
m_flExplodeTime = gpGlobals->time + 5;
pev->nextthink = gpGlobals->time + 0.01;
}
void COp4Mortar::UpdatePosition(float direction, int controller)
{
if(m_vGunAngle.y > 90)
m_vGunAngle.y = 90;
if(m_vGunAngle.y < -90)
m_vGunAngle.y = -90;
if(m_vGunAngle.x > 90)
m_vGunAngle.x = 90;
if(m_vGunAngle.x < 0)
m_vGunAngle.x = 0;
if(controller == 1)
m_vGunAngle.y += direction/2;
else
m_vGunAngle.x += direction/2;
if(m_iUpdateTime >= 15)
{
EMIT_SOUND_DYN(ENT(pev), 2, "player/pl_grate1.wav", 1, 0.8, 0, 100);
m_iUpdateTime = 0;
}
SetBoneController(1, m_vGunAngle.y);
SetBoneController(0, m_vGunAngle.x);
m_lastupdate = gpGlobals->time + 0.1;
m_iUpdateTime++;
}
void COp4Mortar::MortarThink()
{
Vector pos, angle, vecTarget;
if(m_fSequenceFinished)
{
if(pev->sequence != LookupSequence("idle"))
{
pev->frame = 0;
pev->sequence = LookupSequence("idle");
ResetSequenceInfo();
}
}
DispatchAnimEvents();
StudioFrameAdvance();
pev->nextthink = gpGlobals->time + 0.1;
if(pev->spawnflags & SF_MORTAR_ACTIVE)
{
if(m_hEnemy == 0 || !m_hEnemy->IsAlive())
{
m_hEnemy = FindTarget();
}
}
if(m_hEnemy)
{
vecTarget = Vector( m_hEnemy->pev->origin.x, m_hEnemy->pev->origin.y, m_hEnemy->pev->absmax.z);
if((pev->origin - m_hEnemy->pev->origin).Length() <= m_maxRange)
{
GetAttachment(0, pos, angle);
m_vIdealGunVector = VecCheckThrow(pev, pos, vecTarget, 700, 1);
m_vIdealGunVector.x =- m_vIdealGunVector.x;
m_vIdealGunVector.y =- m_vIdealGunVector.y;
m_vIdealGunAngle = UTIL_VecToAngles(m_vIdealGunVector);
m_trackDelay = gpGlobals->time;
}
AIUpdatePosition();
}
if(m_hEnemy != 0 && gpGlobals->time - m_lastFire > 5 && (m_hEnemy->pev->origin - pev->origin).Length() > 710)
{
EMIT_SOUND_DYN(ENT(pev), 2, "weapons/mortarhit.wav", 1, 0.8, 0, 100);
UTIL_ScreenShake(pev->origin, 12, 100, 2, 1000);
float speed = m_vIdealGunVector.Length();
angle = m_vIdealGunAngle;
if(speed > 0)
{
if(CMortarShell::CreateMortarShell(pev->origin, angle, this, floor(speed)))
{
pev->frame = 0;
pev->sequence = LookupSequence("fire");
ResetSequenceInfo();
m_lastFire = gpGlobals->time;
}
}
}
}
CBaseEntity *COp4Mortar::FindTarget()
{
CBaseEntity *pPlayer;
Vector BarretEnd;
Vector BarretAngle;
Vector targetPosition;
TraceResult tr;
CBaseEntity *pIdealTarget;
if((pPlayer = UTIL_FindEntityByClassname(0, "player")) == NULL )
return NULL;
m_pLink = 0;
GetAttachment(0, BarretEnd, BarretAngle);
float dist = (pPlayer->pev->origin - pev->origin).Length();
if(pPlayer->IsAlive())
{
if(m_maxRange >= dist)
{
targetPosition = pPlayer->pev->origin + pev->view_ofs;
UTIL_TraceLine(BarretEnd, targetPosition, ignore_monsters, dont_ignore_glass, ENT(pev), &tr);
if((!(pev->spawnflags & SF_MORTAR_LINE_OF_SIGHT) || tr.pHit == ENT(pPlayer->pev)) && !m_iEnemyType)
{
pIdealTarget = pPlayer;
}
}
}
else
{
pIdealTarget = NULL;
}
return pIdealTarget;
}
int COp4Mortar::TakeDamage(entvars_t *pevInflictor, entvars_t *pevAttacker, float flDamage, int bitsDamageType)
{
return CBaseMonster::TakeDamage(pevInflictor, pevAttacker, 0, bitsDamageType);
}
void COp4Mortar::KeyValue(KeyValueData *pvkd)
{
if(strcmp(pvkd->szKeyName, "m_hmax"))
{
m_hmax = atoi(pvkd->szValue);
pvkd->fHandled = 1;
}
else if(strcmp(pvkd->szKeyName, "m_hmin"))
{
m_hmin = atoi(pvkd->szValue);
pvkd->fHandled = 1;
}
else if(strcmp(pvkd->szKeyName, "mortar_velocity"))
{
m_velocity = atoi(pvkd->szValue);
pvkd->fHandled = 1;
}
else if(strcmp(pvkd->szKeyName, "mindist"))
{
m_minRange = atoi(pvkd->szValue);
pvkd->fHandled = 1;
}
else if(strcmp(pvkd->szKeyName, "maxdist"))
{
m_maxRange = atoi(pvkd->szValue);
pvkd->fHandled = 1;
}
else if(strcmp(pvkd->szKeyName, "enemytype"))
{
m_iEnemyType = atoi(pvkd->szValue);
pvkd->fHandled = 1;
}
else if(strcmp(pvkd->szKeyName, "firedelay"))
{
m_FireDelay = atoi(pvkd->szValue);
pvkd->fHandled = 1;
}
else
{
CBaseToggle::KeyValue(pvkd);
}
}
void COp4Mortar::Use(CBaseEntity *pActivator, CBaseEntity *pCaller, USE_TYPE useType, float value)
{
SetThink(NULL);
if((pev->spawnflags & SF_MORTAR_CAN_CONTROL) && (pActivator->pev->origin - pev->origin).Length() < 170)
{
EMIT_SOUND_DYN(ENT(pev), 2, "weapons/mortarhit.wav", 1, 0, 0, 100);
UTIL_ScreenShake(pev->origin, 12, 100, 2, 1000);
Vector pos, angle;
GetAttachment(0, pos, angle);
angle = m_vGunAngle;
float anglemod = pev->angles.y + m_vGunAngle.y;
angle.y = UTIL_AngleMod(anglemod);
if((CMortarShell::CreateMortarShell(pos, angle, this, 2000 - (m_vGunAngle.x * 12.25))) != NULL)
{
pev->frame = 0;
pev->sequence = LookupSequence("fire");
ResetSequenceInfo();
}
}
}
void COp4Mortar::AIUpdatePosition()
{
if(m_hEnemy != 0 && (m_hEnemy->pev->origin - pev->origin).Length() < 710)
return;
if(m_vIdealGunAngle.x == 270 && m_vIdealGunAngle.y == 0)
{
m_vIdealGunAngle.x = 0;
m_vIdealGunAngle.y = 0;
}
if(m_vIdealGunAngle.x <= 0)
m_vIdealGunAngle.x = 0;
if(m_vIdealGunAngle.x > 90)
m_vIdealGunAngle.x = 90;
if(m_vIdealGunAngle.y > 165 && m_vIdealGunAngle.y < 270)
m_vIdealGunAngle.y = -90;
else if(m_vIdealGunAngle.y > 90 && m_vIdealGunAngle.y < 165)
m_vIdealGunAngle.y = 90;
SetBoneController(0, m_vIdealGunAngle.x);
SetBoneController(1, m_vIdealGunAngle.y);
m_vGunAngle = m_vIdealGunAngle;
}
//========================================================
// COp4MortarController
//========================================================
class COp4MortarController : public CBaseToggle
{
public:
void Spawn();
int Restore(CRestore &restore);
int Save(CSave &save);
void KeyValue(KeyValueData *pvkd);
void Use(CBaseEntity *pActivator, CBaseEntity *pCaller, USE_TYPE useType, float value);
int ObjectCaps() { return FCAP_CONTINUOUS_USE; }
static TYPEDESCRIPTION m_SaveData[];
float m_direction;
float m_lastpush;
int m_controller;
};
LINK_ENTITY_TO_CLASS(func_op4mortarcontroller, COp4MortarController)
TYPEDESCRIPTION COp4MortarController::m_SaveData[] =
{
DEFINE_FIELD(COp4MortarController, m_controller, FIELD_INTEGER),
DEFINE_FIELD(COp4MortarController, m_direction, FIELD_INTEGER),
};
IMPLEMENT_SAVERESTORE( COp4MortarController, CBaseToggle )
void COp4MortarController::Spawn()
{
pev->solid = SOLID_NOT;
pev->movetype = MOVETYPE_PUSH;
UTIL_SetOrigin(pev, pev->origin);
SET_MODEL(ENT(pev), STRING(pev->model));
m_direction = -1;
m_lastpush = gpGlobals->time;
}
void COp4MortarController::Use(CBaseEntity *pActivator, CBaseEntity *pCaller, USE_TYPE useType, float value)
{
if(gpGlobals->time - m_lastpush > 0.5)
m_direction = -m_direction;
CBaseEntity* ent = UTIL_FindEntityByTargetname(NULL, STRING(pev->target));
if (ent) {
if (FClassnameIs(ent->pev, "op4mortar")) {
COp4Mortar *Mortar = (COp4Mortar*)ent;
Mortar->UpdatePosition(m_direction, m_controller);
} else {
ALERT(at_console, "Found %s, but it's not op4mortar!\n", STRING(pev->target));
}
}
m_lastpush = gpGlobals->time;
}
void COp4MortarController::KeyValue(KeyValueData *pvkd)
{
if(FStrEq(pvkd->szKeyName, "mortar_axis"))
{
m_controller = atoi(pvkd->szValue);
pvkd->fHandled = 1;
}
else
CBaseToggle::KeyValue(pvkd);
}