ef2-sdk/dlls/game/vehicle.cpp

2579 lines
54 KiB
C++
Raw Permalink Normal View History

2003-11-05 00:00:00 +00:00
//-----------------------------------------------------------------------------
//
// $Logfile:: /EF2/Code/DLLs/game/vehicle.cpp $
// $Revision:: 34 $
// $Author:: Singlis $
// $Date:: 9/26/03 2:36p $
//
// Copyright (C) 1997 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:
// Script controlled Vehicles.
//
#include "_pch_cpp.h"
#include "scriptslave.h"
#include "vehicle.h"
#include "player.h"
#include "specialfx.h"
#include "explosion.h"
#include "earthquake.h"
#include "gibs.h"
#include "player.h"
Event EV_Vehicle_Start
(
"start",
EV_DEFAULT,
NULL,
NULL,
"Initialize the vehicle."
);
Event EV_Vehicle_Enter
(
"enter",
EV_CODEONLY,
"eS",
"vehicle driver_anim",
"Called when someone gets into a vehicle."
);
Event EV_Vehicle_Exit
(
"exit",
EV_DEFAULT,
"e",
"vehicle",
"Called when driver gets out of the vehicle."
);
Event EV_Vehicle_Drivable
(
"drivable",
EV_DEFAULT,
NULL,
NULL,
"Make the vehicle drivable"
);
Event EV_Vehicle_UnDrivable
(
"undriveable",
EV_DEFAULT,
NULL,
NULL,
"Make the vehicle undrivable"
);
Event EV_Vehicle_Jumpable
(
"canjump",
EV_DEFAULT,
"b",
"jumpable",
"Sets whether or not the vehicle can jump"
);
Event EV_Vehicle_Lock
(
"lock",
EV_DEFAULT,
NULL,
NULL,
"Sets the vehicle to be locked"
);
Event EV_Vehicle_UnLock
(
"unlock",
EV_DEFAULT,
NULL,
NULL,
"Sets the vehicle to be unlocked"
);
Event EV_Vehicle_SeatAnglesOffset
(
"seatanglesoffset",
EV_DEFAULT,
"v",
"angles",
"Set the angles offset of the seat"
);
Event EV_Vehicle_SeatOffset
(
"seatoffset",
EV_DEFAULT,
"v",
"offset",
"Set the offset of the seat"
);
Event EV_Vehicle_DriverAnimation
(
"driveranim",
EV_DEFAULT,
"s",
"animation",
"Set the animation of the driver to use when the driver is in the vehicle"
);
Event EV_Vehicle_SetWeapon
(
"setweapon",
EV_DEFAULT,
"s",
"weaponname",
"Set the weapon for the vehicle"
);
Event EV_Vehicle_SetSpeed
(
"vehiclespeed",
EV_DEFAULT,
"f",
"speed",
"Set the speed of the vehicle"
);
Event EV_Vehicle_SetTurnRate
(
"turnrate",
EV_DEFAULT,
"f",
"rate",
"Set the turning rate of the vehicle"
);
Event EV_Vehicle_SteerInPlace
(
"steerinplace",
EV_DEFAULT,
NULL,
NULL,
"Set the vehicle to turn in place"
);
Event EV_Vehicle_ShowWeapon
(
"showweapon",
EV_DEFAULT,
NULL,
NULL,
"Set the weapon to be show in the view"
);
Event EV_Vehicle_RestrictPitch
(
"restrictpitch",
EV_DEFAULT,
"f",
"pitchDelta",
"The max and minimum pitch of the driver"
);
Event EV_Vehicle_RestrictRotation
(
"restrictrotation",
EV_DEFAULT,
"f",
"rotateDelta",
"The max and min rotation of the driver"
);
Event EV_Vehicle_NoPrediction
(
"noprediction",
EV_DEFAULT,
"b",
"bool",
"Turns no prediction on or off"
);
Event EV_Vehicle_DisableInventory
(
"disableinventory",
EV_DEFAULT,
NULL,
NULL,
"Disables the inventory when the player uses this vehicle"
);
extern Event EV_Player_DisableUseWeapon;
extern Event EV_Player_PutawayWeapon;
CLASS_DECLARATION( ScriptModel, VehicleBase, NULL )
{
{ NULL, NULL }
};
VehicleBase::VehicleBase()
{
if ( LoadingSavegame )
{
// Archive function will setup all necessary data
return;
}
takedamage = DAMAGE_NO;
showModel();
setSolidType( SOLID_NOT );
setMoveType( MOVETYPE_NONE );
setOrigin( GetLocalOrigin() + Vector( "0 0 30") );
//
// we want the bounds of this model auto-rotated
//
flags |= FL_ROTATEDBOUNDS;
//
// rotate the mins and maxs for the model
//
setSize( mins, maxs );
vlink = NULL;
offset = Vector(0, 0, 0);
PostEvent( EV_BecomeNonSolid, EV_POSTSPAWN );
}
CLASS_DECLARATION( VehicleBase, BackWheels, "script_wheelsback" )
{
{ NULL, NULL }
};
CLASS_DECLARATION( VehicleBase, FrontWheels, "script_wheelsfront" )
{
{ NULL, NULL }
};
CLASS_DECLARATION( VehicleBase, Vehicle, "script_vehicle" )
{
{ &EV_Blocked, &Vehicle::VehicleBlocked },
{ &EV_Touch, &Vehicle::VehicleTouched },
{ &EV_Use, &Vehicle::DriverUse },
{ &EV_Vehicle_Start, &Vehicle::VehicleStart },
{ &EV_Vehicle_Drivable, &Vehicle::Drivable },
{ &EV_Vehicle_UnDrivable, &Vehicle::UnDrivable },
{ &EV_Vehicle_Jumpable, &Vehicle::Jumpable },
{ &EV_Vehicle_SeatAnglesOffset, &Vehicle::SeatAnglesOffset},
{ &EV_Vehicle_SeatOffset, &Vehicle::SeatOffset },
{ &EV_Vehicle_Lock, &Vehicle::Lock },
{ &EV_Vehicle_UnLock, &Vehicle::UnLock },
{ &EV_Vehicle_SetWeapon, &Vehicle::SetWeapon },
{ &EV_Vehicle_DriverAnimation, &Vehicle::DriverAnimation },
{ &EV_Vehicle_SetSpeed, &Vehicle::SetSpeed },
{ &EV_Vehicle_SetTurnRate, &Vehicle::SetTurnRate },
{ &EV_Vehicle_SteerInPlace, &Vehicle::SteerInPlace },
{ &EV_Vehicle_ShowWeapon, &Vehicle::ShowWeaponEvent },
{ &EV_Vehicle_RestrictPitch, &Vehicle::RestrictPitch },
{ &EV_Vehicle_RestrictRotation, &Vehicle::RestrictRotation},
{ &EV_Vehicle_NoPrediction, &Vehicle::SetNoPrediction },
{ &EV_Vehicle_DisableInventory, &Vehicle::DisableInventory},
{ NULL, NULL }
};
Vehicle::Vehicle()
{
if ( LoadingSavegame )
{
// Archive function will setup all necessary data
return;
}
takedamage = DAMAGE_YES;
seatangles = vec_zero;
driveroffset = vec_zero;
seatoffset = vec_zero;
driver = 0;
lastdriver = 0;
currentspeed = 0;
turnangle = 0;
turnimpulse = 0;
moveimpulse = 0;
jumpimpulse = 0;
conesize = 75;
hasweapon = false;
locked = false;
steerinplace = false;
drivable = true;
jumpable = false;
showweapon = false;
flags |= FL_DIE_EXPLODE;
// touch triggers by default
flags |= FL_TOUCH_TRIGGERS;
gravity = 1;
mass = size.length() * 10.0f;
health = 1000;
speed = 1200;
maxturnrate = 40.0f;
usetime = 0.0f;
maxtracedist = 0;
_restrictPitch = false;
_restrictYaw = false;
_noPrediction = false;
_disableInventory = false;
PostEvent( EV_Vehicle_Start, FRAMETIME );
}
void Vehicle::VehicleStart( Event *ev )
{
Entity *ent;
VehicleBase *last;
Vector drivemins, drivemaxs;
float max;
float width,height;
orientation_t orn;
// become solid
setSolidType( SOLID_BBOX );
last = this;
for( ent = G_NextEntity( NULL ); ent != NULL; ent = G_NextEntity( ent ) )
{
if ( ( ent != this ) && ( ent->isSubclassOf( VehicleBase ) ) )
{
if ( ( ent->absmax.x >= absmin.x ) && ( ent->absmax.y >= absmin.y ) && ( ent->absmax.z >= absmin.z ) &&
( ent->absmin.x <= absmax.x ) && ( ent->absmin.y <= absmax.y ) && ( ent->absmin.z <= absmax.z ) )
{
last->vlink = ( VehicleBase * )ent;
last = ( VehicleBase * )ent;
last->offset = last->origin - origin;
last->offset = getLocalVector( last->offset );
last->edict->s.scale *= edict->s.scale;
}
}
}
last->vlink = NULL;
//
// get the seat offset
//
if ( GetRawTag( "tag_rider", &orn ) )
{
driveroffset = Vector( orn.origin );
}
driveroffset += seatoffset * edict->s.scale;
SetDriverAngles( angles + seatangles );
max_health = health;
//
// calculate drive mins and maxs
//
max = 0;
if ( fabs( mins[ 0 ] ) > max )
max = fabs( mins[ 0 ] );
if ( fabs( maxs[ 0 ] ) > max )
max = fabs( maxs[ 0 ] );
if ( fabs( mins[ 1 ] ) > max )
max = fabs( mins[ 1 ] );
if ( fabs( maxs[ 1 ] ) > max )
max = fabs( maxs[ 1 ] );
drivemins = Vector( -max, -max, mins[ 2 ] ) * edict->s.scale;
drivemaxs = Vector( max, max, maxs[ 2 ] ) * edict->s.scale;
width = maxs[ 1 ] - mins[ 1 ];
height = maxs[ 0 ] - mins[ 0 ];
maxtracedist = height;
Corners[ 0 ][ 0 ] = -( width / 4.0f );
Corners[ 0 ][ 1 ] = ( height / 4.0f );
Corners[ 0 ][ 2 ] = 0.0f;
Corners[ 1 ][ 0 ] = ( width / 4.0f );
Corners[ 1 ][ 1 ] = ( height / 4.0f );
Corners[ 1 ][ 2 ] = 0.0f;
Corners[ 2 ][ 0 ] = -( width / 4.0f );
Corners[ 2 ][ 1 ] = -( height / 4.0f );
Corners[ 2 ][ 2 ] = 0.0f;
Corners[ 3 ][ 0 ] = ( width / 4.0f );
Corners[ 3 ][ 1 ] = -( height / 4.0f );
Corners[ 3 ][ 2 ] = 0.0f;
if ( drivable )
{
// drop everything back to the floor
//droptofloor( 250.0f );
Postthink();
}
last_origin = origin;
setSize( drivemins, drivemaxs );
}
void Vehicle::Drivable( Event *ev )
{
setMoveType( MOVETYPE_NONE );
drivable = true;
}
void Vehicle::UnDrivable( Event *ev )
{
setMoveType( MOVETYPE_PUSH );
drivable = false;
}
void Vehicle::Jumpable( Event *ev )
{
jumpable = true;
}
void Vehicle::Lock( Event *ev )
{
locked = true;
}
void Vehicle::UnLock( Event *ev )
{
locked = false;
}
void Vehicle::SteerInPlace( Event *ev )
{
steerinplace = true;
}
void Vehicle::SeatAnglesOffset( Event *ev )
{
seatangles = ev->GetVector( 1 );
}
void Vehicle::SeatOffset( Event *ev )
{
seatoffset = ev->GetVector( 1 );
}
void Vehicle::SetWeapon( Event *ev )
{
showweapon = true;
hasweapon = true;
weaponName = ev->GetString( 1 );
}
void Vehicle::ShowWeaponEvent( Event *ev )
{
showweapon = true;
}
//-----------------------------------------------------
//
// Name: RestrictPitch
// Class: Vehicle
//
// Description: Restricts the drivers pitch while in the vehicle. This calculates the pitchSeam.
// A seam is the location where the normalized angle range boundary. For example
// on a normalized range of -180, 180, the seam is located at -180/180. To solve the seam
// problem, the seam is always positioned directly (-180 degrees) behind the starting pitch.
//
// Parameters: event - event containing the pitch restrictions
//
// Returns: None
//-----------------------------------------------------
void Vehicle::RestrictPitch(Event* event)
{
_startPitch = angles[PITCH];
_pitchSeam = _startPitch - 180.0f;
_minimumPitch = AngleNormalizeArbitrary( _startPitch - event->GetFloat(1), _pitchSeam);
_maximumPitch = AngleNormalizeArbitrary( _startPitch + event->GetFloat(1), _pitchSeam);
_restrictPitch = true;
}
//-----------------------------------------------------
//
// Name: RestrictRotation
// Class: Vehicle
//
// Description: Restricts the drivers rotation while in the vehicle.This calculates the yawSeam.
// A seam is the location where the normalized angle range boundary. For example
// on a normalized range of -180, 180, the seam is located at -180/180. To solve the seam
// problem, the seam is always positioned directly (-180 degrees) behind the starting yaw.
//
// Parameters: event - the event containing the rotation restrictions
//
// Returns: None
//-----------------------------------------------------
void Vehicle::RestrictRotation(Event* event)
{
_startYaw = angles[YAW];
_yawSeam = _startYaw - 180.0f;
_minimumYaw = AngleNormalizeArbitrary( _startYaw - event->GetFloat(1), _yawSeam);
_maximumYaw = AngleNormalizeArbitrary( _startYaw + event->GetFloat(1), _yawSeam);
_restrictYaw = true;
}
//-----------------------------------------------------
//
// Name: SetNoPrediction
// Class: Vehicle
//
// Description: Turns the no prediction flag
//
// Parameters: event - the event that turns no prediction on or off
//
// Returns: None
//-----------------------------------------------------
void Vehicle::SetNoPrediction(Event* event)
{
_noPrediction = event->GetBoolean(1);
}
void Vehicle::DisableInventory(Event* event)
{
_disableInventory = true;
}
void Vehicle::DriverAnimation( Event *ev )
{
driveranim = ev->GetString( 1 );
}
qboolean Vehicle::HasWeapon( void )
{
return hasweapon;
}
qboolean Vehicle::ShowWeapon( void )
{
return showweapon;
}
void Vehicle::SetDriverAngles( const Vector &angles )
{
int i;
if ( !driver )
return;
for( i = 0; i < 3; i++ )
{
driver->client->ps.delta_angles[ i ] = ANGLE2SHORT( angles[ i ] - driver->client->cmd_angles[ i ] );
}
}
void Vehicle::HandleEvent( Event *ev )
{
}
/*
=============
CheckWater
=============
*/
void Vehicle::CheckWater( void )
{
Vector point;
int cont;
int sample1;
int sample2;
VehicleBase *v;
unlink();
v = this;
while( v->vlink )
{
v = v->vlink;
v->unlink();
}
if ( driver )
{
driver->unlink();
}
//
// get waterlevel
//
waterlevel = 0;
watertype = 0;
sample2 = maxs[ 2 ] - mins[ 2 ];
sample1 = sample2 / 2;
point = origin;
point[ 2 ] += mins[ 2 ];
cont = gi.pointcontents( point, 0 );
if ( cont & MASK_WATER )
{
watertype = cont;
waterlevel = 1;
point[ 2 ] = origin[ 2 ] + mins[ 2 ] + sample1;
cont = gi.pointcontents( point, 0 );
if ( cont & MASK_WATER )
{
waterlevel = 2;
point[ 2 ] = origin[ 2 ] + mins[ 2 ] + sample2;
cont = gi.pointcontents( point, 0 );
if ( cont & MASK_WATER )
{
waterlevel = 3;
}
}
}
link();
v = this;
while( v->vlink )
{
v = v->vlink;
v->link();
}
if ( driver )
{
driver->link();
driver->waterlevel = waterlevel;
driver->watertype = watertype;
}
}
/*
=============
WorldEffects
=============
*/
void Vehicle::WorldEffects( void )
{
//
// Check for earthquakes
//
/* if ( groundentity && ( level.earthquake > level.time ) )
{
velocity += Vector
(
level.earthquake_magnitude * EARTHQUAKE_STRENGTH * G_CRandom(),
level.earthquake_magnitude * EARTHQUAKE_STRENGTH * G_CRandom(),
level.earthquake_magnitude * 1.5f * G_Random()
);
} */
//
// check for lava
//
if ( watertype & CONTENTS_LAVA )
{
Damage( world, world, 20.0f * waterlevel, origin, vec_zero, vec_zero, 0, DAMAGE_NO_ARMOR, MOD_LAVA );
}
}
void Vehicle::DriverUse( Event *ev )
{
Event *event;
Entity *other;
other = ev->GetEntity( 1 );
if ( level.time < ( usetime + 1.0f ) )
return;
else
usetime = level.time;
if ( !other || !other->isSubclassOf( Sentient ) )
{
return;
}
if ( driver )
{
// int height;
// int ang;
Vector angles;
Vector forward;
Vector pos;
// float ofs;
// trace_t trace;
if ( other != driver )
{
return;
}
if ( locked )
return;
/*
//
// place the driver on the ground
//
ofs = size.length() * 0.5f;
for ( height = 0; height < 100; height += 16 )
{
for ( ang = 0; ang < 360; ang += 30 )
{
angles[ 1 ] = driver->angles[ 1 ] + ang + 90.0f;
angles.AngleVectors( &forward, NULL, NULL );
pos = origin + (forward * ofs);
pos[2] += height;
trace = G_Trace( pos, driver->mins, driver->maxs, pos, NULL, MASK_PLAYERSOLID, false, "Vehicle::DriverUse 1" );
if ( !trace.startsolid && !trace.allsolid )
{
Vector end;
end = pos;
end[ 2 ] -= 128.0f;
trace = G_Trace( pos, driver->mins, driver->maxs, end, NULL, MASK_PLAYERSOLID, false, "Vehicle::DriverUse 2" );
if ( trace.fraction < 1.0f )
{
driver->setOrigin( pos );
goto foundpos;
}
}
}
}
*/
// return;
//foundpos:
turnimpulse = 0;
moveimpulse = 0;
jumpimpulse = 0;
event = new Event( EV_Vehicle_Exit );
event->AddEntity( this );
driver->ProcessEvent( event );
if ( drivable )
{
StopLoopSound();
Sound( "snd_dooropen", CHAN_BODY );
Sound( "snd_stop", CHAN_VOICE );
driver->setSolidType( SOLID_BBOX );
}
driver->setOrigin(_oldOrigin);
enableDriverInventory();
if( hasweapon )
{
driver->DeactivateWeapon( WEAPON_DUAL );
driver->takeItem( weaponName.c_str() );
}
driver = NULL;
}
else
{
driver = ( Sentient * )other;
VectorCopy(driver->edict->client->ps.origin, _oldOrigin);
lastdriver = driver;
if ( drivable )
setMoveType( MOVETYPE_VEHICLE );
if ( hasweapon )
{
driver->giveItem( weaponName.c_str() );
driver->useWeapon(weaponName.c_str(), WEAPON_DUAL);
}
disableDriverInventory();
if ( drivable )
{
Sound( "snd_doorclose", CHAN_BODY );
Sound( "snd_start", CHAN_VOICE );
driver->setSolidType( SOLID_NOT );
}
event = new Event( EV_Vehicle_Enter );
event->AddEntity( this );
if ( driveranim.length() )
event->AddString( driveranim );
driver->ProcessEvent( event );
offset = other->origin - origin;
flags |= FL_POSTTHINK;
SetDriverAngles( angles + seatangles );
}
}
//-----------------------------------------------------
//
// Name:
// Class:
//
// Description:
//
// Parameters:
//
// Returns:
//-----------------------------------------------------
void Vehicle::disableDriverInventory( void )
{
if( _disableInventory == false )
return;
if(driver != 0 && driver->isSubclassOf(Player))
{
((Player*)driver.Pointer())->disableInventory();
}
}
//-----------------------------------------------------
//
// Name:
// Class:
//
// Description:
//
// Parameters:
//
// Returns:
//-----------------------------------------------------
void Vehicle::enableDriverInventory( void )
{
if( driver != 0 && driver->isSubclassOf(Player))
{
((Player*)driver.Pointer())->enableInventory();
}
}
qboolean Vehicle::Drive( usercmd_t *ucmd )
{
Vector i, j, k;
if ( !driver || !driver->isClient() )
{
return false;
}
if ( !drivable )
{
driver->client->ps.pm_flags |= PMF_FROZEN;
ucmd->forwardmove = 0;
ucmd->rightmove = 0;
return false;
}
if(_noPrediction )
{
driver->client->ps.pm_flags |= PMF_NO_PREDICTION;
}
moveimpulse = ( ( float )ucmd->forwardmove ) * 3.0f;
//turnimpulse = ( ( float )-ucmd->rightmove ) * 3.0f;
jumpimpulse = ( ( float )ucmd->upmove * gravity ) / 350.0f;
ucmd->lean = 0;
if ( ( jumpimpulse < 0.0f ) || ( !jumpable ) )
jumpimpulse = 0.0f;
turnimpulse = 10.0f * angledist( SHORT2ANGLE( ucmd->angles[ 1 ] ) - driver->client->cmd_angles[ 1 ] );
return true;
}
void Vehicle::Postthink( void )
{
float turn;
Vector i, j, k;
Vector normalsum;
Vector temp;
Vector pitch;
Vector roll;
VehicleBase *v;
VehicleBase *last;
float drivespeed;
if ( drivable )
{
currentspeed = moveimpulse / 4.0f;
turnangle = turnangle * 0.25f + turnimpulse;
if ( turnangle > maxturnrate )
{
turnangle = maxturnrate;
}
else if ( turnangle < -maxturnrate )
{
turnangle = -maxturnrate;
}
else if ( fabs( turnangle ) < 2.0f )
{
turnangle = 0;
}
CalculateOrientation();
turn = turnangle * ( 1.0f / 200.0f );
if ( groundentity )
{
float dot;
Vector newvel;
Vector flatvel;
velocity[ 0 ] *= 0.925f;
velocity[ 1 ] *= 0.925f;
flatvel = Vector( orientation[ 0 ] );
velocity += flatvel * currentspeed;
flatvel[ 2 ] = 0;
dot = velocity * flatvel;
if ( dot > speed )
{
dot = speed;
}
else if ( dot < -speed )
{
dot = -speed;
}
else if ( fabs( dot ) < 20.0f )
{
dot = 0;
}
newvel = flatvel * dot;
velocity[ 0 ] = newvel[ 0 ];
velocity[ 1 ] = newvel[ 1 ];
velocity[ 2 ] += dot * jumpimpulse;
avelocity *= 0.05f;
if ( steerinplace )
{
if ( dot < 350.0f )
dot = 350.0f;
avelocity.y += turn * dot;
}
else
{
avelocity.y += turn * dot;
}
}
else
{
avelocity *= 0.1f;
}
angles += avelocity * level.frametime;
setAngles( angles );
}
drivespeed = velocity * Vector( orientation[ 0 ] );
if ( drivable && driver )
{
str sound_name;
if ( currentspeed > 0.0f )
sound_name = "snd_forward";
else if ( currentspeed < 0.0f )
sound_name = "snd_backward";
else
sound_name = "snd_idle";
LoopSound( sound_name.c_str() );
}
i = Vector( orientation[ 0 ] );
j = Vector( orientation[ 1 ] );
k = Vector( orientation[ 2 ] );
if ( driver )
{
Player * player;
if ( driver->isSubclassOf ( Player ) )
{
player = ( Player * )( Sentient * )driver;
player->setOrigin( origin + ( i * driveroffset[0] ) + ( j * driveroffset[1] ) + ( k * driveroffset[2] ) );
if ( drivable )
{
player->velocity = vec_zero;
Vector playerAngles = player->v_angle;
Vector viewAngles = angles;
if( _restrictPitch )
{
if(viewAngles[PITCH] > _maximumPitch)
viewAngles[PITCH] = _maximumPitch;
else if(viewAngles[PITCH] < _minimumPitch)
viewAngles[PITCH] = _minimumPitch;
}
if( _restrictYaw )
{
if(viewAngles[YAW] > _maximumYaw)
viewAngles[YAW] = _maximumYaw;
else if(viewAngles[YAW] <= _minimumYaw)
viewAngles[YAW] = _minimumYaw;
}
angles = viewAngles;
playerAngles.y = angles.y;
playerAngles.z = angles.z;
player->SetViewAngles(playerAngles);
}
}
}
last = this;
while( last->vlink )
{
v = last->vlink;
v->setOrigin( origin + ( i * v->offset.x ) + ( j * v->offset.y ) + ( k * v->offset.z ) );
v->avelocity = avelocity;
v->velocity = velocity;
v->angles[ ROLL ] = angles[ ROLL ];
v->angles[ YAW ] = angles[ YAW ];
v->angles[ PITCH ] = (float)( (int)( v->angles[ PITCH ] + (drivespeed/4) ) % 360 );
if ( v->isSubclassOf( FrontWheels ) )
{
v->angles += Vector( 0.0f, turnangle, 0.0f );
}
v->setAngles( v->angles );
last = v;
}
//CheckWater();
WorldEffects();
// save off last origin
last_origin = origin;
if ( !driver && !velocity.length() && groundentity && !( watertype & CONTENTS_LAVA ) )
{
flags &= ~FL_POSTTHINK;
if ( drivable )
setMoveType( MOVETYPE_NONE );
}
}
/////////////////////////////////////////////////////////
//
// Name: CalculateOrientation()
//
// Description: Calculates the orientation of the vehicle
// by getting the normals of all the poly's underneath
// each corner of the vehicle's bounding box, this
// allows the vehicle to pitch appropriately
//
/////////////////////////////////////////////////////////
void Vehicle::CalculateOrientation( void )
{
Vector temp, pitch, normalsum;
Vector i, j, k;
int numnormals;
trace_t trace;
temp[ PITCH ] = 0;
temp[ YAW ] = angles[ YAW ];
temp[ ROLL ] = 0;
temp.AngleVectors( &i, &j, &k );
j = vec_zero - j;
//
// figure out what our orientation is
//
numnormals = 0;
for ( int index = 0; index < 4; index++ )
{
Vector start, end;
Vector boxoffset;
boxoffset = Corners[ index ];
start = origin + ( i * boxoffset[0] ) + ( j * boxoffset[1] ) + ( k * boxoffset[2] );
end = start;
end[ 2 ] -= maxtracedist;
trace = G_Trace( start, vec_zero, vec_zero, end, NULL, MASK_SOLID, false, "Vehicle::PostThink Corners" );
if ( ( trace.fraction > 0.0f ) && ( trace.fraction != 1.0f ) && !trace.startsolid )
{
normalsum += Vector( trace.plane.normal );
numnormals++;
}
}
if ( numnormals > 1 )
{
temp = normalsum * ( 1.0f/ numnormals );
temp.normalize();
i = temp.CrossProduct( temp, j );
pitch = i;
// determine pitch
angles[ 0 ] = -(pitch.toPitch());
}
}
void Vehicle::VehicleTouched( Event *ev )
{
Entity *other;
float speed;
Vector delta;
Vector dir;
other = ev->GetEntity( 1 );
if ( other == driver )
{
return;
}
if ( other == world )
{
return;
}
if ( drivable && !driver )
{
return;
}
delta = origin - last_origin;
speed = delta.length();
if ( speed > 2 )
{
Sound( "vehicle_crash", true );
dir = delta * ( 1.0f / speed );
other->Damage( this, lastdriver, speed * 8.0f, origin, dir, vec_zero, speed * 15, 0, MOD_VEHICLE );
}
}
void Vehicle::VehicleBlocked( Event *ev )
{
return;
/*
Entity *other;
float speed;
float damage;
Vector delta;
Vector newvel;
Vector dir;
if ( !velocity[0] && !velocity[1] )
return;
other = ev->GetEntity( 1 );
if ( other == driver )
{
return;
}
if ( other->isSubclassOf( VehicleBase ) )
{
delta = other->origin - origin;
delta.normalize();
newvel = vec_zero - ( velocity) + ( other->velocity * 0.25 );
if ( newvel * delta < 0 )
{
velocity = newvel;
delta = velocity - other->velocity;
damage = delta.length()/4;
}
else
{
return;
}
}
else if ( ( velocity.length() < 350 ) )
{
other->velocity += velocity*1.25f;
other->velocity[ 2 ] += 100;
damage = velocity.length()/4;
}
else
{
damage = other->health + 1000;
}
// Gib 'em outright
speed = fabs( velocity.length() );
dir = velocity * ( 1 / speed );
other->Damage( this, lastdriver, damage, origin, dir, vec_zero, speed, 0, MOD_VEHICLE, -1, -1, 1.0f );
*/
}
Sentient *Vehicle::Driver( void )
{
return driver;
}
qboolean Vehicle::IsDrivable( void )
{
return drivable;
}
void Vehicle::SetSpeed( Event *ev )
{
speed = ev->GetFloat( 1 );
}
void Vehicle::SetTurnRate( Event *ev )
{
maxturnrate = ev->GetFloat( 1 );
}
CLASS_DECLARATION( Vehicle, DrivableVehicle, "script_drivablevehicle" )
{
{ &EV_Damage, &Entity::DamageEvent },
{ &EV_Killed, &DrivableVehicle::Killed },
{ NULL, NULL }
};
DrivableVehicle::DrivableVehicle()
{
if ( LoadingSavegame )
{
// Archive function will setup all necessary data
return;
}
drivable = true;
flags |= FL_DIE_EXPLODE;
}
void DrivableVehicle::Killed(Event *ev)
{
Entity * ent;
Entity * attacker;
Vector dir;
Event * event;
const char * name;
VehicleBase *last;
takedamage = DAMAGE_NO;
setSolidType( SOLID_NOT );
hideModel();
attacker = ev->GetEntity( 1 );
//
// kill the driver
//
if ( driver )
{
Vector dir;
SentientPtr sent;
Event * event;
velocity = vec_zero;
sent = driver;
event = new Event( EV_Use );
event->AddEntity( sent );
ProcessEvent( event );
dir = sent->origin - origin;
dir[ 2 ] += 64.0f;
dir.normalize();
sent->Damage( this, this, sent->health * 2.0f, origin, dir, vec_zero, 50, 0, MOD_VEHICLE );
}
if (flags & FL_DIE_EXPLODE)
{
CreateExplosion( origin, 150.0f * edict->s.scale, this, this, this );
}
if (flags & FL_DIE_GIBS)
{
setSolidType( SOLID_NOT );
hideModel();
CreateGibs( this, -150.0f, edict->s.scale, 3 );
}
//
// kill all my wheels
//
last = this;
while( last->vlink )
{
last->vlink->PostEvent( EV_Remove, 0.0f );
last = last->vlink;
}
//
// kill the killtargets
//
name = KillTarget();
if ( name && strcmp( name, "" ) )
{
ent = NULL;
do
{
ent = G_FindTarget( ent, name );
if ( !ent )
{
break;
}
ent->PostEvent( EV_Remove, 0.0f );
}
while ( 1 );
}
//
// fire targets
//
name = Target();
if ( name && strcmp( name, "" ) )
{
ent = NULL;
do
{
ent = G_FindTarget( ent, name );
if ( !ent )
{
break;
}
event = new Event( EV_Activate );
event->AddEntity( attacker );
ent->ProcessEvent( event );
}
while ( 1 );
}
PostEvent( EV_Remove, 0.0f );
}
//===============================================================
// Horse Vehicle Class
// New style of vehicle -- Allows the player to drive the vehicle
// but still mouselook and fire ( turret style )
// different control configurations ( ie Strafe Only ) etc can
// be subclassed from movemode and plugged in easily
//
//===============================================================
Event EV_HorseVehicle_SetSpeed
(
"sethorsespeed",
EV_SCRIPTONLY,
"f",
"speed",
"Set the speed of the horse"
);
Event EV_HorseVehicle_SetMoveMode
(
"setmovemode",
EV_SCRIPTONLY,
"sF",
"mode angles",
"Mode ( standard , strafe, or locked ) and a Vector describing the view Angle )"
);
Event EV_HorseVehicle_SetForcedForward
(
"forceforwardspeed",
EV_SCRIPTONLY,
"f",
"speed",
"Forces the vehicle to move forward at the specified speed"
);
Event EV_Vehicle_AnimDone
(
"animdone",
EV_CODEONLY,
NULL,
NULL,
"animdoneevent"
);
Event EV_Driver_AnimDone
(
"driveranimdone",
EV_CODEONLY,
NULL,
NULL,
"driveranimdoneevent"
);
CLASS_DECLARATION( Vehicle, HorseVehicle, "script_horsevehicle" )
{
{ &EV_HorseVehicle_SetSpeed , &HorseVehicle::SetSpeed },
{ &EV_HorseVehicle_SetMoveMode , &HorseVehicle::SetVehicleMoveMode },
{ &EV_HorseVehicle_SetForcedForward, &HorseVehicle::SetForcedForwardSpeed },
{ &EV_Vehicle_AnimDone, &HorseVehicle::AnimDone },
{ &EV_Driver_AnimDone, &HorseVehicle::DriverAnimDone },
//Move Mode Events
{ &EV_FollowPath_SetWayPointName, &HorseVehicle::PassToMoveMode },
{ NULL, NULL }
};
/////////////////////////////////////////////////////////
//
// Name: HorseVehicle()
//
// Description: Constructor
//
/////////////////////////////////////////////////////////
HorseVehicle::HorseVehicle()
{
if ( LoadingSavegame )
{
// Archive function will setup all necessary data
return;
}
drivable = true;
jumpable = true;
driver = NULL;
currentspeed = 300;
_baseYaw = 0;
_driverYaw = 0;
_lastYaw = 0;
_driverPitch = 0;
_lastPitch = 0;
_forcedForwardSpeed = 0;
_jumpflag = false;
_jumped = false;
_jumpSpeed = 0;
_ducked = false;
_minYawThreshold = 90;
_maxYawThreshold = 90;
_minPitchThreshold = 90;
_maxPitchThreshold = 90;
_moveMode = NULL;
_SetMoveMode( "standard" );
_currentCrosshairMode = CROSSHAIR_MODE_STRAIGHT;
_newCrosshairMode = CROSSHAIR_MODE_STRAIGHT;
_jumpmode = JUMPMODE_DONE;
_duckmode = DUCKMODE_DONE;
_animDone = false;
_driverAnimDone = false;
_ducked = false;
_duckheld = false;
_DriverBBoxMaxs = vec_zero;
_DriverBBoxMins = vec_zero;
_originalBBoxMaxs = vec_zero;
_originalBBoxMins = vec_zero;
//Create the Animate Object so that we can set the
//animation on the vehicle
if ( !animate )
animate = new Animate;
}
HorseVehicle::~HorseVehicle()
{
if ( _moveMode )
{
delete _moveMode;
_moveMode = 0;
}
}
/////////////////////////////////////////////////////////
//
// Name: PassToMoveMode()
//
// Description: Hands off the event to the move mode for
// it to handle -- allows the vehicle to remain dumb in
// regards to movement specific events
//
/////////////////////////////////////////////////////////
void HorseVehicle::PassToMoveMode( Event *ev )
{
if ( _moveMode )
_moveMode->HandleEvent( ev );
}
/////////////////////////////////////////////////////////
//
// Name: Drive()
//
// Description: Called from Player->ClientThink
// Player passes it a ucmd pointer, which points to the
// latest input data from the user. Drive() uses this
// data to set up the movement impulses that help control
// how the vehicle moves
//
/////////////////////////////////////////////////////////
qboolean HorseVehicle::Drive( usercmd_t *ucmd )
{
Vector i, j, k;
if ( !driver || !driver->isClient() )
return false;
if ( !drivable )
{
driver->client->ps.pm_flags |= PMF_FROZEN;
ucmd->forwardmove = 0;
ucmd->rightmove = 0;
return false;
}
//Turn off Client Prediction
//driver->client->ps.pm_flags |= PMF_NO_PREDICTION;
driver->client->ps.in_vehicle = true;
driver->client->ps.vehicleoffset[0] = origin[0];
driver->client->ps.vehicleoffset[1] = origin[1];
driver->client->ps.vehicleoffset[2] = origin[2] + 160.0f;
driver->velocity = velocity;
//jumpimpulse is used to calculate a jump value
//moveimpulse is used to calculate forward and back movement
//turnimpulse is used to calculate strafe movement
jumpimpulse = ( ( float )ucmd->upmove * gravity ) / 350.0f;
moveimpulse = ucmd->forwardmove;
turnimpulse = ucmd->rightmove;
/*
if ( ( jumpimpulse < 0 ) || ( !jumpable ) )
jumpimpulse = 0;
*/
return true;
}
/////////////////////////////////////////////////////////
//
// Name: Postthink()
//
// Description: Think Function ( called every frame )
// Postthink is the master function for the vehicle.
// it delegates out movement, animation, and FX to
// subfunctions.
/////////////////////////////////////////////////////////
void HorseVehicle::Postthink ( void )
{
if ( drivable )
{
CalculateOrientation();
_moveMode->Move( this );
if (driver)
{
_PlayMovementSound();
if ( !_jumpflag)
_AnimateVehicle("run");
if ( !_duckflag )
_PositionDriverModel();
if ( groundentity && ( jumpimpulse > 0 ) && ( _jumpmode == JUMPMODE_DONE ) && ( _duckmode == DUCKMODE_DONE ) )
_InitializeJump();
if ( _jumpflag )
_HandleJump();
if ( groundentity && ( jumpimpulse < 0 ) && ( _duckmode == DUCKMODE_DONE ) && ( _jumpmode == JUMPMODE_DONE ) && !_duckflag )
_InitializeDuck();
//Released the duck key
if ( _duckflag && ( jumpimpulse >= 0 ) )
_ducked = false;
if ( _duckflag )
_HandleDuck();
}
}
//Do World Effects ( for earthquakes and the like )
WorldEffects();
Vector flatvel;
if ( _forcedForwardSpeed )
{
flatvel = Vector( orientation[ 0 ] );
velocity += flatvel * _forcedForwardSpeed;
}
// Turn off think if we aren't being used
if ( !driver && !velocity.length() && groundentity && !( watertype & CONTENTS_LAVA ) )
{
flags &= ~FL_POSTTHINK;
if ( drivable )
setMoveType( MOVETYPE_NONE );
}
}
/////////////////////////////////////////////////////////
//
// Name: DriverUse()
//
// Description: Function is called when the player "uses"
// the vehicle. It handles putting the driver in and
// taking the driver out of the vehicle.
//
// Some sort of use-delay mechanism must be in place or
// it will be very difficult for the player to get on and
// off the vehicle, because the use events are stepping
// on each other
//
/////////////////////////////////////////////////////////
void HorseVehicle::DriverUse( Event *ev )
{
Event *event;
Entity *other;
other = ev->GetEntity( 1 );
if ( level.time < usetime + 1.0f )
return;
else
usetime = level.time;
if ( !other || !other->isSubclassOf( Sentient ) )
{
return;
}
if ( driver )
{
int height;
int ang;
Vector angles;
Vector forward;
Vector pos;
float ofs;
trace_t trace;
if ( other != driver )
{
return;
}
if ( locked )
return;
//
// place the driver on the ground
//
driver->detach();
_AnimateDriver( "idle" );
_AnimateVehicle( "idle" );
driver->client->ps.in_vehicle = false;
ofs = size.length() * 0.5f;
for ( height = 0; height < 100; height += 16 )
{
for ( ang = 0; ang < 360; ang += 30 )
{
angles[ 1 ] = driver->angles[ 1 ] + ang + 90.0f;
angles.AngleVectors( &forward, NULL, NULL );
pos = origin + (forward * ofs);
pos[2] += height;
trace = G_Trace( pos, driver->mins, driver->maxs, pos, NULL, MASK_PLAYERSOLID, false, "Vehicle::DriverUse 1" );
if ( !trace.startsolid && !trace.allsolid )
{
Vector end;
end = pos;
end[ 2 ] -= 128.0f;
trace = G_Trace( pos, driver->mins, driver->maxs, end, NULL, MASK_PLAYERSOLID, false, "Vehicle::DriverUse 2" );
if ( trace.fraction < 1.0f )
{
driver->setOrigin( pos );
goto foundpos;
}
}
}
}
return;
foundpos:
turnimpulse = 0;
moveimpulse = 0;
jumpimpulse = 0;
event = new Event( EV_Vehicle_Exit );
event->AddEntity( this );
driver->ProcessEvent( event );
if ( hasweapon )
{
driver->takeItem( weaponName.c_str() );
}
if ( drivable )
{
StopLoopSound();
Sound( "snd_dooropen", CHAN_BODY );
Sound( "snd_stop", CHAN_VOICE );
driver->setSolidType( SOLID_BBOX );
}
driver = NULL;
}
else
{
driver = ( Sentient * )other;
lastdriver = driver;
if ( drivable )
setMoveType( MOVETYPE_VEHICLE );
if ( drivable )
{
Sound( "snd_doorclose", CHAN_BODY );
Sound( "snd_start", CHAN_VOICE );
driver->setSolidType( SOLID_NOT );
}
event = new Event( EV_Vehicle_Enter );
event->AddEntity( this );
if ( driveranim.length() )
event->AddString( driveranim );
driver->ProcessEvent( event );
offset = other->origin - origin;
flags |= FL_POSTTHINK;
SetDriverAngles( angles + seatangles );
int tagnum = gi.Tag_NumForName( this->edict->s.modelindex, "tag_rider" );
driver->attach(this->entnum , tagnum , false , seatoffset );
if ( driver->isSubclassOf( Player ) )
{
Player* player;
player = ( Player * )( Sentient * )driver;
player->v_angle = angles;
_lastYaw = player->client->cmd_angles[YAW];
_DriverBBoxMaxs = player->maxs;
_DriverBBoxMins = player->mins;
}
_driverYaw = angles[YAW];
_driverPitch = angles[PITCH];
_originalBBoxMaxs = _DriverBBoxMaxs;
_originalBBoxMins = _DriverBBoxMins;
}
}
/////////////////////////////////////////////////////////
//
// Name: _PlayMovementSound()
//
// Description: Plays a sound based on movement
//
/////////////////////////////////////////////////////////
void HorseVehicle::_PlayMovementSound()
{
str sound_name;
if ( currentspeed > 0.0f )
sound_name = "snd_forward";
else if ( currentspeed < 0.0f )
sound_name = "snd_backward";
else
sound_name = "snd_idle";
LoopSound( sound_name.c_str() );
}
/////////////////////////////////////////////////////////
//
// Name: _AnimateVehicle()
//
// Description: Sets the animation on the vehicle
//
/////////////////////////////////////////////////////////
void HorseVehicle::_AnimateVehicle(const str &anim , qboolean useEvent )
{
if ( animate )
{
int anim_num = gi.Anim_Random ( this->edict->s.modelindex, anim.c_str() );
if ( anim_num != -1 )
{
if ( animate->CurrentAnim() != anim_num )
{
animate->ClearLegsAnim();
animate->ClearTorsoAnim();
animate->NewAnim( anim_num );
if ( useEvent )
{
Event *ev = new Event( EV_Vehicle_AnimDone );
animate->SetAnimDoneEvent( ev );
_animDone = false;
}
}
}
}
}
/////////////////////////////////////////////////////////
//
// Name: _AnimateDriver()
//
// Description: Sets the animation on the vehicle
//
/////////////////////////////////////////////////////////
void HorseVehicle::_AnimateDriver(const str &anim , qboolean useEvent )
{
if ( !driver )
return;
Player *player;
player = ( Player * )( Sentient * )driver;
if ( player->animate )
{
int anim_num = gi.Anim_Random ( player->edict->s.modelindex, anim.c_str() );
if ( anim_num != -1 )
{
if ( player->animate->CurrentAnim() != anim_num )
{
player->animate->ClearLegsAnim();
player->animate->ClearTorsoAnim();
player->animate->NewAnim( anim_num );
if ( useEvent )
{
Event *ev = new Event( EV_Driver_AnimDone );
player->animate->SetAnimDoneEvent( ev );
_driverAnimDone = false;
}
}
}
}
}
/////////////////////////////////////////////////////////
//
// Name: _PositionDriverModel()
//
// Description: Places the driver model in the correct position
//
/////////////////////////////////////////////////////////
void HorseVehicle::_PositionDriverModel()
{
Vector i, j, k;
i = Vector( orientation[ 0 ] );
j = Vector( orientation[ 1 ] );
k = Vector( orientation[ 2 ] );
if ( driver )
{
Player *player;
player = ( Player * )( Sentient * )driver;
if ( drivable )
{
player->velocity = vec_zero;
player->setAngles( angles );
float delta;
delta = AngleDelta(_lastYaw , player->client->cmd_angles[YAW] );
_driverYaw += ( delta * -1.0f );
_lastYaw = player->client->cmd_angles[YAW];
_newCrosshairMode = CROSSHAIR_MODE_STRAIGHT;
if ( _driverYaw >= ( angles[YAW] + _maxYawThreshold ) )
{
_driverYaw = angles[YAW] + _maxYawThreshold;
_newCrosshairMode = CROSSHAIR_MODE_RIGHT;
}
if ( _driverYaw <= ( angles[YAW] - _minYawThreshold ) )
{
_driverYaw = angles[YAW] - _minYawThreshold;
_newCrosshairMode = CROSSHAIR_MODE_LEFT;
}
delta = AngleDelta(_lastPitch , player->client->cmd_angles[PITCH] );
_driverPitch += ( delta * -1.0f );
_lastPitch = player->client->cmd_angles[PITCH];
if ( _driverPitch > ( angles[PITCH] + _maxPitchThreshold ) )
_driverPitch = angles[PITCH] + _maxPitchThreshold;
if ( _driverPitch < ( angles[PITCH] - _minPitchThreshold ) )
_driverPitch = angles[PITCH] - _minPitchThreshold;
_SetCrossHairMode();
player->v_angle = player->client->cmd_angles;
player->v_angle[YAW] = _driverYaw;
player->v_angle[PITCH] = _driverPitch;
if ( !_duckflag )
player->SetAnim( "ride" , legs );
}
}
}
/////////////////////////////////////////////////////////
//
// Name: _SetSpeed()
//
// Description: Specifies the speed that the vehicle will move
//
/////////////////////////////////////////////////////////
void HorseVehicle::_SetSpeed ( Event *ev )
{
currentspeed = ev->GetFloat( 1 );
}
/////////////////////////////////////////////////////////
//
// Name: SetVehicleMoveMode()
//
// Description: Script Interface to set the moveMode object
//
/////////////////////////////////////////////////////////
void HorseVehicle::SetVehicleMoveMode( Event *ev )
{
str modeName = ev->GetString( 1 );
_SetMoveMode( modeName );
}
/////////////////////////////////////////////////////////
//
// Name: SetForcedForwardSpeed()
//
// Description: Script interface for setting the
// _forcedForwardSpeed
//
/////////////////////////////////////////////////////////
void HorseVehicle::SetForcedForwardSpeed( Event *ev )
{
_forcedForwardSpeed = ev->GetFloat( 1 );
}
/////////////////////////////////////////////////////////
//
// Name: _HandleJump()
//
// Description: Takes care of jumping
//
/////////////////////////////////////////////////////////
void HorseVehicle::_HandleJump()
{
if ( groundentity && _jumped && ( _jumpmode == JUMPMODE_DONE ) )
{
velocity[2] = 0;
_jumpflag = false;
_jumped = false;
_jumpSpeed = 0;
return;
}
switch ( _jumpmode )
{
case JUMPMODE_START:
flags |= FL_FLY;
_jumpSpeed += 100.0f;
if ( _jumpSpeed > 600.0f )
_jumpSpeed = 600.0f;
velocity[2] = _jumpSpeed;
_AnimateVehicle ( "jump_to_rise" , true );
if ( _animDone )
{
_jumpSpeed = 0;
_animDone = false;
_jumpmode = JUMPMODE_HOLD;
}
break;
case JUMPMODE_HOLD:
if ( level.time < _holdtime )
{
_jumpSpeed += 10.0f;
velocity[2] = _jumpSpeed;
_AnimateVehicle("rise" , true);
}
else
{
_jumpSpeed = 0;
_animDone = false;
_jumpmode = JUMPMODE_LAND;
}
break;
case JUMPMODE_LAND:
_jumpSpeed -= 100.0f;
if ( _jumpSpeed < -600.0f )
_jumpSpeed = -600.0f;
velocity[2] = _jumpSpeed;
_AnimateVehicle( "rise_to_land" , true );
if ( _animDone )
{
flags &= ~FL_FLY;
_jumpmode = JUMPMODE_DONE;
_AnimateVehicle( "run" );
}
break;
default:
//Should never get here, but you know... It's code.
break;
}
_jumped = true;
}
/////////////////////////////////////////////////////////
//
// Name: _InitializeDuck()
//
// Description: Starts Jump
//
/////////////////////////////////////////////////////////
void HorseVehicle::_InitializeDuck()
{
if ( !driver )
return;
_duckflag = true;
_duckmode = DUCKMODE_START;
_driverAnimDone = false;
_ducked = true;
//Player *player;
//player = ( Player * )( Sentient * )driver;
_DriverBBoxMaxs[2] -= 64.0f;
}
/////////////////////////////////////////////////////////
//
// Name: _HandleDuck()
//
// Description: Starts Jump
//
/////////////////////////////////////////////////////////
void HorseVehicle::_HandleDuck()
{
//Going to position torso Forward
//_driverYaw = 0;
//_lastYaw = 0;
//_driverPitch = 0;
//_lastPitch = 0;
if ( !driver )
return;
Player *player;
player = ( Player * )( Sentient * )driver;
player->v_angle = angles;
player->setAngles( angles );
_driverYaw = angles[YAW];
_lastYaw = player->client->cmd_angles[YAW];
Vector tempMins;
Vector tempMaxs;
switch ( _duckmode )
{
case DUCKMODE_START:
_AnimateDriver("ride_to_duck" , true );
if ( _driverAnimDone )
_duckmode = DUCKMODE_HOLD;
break;
case DUCKMODE_HOLD:
if ( _ducked )
_AnimateDriver("ride_duck_hold" , true );
else
_duckmode = DUCKMODE_FINISH;
break;
case DUCKMODE_FINISH:
_AnimateDriver("duck_to_ride" , true );
if ( _driverAnimDone )
_duckmode = DUCKMODE_DONE;
break;
case DUCKMODE_DONE:
_AnimateDriver( "ride" );
tempMaxs[2] +=64.0f;
player->setSize(_DriverBBoxMaxs, _DriverBBoxMaxs );
_DriverBBoxMaxs = _originalBBoxMaxs;
_DriverBBoxMins = _originalBBoxMins;
_duckflag = false;
break;
}
}
/////////////////////////////////////////////////////////
//
// Name: _InitializeJump()
//
// Description: Starts Jump
//
/////////////////////////////////////////////////////////
void HorseVehicle::_InitializeJump()
{
_jumptime = level.time + .25f;
_holdtime = _jumptime + .15f;
_jumpflag = true;
_jumpmode = JUMPMODE_START;
_animDone = false;
}
/////////////////////////////////////////////////////////
//
// Name: _SetMoveMode()
//
// Description: _moveMode Factory
//
/////////////////////////////////////////////////////////
void HorseVehicle::_SetMoveMode( const str &modeName )
{
if ( _moveMode )
{
delete _moveMode;
_moveMode = 0;
}
if ( !Q_stricmp(modeName.c_str() , "standard" ) )
_moveMode = new HVMoveMode_Standard;
else if ( !Q_stricmp(modeName.c_str() , "strafe" ) )
_moveMode = new HVMoveMode_Strafe;
else if ( !Q_stricmp(modeName.c_str() , "locked" ) )
_moveMode = new HVMoveMode_Locked;
else if ( !Q_stricmp(modeName.c_str() , "waypoint" ) )
_moveMode = new HVMoveMode_FollowPath;
}
void HorseVehicle::_SetCrossHairMode()
{
if ( _newCrosshairMode == _currentCrosshairMode )
return;
switch ( _newCrosshairMode )
{
case CROSSHAIR_MODE_STRAIGHT:
gi.cvar_set("cl_chright", "100" );
gi.cvar_set("cl_chleft" , "-100" );
_currentCrosshairMode = CROSSHAIR_MODE_STRAIGHT;
break;
case CROSSHAIR_MODE_LEFT:
gi.cvar_set("cl_chright", "310" );
gi.cvar_set("cl_chleft" , "-100" );
_currentCrosshairMode = CROSSHAIR_MODE_LEFT;
break;
case CROSSHAIR_MODE_RIGHT:
gi.cvar_set("cl_chright", "100" );
gi.cvar_set("cl_chleft" , "-310" );
_currentCrosshairMode = CROSSHAIR_MODE_RIGHT;
break;
default:
return;
}
}
void HorseVehicle::AnimDone( Event *ev )
{
_animDone = true;
}
void HorseVehicle::DriverAnimDone( Event *ev )
{
_driverAnimDone = true;
}
void HorseVehicle::HandleEvent( Event *ev )
{
Event *new_event;
new_event = new Event( ev );
ProcessEvent(new_event);
}
void HorseVehicle::_SetBaseYaw()
{
if ( !driver )
return;
Player *player;
player = ( Player * )( Sentient * )driver;
_baseYaw = player->client->cmd_angles[YAW];
_lastYaw = _baseYaw;
}
//===============================================================================
//
// Vehicle Move Mode Classes:
// These classes act as movement handling strategies for vehicles. This way
// we can specify multiple different ways a single vehicle reacts, based on
// the context of the game.
//
//===============================================================================
CLASS_DECLARATION( Listener, VehicleMoveMode, "MoveModeBaseClass" )
{
{ NULL, NULL }
};
VehicleMoveMode::VehicleMoveMode()
{
}
void VehicleMoveMode::Move( Vehicle *vehicle )
{
}
void VehicleMoveMode::HandleEvent( Event *ev )
{
}
//==============================================================================
//
// HVMoveMode_Standard
//
// Standard Move Strategy for the Horse Vehicle. This is the strategy that is
// instaniated by default
//
//===============================================================================
HVMoveMode_Standard::HVMoveMode_Standard()
{
}
void HVMoveMode_Standard::Move( Vehicle *base_vehicle )
{
HorseVehicle *vehicle;
vehicle = (HorseVehicle*)base_vehicle;
Vector flatvel;
//Zero Out our Velocity
vehicle->velocity = vec_zero;
if ( vehicle->moveimpulse > 0.0f )
{
if ( vehicle->currentspeed < 0.0f )
vehicle->currentspeed *= -1.0f;
flatvel = Vector( vehicle->orientation[ 0 ] );
vehicle->velocity += flatvel * vehicle->currentspeed;
}
if ( vehicle->moveimpulse < 0.0f )
{
if ( vehicle->currentspeed > 0.0f )
vehicle->currentspeed *= -1.0f;
flatvel = Vector( vehicle->orientation[ 0 ] );
vehicle->velocity += flatvel * vehicle->currentspeed;
}
if ( vehicle->turnimpulse > 0.0f )
{
if ( vehicle->currentspeed > 0.0f )
vehicle->currentspeed *= -1.0f;
flatvel = Vector( vehicle->orientation[ 1 ] );
vehicle->velocity += flatvel * vehicle->currentspeed;
}
if ( vehicle->turnimpulse < 0.0f )
{
if ( vehicle->currentspeed < 0.0f )
vehicle->currentspeed *= -1.0f;
flatvel = Vector( vehicle->orientation[ 1 ] );
vehicle->velocity += flatvel * vehicle->currentspeed;
}
}
//==============================================================================
//
// HVMoveMode_Strafe
//
// The only movement allowed by the horse is strafing left and right
//
//===============================================================================
HVMoveMode_Strafe::HVMoveMode_Strafe()
{
}
void HVMoveMode_Strafe::Move( Vehicle *base_vehicle )
{
HorseVehicle *vehicle;
vehicle = (HorseVehicle*)base_vehicle;
Vector flatvel;
//Zero Out our Velocity
vehicle->velocity = vec_zero;
if ( vehicle->turnimpulse > 0)
{
if ( vehicle->currentspeed > 0 )
vehicle->currentspeed*=-1;
flatvel = Vector( vehicle->orientation[ 1 ] );
vehicle->velocity += flatvel * vehicle->currentspeed;
}
if ( vehicle->turnimpulse < 0)
{
if ( vehicle->currentspeed < 0 )
vehicle->currentspeed*=-1;
flatvel = Vector( vehicle->orientation[ 1 ] );
vehicle->velocity += flatvel * vehicle->currentspeed;
}
}
//==============================================================================
//
// HVMoveMode_Locked
//
// A bit strange... Allows No Movement at all
//
//===============================================================================
HVMoveMode_Locked::HVMoveMode_Locked()
{
}
void HVMoveMode_Locked::Move( Vehicle *base_vehicle )
{
}
//==============================================================================
//
// HVMoveMode_FollowPath
//
// Makes the Vehicle Follow WayPointNodes -- There is currently NO collision
// avoidance here
//
//===============================================================================
Event EV_FollowPath_SetWayPointName
(
"waypointname",
EV_SCRIPTONLY,
"s",
"waypoint_target_name",
"Set the waypoint node name to go to first"
);
CLASS_DECLARATION( VehicleMoveMode, HVMoveMode_FollowPath, "FollowPath_MoveModeStrategy" )
{
{ &EV_FollowPath_SetWayPointName, &HVMoveMode_FollowPath::SetWaypointName },
{ NULL, NULL }
};
HVMoveMode_FollowPath::HVMoveMode_FollowPath()
{
_pathcompleted = false;
_currentWaypoint = NULL;
}
void HVMoveMode_FollowPath::Move( Vehicle *base_vehicle )
{
HorseVehicle *vehicle;
vehicle = (HorseVehicle*)base_vehicle;
//Zero Out our Velocity
vehicle->velocity = vec_zero;
if ( vehicle->currentspeed < 0.0f )
vehicle->currentspeed *= -1.0f;
//First Check if we are at a way point;
Vector dest;
Vector check;
if ( _pathcompleted )
return;
if (!_currentWaypoint)
{
_SetWayPoint( _currentWaypointName );
if (!_currentWaypoint)
return;
}
//Paranoia check, as it keeps crashing one particular box
if ( !_currentWaypoint )
return;
dest = _currentWaypoint->origin;
check = dest - vehicle->origin;
// Take care of Z - Differences
check[2] = 0;
// Check if we're close enough
if ( check.length() < 25.0f )
{
// Run Our Thread
str waypointThread;
waypointThread = _currentWaypoint->GetThread();
if (waypointThread.length() )
_RunThread( waypointThread );
// See if we have another point to go to
if ( _currentWaypoint->target.length() == 0 )
{
vehicle->velocity = vec_zero;
_currentWaypoint = NULL;
_pathcompleted = true;
return;
}
// Go To the Next Point
_currentWaypointName = _currentWaypoint->target;
_SetWayPoint( _currentWaypointName );
if (!_currentWaypoint)
return;
}
if ( _currentWaypoint )
{
vehicle->velocity = check;
vehicle->velocity.normalize();
vehicle->velocity *= vehicle->currentspeed;
}
}
void HVMoveMode_FollowPath::SetWaypointName( Event *ev )
{
_currentWaypointName = ev->GetString( 1 );
_pathcompleted = false;
}
void HVMoveMode_FollowPath::_SetWayPoint( const str& name )
{
Entity* ent_in_range;
gentity_t *ed;
for ( int i = 0; i < MAX_GENTITIES; i++ )
{
ed = &g_entities[i];
if ( !ed->inuse || !ed->entity )
{
continue;
}
ent_in_range = g_entities[i].entity;
if( ent_in_range->isSubclassOf( WayPointNode ) )
{
if (!Q_stricmp(ent_in_range->targetname.c_str() , name.c_str() ))
{
_currentWaypoint = (WayPointNode*)ent_in_range;
return;
}
}
}
_currentWaypoint = NULL;
}
void HVMoveMode_FollowPath::_RunThread( const str &thread_name )
{
if ( thread_name.length() <= 0 )
return;
CThread *thread;
thread = Director.CreateThread( thread_name );
if ( thread )
thread->DelayedStart( 0.0f );
}
void HVMoveMode_FollowPath::HandleEvent( Event *ev )
{
Event *new_event;
new_event = new Event( ev );
ProcessEvent(new_event);
}