news
current
submit
tutorials
database
-mp
-client
submit
search
forum
main
contact
email
|
Sensor Mine
Created by ->ViP<- Supertramp
HalfLife's Code is BLUE
->ViP<- Supertramp's Code is RED
->ViP<- Supertramp's Commentary is GREEN
If have added the sensor-mine-functionality in the tripmine, we''ll use
secondary-attack to switch between the laser and the sensor-mode of the
tripmine.
the sensor-mine will be invisible for 30 sekonds, so it''s even more
dangerous (you can still destroy it since it''s invsible, but you''ve to
know where it is *g*)
here is the code, feel free to use it but give credit!!
the only thing you have to do is to replace all the code in tripmine.cpp with the code following
/***
*
* 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 "effects.h"
#include "gamerules.h"
#define TRIPMINE_PRIMARY_VOLUME 450
enum tripmine_e {
TRIPMINE_IDLE1 = 0,
TRIPMINE_IDLE2,
TRIPMINE_ARM1,
TRIPMINE_ARM2,
TRIPMINE_FIDGET,
TRIPMINE_HOLSTER,
TRIPMINE_DRAW,
TRIPMINE_WORLD,
TRIPMINE_GROUND,
};
class CTripmineGrenade : public CGrenade
{
private:
void Spawn( void );
void Precache( void );
virtual int Save( CSave &save );
virtual int Restore( CRestore &restore );
static TYPEDESCRIPTION m_SaveData[];
int TakeDamage( entvars_t *pevInflictor, entvars_t *pevAttacker, float flDamage, int bitsDamageType );
void EXPORT WarningThink( void );
void EXPORT PowerupThink( void );
void EXPORT BeamBreakThink( void );
void EXPORT SensorFieldThink ( void );
void EXPORT SensorExplode( void );
void EXPORT DelayDeathThink( void );
void Killed( entvars_t *pevAttacker, int iGib );
void EXPORT SensorThink( void );
void PrepareSensor (void);
void MakeBeam( void );
void KillBeam( void );
float m_flPowerUp;
Vector m_vecDir;
Vector m_vecEnd;
float m_flBeamLength;
int m_TripmineMode;
float m_InvisibleTime;
EHANDLE m_hOwner;
CBeam *m_pBeam;
Vector m_posOwner;
Vector m_angleOwner;
edict_t *m_pRealOwner;// tracelines don''t hit PEV->OWNER, which
means a player couldn''t detonate his own trip mine, so we store the
owner here.
public:
void SetTripmineMode( int mode ) { m_TripmineMode = mode;}
};
LINK_ENTITY_TO_CLASS( monster_tripmine, CTripmineGrenade );
TYPEDESCRIPTION CTripmineGrenade::m_SaveData[] =
{
DEFINE_FIELD( CTripmineGrenade, m_flPowerUp, FIELD_TIME ),
DEFINE_FIELD( CTripmineGrenade, m_vecDir, FIELD_VECTOR ),
DEFINE_FIELD( CTripmineGrenade, m_vecEnd, FIELD_POSITION_VECTOR ),
DEFINE_FIELD( CTripmineGrenade, m_flBeamLength, FIELD_FLOAT ),
DEFINE_FIELD( CTripmineGrenade, m_hOwner, FIELD_EHANDLE ),
DEFINE_FIELD( CTripmineGrenade, m_pBeam, FIELD_CLASSPTR ),
DEFINE_FIELD( CTripmineGrenade, m_posOwner, FIELD_POSITION_VECTOR ),
DEFINE_FIELD( CTripmineGrenade, m_angleOwner, FIELD_VECTOR ),
DEFINE_FIELD( CTripmineGrenade, m_pRealOwner, FIELD_EDICT ),
};
IMPLEMENT_SAVERESTORE(CTripmineGrenade,CGrenade);
void CTripmineGrenade :: Spawn( void )
{
Precache( );
// motor
pev->movetype = MOVETYPE_FLY;
pev->solid = SOLID_NOT;
SET_MODEL(ENT(pev), "models/v_tripmine.mdl");
pev->frame = 0;
pev->body = 3;
pev->sequence = TRIPMINE_WORLD;
ResetSequenceInfo( );
pev->framerate = 0;
UTIL_SetSize(pev, Vector( -8, -8, -8), Vector(8, 8, 8));
UTIL_SetOrigin( pev, pev->origin );
if (pev->spawnflags & 1)
{
// power up quickly
m_flPowerUp = gpGlobals->time + 1.0;
}
else
{
// power up in 2.5 seconds
m_flPowerUp = gpGlobals->time + 2.5;
}
SetThink( PowerupThink );
pev->nextthink = gpGlobals->time + 0.2;
pev->takedamage = DAMAGE_YES;
pev->dmg = gSkillData.plrDmgTripmine;
pev->health = 1; // don''t let die normally
if (pev->owner != NULL)
{
// play deploy sound
EMIT_SOUND( ENT(pev), CHAN_VOICE, "weapons/mine_deploy.wav", 1.0, ATTN_NORM );
EMIT_SOUND( ENT(pev), CHAN_BODY, "weapons/mine_charge.wav", 0.2, ATTN_NORM ); // chargeup
m_pRealOwner = pev->owner;// see CTripmineGrenade for why.
}
UTIL_MakeAimVectors( pev->angles );
m_vecDir = gpGlobals->v_forward;
m_vecEnd = pev->origin + m_vecDir * 2048;
}
void CTripmineGrenade :: Precache( void )
{
PRECACHE_MODEL("models/v_tripmine.mdl");
PRECACHE_SOUND("weapons/mine_deploy.wav");
PRECACHE_SOUND("weapons/mine_activate.wav");
PRECACHE_SOUND("weapons/mine_charge.wav");
}
void CTripmineGrenade :: WarningThink( void )
{
// play warning sound
// EMIT_SOUND( ENT(pev), CHAN_VOICE, "buttons/Blip2.wav", 1.0, ATTN_NORM );
// set to power up
SetThink( PowerupThink );
pev->nextthink = gpGlobals->time + 1.0;
}
void CTripmineGrenade :: PowerupThink( void )
{
TraceResult tr;
if (m_hOwner == NULL)
{
// find an owner
edict_t *oldowner = pev->owner;
pev->owner = NULL;
UTIL_TraceLine( pev->origin + m_vecDir * 8, pev->origin - m_vecDir * 32, dont_ignore_monsters, ENT( pev ), &tr );
if (tr.fStartSolid || (oldowner && tr.pHit == oldowner))
{
pev->owner = oldowner;
m_flPowerUp += 0.1;
pev->nextthink = gpGlobals->time + 0.1;
return;
}
if (tr.flFraction < 1.0)
{
pev->owner = tr.pHit;
m_hOwner = CBaseEntity::Instance( pev->owner );
m_posOwner = m_hOwner->pev->origin;
m_angleOwner = m_hOwner->pev->angles;
}
else
{
STOP_SOUND( ENT(pev), CHAN_VOICE, "weapons/mine_deploy.wav" );
STOP_SOUND( ENT(pev), CHAN_BODY, "weapons/mine_charge.wav" );
SetThink( SUB_Remove );
pev->nextthink = gpGlobals->time + 0.1;
ALERT( at_console, "WARNING:Tripmine at %.0f, %.0f, %.0f removed\n", pev->origin.x, pev->origin.y, pev->origin.z );
KillBeam();
return;
}
}
else if (m_posOwner != m_hOwner->pev->origin || m_angleOwner != m_hOwner->pev->angles)
{
// disable
STOP_SOUND( ENT(pev), CHAN_VOICE, "weapons/mine_deploy.wav" );
STOP_SOUND( ENT(pev), CHAN_BODY, "weapons/mine_charge.wav" );
CBaseEntity *pMine = Create( "weapon_tripmine", pev->origin + m_vecDir * 24, pev->angles );
pMine->pev->spawnflags |= SF_NORESPAWN;
SetThink( SUB_Remove );
KillBeam();
pev->nextthink = gpGlobals->time + 0.1;
return;
}
// ALERT( at_console, "%d %.0f %.0f %0.f\n", pev->owner,
m_pOwner->pev->origin.x, m_pOwner->pev->origin.y,
m_pOwner->pev->origin.z );
if (gpGlobals->time > m_flPowerUp)
{
// make solid
pev->solid = SOLID_BBOX;
UTIL_SetOrigin( pev, pev->origin );
switch ( m_TripmineMode )
{
case 0:
{
MakeBeam( );
break;
}
case 1:
{
PrepareSensor();
break;
}
}
// play enabled sound
EMIT_SOUND_DYN( ENT(pev), CHAN_VOICE, "weapons/mine_activate.wav", 0.5, ATTN_NORM, 1.0, 75 );
}
pev->nextthink = gpGlobals->time + 0.1;
}
void CTripmineGrenade :: KillBeam( void )
{
if ( m_pBeam )
{
UTIL_Remove( m_pBeam );
m_pBeam = NULL;
}
}
void CTripmineGrenade :: MakeBeam( void )
{
TraceResult tr;
// ALERT( at_console, "serverflags %f\n", gpGlobals->serverflags );
UTIL_TraceLine( pev->origin, m_vecEnd, dont_ignore_monsters, ENT( pev ), &tr );
m_flBeamLength = tr.flFraction;
// set to follow laser spot
SetThink( BeamBreakThink );
pev->nextthink = gpGlobals->time + 0.1;
Vector vecTmpEnd = pev->origin + m_vecDir * 2048 * m_flBeamLength;
m_pBeam = CBeam::BeamCreate( g_pModelNameLaser, 10 );
m_pBeam->PointEntInit( vecTmpEnd, entindex() );
m_pBeam->SetColor( 0, 64, 64 );
m_pBeam->SetScrollRate( 255 );
m_pBeam->SetBrightness( 64 );
}
void CTripmineGrenade :: PrepareSensor()
{
//for about 30 seconds the mine should be invisible -> more fun then, hehe
//disabled for test, you know...
pev->rendermode = kRenderTransTexture;
m_InvisibleTime = gpGlobals->time + 30;
SetThink ( SensorFieldThink );
pev->nextthink = gpGlobals->time + 0.1;
}
void CTripmineGrenade :: SensorFieldThink()
{
if (m_InvisibleTime < gpGlobals->time)
pev->rendermode = kRenderNormal;
BOOL bBlowup = 0;
CBaseEntity *pEntity = NULL;
while ((pEntity = UTIL_FindEntityInSphere( pEntity, pev->origin + Vector (0,0,8), 150)) != NULL)
{
if ( pEntity->IsPlayer() )
{
bBlowup = 1;
}
}
if ( bBlowup )
{
SetThink ( SensorExplode );
pev->nextthink = gpGlobals->time + 0.2; //dont explode immediately
}
else
{
SetThink ( SensorFieldThink );
pev->nextthink = gpGlobals->time + 0.1;
}
}
void CTripmineGrenade ::SensorExplode(void)
{
RadiusDamage( pev, VARS(m_pRealOwner), 120, CLASS_NONE, DMG_BLAST );
Vector vecAxis = pev->origin + Vector ( 450, 100, 450 );
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 ); // coord coord coord (center position)
WRITE_COORD ( vecAxis.x );
WRITE_COORD ( vecAxis.y );
WRITE_COORD ( vecAxis.z ); // coord coord coord (axis and radius)
WRITE_SHORT ( g_sModelIndexFireball ); // short (sprite index)
WRITE_BYTE ( 0 ); // byte (starting frame)
WRITE_BYTE ( 0 ); // byte (frame rate in 0.1''s)
WRITE_BYTE ( 6 ); // byte (life in 0.1''s) shouldn''t take long to expand..
WRITE_BYTE ( 20 ); // byte (line width in 0.1''s)
WRITE_BYTE ( 40 ); // byte (noise amplitude in 0.01''s)
WRITE_BYTE ( 0 );
WRITE_BYTE ( 255 );
WRITE_BYTE ( 150 ); // byte,byte,byte (color)
WRITE_BYTE ( 255 ); // byte (brightness)
WRITE_BYTE ( 0 );
MESSAGE_END();
UTIL_Remove ( this );
}
void CTripmineGrenade :: BeamBreakThink( void )
{
BOOL bBlowup = 0;
TraceResult tr;
// HACKHACK Set simple box using this really nice global!
gpGlobals->trace_flags = FTRACE_SIMPLEBOX;
UTIL_TraceLine( pev->origin, m_vecEnd, dont_ignore_monsters, ENT( pev ), &tr );
// ALERT( at_console, "%f : %f\n", tr.flFraction, m_flBeamLength );
// respawn detect.
if ( !m_pBeam )
{
MakeBeam( );
if ( tr.pHit )
m_hOwner = CBaseEntity::Instance( tr.pHit ); // reset owner too
}
if (fabs( m_flBeamLength - tr.flFraction ) > 0.001)
{
bBlowup = 1;
}
else
{
if (m_hOwner == NULL)
bBlowup = 1;
else if (m_posOwner != m_hOwner->pev->origin)
bBlowup = 1;
else if (m_angleOwner != m_hOwner->pev->angles)
bBlowup = 1;
}
if (bBlowup)
{
// a bit of a hack, but all CGrenade code passes pev->owner along to make sure the proper player gets credit for the kill
// so we have to restore pev->owner from pRealOwner, because an
entity''s tracelines don''t strike it''s pev->owner which meant
// that a player couldn''t trigger his own tripmine. Now that the mine is exploding, it''s safe the restore the owner so the
// CGrenade code knows who the explosive really belongs to.
pev->owner = m_pRealOwner;
pev->health = 0;
Killed( VARS( pev->owner ), GIB_NORMAL );
return;
}
pev->nextthink = gpGlobals->time + 0.1;
}
int CTripmineGrenade :: TakeDamage( entvars_t *pevInflictor, entvars_t *pevAttacker, float flDamage, int bitsDamageType )
{
if (gpGlobals->time < m_flPowerUp && flDamage < pev->health)
{
// disable
// Create( "weapon_tripmine", pev->origin + m_vecDir * 24, pev->angles );
SetThink( SUB_Remove );
pev->nextthink = gpGlobals->time + 0.1;
KillBeam();
return FALSE;
}
return CGrenade::TakeDamage( pevInflictor, pevAttacker, flDamage, bitsDamageType );
}
void CTripmineGrenade::Killed( entvars_t *pevAttacker, int iGib )
{
pev->takedamage = DAMAGE_NO;
if ( pevAttacker && ( pevAttacker->flags & FL_CLIENT ) )
{
// some client has destroyed this mine, he''ll get credit for any kills
pev->owner = ENT( pevAttacker );
}
switch ( m_TripmineMode )
{
case 0:
{
SetThink( DelayDeathThink );
break;
}
case 1:
{
SetThink( SensorExplode );
break;
};
}
pev->nextthink = gpGlobals->time + RANDOM_FLOAT( 0.1, 0.3 );
EMIT_SOUND( ENT(pev), CHAN_BODY, "common/null.wav", 0.5, ATTN_NORM ); // shut off chargeup
}
void CTripmineGrenade::DelayDeathThink( void )
{
KillBeam();
TraceResult tr;
UTIL_TraceLine ( pev->origin + m_vecDir * 8, pev->origin - m_vecDir * 64, dont_ignore_monsters, ENT(pev), & tr);
Explode( &tr, DMG_BLAST );
}
class CTripmine : public CBasePlayerWeapon
{
public:
void Spawn( void );
void Precache( void );
int iItemSlot( void ) { return 5; }
int GetItemInfo(ItemInfo *p);
void SetObjectCollisionBox( void )
{
//!!!BUGBUG - fix the model!
pev->absmin = pev->origin + Vector(-16, -16, -5);
pev->absmax = pev->origin + Vector(16, 16, 28);
}
void PrimaryAttack( void );
void SecondaryAttack( void );
BOOL Deploy( void );
void Holster( int skiplocal = 0 );
void WeaponIdle( void );
private:
int m_TripmineMode;
};
LINK_ENTITY_TO_CLASS( weapon_tripmine, CTripmine );
void CTripmine::Spawn( )
{
Precache( );
m_iId = WEAPON_TRIPMINE;
SET_MODEL(ENT(pev), "models/v_tripmine.mdl");
pev->frame = 0;
pev->body = 3;
pev->sequence = TRIPMINE_GROUND;
// ResetSequenceInfo( );
pev->framerate = 0;
FallInit();// get ready to fall down
m_iDefaultAmmo = TRIPMINE_DEFAULT_GIVE;
if ( !g_pGameRules->IsDeathmatch() )
{
UTIL_SetSize(pev, Vector(-16, -16, 0), Vector(16, 16, 28) );
}
}
void CTripmine::Precache( void )
{
PRECACHE_MODEL ("models/v_tripmine.mdl");
PRECACHE_MODEL ("models/p_tripmine.mdl");
UTIL_PrecacheOther( "monster_tripmine" );
}
int CTripmine::GetItemInfo(ItemInfo *p)
{
p->pszName = STRING(pev->classname);
p->pszAmmo1 = "Trip Mine";
p->iMaxAmmo1 = TRIPMINE_MAX_CARRY;
p->pszAmmo2 = NULL;
p->iMaxAmmo2 = -1;
p->iMaxClip = WEAPON_NOCLIP;
p->iSlot = 4;
p->iPosition = 2;
p->iId = m_iId = WEAPON_TRIPMINE;
p->iWeight = TRIPMINE_WEIGHT;
p->iFlags = ITEM_FLAG_LIMITINWORLD | ITEM_FLAG_EXHAUSTIBLE;
return 1;
}
BOOL CTripmine::Deploy( )
{
pev->body = 0;
return DefaultDeploy( "models/v_tripmine.mdl", "models/p_tripmine.mdl", TRIPMINE_DRAW, "trip" );
}
void CTripmine::Holster( int skiplocal /* = 0 */ )
{
m_pPlayer->m_flNextAttack = UTIL_WeaponTimeBase() + 0.5;
if (!m_pPlayer->m_rgAmmo[m_iPrimaryAmmoType])
{
// out of mines
m_pPlayer->pev->weapons &= ~(1<
SetThink( DestroyItem );
pev->nextthink = gpGlobals->time + 0.1;
}
SendWeaponAnim( TRIPMINE_HOLSTER );
EMIT_SOUND(ENT(m_pPlayer->pev), CHAN_WEAPON, "common/null.wav", 1.0, ATTN_NORM);
}
void CTripmine::PrimaryAttack( void )
{
if (m_pPlayer->m_rgAmmo[m_iPrimaryAmmoType] <= 0)
return;
UTIL_MakeVectors( m_pPlayer->pev->v_angle + m_pPlayer->pev->punchangle );
Vector vecSrc = m_pPlayer->GetGunPosition( );
Vector vecAiming = gpGlobals->v_forward;
TraceResult tr;
UTIL_TraceLine( vecSrc, vecSrc + vecAiming * 128, dont_ignore_monsters, ENT( m_pPlayer->pev ), &tr );
if (tr.flFraction < 1.0)
{
// ALERT( at_console, "hit %f\n", tr.flFraction );
CBaseEntity *pEntity = CBaseEntity::Instance( tr.pHit );
if (pEntity && !(pEntity->pev->flags & FL_CONVEYOR))
{
Vector angles = UTIL_VecToAngles( tr.vecPlaneNormal );
CBaseEntity *pEnt = CBaseEntity::Create( "monster_tripmine",
tr.vecEndPos + tr.vecPlaneNormal * 8, angles, m_pPlayer->edict() );
CTripmineGrenade *pMine = (CTripmineGrenade *)pEnt;
pMine->SetTripmineMode(m_TripmineMode);
m_pPlayer->m_rgAmmo[m_iPrimaryAmmoType]--;
// player "shoot" animation
m_pPlayer->SetAnimation( PLAYER_ATTACK1 );
if (m_pPlayer->m_rgAmmo[m_iPrimaryAmmoType] > 0)
{
SendWeaponAnim( TRIPMINE_DRAW );
}
else
{
// no more mines!
RetireWeapon();
return;
}
}
else
{
// ALERT( at_console, "no deploy\n" );
}
}
else
{
}
m_flNextPrimaryAttack = gpGlobals->time + 0.3;
m_flTimeWeaponIdle = gpGlobals->time + RANDOM_FLOAT ( 10, 15 );
}
void CTripmine::SecondaryAttack(void)
{
char text[1024];
switch ( m_TripmineMode )
{
case 0:
{
m_TripmineMode = 1;
sprintf ( text, "* Sensorfield");
break;
}
case 1:
{
m_TripmineMode = 0;
sprintf ( text, "* Sensorbeam");
break;
}
}
UTIL_SayText( text, m_pPlayer );
m_flNextSecondaryAttack = gpGlobals->time + 0.3;
}
void CTripmine::WeaponIdle( void )
{
if (m_flTimeWeaponIdle > gpGlobals->time)
return;
if (m_pPlayer->m_rgAmmo[m_iPrimaryAmmoType] > 0)
{
SendWeaponAnim( TRIPMINE_DRAW );
}
else
{
RetireWeapon();
return;
}
int iAnim;
float flRand = RANDOM_FLOAT(0, 1);
if (flRand <= 0.25)
{
iAnim = TRIPMINE_IDLE1;
m_flTimeWeaponIdle = gpGlobals->time + 90.0 / 30.0;
}
else if (flRand <= 0.75)
{
iAnim = TRIPMINE_IDLE2;
m_flTimeWeaponIdle = gpGlobals->time + 60.0 / 30.0;
}
else
{
iAnim = TRIPMINE_FIDGET;
m_flTimeWeaponIdle = gpGlobals->time + 100.0 / 30.0;
}
SendWeaponAnim( iAnim );
}
okay, in CTripmine::SecondaryAttack we switch between the normal tripmine and the new sensor-mine
in CTripmine::PrimaryAttack we call pMine->SetTripmineMode to say the just spwaned mine how it should behave
In the CTrpimineGrenade::PowerupThink we check the tripminemode and
depending on it''s value we call different functions to initialize the
mine. the normal function creates the beam, our function makes it
invisible
in CTripmineGrenade::SensorFieldThink we check if something has entered
the area 150 units close to the mine. if this "something" is a player,
we tell the mine to explode
hm, and in CTripmineGrenade::SensorExplode we tell the mine to do some
radiusdamage (it''s damage is 120, but cause you can''t reach the center
of the mine-area you''ll only lose about 70 to 80 hps).
the other stuff from MESSAGE_BEGIN to MESSAGE_END creates a nice effect but feel free to make something else there...
that was it, if there are any questions, just email me
|