Wayback Machine
OCT MAY Jun
Previous capture 5 Next capture
2004 2006 2007
14 captures
5 Jun 01 - 5 May 06
sparklines
Close Help

# menu
 
http://www.planethalflife.com/hlprogramming > tutorials > database 

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