sin-sdk/spidermine.cpp
1999-11-02 00:00:00 +00:00

832 lines
17 KiB
C++

//-----------------------------------------------------------------------------
//
// $Logfile:: /Quake 2 Engine/Sin/code/game/spidermine.cpp $
// $Revision:: 50 $
// $Author:: Markd $
// $Date:: 6/07/99 2:29p $
//
// Copyright (C) 1998 by Ritual Entertainment, Inc.
// All rights reserved.
//
// This source is may not be distributed and/or modified without
// expressly written permission by Ritual Entertainment, Inc.
//
// $Log:: /Quake 2 Engine/Sin/code/game/spidermine.cpp $
//
// 50 6/07/99 2:29p Markd
// fixed null pointer bug
//
// 49 11/18/98 1:27a Jimdose
// Fixed sliding and orientation issues with gravaxis
//
// 48 11/16/98 9:44p Jimdose
// fixed sticking on alternate gravaxis
//
// 47 11/16/98 3:12a Jimdose
// mine takes gravity of owner
//
// 46 11/15/98 10:31p Jimdose
// view of spidermine is now towards direction it's moving
//
// 45 11/13/98 8:00p Markd
// Fixed spider mine bug with owner being reset to world too soon
//
// 44 11/13/98 3:30p Markd
// put in more precaching on weapons
//
// 43 11/13/98 2:35a Aldie
// Lowered the rank of spidermines
//
// 42 11/11/98 5:12p Aldie
// Spidermine fix when player dies - don't drop it
//
// 41 11/09/98 2:52a Aldie
// increase health
//
// 40 11/09/98 12:12a Aldie
// Made it so you can reclaim your mines when you die, and mines are now shootable
//
// 39 10/26/98 2:50p Aldie
// Fixed a bug with checking of NULL owners
//
// 38 10/24/98 12:42a Markd
// changed origins to worldorigins where appropriate
//
// 37 10/22/98 7:57p Markd
// put in proper pre-caching in all the classes
//
// 36 10/22/98 4:33p Aldie
// Fix for spidermine and detonators
//
// 35 10/21/98 12:09a Aldie
// Removed friction
//
// 34 10/20/98 8:26p Markd
// Added Attacker to DamageSurface stuff
//
// 33 10/19/98 12:07a Jimdose
// made all code use fast checks for inheritance (no text lookups when possible)
// isSubclassOf no longer requires ::_classinfo()
//
// 32 10/16/98 10:22p Aldie
// Updated single player damage settings
//
// 31 10/16/98 2:30p Aldie
// Check for NULL detonator
//
// 30 10/10/98 9:14p Aldie
// Damage tweak and detonator tweak
//
// 29 10/10/98 7:14p Aldie
// Tweaked damage
//
// 28 10/08/98 12:03a Jimdose
// Got rid of unused variables speed and direction from Mine
//
// 27 10/05/98 10:20p Aldie
// Changed damage a bit
//
// 26 10/02/98 5:47p Aldie
// Add MOD to RadiusDamage
//
// 25 9/18/98 8:14p Markd
// rewrote surface system so that surfaces are now damaged by surface name instead
// of by surfinfo
//
// 24 9/13/98 8:37p Aldie
// Don't autoswitch to detonator
//
// 23 9/11/98 3:19p Aldie
// Make mines bounce of entities that take damage.
//
// 22 9/05/98 12:13p Aldie
// Made explosion driven by animation of the spidermine.
//
// 21 9/01/98 7:53p Aldie
// Removed sound error
//
// 20 9/01/98 7:46p Aldie
// Added area checking for spidermines
//
// 19 9/01/98 3:05p Markd
// Rewrote explosion code
//
// 18 8/29/98 9:46p Jimdose
// Added call info to G_Trace
//
// 17 8/27/98 2:31p Aldie
// Fix for dead owner
//
// 16 8/26/98 6:19p Aldie
// Update fov to 120 and remove the proximity stuff
//
// 15 8/25/98 7:39p Aldie
// Made spidermine immediately switch to detonator.
//
// 14 8/25/98 5:29p Aldie
// New version of the spidermine which uses cameras
//
// 13 8/22/98 9:36p Jimdose
// Added support for alternate gravity axis
//
// 12 8/18/98 11:08p Markd
// Added new Alias System
//
// 11 7/25/98 7:10p Markd
// Put in EV_Removes for demo
//
// 10 7/22/98 9:57p Markd
// Defined weapon type
//
// 9 6/27/98 7:58p Aldie
// Minor adjustments to stuff
//
// 8 6/19/98 9:30p Jimdose
// Moved gun orientation code to Weapon
//
// 7 6/10/98 2:10p Aldie
// Updated damage function.
//
// 6 6/08/98 9:04p Aldie
// Updated doneFiring event
//
// 5 6/08/98 8:43p Aldie
// Added ReadyToUse support
//
// 4 6/08/98 8:21p Aldie
// Updated spidermines with new anims and logic
//
// 3 6/08/98 11:34a Aldie
// New version of spidermines.
//
// 2 5/11/98 11:24a Markd
// First time
//
// 1 5/11/98 11:10a Markd
//
// 1 5/11/98 10:24a Markd
//
// 1 5/11/98 10:20a Markd
//
// 1 5/11/98 10:18a Markd
//
// 1 5/11/98 9:55a Markd
//
// DESCRIPTION:
// Spider Mine
//
#include "g_local.h"
#include "spidermine.h"
#include "explosion.h"
#include "player.h"
#include "surface.h"
CLASS_DECLARATION( Projectile, Mine, "Mine" );
Event EV_Mine_Explode( "mine_explode" );
Event EV_Mine_CheckForTargets( "mine_targets" );
Event EV_Mine_Run( "mine_run" );
ResponseDef Mine::Responses[] =
{
{ &EV_Touch, ( Response )Mine::SlideOrStick },
{ &EV_Killed, ( Response )Mine::Explode },
{ &EV_Mine_Explode, ( Response )Mine::Explode },
{ &EV_Trigger_Effect, ( Response )Mine::SlideOrStick },
{ &EV_Mine_CheckForTargets, ( Response )Mine::CheckForTargets },
{ &EV_Mine_Run, ( Response )Mine::Run },
{ NULL, NULL }
};
void Mine::Run
(
Event *ev
)
{
RandomAnimate( "run", NULL );
}
void Mine::CheckForTargets
(
Event *ev
)
{
Entity *ent;
Event *event;
trace_t trace;
// Playtest
return;
if (detonate)
return;
ent = findradius( NULL, worldorigin, 150 );
while( ent )
{
if ( ( ent != this ) &&
( !ent->deadflag ) &&
( ent->entnum != owner ) &&
( ent->takedamage ) &&
( !(ent->flags & FL_NOTARGET) ) &&
( strcmp(ent->getClassname(),"Mine") ) )
{
detonate = true;
trace = G_Trace( worldorigin, vec_zero, vec_zero, ent->worldorigin, ent, MASK_PROJECTILE, "Mine::CheckForTargets" );
if (trace.fraction != 1.0f)
detonate = false;
else
break;
}
ent = findradius( ent, worldorigin, 150 );
}
if (detonate)
{
RandomGlobalSound("spider_arm",1);
event = new Event(EV_Mine_Explode);
PostEvent(event,0.5f);
}
else
{
event = new Event(EV_Mine_CheckForTargets);
PostEvent(event,0.5f);
}
}
void Mine::SlideOrStick
(
Event *ev
)
{
Entity *other;
Event *event;
Vector norm;
Vector addSpeed;
if (detonate)
return;
other = ev->GetEntity( 1 );
assert( other );
if ( other->takedamage )
{
setMoveType(MOVETYPE_BOUNCE);
return;
}
// Check to see if we hit the ground, if so then slide along,
// otherwise stick to the wall.
if (( (level.impact_trace.plane.normal[ gravity_axis[ gravaxis ].z ] * gravity_axis[ gravaxis ].sign ) > 0.7 ) &&
!sticky)
{
setMoveType(MOVETYPE_SLIDE);
event = new Event(EV_Mine_CheckForTargets);
PostEvent(event,2.5);
}
else
{
// So that we can shoot our own mines
edict->owner = world->edict;
CancelEventsOfType(EV_Mine_Run);
setMoveType(MOVETYPE_NONE);
RandomAnimate( "stick", NULL );
norm = level.impact_trace.plane.normal;
angles = norm.toAngles();
angles.x = -angles.x;
setAngles(angles);
velocity = "0 0 0";
// Check to see if we are on the floor and adjust origin
if (!sticky)
{
CheckGround();
if (groundentity)
setOrigin(origin + "0 0 12" );
}
setOrigin(origin);
event = new Event(EV_Mine_CheckForTargets);
PostEvent(event,0.7f);
}
}
void Mine::Explode
(
Event *ev
)
{
int damg;
Vector v;
Vector norm;
Player *client;
Camera *cam;
Entity *owner;
detonate = true;
takedamage = DAMAGE_NO;
stopsound( CHAN_VOICE );
setSolidType( SOLID_NOT );
if ( HitSky() )
{
PostEvent( EV_Remove, 0 );
return;
}
owner = G_GetEntity( this->owner );
if ( !owner )
owner = world;
damg = 100;
if ( !deathmatch->value && owner->isClient() )
damg *= 1.5;
surfaceManager.DamageSurface (&level.impact_trace, damg, owner );
v = velocity;
v.normalize();
v = worldorigin - v * 24;
RadiusDamage( this, owner, damg, this, MOD_SPIDERSPLASH );
if ( owner->isSubclassOf( Player ) )
{
client = ( Player * )owner;
cam = client->CurrentCamera();
// If we are in the camera, get out of it
if ( cam == ( Camera * )this )
client->SetCamera( NULL );
}
// Remove the mine from the detonator
if ( detonator )
detonator->RemoveMine( this );
RandomAnimate( "explode", NULL );
PostEvent( EV_Remove, 0.2 );
}
void Mine::SetDetonator
(
Detonator *det
)
{
detonator = det;
}
qboolean Mine::IsOwner
(
Sentient *sent
)
{
return ( sent == G_GetEntity( this->owner ) );
}
void Mine::Setup
(
Entity *owner,
Vector pos,
Vector dir
)
{
Event *ev;
Vector t[ 3 ];
Vector forward;
Vector right;
Vector up;
this->owner = owner->entnum;
edict->owner = owner->edict;
setModel( "spidermine.def" );
showModel();
setMoveType( MOVETYPE_SLIDE );
setSolidType( SOLID_BBOX );
edict->clipmask = MASK_PROJECTILE;
RandomAnimate( "open", EV_Mine_Run );
SetGravityAxis( owner->gravaxis );
velocity = dir * 600;
sticky = false;
const gravityaxis_t &grav = gravity_axis[ gravaxis ];
setAngles( Vector( 0, dir.toYaw(), 0 ) );
angles.AngleVectors( &t[0], &t[1], &t[2] );
forward[ grav.x ] = t[ 0 ][ 0 ];
forward[ grav.y ] = t[ 0 ][ 1 ] * grav.sign;
forward[ grav.z ] = t[ 0 ][ 2 ] * grav.sign;
right[ grav.x ] = t[ 1 ][ 0 ];
right[ grav.y ] = t[ 1 ][ 1 ] * grav.sign;
right[ grav.z ] = t[ 1 ][ 2 ] * grav.sign;
up[ grav.x ] = t[ 2 ][ 0 ];
up[ grav.y ] = t[ 2 ][ 1 ] * grav.sign;
up[ grav.z ] = t[ 2 ][ 2 ] * grav.sign;
VectorsToEulerAngles( forward.vec3(), right.vec3(), up.vec3(), angles.vec3() );
setAngles( angles );
ev = new Event( EV_Mine_Explode );
ev->AddEntity( world );
PostEvent( ev, 180 );
takedamage = DAMAGE_YES;
health = 150;
edict->svflags |= ( SVF_SHOOTABLE );
setSize("-4 -4 -4", "4 4 4");
setOrigin( pos );
worldorigin.copyTo(edict->s.old_origin);
detonate = false;
fov = 120;
if ( owner->isClient() && owner->client->ps.pmove.pm_flags & PMF_DUCKED )
{
setMoveType( MOVETYPE_TOSS );
sticky = true;
}
}
CLASS_DECLARATION( Weapon, SpiderMine, NULL);
ResponseDef SpiderMine::Responses[] =
{
{ &EV_Weapon_Shoot, ( Response )SpiderMine::Shoot },
{ &EV_Weapon_DoneFiring, ( Response )SpiderMine::DoneFiring },
{ &EV_Weapon_SecondaryUse, ( Response )SpiderMine::ChangeToDetonator },
{ NULL, NULL }
};
SpiderMine::SpiderMine
(
)
{
#ifdef SIN_DEMO
PostEvent( EV_Remove, 0 );
return;
#endif
SetModels( "mines.def", "view_mines.def" );
SetAmmo( "SpiderMines", 1, 0 );
SetRank( 60, 5 );
SetType( WEAPON_1HANDED );
modelIndex( "spidermine.def" );
modelIndex( "mine.def" );
currentMine = 0;
}
qboolean SpiderMine::IsDroppable
(
void
)
{
return false;
}
void SpiderMine::SetOwner
(
Sentient *sent
)
{
Detonator *detonator;
int num;
assert( sent );
if ( !sent )
{
// return to avoid any buggy behaviour
return;
}
Item::SetOwner( sent );
setOrigin( vec_zero );
setAngles( vec_zero );
if ( !viewmodel.length() )
{
error( "setOwner", "Weapon without viewmodel set" );
}
setModel( viewmodel );
// Give the owner a detonator and setup the link
detonator = ( Detonator * )sent->FindItem( "Detonator" );
if ( !detonator )
{
detonator = ( Detonator * )sent->giveWeapon( "Detonator" );
}
SetDetonator( detonator );
// Check the world for any spidermines in existence and
// if the player owns them, add them to the minelist.
num = 0;
while( ( num = G_FindClass( num, "Mine" ) ) != NULL )
{
Mine *mine = ( Mine * )G_GetEntity( num );
if ( mine->IsOwner( sent ) )
{
detonator->AddMine( mine );
mine->SetDetonator( detonator );
}
}
}
void SpiderMine::DoneFiring
(
Event *ev
)
{
assert( owner );
weaponstate = WEAPON_HOLSTERED;
DetachGun();
StopAnimating();
if ( owner )
{
owner->SetCurrentWeapon( detonator );
}
}
void SpiderMine::ChangeToDetonator
(
Event *ev
)
{
if ( owner && detonator->mineList.NumObjects() )
owner->ChangeWeapon( detonator );
}
qboolean SpiderMine::ReadyToUse
(
void
)
{
Event *ev;
if ( HasAmmo() )
{
return true;
}
else
{
if ( owner->isClient() )
{
// Check to see if detonator has mines active
if ( detonator && detonator->mineList.NumObjects() )
{
ev = new Event( "use" );
ev->AddString( "Detonator" );
owner->PostEvent( ev, 0 );
}
}
return false;
}
}
void SpiderMine::SetDetonator
(
Detonator *det
)
{
detonator = det;
}
void SpiderMine::Shoot
(
Event *ev
)
{
Vector pos;
Vector dir;
Mine *mine;
Event *event;
assert( owner );
if ( !owner )
{
return;
}
GetMuzzlePosition( &pos, &dir );
if ( currentMine )
{
event = new Event(EV_Mine_Explode);
currentMine->ProcessEvent( event );
}
else
{
mine = new Mine;
mine->Setup( owner, pos, dir );
mine->SetDetonator( detonator );
detonator->AddMine( mine );
}
NextAttack( 1.8f );
}
CLASS_DECLARATION( Weapon, Detonator, NULL);
ResponseDef Detonator::Responses[] =
{
{ &EV_Weapon_Shoot, ( Response )Detonator::Shoot },
{ &EV_Weapon_DoneFiring, ( Response )Detonator::DoneFiring },
{ &EV_Weapon_SecondaryUse, ( Response )Detonator::CycleCamera },
{ NULL, NULL }
};
Detonator::Detonator
(
)
{
#ifdef SIN_DEMO
PostEvent( EV_Remove, 0 );
return;
#endif
SetModels( NULL, "view_detonator.def" );
SetType( WEAPON_1HANDED );
currentMine = 0;
}
void Detonator::RemoveMine
(
Mine *mine
)
{
mineList.RemoveObject( MinePtr( mine ) );
}
void Detonator::AddMine
(
Mine *mine
)
{
mineList.AddObject( MinePtr( mine ) );
}
void Detonator::CycleCamera
(
Event *ev
)
{
Player *client;
int numMines;
Mine *mine;
assert( owner );
if ( !owner )
{
return;
}
numMines = mineList.NumObjects();
if ( !numMines )
return;
currentMine++;
mine = mineList.ObjectAt( ( currentMine % numMines ) + 1 );
if ( !mine )
{
mineList.RemoveObjectAt( ( currentMine % numMines ) + 1 );
return;
}
client = ( Player * )( Entity * ) owner;
if ( client->edict->areanum == mine->edict->areanum )
client->SetCamera( mine );
else
{
gi.cprintf(client->edict, PRINT_HIGH, "Spidermine %d out of range\n", ( currentMine % numMines ) + 1 );
}
}
void Detonator::DoneFiring
(
Event *ev
)
{
Weapon *weapon;
weaponstate = WEAPON_READY;
if ( owner )
{
owner->ProcessEvent( EV_Sentient_WeaponDoneFiring );
}
// Change back to spidermines if there is ammo, otherwise change weapons.
weapon = ( Weapon * )owner->FindItem( "SpiderMine" );
if ( !weapon || !weapon->HasAmmo() )
{
weapon = owner->BestWeapon();
}
owner->ChangeWeapon( weapon );
}
void Detonator::Shoot
(
Event *ev
)
{
int num,i;
Player *player;
Entity *cameraMine;
Event *event;
// If the owner is in a camera, the only detonate that mine,
// otherwise detonate them all
NextAttack( 0.1 );
if ( owner->isClient() )
{
player = ( Player * )( Entity * )owner;
cameraMine = player->CurrentCamera();
if ( cameraMine && cameraMine->isSubclassOf( Mine ) )
{
event = new Event(EV_Mine_Explode);
cameraMine->PostEvent( event, 0 );
return;
}
}
// Go through all the mines and detonate them
num = mineList.NumObjects();
for( i = num; i >= 1; i-- )
{
Event *explodeEvent;
Mine *mine;
mine = mineList.ObjectAt( i );
explodeEvent = new Event(EV_Mine_Explode);
mine->PostEvent( explodeEvent, 0 );
}
}
qboolean Detonator::IsDroppable
(
void
)
{
return false;
}
qboolean Detonator::AutoChange
(
void
)
{
return false;
}