diff --git a/dlls/CMakeLists.txt b/dlls/CMakeLists.txt index 1f2d271c..ecdd069c 100644 --- a/dlls/CMakeLists.txt +++ b/dlls/CMakeLists.txt @@ -66,6 +66,7 @@ set (SVDLL_SOURCES gearbox/m249.cpp gearbox/massn.cpp gearbox/nuclearbomb.cpp + gearbox/op4mortar.cpp gearbox/otis.cpp gearbox/penguin.cpp gearbox/pipewrench.cpp diff --git a/dlls/gearbox/op4mortar.cpp b/dlls/gearbox/op4mortar.cpp new file mode 100644 index 00000000..1c03f5ab --- /dev/null +++ b/dlls/gearbox/op4mortar.cpp @@ -0,0 +1,631 @@ +#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); +}