ef2-sdk/dlls/game/equipment.cpp

659 lines
13 KiB
C++
Raw Normal View History

2003-11-05 00:00:00 +00:00
//-----------------------------------------------------------------------------
//
// $Logfile:: /EF2/Code/DLLs/game/equipment.cpp $
// $Revision:: 53 $
// $Author:: Singlis $
// $Date:: 9/26/03 2:36p $
//
// Copyright (C) 2002 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:
#include "_pch_cpp.h"
#include "equipment.h"
#include "player.h"
#include "weaputils.h"
#define DETAILED_SCAN_TIME 1.5
#define MAX_TARGET_DISTANCE 600.0f
#define MAX_TARGET_DISTANCE_SQUARED MAX_TARGET_DISTANCE * MAX_TARGET_DISTANCE
Event EV_AirStrike
(
"airstrike",
EV_CODEONLY,
NULL,
NULL,
"call airstrike from equipment"
);
Event EV_ScanStart
(
"scanstart",
EV_TIKIONLY,
NULL,
NULL,
"The start of the scan"
);
Event EV_ScanEnd
(
"scanend",
EV_TIKIONLY,
NULL,
NULL,
"The end of the scan"
);
Event EV_Scanner
(
"hasscanner",
EV_TIKIONLY,
NULL,
NULL,
"The equipment has a scanner"
);
Event EV_Radar
(
"hasradar",
EV_TIKIONLY,
NULL,
NULL,
"The equipment has a radar"
);
Event EV_Equipment_HasModes
(
"hasmodes",
EV_TIKIONLY,
"sSSSSSSSSS",
"mode1 mode2 mode3 mode4 mode5 mode6 mode7 mode8 mode9 mode10",
"Specify the modes this equipment has."
);
Event EV_Equipment_ChangeMode
(
"changemode",
EV_TIKIONLY,
NULL,
NULL,
"Change to the next mode."
);
Event EV_Scan
(
"scan",
EV_TIKIONLY,
NULL,
NULL,
"Scan"
);
Event EV_Equipment_SetTypeName
(
"typeName",
EV_TIKIONLY,
"s",
"typeName",
"Sets the type name of this equipment"
);
CLASS_DECLARATION( Weapon, Equipment, NULL)
{
{ &EV_AirStrike, &Equipment::airStrike },
{ &EV_ScanStart, &Equipment::scanStart },
{ &EV_ScanEnd, &Equipment::scanEnd },
{ &EV_Scanner, &Equipment::setScanner },
{ &EV_Radar, &Equipment::setRadar },
{ &EV_Equipment_HasModes, &Equipment::hasModes },
{ &EV_Equipment_ChangeMode, &Equipment::changeMode },
{ &EV_Equipment_SetTypeName, &Equipment::setTypeName },
{ &EV_Scan, &Equipment::scan },
{ NULL, NULL }
};
Equipment::Equipment()
{
init();
}
Equipment::Equipment( const char* file ) :
Weapon(file)
{
init();
}
void Equipment::init( void )
{
scanning = false;
scanner = false;
radar = false;
_active = false;
scanTime = 0;
_nextUseTime = 0.0f;
_scannedEntity = NULL;
_currentMode = 1;
_lastMode = 1;
_scanEndFrame = -1;
turnThinkOn();
}
Equipment::~Equipment()
{
_modes.FreeObjectList();
}
void Equipment::Think()
{
if ( _modes.NumObjects() > 0 )
{
str *modeName;
// Make sure this mode is still available
modeName = &_modes.ObjectAt( _currentMode );
if ( _currentMode != 1 && world->isAnyViewModeAvailable() && !world->isViewModeAvailable( modeName->c_str() ) )
{
Event *newEvent;
newEvent = new Event( EV_Equipment_ChangeMode );
newEvent->AddInteger( 1 );
ProcessEvent( newEvent );
}
}
Player* player = 0;
if( owner && owner->isSubclassOf(Player) )
{
player= (Player*)(Sentient*)owner;
}
if ( player && _active == qtrue)
{
player->client->ps.pm_flags &= ~( PMF_RADAR_MODE | PMF_SCANNER );
if( hasRadar() )
{
player->client->ps.pm_flags |= ( PMF_RADAR_MODE );
}
if( hasScanner() )
{
player->client->ps.pm_flags |= PMF_SCANNER;
}
}
}
//----------------------------------------------------------------
// Name: airStrike
// Class: Equipment
//
// Description: does a few traces to find a sane location, places a model
// against the edge of the skybox, and starts it up to do an air strike
//
// Parameters: none
//
// Returns: none
//----------------------------------------------------------------
void Equipment::airStrike(Event *ev)
{
str modeName;
Player* player;
str airStrikeSound;
// Make sure the scanning stuff is ok
if ( !scanner || !scanning )
return;
// Make sure we are still in the correct mode
if ( ( _currentMode > _modes.NumObjects() ) || ( _currentMode < 1 ) )
return;
modeName = _modes.ObjectAt( _currentMode );
if ( stricmp( modeName, "torpedostrike") != 0 )
return;
// Make sure our owner is a player
assert( owner );
if ( !owner || !owner->isSubclassOf( Player ) )
return;
player = (Player*)(Sentient*)owner;
// Try to do the torpedo strike
trace_t trace;
Vector start;
Vector forward,right;
Vector end;
int i;
Vector angles;
Vector dir;
bool hitSky;
Vector mins;
Vector maxs;
float bestFraction = 0.0f;
Vector skyPosition;
Vector bestSkyPosition;
player->GetViewTrace( trace, MASK_PROJECTILE, 5000.0f );
start = trace.endpos;
hitSky = false;
mins = Vector( -5.0f, -5.0f, -5.0f );
maxs = Vector( 5.0f, 5.0f, 5.0f );
for ( i = 0 ; i <= 360 ; i += 45 )
{
end = start + Vector( 0.0f, 0.0f, 10000.0f );
if ( i != 360 )
{
angles = vec_zero;
angles[ YAW ] = i;
angles.AngleVectors( &dir );
end += dir * 7500.0f;
}
// Try to trace for sky (that means we're outside and in range)
trace = G_Trace( start, vec_zero, vec_zero, end, player, MASK_PROJECTILE, false, "airStrike::skyspot1" );
/* gentity_t *loopent = NULL;
// if we hit a bmodel, move to the top of its box and try again
while ( (trace.ent) && (trace.ent->bmodel) && (loopent != trace.ent) )
{
loopent = trace.ent;
start[2] = loopent->absmax[2] + 5.0f;
trace = G_Trace( start, vec_zero,vec_zero, end, player, MASK_PROJECTILE, false, "airStrike::skyspot1" );
} */
if ( trace.surfaceFlags & SURF_SKY )
{
skyPosition = trace.endpos;
trace = G_Trace( skyPosition, mins, maxs, start, player, MASK_PROJECTILE, false, "airStrike::skyspot2" );
if ( ( bestFraction == 0.0f ) || ( trace.fraction > bestFraction ) )
{
bestFraction = trace.fraction;
bestSkyPosition = skyPosition;
}
hitSky = true;
//break;
}
}
// Show effects and play sound based on whether or not we hit
if ( hitSky )
{
right = bestSkyPosition;
forward = right - start;
if (forward.normalize() > 4000)
right = start + (forward * 4000); // abusing right for final start point
else
right = trace.endpos;
Projectile *airstrike;
airstrike = new Projectile; //this needs to be projectile so triggers respond properly (they won't respond to Entity)
airstrike->setModel("models/weapons/projectile_airstrike.tik");
airstrike->setOrigin(right);
forward *= -1.0f; // airstrike model tag is backward, easier to just reverse the direction here
airstrike->setAngles(forward.toAngles());
airstrike->PostEvent(EV_Remove,30);
airStrikeSound = GetRandomAlias( "snd_strikeincoming" ); // play this sound at beginning of airstrike
_nextUseTime = level.time + 30.0f;
}
else
{
airStrikeSound = GetRandomAlias( "snd_strikeblocked" ); // play this sound if we can't see sky
}
Sound( airStrikeSound );
}
void Equipment::scanStart(Event* ev)
{
if(scanner == true)
scanning = qtrue;
//if we end scan and start scan on the same frame,
//then ignore the end scan
if(_scanEndFrame == level.framenum)
{
PostEvent( EV_Scan, 0.05f );
return;
}
if ( shootingSkin )
ChangeSkin( shootingSkin, true );
PostEvent( EV_Scan, 0.25f );
if ( level.time > _nextUseTime )
{
CancelEventsOfType( EV_AirStrike );
PostEvent(EV_AirStrike,5.0f);
}
}
void Equipment::scanEnd(Event* ev)
{
if(scanner == true)
scanning = false;
_scanEndFrame = level.framenum;
if ( shootingSkin )
ChangeSkin( shootingSkin, false );
CancelEventsOfType( EV_Scan );
}
void Equipment::scan( Event *ev )
{
if ( owner && owner->isSubclassOf( Player ) )
{
Event *newEvent;
Player *player = (Player *)(Entity *)owner;
newEvent = new Event( EV_Player_DoUse );
newEvent->AddEntity( this );
player->ProcessEvent( newEvent );
}
CancelEventsOfType( EV_Scan );
PostEvent( EV_Scan, 0.05f );
}
void Equipment::setScanner(Event* ev)
{
if(ev == 0)
return;
scanner = true;
}
void Equipment::setRadar(Event* ev)
{
if(ev == 0)
return;
radar = true;
}
void Equipment::PutAway(void)
{
Weapon::PutAway();
}
void Equipment::ProcessTargetedEntity(EntityPtr entity)
{
if(_scannedEntity != entity && entity != 0)
{
Player* player = (Player*)(Sentient*)owner;
float distanceSquared = DistanceSquared(entity->origin, player->client->ps.origin);
if(distanceSquared > MAX_TARGET_DISTANCE_SQUARED)
scanTime = (int) level.time;
}
if(_scannedEntity != entity)
scanTime = (int) level.time;
_scannedEntity = entity;
if(_scannedEntity == 0)
return;
Weapon::ProcessTargetedEntity(entity);
if(isScanning())
{
entity->edict->s.eFlags &= ~EF_DISPLAY_DESC1; //turn off description 1 if displaying description 2.
entity->edict->s.eFlags |= EF_DISPLAY_DESC2;
long timeElapsed = (long)(level.time - scanTime);
if(timeElapsed >= DETAILED_SCAN_TIME)
entity->edict->s.eFlags |= EF_DISPLAY_DESC3;
}
else
{
scanTime = (int)level.time;
entity->edict->s.eFlags &= ~EF_DISPLAY_DESC2;
entity->edict->s.eFlags &= ~EF_DISPLAY_DESC3;
}
}
void Equipment::AttachToOwner(weaponhand_t hand)
{
_active = true;
Weapon::AttachToOwner(hand);
if ( _lastMode > 1 )
{
changeMode( _lastMode );
}
}
void Equipment::hasModes( Event *ev )
{
int i;
str newMode;
for ( i = 1 ; i <= ev->NumArgs() ; i++ )
{
newMode = ev->GetString( i );
_modes.AddObject( newMode );
}
}
void Equipment::changeMode( Event *ev )
{
int newMode;
if ( ev->NumArgs() > 0 )
{
newMode = ev->GetInteger( 1 );
}
else
{
newMode = _currentMode + 1;
}
changeMode( newMode );
}
void Equipment::updateMode( void )
{
changeMode( _currentMode );
}
void Equipment::changeMode( int newMode )
{
int numModes;
Event *newEvent;
str *modeName;
numModes = _modes.NumObjects();
if ( !numModes )
return;
// Set the desired mode
_currentMode = newMode;
if ( _currentMode > numModes )
_currentMode = 1;
// Continue until we have a valid mode set
while( 1 )
{
if (_currentMode == 1) // if mode is unset, query player to see if we want nightvision instead (divorced from equipment)
{
assert(owner);
Player* player;
if ( owner->isSubclassOf(Player))
player = (Player*)(Sentient*)owner;
else
player = NULL;
if ( (player) && (player->client->ps.pm_flags & PMF_NIGHTVISION) )
{
newEvent = new Event( EV_Sentient_SetViewMode );
newEvent->AddString( "nightvision" );
owner->ProcessEvent( newEvent );
return;
}
}
modeName = &_modes.ObjectAt( _currentMode );
// Make sure this mode is available
if ( _currentMode != 1 && world->isAnyViewModeAvailable() && !world->isViewModeAvailable( modeName->c_str() ) )
{
// This mode isn't allowed
_currentMode++;
if ( _currentMode > numModes )
_currentMode = 1;
continue;
}
// We have a valid mode so set it
newEvent = new Event( EV_Sentient_SetViewMode );
newEvent->AddString( modeName->c_str() );
owner->ProcessEvent( newEvent );
return;
}
}
void Equipment::resetMode( void )
{
changeMode( 1 );
}
void Equipment::Uninitialize( void )
{
if ( !_active )
return;
Weapon::Uninitialize();
if ( !owner )
return;
if ( !owner->isSubclassOf( Player ) )
return;
Player* player = (Player*)(Sentient*)owner;
if ( player )
{
player->client->ps.pm_flags &= ~(PMF_SCANNER | PMF_RADAR_MODE);
}
_lastMode = _currentMode;
resetMode();
_active = false;
}
void Equipment::Archive( Archiver &arc )
{
Weapon::Archive( arc );
arc.ArchiveBool( &scanner );
arc.ArchiveBool( &scanning );
arc.ArchiveInteger( &_scanEndFrame );
arc.ArchiveInteger( &scanTime );
arc.ArchiveBool( &radar );
_modes.Archive( arc );
arc.ArchiveInteger( &_currentMode );
arc.ArchiveInteger( &_lastMode );
arc.ArchiveString( &_typeName );
arc.ArchiveSafePointer( &_scannedEntity );
arc.ArchiveBool( &_active );
arc.ArchiveFloat( &_nextUseTime );
}
void Equipment::setTypeName( Event *ev )
{
_typeName = ev->GetString( 1 );
}
int Equipment::getStat( int statNum )
{
if ( statNum == STAT_WEAPON_GENERIC1 )
{
// Return the locking percentage for the torpedo strike
str modeName;
if ( !scanner || !scanning )
return 0;
if ( level.time < _nextUseTime )
return 0;
modeName = _modes.ObjectAt( _currentMode );
if ( modeName == "torpedostrike" )
{
return (int)( ( ( level.time - scanTime ) / 5.0f ) * 100.0f );
}
}
else if ( statNum == STAT_WEAPON_GENERIC2 )
{
if ( level.time > _nextUseTime )
return 100;
return (int)( 100.0f - ( ( _nextUseTime - level.time ) / 30.0f ) * 100.0f );
}
return 0;
}