sin-2015/spidermine.cpp
1999-04-22 00:00:00 +00:00

663 lines
13 KiB
C++

// 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.
//
// 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;
}
//### give it a heat signature
edict->s.effects |= EF_WARM;
//###
}
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 );
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;
}