1178 lines
24 KiB
C++
1178 lines
24 KiB
C++
// 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 "g_local.h"
|
|
#include "scriptslave.h"
|
|
#include "vehicle.h"
|
|
#include "player.h"
|
|
#include "specialfx.h"
|
|
#include "explosion.h"
|
|
#include "earthquake.h"
|
|
#include "gibs.h"
|
|
|
|
Event EV_Vehicle_Start( "start" );
|
|
Event EV_Vehicle_Enter( "enter" );
|
|
Event EV_Vehicle_Exit( "exit" );
|
|
Event EV_Vehicle_Drivable( "drivable" );
|
|
Event EV_Vehicle_UnDrivable( "undrivable" );
|
|
Event EV_Vehicle_Jumpable( "canjump" );
|
|
Event EV_Vehicle_Lock( "lock" );
|
|
Event EV_Vehicle_UnLock( "unlock" );
|
|
Event EV_Vehicle_SeatAnglesOffset( "seatanglesoffset" );
|
|
Event EV_Vehicle_SeatOffset( "seatoffset" );
|
|
Event EV_Vehicle_DriverAnimation( "driveranim" );
|
|
Event EV_Vehicle_SetWeapon( "setweapon" );
|
|
Event EV_Vehicle_SetSpeed( "vehiclespeed" );
|
|
Event EV_Vehicle_SetTurnRate( "turnrate" );
|
|
Event EV_Vehicle_SteerInPlace( "steerinplace" );
|
|
Event EV_Vehicle_ShowWeapon( "showweapon" );
|
|
|
|
|
|
CLASS_DECLARATION( ScriptModel, VehicleBase, NULL );
|
|
|
|
ResponseDef VehicleBase::Responses[] =
|
|
{
|
|
{ NULL, NULL }
|
|
};
|
|
|
|
VehicleBase::VehicleBase()
|
|
{
|
|
takedamage = DAMAGE_NO;
|
|
showModel();
|
|
setSolidType( SOLID_NOT );
|
|
setMoveType( MOVETYPE_NONE );
|
|
setOrigin( origin + 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 = "0 0 0";
|
|
}
|
|
|
|
CLASS_DECLARATION( VehicleBase, BackWheels, "script_wheelsback" );
|
|
|
|
ResponseDef BackWheels::Responses[] =
|
|
{
|
|
{ NULL, NULL }
|
|
};
|
|
|
|
CLASS_DECLARATION( VehicleBase, FrontWheels, "script_wheelsfront" );
|
|
|
|
ResponseDef FrontWheels::Responses[] =
|
|
{
|
|
{ NULL, NULL }
|
|
};
|
|
|
|
CLASS_DECLARATION( VehicleBase, Vehicle, "script_vehicle" );
|
|
|
|
ResponseDef Vehicle::Responses[] =
|
|
{
|
|
{ &EV_Blocked, ( Response )Vehicle::VehicleBlocked },
|
|
{ &EV_Touch, ( Response )Vehicle::VehicleTouched },
|
|
{ &EV_Use, ( Response )Vehicle::DriverUse },
|
|
{ &EV_Vehicle_Start, ( Response )Vehicle::VehicleStart },
|
|
{ &EV_Vehicle_Drivable, ( Response )Vehicle::Drivable },
|
|
{ &EV_Vehicle_UnDrivable, ( Response )Vehicle::UnDrivable },
|
|
{ &EV_Vehicle_Jumpable, ( Response )Vehicle::Jumpable },
|
|
{ &EV_Vehicle_SeatAnglesOffset, ( Response )Vehicle::SeatAnglesOffset },
|
|
{ &EV_Vehicle_SeatOffset, ( Response )Vehicle::SeatOffset },
|
|
{ &EV_Vehicle_Lock, ( Response )Vehicle::Lock },
|
|
{ &EV_Vehicle_UnLock, ( Response )Vehicle::UnLock },
|
|
{ &EV_Vehicle_SetWeapon, ( Response )Vehicle::SetWeapon },
|
|
{ &EV_Vehicle_DriverAnimation, ( Response )Vehicle::DriverAnimation },
|
|
{ &EV_Vehicle_SetSpeed, ( Response )Vehicle::SetSpeed },
|
|
{ &EV_Vehicle_SetTurnRate, ( Response )Vehicle::SetTurnRate },
|
|
{ &EV_Vehicle_SteerInPlace, ( Response )Vehicle::SteerInPlace },
|
|
{ &EV_Vehicle_ShowWeapon, ( Response )Vehicle::ShowWeaponEvent },
|
|
{ NULL, NULL }
|
|
};
|
|
|
|
Vehicle::Vehicle()
|
|
{
|
|
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 = false;
|
|
jumpable = false;
|
|
showweapon = false;
|
|
setSolidType( SOLID_BBOX );
|
|
flags |= FL_SPARKS|FL_DIE_TESSELATE|FL_DIE_EXPLODE|FL_DARKEN;
|
|
gravity = 1;
|
|
mass = size.length() * 10;
|
|
|
|
//
|
|
// we use this to signify the init state of the angles
|
|
//
|
|
health = G_GetFloatArg( "health", 1000 );
|
|
speed = G_GetFloatArg( "speed", 600 );
|
|
maxturnrate = G_GetFloatArg( "maxturnrate", 40.0f );
|
|
PostEvent( EV_Vehicle_Start, 0 );
|
|
}
|
|
|
|
void Vehicle::VehicleStart
|
|
(
|
|
Event *ev
|
|
)
|
|
|
|
{
|
|
Entity *ent;
|
|
VehicleBase *last;
|
|
vec3_t trans[ 3 ];
|
|
vec3_t orient;
|
|
Vector drivemins, drivemaxs;
|
|
int groupindex;
|
|
int tri_num;
|
|
float max;
|
|
float width,height;
|
|
|
|
|
|
last = this;
|
|
|
|
for( ent = G_NextEntity( world ); 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->worldorigin - worldorigin;
|
|
last->offset = getLocalVector( last->offset );
|
|
last->edict->s.scale *= edict->s.scale;
|
|
}
|
|
}
|
|
}
|
|
|
|
last->vlink = NULL;
|
|
|
|
//
|
|
// get the seat offset
|
|
//
|
|
if ( gi.GetBoneInfo( edict->s.modelindex, "seat", &groupindex, &tri_num, orient ) )
|
|
{
|
|
gi.GetBoneTransform( edict->s.modelindex, groupindex, tri_num, orient, edict->s.anim, edict->s.frame,
|
|
edict->s.scale, trans, driveroffset.vec3() );
|
|
}
|
|
driveroffset += seatoffset * edict->s.scale;
|
|
SetDriverAngles( worldangles + 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);
|
|
Corners[ 0 ][ 1 ] = (height/4);
|
|
Corners[ 0 ][ 2 ] = 0;
|
|
|
|
Corners[ 1 ][ 0 ] = (width/4);
|
|
Corners[ 1 ][ 1 ] = (height/4);
|
|
Corners[ 1 ][ 2 ] = 0;
|
|
|
|
Corners[ 2 ][ 0 ] = -(width/4);
|
|
Corners[ 2 ][ 1 ] = -(height/4);
|
|
Corners[ 2 ][ 2 ] = 0;
|
|
|
|
Corners[ 3 ][ 0 ] = (width/4);
|
|
Corners[ 3 ][ 1 ] = -(height/4);
|
|
Corners[ 3 ][ 2 ] = 0;
|
|
if ( drivable )
|
|
{
|
|
// drop everything back to the floor
|
|
droptofloor( 64 );
|
|
Postthink();
|
|
}
|
|
last_origin = worldorigin;
|
|
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;
|
|
}
|
|
|
|
void Vehicle::DriverAnimation
|
|
(
|
|
Event *ev
|
|
)
|
|
|
|
{
|
|
driveranim = ev->GetString( 1 );
|
|
}
|
|
|
|
qboolean Vehicle::HasWeapon
|
|
(
|
|
void
|
|
)
|
|
|
|
{
|
|
return hasweapon;
|
|
}
|
|
|
|
qboolean Vehicle::ShowWeapon
|
|
(
|
|
void
|
|
)
|
|
|
|
{
|
|
return showweapon;
|
|
}
|
|
|
|
void Vehicle::SetDriverAngles
|
|
(
|
|
Vector angles
|
|
)
|
|
{
|
|
int i;
|
|
|
|
if ( !driver )
|
|
return;
|
|
|
|
for( i = 0; i < 3; i++ )
|
|
{
|
|
driver->client->ps.pmove.delta_angles[ i ] = ANGLE2SHORT( angles[ i ] - driver->client->resp.cmd_angles[ i ] );
|
|
}
|
|
}
|
|
|
|
/*
|
|
=============
|
|
CheckWater
|
|
=============
|
|
*/
|
|
void Vehicle::CheckWater
|
|
(
|
|
void
|
|
)
|
|
|
|
{
|
|
Vector point;
|
|
int cont;
|
|
int sample1;
|
|
int sample2;
|
|
VehicleBase *v;
|
|
const gravityaxis_t &grav = gravity_axis[ gravaxis ];
|
|
|
|
unlink();
|
|
v = this;
|
|
while( v->vlink )
|
|
{
|
|
v = v->vlink;
|
|
v->unlink();
|
|
}
|
|
|
|
if ( driver )
|
|
{
|
|
driver->unlink();
|
|
}
|
|
|
|
//
|
|
// get waterlevel
|
|
//
|
|
waterlevel = 0;
|
|
watertype = 0;
|
|
|
|
sample2 = maxs[ grav.z ] - mins[ grav.z ];
|
|
sample1 = sample2 / 2;
|
|
|
|
point[ grav.x ] = worldorigin[ grav.x ];
|
|
point[ grav.y ] = worldorigin[ grav.y ];
|
|
point[ grav.z ] = worldorigin[ grav.z ] + mins[ grav.z ] + grav.sign;
|
|
cont = gi.pointcontents( point.vec3() );
|
|
|
|
if ( cont & MASK_WATER )
|
|
{
|
|
watertype = cont;
|
|
waterlevel = 1;
|
|
point[ grav.z ] = worldorigin[ grav.z ] + mins[ grav.z ] + sample1;
|
|
cont = gi.pointcontents( point.vec3() );
|
|
if ( cont & MASK_WATER )
|
|
{
|
|
waterlevel = 2;
|
|
point[ grav.z ] = worldorigin[ grav.z ] + mins[ grav.z ] + sample2;
|
|
cont = gi.pointcontents( point.vec3() );
|
|
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(EARTHQUAKE_STRENGTH*G_CRandom(),EARTHQUAKE_STRENGTH*G_CRandom(),fabs(50*G_CRandom()));
|
|
}
|
|
|
|
//
|
|
// check for lava
|
|
//
|
|
if ( watertype & CONTENTS_LAVA )
|
|
{
|
|
Damage( world, world, 20 * waterlevel, worldorigin, vec_zero, vec_zero, 0, DAMAGE_NO_ARMOR, MOD_LAVA, -1, -1, 1.0f );
|
|
}
|
|
}
|
|
|
|
void Vehicle::DriverUse
|
|
(
|
|
Event *ev
|
|
)
|
|
|
|
{
|
|
Event *event;
|
|
Entity *other;
|
|
Sentient *sent;
|
|
|
|
other = ev->GetEntity( 1 );
|
|
if ( !other || !other->isSubclassOf( Sentient ) )
|
|
{
|
|
return;
|
|
}
|
|
|
|
sent = ( Sentient * )other;
|
|
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->worldangles[ 1 ] + ang + 90;
|
|
angles.AngleVectors( &forward, NULL, NULL );
|
|
pos = worldorigin + (forward * ofs);
|
|
pos[2] += height;
|
|
trace = G_Trace( pos, driver->mins, driver->maxs, pos, NULL, MASK_PLAYERSOLID, "Vehicle::DriverUse 1" );
|
|
if ( !trace.allsolid )
|
|
{
|
|
Vector end;
|
|
|
|
end = pos;
|
|
end[ 2 ] -= 128;
|
|
trace = G_Trace( pos, driver->mins, driver->maxs, end, NULL, MASK_PLAYERSOLID, "Vehicle::DriverUse 2" );
|
|
if ( trace.fraction < 1.0f )
|
|
{
|
|
driver->setOrigin( pos );
|
|
goto foundpos;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
return;
|
|
|
|
foundpos:
|
|
|
|
turnimpulse = 0;
|
|
moveimpulse = 0;
|
|
jumpimpulse = 0;
|
|
|
|
//driver->unbind();
|
|
event = new Event( EV_Vehicle_Exit );
|
|
event->AddEntity( this );
|
|
driver->ProcessEvent( event );
|
|
if ( hasweapon )
|
|
{
|
|
driver->takeWeapon( weaponName.c_str() );
|
|
}
|
|
if ( drivable )
|
|
{
|
|
edict->s.sound = 0;
|
|
RandomSound( "snd_dooropen", 1, CHAN_BODY );
|
|
RandomSound( "snd_stop", 1, CHAN_VOICE );
|
|
driver->setSolidType( SOLID_BBOX );
|
|
}
|
|
|
|
driver = NULL;
|
|
//if ( drivable )
|
|
// setMoveType( MOVETYPE_NONE );
|
|
}
|
|
else
|
|
{
|
|
driver = ( Sentient * )other;
|
|
lastdriver = driver;
|
|
|
|
if ( drivable )
|
|
setMoveType( MOVETYPE_VEHICLE );
|
|
if ( hasweapon )
|
|
{
|
|
Weapon *weapon;
|
|
|
|
weapon = driver->giveWeapon( weaponName.c_str() );
|
|
if ( weapon )
|
|
{
|
|
driver->ForceChangeWeapon( weapon );
|
|
}
|
|
else
|
|
{
|
|
return;
|
|
}
|
|
}
|
|
if ( drivable )
|
|
{
|
|
RandomSound( "snd_doorclose", 1, CHAN_BODY );
|
|
RandomSound( "snd_start", 1, CHAN_VOICE );
|
|
driver->setSolidType( SOLID_NOT );
|
|
}
|
|
|
|
event = new Event( EV_Vehicle_Enter );
|
|
event->AddEntity( this );
|
|
if ( driveranim.length() )
|
|
event->AddString( driveranim );
|
|
driver->ProcessEvent( event );
|
|
|
|
//driver->bind( this );
|
|
//offset = other->origin;
|
|
offset = other->worldorigin - worldorigin;
|
|
|
|
flags |= FL_POSTTHINK;
|
|
SetDriverAngles( worldangles + seatangles );
|
|
}
|
|
}
|
|
|
|
qboolean Vehicle::Drive
|
|
(
|
|
usercmd_t *ucmd
|
|
)
|
|
|
|
{
|
|
Vector i, j, k;
|
|
|
|
if ( !driver || !driver->isClient() )
|
|
{
|
|
return false;
|
|
}
|
|
|
|
if ( !drivable )
|
|
{
|
|
driver->client->ps.pmove.pm_type = PM_INVEHICLE;
|
|
ucmd->forwardmove = 0;
|
|
ucmd->sidemove = 0;
|
|
return false;
|
|
}
|
|
|
|
driver->client->ps.pmove.pm_type = PM_LOCKVIEW;
|
|
driver->client->ps.pmove.pm_flags |= PMF_NO_PREDICTION;
|
|
|
|
moveimpulse = ( ( float )ucmd->forwardmove ) * 3;
|
|
turnimpulse = ( ( float )-ucmd->sidemove ) * 0.5;
|
|
jumpimpulse = ( ( float )ucmd->upmove * gravity ) / 350;
|
|
if ( ( jumpimpulse < 0 ) || ( !jumpable ) )
|
|
jumpimpulse = 0;
|
|
|
|
turnimpulse += 2*angledist( SHORT2ANGLE( ucmd->angles[ 1 ] ) - driver->client->resp.cmd_angles[ 1 ] );
|
|
|
|
return true;
|
|
}
|
|
|
|
void Vehicle::Postthink
|
|
(
|
|
void
|
|
)
|
|
|
|
{
|
|
float turn;
|
|
Vector i, j, k;
|
|
int index;
|
|
trace_t trace;
|
|
Vector normalsum;
|
|
int numnormals;
|
|
Vector temp;
|
|
Vector pitch;
|
|
Vector roll;
|
|
VehicleBase *v;
|
|
VehicleBase *last;
|
|
float drivespeed;
|
|
|
|
|
|
if ( drivable )
|
|
{
|
|
currentspeed = moveimpulse / 10;
|
|
|
|
turnangle = turnangle * 0.25f + turnimpulse;
|
|
if ( turnangle > maxturnrate )
|
|
{
|
|
turnangle = maxturnrate;
|
|
}
|
|
else if ( turnangle < -maxturnrate )
|
|
{
|
|
turnangle = -maxturnrate;
|
|
}
|
|
else if ( fabs( turnangle ) < 2 )
|
|
{
|
|
turnangle = 0;
|
|
}
|
|
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 ( index = 0; index < 4; index++ )
|
|
{
|
|
Vector start, end;
|
|
Vector boxoffset;
|
|
|
|
boxoffset = Corners[ index ];
|
|
start = worldorigin + 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, "Vehicle::PostThink Corners" );
|
|
if ( 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());
|
|
}
|
|
|
|
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.05;
|
|
if ( steerinplace )
|
|
{
|
|
if ( dot < 350 )
|
|
dot = 350;
|
|
avelocity.y += turn * dot;
|
|
}
|
|
else
|
|
{
|
|
avelocity.y += turn * dot;
|
|
}
|
|
}
|
|
else
|
|
{
|
|
avelocity *= 0.1;
|
|
}
|
|
angles += avelocity * FRAMETIME;
|
|
setAngles( angles );
|
|
}
|
|
drivespeed = velocity * Vector( orientation[ 0 ] );
|
|
|
|
if ( drivable && driver )
|
|
{
|
|
Event * event;
|
|
|
|
event = new Event( EV_RandomEntitySound );
|
|
if ( currentspeed > 0 )
|
|
event->AddString( "snd_forward" );
|
|
else if ( currentspeed < 0 )
|
|
event->AddString( "snd_backward" );
|
|
else
|
|
event->AddString( "snd_idle" );
|
|
ProcessEvent( event );
|
|
}
|
|
i = Vector( orientation[ 0 ] );
|
|
j = Vector( orientation[ 1 ] );
|
|
k = Vector( orientation[ 2 ] );
|
|
|
|
if ( driver )
|
|
{
|
|
Player * player;
|
|
|
|
player = ( Player * )( Sentient * )driver;
|
|
player->setOrigin( worldorigin + i * driveroffset[0] + j * driveroffset[1] + k * driveroffset[2] );
|
|
if ( drivable )
|
|
{
|
|
player->velocity = vec_zero;
|
|
player->setAngles( angles );
|
|
player->v_angle = angles;
|
|
player->v_angle[ PITCH ] = player->v_angle[ PITCH ];
|
|
}
|
|
}
|
|
|
|
last = this;
|
|
while( last->vlink )
|
|
{
|
|
v = last->vlink;
|
|
v->setOrigin( worldorigin + 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 ] = (int)( v->angles[ PITCH ] + (drivespeed/4) ) % 360;
|
|
|
|
if ( v->isSubclassOf( FrontWheels ) )
|
|
{
|
|
v->angles += Vector( 0, turnangle, 0 );
|
|
}
|
|
v->setAngles( v->angles );
|
|
|
|
last = v;
|
|
}
|
|
|
|
CheckWater();
|
|
WorldEffects();
|
|
|
|
// save off last origin
|
|
last_origin = worldorigin;
|
|
|
|
if ( !driver && !velocity.length() && groundentity && !( watertype & CONTENTS_LAVA ) )
|
|
{
|
|
flags &= ~FL_POSTTHINK;
|
|
if ( drivable )
|
|
setMoveType( MOVETYPE_NONE );
|
|
}
|
|
}
|
|
|
|
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 = worldorigin - last_origin;
|
|
speed = delta.length();
|
|
if ( speed > 2 )
|
|
{
|
|
RandomGlobalSound( "vehicle_crash" );
|
|
dir = delta * ( 1 / speed );
|
|
other->Damage( this, lastdriver, speed * 8, worldorigin, dir, vec_zero, speed*15, 0, MOD_VEHICLE, -1, -1, 1.0f );
|
|
}
|
|
|
|
}
|
|
|
|
void Vehicle::VehicleBlocked
|
|
(
|
|
Event *ev
|
|
)
|
|
|
|
{
|
|
Entity *other;
|
|
float speed;
|
|
float damage;
|
|
Vector delta;
|
|
Vector newvel;
|
|
Vector dir;
|
|
|
|
return;
|
|
if ( !velocity[0] && !velocity[1] )
|
|
return;
|
|
|
|
other = ev->GetEntity( 1 );
|
|
if ( other == driver )
|
|
{
|
|
return;
|
|
}
|
|
if ( other->isSubclassOf( VehicleBase ) )
|
|
{
|
|
delta = other->worldorigin - worldorigin;
|
|
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, worldorigin, 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" );
|
|
|
|
ResponseDef DrivableVehicle::Responses[] =
|
|
{
|
|
{ &EV_Damage, ( Response )Entity::DamageEvent },
|
|
{ &EV_Killed, ( Response )DrivableVehicle::Killed },
|
|
{ NULL, NULL }
|
|
};
|
|
|
|
DrivableVehicle::DrivableVehicle()
|
|
{
|
|
drivable = true;
|
|
flags |= FL_SPARKS|FL_DIE_TESSELATE|FL_DIE_EXPLODE|FL_DARKEN;
|
|
}
|
|
|
|
void DrivableVehicle::Killed(Event *ev)
|
|
{
|
|
Entity * ent;
|
|
Entity * attacker;
|
|
Vector dir;
|
|
Event * event;
|
|
const char * name;
|
|
int num;
|
|
VehicleBase *last;
|
|
int i; //###
|
|
|
|
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->worldorigin - worldorigin;
|
|
dir[ 2 ] += 64;
|
|
dir.normalize();
|
|
sent->Damage( this, this, sent->health*2, worldorigin, dir, vec_zero, 50, 0, MOD_VEHICLE, -1, -1, 1.0f );
|
|
}
|
|
|
|
if (flags & FL_DIE_TESSELATE)
|
|
{
|
|
dir = worldorigin - attacker->worldorigin;
|
|
TesselateModel
|
|
(
|
|
this,
|
|
tess_min_size,
|
|
tess_max_size,
|
|
dir,
|
|
ev->GetInteger( 2 ),
|
|
tess_percentage,
|
|
tess_thickness,
|
|
vec3_origin
|
|
);
|
|
ProcessEvent( EV_BreakingSound );
|
|
}
|
|
|
|
if (flags & FL_DIE_EXPLODE)
|
|
{
|
|
CreateExplosion( worldorigin, 150*edict->s.scale, edict->s.scale * 2, true, this, this, this );
|
|
}
|
|
|
|
if (flags & FL_DIE_GIBS)
|
|
{
|
|
setSolidType( SOLID_NOT );
|
|
hideModel();
|
|
|
|
CreateGibs( this, -150, edict->s.scale, 3 );
|
|
}
|
|
//
|
|
// kill all my wheels
|
|
//
|
|
last = this;
|
|
while( last->vlink )
|
|
{
|
|
last->vlink->PostEvent( EV_Remove, 0 );
|
|
last = last->vlink;
|
|
}
|
|
|
|
|
|
//
|
|
// kill the killtargets
|
|
//
|
|
//### added extended targeting stuff
|
|
/*
|
|
name = KillTarget();
|
|
if ( name && strcmp( name, "" ) )
|
|
{
|
|
num = 0;
|
|
do
|
|
{
|
|
num = G_FindTarget( num, name );
|
|
if ( !num )
|
|
{
|
|
break;
|
|
}
|
|
|
|
ent = G_GetEntity( num );
|
|
ent->PostEvent( EV_Remove, 0 );
|
|
}
|
|
while ( 1 );
|
|
}
|
|
*/
|
|
for(i = 0; i < 2; i++)
|
|
{
|
|
switch(i)
|
|
{
|
|
case 0:
|
|
name = KillTarget();
|
|
break;
|
|
case 1:
|
|
name = KillTarget2();
|
|
break;
|
|
}
|
|
|
|
if ( name && strcmp( name, "" ) )
|
|
{
|
|
num = 0;
|
|
do
|
|
{
|
|
num = G_FindTarget( num, name );
|
|
if ( !num )
|
|
{
|
|
break;
|
|
}
|
|
|
|
ent = G_GetEntity( num );
|
|
ent->PostEvent( EV_Remove, 0 );
|
|
} while ( 1 );
|
|
}
|
|
}
|
|
//###
|
|
|
|
//
|
|
// fire targets
|
|
//
|
|
//### added extended targeting stuff
|
|
/*
|
|
name = Target();
|
|
if ( name && strcmp( name, "" ) )
|
|
{
|
|
num = 0;
|
|
do
|
|
{
|
|
num = G_FindTarget( num, name );
|
|
if ( !num )
|
|
{
|
|
break;
|
|
}
|
|
|
|
ent = G_GetEntity( num );
|
|
|
|
event = new Event( EV_Activate );
|
|
event->AddEntity( attacker );
|
|
ent->ProcessEvent( event );
|
|
}
|
|
while ( 1 );
|
|
}
|
|
*/
|
|
for(i = 0; i < 4; i++)
|
|
{
|
|
switch(i)
|
|
{
|
|
case 0:
|
|
name = Target();
|
|
break;
|
|
case 1:
|
|
name = Target2();
|
|
break;
|
|
case 2:
|
|
name = Target3();
|
|
break;
|
|
case 3:
|
|
name = Target4();
|
|
break;
|
|
}
|
|
|
|
if ( name && strcmp( name, "" ) )
|
|
{
|
|
num = 0;
|
|
do
|
|
{
|
|
num = G_FindTarget( num, name );
|
|
if ( !num )
|
|
{
|
|
break;
|
|
}
|
|
|
|
ent = G_GetEntity( num );
|
|
|
|
event = new Event( EV_Activate );
|
|
event->AddEntity( attacker );
|
|
ent->ProcessEvent( event );
|
|
} while ( 1 );
|
|
}
|
|
}
|
|
//###
|
|
|
|
PostEvent( EV_Remove, 0 );
|
|
}
|