/*** * * Copyright (c) 1996-2002, 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 !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" #if !CLIENT_DLL LINK_ENTITY_TO_CLASS( laser_spot, CLaserSpot ) //========================================================= //========================================================= 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( &CLaserSpot::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" ); } LINK_ENTITY_TO_CLASS( rpg_rocket, CRpgRocket ) //========================================================= //========================================================= 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_hLauncher = pLauncher;// remember what RPG fired me. pLauncher->m_cActiveRockets++;// register this missile as active for the launcher pRocket->pev->owner = pOwner->edict(); return pRocket; } void CRpgRocket::Explode( TraceResult *pTrace, int bitsDamageType ) { if( CRpg *pLauncher = GetLauncher()) { // my launcher is still around, tell it I'm dead. pLauncher->m_cActiveRockets--; m_hLauncher = 0; } STOP_SOUND( edict(), CHAN_VOICE, "weapons/rocket1.wav" ); CGrenade::Explode( pTrace, bitsDamageType ); } CRpg *CRpgRocket::GetLauncher( void ) { return (CRpg*)( (CBaseEntity*)m_hLauncher ); } //========================================================= //========================================================= 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( &CRpgRocket::IgniteThink ); SetTouch( &CGrenade::ExplodeTouch ); pev->angles.x -= 30.0f; UTIL_MakeVectors( pev->angles ); pev->angles.x = -( pev->angles.x + 30.0f ); pev->velocity = gpGlobals->v_forward * 250.0f; pev->gravity = 0.5f; pev->nextthink = gpGlobals->time + 0.4f; pev->dmg = gSkillData.plrDmgRPG; } //========================================================= //========================================================= void CRpgRocket::RocketTouch( CBaseEntity *pOther ) { if( CRpg *pLauncher = GetLauncher()) { // my launcher is still around, tell it I'm dead. pLauncher->m_cActiveRockets--; m_hLauncher = 0; } 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.5f ); // 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) m_flIgniteTime = gpGlobals->time; // set to follow laser spot SetThink( &CRpgRocket::FollowThink ); pev->nextthink = gpGlobals->time + 0.1f; } 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.9f ) { 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.0f ) { pev->velocity = pev->velocity * 0.2f + vecTarget * ( flSpeed * 0.8f + 400.0f ); if( pev->waterlevel == 3 ) { // go slow underwater if( pev->velocity.Length() > 300.0f ) { pev->velocity = pev->velocity.Normalize() * 300.0f; } UTIL_BubbleTrail( pev->origin - pev->velocity * 0.1f, pev->origin, 4 ); } else { if( pev->velocity.Length() > 2000.0f ) { pev->velocity = pev->velocity.Normalize() * 2000.0f; } } } else { if( pev->effects & EF_LIGHT ) { pev->effects = 0; STOP_SOUND( ENT( pev ), CHAN_VOICE, "weapons/rocket1.wav" ); } pev->velocity = pev->velocity * 0.2f + vecTarget * flSpeed * 0.798f; if( pev->waterlevel == 0 && pev->velocity.Length() < 1500.0f ) Detonate(); } // ALERT( at_console, "%.0f\n", flSpeed ); if( CRpg *pLauncher = GetLauncher()) { if( ( pev->origin - pLauncher->pev->origin ).Length() > 8192 || gpGlobals->time - m_flIgniteTime > 6.0f ) { // my launcher is still around, tell it I'm dead. pLauncher->m_cActiveRockets--; m_hLauncher = 0; } } if( UTIL_PointContents( pev->origin ) == CONTENTS_SKY ) Detonate(); pev->nextthink = gpGlobals->time + 0.1f; } #endif #endif