mirror of
https://github.com/UberGames/EF2GameSource.git
synced 2024-11-30 15:10:51 +00:00
658 lines
12 KiB
C++
658 lines
12 KiB
C++
//-----------------------------------------------------------------------------
|
|
//
|
|
// $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;
|
|
}
|