// 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; }